Bruce Wayne 5 years ago
parent
commit
35c0516120

+ 2 - 0
NatTypeTester/ViewModels/MainWindowViewModel.cs

@@ -164,7 +164,9 @@ namespace NatTypeTester.ViewModels
             const string path = @"stun.txt";
 
             if (!File.Exists(path))
+            {
                 return;
+            }
 
             using var sw = new StreamReader(path);
             string line;

+ 10 - 8
STUN/Client/StunClient3489.cs

@@ -10,6 +10,7 @@ using System.Linq;
 using System.Net;
 using System.Reactive.Linq;
 using System.Reactive.Subjects;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace STUN.Client
@@ -46,7 +47,7 @@ namespace STUN.Client
 
         public IPEndPoint RemoteEndPoint => Server == null ? null : new IPEndPoint(Server, Port);
 
-        protected IUdpProxy Proxy;
+        protected readonly IUdpProxy Proxy;
 
         public StunClient3489(string server, ushort port = 3478, IPEndPoint local = null, IUdpProxy proxy = null, IDnsQuery dnsQuery = null)
         {
@@ -80,13 +81,14 @@ namespace STUN.Client
             _natTypeSubj.OnNext(res.NatType);
             PubSubj.OnNext(res.PublicEndPoint);
 
+            using var cts = new CancellationTokenSource(Timeout);
             try
             {
-                await Proxy.ConnectAsync();
+                await Proxy.ConnectAsync(cts.Token);
                 // test I
                 var test1 = new StunMessage5389 { StunMessageType = StunMessageType.BindingRequest, MagicCookie = 0 };
 
-                var (response1, remote1, local1) = await TestAsync(test1, RemoteEndPoint, RemoteEndPoint);
+                var (response1, remote1, local1) = await TestAsync(test1, RemoteEndPoint, RemoteEndPoint, cts.Token);
                 if (response1 == null)
                 {
                     res.NatType = NatType.UdpBlocked;
@@ -121,7 +123,7 @@ namespace STUN.Client
                 };
 
                 // test II
-                var (response2, remote2, _) = await TestAsync(test2, RemoteEndPoint, changedAddress1);
+                var (response2, remote2, _) = await TestAsync(test2, RemoteEndPoint, changedAddress1, cts.Token);
                 var mappedAddress2 = AttributeExtensions.GetMappedAddressAttribute(response2);
 
                 if (Equals(mappedAddress1.Address, local1) && mappedAddress1.Port == LocalEndPoint.Port)
@@ -150,7 +152,7 @@ namespace STUN.Client
 
                 // Test I(#2)
                 var test12 = new StunMessage5389 { StunMessageType = StunMessageType.BindingRequest, MagicCookie = 0 };
-                var (response12, _, _) = await TestAsync(test12, changedAddress1, changedAddress1);
+                var (response12, _, _) = await TestAsync(test12, changedAddress1, changedAddress1, cts.Token);
                 var mappedAddress12 = AttributeExtensions.GetMappedAddressAttribute(response12);
 
                 if (mappedAddress12 == null)
@@ -173,7 +175,7 @@ namespace STUN.Client
                     MagicCookie = 0,
                     Attributes = new[] { AttributeExtensions.BuildChangeRequest(false, true) }
                 };
-                var (response3, _, _) = await TestAsync(test3, changedAddress1, changedAddress1);
+                var (response3, _, _) = await TestAsync(test3, changedAddress1, changedAddress1, cts.Token);
                 var mappedAddress3 = AttributeExtensions.GetMappedAddressAttribute(response3);
                 if (mappedAddress3 != null)
                 {
@@ -193,7 +195,7 @@ namespace STUN.Client
             }
         }
 
