Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / Mail / smtpconnection.cs / 1407647 / smtpconnection.cs
namespace System.Net.Mail { using System; using System.Net; using System.Security.Cryptography.X509Certificates; using System.IO; using System.Threading; using System.Globalization; using System.Security.Principal; using System.Security.Permissions; using System.Security.Authentication.ExtendedProtection; using System.Diagnostics; class SmtpConnection { private static PooledStream CreateSmtpPooledStream(ConnectionPool pool) { return (PooledStream)new SmtpPooledStream(pool, TimeSpan.MaxValue, false); } private static readonly CreateConnectionDelegate m_CreateConnectionCallback = new CreateConnectionDelegate(CreateSmtpPooledStream); private static readonly ContextCallback s_AuthenticateCallback = new ContextCallback(AuthenticateCallback); BufferBuilder bufferBuilder = new BufferBuilder(); bool isConnected; bool isClosed; bool isStreamOpen; bool sawNegotiate; EventHandler onCloseHandler; internal SmtpTransport parent; internal SmtpClient client; SmtpReplyReaderFactory responseReader; // accounts for the '=' or ' ' character after AUTH const int sizeOfAuthString = 5; const int sizeOfAuthExtension = 4; // string comparisons for these MUST be case-insensitive const string authExtension = "auth"; const string authLogin = "login"; const string authNtlm = "ntlm"; const string authGssapi = "gssapi"; const string authWDigest = "wdigest"; PooledStream pooledStream; ConnectionPool connectionPool; SupportedAuth supportedAuth = SupportedAuth.None; bool serverSupportsStartTls = false; ISmtpAuthenticationModule[] authenticationModules; ICredentialsByHost credentials; int timeout = 100000; string[] extensions; private ChannelBinding channelBindingToken = null; bool enableSsl; X509CertificateCollection clientCertificates; internal SmtpConnection(SmtpTransport parent, SmtpClient client, ICredentialsByHost credentials, ISmtpAuthenticationModule[] authenticationModules) { this.client = client; this.credentials = credentials; this.authenticationModules = authenticationModules; this.parent = parent; onCloseHandler = new EventHandler(OnClose); } internal BufferBuilder BufferBuilder { get { return bufferBuilder; } } internal bool IsConnected { get { return isConnected; } } internal bool IsStreamOpen { get { return isStreamOpen; } } internal bool DSNEnabled { get { if (pooledStream != null) return ((SmtpPooledStream)pooledStream).dsnEnabled; else return false; } } internal SmtpReplyReaderFactory Reader { get { return responseReader; } } internal bool EnableSsl { get { return enableSsl; } set { #if !FEATURE_PAL enableSsl = value; #else throw new NotImplementedException("ROTORTODO"); #endif } } internal int Timeout { get { return timeout; } set { timeout = value; } } internal X509CertificateCollection ClientCertificates { get { return clientCertificates; } set { clientCertificates = value; } } internal IAsyncResult BeginGetConnection(ServicePoint servicePoint, ContextAwareResult outerResult, AsyncCallback callback, object state) { if (Logging.On) Logging.Associate(Logging.Web, this, servicePoint); Debug.Assert(servicePoint != null, "servicePoint was null from SmtpTransport"); if (EnableSsl && ClientCertificates != null && ClientCertificates.Count > 0) connectionPool = ConnectionPoolManager.GetConnectionPool(servicePoint, ClientCertificates.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), m_CreateConnectionCallback); else connectionPool = ConnectionPoolManager.GetConnectionPool(servicePoint, "", m_CreateConnectionCallback); ConnectAndHandshakeAsyncResult result = new ConnectAndHandshakeAsyncResult(this, servicePoint.Host, servicePoint.Port, outerResult, callback, state); result.GetConnection(false); return result; } internal IAsyncResult BeginFlush(AsyncCallback callback, object state) { return pooledStream.UnsafeBeginWrite(bufferBuilder.GetBuffer(), 0, bufferBuilder.Length, callback, state); } internal void EndFlush(IAsyncResult result) { pooledStream.EndWrite(result); bufferBuilder.Reset(); } internal void Flush() { pooledStream.Write(bufferBuilder.GetBuffer(), 0, bufferBuilder.Length); bufferBuilder.Reset(); } internal void ReleaseConnection() { if (!isClosed) { lock (this) { if (!isClosed && pooledStream != null) { //free cbt buffer if (channelBindingToken != null){ channelBindingToken.Close(); } GlobalLog.Print("SmtpConnectiont#" + ValidationHelper.HashString(this) + "::Close Transport#" + ValidationHelper.HashString(parent) + "putting back pooledStream#" + ValidationHelper.HashString(pooledStream)); ((SmtpPooledStream)pooledStream).previouslyUsed = true; connectionPool.PutConnection(pooledStream, pooledStream.Owner, Timeout); } isClosed = true; } } isConnected = false; } internal void Abort() { if (!isClosed) { lock (this) { if (!isClosed && pooledStream != null){ GlobalLog.Print("SmtpConnectiont#" + ValidationHelper.HashString(this) + "::Close Transport#" + ValidationHelper.HashString(parent) + "closing and putting back pooledStream#" + ValidationHelper.HashString(pooledStream)); //free CBT buffer if (this.channelBindingToken != null){ channelBindingToken.Close(); } // must destroy manually since sending a QUIT here might not be // interpreted correctly by the server if it's in the middle of a // DATA command or some similar situation. This may send a RST // but this is ok in this situation. Do not reuse this connection pooledStream.Close(0); connectionPool.PutConnection(pooledStream, pooledStream.Owner, Timeout, false); } isClosed = true; } } isConnected = false; } internal void ParseExtensions(string[] extensions) { supportedAuth = SupportedAuth.None; foreach (string extension in extensions) { if (String.Compare(extension, 0, authExtension, 0, sizeOfAuthExtension, StringComparison.OrdinalIgnoreCase) == 0) { // remove the AUTH text including the following character // to ensure that split only gets the modules supported string[] authTypes = extension.Remove(0, sizeOfAuthExtension).Split(new char[] { ' ', '=' }, StringSplitOptions.RemoveEmptyEntries); foreach (string authType in authTypes) { if (String.Compare(authType, authLogin, StringComparison.OrdinalIgnoreCase) == 0) { supportedAuth |= SupportedAuth.Login; } #if !FEATURE_PAL else if (String.Compare(authType, authNtlm, StringComparison.OrdinalIgnoreCase) == 0) { supportedAuth |= SupportedAuth.NTLM; } else if (String.Compare(authType, authGssapi, StringComparison.OrdinalIgnoreCase) == 0) { supportedAuth |= SupportedAuth.GSSAPI; } else if (String.Compare(authType, authWDigest, StringComparison.OrdinalIgnoreCase) == 0) { supportedAuth |= SupportedAuth.WDigest; } #endif // FEATURE_PAL } } else if (String.Compare(extension, 0, "dsn ", 0, 3, StringComparison.OrdinalIgnoreCase) == 0) { ((SmtpPooledStream)pooledStream).dsnEnabled = true; } else if (String.Compare(extension, 0, "STARTTLS", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) { serverSupportsStartTls = true; } } } internal bool AuthSupported(ISmtpAuthenticationModule module){ if (module is SmtpLoginAuthenticationModule) { if ((supportedAuth & SupportedAuth.Login) > 0) { return true; } } #if !FEATURE_PAL else if (module is SmtpNegotiateAuthenticationModule) { if ((supportedAuth & SupportedAuth.GSSAPI) > 0) { sawNegotiate = true; return true; } } else if (module is SmtpNtlmAuthenticationModule) { //don't try ntlm if negotiate has been tried if ((!sawNegotiate && (supportedAuth & SupportedAuth.NTLM) > 0)) { return true; } } else if (module is SmtpDigestAuthenticationModule) { if ((supportedAuth & SupportedAuth.WDigest) > 0) { return true; } } #endif // FEATURE_PAL return false; } internal void GetConnection(ServicePoint servicePoint) { if (isConnected) { throw new InvalidOperationException(SR.GetString(SR.SmtpAlreadyConnected)); } if (Logging.On) Logging.Associate(Logging.Web, this, servicePoint); Debug.Assert(servicePoint != null, "servicePoint was null from SmtpTransport"); connectionPool = ConnectionPoolManager.GetConnectionPool(servicePoint, "", m_CreateConnectionCallback); PooledStream pooledStream = connectionPool.GetConnection((object)this, null, Timeout); while (((SmtpPooledStream)pooledStream).creds != null && ((SmtpPooledStream)pooledStream).creds != credentials) { // destroy this connection so that a new connection can be created // in order to use the proper credentials. Do not just close the // connection since it's in a state where a QUIT could be sent connectionPool.PutConnection(pooledStream, pooledStream.Owner, Timeout, false); pooledStream = connectionPool.GetConnection((object)this, null, Timeout); } if (Logging.On) Logging.Associate(Logging.Web, this, pooledStream); lock (this) { this.pooledStream = pooledStream; } ((SmtpPooledStream)pooledStream).creds = credentials; responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); //set connectionlease pooledStream.UpdateLifetime(); //if the stream was already used, then we've already done the handshake if (((SmtpPooledStream)pooledStream).previouslyUsed == true) { isConnected = true; return; } LineInfo info = responseReader.GetNextReplyReader().ReadLine(); switch (info.StatusCode) { case SmtpStatusCode.ServiceReady: { break; } default: { throw new SmtpException(info.StatusCode, info.Line, true); } } try { extensions = EHelloCommand.Send(this, client.clientDomain); ParseExtensions(extensions); } catch (SmtpException e) { if ((e.StatusCode != SmtpStatusCode.CommandUnrecognized) && (e.StatusCode != SmtpStatusCode.CommandNotImplemented)) { throw e; } HelloCommand.Send(this, client.clientDomain); //if ehello isn't supported, assume basic login supportedAuth = SupportedAuth.Login; } #if !FEATURE_PAL // Establish TLS if (enableSsl) { if (!serverSupportsStartTls) { // Either TLS is already established or server does not support TLS if (!(pooledStream.NetworkStream is TlsStream)) { throw new SmtpException(SR.GetString(SR.MailServerDoesNotSupportStartTls)); } } StartTlsCommand.Send(this); TlsStream TlsStream = new TlsStream(servicePoint.Host, pooledStream.NetworkStream, clientCertificates, servicePoint, client, null); pooledStream.NetworkStream = TlsStream; //for SMTP, the CBT should be unique this.channelBindingToken = TlsStream.GetChannelBinding(ChannelBindingKind.Unique); responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); // According to RFC 3207: The client SHOULD send an EHLO command // as the first command after a successful TLS negotiation. extensions = EHelloCommand.Send(this, client.clientDomain); ParseExtensions(extensions); } #endif // !FEATURE_PAL //if no credentials were supplied, try anonymous //servers don't appear to anounce that they support anonymous login. if (credentials != null) { for (int i = 0; i < authenticationModules.Length; i++) { //only authenticate if the auth protocol is supported - [....] if (!AuthSupported(authenticationModules[i])) { continue; } NetworkCredential credential = credentials.GetCredential(servicePoint.Host, servicePoint.Port, authenticationModules[i].AuthenticationType); if (credential == null) continue; Authorization auth = SetContextAndTryAuthenticate(authenticationModules[i], credential, null); if (auth != null && auth.Message != null) { info = AuthCommand.Send(this, authenticationModules[i].AuthenticationType, auth.Message); if (info.StatusCode == SmtpStatusCode.CommandParameterNotImplemented) { continue; } while ((int)info.StatusCode == 334) { auth = authenticationModules[i].Authenticate(info.Line, null, this, this.client.TargetName, this.channelBindingToken); if (auth == null) { throw new SmtpException(SR.GetString(SR.SmtpAuthenticationFailed)); } info = AuthCommand.Send(this, auth.Message); if ((int)info.StatusCode == 235) { authenticationModules[i].CloseContext(this); isConnected = true; return; } } } } } isConnected = true; } // // We may need to impersonate in this method // [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)] private Authorization SetContextAndTryAuthenticate(ISmtpAuthenticationModule module, NetworkCredential credential, ContextAwareResult context) { #if !FEATURE_PAL // We may need to restore user thread token here if (credential is SystemNetworkCredential && ComNetOS.IsWinNt) { // #if DEBUG GlobalLog.Assert(context == null || context.IdentityRequested, "SmtpConnection#{0}::SetContextAndTryAuthenticate|Authentication required when it wasn't expected. (Maybe Credentials was changed on another thread?)", ValidationHelper.HashString(this)); #endif WindowsIdentity w = context == null ? null : context.Identity; try { IDisposable ctx = w == null ? null : w.Impersonate(); if (ctx != null) { using (ctx) { return module.Authenticate(null, credential, this, this.client.TargetName, this.channelBindingToken); } } else { ExecutionContext x = context == null ? null : context.ContextCopy; if (x != null) { AuthenticateCallbackContext authenticationContext = new AuthenticateCallbackContext(this, module, credential, this.client.TargetName, this.channelBindingToken); ExecutionContext.Run(x, s_AuthenticateCallback, authenticationContext); return authenticationContext.result; } else { return module.Authenticate(null, credential, this, this.client.TargetName, this.channelBindingToken); } } } catch { // Prevent the impersonation from leaking to upstack exception filters. throw; } } #endif // !FEATURE_PAL return module.Authenticate(null, credential, this, this.client.TargetName, this.channelBindingToken); } private static void AuthenticateCallback(object state) { AuthenticateCallbackContext context = (AuthenticateCallbackContext)state; context.result = context.module.Authenticate(null, context.credential, context.thisPtr, context.spn, context.token); } private class AuthenticateCallbackContext { internal AuthenticateCallbackContext(SmtpConnection thisPtr, ISmtpAuthenticationModule module, NetworkCredential credential, string spn, ChannelBinding Token) { this.thisPtr = thisPtr; this.module = module; this.credential = credential; this.spn = spn; this.token = Token; this.result = null; } internal readonly SmtpConnection thisPtr; internal readonly ISmtpAuthenticationModule module; internal readonly NetworkCredential credential; internal readonly string spn; internal readonly ChannelBinding token; internal Authorization result; } internal void EndGetConnection(IAsyncResult result) { ConnectAndHandshakeAsyncResult.End(result); } internal Stream GetClosableStream() { ClosableStream cs = new ClosableStream(pooledStream.NetworkStream, onCloseHandler); isStreamOpen = true; return cs; } void OnClose(object sender, EventArgs args) { isStreamOpen = false; DataStopCommand.Send(this); } class ConnectAndHandshakeAsyncResult : LazyAsyncResult { private static readonly GeneralAsyncDelegate m_ConnectionCreatedCallback = new GeneralAsyncDelegate(ConnectionCreatedCallback); string authResponse; SmtpConnection connection; int currentModule = -1; int port; static AsyncCallback handshakeCallback = new AsyncCallback(HandshakeCallback); static AsyncCallback sendEHelloCallback = new AsyncCallback(SendEHelloCallback); static AsyncCallback sendHelloCallback = new AsyncCallback(SendHelloCallback); static AsyncCallback authenticateCallback = new AsyncCallback(AuthenticateCallback); static AsyncCallback authenticateContinueCallback = new AsyncCallback(AuthenticateContinueCallback); string host; private readonly ContextAwareResult m_OuterResult; internal ConnectAndHandshakeAsyncResult(SmtpConnection connection, string host, int port, ContextAwareResult outerResult, AsyncCallback callback, object state) : base(null, state, callback) { this.connection = connection; this.host = host; this.port = port; m_OuterResult = outerResult; } private static void ConnectionCreatedCallback(object request, object state) { GlobalLog.Enter("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(request) + "::ConnectionCreatedCallback"); ConnectAndHandshakeAsyncResult ConnectAndHandshakeAsyncResult = (ConnectAndHandshakeAsyncResult)request; if (state is Exception) { ConnectAndHandshakeAsyncResult.InvokeCallback((Exception)state); return; } SmtpPooledStream pooledStream = (SmtpPooledStream)(PooledStream)state; try { while (pooledStream.creds != null && pooledStream.creds != ConnectAndHandshakeAsyncResult.connection.credentials) { GlobalLog.Print("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(request) + "::Connect pooledStream has wrong creds " + ValidationHelper.HashString(pooledStream)); ConnectAndHandshakeAsyncResult.connection.connectionPool.PutConnection(pooledStream, pooledStream.Owner, ConnectAndHandshakeAsyncResult.connection.Timeout, false); pooledStream = (SmtpPooledStream)ConnectAndHandshakeAsyncResult.connection.connectionPool.GetConnection((object)ConnectAndHandshakeAsyncResult, ConnectAndHandshakeAsyncResult.m_ConnectionCreatedCallback, ConnectAndHandshakeAsyncResult.connection.Timeout); if (pooledStream == null) { GlobalLog.Leave("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(request) + "::Connect returning asynchronously"); return; } } if (Logging.On) Logging.Associate(Logging.Web, ConnectAndHandshakeAsyncResult.connection, pooledStream); pooledStream.Owner = ConnectAndHandshakeAsyncResult.connection; //needs to be updated for gc reasons pooledStream.creds = ConnectAndHandshakeAsyncResult.connection.credentials; lock (ConnectAndHandshakeAsyncResult.connection) { //if we were cancelled while getting the connection, we should close and return if (ConnectAndHandshakeAsyncResult.connection.isClosed) { ConnectAndHandshakeAsyncResult.connection.connectionPool.PutConnection(pooledStream, pooledStream.Owner, ConnectAndHandshakeAsyncResult.connection.Timeout, false); GlobalLog.Print("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(request) + "::ConnectionCreatedCallback Connect was aborted " + ValidationHelper.HashString(pooledStream)); ConnectAndHandshakeAsyncResult.InvokeCallback(null); return; } ConnectAndHandshakeAsyncResult.connection.pooledStream = pooledStream; } ConnectAndHandshakeAsyncResult.Handshake(); } catch (Exception e) { ConnectAndHandshakeAsyncResult.InvokeCallback(e); } GlobalLog.Leave("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(request) + "::ConnectionCreatedCallback pooledStream#" + ValidationHelper.HashString(pooledStream)); } internal static void End(IAsyncResult result) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result; object connectResult = thisPtr.InternalWaitForCompletion(); if (connectResult is Exception){ throw (Exception)connectResult; } } internal void GetConnection(bool synchronous) { GlobalLog.Enter("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(this) + "::Connect: [....]=" + (synchronous ? "true" : "false")); if (connection.isConnected) { throw new InvalidOperationException(SR.GetString(SR.SmtpAlreadyConnected)); } SmtpPooledStream pooledStream = (SmtpPooledStream)connection.connectionPool.GetConnection((object)this, (synchronous ? null : m_ConnectionCreatedCallback), connection.Timeout); GlobalLog.Print("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(this) + "::Connect returned" + ValidationHelper.HashString(this)); if (pooledStream != null) { try { while (pooledStream.creds != null && pooledStream.creds != connection.credentials) { GlobalLog.Print("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(this) + "::Connect pooledStream has wrong creds " + ValidationHelper.HashString(pooledStream)); connection.connectionPool.PutConnection(pooledStream, pooledStream.Owner, connection.Timeout, false); pooledStream = (SmtpPooledStream)connection.connectionPool.GetConnection((object)this, (synchronous ? null : m_ConnectionCreatedCallback), connection.Timeout); if (pooledStream == null) { GlobalLog.Leave("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(this) + "::Connect returning asynchronously"); return; } } pooledStream.creds = connection.credentials; pooledStream.Owner = this.connection; //needs to be updated for gc reasons lock (connection) { connection.pooledStream = pooledStream; } Handshake(); } catch (Exception e) { InvokeCallback(e); } } GlobalLog.Leave("ConnectAndHandshakeAsyncResult#" + ValidationHelper.HashString(this) + "::Connect pooledStream#" + ValidationHelper.HashString(pooledStream)); } void Handshake() { connection.responseReader = new SmtpReplyReaderFactory(connection.pooledStream.NetworkStream); //if we've already used this stream, then we've already done the handshake //set connectionlease connection.pooledStream.UpdateLifetime(); if (((SmtpPooledStream)connection.pooledStream).previouslyUsed == true) { connection.isConnected = true; InvokeCallback(); return; } SmtpReplyReader reader = connection.Reader.GetNextReplyReader(); IAsyncResult result = reader.BeginReadLine(handshakeCallback, this); if (!result.CompletedSynchronously) { return; } LineInfo info = reader.EndReadLine(result); if (info.StatusCode != SmtpStatusCode.ServiceReady) { throw new SmtpException(info.StatusCode, info.Line, true); } try { if (!SendEHello()) { return; } } catch { if (!SendHello()) { return; } } } static void HandshakeCallback(IAsyncResult result) //3 { if (!result.CompletedSynchronously) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result.AsyncState; try { try { LineInfo info = thisPtr.connection.Reader.CurrentReader.EndReadLine(result); if (info.StatusCode != SmtpStatusCode.ServiceReady) { thisPtr.InvokeCallback(new SmtpException(info.StatusCode, info.Line, true)); return; } if (!thisPtr.SendEHello()) { return; } } catch (SmtpException) { if (!thisPtr.SendHello()) { return; } } } catch (Exception e) { thisPtr.InvokeCallback(e); } } } bool SendEHello()//4 { IAsyncResult result = EHelloCommand.BeginSend(connection, connection.client.clientDomain, sendEHelloCallback, this); if (result.CompletedSynchronously) { connection.extensions = EHelloCommand.EndSend(result); connection.ParseExtensions(connection.extensions); // If we already have a TlsStream, this is the second EHLO cmd // that we sent after TLS handshake compelted. So skip TLS and // continue with Authenticate. if (connection.pooledStream.NetworkStream is TlsStream) { Authenticate(); return true; } if (connection.EnableSsl) { #if !FEATURE_PAL if (!connection.serverSupportsStartTls) { // Either TLS is already established or server does not support TLS if (!(connection.pooledStream.NetworkStream is TlsStream)) { throw new SmtpException(SR.GetString(SR.MailServerDoesNotSupportStartTls)); } } SendStartTls(); #else // FEATURE_PAL throw new NotSupportedException("ROTORTODO"); #endif // !FEATURE_PAL } else { Authenticate(); } return true; } return false; } static void SendEHelloCallback(IAsyncResult result)//5 { if (!result.CompletedSynchronously) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result.AsyncState; try { try { thisPtr.connection.extensions = EHelloCommand.EndSend(result); thisPtr.connection.ParseExtensions(thisPtr.connection.extensions); // If we already have a TlsStream, this is the second EHLO cmd // that we sent after TLS handshake compelted. So skip TLS and // continue with Authenticate. if (thisPtr.connection.pooledStream.NetworkStream is TlsStream) { thisPtr.Authenticate(); return; } } catch (SmtpException e) { if ((e.StatusCode != SmtpStatusCode.CommandUnrecognized) && (e.StatusCode != SmtpStatusCode.CommandNotImplemented)){ throw e; } if (!thisPtr.SendHello()) { return; } } if (thisPtr.connection.EnableSsl) { #if !FEATURE_PAL if (!thisPtr.connection.serverSupportsStartTls) { // Either TLS is already established or server does not support TLS if (!(thisPtr.connection.pooledStream.NetworkStream is TlsStream)) { throw new SmtpException(SR.GetString(SR.MailServerDoesNotSupportStartTls)); } } thisPtr.SendStartTls(); #else // FEATURE_PAL throw new NotSupportedException("ROTORTODO"); #endif // !FEATURE_PAL } else { thisPtr.Authenticate(); } } catch (Exception e) { thisPtr.InvokeCallback(e); } } } bool SendHello()//6 { IAsyncResult result = HelloCommand.BeginSend(connection, connection.client.clientDomain, sendHelloCallback, this); //if ehello isn't supported, assume basic auth if (result.CompletedSynchronously) { connection.supportedAuth = SupportedAuth.Login; HelloCommand.EndSend(result); Authenticate(); return true; } return false; } static void SendHelloCallback(IAsyncResult result) //7 { if (!result.CompletedSynchronously) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result.AsyncState; try { HelloCommand.EndSend(result); thisPtr.Authenticate(); } catch (Exception e) { thisPtr.InvokeCallback(e); } } } #if !FEATURE_PAL bool SendStartTls()//6 { IAsyncResult result = StartTlsCommand.BeginSend(connection, SendStartTlsCallback, this); if (result.CompletedSynchronously) { StartTlsCommand.EndSend(result); TlsStream TlsStream = new TlsStream(connection.pooledStream.ServicePoint.Host, connection.pooledStream.NetworkStream, connection.ClientCertificates, connection.pooledStream.ServicePoint, connection.client, m_OuterResult.ContextCopy); connection.pooledStream.NetworkStream = TlsStream; connection.responseReader = new SmtpReplyReaderFactory(connection.pooledStream.NetworkStream); SendEHello(); return true; } return false; } static void SendStartTlsCallback(IAsyncResult result) //7 { if (!result.CompletedSynchronously) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result.AsyncState; try { StartTlsCommand.EndSend(result); TlsStream TlsStream = new TlsStream(thisPtr.connection.pooledStream.ServicePoint.Host, thisPtr.connection.pooledStream.NetworkStream, thisPtr.connection.ClientCertificates, thisPtr.connection.pooledStream.ServicePoint, thisPtr.connection.client, thisPtr.m_OuterResult.ContextCopy); thisPtr.connection.pooledStream.NetworkStream = TlsStream; thisPtr.connection.responseReader = new SmtpReplyReaderFactory(thisPtr.connection.pooledStream.NetworkStream); thisPtr.SendEHello(); } catch (Exception e) { thisPtr.InvokeCallback(e); } } } #endif // !FEATURE_PAL void Authenticate() //8 { //if no credentials were supplied, try anonymous //servers don't appear to anounce that they support anonymous login. if (connection.credentials != null) { while (++currentModule < connection.authenticationModules.Length) { //only authenticate if the auth protocol is supported ISmtpAuthenticationModule module = connection.authenticationModules[currentModule]; if (!connection.AuthSupported(module)) { continue; } NetworkCredential credential = connection.credentials.GetCredential(host, port, module.AuthenticationType); if (credential == null) continue; Authorization auth = connection.SetContextAndTryAuthenticate(module, credential, m_OuterResult); if (auth != null && auth.Message != null) { IAsyncResult result = AuthCommand.BeginSend(connection, connection.authenticationModules[currentModule].AuthenticationType, auth.Message, authenticateCallback, this); if (!result.CompletedSynchronously) { return; } LineInfo info = AuthCommand.EndSend(result); if ((int)info.StatusCode == 334) { authResponse = info.Line; if (!AuthenticateContinue()) { return; } } else if ((int)info.StatusCode == 235) { module.CloseContext(connection); connection.isConnected = true; break; } } } //try anonymous if didn't authenticate //if (!connection.isConnected) { // throw new SmtpException(SR.GetString(SR.SmtpAuthenticationFailed)); // } } connection.isConnected = true; InvokeCallback(); } static void AuthenticateCallback(IAsyncResult result) //9 { if (!result.CompletedSynchronously) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result.AsyncState; try { LineInfo info = AuthCommand.EndSend(result); if ((int)info.StatusCode == 334) { thisPtr.authResponse = info.Line; if (!thisPtr.AuthenticateContinue()) { return; } } else if ((int)info.StatusCode == 235) { thisPtr.connection.authenticationModules[thisPtr.currentModule].CloseContext(thisPtr.connection); thisPtr.connection.isConnected = true; thisPtr.InvokeCallback(); return; } thisPtr.Authenticate(); } catch (Exception e) { thisPtr.InvokeCallback(e); } } } bool AuthenticateContinue() //10 { for (; ; ) { // We don't need credential on the continued auth assuming they were captured on the first call. // That should always work, otherwise what if a new credential has been returned? Authorization auth = connection.authenticationModules[currentModule].Authenticate(authResponse, null, connection, connection.client.TargetName, connection.channelBindingToken); if (auth == null) { throw new SmtpException(SR.GetString(SR.SmtpAuthenticationFailed)); } IAsyncResult result = AuthCommand.BeginSend(connection, auth.Message, authenticateContinueCallback, this); if (!result.CompletedSynchronously) { return false; } LineInfo info = AuthCommand.EndSend(result); if ((int)info.StatusCode == 235) { connection.authenticationModules[currentModule].CloseContext(connection); connection.isConnected = true; InvokeCallback(); return false; } else if ((int)info.StatusCode != 334) { return true; } authResponse = info.Line; } } static void AuthenticateContinueCallback(IAsyncResult result) //11 { if (!result.CompletedSynchronously) { ConnectAndHandshakeAsyncResult thisPtr = (ConnectAndHandshakeAsyncResult)result.AsyncState; try { LineInfo info = AuthCommand.EndSend(result); if ((int)info.StatusCode == 235) { thisPtr.connection.authenticationModules[thisPtr.currentModule].CloseContext(thisPtr.connection); thisPtr.connection.isConnected = true; thisPtr.InvokeCallback(); return; } else if ((int)info.StatusCode == 334) { thisPtr.authResponse = info.Line; if (!thisPtr.AuthenticateContinue()) { return; } } thisPtr.Authenticate(); } catch (Exception e) { thisPtr.InvokeCallback(e); } } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DSACryptoServiceProvider.cs
- SchemaCollectionPreprocessor.cs
- ObjectResult.cs
- ContentElementAutomationPeer.cs
- StylusPointCollection.cs
- BehaviorEditorPart.cs
- XmlLangPropertyAttribute.cs
- OlePropertyStructs.cs
- TcpClientSocketManager.cs
- NotificationContext.cs
- InvokeGenerator.cs
- NumericUpDown.cs
- SessionStateUtil.cs
- PositiveTimeSpanValidatorAttribute.cs
- ApplicationContext.cs
- TopClause.cs
- TextBox.cs
- DbUpdateCommandTree.cs
- TextSimpleMarkerProperties.cs
- QilInvokeEarlyBound.cs
- RuntimeResourceSet.cs
- _RequestLifetimeSetter.cs
- ObjectDataSource.cs
- MsmqIntegrationChannelFactory.cs
- DataGridColumn.cs
- MergeLocalizationDirectives.cs
- ModifierKeysConverter.cs
- WriteFileContext.cs
- DrawingGroup.cs
- Oid.cs
- CancellationScope.cs
- OperationAbortedException.cs
- OpenFileDialog.cs
- FormParameter.cs
- DesignerToolStripControlHost.cs
- ErrorTableItemStyle.cs
- RsaSecurityKey.cs
- LoginName.cs
- SimpleLine.cs
- ShaperBuffers.cs
- X509CertificateChain.cs
- DataSourceControlBuilder.cs
- PhonemeConverter.cs
- ExcludeFromCodeCoverageAttribute.cs
- CommandConverter.cs
- ListBoxItem.cs
- SelectionProviderWrapper.cs
- ContentPosition.cs
- Hashtable.cs
- RSAPKCS1SignatureDeformatter.cs
- COAUTHINFO.cs
- EventLogPermissionEntry.cs
- SqlDataSourceConnectionPanel.cs
- ValidationSummary.cs
- XPathBuilder.cs
- Substitution.cs
- IImplicitResourceProvider.cs
- SectionVisual.cs
- ObjectCache.cs
- StringToken.cs
- FixedSOMSemanticBox.cs
- GB18030Encoding.cs
- CommonXSendMessage.cs
- ResourcePermissionBase.cs
- DataViewListener.cs
- SqlDataReaderSmi.cs
- Marshal.cs
- ContentPlaceHolder.cs
- WebSysDescriptionAttribute.cs
- DoubleUtil.cs
- ReflectionTypeLoadException.cs
- ActivityTypeCodeDomSerializer.cs
- QilList.cs
- HtmlMeta.cs
- XsdBuilder.cs
- LineUtil.cs
- BaseParaClient.cs
- SimplePropertyEntry.cs
- ArcSegment.cs
- EntityModelBuildProvider.cs
- TextChange.cs
- Encoder.cs
- _NetRes.cs
- XmlAttributeOverrides.cs
- ExpressionLink.cs
- ExtractedStateEntry.cs
- TextTrailingCharacterEllipsis.cs
- FormsAuthenticationConfiguration.cs
- Point3DKeyFrameCollection.cs
- DeclarationUpdate.cs
- SortExpressionBuilder.cs
- SqlCacheDependency.cs
- SQLRoleProvider.cs
- QilInvokeLateBound.cs
- FirstMatchCodeGroup.cs
- RemotingException.cs
- TextSelectionProcessor.cs
- AdornerLayer.cs
- WeakReferenceKey.cs
- PolyBezierSegment.cs