public void SendAsync(MailMessage message, object?userToken) { ObjectDisposedException.ThrowIf(_disposed, this); try { if (InCall) { throw new InvalidOperationException(SR.net_inasync); } if (message == null) { throw new ArgumentNullException(nameof(message)); } if (DeliveryMethod == SmtpDeliveryMethod.Network) { CheckHostAndPort(); } _recipients = new MailAddressCollection(); if (message.From == null) { throw new InvalidOperationException(SR.SmtpFromRequired); } if (message.To != null) { foreach (MailAddress address in message.To) { _recipients.Add(address); } } if (message.Bcc != null) { foreach (MailAddress address in message.Bcc) { _recipients.Add(address); } } if (message.CC != null) { foreach (MailAddress address in message.CC) { _recipients.Add(address); } } if (_recipients.Count == 0) { throw new InvalidOperationException(SR.SmtpRecipientRequired); } InCall = true; _cancelled = false; _message = message; string?pickupDirectory = PickupDirectoryLocation; CredentialCache?cache; // Skip token capturing if no credentials are used or they don't include a default one. // Also do capture the token if ICredential is not of CredentialCache type so we don't know what the exact credential response will be. _transport.IdentityRequired = Credentials != null && (ReferenceEquals(Credentials, CredentialCache.DefaultNetworkCredentials) || (cache = Credentials as CredentialCache) == null || IsSystemNetworkCredentialInCache(cache)); _asyncOp = AsyncOperationManager.CreateOperation(userToken); switch (DeliveryMethod) { case SmtpDeliveryMethod.PickupDirectoryFromIis: throw new NotSupportedException(SR.SmtpGetIisPickupDirectoryNotSupported); case SmtpDeliveryMethod.SpecifiedPickupDirectory: { if (EnableSsl) { throw new SmtpException(SR.SmtpPickupDirectoryDoesnotSupportSsl); } _writer = GetFileMailWriter(pickupDirectory); bool allowUnicode = IsUnicodeSupported(); ValidateUnicodeRequirement(message, _recipients, allowUnicode); message.Send(_writer, true, allowUnicode); if (_writer != null) { _writer.Close(); } AsyncCompletedEventArgs eventArgs = new AsyncCompletedEventArgs(null, false, _asyncOp.UserSuppliedState); InCall = false; _asyncOp.PostOperationCompleted(_onSendCompletedDelegate, eventArgs); break; } case SmtpDeliveryMethod.Network: default: _operationCompletedResult = new ContextAwareResult(_transport.IdentityRequired, true, null, this, s_contextSafeCompleteCallback); lock (_operationCompletedResult.StartPostingAsyncOp()) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, $"Calling BeginConnect. Transport: {_transport}"); } _transport.BeginGetConnection(_operationCompletedResult, ConnectCallback, _operationCompletedResult, Host !, Port); _operationCompletedResult.FinishPostingAsyncOp(); } break; } } catch (Exception e) { InCall = false; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(this, e); } if (e is SmtpFailedRecipientException && !((SmtpFailedRecipientException)e).fatal) { throw; } Abort(); if (e is SecurityException || e is AuthenticationException || e is SmtpException) { throw; } throw new SmtpException(SR.SmtpSendMailFailure, e); } }
protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpResponseMessage response; uint redirectCount = 0; while (true) { // Just as with WinHttpHandler and CurlHandler, for security reasons, we drop the server credential if it is // anything other than a CredentialCache on redirection. We allow credentials in a CredentialCache since they // are specifically tied to URIs. ICredentials currentCredential = redirectCount > 0 ? _credentials as CredentialCache : _credentials; if (currentCredential != null && _preAuthenticate) { AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential); } response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); if (currentCredential != null && response.StatusCode == HttpStatusCode.Unauthorized) { AuthenticationHeaderValue selectedAuth = GetSupportedAuthScheme(response.Headers.WwwAuthenticate); if (selectedAuth != null) { switch (selectedAuth.Scheme) { case AuthenticationHelper.Digest: // Update digest response with new parameter from WWWAuthenticate var digestResponse = new AuthenticationHelper.DigestResponse(selectedAuth.Parameter); if (await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); // Retry in case of nonce timeout in server. if (response.StatusCode == HttpStatusCode.Unauthorized) { foreach (AuthenticationHeaderValue ahv in response.Headers.WwwAuthenticate) { if (ahv.Scheme == AuthenticationHelper.Digest) { digestResponse = new AuthenticationHelper.DigestResponse(ahv.Parameter); if (AuthenticationHelper.IsServerNonceStale(digestResponse) && await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; } } } } break; case AuthenticationHelper.Basic: if (_preAuthenticate) { // We already tried these credentials via preauthentication, so no need to try again break; } if (AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; } } } if (!RequestNeedsRedirect(response)) { break; } // Clear the authorization header, if the request requires redirect. request.Headers.Authorization = null; Uri location = response.Headers.Location; if (location == null) { // No location header. Nothing to redirect to. break; } if (!location.IsAbsoluteUri) { location = new Uri(request.RequestUri, location); } // Disallow automatic redirection from secure to non-secure schemes bool allowed = (HttpUtilities.IsSupportedNonSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedScheme(location.Scheme)) || (HttpUtilities.IsSupportedSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedSecureScheme(location.Scheme)); if (!allowed) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Insecure https to http redirect from {request.RequestUri} to {location} blocked."); } break; } redirectCount++; if (redirectCount > _maxAutomaticRedirections) { throw new HttpRequestException(SR.net_http_max_redirects); } // Set up for the automatic redirect request.RequestUri = location; if (RequestRequiresForceGet(response.StatusCode, request.Method)) { request.Method = HttpMethod.Get; request.Content = null; } // Do the redirect. response.Dispose(); } return(response); }
private void Initialize() { _transport = new SmtpTransport(this); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Associate(this, _transport); } _onSendCompletedDelegate = new SendOrPostCallback(SendCompletedWaitCallback); if (_host != null && _host.Length != 0) { _host = _host.Trim(); } if (_port == 0) { _port = DefaultPort; } if (_targetName == null) { _targetName = "SMTPSVC/" + _host; } if (_clientDomain == null) { // We use the local host name as the default client domain // for the client's EHLO or HELO message. This limits the // information about the host that we share. Additionally, the // FQDN is not available to us or useful to the server (internal // machine connecting to public server). // SMTP RFC's require ASCII only host names in the HELO/EHLO message. string clientDomainRaw = IPGlobalProperties.GetIPGlobalProperties().HostName; IdnMapping mapping = new IdnMapping(); try { clientDomainRaw = mapping.GetAscii(clientDomainRaw); } catch (ArgumentException) { } // For some inputs GetAscii may fail (bad Unicode, etc). If that happens // we must strip out any non-ASCII characters. // If we end up with no characters left, we use the string "LocalHost". This // matches Outlook behavior. StringBuilder sb = new StringBuilder(); char ch; for (int i = 0; i < clientDomainRaw.Length; i++) { ch = clientDomainRaw[i]; if ((ushort)ch <= 0x7F) { sb.Append(ch); } } if (sb.Length > 0) { _clientDomain = sb.ToString(); } else { _clientDomain = "LocalHost"; } } }
public void Send(MailMessage message) { ObjectDisposedException.ThrowIf(_disposed, this); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, $"DeliveryMethod={DeliveryMethod}"); NetEventSource.Associate(this, message); } SmtpFailedRecipientException?recipientException = null; if (InCall) { throw new InvalidOperationException(SR.net_inasync); } if (message == null) { throw new ArgumentNullException(nameof(message)); } if (DeliveryMethod == SmtpDeliveryMethod.Network) { CheckHostAndPort(); } MailAddressCollection recipients = new MailAddressCollection(); if (message.From == null) { throw new InvalidOperationException(SR.SmtpFromRequired); } if (message.To != null) { foreach (MailAddress address in message.To) { recipients.Add(address); } } if (message.Bcc != null) { foreach (MailAddress address in message.Bcc) { recipients.Add(address); } } if (message.CC != null) { foreach (MailAddress address in message.CC) { recipients.Add(address); } } if (recipients.Count == 0) { throw new InvalidOperationException(SR.SmtpRecipientRequired); } _transport.IdentityRequired = false; // everything completes on the same thread. try { InCall = true; _timedOut = false; _timer = new Timer(new TimerCallback(TimeOutCallback), null, Timeout, Timeout); bool allowUnicode = false; string?pickupDirectory = PickupDirectoryLocation; MailWriter writer; switch (DeliveryMethod) { case SmtpDeliveryMethod.PickupDirectoryFromIis: throw new NotSupportedException(SR.SmtpGetIisPickupDirectoryNotSupported); case SmtpDeliveryMethod.SpecifiedPickupDirectory: if (EnableSsl) { throw new SmtpException(SR.SmtpPickupDirectoryDoesnotSupportSsl); } allowUnicode = IsUnicodeSupported(); // Determend by the DeliveryFormat paramiter ValidateUnicodeRequirement(message, recipients, allowUnicode); writer = GetFileMailWriter(pickupDirectory); break; case SmtpDeliveryMethod.Network: default: GetConnection(); // Detected durring GetConnection(), restrictable using the DeliveryFormat paramiter allowUnicode = IsUnicodeSupported(); ValidateUnicodeRequirement(message, recipients, allowUnicode); writer = _transport.SendMail(message.Sender ?? message.From, recipients, message.BuildDeliveryStatusNotificationString(), allowUnicode, out recipientException); break; } _message = message; message.Send(writer, DeliveryMethod != SmtpDeliveryMethod.Network, allowUnicode); writer.Close(); //throw if we couldn't send to any of the recipients if (DeliveryMethod == SmtpDeliveryMethod.Network && recipientException != null) { throw recipientException; } } catch (Exception e) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(this, e); } if (e is SmtpFailedRecipientException && !((SmtpFailedRecipientException)e).fatal) { throw; } Abort(); if (_timedOut) { throw new SmtpException(SR.net_timeout); } if (e is SecurityException || e is AuthenticationException || e is SmtpException) { throw; } throw new SmtpException(SR.SmtpSendMailFailure, e); } finally { InCall = false; if (_timer != null) { _timer.Dispose(); } } }
public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection?additionalCertificates, bool offline = false) { if (!target.HasPrivateKey) { throw new NotSupportedException(SR.net_ssl_io_no_server_cert); } X509Certificate2[] intermediates = Array.Empty <X509Certificate2>(); using (X509Chain chain = new X509Chain()) { if (additionalCertificates != null) { foreach (X509Certificate cert in additionalCertificates) { chain.ChainPolicy.ExtraStore.Add(cert); } } chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.DisableCertificateDownloads = offline; bool chainStatus = chain.Build(target); if (!chainStatus && NetEventSource.IsEnabled) { NetEventSource.Error(null, $"Failed to build chain for {target.Subject}"); } int count = chain.ChainElements.Count - (TrimRootCertificate ? 1 : 2); foreach (X509ChainStatus status in chain.ChainStatus) { if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain)) { // The last cert isn't a root cert count++; break; } } // Count can be zero for a self-signed certificate, or a cert issued directly from a root. if (count > 0 && chain.ChainElements.Count > 1) { intermediates = new X509Certificate2[count]; for (int i = 0; i < count; i++) { intermediates[i] = chain.ChainElements[i + 1].Certificate; } } // Dispose the copy of the target cert. chain.ChainElements[0].Certificate.Dispose(); // Dispose the last cert, if we didn't include it. for (int i = count + 1; i < chain.ChainElements.Count; i++) { chain.ChainElements[i].Certificate.Dispose(); } } return(new SslStreamCertificateContext(target, intermediates)); }
// 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. }
// Connects the Client to the specified port on the specified host. public void Connect(string hostname, int port) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this, hostname); } ThrowIfDisposed(); if (hostname == null) { throw new ArgumentNullException(nameof(hostname)); } if (!TcpValidationHelpers.ValidatePortNumber(port)) { throw new ArgumentOutOfRangeException(nameof(port)); } // Check for already connected and throw here. This check // is not required in the other connect methods as they // will throw from WinSock. Here, the situation is more // complex since we have to resolve a hostname so it's // easier to simply block the request up front. if (_active) { throw new SocketException((int)SocketError.IsConnected); } // IPv6: We need to process each of the addresses returned from // DNS when trying to connect. Use of AddressList[0] is // bad form. IPAddress[] addresses = Dns.GetHostAddresses(hostname); ExceptionDispatchInfo lastex = null; try { foreach (IPAddress address in addresses) { try { if (_clientSocket == null) { // We came via the <hostname,port> constructor. Set the address family appropriately, // create the socket and try to connect. Debug.Assert(address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6); if ((address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4) || Socket.OSSupportsIPv6) { var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // Use of Interlocked.Exchanged ensures _clientSocket is written before Disposed is read. Interlocked.Exchange(ref _clientSocket, socket); if (Disposed) { // Dispose the socket so it throws ObjectDisposedException when we Connect. socket.Dispose(); } try { socket.Connect(address, port); } catch { _clientSocket = null; throw; } } _family = address.AddressFamily; _active = true; break; } else if (address.AddressFamily == _family || _family == AddressFamily.Unknown) { // Only use addresses with a matching family Connect(new IPEndPoint(address, port)); _active = true; break; } } catch (Exception ex) when(!(ex is OutOfMemoryException)) { lastex = ExceptionDispatchInfo.Capture(ex); } } } finally { if (!_active) { // The connect failed - rethrow the last error we had lastex?.Throw(); throw new SocketException((int)SocketError.NotConnected); } } if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } }
// return value indicates sync vs async completion // false: sync completion // true: async completion or with error private unsafe bool WriteAsyncFast(HttpListenerAsyncEventArgs eventArgs) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); } Interop.HttpApi.HTTP_FLAGS flags = Interop.HttpApi.HTTP_FLAGS.NONE; eventArgs.StartOperationCommon(this, _outputStream.InternalHttpContext.RequestQueueBoundHandle); eventArgs.StartOperationSend(); uint statusCode; bool completedAsynchronouslyOrWithError = false; try { if (_outputStream.Closed || (eventArgs.Buffer != null && eventArgs.Count == 0)) { eventArgs.FinishOperationSuccess(eventArgs.Count, true); return(false); } if (eventArgs.ShouldCloseOutput) { flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } else { flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; // When using HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA HTTP.SYS will copy the payload to // kernel memory (Non-Paged Pool). Http.Sys will buffer up to // Math.Min(16 MB, current TCP window size) flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA; } uint bytesSent; statusCode = Interop.HttpApi.HttpSendResponseEntityBody( _outputStream.InternalHttpContext.RequestQueueHandle, _outputStream.InternalHttpContext.RequestId, (uint)flags, eventArgs.EntityChunkCount, (Interop.HttpApi.HTTP_DATA_CHUNK *)eventArgs.EntityChunks, &bytesSent, SafeLocalAllocHandle.Zero, 0, eventArgs.NativeOverlapped, null); if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING) { throw new HttpListenerException((int)statusCode); } else if (statusCode == Interop.HttpApi.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion. eventArgs.FinishOperationSuccess((int)bytesSent, true); completedAsynchronouslyOrWithError = false; } else { completedAsynchronouslyOrWithError = true; } } catch (Exception e) { _writeEventArgs.FinishOperationFailure(e, true); _outputStream.SetClosedFlag(); _outputStream.InternalHttpContext.Abort(); completedAsynchronouslyOrWithError = true; } finally { if (NetEventSource.IsEnabled) { NetEventSource.Exit(this, completedAsynchronouslyOrWithError); } } return(completedAsynchronouslyOrWithError); }
public static async Task <string> GetDigestTokenForCredential(NetworkCredential credential, HttpRequestMessage request, DigestResponse digestResponse) { StringBuilder sb = StringBuilderCache.Acquire(); // It is mandatory for servers to implement sha-256 per RFC 7616 // Keep MD5 for backward compatibility. string algorithm; bool isAlgorithmSpecified = digestResponse.Parameters.TryGetValue(Algorithm, out algorithm); if (isAlgorithmSpecified) { if (!algorithm.Equals(Sha256, StringComparison.OrdinalIgnoreCase) && !algorithm.Equals(Md5, StringComparison.OrdinalIgnoreCase) && !algorithm.Equals(Sha256Sess, StringComparison.OrdinalIgnoreCase) && !algorithm.Equals(MD5Sess, StringComparison.OrdinalIgnoreCase)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(digestResponse, "Algorithm not supported: {algorithm}"); } return(null); } } else { algorithm = Md5; } // Check if nonce is there in challenge string nonce; if (!digestResponse.Parameters.TryGetValue(Nonce, out nonce)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(digestResponse, "Nonce missing"); } return(null); } // opaque token may or may not exist string opaque; digestResponse.Parameters.TryGetValue(Opaque, out opaque); string realm; if (!digestResponse.Parameters.TryGetValue(Realm, out realm)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(digestResponse, "Realm missing"); } return(null); } // Add username string userhash; if (digestResponse.Parameters.TryGetValue(UserHash, out userhash) && userhash == "true") { sb.AppendKeyValue(Username, ComputeHash(credential.UserName + ":" + realm, algorithm)); sb.AppendKeyValue(UserHash, userhash, includeQuotes: false); } else { if (HeaderUtilities.ContainsNonAscii(credential.UserName)) { string usernameStar = HeaderUtilities.Encode5987(credential.UserName); sb.AppendKeyValue(UsernameStar, usernameStar, includeQuotes: false); } else { sb.AppendKeyValue(Username, credential.UserName); } } // Add realm if (realm != string.Empty) { sb.AppendKeyValue(Realm, realm); } // Add nonce sb.AppendKeyValue(Nonce, nonce); // Add uri sb.AppendKeyValue(Uri, request.RequestUri.PathAndQuery); // Set qop, default is auth string qop = Auth; bool isQopSpecified = digestResponse.Parameters.ContainsKey(Qop); if (isQopSpecified) { // Check if auth-int present in qop string int index1 = digestResponse.Parameters[Qop].IndexOf(AuthInt, StringComparison.Ordinal); if (index1 != -1) { // Get index of auth if present in qop string int index2 = digestResponse.Parameters[Qop].IndexOf(Auth, StringComparison.Ordinal); // If index2 < index1, auth option is available // If index2 == index1, check if auth option available later in string after auth-int. if (index2 == index1) { index2 = digestResponse.Parameters[Qop].IndexOf(Auth, index1 + AuthInt.Length, StringComparison.Ordinal); if (index2 == -1) { qop = AuthInt; } } } } // Set cnonce string cnonce = GetRandomAlphaNumericString(); // Calculate response string a1 = credential.UserName + ":" + realm + ":" + credential.Password; if (algorithm.EndsWith("sess", StringComparison.OrdinalIgnoreCase)) { a1 = ComputeHash(a1, algorithm) + ":" + nonce + ":" + cnonce; } string a2 = request.Method.Method + ":" + request.RequestUri.PathAndQuery; if (qop == AuthInt) { string content = request.Content == null ? string.Empty : await request.Content.ReadAsStringAsync().ConfigureAwait(false); a2 = a2 + ":" + ComputeHash(content, algorithm); } string response; if (isQopSpecified) { response = ComputeHash(ComputeHash(a1, algorithm) + ":" + nonce + ":" + DigestResponse.NonceCount + ":" + cnonce + ":" + qop + ":" + ComputeHash(a2, algorithm), algorithm); } else { response = ComputeHash(ComputeHash(a1, algorithm) + ":" + nonce + ":" + ComputeHash(a2, algorithm), algorithm); } // Add response sb.AppendKeyValue(Response, response, includeComma: opaque != null || isAlgorithmSpecified || isQopSpecified); // Add opaque if (opaque != null) { sb.AppendKeyValue(Opaque, opaque, includeComma: isAlgorithmSpecified || isQopSpecified); } if (isAlgorithmSpecified) { // Add algorithm sb.AppendKeyValue(Algorithm, algorithm, includeQuotes: false, includeComma: isQopSpecified); } if (isQopSpecified) { // Add qop sb.AppendKeyValue(Qop, qop, includeQuotes: false); // Add nc sb.AppendKeyValue(NC, DigestResponse.NonceCount, includeQuotes: false); // Add cnonce sb.AppendKeyValue(CNonce, cnonce, includeComma: false); } return(StringBuilderCache.GetStringAndRelease(sb)); }
private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) { HttpResponseMessage response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false); if (!isProxyAuth && connection.Kind == HttpConnectionKind.Proxy && !ProxySupportsConnectionAuth(response)) { // Proxy didn't indicate that it supports connection-based auth, so we can't proceed. if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { bool isNewConnection = false; bool needDrain = true; try { if (response.Headers.ConnectionClose.GetValueOrDefault()) { // Server is closing the connection and asking us to authenticate on a new connection. // First, detach the current connection from the pool. This means it will no longer count against the connection limit. // Instead, it will be replaced by the new connection below. connection.DetachFromPool(); connection = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); connection !.Acquire(); isNewConnection = true; needDrain = false; } if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Uri: {authUri.AbsoluteUri}"); } // Calculate SPN (Service Principal Name) using the host name of the request. // Use the request's 'Host' header if available. Otherwise, use the request uri. // Ignore the 'Host' header if this is proxy authentication since we need to use // the host name of the proxy itself for SPN calculation. string hostName; if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null) { // Use the host name without any normalization. hostName = request.Headers.Host; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}"); } } else { // Need to use FQDN normalized host so that CNAME's are traversed. // Use DNS to do the forward lookup to an A (host) record. // But skip DNS lookup on IP literals. Otherwise, we would end up // doing an unintended reverse DNS lookup. UriHostNameType hnt = authUri.HostNameType; if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4) { hostName = authUri.IdnHost; } else { IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost, cancellationToken).ConfigureAwait(false); hostName = result.HostName; } if (!isProxyAuth && !authUri.IsDefaultPort && UsePortInSpn) { hostName = string.Create(null, stackalloc char[128], $"{hostName}:{authUri.Port}"); } } string spn = "HTTP/" + hostName; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}"); } ChannelBinding? channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint); NTAuthentication authContext = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity, channelBinding); string? challengeData = challenge.ChallengeData; try { while (true) { string?challengeResponse = authContext.GetOutgoingBlob(challengeData); if (challengeResponse == null) { // Response indicated denial even after login, so stop processing and return current response. break; } if (needDrain) { await connection.DrainResponseAsync(response !, cancellationToken).ConfigureAwait(false); } SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth); response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false); if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData)) { break; } needDrain = true; } } finally { authContext.CloseContext(); } } finally { if (isNewConnection) { connection !.Release(); } } } } return(response !); }
public bool GetProxyForUrl( SafeWinHttpHandle?sessionHandle, Uri uri, out Interop.WinHttp.WINHTTP_PROXY_INFO proxyInfo) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; proxyInfo.Proxy = IntPtr.Zero; proxyInfo.ProxyBypass = IntPtr.Zero; if (!_useProxy) { return(false); } bool useProxy = false; Interop.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions; autoProxyOptions.AutoConfigUrl = AutoConfigUrl; autoProxyOptions.AutoDetectFlags = AutoDetect ? (Interop.WinHttp.WINHTTP_AUTO_DETECT_TYPE_DHCP | Interop.WinHttp.WINHTTP_AUTO_DETECT_TYPE_DNS_A) : 0; autoProxyOptions.AutoLoginIfChallenged = false; autoProxyOptions.Flags = (AutoDetect ? Interop.WinHttp.WINHTTP_AUTOPROXY_AUTO_DETECT : 0) | (!string.IsNullOrEmpty(AutoConfigUrl) ? Interop.WinHttp.WINHTTP_AUTOPROXY_CONFIG_URL : 0); autoProxyOptions.Reserved1 = IntPtr.Zero; autoProxyOptions.Reserved2 = 0; // AutoProxy Cache. // https://docs.microsoft.com/en-us/windows/desktop/WinHttp/autoproxy-cache // If the out-of-process service is active when WinHttpGetProxyForUrl is called, the cached autoproxy // URL and script are available to the whole computer. However, if the out-of-process service is used, // and the fAutoLogonIfChallenged flag in the pAutoProxyOptions structure is true, then the autoproxy // URL and script are not cached. Therefore, calling WinHttpGetProxyForUrl with the fAutoLogonIfChallenged // member set to TRUE results in additional overhead operations that may affect performance. // The following steps can be used to improve performance: // 1. Call WinHttpGetProxyForUrl with the fAutoLogonIfChallenged parameter set to false. The autoproxy // URL and script are cached for future calls to WinHttpGetProxyForUrl. // 2. If Step 1 fails, with ERROR_WINHTTP_LOGIN_FAILURE, then call WinHttpGetProxyForUrl with the // fAutoLogonIfChallenged member set to TRUE. // // We match behavior of WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY and ignore errors. var repeat = false; do { _autoDetectionFailed = false; if (Interop.WinHttp.WinHttpGetProxyForUrl( sessionHandle !, uri.AbsoluteUri, ref autoProxyOptions, out proxyInfo)) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "Using autoconfig proxy settings"); } useProxy = true; break; } else { var lastError = Marshal.GetLastWin32Error(); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(this, $"error={lastError}"); } if (lastError == Interop.WinHttp.ERROR_WINHTTP_LOGIN_FAILURE) { if (repeat) { // We don't retry more than once. break; } else { repeat = true; autoProxyOptions.AutoLoginIfChallenged = true; } } else { if (lastError == Interop.WinHttp.ERROR_WINHTTP_AUTODETECTION_FAILED) { _autoDetectionFailed = true; _lastTimeAutoDetectionFailed = Environment.TickCount; } break; } } } while (repeat);
private SocketError InnerReleaseHandle() { SocketError errorCode; // If _blockable was set in BlockingRelease, it's safe to block here, which means // we can honor the linger options set on the socket. It also means closesocket() might return WSAEWOULDBLOCK, in which // case we need to do some recovery. if (_blockable) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle}, Following 'blockable' branch"); } errorCode = Interop.Winsock.closesocket(handle); #if DEBUG _closeSocketHandle = handle; _closeSocketResult = errorCode; #endif if (errorCode == SocketError.SocketError) { errorCode = (SocketError)Marshal.GetLastWin32Error(); } if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle}, closesocket()#1:{errorCode}"); } // If it's not WSAEWOULDBLOCK, there's no more recourse - we either succeeded or failed. if (errorCode != SocketError.WouldBlock) { return(errorCode); } // The socket must be non-blocking with a linger timeout set. // We have to set the socket to blocking. int nonBlockCmd = 0; errorCode = Interop.Winsock.ioctlsocket( handle, Interop.Winsock.IoctlSocketConstants.FIONBIO, ref nonBlockCmd); if (errorCode == SocketError.SocketError) { errorCode = (SocketError)Marshal.GetLastWin32Error(); } if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle}, ioctlsocket()#1:{errorCode}"); } // If that succeeded, try again. if (errorCode == SocketError.Success) { errorCode = Interop.Winsock.closesocket(handle); #if DEBUG _closeSocketHandle = handle; _closeSocketResult = errorCode; #endif if (errorCode == SocketError.SocketError) { errorCode = (SocketError)Marshal.GetLastWin32Error(); } if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle}, closesocket#2():{errorCode}"); } // If it's not WSAEWOULDBLOCK, there's no more recourse - we either succeeded or failed. if (errorCode != SocketError.WouldBlock) { return(errorCode); } } // It failed. Fall through to the regular abortive close. } // By default or if CloseAsIs() path failed, set linger timeout to zero to get an abortive close (RST). Interop.Winsock.Linger lingerStruct; lingerStruct.OnOff = 1; lingerStruct.Time = 0; errorCode = Interop.Winsock.setsockopt( handle, SocketOptionLevel.Socket, SocketOptionName.Linger, ref lingerStruct, 4); #if DEBUG _closeSocketLinger = errorCode; #endif if (errorCode == SocketError.SocketError) { errorCode = (SocketError)Marshal.GetLastWin32Error(); } if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle}, setsockopt():{errorCode}"); } if (errorCode != SocketError.Success && errorCode != SocketError.InvalidArgument && errorCode != SocketError.ProtocolOption) { // Too dangerous to try closesocket() - it might block! return(errorCode); } errorCode = Interop.Winsock.closesocket(handle); #if DEBUG _closeSocketHandle = handle; _closeSocketResult = errorCode; #endif if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle}, closesocket#3():{(errorCode == SocketError.SocketError ? (SocketError)Marshal.GetLastWin32Error() : errorCode)}"); } return(errorCode); }
private async ValueTask <int> ReadAsyncInternal <TReadAdapter>(TReadAdapter adapter, Memory <byte> buffer) where TReadAdapter : ISslReadAdapter { 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.LockAsync(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(extraBuffer, adapter.CancellationToken).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; } }
private async Task <int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this, HttpWebSocket.GetTraceMsgForParameters(offset, count, cancellationToken)); } CancellationTokenRegistration cancellationTokenRegistration = default; int bytesRead = 0; try { if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false); } if (!_inOpaqueMode) { bytesRead = await _inputStream.ReadAsync(buffer, offset, count, cancellationToken).SuppressContextFlow <int>(); } else { #if DEBUG // When using fast path only one outstanding read is permitted. By switching into opaque mode // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition) // caller takes responsibility for enforcing this constraint. Debug.Assert(Interlocked.Increment(ref _outstandingOperations._reads) == 1, "Only one outstanding read allowed at any given time."); #endif _readTaskCompletionSource = new TaskCompletionSource <int>(); _readEventArgs.SetBuffer(buffer, offset, count); if (!ReadAsyncFast(_readEventArgs)) { if (_readEventArgs.Exception != null) { throw _readEventArgs.Exception; } bytesRead = _readEventArgs.BytesTransferred; } else { bytesRead = await _readTaskCompletionSource.Task.SuppressContextFlow <int>(); } } } catch (Exception error) { if (s_CanHandleException(error)) { cancellationToken.ThrowIfCancellationRequested(); } throw; } finally { cancellationTokenRegistration.Dispose(); if (NetEventSource.IsEnabled) { NetEventSource.Exit(this, bytesRead); } } return(bytesRead); }
static MsQuicApi() { IntPtr msQuicHandle; if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) && !NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle)) { return; } try { if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) { return; } QUIC_API_TABLE *apiTable = null; delegate * unmanaged[Cdecl] < uint, QUIC_API_TABLE **, int > msQuicOpenVersion = (delegate * unmanaged[Cdecl] < uint, QUIC_API_TABLE **, int >)msQuicOpenVersionAddress; if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &apiTable))) { return; } try { int arraySize = 4; uint *libVersion = stackalloc uint[arraySize]; uint size = (uint)arraySize * sizeof(uint); if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion))) { return; } var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]); if (version < MsQuicVersion) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'"); } return; } // Assume SChannel is being used on windows and query for the actual provider from the library QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL; size = sizeof(QUIC_TLS_PROVIDER); apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider); UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL; if (UsesSChannelBackend) { // Implies windows platform, check TLS1.3 availability if (!IsWindowsVersionSupported()) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}"); } return; } Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true); Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false); } Api = new MsQuicApi(apiTable); IsQuicSupported = true; } finally { if (!IsQuicSupported && NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose)) { // Gracefully close the API table ((delegate * unmanaged[Cdecl] < QUIC_API_TABLE *, void >)msQuicClose)(apiTable); } } } finally { if (!IsQuicSupported) { NativeLibrary.Free(msQuicHandle); } } }
// return value indicates sync vs async completion // false: sync completion // true: async completion or error private unsafe bool ReadAsyncFast(HttpListenerAsyncEventArgs eventArgs) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); } eventArgs.StartOperationCommon(this, _inputStream.InternalHttpContext.RequestQueueBoundHandle); eventArgs.StartOperationReceive(); uint statusCode = 0; bool completedAsynchronouslyOrWithError = false; try { Debug.Assert(eventArgs.Buffer != null, "'BufferList' is not supported for read operations."); if (eventArgs.Count == 0 || _inputStream.Closed) { eventArgs.FinishOperationSuccess(0, true); return(false); } uint dataRead = 0; int offset = eventArgs.Offset; int remainingCount = eventArgs.Count; if (_inputStream.BufferedDataChunksAvailable) { dataRead = _inputStream.GetChunks(eventArgs.Buffer, eventArgs.Offset, eventArgs.Count); if (_inputStream.BufferedDataChunksAvailable && dataRead == eventArgs.Count) { eventArgs.FinishOperationSuccess(eventArgs.Count, true); return(false); } } Debug.Assert(!_inputStream.BufferedDataChunksAvailable, "'m_InputStream.BufferedDataChunksAvailable' MUST BE 'FALSE' at this point."); Debug.Assert(dataRead <= eventArgs.Count, "'dataRead' MUST NOT be bigger than 'eventArgs.Count'."); if (dataRead != 0) { offset += (int)dataRead; remainingCount -= (int)dataRead; //the http.sys team recommends that we limit the size to 128kb if (remainingCount > HttpRequestStream.MaxReadSize) { remainingCount = HttpRequestStream.MaxReadSize; } eventArgs.SetBuffer(eventArgs.Buffer, offset, remainingCount); } else if (remainingCount > HttpRequestStream.MaxReadSize) { remainingCount = HttpRequestStream.MaxReadSize; eventArgs.SetBuffer(eventArgs.Buffer, offset, remainingCount); } uint flags = 0; uint bytesReturned = 0; statusCode = Interop.HttpApi.HttpReceiveRequestEntityBody( _inputStream.InternalHttpContext.RequestQueueHandle, _inputStream.InternalHttpContext.RequestId, flags, (byte *)_webSocket.InternalBuffer.ToIntPtr(eventArgs.Offset), (uint)eventArgs.Count, out bytesReturned, eventArgs.NativeOverlapped); if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF) { throw new HttpListenerException((int)statusCode); } else if (statusCode == Interop.HttpApi.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously. No IO completion port callback is used because // it was disabled in SwitchToOpaqueMode() eventArgs.FinishOperationSuccess((int)bytesReturned, true); completedAsynchronouslyOrWithError = false; } else if (statusCode == Interop.HttpApi.ERROR_HANDLE_EOF) { eventArgs.FinishOperationSuccess(0, true); completedAsynchronouslyOrWithError = false; } else { completedAsynchronouslyOrWithError = true; } } catch (Exception e) { _readEventArgs.FinishOperationFailure(e, true); _outputStream.SetClosedFlag(); _outputStream.InternalHttpContext.Abort(); completedAsynchronouslyOrWithError = true; } finally { if (NetEventSource.IsEnabled) { NetEventSource.Exit(this, completedAsynchronouslyOrWithError); } } return(completedAsynchronouslyOrWithError); }
private unsafe SocketError DoCloseHandle(bool abortive) { Interop.Error errorCode = Interop.Error.SUCCESS; // If abortive is not set, we're not running on the finalizer thread, so it's safe to block here. // We can honor the linger options set on the socket. It also means closesocket() might return // EWOULDBLOCK, in which case we need to do some recovery. if (!abortive) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle} Following 'non-abortive' branch."); } // Close, and if its errno is other than EWOULDBLOCK, there's nothing more to do - we either succeeded or failed. errorCode = CloseHandle(handle); if (errorCode != Interop.Error.EWOULDBLOCK) { return(SocketPal.GetSocketErrorForErrorCode(errorCode)); } // The socket must be non-blocking with a linger timeout set. // We have to set the socket to blocking. if (Interop.Sys.Fcntl.DangerousSetIsNonBlocking(handle, 0) == 0) { // The socket successfully made blocking; retry the close(). return(SocketPal.GetSocketErrorForErrorCode(CloseHandle(handle))); } // The socket could not be made blocking; fall through to the regular abortive close. } // By default or if the non-abortive path failed, set linger timeout to zero to get an abortive close (RST). var linger = new Interop.Sys.LingerOption { OnOff = 1, Seconds = 0 }; errorCode = Interop.Sys.SetLingerOption(handle, &linger); #if DEBUG _closeSocketLinger = SocketPal.GetSocketErrorForErrorCode(errorCode); #endif if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"handle:{handle}, setsockopt():{errorCode}"); } switch (errorCode) { case Interop.Error.SUCCESS: case Interop.Error.EINVAL: case Interop.Error.ENOPROTOOPT: errorCode = CloseHandle(handle); break; // For other errors, it's too dangerous to try closesocket() - it might block! } return(SocketPal.GetSocketErrorForErrorCode(errorCode)); }
private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle sessionHandle) { _proxyHelper = proxyHelper; _sessionHandle = sessionHandle; if (proxyHelper.ManualSettingsUsed) { if (NetEventSource.IsEnabled) { NetEventSource.Info(proxyHelper, $"ManualSettingsUsed, {proxyHelper.Proxy}"); } ParseProxyConfig(proxyHelper.Proxy, out _insecureProxyUri, out _secureProxyUri); if (_insecureProxyUri == null && _secureProxyUri == null) { // If advanced parsing by protocol fails, fall-back to simplified parsing. _insecureProxyUri = _secureProxyUri = GetUriFromString(proxyHelper.Proxy); } if (!string.IsNullOrWhiteSpace(proxyHelper.ProxyBypass)) { int idx = 0; int start = 0; string tmp; // Process bypass list for manual setting. // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average. _bypass = new List <Regex>(proxyHelper.ProxyBypass.Length / 5); while (idx < proxyHelper.ProxyBypass.Length) { // Strip leading spaces and scheme if any. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ' ') { idx += 1; } ; if (string.Compare(proxyHelper.ProxyBypass, idx, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { idx += 7; } else if (string.Compare(proxyHelper.ProxyBypass, idx, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) { idx += 8; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == '[') { // Strip [] from IPv6 so we can use IdnHost laster for matching. idx += 1; } start = idx; while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ' ' && proxyHelper.ProxyBypass[idx] != ';' && proxyHelper.ProxyBypass[idx] != ']') { idx += 1; } ; if (idx == start) { // Empty string. tmp = null; } else if (string.Compare(proxyHelper.ProxyBypass, start, "<local>", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { _bypassLocal = true; tmp = null; } else { tmp = proxyHelper.ProxyBypass.Substring(start, idx - start); } // Skip trailing characters if any. if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { // Got stopped at space or ']'. Strip until next ';' or end. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { idx += 1; } ; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ';') { idx++; } if (tmp == null) { continue; } try { // Escape any special characters and unescape * to get wildcard pattern match. Regex re = new Regex(Regex.Escape(tmp).Replace("\\*", ".*?") + "$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); _bypass.Add(re); } catch (Exception ex) { if (NetEventSource.IsEnabled) { NetEventSource.Error(this, $"Failed to process {tmp} from bypass list: {ex}"); } } } if (_bypass.Count == 0) { // Bypass string only had garbage we did not parse. _bypass = null; } } if (_bypassLocal) { _localIp = new List <IPAddress>(); foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) { IPInterfaceProperties ipProps = netInterface.GetIPProperties(); foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) { _localIp.Add(addr.Address); } } } } }
protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this, request, cancellationToken); } HttpResponseMessage response = await _initialInnerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); uint redirectCount = 0; Uri redirectUri; while ((redirectUri = GetUriForRedirect(request.RequestUri, response)) != null) { redirectCount++; if (redirectCount > _maxAutomaticRedirections) { // If we exceed the maximum number of redirects // then just return the 3xx response. if (NetEventSource.IsEnabled) { TraceError($"Exceeded max number of redirects. Redirect from {request.RequestUri} to {redirectUri} blocked.", request.GetHashCode()); } break; } response.Dispose(); // Clear the authorization header. request.Headers.Authorization = null; if (NetEventSource.IsEnabled) { Trace($"Redirecting from {request.RequestUri} to {redirectUri} in response to status code {(int)response.StatusCode} '{response.StatusCode}'.", request.GetHashCode()); } // Set up for the redirect request.RequestUri = redirectUri; if (RequestRequiresForceGet(response.StatusCode, request.Method)) { if (NetEventSource.IsEnabled) { Trace($"Modified request from {request.Method} to {HttpMethod.Get} in response to status code {(int)response.StatusCode} '{response.StatusCode}'.", request.GetHashCode()); } request.Method = HttpMethod.Get; request.Content = null; request.Headers.TransferEncodingChunked = false; } // Issue the redirected request. response = await _redirectInnerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); } if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } return(response); }
internal static unsafe int InitializeSecurityContext( ref SafeFreeCredentials?inCredentials, ref SafeDeleteSslContext?refContext, string?targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, InputSecurityBuffers inSecBuffers, ref SecurityBuffer outSecBuffer, ref Interop.SspiCli.ContextFlags outFlags) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(null, $"credential:{inCredentials}, crefContext:{refContext}, targetName:{targetName}, inFlags:{inFlags}, endianness:{endianness}"); } if (inCredentials == null) { throw new ArgumentNullException(nameof(inCredentials)); } Debug.Assert(inSecBuffers.Count <= 3); Interop.SspiCli.SecBufferDesc inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Count); Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1); // Actually, this is returned in outFlags. bool isSspiAllocated = (inFlags & Interop.SspiCli.ContextFlags.AllocateMemory) != 0 ? true : false; int errorCode = -1; bool isContextAbsent = true; if (refContext != null) { isContextAbsent = refContext._handle.IsZero; } // Optional output buffer that may need to be freed. SafeFreeContextBuffer?outFreeContextBuffer = null; try { Span <Interop.SspiCli.SecBuffer> inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3]; fixed(void *inUnmanagedBufferPtr = inUnmanagedBuffer) fixed(void *pinnedToken0 = inSecBuffers._item0.Token) fixed(void *pinnedToken1 = inSecBuffers._item1.Token) fixed(void *pinnedToken2 = inSecBuffers._item2.Token) { // Fix Descriptor pointer that points to unmanaged SecurityBuffers. inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr; // Updated pvBuffer with pinned address. UnmanagedToken takes precedence. if (inSecBuffers.Count > 2) { inUnmanagedBuffer[2].BufferType = inSecBuffers._item2.Type; inUnmanagedBuffer[2].cbBuffer = inSecBuffers._item2.Token.Length; inUnmanagedBuffer[2].pvBuffer = inSecBuffers._item2.UnmanagedToken != null ? (IntPtr)inSecBuffers._item2.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken2; } if (inSecBuffers.Count > 1) { inUnmanagedBuffer[1].BufferType = inSecBuffers._item1.Type; inUnmanagedBuffer[1].cbBuffer = inSecBuffers._item1.Token.Length; inUnmanagedBuffer[1].pvBuffer = inSecBuffers._item1.UnmanagedToken != null ? (IntPtr)inSecBuffers._item1.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken1; } if (inSecBuffers.Count > 0) { inUnmanagedBuffer[0].BufferType = inSecBuffers._item0.Type; inUnmanagedBuffer[0].cbBuffer = inSecBuffers._item0.Token.Length; inUnmanagedBuffer[0].pvBuffer = inSecBuffers._item0.UnmanagedToken != null ? (IntPtr)inSecBuffers._item0.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken0; } fixed(byte *pinnedOutBytes = outSecBuffer.token) { // Fix Descriptor pointer that points to unmanaged SecurityBuffers. Interop.SspiCli.SecBuffer outUnmanagedBuffer = default; outSecurityBufferDescriptor.pBuffers = &outUnmanagedBuffer; outUnmanagedBuffer.cbBuffer = outSecBuffer.size; outUnmanagedBuffer.BufferType = outSecBuffer.type; outUnmanagedBuffer.pvBuffer = outSecBuffer.token == null || outSecBuffer.token.Length == 0 ? IntPtr.Zero : (IntPtr)(pinnedOutBytes + outSecBuffer.offset); if (isSspiAllocated) { outFreeContextBuffer = SafeFreeContextBuffer.CreateEmptyHandle(); } if (refContext == null || refContext.IsInvalid) { // Previous versions unconditionally built a new "refContext" here, but would pass // incorrect arguments to InitializeSecurityContextW in cases where an "contextHandle" was // already present and non-zero. if (isContextAbsent) { refContext = new SafeDeleteSslContext(); } } if (targetName == null || targetName.Length == 0) { targetName = dummyStr; } string punyCode = s_idnMapping.GetAscii(targetName); fixed(char *namePtr = punyCode) { errorCode = MustRunInitializeSecurityContext( ref inCredentials, isContextAbsent, (byte *)(((object)targetName == (object)dummyStr) ? null : namePtr), inFlags, endianness, &inSecurityBufferDescriptor, refContext !, ref outSecurityBufferDescriptor, ref outFlags, outFreeContextBuffer); } if (NetEventSource.IsEnabled) { NetEventSource.Info(null, "Marshalling OUT buffer"); } // Get unmanaged buffer with index 0 as the only one passed into PInvoke. outSecBuffer.size = outUnmanagedBuffer.cbBuffer; outSecBuffer.type = outUnmanagedBuffer.BufferType; outSecBuffer.token = outSecBuffer.size > 0 ? new Span <byte>((byte *)outUnmanagedBuffer.pvBuffer, outUnmanagedBuffer.cbBuffer).ToArray() : null; } } } finally { outFreeContextBuffer?.Dispose(); } if (NetEventSource.IsEnabled) { NetEventSource.Exit(null, $"errorCode:0x{errorCode:x8}, refContext:{refContext}"); } return(errorCode); }
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); 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 GetLastSocketError() will return the correct error. SocketFlags ignore; bool success = Interop.Winsock.WSAGetOverlappedResult( socket.SafeHandle, asyncResult._nativeOverlapped, out numBytes, false, out ignore); if (!success) { socketError = SocketPal.GetLastSocketError(); } 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; } } } // Set results and invoke callback asyncResult.CompletionCallback((int)numBytes, socketError); #if DEBUG } #endif }