Socks5UdpProxy.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. using Microsoft;
  2. using Socks5.Clients;
  3. using Socks5.Enums;
  4. using Socks5.Models;
  5. using Socks5.Utils;
  6. using STUN.Utils;
  7. using System;
  8. using System.Buffers;
  9. using System.IO;
  10. using System.Net;
  11. using System.Net.Sockets;
  12. using System.Runtime.CompilerServices;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. namespace STUN.Proxy
  16. {
  17. public class Socks5UdpProxy : IUdpProxy
  18. {
  19. public Socket Client
  20. {
  21. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  22. get
  23. {
  24. Verify.Operation(_socks5Client?.UdpClient is not null, @"Socks5 is not established.");
  25. return _socks5Client.UdpClient;
  26. }
  27. }
  28. private readonly Socks5CreateOption _socks5Options;
  29. private readonly IPEndPoint _localEndPoint;
  30. private Socks5Client? _socks5Client;
  31. private ServerBound _udpServerBound;
  32. public Socks5UdpProxy(IPEndPoint localEndPoint, Socks5CreateOption socks5Options)
  33. {
  34. _socks5Options = socks5Options;
  35. Requires.NotNull(localEndPoint, nameof(localEndPoint));
  36. Requires.NotNull(socks5Options, nameof(socks5Options));
  37. Requires.Argument(socks5Options.Address is not null, nameof(socks5Options), @"SOCKS5 address is null");
  38. _localEndPoint = localEndPoint;
  39. _socks5Options = socks5Options;
  40. }
  41. public async ValueTask ConnectAsync(CancellationToken cancellationToken = default)
  42. {
  43. Verify.Operation(_socks5Client?.Status is not Status.Established, @"SOCKS5 client has been connected");
  44. _socks5Client?.Dispose();
  45. _socks5Client = new Socks5Client(_socks5Options);
  46. _udpServerBound = await _socks5Client.UdpAssociateAsync(_localEndPoint.Address, (ushort)_localEndPoint.Port, cancellationToken);
  47. }
  48. public ValueTask CloseAsync(CancellationToken cancellationToken = default)
  49. {
  50. if (_socks5Client is not null)
  51. {
  52. _socks5Client.Dispose();
  53. _socks5Client = null;
  54. }
  55. return default;
  56. }
  57. public async ValueTask<SocketReceiveMessageFromResult> ReceiveMessageFromAsync(Memory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default)
  58. {
  59. Verify.Operation(_socks5Client?.Status is Status.Established && _socks5Client.UdpClient is not null, @"Socks5 is not established.");
  60. var t = ArrayPool<byte>.Shared.Rent(buffer.Length);
  61. try
  62. {
  63. if (_udpServerBound.Type is AddressType.Domain)
  64. {
  65. ThrowErrorAddressType();
  66. }
  67. var remote = new IPEndPoint(_udpServerBound.Address!, _udpServerBound.Port);
  68. var r = await _socks5Client.UdpClient.ReceiveMessageFromAsync(t, socketFlags, remote, cancellationToken);
  69. var u = Unpack.Udp(t.AsMemory(0, r.ReceivedBytes));
  70. u.Data.CopyTo(buffer);
  71. if (u.Type is AddressType.Domain)
  72. {
  73. ThrowErrorAddressType();
  74. }
  75. return new SocketReceiveMessageFromResult
  76. {
  77. ReceivedBytes = u.Data.Length,
  78. SocketFlags = r.SocketFlags,
  79. RemoteEndPoint = new IPEndPoint(u.Address!, u.Port),
  80. PacketInformation = r.PacketInformation
  81. };
  82. }
  83. finally
  84. {
  85. ArrayPool<byte>.Shared.Return(t);
  86. }
  87. static void ThrowErrorAddressType()
  88. {
  89. throw new InvalidDataException(@"Received error AddressType");
  90. }
  91. }
  92. public async ValueTask<int> SendToAsync(ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = default)
  93. {
  94. Verify.Operation(_socks5Client is not null, @"SOCKS5 client is not connected");
  95. if (remoteEP is not IPEndPoint remote)
  96. {
  97. ThrowNotSupportedException();
  98. }
  99. return await _socks5Client.SendUdpAsync(buffer, remote.Address, (ushort)remote.Port, cancellationToken);
  100. static void ThrowNotSupportedException()
  101. {
  102. throw new NotSupportedException();
  103. }
  104. }
  105. public void Dispose()
  106. {
  107. _socks5Client?.Dispose();
  108. GC.SuppressFinalize(this);
  109. }
  110. }
  111. }