RFC5780ViewModel.cs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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 RFC5780ViewModel : ViewModelBase, IRoutableViewModel
  19. {
  20. public string UrlPathSegment => @"RFC5780";
  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. public StunResult5389 Result5389 { get; set; }
  27. public ReactiveCommand<Unit, Unit> DiscoveryNatType { get; }
  28. public RFC5780ViewModel()
  29. {
  30. Result5389 = new StunResult5389();
  31. DiscoveryNatType = ReactiveCommand.CreateFromTask(DiscoveryNatTypeAsync);
  32. }
  33. private async Task DiscoveryNatTypeAsync(CancellationToken token)
  34. {
  35. Verify.Operation(StunServer.TryParse(Config.StunServer, out StunServer? server), @"Wrong STUN Server!");
  36. if (!HostnameEndpoint.TryParse(Config.ProxyServer, out HostnameEndpoint? proxyIpe))
  37. {
  38. throw new NotSupportedException(@"Unknown proxy address");
  39. }
  40. Socks5CreateOption socks5Option = new()
  41. {
  42. Address = await DnsClient.QueryAsync(proxyIpe.Hostname, token),
  43. Port = proxyIpe.Port,
  44. UsernamePassword = new UsernamePassword
  45. {
  46. UserName = Config.ProxyUser,
  47. Password = Config.ProxyPassword
  48. }
  49. };
  50. IPAddress? serverIp;
  51. if (Result5389.LocalEndPoint is null)
  52. {
  53. serverIp = await DnsClient.QueryAsync(server.Hostname, token);
  54. Result5389.LocalEndPoint = serverIp.AddressFamily is AddressFamily.InterNetworkV6 ? new IPEndPoint(IPAddress.IPv6Any, IPEndPoint.MinPort) : new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
  55. }
  56. else
  57. {
  58. if (Result5389.LocalEndPoint.AddressFamily is AddressFamily.InterNetworkV6)
  59. {
  60. serverIp = await AAAADnsClient.QueryAsync(server.Hostname, token);
  61. }
  62. else
  63. {
  64. serverIp = await ADnsClient.QueryAsync(server.Hostname, token);
  65. }
  66. }
  67. using IUdpProxy proxy = ProxyFactory.CreateProxy(Config.ProxyType, Result5389.LocalEndPoint, socks5Option);
  68. using StunClient5389UDP client = new(new IPEndPoint(serverIp, server.Port), Result5389.LocalEndPoint, proxy);
  69. Result5389 = client.State;
  70. using (Observable.Interval(TimeSpan.FromSeconds(0.1))
  71. .ObserveOn(RxApp.MainThreadScheduler)
  72. .Subscribe(_ => this.RaisePropertyChanged(nameof(Result5389))))
  73. {
  74. await client.ConnectProxyAsync(token);
  75. try
  76. {
  77. await client.QueryAsync(token);
  78. }
  79. finally
  80. {
  81. await client.CloseProxyAsync(token);
  82. }
  83. }
  84. Result5389 = new StunResult5389();
  85. Result5389.Clone(client.State);
  86. this.RaisePropertyChanged(nameof(Result5389));
  87. }
  88. }