Exemple #1
0
        // Accepts an incoming binary security blob and returns an outgoing binary security blob.
        internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out SecurityStatusPal statusCode)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this, incomingBlob);
            }

            var list = new List <SecurityBuffer>(2);

            if (incomingBlob != null)
            {
                list.Add(new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN));
            }

            if (_channelBinding != null)
            {
                list.Add(new SecurityBuffer(_channelBinding));
            }

            SecurityBuffer[] inSecurityBufferArray = null;
            if (list.Count > 0)
            {
                inSecurityBufferArray = list.ToArray();
            }

            var outSecurityBuffer = new SecurityBuffer(_tokenSize, SecurityBufferType.SECBUFFER_TOKEN);

            bool firstTime = _securityContext == null;

            try
            {
                if (!_isServer)
                {
                    // client session
                    statusCode = NegotiateStreamPal.InitializeSecurityContext(
                        _credentialsHandle,
                        ref _securityContext,
                        _spn,
                        _requestedContextFlags,
                        inSecurityBufferArray,
                        outSecurityBuffer,
                        ref _contextFlags);

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"SSPIWrapper.InitializeSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                    }

                    if (statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded)
                    {
                        var inSecurityBuffers = new SecurityBuffer[1];
                        inSecurityBuffers[0] = outSecurityBuffer;

                        statusCode = NegotiateStreamPal.CompleteAuthToken(ref _securityContext, inSecurityBuffers);

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(this, $"SSPIWrapper.CompleteAuthToken() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                        }

                        outSecurityBuffer.token = null;
                    }
                }
                else
                {
                    // Server session.
                    statusCode = NegotiateStreamPal.AcceptSecurityContext(
                        _credentialsHandle,
                        ref _securityContext,
                        _requestedContextFlags,
                        inSecurityBufferArray,
                        outSecurityBuffer,
                        ref _contextFlags);

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"SSPIWrapper.AcceptSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                    }
                }
            }
            finally
            {
                //
                // Assuming the ISC or ASC has referenced the credential on the first successful call,
                // we want to decrement the effective ref count by "disposing" it.
                // The real dispose will happen when the security context is closed.
                // Note if the first call was not successful the handle is physically destroyed here.
                //
                if (firstTime && _credentialsHandle != null)
                {
                    _credentialsHandle.Dispose();
                }
            }


            if (NegoState.IsError(statusCode))
            {
                CloseContext();
                _isCompleted = true;
                if (throwOnError)
                {
                    Exception exception = NegotiateStreamPal.CreateExceptionFromError(statusCode);
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Exit(this, exception);
                    }
                    throw exception;
                }

                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, $"null statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                }
                return(null);
            }
            else if (firstTime && _credentialsHandle != null)
            {
                // Cache until it is pushed out by newly incoming handles.
                SSPIHandleCache.CacheCredential(_credentialsHandle);
            }

            // The return value will tell us correctly if the handshake is over or not
            if (statusCode.ErrorCode == SecurityStatusPalErrorCode.OK)
            {
                // Success.
                _isCompleted = true;
            }
            else if (NetEventSource.IsEnabled)
            {
                // We need to continue.
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"need continue statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode}) _securityContext:{_securityContext}");
                }
            }

            if (NetEventSource.IsEnabled)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, $"IsCompleted: {IsCompleted}");
                }
            }

            return(outSecurityBuffer.token);
        }
Exemple #2
0
        internal byte[]? GetOutgoingBlob(ReadOnlySpan <byte> incomingBlob, bool throwOnError, out SecurityStatusPal statusCode)
        {
            _tokenBuffer ??= _tokenSize == 0 ? Array.Empty <byte>() : new byte[_tokenSize];

            bool firstTime = _securityContext == null;
            int  resultBlobLength;

            try
            {
                if (!_isServer)
                {
                    // client session
                    statusCode = NegotiateStreamPal.InitializeSecurityContext(
                        ref _credentialsHandle !,
                        ref _securityContext,
                        _spn,
                        _requestedContextFlags,
                        incomingBlob,
                        _channelBinding,
                        ref _tokenBuffer,
                        out resultBlobLength,
                        ref _contextFlags);

                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, $"SSPIWrapper.InitializeSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                    }

                    if (statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded)
                    {
                        statusCode = NegotiateStreamPal.CompleteAuthToken(ref _securityContext, _tokenBuffer.AsSpan(0, resultBlobLength));

                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(this, $"SSPIWrapper.CompleteAuthToken() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                        }

                        resultBlobLength = 0;
                    }
                }
                else
                {
                    // Server session.
                    statusCode = NegotiateStreamPal.AcceptSecurityContext(
                        _credentialsHandle,
                        ref _securityContext,
                        _requestedContextFlags,
                        incomingBlob,
                        _channelBinding,
                        ref _tokenBuffer,
                        out resultBlobLength,
                        ref _contextFlags);

                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, $"SSPIWrapper.AcceptSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                    }
                }
            }
            finally
            {
                //
                // Assuming the ISC or ASC has referenced the credential on the first successful call,
                // we want to decrement the effective ref count by "disposing" it.
                // The real dispose will happen when the security context is closed.
                // Note if the first call was not successful the handle is physically destroyed here.
                //
                if (firstTime)
                {
                    _credentialsHandle?.Dispose();
                }
            }


            if (((int)statusCode.ErrorCode >= (int)SecurityStatusPalErrorCode.OutOfMemory))
            {
                CloseContext();
                _isCompleted = true;
                _tokenBuffer = null;
                if (throwOnError)
                {
                    throw NegotiateStreamPal.CreateExceptionFromError(statusCode);
                }

                return(null);
            }
            else if (firstTime && _credentialsHandle != null)
            {
                // Cache until it is pushed out by newly incoming handles.
                SSPIHandleCache.CacheCredential(_credentialsHandle);
            }

            byte[]? result =
                resultBlobLength == 0 || _tokenBuffer == null ? null :
                _tokenBuffer.Length == resultBlobLength ? _tokenBuffer :
                _tokenBuffer[0..resultBlobLength];
Exemple #3
0
        private int ReadCore(byte[] buffer, int offset, int size)
        {
            uint dataRead = 0;

            if (_dataChunkIndex != -1)
            {
                dataRead = Interop.HttpApi.GetChunks(_httpContext.Request.RequestBuffer, _httpContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
            }

            if (_dataChunkIndex == -1 && dataRead < size)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(this, "size:" + size + " offset:" + offset);
                }
                uint statusCode    = 0;
                uint extraDataRead = 0;
                offset += (int)dataRead;
                size   -= (int)dataRead;

                //the http.sys team recommends that we limit the size to 128kb
                if (size > MaxReadSize)
                {
                    size = MaxReadSize;
                }

                fixed(byte *pBuffer = buffer)
                {
                    // issue unmanaged blocking call
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, "Calling Interop.HttpApi.HttpReceiveRequestEntityBody");
                    }

                    uint flags = 0;

                    if (!_inOpaqueMode)
                    {
                        flags = (uint)Interop.HttpApi.HTTP_FLAGS.HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY;
                    }

                    statusCode =
                        Interop.HttpApi.HttpReceiveRequestEntityBody(
                            _httpContext.RequestQueueHandle,
                            _httpContext.RequestId,
                            flags,
                            (void *)(pBuffer + offset),
                            (uint)size,
                            out extraDataRead,
                            null);

                    dataRead += extraDataRead;
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, "Call to Interop.HttpApi.HttpReceiveRequestEntityBody returned:" + statusCode + " dataRead:" + dataRead);
                    }
                }

                if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF)
                {
                    Exception exception = new HttpListenerException((int)statusCode);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(this, exception.ToString());
                    }
                    throw exception;
                }
                UpdateAfterRead(statusCode, dataRead);
            }
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.DumpBuffer(this, buffer, offset, (int)dataRead);
                NetEventSource.Info(this, "returning dataRead:" + dataRead);
                NetEventSource.Exit(this, "dataRead:" + dataRead);
            }
            return((int)dataRead);
        }
Exemple #4
0
        /// <summary>
        /// <para>Thread for the timer.  Ignores all exceptions.  If no activity occurs for a while,
        /// the thread will shut down.</para>
        /// </summary>
        private static void ThreadProc()
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(null);
            }
#if DEBUG
            DebugThreadTracking.SetThreadSource(ThreadKinds.Timer);
            using (DebugThreadTracking.SetThreadKind(ThreadKinds.System | ThreadKinds.Async))
            {
#endif
            // Set this thread as a background thread.  On AppDomain/Process shutdown, the thread will just be killed.
            Thread.CurrentThread.IsBackground = true;

            // Keep a permanent lock on s_Queues.  This lets for example Shutdown() know when this thread isn't running.
            lock (s_queues)
            {
                // If shutdown was recently called, abort here.
                if (Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Running, (int)TimerThreadState.Running) !=
                    (int)TimerThreadState.Running)
                {
                    return;
                }

                bool running = true;
                while (running)
                {
                    try
                    {
                        s_threadReadyEvent.Reset();

                        while (true)
                        {
                            // Copy all the new queues to the real queues.  Since only this thread modifies the real queues, it doesn't have to lock it.
                            if (s_newQueues.Count > 0)
                            {
                                lock (s_newQueues)
                                {
                                    for (LinkedListNode <WeakReference> node = s_newQueues.First; node != null; node = s_newQueues.First)
                                    {
                                        s_newQueues.Remove(node);
                                        s_queues.AddLast(node);
                                    }
                                }
                            }

                            int  now          = Environment.TickCount;
                            int  nextTick     = 0;
                            bool haveNextTick = false;
                            for (LinkedListNode <WeakReference> node = s_queues.First; node != null; /* node = node.Next must be done in the body */)
                            {
                                TimerQueue queue = (TimerQueue)node.Value.Target;
                                if (queue == null)
                                {
                                    LinkedListNode <WeakReference> next = node.Next;
                                    s_queues.Remove(node);
                                    node = next;
                                    continue;
                                }

                                // Fire() will always return values that should be interpreted as later than 'now' (that is, even if 'now' is
                                // returned, it is 0x100000000 milliseconds in the future).  There's also a chance that Fire() will return a value
                                // intended as > 0x100000000 milliseconds from 'now'.  Either case will just cause an extra scan through the timers.
                                int nextTickInstance;
                                if (queue.Fire(out nextTickInstance) && (!haveNextTick || IsTickBetween(now, nextTick, nextTickInstance)))
                                {
                                    nextTick     = nextTickInstance;
                                    haveNextTick = true;
                                }

                                node = node.Next;
                            }

                            // Figure out how long to wait, taking into account how long the loop took.
                            // Add 15 ms to compensate for poor TickCount resolution (want to guarantee a firing).
                            int newNow       = Environment.TickCount;
                            int waitDuration = haveNextTick ?
                                               (int)(IsTickBetween(now, nextTick, newNow) ?
                                                     Math.Min(unchecked ((uint)(nextTick - newNow)), (uint)(Int32.MaxValue - TickCountResolution)) + TickCountResolution :
                                                     0) :
                                               ThreadIdleTimeoutMilliseconds;

                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Info(null, $"Waiting for {waitDuration}ms");
                            }

                            int waitResult = WaitHandle.WaitAny(s_threadEvents, waitDuration, false);

                            // 0 is s_ThreadShutdownEvent - die.
                            if (waitResult == 0)
                            {
                                if (NetEventSource.IsEnabled)
                                {
                                    NetEventSource.Info(null, "Awoke, cause: Shutdown");
                                }
                                running = false;
                                break;
                            }

                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Info(null, $"Awoke, cause {(waitResult == WaitHandle.WaitTimeout ? "Timeout" : "Prod")}");
                            }

                            // If we timed out with nothing to do, shut down.
                            if (waitResult == WaitHandle.WaitTimeout && !haveNextTick)
                            {
                                Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Idle, (int)TimerThreadState.Running);
                                // There could have been one more prod between the wait and the exchange.  Check, and abort if necessary.
                                if (s_threadReadyEvent.WaitOne(0, false))
                                {
                                    if (Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Running, (int)TimerThreadState.Idle) ==
                                        (int)TimerThreadState.Idle)
                                    {
                                        continue;
                                    }
                                }

                                running = false;
                                break;
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        if (ExceptionCheck.IsFatal(exception))
                        {
                            throw;
                        }

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Error(null, exception);
                        }

                        // The only options are to continue processing and likely enter an error-loop,
                        // shut down timers for this AppDomain, or shut down the AppDomain.  Go with shutting
                        // down the AppDomain in debug, and going into a loop in retail, but try to make the
                        // loop somewhat slow.  Note that in retail, this can only be triggered by OutOfMemory or StackOverflow,
                        // or an exception thrown within TimerThread - the rest are caught in Fire().
