Merge into master

This commit is contained in:
BruceChen 2022-12-06 20:32:46 +08:00
commit 892999ac98
155 changed files with 10911 additions and 9860 deletions

View file

@ -1,4 +1,4 @@
namespace MinecraftClient.Protocol
namespace MinecraftClient.Protocol
{
public enum EntityActionType
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{
@ -173,4 +173,4 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
return typeOut;
}
}
}
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{
@ -174,4 +174,4 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
return typeOut;
}
}
}
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{
@ -177,4 +177,4 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
return typeOut;
}
}
}
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@ -42,19 +42,19 @@ namespace MinecraftClient.Protocol.Handlers
if (Handler.GetTerrainEnabled())
{
ConsoleIO.WriteLineFormatted(Translations.extra_terrainandmovement_disabled, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§c" + Translations.extra_terrainandmovement_disabled, acceptnewlines: true);
Handler.SetTerrainEnabled(false);
}
if (handler.GetInventoryEnabled())
{
ConsoleIO.WriteLineFormatted(Translations.extra_inventory_disabled, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§c" + Translations.extra_inventory_disabled, acceptnewlines: true);
handler.SetInventoryEnabled(false);
}
if (handler.GetEntityHandlingEnabled())
{
ConsoleIO.WriteLineFormatted(Translations.extra_entity_disabled, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§c" + Translations.extra_entity_disabled, acceptnewlines: true);
handler.SetEntityHandlingEnabled(false);
}
}
@ -500,15 +500,15 @@ namespace MinecraftClient.Protocol.Handlers
byte[] token = ReadNextByteArray();
if (serverID == "-")
ConsoleIO.WriteLineFormatted(Translations.mcc_server_offline, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.mcc_server_offline, acceptnewlines: true);
else if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_handshake, serverID));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_handshake, serverID));
return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey, session);
}
else
{
ConsoleIO.WriteLineFormatted(Translations.error_invalid_response, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.error_invalid_response, acceptnewlines: true);
return false;
}
}
@ -519,7 +519,7 @@ namespace MinecraftClient.Protocol.Handlers
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.debug_crypto, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.debug_crypto, acceptnewlines: true);
if (serverIDhash != "-")
{
@ -584,7 +584,7 @@ namespace MinecraftClient.Protocol.Handlers
}
else
{
ConsoleIO.WriteLineFormatted(Translations.error_invalid_encrypt, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.error_invalid_encrypt, acceptnewlines: true);
return false;
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
@ -106,18 +106,18 @@ namespace MinecraftClient.Protocol.Handlers
if (handler.GetTerrainEnabled() && protocolVersion > MC_1_19_2_Version)
{
log.Error(Translations.extra_terrainandmovement_disabled);
log.Error("§c" + Translations.extra_terrainandmovement_disabled);
handler.SetTerrainEnabled(false);
}
if (handler.GetInventoryEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_2_Version))
{
log.Error(Translations.extra_inventory_disabled);
log.Error("§c" + Translations.extra_inventory_disabled);
handler.SetInventoryEnabled(false);
}
if (handler.GetEntityHandlingEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_2_Version))
{
log.Error(Translations.extra_entity_disabled);
log.Error("§c" + Translations.extra_entity_disabled);
handler.SetEntityHandlingEnabled(false);
}
@ -1955,12 +1955,12 @@ namespace MinecraftClient.Protocol.Handlers
}
else if (packetID == 0x02) //Login successful
{
log.Info(Translations.mcc_server_offline);
log.Info("§8" + Translations.mcc_server_offline);
login_phase = false;
if (!pForge.CompleteForgeHandshake())
{
log.Error(Translations.error_forge);
log.Error("§8" + Translations.error_forge);
return false;
}
@ -1980,7 +1980,7 @@ namespace MinecraftClient.Protocol.Handlers
RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!;
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
log.Debug(Translations.debug_crypto);
log.Debug("§8" + Translations.debug_crypto);
if (serverIDhash != "-")
{
@ -2049,7 +2049,7 @@ namespace MinecraftClient.Protocol.Handlers
(int packetID, Queue<byte> packetData) = ReadNextPacket();
if (packetID < 0 || loopPrevention-- < 0) // Failed to read packet or too many iterations (issue #1150)
{
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, Translations.error_invalid_encrypt);
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "§8" + Translations.error_invalid_encrypt);
return false;
}
else if (packetID == 0x00) //Login rejected
@ -2085,7 +2085,7 @@ namespace MinecraftClient.Protocol.Handlers
if (!pForge.CompleteForgeHandshake())
{
log.Error(Translations.error_forge_encrypt);
log.Error("§8" + Translations.error_forge_encrypt);
return false;
}
@ -2207,7 +2207,7 @@ namespace MinecraftClient.Protocol.Handlers
// Check for forge on the server.
Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo);
ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_server_protocol, version, protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : "")));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version, protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : "")));
return true;
}