-        protected async Task<(StunMessage5389, IPEndPoint, IPAddress)> TestAsync(StunMessage5389 sendMessage, IPEndPoint remote, IPEndPoint receive)
+        protected async Task<(StunMessage5389, IPEndPoint, IPAddress)> TestAsync(StunMessage5389 sendMessage, IPEndPoint remote, IPEndPoint receive, CancellationToken token)
         {
             try
             {
@@ -206,7 +208,7 @@ namespace STUN.Client
                 {
                     try
                     {
-                        var (receive1, ipe, local) = await Proxy.ReceiveAsync(b1, remote, receive);
+                        var (receive1, ipe, local) = await Proxy.ReceiveAsync(b1, remote, receive, token);
 
                         var message = new StunMessage5389();
                         if (message.TryParse(receive1) &&

+ 24 - 18
STUN/Client/StunClient5389UDP.cs

@@ -7,6 +7,7 @@ using System;
 using System.Net;
 using System.Reactive.Linq;
 using System.Reactive.Subjects;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace STUN.Client
@@ -46,9 +47,11 @@ namespace STUN.Client
                 _filteringBehaviorSubj.OnNext(result.FilteringBehavior);
                 PubSubj.OnNext(result.PublicEndPoint);
 
-                await Proxy.ConnectAsync();
+                using var cts = new CancellationTokenSource(Timeout);
 
-                result = await FilteringBehaviorTestBaseAsync();
+                await Proxy.ConnectAsync(cts.Token);
+
+                result = await FilteringBehaviorTestBaseAsync(cts.Token);
                 if (result.BindingTestResult != BindingTestResult.Success
                 || result.FilteringBehavior == FilteringBehavior.UnsupportedServer
                 )
@@ -63,7 +66,7 @@ namespace STUN.Client
                 }
 
                 // MappingBehaviorTest test II
-                var result2 = await BindingTestBaseAsync(new IPEndPoint(result.OtherEndPoint.Address, RemoteEndPoint.Port), false);
+                var result2 = await BindingTestBaseAsync(new IPEndPoint(result.OtherEndPoint.Address, RemoteEndPoint.Port), false, cts.Token);
                 if (result2.BindingTestResult != BindingTestResult.Success)
                 {
                     result.MappingBehavior = MappingBehavior.Fail;
@@ -77,7 +80,7 @@ namespace STUN.Client
                 }
 
                 // MappingBehaviorTest test III
-                var result3 = await BindingTestBaseAsync(result.OtherEndPoint, false);
+                var result3 = await BindingTestBaseAsync(result.OtherEndPoint, false, cts.Token);
                 if (result3.BindingTestResult != BindingTestResult.Success)
                 {
                     result.MappingBehavior = MappingBehavior.Fail;
@@ -99,8 +102,9 @@ namespace STUN.Client
         {
             try
             {
-                await Proxy.ConnectAsync();
-                var result = await BindingTestBaseAsync(RemoteEndPoint, true);
+                using var cts = new CancellationTokenSource(Timeout);
+                await Proxy.ConnectAsync(cts.Token);
+                var result = await BindingTestBaseAsync(RemoteEndPoint, true, cts.Token);
                 return result;
             }
             finally
@@ -109,11 +113,11 @@ namespace STUN.Client
             }
         }
 
-        private async Task<StunResult5389> BindingTestBaseAsync(IPEndPoint remote, bool notifyChanged)
+        private async Task<StunResult5389> BindingTestBaseAsync(IPEndPoint remote, bool notifyChanged, CancellationToken token)
         {
             BindingTestResult res;
             var test = new StunMessage5389 { StunMessageType = StunMessageType.BindingRequest };
-            var (response1, _, local1) = await TestAsync(test, remote, remote);
+            var (response1, _, local1) = await TestAsync(test, remote, remote, token);
             var mappedAddress1 = AttributeExtensions.GetXorMappedAddressAttribute(response1);
             var otherAddress = AttributeExtensions.GetOtherAddressAttribute(response1);
             var local = local1 == null ? null : new IPEndPoint(local1, LocalEndPoint.Port);
@@ -150,11 +154,12 @@ namespace STUN.Client
         public async Task<StunResult5389> MappingBehaviorTestAsync()
         {
             var result = new StunResult5389();
+            using var cts = new CancellationTokenSource(Timeout);
             try
             {
-                await Proxy.ConnectAsync();
+                await Proxy.ConnectAsync(cts.Token);
                 // test I
-                result = await BindingTestBaseAsync(RemoteEndPoint, true);
+                result = await BindingTestBaseAsync(RemoteEndPoint, true, cts.Token);
                 if (result.BindingTestResult != BindingTestResult.Success)
                 {
                     return result;
@@ -175,7 +180,7 @@ namespace STUN.Client
                 }
 
                 // test II
-                var result2 = await BindingTestBaseAsync(new IPEndPoint(result.OtherEndPoint.Address, RemoteEndPoint.Port), false);
+                var result2 = await BindingTestBaseAsync(new IPEndPoint(result.OtherEndPoint.Address, RemoteEndPoint.Port), false, cts.Token);
                 if (result2.BindingTestResult != BindingTestResult.Success)
                 {
                     result.MappingBehavior = MappingBehavior.Fail;
@@ -189,7 +194,7 @@ namespace STUN.Client
                 }
 
                 // test III
-                var result3 = await BindingTestBaseAsync(result.OtherEndPoint, false);
+                var result3 = await BindingTestBaseAsync(result.OtherEndPoint, false, cts.Token);
                 if (result3.BindingTestResult != BindingTestResult.Success)
                 {
                     result.MappingBehavior = MappingBehavior.Fail;
@@ -207,10 +212,10 @@ namespace STUN.Client
             }
         }
 
-        private async Task<StunResult5389> FilteringBehaviorTestBaseAsync()
+        private async Task<StunResult5389> FilteringBehaviorTestBaseAsync(CancellationToken token)
         {
             // test I
-            var result1 = await BindingTestBaseAsync(RemoteEndPoint, true);
+            var result1 = await BindingTestBaseAsync(RemoteEndPoint, true, token);
             try
             {
                 if (result1.BindingTestResult != BindingTestResult.Success)
@@ -232,7 +237,7 @@ namespace STUN.Client
                     StunMessageType = StunMessageType.BindingRequest,
                     Attributes = new[] { AttributeExtensions.BuildChangeRequest(true, true) }
                 };
-                var (response2, _, _) = await TestAsync(test2, RemoteEndPoint, result1.OtherEndPoint);
+                var (response2, _, _) = await TestAsync(test2, RemoteEndPoint, result1.OtherEndPoint, token);
 
                 if (response2 != null)
                 {
@@ -246,7 +251,7 @@ namespace STUN.Client
                     StunMessageType = StunMessageType.BindingRequest,
                     Attributes = new[] { AttributeExtensions.BuildChangeRequest(false, true) }
                 };
-                var (response3, remote3, _) = await TestAsync(test3, RemoteEndPoint, RemoteEndPoint);
+                var (response3, remote3, _) = await TestAsync(test3, RemoteEndPoint, RemoteEndPoint, token);
 
                 if (response3 == null)
                 {
@@ -274,8 +279,9 @@ namespace STUN.Client
         {
             try
             {
-                await Proxy.ConnectAsync();
-                var result = await FilteringBehaviorTestBaseAsync();
+                using var cts = new CancellationTokenSource(Timeout);
+                await Proxy.ConnectAsync(cts.Token);
+                var result = await FilteringBehaviorTestBaseAsync(cts.Token);
                 return result;
             }
             finally

+ 1 - 1
STUN/Interfaces/IUdpProxy.cs

@@ -11,6 +11,6 @@ namespace STUN.Interfaces
         IPEndPoint LocalEndPoint { get; }
         Task ConnectAsync(CancellationToken token = default);
         Task<(byte[], IPEndPoint, IPAddress)> ReceiveAsync(byte[] bytes, IPEndPoint remote, EndPoint receive, CancellationToken token = default);
-        Task DisconnectAsync(CancellationToken token = default);
+        Task DisconnectAsync();
     }
 }

+ 1 - 1
STUN/Proxy/NoneUdpProxy.cs

@@ -31,7 +31,7 @@ namespace STUN.Proxy
             return Task.CompletedTask;
         }
 
-        public Task DisconnectAsync(CancellationToken token = default)
+        public Task DisconnectAsync()
         {
             UdpClient.Close();
             return Task.CompletedTask;

+ 52 - 3
STUN/Proxy/Socks5UdpProxy.cs

@@ -21,7 +21,12 @@ namespace STUN.Proxy
         public TimeSpan Timeout
         {
             get => TimeSpan.FromMilliseconds(_udpClient.Client.ReceiveTimeout);
-            set => _udpClient.Client.ReceiveTimeout = Convert.ToInt32(value.TotalMilliseconds);
+            set
+            {
+                var timeout = Convert.ToInt32(value.TotalMilliseconds);
+                _udpClient.Client.ReceiveTimeout = timeout;
+                _assoc.ReceiveTimeout = timeout;
+            }
         }
 
         public IPEndPoint LocalEndPoint => (IPEndPoint)_udpClient.Client.LocalEndPoint;
@@ -51,21 +56,30 @@ namespace STUN.Proxy
                 var buf = new byte[1024];
                 await _assoc.ConnectAsync(_socksTcpEndPoint.Address, _socksTcpEndPoint.Port);
                 var s = _assoc.GetStream();
+                using var _ = token.Register(() => s.Close());
                 var requestPasswordAuth = !string.IsNullOrEmpty(_user);
 
                 #region Handshake
+
                 // we have no GSS-API support
                 var handShake = requestPasswordAuth ? new byte[] { 5, 2, 0, 2 } : new byte[] { 5, 1, 0 };
                 await s.WriteAsync(handShake, 0, handShake.Length, token);
 
                 // 5 auth(ff=deny)
                 if (await s.ReadAsync(buf, 0, 2, token) != 2)
+                {
                     throw new ProtocolViolationException();
+                }
+
                 if (buf[0] != 5)
+                {
                     throw new ProtocolViolationException();
+                }
+
                 #endregion
 
                 #region Auth
+
                 var auth = buf[1];
                 switch (auth)
                 {
@@ -83,20 +97,31 @@ namespace STUN.Proxy
                         await s.WriteAsync(buf, 0, uByte.Length + pByte.Length + 3, token);
                         // 1 state(0=ok)
                         if (await s.ReadAsync(buf, 0, 2, token) != 2)
+                        {
                             throw new ProtocolViolationException();
+                        }
+
                         if (buf[0] != 1)
+                        {
                             throw new ProtocolViolationException();
+                        }
+
                         if (buf[1] != 0)
+                        {
                             throw new UnauthorizedAccessException();
+                        }
+
                         break;
                     case 0xff:
                         throw new UnauthorizedAccessException();
                     default:
                         throw new ProtocolViolationException();
                 }
+
                 #endregion
 
                 #region UDP Assoc Send
+
                 buf[0] = 5;
                 buf[1] = 3;
                 buf[2] = 0;
@@ -106,33 +131,55 @@ namespace STUN.Proxy
                 Array.Copy(abyte, 0, buf, 3, addrLen);
                 // 5 cmd(3=udpassoc) 0 atyp(1=v4 3=dns 4=v5) addr port
                 await s.WriteAsync(buf, 0, addrLen + 3, token);
+
                 #endregion
 
                 #region UDP Assoc Response
+
                 if (await s.ReadAsync(buf, 0, 4, token) != 4)
+                {
                     throw new ProtocolViolationException();
+                }
+
                 if (buf[0] != 5 || buf[2] != 0)
+                {
                     throw new ProtocolViolationException();
+                }
+
                 if (buf[1] != 0)
+                {
                     throw new UnauthorizedAccessException();
+                }
 
                 addrLen = GetAddressLength(buf[3]);
 
                 var addr = new byte[addrLen];
                 if (await s.ReadAsync(addr, 0, addrLen, token) != addrLen)
+                {
                     throw new ProtocolViolationException();
+                }
+
                 var assocIP = new IPAddress(addr);
                 if (await s.ReadAsync(buf, 0, 2, token) != 2)
+                {
                     throw new ProtocolViolationException();
+                }
+
                 var assocPort = buf[0] * 256 + buf[1];
+
                 #endregion
 
                 _assocEndPoint = new IPEndPoint(assocIP, assocPort);
             }
+            catch (ObjectDisposedException ex) when (ex.ObjectName == typeof(NetworkStream).FullName)
+            {
+                await DisconnectAsync();
+                throw new TimeoutException();
+            }
             catch (Exception e)
             {
                 Debug.WriteLine(e);
-                await DisconnectAsync(token);
+                await DisconnectAsync();
                 throw;
             }
         }
@@ -141,7 +188,9 @@ namespace STUN.Proxy
         {
             var state = _assoc.GetState();
             if (state != TcpState.Established)
+            {
                 throw new InvalidOperationException("No UDP association, maybe already disconnected or not connected");
+            }
 
             var remoteBytes = GetEndPointByte(remote);
             var proxyBytes = new byte[bytes.Length + remoteBytes.Length + 3];
@@ -174,7 +223,7 @@ namespace STUN.Proxy
                 ipPacketInformation.Address);
         }
 
-        public Task DisconnectAsync(CancellationToken token = default)
+        public Task DisconnectAsync()
         {
             try
             {