Example #1
0
        // Returns true if the address table is already stable.  Otherwise, calls callback when it becomes stable.
        // 'Unsafe' because it does not flow ExecutionContext to the callback.
        public static bool UnsafeNotifyStableUnicastIpAddressTable(Action <object> callback, object state)
        {
            if (callback == null)
            {
                NetEventSource.Fail(null, "UnsafeNotifyStableUnicastIpAddressTable called without specifying callback!");
            }

            TeredoHelper helper = new TeredoHelper(callback, state);

            uint             err   = Interop.IpHlpApi.ERROR_SUCCESS;
            SafeFreeMibTable table = null;

            lock (s_pendingNotifications)
            {
                // If OnAppDomainUnload gets the lock first, tell our caller that we'll finish async.  Their AppDomain
                // is about to go down anyways.  If we do, hold the lock until we've added helper to the
                // s_pendingNotifications list (if we're going to complete asynchronously).
                if (Environment.HasShutdownStarted)
                {
                    return(false);
                }

                err = Interop.IpHlpApi.NotifyStableUnicastIpAddressTable(AddressFamily.Unspecified,
                                                                         out table, helper._onStabilizedDelegate, IntPtr.Zero, out helper._cancelHandle);

                if (table != null)
                {
                    table.Dispose();
                }

                if (err == Interop.IpHlpApi.ERROR_IO_PENDING)
                {
                    if (helper._cancelHandle.IsInvalid)
                    {
                        NetEventSource.Fail(null, "CancelHandle invalid despite returning ERROR_IO_PENDING");
                    }

                    // Async completion: add us to the s_pendingNotifications list so we'll be canceled in the
                    // event of an AppDomain unload.
                    s_pendingNotifications.Add(helper);
                    return(false);
                }
            }

            if (err != Interop.IpHlpApi.ERROR_SUCCESS)
            {
                throw new Win32Exception((int)err);
            }

            return(true);
        }
Example #2
0
        private bool CloseHandle(bool abortive, bool canceledOperations)
        {
            bool ret = false;

#if DEBUG
            try
            {
#endif
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"handle:{handle}");
            }

            canceledOperations |= OnHandleClose();

            // In case we cancel operations, switch to an abortive close.
            // Unless the user requested a normal close using Socket.Shutdown.
            if (canceledOperations && !_hasShutdownSend)
            {
                abortive = true;
            }

            SocketError errorCode = DoCloseHandle(abortive);
            return(ret = errorCode == SocketError.Success);

#if DEBUG
        }

        catch (Exception exception)
        {
            if (!ExceptionCheck.IsFatal(exception))
            {
                NetEventSource.Fail(this, $"handle:{handle}, error:{exception}");
            }

            ret = true;      // Avoid a second assert.
            throw;
        }
        finally
        {
            _closeSocketThread = Environment.CurrentManagedThreadId;
            _closeSocketTick   = Environment.TickCount;
            if (!ret)
            {
                NetEventSource.Fail(this, $"ReleaseHandle failed. handle:{handle}");
            }
        }
#endif
        }
        internal static int VerifySignature(SafeDeleteContext securityContext, byte[] buffer, int offset, int count)
        {
            if (offset < 0 || offset > (buffer == null ? 0 : buffer.Length))
            {
                NetEventSource.Fail(securityContext, "Argument 'offset' out of range");
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (count < 0 || count > (buffer == null ? 0 : buffer.Length - offset))
            {
                NetEventSource.Fail(securityContext, "Argument 'count' out of range.");
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            return(GssUnwrap(((SafeDeleteNegoContext)securityContext).GssContext, buffer !, offset, count));
        }
Example #4
0
        public SecurityBuffer(byte[] data, int offset, int size, SecurityBufferType tokentype)
        {
            if (offset < 0 || offset > (data == null ? 0 : data.Length))
            {
                NetEventSource.Fail(this, $"'offset' out of range.  [{offset}]");
            }

            if (size < 0 || size > (data == null ? 0 : data.Length - offset))
            {
                NetEventSource.Fail(this, $"'size' out of range.  [{size}]");
            }

            this.offset = data == null || offset < 0 ? 0 : Math.Min(offset, data.Length);
            this.size   = data == null || size < 0 ? 0 : Math.Min(size, data.Length - this.offset);
            this.type   = tokentype;
            this.token  = size == 0 ? null : data;
        }
Example #5
0
        internal void CloseAsIs(bool abortive)
        {
#if DEBUG
            // If this throws it could be very bad.
            try
            {
#endif
            bool shouldClose = TryOwnClose();

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"shouldClose={shouldClose}");
            }

            Dispose();

            if (shouldClose)
            {
                bool canceledOperations = false;

                // Wait until it's safe.
                SpinWait sw = default;
                while (!_released)
                {
                    // The socket was not released due to the SafeHandle being used.
                    // Try to make those on-going calls return.
                    // On Linux, TryUnblockSocket will unblock current operations but it doesn't prevent
                    // a new one from starting. So we must call TryUnblockSocket multiple times.
                    canceledOperations |= TryUnblockSocket(abortive);
                    sw.SpinOnce();
                }

                CloseHandle(abortive, canceledOperations);
            }
#if DEBUG
        }

        catch (Exception exception) when(!ExceptionCheck.IsFatal(exception))
        {
            NetEventSource.Fail(this, $"handle:{handle}, error:{exception}");
            throw;
        }
#endif
        }
        // Called by Socket to kick off the ConnectAsync process.  We'll complete the user's SAEA when it's done.
        // Returns true if the operation is pending, false if it completed synchronously.
        public bool StartConnectAsync(SocketAsyncEventArgs args, DnsEndPoint endPoint)
        {
            IAsyncResult result;

            Debug.Assert(!Monitor.IsEntered(_lockObject));
            lock (_lockObject)
            {
                if (endPoint.AddressFamily != AddressFamily.Unspecified &&
                    endPoint.AddressFamily != AddressFamily.InterNetwork &&
                    endPoint.AddressFamily != AddressFamily.InterNetworkV6)
                {
                    NetEventSource.Fail(this, $"Unexpected endpoint address family: {endPoint.AddressFamily}");
                }

                _userArgs = args;
                _endPoint = endPoint;

                // If Cancel() was called before we got the lock, it only set the state to Canceled: we need to
                // fail synchronously from here.  Once State.DnsQuery is set, the Cancel() call will handle calling AsyncFail.
                if (_state == State.Canceled)
                {
                    SyncFail(new SocketException((int)SocketError.OperationAborted));
                    return(false);
                }

                if (_state != State.NotStarted)
                {
                    NetEventSource.Fail(this, "MultipleConnectAsync.StartConnectAsync(): Unexpected object state");
                }

                _state = State.DnsQuery;

                result = Dns.BeginGetHostAddresses(endPoint.Host, new AsyncCallback(DnsCallback), null);
            }

            if (result.CompletedSynchronously)
            {
                return(DoDnsCallback(result, true));
            }
            else
            {
                return(true);
            }
        }
        internal void CloseAsIs(bool abortive)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"_innerSocket={_innerSocket}");
            }