View file

@ -133,7 +133,7 @@ namespace MinecraftClient.Protocol.Handlers
byte fmlProtocolVersion = dataTypes.ReadNextByte(packetData);
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_version, fmlProtocolVersion));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_version, fmlProtocolVersion));
if (fmlProtocolVersion >= 1)
currentDimension = dataTypes.ReadNextInt(packetData);
@ -143,7 +143,7 @@ namespace MinecraftClient.Protocol.Handlers
// Then tell the server that we're running the same mods.
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.forge_send_mod, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_send_mod, acceptnewlines: true);
byte[][] mods = new byte[forgeInfo.Mods.Count][];
for (int i = 0; i < forgeInfo.Mods.Count; i++)
{
@ -163,7 +163,7 @@ namespace MinecraftClient.Protocol.Handlers
Thread.Sleep(2000);
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.forge_accept, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_accept, acceptnewlines: true);
// Tell the server that yes, we are OK with the mods it has
// even though we don't actually care what mods it has.
@ -185,7 +185,7 @@ namespace MinecraftClient.Protocol.Handlers
int registrySize = dataTypes.ReadNextVarInt(packetData);
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_registry, registrySize));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_registry, registrySize));
fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
}
@ -197,7 +197,7 @@ namespace MinecraftClient.Protocol.Handlers
string registryName = dataTypes.ReadNextString(packetData);
int registrySize = dataTypes.ReadNextVarInt(packetData);
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_registry_2, registryName, registrySize));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_registry_2, registryName, registrySize));
if (!hasNextRegistry)
fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
}
@ -209,7 +209,7 @@ namespace MinecraftClient.Protocol.Handlers
if (discriminator != FMLHandshakeDiscriminator.HandshakeAck)
return false;
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.forge_accept_registry, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_accept_registry, acceptnewlines: true);
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE });
fmlHandshakeState = FMLHandshakeClientState.COMPLETE;
@ -303,7 +303,7 @@ namespace MinecraftClient.Protocol.Handlers
// [1]: Version is usually set to "FML2" for FML stuff and "1" for mods
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.forge_fml2_mod, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_fml2_mod, acceptnewlines: true);
List<string> mods = new();
int modCount = dataTypes.ReadNextVarInt(packetData);
@ -335,7 +335,7 @@ namespace MinecraftClient.Protocol.Handlers
// In MCC, we just want to send a valid response so we'll reply back with data collected from the server.
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.forge_fml2_mod_send, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_fml2_mod_send, acceptnewlines: true);
// Packet ID 2: Client to Server Mod List
fmlResponsePacket.AddRange(dataTypes.GetVarInt(2));
@ -373,7 +373,7 @@ namespace MinecraftClient.Protocol.Handlers
if (Settings.Config.Logging.DebugMessages)
{
string registryName = dataTypes.ReadNextString(packetData);
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_fml2_registry, registryName));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_registry, registryName));
}
fmlResponsePacket.AddRange(dataTypes.GetVarInt(99));
@ -392,7 +392,7 @@ namespace MinecraftClient.Protocol.Handlers
if (Settings.Config.Logging.DebugMessages)
{
string configName = dataTypes.ReadNextString(packetData);
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_fml2_config, configName));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_config, configName));
}
fmlResponsePacket.AddRange(dataTypes.GetVarInt(99));
@ -401,7 +401,7 @@ namespace MinecraftClient.Protocol.Handlers
default:
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_fml2_unknown, packetID));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_unknown, packetID));
break;
}
@ -417,7 +417,7 @@ namespace MinecraftClient.Protocol.Handlers
}
else if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_fml2_unknown_channel, fmlChannel));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_unknown_channel, fmlChannel));
}
}
return false;
@ -506,10 +506,10 @@ namespace MinecraftClient.Protocol.Handlers
forgeInfo = new ForgeInfo(modData, fmlVersion);
if (forgeInfo.Mods.Any())
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.forge_with_mod, forgeInfo.Mods.Count));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_with_mod, forgeInfo.Mods.Count));
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted(Translations.forge_mod_list, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_mod_list, acceptnewlines: true);
foreach (ForgeInfo.ForgeMod mod in forgeInfo.Mods)
ConsoleIO.WriteLineFormatted("§8 " + mod.ToString());
}
@ -517,7 +517,7 @@ namespace MinecraftClient.Protocol.Handlers
}
else
{
ConsoleIO.WriteLineFormatted(Translations.forge_no_mod, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_no_mod, acceptnewlines: true);
forgeInfo = null;
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory;
using MinecraftClient.Logger;

View file

@ -218,7 +218,7 @@ namespace MinecraftClient.Protocol.Message
//File not found? Try downloading language file from Mojang's servers?
if (!File.Exists(Language_File))
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_download, Config.Main.Advanced.Language));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_download, Config.Main.Advanced.Language));
HttpClient httpClient = new();
try
{
@ -244,11 +244,11 @@ namespace MinecraftClient.Protocol.Message
stringBuilder.Append(entry.Key).Append('=').Append(entry.Value.StringValue.Replace("\n", "\\n").Replace("\r", string.Empty)).Append(Environment.NewLine);
File.WriteAllText(Language_File, stringBuilder.ToString());
ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_done, Language_File));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_done, Language_File));
}
catch
{
ConsoleIO.WriteLineFormatted(Translations.chat_fail, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.chat_fail, acceptnewlines: true);
}
httpClient.Dispose();
}
@ -281,7 +281,7 @@ namespace MinecraftClient.Protocol.Message
}
else //No external dictionnary found.
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_not_found, Language_File));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_not_found, Language_File));
}
}

