StunClient5389UDP.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using STUN.Enums;
  2. using STUN.Messages;
  3. using STUN.Proxy;
  4. using STUN.StunResult;
  5. using STUN.Utils;
  6. using System;
  7. using System.Net;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace STUN.Client
  11. {
  12. /// <summary>
  13. /// https://tools.ietf.org/html/rfc5389#section-7.2.1
  14. /// https://tools.ietf.org/html/rfc5780#section-4.2
  15. /// </summary>
  16. public class StunClient5389UDP : StunClient3489
  17. {
  18. public new StunResult5389 Status { get; } = new();
  19. public StunClient5389UDP(IPAddress server, ushort port = 3478, IPEndPoint? local = null, IUdpProxy? proxy = null)
  20. : base(server, port, local, proxy)
  21. {
  22. Timeout = TimeSpan.FromSeconds(3);
  23. Status.LocalEndPoint = local;
  24. }
  25. public async Task QueryAsync()
  26. {
  27. try
  28. {
  29. Status.Reset();
  30. using var cts = new CancellationTokenSource(Timeout);
  31. await Proxy.ConnectAsync(cts.Token);
  32. await FilteringBehaviorTestBaseAsync(cts.Token);
  33. if (Status.BindingTestResult != BindingTestResult.Success
  34. || Status.FilteringBehavior == FilteringBehavior.UnsupportedServer
  35. )
  36. {
  37. return;
  38. }
  39. if (Equals(Status.PublicEndPoint, Status.LocalEndPoint))
  40. {
  41. Status.MappingBehavior = MappingBehavior.Direct;
  42. return;
  43. }
  44. // MappingBehaviorTest test II
  45. var (success2, result2) = await MappingBehaviorTestBase2Async(cts.Token);
  46. if (!success2)
  47. {
  48. return;
  49. }
  50. // MappingBehaviorTest test III
  51. await MappingBehaviorTestBase3Async(result2, cts.Token);
  52. }
  53. finally
  54. {
  55. await Proxy.DisconnectAsync();
  56. }
  57. }
  58. public async Task BindingTestAsync()
  59. {
  60. try
  61. {
  62. Status.Reset();
  63. using var cts = new CancellationTokenSource(Timeout);
  64. await Proxy.ConnectAsync(cts.Token);
  65. await BindingTestInternalAsync(cts.Token);
  66. }
  67. finally
  68. {
  69. await Proxy.DisconnectAsync();
  70. }
  71. }
  72. private async Task BindingTestInternalAsync(CancellationToken token)
  73. {
  74. Status.Clone(await BindingTestBaseAsync(RemoteEndPoint, token));
  75. }
  76. private async Task<StunResult5389> BindingTestBaseAsync(IPEndPoint remote, CancellationToken token)
  77. {
  78. var result = new StunResult5389();
  79. var test = new StunMessage5389 { StunMessageType = StunMessageType.BindingRequest };
  80. var (response1, _, local1) = await TestAsync(test, remote, remote, token);
  81. var mappedAddress1 = response1.GetXorMappedAddressAttribute();
  82. var otherAddress = response1.GetOtherAddressAttribute();
  83. var local = local1 is null ? null : new IPEndPoint(local1, LocalEndPoint.Port);
  84. if (response1 is null)
  85. {
  86. result.BindingTestResult = BindingTestResult.Fail;
  87. }
  88. else if (mappedAddress1 is null)
  89. {
  90. result.BindingTestResult = BindingTestResult.UnsupportedServer;
  91. }
  92. else
  93. {
  94. result.BindingTestResult = BindingTestResult.Success;
  95. }
  96. result.LocalEndPoint = local;
  97. result.PublicEndPoint = mappedAddress1;
  98. result.OtherEndPoint = otherAddress;
  99. return result;
  100. }
  101. public async Task MappingBehaviorTestAsync()
  102. {
  103. try
  104. {
  105. Status.Reset();
  106. using var cts = new CancellationTokenSource(Timeout);
  107. await Proxy.ConnectAsync(cts.Token);
  108. // test I
  109. await BindingTestInternalAsync(cts.Token);
  110. if (Status.BindingTestResult != BindingTestResult.Success)
  111. {
  112. return;
  113. }
  114. if (Status.OtherEndPoint is null
  115. || Equals(Status.OtherEndPoint.Address, RemoteEndPoint.Address)
  116. || Status.OtherEndPoint.Port == RemoteEndPoint.Port)
  117. {
  118. Status.MappingBehavior = MappingBehavior.UnsupportedServer;
  119. return;
  120. }
  121. if (Equals(Status.PublicEndPoint, Status.LocalEndPoint))
  122. {
  123. Status.MappingBehavior = MappingBehavior.Direct;
  124. return;
  125. }
  126. // test II
  127. var (success2, result2) = await MappingBehaviorTestBase2Async(cts.Token);
  128. if (!success2)
  129. {
  130. return;
  131. }
  132. // test III
  133. await MappingBehaviorTestBase3Async(result2, cts.Token);
  134. }
  135. finally
  136. {
  137. await Proxy.DisconnectAsync();
  138. }
  139. }
  140. private async Task<(bool, StunResult5389)> MappingBehaviorTestBase2Async(CancellationToken token)
  141. {
  142. var result2 = await BindingTestBaseAsync(new IPEndPoint(Status.OtherEndPoint!.Address, RemoteEndPoint.Port), token);
  143. if (result2.BindingTestResult != BindingTestResult.Success)
  144. {
  145. Status.MappingBehavior = MappingBehavior.Fail;
  146. return (false, result2);
  147. }
  148. if (Equals(result2.PublicEndPoint, Status.PublicEndPoint))
  149. {
  150. Status.MappingBehavior = MappingBehavior.EndpointIndependent;
  151. return (false, result2);
  152. }
  153. return (true, result2);
  154. }
  155. private async Task MappingBehaviorTestBase3Async(StunResult5389 result2, CancellationToken token)
  156. {
  157. var result3 = await BindingTestBaseAsync(Status.OtherEndPoint!, token);
  158. if (result3.BindingTestResult != BindingTestResult.Success)
  159. {
  160. Status.MappingBehavior = MappingBehavior.Fail;
  161. return;
  162. }
  163. Status.MappingBehavior = Equals(result3.PublicEndPoint, result2.PublicEndPoint) ? MappingBehavior.AddressDependent : MappingBehavior.AddressAndPortDependent;
  164. }
  165. private async Task FilteringBehaviorTestBaseAsync(CancellationToken token)
  166. {
  167. // test I
  168. await BindingTestInternalAsync(token);
  169. if (Status.BindingTestResult != BindingTestResult.Success)
  170. {
  171. return;
  172. }
  173. if (Status.OtherEndPoint is null
  174. || Equals(Status.OtherEndPoint.Address, RemoteEndPoint.Address)
  175. || Status.OtherEndPoint.Port == RemoteEndPoint.Port)
  176. {
  177. Status.FilteringBehavior = FilteringBehavior.UnsupportedServer;
  178. return;
  179. }
  180. // test II
  181. var test2 = new StunMessage5389
  182. {
  183. StunMessageType = StunMessageType.BindingRequest,
  184. Attributes = new[] { AttributeExtensions.BuildChangeRequest(true, true) }
  185. };
  186. var (response2, _, _) = await TestAsync(test2, RemoteEndPoint, Status.OtherEndPoint, token);
  187. if (response2 is not null)
  188. {
  189. Status.FilteringBehavior = FilteringBehavior.EndpointIndependent;
  190. return;
  191. }
  192. // test III
  193. var test3 = new StunMessage5389
  194. {
  195. StunMessageType = StunMessageType.BindingRequest,
  196. Attributes = new[] { AttributeExtensions.BuildChangeRequest(false, true) }
  197. };
  198. var (response3, remote3, _) = await TestAsync(test3, RemoteEndPoint, RemoteEndPoint, token);
  199. if (response3 is null || remote3 is null)
  200. {
  201. Status.FilteringBehavior = FilteringBehavior.AddressAndPortDependent;
  202. return;
  203. }
  204. if (Equals(remote3.Address, RemoteEndPoint.Address) && remote3.Port != RemoteEndPoint.Port)
  205. {
  206. Status.FilteringBehavior = FilteringBehavior.AddressAndPortDependent;
  207. }
  208. else
  209. {
  210. Status.FilteringBehavior = FilteringBehavior.UnsupportedServer;
  211. }
  212. }
  213. public async Task FilteringBehaviorTestAsync()
  214. {
  215. try
  216. {
  217. Status.Reset();
  218. using var cts = new CancellationTokenSource(Timeout);
  219. await Proxy.ConnectAsync(cts.Token);
  220. await FilteringBehaviorTestBaseAsync(cts.Token);
  221. }
  222. finally
  223. {
  224. await Proxy.DisconnectAsync();
  225. }
  226. }
  227. }
  228. }