Socks5Handler.cs 9.0 KB

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