| | | 1 | | using System; |
| | | 2 | | using System.Net.Sockets; |
| | | 3 | | using System.Text; |
| | | 4 | | |
| | | 5 | | using Renci.SshNet.Abstractions; |
| | | 6 | | using Renci.SshNet.Common; |
| | | 7 | | |
| | | 8 | | namespace Renci.SshNet.Connection |
| | | 9 | | { |
| | | 10 | | /// <summary> |
| | | 11 | | /// Establishes a tunnel via a SOCKS4 proxy server. |
| | | 12 | | /// </summary> |
| | | 13 | | /// <remarks> |
| | | 14 | | /// https://www.openssh.com/txt/socks4.protocol. |
| | | 15 | | /// </remarks> |
| | | 16 | | internal sealed class Socks4Connector : ProxyConnector |
| | | 17 | | { |
| | | 18 | | public Socks4Connector(ISocketFactory socketFactory) |
| | 96 | 19 | | : base(socketFactory) |
| | 96 | 20 | | { |
| | 96 | 21 | | } |
| | | 22 | | |
| | | 23 | | /// <summary> |
| | | 24 | | /// Establishes a connection to the server via a SOCKS5 proxy. |
| | | 25 | | /// </summary> |
| | | 26 | | /// <param name="connectionInfo">The connection information.</param> |
| | | 27 | | /// <param name="socket">The <see cref="Socket"/>.</param> |
| | | 28 | | protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) |
| | 72 | 29 | | { |
| | 72 | 30 | | var connectionRequest = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, conn |
| | 72 | 31 | | SocketAbstraction.Send(socket, connectionRequest); |
| | | 32 | | |
| | | 33 | | // Read reply version |
| | 72 | 34 | | if (SocketReadByte(socket, connectionInfo.Timeout) != 0x00) |
| | 0 | 35 | | { |
| | 0 | 36 | | throw new ProxyException("SOCKS4: Null is expected."); |
| | | 37 | | } |
| | | 38 | | |
| | | 39 | | // Read response code |
| | 57 | 40 | | var code = SocketReadByte(socket, connectionInfo.Timeout); |
| | | 41 | | |
| | 42 | 42 | | switch (code) |
| | | 43 | | { |
| | | 44 | | case 0x5a: |
| | 30 | 45 | | break; |
| | | 46 | | case 0x5b: |
| | 12 | 47 | | throw new ProxyException("SOCKS4: Connection rejected."); |
| | | 48 | | case 0x5c: |
| | 0 | 49 | | throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server."); |
| | | 50 | | case 0x5d: |
| | 0 | 51 | | throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the reques |
| | | 52 | | default: |
| | 0 | 53 | | throw new ProxyException("SOCKS4: Not valid response."); |
| | | 54 | | } |
| | | 55 | | |
| | 30 | 56 | | var destBuffer = new byte[6]; // destination port and IP address should be ignored |
| | 30 | 57 | | _ = SocketRead(socket, destBuffer, 0, destBuffer.Length, connectionInfo.Timeout); |
| | 15 | 58 | | } |
| | | 59 | | |
| | | 60 | | private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port, string username) |
| | 72 | 61 | | { |
| | 72 | 62 | | var addressBytes = GetSocks4DestinationAddress(hostname); |
| | 72 | 63 | | var proxyUserBytes = GetProxyUserBytes(username); |
| | | 64 | | |
| | 72 | 65 | | var connectionRequest = new byte[// SOCKS version number |
| | 72 | 66 | | 1 + |
| | 72 | 67 | | |
| | 72 | 68 | | // Command code |
| | 72 | 69 | | 1 + |
| | 72 | 70 | | |
| | 72 | 71 | | // Port number |
| | 72 | 72 | | 2 + |
| | 72 | 73 | | |
| | 72 | 74 | | // IP address |
| | 72 | 75 | | addressBytes.Length + |
| | 72 | 76 | | |
| | 72 | 77 | | // Username |
| | 72 | 78 | | proxyUserBytes.Length + |
| | 72 | 79 | | |
| | 72 | 80 | | // Null terminator |
| | 72 | 81 | | 1]; |
| | | 82 | | |
| | 72 | 83 | | var index = 0; |
| | | 84 | | |
| | | 85 | | // SOCKS version number |
| | 72 | 86 | | connectionRequest[index++] = 0x04; |
| | | 87 | | |
| | | 88 | | // Command code |
| | 72 | 89 | | connectionRequest[index++] = 0x01; // establish a TCP/IP stream connection |
| | | 90 | | |
| | | 91 | | // Port number |
| | 72 | 92 | | Pack.UInt16ToBigEndian(port, connectionRequest, index); |
| | 72 | 93 | | index += 2; |
| | | 94 | | |
| | | 95 | | // Address |
| | 72 | 96 | | Buffer.BlockCopy(addressBytes, 0, connectionRequest, index, addressBytes.Length); |
| | 72 | 97 | | index += addressBytes.Length; |
| | | 98 | | |
| | | 99 | | // User name |
| | 72 | 100 | | Buffer.BlockCopy(proxyUserBytes, 0, connectionRequest, index, proxyUserBytes.Length); |
| | 72 | 101 | | index += proxyUserBytes.Length; |
| | | 102 | | |
| | | 103 | | // Null terminator |
| | 72 | 104 | | connectionRequest[index] = 0x00; |
| | | 105 | | |
| | 72 | 106 | | return connectionRequest; |
| | 72 | 107 | | } |
| | | 108 | | |
| | | 109 | | private static byte[] GetSocks4DestinationAddress(string hostname) |
| | 72 | 110 | | { |
| | 72 | 111 | | var addresses = DnsAbstraction.GetHostAddresses(hostname); |
| | | 112 | | |
| | 144 | 113 | | for (var i = 0; i < addresses.Length; i++) |
| | 72 | 114 | | { |
| | 72 | 115 | | var address = addresses[i]; |
| | 72 | 116 | | if (address.AddressFamily == AddressFamily.InterNetwork) |
| | 72 | 117 | | { |
| | 72 | 118 | | return address.GetAddressBytes(); |
| | | 119 | | } |
| | 0 | 120 | | } |
| | | 121 | | |
| | 0 | 122 | | throw new ProxyException(string.Format("SOCKS4 only supports IPv4. No such address found for '{0}'.", hostna |
| | 72 | 123 | | } |
| | | 124 | | |
| | | 125 | | private static byte[] GetProxyUserBytes(string proxyUser) |
| | 72 | 126 | | { |
| | 72 | 127 | | if (proxyUser == null) |
| | 0 | 128 | | { |
| | 0 | 129 | | return Array.Empty<byte>(); |
| | | 130 | | } |
| | | 131 | | |
| | 72 | 132 | | return Encoding.ASCII.GetBytes(proxyUser); |
| | 72 | 133 | | } |
| | | 134 | | } |
| | | 135 | | } |