#if !DEBUG
                        Thread.Sleep(1000);
#else
                        throw;
#endif
                    }
                }
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(null, "Stop");
            }
#if DEBUG
        }
#endif
        }
        internal Interop.HttpApi.HTTP_FLAGS ComputeHeaders()
        {
            Interop.HttpApi.HTTP_FLAGS flags = Interop.HttpApi.HTTP_FLAGS.NONE;
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this);
            }
            Debug.Assert(!ComputedHeaders, "ComputedHeaders is true.");
            _responseState = ResponseState.ComputedHeaders;

            ComputeCoreHeaders();

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this,
                                    $"flags: {flags} _boundaryType: {_boundaryType} _contentLength: {_contentLength} _keepAlive: {_keepAlive}");
            }
            if (_boundaryType == BoundaryType.None)
            {
                if (HttpListenerRequest.ProtocolVersion.Minor == 0)
                {
                    _keepAlive = false;
                }
                else
                {
                    _boundaryType = BoundaryType.Chunked;
                }
                if (CanSendResponseBody(_httpContext.Response.StatusCode))
                {
                    _contentLength = -1;
                }
                else
                {
                    ContentLength64 = 0;
                }
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"flags:{flags} _BoundaryType:{_boundaryType} _contentLength:{_contentLength} _keepAlive: {_keepAlive}");
            }
            if (_boundaryType == BoundaryType.ContentLength)
            {
                Headers[HttpResponseHeader.ContentLength] = _contentLength.ToString("D", NumberFormatInfo.InvariantInfo);
                if (_contentLength == 0)
                {
                    flags = Interop.HttpApi.HTTP_FLAGS.NONE;
                }
            }
            else if (_boundaryType == BoundaryType.Chunked)
            {
                Headers[HttpResponseHeader.TransferEncoding] = "chunked";
            }
            else if (_boundaryType == BoundaryType.None)
            {
                flags = Interop.HttpApi.HTTP_FLAGS.NONE; // seems like HTTP_SEND_RESPONSE_FLAG_MORE_DATA but this hangs the app;
            }
            else
            {
                _keepAlive = false;
            }
            if (!_keepAlive)
            {
                Headers.Add(HttpResponseHeader.Connection, "close");
                if (flags == Interop.HttpApi.HTTP_FLAGS.NONE)
                {
                    flags = Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
                }
            }
            else
            {
                if (HttpListenerRequest.ProtocolVersion.Minor == 0)
                {
                    Headers[HttpResponseHeader.KeepAlive] = "true";
                }
            }
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"flags:{flags} _BoundaryType:{_boundaryType} _contentLength:{_contentLength} _keepAlive: {_keepAlive}");
            }
            return(flags);
        }
Exemple #6
0
        private void DisposeCore()
        {
            Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (_leftToWrite > 0 && !_inOpaqueMode)
            {
                throw new InvalidOperationException(SR.net_io_notenoughbyteswritten);
            }
            bool sentHeaders = _httpContext.Response.SentHeaders;

            if (sentHeaders && _leftToWrite == 0)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this);
                }
                return;
            }

            uint statusCode = 0;

            if ((_httpContext.Response.BoundaryType == BoundaryType.Chunked || _httpContext.Response.BoundaryType == BoundaryType.None) && !string.Equals(_httpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase))
            {
                if (_httpContext.Response.BoundaryType == BoundaryType.None)
                {
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
                }

                fixed(void *pBuffer = &s_chunkTerminator[0])
                {
                    Interop.HttpApi.HTTP_DATA_CHUNK *pDataChunk = null;
                    if (_httpContext.Response.BoundaryType == BoundaryType.Chunked)
                    {
                        Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = default;
                        dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                        dataChunk.pBuffer       = (byte *)pBuffer;
                        dataChunk.BufferLength  = (uint)s_chunkTerminator.Length;
                        pDataChunk = &dataChunk;
                    }
                    if (!sentHeaders)
                    {
                        statusCode = _httpContext.Response.SendHeaders(pDataChunk, null, flags, false);
                    }
                    else
                    {
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
                        }

                        statusCode =
                            Interop.HttpApi.HttpSendResponseEntityBody(
                                _httpContext.RequestQueueHandle,
                                _httpContext.RequestId,
                                (uint)flags,
                                pDataChunk != null ? (ushort)1 : (ushort)0,
                                pDataChunk,
                                null,
                                SafeLocalAllocHandle.Zero,
                                0,
                                null,
                                null);

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
                        }
                        if (_httpContext.Listener.IgnoreWriteExceptions)
                        {
                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Info(this, "Suppressing error");
                            }
                            statusCode = Interop.HttpApi.ERROR_SUCCESS;
                        }
                    }
                }
            }
            else
            {
                if (!sentHeaders)
                {
                    statusCode = _httpContext.Response.SendHeaders(null, null, flags, false);
                }
            }
            if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF)
            {
                Exception exception = new HttpListenerException((int)statusCode);
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(this, exception.ToString());
                }
                _httpContext.Abort();
                throw exception;
            }
            _leftToWrite = 0;
        }
Exemple #7
0
        ///     Pipelined command resolution.
        ///     How this works:
        ///     A list of commands that need to be sent to the FTP server are spliced together into an array,
        ///     each command such STOR, PORT, etc, is sent to the server, then the response is parsed into a string,
        ///     with the response, the delegate is called, which returns an instruction (either continue, stop, or read additional
        ///     responses from server).
        protected Stream?ContinueCommandPipeline()
        {
            // In async case, The BeginWrite can actually result in a
            // series of synchronous completions that eventually close
            // the connection. So we need to save the members that
            // we need to access, since they may not be valid after
            // BeginWrite returns
            bool isAsync = _isAsync;

            while (_index < _commands !.Length)
            {
                if (_doSend)
                {
                    if (_index < 0)
                    {
                        throw new InternalException();
                    }

                    byte[] sendBuffer = Encoding.GetBytes(_commands[_index].Command);

                    if (NetEventSource.Log.IsEnabled())
                    {
                        string sendCommand = _commands[_index].Command.Substring(0, _commands[_index].Command.Length - 2);
                        if (_commands[_index].HasFlag(PipelineEntryFlags.DontLogParameter))
                        {
                            int index = sendCommand.IndexOf(' ');
                            if (index != -1)
                            {
                                sendCommand = string.Concat(sendCommand.AsSpan(0, index), " ********");
                            }
                        }
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(this, $"Sending command {sendCommand}");
                        }
                    }

                    try
                    {
                        if (isAsync)
                        {
                            BeginWrite(sendBuffer, 0, sendBuffer.Length, s_writeCallbackDelegate, this);
                        }
                        else
                        {
                            Write(sendBuffer, 0, sendBuffer.Length);
                        }
                    }
                    catch (IOException)
                    {
                        MarkAsRecoverableFailure();
                        throw;
                    }
                    catch
                    {
                        throw;
                    }

                    if (isAsync)
                    {
                        return(null);
                    }
                }

                Stream?stream   = null;
                bool   isReturn = PostSendCommandProcessing(ref stream);
                if (isReturn)
                {
                    return(stream);
                }
            }

            lock (this)
            {
                Close();
            }

            return(null);
        }
        internal HttpResponseStreamAsyncResult(object asyncObject, object?userState, AsyncCallback?callback, byte[] buffer, int offset, int size, bool chunked, bool sentHeaders, ThreadPoolBoundHandle boundHandle) : base(asyncObject, userState, callback)
        {
            _boundHandle = boundHandle;
            _sentHeaders = sentHeaders;

            if (size == 0)
            {
                _dataChunks  = null;
                _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: null);
            }
            else
            {
                _dataChunks = new Interop.HttpApi.HTTP_DATA_CHUNK[chunked ? 3 : 1];

                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(this, "m_pOverlapped:0x" + ((IntPtr)_pOverlapped).ToString("x8"));
                }

                object[] objectsToPin = new object[1 + _dataChunks.Length];
                objectsToPin[_dataChunks.Length] = _dataChunks;


                int chunkHeaderOffset = 0;
                byte[]? chunkHeaderBuffer = null;
                if (chunked)
                {
                    chunkHeaderBuffer = GetChunkHeader(size, out chunkHeaderOffset);

                    _dataChunks[0] = default;
                    _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                    _dataChunks[0].BufferLength  = (uint)(chunkHeaderBuffer.Length - chunkHeaderOffset);

                    objectsToPin[0] = chunkHeaderBuffer;

                    _dataChunks[1] = default;
                    _dataChunks[1].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                    _dataChunks[1].BufferLength  = (uint)size;

                    objectsToPin[1] = buffer;

                    _dataChunks[2] = default;
                    _dataChunks[2].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                    _dataChunks[2].BufferLength  = (uint)s_CRLFArray.Length;

                    objectsToPin[2] = s_CRLFArray;
                }
                else
                {
                    _dataChunks[0] = default;
                    _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                    _dataChunks[0].BufferLength  = (uint)size;

                    objectsToPin[0] = buffer;
                }

                // This call will pin needed memory
                _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: objectsToPin);

                if (chunked)
                {
                    _dataChunks[0].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(chunkHeaderBuffer !, chunkHeaderOffset));
                    _dataChunks[1].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset));
                    _dataChunks[2].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(s_CRLFArray, 0));
                }
                else
                {
                    _dataChunks[0].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset));
                }
            }
        }
        protected override bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength)
        {
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(this, $"CheckValid({response.StatusBuffer})");
            }

            // If the response is less than 4 bytes long, it is too short to tell, so return true, valid so far.
            if (response.StatusBuffer.Length < 4)
            {
                return(true);
            }
            string responseString = response.StatusBuffer.ToString();

            // Otherwise, if there is no status code for this response yet, get one.
            if (response.Status == ResponseDescription.NoStatus)
            {
                // If the response does not start with three digits, then it is not a valid response from an FTP server.
                if (!(char.IsDigit(responseString[0]) && char.IsDigit(responseString[1]) && char.IsDigit(responseString[2]) && (responseString[3] == ' ' || responseString[3] == '-')))
                {
                    return(false);
                }
                else
                {
                    response.StatusCodeString = responseString.Substring(0, 3);
                    response.Status           = Convert.ToInt16(response.StatusCodeString, NumberFormatInfo.InvariantInfo);
                }

                // IF a hyphen follows the status code on the first line of the response, then we have a multiline response coming.
                if (responseString[3] == '-')
                {
                    response.Multiline = true;
                }
            }

            // If a complete line of response has been received from the server, then see if the
            // overall response is complete.
            // If this was not a multiline response, then the response is complete at the end of the line.

            // If this was a multiline response (indicated by three digits followed by a '-' in the first line),
            // then we see if the last line received started with the same three digits followed by a space.
            // If it did, then this is the sign of a complete multiline response.
            // If the line contained three other digits followed by the response, then this is a violation of the
            // FTP protocol for multiline responses.
            // All other cases indicate that the response is not yet complete.
            int index = 0;

            while ((index = responseString.IndexOf("\r\n", validThrough, StringComparison.Ordinal)) != -1)  // gets the end line.
            {
                int lineStart = validThrough;
                validThrough = index + 2;  // validThrough now marks the end of the line being examined.
                if (!response.Multiline)
                {
                    completeLength = validThrough;
                    return(true);
                }

                if (responseString.Length > lineStart + 4)
                {
                    // If the first three characters of the response line currently being examined
                    // match the status code, then if they are followed by a space, then we
                    // have reached the end of the reply.
                    if (responseString.Substring(lineStart, 3) == response.StatusCodeString)
                    {
                        if (responseString[lineStart + 3] == ' ')
                        {
                            completeLength = validThrough;
                            return(true);
                        }
                    }
                }
            }
            return(true);
        }
        internal byte[]? GetOutgoingBlob(byte[]?incomingBlob, bool throwOnError, out SecurityStatusPal statusCode)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this, incomingBlob);
            }

            byte[]? result = new byte[_tokenSize];

            bool firstTime = _securityContext == null;

            try
            {
                if (!_isServer)
                {
                    // client session
                    statusCode = NegotiateStreamPal.InitializeSecurityContext(
                        ref _credentialsHandle !,
                        ref _securityContext,
                        _spn,
                        _requestedContextFlags,
                        incomingBlob,
                        _channelBinding,
                        ref result,
                        ref _contextFlags);

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"SSPIWrapper.InitializeSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                    }

                    if (statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded)
                    {
                        statusCode = NegotiateStreamPal.CompleteAuthToken(ref _securityContext, result);

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(this, $"SSPIWrapper.CompleteAuthToken() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                        }

                        result = null;
                    }
                }
                else
                {
                    // Server session.
                    statusCode = NegotiateStreamPal.AcceptSecurityContext(
                        _credentialsHandle,
                        ref _securityContext,
                        _requestedContextFlags,
                        incomingBlob,
                        _channelBinding,
                        ref result,
                        ref _contextFlags);

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"SSPIWrapper.AcceptSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                    }
                }
            }
            finally
            {
                //
                // Assuming the ISC or ASC has referenced the credential on the first successful call,
                // we want to decrement the effective ref count by "disposing" it.
                // The real dispose will happen when the security context is closed.
                // Note if the first call was not successful the handle is physically destroyed here.
                //
                if (firstTime)
                {
                    _credentialsHandle?.Dispose();
                }
            }


            if (((int)statusCode.ErrorCode >= (int)SecurityStatusPalErrorCode.OutOfMemory))
            {
                CloseContext();
                _isCompleted = true;
                if (throwOnError)
                {
                    Exception exception = NegotiateStreamPal.CreateExceptionFromError(statusCode);
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Exit(this, exception);
                    }
                    throw exception;
                }

                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, $"null statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})");
                }
                return(null);
            }
            else if (firstTime && _credentialsHandle != null)
            {
                // Cache until it is pushed out by newly incoming handles.
                SSPIHandleCache.CacheCredential(_credentialsHandle);
            }

            // The return value will tell us correctly if the handshake is over or not
            if (statusCode.ErrorCode == SecurityStatusPalErrorCode.OK ||
                (_isServer && statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded))
            {
                // Success.
                _isCompleted = true;
            }
            else if (NetEventSource.IsEnabled)
            {
                // We need to continue.
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"need continue statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode}) _securityContext:{_securityContext}");
                }
            }

            if (NetEventSource.IsEnabled)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, $"IsCompleted: {IsCompleted}");
                }
            }

            return(result);
        }
