Socks5UdpProxy.cs 7.9 KB

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