RFC3489ViewModel.cs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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.Proxy;
  11. using STUN.StunResult;
  12. using System.Net;
  13. using System.Net.Sockets;
  14. using System.Reactive;
  15. using System.Reactive.Linq;
  16. namespace NatTypeTester.ViewModels;
  17. [UsedImplicitly]
  18. public class RFC3489ViewModel : ViewModelBase, IRoutableViewModel
  19. {
  20. public string UrlPathSegment => @"RFC3489";
  21. public IScreen HostScreen => LazyServiceProvider.LazyGetRequiredService<IScreen>();
  22. private Config Config => LazyServiceProvider.LazyGetRequiredService<Config>();
  23. private IDnsClient DnsClient => LazyServiceProvider.LazyGetRequiredService<IDnsClient>();
  24. private IDnsClient AAAADnsClient => LazyServiceProvider.LazyGetRequiredService<DefaultAAAAClient>();
  25. private IDnsClient ADnsClient => LazyServiceProvider.LazyGetRequiredService<DefaultAClient>();
  26. private ClassicStunResult _result3489;
  27. public ClassicStunResult Result3489
  28. {
  29. get => _result3489;
  30. set => this.RaiseAndSetIfChanged(ref _result3489, value);
  31. }
  32. public ReactiveCommand<Unit, Unit> TestClassicNatType { get; }
  33. public RFC3489ViewModel()
  34. {
  35. _result3489 = new ClassicStunResult();
  36. TestClassicNatType = ReactiveCommand.CreateFromTask(TestClassicNatTypeAsync);
  37. }
  38. private async Task TestClassicNatTypeAsync(CancellationToken token)
  39. {
  40. Verify.Operation(StunServer.TryParse(Config.StunServer, out StunServer? server), @"Wrong STUN Server!");
  41. if (!HostnameEndpoint.TryParse(Config.ProxyServer, out HostnameEndpoint? proxyIpe))
  42. {
  43. throw new NotSupportedException(@"Unknown proxy address");
  44. }
  45. Socks5CreateOption socks5Option = new()
  46. {
  47. Address = await DnsClient.QueryAsync(proxyIpe.Hostname, token),
  48. Port = proxyIpe.Port,
  49. UsernamePassword = new UsernamePassword
  50. {
  51. UserName = Config.ProxyUser,
  52. Password = Config.ProxyPassword
  53. }
  54. };
  55. IPAddress? serverIp;
  56. if (Result3489.LocalEndPoint is null)
  57. {
  58. serverIp = await DnsClient.QueryAsync(server.Hostname, token);
  59. Result3489.LocalEndPoint = serverIp.AddressFamily is AddressFamily.InterNetworkV6 ? new IPEndPoint(IPAddress.IPv6Any, IPEndPoint.MinPort) : new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
  60. }
  61. else
  62. {
  63. if (Result3489.LocalEndPoint.AddressFamily is AddressFamily.InterNetworkV6)
  64. {
  65. serverIp = await AAAADnsClient.QueryAsync(server.Hostname, token);
  66. }
  67. else
  68. {
  69. serverIp = await ADnsClient.QueryAsync(server.Hostname, token);
  70. }
  71. }
  72. using IUdpProxy proxy = ProxyFactory.CreateProxy(Config.ProxyType, Result3489.LocalEndPoint, socks5Option);
  73. using StunClient3489 client = new(new IPEndPoint(serverIp, server.Port), Result3489.LocalEndPoint, proxy);
  74. try
  75. {
  76. using (Observable.Interval(TimeSpan.FromSeconds(0.1))
  77. .ObserveOn(RxApp.MainThreadScheduler)
  78. // ReSharper disable once AccessToDisposedClosure
  79. .Subscribe(_ => Result3489 = client.State with { }))
  80. {
  81. await client.ConnectProxyAsync(token);
  82. try
  83. {
  84. await client.QueryAsync(token);
  85. }
  86. finally
  87. {
  88. await client.CloseProxyAsync(token);
  89. }
  90. }
  91. }
  92. finally
  93. {
  94. Result3489 = client.State with { };
  95. }
  96. }
  97. }