Exemple #11
0
        // Helpers for async GetHostByName, ResolveToAddresses, and Resolve - they're almost identical
        // If hostName is an IPString and justReturnParsedIP==true then no reverse lookup will be attempted, but the original address is returned.
        private static IAsyncResult HostResolutionBeginHelper(string hostName, bool justReturnParsedIp, bool throwOnIIPAny, AsyncCallback requestCallback, object state)
        {
            if (hostName == null)
            {
                throw new ArgumentNullException(nameof(hostName));
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(null, hostName);
            }

            // See if it's an IP Address.
            IPAddress             ipAddress;
            DnsResolveAsyncResult asyncResult;

            if (IPAddress.TryParse(hostName, out ipAddress))
            {
                if (throwOnIIPAny && (ipAddress.Equals(IPAddress.Any) || ipAddress.Equals(IPAddress.IPv6Any)))
                {
                    throw new ArgumentException(SR.net_invalid_ip_addr, nameof(hostName));
                }

                asyncResult = new DnsResolveAsyncResult(ipAddress, null, state, requestCallback);

                if (justReturnParsedIp)
                {
                    IPHostEntry hostEntry = NameResolutionUtilities.GetUnresolvedAnswer(ipAddress);
                    asyncResult.StartPostingAsyncOp(false);
                    asyncResult.InvokeCallback(hostEntry);
                    asyncResult.FinishPostingAsyncOp();
                    return(asyncResult);
                }
            }
            else
            {
                asyncResult = new DnsResolveAsyncResult(hostName, null, state, requestCallback);
            }

            // Set up the context, possibly flow.
            asyncResult.StartPostingAsyncOp(false);

            // If the OS supports it and 'hostName' is not an IP Address, resolve the name asynchronously
            // instead of calling the synchronous version in the ThreadPool.
            if (NameResolutionPal.SupportsGetAddrInfoAsync && ipAddress == null)
            {
                ValidateHostName(hostName);
                NameResolutionPal.GetAddrInfoAsync(asyncResult);
            }
            else
            {
                // Start the resolve.
                Task.Factory.StartNew(
                    s => ResolveCallback(s),
                    asyncResult,
                    CancellationToken.None,
                    TaskCreationOptions.DenyChildAttach,
                    TaskScheduler.Default);
            }

            // Finish the flowing, maybe it completed?  This does nothing if we didn't initiate the flowing above.
            asyncResult.FinishPostingAsyncOp();
            return(asyncResult);
        }
Exemple #12
0
        private bool CaptureOrComplete(ref ExecutionContext?cachedContext, bool returnContext)
        {
            if ((_flags & StateFlags.PostBlockStarted) == 0)
            {
                NetEventSource.Fail(this, "Called without calling StartPostingAsyncOp.");
            }

            // See if we're going to need to capture the context.
            bool capturingContext = AsyncCallback != null || (_flags & StateFlags.CaptureContext) != 0;

            // Peek if we've already completed, but don't fix CompletedSynchronously yet
            // Capture the identity if requested, unless we're going to capture the context anyway, unless
            // capturing the context won't be sufficient.
            if ((_flags & StateFlags.CaptureIdentity) != 0 && !InternalPeekCompleted && (!capturingContext))
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "starting identity capture");
                }
                SafeCaptureIdentity();
            }

            // No need to flow if there's no callback, unless it's been specifically requested.
            // Note that Capture() can return null, for example if SuppressFlow() is in effect.
            if (capturingContext && !InternalPeekCompleted)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "starting capture");
                }

                if (cachedContext == null)
                {
                    cachedContext = ExecutionContext.Capture();
                }

                if (cachedContext != null)
                {
                    if (!returnContext)
                    {
                        _context      = cachedContext;
                        cachedContext = null;
                    }
                    else
                    {
                        _context = cachedContext;
                    }
                }

                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"_context:{_context}");
                }
            }
            else
            {
                // Otherwise we have to have completed synchronously, or not needed the context.
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "Skipping capture");
                }

                cachedContext = null;
                if (AsyncCallback != null && !CompletedSynchronously)
                {
                    NetEventSource.Fail(this, "Didn't capture context, but didn't complete synchronously!");
                }
            }

            // Now we want to see for sure what to do.  We might have just captured the context for no reason.
            // This has to be the first time the state has been queried "for real" (apart from InvokeCallback)
            // to guarantee synchronization with Complete() (otherwise, Complete() could try to call the
            // callback without the context having been gotten).
            DebugProtectState(false);
            if (CompletedSynchronously)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "Completing synchronously");
                }
                base.Complete(IntPtr.Zero);
                return(true);
            }

            return(false);
        }
Exemple #13
0
        /// <summary>
        /// ReceiveCommandResponseCallback is the main "while loop" of the ReceiveCommandResponse function family.
        /// In general, what is does is perform an EndReceive() to complete the previous retrieval of bytes from the
        /// server (unless it is using a buffered response)  It then processes what is received by using the
        /// implementing class's CheckValid() function, as described above. If the response is complete, it returns the single complete
        /// response in the GeneralResponseDescription created in BeginReceiveComamndResponse, and buffers the rest as described above.
        ///
        /// If the response is not complete, it issues another Connection.BeginReceive, with callback ReceiveCommandResponse2,
        /// so the action will continue at the next invocation of ReceiveCommandResponse2.
        /// </summary>
        private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead)
        {
            // completeLength will be set to a nonnegative number by CheckValid if the response is complete:
            // it will set completeLength to the length of a complete response.
            int completeLength = -1;

            while (true)
            {
                int validThrough = state.ValidThrough; // passed to checkvalid

                // If we have a Buffered response (ie data was received with the last response that was past the end of that response)
                // deal with it as if we had just received it now instead of actually doing another receive
                if (_buffer.Length > 0)
                {
                    // Append the string we got from the buffer, and flush it out.
                    state.Resp.StatusBuffer.Append(_buffer);
                    _buffer = string.Empty;

                    // invoke checkvalid.
                    if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
                    {
                        throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
                    }
                }
                else // we did a Connection.BeginReceive.  Note that in this case, all bytes received are in the receive buffer (because bytes from
                     // the buffer were transferred there if necessary
                {
                    // this indicates the connection was closed.
                    if (bytesRead <= 0)
                    {
                        throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
                    }

                    // decode the bytes in the receive buffer into a string, append it to the statusbuffer, and invoke checkvalid.
                    // Decoder automatically takes care of caching partial codepoints at the end of a buffer.

                    char[] chars    = new char[_decoder.GetCharCount(state.Buffer, 0, bytesRead)];
                    int    numChars = _decoder.GetChars(state.Buffer, 0, bytesRead, chars, 0, false);

                    string szResponse = new string(chars, 0, numChars);

                    state.Resp.StatusBuffer.Append(szResponse);
                    if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
                    {
                        throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
                    }

                    // If the response is complete, then determine how many characters are left over...these bytes need to be set into Buffer.
                    if (completeLength >= 0)
                    {
                        int unusedChars = state.Resp.StatusBuffer.Length - completeLength;
                        if (unusedChars > 0)
                        {
                            _buffer = szResponse.Substring(szResponse.Length - unusedChars, unusedChars);
                        }
                    }
                }

                // Now, in general, if the response is not complete, update the "valid through" length for the efficiency of checkValid,
                // and perform the next receive.
                // Note that there may NOT be bytes in the beginning of the receive buffer (even if there were partial characters left over after the
                // last encoding), because they get tracked in the Decoder.
                if (completeLength < 0)
                {
                    state.ValidThrough = validThrough;
                    try
                    {
                        if (_isAsync)
                        {
                            BeginRead(state.Buffer, 0, state.Buffer.Length, s_readCallbackDelegate, state);
                            return;
                        }
                        else
                        {
                            bytesRead = Read(state.Buffer, 0, state.Buffer.Length);
                            if (bytesRead == 0)
                            {
                                CloseSocket();
                            }
                            continue;
                        }
                    }
                    catch (IOException)
                    {
                        MarkAsRecoverableFailure();
                        throw;
                    }
                    catch
                    {
                        throw;
                    }
                }

                // The response is completed
                break;
            }

            // Otherwise, we have a complete response.
            string responseString = state.Resp.StatusBuffer.ToString();

            state.Resp.StatusDescription = responseString.Substring(0, completeLength);
            // Set the StatusDescription to the complete part of the response.  Note that the Buffer has already been taken care of above.

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"Received response: {responseString.Substring(0, completeLength - 2)}");
            }

            if (_isAsync)
            {
                // Tell who is listening what was received.
                if (state.Resp != null)
                {
                    _currentResponseDescription = state.Resp;
                }
                Stream stream = null;
                if (PostReadCommandProcessing(ref stream))
                {
                    return;
                }
                ContinueCommandPipeline();
            }
        }
