RFC5780ViewModel.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. using Dns.Net.Abstractions;
  2. using Dns.Net.Clients;
  3. using JetBrains.Annotations;
  4. using Microsoft;
  5. using NatTypeTester.Models;
  6. using ReactiveUI;
  7. using Socks5.Models;
  8. using STUN;
  9. using STUN.Client;
  10. using STUN.Enums;
  11. using STUN.Proxy;
  12. using STUN.StunResult;
  13. using System.Net;
  14. using System.Net.Sockets;
  15. using System.Reactive;
  16. using System.Reactive.Linq;
  17. namespace NatTypeTester.ViewModels;
  18. [UsedImplicitly]
  19. public class RFC5780ViewModel : ViewModelBase, IRoutableViewModel
  20. {
  21. public string UrlPathSegment => @"RFC5780";
  22. public IScreen HostScreen => LazyServiceProvider.LazyGetRequiredService<IScreen>();
  23. private Config Config => LazyServiceProvider.LazyGetRequiredService<Config>();
  24. private IDnsClient DnsClient => LazyServiceProvider.LazyGetRequiredService<IDnsClient>();
  25. private IDnsClient AAAADnsClient => LazyServiceProvider.LazyGetRequiredService<DefaultAAAAClient>();
  26. private IDnsClient ADnsClient => LazyServiceProvider.LazyGetRequiredService<DefaultAClient>();
  27. private StunResult5389 _result5389;
  28. public StunResult5389 Result5389
  29. {
  30. get => _result5389;
  31. set => this.RaiseAndSetIfChanged(ref _result5389, value);
  32. }
  33. private StunResult5389 _udpResult;
  34. private StunResult5389 _tcpResult;
  35. private StunResult5389 _tlsResult;
  36. private TransportType _transportType;
  37. public TransportType TransportType
  38. {
  39. get => _transportType;
  40. set => this.RaiseAndSetIfChanged(ref _transportType, value);
  41. }
  42. public ReactiveCommand<Unit, Unit> DiscoveryNatType { get; }
  43. public RFC5780ViewModel()
  44. {
  45. _udpResult = new StunResult5389();
  46. _tcpResult = new StunResult5389();
  47. _tlsResult = new StunResult5389();
  48. _result5389 = _udpResult;
  49. DiscoveryNatType = ReactiveCommand.CreateFromTask(DiscoveryNatTypeAsync);
  50. }
  51. private async Task DiscoveryNatTypeAsync(CancellationToken token)
  52. {
  53. Verify.Operation(StunServer.TryParse(Config.StunServer, out StunServer? server, TransportType is TransportType.Tls ? StunServer.DefaultTlsPort : StunServer.DefaultPort), @"Wrong STUN Server!");
  54. if (!HostnameEndpoint.TryParse(Config.ProxyServer, out HostnameEndpoint? proxyIpe))
  55. {
  56. throw new NotSupportedException(@"Unknown proxy address");
  57. }
  58. Socks5CreateOption socks5Option = new()
  59. {
  60. Address = await DnsClient.QueryAsync(proxyIpe.Hostname, token),
  61. Port = proxyIpe.Port,
  62. UsernamePassword = new UsernamePassword
  63. {
  64. UserName = Config.ProxyUser,
  65. Password = Config.ProxyPassword
  66. }
  67. };
  68. IPAddress? serverIp;
  69. if (Result5389.LocalEndPoint is null)
  70. {
  71. serverIp = await DnsClient.QueryAsync(server.Hostname, token);
  72. Result5389.LocalEndPoint = serverIp.AddressFamily is AddressFamily.InterNetworkV6 ? new IPEndPoint(IPAddress.IPv6Any, IPEndPoint.MinPort) : new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
  73. }
  74. else
  75. {
  76. if (Result5389.LocalEndPoint.AddressFamily is AddressFamily.InterNetworkV6)
  77. {
  78. serverIp = await AAAADnsClient.QueryAsync(server.Hostname, token);
  79. }
  80. else
  81. {
  82. serverIp = await ADnsClient.QueryAsync(server.Hostname, token);
  83. }
  84. }
  85. TransportType transport = TransportType;
  86. if (transport is TransportType.Udp)
  87. {
  88. using IUdpProxy proxy = ProxyFactory.CreateProxy(Config.ProxyType, Result5389.LocalEndPoint, socks5Option);
  89. using StunClient5389UDP client = new(new IPEndPoint(serverIp, server.Port), Result5389.LocalEndPoint, proxy);
  90. try
  91. {
  92. using (Observable.Interval(TimeSpan.FromSeconds(0.1))
  93. .ObserveOn(RxApp.MainThreadScheduler)
  94. // ReSharper disable once AccessToDisposedClosure
  95. .Subscribe(_ => Result5389 = _udpResult = client.State with { }))
  96. {
  97. await client.ConnectProxyAsync(token);
  98. try
  99. {
  100. await client.QueryAsync(token);
  101. }
  102. finally
  103. {
  104. await client.CloseProxyAsync(token);
  105. }
  106. }
  107. }
  108. finally
  109. {
  110. Result5389 = _udpResult = client.State with { };
  111. }
  112. }
  113. else
  114. {
  115. using ITcpProxy proxy = ProxyFactory.CreateProxy(transport, Config.ProxyType, socks5Option, server.Hostname);
  116. using IStunClient5389 client = new StunClient5389TCP(new IPEndPoint(serverIp, server.Port), Result5389.LocalEndPoint, proxy);
  117. try
  118. {
  119. using (Observable.Interval(TimeSpan.FromSeconds(0.1))
  120. .ObserveOn(RxApp.MainThreadScheduler)
  121. .Subscribe(_ => UpdateData()))
  122. {
  123. await client.QueryAsync(token);
  124. }
  125. }
  126. finally
  127. {
  128. UpdateData();
  129. }
  130. void UpdateData()
  131. {
  132. // ReSharper disable once AccessToDisposedClosure
  133. Result5389 = client.State with { };
  134. if (transport is TransportType.Tcp)
  135. {
  136. _tcpResult = Result5389;
  137. }
  138. else
  139. {
  140. _tlsResult = Result5389;
  141. }
  142. }
  143. }
  144. }
  145. public void ResetResult()
  146. {
  147. Result5389 = TransportType switch
  148. {
  149. TransportType.Tcp => _tcpResult,
  150. TransportType.Tls => _tlsResult,
  151. _ => _udpResult
  152. };
  153. }
  154. }