Socks5UdpProxy.cs 3.5 KB

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