Exemple #14
0
        //
        // Security: We temporarily reset thread token to open the cert store under process account.
        //
        internal static X509Store EnsureStoreOpened(bool isMachineStore)
        {
            X509Store store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;

            // TODO #3862 Investigate if this can be switched to either the static or Lazy<T> patterns.
            if (store == null)
            {
                lock (s_syncObject)
                {
                    store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;
                    if (store == null)
                    {
                        // NOTE: that if this call fails we won't keep track and the next time we enter we will try to open the store again.
                        StoreLocation storeLocation = isMachineStore ? StoreLocation.LocalMachine : StoreLocation.CurrentUser;
                        store = new X509Store(StoreName.My, storeLocation);
                        try
                        {
                            // For app-compat We want to ensure the store is opened under the **process** account.
                            try
                            {
                                WindowsIdentity.RunImpersonated(SafeAccessTokenHandle.InvalidHandle, () =>
                                {
                                    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
                                    if (NetEventSource.IsEnabled)
                                    {
                                        NetEventSource.Info(null, $"storeLocation {storeLocation} returned store: {store}");
                                    }
                                });
                            }
                            catch
                            {
                                throw;
                            }

                            if (isMachineStore)
                            {
                                s_myMachineCertStoreEx = store;
                            }
                            else
                            {
                                s_myCertStoreEx = store;
                            }

                            return(store);
                        }
                        catch (Exception exception)
                        {
                            if (exception is CryptographicException || exception is SecurityException)
                            {
                                NetEventSource.Fail(null, $"Failed to open cert store, location: {storeLocation} exception: {exception}");
                                return(null);
                            }

                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Error(null, SR.Format(SR.net_log_open_store_failed, storeLocation, exception));
                            }
                            throw;
                        }
                    }
                }
            }

            return(store);
        }
Exemple #15
0
        void ICloseEx.CloseEx(CloseExState closeState)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"state = {closeState}");
            }

            lock (this)
            {
                if (_closing == true)
                {
                    return;
                }
                _closing   = true;
                _writeable = false;
                _readable  = false;
            }

            try
            {
                try
                {
                    if ((closeState & CloseExState.Abort) == 0)
                    {
                        _networkStream.Close(DefaultCloseTimeout);
                    }
                    else
                    {
                        _networkStream.Close(0);
                    }
                }
                finally
                {
                    _request.DataStreamClosed(closeState);
                }
            }
            catch (Exception exception)
            {
                bool         doThrow      = true;
                WebException webException = exception as WebException;
                if (webException != null)
                {
                    FtpWebResponse response = webException.Response as FtpWebResponse;
                    if (response != null)
                    {
                        if (!_isFullyRead &&
                            response.StatusCode == FtpStatusCode.ConnectionClosed)
                        {
                            doThrow = false;
                        }
                    }
                }

                if (doThrow)
                {
                    if ((closeState & CloseExState.Silent) == 0)
                    {
                        throw;
                    }
                }
            }
        }
        //    This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed.
        //    This function controls the setting up of a data socket/connection, and of saving off the server responses.
        protected override PipelineInstruction PipelineCallback(PipelineEntry?entry, ResponseDescription?response, bool timeout, ref Stream?stream)
        {
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(this, $"Command:{entry?.Command} Description:{response?.StatusDescription}");
            }

            // null response is not expected
            if (response == null)
            {
                return(PipelineInstruction.Abort);
            }

            FtpStatusCode status = (FtpStatusCode)response.Status;

            //
            // Update global "current status" for FtpWebRequest
            //
            if (status != FtpStatusCode.ClosingControl)
            {
                // A 221 status won't be reflected on the user FTP response
                // Anything else will (by design?)
                StatusCode = status;
                StatusLine = response.StatusDescription;
            }

            // If the status code is outside the range defined in RFC (1xx to 5xx) throw
            if (response.InvalidStatusCode)
            {
                throw new WebException(SR.net_InvalidStatusCode, WebExceptionStatus.ProtocolError);
            }

            // Update the banner message if any, this is a little hack because the "entry" param is null
            if (_index == -1)
            {
                if (status == FtpStatusCode.SendUserCommand)
                {
                    _bannerMessage = new StringBuilder();
                    _bannerMessage.Append(StatusLine);
                    return(PipelineInstruction.Advance);
                }
                else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable)
                {
                    return(PipelineInstruction.Reread);
                }
                else
                {
                    throw GenerateException(status, response.StatusDescription, null);
                }
            }

            //
            // Check for the result of our attempt to use UTF8
            //
            if (entry !.Command == "OPTS utf8 on\r\n")
            {
                if (response.PositiveCompletion)
                {
                    Encoding = Encoding.UTF8;
                }
                else
                {
                    Encoding = Encoding.Default;
                }
                return(PipelineInstruction.Advance);
            }

            // If we are already logged in and the server returns 530 then
            // the server does not support re-issuing a USER command,
            // tear down the connection and start all over again
            if (entry.Command.IndexOf("USER", StringComparison.Ordinal) != -1)
            {
                // The server may not require a password for this user, so bypass the password command
                if (status == FtpStatusCode.LoggedInProceed)
                {
                    _loginState = FtpLoginState.LoggedIn;
                    _index++;
                }
            }

            //
            // Throw on an error with possible recovery option
            //
            if (response.TransientFailure || response.PermanentFailure)
            {
                if (status == FtpStatusCode.ServiceNotAvailable)
                {
                    MarkAsRecoverableFailure();
                }
                throw GenerateException(status, response.StatusDescription, null);
            }

            if (_loginState != FtpLoginState.LoggedIn &&
                entry.Command.IndexOf("PASS", StringComparison.Ordinal) != -1)
            {
                // Note the fact that we logged in
                if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed)
                {
                    _loginState = FtpLoginState.LoggedIn;
                }
                else
                {
                    throw GenerateException(status, response.StatusDescription, null);
                }
            }

            //
            // Parse special cases
            //
            if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate))
            {
                bool isSocketReady;
                PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady);
                if (!isSocketReady)
                {
                    return(result);
                }
                // otherwise we have a stream to create
            }
            //
            // This is part of the above case and it's all about giving data stream back
            //
            if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen)
            {
                if (_dataSocket == null)
                {
                    return(PipelineInstruction.Abort);
                }
                if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream))
                {
                    _abortReason = SR.Format(SR.net_ftp_invalid_status_response, status, entry.Command);
                    return(PipelineInstruction.Abort);
                }

                // Parse out the Content length, if we can
                TryUpdateContentLength(response.StatusDescription !);

                // Parse out the file name, when it is returned and use it for our ResponseUri
                FtpWebRequest request = (FtpWebRequest)_request !;
                if (request.MethodInfo.ShouldParseForResponseUri)
                {
                    TryUpdateResponseUri(response.StatusDescription !, request);
                }

                return(QueueOrCreateFtpDataStream(ref stream));
            }


            //
            // Parse responses by status code exclusivelly
            //

            // Update welcome message
            if (status == FtpStatusCode.LoggedInProceed)
            {
                _welcomeMessage !.Append(StatusLine);
            }
            // OR set the user response ExitMessage
            else if (status == FtpStatusCode.ClosingControl)
            {
                _exitMessage !.Append(response.StatusDescription);
                // And close the control stream socket on "QUIT"
                CloseSocket();
            }
            // OR set us up for SSL/TLS, after this we'll be writing securely
            else if (status == FtpStatusCode.ServerWantsSecureSession)
            {
                // If NetworkStream is a TlsStream, then this must be in the async callback
                // from completing the SSL handshake.
                // So just let the pipeline continue.
                if (!(NetworkStream is TlsStream))
                {
                    FtpWebRequest request   = (FtpWebRequest)_request !;
                    TlsStream     tlsStream = new TlsStream(NetworkStream, Socket, request.RequestUri.Host, request.ClientCertificates);

                    if (_isAsync)
                    {
                        tlsStream.BeginAuthenticateAsClient(ar =>
                        {
                            try
                            {
                                tlsStream.EndAuthenticateAsClient(ar);
                                NetworkStream = tlsStream;
                                this.ContinueCommandPipeline();
                            }
                            catch (Exception e)
                            {
                                this.CloseSocket();
                                this.InvokeRequestCallback(e);
                            }
                        }, null);

                        return(PipelineInstruction.Pause);
                    }
                    else
                    {
                        tlsStream.AuthenticateAsClient();
                        NetworkStream = tlsStream;
                    }
                }
            }
            // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands
            else if (status == FtpStatusCode.FileStatus)
            {
                if (entry.Command.StartsWith("SIZE ", StringComparison.Ordinal))
                {
                    _contentLength = GetContentLengthFrom213Response(response.StatusDescription !);
                }
                else if (entry.Command.StartsWith("MDTM ", StringComparison.Ordinal))
                {
                    _lastModified = GetLastModifiedFrom213Response(response.StatusDescription !);
                }
            }
            // OR parse out our login directory
            else if (status == FtpStatusCode.PathnameCreated)
            {
                if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand))
                {
                    _loginDirectory = GetLoginDirectory(response.StatusDescription !);
                }
            }
            // Asserting we have some positive response
            else
            {
                // We only use CWD to reset ourselves back to the login directory.
                if (entry.Command.IndexOf("CWD", StringComparison.Ordinal) != -1)
                {
                    _establishedServerDirectory = _requestedServerDirectory;
                }
            }

            // Intermediate responses require rereading
            if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n"))
            {
                return(PipelineInstruction.Reread);
            }

            return(PipelineInstruction.Advance);
        }
