diff --git a/src/Common/src/Interop/Windows/Winsock/Interop.SocketConstructorFlags.cs b/src/Common/src/Interop/Windows/Winsock/Interop.SocketConstructorFlags.cs
new file mode 100644
index 000000000000..f3b61aab6252
--- /dev/null
+++ b/src/Common/src/Interop/Windows/Winsock/Interop.SocketConstructorFlags.cs
@@ -0,0 +1,25 @@
+// 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;
+
+internal static partial class Interop
+{
+ internal static partial class Winsock
+ {
+ // Used as last parameter to WSASocket call.
+ [Flags]
+ internal enum SocketConstructorFlags
+ {
+ WSA_FLAG_OVERLAPPED = 0x01,
+ WSA_FLAG_MULTIPOINT_C_ROOT = 0x02,
+ WSA_FLAG_MULTIPOINT_C_LEAF = 0x04,
+ WSA_FLAG_MULTIPOINT_D_ROOT = 0x08,
+ WSA_FLAG_MULTIPOINT_D_LEAF = 0x10,
+ WSA_FLAG_NO_HANDLE_INHERIT = 0x80,
+ }
+ }
+}
diff --git a/src/Common/src/Interop/Windows/Winsock/Interop.WSASocketW.SafeCloseSocket.cs b/src/Common/src/Interop/Windows/Winsock/Interop.WSASocketW.SafeCloseSocket.cs
index 36e7a404078e..7dac8e9e30cb 100644
--- a/src/Common/src/Interop/Windows/Winsock/Interop.WSASocketW.SafeCloseSocket.cs
+++ b/src/Common/src/Interop/Windows/Winsock/Interop.WSASocketW.SafeCloseSocket.cs
@@ -10,17 +10,6 @@ internal static partial class Interop
{
internal static partial class Winsock
{
- // Used as last parameter to WSASocket call.
- [Flags]
- internal enum SocketConstructorFlags
- {
- WSA_FLAG_OVERLAPPED = 0x01,
- WSA_FLAG_MULTIPOINT_C_ROOT = 0x02,
- WSA_FLAG_MULTIPOINT_C_LEAF = 0x04,
- WSA_FLAG_MULTIPOINT_D_ROOT = 0x08,
- WSA_FLAG_MULTIPOINT_D_LEAF = 0x10,
- }
-
[DllImport(Interop.Libraries.Ws2_32, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SafeCloseSocket.InnerSafeCloseSocket WSASocketW(
[In] AddressFamily addressFamily,
diff --git a/src/Common/src/System/Net/SafeCloseSocket.Windows.cs b/src/Common/src/System/Net/SafeCloseSocket.Windows.cs
index bc1cdf754a38..1cb22cbd0dfb 100644
--- a/src/Common/src/System/Net/SafeCloseSocket.Windows.cs
+++ b/src/Common/src/System/Net/SafeCloseSocket.Windows.cs
@@ -217,7 +217,7 @@ private SocketError InnerReleaseHandle()
internal static InnerSafeCloseSocket CreateWSASocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
- InnerSafeCloseSocket result = Interop.Winsock.WSASocketW(addressFamily, socketType, protocolType, IntPtr.Zero, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED);
+ InnerSafeCloseSocket result = Interop.Winsock.WSASocketW(addressFamily, socketType, protocolType, IntPtr.Zero, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED | Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT);
if (result.IsInvalid)
{
result.SetHandleAsInvalid();
diff --git a/src/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs b/src/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
index 9717a2dda11a..9ab7db460425 100644
--- a/src/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
+++ b/src/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
@@ -63,7 +63,7 @@ private static bool IsProtocolSupported(AddressFamily af)
try
{
- s = Interop.Winsock.WSASocketW(af, SocketType.Dgram, 0, IntPtr.Zero, 0, 0);
+ s = Interop.Winsock.WSASocketW(af, SocketType.Dgram, 0, IntPtr.Zero, 0, (int)Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT);
if (s == IntPtr.Zero)
{
diff --git a/src/Native/Unix/System.Native/pal_networking.c b/src/Native/Unix/System.Native/pal_networking.c
index 380d7a24c468..96ffcc5c4a12 100644
--- a/src/Native/Unix/System.Native/pal_networking.c
+++ b/src/Native/Unix/System.Native/pal_networking.c
@@ -1978,7 +1978,15 @@ int32_t SystemNative_Socket(int32_t addressFamily, int32_t socketType, int32_t p
platformSocketType |= SOCK_CLOEXEC;
#endif
*createdSocket = socket(platformAddressFamily, platformSocketType, platformProtocolType);
- return *createdSocket != -1 ? Error_SUCCESS : SystemNative_ConvertErrorPlatformToPal(errno);
+ if (*createdSocket == -1)
+ {
+ return SystemNative_ConvertErrorPlatformToPal(errno);
+ }
+
+#ifndef SOCK_CLOEXEC
+ fcntl(ToFileDescriptor(*createdSocket), F_SETFD, FD_CLOEXEC); // ignore any failures; this is best effort
+#endif
+ return Error_SUCCESS;
}
int32_t SystemNative_GetAtOutOfBandMark(intptr_t socket, int32_t* atMark)
diff --git a/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj b/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj
index 61c73ebcdb26..a7e6b46976a6 100644
--- a/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj
+++ b/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj
@@ -103,6 +103,9 @@
Interop\Windows\Winsock\Interop.WSASocketW.cs
+
+ Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs
+
Common\System\Net\Sockets\ProtocolFamily.cs
@@ -198,4 +201,4 @@
-
\ No newline at end of file
+
diff --git a/src/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj b/src/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj
index 373429d382fe..ba2fc45c19aa 100644
--- a/src/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj
+++ b/src/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj
@@ -108,6 +108,9 @@
Interop\Windows\Winsock\Interop.WSASocketW.cs
+
+ Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs
+
Common\System\Net\Sockets\ProtocolFamily.cs
@@ -183,4 +186,4 @@
Interop\Unix\System.Native\Interop.SocketAddress.cs
-
\ No newline at end of file
+
diff --git a/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj b/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
index 90b86db3506e..b45e5145f515 100644
--- a/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
+++ b/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
@@ -206,6 +206,9 @@
Interop\Windows\Winsock\Interop.WSASocketW.SafeCloseSocket.cs
+
+ Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs
+
Interop\Windows\Winsock\WSABuffer.cs
@@ -358,4 +361,4 @@
-
\ No newline at end of file
+
diff --git a/src/System.Net.Ping/src/System.Net.Ping.csproj b/src/System.Net.Ping/src/System.Net.Ping.csproj
index 53fe1d44c5ae..d67821b68447 100644
--- a/src/System.Net.Ping/src/System.Net.Ping.csproj
+++ b/src/System.Net.Ping/src/System.Net.Ping.csproj
@@ -123,6 +123,9 @@
Common\Interop\Windows\Winsock\Interop.WSASocketW.cs
+
+ Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs
+
Common\System\Net\Sockets\SocketType.cs
@@ -151,4 +154,4 @@
-
\ No newline at end of file
+
diff --git a/src/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/System.Net.Sockets/src/System.Net.Sockets.csproj
index cf8db1b0d0cc..d462e97809f7 100644
--- a/src/System.Net.Sockets/src/System.Net.Sockets.csproj
+++ b/src/System.Net.Sockets/src/System.Net.Sockets.csproj
@@ -228,6 +228,9 @@
Interop\Windows\Winsock\Interop.WSASocketW.SafeCloseSocket.cs
+
+ Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs
+
Interop\Windows\Winsock\SafeNativeOverlapped.cs
@@ -403,4 +406,4 @@
-
\ No newline at end of file
+
diff --git a/src/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs b/src/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs
index ebbcc89011a7..58ab9d4714b8 100644
--- a/src/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs
+++ b/src/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs
@@ -2,11 +2,15 @@
// 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.IO;
+using System.IO.Pipes;
+using System.Threading.Tasks;
using Xunit;
namespace System.Net.Sockets.Tests
{
- public class CreateSocket
+ public class CreateSocket : RemoteExecutorTestBase
{
public static object[][] DualModeSuccessInputs = {
new object[] { SocketType.Stream, ProtocolType.Tcp },
@@ -89,9 +93,95 @@ public void Ctor_Failure(AddressFamily addressFamily, SocketType socketType, Pro
[ConditionalTheory(nameof(SupportsRawSockets))]
public void Ctor_Raw_Success(AddressFamily addressFamily, ProtocolType protocolType)
{
- using (new Socket(addressFamily, SocketType.Raw, protocolType))
- {
- }
+ using (new Socket(addressFamily, SocketType.Raw, protocolType))
+ {
+ }
+ }
+
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Sockets are still inheritable on netfx: https://github.com/dotnet/corefx/pull/32903")]
+ [Theory]
+ [InlineData(true, 0)] // Accept
+ [InlineData(false, 0)]
+ [InlineData(true, 1)] // AcceptAsync
+ [InlineData(false, 1)]
+ [InlineData(true, 2)] // Begin/EndAccept
+ [InlineData(false, 2)]
+ public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOuter, int acceptApiOuter)
+ {
+ // Run the test in another process so as to not have trouble with other tests
+ // launching child processes that might impact inheritance.
+ RemoteInvoke((validateClientString, acceptApiString) =>
+ {
+ bool validateClient = bool.Parse(validateClientString);
+ int acceptApi = int.Parse(acceptApiString);
+
+ // Create a listening server.
+ using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
+ listener.Listen(int.MaxValue);
+ EndPoint ep = listener.LocalEndPoint;
+
+ // Create a client and connect to that listener.
+ using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ client.Connect(ep);
+
+ // Accept the connection using one of multiple accept mechanisms.
+ Socket server =
+ acceptApi == 0 ? listener.Accept() :
+ acceptApi == 1 ? listener.AcceptAsync().GetAwaiter().GetResult() :
+ acceptApi == 2 ? Task.Factory.FromAsync(listener.BeginAccept, listener.EndAccept, null).GetAwaiter().GetResult() :
+ throw new Exception($"Unexpected {nameof(acceptApi)}: {acceptApi}");
+
+ // Get streams for the client and server, and create a pipe that we'll use
+ // to communicate with a child process.
+ using (var serverStream = new NetworkStream(server, ownsSocket: true))
+ using (var clientStream = new NetworkStream(client, ownsSocket: true))
+ using (var serverPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable))
+ {
+ // 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 (RemoteInvoke(clientPipeHandle =>
+ {
+ using (var clientPipe = new AnonymousPipeClientStream(PipeDirection.In, clientPipeHandle))
+ {
+ Assert.Equal(42, clientPipe.ReadByte());
+ }
+ }, serverPipe.GetClientHandleAsString()))
+ {
+ if (validateClient) // Validate that the child isn't keeping alive the "new Socket" for the client
+ {
+ // Send data from the server to client, then validate the client gets EOF when the server closes.
+ serverStream.WriteByte(84);
+ Assert.Equal(84, clientStream.ReadByte());
+ serverStream.Close();
+ Assert.Equal(-1, clientStream.ReadByte());
+ }
+ else // Validate that the child isn't keeping alive the "listener.Accept" for the server
+ {
+ // Send data from the client to server, then validate the server gets EOF when the client closes.
+ clientStream.WriteByte(84);
+ Assert.Equal(84, serverStream.ReadByte());
+ clientStream.Close();
+ Assert.Equal(-1, serverStream.ReadByte());
+ }
+
+ // And validate that we after closing the listening socket, we're not able to connect.
+ listener.Dispose();
+ using (var tmpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ Assert.ThrowsAny(() => tmpClient.Connect(ep));
+ }
+
+ // Let the child process terminate.
+ serverPipe.WriteByte(42);
+ }
+ }
+ }
+ }
+ }, validateClientOuter.ToString(), acceptApiOuter.ToString()).Dispose();
}
}
}