Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 916f352

Browse files
committed
Do not inherit socket handles #2789
1 parent 1d3090f commit 916f352

File tree

3 files changed

+144
-0
lines changed

3 files changed

+144
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Net.Sockets;
6+
using System.Runtime.InteropServices;
7+
8+
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
9+
{
10+
internal static class NativeMethods
11+
{
12+
[DllImport("kernel32.dll", SetLastError = true)]
13+
private static extern bool SetHandleInformation(IntPtr hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags);
14+
15+
[Flags]
16+
private enum HANDLE_FLAGS : uint
17+
{
18+
None = 0,
19+
INHERIT = 1,
20+
PROTECT_FROM_CLOSE = 2
21+
}
22+
23+
internal static void DisableHandleInheritance(Socket socket)
24+
{
25+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
26+
{
27+
SetHandleInformation(socket.Handle, HANDLE_FLAGS.INHERIT, 0);
28+
}
29+
}
30+
}
31+
}

src/Kestrel.Transport.Sockets/SocketTransport.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public Task BindAsync()
8080
IPEndPoint endPoint = _endPointInformation.IPEndPoint;
8181

8282
var listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
83+
NativeMethods.DisableHandleInheritance(listenSocket);
8384

8485
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
8586
if (endPoint.Address == IPAddress.IPv6Any)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.IO;
7+
using System.Net.Sockets;
8+
using System.Runtime.InteropServices;
9+
using System.Threading.Tasks;
10+
using Microsoft.AspNetCore.Builder;
11+
using Microsoft.AspNetCore.Hosting;
12+
using Microsoft.AspNetCore.Http;
13+
using Microsoft.AspNetCore.Testing;
14+
using Microsoft.AspNetCore.Testing.xunit;
15+
using Xunit;
16+
17+
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
18+
{
19+
public class HandleInheritanceTests : TestApplicationErrorLoggerLoggedTest
20+
{
21+
[ConditionalFact]
22+
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "No fix available for Mac https://github.com/aspnet/KestrelHttpServer/pull/2944#issuecomment-426397600")]
23+
public async Task SpawnChildProcess_DoesNotInheritListenHandle()
24+
{
25+
var hostBuilder = TransportSelector.GetWebHostBuilder()
26+
.UseKestrel()
27+
.ConfigureServices(AddTestLogging)
28+
.UseUrls("http://127.0.0.1:0")
29+
.Configure(app =>
30+
{
31+
app.Run(context =>
32+
{
33+
return context.Response.WriteAsync("Hello World");
34+
});
35+
});
36+
37+
using (var host = hostBuilder.Build())
38+
{
39+
await host.StartAsync();
40+
41+
var processInfo = new ProcessStartInfo
42+
{
43+
FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "vi",
44+
CreateNoWindow = true,
45+
};
46+
using (var process = Process.Start(processInfo))
47+
{
48+
var port = host.GetPort();
49+
await host.StopAsync();
50+
51+
// We should not be able to connect if the handle was correctly closed and not inherited by the child process.
52+
using (var client = new TcpClient())
53+
{
54+
await Assert.ThrowsAnyAsync<SocketException>(() => client.ConnectAsync("127.0.0.1", port));
55+
}
56+
57+
process.Kill();
58+
}
59+
}
60+
}
61+
62+
[Fact]
63+
public async Task SpawnChildProcess_DoesNotInheritConnectionHandle()
64+
{
65+
var hostBuilder = TransportSelector.GetWebHostBuilder()
66+
.UseKestrel()
67+
.ConfigureServices(AddTestLogging)
68+
.UseUrls("http://127.0.0.1:0")
69+
.Configure(app =>
70+
{
71+
app.Run(context =>
72+
{
73+
return context.Response.WriteAsync("Hello World");
74+
});
75+
});
76+
77+
using (var host = hostBuilder.Build())
78+
{
79+
await host.StartAsync();
80+
81+
using (var client = new TcpClient())
82+
{
83+
// First connect and then spawn a child process
84+
await client.ConnectAsync("127.0.0.1", host.GetPort());
85+
var stream = client.GetStream();
86+
87+
var processInfo = new ProcessStartInfo
88+
{
89+
FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "vi",
90+
CreateNoWindow = true,
91+
};
92+
using (var process = Process.Start(processInfo))
93+
{
94+
await host.StopAsync();
95+
96+
// The connection should fail when the server shuts down if it wasn't inherited by the child process.
97+
try
98+
{
99+
var read = await stream.ReadAsync(new byte[100], 0, 100).DefaultTimeout();
100+
Assert.Equal(0, read);
101+
}
102+
catch (IOException)
103+
{
104+
}
105+
106+
process.Kill();
107+
}
108+
}
109+
}
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)