#if DEBUG
            // If this throws it could be very bad.
            try
            {
#endif
            InnerSafeCloseSocket innerSocket = _innerSocket == null ? null : Interlocked.Exchange <InnerSafeCloseSocket>(ref _innerSocket, null);

            Dispose();
            if (innerSocket != null)
            {
                // Wait until it's safe.
                SpinWait sw = new SpinWait();
                while (!_released)
                {
                    // The socket was not released due to the SafeHandle being used.
                    // Try to make those on-going calls return.
                    // On Linux, TryUnblockSocket will unblock current operations but it doesn't prevent
                    // a new one from starting. So we must call TryUnblockSocket multiple times.
                    abortive |= innerSocket.TryUnblockSocket(abortive);
                    sw.SpinOnce();
                }

                abortive |= DoReleaseHandle();

                innerSocket.Close(abortive);
            }
#if DEBUG
        }

        catch (Exception exception) when(!ExceptionCheck.IsFatal(exception))
        {
            NetEventSource.Fail(this, $"handle:{handle}, error:{exception}");
            throw;
        }
#endif
        }
Example #8
0
        private static void WriteCallback(IAsyncResult transportResult)
        {
            if (transportResult.CompletedSynchronously)
            {
                return;
            }

            if (!(transportResult.AsyncState is AsyncProtocolRequest))
            {
                NetEventSource.Fail(transportResult, "State type is wrong, expected AsyncProtocolRequest.");
            }

            AsyncProtocolRequest asyncRequest = (AsyncProtocolRequest)transportResult.AsyncState;

            var sslStream = (SslStreamInternal)asyncRequest.AsyncObject;

            try
            {
                TaskToApm.End(transportResult);
                sslStream._sslState.FinishWrite();

                if (asyncRequest.Count == 0)
                {
                    // This was the last chunk.
                    asyncRequest.Count = -1;
                }

                sslStream.StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
            }
            catch (Exception e)
            {
                if (asyncRequest.IsUserCompleted)
                {
                    // This will throw on a worker thread.
                    throw;
                }

                sslStream._sslState.FinishWrite();
                asyncRequest.CompleteUserWithError(e);
            }
        }
Example #9
0
 internal void CancelConnectAsync()
 {
     if (_operating == InProgress && _completedOperation == SocketAsyncOperation.Connect)
     {
         if (_multipleConnect != null)
         {
             // If a multiple connect is in progress, abort it.
             _multipleConnect.Cancel();
         }
         else
         {
             // Otherwise we're doing a normal ConnectAsync - cancel it by closing the socket.
             // _currentSocket will only be null if _multipleConnect was set, so we don't have to check.
             if (_currentSocket == null)
             {
                 NetEventSource.Fail(this, "CurrentSocket and MultipleConnect both null!");
             }
             _currentSocket.Dispose();
         }
     }
 }
Example #10
0
        internal void CloseAsIs()
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"_innerSocket={_innerSocket}");
            }

#if DEBUG
            // If this throws it could be very bad.
            try
            {
#endif
            InnerSafeCloseSocket innerSocket = _innerSocket == null ? null : Interlocked.Exchange <InnerSafeCloseSocket>(ref _innerSocket, null);

            Dispose();
            if (innerSocket != null)
            {
                // Wait until it's safe.
                SpinWait sw = new SpinWait();
                while (!_released)
                {
                    sw.SpinOnce();
                }

                InnerReleaseHandle();

                // Now free it with blocking.
                innerSocket.BlockingRelease();
            }

#if DEBUG
        }

        catch (Exception exception) when(!ExceptionCheck.IsFatal(exception))
        {
            NetEventSource.Fail(this, $"handle:{handle}, error:{exception}");
            throw;
        }
#endif
        }
            // This method is implicitly reliable and called from a CER.
            protected override bool ReleaseHandle()
            {
                bool ret = false;

#if DEBUG
                try
                {
#endif
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"handle:{handle}");
                }

                SocketError errorCode = InnerReleaseHandle();
                return(ret = errorCode == SocketError.Success);

#if DEBUG
            }

            catch (Exception exception)
            {
                if (!ExceptionCheck.IsFatal(exception))
                {
                    NetEventSource.Fail(this, $"handle:{handle}, error:{exception}");
                }

                ret = true;      // Avoid a second assert.
                throw;
            }
            finally
            {
                _closeSocketThread = Environment.CurrentManagedThreadId;
                _closeSocketTick   = Environment.TickCount;
                if (!ret)
                {
                    NetEventSource.Fail(this, $"ReleaseHandle failed. handle:{handle}");
                }
            }
#endif
            }
Example #12
0
        private static void WriteCallback(IAsyncResult transportResult)
        {
            if (!(transportResult.AsyncState is LazyAsyncResult))
            {
                NetEventSource.Fail(transportResult, "State type is wrong, expected LazyAsyncResult.");
            }

            if (transportResult.CompletedSynchronously)
            {
                return;
            }

            LazyAsyncResult lazyResult = (LazyAsyncResult)transportResult.AsyncState;

            // Async completion.
            try
            {
                NegoState authState = (NegoState)lazyResult.AsyncObject;
                authState._framer.EndWriteMessage(transportResult);

                // Special case for an error notification.
                if (lazyResult.Result is Exception e)
                {
                    authState._canRetryAuthentication = true;
                    ExceptionDispatchInfo.Throw(e);
                }

                authState.CheckCompletionBeforeNextReceive(lazyResult);
            }
            catch (Exception e)
            {
                if (lazyResult.InternalPeekCompleted)
                {
                    // This will throw on a worker thread.
                    throw;
                }

                lazyResult.InvokeCallback(e);
            }
        }
