Lower .NET requirement for Session Token

Changing constructs that weren't .NET 4.0 compliant.
Also fix \n to \r\n line returns in ProtocolHandler.cs
This commit is contained in:
ORelio 2016-03-05 19:10:13 +01:00
parent 0fbefb5068
commit 578a6170ef
2 changed files with 434 additions and 427 deletions

View file

@ -1,437 +1,437 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Handlers;
using MinecraftClient.Proxy; using MinecraftClient.Proxy;
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.Security; using System.Net.Security;
using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Handlers.Forge;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol
{ {
/// <summary> /// <summary>
/// Handle login, session, server ping and provide a protocol handler for interacting with a minecraft server. /// Handle login, session, server ping and provide a protocol handler for interacting with a minecraft server.
/// </summary> /// </summary>
public static class ProtocolHandler public static class ProtocolHandler
{ {
/// <summary> /// <summary>
/// Retrieve information about a Minecraft server /// Retrieve information about a Minecraft server
/// </summary> /// </summary>
/// <param name="serverIP">Server IP to ping</param> /// <param name="serverIP">Server IP to ping</param>
/// <param name="serverPort">Server Port to ping</param> /// <param name="serverPort">Server Port to ping</param>
/// <param name="protocolversion">Will contain protocol version, if ping successful</param> /// <param name="protocolversion">Will contain protocol version, if ping successful</param>
/// <returns>TRUE if ping was successful</returns> /// <returns>TRUE if ping was successful</returns>
public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo forgeInfo) public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo forgeInfo)
{ {
bool success = false; bool success = false;
int protocolversionTmp = 0; int protocolversionTmp = 0;
ForgeInfo forgeInfoTmp = null; ForgeInfo forgeInfoTmp = null;
if (AutoTimeout.Perform(() => if (AutoTimeout.Perform(() =>
{ {
try try
{ {
if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp) if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp)
|| Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp)) || Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp))
{ {
success = true; success = true;
} }
else ConsoleIO.WriteLineFormatted("§8Unexpected response from the server (is that a Minecraft server?)"); else ConsoleIO.WriteLineFormatted("§8Unexpected response from the server (is that a Minecraft server?)");
} }
catch (Exception e) catch (Exception e)
{ {
ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message)); ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message));
} }
}, TimeSpan.FromSeconds(30))) }, TimeSpan.FromSeconds(30)))
{ {
protocolversion = protocolversionTmp; protocolversion = protocolversionTmp;
forgeInfo = forgeInfoTmp; forgeInfo = forgeInfoTmp;
return success; return success;
} }
else else
{ {
ConsoleIO.WriteLineFormatted("§8A timeout occured while attempting to connect to this IP."); ConsoleIO.WriteLineFormatted("§8A timeout occured while attempting to connect to this IP.");
return false; return false;
} }
} }
/// <summary> /// <summary>
/// Get a protocol handler for the specified Minecraft version /// Get a protocol handler for the specified Minecraft version
/// </summary> /// </summary>
/// <param name="Client">Tcp Client connected to the server</param> /// <param name="Client">Tcp Client connected to the server</param>
/// <param name="ProtocolVersion">Protocol version to handle</param> /// <param name="ProtocolVersion">Protocol version to handle</param>
/// <param name="Handler">Handler with the appropriate callbacks</param> /// <param name="Handler">Handler with the appropriate callbacks</param>
/// <returns></returns> /// <returns></returns>
public static IMinecraftCom getProtocolHandler(TcpClient Client, int ProtocolVersion, ForgeInfo forgeInfo, IMinecraftComHandler Handler) public static IMinecraftCom getProtocolHandler(TcpClient Client, int ProtocolVersion, ForgeInfo forgeInfo, IMinecraftComHandler Handler)
{ {
int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 }; int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 };
if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1) if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1)
return new Protocol16Handler(Client, ProtocolVersion, Handler); return new Protocol16Handler(Client, ProtocolVersion, Handler);
int[] supportedVersions_Protocol18 = { 4, 5, 47 }; int[] supportedVersions_Protocol18 = { 4, 5, 47 };
if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1) if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1)
return new Protocol18Handler(Client, ProtocolVersion, Handler, forgeInfo); return new Protocol18Handler(Client, ProtocolVersion, Handler, forgeInfo);
throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported."); throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported.");
} }
/// <summary> /// <summary>
/// Convert a human-readable Minecraft version number to network protocol version number /// Convert a human-readable Minecraft version number to network protocol version number
/// </summary> /// </summary>
/// <param name="MCVersion">The Minecraft version number</param> /// <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> /// <returns>The protocol version number or 0 if could not determine protocol version: error, unknown, not supported</returns>
public static int MCVer2ProtocolVersion(string MCVersion) public static int MCVer2ProtocolVersion(string MCVersion)
{ {
if (MCVersion.Contains('.')) if (MCVersion.Contains('.'))
{ {
switch (MCVersion.Split(' ')[0].Trim()) switch (MCVersion.Split(' ')[0].Trim())
{ {
case "1.4.6": case "1.4.6":
case "1.4.7": case "1.4.7":
return 51; return 51;
case "1.5.1": case "1.5.1":
return 60; return 60;
case "1.5.2": case "1.5.2":
return 61; return 61;
case "1.6.0": case "1.6.0":
return 72; return 72;
case "1.6.1": case "1.6.1":
case "1.6.2": case "1.6.2":
case "1.6.3": case "1.6.3":
case "1.6.4": case "1.6.4":
return 73; return 73;
case "1.7.2": case "1.7.2":
case "1.7.3": case "1.7.3":
case "1.7.4": case "1.7.4":
case "1.7.5": case "1.7.5":
return 4; return 4;
case "1.7.6": case "1.7.6":
case "1.7.7": case "1.7.7":
case "1.7.8": case "1.7.8":
case "1.7.9": case "1.7.9":
case "1.7.10": case "1.7.10":
return 5; return 5;
case "1.8.0": case "1.8.0":
case "1.8.1": case "1.8.1":
case "1.8.2": case "1.8.2":
case "1.8.3": case "1.8.3":
case "1.8.4": case "1.8.4":
case "1.8.5": case "1.8.5":
case "1.8.6": case "1.8.6":
case "1.8.7": case "1.8.7":
case "1.8.8": case "1.8.8":
return 47; return 47;
default: default:
return 0; return 0;
} }
} }
else else
{ {
try try
{ {
return Int32.Parse(MCVersion); return Int32.Parse(MCVersion);
} }
catch catch
{ {
return 0; return 0;
} }
} }
} }
public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, NullError }; public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, NullError };
/// <summary> /// <summary>
/// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme. /// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme.
/// </summary> /// </summary>
/// <param name="user">Login</param> /// <param name="user">Login</param>
/// <param name="pass">Password</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="accesstoken">Will contain the access token returned by Minecraft.net, if the login is successful</param>
/// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param> /// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param>
/// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param> /// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param>
/// <returns>Returns the status of the login (Success, Failure, etc.)</returns> /// <returns>Returns the status of the login (Success, Failure, etc.)</returns>
public static LoginResult GetLogin(string user, string pass, out SessionToken session) public static LoginResult GetLogin(string user, string pass, out SessionToken session)
{ {
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") }; session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
try try
{ {
string result = ""; string result = "";
string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + jsonEncode(user) + "\", \"password\": \"" + jsonEncode(pass) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }"; string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + jsonEncode(user) + "\", \"password\": \"" + jsonEncode(pass) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }";
int code = doHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result); int code = doHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result);
if (code == 200) if (code == 200)
{ {
if (result.Contains("availableProfiles\":[]}")) if (result.Contains("availableProfiles\":[]}"))
{ {
return LoginResult.NotPremium; return LoginResult.NotPremium;
} }
else else
{ {
string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries); string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { session.ID = temp[1].Split('"')[0]; } if (temp.Length >= 2) { session.ID = temp[1].Split('"')[0]; }
temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries); temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { session.PlayerName = temp[1].Split('"')[0]; } if (temp.Length >= 2) { session.PlayerName = temp[1].Split('"')[0]; }
temp = result.Split(new string[] { "availableProfiles\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries); temp = result.Split(new string[] { "availableProfiles\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { session.PlayerID = temp[1].Split('"')[0]; } if (temp.Length >= 2) { session.PlayerID = temp[1].Split('"')[0]; }
return LoginResult.Success; return LoginResult.Success;
} }
} }
else if (code == 403) else if (code == 403)
{ {
if (result.Contains("UserMigratedException")) if (result.Contains("UserMigratedException"))
{ {
return LoginResult.AccountMigrated; return LoginResult.AccountMigrated;
} }
else return LoginResult.WrongPassword; else return LoginResult.WrongPassword;
} }
else if (code == 503) else if (code == 503)
{ {
return LoginResult.ServiceUnavailable; return LoginResult.ServiceUnavailable;
} }
else else
{ {
ConsoleIO.WriteLineFormatted("§8Got error code from server: " + code); ConsoleIO.WriteLineFormatted("§8Got error code from server: " + code);
return LoginResult.OtherError; return LoginResult.OtherError;
} }
} }
catch (System.Security.Authentication.AuthenticationException) catch (System.Security.Authentication.AuthenticationException)
{ {
return LoginResult.SSLError; return LoginResult.SSLError;
} }
catch (System.IO.IOException e) catch (System.IO.IOException e)
{ {
if (e.Message.Contains("authentication")) if (e.Message.Contains("authentication"))
{ {
return LoginResult.SSLError; return LoginResult.SSLError;
} }
else return LoginResult.OtherError; else return LoginResult.OtherError;
} }
catch catch
{ {
return LoginResult.OtherError; return LoginResult.OtherError;
} }
} }
/// <summary> /// <summary>
/// Validates whether accessToken must be refreshed /// Validates whether accessToken must be refreshed
/// </summary> /// </summary>
/// <param name="accesstoken">Will contain the cached access token previously returned by Minecraft.net</param> /// <param name="accesstoken">Will contain the cached access token previously returned by Minecraft.net</param>
/// <param name="clienttoken">Will contain the cached client token created on login</param> /// <param name="clienttoken">Will contain the cached client token created on login</param>
/// <returns>Returns the status of the token (Valid, Invalid, etc.)</returns> /// <returns>Returns the status of the token (Valid, Invalid, etc.)</returns>
/// ///
public static LoginResult GetTokenValidation(SessionToken session) public static LoginResult GetTokenValidation(SessionToken session)
{ {
try try
{ {
string result = ""; string result = "";
string json_request = "{\"accessToken\": \"" + jsonEncode(session.ID) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }"; string json_request = "{\"accessToken\": \"" + jsonEncode(session.ID) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }";
int code = doHTTPSPost("authserver.mojang.com", "/validate", json_request, ref result); int code = doHTTPSPost("authserver.mojang.com", "/validate", json_request, ref result);
if (code == 204) if (code == 204)
{ {
return LoginResult.Success; return LoginResult.Success;
} }
else if (code == 403) else if (code == 403)
{ {
return LoginResult.LoginRequired; return LoginResult.LoginRequired;
} }
else else
{ {
return LoginResult.OtherError; return LoginResult.OtherError;
}
}
catch
{
return LoginResult.OtherError;
}
}
/// <summary>
/// Refreshes invalid token
/// </summary>
/// <param name="user">Login</param>
/// <param name="accesstoken">Will contain the new access token returned by Minecraft.net, if the refresh is successful</param>
/// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param>
/// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param>
/// <returns>Returns the status of the new token request (Success, Failure, etc.)</returns>
///
public static LoginResult GetNewToken(SessionToken currentsession, out SessionToken newsession)
{
newsession = new SessionToken();
try
{
string result = "";
string json_request = "{ \"accessToken\": \"" + jsonEncode(currentsession.ID) + "\", \"clientToken\": \"" + jsonEncode(currentsession.ClientID) + "\", \"selectedProfile\": { \"id\": \"" + jsonEncode(currentsession.PlayerID) + "\", \"name\": \"" + jsonEncode(currentsession.PlayerName) + "\" } }";
int code = doHTTPSPost("authserver.mojang.com", "/refresh", json_request, ref result);
if (code == 200)
{
if (result == null)
{
return LoginResult.NullError;
}
else {
string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { newsession.ID = temp[1].Split('"')[0]; }
temp = result.Split(new string[] { "clientToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { newsession.ClientID = temp[1].Split('"')[0]; }
temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { newsession.PlayerName = temp[1].Split('"')[0]; }
temp = result.Split(new string[] { "selectedProfile\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { newsession.PlayerID = temp[1].Split('"')[0]; }
return LoginResult.Success;
}
}
else if (code == 403 && result.Contains("InvalidToken"))
{
return LoginResult.InvalidToken;
}
else
{
ConsoleIO.WriteLineFormatted("§8Got error code from server while refreshing authentication: " + code);
return LoginResult.OtherError;
} }
} }
catch catch
{ {
return LoginResult.OtherError; return LoginResult.OtherError;
} }
} }
/// <summary> /// <summary>
/// Check session using Mojang's Yggdrasil authentication scheme. Allows to join an online-mode server /// Refreshes invalid token
/// </summary> /// </summary>
/// <param name="user">Username</param> /// <param name="user">Login</param>
/// <param name="accesstoken">Session ID</param> /// <param name="accesstoken">Will contain the new access token returned by Minecraft.net, if the refresh is successful</param>
/// <param name="serverhash">Server ID</param> /// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param>
/// <returns>TRUE if session was successfully checked</returns> /// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param>
/// <returns>Returns the status of the new token request (Success, Failure, etc.)</returns>
public static bool SessionCheck(string uuid, string accesstoken, string serverhash) ///
{ public static LoginResult GetNewToken(SessionToken currentsession, out SessionToken newsession)
try {
{ newsession = new SessionToken();
string result = ""; try
string json_request = "{\"accessToken\":\"" + accesstoken + "\",\"selectedProfile\":\"" + uuid + "\",\"serverId\":\"" + serverhash + "\"}"; {
int code = doHTTPSPost("sessionserver.mojang.com", "/session/minecraft/join", json_request, ref result); string result = "";
return (result == ""); string json_request = "{ \"accessToken\": \"" + jsonEncode(currentsession.ID) + "\", \"clientToken\": \"" + jsonEncode(currentsession.ClientID) + "\", \"selectedProfile\": { \"id\": \"" + jsonEncode(currentsession.PlayerID) + "\", \"name\": \"" + jsonEncode(currentsession.PlayerName) + "\" } }";
} int code = doHTTPSPost("authserver.mojang.com", "/refresh", json_request, ref result);
catch { return false; } if (code == 200)
} {
if (result == null)
public static void RealmsListWorlds(string username, string uuid, string accesstoken) {
{ return LoginResult.NullError;
string result = ""; }
string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, Program.MCHighestVersion); else {
doHTTPSGet("mcoapi.minecraft.net", "/worlds", cookies, ref result); string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine(result); if (temp.Length >= 2) { newsession.ID = temp[1].Split('"')[0]; }
} temp = result.Split(new string[] { "clientToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
if (temp.Length >= 2) { newsession.ClientID = temp[1].Split('"')[0]; }
/// <summary> temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries);
/// Make a HTTPS GET request to the specified endpoint of the Mojang API if (temp.Length >= 2) { newsession.PlayerName = temp[1].Split('"')[0]; }
/// </summary> temp = result.Split(new string[] { "selectedProfile\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries);
/// <param name="host">Host to connect to</param> if (temp.Length >= 2) { newsession.PlayerID = temp[1].Split('"')[0]; }
/// <param name="endpoint">Endpoint for making the request</param> return LoginResult.Success;
/// <param name="cookies">Cookies for making the request</param> }
/// <param name="result">Request result</param> }
/// <returns>HTTP Status code</returns> else if (code == 403 && result.Contains("InvalidToken"))
{
private static int doHTTPSGet(string host, string endpoint, string cookies, ref string result) return LoginResult.InvalidToken;
{ }
List<String> http_request = new List<string>(); else
http_request.Add("GET " + endpoint + " HTTP/1.1"); {
http_request.Add("Cookie: " + cookies); ConsoleIO.WriteLineFormatted("§8Got error code from server while refreshing authentication: " + code);
http_request.Add("Cache-Control: no-cache"); return LoginResult.OtherError;
http_request.Add("Pragma: no-cache"); }
http_request.Add("Host: " + host); }
http_request.Add("User-Agent: Java/1.6.0_27"); catch
http_request.Add("Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7"); {
http_request.Add("Connection: close"); return LoginResult.OtherError;
http_request.Add(""); }
return doHTTPSRequest(http_request, host, ref result); }
}
/// <summary>
/// <summary> /// Check session using Mojang's Yggdrasil authentication scheme. Allows to join an online-mode server
/// Make a HTTPS POST request to the specified endpoint of the Mojang API /// </summary>
/// </summary> /// <param name="user">Username</param>
/// <param name="host">Host to connect to</param> /// <param name="accesstoken">Session ID</param>
/// <param name="endpoint">Endpoint for making the request</param> /// <param name="serverhash">Server ID</param>
/// <param name="request">Request payload</param> /// <returns>TRUE if session was successfully checked</returns>
/// <param name="result">Request result</param>
/// <returns>HTTP Status code</returns> public static bool SessionCheck(string uuid, string accesstoken, string serverhash)
{
private static int doHTTPSPost(string host, string endpoint, string request, ref string result) try
{ {
List<String> http_request = new List<string>(); string result = "";
http_request.Add("POST " + endpoint + " HTTP/1.1"); string json_request = "{\"accessToken\":\"" + accesstoken + "\",\"selectedProfile\":\"" + uuid + "\",\"serverId\":\"" + serverhash + "\"}";
http_request.Add("Host: " + host); int code = doHTTPSPost("sessionserver.mojang.com", "/session/minecraft/join", json_request, ref result);
http_request.Add("User-Agent: MCC/" + Program.Version); return (result == "");
http_request.Add("Content-Type: application/json"); }
http_request.Add("Content-Length: " + Encoding.ASCII.GetBytes(request).Length); catch { return false; }
http_request.Add("Connection: close"); }
http_request.Add("");
http_request.Add(request); public static void RealmsListWorlds(string username, string uuid, string accesstoken)
return doHTTPSRequest(http_request, host, ref result); {
} string result = "";
string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, Program.MCHighestVersion);
/// <summary> doHTTPSGet("mcoapi.minecraft.net", "/worlds", cookies, ref result);
/// Manual HTTPS request since we must directly use a TcpClient because of the proxy. Console.WriteLine(result);
/// This method connects to the server, enables SSL, do the request and read the response. }
/// </summary>
/// <param name="headers">Request headers and optional body (POST)</param> /// <summary>
/// <param name="host">Host to connect to</param> /// Make a HTTPS GET request to the specified endpoint of the Mojang API
/// <param name="result">Request result</param> /// </summary>
/// <returns>HTTP Status code</returns> /// <param name="host">Host to connect to</param>
/// <param name="endpoint">Endpoint for making the request</param>
private static int doHTTPSRequest(List<string> headers, string host, ref string result) /// <param name="cookies">Cookies for making the request</param>
{ /// <param name="result">Request result</param>
string postResult = null; /// <returns>HTTP Status code</returns>
int statusCode = 520;
AutoTimeout.Perform(() => private static int doHTTPSGet(string host, string endpoint, string cookies, ref string result)
{ {
TcpClient client = ProxyHandler.newTcpClient(host, 443, true); List<String> http_request = new List<string>();
SslStream stream = new SslStream(client.GetStream()); http_request.Add("GET " + endpoint + " HTTP/1.1");
stream.AuthenticateAsClient(host); http_request.Add("Cookie: " + cookies);
stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", headers.ToArray()))); http_request.Add("Cache-Control: no-cache");
System.IO.StreamReader sr = new System.IO.StreamReader(stream); http_request.Add("Pragma: no-cache");
string raw_result = sr.ReadToEnd(); http_request.Add("Host: " + host);
if (raw_result.StartsWith("HTTP/1.1")) http_request.Add("User-Agent: Java/1.6.0_27");
{ http_request.Add("Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7");
postResult = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4); http_request.Add("Connection: close");
statusCode = Settings.str2int(raw_result.Split(' ')[1]); http_request.Add("");
} return doHTTPSRequest(http_request, host, ref result);
else statusCode = 520; //Web server is returning an unknown error }
}, TimeSpan.FromSeconds(30));
result = postResult; /// <summary>
return statusCode; /// Make a HTTPS POST request to the specified endpoint of the Mojang API
} /// </summary>
/// <param name="host">Host to connect to</param>
/// <summary> /// <param name="endpoint">Endpoint for making the request</param>
/// Encode a string to a json string. /// <param name="request">Request payload</param>
/// Will convert special chars to \u0000 unicode escape sequences. /// <param name="result">Request result</param>
/// </summary> /// <returns>HTTP Status code</returns>
/// <param name="text">Source text</param>
/// <returns>Encoded text</returns> private static int doHTTPSPost(string host, string endpoint, string request, ref string result)
{
private static string jsonEncode(string text) List<String> http_request = new List<string>();
{ http_request.Add("POST " + endpoint + " HTTP/1.1");
StringBuilder result = new StringBuilder(); http_request.Add("Host: " + host);
foreach (char c in text) http_request.Add("User-Agent: MCC/" + Program.Version);
{ http_request.Add("Content-Type: application/json");
if ((c >= '0' && c <= '9') || http_request.Add("Content-Length: " + Encoding.ASCII.GetBytes(request).Length);
(c >= 'a' && c <= 'z') || http_request.Add("Connection: close");
(c >= 'A' && c <= 'Z')) http_request.Add("");
{ http_request.Add(request);
result.Append(c); return doHTTPSRequest(http_request, host, ref result);
} }
else
{ /// <summary>
result.AppendFormat(@"\u{0:x4}", (int)c); /// 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="headers">Request headers and optional body (POST)</param>
return result.ToString(); /// <param name="host">Host to connect to</param>
} /// <param name="result">Request result</param>
} /// <returns>HTTP Status code</returns>
}
private static int doHTTPSRequest(List<string> headers, string host, ref string result)
{
string postResult = null;
int statusCode = 520;
AutoTimeout.Perform(() =>
{
TcpClient client = ProxyHandler.newTcpClient(host, 443, true);
SslStream stream = new SslStream(client.GetStream());
stream.AuthenticateAsClient(host);
stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", headers.ToArray())));
System.IO.StreamReader sr = new System.IO.StreamReader(stream);
string raw_result = sr.ReadToEnd();
if (raw_result.StartsWith("HTTP/1.1"))
{
postResult = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4);
statusCode = Settings.str2int(raw_result.Split(' ')[1]);
}
else statusCode = 520; //Web server is returning an unknown error
}, TimeSpan.FromSeconds(30));
result = postResult;
return statusCode;
}
/// <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 ((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z'))
{
result.Append(c);
}
else
{
result.AppendFormat(@"\u{0:x4}", (int)c);
}
}
return result.ToString();
}
}
}

View file

@ -1,14 +1,21 @@
using System; using System;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol
{ {
[Serializable] [Serializable]
public class SessionToken public class SessionToken
{ {
public string ID { get; set; } = string.Empty; public string ID { get; set; }
public string PlayerName { get; set; } = string.Empty; public string PlayerName { get; set; }
public string PlayerID { get; set; } = string.Empty; public string PlayerID { get; set; }
public string ClientID { get; set; } = string.Empty; public string ClientID { get; set; }
public SessionToken()
{
ID = String.Empty;
PlayerName = String.Empty;
PlayerID = String.Empty;
ClientID = String.Empty;
}
} }
} }