Exemple #17
0
        private IAsyncResult BeginWriteCore(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
        {
            Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (_closed || (size == 0 && _leftToWrite != 0))
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this);
                }
                HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback);
                result.InvokeCallback((uint)0);
                return(result);
            }
            if (_leftToWrite >= 0 && size > _leftToWrite)
            {
                throw new ProtocolViolationException(SR.net_entitytoobig);
            }

            uint statusCode;
            uint bytesSent = 0;

            flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
            bool sentHeaders = _httpContext.Response.SentHeaders;
            HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, _httpContext.Response.BoundaryType == BoundaryType.Chunked, sentHeaders, _httpContext.RequestQueueBoundHandle);

            // Update m_LeftToWrite now so we can queue up additional BeginWrite's without waiting for EndWrite.
            UpdateAfterWrite((uint)((_httpContext.Response.BoundaryType == BoundaryType.Chunked) ? 0 : size));

            try
            {
                if (!sentHeaders)
                {
                    statusCode = _httpContext.Response.SendHeaders(null, asyncResult, flags, false);
                }
                else
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
                    }

                    statusCode =
                        Interop.HttpApi.HttpSendResponseEntityBody(
                            _httpContext.RequestQueueHandle,
                            _httpContext.RequestId,
                            (uint)flags,
                            asyncResult.dataChunkCount,
                            asyncResult.pDataChunks,
                            &bytesSent,
                            SafeLocalAllocHandle.Zero,
                            0,
                            asyncResult._pOverlapped,
                            null);

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
                    }
                }
            }
            catch (Exception e)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(this, e.ToString());
                }
                asyncResult.InternalCleanup();
                _closed = true;
                _httpContext.Abort();
                throw;
            }

            if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING)
            {
                asyncResult.InternalCleanup();
                if (_httpContext.Listener.IgnoreWriteExceptions && sentHeaders)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, "BeginWrite() Suppressing error");
                    }
                }
                else
                {
                    Exception exception = new HttpListenerException((int)statusCode);
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Error(this, exception.ToString());
                    }
                    _closed = true;
                    _httpContext.Abort();
                    throw exception;
                }
            }

            if (statusCode == Interop.HttpApi.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess)
            {
                // IO operation completed synchronously - callback won't be called to signal completion.
                asyncResult.IOCompleted(statusCode, bytesSent);
            }

            // Last write, cache it for special cancelation handling.
            if ((flags & Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
            {
                _lastWrite = asyncResult;
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }
            return(asyncResult);
        }
        /// <summary>
        ///    <para>Creates an array of commands, that will be sent to the server</para>
        /// </summary>
        protected override PipelineEntry[] BuildCommandsList(WebRequest req)
        {
            bool          resetLoggedInState = false;
            FtpWebRequest request            = (FtpWebRequest)req;

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(this);
            }

            _responseUri = request.RequestUri;
            ArrayList commandList = new ArrayList();

            if (request.EnableSsl && !UsingSecureStream)
            {
                commandList.Add(new PipelineEntry(FormatFtpCommand("AUTH", "TLS")));
                // According to RFC we need to re-authorize with USER/PASS after we re-authenticate.
                resetLoggedInState = true;
            }

            if (resetLoggedInState)
            {
                _loginDirectory             = null;
                _establishedServerDirectory = null;
                _requestedServerDirectory   = null;
                _currentTypeSetting         = string.Empty;
                if (_loginState == FtpLoginState.LoggedIn)
                {
                    _loginState = FtpLoginState.LoggedInButNeedsRelogin;
                }
            }

            if (_loginState != FtpLoginState.LoggedIn)
            {
                Credentials     = request.Credentials !.GetCredential(request.RequestUri, "basic");
                _welcomeMessage = new StringBuilder();
                _exitMessage    = new StringBuilder();

                string domainUserName = string.Empty;
                string password       = string.Empty;

                if (Credentials != null)
                {
                    domainUserName = Credentials.UserName;
                    string domain = Credentials.Domain;
                    if (!string.IsNullOrEmpty(domain))
                    {
                        domainUserName = domain + "\\" + domainUserName;
                    }

                    password = Credentials.Password;
                }

                if (domainUserName.Length == 0 && password.Length == 0)
                {
                    domainUserName = "******";
                    password       = "******";
                }

                commandList.Add(new PipelineEntry(FormatFtpCommand("USER", domainUserName)));
                commandList.Add(new PipelineEntry(FormatFtpCommand("PASS", password), PipelineEntryFlags.DontLogParameter));

                // If SSL, always configure data channel encryption after authentication to maximum RFC compatibility.   The RFC allows for
                // PBSZ/PROT commands to come either before or after the USER/PASS, but some servers require USER/PASS immediately after
                // the AUTH TLS command.
                if (request.EnableSsl && !UsingSecureStream)
                {
                    commandList.Add(new PipelineEntry(FormatFtpCommand("PBSZ", "0")));
                    commandList.Add(new PipelineEntry(FormatFtpCommand("PROT", "P")));
                }

                commandList.Add(new PipelineEntry(FormatFtpCommand("OPTS", "utf8 on")));
                commandList.Add(new PipelineEntry(FormatFtpCommand("PWD", null)));
            }

            GetPathOption getPathOption = GetPathOption.Normal;

            if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter))
            {
                getPathOption = GetPathOption.AssumeNoFilename;
            }
            else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory))
            {
                getPathOption = GetPathOption.AssumeFilename;
            }

            string requestPath;
            string requestDirectory;
            string requestFilename;

            GetPathInfo(getPathOption, request.RequestUri, out requestPath, out requestDirectory, out requestFilename);

            if (requestFilename.Length == 0 && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter))
            {
                throw new WebException(SR.net_ftp_invalid_uri);
            }

            // We optimize for having the current working directory staying at the login directory.  This ensure that
            // our relative paths work right and reduces unnecessary CWD commands.
            // Usually, we don't change the working directory except for some FTP commands.  If necessary,
            // we need to reset our working directory back to the login directory.
            if (_establishedServerDirectory != null && _loginDirectory != null && _establishedServerDirectory != _loginDirectory)
            {
                commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", _loginDirectory), PipelineEntryFlags.UserCommand));
                _requestedServerDirectory = _loginDirectory;
            }

            // For most commands, we don't need to navigate to the directory since we pass in the full
            // path as part of the FTP protocol command.   However,  some commands require it.
            if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath) && requestDirectory.Length > 0)
            {
                commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", requestDirectory), PipelineEntryFlags.UserCommand));
                _requestedServerDirectory = requestDirectory;
            }

            if (!request.MethodInfo.IsCommandOnly)
            {
                string requestedTypeSetting = request.UseBinary ? "I" : "A";
                if (_currentTypeSetting != requestedTypeSetting)
                {
                    commandList.Add(new PipelineEntry(FormatFtpCommand("TYPE", requestedTypeSetting)));
                    _currentTypeSetting = requestedTypeSetting;
                }

                if (request.UsePassive)
                {
                    string passiveCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork || ServerAddress.IsIPv4MappedToIPv6) ? "PASV" : "EPSV";
                    commandList.Add(new PipelineEntry(FormatFtpCommand(passiveCommand, null), PipelineEntryFlags.CreateDataConnection));
                }
                else
                {
                    string portCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork || ServerAddress.IsIPv4MappedToIPv6) ? "PORT" : "EPRT";
                    CreateFtpListenerSocket(request);
                    commandList.Add(new PipelineEntry(FormatFtpCommand(portCommand, GetPortCommandLine(request))));
                }

                if (request.ContentOffset > 0)
                {
                    // REST command must always be the last sent before the main file command is sent.
                    commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture))));
                }
            }

            PipelineEntryFlags flags = PipelineEntryFlags.UserCommand;

            if (!request.MethodInfo.IsCommandOnly)
            {
                flags |= PipelineEntryFlags.GiveDataStream;
                if (!request.UsePassive)
                {
                    flags |= PipelineEntryFlags.CreateDataConnection;
                }
            }

            if (request.MethodInfo.Operation == FtpOperation.Rename)
            {
                string baseDir = (requestDirectory.Length == 0)
                    ? string.Empty : requestDirectory + "/";
                commandList.Add(new PipelineEntry(FormatFtpCommand("RNFR", baseDir + requestFilename), flags));

                string renameTo;
                if (!string.IsNullOrEmpty(request.RenameTo) &&
                    request.RenameTo.StartsWith("/", StringComparison.OrdinalIgnoreCase))
                {
                    renameTo = request.RenameTo; // Absolute path
                }
                else
                {
                    renameTo = baseDir + request.RenameTo; // Relative path
                }
                commandList.Add(new PipelineEntry(FormatFtpCommand("RNTO", renameTo), flags));
            }
            else if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter))
            {
                commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, string.Empty), flags));
            }
            else if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath))
            {
                commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestFilename), flags));
            }
            else
            {
                commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestPath), flags));
            }

            commandList.Add(new PipelineEntry(FormatFtpCommand("QUIT", null)));

            return((PipelineEntry[])commandList.ToArray(typeof(PipelineEntry)));
        }
Exemple #19
0
        private void WriteCore(byte[] buffer, int offset, int size)
        {
            Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (size == 0 && _leftToWrite != 0)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this);
                }
                return;
            }
            if (_leftToWrite >= 0 && size > _leftToWrite)
            {
                throw new ProtocolViolationException(SR.net_entitytoobig);
            }

            uint statusCode;
            uint dataToWrite = (uint)size;
            SafeLocalAllocHandle bufferAsIntPtr = null;
            IntPtr pBufferAsIntPtr = IntPtr.Zero;
            bool   sentHeaders     = _httpContext.Response.SentHeaders;

            try
            {
                if (size == 0)
                {
                    statusCode = _httpContext.Response.SendHeaders(null, null, flags, false);
                }
                else
                {
                    fixed(byte *pDataBuffer = buffer)
                    {
                        byte *pBuffer = pDataBuffer;

                        if (_httpContext.Response.BoundaryType == BoundaryType.Chunked)
                        {
                            string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture);
                            dataToWrite     = dataToWrite + (uint)(chunkHeader.Length + 4);
                            bufferAsIntPtr  = SafeLocalAllocHandle.LocalAlloc((int)dataToWrite);
                            pBufferAsIntPtr = bufferAsIntPtr.DangerousGetHandle();
                            for (int i = 0; i < chunkHeader.Length; i++)
                            {
                                Marshal.WriteByte(pBufferAsIntPtr, i, (byte)chunkHeader[i]);
                            }
                            Marshal.WriteInt16(pBufferAsIntPtr, chunkHeader.Length, 0x0A0D);
                            Marshal.Copy(buffer, offset, pBufferAsIntPtr + chunkHeader.Length + 2, size);
                            Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D);
                            pBuffer = (byte *)pBufferAsIntPtr;
                            offset  = 0;
                        }
                        Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = default;
                        dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                        dataChunk.pBuffer       = (byte *)(pBuffer + offset);
                        dataChunk.BufferLength  = dataToWrite;

                        flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
                        if (!sentHeaders)
                        {
                            statusCode = _httpContext.Response.SendHeaders(&dataChunk, null, flags, false);
                        }
                        else
                        {
                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
                            }

                            statusCode =
                                Interop.HttpApi.HttpSendResponseEntityBody(
                                    _httpContext.RequestQueueHandle,
                                    _httpContext.RequestId,
                                    (uint)flags,
                                    1,
                                    &dataChunk,
                                    null,
                                    SafeLocalAllocHandle.Zero,
                                    0,
                                    null,
                                    null);

                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
                            }
                            if (_httpContext.Listener.IgnoreWriteExceptions)
                            {
                                if (NetEventSource.IsEnabled)
                                {
                                    NetEventSource.Info(this, "Write() suppressing error");
                                }
                                statusCode = Interop.HttpApi.ERROR_SUCCESS;
                            }
                        }
                    }
                }
            }
            finally
            {
                // free unmanaged buffer
                bufferAsIntPtr?.Close();
            }

            if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF)
            {
                Exception exception = new HttpListenerException((int)statusCode);
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(this, exception.ToString());
                }
                _closed = true;
                _httpContext.Abort();
                throw exception;
            }
            UpdateAfterWrite(dataToWrite);
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.DumpBuffer(this, buffer, offset, (int)dataToWrite);
            }
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }
        }
        private PipelineInstruction QueueOrCreateDataConection(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream?stream, out bool isSocketReady)
        {
            isSocketReady = false;
            if (_dataHandshakeStarted)
            {
                isSocketReady = true;
                return(PipelineInstruction.Pause); //if we already started then this is re-entering into the callback where we proceed with the stream
            }

            _dataHandshakeStarted = true;

            // Handle passive responses by parsing the port and later doing a Connect(...)
            bool isPassive = false;
            int  port      = -1;

            if (entry.Command == "PASV\r\n" || entry.Command == "EPSV\r\n")
            {
                if (!response.PositiveCompletion)
                {
                    _abortReason = SR.Format(SR.net_ftp_server_failed_passive, response.Status);
                    return(PipelineInstruction.Abort);
                }
                if (entry.Command == "PASV\r\n")
                {
                    port = GetPortV4(response.StatusDescription !);
                }
                else
                {
                    port = GetPortV6(response.StatusDescription !);
                }

                isPassive = true;
            }

            if (isPassive)
            {
                if (port == -1)
                {
                    NetEventSource.Fail(this, "'port' not set.");
                }

                try
                {
                    _dataSocket = CreateFtpDataSocket((FtpWebRequest)_request !, Socket);
                }
                catch (ObjectDisposedException)
                {
                    throw ExceptionHelper.RequestAbortedException;
                }

                IPEndPoint localEndPoint = new IPEndPoint(((IPEndPoint)Socket.LocalEndPoint !).Address, 0);
                _dataSocket.Bind(localEndPoint);

                _passiveEndPoint = new IPEndPoint(ServerAddress, port);
            }

            PipelineInstruction result;

            if (_passiveEndPoint != null)
            {
                IPEndPoint passiveEndPoint = _passiveEndPoint;
                _passiveEndPoint = null;
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(this, "starting Connect()");
                }
                if (_isAsync)
                {
                    _dataSocket !.BeginConnect(passiveEndPoint, s_connectCallbackDelegate, this);
                    result = PipelineInstruction.Pause;
                }
                else
                {
                    _dataSocket !.Connect(passiveEndPoint);
                    result = PipelineInstruction.Advance; // for passive mode we end up going to the next command
                }
            }
            else
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(this, "starting Accept()");
                }

                if (_isAsync)
                {
                    _dataSocket !.BeginAccept(s_acceptCallbackDelegate, this);
                    result = PipelineInstruction.Pause;
                }
                else
                {
                    Socket listenSocket = _dataSocket !;
                    try
                    {
                        _dataSocket = _dataSocket !.Accept();
                        if (!ServerAddress.Equals(((IPEndPoint)_dataSocket.RemoteEndPoint !).Address))
                        {
                            _dataSocket.Close();
                            throw new WebException(SR.net_ftp_active_address_different, WebExceptionStatus.ProtocolError);
                        }
                        isSocketReady = true;   // for active mode we end up creating a stream before advancing the pipeline
                        result        = PipelineInstruction.Pause;
                    }
                    finally
                    {
                        listenSocket.Close();
                    }
                }
            }
            return(result);
        }
