Socks5Connector
Establishes a tunnel via a SOCKS5 proxy server.
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using System;
using System.Net;
using System.Net.Sockets;
namespace Renci.SshNet.Connection
{
internal class Socks5Connector : ConnectorBase
{
public Socks5Connector(ISocketFactory socketFactory)
: base(socketFactory)
{
}
public override Socket Connect(IConnectionInfo connectionInfo)
{
Socket socket = SocketConnect(connectionInfo.ProxyHost, connectionInfo.ProxyPort, connectionInfo.Timeout);
try {
HandleProxyConnect(connectionInfo, socket);
return socket;
} catch (Exception) {
socket.Shutdown(SocketShutdown.Both);
socket.Dispose();
throw;
}
}
private void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
{
byte[] data = new byte[4] {
5,
2,
0,
2
};
SocketAbstraction.Send(socket, data);
byte b = ConnectorBase.SocketReadByte(socket);
if (b != 5)
throw new ProxyException($"""{b}""");
switch (ConnectorBase.SocketReadByte(socket)) {
case 2: {
byte[] data2 = CreateSocks5UserNameAndPasswordAuthenticationRequest(connectionInfo.ProxyUsername, connectionInfo.ProxyPassword);
SocketAbstraction.Send(socket, data2);
byte[] array = SocketAbstraction.Read(socket, 2, connectionInfo.Timeout);
if (array[0] != 1)
throw new ProxyException("SOCKS5: Server authentication version is not valid.");
if (array[1] != 0)
throw new ProxyException("SOCKS5: Username/Password authentication failed.");
break;
}
case byte.MaxValue:
throw new ProxyException("SOCKS5: No acceptable authentication methods were offered.");
}
byte[] data3 = CreateSocks5ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port);
SocketAbstraction.Send(socket, data3);
if (ConnectorBase.SocketReadByte(socket) != 5)
throw new ProxyException("SOCKS5: Version 5 is expected.");
switch (ConnectorBase.SocketReadByte(socket)) {
case 1:
throw new ProxyException("SOCKS5: General failure.");
case 2:
throw new ProxyException("SOCKS5: Connection not allowed by ruleset.");
case 3:
throw new ProxyException("SOCKS5: Network unreachable.");
case 4:
throw new ProxyException("SOCKS5: Host unreachable.");
case 5:
throw new ProxyException("SOCKS5: Connection refused by destination host.");
case 6:
throw new ProxyException("SOCKS5: TTL expired.");
case 7:
throw new ProxyException("SOCKS5: Command not supported or protocol error.");
case 8:
throw new ProxyException("SOCKS5: Address type not supported.");
default:
throw new ProxyException("SOCKS5: Not valid response.");
case 0: {
if (ConnectorBase.SocketReadByte(socket) != 0)
throw new ProxyException("SOCKS5: 0 byte is expected.");
byte b2 = ConnectorBase.SocketReadByte(socket);
switch (b2) {
case 1: {
byte[] buffer2 = new byte[4];
ConnectorBase.SocketRead(socket, buffer2, 0, 4);
break;
}
case 4: {
byte[] buffer = new byte[16];
ConnectorBase.SocketRead(socket, buffer, 0, 16);
break;
}
default:
throw new ProxyException($"""{b2}""");
}
byte[] buffer3 = new byte[2];
ConnectorBase.SocketRead(socket, buffer3, 0, 2);
break;
}
}
}
private static byte[] CreateSocks5UserNameAndPasswordAuthenticationRequest(string username, string password)
{
if (username.Length > 255)
throw new ProxyException("Proxy username is too long.");
if (password.Length > 255)
throw new ProxyException("Proxy password is too long.");
byte[] array = new byte[2 + username.Length + 1 + password.Length];
int num = 0;
array[num++] = 1;
array[num++] = (byte)username.Length;
SshData.Ascii.GetBytes(username, 0, username.Length, array, num);
num += username.Length;
array[num++] = (byte)password.Length;
SshData.Ascii.GetBytes(password, 0, password.Length, array, num);
return array;
}
private static byte[] CreateSocks5ConnectionRequest(string hostname, ushort port)
{
byte addressType;
byte[] socks5DestinationAddress = GetSocks5DestinationAddress(hostname, out addressType);
byte[] array = new byte[4 + socks5DestinationAddress.Length + 2];
int num = 0;
array[num++] = 5;
array[num++] = 1;
array[num++] = 0;
array[num++] = addressType;
Buffer.BlockCopy(socks5DestinationAddress, 0, array, num, socks5DestinationAddress.Length);
num += socks5DestinationAddress.Length;
Pack.UInt16ToBigEndian(port, array, num);
return array;
}
private static byte[] GetSocks5DestinationAddress(string hostname, out byte addressType)
{
IPAddress iPAddress = DnsAbstraction.GetHostAddresses(hostname)[0];
switch (iPAddress.AddressFamily) {
case AddressFamily.InterNetwork:
addressType = 1;
return iPAddress.GetAddressBytes();
case AddressFamily.InterNetworkV6:
addressType = 4;
return iPAddress.GetAddressBytes();
default:
throw new ProxyException($"""{iPAddress}""");
}
}
}
}