<PackageReference Include="SSH.NET" Version="2020.0.1" />

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}"""); } } } }