Exemple #21
0
            /// <summary>
            /// <para>Fires the timer if it is still active and has expired.  Returns
            /// true if it can be deleted, or false if it is still timing.</para>
            /// </summary>
            internal bool Fire()
            {
                if (_timerState == TimerState.Sentinel)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, "TimerQueue tried to Fire a Sentinel.");
                    }
                }

                if (_timerState != TimerState.Ready)
                {
                    return(true);
                }

                // Must get the current tick count within this method so it is guaranteed not to be before
                // StartTime, which is set in the constructor.
                int nowMilliseconds = Environment.TickCount;

                if (IsTickBetween(StartTime, Expiration, nowMilliseconds))
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"TimerThreadTimer#{StartTime}::Fire() Not firing ({StartTime} <= {nowMilliseconds} < {Expiration})");
                    }
                    return(false);
                }

                bool needCallback = false;

                lock (_queueLock)
                {
                    if (_timerState == TimerState.Ready)
                    {
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(this, $"TimerThreadTimer#{StartTime}::Fire() Firing ({StartTime} <= {nowMilliseconds} >= " + Expiration + ")");
                        }
                        _timerState = TimerState.Fired;

                        // Remove it from the list.
                        Next.Prev = Prev;
                        Prev.Next = Next;

                        Next         = null;
                        Prev         = null;
                        needCallback = _callback != null;
                    }
                }

                if (needCallback)
                {
                    try
                    {
                        Callback callback = _callback;
                        object   context  = _context;
                        _callback = null;
                        _context  = null;
                        callback(this, nowMilliseconds, context);
                    }
                    catch (Exception exception)
                    {
                        if (ExceptionCheck.IsFatal(exception))
                        {
                            throw;
                        }

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Error(this, $"exception in callback: {exception}");
                        }

                        // This thread is not allowed to go into user code, so we should never get an exception here.
                        // So, in debug, throw it up, killing the AppDomain.  In release, we'll just ignore it.
#if DEBUG
                        throw;
#endif
                    }
                }

                return(true);
            }
