using System; using System.Net.Sockets; using System.Text; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; namespace Renci.SshNet.Connection { /// /// Establishes a tunnel via a SOCKS4 proxy server. /// /// /// https://www.openssh.com/txt/socks4.protocol. /// internal sealed class Socks4Connector : ProxyConnector { public Socks4Connector(ISocketFactory socketFactory) : base(socketFactory) { } /// /// Establishes a connection to the server via a SOCKS5 proxy. /// /// The connection information. /// The . protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) { var connectionRequest = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, connectionInfo.ProxyUsername); SocketAbstraction.Send(socket, connectionRequest); // Read reply version if (SocketReadByte(socket, connectionInfo.Timeout) != 0x00) { throw new ProxyException("SOCKS4: Null is expected."); } // Read response code var code = SocketReadByte(socket, connectionInfo.Timeout); switch (code) { case 0x5a: break; case 0x5b: throw new ProxyException("SOCKS4: Connection rejected."); case 0x5c: throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server."); case 0x5d: throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request."); default: throw new ProxyException("SOCKS4: Not valid response."); } var destBuffer = new byte[6]; // destination port and IP address should be ignored _ = SocketRead(socket, destBuffer, 0, destBuffer.Length, connectionInfo.Timeout); } private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port, string username) { var addressBytes = GetSocks4DestinationAddress(hostname); var proxyUserBytes = GetProxyUserBytes(username); var connectionRequest = new byte [ // SOCKS version number 1 + // Command code 1 + // Port number 2 + // IP address addressBytes.Length + // Username proxyUserBytes.Length + // Null terminator 1 ]; var index = 0; // SOCKS version number connectionRequest[index++] = 0x04; // Command code connectionRequest[index++] = 0x01; // establish a TCP/IP stream connection // Port number Pack.UInt16ToBigEndian(port, connectionRequest, index); index += 2; // Address Buffer.BlockCopy(addressBytes, 0, connectionRequest, index, addressBytes.Length); index += addressBytes.Length; // User name Buffer.BlockCopy(proxyUserBytes, 0, connectionRequest, index, proxyUserBytes.Length); index += proxyUserBytes.Length; // Null terminator connectionRequest[index] = 0x00; return connectionRequest; } private static byte[] GetSocks4DestinationAddress(string hostname) { var addresses = DnsAbstraction.GetHostAddresses(hostname); for (var i = 0; i < addresses.Length; i++) { var address = addresses[i]; if (address.AddressFamily == AddressFamily.InterNetwork) { return address.GetAddressBytes(); } } throw new ProxyException(string.Format("SOCKS4 only supports IPv4. No such address found for '{0}'.", hostname)); } private static byte[] GetProxyUserBytes(string proxyUser) { if (proxyUser == null) { return Array.Empty(); } return Encoding.ASCII.GetBytes(proxyUser); } } }