private int ProcessReadErrorCode(SecurityStatusPal status, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest, byte[] extraBuffer) { ProtocolToken message = new ProtocolToken(null, status); if (GlobalLog.IsEnabled) { GlobalLog.Print("SecureChannel#" + LoggingHash.HashString(this) + "::***Processing an error Status = " + message.Status.ToString()); } if (message.Renegotiate) { _sslState.ReplyOnReAuthentication(extraBuffer); // Loop on read. return(-1); } if (message.CloseConnection) { _sslState.FinishRead(null); if (asyncRequest != null) { asyncRequest.CompleteUser((object)0); } return(0); } throw new IOException(SR.net_io_decrypt, message.GetException()); }
private static int DecryptNtlm( SafeDeleteContext securityContext, byte[] buffer, int offset, int count, bool isConfidential, out int newOffset, uint sequenceNumber) { const int ntlmSignatureLength = 16; // For the most part the arguments are verified in Decrypt(). if (count < ntlmSignatureLength) { if (GlobalLog.IsEnabled) { GlobalLog.Assert("NTAuthentication#" + LoggingHash.HashString(securityContext) + "::DecryptNtlm", "Argument 'count' out of range."); } Debug.Fail("NTAuthentication#" + LoggingHash.HashString(securityContext) + "::DecryptNtlm", "Argument 'count' out of range."); throw new ArgumentOutOfRangeException(nameof(count)); } var securityBuffer = new SecurityBuffer[2]; securityBuffer[0] = new SecurityBuffer(buffer, offset, ntlmSignatureLength, SecurityBufferType.Token); securityBuffer[1] = new SecurityBuffer(buffer, offset + ntlmSignatureLength, count - ntlmSignatureLength, SecurityBufferType.Data); int errorCode; SecurityBufferType realDataType = SecurityBufferType.Data; if (isConfidential) { errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber); } else { realDataType |= SecurityBufferType.ReadOnlyFlag; securityBuffer[1].type = realDataType; errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber); } if (errorCode != 0) { if (GlobalLog.IsEnabled) { GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(securityContext) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo)); } throw new Win32Exception(errorCode); } if (securityBuffer[1].type != realDataType) { throw new InternalException(); } newOffset = securityBuffer[1].offset; return(securityBuffer[1].size); }
private void ConnectCallback(IAsyncResult result) { if (GlobalLog.IsEnabled) { GlobalLog.Enter("SmtpClient#" + LoggingHash.HashString(this) + "::ConnectCallback"); } try { _transport.EndGetConnection(result); if (_cancelled) { Complete(null, result); } else { // Detected durring Begin/EndGetConnection, restrictable using DeliveryFormat bool allowUnicode = IsUnicodeSupported(); ValidateUnicodeRequirement(_message, _recipients, allowUnicode); _transport.BeginSendMail(_message.Sender ?? _message.From, _recipients, _message.BuildDeliveryStatusNotificationString(), allowUnicode, new AsyncCallback(SendMailCallback), result.AsyncState); } } catch (Exception e) { Complete(e, result); } if (GlobalLog.IsEnabled) { GlobalLog.Leave("SmtpClient#" + LoggingHash.HashString(this) + "::ConnectCallback"); } }
// SetUnmanagedStructures // // This needs to be called for overlapped IO to function properly. // // Fills in overlapped Structures used in an async overlapped Winsock call. // These calls are outside the runtime and are unmanaged code, so we need // to prepare specific structures and ints that lie in unmanaged memory // since the overlapped calls may complete asynchronously. internal void SetUnmanagedStructures(object objectsToPin) { Socket s = (Socket)AsyncObject; // Bind the Win32 Socket Handle to the ThreadPool Debug.Assert(s != null, "m_CurrentSocket is null"); Debug.Assert(s.SafeHandle != null, "m_CurrentSocket.SafeHandle is null"); if (s.SafeHandle.IsInvalid) { throw new ObjectDisposedException(s.GetType().FullName); } ThreadPoolBoundHandle boundHandle = s.SafeHandle.GetOrAllocateThreadPoolBoundHandle(); unsafe { NativeOverlapped *overlapped = boundHandle.AllocateNativeOverlapped(s_ioCallback, this, objectsToPin); _nativeOverlapped = new SafeNativeOverlapped(s.SafeHandle, overlapped); GlobalLog.Print( "BaseOverlappedAsyncResult#" + LoggingHash.HashString(this) + "::boundHandle#" + LoggingHash.HashString(boundHandle) + "::AllocateNativeOverlapped. Return=" + _nativeOverlapped.DangerousGetHandle().ToString("x")); } }
protected override bool ReleaseHandle() { GlobalLog.Print("SafeNativeOverlapped#" + LoggingHash.HashString(this) + "::ReleaseHandle()"); FreeNativeOverlapped(); return(true); }
internal BaseOverlappedAsyncResult(Socket socket, Object asyncState, AsyncCallback asyncCallback) : base(socket, asyncState, asyncCallback) { _cleanupCount = 1; GlobalLog.Print( "BaseOverlappedAsyncResult#" + LoggingHash.HashString(this) + "(Socket#" + LoggingHash.HashString(socket) + ")"); }
private SafeNativeOverlapped() : this(IntPtr.Zero) { if (GlobalLog.IsEnabled) { GlobalLog.Print("SafeNativeOverlapped#" + LoggingHash.HashString(this) + "::ctor(null)"); } }
/*++ * Encrypt - Encrypts our bytes before we send them over the wire * * PERF: make more efficient, this does an extra copy when the offset * is non-zero. * * Input: * buffer - bytes for sending * offset - * size - * output - Encrypted bytes * --*/ internal SecurityStatusPal Encrypt(byte[] buffer, int offset, int size, ref byte[] output, out int resultSize) { GlobalLog.Enter("SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt"); GlobalLog.Print("SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt() - offset: " + offset.ToString() + " size: " + size.ToString() + " buffersize: " + buffer.Length.ToString()); GlobalLog.Print("SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt() buffer:"); GlobalLog.Dump(buffer, Math.Min(buffer.Length, 128)); byte[] writeBuffer; try { if (offset < 0 || offset > (buffer == null ? 0 : buffer.Length)) { throw new ArgumentOutOfRangeException("offset"); } if (size < 0 || size > (buffer == null ? 0 : buffer.Length - offset)) { throw new ArgumentOutOfRangeException("size"); } resultSize = 0; int bufferSizeNeeded = checked (size + _headerSize + _trailerSize); if (output != null && bufferSizeNeeded <= output.Length) { writeBuffer = output; } else { writeBuffer = new byte[bufferSizeNeeded]; } Buffer.BlockCopy(buffer, offset, writeBuffer, _headerSize, size); } catch (Exception e) { if (!ExceptionCheck.IsFatal(e)) { GlobalLog.Assert(false, "SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt", "Arguments out of range."); } throw; } SecurityStatusPal secStatus = SslStreamPal.EncryptMessage(_securityContext, writeBuffer, size, _headerSize, _trailerSize, out resultSize); if (secStatus != SecurityStatusPal.OK) { GlobalLog.Leave("SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt ERROR", secStatus.ToString()); } else { output = writeBuffer; GlobalLog.Leave("SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt OK", "data size:" + resultSize.ToString()); } return(secStatus); }
public static SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, byte[] input, int offset, int size, int headerSize, int trailerSize, ref byte[] output, out int resultSize) { // Ensure that there is sufficient space for the message output. try { int bufferSizeNeeded = checked (size + headerSize + trailerSize); if (output == null || output.Length < bufferSizeNeeded) { output = new byte[bufferSizeNeeded]; } } catch (Exception e) { if (!ExceptionCheck.IsFatal(e)) { if (GlobalLog.IsEnabled) { GlobalLog.Assert("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range."); } Debug.Fail("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range."); } throw; } byte[] writeBuffer = output; // Copy the input into the output buffer to prepare for SCHANNEL's expectations Buffer.BlockCopy(input, offset, writeBuffer, headerSize, size); // Encryption using SCHANNEL requires 4 buffers: header, payload, trailer, empty. SecurityBuffer[] securityBuffer = new SecurityBuffer[4]; securityBuffer[0] = new SecurityBuffer(writeBuffer, 0, headerSize, SecurityBufferType.SECBUFFER_STREAM_HEADER); securityBuffer[1] = new SecurityBuffer(writeBuffer, headerSize, size, SecurityBufferType.SECBUFFER_DATA); securityBuffer[2] = new SecurityBuffer(writeBuffer, headerSize + size, trailerSize, SecurityBufferType.SECBUFFER_STREAM_TRAILER); securityBuffer[3] = new SecurityBuffer(null, SecurityBufferType.SECBUFFER_EMPTY); int errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPISecureChannel, securityContext, securityBuffer, 0); if (errorCode != 0) { if (GlobalLog.IsEnabled) { GlobalLog.Print("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt ERROR" + errorCode.ToString("x")); } resultSize = 0; } else { // The full buffer may not be used. resultSize = securityBuffer[0].size + securityBuffer[1].size + securityBuffer[2].size; } return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode)); }
protected override void Dispose(bool disposing) { if (disposing) { // It is important that the boundHandle is released immediately to allow new overlapped operations. GlobalLog.Print("SafeNativeOverlapped#" + LoggingHash.HashString(this) + "::Dispose(true)"); FreeNativeOverlapped(); } }
private void Complete(Exception exception, IAsyncResult result) { ContextAwareResult operationCompletedResult = (ContextAwareResult)result.AsyncState; if (GlobalLog.IsEnabled) { GlobalLog.Enter("SmtpClient#" + LoggingHash.HashString(this) + "::Complete"); } try { if (_cancelled) { //any exceptions were probably caused by cancellation, clear it. exception = null; Abort(); } // An individual failed recipient exception is benign, only abort here if ALL the recipients failed. else if (exception != null && (!(exception is SmtpFailedRecipientException) || ((SmtpFailedRecipientException)exception).fatal)) { if (GlobalLog.IsEnabled) { GlobalLog.Print("SmtpClient#" + LoggingHash.HashString(this) + "::Complete Exception: " + exception.ToString()); } Abort(); if (!(exception is SmtpException)) { exception = new SmtpException(SR.SmtpSendMailFailure, exception); } } else { if (_writer != null) { try { _writer.Close(); } // Close may result in a DataStopCommand and the server may return error codes at this time. catch (SmtpException se) { exception = se; } } _transport.ReleaseConnection(); } } finally { operationCompletedResult.InvokeCallback(exception); } if (GlobalLog.IsEnabled) { GlobalLog.Leave("SmtpClient#" + LoggingHash.HashString(this) + "::Complete"); } }
public unsafe SafeNativeOverlapped(SafeCloseSocket socketHandle, NativeOverlapped *handle) : this((IntPtr)handle) { _safeCloseSocket = socketHandle; GlobalLog.Print("SafeNativeOverlapped#" + LoggingHash.HashString(this) + "::ctor(socket#" + LoggingHash.HashString(socketHandle) + ")"); #if DEBUG _safeCloseSocket.AddRef(); #endif }
private static SafeCloseSocket CreateSocket(InnerSafeCloseSocket socket) { SafeCloseSocket ret = new SafeCloseSocket(); CreateSocket(socket, ret); GlobalLog.Print("SafeCloseSocket#" + LoggingHash.HashString(ret) + "::CreateSocket()"); return(ret); }
public BaseOverlappedAsyncResult(Socket socket, Object asyncState, AsyncCallback asyncCallback) : base(socket, asyncState, asyncCallback) { if (GlobalLog.IsEnabled) { GlobalLog.Print( "BaseOverlappedAsyncResult#" + LoggingHash.HashString(this) + "(Socket#" + LoggingHash.HashString(socket) + ")"); } }
// Utility cleanup routine. Frees the overlapped structure. // This should be overriden to free pinned and unmanaged memory in the subclass. // It needs to also be invoked from the subclass. protected virtual void ForceReleaseUnmanagedStructures() { // Free the unmanaged memory if allocated. GlobalLog.Print( "BaseOverlappedAsyncResult#" + LoggingHash.HashString(this) + "::ForceReleaseUnmanagedStructures"); _nativeOverlapped.Dispose(); _nativeOverlapped = null; GC.SuppressFinalize(this); }
private void LogBuffer(int size) { if (!SocketsEventSource.Log.IsEnabled()) { if (GlobalLog.IsEnabled) { GlobalLog.AssertFormat("ReceiveMessageOverlappedAsyncResult#{0}::LogBuffer()|Logging is off!", LoggingHash.HashString(this)); } Debug.Fail("ReceiveMessageOverlappedAsyncResult#" + LoggingHash.HashString(this) + "::LogBuffer()|Logging is off!"); } SocketsEventSource.Dump(_wsaBuffer->Pointer, Math.Min(_wsaBuffer->Length, size)); }
internal void GetConnection() { if (GlobalLog.IsEnabled) { GlobalLog.Enter("ConnectAndHandshakeAsyncResult#" + LoggingHash.HashString(this) + "::Connect:"); } if (_connection._isConnected) { throw new InvalidOperationException(SR.SmtpAlreadyConnected); } InitializeConnection(); }
public unsafe SafeNativeOverlapped(SafeCloseSocket socketHandle, NativeOverlapped *handle) : this((IntPtr)handle) { SocketHandle = socketHandle; if (GlobalLog.IsEnabled) { GlobalLog.Print("SafeNativeOverlapped#" + LoggingHash.HashString(this) + "::ctor(socket#" + LoggingHash.HashString(socketHandle) + ")"); } #if DEBUG SocketHandle.AddRef(); #endif }
protected override bool ReleaseHandle() { if (GlobalLog.IsEnabled) { GlobalLog.Print("SafeNativeOverlapped#" + LoggingHash.HashString(this) + "::ReleaseHandle()"); } FreeNativeOverlapped(); #if DEBUG SocketHandle.Release(); #endif return(true); }
internal ChannelBinding GetChannelBinding(ChannelBindingKind kind) { GlobalLog.Enter("SecureChannel#" + LoggingHash.HashString(this) + "::GetChannelBindingToken", kind.ToString()); ChannelBinding result = null; if (_securityContext != null) { result = SslStreamPal.QueryContextChannelBinding(_securityContext, kind); } GlobalLog.Leave("SecureChannel#" + LoggingHash.HashString(this) + "::GetChannelBindingToken", LoggingHash.HashString(result)); return(result); }
internal void CloseAsIs() { if (GlobalLog.IsEnabled) { GlobalLog.Print( "SafeCloseSocket#" + LoggingHash.HashString(this) + "::CloseAsIs() m_InnerSocket=" + _innerSocket == null ? "null" : LoggingHash.HashString(_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(); } // Now free it with blocking. innerSocket.BlockingRelease(); } InnerReleaseHandle(); #if DEBUG } catch (Exception exception) { if (!ExceptionCheck.IsFatal(exception)) { if (GlobalLog.IsEnabled) { GlobalLog.Assert("SafeCloseSocket::CloseAsIs(handle:" + handle.ToString("x") + ")", exception.Message); } Debug.Fail("SafeCloseSocket::CloseAsIs(handle:" + handle.ToString("x") + ")", exception.Message); } throw; } #endif }
private void SendMailCallback(IAsyncResult result) { if (GlobalLog.IsEnabled) { GlobalLog.Enter("SmtpClient#" + LoggingHash.HashString(this) + "::SendMailCallback"); } try { _writer = _transport.EndSendMail(result); // If some recipients failed but not others, send the e-mail anyways, but then return the // "Non-fatal" exception reporting the failures. The sync code path does it this way. // Fatal exceptions would have thrown above at transport.EndSendMail(...) SendMailAsyncResult sendResult = (SendMailAsyncResult)result; // Save these and throw them later in SendMessageCallback, after the message has sent. _failedRecipientException = sendResult.GetFailedRecipientException(); } catch (Exception e) { Complete(e, result); if (GlobalLog.IsEnabled) { GlobalLog.Leave("SmtpClient#" + LoggingHash.HashString(this) + "::SendMailCallback"); } return; } try { if (_cancelled) { Complete(null, result); } else { _message.BeginSend(_writer, DeliveryMethod != SmtpDeliveryMethod.Network, ServerSupportsEai, new AsyncCallback(SendMessageCallback), result.AsyncState); } } catch (Exception e) { Complete(e, result); } if (GlobalLog.IsEnabled) { GlobalLog.Leave("SmtpClient#" + LoggingHash.HashString(this) + "::SendMailCallback"); } }
// internal ProtocolToken NextMessage(byte[] incoming, int offset, int count) { GlobalLog.Enter("SecureChannel#" + LoggingHash.HashString(this) + "::NextMessage"); byte[] nextmsg = null; SecurityStatusPal errorCode = GenerateToken(incoming, offset, count, ref nextmsg); if (!_serverMode && errorCode == SecurityStatusPal.CredentialsNeeded) { GlobalLog.Print("SecureChannel#" + LoggingHash.HashString(this) + "::NextMessage() returned SecurityStatusPal.CredentialsNeeded"); SetRefreshCredentialNeeded(); errorCode = GenerateToken(incoming, offset, count, ref nextmsg); } ProtocolToken token = new ProtocolToken(nextmsg, errorCode); GlobalLog.Leave("SecureChannel#" + LoggingHash.HashString(this) + "::NextMessage", token.ToString()); return(token); }
internal void EndGetConnection(IAsyncResult result) { if (GlobalLog.IsEnabled) { GlobalLog.Enter("SmtpTransport#" + LoggingHash.HashString(this) + "::EndGetConnection"); } try { _connection.EndGetConnection(result); } finally { if (GlobalLog.IsEnabled) { GlobalLog.Leave("SmtpTransport#" + LoggingHash.HashString(this) + "::EndConnect"); } } }
// Binds the Socket Win32 Handle to the ThreadPool's CompletionPort. public ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle() { if (_released) { // Keep the exception message pointing at the external type. throw new ObjectDisposedException(typeof(Socket).FullName); } // Check to see if the socket native _handle is already // bound to the ThreadPool's completion port. if (_iocpBoundHandle == null) { lock (_iocpBindingLock) { if (_iocpBoundHandle == null) { // Bind the socket native _handle to the ThreadPool. if (GlobalLog.IsEnabled) { GlobalLog.Print("SafeCloseSocket#" + LoggingHash.HashString(this) + "::BindToCompletionPort() calling ThreadPool.BindHandle()"); } try { // The handle (this) may have been already released: // E.g.: The socket has been disposed in the main thread. A completion callback may // attempt starting another operation. _iocpBoundHandle = ThreadPoolBoundHandle.BindHandle(this); } catch (Exception exception) { if (ExceptionCheck.IsFatal(exception)) { throw; } CloseAsIs(); throw; } } } } return(_iocpBoundHandle); }
private static void ConnectionCreatedCallback(object request, object state) { if (GlobalLog.IsEnabled) { GlobalLog.Enter("ConnectAndHandshakeAsyncResult#" + LoggingHash.HashString(request) + "::ConnectionCreatedCallback"); } ConnectAndHandshakeAsyncResult ConnectAndHandshakeAsyncResult = (ConnectAndHandshakeAsyncResult)request; if (state is Exception) { ConnectAndHandshakeAsyncResult.InvokeCallback((Exception)state); return; } try { lock (ConnectAndHandshakeAsyncResult._connection) { //if we were cancelled while getting the connection, we should close and return if (ConnectAndHandshakeAsyncResult._connection._isClosed) { ConnectAndHandshakeAsyncResult._connection.ReleaseConnection(); if (GlobalLog.IsEnabled) { GlobalLog.Print("ConnectAndHandshakeAsyncResult#" + LoggingHash.HashString(request) + "::ConnectionCreatedCallback Connect was aborted "); } ConnectAndHandshakeAsyncResult.InvokeCallback(null); return; } } ConnectAndHandshakeAsyncResult.Handshake(); } catch (Exception e) { ConnectAndHandshakeAsyncResult.InvokeCallback(e); } if (GlobalLog.IsEnabled) { GlobalLog.Leave("ConnectAndHandshakeAsyncResult#" + LoggingHash.HashString(request) + "::ConnectionCreatedCallback"); } }
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) { if (GlobalLog.IsEnabled) { GlobalLog.AssertFormat("SmtpConnection#{0}::SetContextAndTryAuthenticate|Authentication required when it wasn't expected. (Maybe Credentials was changed on another thread?)", LoggingHash.HashString(this)); } Debug.Fail("SmtpConnection#" + LoggingHash.HashString(this) + "::SetContextAndTryAuthenticate|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)); }
internal SecurityStatusPal Decrypt(byte[] payload, ref int offset, ref int count) { GlobalLog.Print("SecureChannel#" + LoggingHash.HashString(this) + "::Decrypt() - offset: " + offset.ToString() + " size: " + count.ToString() + " buffersize: " + payload.Length.ToString()); if (offset < 0 || offset > (payload == null ? 0 : payload.Length)) { GlobalLog.Assert(false, "SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt", "Argument 'offset' out of range."); throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || count > (payload == null ? 0 : payload.Length - offset)) { GlobalLog.Assert(false, "SecureChannel#" + LoggingHash.HashString(this) + "::Encrypt", "Argument 'count' out of range."); throw new ArgumentOutOfRangeException("count"); } SecurityStatusPal secStatus = SslStreamPal.DecryptMessage(_securityContext, payload, ref offset, ref count); return(secStatus); }
private static void InitializeConnectionCallback(IAsyncResult result) { if (!result.CompletedSynchronously) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result.AsyncState; thisPtr._connection.EndInitializeConnection(result); if (GlobalLog.IsEnabled) { GlobalLog.Print("ConnectAndHandshakeAsyncResult#" + LoggingHash.HashString(thisPtr) + "::Connect returned" + LoggingHash.HashString(thisPtr)); } try { thisPtr.Handshake(); } catch (Exception e) { thisPtr.InvokeCallback(e); } } }
private void SendMessageCallback(IAsyncResult result) { if (GlobalLog.IsEnabled) { GlobalLog.Enter("SmtpClient#" + LoggingHash.HashString(this) + "::SendMessageCallback"); } try { _message.EndSend(result); // If some recipients failed but not others, throw AFTER sending the message. Complete(_failedRecipientException, result); } catch (Exception e) { Complete(e, result); } if (GlobalLog.IsEnabled) { GlobalLog.Leave("SmtpClient#" + LoggingHash.HashString(this) + "::SendMessageCallback"); } }