Example #13
0
        //
        // To avoid recursion when decrypted 0 bytes this method will loop until a decrypted result at least 1 byte.
        //
        private int StartReading(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
        {
            int result = 0;

            if (InternalBufferCount != 0)
            {
                NetEventSource.Fail(this, $"Previous frame was not consumed. InternalBufferCount: {InternalBufferCount}");
            }

            do
            {
                if (asyncRequest != null)
                {
                    asyncRequest.SetNextRequest(buffer, offset, count, s_resumeAsyncReadCallback);
                }

                int copyBytes = _sslState.CheckEnqueueRead(buffer, offset, count, asyncRequest);
                if (copyBytes == 0)
                {
                    // Queued but not completed!
                    return(0);
                }

                if (copyBytes != -1)
                {
                    if (asyncRequest != null)
                    {
                        asyncRequest.CompleteUser((object)copyBytes);
                    }

                    return(copyBytes);
                }
            }
            // When we read -1 bytes means we have decrypted 0 bytes or rehandshaking, need looping.
            while ((result = StartFrameHeader(buffer, offset, count, asyncRequest)) == -1);

            return(result);
        }
        private Exception AttemptUserConnection()
        {
            Debug.Assert(!SocketPal.SupportsMultipleConnectAttempts);
            Debug.Assert(_lastAttemptSocket != null);

            try
            {
                _lastAttemptSocket.Dispose();

                // Setup the internal args. RemoteEndpoint should already be correct.
                _internalArgs.SetBuffer(_userArgs.Buffer, _userArgs.Offset, _userArgs.Count);

                return(AttemptConnection(UserSocket, _internalArgs));
            }
            catch (Exception e)
            {
                if (e is ObjectDisposedException)
                {
                    NetEventSource.Fail(this, "unexpected ObjectDisposedException");
                }
                return(e);
            }
        }
        private static void WriteCallback(IAsyncResult transportResult)
        {
            if (transportResult.CompletedSynchronously)
            {
                return;
            }

            if (!(transportResult.AsyncState is AsyncProtocolRequest))
            {
                NetEventSource.Fail(transportResult, "State type is wrong, expected AsyncProtocolRequest.");
            }

            AsyncProtocolRequest asyncRequest = (AsyncProtocolRequest)transportResult.AsyncState;

            try
            {
                NegotiateStream negoStream = (NegotiateStream)asyncRequest.AsyncObject;
                negoStream.InnerStream.EndWrite(transportResult);
                if (asyncRequest.Count == 0)
                {
                    // This was the last chunk.
                    asyncRequest.Count = -1;
                }

                negoStream.StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
            }
            catch (Exception e)
            {
                if (asyncRequest.IsUserCompleted)
                {
                    // This will throw on a worker thread.
                    throw;
                }

                asyncRequest.CompleteWithError(e);
            }
        }
Example #16
0
        private Authorization SetContextAndTryAuthenticate(ISmtpAuthenticationModule module, NetworkCredential credential, ContextAwareResult context)
        {
            // We may need to restore user thread token here
            if (ReferenceEquals(credential, CredentialCache.DefaultNetworkCredentials))
            {
#if DEBUG
                if (context != null && !context.IdentityRequested)
                {
                    NetEventSource.Fail(this, "Authentication required when it wasn't expected.  (Maybe Credentials was changed on another thread?)");
                }
#endif

                try
                {
                    ExecutionContext x = context == null ? null : context.ContextCopy;
                    if (x != null)
                    {
                        AuthenticateCallbackContext authenticationContext =
                            new AuthenticateCallbackContext(this, module, credential, _client.TargetName, _channelBindingToken);

                        ExecutionContext.Run(x, s_AuthenticateCallback, authenticationContext);
                        return(authenticationContext._result);
                    }
                    else
                    {
                        return(module.Authenticate(null, credential, this, _client.TargetName, _channelBindingToken));
                    }
                }
                catch
                {
                    // Prevent the impersonation from leaking to upstack exception filters.
                    throw;
                }
            }

            return(module.Authenticate(null, credential, this, _client.TargetName, _channelBindingToken));
        }
Example #17
0
        private void RunCallback(object o)
        {
            if (!_runCallbackCalled)
            {
                NetEventSource.Fail(null, "RunCallback called without setting runCallbackCalled!");
            }

            // If OnAppDomainUnload beats us to the lock, do nothing: the AppDomain is going down soon anyways.
            // Otherwise, wait until the call to CancelMibChangeNotify2 is done before giving it up.
            lock (s_pendingNotifications)
            {
                if (Environment.HasShutdownStarted)
                {
                    return;
                }

#if DEBUG
                bool successfullyRemoved = s_pendingNotifications.Remove(this);
                if (!successfullyRemoved)
                {
                    NetEventSource.Fail(null, "RunCallback for a TeredoHelper which is not in s_pendingNotifications!");
                }
#else
                s_pendingNotifications.Remove(this);
#endif

                if ((_cancelHandle == null || _cancelHandle.IsInvalid))
                {
                    NetEventSource.Fail(null, "Invalid cancelHandle in RunCallback");
                }

                _cancelHandle.Dispose();
            }

            _callback.Invoke(_state);
        }
Example #18
0
        internal static int Decrypt(
            SafeDeleteContext securityContext,
            byte[] buffer,
            int offset,
            int count,
            bool isConfidential,
            bool isNtlm,
            out int newOffset,
            uint sequenceNumber)
        {
            if (offset < 0 || offset > (buffer == null ? 0 : buffer.Length))
            {
                NetEventSource.Fail(null, "Argument 'offset' out of range.");
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (count < 0 || count > (buffer == null ? 0 : buffer.Length - offset))
            {
                NetEventSource.Fail(null, "Argument 'count' out of range.");
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            if (isNtlm)
            {
                return(DecryptNtlm(securityContext, buffer, offset, count, isConfidential, out newOffset, sequenceNumber));
            }

            //
            // Kerberos and up
            //
            TwoSecurityBuffers buffers = default;
            var securityBuffer         = MemoryMarshal.CreateSpan(ref buffers._item0, 2);

            securityBuffer[0] = new SecurityBuffer(buffer, offset, count, SecurityBufferType.SECBUFFER_STREAM);
            securityBuffer[1] = new SecurityBuffer(0, SecurityBufferType.SECBUFFER_DATA);

            int errorCode;

            if (isConfidential)
            {
                errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
            }
            else
            {
                errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
            }

            if (errorCode != 0)
            {
                Exception e = new Win32Exception(errorCode);
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(null, e);
                }
                throw e;
            }

            if (securityBuffer[1].type != SecurityBufferType.SECBUFFER_DATA)
            {
                throw new InternalException(securityBuffer[1].type);
            }

            newOffset = securityBuffer[1].offset;
            return(securityBuffer[1].size);
        }
Example #19
0
        public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, ReadOnlyMemory <byte> input, int headerSize, int trailerSize, ref byte[] output, out int resultSize)
        {
            // Ensure that there is sufficient space for the message output.
            int bufferSizeNeeded;

            try
            {
                bufferSizeNeeded = checked (input.Length + headerSize + trailerSize);
            }
            catch
            {
                NetEventSource.Fail(securityContext, "Arguments out of range");
                throw;
            }
            if (output == null || output.Length < bufferSizeNeeded)
            {
                output = new byte[bufferSizeNeeded];
            }

            // Copy the input into the output buffer to prepare for SCHANNEL's expectations
            input.Span.CopyTo(new Span <byte>(output, headerSize, input.Length));

            const int NumSecBuffers   = 4; // header + data + trailer + empty
            var       unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var       sdcInOut        = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);

            sdcInOut.pBuffers = unmanagedBuffer;
            fixed(byte *outputPtr = output)
            {
                Interop.SspiCli.SecBuffer *headerSecBuffer = &unmanagedBuffer[0];
                headerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_HEADER;
                headerSecBuffer->pvBuffer   = (IntPtr)outputPtr;
                headerSecBuffer->cbBuffer   = headerSize;

                Interop.SspiCli.SecBuffer *dataSecBuffer = &unmanagedBuffer[1];
                dataSecBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize);
                dataSecBuffer->cbBuffer   = input.Length;

                Interop.SspiCli.SecBuffer *trailerSecBuffer = &unmanagedBuffer[2];
                trailerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_TRAILER;
                trailerSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize + input.Length);
                trailerSecBuffer->cbBuffer   = trailerSize;

                Interop.SspiCli.SecBuffer *emptySecBuffer = &unmanagedBuffer[3];
                emptySecBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                emptySecBuffer->cbBuffer   = 0;
                emptySecBuffer->pvBuffer   = IntPtr.Zero;

                int errorCode = GlobalSSPI.SSPISecureChannel.EncryptMessage(securityContext, ref sdcInOut, 0);

                if (errorCode != 0)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(securityContext, $"Encrypt ERROR {errorCode:X}");
                    }
                    resultSize = 0;
                    return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
                }

                Debug.Assert(headerSecBuffer->cbBuffer >= 0 && dataSecBuffer->cbBuffer >= 0 && trailerSecBuffer->cbBuffer >= 0);
                Debug.Assert(checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer) <= output.Length);

                resultSize = checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer);
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.OK));
            }
        }
        // Callback which fires when an internal connection attempt completes.
        // If it failed and there are more addresses to try, do it.
        private void InternalConnectCallback(object sender, SocketAsyncEventArgs args)
        {
            Exception exception = null;

            lock (_lockObject)
            {
                if (_state == State.Canceled)
                {
                    // If Cancel was called before we got the lock, the Socket will be closed soon.  We need to report
                    // OperationAborted (even though the connection actually completed), or the user will try to use a
                    // closed Socket.
                    exception = new SocketException((int)SocketError.OperationAborted);
                }
                else if (_state == State.ConnectAttempt)
                {
                    if (args.SocketError == SocketError.Success)
                    {
                        if (RequiresUserConnectAttempt)
                        {
                            exception = AttemptUserConnection();

                            if (exception == null)
                            {
                                // Don't call the callback; we've started a connection attempt on the
                                // user's socket.
                                _state = State.UserConnectAttempt;
                                return;
                            }
                        }

                        // The connection attempt succeeded or the user connect attempt failed synchronously; go to the
                        // completed state. The callback will be called outside the lock.
                        _state = State.Completed;
                    }
                    else if (args.SocketError == SocketError.OperationAborted)
                    {
                        // The socket was closed while the connect was in progress.  This can happen if the user
                        // closes the socket, and is equivalent to a call to CancelConnectAsync
                        exception = new SocketException((int)SocketError.OperationAborted);
                        _state    = State.Canceled;
                    }
                    else
                    {
                        // Try again, if there are more IPAddresses to be had.

                        // If the underlying OS does not support multiple connect attempts
                        // on the same socket, dispose the socket used for the last attempt.
                        if (!SocketPal.SupportsMultipleConnectAttempts)
                        {
                            _lastAttemptSocket.Dispose();
                        }

                        // Keep track of this because it will be overwritten by AttemptConnection
                        SocketError currentFailure   = args.SocketError;
                        Exception   connectException = AttemptConnection();

                        if (connectException == null)
                        {
                            // don't call the callback, another connection attempt is successfully started
                            return;
                        }
                        else
                        {
                            SocketException socketException = connectException as SocketException;
                            if (socketException != null && socketException.SocketErrorCode == SocketError.NoData)
                            {
                                // If the error is NoData, that means there are no more IPAddresses to attempt
                                // a connection to.  Return the last error from an actual connection instead.
                                exception = new SocketException((int)currentFailure);
                            }
                            else
                            {
                                exception = connectException;
                            }

                            _state = State.Completed;
                        }
                    }
                }
                else
                {
                    if (_state != State.UserConnectAttempt)
                    {
                        NetEventSource.Fail(this, "Unexpected object state");
                    }
                    if (!RequiresUserConnectAttempt)
                    {
                        NetEventSource.Fail(this, "State.UserConnectAttempt without RequiresUserConnectAttempt");
                    }

                    if (args.SocketError == SocketError.Success)
                    {
                        _state = State.Completed;
                    }
                    else if (args.SocketError == SocketError.OperationAborted)
                    {
                        // The socket was closed while the connect was in progress.  This can happen if the user
                        // closes the socket, and is equivalent to a call to CancelConnectAsync
                        exception = new SocketException((int)SocketError.OperationAborted);
                        _state    = State.Canceled;
                    }
                    else
                    {
                        // The connect attempt on the user's socket failed. Return the corresponding error.
                        exception = new SocketException((int)args.SocketError);
                        _state    = State.Completed;
                    }
                }
            }

            if (exception == null)
            {
                Succeed();
            }
            else
            {
                AsyncFail(exception);
            }
        }
        private static unsafe void CompletionPortCallback(uint errorCode, uint numBytes, NativeOverlapped *nativeOverlapped)
        {
#if DEBUG
            DebugThreadTracking.SetThreadSource(ThreadKinds.CompletionPort);
            using (DebugThreadTracking.SetThreadKind(ThreadKinds.System))
            {
#endif
            BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped);

            object returnObject = null;

            if (asyncResult.InternalPeekCompleted)
            {
                NetEventSource.Fail(null, $"asyncResult.IsCompleted: {asyncResult}");
            }
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(null, $"errorCode:{errorCode} numBytes:{numBytes} nativeOverlapped:{(IntPtr)nativeOverlapped}");
            }

            // Complete the IO and invoke the user's callback.
            SocketError socketError = (SocketError)errorCode;

            if (socketError != SocketError.Success && socketError != SocketError.OperationAborted)
            {
                // There are cases where passed errorCode does not reflect the details of the underlined socket error.
                // "So as of today, the key is the difference between WSAECONNRESET and ConnectionAborted,
                //  .e.g remote party or network causing the connection reset or something on the local host (e.g. closesocket
                // or receiving data after shutdown (SD_RECV)).  With Winsock/TCP stack rewrite in longhorn, there may
                // be other differences as well."

                Socket socket = asyncResult.AsyncObject as Socket;
                if (socket == null)
                {
                    socketError = SocketError.NotSocket;
                }
                else if (socket.CleanedUp)
                {
                    socketError = SocketError.OperationAborted;
                }
                else
                {
                    try
                    {
                        // The async IO completed with a failure.
                        // Here we need to call WSAGetOverlappedResult() just so Marshal.GetLastWin32Error() will return the correct error.
                        SocketFlags ignore;
                        bool        success = Interop.Winsock.WSAGetOverlappedResult(
                            socket.SafeHandle,
                            asyncResult.NativeOverlapped,
                            out numBytes,
                            false,
                            out ignore);
                        if (!success)
                        {
                            socketError = (SocketError)Marshal.GetLastWin32Error();
                            if (socketError == 0)
                            {
                                NetEventSource.Fail(asyncResult, $"socketError:0 numBytes:{numBytes}");
                            }
                        }
                        if (success)
                        {
                            NetEventSource.Fail(asyncResult, $"Unexpectedly succeeded. errorCode:{errorCode} numBytes:{numBytes}");
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // CleanedUp check above does not always work since this code is subject to race conditions
                        socketError = SocketError.OperationAborted;
                    }
                }
            }
            asyncResult.ErrorCode = (int)socketError;
            returnObject          = asyncResult.PostCompletion((int)numBytes);
            asyncResult.ReleaseUnmanagedStructures();
            asyncResult.InvokeCallback(returnObject);
#if DEBUG
        }
