2
0

Socks5Handler.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. using System.Net;
  2. using System.Net.Sockets;
  3. using Renci.SshNet.Abstractions;
  4. using Renci.SshNet.Common;
  5. using Renci.SshNet.Messages.Transport;
  6. namespace Renci.SshNet.IntegrationTests.Common
  7. {
  8. class Socks5Handler
  9. {
  10. private readonly IPEndPoint _proxyEndPoint;
  11. private readonly string _userName;
  12. private readonly string _password;
  13. public Socks5Handler(IPEndPoint proxyEndPoint, string userName, string password)
  14. {
  15. _proxyEndPoint = proxyEndPoint;
  16. _userName = userName;
  17. _password = password;
  18. }
  19. public Socket Connect(IPEndPoint endPoint)
  20. {
  21. ThrowHelper.ThrowIfNull(endPoint);
  22. var addressBytes = GetAddressBytes(endPoint);
  23. return Connect(addressBytes, endPoint.Port);
  24. }
  25. public Socket Connect(string host, int port)
  26. {
  27. ThrowHelper.ThrowIfNull(host);
  28. if (host.Length > byte.MaxValue)
  29. {
  30. throw new ArgumentException($@"Cannot be more than {byte.MaxValue} characters.", nameof(host));
  31. }
  32. var addressBytes = new byte[host.Length + 2];
  33. addressBytes[0] = 0x03;
  34. addressBytes[1] = (byte)host.Length;
  35. Encoding.ASCII.GetBytes(host, 0, host.Length, addressBytes, 2);
  36. return Connect(addressBytes, port);
  37. }
  38. private Socket Connect(byte[] addressBytes, int port)
  39. {
  40. var socket = SocketAbstraction.Connect(_proxyEndPoint, TimeSpan.FromSeconds(5));
  41. // Send socks version number
  42. SocketWriteByte(socket, 0x05);
  43. // Send number of supported authentication methods
  44. SocketWriteByte(socket, 0x02);
  45. // Send supported authentication methods
  46. SocketWriteByte(socket, 0x00); // No authentication
  47. SocketWriteByte(socket, 0x02); // Username/Password
  48. var socksVersion = SocketReadByte(socket);
  49. if (socksVersion != 0x05)
  50. {
  51. throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion));
  52. }
  53. var authenticationMethod = SocketReadByte(socket);
  54. switch (authenticationMethod)
  55. {
  56. case 0x00:
  57. break;
  58. case 0x02:
  59. // Send version
  60. SocketWriteByte(socket, 0x01);
  61. var username = Encoding.ASCII.GetBytes(_userName);
  62. if (username.Length > byte.MaxValue)
  63. {
  64. throw new ProxyException("Proxy username is too long.");
  65. }
  66. // Send username length
  67. SocketWriteByte(socket, (byte)username.Length);
  68. // Send username
  69. SocketAbstraction.Send(socket, username);
  70. var password = Encoding.ASCII.GetBytes(_password);
  71. if (password.Length > byte.MaxValue)
  72. {
  73. throw new ProxyException("Proxy password is too long.");
  74. }
  75. // Send username length
  76. SocketWriteByte(socket, (byte)password.Length);
  77. // Send username
  78. SocketAbstraction.Send(socket, password);
  79. var serverVersion = SocketReadByte(socket);
  80. if (serverVersion != 1)
  81. {
  82. throw new ProxyException("SOCKS5: Server authentication version is not valid.");
  83. }
  84. var statusCode = SocketReadByte(socket);
  85. if (statusCode != 0)
  86. {
  87. throw new ProxyException("SOCKS5: Username/Password authentication failed.");
  88. }
  89. break;
  90. case 0xFF:
  91. throw new ProxyException("SOCKS5: No acceptable authentication methods were offered.");
  92. default:
  93. throw new ProxyException("SOCKS5: No acceptable authentication methods were offered.");
  94. }
  95. // Send socks version number
  96. SocketWriteByte(socket, 0x05);
  97. // Send command code
  98. SocketWriteByte(socket, 0x01); // establish a TCP/IP stream connection
  99. // Send reserved, must be 0x00
  100. SocketWriteByte(socket, 0x00);
  101. // Send address type and address
  102. SocketAbstraction.Send(socket, addressBytes);
  103. // Send port
  104. SocketWriteByte(socket, (byte)(port / 0xFF));
  105. SocketWriteByte(socket, (byte)(port % 0xFF));
  106. // Read Server SOCKS5 version
  107. if (SocketReadByte(socket) != 5)
  108. {
  109. throw new ProxyException("SOCKS5: Version 5 is expected.");
  110. }
  111. // Read response code
  112. var status = SocketReadByte(socket);
  113. switch (status)
  114. {
  115. case 0x00:
  116. break;
  117. case 0x01:
  118. throw new ProxyException("SOCKS5: General failure.");
  119. case 0x02:
  120. throw new ProxyException("SOCKS5: Connection not allowed by ruleset.");
  121. case 0x03:
  122. throw new ProxyException("SOCKS5: Network unreachable.");
  123. case 0x04:
  124. throw new ProxyException("SOCKS5: Host unreachable.");
  125. case 0x05:
  126. throw new ProxyException("SOCKS5: Connection refused by destination host.");
  127. case 0x06:
  128. throw new ProxyException("SOCKS5: TTL expired.");
  129. case 0x07:
  130. throw new ProxyException("SOCKS5: Command not supported or protocol error.");
  131. case 0x08:
  132. throw new ProxyException("SOCKS5: Address type not supported.");
  133. default:
  134. throw new ProxyException("SOCKS4: Not valid response.");
  135. }
  136. // Read 0
  137. if (SocketReadByte(socket) != 0)
  138. {
  139. throw new ProxyException("SOCKS5: 0 byte is expected.");
  140. }
  141. var addressType = SocketReadByte(socket);
  142. var responseIp = new byte[16];
  143. switch (addressType)
  144. {
  145. case 0x01:
  146. SocketRead(socket, responseIp, 0, 4);
  147. break;
  148. case 0x04:
  149. SocketRead(socket, responseIp, 0, 16);
  150. break;
  151. default:
  152. throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType));
  153. }
  154. var portBytes = new byte[2];
  155. // Read 2 bytes to be ignored
  156. SocketRead(socket, portBytes, 0, 2);
  157. return socket;
  158. }
  159. private static byte[] GetAddressBytes(IPEndPoint endPoint)
  160. {
  161. if (endPoint.AddressFamily == AddressFamily.InterNetwork)
  162. {
  163. var addressBytes = new byte[4 + 1];
  164. addressBytes[0] = 0x01;
  165. var address = endPoint.Address.GetAddressBytes();
  166. Buffer.BlockCopy(address, 0, addressBytes, 1, address.Length);
  167. return addressBytes;
  168. }
  169. if (endPoint.AddressFamily == AddressFamily.InterNetworkV6)
  170. {
  171. var addressBytes = new byte[16 + 1];
  172. addressBytes[0] = 0x04;
  173. var address = endPoint.Address.GetAddressBytes();
  174. Buffer.BlockCopy(address, 0, addressBytes, 1, address.Length);
  175. return addressBytes;
  176. }
  177. throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", endPoint.Address));
  178. }
  179. private static void SocketWriteByte(Socket socket, byte data)
  180. {
  181. SocketAbstraction.Send(socket, new[] { data });
  182. }
  183. private static byte SocketReadByte(Socket socket)
  184. {
  185. var buffer = new byte[1];
  186. SocketRead(socket, buffer, 0, 1);
  187. return buffer[0];
  188. }
  189. private static int SocketRead(Socket socket, byte[] buffer, int offset, int length)
  190. {
  191. var bytesRead = SocketAbstraction.Read(socket, buffer, offset, length, TimeSpan.FromMilliseconds(-1));
  192. if (bytesRead == 0)
  193. {
  194. // when we're in the disconnecting state (either triggered by client or server), then the
  195. // SshConnectionException will interrupt the message listener loop (if not already interrupted)
  196. // and the exception itself will be ignored (in RaiseError)
  197. throw new SshConnectionException("An established connection was aborted by the server.",
  198. DisconnectReason.ConnectionLost);
  199. }
  200. return bytesRead;
  201. }
  202. }
  203. }