private CommandStream.PipelineInstruction QueueOrCreateFtpDataStream(ref Stream stream) { if (this.m_DataSocket == null) { throw new InternalException(); } if (this.m_TlsStream != null) { stream = new FtpDataStream(this.m_TlsStream, (FtpWebRequest)base.m_Request, this.IsFtpDataStreamWriteable()); this.m_TlsStream = null; return(CommandStream.PipelineInstruction.GiveStream); } NetworkStream networkStream = new NetworkStream(this.m_DataSocket, true); if (base.UsingSecureStream) { FtpWebRequest initiatingRequest = (FtpWebRequest)base.m_Request; TlsStream stream3 = new TlsStream(initiatingRequest.RequestUri.Host, networkStream, initiatingRequest.ClientCertificates, base.Pool.ServicePoint, initiatingRequest, base.m_Async ? initiatingRequest.GetWritingContext().ContextCopy : null); networkStream = stream3; if (base.m_Async) { this.m_TlsStream = stream3; LazyAsyncResult result = new LazyAsyncResult(null, this, m_SSLHandshakeCallback); stream3.ProcessAuthentication(result); return(CommandStream.PipelineInstruction.Pause); } stream3.ProcessAuthentication(null); } stream = new FtpDataStream(networkStream, (FtpWebRequest)base.m_Request, this.IsFtpDataStreamWriteable()); return(CommandStream.PipelineInstruction.GiveStream); }
protected override void ClearState() { this.m_ContentLength = -1L; this.m_LastModified = DateTime.MinValue; this.m_ResponseUri = null; this.m_DataHandshakeStarted = false; this.StatusCode = FtpStatusCode.Undefined; this.StatusLine = null; this.m_DataSocket = null; this.m_PassiveEndPoint = null; this.m_TlsStream = null; base.ClearState(); }
protected override void ClearState() { _contentLength = -1; _lastModified = DateTime.MinValue; _responseUri = null; _dataHandshakeStarted = false; StatusCode = FtpStatusCode.Undefined; StatusLine = null; _dataSocket = null; _passiveEndPoint = null; _tlsStream = null; base.ClearState(); }
protected override void ProcessAsSsl3() { // Client Version this.Write(this.Context.Protocol); // Random bytes - Unix time + Radom bytes [28] TlsStream clientRandom = new TlsStream(); clientRandom.Write(this.Context.GetUnixTime()); clientRandom.Write(this.Context.GetSecureRandomBytes(28)); this.random = clientRandom.ToArray(); clientRandom.Reset(); this.Write(this.random); // Session id // Check if we have a cache session we could reuse this.Context.SessionId = ClientSessionCache.FromHost (this.Context.ClientSettings.TargetHost); if (this.Context.SessionId != null) { this.Write((byte)this.Context.SessionId.Length); if (this.Context.SessionId.Length > 0) { this.Write(this.Context.SessionId); } } else { this.Write((byte)0); } // Write length of Cipher suites this.Write((short)(this.Context.SupportedCiphers.Count*2)); // Write Supported Cipher suites for (int i = 0; i < this.Context.SupportedCiphers.Count; i++) { this.Write((short)this.Context.SupportedCiphers[i].Code); } // Compression methods length this.Write((byte)1); // Compression methods ( 0 = none ) this.Write((byte)this.Context.CompressionMethod); }
internal bool AcceptCertificate(TlsStream secureStream, WebRequest request) { X509Certificate newCertificate = secureStream.Certificate; if (ServicePointManager.CertificatePolicy != null) { PolicyWrapper cpw = new PolicyWrapper(ServicePointManager.CertificatePolicy, this, request); bool acceptable = secureStream.VerifyServerCertificate(cpw); if (!acceptable) { return(false); } } m_ServerCertificate = newCertificate; m_ClientCertificate = secureStream.ClientCertificate; return(true); }
// Creates a FtpDataStream object, constructs a TLS stream if needed. // In case SSL and ASYNC we delay sigaling the user stream until the handshake is done. private PipelineInstruction QueueOrCreateFtpDataStream(ref Stream stream) { if (_dataSocket == null) { throw new InternalException(); } // // Re-entered pipeline with completed read on the TlsStream // if (_tlsStream != null) { stream = new FtpDataStream(_tlsStream, (FtpWebRequest)_request, IsFtpDataStreamWriteable()); _tlsStream = null; return(PipelineInstruction.GiveStream); } NetworkStream networkStream = new NetworkStream(_dataSocket, true); if (UsingSecureStream) { FtpWebRequest request = (FtpWebRequest)_request; TlsStream tlsStream = new TlsStream(networkStream, _dataSocket, request.RequestUri.Host, request.ClientCertificates); networkStream = tlsStream; if (_isAsync) { _tlsStream = tlsStream; tlsStream.BeginAuthenticateAsClient(s_SSLHandshakeCallback, this); return(PipelineInstruction.Pause); } else { tlsStream.AuthenticateAsClient(); } } stream = new FtpDataStream(networkStream, (FtpWebRequest)_request, IsFtpDataStreamWriteable()); return(PipelineInstruction.GiveStream); }
private CommandStream.PipelineInstruction QueueOrCreateFtpDataStream(ref Stream stream) { if (this.m_DataSocket == null) { throw new InternalException(); } if (this.m_TlsStream != null) { stream = new FtpDataStream(this.m_TlsStream, (FtpWebRequest) base.m_Request, this.IsFtpDataStreamWriteable()); this.m_TlsStream = null; return CommandStream.PipelineInstruction.GiveStream; } NetworkStream networkStream = new NetworkStream(this.m_DataSocket, true); if (base.UsingSecureStream) { FtpWebRequest initiatingRequest = (FtpWebRequest) base.m_Request; TlsStream stream3 = new TlsStream(initiatingRequest.RequestUri.Host, networkStream, initiatingRequest.ClientCertificates, base.Pool.ServicePoint, initiatingRequest, base.m_Async ? initiatingRequest.GetWritingContext().ContextCopy : null); networkStream = stream3; if (base.m_Async) { this.m_TlsStream = stream3; LazyAsyncResult result = new LazyAsyncResult(null, this, m_SSLHandshakeCallback); stream3.ProcessAuthentication(result); return CommandStream.PipelineInstruction.Pause; } stream3.ProcessAuthentication(null); } stream = new FtpDataStream(networkStream, (FtpWebRequest) base.m_Request, this.IsFtpDataStreamWriteable()); return CommandStream.PipelineInstruction.GiveStream; }
protected override CommandStream.PipelineInstruction PipelineCallback(CommandStream.PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (response == null) { return(CommandStream.PipelineInstruction.Abort); } FtpStatusCode status = (FtpStatusCode)response.Status; if (status != FtpStatusCode.ClosingControl) { this.StatusCode = status; this.StatusLine = response.StatusDescription; } if (response.InvalidStatusCode) { throw new WebException(SR.GetString("net_InvalidStatusCode"), WebExceptionStatus.ProtocolError); } if (base.m_Index == -1) { switch (status) { case FtpStatusCode.SendUserCommand: this.m_BannerMessage = new StringBuilder(); this.m_BannerMessage.Append(this.StatusLine); return(CommandStream.PipelineInstruction.Advance); case FtpStatusCode.ServiceTemporarilyNotAvailable: return(CommandStream.PipelineInstruction.Reread); } throw base.GenerateException(status, response.StatusDescription, null); } if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { base.Encoding = Encoding.UTF8; } else { base.Encoding = Encoding.Default; } return(CommandStream.PipelineInstruction.Advance); } if (entry.Command.IndexOf("USER") != -1) { if (status == FtpStatusCode.LoggedInProceed) { this.m_LoginState = FtpLoginState.LoggedIn; base.m_Index++; } else if ((status == FtpStatusCode.NotLoggedIn) && (this.m_LoginState != FtpLoginState.NotLoggedIn)) { this.m_LoginState = FtpLoginState.ReloginFailed; throw ExceptionHelper.IsolatedException; } } if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { base.MarkAsRecoverableFailure(); } throw base.GenerateException(status, response.StatusDescription, null); } if ((this.m_LoginState != FtpLoginState.LoggedIn) && (entry.Command.IndexOf("PASS") != -1)) { switch (status) { case FtpStatusCode.NeedLoginAccount: case FtpStatusCode.LoggedInProceed: this.m_LoginState = FtpLoginState.LoggedIn; goto Label_017A; } throw base.GenerateException(status, response.StatusDescription, null); } Label_017A: if (entry.HasFlag(CommandStream.PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool flag; CommandStream.PipelineInstruction instruction = this.QueueOrCreateDataConection(entry, response, timeout, ref stream, out flag); if (!flag) { return(instruction); } } switch (status) { case FtpStatusCode.OpeningData: case FtpStatusCode.DataAlreadyOpen: { if (this.m_DataSocket == null) { return(CommandStream.PipelineInstruction.Abort); } if (!entry.HasFlag(CommandStream.PipelineEntryFlags.GiveDataStream)) { base.m_AbortReason = SR.GetString("net_ftp_invalid_status_response", new object[] { status, entry.Command }); return(CommandStream.PipelineInstruction.Abort); } this.TryUpdateContentLength(response.StatusDescription); FtpWebRequest request = (FtpWebRequest)base.m_Request; if (request.MethodInfo.ShouldParseForResponseUri) { this.TryUpdateResponseUri(response.StatusDescription, request); } return(this.QueueOrCreateFtpDataStream(ref stream)); } case FtpStatusCode.LoggedInProceed: this.m_WelcomeMessage.Append(this.StatusLine); break; case FtpStatusCode.ClosingControl: this.m_ExitMessage.Append(response.StatusDescription); base.CloseSocket(); break; case FtpStatusCode.ServerWantsSecureSession: { FtpWebRequest initiatingRequest = (FtpWebRequest)base.m_Request; TlsStream stream2 = new TlsStream(initiatingRequest.RequestUri.Host, base.NetworkStream, initiatingRequest.ClientCertificates, base.Pool.ServicePoint, initiatingRequest, base.m_Async ? initiatingRequest.GetWritingContext().ContextCopy : null); base.NetworkStream = stream2; break; } case FtpStatusCode.FileStatus: { FtpWebRequest request1 = (FtpWebRequest)base.m_Request; if (entry.Command.StartsWith("SIZE ")) { this.m_ContentLength = this.GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { this.m_LastModified = this.GetLastModifiedFrom213Response(response.StatusDescription); } break; } default: if (status == FtpStatusCode.PathnameCreated) { if ((entry.Command == "PWD\r\n") && !entry.HasFlag(CommandStream.PipelineEntryFlags.UserCommand)) { this.m_LoginDirectory = this.GetLoginDirectory(response.StatusDescription); } } else if (entry.Command.IndexOf("CWD") != -1) { this.m_EstablishedServerDirectory = this.m_RequestedServerDirectory; } break; } if (!response.PositiveIntermediate && (base.UsingSecureStream || !(entry.Command == "AUTH TLS\r\n"))) { return(CommandStream.PipelineInstruction.Advance); } return(CommandStream.PipelineInstruction.Reread); }
internal void GetConnection(string host, int port) { if (_isConnected) { throw new InvalidOperationException(SR.SmtpAlreadyConnected); } InitializeConnection(host, port); _responseReader = new SmtpReplyReaderFactory(_networkStream); 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 (_enableSsl) { if (!_serverSupportsStartTls) { // Either TLS is already established or server does not support TLS if (!(_networkStream is TlsStream)) { throw new SmtpException(SR.MailServerDoesNotSupportStartTls); } } StartTlsCommand.Send(this); TlsStream tlsStream = new TlsStream(_networkStream, _tcpClient.Client, host, _clientCertificates); tlsStream.AuthenticateAsClient(); _networkStream = tlsStream; _responseReader = new SmtpReplyReaderFactory(_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); } // 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 - chadmu if (!AuthSupported(_authenticationModules[i])) { continue; } NetworkCredential credential = _credentials.GetCredential(host, 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, _client.TargetName, _channelBindingToken); if (auth == null) { throw new SmtpException(SR.SmtpAuthenticationFailed); } info = AuthCommand.Send(this, auth.Message); if ((int)info.StatusCode == 235) { _authenticationModules[i].CloseContext(this); _isConnected = true; return; } } } } } _isConnected = true; }
/// <summary> /// <para>Cleans up state variables for reuse of the connection</para> /// </summary> protected override void ClearState() { m_ContentLength = -1; m_LastModified = DateTime.MinValue; m_ResponseUri = null; m_DataHandshakeStarted = false; StatusCode = FtpStatusCode.Undefined; StatusLine = null; m_DataSocket = null; m_PassiveEndPoint = null; m_TlsStream = null; base.ClearState(); }
// This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the setting up of a data socket/connection, and of saving off the server responses. protected override PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Command:{entry?.Command} Description:{response?.StatusDescription}"); // null response is not expected if (response == null) return PipelineInstruction.Abort; FtpStatusCode status = (FtpStatusCode)response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) throw new WebException(SR.net_InvalidStatusCode, WebExceptionStatus.ProtocolError); // Update the banner message if any, this is a little hack because the "entry" param is null if (_index == -1) { if (status == FtpStatusCode.SendUserCommand) { _bannerMessage = new StringBuilder(); _bannerMessage.Append(StatusLine); return PipelineInstruction.Advance; } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return PipelineInstruction.Reread; } else throw GenerateException(status, response.StatusDescription, null); } // // Check for the result of our attempt to use UTF8 // if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return PipelineInstruction.Advance; } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER") != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; _index++; } } // // Throw on an error with possible recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status, response.StatusDescription, null); } if (_loginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS") != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) _loginState = FtpLoginState.LoggedIn; else throw GenerateException(status, response.StatusDescription, null); } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) return result; // otheriwse we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (_dataSocket == null) { return PipelineInstruction.Abort; } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { _abortReason = SR.Format(SR.net_ftp_invalid_status_response, status, entry.Command); return PipelineInstruction.Abort; } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription); // Parse out the file name, when it is returned and use it for our ResponseUri FtpWebRequest request = (FtpWebRequest)_request; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription, request); } return QueueOrCreateFtpDataStream(ref stream); } // // Parse responses by status code exclusivelly // // Update welcome message if (status == FtpStatusCode.LoggedInProceed) { _welcomeMessage.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { _exitMessage.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { // If NetworkStream is a TlsStream, then this must be in the async callback // from completing the SSL handshake. // So just let the pipeline continue. if (!(NetworkStream is TlsStream)) { FtpWebRequest request = (FtpWebRequest)_request; TlsStream tlsStream = new TlsStream(NetworkStream, Socket, request.RequestUri.Host, request.ClientCertificates); if (_isAsync) { tlsStream.BeginAuthenticateAsClient(ar => { try { tlsStream.EndAuthenticateAsClient(ar); NetworkStream = tlsStream; this.ContinueCommandPipeline(); } catch (Exception e) { this.CloseSocket(); this.InvokeRequestCallback(e); } }, null); return PipelineInstruction.Pause; } else { tlsStream.AuthenticateAsClient(); NetworkStream = tlsStream; } } } // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { FtpWebRequest request = (FtpWebRequest)_request; if (entry.Command.StartsWith("SIZE ")) { _contentLength = GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { _lastModified = GetLastModifiedFrom213Response(response.StatusDescription); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { _loginDirectory = GetLoginDirectory(response.StatusDescription); } } // Asserting we have some positive response else { // We only use CWD to reset ourselves back to the login directory. if (entry.Command.IndexOf("CWD") != -1) { _establishedServerDirectory = _requestedServerDirectory; } } // Intermediate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return PipelineInstruction.Reread; } return PipelineInstruction.Advance; }
internal static RemoteCertValidationCallback CreateAdapter(ServicePoint serviePoint, TlsStream secureStream, object request) { ServicePoint.HandshakeDoneProcedure procedure = new ServicePoint.HandshakeDoneProcedure(serviePoint, secureStream, request); return new RemoteCertValidationCallback(procedure.CertValidationCallback); }
internal static RemoteCertValidationCallback CreateAdapter(ServicePoint serviePoint, TlsStream secureStream, object request) { ServicePoint.HandshakeDoneProcedure procedure = new ServicePoint.HandshakeDoneProcedure(serviePoint, secureStream, request); return(new RemoteCertValidationCallback(procedure.CertValidationCallback)); }
// Creates a FtpDataStream object, constructs a TLS stream if needed. // In case SSL and ASYNC we delay sigaling the user stream until the handshake is done. private PipelineInstruction QueueOrCreateFtpDataStream(ref Stream stream) { if (_dataSocket == null) throw new InternalException(); // // Re-entered pipeline with completed read on the TlsStream // if (_tlsStream != null) { stream = new FtpDataStream(_tlsStream, (FtpWebRequest)_request, IsFtpDataStreamWriteable()); _tlsStream = null; return PipelineInstruction.GiveStream; } NetworkStream networkStream = new NetworkStream(_dataSocket, true); if (UsingSecureStream) { FtpWebRequest request = (FtpWebRequest)_request; TlsStream tlsStream = new TlsStream(networkStream, _dataSocket, request.RequestUri.Host, request.ClientCertificates); networkStream = tlsStream; if (_isAsync) { _tlsStream = tlsStream; tlsStream.BeginAuthenticateAsClient(s_SSLHandshakeCallback, this); return PipelineInstruction.Pause; } else { tlsStream.AuthenticateAsClient(); } } stream = new FtpDataStream(networkStream, (FtpWebRequest)_request, IsFtpDataStreamWriteable()); return PipelineInstruction.GiveStream; }
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); } } }
private HandshakeDoneProcedure(ServicePoint serviePoint, TlsStream secureStream, object request) { this.m_ServicePoint = serviePoint; this.m_SecureStream = secureStream; this.m_Request = request; }
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; }
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; }
// // This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the seting up of a data socket/connection, and of saving off the server responses // protected override PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + ">" + (entry == null? "null" : entry.Command)); GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + ">" + ((response == null) ? "null" : response.StatusDescription)); // null response is not expected if (response == null) return PipelineInstruction.Abort; FtpStatusCode status = (FtpStatusCode) response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) throw new WebException(SR.GetString(SR.net_InvalidStatusCode), WebExceptionStatus.ProtocolError); // Update the banner message if any, this is a little hack because the "entry" param is null if (m_Index == -1) { if (status == FtpStatusCode.SendUserCommand) { m_BannerMessage = new StringBuilder(); m_BannerMessage.Append(StatusLine); return PipelineInstruction.Advance; } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return PipelineInstruction.Reread; } else throw GenerateException(status,response.StatusDescription, null); } // // Check for the result of our attempt to use UTF8 // Condsider: optimize this for speed (avoid string compare) as that is the only command that may fail // if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return PipelineInstruction.Advance; } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER") != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { m_LoginState = FtpLoginState.LoggedIn; m_Index++; } // The server does not like re-login // (We are logged in already but want to re-login under a different user) else if (status == FtpStatusCode.NotLoggedIn && m_LoginState != FtpLoginState.NotLoggedIn) { m_LoginState = FtpLoginState.ReloginFailed; throw ExceptionHelper.IsolatedException; } } // // Throw on an error with possible recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status,response.StatusDescription, null); } if (m_LoginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS") != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) m_LoginState = FtpLoginState.LoggedIn; else throw GenerateException(status,response.StatusDescription, null); } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) return result; // otheriwse we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (m_DataSocket == null) { // a better diagnostic? return PipelineInstruction.Abort; } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { m_AbortReason = SR.GetString(SR.net_ftp_invalid_status_response, status, entry.Command); return PipelineInstruction.Abort; } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription); // Parse out the file name, when it is returned and use it for our ResponseUri FtpWebRequest request = (FtpWebRequest) m_Request; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription, request); } return QueueOrCreateFtpDataStream(ref stream); } // // Parse responses by status code exclusivelly // //Update welcome message if (status == FtpStatusCode.LoggedInProceed) { m_WelcomeMessage.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { m_ExitMessage.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } #if !FEATURE_PAL // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { FtpWebRequest request = (FtpWebRequest) m_Request; TlsStream tlsStream = new TlsStream(request.RequestUri.Host, NetworkStream, request.ClientCertificates, Pool.ServicePoint, request, m_Async ? request.GetWritingContext().ContextCopy : null); NetworkStream = tlsStream; } #endif // !FEATURE_PAL // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { FtpWebRequest request = (FtpWebRequest) m_Request; if (entry.Command.StartsWith("SIZE ")) { m_ContentLength = GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { m_LastModified = GetLastModifiedFrom213Response(response.StatusDescription); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { m_LoginDirectory = GetLoginDirectory(response.StatusDescription); } } // Asserting we have some positive response else { // We only use CWD to reset ourselves back to the login directory. if (entry.Command.IndexOf("CWD") != -1) { m_EstablishedServerDirectory = m_RequestedServerDirectory; } } // Intermediate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return PipelineInstruction.Reread; } return PipelineInstruction.Advance; }
protected override CommandStream.PipelineInstruction PipelineCallback(CommandStream.PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (response == null) { return CommandStream.PipelineInstruction.Abort; } FtpStatusCode status = (FtpStatusCode) response.Status; if (status != FtpStatusCode.ClosingControl) { this.StatusCode = status; this.StatusLine = response.StatusDescription; } if (response.InvalidStatusCode) { throw new WebException(SR.GetString("net_InvalidStatusCode"), WebExceptionStatus.ProtocolError); } if (base.m_Index == -1) { switch (status) { case FtpStatusCode.SendUserCommand: this.m_BannerMessage = new StringBuilder(); this.m_BannerMessage.Append(this.StatusLine); return CommandStream.PipelineInstruction.Advance; case FtpStatusCode.ServiceTemporarilyNotAvailable: return CommandStream.PipelineInstruction.Reread; } throw base.GenerateException(status, response.StatusDescription, null); } if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { base.Encoding = Encoding.UTF8; } else { base.Encoding = Encoding.Default; } return CommandStream.PipelineInstruction.Advance; } if (entry.Command.IndexOf("USER") != -1) { if (status == FtpStatusCode.LoggedInProceed) { this.m_LoginState = FtpLoginState.LoggedIn; base.m_Index++; } else if ((status == FtpStatusCode.NotLoggedIn) && (this.m_LoginState != FtpLoginState.NotLoggedIn)) { this.m_LoginState = FtpLoginState.ReloginFailed; throw ExceptionHelper.IsolatedException; } } if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { base.MarkAsRecoverableFailure(); } throw base.GenerateException(status, response.StatusDescription, null); } if ((this.m_LoginState != FtpLoginState.LoggedIn) && (entry.Command.IndexOf("PASS") != -1)) { switch (status) { case FtpStatusCode.NeedLoginAccount: case FtpStatusCode.LoggedInProceed: this.m_LoginState = FtpLoginState.LoggedIn; goto Label_017A; } throw base.GenerateException(status, response.StatusDescription, null); } Label_017A: if (entry.HasFlag(CommandStream.PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool flag; CommandStream.PipelineInstruction instruction = this.QueueOrCreateDataConection(entry, response, timeout, ref stream, out flag); if (!flag) { return instruction; } } switch (status) { case FtpStatusCode.OpeningData: case FtpStatusCode.DataAlreadyOpen: { if (this.m_DataSocket == null) { return CommandStream.PipelineInstruction.Abort; } if (!entry.HasFlag(CommandStream.PipelineEntryFlags.GiveDataStream)) { base.m_AbortReason = SR.GetString("net_ftp_invalid_status_response", new object[] { status, entry.Command }); return CommandStream.PipelineInstruction.Abort; } this.TryUpdateContentLength(response.StatusDescription); FtpWebRequest request = (FtpWebRequest) base.m_Request; if (request.MethodInfo.ShouldParseForResponseUri) { this.TryUpdateResponseUri(response.StatusDescription, request); } return this.QueueOrCreateFtpDataStream(ref stream); } case FtpStatusCode.LoggedInProceed: this.m_WelcomeMessage.Append(this.StatusLine); break; case FtpStatusCode.ClosingControl: this.m_ExitMessage.Append(response.StatusDescription); base.CloseSocket(); break; case FtpStatusCode.ServerWantsSecureSession: { FtpWebRequest initiatingRequest = (FtpWebRequest) base.m_Request; TlsStream stream2 = new TlsStream(initiatingRequest.RequestUri.Host, base.NetworkStream, initiatingRequest.ClientCertificates, base.Pool.ServicePoint, initiatingRequest, base.m_Async ? initiatingRequest.GetWritingContext().ContextCopy : null); base.NetworkStream = stream2; break; } case FtpStatusCode.FileStatus: { FtpWebRequest request1 = (FtpWebRequest) base.m_Request; if (entry.Command.StartsWith("SIZE ")) { this.m_ContentLength = this.GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { this.m_LastModified = this.GetLastModifiedFrom213Response(response.StatusDescription); } break; } default: if (status == FtpStatusCode.PathnameCreated) { if ((entry.Command == "PWD\r\n") && !entry.HasFlag(CommandStream.PipelineEntryFlags.UserCommand)) { this.m_LoginDirectory = this.GetLoginDirectory(response.StatusDescription); } } else if (entry.Command.IndexOf("CWD") != -1) { this.m_EstablishedServerDirectory = this.m_RequestedServerDirectory; } break; } if (!response.PositiveIntermediate && (base.UsingSecureStream || !(entry.Command == "AUTH TLS\r\n"))) { return CommandStream.PipelineInstruction.Advance; } return CommandStream.PipelineInstruction.Reread; }
internal RemoteCertValidationCallback SetupHandshakeDoneProcedure(TlsStream secureStream, object request) { return(HandshakeDoneProcedure.CreateAdapter(this, secureStream, request)); }
private void CompleteConnection(bool async, HttpWebRequest request) { GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection", "async:" + async.ToString() + " request:" + ValidationHelper.HashString(request)); GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection"); WebExceptionStatus ws = WebExceptionStatus.ConnectFailure; // // From now on the request.SetRequestSubmitDone must be called or it may hang // For a [....] request the write side reponse windowwas opened in HttpWebRequest.SubmitRequest if (request.Async) request.OpenWriteSideResponseWindow(); try { try { #if !FEATURE_PAL if (request.Address.Scheme == Uri.UriSchemeHttps) { TlsStream tlsStream = new TlsStream(request.GetRemoteResourceUri().IdnHost, NetworkStream, request.ClientCertificates, ServicePoint, request, request.Async ? request.GetConnectingContext().ContextCopy : null); NetworkStream = tlsStream; } #endif ws = WebExceptionStatus.Success; } catch { // The TLS stream could not be created. Close the current non-TLS stream immediately // to prevent any future use of it. Due to race conditions, the error handling will sometimes // try to write (flush) out some of the HTTP headers to the stream as it is closing down the failed // HttpWebRequest. This would cause plain text to go on the wire even though the stream should // have been TLS encrypted. NetworkStream.Close(); throw; } finally { // // There is a ---- with Abort so TlsStream ctor may throw. // SetRequestSubmitDone will deal with this kind of errors. // m_ReadState = ReadState.Start; ClearReaderState(); request.SetRequestSubmitDone(new ConnectStream(this, request)); } } catch (Exception exception) { if (m_InnerException == null) m_InnerException = exception; WebException webException = exception as WebException; if (webException != null) { ws = webException.Status; } } if (ws != WebExceptionStatus.Success) { ConnectionReturnResult returnResult = null; HandleError(false, false, ws, ref returnResult); ConnectionReturnResult.SetResponses(returnResult); if (Logging.On) Logging.PrintError(Logging.Web, this, "CompleteConnection", "on error"); GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection", "on error"); } else { GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection"); } }
private void CompleteConnection(bool async, HttpWebRequest request) { WebExceptionStatus connectFailure = WebExceptionStatus.ConnectFailure; if (request.Async) { request.OpenWriteSideResponseWindow(); } try { try { if (request.Address.Scheme == Uri.UriSchemeHttps) { TlsStream stream = new TlsStream(request.GetRemoteResourceUri().Host, base.NetworkStream, request.ClientCertificates, this.ServicePoint, request, request.Async ? request.GetConnectingContext().ContextCopy : null); base.NetworkStream = stream; } } finally { this.m_ReadState = ReadState.Start; this.ClearReaderState(); request.SetRequestSubmitDone(new ConnectStream(this, request)); connectFailure = WebExceptionStatus.Success; } } catch (Exception exception) { if (this.m_InnerException == null) { this.m_InnerException = exception; } WebException exception2 = exception as WebException; if (exception2 != null) { connectFailure = exception2.Status; } } if (connectFailure != WebExceptionStatus.Success) { ConnectionReturnResult returnResult = null; this.HandleError(false, false, connectFailure, ref returnResult); ConnectionReturnResult.SetResponses(returnResult); } }
protected override void ProcessAsTls1() { ProcessAsSsl3 (); // If applicable add the "server_name" extension to the hello message // http://www.ietf.org/rfc/rfc3546.txt string host = Context.ClientSettings.TargetHost; // Our TargetHost might be an address (not a host *name*) - see bug #8553 // RFC3546 -> Literal IPv4 and IPv6 addresses are not permitted in "HostName". IPAddress addr; if (IPAddress.TryParse (host, out addr)) return; TlsStream extensions = new TlsStream (); byte[] server_name = System.Text.Encoding.UTF8.GetBytes (host); extensions.Write ((short) 0x0000); // ExtensionType: server_name (0) extensions.Write ((short) (server_name.Length + 5)); // ServerNameList (length) extensions.Write ((short) (server_name.Length + 3)); // ServerName (length) extensions.Write ((byte) 0x00); // NameType: host_name (0) extensions.Write ((short) server_name.Length); // HostName (length) extensions.Write (server_name); // HostName (UTF8) this.Write ((short) extensions.Length); this.Write (extensions.ToArray ()); }
private static void SendStartTlsCallback(IAsyncResult result) { if (!result.CompletedSynchronously) { SmtpConnection.ConnectAndHandshakeAsyncResult asyncState = (SmtpConnection.ConnectAndHandshakeAsyncResult) result.AsyncState; try { StartTlsCommand.EndSend(result); TlsStream stream = new TlsStream(asyncState.connection.pooledStream.ServicePoint.Host, asyncState.connection.pooledStream.NetworkStream, asyncState.connection.ClientCertificates, asyncState.connection.pooledStream.ServicePoint, asyncState.connection.client, asyncState.m_OuterResult.ContextCopy); asyncState.connection.pooledStream.NetworkStream = stream; asyncState.connection.responseReader = new SmtpReplyReaderFactory(asyncState.connection.pooledStream.NetworkStream); asyncState.SendEHello(); } catch (Exception exception) { asyncState.InvokeCallback(exception); } } }
internal RemoteCertValidationCallback SetupHandshakeDoneProcedure(TlsStream secureStream, object request) { return HandshakeDoneProcedure.CreateAdapter(this, secureStream, request); }
internal RemoteCertValidationCallback SetupHandshakeDoneProcedure(TlsStream secureStream, Object request) { // Use a private adapter to connect tlsstream and this service point return HandshakeDoneProcedure.CreateAdapter(this, secureStream, request); }
internal void GetConnection(ServicePoint servicePoint) { if (this.isConnected) { throw new InvalidOperationException(SR.GetString("SmtpAlreadyConnected")); } if (Logging.On) { Logging.Associate(Logging.Web, this, servicePoint); } this.connectionPool = ConnectionPoolManager.GetConnectionPool(servicePoint, "", m_CreateConnectionCallback); PooledStream pooledStream = this.connectionPool.GetConnection(this, null, this.Timeout); while ((((SmtpPooledStream) pooledStream).creds != null) && (((SmtpPooledStream) pooledStream).creds != this.credentials)) { this.connectionPool.PutConnection(pooledStream, pooledStream.Owner, this.Timeout, false); pooledStream = this.connectionPool.GetConnection(this, null, this.Timeout); } if (Logging.On) { Logging.Associate(Logging.Web, this, pooledStream); } lock (this) { this.pooledStream = pooledStream; } ((SmtpPooledStream) pooledStream).creds = this.credentials; this.responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); pooledStream.UpdateLifetime(); if (((SmtpPooledStream) pooledStream).previouslyUsed) { this.isConnected = true; } else { LineInfo info = this.responseReader.GetNextReplyReader().ReadLine(); if (info.StatusCode != SmtpStatusCode.ServiceReady) { throw new SmtpException(info.StatusCode, info.Line, true); } try { this.extensions = EHelloCommand.Send(this, this.client.clientDomain); this.ParseExtensions(this.extensions); } catch (SmtpException exception) { if ((exception.StatusCode != SmtpStatusCode.CommandUnrecognized) && (exception.StatusCode != SmtpStatusCode.CommandNotImplemented)) { throw exception; } HelloCommand.Send(this, this.client.clientDomain); this.supportedAuth = SupportedAuth.Login; } if (this.enableSsl) { if (!this.serverSupportsStartTls && !(pooledStream.NetworkStream is TlsStream)) { throw new SmtpException(SR.GetString("MailServerDoesNotSupportStartTls")); } StartTlsCommand.Send(this); TlsStream stream2 = new TlsStream(servicePoint.Host, pooledStream.NetworkStream, this.clientCertificates, servicePoint, this.client, null); pooledStream.NetworkStream = stream2; this.channelBindingToken = stream2.GetChannelBinding(ChannelBindingKind.Unique); this.responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); this.extensions = EHelloCommand.Send(this, this.client.clientDomain); this.ParseExtensions(this.extensions); } if (this.credentials != null) { for (int i = 0; i < this.authenticationModules.Length; i++) { Authorization authorization; if (this.AuthSupported(this.authenticationModules[i])) { NetworkCredential credential = this.credentials.GetCredential(servicePoint.Host, servicePoint.Port, this.authenticationModules[i].AuthenticationType); if (credential != null) { authorization = this.SetContextAndTryAuthenticate(this.authenticationModules[i], credential, null); if ((authorization != null) && (authorization.Message != null)) { info = AuthCommand.Send(this, this.authenticationModules[i].AuthenticationType, authorization.Message); if (info.StatusCode != SmtpStatusCode.CommandParameterNotImplemented) { goto Label_0363; } } } } continue; Label_02F2: authorization = this.authenticationModules[i].Authenticate(info.Line, null, this, this.client.TargetName, this.channelBindingToken); if (authorization == null) { throw new SmtpException(SR.GetString("SmtpAuthenticationFailed")); } info = AuthCommand.Send(this, authorization.Message); if (info.StatusCode == ((SmtpStatusCode) 0xeb)) { this.authenticationModules[i].CloseContext(this); this.isConnected = true; return; } Label_0363: if (info.StatusCode == ((SmtpStatusCode) 0x14e)) { goto Label_02F2; } } } this.isConnected = true; } }
// Creates a FtpDataStream object, constructs a TLS stream if needed. // In case SSL we issue a 0 bytes read on that stream to force handshake. // In case SSL and ASYNC we delay sigaling the user stream until the handshake is done. // private PipelineInstruction QueueOrCreateFtpDataStream(ref Stream stream) { if (m_DataSocket == null) throw new InternalException(); // // Re-entered pipeline with completed read on the TlsStream // if (this.m_TlsStream != null ) { stream = new FtpDataStream(this.m_TlsStream, (FtpWebRequest) m_Request, IsFtpDataStreamWriteable()); this.m_TlsStream = null; return PipelineInstruction.GiveStream; } NetworkStream networkStream = new NetworkStream(m_DataSocket, true); #if !FEATURE_PAL if (UsingSecureStream) { FtpWebRequest request = (FtpWebRequest) m_Request; TlsStream tlsStream = new TlsStream(request.RequestUri.Host, networkStream, request.ClientCertificates, Pool.ServicePoint, request, m_Async ? request.GetWritingContext().ContextCopy : null); networkStream = tlsStream; if (m_Async) { this.m_TlsStream = tlsStream; LazyAsyncResult handshakeResult = new LazyAsyncResult(null, this, m_SSLHandshakeCallback); tlsStream.ProcessAuthentication(handshakeResult); return PipelineInstruction.Pause; } else { tlsStream.ProcessAuthentication(null); } } #endif // !FEATURE_PAL stream = new FtpDataStream(networkStream, (FtpWebRequest) m_Request, IsFtpDataStreamWriteable()); return PipelineInstruction.GiveStream; }
// This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the setting up of a data socket/connection, and of saving off the server responses. protected override PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Command:{entry?.Command} Description:{response?.StatusDescription}"); } // null response is not expected if (response == null) { return(PipelineInstruction.Abort); } FtpStatusCode status = (FtpStatusCode)response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) { throw new WebException(SR.net_InvalidStatusCode, WebExceptionStatus.ProtocolError); } // Update the banner message if any, this is a little hack because the "entry" param is null if (_index == -1) { if (status == FtpStatusCode.SendUserCommand) { _bannerMessage = new StringBuilder(); _bannerMessage.Append(StatusLine); return(PipelineInstruction.Advance); } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return(PipelineInstruction.Reread); } else { throw GenerateException(status, response.StatusDescription, null); } } // // Check for the result of our attempt to use UTF8 // if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return(PipelineInstruction.Advance); } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER") != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; _index++; } } // // Throw on an error with possible recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status, response.StatusDescription, null); } if (_loginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS") != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; } else { throw GenerateException(status, response.StatusDescription, null); } } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) { return(result); } // otherwise we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (_dataSocket == null) { return(PipelineInstruction.Abort); } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { _abortReason = SR.Format(SR.net_ftp_invalid_status_response, status, entry.Command); return(PipelineInstruction.Abort); } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription); // Parse out the file name, when it is returned and use it for our ResponseUri FtpWebRequest request = (FtpWebRequest)_request; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription, request); } return(QueueOrCreateFtpDataStream(ref stream)); } // // Parse responses by status code exclusivelly // // Update welcome message if (status == FtpStatusCode.LoggedInProceed) { _welcomeMessage.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { _exitMessage.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { // If NetworkStream is a TlsStream, then this must be in the async callback // from completing the SSL handshake. // So just let the pipeline continue. if (!(NetworkStream is TlsStream)) { FtpWebRequest request = (FtpWebRequest)_request; TlsStream tlsStream = new TlsStream(NetworkStream, Socket, request.RequestUri.Host, request.ClientCertificates); if (_isAsync) { tlsStream.BeginAuthenticateAsClient(ar => { try { tlsStream.EndAuthenticateAsClient(ar); NetworkStream = tlsStream; this.ContinueCommandPipeline(); } catch (Exception e) { this.CloseSocket(); this.InvokeRequestCallback(e); } }, null); return(PipelineInstruction.Pause); } else { tlsStream.AuthenticateAsClient(); NetworkStream = tlsStream; } } } // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { FtpWebRequest request = (FtpWebRequest)_request; if (entry.Command.StartsWith("SIZE ")) { _contentLength = GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { _lastModified = GetLastModifiedFrom213Response(response.StatusDescription); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { _loginDirectory = GetLoginDirectory(response.StatusDescription); } } // Asserting we have some positive response else { // We only use CWD to reset ourselves back to the login directory. if (entry.Command.IndexOf("CWD") != -1) { _establishedServerDirectory = _requestedServerDirectory; } } // Intermediate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return(PipelineInstruction.Reread); } return(PipelineInstruction.Advance); }
private bool SendStartTls() { IAsyncResult result = StartTlsCommand.BeginSend(this.connection, new AsyncCallback(SmtpConnection.ConnectAndHandshakeAsyncResult.SendStartTlsCallback), this); if (result.CompletedSynchronously) { StartTlsCommand.EndSend(result); TlsStream stream = new TlsStream(this.connection.pooledStream.ServicePoint.Host, this.connection.pooledStream.NetworkStream, this.connection.ClientCertificates, this.connection.pooledStream.ServicePoint, this.connection.client, this.m_OuterResult.ContextCopy); this.connection.pooledStream.NetworkStream = stream; this.connection.responseReader = new SmtpReplyReaderFactory(this.connection.pooledStream.NetworkStream); this.SendEHello(); return true; } return false; }