492 lines
10 KiB
C#
492 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using Server.Connection;
|
|
using Server.Helper;
|
|
|
|
namespace Server.ReverseProxy;
|
|
|
|
public class ReverseProxyClient
|
|
{
|
|
public enum ProxyType
|
|
{
|
|
Unknown,
|
|
SOCKS5,
|
|
HTTPS
|
|
}
|
|
|
|
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 = byte.MaxValue;
|
|
|
|
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 string UserIP
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
return ((IPEndPoint)Handle.RemoteEndPoint).Address.ToString();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
public string ClientIP => Client.Ip;
|
|
|
|
public string TypeStr
|
|
{
|
|
get
|
|
{
|
|
if (Type == ProxyType.SOCKS5)
|
|
{
|
|
return "SOCK5";
|
|
}
|
|
if (Type == ProxyType.HTTPS)
|
|
{
|
|
return "HTTPS";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
public string UserCountry => Utils.GetCountryName(UserIP);
|
|
|
|
public string ClientCountry => Utils.GetCountryName(ClientIP);
|
|
|
|
public string ReceivedStr => Utils.GetSizeString(LengthReceived);
|
|
|
|
public string SendStr => Utils.GetSizeString(LengthSent);
|
|
|
|
public string TargetStr => $"{TargetServer} : {TargetPort}";
|
|
|
|
public Clients 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 => Handle.Handle.ToInt32();
|
|
|
|
public string TargetServer { get; private set; }
|
|
|
|
public ushort TargetPort { get; private set; }
|
|
|
|
public bool IsConnected { get; private set; }
|
|
|
|
public ProxyType Type { get; private set; }
|
|
|
|
public string HostName { get; private set; }
|
|
|
|
public bool ProxySuccessful { get; private set; }
|
|
|
|
public ReverseProxyClient(Clients client, Socket socket, ReverseProxyServer server)
|
|
{
|
|
Handle = socket;
|
|
Client = client;
|
|
_handshakeStream = new MemoryStream();
|
|
_buffer = new byte[8192];
|
|
IsConnected = true;
|
|
TargetServer = "";
|
|
Type = ProxyType.Unknown;
|
|
Server = server;
|
|
try
|
|
{
|
|
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, AsyncReceive, null);
|
|
}
|
|
catch
|
|
{
|
|
Disconnect();
|
|
}
|
|
}
|
|
|
|
private void AsyncReceive(IAsyncResult ar)
|
|
{
|
|
try
|
|
{
|
|
int num = Handle.EndReceive(ar);
|
|
if (num <= 0)
|
|
{
|
|
Disconnect();
|
|
return;
|
|
}
|
|
if (num > 5000 || _handshakeStream.Length + num > 5000)
|
|
{
|
|
Disconnect();
|
|
return;
|
|
}
|
|
LengthReceived += num;
|
|
_handshakeStream.Write(_buffer, 0, num);
|
|
}
|
|
catch
|
|
{
|
|
Disconnect();
|
|
return;
|
|
}
|
|
byte[] array = _handshakeStream.ToArray();
|
|
switch (PacketsReceived)
|
|
{
|
|
case 0L:
|
|
{
|
|
if (array.Length < 3)
|
|
{
|
|
break;
|
|
}
|
|
string @string = Encoding.ASCII.GetString(array);
|
|
if (array[0] == 5)
|
|
{
|
|
Type = ProxyType.SOCKS5;
|
|
}
|
|
if (!@string.StartsWith("CONNECT") || !@string.Contains(":"))
|
|
{
|
|
break;
|
|
}
|
|
Type = ProxyType.HTTPS;
|
|
using StreamReader streamReader = new StreamReader(new MemoryStream(array));
|
|
string text = streamReader.ReadLine();
|
|
if (text == null)
|
|
{
|
|
break;
|
|
}
|
|
string[] array2 = text.Split(new string[1] { " " }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (array2.Length != 0)
|
|
{
|
|
try
|
|
{
|
|
string text2 = array2[1];
|
|
TargetServer = text2.Split(':')[0];
|
|
TargetPort = ushort.Parse(text2.Split(':')[1]);
|
|
_isConnectCommand = true;
|
|
_isDomainNameType = true;
|
|
Client.SendProxyCommand(new ReverseProxyConnect
|
|
{
|
|
ConnectionId = ConnectionId,
|
|
Target = TargetServer,
|
|
Port = TargetPort
|
|
}, ReverseProxyCommands.CONNECT);
|
|
Server.CallonConnectionEstablished(this);
|
|
return;
|
|
}
|
|
catch
|
|
{
|
|
Disconnect();
|
|
}
|
|
}
|
|
goto IL_01cf;
|
|
}
|
|
case 1L:
|
|
{
|
|
int num2 = 6;
|
|
if (array.Length < num2)
|
|
{
|
|
break;
|
|
}
|
|
if (!CheckProxyVersion(array))
|
|
{
|
|
return;
|
|
}
|
|
_isConnectCommand = array[1] == 1;
|
|
_isBindCommand = array[1] == 2;
|
|
_isUdpCommand = array[1] == 3;
|
|
_isIpType = array[3] == 1;
|
|
_isDomainNameType = array[3] == 3;
|
|
_isIPv6NameType = array[3] == 4;
|
|
Array.Reverse(array, array.Length - 2, 2);
|
|
TargetPort = BitConverter.ToUInt16(array, array.Length - 2);
|
|
if (_isConnectCommand)
|
|
{
|
|
if (_isIpType)
|
|
{
|
|
TargetServer = array[4] + "." + array[5] + "." + array[6] + "." + array[7];
|
|
}
|
|
else if (_isDomainNameType)
|
|
{
|
|
int num3 = array[4];
|
|
if (num2 + num3 < array.Length)
|
|
{
|
|
TargetServer = Encoding.ASCII.GetString(array, 5, num3);
|
|
}
|
|
}
|
|
if (TargetServer.Length > 0)
|
|
{
|
|
Client.SendProxyCommand(new ReverseProxyConnect
|
|
{
|
|
ConnectionId = ConnectionId,
|
|
Target = TargetServer,
|
|
Port = TargetPort
|
|
}, ReverseProxyCommands.CONNECT);
|
|
}
|
|
Server.CallonUpdateConnection(this);
|
|
}
|
|
else
|
|
{
|
|
SendFailToClient();
|
|
}
|
|
return;
|
|
}
|
|
IL_01cf:
|
|
if (CheckProxyVersion(array))
|
|
{
|
|
SendSuccessToClient();
|
|
PacketsReceived++;
|
|
_handshakeStream.SetLength(0L);
|
|
Server.CallonConnectionEstablished(this);
|
|
}
|
|
break;
|
|
}
|
|
try
|
|
{
|
|
Handle.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, AsyncReceive, null);
|
|
}
|
|
catch
|
|
{
|
|
Disconnect();
|
|
}
|
|
}
|
|
|
|
public void Disconnect()
|
|
{
|
|
if (!_disconnectIsSend)
|
|
{
|
|
_disconnectIsSend = true;
|
|
Client.SendProxyCommand(new ReverseProxyDisconnect
|
|
{
|
|
ConnectionId = ConnectionId
|
|
}, ReverseProxyCommands.DISCONNECT);
|
|
}
|
|
try
|
|
{
|
|
Handle.Close();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
IsConnected = false;
|
|
Server.CallonUpdateConnection(this);
|
|
}
|
|
|
|
public void SendToClient(byte[] payload)
|
|
{
|
|
lock (Handle)
|
|
{
|
|
try
|
|
{
|
|
LengthSent += payload.Length;
|
|
Handle.Send(payload);
|
|
}
|
|
catch
|
|
{
|
|
Disconnect();
|
|
}
|
|
}
|
|
Server.CallonUpdateConnection(this);
|
|
}
|
|
|
|
private void SendFailToClient()
|
|
{
|
|
if (Type == ProxyType.HTTPS)
|
|
{
|
|
Disconnect();
|
|
}
|
|
if (Type == ProxyType.SOCKS5)
|
|
{
|
|
SendToClient(new byte[2] { 5, 255 });
|
|
Disconnect();
|
|
}
|
|
}
|
|
|
|
private void SendSuccessToClient()
|
|
{
|
|
if (Type == ProxyType.SOCKS5)
|
|
{
|
|
SendToClient(new byte[2] { 5, 0 });
|
|
}
|
|
}
|
|
|
|
private bool CheckProxyVersion(byte[] payload)
|
|
{
|
|
if (Type == ProxyType.HTTPS)
|
|
{
|
|
return true;
|
|
}
|
|
if (payload.Length != 0 && payload[0] != 5)
|
|
{
|
|
SendFailToClient();
|
|
Disconnect();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void HandleCommandResponse(ReverseProxyConnectResponse response)
|
|
{
|
|
if (_receivedConnResponse)
|
|
{
|
|
return;
|
|
}
|
|
_receivedConnResponse = true;
|
|
if (response.IsConnected)
|
|
{
|
|
HostName = response.HostName;
|
|
if (Type == ProxyType.HTTPS)
|
|
{
|
|
SendToClient(Encoding.ASCII.GetBytes("HTTP/1.0 200 Connection established\r\n\r\n"));
|
|
}
|
|
else if (Type == ProxyType.SOCKS5)
|
|
{
|
|
try
|
|
{
|
|
List<byte> list = new List<byte>();
|
|
list.Add(5);
|
|
list.Add(0);
|
|
list.Add(0);
|
|
list.Add(1);
|
|
list.AddRange(response.LocalAddress);
|
|
list.Add((byte)Math.Floor((decimal)response.LocalPort / 256m));
|
|
list.Add((byte)(response.LocalPort % 256));
|
|
SendToClient(list.ToArray());
|
|
}
|
|
catch
|
|
{
|
|
SendToClient(new byte[10] { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 });
|
|
}
|
|
}
|
|
_handshakeStream.Close();
|
|
ProxySuccessful = true;
|
|
try
|
|
{
|
|
Handle.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, AsyncReceiveProxy, null);
|
|
}
|
|
catch
|
|
{
|
|
Disconnect();
|
|
}
|
|
}
|
|
else if (Type == ProxyType.HTTPS)
|
|
{
|
|
Disconnect();
|
|
}
|
|
else if (Type == ProxyType.SOCKS5)
|
|
{
|
|
SendToClient(new byte[10] { 5, 5, 0, 1, 0, 0, 0, 0, 0, 0 });
|
|
}
|
|
Server.CallonUpdateConnection(this);
|
|
}
|
|
|
|
private void AsyncReceiveProxy(IAsyncResult ar)
|
|
{
|
|
try
|
|
{
|
|
int num = Handle.EndReceive(ar);
|
|
if (num <= 0)
|
|
{
|
|
Disconnect();
|
|
return;
|
|
}
|
|
LengthReceived += num;
|
|
ReverseProxyData reverseProxyData = new ReverseProxyData
|
|
{
|
|
ConnectionId = ConnectionId
|
|
};
|
|
reverseProxyData.Data = new byte[num];
|
|
Array.Copy(_buffer, reverseProxyData.Data, num);
|
|
Client.SendProxyCommand(reverseProxyData, ReverseProxyCommands.DATA);
|
|
LengthSent += num;
|
|
PacketsSended++;
|
|
}
|
|
catch
|
|
{
|
|
Disconnect();
|
|
return;
|
|
}
|
|
PacketsReceived++;
|
|
Server.CallonUpdateConnection(this);
|
|
try
|
|
{
|
|
Handle.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, AsyncReceiveProxy, null);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|