#endif
        }
        private async ValueTask <int> ReadAsyncInternal <TIOAdapter>(TIOAdapter adapter, Memory <byte> buffer)
            where TIOAdapter : ISslIOAdapter
        {
            if (Interlocked.Exchange(ref _nestedRead, 1) == 1)
            {
                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(SslStream.ReadAsync), "read"));
            }

            try
            {
                while (true)
                {
                    int copyBytes;
                    if (_decryptedBytesCount != 0)
                    {
                        copyBytes = CopyDecryptedData(buffer);

                        return(copyBytes);
                    }

                    copyBytes = await adapter.ReadLockAsync(buffer).ConfigureAwait(false);

                    if (copyBytes > 0)
                    {
                        return(copyBytes);
                    }

                    ResetReadBuffer();
                    int readBytes = await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize).ConfigureAwait(false);

                    if (readBytes == 0)
                    {
                        return(0);
                    }

                    int payloadBytes = GetRemainingFrameSize(_internalBuffer, _internalOffset, readBytes);
                    if (payloadBytes < 0)
                    {
                        throw new IOException(SR.net_frame_read_size);
                    }

                    readBytes = await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize + payloadBytes).ConfigureAwait(false);

                    Debug.Assert(readBytes >= 0);
                    if (readBytes == 0)
                    {
                        throw new IOException(SR.net_io_eof);
                    }

                    // At this point, readBytes contains the size of the header plus body.
                    // Set _decrytpedBytesOffset/Count to the current frame we have (including header)
                    // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller.
                    _decryptedBytesOffset = _internalOffset;
                    _decryptedBytesCount  = readBytes;
                    SecurityStatusPal status = DecryptData();

                    // Treat the bytes we just decrypted as consumed
                    // Note, we won't do another buffer read until the decrypted bytes are processed
                    ConsumeBufferedBytes(readBytes);

                    if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
                    {
                        byte[] extraBuffer = null;
                        if (_decryptedBytesCount != 0)
                        {
                            extraBuffer = new byte[_decryptedBytesCount];
                            Buffer.BlockCopy(_internalBuffer, _decryptedBytesOffset, extraBuffer, 0, _decryptedBytesCount);

                            _decryptedBytesCount = 0;
                        }

                        ProtocolToken message = new ProtocolToken(null, status);
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(null, $"***Processing an error Status = {message.Status}");
                        }

                        if (message.Renegotiate)
                        {
                            if (!_sslAuthenticationOptions.AllowRenegotiation)
                            {
                                if (NetEventSource.IsEnabled)
                                {
                                    NetEventSource.Fail(this, "Renegotiation was requested but it is disallowed");
                                }
                                throw new IOException(SR.net_ssl_io_renego);
                            }

                            await ReplyOnReAuthenticationAsync(adapter, extraBuffer).ConfigureAwait(false);

                            // Loop on read.
                            continue;
                        }

                        if (message.CloseConnection)
                        {
                            return(0);
                        }

                        throw new IOException(SR.net_io_decrypt, message.GetException());
                    }
                }
            }
            catch (Exception e)
            {
                if (e is IOException || (e is OperationCanceledException && adapter.CancellationToken.IsCancellationRequested))
                {
                    throw;
                }

                throw new IOException(SR.net_io_read, e);
            }
            finally
            {
                _nestedRead = 0;
            }
        }
        // We need at least 5 bytes to determine what we have.
        private Framing DetectFraming(byte[] bytes, int length)
        {
            /* PCTv1.0 Hello starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * PCT1_CLIENT_HELLO  (must be equal)
             * PCT1_CLIENT_VERSION_MSB (if version greater than PCTv1)
             * PCT1_CLIENT_VERSION_LSB (if version greater than PCTv1)
             *
             * ... PCT hello ...
             */

            /* Microsoft Unihello starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_CLIENT_HELLO  (must be equal)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3)
             *
             * ... SSLv2 Compatible Hello ...
             */

            /* SSLv2 CLIENT_HELLO starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_CLIENT_HELLO  (must be equal)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3)
             *
             * ... SSLv2 CLIENT_HELLO ...
             */

            /* SSLv2 SERVER_HELLO starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_SERVER_HELLO  (must be equal)
             * SSL2_SESSION_ID_HIT (ignore)
             * SSL2_CERTIFICATE_TYPE (ignore)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3)
             *
             * ... SSLv2 SERVER_HELLO ...
             */

            /* SSLv3 Type 2 Hello starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_CLIENT_HELLO  (must be equal)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv3)
             *
             * ... SSLv2 Compatible Hello ...
             */

            /* SSLv3 Type 3 Hello starts with
             * 22 (HANDSHAKE MESSAGE)
             * VERSION MSB
             * VERSION LSB
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * HS TYPE (CLIENT_HELLO)
             * 3 bytes HS record length
             * HS Version
             * HS Version
             */

            /* SSLv2 message codes
             * SSL_MT_ERROR                0
             * SSL_MT_CLIENT_HELLO         1
             * SSL_MT_CLIENT_MASTER_KEY    2
             * SSL_MT_CLIENT_FINISHED      3
             * SSL_MT_SERVER_HELLO         4
             * SSL_MT_SERVER_VERIFY        5
             * SSL_MT_SERVER_FINISHED      6
             * SSL_MT_REQUEST_CERTIFICATE  7
             * SSL_MT_CLIENT_CERTIFICATE   8
             */

            int version = -1;

            if ((bytes == null || bytes.Length <= 0))
            {
                NetEventSource.Fail(this, "Header buffer is not allocated.");
            }

            // If the first byte is SSL3 HandShake, then check if we have a SSLv3 Type3 client hello.
            if (bytes[0] == (byte)FrameType.Handshake || bytes[0] == (byte)FrameType.AppData ||
                bytes[0] == (byte)FrameType.Alert)
            {
                if (length < 3)
                {
                    return(Framing.Invalid);
                }

#if TRACE_VERBOSE
                if (bytes[1] != 3 && NetEventSource.IsEnabled)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL protocol is > 3, trying SSL3 framing in retail = {bytes[1]:x}");
                    }
                }