View file

@ -163,7 +163,7 @@ namespace MinecraftClient.Protocol.ProfileKey
}
catch (IOException e)
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_read_fail_plain_keys, e.Message));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_read_fail_plain_keys, e.Message));
}
}
@ -176,7 +176,7 @@ namespace MinecraftClient.Protocol.ProfileKey
private static void SaveToDisk()
{
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.cache_saving_keys, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.cache_saving_keys, acceptnewlines: true);
List<string> KeysCacheLines = new()
{
@ -192,7 +192,7 @@ namespace MinecraftClient.Protocol.ProfileKey
}
catch (IOException e)
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_save_fail_keys, e.Message));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_save_fail_keys, e.Message));
}
}
}

View file

@ -56,7 +56,7 @@ namespace MinecraftClient.Protocol
.ThenBy(record => Guid.NewGuid())
.First();
string target = result.Target.Value.Trim('.');
ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_found, target, result.Port, domainVal));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_found, target, result.Port, domainVal));
domainVal = target;
portVal = result.Port;
foundService = true;
@ -64,7 +64,7 @@ namespace MinecraftClient.Protocol
}
catch (Exception e)
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_not_found, domainVal, e.GetType().FullName, e.Message));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_not_found, domainVal, e.GetType().FullName, e.Message));
}
}, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30));
}
@ -96,7 +96,7 @@ namespace MinecraftClient.Protocol
success = true;
}
else
ConsoleIO.WriteLineFormatted(Translations.error_unexpect_response, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.error_unexpect_response, acceptnewlines: true);
}
catch (Exception e)
{
@ -105,9 +105,9 @@ namespace MinecraftClient.Protocol
}, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30)))
{
if (protocolversion != 0 && protocolversion != protocolversionTmp)
ConsoleIO.WriteLineFormatted(Translations.error_version_different, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.error_version_different, acceptnewlines: true);
if (protocolversion == 0 && protocolversionTmp <= 1)
ConsoleIO.WriteLineFormatted(Translations.error_no_version_report, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.error_no_version_report, acceptnewlines: true);
if (protocolversion == 0)
protocolversion = protocolversionTmp;
forgeInfo = forgeInfoTmp;
@ -115,7 +115,7 @@ namespace MinecraftClient.Protocol
}
else
{
ConsoleIO.WriteLineFormatted(Translations.error_connection_timeout, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.error_connection_timeout, acceptnewlines: true);
return false;
}
}
@ -491,7 +491,7 @@ namespace MinecraftClient.Protocol
}
else
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.error_http_code, code));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.error_http_code, code));
return LoginResult.OtherError;
}
}
@ -694,7 +694,7 @@ namespace MinecraftClient.Protocol
}
else
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.error_auth, code));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.error_auth, code));
return LoginResult.OtherError;
}
}
@ -895,7 +895,7 @@ namespace MinecraftClient.Protocol
try
{
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.debug_request, host));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.debug_request, host));
TcpClient client = ProxyHandler.NewTcpClient(host, 443, true);
SslStream stream = new(client.GetStream());

