You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

UDPRelay.cs 7.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. using Shadowsocks.Models;
  2. using Shadowsocks.Net.Crypto;
  3. using Splat;
  4. using System;
  5. using System.Buffers;
  6. using System.Collections.Generic;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Runtime.CompilerServices;
  10. using System.Runtime.InteropServices;
  11. using System.Threading.Tasks;
  12. namespace Shadowsocks.Net
  13. {
  14. public class UDPRelay : DatagramService
  15. {
  16. Server _server;
  17. // TODO: choose a smart number
  18. private LRUCache<IPEndPoint, UDPHandler> _cache = new LRUCache<IPEndPoint, UDPHandler>(512);
  19. public long outbound = 0;
  20. public long inbound = 0;
  21. public UDPRelay(Server server)
  22. {
  23. _server = server;
  24. }
  25. public override async Task<bool> Handle(Memory<byte> packet, Socket socket, EndPoint client)
  26. {
  27. if (socket.ProtocolType != ProtocolType.Udp)
  28. {
  29. return false;
  30. }
  31. if (packet.Length < 4)
  32. {
  33. return false;
  34. }
  35. IPEndPoint remoteEndPoint = (IPEndPoint)client;
  36. UDPHandler handler = _cache.get(remoteEndPoint);
  37. if (handler == null)
  38. {
  39. handler = new UDPHandler(socket, _server, remoteEndPoint);
  40. handler.Receive();
  41. _cache.add(remoteEndPoint, handler);
  42. }
  43. await handler.SendAsync(packet);
  44. return true;
  45. }
  46. public class UDPHandler : IEnableLogger
  47. {
  48. private static MemoryPool<byte> pool = MemoryPool<byte>.Shared;
  49. private Socket _local;
  50. private Socket _remote;
  51. private Server _server;
  52. private byte[] _buffer = new byte[65536];
  53. private IPEndPoint _localEndPoint;
  54. private IPEndPoint _remoteEndPoint;
  55. private IPAddress ListenAddress
  56. {
  57. get
  58. {
  59. return _remote.AddressFamily switch
  60. {
  61. AddressFamily.InterNetwork => IPAddress.Any,
  62. AddressFamily.InterNetworkV6 => IPAddress.IPv6Any,
  63. _ => throw new NotSupportedException(),
  64. };
  65. }
  66. }
  67. public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint)
  68. {
  69. _local = local;
  70. _server = server;
  71. _localEndPoint = localEndPoint;
  72. // TODO async resolving
  73. bool parsed = IPAddress.TryParse(server.Host, out IPAddress ipAddress);
  74. if (!parsed)
  75. {
  76. IPHostEntry ipHostInfo = Dns.GetHostEntry(server.Host);
  77. ipAddress = ipHostInfo.AddressList[0];
  78. }
  79. _remoteEndPoint = new IPEndPoint(ipAddress, server.Port);
  80. _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
  81. _remote.Bind(new IPEndPoint(ListenAddress, 0));
  82. }
  83. public async Task SendAsync(ReadOnlyMemory<byte> data)
  84. {
  85. using ICrypto encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password);
  86. using IMemoryOwner<byte> mem = pool.Rent(data.Length + 1000);
  87. // byte[] dataOut = new byte[slicedData.Length + 1000];
  88. int outlen = encryptor.EncryptUDP(data.Span[3..], mem.Memory.Span);
  89. this.Log().Debug($"{_localEndPoint} {_remoteEndPoint} {outlen} UDP Relay up");
  90. if (!MemoryMarshal.TryGetArray(mem.Memory[..outlen], out ArraySegment<byte> outData))
  91. {
  92. throw new InvalidOperationException("Can't extract underly array segment");
  93. };
  94. await _remote?.SendToAsync(outData, SocketFlags.None, _remoteEndPoint);
  95. }
  96. public async Task ReceiveAsync()
  97. {
  98. EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0);
  99. this.Log().Debug($"++++++Receive Server Port, size:" + _buffer.Length);
  100. try
  101. {
  102. while (true)
  103. {
  104. var result = await _remote.ReceiveFromAsync(_buffer, SocketFlags.None, remoteEndPoint);
  105. int bytesRead = result.ReceivedBytes;
  106. using IMemoryOwner<byte> owner = pool.Rent(bytesRead + 3);
  107. Memory<byte> o = owner.Memory;
  108. using ICrypto encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password);
  109. int outlen = encryptor.DecryptUDP(o.Span[3..], _buffer.AsSpan(0, bytesRead));
  110. this.Log().Debug($"{_remoteEndPoint} {_localEndPoint} {outlen} UDP Relay down");
  111. if (!MemoryMarshal.TryGetArray(o[..(outlen + 3)], out ArraySegment<byte> data))
  112. {
  113. throw new InvalidOperationException("Can't extract underly array segment");
  114. };
  115. await _local?.SendToAsync(data, SocketFlags.None, _localEndPoint);
  116. }
  117. }
  118. catch (Exception e)
  119. {
  120. this.Log().Warn(e, "");
  121. }
  122. }
  123. public void Receive()
  124. {
  125. _ = ReceiveAsync();
  126. }
  127. public void Close()
  128. {
  129. try
  130. {
  131. _remote?.Close();
  132. }
  133. catch (ObjectDisposedException)
  134. {
  135. // TODO: handle the ObjectDisposedException
  136. }
  137. catch (Exception)
  138. {
  139. // TODO: need more think about handle other Exceptions, or should remove this catch().
  140. }
  141. }
  142. }
  143. }
  144. #region LRU cache
  145. // cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
  146. class LRUCache<K, V> where V : UDPRelay.UDPHandler
  147. {
  148. private int capacity;
  149. private Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>> cacheMap = new Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>>();
  150. private LinkedList<LRUCacheItem<K, V>> lruList = new LinkedList<LRUCacheItem<K, V>>();
  151. public LRUCache(int capacity)
  152. {
  153. this.capacity = capacity;
  154. }
  155. [MethodImpl(MethodImplOptions.Synchronized)]
  156. public V get(K key)
  157. {
  158. LinkedListNode<LRUCacheItem<K, V>> node;
  159. if (cacheMap.TryGetValue(key, out node))
  160. {
  161. V value = node.Value.value;
  162. lruList.Remove(node);
  163. lruList.AddLast(node);
  164. return value;
  165. }
  166. return default(V);
  167. }
  168. [MethodImpl(MethodImplOptions.Synchronized)]
  169. public void add(K key, V val)
  170. {
  171. if (cacheMap.Count >= capacity)
  172. {
  173. RemoveFirst();
  174. }
  175. LRUCacheItem<K, V> cacheItem = new LRUCacheItem<K, V>(key, val);
  176. LinkedListNode<LRUCacheItem<K, V>> node = new LinkedListNode<LRUCacheItem<K, V>>(cacheItem);
  177. lruList.AddLast(node);
  178. cacheMap.Add(key, node);
  179. }
  180. private void RemoveFirst()
  181. {
  182. // Remove from LRUPriority
  183. LinkedListNode<LRUCacheItem<K, V>> node = lruList.First;
  184. lruList.RemoveFirst();
  185. // Remove from cache
  186. cacheMap.Remove(node.Value.key);
  187. node.Value.value.Close();
  188. }
  189. }
  190. class LRUCacheItem<K, V>
  191. {
  192. public LRUCacheItem(K k, V v)
  193. {
  194. key = k;
  195. value = v;
  196. }
  197. public K key;
  198. public V value;
  199. }
  200. #endregion
  201. }