StunClient3489.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. using Microsoft;
  2. using STUN.Enums;
  3. using STUN.Messages;
  4. using STUN.Proxy;
  5. using STUN.StunResult;
  6. using STUN.Utils;
  7. using System;
  8. using System.Buffers;
  9. using System.Diagnostics;
  10. using System.Net;
  11. using System.Net.Sockets;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. namespace STUN.Client;
  15. /// <summary>
  16. /// https://tools.ietf.org/html/rfc3489#section-10.1
  17. /// https://upload.wikimedia.org/wikipedia/commons/6/63/STUN_Algorithm3.svg
  18. /// </summary>
  19. public class StunClient3489 : IStunClient
  20. {
  21. public virtual IPEndPoint LocalEndPoint => (IPEndPoint)_proxy.Client.LocalEndPoint!;
  22. public TimeSpan ReceiveTimeout { get; set; } = TimeSpan.FromSeconds(3);
  23. private readonly IPEndPoint _remoteEndPoint;
  24. private readonly IUdpProxy _proxy;
  25. public ClassicStunResult State { get; } = new();
  26. public StunClient3489(IPEndPoint server, IPEndPoint local, IUdpProxy? proxy = null)
  27. {
  28. Requires.NotNull(server, nameof(server));
  29. Requires.NotNull(local, nameof(local));
  30. _proxy = proxy ?? new NoneUdpProxy(local);
  31. _remoteEndPoint = server;
  32. State.LocalEndPoint = local;
  33. }
  34. public async ValueTask ConnectProxyAsync(CancellationToken cancellationToken = default)
  35. {
  36. using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  37. cts.CancelAfter(ReceiveTimeout);
  38. await _proxy.ConnectAsync(cts.Token);
  39. }
  40. public async ValueTask CloseProxyAsync(CancellationToken cancellationToken = default)
  41. {
  42. await _proxy.CloseAsync(cancellationToken);
  43. }
  44. public async ValueTask QueryAsync(CancellationToken cancellationToken = default)
  45. {
  46. State.Reset();
  47. // test I
  48. var response1 = await Test1Async(cancellationToken);
  49. if (response1 is null)
  50. {
  51. State.NatType = NatType.UdpBlocked;
  52. return;
  53. }
  54. State.LocalEndPoint = new IPEndPoint(response1.LocalAddress, LocalEndPoint.Port);
  55. var mappedAddress1 = response1.Message.GetMappedAddressAttribute();
  56. var changedAddress = response1.Message.GetChangedAddressAttribute();
  57. State.PublicEndPoint = mappedAddress1; // 显示 test I 得到的映射地址
  58. // 某些单 IP 服务器的迷惑操作
  59. if (mappedAddress1 is null || changedAddress is null
  60. || Equals(changedAddress.Address, response1.Remote.Address)
  61. || changedAddress.Port == response1.Remote.Port)
  62. {
  63. State.NatType = NatType.UnsupportedServer;
  64. return;
  65. }
  66. // test II
  67. var response2 = await Test2Async(changedAddress, cancellationToken);
  68. var mappedAddress2 = response2?.Message.GetMappedAddressAttribute();
  69. if (response2 is not null)
  70. {
  71. // 有些单 IP 服务器并不能测 NAT 类型
  72. if (Equals(response1.Remote.Address, response2.Remote.Address) || response1.Remote.Port == response2.Remote.Port)
  73. {
  74. State.NatType = NatType.UnsupportedServer;
  75. State.PublicEndPoint = mappedAddress2;
  76. return;
  77. }
  78. }
  79. // is Public IP == link's IP?
  80. if (Equals(mappedAddress1.Address, response1.LocalAddress) && mappedAddress1.Port == LocalEndPoint.Port)
  81. {
  82. // No NAT
  83. if (response2 is null)
  84. {
  85. State.NatType = NatType.SymmetricUdpFirewall;
  86. State.PublicEndPoint = mappedAddress1;
  87. }
  88. else
  89. {
  90. State.NatType = NatType.OpenInternet;
  91. State.PublicEndPoint = mappedAddress2;
  92. }
  93. return;
  94. }
  95. // NAT
  96. if (response2 is not null)
  97. {
  98. State.NatType = NatType.FullCone;
  99. State.PublicEndPoint = mappedAddress2;
  100. return;
  101. }
  102. // Test I(#2)
  103. var response12 = await Test1_2Async(changedAddress, cancellationToken);
  104. var mappedAddress12 = response12?.Message.GetMappedAddressAttribute();
  105. if (mappedAddress12 is null)
  106. {
  107. State.NatType = NatType.Unknown;
  108. return;
  109. }
  110. if (!Equals(mappedAddress12, mappedAddress1))
  111. {
  112. State.NatType = NatType.Symmetric;
  113. State.PublicEndPoint = mappedAddress12;
  114. return;
  115. }
  116. // Test III
  117. var response3 = await Test3Async(cancellationToken);
  118. if (response3 is not null)
  119. {
  120. var mappedAddress3 = response3.Message.GetMappedAddressAttribute();
  121. if (mappedAddress3 is not null
  122. && Equals(response3.Remote.Address, response1.Remote.Address)
  123. && response3.Remote.Port != response1.Remote.Port)
  124. {
  125. State.NatType = NatType.RestrictedCone;
  126. State.PublicEndPoint = mappedAddress3;
  127. return;
  128. }
  129. }
  130. State.NatType = NatType.PortRestrictedCone;
  131. State.PublicEndPoint = mappedAddress12;
  132. }
  133. private async ValueTask<StunResponse?> RequestAsync(StunMessage5389 sendMessage, IPEndPoint remote, IPEndPoint receive, CancellationToken cancellationToken)
  134. {
  135. try
  136. {
  137. using var memoryOwner = MemoryPool<byte>.Shared.Rent(0x10000);
  138. var buffer = memoryOwner.Memory;
  139. var length = sendMessage.WriteTo(buffer.Span);
  140. await _proxy.SendToAsync(buffer[..length], SocketFlags.None, remote, cancellationToken);
  141. using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  142. cts.CancelAfter(ReceiveTimeout);
  143. var r = await _proxy.ReceiveMessageFromAsync(buffer, SocketFlags.None, receive, cts.Token);
  144. var message = new StunMessage5389();
  145. if (message.TryParse(buffer.Span[..r.ReceivedBytes]) && message.IsSameTransaction(sendMessage))
  146. {
  147. return new StunResponse(message, (IPEndPoint)r.RemoteEndPoint, r.PacketInformation.Address);
  148. }
  149. }
  150. catch (Exception ex)
  151. {
  152. Debug.WriteLine(ex);
  153. }
  154. return default;
  155. }
  156. public virtual async ValueTask<StunResponse?> Test1Async(CancellationToken cancellationToken)
  157. {
  158. var message = new StunMessage5389
  159. {
  160. StunMessageType = StunMessageType.BindingRequest,
  161. MagicCookie = 0
  162. };
  163. return await RequestAsync(message, _remoteEndPoint, _remoteEndPoint, cancellationToken);
  164. }
  165. public virtual async ValueTask<StunResponse?> Test2Async(IPEndPoint other, CancellationToken cancellationToken)
  166. {
  167. var message = new StunMessage5389
  168. {
  169. StunMessageType = StunMessageType.BindingRequest,
  170. MagicCookie = 0,
  171. Attributes = new[] { AttributeExtensions.BuildChangeRequest(true, true) }
  172. };
  173. return await RequestAsync(message, _remoteEndPoint, other, cancellationToken);
  174. }
  175. public virtual async ValueTask<StunResponse?> Test1_2Async(IPEndPoint other, CancellationToken cancellationToken)
  176. {
  177. var message = new StunMessage5389
  178. {
  179. StunMessageType = StunMessageType.BindingRequest,
  180. MagicCookie = 0
  181. };
  182. return await RequestAsync(message, other, other, cancellationToken);
  183. }
  184. public virtual async ValueTask<StunResponse?> Test3Async(CancellationToken cancellationToken)
  185. {
  186. var message = new StunMessage5389
  187. {
  188. StunMessageType = StunMessageType.BindingRequest,
  189. MagicCookie = 0,
  190. Attributes = new[] { AttributeExtensions.BuildChangeRequest(false, true) }
  191. };
  192. return await RequestAsync(message, _remoteEndPoint, _remoteEndPoint, cancellationToken);
  193. }
  194. public void Dispose()
  195. {
  196. _proxy.Dispose();
  197. }
  198. }