View file

@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Globalization;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using MinecraftClient.Proxy;
namespace MinecraftClient.Protocol
@ -16,12 +18,21 @@ namespace MinecraftClient.Protocol
/// </summary>
public class ProxiedWebRequest
{
private readonly string httpVersion = "HTTP/1.0"; // Use 1.0 here because 1.1 server may send chunked data
public interface ITcpFactory
{
TcpClient CreateTcpClient(string host, int port);
};
private readonly string httpVersion = "HTTP/1.1";
private ITcpFactory? tcpFactory;
private bool isProxied = false; // Send absolute Url in request if true
private readonly Uri uri;
private string Host { get { return uri.Host; } }
private int Port { get { return uri.Port; } }
private string Path { get { return uri.PathAndQuery; } }
private string AbsoluteUrl { get { return uri.AbsoluteUri; } }
private bool IsSecure { get { return uri.Scheme == "https"; } }
public NameValueCollection Headers = new();
@ -30,6 +41,12 @@ namespace MinecraftClient.Protocol
public string Accept { get { return Headers.Get("Accept") ?? String.Empty; } set { Headers.Set("Accept", value); } }
public string Cookie { set { Headers.Set("Cookie", value); } }
/// <summary>
/// Set to true to tell the http client proxy is enabled
/// </summary>
public bool IsProxy { get { return isProxied; } set { isProxied = value; } }
public bool Debug { get { return Settings.Config.Logging.DebugMessages; } }
/// <summary>
/// Create a new http request
/// </summary>
@ -52,13 +69,34 @@ namespace MinecraftClient.Protocol
SetupBasicHeaders();
}
/// <summary>
/// Create a new http request with custom tcp client
/// </summary>
/// <param name="tcpFactory">Tcp factory to be used</param>
/// <param name="url">Target URL</param>
public ProxiedWebRequest(ITcpFactory tcpFactory, string url) : this(url)
{
this.tcpFactory = tcpFactory;
}
/// <summary>
/// Create a new http request with custom tcp client and cookies
/// </summary>
/// <param name="tcpFactory">Tcp factory to be used</param>
/// <param name="url">Target URL</param>
/// <param name="cookies">Cookies to use</param>
public ProxiedWebRequest(ITcpFactory tcpFactory, string url, NameValueCollection cookies) : this(url, cookies)
{
this.tcpFactory = tcpFactory;
}
/// <summary>
/// Setup some basic headers
/// </summary>
private void SetupBasicHeaders()
{
Headers.Add("Host", Host);
Headers.Add("User-Agent", "MCC/" + Program.Version);
Headers.Add("User-Agent", "MCC/1.0");
Headers.Add("Accept", "*/*");
Headers.Add("Connection", "close");
}
@ -96,7 +134,7 @@ namespace MinecraftClient.Protocol
{
List<string> requestMessage = new()
{
string.Format("{0} {1} {2}", method.ToUpper(), Path, httpVersion) // Request line
string.Format("{0} {1} {2}", method.ToUpper(), isProxied ? AbsoluteUrl : Path, httpVersion) // Request line
};
foreach (string key in Headers) // Headers
{
@ -109,7 +147,7 @@ namespace MinecraftClient.Protocol
requestMessage.Add(body);
}
else requestMessage.Add(""); // <CR><LF>
if (Settings.Config.Logging.DebugMessages)
if (Debug)
{
foreach (string l in requestMessage)
{
@ -117,84 +155,240 @@ namespace MinecraftClient.Protocol
}
}
Response response = Response.Empty();
AutoTimeout.Perform(() =>
// FIXME: Use TcpFactory interface to avoid direct usage of the ProxyHandler class
// TcpClient client = tcpFactory.CreateTcpClient(Host, Port);
TcpClient client = ProxyHandler.NewTcpClient(Host, Port, true);
Stream stream;
if (IsSecure)
{
TcpClient client = ProxyHandler.NewTcpClient(Host, Port, true);
Stream stream;
if (IsSecure)
{
stream = new SslStream(client.GetStream());
((SslStream)stream).AuthenticateAsClient(Host, null, SslProtocols.Tls12, true); // Enable TLS 1.2. Hotfix for #1774
}
else
{
stream = client.GetStream();
}
string h = string.Join("\r\n", requestMessage.ToArray());
byte[] data = Encoding.ASCII.GetBytes(h);
stream.Write(data, 0, data.Length);
stream.Flush();
StreamReader sr = new(stream);
string rawResult = sr.ReadToEnd();
response = ParseResponse(rawResult);
try
{
sr.Close();
stream.Close();
client.Close();
}
catch { }
},
TimeSpan.FromSeconds(30));
stream = new SslStream(client.GetStream());
((SslStream)stream).AuthenticateAsClient(Host, null, SslProtocols.Tls12, true); // Enable TLS 1.2. Hotfix for #1774
}
else
{
stream = client.GetStream();
}
string h = string.Join("\r\n", requestMessage.ToArray());
byte[] data = Encoding.ASCII.GetBytes(h);
stream.Write(data, 0, data.Length);
stream.Flush();
// Read response
int statusCode = ReadHttpStatus(stream);
var headers = ReadHeader(stream);
string? rbody;
if (headers.Get("transfer-encoding") == "chunked")
{
rbody = ReadBodyChunked(stream);
}
else
{
rbody = ReadBody(stream, int.Parse(headers.Get("content-length") ?? "0"));
}
if (headers.Get("set-cookie") != null)
{
response.Cookies = ParseSetCookie(headers.GetValues("set-cookie") ?? Array.Empty<string>());
}
response.Body = rbody ?? "";
response.StatusCode = statusCode;
response.Headers = headers;
try
{
stream.Close();
client.Close();
}
catch { }
return response;
}
/// <summary>
/// Parse a raw response string to response object
/// Read HTTP response line from a Stream
/// </summary>
/// <param name="raw">raw response string</param>
/// <param name="s">Stream to read</param>
/// <returns></returns>
private Response ParseResponse(string raw)
/// <exception cref="InvalidDataException">If server return unknown data</exception>
private static int ReadHttpStatus(Stream s)
{
int statusCode;
string responseBody = "";
NameValueCollection headers = new();
NameValueCollection cookies = new();
if (raw.StartsWith("HTTP/1.1") || raw.StartsWith("HTTP/1.0"))
var httpHeader = ReadLine(s); // http header line
if (httpHeader.StartsWith("HTTP/1.1") || httpHeader.StartsWith("HTTP/1.0"))
{
Queue<string> msg = new(raw.Split(new string[] { "\r\n" }, StringSplitOptions.None));
statusCode = int.Parse(msg.Dequeue().Split(' ')[1], NumberStyles.Any, CultureInfo.CurrentCulture);
while (msg.Peek() != "")
{
string[] header = msg.Dequeue().Split(new char[] { ':' }, 2); // Split first ':' only
string key = header[0].ToLower(); // Key is case-insensitive
string value = header[1];
if (key == "set-cookie")
{
string[] cookie = value.Split(';'); // cookie options are ignored
string[] tmp = cookie[0].Split(new char[] { '=' }, 2); // Split first '=' only
string cname = tmp[0].Trim();
string cvalue = tmp[1].Trim();
cookies.Add(cname, cvalue);
}
else
{
headers.Add(key, value.Trim());
}
}
msg.Dequeue();
if (msg.Count > 0)
responseBody = msg.Dequeue();
return new Response(statusCode, responseBody, headers, cookies);
return int.Parse(httpHeader.Split(' ')[1], NumberStyles.Any, CultureInfo.CurrentCulture);
}
else
{
return new Response(520 /* Web Server Returned an Unknown Error */, "", headers, cookies);
throw new InvalidDataException("Unexpect data from server");
}
}
/// <summary>
/// Read HTTP headers from a Stream
/// </summary>
/// <param name="s">Stream to read</param>
/// <returns>Headers in lower-case</returns>
private static NameValueCollection ReadHeader(Stream s)
{
var headers = new NameValueCollection();
// Read headers
string header;
do
{
header = ReadLine(s);
if (!String.IsNullOrEmpty(header))
{
var tmp = header.Split(new char[] { ':' }, 2);
var name = tmp[0].ToLower();
var value = tmp[1].Trim();
headers.Add(name, value);
}
}
while (!String.IsNullOrEmpty(header));
return headers;
}
/// <summary>
/// Read HTTP body from a Stream
/// </summary>
/// <param name="s">Stream to read</param>
/// <param name="length">Length of the body (the Content-Length header)</param>
/// <returns>Body or null if length is zero</returns>
private static string? ReadBody(Stream s, int length)
{
if (length > 0)
{
byte[] buffer = new byte[length];
int r = 0;
while (r < length)
{
var read = s.Read(buffer, r, length - r);
r += read;
Thread.Sleep(50);
}
return Encoding.UTF8.GetString(buffer);
}
else
{
return null;
}
}
/// <summary>
/// Read HTTP chunked body from a Stream
/// </summary>
/// <param name="s">Stream to read</param>
/// <returns>Body or empty string if nothing is received</returns>
private static string ReadBodyChunked(Stream s)
{
List<byte> buffer1 = new();
while (true)
{
string l = ReadLine(s);
int size = Int32.Parse(l, NumberStyles.HexNumber);
if (size == 0)
break;
byte[] buffer2 = new byte[size];
int r = 0;
while (r < size)
{
var read = s.Read(buffer2, r, size - r);
r += read;
Thread.Sleep(50);
}
ReadLine(s);
buffer1.AddRange(buffer2);
}
return Encoding.UTF8.GetString(buffer1.ToArray());
}
/// <summary>
/// Parse the Set-Cookie header value into NameValueCollection. Cookie options are ignored
/// </summary>
/// <param name="headerValue">Array of value strings</param>
/// <returns>Parsed cookies</returns>
private static NameValueCollection ParseSetCookie(IEnumerable<string> headerValue)
{
NameValueCollection cookies = new();
foreach (var value in headerValue)
{
string[] cookie = value.Split(';'); // cookie options are ignored
string[] tmp = cookie[0].Split(new char[] { '=' }, 2); // Split first '=' only
string[] options = cookie[1..];
string cname = tmp[0].Trim();
string cvalue = tmp[1].Trim();
// Check expire
bool isExpired = false;
foreach (var option in options)
{
var tmp2 = option.Trim().Split(new char[] { '=' }, 2);
// Check for Expires=<date> and Max-Age=<number>
if (tmp2.Length == 2)
{
var optName = tmp2[0].Trim().ToLower();
var optValue = tmp2[1].Trim();
switch (optName)
{
case "expires":
{
if (DateTime.TryParse(optValue, out var expDate))
{
if (expDate < DateTime.Now)
isExpired = true;
}
break;
}
case "max-age":
{
if (int.TryParse(optValue, out var expInt))
{
if (expInt <= 0)
isExpired = true;
}
break;
}
}
}
if (isExpired)
break;
}
if (!isExpired)
cookies.Add(cname, cvalue);
}
return cookies;
}
/// <summary>
/// Read a line from a Stream
/// </summary>
/// <remarks>
/// Line break by \r\n and they are not included in returned string
/// </remarks>
/// <param name="s">Stream to read</param>
/// <returns>String</returns>
private static string ReadLine(Stream s)
{
List<byte> buffer = new();
byte c;
while (true)
{
int b = s.ReadByte();
if (b == -1)
break;
c = (byte)b;
if (c == '\n')
{
if (buffer.Last() == '\r')
{
buffer.RemoveAt(buffer.Count - 1);
break;
}
}
buffer.Add(c);
}
return Encoding.UTF8.GetString(buffer.ToArray());
}
/// <summary>
/// Get the cookie string representation to use in header
/// </summary>
@ -271,4 +465,4 @@ namespace MinecraftClient.Protocol
}
}
}
}
}

View file

@ -190,7 +190,7 @@ namespace MinecraftClient.Protocol.Session
}
catch (IOException ex)
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_read_fail, ex.Message));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_read_fail, ex.Message));
}
catch (SerializationException ex2)
{
@ -236,7 +236,7 @@ namespace MinecraftClient.Protocol.Session
}
catch (IOException e)
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_read_fail_plain, e.Message));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_read_fail_plain, e.Message));
}
}
@ -249,7 +249,7 @@ namespace MinecraftClient.Protocol.Session
private static void SaveToDisk()
{
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.cache_saving, acceptnewlines: true);
ConsoleIO.WriteLineFormatted("§8" + Translations.cache_saving, acceptnewlines: true);
List<string> sessionCacheLines = new()
{
@ -265,7 +265,7 @@ namespace MinecraftClient.Protocol.Session
}
catch (IOException e)
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_save_fail, e.Message));
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_save_fail, e.Message));
}
}
}