Inheritance: NetworkStream, IDisposable
        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();
 }
Beispiel #3
0
        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();
        }
Beispiel #4
0
		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);
		}
Beispiel #5
0
        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);
        }
Beispiel #6
0
        //    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);
        }
Beispiel #9
0
        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;
        }
Beispiel #10
0
        /// <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();
        }
Beispiel #11
0
        //    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));
 }
Beispiel #14
0
        //    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;
        }
Beispiel #19
0
        //
        //    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);
     }
 }
Beispiel #24
0
        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();
        }
Beispiel #25
0
		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 HandshakeDoneProcedure(ServicePoint serviePoint, TlsStream secureStream, object request)
 {
     this.m_ServicePoint = serviePoint;
     this.m_SecureStream = secureStream;
     this.m_Request = request;
 }
 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;
     }
 }
Beispiel #31
0
        //    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;
        }
Beispiel #32
0
        //    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;
 }