// Decompiled with JetBrains decompiler
// Type: Starksoft.Net.Proxy.Socks5ProxyClient
// Assembly: svchost, Version=3.2.4.0, Culture=neutral, PublicKeyToken=null
// MVID: 8F51F928-E1DB-4815-857F-7D580385B815
// Assembly location: C:\Users\Administrateur\Downloads\Virusshare-00000-msil\Backdoor.MSIL.IrcBot.lp-4dd7df88cffbf85a5894bc1b38660fa613c24b0e6c004ac00b2b119c03a35d3f.exe

using System;
using System.ComponentModel;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Starksoft.Net.Proxy
{
  public class Socks5ProxyClient : IProxyClient
  {
    private const string PROXY_NAME = "SOCKS5";
    private const int SOCKS5_DEFAULT_PORT = 1080;
    private const byte SOCKS5_VERSION_NUMBER = 5;
    private const byte SOCKS5_RESERVED = 0;
    private const byte SOCKS5_AUTH_NUMBER_OF_AUTH_METHODS_SUPPORTED = 2;
    private const byte SOCKS5_AUTH_METHOD_NO_AUTHENTICATION_REQUIRED = 0;
    private const byte SOCKS5_AUTH_METHOD_GSSAPI = 1;
    private const byte SOCKS5_AUTH_METHOD_USERNAME_PASSWORD = 2;
    private const byte SOCKS5_AUTH_METHOD_IANA_ASSIGNED_RANGE_BEGIN = 3;
    private const byte SOCKS5_AUTH_METHOD_IANA_ASSIGNED_RANGE_END = 127;
    private const byte SOCKS5_AUTH_METHOD_RESERVED_RANGE_BEGIN = 128;
    private const byte SOCKS5_AUTH_METHOD_RESERVED_RANGE_END = 254;
    private const byte SOCKS5_AUTH_METHOD_REPLY_NO_ACCEPTABLE_METHODS = 255;
    private const byte SOCKS5_CMD_CONNECT = 1;
    private const byte SOCKS5_CMD_BIND = 2;
    private const byte SOCKS5_CMD_UDP_ASSOCIATE = 3;
    private const byte SOCKS5_CMD_REPLY_SUCCEEDED = 0;
    private const byte SOCKS5_CMD_REPLY_GENERAL_SOCKS_SERVER_FAILURE = 1;
    private const byte SOCKS5_CMD_REPLY_CONNECTION_NOT_ALLOWED_BY_RULESET = 2;
    private const byte SOCKS5_CMD_REPLY_NETWORK_UNREACHABLE = 3;
    private const byte SOCKS5_CMD_REPLY_HOST_UNREACHABLE = 4;
    private const byte SOCKS5_CMD_REPLY_CONNECTION_REFUSED = 5;
    private const byte SOCKS5_CMD_REPLY_TTL_EXPIRED = 6;
    private const byte SOCKS5_CMD_REPLY_COMMAND_NOT_SUPPORTED = 7;
    private const byte SOCKS5_CMD_REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 8;
    private const byte SOCKS5_ADDRTYPE_IPV4 = 1;
    private const byte SOCKS5_ADDRTYPE_DOMAIN_NAME = 3;
    private const byte SOCKS5_ADDRTYPE_IPV6 = 4;
    private string _proxyHost;
    private int _proxyPort;
    private string _proxyUserName;
    private string _proxyPassword;
    private Socks5ProxyClient.SocksAuthentication _proxyAuthMethod;
    private TcpClient _tcpClient;
    private BackgroundWorker _asyncWorker;
    private Exception _asyncException;
    private bool _asyncCancelled;

    public Socks5ProxyClient()
    {
    }

    public Socks5ProxyClient(TcpClient tcpClient) => this._tcpClient = tcpClient != null ? tcpClient : throw new ArgumentNullException(nameof (tcpClient));

    public Socks5ProxyClient(string proxyHost)
    {
      this._proxyHost = !string.IsNullOrEmpty(proxyHost) ? proxyHost : throw new ArgumentNullException(nameof (proxyHost));
      this._proxyPort = 1080;
    }

    public Socks5ProxyClient(string proxyHost, int proxyPort)
    {
      if (string.IsNullOrEmpty(proxyHost))
        throw new ArgumentNullException(nameof (proxyHost));
      if (proxyPort <= 0 || proxyPort > (int) ushort.MaxValue)
        throw new ArgumentOutOfRangeException(nameof (proxyPort), "port must be greater than zero and less than 65535");
      this._proxyHost = proxyHost;
      this._proxyPort = proxyPort;
    }

    public Socks5ProxyClient(string proxyHost, string proxyUserName, string proxyPassword)
    {
      if (string.IsNullOrEmpty(proxyHost))
        throw new ArgumentNullException(nameof (proxyHost));
      if (proxyUserName == null)
        throw new ArgumentNullException(nameof (proxyUserName));
      if (proxyPassword == null)
        throw new ArgumentNullException(nameof (proxyPassword));
      this._proxyHost = proxyHost;
      this._proxyPort = 1080;
      this._proxyUserName = proxyUserName;
      this._proxyPassword = proxyPassword;
    }

    public Socks5ProxyClient(
      string proxyHost,
      int proxyPort,
      string proxyUserName,
      string proxyPassword)
    {
      if (string.IsNullOrEmpty(proxyHost))
        throw new ArgumentNullException(nameof (proxyHost));
      if (proxyPort <= 0 || proxyPort > (int) ushort.MaxValue)
        throw new ArgumentOutOfRangeException(nameof (proxyPort), "port must be greater than zero and less than 65535");
      if (proxyUserName == null)
        throw new ArgumentNullException(nameof (proxyUserName));
      if (proxyPassword == null)
        throw new ArgumentNullException(nameof (proxyPassword));
      this._proxyHost = proxyHost;
      this._proxyPort = proxyPort;
      this._proxyUserName = proxyUserName;
      this._proxyPassword = proxyPassword;
    }

    public string ProxyHost
    {
      get => this._proxyHost;
      set => this._proxyHost = value;
    }

    public int ProxyPort
    {
      get => this._proxyPort;
      set => this._proxyPort = value;
    }

    public string ProxyName => "SOCKS5";

    public string ProxyUserName
    {
      get => this._proxyUserName;
      set => this._proxyUserName = value;
    }

    public string ProxyPassword
    {
      get => this._proxyPassword;
      set => this._proxyPassword = value;
    }

    public TcpClient TcpClient
    {
      get => this._tcpClient;
      set => this._tcpClient = value;
    }

    public TcpClient CreateConnection(string destinationHost, int destinationPort)
    {
      if (string.IsNullOrEmpty(destinationHost))
        throw new ArgumentNullException(nameof (destinationHost));
      if (destinationPort > 0)
      {
        if (destinationPort <= (int) ushort.MaxValue)
        {
          try
          {
            if (this._tcpClient == null)
            {
              if (string.IsNullOrEmpty(this._proxyHost))
                throw new ProxyException("ProxyHost property must contain a value.");
              if (this._proxyPort <= 0 || this._proxyPort > (int) ushort.MaxValue)
                throw new ProxyException("ProxyPort value must be greater than zero and less than 65535");
              this._tcpClient = new TcpClient();
              this._tcpClient.Connect(this._proxyHost, this._proxyPort);
            }
            this.DetermineClientAuthMethod();
            this.NegotiateServerAuthMethod();
            this.SendCommand((byte) 1, destinationHost, destinationPort);
            return this._tcpClient;
          }
          catch (Exception ex)
          {
            throw new ProxyException(string.Format((IFormatProvider) CultureInfo.InvariantCulture, "Connection to proxy host {0} on port {1} failed.", new object[2]
            {
              (object) Utils.GetHost(this._tcpClient),
              (object) Utils.GetPort(this._tcpClient)
            }), ex);
          }
        }
      }
      throw new ArgumentOutOfRangeException(nameof (destinationPort), "port must be greater than zero and less than 65535");
    }

    private void DetermineClientAuthMethod()
    {
      if (this._proxyUserName != null && this._proxyPassword != null)
        this._proxyAuthMethod = Socks5ProxyClient.SocksAuthentication.UsernamePassword;
      else
        this._proxyAuthMethod = Socks5ProxyClient.SocksAuthentication.None;
    }

    private void NegotiateServerAuthMethod()
    {
      NetworkStream stream = this._tcpClient.GetStream();
      byte[] buffer1 = new byte[4]
      {
        (byte) 5,
        (byte) 2,
        (byte) 0,
        (byte) 2
      };
      stream.Write(buffer1, 0, buffer1.Length);
      byte[] buffer2 = new byte[2];
      stream.Read(buffer2, 0, buffer2.Length);
      byte num = buffer2[1];
      if (num == byte.MaxValue)
      {
        this._tcpClient.Close();
        throw new ProxyException("The proxy destination does not accept the supported proxy client authentication methods.");
      }
      if (num == (byte) 2 && this._proxyAuthMethod == Socks5ProxyClient.SocksAuthentication.None)
      {
        this._tcpClient.Close();
        throw new ProxyException("The proxy destination requires a username and password for authentication.");
      }
      if (num != (byte) 2)
        return;
      byte[] destinationArray = new byte[this._proxyUserName.Length + this._proxyPassword.Length + 3];
      destinationArray[0] = (byte) 5;
      destinationArray[1] = (byte) this._proxyUserName.Length;
      Array.Copy((Array) Encoding.ASCII.GetBytes(this._proxyUserName), 0, (Array) destinationArray, 2, this._proxyUserName.Length);
      destinationArray[this._proxyUserName.Length + 2] = (byte) this._proxyPassword.Length;
      Array.Copy((Array) Encoding.ASCII.GetBytes(this._proxyPassword), 0, (Array) destinationArray, this._proxyUserName.Length + 3, this._proxyPassword.Length);
    }

    private byte GetDestAddressType(string host)
    {
      IPAddress address = (IPAddress) null;
      if (!IPAddress.TryParse(host, out address))
        return 3;
      switch (address.AddressFamily)
      {
        case AddressFamily.InterNetwork:
          return 1;
        case AddressFamily.InterNetworkV6:
          return 4;
        default:
          throw new ProxyException(string.Format((IFormatProvider) CultureInfo.InvariantCulture, "The host addess {0} of type '{1}' is not a supported address type.  The supported types are InterNetwork and InterNetworkV6.", new object[2]
          {
            (object) host,
            (object) System.Enum.GetName(typeof (AddressFamily), (object) address.AddressFamily)
          }));
      }
    }

    private byte[] GetDestAddressBytes(byte addressType, string host)
    {
      switch (addressType)
      {
        case 1:
        case 4:
          return IPAddress.Parse(host).GetAddressBytes();
        case 3:
          byte[] destAddressBytes = new byte[host.Length + 1];
          destAddressBytes[0] = Convert.ToByte(host.Length);
          Encoding.ASCII.GetBytes(host).CopyTo((Array) destAddressBytes, 1);
          return destAddressBytes;
        default:
          return (byte[]) null;
      }
    }

    private byte[] GetDestPortBytes(int value) => new byte[2]
    {
      Convert.ToByte(value / 256),
      Convert.ToByte(value % 256)
    };

    private void SendCommand(byte command, string destinationHost, int destinationPort)
    {
      NetworkStream stream = this._tcpClient.GetStream();
      byte destAddressType = this.GetDestAddressType(destinationHost);
      byte[] destAddressBytes = this.GetDestAddressBytes(destAddressType, destinationHost);
      byte[] destPortBytes = this.GetDestPortBytes(destinationPort);
      byte[] buffer = new byte[4 + destAddressBytes.Length + 2];
      buffer[0] = (byte) 5;
      buffer[1] = command;
      buffer[2] = (byte) 0;
      buffer[3] = destAddressType;
      destAddressBytes.CopyTo((Array) buffer, 4);
      destPortBytes.CopyTo((Array) buffer, 4 + destAddressBytes.Length);
      stream.Write(buffer, 0, buffer.Length);
      byte[] numArray = new byte[(int) byte.MaxValue];
      stream.Read(numArray, 0, numArray.Length);
      if (numArray[1] == (byte) 0)
        return;
      this.HandleProxyCommandError(numArray, destinationHost, destinationPort);
    }

    private void HandleProxyCommandError(
      byte[] response,
      string destinationHost,
      int destinationPort)
    {
      byte num1 = response[1];
      byte num2 = response[3];
      string str1 = "";
      short num3 = 0;
      switch (num2)
      {
        case 1:
          byte[] address1 = new byte[4];
          for (int index = 0; index < 4; ++index)
            address1[index] = response[index + 4];
          str1 = new IPAddress(address1).ToString();
          num3 = BitConverter.ToInt16(new byte[2]
          {
            response[9],
            response[8]
          }, 0);
          break;
        case 3:
          int int32 = Convert.ToInt32(response[4]);
          byte[] bytes = new byte[int32];
          for (int index = 0; index < int32; ++index)
            bytes[index] = response[index + 5];
          str1 = Encoding.ASCII.GetString(bytes);
          num3 = BitConverter.ToInt16(new byte[2]
          {
            response[6 + int32],
            response[5 + int32]
          }, 0);
          break;
        case 4:
          byte[] address2 = new byte[16];
          for (int index = 0; index < 16; ++index)
            address2[index] = response[index + 4];
          str1 = new IPAddress(address2).ToString();
          num3 = BitConverter.ToInt16(new byte[2]
          {
            response[21],
            response[20]
          }, 0);
          break;
      }
      string str2;
      switch (num1)
      {
        case 1:
          str2 = "a general socks destination failure occurred";
          break;
        case 2:
          str2 = "the connection is not allowed by proxy destination rule set";
          break;
        case 3:
          str2 = "the network was unreachable";
          break;
        case 4:
          str2 = "the host was unreachable";
          break;
        case 5:
          str2 = "the connection was refused by the remote network";
          break;
        case 6:
          str2 = "the time to live (TTL) has expired";
          break;
        case 7:
          str2 = "the command issued by the proxy client is not supported by the proxy destination";
          break;
        case 8:
          str2 = "the address type specified is not supported";
          break;
        default:
          str2 = string.Format((IFormatProvider) CultureInfo.InvariantCulture, "that an unknown reply with the code value '{0}' was received by the destination", new object[1]
          {
            (object) num1.ToString((IFormatProvider) CultureInfo.InvariantCulture)
          });
          break;
      }
      throw new ProxyException(string.Format((IFormatProvider) CultureInfo.InvariantCulture, "The {0} concerning destination host {1} port number {2}.  The destination reported the host as {3} port {4}.", (object) str2, (object) destinationHost, (object) destinationPort, (object) str1, (object) num3.ToString((IFormatProvider) CultureInfo.InvariantCulture)));
    }

    public bool IsBusy => this._asyncWorker != null && this._asyncWorker.IsBusy;

    public bool IsAsyncCancelled => this._asyncCancelled;

    public void CancelAsync()
    {
      if (this._asyncWorker == null || this._asyncWorker.CancellationPending || !this._asyncWorker.IsBusy)
        return;
      this._asyncCancelled = true;
      this._asyncWorker.CancelAsync();
    }

    private void CreateAsyncWorker()
    {
      if (this._asyncWorker != null)
        this._asyncWorker.Dispose();
      this._asyncException = (Exception) null;
      this._asyncWorker = (BackgroundWorker) null;
      this._asyncCancelled = false;
      this._asyncWorker = new BackgroundWorker();
    }

    public event EventHandler<CreateConnectionAsyncCompletedEventArgs> CreateConnectionAsyncCompleted;

    public void CreateConnectionAsync(string destinationHost, int destinationPort)
    {
      if (this._asyncWorker != null && this._asyncWorker.IsBusy)
        throw new InvalidOperationException("The Socks4 object is already busy executing another asynchronous operation.  You can only execute one asychronous method at a time.");
      this.CreateAsyncWorker();
      this._asyncWorker.WorkerSupportsCancellation = true;
      this._asyncWorker.DoWork += new DoWorkEventHandler(this.CreateConnectionAsync_DoWork);
      this._asyncWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.CreateConnectionAsync_RunWorkerCompleted);
      this._asyncWorker.RunWorkerAsync((object) new object[2]
      {
        (object) destinationHost,
        (object) destinationPort
      });
    }

    private void CreateConnectionAsync_DoWork(object sender, DoWorkEventArgs e)
    {
      try
      {
        object[] objArray = (object[]) e.Argument;
        e.Result = (object) this.CreateConnection((string) objArray[0], (int) objArray[1]);
      }
      catch (Exception ex)
      {
        this._asyncException = ex;
      }
    }

    private void CreateConnectionAsync_RunWorkerCompleted(
      object sender,
      RunWorkerCompletedEventArgs e)
    {
      if (this.CreateConnectionAsyncCompleted == null)
        return;
      this.CreateConnectionAsyncCompleted((object) this, new CreateConnectionAsyncCompletedEventArgs(this._asyncException, this._asyncCancelled, (TcpClient) e.Result));
    }

    private enum SocksAuthentication
    {
      None,
      UsernamePassword,
    }
  }
}