Exemple #22
0
        internal static X509Store EnsureStoreOpened(bool isMachineStore)
        {
            X509Store store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;

            if (store == null)
            {
                StoreLocation storeLocation = isMachineStore ? StoreLocation.LocalMachine : StoreLocation.CurrentUser;

                // On Windows and OSX CheckSupportsStore is not defined, so the call is eliminated and the
                // if should be folded out.
                //
                // On Unix it will prevent the lock from being held and released over and over for the LocalMachine store.
                bool supportsStore = true;
                CheckSupportsStore(storeLocation, ref supportsStore);

                if (!supportsStore)
                {
                    return(null);
                }

                lock (s_syncObject)
                {
                    store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;

                    if (store == null)
                    {
                        try
                        {
                            // NOTE: that if this call fails we won't keep track and the next time we enter we will try to open the store again.
                            store = OpenStore(storeLocation);

                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Info(null, $"storeLocation: {storeLocation} returned store {store}");
                            }

                            if (isMachineStore)
                            {
                                s_myMachineCertStoreEx = store;
                            }
                            else
                            {
                                s_myCertStoreEx = store;
                            }
                        }
                        catch (Exception exception)
                        {
                            if (exception is CryptographicException || exception is SecurityException)
                            {
                                NetEventSource.Fail(null,
                                                    $"Failed to open cert store, location: {storeLocation} exception: {exception}");
                                return(null);
                            }

                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Error(null, SR.Format(SR.net_log_open_store_failed, storeLocation, exception));
                            }

                            throw;
                        }
                    }
                }
            }

            return(store);
        }
        /*
         * 12.3
         * HttpSendHttpResponse() and HttpSendResponseEntityBody() Flag Values.
         * The following flags can be used on calls to HttpSendHttpResponse() and HttpSendResponseEntityBody() API calls:
         *
         #define HTTP_SEND_RESPONSE_FLAG_DISCONNECT          0x00000001
         #define HTTP_SEND_RESPONSE_FLAG_MORE_DATA           0x00000002
         #define HTTP_SEND_RESPONSE_FLAG_RAW_HEADER          0x00000004
         #define HTTP_SEND_RESPONSE_FLAG_VALID               0x00000007
         *
         * HTTP_SEND_RESPONSE_FLAG_DISCONNECT:
         *  specifies that the network connection should be disconnected immediately after
         *  sending the response, overriding the HTTP protocol's persistent connection features.
         * HTTP_SEND_RESPONSE_FLAG_MORE_DATA:
         *  specifies that additional entity body data will be sent by the caller. Thus,
         *  the last call HttpSendResponseEntityBody for a RequestId, will have this flag reset.
         * HTTP_SEND_RESPONSE_RAW_HEADER:
         *  specifies that a caller of HttpSendResponseEntityBody() is intentionally omitting
         *  a call to HttpSendHttpResponse() in order to bypass normal header processing. The
         *  actual HTTP header will be generated by the application and sent as entity body.
         *  This flag should be passed on the first call to HttpSendResponseEntityBody, and
         *  not after. Thus, flag is not applicable to HttpSendHttpResponse.
         */
        internal unsafe uint SendHeaders(Interop.HttpApi.HTTP_DATA_CHUNK *pDataChunk,
                                         HttpResponseStreamAsyncResult asyncResult,
                                         Interop.HttpApi.HTTP_FLAGS flags,
                                         bool isWebSocketHandshake)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"pDataChunk: { ((IntPtr)pDataChunk)}, asyncResult: {asyncResult}");
            }
            Debug.Assert(!SentHeaders, "SentHeaders is true.");

            if (StatusCode == (int)HttpStatusCode.Unauthorized)
            {
                // User set 401
                // Using the configured Auth schemes, populate the auth challenge headers. This is for scenarios where
                // Anonymous access is allowed for some resources, but the server later determines that authorization
                // is required for this request.
                HttpListenerContext.SetAuthenticationHeaders();
            }

            // Log headers
            if (NetEventSource.IsEnabled)
            {
                StringBuilder sb = new StringBuilder("HttpListenerResponse Headers:\n");
                for (int i = 0; i < Headers.Count; i++)
                {
                    sb.Append("\t");
                    sb.Append(Headers.GetKey(i));
                    sb.Append(" : ");
                    sb.Append(Headers.Get(i));
                    sb.Append("\n");
                }
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, sb.ToString());
                }
            }
            _responseState = ResponseState.SentHeaders;

            uint            statusCode;
            uint            bytesSent;
            List <GCHandle> pinnedHeaders = SerializeHeaders(ref _nativeResponse.Headers, isWebSocketHandshake);

            try
            {
                if (pDataChunk != null)
                {
                    _nativeResponse.EntityChunkCount = 1;
                    _nativeResponse.pEntityChunks    = pDataChunk;
                }
                else if (asyncResult != null && asyncResult.pDataChunks != null)
                {
                    _nativeResponse.EntityChunkCount = asyncResult.dataChunkCount;
                    _nativeResponse.pEntityChunks    = asyncResult.pDataChunks;
                }
                else
                {
                    _nativeResponse.EntityChunkCount = 0;
                    _nativeResponse.pEntityChunks    = null;
                }
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendHttpResponse flags:" + flags);
                }
                if (StatusDescription.Length > 0)
                {
                    byte[] statusDescriptionBytes = new byte[WebHeaderEncoding.GetByteCount(StatusDescription)];
                    fixed(byte *pStatusDescription = statusDescriptionBytes)
                    {
                        _nativeResponse.ReasonLength = (ushort)statusDescriptionBytes.Length;
                        WebHeaderEncoding.GetBytes(StatusDescription, 0, statusDescriptionBytes.Length, statusDescriptionBytes, 0);
                        _nativeResponse.pReason = (sbyte *)pStatusDescription;
                        fixed(Interop.HttpApi.HTTP_RESPONSE *pResponse = &_nativeResponse)
                        {
                            statusCode =
                                Interop.HttpApi.HttpSendHttpResponse(
                                    HttpListenerContext.RequestQueueHandle,
                                    HttpListenerRequest.RequestId,
                                    (uint)flags,
                                    pResponse,
                                    null,
                                    &bytesSent,
                                    SafeLocalAllocHandle.Zero,
                                    0,
                                    asyncResult == null ? null : asyncResult._pOverlapped,
                                    null);

                            if (asyncResult != null &&
                                statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                                HttpListener.SkipIOCPCallbackOnSuccess)
                            {
                                asyncResult.IOCompleted(statusCode, bytesSent);
                                // IO operation completed synchronously - callback won't be called to signal completion.
                            }
                        }
                    }
                }
                else
                {
                    fixed(Interop.HttpApi.HTTP_RESPONSE *pResponse = &_nativeResponse)
                    {
                        statusCode =
                            Interop.HttpApi.HttpSendHttpResponse(
                                HttpListenerContext.RequestQueueHandle,
                                HttpListenerRequest.RequestId,
                                (uint)flags,
                                pResponse,
                                null,
                                &bytesSent,
                                SafeLocalAllocHandle.Zero,
                                0,
                                asyncResult == null ? null : asyncResult._pOverlapped,
                                null);

                        if (asyncResult != null &&
                            statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                            HttpListener.SkipIOCPCallbackOnSuccess)
                        {
                            asyncResult.IOCompleted(statusCode, bytesSent);
                            // IO operation completed synchronously - callback won't be called to signal completion.
                        }
                    }
                }
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendHttpResponse returned:" + statusCode);
                }
            }
            finally
            {
                FreePinnedHeaders(pinnedHeaders);
            }
            return(statusCode);
        }
        private static void IOCompleted(ListenerAsyncResult asyncResult, uint errorCode, uint numBytes)
        {
            object result = null;

            try
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(null, $"errorCode:[{errorCode}] numBytes:[{numBytes}]");
                }

                if (errorCode != Interop.HttpApi.ERROR_SUCCESS &&
                    errorCode != Interop.HttpApi.ERROR_MORE_DATA)
                {
                    asyncResult.ErrorCode = (int)errorCode;
                    result = new HttpListenerException((int)errorCode);
                }
                else
                {
                    HttpListener httpWebListener = asyncResult.AsyncObject as HttpListener;
                    if (errorCode == Interop.HttpApi.ERROR_SUCCESS)
                    {
                        // at this point we have received an unmanaged HTTP_REQUEST and memoryBlob
                        // points to it we need to hook up our authentication handling code here.
                        bool stoleBlob = false;
                        try
                        {
                            if (httpWebListener.ValidateRequest(asyncResult._requestContext))
                            {
                                result = httpWebListener.HandleAuthentication(asyncResult._requestContext, out stoleBlob);
                            }
                        }
                        finally
                        {
                            if (stoleBlob)
                            {
                                // The request has been handed to the user, which means this code can't reuse the blob.  Reset it here.
                                asyncResult._requestContext = result == null ? new AsyncRequestContext(httpWebListener.RequestQueueBoundHandle, asyncResult) : null;
                            }
                            else
                            {
                                asyncResult._requestContext.Reset(httpWebListener.RequestQueueBoundHandle, 0, 0);
                            }
                        }
                    }
                    else
                    {
                        asyncResult._requestContext.Reset(httpWebListener.RequestQueueBoundHandle, asyncResult._requestContext.RequestBlob->RequestId, numBytes);
                    }

                    // We need to issue a new request, either because auth failed, or because our buffer was too small the first time.
                    if (result == null)
                    {
                        uint statusCode = asyncResult.QueueBeginGetContext();
                        if (statusCode != Interop.HttpApi.ERROR_SUCCESS &&
                            statusCode != Interop.HttpApi.ERROR_IO_PENDING)
                        {
                            // someother bad error, possible return values are:
                            // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
                            result = new HttpListenerException((int)statusCode);
                        }
                    }
                    if (result == null)
                    {
                        return;
                    }
                }

                // complete the async IO and invoke the callback
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(null, "Calling Complete()");
                }
            }
            catch (Exception exception) when(!ExceptionCheck.IsFatal(exception))
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(null, $"Caught exception: {exception}");
                }
                result = exception;
            }
            asyncResult.InvokeCallback(result);
        }
        private List <GCHandle> SerializeHeaders(ref Interop.HttpApi.HTTP_RESPONSE_HEADERS headers,
                                                 bool isWebSocketHandshake)
        {
            Interop.HttpApi.HTTP_UNKNOWN_HEADER[] unknownHeaders = null;
            List <GCHandle> pinnedHeaders;
            GCHandle        gcHandle;

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, "SerializeHeaders(HTTP_RESPONSE_HEADERS)");
            }
            if (Headers.Count == 0)
            {
                return(null);
            }
            string headerName;
            string headerValue;
            int    lookup;

            byte[] bytes = null;
            pinnedHeaders = new List <GCHandle>();

            //---------------------------------------------------
            // The Set-Cookie headers are being merged into one.
            // There are two issues here.
            // 1. When Set-Cookie headers are set through SetCookie method on the ListenerResponse,
            // there is code in the SetCookie method and the methods it calls to flatten the Set-Cookie
            // values. This blindly concatenates the cookies with a comma delimiter. There could be
            // a cookie value that contains comma, but we don't escape it with %XX value
            //
            // As an alternative users can add the Set-Cookie header through the AddHeader method
            // like ListenerResponse.Headers.Add("name", "value")
            // That way they can add multiple headers - AND They can format the value like they want it.
            //
            // 2. Now that the header collection contains multiple Set-Cookie name, value pairs
            // you would think the problem would go away. However here is an interesting thing.
            // For NameValueCollection, when you add
            // "Set-Cookie", "value1"
            // "Set-Cookie", "value2"
            //  The NameValueCollection.Count == 1. Because there is only one key
            //  NameValueCollection.Get("Set-Cookie") would conviniently take these two valuess
            //  concatenate them with a comma like
            //  value1,value2.
            //  In order to get individual values, you need to use
            //  string[] values = NameValueCollection.GetValues("Set-Cookie");
            //
            //  -------------------------------------------------------------
            //  So here is the proposed fix here.
            //  We must first to loop through all the NameValueCollection keys
            //  and if the name is a unknown header, we must compute the number of
            //  values it has. Then, we should allocate that many unknown header array
            //  elements.
            //
            //  Note that a part of the fix here is to treat Set-Cookie as an unknown header
            //
            //
            //-----------------------------------------------------------
            int numUnknownHeaders = 0;

            for (int index = 0; index < Headers.Count; index++)
            {
                headerName = Headers.GetKey(index) as string;

                //See if this is an unknown header
                lookup = Interop.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName);

                //Treat Set-Cookie as well as Connection header in Websocket mode as unknown
                if (lookup == (int)HttpResponseHeader.SetCookie ||
                    isWebSocketHandshake && lookup == (int)HttpResponseHeader.Connection)
                {
                    lookup = -1;
                }

                if (lookup == -1)
                {
                    string[] headerValues = Headers.GetValues(index);
                    numUnknownHeaders += headerValues.Length;
                }
            }

            try
            {
                fixed(Interop.HttpApi.HTTP_KNOWN_HEADER *pKnownHeaders = &headers.KnownHeaders)
                {
                    for (int index = 0; index < Headers.Count; index++)
                    {
                        headerName  = Headers.GetKey(index) as string;
                        headerValue = Headers.Get(index) as string;
                        lookup      = Interop.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName);
                        if (lookup == (int)HttpResponseHeader.SetCookie ||
                            isWebSocketHandshake && lookup == (int)HttpResponseHeader.Connection)
                        {
                            lookup = -1;
                        }
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(this,
                                                $"index={index},headers.count={Headers.Count},headerName:{headerName},lookup:{lookup} headerValue:{headerValue}");
                        }
                        if (lookup == -1)
                        {
                            if (unknownHeaders == null)
                            {
                                unknownHeaders = new Interop.HttpApi.HTTP_UNKNOWN_HEADER[numUnknownHeaders];
                                gcHandle       = GCHandle.Alloc(unknownHeaders, GCHandleType.Pinned);
                                pinnedHeaders.Add(gcHandle);
                                headers.pUnknownHeaders = (Interop.HttpApi.HTTP_UNKNOWN_HEADER *)gcHandle.AddrOfPinnedObject();
                            }

                            //----------------------------------------
                            //FOR UNKNOWN HEADERS
                            //ALLOW MULTIPLE HEADERS to be added
                            //---------------------------------------
                            string[] headerValues = Headers.GetValues(index);
                            for (int headerValueIndex = 0; headerValueIndex < headerValues.Length; headerValueIndex++)
                            {
                                //Add Name
                                bytes = new byte[WebHeaderEncoding.GetByteCount(headerName)];
                                unknownHeaders[headers.UnknownHeaderCount].NameLength = (ushort)bytes.Length;
                                WebHeaderEncoding.GetBytes(headerName, 0, bytes.Length, bytes, 0);
                                gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                                pinnedHeaders.Add(gcHandle);
                                unknownHeaders[headers.UnknownHeaderCount].pName = (sbyte *)gcHandle.AddrOfPinnedObject();

                                //Add Value
                                headerValue = headerValues[headerValueIndex];
                                bytes       = new byte[WebHeaderEncoding.GetByteCount(headerValue)];
                                unknownHeaders[headers.UnknownHeaderCount].RawValueLength = (ushort)bytes.Length;
                                WebHeaderEncoding.GetBytes(headerValue, 0, bytes.Length, bytes, 0);
                                gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                                pinnedHeaders.Add(gcHandle);
                                unknownHeaders[headers.UnknownHeaderCount].pRawValue = (sbyte *)gcHandle.AddrOfPinnedObject();
                                headers.UnknownHeaderCount++;
                                if (NetEventSource.IsEnabled)
                                {
                                    NetEventSource.Info(this, "UnknownHeaderCount:" + headers.UnknownHeaderCount);
                                }
                            }
                        }
                        else
                        {
                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Info(this, $"HttpResponseHeader[{lookup}]:{((HttpResponseHeader)lookup)} headerValue:{headerValue}");
                            }
                            if (headerValue != null)
                            {
                                bytes = new byte[WebHeaderEncoding.GetByteCount(headerValue)];
                                pKnownHeaders[lookup].RawValueLength = (ushort)bytes.Length;
                                WebHeaderEncoding.GetBytes(headerValue, 0, bytes.Length, bytes, 0);
                                gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                                pinnedHeaders.Add(gcHandle);
                                pKnownHeaders[lookup].pRawValue = (sbyte *)gcHandle.AddrOfPinnedObject();
                                if (NetEventSource.IsEnabled)
                                {
                                    NetEventSource.Info(this, $"pRawValue:{((IntPtr)(pKnownHeaders[lookup].pRawValue))} RawValueLength:{pKnownHeaders[lookup].RawValueLength} lookup: {lookup}");
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                FreePinnedHeaders(pinnedHeaders);
                throw;
            }
            return(pinnedHeaders);
        }
Exemple #26
0
        private object WaitForCompletion(bool snap)
        {
            ManualResetEvent waitHandle  = null;
            bool             createdByMe = false;
            bool             complete    = snap ? IsCompleted : InternalPeekCompleted;

            if (!complete)
            {
                // Not done yet, so wait:
                waitHandle = (ManualResetEvent)_event;
                if (waitHandle == null)
                {
                    createdByMe = LazilyCreateEvent(out waitHandle);
                }
            }

            if (waitHandle != null)
            {
                try
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"Waiting for completion event {waitHandle}");
                    }
                    waitHandle.WaitOne(Timeout.Infinite);
                }
                catch (ObjectDisposedException)
                {
                    // This can occur if this method is called from two different threads.
                    // This possibility is the trade-off for not locking.
                }
                finally
                {
                    // We also want to dispose the event although we can't unless we did wait on it here.
                    if (createdByMe && !_userEvent)
                    {
                        // Does _userEvent need to be volatile (or _event set via Interlocked) in order
                        // to avoid giving a user a disposed event?
                        ManualResetEvent oldEvent = (ManualResetEvent)_event;
                        _event = null;
                        if (!_userEvent)
                        {
                            oldEvent.Dispose();
                        }
                    }
                }
            }

            // A race condition exists because InvokeCallback sets _intCompleted before _result (so that _result
            // can benefit from the synchronization of _intCompleted).  That means you can get here before _result got
            // set (although rarely - once every eight hours of stress).  Handle that case with a spin-lock.

            SpinWait sw = default;

            while (_result == DBNull.Value)
            {
                sw.SpinOnce();
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this, _result);
            }
            return(_result);
        }
