From 52dc5527790f904be30fbb85c0642ebc7d0fe051 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 17 Jan 2020 16:02:29 +0100 Subject: [PATCH 01/15] reimplement socket duplication, fix #1760 --- .../WinSock/Interop.WSADuplicateSocket.cs | 52 +++ .../Net/Sockets/SocketTestExtensions.cs | 13 + .../src/Resources/Strings.resx | 3 + .../src/System.Net.Sockets.csproj | 5 +- .../Net/Sockets/SafeSocketHandle.Windows.cs | 7 + .../src/System/Net/Sockets/Socket.Unix.cs | 19 ++ .../src/System/Net/Sockets/Socket.Windows.cs | 79 +++++ .../src/System/Net/Sockets/Socket.cs | 21 +- .../System/Net/Sockets/SocketInformation.cs | 13 + .../System/Net/Sockets/SocketPal.Windows.cs | 51 +++ .../tests/FunctionalTests/OSSupport.cs | 15 - .../tests/FunctionalTests/SendFile.cs | 17 +- .../tests/FunctionalTests/SendReceive.cs | 25 +- .../FunctionalTests/SocketDuplicationTests.cs | 306 ++++++++++++++++++ .../FunctionalTests/SocketInformationTest.cs | 10 - .../System.Net.Sockets.Tests.csproj | 1 + 16 files changed, 555 insertions(+), 82 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs create mode 100644 src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs new file mode 100644 index 00000000000000..66d3dd8b53443a --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs @@ -0,0 +1,52 @@ +using System; +using System.Net.Sockets; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Winsock + { + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + internal struct WSAProtocolChain + { + internal int ChainLen; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=7)] + internal uint[] ChainEntries; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + internal struct WSAProtocolInfo + { + internal uint ServiceFlags1; + internal uint ServiceFlags2; + internal uint ServiceFlags3; + internal uint ServiceFlags4; + internal uint ProviderFlags; + internal Guid ProviderId; + internal uint CatalogEntryId; + internal WSAProtocolChain ProtocolChain; + internal int Version; + internal AddressFamily AddressFamily; + internal int MaxSockAddr; + internal int MinSockAddr; + internal SocketType SocketType; + internal ProtocolType ProtocolType; + internal int ProtocolMaxOffset; + internal int NetworkByteOrder; + internal int SecurityScheme; + internal uint MessageSize; + internal uint ProviderReserved; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + internal string ProtocolName; + + public static readonly int Size = Marshal.SizeOf(typeof(WSAProtocolInfo)); + } + + [DllImport(Interop.Libraries.Ws2_32, SetLastError = true)] + internal static extern unsafe SocketError WSADuplicateSocket( + [In] SafeHandle socketHandle, + [In] uint targetProcessId, + [In] byte* pinnedBuffer + ); + } +} diff --git a/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs b/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs index dacf42640985fa..76043a3d3653e9 100644 --- a/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs +++ b/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs @@ -36,5 +36,18 @@ public static void ForceNonBlocking(this Socket socket, bool force) socket.Blocking = true; } } + + public static (Socket, Socket) CreateConnectedSocketPair() + { + using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + listener.Listen(1); + + Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + client.Connect(listener.LocalEndPoint); + Socket server = listener.Accept(); + + return (client, server); + } } } diff --git a/src/libraries/System.Net.Sockets/src/Resources/Strings.resx b/src/libraries/System.Net.Sockets/src/Resources/Strings.resx index cad4341429f314..92c78d49cd8e52 100644 --- a/src/libraries/System.Net.Sockets/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Sockets/src/Resources/Strings.resx @@ -249,4 +249,7 @@ A ValueTask returned from an asynchronous socket operation was consumed concurrently. ValueTasks must only ever be awaited once. (Id: {0}). + + The specified value for the socket information is invalid. + diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index 5126d460fccdd2..3c15df46e493f6 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -1,4 +1,4 @@ - + System.Net.Sockets true @@ -197,6 +197,9 @@ Common\Interop\Windows\WinSock\Interop.WSAConnect.cs + + Common\Interop\Windows\WinSock\Interop.WSADuplicateSocket.cs + Common\Interop\Windows\WinSock\Interop.WSAGetOverlappedResult.cs diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs index 8a28356077a783..0ac2ff1a5b61e4 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs @@ -59,6 +59,7 @@ internal ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle(bool trySkipCo catch (Exception exception) when (!ExceptionCheck.IsFatal(exception)) { bool closed = IsClosed; + bool alreadyBound = !IsInvalid && !IsClosed && (exception is ArgumentException); CloseAsIs(abortive: false); if (closed) { @@ -67,6 +68,12 @@ internal ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle(bool trySkipCo // instead propagate as an ObjectDisposedException. ThrowSocketDisposedException(exception); } + + if (alreadyBound) + { + throw new InvalidOperationException("Asynchronous operations are not allowed on this socket. It's handle might have been previously bound to a Thread Pool / IOCP port."); + } + throw; } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs index 5afe05b6cfa71a..bd195ce4a61c3b 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs @@ -11,6 +11,25 @@ namespace System.Net.Sockets { public partial class Socket { + public Socket(SocketInformation socketInformation) + { + // + // This constructor works in conjunction with DuplicateAndClose, which is not supported. + // See comments in DuplicateAndClose. + // + throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); + } + + public SocketInformation DuplicateAndClose(int targetProcessId) + { + // + // DuplicateAndClose is not supported on Unix, since passing FD-s between processes + // should involve Unix Domain Sockets. This programming model is fundamentally different, + // and incompatible with the design of SocketInformation API-s. + // + throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); + } + partial void ValidateForMultiConnect(bool isMultiEndpoint) { // ValidateForMultiConnect is called before any {Begin}Connect{Async} call, diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index 53065619f682c8..8a9919a141eaf3 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -17,6 +17,85 @@ public partial class Socket internal void ReplaceHandleIfNecessaryAfterFailedConnect() { /* nop on Windows */ } + public Socket(SocketInformation socketInformation) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + InitializeSockets(); + + SocketError errorCode = SocketPal.CreateSocket(socketInformation, out _handle, + ref _addressFamily, ref _socketType, ref _protocolType); + + if (errorCode != SocketError.Success) + { + Debug.Assert(_handle.IsInvalid); + + if (errorCode == SocketError.InvalidArgument) + { + throw new ArgumentException(SR.net_sockets_invalid_socketinformation, nameof(socketInformation)); + } + + // Failed to create the socket, throw. + throw new SocketException((int)errorCode); + } + + if (_handle.IsInvalid) + { + throw new SocketException(); + } + + if (_addressFamily != AddressFamily.InterNetwork && _addressFamily != AddressFamily.InterNetworkV6) + { + throw new NotSupportedException(SR.net_invalidversion); + } + + _isConnected = socketInformation.GetOption(SocketInformationOptions.Connected); + _willBlock = !socketInformation.GetOption(SocketInformationOptions.NonBlocking); + InternalSetBlocking(_willBlock); + _isListening = socketInformation.GetOption(SocketInformationOptions.Listening); + + IPAddress tempAddress = _addressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any; + IPEndPoint ep = new IPEndPoint(tempAddress, 0); + + Internals.SocketAddress socketAddress = IPEndPointExtensions.Serialize(ep); + errorCode = SocketPal.GetSockName(_handle, socketAddress.Buffer, ref socketAddress.InternalSize); + if (errorCode == SocketError.Success) + { + try + { + _rightEndPoint = ep.Create(socketAddress); + } + catch + { + } + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + public SocketInformation DuplicateAndClose(int targetProcessId) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this, targetProcessId); + + ThrowIfDisposed(); + + SocketError errorCode = SocketPal.DuplicateSocket(_handle, targetProcessId, out SocketInformation info); + + if (errorCode != SocketError.Success) + { + throw new SocketException((int)errorCode); + } + + info.SetOption(SocketInformationOptions.Connected, Connected); + info.SetOption(SocketInformationOptions.NonBlocking, !Blocking); + info.SetOption(SocketInformationOptions.Listening, _isListening); + + Close(-1); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return info; + } + private void EnsureDynamicWinsockMethods() { if (_dynamicWinsockMethods == null) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index cfa04ff669a8c7..5ab52ce72f6d61 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -92,7 +92,6 @@ public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType p if (errorCode != SocketError.Success) { Debug.Assert(_handle.IsInvalid); - // Failed to create the socket, throw. throw new SocketException((int)errorCode); } @@ -106,15 +105,6 @@ public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType p if (NetEventSource.IsEnabled) NetEventSource.Exit(this); } - public Socket(SocketInformation socketInformation) - { - // - // This constructor works in conjunction with DuplicateAndClose, which is not supported. - // See comments in DuplicateAndClose. - // - throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); - } - // Called by the class to create a socket to accept an incoming request. private Socket(SafeSocketHandle fd) { @@ -2043,16 +2033,7 @@ private bool CanUseConnectEx(EndPoint remoteEP) (_rightEndPoint != null || remoteEP.GetType() == typeof(IPEndPoint)); } - public SocketInformation DuplicateAndClose(int targetProcessId) - { - // - // On Windows, we cannot duplicate a socket that is bound to an IOCP. In this implementation, we *only* - // support IOCPs, so this will not work. - // - // On Unix, duplication of a socket into an arbitrary process is not supported at all. - // - throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); - } + internal IAsyncResult UnsafeBeginConnect(EndPoint remoteEP, AsyncCallback callback, object state, bool flowContext = false) { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs index 2f9e510a3978a2..95ed8a183047a1 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs @@ -2,11 +2,24 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Runtime.Serialization; + namespace System.Net.Sockets { public struct SocketInformation { public byte[] ProtocolInformation { get; set; } public SocketInformationOptions Options { get; set; } + + internal void SetOption(SocketInformationOptions option, bool value) + { + if (value) Options |= option; + else Options &= ~option; + } + + internal bool GetOption(SocketInformationOptions option) + { + return ((Options & option) != 0); + } } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index dad69e95d86906..7e209c250e6e84 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; @@ -49,6 +50,43 @@ public static SocketError CreateSocket(AddressFamily addressFamily, SocketType s return socket.IsInvalid ? GetLastSocketError() : SocketError.Success; } + public static unsafe SocketError CreateSocket( + SocketInformation socketInformation, + out SafeSocketHandle socket, + ref AddressFamily addressFamily, + ref SocketType socketType, + ref ProtocolType protocolType) + { + if (socketInformation.ProtocolInformation == null || socketInformation.ProtocolInformation.Length < Interop.Winsock.WSAProtocolInfo.Size) + { + throw new ArgumentException(SR.net_sockets_invalid_socketinformation, nameof(socketInformation)); + } + + fixed (byte* pinnedBuffer = socketInformation.ProtocolInformation) + { + IntPtr handle = Interop.Winsock.WSASocketW( + (AddressFamily)(-1), + (SocketType)(-1), + (ProtocolType)(-1), + (IntPtr)pinnedBuffer, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED); + + socket = new SafeSocketHandle(handle, ownsHandle: true); + if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); + + if (socket.IsInvalid) + { + return GetLastSocketError(); + } + + Interop.Winsock.WSAProtocolInfo protocolInfo = Marshal.PtrToStructure((IntPtr)pinnedBuffer); + addressFamily = protocolInfo.AddressFamily; + socketType = protocolInfo.SocketType; + protocolType = protocolInfo.ProtocolType; + + return SocketError.Success; + } + } + public static SocketError SetBlocking(SafeSocketHandle handle, bool shouldBlock, out bool willBlock) { int intBlocking = shouldBlock ? 0 : -1; @@ -1246,5 +1284,18 @@ internal static SocketError Disconnect(Socket socket, SafeSocketHandle handle, b return errorCode; } + + internal static unsafe SocketError DuplicateSocket(SafeSocketHandle handle, int targetProcessId, out SocketInformation socketInformation) + { + socketInformation = new SocketInformation + { + ProtocolInformation = new byte[Interop.Winsock.WSAProtocolInfo.Size] + }; + + fixed (byte* pinnedBuffer = socketInformation.ProtocolInformation) + { + return Interop.Winsock.WSADuplicateSocket(handle, (uint)targetProcessId, pinnedBuffer); + } + } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs index d49fe656be0b29..c63500fb0b5319 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs @@ -26,21 +26,6 @@ public void SupportsIPv6_MatchesOSSupportsIPv6() #pragma warning restore } - [Fact] - public void UseOnlyOverlappedIO_AlwaysFalse() - { - using (var s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - Assert.Equal(AddressFamily.InterNetwork, s.AddressFamily); - Assert.Equal(SocketType.Stream, s.SocketType); - Assert.Equal(ProtocolType.Tcp, s.ProtocolType); - - Assert.False(s.UseOnlyOverlappedIO); - s.UseOnlyOverlappedIO = true; - Assert.False(s.UseOnlyOverlappedIO); - } - } - [Fact] public void IOControl_FIONREAD_Success() { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs index 26371fc4b1a18a..832b6ce6d7920f 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs @@ -189,7 +189,7 @@ public async Task SyncSendFileGetsCanceledByDispose() int msDelay = 100; await RetryHelper.ExecuteAsync(async () => { - (Socket socket1, Socket socket2) = CreateConnectedSocketPair(); + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); using (socket2) { Task socketOperation = Task.Run(() => @@ -315,20 +315,5 @@ await Task.Factory.FromAsync( // Clean up the file we created File.Delete(filename); } - - protected static (Socket, Socket) CreateConnectedSocketPair() - { - using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); - listener.Listen(1); - - Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - client.Connect(listener.LocalEndPoint); - Socket server = listener.Accept(); - - return (client, server); - } - } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs index cd8d032e2fc91b..9127d0fd35c5df 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs @@ -865,7 +865,7 @@ public async Task SendAsync_ConcurrentDispose_SucceedsOrThrowsAppropriateExcepti for (int i = 0; i < 20; i++) // run multiple times to attempt to force various interleavings { - (Socket client, Socket server) = CreateConnectedSocketPair(); + (Socket client, Socket server) = SocketTestExtensions.CreateConnectedSocketPair(); using (client) using (server) using (var b = new Barrier(2)) @@ -903,7 +903,7 @@ public async Task ReceiveAsync_ConcurrentDispose_SucceedsOrThrowsAppropriateExce for (int i = 0; i < 20; i++) // run multiple times to attempt to force various interleavings { - (Socket client, Socket server) = CreateConnectedSocketPair(); + (Socket client, Socket server) = SocketTestExtensions.CreateConnectedSocketPair(); using (client) using (server) using (var b = new Barrier(2)) @@ -935,21 +935,6 @@ error is SocketException || } } - protected static (Socket, Socket) CreateConnectedSocketPair() - { - using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); - listener.Listen(1); - - Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - client.Connect(listener.LocalEndPoint); - Socket server = listener.Accept(); - - return (client, server); - } - } - [Fact] [PlatformSpecific(~TestPlatforms.OSX)] // Not supported on OSX. public async Task UdpReceiveGetsCanceledByDispose() @@ -1018,7 +1003,7 @@ public async Task TcpReceiveSendGetsCanceledByDispose(bool receiveOrSend) int msDelay = 100; await RetryHelper.ExecuteAsync(async () => { - (Socket socket1, Socket socket2) = CreateConnectedSocketPair(); + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); using (socket2) { Task socketOperation; @@ -1117,7 +1102,7 @@ public async Task TcpPeerReceivesFinOnShutdownWithPendingData() byte[] receiveBuffer = new byte[1024]; await RetryHelper.ExecuteAsync(async () => { - (Socket socket1, Socket socket2) = CreateConnectedSocketPair(); + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); using (socket1) using (socket2) { @@ -1590,7 +1575,7 @@ public void BlockingRead_DoesntRequireAnotherThreadPoolThread() ThreadPool.SetMaxThreads(Environment.ProcessorCount, completionPortThreads); // Create twice that many socket pairs, for good measure. - (Socket, Socket)[] socketPairs = Enumerable.Range(0, Environment.ProcessorCount * 2).Select(_ => CreateConnectedSocketPair()).ToArray(); + (Socket, Socket)[] socketPairs = Enumerable.Range(0, Environment.ProcessorCount * 2).Select(_ => SocketTestExtensions.CreateConnectedSocketPair()).ToArray(); try { // Ensure that on Unix all of the first socket in each pair are configured for sync-over-async. diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs new file mode 100644 index 00000000000000..7bcb5746bc2d88 --- /dev/null +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -0,0 +1,306 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.IO; +using System.IO.Pipes; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; +using Xunit.Abstractions; + +namespace System.Net.Sockets.Tests +{ + // Test cases for DuplicateAndClose, Socket(socketInformation), Socket.UseOnlyOverlappedIO, + // and asynchronous IO behavior for duplicate sockets. + public class SocketDuplicationTests + { + private readonly ArraySegment _receiveBuffer = new ArraySegment(new byte[32]); + const string TestMessage = "test123!"; + private static ArraySegment TestBytes => Encoding.ASCII.GetBytes(TestMessage); + private static string GetMessageString(ArraySegment data, int count) => + Encoding.ASCII.GetString(data.AsSpan().Slice(0, count)); + private static int CurrentProcessId => Process.GetCurrentProcess().Id; + + + [Fact] + public void UseOnlyOverlappedIO_AlwaysFalse() + { + using Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + Assert.False(s.UseOnlyOverlappedIO); + s.UseOnlyOverlappedIO = true; + Assert.False(s.UseOnlyOverlappedIO); + } + + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public void DuplicateAndClose_TargetProcessDoesNotExist_Throws_SocketException() + { + using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + Assert.Throws(() => socket.DuplicateAndClose(-1)); + } + + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public void DuplicateAndClose_WhenDisposed_Throws() + { + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Dispose(); + + Assert.Throws(() => socket.DuplicateAndClose(CurrentProcessId)); + } + + [PlatformSpecific(TestPlatforms.Windows)] + [Theory] + [InlineData(false)] + [InlineData(true)] + public void BlockingState_IsTransferred(bool blocking) + { + using Socket original = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) + { + Blocking = blocking + }; + Assert.Equal(blocking, original.Blocking); + + SocketInformation info = original.DuplicateAndClose(CurrentProcessId); + + using Socket clone = new Socket(info); + Assert.Equal(blocking, clone.Blocking); + } + + public class NotSupportedOnUnix + { + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public void SocketCtr_SocketInformation() + { + SocketInformation socketInformation = default; + Assert.Throws(() => new Socket(socketInformation)); + } + + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public void DuplicateAndClose() + { + using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + int processId = CurrentProcessId; + + Assert.Throws(() => socket.DuplicateAndClose(processId)); + } + } + + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public async Task DuplicateAndClose_TcpClient() + { + using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + listener.Listen(1); + + using Socket client0 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + using Socket client1 = new Socket(client0.DuplicateAndClose(CurrentProcessId)); + Assert.False(client1.Connected); + client1.Connect(listener.LocalEndPoint); + + using Socket client2 = new Socket(client1.DuplicateAndClose(CurrentProcessId)); + Assert.True(client2.Connected); + + using Socket handler = await listener.AcceptAsync(); + await client2.SendAsync(TestBytes, SocketFlags.None); + + int rcvCount = await handler.ReceiveAsync(_receiveBuffer, SocketFlags.None); + + string receivedMessage = GetMessageString(_receiveBuffer, rcvCount); + Assert.Equal(TestMessage, receivedMessage); + } + + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public async Task DuplicateAndClose_TcpListener() + { + using Socket listener0 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + listener0.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + listener0.Listen(1); + + using Socket listener1 = new Socket(listener0.DuplicateAndClose(CurrentProcessId)); + + using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _ = client.ConnectAsync(listener1.LocalEndPoint); + + using Socket handler = await listener1.AcceptAsync(); + await client.SendAsync(TestBytes, SocketFlags.None); + + byte[] receivedBuffer = new byte[32]; + int rcvCount = await handler.ReceiveAsync(new ArraySegment(receivedBuffer), SocketFlags.None); + + string receivedMessage = GetMessageString(receivedBuffer, rcvCount); + Assert.Equal(TestMessage, receivedMessage); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public async Task DoAsyncOperation_OnBothOriginalAndClone_ThrowsInvalidOperationException() + { + // Not applicable for synchronous operations: + (Socket client, Socket originalServer) = SocketTestExtensions.CreateConnectedSocketPair(); + + using (client) + using (originalServer) + { + client.Send(TestBytes); + + await originalServer.ReceiveAsync(_receiveBuffer, SocketFlags.None); + + SocketInformation info = originalServer.DuplicateAndClose(CurrentProcessId); + + using Socket cloneServer = new Socket(info); + await Assert.ThrowsAsync(() => + cloneServer.ReceiveAsync(_receiveBuffer, SocketFlags.None)); + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void SocketCtr_SocketInformation_WhenProtocolInformationIsNull_Throws() + { + SocketInformation socketInformation = default; + + ArgumentException ex = Assert.Throws(() => new Socket(socketInformation)); + Assert.Equal("socketInformation", ex.ParamName); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void SocketCtr_SocketInformation_WhenProtocolInformationTooShort_Throws() + { + SocketInformation socketInformation = new SocketInformation() {ProtocolInformation = new byte[4]}; + + ArgumentException ex = Assert.Throws(() => new Socket(socketInformation)); + Assert.Equal("socketInformation", ex.ParamName); + } + + // A smaller subset of the tests is being executed against the different Send/Receive implementations of Socket + // to make sure async IO works as expected in all of those cases. + public abstract class PolymorphicTests where T : SocketHelperBase, new() + { + private static readonly T Helper = new T(); + private readonly string _ipcPipeName = Path.GetRandomFileName(); + + private static void WriteSocketInfo(Stream stream, SocketInformation socketInfo) + { + BinaryWriter bw = new BinaryWriter(stream); + bw.Write((int)socketInfo.Options); + bw.Write(socketInfo.ProtocolInformation.Length); + bw.Write(socketInfo.ProtocolInformation); + } + + private static SocketInformation ReadSocketInfo(Stream stream) + { + BinaryReader br = new BinaryReader(stream); + SocketInformationOptions options = (SocketInformationOptions)br.ReadInt32(); + int protocolInfoLength = br.ReadInt32(); + SocketInformation result = new SocketInformation() + { + Options = options, ProtocolInformation = new byte[protocolInfoLength] + }; + br.Read(result.ProtocolInformation); + return result; + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData(false)] + [InlineData(true)] + public async Task DuplicateAndClose_TcpServerHandler(bool sameProcess) + { + using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + listener.BindToAnonymousPort(IPAddress.Loopback); + listener.Listen(1); + + client.Connect(listener.LocalEndPoint); + + // Async is allowed on the listener: + using Socket handlerOriginal = await listener.AcceptAsync(); + + // pipe used to exchange socket info + await using NamedPipeServerStream pipeServerStream = + new NamedPipeServerStream(_ipcPipeName, PipeDirection.Out); + + if (sameProcess) + { + Task handlerCode = Task.Run(() => HandlerServerCode(_ipcPipeName)); + RunCommonHostLogic(CurrentProcessId); + await handlerCode; + } + else + { + RemoteInvokeOptions options = new RemoteInvokeOptions() {TimeOut = 500}; + using RemoteInvokeHandle hServerProc = + RemoteExecutor.Invoke(HandlerServerCode, _ipcPipeName, options); + RunCommonHostLogic(hServerProc.Process.Id); + } + + void RunCommonHostLogic(int processId) + { + pipeServerStream.WaitForConnection(); + + // Duplicate the socket: + SocketInformation socketInfo = handlerOriginal.DuplicateAndClose(processId); + WriteSocketInfo(pipeServerStream, socketInfo); + + // Send client data: + client.Send(TestBytes); + } + + static async Task HandlerServerCode(string ipcPipeName) + { + await using NamedPipeClientStream pipeClientStream = + new NamedPipeClientStream(".", ipcPipeName, PipeDirection.In); + pipeClientStream.Connect(); + + SocketInformation socketInfo = ReadSocketInfo(pipeClientStream); + using Socket handler = new Socket(socketInfo); + + Assert.True(handler.IsBound); + Assert.NotNull(handler.RemoteEndPoint); + Assert.NotNull(handler.LocalEndPoint); + + byte[] data = new byte[32]; + + int rcvCount = await Helper.ReceiveAsync(handler, new ArraySegment(data)); + string actual = GetMessageString(data, rcvCount); + + Assert.Equal(TestMessage, actual); + + return RemoteExecutor.SuccessExitCode; + } + } + } + + public class Synchronous : PolymorphicTests + { + } + + public class Apm : PolymorphicTests + { + } + + public class TaskBased : PolymorphicTests + { + } + + public class Eap : PolymorphicTests + { + } + } +} diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketInformationTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketInformationTest.cs index 3adc5f253b8912..d0cabfe15e4237 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketInformationTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketInformationTest.cs @@ -9,16 +9,6 @@ namespace System.Net.Sockets.Tests { public class SocketInformationTest { - [Fact] - public void Socket_Ctor_DuplicateAndClose_Throw() - { - Assert.Throws(() => new Socket(new SocketInformation())); - using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - Assert.Throws(() => s.DuplicateAndClose(0)); - } - } - [Fact] public void Properties_Roundtrip() { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj index 2759f21e0a6e0f..829ac7fa35156b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj @@ -33,6 +33,7 @@ + From 5b617e604cca42aac512f6be809d2a5330586400 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 17 Jan 2020 16:12:52 +0100 Subject: [PATCH 02/15] additional cleanup --- src/libraries/System.Net.Sockets/src/Resources/Strings.resx | 3 +++ .../src/System/Net/Sockets/SafeSocketHandle.Windows.cs | 2 +- .../System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs | 4 ++-- .../System.Net.Sockets/src/System/Net/Sockets/Socket.cs | 1 + .../src/System/Net/Sockets/SocketInformation.cs | 2 -- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/Resources/Strings.resx b/src/libraries/System.Net.Sockets/src/Resources/Strings.resx index 92c78d49cd8e52..3b274c84c10541 100644 --- a/src/libraries/System.Net.Sockets/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Sockets/src/Resources/Strings.resx @@ -252,4 +252,7 @@ The specified value for the socket information is invalid. + + Asynchronous operations are not allowed on this socket. The underlying OS handle might have been already bound to an IO completion port. + diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs index 0ac2ff1a5b61e4..00cd87b582ef59 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs @@ -71,7 +71,7 @@ internal ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle(bool trySkipCo if (alreadyBound) { - throw new InvalidOperationException("Asynchronous operations are not allowed on this socket. It's handle might have been previously bound to a Thread Pool / IOCP port."); + throw new InvalidOperationException(SR.net_sockets_asyncoperations_notallowed); } throw; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs index bd195ce4a61c3b..6c95f9634020cb 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs @@ -14,7 +14,7 @@ public partial class Socket public Socket(SocketInformation socketInformation) { // - // This constructor works in conjunction with DuplicateAndClose, which is not supported. + // This constructor works in conjunction with DuplicateAndClose, which is not supported on Unix. // See comments in DuplicateAndClose. // throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); @@ -24,7 +24,7 @@ public SocketInformation DuplicateAndClose(int targetProcessId) { // // DuplicateAndClose is not supported on Unix, since passing FD-s between processes - // should involve Unix Domain Sockets. This programming model is fundamentally different, + // should involve Unix Domain Sockets. The programming model is fundamentally different, // and incompatible with the design of SocketInformation API-s. // throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index 5ab52ce72f6d61..32fdf618ec0158 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -92,6 +92,7 @@ public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType p if (errorCode != SocketError.Success) { Debug.Assert(_handle.IsInvalid); + // Failed to create the socket, throw. throw new SocketException((int)errorCode); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs index 95ed8a183047a1..3539b7e75d6ee3 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; - namespace System.Net.Sockets { public struct SocketInformation From bee3140d0fe5cffb8d963cf8789c5bb04a1200e9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 17 Jan 2020 18:09:09 +0100 Subject: [PATCH 03/15] ungroup PNSE tests, no RemoteExecutor timeout --- .../FunctionalTests/SocketDuplicationTests.cs | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index 7bcb5746bc2d88..cf20771524cd84 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -75,25 +75,22 @@ public void BlockingState_IsTransferred(bool blocking) Assert.Equal(blocking, clone.Blocking); } - public class NotSupportedOnUnix + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public void SocketCtr_SocketInformation_Unix_ThrowsPlatformNotSupportedException() { - [PlatformSpecific(TestPlatforms.AnyUnix)] - [Fact] - public void SocketCtr_SocketInformation() - { - SocketInformation socketInformation = default; - Assert.Throws(() => new Socket(socketInformation)); - } + SocketInformation socketInformation = default; + Assert.Throws(() => new Socket(socketInformation)); + } - [PlatformSpecific(TestPlatforms.AnyUnix)] - [Fact] - public void DuplicateAndClose() - { - using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - int processId = CurrentProcessId; + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public void DuplicateAndClose_Unix_ThrowsPlatformNotSupportedException() + { + using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + int processId = CurrentProcessId; - Assert.Throws(() => socket.DuplicateAndClose(processId)); - } + Assert.Throws(() => socket.DuplicateAndClose(processId)); } [PlatformSpecific(TestPlatforms.Windows)] @@ -244,9 +241,7 @@ public async Task DuplicateAndClose_TcpServerHandler(bool sameProcess) } else { - RemoteInvokeOptions options = new RemoteInvokeOptions() {TimeOut = 500}; - using RemoteInvokeHandle hServerProc = - RemoteExecutor.Invoke(HandlerServerCode, _ipcPipeName, options); + using RemoteInvokeHandle hServerProc = RemoteExecutor.Invoke(HandlerServerCode, _ipcPipeName); RunCommonHostLogic(hServerProc.Process.Id); } From 34bf608386fc08c7e13222f81c6bd425e7905317 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 17 Jan 2020 19:39:50 +0100 Subject: [PATCH 04/15] address review findings --- .../Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs | 8 ++++---- .../src/System/Net/Sockets/SocketInformation.cs | 2 +- .../src/System/Net/Sockets/SocketPal.Windows.cs | 3 ++- .../tests/FunctionalTests/SocketDuplicationTests.cs | 6 +++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs index 66d3dd8b53443a..f8e30f3291ac76 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs @@ -6,7 +6,7 @@ internal static partial class Interop { internal static partial class Winsock { - [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + [StructLayout(LayoutKind.Sequential)] internal struct WSAProtocolChain { internal int ChainLen; @@ -43,10 +43,10 @@ internal struct WSAProtocolInfo } [DllImport(Interop.Libraries.Ws2_32, SetLastError = true)] - internal static extern unsafe SocketError WSADuplicateSocket( - [In] SafeHandle socketHandle, + internal static extern unsafe int WSADuplicateSocket( + [In] SafeSocketHandle socketHandle, [In] uint targetProcessId, - [In] byte* pinnedBuffer + [In] byte* lpProtocolInfo ); } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs index 3539b7e75d6ee3..c8c4e1240317b2 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs @@ -17,7 +17,7 @@ internal void SetOption(SocketInformationOptions option, bool value) internal bool GetOption(SocketInformationOptions option) { - return ((Options & option) != 0); + return (Options & option) != 0; } } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index 7e209c250e6e84..852f64c0eca824 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -1294,7 +1294,8 @@ internal static unsafe SocketError DuplicateSocket(SafeSocketHandle handle, int fixed (byte* pinnedBuffer = socketInformation.ProtocolInformation) { - return Interop.Winsock.WSADuplicateSocket(handle, (uint)targetProcessId, pinnedBuffer); + int result = Interop.Winsock.WSADuplicateSocket(handle, (uint)targetProcessId, pinnedBuffer); + return result == 0 ? SocketError.Success : GetLastSocketError(); } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index cf20771524cd84..c41eec91387a8f 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -27,7 +27,6 @@ private static string GetMessageString(ArraySegment data, int count) => Encoding.ASCII.GetString(data.AsSpan().Slice(0, count)); private static int CurrentProcessId => Process.GetCurrentProcess().Id; - [Fact] public void UseOnlyOverlappedIO_AlwaysFalse() { @@ -44,7 +43,8 @@ public void DuplicateAndClose_TargetProcessDoesNotExist_Throws_SocketException() { using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - Assert.Throws(() => socket.DuplicateAndClose(-1)); + SocketException ex = Assert.Throws(() => socket.DuplicateAndClose(-1)); + Assert.Equal(SocketError.InvalidArgument, ex.SocketErrorCode); } [PlatformSpecific(TestPlatforms.Windows)] @@ -130,7 +130,7 @@ public async Task DuplicateAndClose_TcpListener() using Socket listener1 = new Socket(listener0.DuplicateAndClose(CurrentProcessId)); using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _ = client.ConnectAsync(listener1.LocalEndPoint); + await client.ConnectAsync(listener1.LocalEndPoint); using Socket handler = await listener1.AcceptAsync(); await client.SendAsync(TestBytes, SocketFlags.None); From fe81eeb41330af447ad0b3feb35882275b153cd5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 20 Jan 2020 18:17:55 +0100 Subject: [PATCH 05/15] fix path for Interop.WSADuplicateSocket.cs --- src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index 3c15df46e493f6..b6f74d901fce09 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -197,7 +197,7 @@ Common\Interop\Windows\WinSock\Interop.WSAConnect.cs - + Common\Interop\Windows\WinSock\Interop.WSADuplicateSocket.cs From 23fdf7550c39eea917bad056879ea69d0a20bbf0 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 21 Jan 2020 14:18:37 +0100 Subject: [PATCH 06/15] remove swallowing in Socket(SocketInformation) --- .../src/System/Net/Sockets/Socket.Windows.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index 8a9919a141eaf3..52bbfa053b1c5d 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -61,13 +61,7 @@ public Socket(SocketInformation socketInformation) errorCode = SocketPal.GetSockName(_handle, socketAddress.Buffer, ref socketAddress.InternalSize); if (errorCode == SocketError.Success) { - try - { - _rightEndPoint = ep.Create(socketAddress); - } - catch - { - } + _rightEndPoint = ep.Create(socketAddress); } if (NetEventSource.IsEnabled) NetEventSource.Exit(this); From 3d801cc0e436d5de22c1543de6fa8e156abebc5e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 22 Jan 2020 14:29:57 +0100 Subject: [PATCH 07/15] review suggestions --- .../System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs | 2 +- .../src/System/Net/Sockets/SocketInformation.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index 52bbfa053b1c5d..b0932e1fce224e 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -84,7 +84,7 @@ public SocketInformation DuplicateAndClose(int targetProcessId) info.SetOption(SocketInformationOptions.NonBlocking, !Blocking); info.SetOption(SocketInformationOptions.Listening, _isListening); - Close(-1); + Close(timeout: -1); if (NetEventSource.IsEnabled) NetEventSource.Exit(this); return info; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs index c8c4e1240317b2..0ec4f79cfe2ef6 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketInformation.cs @@ -17,7 +17,7 @@ internal void SetOption(SocketInformationOptions option, bool value) internal bool GetOption(SocketInformationOptions option) { - return (Options & option) != 0; + return (Options & option) == option; } } } From 54df6d9791a879f91faf9f77eab315dd7d158f17 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 24 Jan 2020 17:42:24 +0100 Subject: [PATCH 08/15] make sure duplicate socket is not inheritable --- .../Kernel32/Interop.HandleInformation.cs | 24 ++++++++++ .../WinSock/Interop.WSADuplicateSocket.cs | 6 ++- .../src/System.Net.Sockets.csproj | 3 ++ .../System/Net/Sockets/SocketPal.Windows.cs | 23 +++++++-- .../FunctionalTests/SocketDuplicationTests.cs | 47 +++++++++++++++++++ 5 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs new file mode 100644 index 00000000000000..baee76e40ae878 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [Flags] + internal enum HandleFlags: uint + { + None = 0, + Inherit = 1, + ProtectFromClose = 2 + } + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool SetHandleInformation(SafeHandle handle, HandleFlags mask, HandleFlags flags); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs index f8e30f3291ac76..6c713c8ea9d45f 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Net.Sockets; using System.Runtime.InteropServices; diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index b6f74d901fce09..204bd3ebd9bb23 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -239,6 +239,9 @@ Common\Interop\Windows\Kernel32\Interop.SetFileCompletionNotificationModes.cs + + Common\Interop\Windows\Kernel32\Interop.HandleInformation.cs + Common\System\Net\CompletionPortHelper.Windows.cs diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index 852f64c0eca824..cfec6fd2a0938f 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -40,9 +40,13 @@ public static SocketError GetLastSocketError() return (SocketError)win32Error; } + [DllImport("kernel32.dll")] + private static extern bool GetHandleInformation(SafeHandle hObject, out Interop.Kernel32.HandleFlags flags); + public static SocketError CreateSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, out SafeSocketHandle socket) { - IntPtr handle = Interop.Winsock.WSASocketW(addressFamily, socketType, protocolType, IntPtr.Zero, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED | Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT);; + IntPtr handle = Interop.Winsock.WSASocketW(addressFamily, socketType, protocolType, IntPtr.Zero, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED | + Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT); socket = new SafeSocketHandle(handle, ownsHandle: true); if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); @@ -62,13 +66,22 @@ public static unsafe SocketError CreateSocket( throw new ArgumentException(SR.net_sockets_invalid_socketinformation, nameof(socketInformation)); } - fixed (byte* pinnedBuffer = socketInformation.ProtocolInformation) + fixed (byte* pinnedProtocolInformation = socketInformation.ProtocolInformation) { + // Sockets are non-inheritable in .NET Core. + // Handle properties like HANDLE_FLAG_INHERIT are not cloned with socket duplication, therefore + // we need to disable handle inheritance when constructing the new socket handle from Protcol Info. + // Additionally, it looks like WSA_FLAG_NO_HANDLE_INHERIT has no effect when being used with the Protocol Info + // variant of WSASocketW, so it is being passed to that call only for consistency. + // Inheritance is being disabled with SetHandleInformation(...) after the WSASocketW call. IntPtr handle = Interop.Winsock.WSASocketW( (AddressFamily)(-1), (SocketType)(-1), (ProtocolType)(-1), - (IntPtr)pinnedBuffer, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED); + (IntPtr)pinnedProtocolInformation, + 0, + Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED | + Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT); socket = new SafeSocketHandle(handle, ownsHandle: true); if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); @@ -78,7 +91,9 @@ public static unsafe SocketError CreateSocket( return GetLastSocketError(); } - Interop.Winsock.WSAProtocolInfo protocolInfo = Marshal.PtrToStructure((IntPtr)pinnedBuffer); + Interop.Kernel32.SetHandleInformation(socket, Interop.Kernel32.HandleFlags.Inherit, 0); + + Interop.Winsock.WSAProtocolInfo protocolInfo = Marshal.PtrToStructure((IntPtr)pinnedProtocolInformation); addressFamily = protocolInfo.AddressFamily; socketType = protocolInfo.SocketType; protocolType = protocolInfo.ProtocolType; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index c41eec91387a8f..81dd635d3c8d08 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -142,6 +142,53 @@ public async Task DuplicateAndClose_TcpListener() Assert.Equal(TestMessage, receivedMessage); } + [OuterLoop] // long-running + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public void DuplicateSocket_IsNotInheritable() + { + // The test is based on CreateSocket.CtorAndAccept_SocketNotKeptAliveViaInheritance, + // but contains simpler validation logic, sufficient to test the behavior on Windows + static void RunTest() + { + using Socket listenerProto = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + listenerProto.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + listenerProto.Listen(1); + EndPoint ep = listenerProto.LocalEndPoint; + + using Socket listenerDuplicate = new Socket(listenerProto.DuplicateAndClose(CurrentProcessId)); + + using var serverPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable); + + static void ChildProcessBody(string clientPipeHandle) + { + using var clientPipe = new AnonymousPipeClientStream(PipeDirection.In, clientPipeHandle); + Assert.Equal(42, clientPipe.ReadByte()); + } + + // Create a child process that blocks waiting to receive a signal on the anonymous pipe. + // The whole purpose of the child is to test whether handles are inherited, so we + // keep the child process alive until we're done validating that handles close as expected. + using (RemoteExecutor.Invoke(ChildProcessBody, serverPipe.GetClientHandleAsString())) + { + using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + // Close the listening socket: + listenerDuplicate.Dispose(); + + // Validate that we after closing the listening socket, we're not able to connect: + SocketException ex = Assert.ThrowsAny(() => client.Connect(ep)); + Debug.Print(ex.Message); + + serverPipe.WriteByte(42); + } + } + + // Run the test in another process so as to not have trouble with other tests + // launching child processes that might impact inheritance. + RemoteExecutor.Invoke(RunTest).Dispose(); + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] public async Task DoAsyncOperation_OnBothOriginalAndClone_ThrowsInvalidOperationException() From 7ea829a9f76ef2ca5f3c23405a2e3ffdfb36c9b2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 27 Jan 2020 15:47:38 +0100 Subject: [PATCH 09/15] remove debug code leftover --- .../src/System/Net/Sockets/SocketPal.Windows.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index cfec6fd2a0938f..03972049a0c6c0 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -40,9 +40,6 @@ public static SocketError GetLastSocketError() return (SocketError)win32Error; } - [DllImport("kernel32.dll")] - private static extern bool GetHandleInformation(SafeHandle hObject, out Interop.Kernel32.HandleFlags flags); - public static SocketError CreateSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, out SafeSocketHandle socket) { IntPtr handle = Interop.Winsock.WSASocketW(addressFamily, socketType, protocolType, IntPtr.Zero, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED | From 6d9d5af99b9c9aba9af8a62517da90662083ea83 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 31 Jan 2020 02:55:51 +0100 Subject: [PATCH 10/15] harden SocketPal.CreateSocket() --- .../System/Net/Sockets/SocketPal.Windows.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index 03972049a0c6c0..e813ba8bf1b14a 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -46,9 +46,16 @@ public static SocketError CreateSocket(AddressFamily addressFamily, SocketType s Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT); socket = new SafeSocketHandle(handle, ownsHandle: true); - if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); + if (socket.IsInvalid) + { + SocketError error = GetLastSocketError(); + if (NetEventSource.IsEnabled) NetEventSource.Error(null, $"WSASocketW failed with error {error}"); + socket.Dispose(); + return error; + } - return socket.IsInvalid ? GetLastSocketError() : SocketError.Success; + if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); + return SocketError.Success; } public static unsafe SocketError CreateSocket( @@ -81,14 +88,27 @@ public static unsafe SocketError CreateSocket( Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT); socket = new SafeSocketHandle(handle, ownsHandle: true); - if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); if (socket.IsInvalid) { - return GetLastSocketError(); + SocketError error = GetLastSocketError(); + if (NetEventSource.IsEnabled) NetEventSource.Error(null, $"WSASocketW failed with error {error}"); + socket.Dispose(); + return error; + } + + if (!Interop.Kernel32.SetHandleInformation(socket, Interop.Kernel32.HandleFlags.Inherit, 0)) + { + SocketError error = GetLastSocketError(); + if (NetEventSource.IsEnabled) NetEventSource.Error(null, $"SetHandleInformation failed with error {error}"); + socket.Dispose(); + + // Returning SocketError here for consistency, + // the call site can handle it and pass the error code to SocketException() + return error; } - Interop.Kernel32.SetHandleInformation(socket, Interop.Kernel32.HandleFlags.Inherit, 0); + if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); Interop.Winsock.WSAProtocolInfo protocolInfo = Marshal.PtrToStructure((IntPtr)pinnedProtocolInformation); addressFamily = protocolInfo.AddressFamily; From b52c214f5e4768e47fb3e65975a69abd435bc624 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 31 Jan 2020 16:07:19 +0100 Subject: [PATCH 11/15] blittable WSAPROTOCOL_INFOW --- .../WinSock/Interop.WSADuplicateSocket.cs | 56 ++++++++++--------- .../System/Net/Sockets/SocketPal.Windows.cs | 25 +++++---- .../FunctionalTests/SocketDuplicationTests.cs | 18 ++++++ 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs index 6c713c8ea9d45f..95099f7d727585 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs @@ -11,46 +11,48 @@ internal static partial class Interop internal static partial class Winsock { [StructLayout(LayoutKind.Sequential)] - internal struct WSAProtocolChain + internal unsafe struct WSAPROTOCOLCHAIN { + private const int MAX_PROTOCOL_CHAIN = 7; + internal int ChainLen; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=7)] - internal uint[] ChainEntries; + internal fixed uint ChainEntries[MAX_PROTOCOL_CHAIN]; } - [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] - internal struct WSAProtocolInfo + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal unsafe struct WSAPROTOCOL_INFOW { - internal uint ServiceFlags1; - internal uint ServiceFlags2; - internal uint ServiceFlags3; - internal uint ServiceFlags4; - internal uint ProviderFlags; + private const int WSAPROTOCOL_LEN = 255; + + internal uint dwServiceFlags1; + internal uint dwServiceFlags2; + internal uint dwServiceFlags3; + internal uint dwServiceFlags4; + internal uint dwProviderFlags; internal Guid ProviderId; - internal uint CatalogEntryId; - internal WSAProtocolChain ProtocolChain; - internal int Version; - internal AddressFamily AddressFamily; - internal int MaxSockAddr; - internal int MinSockAddr; - internal SocketType SocketType; - internal ProtocolType ProtocolType; - internal int ProtocolMaxOffset; - internal int NetworkByteOrder; - internal int SecurityScheme; - internal uint MessageSize; - internal uint ProviderReserved; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - internal string ProtocolName; + internal uint dwCatalogEntryId; + internal WSAPROTOCOLCHAIN ProtocolChain; + internal int iVersion; + internal AddressFamily iAddressFamily; + internal int iMaxSockAddr; + internal int iMinSockAddr; + internal SocketType iSocketType; + internal ProtocolType iProtocol; + internal int iProtocolMaxOffset; + internal int iNetworkByteOrder; + internal int iSecurityScheme; + internal uint dwMessageSize; + internal uint dwProviderReserved; + internal fixed char szProtocol[WSAPROTOCOL_LEN + 1]; - public static readonly int Size = Marshal.SizeOf(typeof(WSAProtocolInfo)); + public static readonly int Size = sizeof(WSAPROTOCOL_INFOW); } [DllImport(Interop.Libraries.Ws2_32, SetLastError = true)] internal static extern unsafe int WSADuplicateSocket( [In] SafeSocketHandle socketHandle, [In] uint targetProcessId, - [In] byte* lpProtocolInfo + [In] WSAPROTOCOL_INFOW* lpProtocolInfo ); } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index e813ba8bf1b14a..b0fa2cf56e2f08 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -65,12 +65,12 @@ public static unsafe SocketError CreateSocket( ref SocketType socketType, ref ProtocolType protocolType) { - if (socketInformation.ProtocolInformation == null || socketInformation.ProtocolInformation.Length < Interop.Winsock.WSAProtocolInfo.Size) + if (socketInformation.ProtocolInformation == null || socketInformation.ProtocolInformation.Length < Interop.Winsock.WSAPROTOCOL_INFOW.Size) { throw new ArgumentException(SR.net_sockets_invalid_socketinformation, nameof(socketInformation)); } - fixed (byte* pinnedProtocolInformation = socketInformation.ProtocolInformation) + fixed (byte* protocolInfoBytes = socketInformation.ProtocolInformation) { // Sockets are non-inheritable in .NET Core. // Handle properties like HANDLE_FLAG_INHERIT are not cloned with socket duplication, therefore @@ -82,7 +82,7 @@ public static unsafe SocketError CreateSocket( (AddressFamily)(-1), (SocketType)(-1), (ProtocolType)(-1), - (IntPtr)pinnedProtocolInformation, + (IntPtr)protocolInfoBytes, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED | Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT); @@ -99,21 +99,21 @@ public static unsafe SocketError CreateSocket( if (!Interop.Kernel32.SetHandleInformation(socket, Interop.Kernel32.HandleFlags.Inherit, 0)) { + // Returning SocketError for consistency, since the call site can deal with conversion, and + // the most common SetHandleInformation error (AccessDenied) is included in SocketError anyways: SocketError error = GetLastSocketError(); if (NetEventSource.IsEnabled) NetEventSource.Error(null, $"SetHandleInformation failed with error {error}"); socket.Dispose(); - // Returning SocketError here for consistency, - // the call site can handle it and pass the error code to SocketException() return error; } if (NetEventSource.IsEnabled) NetEventSource.Info(null, socket); - Interop.Winsock.WSAProtocolInfo protocolInfo = Marshal.PtrToStructure((IntPtr)pinnedProtocolInformation); - addressFamily = protocolInfo.AddressFamily; - socketType = protocolInfo.SocketType; - protocolType = protocolInfo.ProtocolType; + Interop.Winsock.WSAPROTOCOL_INFOW* protocolInfo = (Interop.Winsock.WSAPROTOCOL_INFOW*)protocolInfoBytes; + addressFamily = protocolInfo->iAddressFamily; + socketType = protocolInfo->iSocketType; + protocolType = protocolInfo->iProtocol; return SocketError.Success; } @@ -1321,12 +1321,13 @@ internal static unsafe SocketError DuplicateSocket(SafeSocketHandle handle, int { socketInformation = new SocketInformation { - ProtocolInformation = new byte[Interop.Winsock.WSAProtocolInfo.Size] + ProtocolInformation = new byte[Interop.Winsock.WSAPROTOCOL_INFOW.Size] }; - fixed (byte* pinnedBuffer = socketInformation.ProtocolInformation) + fixed (byte* protocolInfoBytes = socketInformation.ProtocolInformation) { - int result = Interop.Winsock.WSADuplicateSocket(handle, (uint)targetProcessId, pinnedBuffer); + Interop.Winsock.WSAPROTOCOL_INFOW* lpProtocolInfo = (Interop.Winsock.WSAPROTOCOL_INFOW*)protocolInfoBytes; + int result = Interop.Winsock.WSADuplicateSocket(handle, (uint)targetProcessId, lpProtocolInfo); return result == 0 ? SocketError.Success : GetLastSocketError(); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index 81dd635d3c8d08..34f610769b0c90 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -18,6 +18,8 @@ namespace System.Net.Sockets.Tests { // Test cases for DuplicateAndClose, Socket(socketInformation), Socket.UseOnlyOverlappedIO, // and asynchronous IO behavior for duplicate sockets. + // Since the constructor Socket(socketInformation) is strongly coupled + // with the rest of the duplication logic, it's being tested here instead of CreateSocketTests. public class SocketDuplicationTests { private readonly ArraySegment _receiveBuffer = new ArraySegment(new byte[32]); @@ -75,6 +77,22 @@ public void BlockingState_IsTransferred(bool blocking) Assert.Equal(blocking, clone.Blocking); } + [Theory] + [InlineData(null)] // ProtocolInformation == null + [InlineData(1)] // ProtocolInformation too short + [InlineData(1000)] // corrupt ProtocolInformation + [PlatformSpecific(TestPlatforms.Windows)] + public void SocketCtr_InvalidProtocolInformation_ThrowsArgumentException(int? protocolInfoLength) + { + SocketInformation invalidInfo = new SocketInformation(); + if (protocolInfoLength != null) + { + invalidInfo.ProtocolInformation = new byte[protocolInfoLength.Value]; + } + + Assert.Throws(() => new Socket(invalidInfo)); + } + [PlatformSpecific(TestPlatforms.AnyUnix)] [Fact] public void SocketCtr_SocketInformation_Unix_ThrowsPlatformNotSupportedException() From ab6004b235ce149f861b93d8ae9b8bb8072498a9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 31 Jan 2020 16:22:55 +0100 Subject: [PATCH 12/15] additional naming fixes, default System.Net.Sockets.sln configurations to Windows_NT again --- .../src/Interop/Windows/Kernel32/Interop.HandleInformation.cs | 4 ++-- src/libraries/System.Net.Sockets/System.Net.Sockets.sln | 4 ++-- .../src/System/Net/Sockets/SocketPal.Windows.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs index baee76e40ae878..1b5358daf210d7 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs @@ -14,8 +14,8 @@ internal static partial class Kernel32 internal enum HandleFlags: uint { None = 0, - Inherit = 1, - ProtectFromClose = 2 + HANDLE_FLAG_INHERIT = 1, + HANDLE_FLAG_PROTECT_FROM_CLOSE = 2 } [DllImport(Libraries.Kernel32, SetLastError = true)] diff --git a/src/libraries/System.Net.Sockets/System.Net.Sockets.sln b/src/libraries/System.Net.Sockets/System.Net.Sockets.sln index 85d259ae957386..f526d469cfc679 100644 --- a/src/libraries/System.Net.Sockets/System.Net.Sockets.sln +++ b/src/libraries/System.Net.Sockets/System.Net.Sockets.sln @@ -30,8 +30,8 @@ Global {8CBA022C-635F-4C8D-9D29-CD8AAC68C8E6}.Debug|Any CPU.Build.0 = netcoreapp5.0-Windows_NT-Debug|Any CPU {8CBA022C-635F-4C8D-9D29-CD8AAC68C8E6}.Release|Any CPU.ActiveCfg = netcoreapp5.0-Windows_NT-Release|Any CPU {8CBA022C-635F-4C8D-9D29-CD8AAC68C8E6}.Release|Any CPU.Build.0 = netcoreapp5.0-Windows_NT-Release|Any CPU - {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|Any CPU.ActiveCfg = netcoreapp5.0-Unix-Debug|Any CPU - {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|Any CPU.Build.0 = netcoreapp5.0-Unix-Debug|Any CPU + {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|Any CPU.ActiveCfg = netcoreapp5.0-Windows_NT-Debug|Any CPU + {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|Any CPU.Build.0 = netcoreapp5.0-Windows_NT-Debug|Any CPU {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Release|Any CPU.ActiveCfg = netcoreapp5.0-Windows_NT-Release|Any CPU {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Release|Any CPU.Build.0 = netcoreapp5.0-Windows_NT-Release|Any CPU {834E3534-6A11-4A8D-923F-35C1E71CCEC3}.Debug|Any CPU.ActiveCfg = netcoreapp5.0-Debug|Any CPU diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index b0fa2cf56e2f08..2ef5667fcfc142 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -97,7 +97,7 @@ public static unsafe SocketError CreateSocket( return error; } - if (!Interop.Kernel32.SetHandleInformation(socket, Interop.Kernel32.HandleFlags.Inherit, 0)) + if (!Interop.Kernel32.SetHandleInformation(socket, Interop.Kernel32.HandleFlags.HANDLE_FLAG_INHERIT, 0)) { // Returning SocketError for consistency, since the call site can deal with conversion, and // the most common SetHandleInformation error (AccessDenied) is included in SocketError anyways: From 502619f6128187e913b401389aff4ea183efaa06 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 3 Feb 2020 15:34:10 +0100 Subject: [PATCH 13/15] disposal on errors, improve comments, more tests --- .../Kernel32/Interop.HandleInformation.cs | 2 +- .../WinSock/Interop.WSADuplicateSocket.cs | 2 +- .../Net/Sockets/SafeSocketHandle.Windows.cs | 2 +- .../src/System/Net/Sockets/Socket.Unix.cs | 10 ++--- .../src/System/Net/Sockets/Socket.Windows.cs | 8 ++-- .../System/Net/Sockets/SocketPal.Windows.cs | 2 +- .../FunctionalTests/SocketDuplicationTests.cs | 37 ++++++++++++++++--- 7 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs index 1b5358daf210d7..ad05756408e730 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs @@ -11,7 +11,7 @@ internal static partial class Interop internal static partial class Kernel32 { [Flags] - internal enum HandleFlags: uint + internal enum HandleFlags : uint { None = 0, HANDLE_FLAG_INHERIT = 1, diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs index 95099f7d727585..a19ac746e49d83 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs @@ -48,7 +48,7 @@ internal unsafe struct WSAPROTOCOL_INFOW public static readonly int Size = sizeof(WSAPROTOCOL_INFOW); } - [DllImport(Interop.Libraries.Ws2_32, SetLastError = true)] + [DllImport(Interop.Libraries.Ws2_32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe int WSADuplicateSocket( [In] SafeSocketHandle socketHandle, [In] uint targetProcessId, diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs index 00cd87b582ef59..30340947d126bd 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Windows.cs @@ -71,7 +71,7 @@ internal ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle(bool trySkipCo if (alreadyBound) { - throw new InvalidOperationException(SR.net_sockets_asyncoperations_notallowed); + throw new InvalidOperationException(SR.net_sockets_asyncoperations_notallowed, exception); } throw; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs index 6c95f9634020cb..fc9ef1dc17de6a 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs @@ -13,20 +13,16 @@ public partial class Socket { public Socket(SocketInformation socketInformation) { - // // This constructor works in conjunction with DuplicateAndClose, which is not supported on Unix. // See comments in DuplicateAndClose. - // throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); } public SocketInformation DuplicateAndClose(int targetProcessId) { - // - // DuplicateAndClose is not supported on Unix, since passing FD-s between processes - // should involve Unix Domain Sockets. The programming model is fundamentally different, - // and incompatible with the design of SocketInformation API-s. - // + // DuplicateAndClose is not supported on Unix, since passing file descriptors between processes + // requires Unix Domain Sockets. The programming model is fundamentally different, + // and incompatible with the design of SocketInformation-related methods. throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index b0932e1fce224e..73c12e68762bc1 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -29,6 +29,7 @@ public Socket(SocketInformation socketInformation) if (errorCode != SocketError.Success) { Debug.Assert(_handle.IsInvalid); + _handle = null; if (errorCode == SocketError.InvalidArgument) { @@ -39,13 +40,10 @@ public Socket(SocketInformation socketInformation) throw new SocketException((int)errorCode); } - if (_handle.IsInvalid) - { - throw new SocketException(); - } - if (_addressFamily != AddressFamily.InterNetwork && _addressFamily != AddressFamily.InterNetworkV6) { + _handle.Dispose(); + _handle = null; throw new NotSupportedException(SR.net_invalidversion); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index 2ef5667fcfc142..d1616d903829c1 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -74,7 +74,7 @@ public static unsafe SocketError CreateSocket( { // Sockets are non-inheritable in .NET Core. // Handle properties like HANDLE_FLAG_INHERIT are not cloned with socket duplication, therefore - // we need to disable handle inheritance when constructing the new socket handle from Protcol Info. + // we need to disable handle inheritance when constructing the new socket handle from Protocol Info. // Additionally, it looks like WSA_FLAG_NO_HANDLE_INHERIT has no effect when being used with the Protocol Info // variant of WSASocketW, so it is being passed to that call only for consistency. // Inheritance is being disabled with SetHandleInformation(...) after the WSASocketW call. diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index 34f610769b0c90..dc89fd3bf5cef3 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -29,6 +29,7 @@ private static string GetMessageString(ArraySegment data, int count) => Encoding.ASCII.GetString(data.AsSpan().Slice(0, count)); private static int CurrentProcessId => Process.GetCurrentProcess().Id; + [Fact] public void UseOnlyOverlappedIO_AlwaysFalse() { @@ -229,6 +230,18 @@ await Assert.ThrowsAsync(() => } } + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public void SocketCtr_SocketInformation_NonIpSocket_ThrowsNotSupportedException() + { + // UDS unsupported: + if (!PlatformDetection.IsWindows10Version1803OrGreater || !Environment.Is64BitProcess) return; + + using Socket original = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); + SocketInformation info = original.DuplicateAndClose(CurrentProcessId); + Assert.ThrowsAny(() => _ = new Socket(info)); + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] public void SocketCtr_SocketInformation_WhenProtocolInformationIsNull_Throws() @@ -277,16 +290,28 @@ private static SocketInformation ReadSocketInfo(Stream stream) return result; } + public static readonly TheoryData TcpServerHandlerData = + new TheoryData() + { + { AddressFamily.InterNetwork, false }, + { AddressFamily.InterNetwork, true }, + { AddressFamily.InterNetworkV6, false }, + { AddressFamily.InterNetworkV6, true }, + }; + [Theory] [PlatformSpecific(TestPlatforms.Windows)] - [InlineData(false)] - [InlineData(true)] - public async Task DuplicateAndClose_TcpServerHandler(bool sameProcess) + [MemberData(nameof(TcpServerHandlerData))] + public async Task DuplicateAndClose_TcpServerHandler(AddressFamily addressFamily, bool sameProcess) { - using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + IPAddress address = addressFamily == AddressFamily.InterNetwork + ? IPAddress.Loopback + : IPAddress.IPv6Loopback; + + using Socket listener = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp); + using Socket client = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp); - listener.BindToAnonymousPort(IPAddress.Loopback); + listener.BindToAnonymousPort(address); listener.Listen(1); client.Connect(listener.LocalEndPoint); From 9a476af19025ed4ab214a7bee5d1bf8501ba61ec Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 3 Feb 2020 20:51:04 +0100 Subject: [PATCH 14/15] nits --- .../Windows/Kernel32/Interop.HandleInformation.cs | 2 +- .../Windows/WinSock/Interop.WSADuplicateSocket.cs | 6 ++---- .../src/System/Net/Sockets/SocketPal.Windows.cs | 4 ++-- .../tests/FunctionalTests/SocketDuplicationTests.cs | 10 ++++++++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs index ad05756408e730..5a6290c454dad9 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleInformation.cs @@ -19,6 +19,6 @@ internal enum HandleFlags : uint } [DllImport(Libraries.Kernel32, SetLastError = true)] - internal static extern bool SetHandleInformation(SafeHandle handle, HandleFlags mask, HandleFlags flags); + internal static extern bool SetHandleInformation(SafeHandle hObject, HandleFlags dwMask, HandleFlags dwFlags); } } diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs index a19ac746e49d83..23fa0328c440d3 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WSADuplicateSocket.cs @@ -44,14 +44,12 @@ internal unsafe struct WSAPROTOCOL_INFOW internal uint dwMessageSize; internal uint dwProviderReserved; internal fixed char szProtocol[WSAPROTOCOL_LEN + 1]; - - public static readonly int Size = sizeof(WSAPROTOCOL_INFOW); } [DllImport(Interop.Libraries.Ws2_32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe int WSADuplicateSocket( - [In] SafeSocketHandle socketHandle, - [In] uint targetProcessId, + [In] SafeSocketHandle s, + [In] uint dwProcessId, [In] WSAPROTOCOL_INFOW* lpProtocolInfo ); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index d1616d903829c1..8118e477813776 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -65,7 +65,7 @@ public static unsafe SocketError CreateSocket( ref SocketType socketType, ref ProtocolType protocolType) { - if (socketInformation.ProtocolInformation == null || socketInformation.ProtocolInformation.Length < Interop.Winsock.WSAPROTOCOL_INFOW.Size) + if (socketInformation.ProtocolInformation == null || socketInformation.ProtocolInformation.Length < sizeof(Interop.Winsock.WSAPROTOCOL_INFOW)) { throw new ArgumentException(SR.net_sockets_invalid_socketinformation, nameof(socketInformation)); } @@ -1321,7 +1321,7 @@ internal static unsafe SocketError DuplicateSocket(SafeSocketHandle handle, int { socketInformation = new SocketInformation { - ProtocolInformation = new byte[Interop.Winsock.WSAPROTOCOL_INFOW.Size] + ProtocolInformation = new byte[sizeof(Interop.Winsock.WSAPROTOCOL_INFOW)] }; fixed (byte* protocolInfoBytes = socketInformation.ProtocolInformation) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index dc89fd3bf5cef3..7aababd00fd90d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -23,12 +23,18 @@ namespace System.Net.Sockets.Tests public class SocketDuplicationTests { private readonly ArraySegment _receiveBuffer = new ArraySegment(new byte[32]); - const string TestMessage = "test123!"; + private const string TestMessage = "test123!"; private static ArraySegment TestBytes => Encoding.ASCII.GetBytes(TestMessage); private static string GetMessageString(ArraySegment data, int count) => Encoding.ASCII.GetString(data.AsSpan().Slice(0, count)); - private static int CurrentProcessId => Process.GetCurrentProcess().Id; + private static readonly Lazy s_currentProcessId = new Lazy(() => + { + using var process = Process.GetCurrentProcess(); + return process.Id; + }, LazyThreadSafetyMode.PublicationOnly); + + private static int CurrentProcessId => s_currentProcessId.Value; [Fact] public void UseOnlyOverlappedIO_AlwaysFalse() From 4b0ccb206e8de2727430b15790275b4b269f59d5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 3 Feb 2020 22:08:59 +0100 Subject: [PATCH 15/15] handle GetSockName() error --- .../src/System/Net/Sockets/Socket.Windows.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index 73c12e68762bc1..741b9f65d440db 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -57,10 +57,21 @@ public Socket(SocketInformation socketInformation) Internals.SocketAddress socketAddress = IPEndPointExtensions.Serialize(ep); errorCode = SocketPal.GetSockName(_handle, socketAddress.Buffer, ref socketAddress.InternalSize); + if (errorCode == SocketError.Success) { _rightEndPoint = ep.Create(socketAddress); } + else if (errorCode == SocketError.InvalidArgument) + { + // Socket is not yet bound. + } + else + { + _handle.Dispose(); + _handle = null; + throw new SocketException((int)errorCode); + } if (NetEventSource.IsEnabled) NetEventSource.Exit(this); }