StunClient3489.cs 6.5 KB

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