MainWindowViewModel.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. using DynamicData;
  2. using DynamicData.Binding;
  3. using ReactiveUI;
  4. using ReactiveUI.Fody.Helpers;
  5. using STUN.Client;
  6. using STUN.Enums;
  7. using STUN.Proxy;
  8. using STUN.StunResult;
  9. using STUN.Utils;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Net;
  15. using System.Reactive;
  16. using System.Reactive.Linq;
  17. using System.Threading;
  18. using System.Threading.Tasks;
  19. namespace NatTypeTester.ViewModels
  20. {
  21. public class MainWindowViewModel : ReactiveObject
  22. {
  23. #region RFC3489
  24. [Reactive]
  25. public ClassicStunResult Result3489 { get; set; }
  26. public ReactiveCommand<Unit, Unit> TestClassicNatType { get; }
  27. #endregion
  28. #region RFC5780
  29. [Reactive]
  30. public StunResult5389 Result5389 { get; set; }
  31. public ReactiveCommand<Unit, Unit> DiscoveryNatType { get; }
  32. #endregion
  33. #region Servers
  34. [Reactive]
  35. public string StunServer { get; set; } = @"stun.syncthing.net";
  36. private readonly IEnumerable<string> _defaultServers = new HashSet<string>
  37. {
  38. @"stun.syncthing.net",
  39. @"stun.qq.com",
  40. @"stun.miwifi.com",
  41. @"stun.bige0.com",
  42. @"stun.stunprotocol.org"
  43. };
  44. private SourceList<string> List { get; } = new();
  45. public readonly IObservableCollection<string> StunServers = new ObservableCollectionExtended<string>();
  46. #endregion
  47. #region Proxy
  48. [Reactive]
  49. public ProxyType ProxyType { get; set; } = ProxyType.Socks5;
  50. [Reactive]
  51. public string ProxyServer { get; set; } = @"127.0.0.1:1080";
  52. [Reactive]
  53. public string? ProxyUser { get; set; }
  54. [Reactive]
  55. public string? ProxyPassword { get; set; }
  56. #endregion
  57. public MainWindowViewModel()
  58. {
  59. Result3489 = new ClassicStunResult
  60. {
  61. LocalEndPoint = new IPEndPoint(IPAddress.Any, 0)
  62. };
  63. Result5389 = new StunResult5389
  64. {
  65. LocalEndPoint = new IPEndPoint(IPAddress.Any, 0)
  66. };
  67. LoadStunServer();
  68. List.Connect()
  69. .DistinctValues(x => x)
  70. .ObserveOnDispatcher()
  71. .Bind(StunServers)
  72. .Subscribe();
  73. TestClassicNatType = ReactiveCommand.CreateFromTask(TestClassicNatTypeImpl);
  74. DiscoveryNatType = ReactiveCommand.CreateFromTask(DiscoveryNatTypeImpl);
  75. }
  76. private async void LoadStunServer()
  77. {
  78. foreach (var server in _defaultServers)
  79. {
  80. List.Add(server);
  81. }
  82. StunServer = _defaultServers.First();
  83. const string path = @"stun.txt";
  84. if (!File.Exists(path))
  85. {
  86. return;
  87. }
  88. using var sw = new StreamReader(path);
  89. string line;
  90. var stun = new StunServer();
  91. while ((line = await sw.ReadLineAsync()) != null)
  92. {
  93. if (!string.IsNullOrWhiteSpace(line) && stun.Parse(line))
  94. {
  95. List.Add(stun.ToString());
  96. }
  97. }
  98. }
  99. private async Task TestClassicNatTypeImpl(CancellationToken token)
  100. {
  101. var server = new StunServer();
  102. if (!server.Parse(StunServer))
  103. {
  104. throw new Exception(@"Wrong STUN Server!");
  105. }
  106. using var proxy = ProxyFactory.CreateProxy(
  107. ProxyType,
  108. Result3489.LocalEndPoint,
  109. NetUtils.ParseEndpoint(ProxyServer),
  110. ProxyUser, ProxyPassword
  111. );
  112. using var client = new StunClient3489(server.Hostname, server.Port, Result3489.LocalEndPoint, proxy);
  113. Result3489 = client.Status;
  114. await client.Query3489Async();
  115. Result3489.LocalEndPoint = client.LocalEndPoint;
  116. }
  117. private async Task DiscoveryNatTypeImpl(CancellationToken token)
  118. {
  119. var server = new StunServer();
  120. if (!server.Parse(StunServer))
  121. {
  122. throw new Exception(@"Wrong STUN Server!");
  123. }
  124. using var proxy = ProxyFactory.CreateProxy(
  125. ProxyType,
  126. Result5389.LocalEndPoint,
  127. NetUtils.ParseEndpoint(ProxyServer),
  128. ProxyUser, ProxyPassword
  129. );
  130. using var client = new StunClient5389UDP(server.Hostname, server.Port, Result5389.LocalEndPoint, proxy);
  131. Result5389 = client.Status;
  132. await client.QueryAsync();
  133. var cache = new StunResult5389();
  134. cache.Clone(client.Status);
  135. cache.LocalEndPoint = client.LocalEndPoint;
  136. Result5389 = cache;
  137. }
  138. }
  139. }