From eee9520ffdfb01f4c8a9d061e1ff41184910fb0c Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 8 Nov 2016 17:07:23 -0800 Subject: [PATCH] Ensure clients connecting to Kestrel's dispatch pipe are listeners --- .../Internal/Http/ListenerPrimary.cs | 61 ++++++++++++++++++- .../Internal/Http/ListenerSecondary.cs | 25 +++++++- .../Internal/Infrastructure/Constants.cs | 3 + .../Internal/Networking/UvWriteReq.cs | 38 +++++++++--- 4 files changed, 114 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerPrimary.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerPrimary.cs index a9b938d158..1bfc4c0ad0 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerPrimary.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerPrimary.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; @@ -75,15 +76,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http try { pipe.Accept(dispatchPipe); + + // Ensure client sends "Kestrel" before adding pipe to _dispatchPipes. + var readContext = new PipeReadContext(this); + dispatchPipe.ReadStart( + (handle, status2, state) => ((PipeReadContext)state).AllocCallback(handle, status2), + (handle, status2, state) => ((PipeReadContext)state).ReadCallback(handle, status2), + readContext); } catch (UvException ex) { dispatchPipe.Dispose(); Log.LogError(0, ex, "ListenerPrimary.OnListenPipe"); - return; } - - _dispatchPipes.Add(dispatchPipe); } protected override void DispatchConnection(UvStreamHandle socket) @@ -179,5 +184,55 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http }, this).ConfigureAwait(false); } } + + private class PipeReadContext + { + private readonly ListenerPrimary _listener; + private readonly IntPtr _bufPtr; + private GCHandle _bufHandle; + private int _bytesRead; + + public PipeReadContext(ListenerPrimary listener) + { + _listener = listener; + _bufHandle = GCHandle.Alloc(new byte[8], GCHandleType.Pinned); + _bufPtr = _bufHandle.AddrOfPinnedObject(); + } + + public Libuv.uv_buf_t AllocCallback(UvStreamHandle dispatchPipe, int suggestedSize) + { + return dispatchPipe.Libuv.buf_init(_bufPtr + _bytesRead, 8 - _bytesRead); + } + + public unsafe void ReadCallback(UvStreamHandle dispatchPipe, int status) + { + try + { + dispatchPipe.Libuv.Check(status); + + _bytesRead += status; + + if (_bytesRead == 8) + { + if (*(ulong*)_bufPtr == Constants.PipeMessage) + { + _listener._dispatchPipes.Add((UvPipeHandle) dispatchPipe); + dispatchPipe.ReadStop(); + _bufHandle.Free(); + } + else + { + throw new IOException("Bad data sent over Kestrel pipe."); + } + } + } + catch (Exception ex) + { + dispatchPipe.Dispose(); + _bufHandle.Free(); + _listener.Log.LogError(0, ex, "ListenerPrimary.ReadCallback"); + } + } + } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerSecondary.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerSecondary.cs index e11729200f..bd4d384fb2 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerSecondary.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ListenerSecondary.cs @@ -17,6 +17,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http /// public abstract class ListenerSecondary : ListenerContext, IAsyncDisposable { + private static ArraySegment> _pipeMessage = + new ArraySegment>(new[] { new ArraySegment(BitConverter.GetBytes(Constants.PipeMessage)) }); + private string _pipeName; private IntPtr _ptr; private Libuv.uv_buf_t _buf; @@ -89,6 +92,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http return; } + var writeReq = new UvWriteReq(Log); + try { DispatchPipe.ReadStart( @@ -96,10 +101,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http (handle, status2, state) => ((ListenerSecondary)state).ReadStartCallback(handle, status2), this); - tcs.SetResult(0); + writeReq.Init(Thread.Loop); + writeReq.Write( + DispatchPipe, + _pipeMessage, + (req, status2, ex, state) => + { + req.Dispose(); + + if (ex != null) + { + tcs.SetException(ex); + } + else + { + tcs.SetResult(0); + } + }, + tcs); } catch (Exception ex) { + writeReq.Dispose(); DispatchPipe.Dispose(); tcs.SetException(ex); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/Constants.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/Constants.cs index 2d270d9955..47b04be220 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/Constants.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/Constants.cs @@ -26,6 +26,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure public const string ServerName = "Kestrel"; + // "Kestrel\0" + public const ulong PipeMessage = 0x006C65727473654B; + private static int? GetECONNRESET() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Networking/UvWriteReq.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Networking/UvWriteReq.cs index 4a0aec28bc..93a06519a0 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Networking/UvWriteReq.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Networking/UvWriteReq.cs @@ -85,18 +85,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Networking _callback = null; _state = null; Unpin(this); - - var block = start.Block; - for (var index = 0; index < nBuffers; index++) - { - block = block.Next; - } - throw; } } - public unsafe void Write2( + public void Write( + UvStreamHandle handle, + ArraySegment> bufs, + Action callback, + object state) + { + WriteArraySegmentInternal(handle, bufs, sendHandle: null, callback: callback, state: state); + } + + public void Write2( + UvStreamHandle handle, + ArraySegment> bufs, + UvStreamHandle sendHandle, + Action callback, + object state) + { + WriteArraySegmentInternal(handle, bufs, sendHandle, callback, state); + } + + private unsafe void WriteArraySegmentInternal( UvStreamHandle handle, ArraySegment> bufs, UvStreamHandle sendHandle, @@ -133,7 +145,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Networking _callback = callback; _state = state; - _uv.write2(this, handle, pBuffers, nBuffers, sendHandle, _uv_write_cb); + + if (sendHandle == null) + { + _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); + } + else + { + _uv.write2(this, handle, pBuffers, nBuffers, sendHandle, _uv_write_cb); + } } catch {