Exemple #27
0
        public IAsyncResult BeginReadCore(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
        {
            if (size == 0 || _closed)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Exit(this);
                }
                HttpRequestStreamAsyncResult result = new HttpRequestStreamAsyncResult(this, state, callback);
                result.InvokeCallback((uint)0);
                return(result);
            }

            HttpRequestStreamAsyncResult asyncResult = null;

            uint dataRead = 0;

            if (_dataChunkIndex != -1)
            {
                dataRead = Interop.HttpApi.GetChunks(_httpContext.Request.RequestBuffer, _httpContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
                if (_dataChunkIndex != -1 && dataRead == size)
                {
                    asyncResult = new HttpRequestStreamAsyncResult(_httpContext.RequestQueueBoundHandle, this, state, callback, buffer, offset, (uint)size, 0);
                    asyncResult.InvokeCallback(dataRead);
                }
            }

            if (_dataChunkIndex == -1 && dataRead < size)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(this, "size:" + size + " offset:" + offset);
                }
                uint statusCode = 0;
                offset += (int)dataRead;
                size   -= (int)dataRead;

                //the http.sys team recommends that we limit the size to 128kb
                if (size > MaxReadSize)
                {
                    size = MaxReadSize;
                }

                asyncResult = new HttpRequestStreamAsyncResult(_httpContext.RequestQueueBoundHandle, this, state, callback, buffer, offset, (uint)size, dataRead);
                uint bytesReturned;

                try
                {
                    fixed(byte *pBuffer = buffer)
                    {
                        // issue unmanaged blocking call
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(this, "Calling Interop.HttpApi.HttpReceiveRequestEntityBody");
                        }

                        uint flags = 0;

                        if (!_inOpaqueMode)
                        {
                            flags = (uint)Interop.HttpApi.HTTP_FLAGS.HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY;
                        }

                        statusCode =
                            Interop.HttpApi.HttpReceiveRequestEntityBody(
                                _httpContext.RequestQueueHandle,
                                _httpContext.RequestId,
                                flags,
                                asyncResult._pPinnedBuffer,
                                (uint)size,
                                out bytesReturned,
                                asyncResult._pOverlapped);

                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(this, "Call to Interop.HttpApi.HttpReceiveRequestEntityBody returned:" + statusCode + " dataRead:" + dataRead);
                        }
                    }
                }
                catch (Exception e)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(this, e.ToString());
                    }
                    asyncResult.InternalCleanup();
                    throw;
                }

                if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING)
                {
                    asyncResult.InternalCleanup();
                    if (statusCode == Interop.HttpApi.ERROR_HANDLE_EOF)
                    {
                        asyncResult = new HttpRequestStreamAsyncResult(this, state, callback, dataRead);
                        asyncResult.InvokeCallback((uint)0);
                    }
                    else
                    {
                        Exception exception = new HttpListenerException((int)statusCode);
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Error(this, exception.ToString());
                        }
                        asyncResult.InternalCleanup();
                        throw exception;
                    }
                }
                else if (statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                         HttpListener.SkipIOCPCallbackOnSuccess)
                {
                    // IO operation completed synchronously - callback won't be called to signal completion.
                    asyncResult.IOCompleted(statusCode, bytesReturned);
                }
            }
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Exit(this);
            }
            return(asyncResult);
        }
        /*++
         *
         * Routine Description:
         *
         *  Takes a native pointer (expressed as an int) to a hostent structure,
         *  and converts the information in their to an IPHostEntry class. This
         *  involves walking through an array of native pointers, and a temporary
         *  ArrayList object is used in doing this.
         *
         * Arguments:
         *
         *  nativePointer   - Native pointer to hostent structure.
         *
         *
         *
         * Return Value:
         *
         *  An IPHostEntry structure.
         *
         * --*/
        private static IPHostEntry NativeToHostEntry(IntPtr nativePointer)
        {
            //
            // marshal pointer to struct
            //

            hostent     Host      = Marshal.PtrToStructure <hostent>(nativePointer);
            IPHostEntry HostEntry = new IPHostEntry();

            if (Host.h_name != IntPtr.Zero)
            {
                HostEntry.HostName = Marshal.PtrToStringAnsi(Host.h_name);
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(null, $"HostEntry.HostName: {HostEntry.HostName}");
                }
            }

            // decode h_addr_list to ArrayList of IP addresses.
            // The h_addr_list field is really a pointer to an array of pointers
            // to IP addresses. Loop through the array, and while the pointer
            // isn't NULL read the IP address, convert it to an IPAddress class,
            // and add it to the list.

            var    TempIPAddressList = new List <IPAddress>();
            int    IPAddressToAdd;
            string AliasToAdd;
            IntPtr currentArrayElement;

            //
            // get the first pointer in the array
            //
            currentArrayElement = Host.h_addr_list;
            nativePointer       = Marshal.ReadIntPtr(currentArrayElement);

            while (nativePointer != IntPtr.Zero)
            {
                //
                // if it's not null it points to an IPAddress,
                // read it...
                //
                IPAddressToAdd = Marshal.ReadInt32(nativePointer);
#if BIGENDIAN
                // IP addresses from native code are always a byte array
                // converted to int.  We need to convert the address into
                // a uniform integer value.
                IPAddressToAdd = (int)(((uint)IPAddressToAdd << 24) |
                                       (((uint)IPAddressToAdd & 0x0000FF00) << 8) |
                                       (((uint)IPAddressToAdd >> 8) & 0x0000FF00) |
                                       ((uint)IPAddressToAdd >> 24));
#endif

                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(null, $"currentArrayElement:{currentArrayElement} nativePointer:{nativePointer} IPAddressToAdd:{IPAddressToAdd}");
                }

                //
                // ...and add it to the list
                //
                TempIPAddressList.Add(new IPAddress((long)IPAddressToAdd & 0x0FFFFFFFF));

                //
                // now get the next pointer in the array and start over
                //
                currentArrayElement = currentArrayElement + IntPtr.Size;
                nativePointer       = Marshal.ReadIntPtr(currentArrayElement);
            }

            HostEntry.AddressList = TempIPAddressList.ToArray();

            //
            // Now do the same thing for the aliases.
            //

            var TempAliasList = new List <string>();

            currentArrayElement = Host.h_aliases;
            nativePointer       = Marshal.ReadIntPtr(currentArrayElement);

            while (nativePointer != IntPtr.Zero)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(null, $"currentArrayElement:{currentArrayElement} nativePointer:{nativePointer}");
                }

                //
                // if it's not null it points to an Alias,
                // read it...
                //
                AliasToAdd = Marshal.PtrToStringAnsi(nativePointer);

                //
                // ...and add it to the list
                //
                TempAliasList.Add(AliasToAdd);

                //
                // now get the next pointer in the array and start over
                //
                currentArrayElement = currentArrayElement + IntPtr.Size;
                nativePointer       = Marshal.ReadIntPtr(currentArrayElement);
            }

            HostEntry.Aliases = TempAliasList.ToArray();

            return(HostEntry);
        } // NativeToHostEntry
Exemple #29
0
        internal CookieCollection CookieCutter(Uri uri, string headerName, string setCookieHeader, bool isThrow)
        {
            if (NetEventSource.IsEnabled)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"uri:{uri} headerName:{headerName} setCookieHeader:{setCookieHeader} isThrow:{isThrow}");
                }
            }

            CookieCollection cookies = new CookieCollection();
            CookieVariant    variant = CookieVariant.Unknown;

            if (headerName == null)
            {
                variant = CookieVariant.Default;
            }
            else
            {
                for (int i = 0; i < s_headerInfo.Length; ++i)
                {
                    if ((string.Equals(headerName, s_headerInfo[i].Name, StringComparison.OrdinalIgnoreCase)))
                    {
                        variant = s_headerInfo[i].Variant;
                    }
                }
            }

            bool isLocalDomain = IsLocalDomain(uri.Host);

            try
            {
                CookieParser parser = new CookieParser(setCookieHeader);
                do
                {
                    Cookie cookie = parser.Get();
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"CookieParser returned cookie:{cookie}");
                    }

                    if (cookie == null)
                    {
                        if (parser.EndofHeader())
                        {
                            break;
                        }
                        continue;
                    }

                    // Parser marks invalid cookies this way
                    if (string.IsNullOrEmpty(cookie.Name))
                    {
                        if (isThrow)
                        {
                            throw new CookieException(SR.net_cookie_format);
                        }
                        // Otherwise, ignore (reject) cookie
                        continue;
                    }

                    // This will set the default values from the response URI
                    // AND will check for cookie validity
                    if (!cookie.VerifySetDefaults(variant, uri, isLocalDomain, m_fqdnMyDomain, true, isThrow))
                    {
                        continue;
                    }
                    // If many same cookies arrive we collapse them into just one, hence setting
                    // parameter isStrict = true below
                    cookies.InternalAdd(cookie, true);
                } while (true);
            }
            catch (OutOfMemoryException)
            {
                throw;
            }
            catch (Exception e)
            {
                if (isThrow)
                {
                    throw new CookieException(SR.Format(SR.net_cookie_parse_header, uri.AbsoluteUri), e);
                }
            }

            foreach (Cookie c in cookies)
            {
                Add(c, isThrow);
            }

            return(cookies);
        }
Exemple #30
0
        } // GetHostByAddress

        // Does internal IPAddress reverse and then forward lookups (for Legacy and current public methods).
        private static IPHostEntry InternalGetHostByAddress(IPAddress address, bool includeIPv6)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(null, address);
            }

            //
            // IPv6 Changes: We need to use the new getnameinfo / getaddrinfo functions
            //               for resolution of IPv6 addresses.
            //

            if (SocketProtocolSupportPal.OSSupportsIPv6 || includeIPv6)
            {
                //
                // Try to get the data for the host from it's address
                //
                // We need to call getnameinfo first, because getaddrinfo w/ the ipaddress string
                // will only return that address and not the full list.

                // Do a reverse lookup to get the host name.
                SocketError errorCode;
                int         nativeErrorCode;
                string      name = NameResolutionPal.TryGetNameInfo(address, out errorCode, out nativeErrorCode);
                if (errorCode == SocketError.Success)
                {
                    // Do the forward lookup to get the IPs for that host name
                    IPHostEntry hostEntry;
                    errorCode = NameResolutionPal.TryGetAddrInfo(name, out hostEntry, out nativeErrorCode);
                    if (errorCode == SocketError.Success)
                    {
                        return(hostEntry);
                    }

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Error(null, SocketExceptionFactory.CreateSocketException(errorCode, nativeErrorCode));
                    }

                    // One of two things happened:
                    // 1. There was a ptr record in dns, but not a corollary A/AAA record.
                    // 2. The IP was a local (non-loopback) IP that resolved to a connection specific dns suffix.
                    //    - Workaround, Check "Use this connection's dns suffix in dns registration" on that network
                    //      adapter's advanced dns settings.

                    // Just return the resolved host name and no IPs.
                    return(hostEntry);
                }

                throw SocketExceptionFactory.CreateSocketException(errorCode, nativeErrorCode);
            }

            //
            // If IPv6 is not enabled (maybe config switch) but we've been
            // given an IPv6 address then we need to bail out now.
            //
            else
            {
                if (address.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    //
                    // Protocol not supported
                    //
                    throw new SocketException((int)SocketError.ProtocolNotSupported);
                }
                //
                // Use gethostbyaddr() to try to resolve the IP address
                //
                // End IPv6 Changes
                //
                return(NameResolutionPal.GetHostByAddr(address));
            }
        } // InternalGetHostByAddress