#endif

                version = (bytes[1] << 8) | bytes[2];
                if (version < 0x300 || version >= 0x500)
                {
                    return(Framing.Invalid);
                }

                //
                // This is an SSL3 Framing
                //
                return(Framing.SinceSSL3);
            }

#if TRACE_VERBOSE
            if ((bytes[0] & 0x80) == 0 && NetEventSource.IsEnabled)
            {
                // We have a three-byte header format
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL v <=2 HELLO has no high bit set for 3 bytes header, we are broken, received byte = {bytes[0]:x}");
                }
            }
#endif

            if (length < 3)
            {
                return(Framing.Invalid);
            }

            if (bytes[2] > 8)
            {
                return(Framing.Invalid);
            }

            if (bytes[2] == 0x1)  // SSL_MT_CLIENT_HELLO
            {
                if (length >= 5)
                {
                    version = (bytes[3] << 8) | bytes[4];
                }
            }
            else if (bytes[2] == 0x4) // SSL_MT_SERVER_HELLO
            {
                if (length >= 7)
                {
                    version = (bytes[5] << 8) | bytes[6];
                }
            }

            if (version != -1)
            {
                // If this is the first packet, the client may start with an SSL2 packet
                // but stating that the version is 3.x, so check the full range.
                // For the subsequent packets we assume that an SSL2 packet should have a 2.x version.
                if (_framing == Framing.Unknown)
                {
                    if (version != 0x0002 && (version < 0x200 || version >= 0x500))
                    {
                        return(Framing.Invalid);
                    }
                }
                else
                {
                    if (version != 0x0002)
                    {
                        return(Framing.Invalid);
                    }
                }
            }

            // When server has replied the framing is already fixed depending on the prior client packet
            if (!_context.IsServer || _framing == Framing.Unified)
            {
                return(Framing.BeforeSSL3);
            }

            return(Framing.Unified); // Will use Ssl2 just for this frame.
        }
