2014-05-31 01:59:03 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using MinecraftClient.Protocol.Handlers;
|
|
|
|
|
|
using MinecraftClient.Proxy;
|
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
|
using System.Net.Security;
|
|
|
|
|
|
|
|
|
|
|
|
namespace MinecraftClient.Protocol
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Handle login, session, server ping and provide a protocol handler for interacting with a minecraft server.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
|
|
|
|
public static class ProtocolHandler
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve information about a Minecraft server
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="serverIP">Server IP to ping</param>
|
2014-06-14 16:01:19 +02:00
|
|
|
|
/// <param name="serverPort">Server Port to ping</param>
|
2014-05-31 01:59:03 +02:00
|
|
|
|
/// <param name="protocolversion">Will contain protocol version, if ping successful</param>
|
|
|
|
|
|
/// <returns>TRUE if ping was successful</returns>
|
|
|
|
|
|
|
2014-11-06 17:59:19 +11:00
|
|
|
|
public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion)
|
2014-05-31 01:59:03 +02:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2014-06-14 16:01:19 +02:00
|
|
|
|
if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversion))
|
2014-05-31 01:59:03 +02:00
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2014-06-14 16:01:19 +02:00
|
|
|
|
else if (Protocol17Handler.doPing(serverIP, serverPort, ref protocolversion))
|
2014-05-31 01:59:03 +02:00
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2014-06-11 20:40:25 +02:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8Unexpected answer from the server (is that a Minecraft server ?)");
|
2014-05-31 01:59:03 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
2014-06-11 20:40:25 +02:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8An error occured while attempting to connect to this IP.");
|
2014-05-31 01:59:03 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get a protocol handler for the specified Minecraft version
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="Client">Tcp Client connected to the server</param>
|
|
|
|
|
|
/// <param name="ProtocolVersion">Protocol version to handle</param>
|
|
|
|
|
|
/// <param name="Handler">Handler with the appropriate callbacks</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
|
|
|
|
|
|
public static IMinecraftCom getProtocolHandler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler)
|
|
|
|
|
|
{
|
|
|
|
|
|
int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 };
|
|
|
|
|
|
if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1)
|
|
|
|
|
|
return new Protocol16Handler(Client, ProtocolVersion, Handler);
|
|
|
|
|
|
int[] supportedVersions_Protocol17 = { 4, 5 };
|
|
|
|
|
|
if (Array.IndexOf(supportedVersions_Protocol17, ProtocolVersion) > -1)
|
|
|
|
|
|
return new Protocol17Handler(Client, ProtocolVersion, Handler);
|
2014-09-04 13:58:49 +02:00
|
|
|
|
int[] supportedVersions_Protocol18 = { 47 };
|
|
|
|
|
|
if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1)
|
|
|
|
|
|
return new Protocol18Handler(Client, ProtocolVersion, Handler);
|
2014-06-14 16:01:19 +02:00
|
|
|
|
throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Convert a human-readable Minecraft version number to network protocol version number
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="MCVersion">The Minecraft version number</param>
|
|
|
|
|
|
/// <returns>The protocol version number or 0 if could not determine protocol version: error, unknown, not supported</returns>
|
|
|
|
|
|
|
|
|
|
|
|
public static int MCVer2ProtocolVersion(string MCVersion)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (MCVersion.Contains('.'))
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (MCVersion.Split(' ')[0].Trim())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "1.4.6":
|
|
|
|
|
|
case "1.4.7":
|
|
|
|
|
|
return 51;
|
|
|
|
|
|
case "1.5.1":
|
|
|
|
|
|
return 60;
|
|
|
|
|
|
case "1.5.2":
|
|
|
|
|
|
return 61;
|
|
|
|
|
|
case "1.6.0":
|
|
|
|
|
|
return 72;
|
|
|
|
|
|
case "1.6.1":
|
|
|
|
|
|
case "1.6.2":
|
|
|
|
|
|
case "1.6.3":
|
|
|
|
|
|
case "1.6.4":
|
|
|
|
|
|
return 73;
|
|
|
|
|
|
case "1.7.2":
|
|
|
|
|
|
case "1.7.3":
|
|
|
|
|
|
case "1.7.4":
|
|
|
|
|
|
case "1.7.5":
|
|
|
|
|
|
return 4;
|
|
|
|
|
|
case "1.7.6":
|
|
|
|
|
|
case "1.7.7":
|
|
|
|
|
|
case "1.7.8":
|
|
|
|
|
|
case "1.7.9":
|
2014-08-18 13:33:50 +02:00
|
|
|
|
case "1.7.10":
|
2014-06-14 16:01:19 +02:00
|
|
|
|
return 5;
|
2014-09-04 13:58:49 +02:00
|
|
|
|
case "1.8.0":
|
|
|
|
|
|
return 47;
|
2014-06-14 16:01:19 +02:00
|
|
|
|
default:
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return Int32.Parse(MCVersion);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-05-31 01:59:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium };
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="user">Login</param>
|
|
|
|
|
|
/// <param name="pass">Password</param>
|
|
|
|
|
|
/// <param name="accesstoken">Will contain the access token returned by Minecraft.net, if the login is successful</param>
|
|
|
|
|
|
/// <param name="uuid">Will contain the player's UUID, needed for multiplayer</param>
|
|
|
|
|
|
/// <returns>Returns the status of the login (Success, Failure, etc.)</returns>
|
|
|
|
|
|
|
|
|
|
|
|
public static LoginResult GetLogin(ref string user, string pass, ref string accesstoken, ref string uuid)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
string result = "";
|
2014-06-02 11:21:05 +02:00
|
|
|
|
string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + jsonEncode(user) + "\", \"password\": \"" + jsonEncode(pass) + "\" }";
|
2014-05-31 01:59:03 +02:00
|
|
|
|
int code = doHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result);
|
|
|
|
|
|
if (code == 200)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (result.Contains("availableProfiles\":[]}"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoginResult.NotPremium;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
if (temp.Length >= 2) { accesstoken = temp[1].Split('"')[0]; }
|
|
|
|
|
|
temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
if (temp.Length >= 2) { user = temp[1].Split('"')[0]; }
|
|
|
|
|
|
temp = result.Split(new string[] { "availableProfiles\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
if (temp.Length >= 2) { uuid = temp[1].Split('"')[0]; }
|
|
|
|
|
|
return LoginResult.Success;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (code == 403)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (result.Contains("UserMigratedException"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoginResult.AccountMigrated;
|
|
|
|
|
|
}
|
|
|
|
|
|
else return LoginResult.WrongPassword;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (code == 503)
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoginResult.ServiceUnavailable;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2014-06-11 20:40:25 +02:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8Got error code from server: " + code);
|
2014-05-31 01:59:03 +02:00
|
|
|
|
return LoginResult.OtherError;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.Security.Authentication.AuthenticationException)
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoginResult.SSLError;
|
2014-06-30 13:55:51 +02:00
|
|
|
|
}
|
2014-09-06 18:19:39 +02:00
|
|
|
|
catch (System.IO.IOException e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (e.Message.Contains("authentication"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoginResult.SSLError;
|
|
|
|
|
|
}
|
|
|
|
|
|
else return LoginResult.OtherError;
|
|
|
|
|
|
}
|
2014-05-31 01:59:03 +02:00
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoginResult.OtherError;
|
2014-06-30 13:55:51 +02:00
|
|
|
|
}
|
2014-05-31 01:59:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2014-06-03 13:05:53 +02:00
|
|
|
|
/// Check session using Mojang's Yggdrasil authentication scheme. Allows to join an online-mode server
|
2014-05-31 01:59:03 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="user">Username</param>
|
|
|
|
|
|
/// <param name="accesstoken">Session ID</param>
|
|
|
|
|
|
/// <param name="serverhash">Server ID</param>
|
|
|
|
|
|
/// <returns>TRUE if session was successfully checked</returns>
|
|
|
|
|
|
|
|
|
|
|
|
public static bool SessionCheck(string uuid, string accesstoken, string serverhash)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
string result = "";
|
|
|
|
|
|
string json_request = "{\"accessToken\":\"" + accesstoken + "\",\"selectedProfile\":\"" + uuid + "\",\"serverId\":\"" + serverhash + "\"}";
|
|
|
|
|
|
int code = doHTTPSPost("sessionserver.mojang.com", "/session/minecraft/join", json_request, ref result);
|
|
|
|
|
|
return (result == "");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch { return false; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Manual HTTPS request since we must directly use a TcpClient because of the proxy.
|
|
|
|
|
|
/// This method connects to the server, enables SSL, do the request and read the response.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="host">Host to connect to</param>
|
|
|
|
|
|
/// <param name="endpoint">Endpoint for making the request</param>
|
|
|
|
|
|
/// <param name="request">Request payload</param>
|
|
|
|
|
|
/// <param name="result">Request result</param>
|
|
|
|
|
|
/// <returns>HTTP Status code</returns>
|
|
|
|
|
|
|
|
|
|
|
|
private static int doHTTPSPost(string host, string endpoint, string request, ref string result)
|
|
|
|
|
|
{
|
|
|
|
|
|
TcpClient client = ProxyHandler.newTcpClient(host, 443);
|
|
|
|
|
|
SslStream stream = new SslStream(client.GetStream());
|
|
|
|
|
|
stream.AuthenticateAsClient(host);
|
|
|
|
|
|
|
|
|
|
|
|
List<String> http_request = new List<string>();
|
|
|
|
|
|
http_request.Add("POST " + endpoint + " HTTP/1.1");
|
|
|
|
|
|
http_request.Add("Host: " + host);
|
|
|
|
|
|
http_request.Add("User-Agent: MCC/" + Program.Version);
|
|
|
|
|
|
http_request.Add("Content-Type: application/json");
|
|
|
|
|
|
http_request.Add("Content-Length: " + Encoding.ASCII.GetBytes(request).Length);
|
|
|
|
|
|
http_request.Add("Connection: close");
|
|
|
|
|
|
http_request.Add("");
|
|
|
|
|
|
http_request.Add(request);
|
|
|
|
|
|
|
|
|
|
|
|
stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", http_request.ToArray())));
|
|
|
|
|
|
System.IO.StreamReader sr = new System.IO.StreamReader(stream);
|
|
|
|
|
|
string raw_result = sr.ReadToEnd();
|
|
|
|
|
|
|
|
|
|
|
|
if (raw_result.StartsWith("HTTP/1.1"))
|
|
|
|
|
|
{
|
|
|
|
|
|
result = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4);
|
|
|
|
|
|
return Settings.str2int(raw_result.Split(' ')[1]);
|
|
|
|
|
|
}
|
|
|
|
|
|
else return 520; //Web server is returning an unknown error
|
|
|
|
|
|
}
|
2014-06-02 11:21:05 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Encode a string to a json string.
|
|
|
|
|
|
/// Will convert special chars to \u0000 unicode escape sequences.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="text">Source text</param>
|
|
|
|
|
|
/// <returns>Encoded text</returns>
|
|
|
|
|
|
|
|
|
|
|
|
private static string jsonEncode(string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
|
|
foreach (char c in text)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (char.IsLetterOrDigit(c))
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Append(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Append("\\u");
|
|
|
|
|
|
result.Append(((int)c).ToString("x4"));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result.ToString();
|
|
|
|
|
|
}
|
2014-05-31 01:59:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|