From: Marcos Henrich Date: Thu, 19 Nov 2015 10:53:16 +0000 (+0000) Subject: [System] Check SocketOptionName.ReuseAddress support. X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=9d3d44013f5ca5311404737bb384b76654d414ff;p=mono.git [System] Check SocketOptionName.ReuseAddress support. Operating systems that use linux kernels before 3.9 do not support binding multiple socket to the same address and port. In those systems even if it was possible to set SocketOptionName.ReuseAddress an exception was thrown on the second bind. These changes introduce a SocketException that is thrown when SocketOptionName.ReuseAddress is set on a system that will fail to bind multiple times to the same address. --- diff --git a/mcs/class/System/System.Net.Sockets/Socket.cs b/mcs/class/System/System.Net.Sockets/Socket.cs index 0f1c977a71d..98d5dc42d0a 100644 --- a/mcs/class/System/System.Net.Sockets/Socket.cs +++ b/mcs/class/System/System.Net.Sockets/Socket.cs @@ -3163,27 +3163,24 @@ namespace System.Net.Sockets public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue) { - ThrowIfDisposedAndClosed (); - - int error; int int_val = optionValue ? 1 : 0; - SetSocketOption_internal (safe_handle, optionLevel, optionName, null, null, int_val, out error); - if (error != 0) { - if (error == (int) SocketError.InvalidArgument) - throw new ArgumentException (); - throw new SocketException (error); - } + SetSocketOption (optionLevel, optionName, int_val); } public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) { ThrowIfDisposedAndClosed (); + if (optionName == SocketOptionName.ReuseAddress && optionValue != 0 && !SupportsPortReuse ()) + throw new SocketException ((int) SocketError.OperationNotSupported, "Operating system sockets do not support ReuseAddress.\nIf your socket is not intended to bind to the same address and port multiple times remove this option, otherwise you should ignore this exception inside a try catch and check that ReuseAddress is true before binding to the same address and port multiple times."); + int error; SetSocketOption_internal (safe_handle, optionLevel, optionName, null, null, optionValue, out error); if (error != 0) { + if (error == (int) SocketError.InvalidArgument) + throw new ArgumentException (); throw new SocketException (error); } } diff --git a/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs b/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs index a53a5d5624a..14a4490fa4b 100755 --- a/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs +++ b/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs @@ -3500,27 +3500,6 @@ namespace MonoTests.System.Net.Sockets s.Close (); } -#if MONOTOUCH - // when the linker is enabled then reflection won't work and would throw an NRE - // this is also always true for iOS - so we do not need to poke internals - static bool SupportsPortReuse () - { - return true; - } -#else - static bool? supportsPortReuse; - static bool SupportsPortReuse () - { - if (supportsPortReuse.HasValue) - return supportsPortReuse.Value; - - supportsPortReuse = (bool) typeof (Socket).GetMethod ("SupportsPortReuse", - BindingFlags.Static | BindingFlags.NonPublic) - .Invoke (null, new object [] {}); - return supportsPortReuse.Value; - } -#endif - // Test case for bug #31557 [Test] public void TcpDoubleBind () @@ -3529,22 +3508,28 @@ namespace MonoTests.System.Net.Sockets SocketType.Stream, ProtocolType.Tcp)) using (Socket ss = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { - s.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + var supportsReuseAddress = true; + try { + s.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + } catch (SocketException e) { + // Exception is thrown when ReuseAddress is not supported + supportsReuseAddress = false; + } + var ep = new IPEndPoint (IPAddress.Any, NetworkHelpers.FindFreePort ()); s.Bind (ep); s.Listen(1); - ss.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + if (supportsReuseAddress) + ss.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - Exception ex = null; try { ss.Bind (new IPEndPoint (IPAddress.Any, ep.Port)); ss.Listen(1); + if (!supportsReuseAddress) + Assert.Fail ("Reusing address is not supported, exception was expected on second bind."); } catch (SocketException e) { - ex = e; } - - Assert.AreEqual (SupportsPortReuse (), ex == null); } }