Example #24
0
        internal static int Encrypt(
            SafeDeleteContext securityContext,
            byte[] buffer,
            int offset,
            int count,
            bool isConfidential,
            bool isNtlm,
            ref byte[] output,
            uint sequenceNumber)
        {
            SecPkgContext_Sizes sizes = default;
            bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes);

            Debug.Assert(success);

            try
            {
                int maxCount = checked (int.MaxValue - 4 - sizes.cbBlockSize - sizes.cbSecurityTrailer);

                if (count > maxCount || count < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.net_io_out_range, maxCount));
                }
            }
            catch (Exception e) when(!ExceptionCheck.IsFatal(e))
            {
                NetEventSource.Fail(null, "Arguments out of range.");
                throw;
            }

            int resultSize = count + sizes.cbSecurityTrailer + sizes.cbBlockSize;

            if (output == null || output.Length < resultSize + 4)
            {
                output = new byte[resultSize + 4];
            }

            // Make a copy of user data for in-place encryption.
            Buffer.BlockCopy(buffer, offset, output, 4 + sizes.cbSecurityTrailer, count);

            // Prepare buffers TOKEN(signature), DATA and Padding.
            ThreeSecurityBuffers buffers = default;
            var securityBuffer           = MemoryMarshal.CreateSpan(ref buffers._item0, 3);

            securityBuffer[0] = new SecurityBuffer(output, 4, sizes.cbSecurityTrailer, SecurityBufferType.SECBUFFER_TOKEN);
            securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.cbSecurityTrailer, count, SecurityBufferType.SECBUFFER_DATA);
            securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.cbSecurityTrailer + count, sizes.cbBlockSize, SecurityBufferType.SECBUFFER_PADDING);

            int errorCode;

            if (isConfidential)
            {
                errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
            }
            else
            {
                if (isNtlm)
                {
                    securityBuffer[1].type |= SecurityBufferType.SECBUFFER_READONLY;
                }

                errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, 0);
            }

            if (errorCode != 0)
            {
                Exception e = new Win32Exception(errorCode);
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(null, e);
                }
                throw e;
            }

            // Compacting the result.
            resultSize = securityBuffer[0].size;
            bool forceCopy = false;

            if (resultSize != sizes.cbSecurityTrailer)
            {
                forceCopy = true;
                Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size);
            }

            resultSize += securityBuffer[1].size;
            if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (count + sizes.cbSecurityTrailer)))
            {
                Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size);
            }

            resultSize += securityBuffer[2].size;
            unchecked
            {
                output[0] = (byte)((resultSize) & 0xFF);
                output[1] = (byte)(((resultSize) >> 8) & 0xFF);
                output[2] = (byte)(((resultSize) >> 16) & 0xFF);
                output[3] = (byte)(((resultSize) >> 24) & 0xFF);
            }

            return(resultSize + 4);
        }
        private async ValueTask <int> ReadAsyncInternal <TIOAdapter>(TIOAdapter adapter, Memory <byte> buffer)
            where TIOAdapter : ISslIOAdapter
        {
            if (Interlocked.Exchange(ref _nestedRead, 1) == 1)
            {
                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(SslStream.ReadAsync), "read"));
            }

            try
            {
                while (true)
                {
                    if (_decryptedBytesCount != 0)
                    {
                        return(CopyDecryptedData(buffer));
                    }

                    int copyBytes = await adapter.ReadLockAsync(buffer).ConfigureAwait(false);

                    if (copyBytes > 0)
                    {
                        return(copyBytes);
                    }

                    ResetReadBuffer();

                    // Read the next frame header.
                    if (_internalBufferCount < SecureChannel.ReadHeaderSize)
                    {
                        // We don't have enough bytes buffered, so issue an initial read to try to get enough.  This is
                        // done in this method both to better consolidate error handling logic (the first read is the special
                        // case that needs to differentiate reading 0 from > 0, and everything else needs to throw if it
                        // doesn't read enough), and to minimize the chances that in the common case the FillBufferAsync
                        // helper needs to yield and allocate a state machine.
                        int readBytes = await adapter.ReadAsync(_internalBuffer.AsMemory(_internalBufferCount)).ConfigureAwait(false);

                        if (readBytes == 0)
                        {
                            return(0);
                        }

                        _internalBufferCount += readBytes;
                        if (_internalBufferCount < SecureChannel.ReadHeaderSize)
                        {
                            await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize).ConfigureAwait(false);
                        }
                    }
                    Debug.Assert(_internalBufferCount >= SecureChannel.ReadHeaderSize);

                    // Parse the frame header to determine the payload size (which includes the header size).
                    int payloadBytes = GetFrameSize(_internalBuffer.AsSpan(_internalOffset));
                    if (payloadBytes < 0)
                    {
                        throw new IOException(SR.net_frame_read_size);
                    }

                    // Read in the rest of the payload if we don't have it.
                    if (_internalBufferCount < payloadBytes)
                    {
                        await FillBufferAsync(adapter, payloadBytes).ConfigureAwait(false);
                    }

                    // Set _decrytpedBytesOffset/Count to the current frame we have (including header)
                    // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller.
                    _decryptedBytesOffset = _internalOffset;
                    _decryptedBytesCount  = payloadBytes;
                    SecurityStatusPal status = DecryptData();

                    // Treat the bytes we just decrypted as consumed
                    // Note, we won't do another buffer read until the decrypted bytes are processed
                    ConsumeBufferedBytes(payloadBytes);

                    if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
                    {
                        byte[] extraBuffer = null;
                        if (_decryptedBytesCount != 0)
                        {
                            extraBuffer = new byte[_decryptedBytesCount];
                            Buffer.BlockCopy(_internalBuffer, _decryptedBytesOffset, extraBuffer, 0, _decryptedBytesCount);

                            _decryptedBytesCount = 0;
                        }

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(null, $"***Processing an error Status = {status}");
                        }

                        if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
                        {
                            if (!_sslAuthenticationOptions.AllowRenegotiation)
                            {
                                if (NetEventSource.IsEnabled)
                                {
                                    NetEventSource.Fail(this, "Renegotiation was requested but it is disallowed");
                                }
                                throw new IOException(SR.net_ssl_io_renego);
                            }

                            await ReplyOnReAuthenticationAsync(adapter, extraBuffer).ConfigureAwait(false);

                            // Loop on read.
                            continue;
                        }

                        if (status.ErrorCode == SecurityStatusPalErrorCode.ContextExpired)
                        {
                            return(0);
                        }

                        throw new IOException(SR.net_io_decrypt, SslStreamPal.GetException(status));
                    }
                }
            }
            catch (Exception e)
            {
                if (e is IOException || (e is OperationCanceledException && adapter.CancellationToken.IsCancellationRequested))
                {
                    throw;
                }

                throw new IOException(SR.net_io_read, e);
            }
            finally
            {
                _nestedRead = 0;
            }
        }
