Socks5UdpProxy.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. using STUN.Utils;
  2. using System;
  3. using System.Diagnostics;
  4. using System.Net;
  5. using System.Net.NetworkInformation;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace STUN.Proxy
  11. {
  12. public class Socks5UdpProxy : IUdpProxy
  13. {
  14. private readonly TcpClient _assoc;
  15. private readonly IPEndPoint _socksTcpEndPoint;
  16. private IPEndPoint? _assocEndPoint;
  17. public TimeSpan Timeout
  18. {
  19. get => TimeSpan.FromMilliseconds(_udpClient.Client.ReceiveTimeout);
  20. set
  21. {
  22. var timeout = Convert.ToInt32(value.TotalMilliseconds);
  23. _udpClient.Client.ReceiveTimeout = timeout;
  24. _assoc.ReceiveTimeout = timeout;
  25. }
  26. }
  27. public IPEndPoint LocalEndPoint => (IPEndPoint)_udpClient.Client.LocalEndPoint!;
  28. private readonly UdpClient _udpClient;
  29. private readonly string? _user;
  30. private readonly string? _password;
  31. public Socks5UdpProxy(IPEndPoint? local, IPEndPoint proxy)
  32. {
  33. _udpClient = local is null ? new UdpClient() : new UdpClient(local);
  34. _assoc = new TcpClient(proxy.AddressFamily);
  35. _socksTcpEndPoint = proxy;
  36. }
  37. public Socks5UdpProxy(IPEndPoint? local, IPEndPoint proxy, string? user, string? password) : this(local, proxy)
  38. {
  39. _user = user;
  40. _password = password;
  41. }
  42. public async Task ConnectAsync(CancellationToken token = default)
  43. {
  44. try
  45. {
  46. var buf = new byte[1024];
  47. await _assoc.ConnectAsync(_socksTcpEndPoint.Address, _socksTcpEndPoint.Port, token);
  48. var s = _assoc.GetStream();
  49. using var _ = token.Register(() => s.Close());
  50. var requestPasswordAuth = !string.IsNullOrEmpty(_user);
  51. #region Handshake
  52. // we have no GSS-API support
  53. var handShake = requestPasswordAuth ? new byte[] { 5, 2, 0, 2 } : new byte[] { 5, 1, 0 };
  54. await s.WriteAsync(handShake, 0, handShake.Length, token);
  55. // 5 auth(ff=deny)
  56. if (await s.ReadAsync(buf, 0, 2, token) != 2)
  57. {
  58. throw new ProtocolViolationException();
  59. }
  60. if (buf[0] != 5)
  61. {
  62. throw new ProtocolViolationException();
  63. }
  64. #endregion
  65. #region Auth
  66. var auth = buf[1];
  67. switch (auth)
  68. {
  69. case 0:
  70. break;
  71. case 2:
  72. var uByte = Encoding.UTF8.GetBytes(_user ?? string.Empty);
  73. var pByte = Encoding.UTF8.GetBytes(_password ?? string.Empty);
  74. buf[0] = 1;
  75. buf[1] = (byte)uByte.Length;
  76. Array.Copy(uByte, 0, buf, 2, uByte.Length);
  77. buf[uByte.Length + 2] = (byte)pByte.Length;
  78. Array.Copy(pByte, 0, buf, uByte.Length + 3, pByte.Length);
  79. // 1 userLen user passLen pass
  80. await s.WriteAsync(buf, 0, uByte.Length + pByte.Length + 3, token);
  81. // 1 state(0=ok)
  82. if (await s.ReadAsync(buf, 0, 2, token) != 2)
  83. {
  84. throw new ProtocolViolationException();
  85. }
  86. if (buf[0] != 1)
  87. {
  88. throw new ProtocolViolationException();
  89. }
  90. if (buf[1] != 0)
  91. {
  92. throw new UnauthorizedAccessException();
  93. }
  94. break;
  95. case 0xff:
  96. throw new UnauthorizedAccessException();
  97. default:
  98. throw new ProtocolViolationException();
  99. }
  100. #endregion
  101. #region UDP Assoc Send
  102. buf[0] = 5;
  103. buf[1] = 3;
  104. buf[2] = 0;
  105. var abyte = GetEndPointByte(new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort));
  106. var addrLen = abyte.Length;
  107. Array.Copy(abyte, 0, buf, 3, addrLen);
  108. // 5 cmd(3=udpassoc) 0 atyp(1=v4 3=dns 4=v5) addr port
  109. await s.WriteAsync(buf, 0, addrLen + 3, token);
  110. #endregion
  111. #region UDP Assoc Response
  112. if (await s.ReadAsync(buf, 0, 4, token) != 4)
  113. {
  114. throw new ProtocolViolationException();
  115. }
  116. if (buf[0] != 5 || buf[2] != 0)
  117. {
  118. throw new ProtocolViolationException();
  119. }
  120. if (buf[1] != 0)
  121. {
  122. throw new UnauthorizedAccessException();
  123. }
  124. addrLen = GetAddressLength(buf[3]);
  125. var addr = new byte[addrLen];
  126. if (await s.ReadAsync(addr, 0, addrLen, token) != addrLen)
  127. {
  128. throw new ProtocolViolationException();
  129. }
  130. var assocIP = new IPAddress(addr);
  131. if (await s.ReadAsync(buf, 0, 2, token) != 2)
  132. {
  133. throw new ProtocolViolationException();
  134. }
  135. var assocPort = buf[0] * 256 + buf[1];
  136. #endregion
  137. _assocEndPoint = new IPEndPoint(assocIP, assocPort);
  138. }
  139. catch (ObjectDisposedException ex) when (ex.ObjectName == typeof(NetworkStream).FullName)
  140. {
  141. await DisconnectAsync();
  142. throw new TimeoutException();
  143. }
  144. catch (Exception e)
  145. {
  146. Debug.WriteLine(e);
  147. await DisconnectAsync();
  148. throw;
  149. }
  150. }
  151. public async Task<(byte[], IPEndPoint, IPAddress)> ReceiveAsync(byte[] bytes, IPEndPoint remote, EndPoint receive, CancellationToken token = default)
  152. {
  153. var state = _assoc.GetState();
  154. if (state != TcpState.Established)
  155. {
  156. throw new InvalidOperationException(@"No UDP association, maybe already disconnected or not connected");
  157. }
  158. var remoteBytes = GetEndPointByte(remote);
  159. var proxyBytes = new byte[bytes.Length + remoteBytes.Length + 3];
  160. Array.Copy(remoteBytes, 0, proxyBytes, 3, remoteBytes.Length);
  161. Array.Copy(bytes, 0, proxyBytes, remoteBytes.Length + 3, bytes.Length);
  162. await _udpClient.SendAsync(proxyBytes, proxyBytes.Length, _assocEndPoint);
  163. var res = new byte[ushort.MaxValue];
  164. var (local, length, _) = await _udpClient.Client.ReceiveMessageFromAsync(_assocEndPoint!, res, SocketFlags.None);
  165. if (res[0] != 0 || res[1] != 0 || res[2] != 0)
  166. {
  167. throw new Exception();
  168. }
  169. var addressLen = GetAddressLength(res[3]);
  170. var ipByte = new byte[addressLen];
  171. Array.Copy(res, 4, ipByte, 0, addressLen);
  172. var ip = new IPAddress(ipByte);
  173. var port = res[addressLen + 4] * 256 + res[addressLen + 5];
  174. var ret = new byte[length - addressLen - 6];
  175. Array.Copy(res, addressLen + 6, ret, 0, length - addressLen - 6);
  176. return (
  177. ret,
  178. new IPEndPoint(ip, port),
  179. local);
  180. }
  181. public Task DisconnectAsync()
  182. {
  183. try
  184. {
  185. _assoc.Close();
  186. }
  187. catch
  188. {
  189. // ignored
  190. }
  191. return Task.CompletedTask;
  192. }
  193. private static byte[] GetEndPointByte(IPEndPoint ep)
  194. {
  195. var ipByte = ep.Address.GetAddressBytes();
  196. var ret = new byte[ipByte.Length + 3];
  197. ret[0] = ipByte.Length == 4 ? (byte)1 : (byte)4;
  198. Array.Copy(ipByte, 0, ret, 1, ipByte.Length);
  199. ret[ipByte.Length + 1] = (byte)(ep.Port / 256);
  200. ret[ipByte.Length + 2] = (byte)(ep.Port % 256);
  201. return ret;
  202. }
  203. private static int GetAddressLength(byte b)
  204. {
  205. return b switch
  206. {
  207. 1 => 4,
  208. 4 => 16,
  209. _ => throw new NotSupportedException()
  210. };
  211. }
  212. public void Dispose()
  213. {
  214. _udpClient.Dispose();
  215. _assoc.Dispose();
  216. }
  217. }
  218. }