AllowNatTraversal is properly set for stopped socket (#32603)
authorAlexander Nikolaev <55398552+alnikola@users.noreply.github.com>
Fri, 21 Feb 2020 18:44:22 +0000 (19:44 +0100)
committerGitHub <noreply@github.com>
Fri, 21 Feb 2020 18:44:22 +0000 (19:44 +0100)
If AllowNatTraversal is called on a stopped TcpListener when ```_serverSocket``` is null, ```allow``` value gets stored in a field. Subsequently, if a new socket is created on Start call, the saved value is read and the corresponding argument is passed to SetIPProtectionLevel call after which the value is reset to preserve the current IPProtectionLevel propagation logic. The follow-up issue is #32653
Fixes #32551

src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs

index 95b5622..3220214 100644 (file)
@@ -15,6 +15,7 @@ namespace System.Net.Sockets
         private Socket _serverSocket;
         private bool _active;
         private bool _exclusiveAddressUse;
+        private bool? _allowNatTraversal;
 
         // Initializes a new instance of the TcpListener class with the specified local end point.
         public TcpListener(IPEndPoint localEP)
@@ -118,7 +119,14 @@ namespace System.Net.Sockets
                 throw new InvalidOperationException(SR.net_tcplistener_mustbestopped);
             }
 
-            _serverSocket.SetIPProtectionLevel(allowed ? IPProtectionLevel.Unrestricted : IPProtectionLevel.EdgeRestricted);
+            if (_serverSocket != null)
+            {
+                SetIPProtectionLevel(allowed); // Set it only for the current socket to preserve existing behavior
+            }
+            else
+            {
+                _allowNatTraversal = allowed;
+            }
         }
 
         // Starts listening to network requests.
@@ -335,6 +343,9 @@ namespace System.Net.Sockets
             return listener;
         }
 
+        private void SetIPProtectionLevel(bool allowed)
+            => _serverSocket.SetIPProtectionLevel(allowed ? IPProtectionLevel.Unrestricted : IPProtectionLevel.EdgeRestricted);
+
         private void CreateNewSocketIfNeeded()
         {
             _serverSocket ??= new Socket(_serverSocketEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
@@ -343,6 +354,12 @@ namespace System.Net.Sockets
             {
                 _serverSocket.ExclusiveAddressUse = true;
             }
+
+            if (_allowNatTraversal != null)
+            {
+                SetIPProtectionLevel(_allowNatTraversal.GetValueOrDefault());
+                _allowNatTraversal = null; // Reset value to avoid affecting more sockets
+            }
         }
     }
 }
index 3bdc803..02be71a 100644 (file)
@@ -45,6 +45,36 @@ namespace System.Net.Sockets.Tests
         }
 
         [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void AllowNatTraversal_NotStarted_SetSuccessfully()
+        {
+            var listener = new TcpListener(IPAddress.Loopback, 0);
+            listener.AllowNatTraversal(true);
+            listener.Start();
+            listener.Stop();
+        }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void AllowNatTraversal_Started_ThrowsException()
+        {
+            var listener = new TcpListener(IPAddress.Loopback, 0);
+            listener.Start();
+            Assert.Throws<InvalidOperationException>(() => listener.AllowNatTraversal(true));
+            listener.Stop();
+        }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void AllowNatTraversal_StartedAndStopped_SetSuccessfully()
+        {
+            var listener = new TcpListener(IPAddress.Loopback, 0);
+            listener.Start();
+            listener.Stop();
+            listener.AllowNatTraversal(true);
+        }
+
+        [Fact]
         public void Start_InvalidArgs_Throws()
         {
             var listener = new DerivedTcpListener(IPAddress.Loopback, 0);