Example #26
0
        // Called when the DNS query completes (either synchronously or asynchronously).  Checks for failure and
        // starts the first connection attempt if it succeeded.  Returns true if the operation will be asynchronous,
        // false if it has failed synchronously.
        private bool DoDnsCallback(IAsyncResult result, bool sync)
        {
            Exception exception = null;

            lock (_lockObject)
            {
                // If the connection attempt was canceled during the dns query, the user's callback has already been
                // called asynchronously and we simply need to return.
                if (_state == State.Canceled)
                {
                    return(true);
                }

                if (_state != State.DnsQuery)
                {
                    NetEventSource.Fail(this, "MultipleConnectAsync.DoDnsCallback(): Unexpected object state");
                }

                try
                {
                    _addressList = Dns.EndGetHostAddresses(result);
                    if (_addressList == null)
                    {
                        NetEventSource.Fail(this, "MultipleConnectAsync.DoDnsCallback(): EndGetHostAddresses returned null!");
                    }
                }
                catch (Exception e)
                {
                    _state    = State.Completed;
                    exception = e;
                }

                // If the dns query succeeded, try to connect to the first address
                if (exception == null)
                {
                    _state = State.ConnectAttempt;

                    _internalArgs            = new SocketAsyncEventArgs();
                    _internalArgs.Completed += InternalConnectCallback;
                    _internalArgs.CopyBufferFrom(_userArgs);

                    exception = AttemptConnection();

                    if (exception != null)
                    {
                        // There was a synchronous error while connecting
                        _state = State.Completed;
                    }
                }
            }

            // Call this outside of the lock because it might call the user's callback.
            if (exception != null)
            {
                return(Fail(sync, exception));
            }
            else
            {
                return(true);
            }
        }
Example #27
0
 internal InternalException()
 {
     NetEventSource.Fail(this, "InternalException thrown.");
 }
Example #28
0
        //
        // The app is calling this method after starting an SSL handshake.
        //
        // ATTN: The thumbPrint must be from inspected and possibly cloned user Cert object or we get a security hole in SslCredKey ctor.
        //
        internal static void CacheCredential(SafeFreeCredentials creds, byte[]?thumbPrint, SslProtocols sslProtocols, bool isServer, EncryptionPolicy encryptionPolicy)
        {
            if (creds == null)
            {
                NetEventSource.Fail(null, "creds == null");
            }

            if (creds !.IsInvalid)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(null, $"Refused to cache an Invalid Handle {creds}, Current Cache Count = {s_cachedCreds.Count}");
                }
                return;
            }

            var key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy);

            SafeCredentialReference?cached;

            if (!s_cachedCreds.TryGetValue(key, out cached) || cached.IsClosed || cached.Target.IsInvalid)
            {
                lock (s_cachedCreds)
                {
                    if (!s_cachedCreds.TryGetValue(key, out cached) || cached.IsClosed)
                    {
                        cached = SafeCredentialReference.CreateReference(creds);

                        if (cached == null)
                        {
                            // Means the handle got closed in between, return it back and let caller deal with the issue.
                            return;
                        }

                        s_cachedCreds[key] = cached;
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(null, $"Caching New Handle = {creds}, Current Cache Count = {s_cachedCreds.Count}");
                        }

                        //
                        // A simplest way of preventing infinite cache grows.
                        //
                        // Security relief (DoS):
                        //     A number of active creds is never greater than a number of _outstanding_
                        //     security sessions, i.e. SSL connections.
                        //     So we will try to shrink cache to the number of active creds once in a while.
                        //
                        //    We won't shrink cache in the case when NO new handles are coming to it.
                        //
                        if ((s_cachedCreds.Count % CheckExpiredModulo) == 0)
                        {
                            KeyValuePair <SslCredKey, SafeCredentialReference>[] toRemoveAttempt = s_cachedCreds.ToArray();

                            for (int i = 0; i < toRemoveAttempt.Length; ++i)
                            {
                                cached = toRemoveAttempt[i].Value;

                                if (cached != null)
                                {
                                    creds = cached.Target;
                                    cached.Dispose();

                                    if (!creds.IsClosed && !creds.IsInvalid && (cached = SafeCredentialReference.CreateReference(creds)) != null)
                                    {
                                        s_cachedCreds[toRemoveAttempt[i].Key] = cached;
                                    }
                                    else
                                    {
                                        s_cachedCreds.TryRemove(toRemoveAttempt[i].Key, out cached);
                                    }
                                }
                            }
                            if (NetEventSource.Log.IsEnabled())
                            {
                                NetEventSource.Info(null, $"Scavenged cache, New Cache Count = {s_cachedCreds.Count}");
                            }
                        }
                    }
                    else if (NetEventSource.Log.IsEnabled())
                    {
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(null, $"CacheCredential() (locked retry) Found already cached Handle = {cached.Target}");
                        }
                    }
                }
            }
            else if (NetEventSource.Log.IsEnabled())
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(null, $"CacheCredential() Ignoring incoming handle = {creds} since found already cached Handle = {cached.Target}");
                }
            }
        }
Example #29
0
        private static unsafe void CompletionPortCallback(uint errorCode, uint numBytes, NativeOverlapped *nativeOverlapped)
        {
            Debug.Assert(OperatingSystem.IsWindows());
            BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped) !;

            if (asyncResult.InternalPeekCompleted)
            {
                NetEventSource.Fail(null, $"asyncResult.IsCompleted: {asyncResult}");
            }
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(null, $"errorCode:{errorCode} numBytes:{numBytes} nativeOverlapped:{(IntPtr)nativeOverlapped}");
            }

            // Complete the IO and invoke the user's callback.
            SocketError socketError = (SocketError)errorCode;

            if (socketError != SocketError.Success && socketError != SocketError.OperationAborted)
            {
                // There are cases where passed errorCode does not reflect the details of the underlined socket error.
                // "So as of today, the key is the difference between WSAECONNRESET and ConnectionAborted,
                //  .e.g remote party or network causing the connection reset or something on the local host (e.g. closesocket
                // or receiving data after shutdown (SD_RECV)).  With Winsock/TCP stack rewrite in longhorn, there may
                // be other differences as well."

                Socket?socket = asyncResult.AsyncObject as Socket;
                if (socket == null)
                {
                    socketError = SocketError.NotSocket;
                }
                else if (socket.Disposed)
                {
                    socketError = SocketError.OperationAborted;
                }
                else
                {
                    try
                    {
                        // The async IO completed with a failure.
                        // Here we need to call WSAGetOverlappedResult() just so GetLastSocketError() will return the correct error.
                        SocketFlags ignore;
                        bool        success = Interop.Winsock.WSAGetOverlappedResult(
                            socket.SafeHandle,
                            nativeOverlapped,
                            out numBytes,
                            false,
                            out ignore);
                        if (!success)
                        {
                            socketError = SocketPal.GetLastSocketError();
                        }
                        if (success)
                        {
                            NetEventSource.Fail(asyncResult, $"Unexpectedly succeeded. errorCode:{errorCode} numBytes:{numBytes}");
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // Disposed check above does not always work since this code is subject to race conditions
                        socketError = SocketError.OperationAborted;
                    }
                }
            }

            // Set results and invoke callback
            asyncResult.CompletionCallback((int)numBytes, socketError);
        }