Contains the parsed status line from the server
 internal ReceiveState(CommandStream connection)
 {
     this.Connection = connection;
     this.Resp = new ResponseDescription();
     this.Buffer = new byte[0x400];
     this.ValidThrough = 0;
 }
Beispiel #2
0
 protected void InitCommandPipeline(WebRequest request, PipelineEntry[] commands, bool isAsync)
 {
     _commands = commands;
     _index    = 0;
     _request  = request;
     _aborted  = false;
     _doRead   = true;
     _doSend   = true;
     _currentResponseDescription = null;
     _isAsync            = isAsync;
     _recoverableFailure = false;
     _abortReason        = string.Empty;
 }
 protected void InitCommandPipeline(WebRequest request, PipelineEntry [] commands, bool async)
 {
     m_Commands = commands;
     m_Index    = 0;
     m_Request  = request;
     m_Aborted  = false;
     m_DoRead   = true;
     m_DoSend   = true;
     m_CurrentResponseDescription = null;
     m_Async = async;
     m_RecoverableFailure = false;
     m_AbortReason        = string.Empty;
 }
        //
        private bool PostSendCommandProcessing(ref Stream stream)
        {
/*
** I don;t see how this code can be still relevant, remove it of no problems observed **
**
**          //
**          // This is a general race condition in [....] mode, if the server returns an error
**          // after we open the data connection, we will be off reading the data connection,
**          // and not the control connection. The best we can do is try to poll, and in the
**          // the worst case, we will timeout on establishing the data connection.
**          //
**          if (!m_DoRead && !m_Async) {
**              m_DoRead = Poll(100 * 1000, SelectMode.SelectRead);   // Poll is in Microseconds.
**          }
*/
            if (m_DoRead)
            {
                // In async case, The next call can actually result in a
                // series of synchronous completions that eventually close
                // the connection. So we need to save the members that
                // we need to access, since they may not be valid after the
                // next call returns
                bool            async    = m_Async;
                int             index    = m_Index;
                PipelineEntry[] commands = m_Commands;

                try {
                    ResponseDescription response = ReceiveCommandResponse();
                    if (async)
                    {
                        return(true);
                    }
                    m_CurrentResponseDescription = response;
                } catch {
                    // If we get an exception on the QUIT command (which is
                    // always the last command), ignore the final exception
                    // and continue with the pipeline regardlss of [....]/async
                    if (index < 0 || index >= commands.Length ||
                        commands[index].Command != "QUIT\r\n")
                    {
                        throw;
                    }
                }
            }
            return(PostReadCommandProcessing(ref stream));
        }
Beispiel #5
0
        private bool PostSendCommandProcessing(ref Stream stream)
        {
            if (_doRead)
            {
                // In async case, the next call can actually result in a
                // series of synchronous completions that eventually close
                // the connection. So we need to save the members that
                // we need to access, since they may not be valid after the
                // next call returns
                bool            isAsync  = _isAsync;
                int             index    = _index;
                PipelineEntry[] commands = _commands;

                try
                {
                    ResponseDescription response = ReceiveCommandResponse();
                    if (isAsync)
                    {
                        return(true);
                    }
                    _currentResponseDescription = response;
                }
                catch
                {
                    // If we get an exception on the QUIT command (which is
                    // always the last command), ignore the final exception
                    // and continue with the pipeline regardlss of sync/async
                    if (index < 0 || index >= commands.Length ||
                        commands[index].Command != "QUIT\r\n")
                    {
                        throw;
                    }
                }
            }
            return(PostReadCommandProcessing(ref stream));
        }
 /// <summary>
 /// This function is called a derived class to determine whether a response is valid, and when it is complete.
 /// </summary>
 protected virtual bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength)
 {
     return(false);
 }
 protected override bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength)
 {
     if (response.StatusBuffer.Length >= 4)
     {
         string str = response.StatusBuffer.ToString();
         if (response.Status == -1)
         {
             if ((!char.IsDigit(str[0]) || !char.IsDigit(str[1])) || (!char.IsDigit(str[2]) || ((str[3] != ' ') && (str[3] != '-'))))
             {
                 return false;
             }
             response.StatusCodeString = str.Substring(0, 3);
             response.Status = Convert.ToInt16(response.StatusCodeString, NumberFormatInfo.InvariantInfo);
             if (str[3] == '-')
             {
                 response.Multiline = true;
             }
         }
         int num = 0;
         while ((num = str.IndexOf("\r\n", validThrough)) != -1)
         {
             int startIndex = validThrough;
             validThrough = num + 2;
             if (!response.Multiline)
             {
                 completeLength = validThrough;
                 return true;
             }
             if (((str.Length > (startIndex + 4)) && (str.Substring(startIndex, 3) == response.StatusCodeString)) && (str[startIndex + 3] == ' '))
             {
                 completeLength = validThrough;
                 return true;
             }
         }
     }
     return true;
 }
Beispiel #8
0
 /// <summary>
 /// This function is implemented in a derived class to determine whether a response is valid, and when it is complete.
 /// </summary>
 protected virtual bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength)
 {
     return false;
 }
Beispiel #9
0
        private bool PostReadCommandProcessing(ref Stream stream)
        {
            if (_index >= _commands.Length)
                return false;

            // Set up front to prevent a race condition on result == PipelineInstruction.Pause
            _doSend = false;
            _doRead = false;

            PipelineInstruction result;
            PipelineEntry entry;
            if (_index == -1)
                entry = null;
            else
                entry = _commands[_index];

            // Final QUIT command may get exceptions since the connection 
            // may be already closed by the server. So there is no response 
            // to process, just advance the pipeline to continue.
            if (_currentResponseDescription == null && entry.Command == "QUIT\r\n")
                result = PipelineInstruction.Advance;
            else
                result = PipelineCallback(entry, _currentResponseDescription, false, ref stream);

            if (result == PipelineInstruction.Abort)
            {
                Exception exception;
                if (_abortReason != string.Empty)
                    exception = new WebException(_abortReason);
                else
                    exception = GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
                Abort(exception);
                throw exception;
            }
            else if (result == PipelineInstruction.Advance)
            {
                _currentResponseDescription = null;
                _doSend = true;
                _doRead = true;
                _index++;
            }
            else if (result == PipelineInstruction.Pause)
            {
                // PipelineCallback did an async operation and will have to re-enter again.
                // Hold on for now.
                return true;
            }
            else if (result == PipelineInstruction.GiveStream)
            {
                // We will have another response coming, don't send
                _currentResponseDescription = null;
                _doRead = true;
                if (_isAsync)
                {
                    // If they block in the requestcallback we should still continue the pipeline
                    ContinueCommandPipeline();
                    InvokeRequestCallback(stream);
                }
                return true;
            }
            else if (result == PipelineInstruction.Reread)
            {
                // Another response is expected after this one
                _currentResponseDescription = null;
                _doRead = true;
            }
            return false;
        }
Beispiel #10
0
 protected void InitCommandPipeline(WebRequest request, PipelineEntry[] commands, bool isAsync)
 {
     _commands = commands;
     _index = 0;
     _request = request;
     _aborted = false;
     _doRead = true;
     _doSend = true;
     _currentResponseDescription = null;
     _isAsync = isAsync;
     _recoverableFailure = false;
     _abortReason = string.Empty;
 }
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;
        }
Beispiel #12
0
        //
        private bool PostSendCommandProcessing(ref Stream stream)
        {
/*
            ** I don;t see how this code can be still relevant, remove it of no problems observed **

            //
            // This is a general race condition in Sync mode, if the server returns an error
            // after we open the data connection, we will be off reading the data connection,
            // and not the control connection. The best we can do is try to poll, and in the
            // the worst case, we will timeout on establishing the data connection.
            //
            if (!m_DoRead && !m_Async) {
                m_DoRead = Poll(100 * 1000, SelectMode.SelectRead);   // Poll is in Microseconds.
            }
*/
            if (m_DoRead)
            {
                // In async case, The next call can actually result in a
                // series of synchronous completions that eventually close
                // the connection. So we need to save the members that 
                // we need to access, since they may not be valid after the 
                // next call returns
                bool async               = m_Async;
                int index                = m_Index;
                PipelineEntry[] commands = m_Commands;

                try {
                    ResponseDescription response = ReceiveCommandResponse();
                    if (async) {
                        return true;
                    }
                    m_CurrentResponseDescription = response;
                } catch {
                    // If we get an exception on the QUIT command (which is 
                    // always the last command), ignore the final exception
                    // and continue with the pipeline regardlss of sync/async
                    if (index < 0 || index >= commands.Length ||
                        commands[index].Command != "QUIT\r\n")
                        throw;
                }
            }
            return PostReadCommandProcessing(ref stream);
        }
        private bool PostReadCommandProcessing(ref Stream stream)
        {
            if (this.m_Index < this.m_Commands.Length)
            {
                PipelineInstruction advance;
                PipelineEntry entry;
                this.m_DoSend = false;
                this.m_DoRead = false;
                if (this.m_Index == -1)
                {
                    entry = null;
                }
                else
                {
                    entry = this.m_Commands[this.m_Index];
                }
                if ((this.m_CurrentResponseDescription == null) && (entry.Command == "QUIT\r\n"))
                {
                    advance = PipelineInstruction.Advance;
                }
                else
                {
                    advance = this.PipelineCallback(entry, this.m_CurrentResponseDescription, false, ref stream);
                }
                switch (advance)
                {
                    case PipelineInstruction.Advance:
                        this.m_CurrentResponseDescription = null;
                        this.m_DoSend = true;
                        this.m_DoRead = true;
                        this.m_Index++;
                        break;

                    case PipelineInstruction.Pause:
                        return true;

                    case PipelineInstruction.Abort:
                        Exception exception;
                        if (this.m_AbortReason != string.Empty)
                        {
                            exception = new WebException(this.m_AbortReason);
                        }
                        else
                        {
                            exception = this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                        }
                        this.Abort(exception);
                        throw exception;

                    case PipelineInstruction.GiveStream:
                        this.m_CurrentResponseDescription = null;
                        this.m_DoRead = true;
                        if (this.m_Async)
                        {
                            this.ContinueCommandPipeline();
                            this.InvokeRequestCallback(stream);
                        }
                        return true;

                    case PipelineInstruction.Reread:
                        this.m_CurrentResponseDescription = null;
                        this.m_DoRead = true;
                        break;
                }
            }
            return false;
        }
Beispiel #14
0
        private PipelineInstruction QueueOrCreateDataConection(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream, out bool isSocketReady)
        {
            isSocketReady = false;
            if (m_DataHandshakeStarted)
            {
                isSocketReady = true;
                return PipelineInstruction.Pause; //if we already started then this is re-entering into the callback where we proceed with the stream
            }

            m_DataHandshakeStarted = true;

            // handle passive responses by parsing the port and later doing a Connect(...)
            bool isPassive = false;
            int port = -1;
            if (entry.Command == "PASV\r\n" || entry.Command == "EPSV\r\n")
            {
                if (!response.PositiveCompletion)
                {
                    m_AbortReason = SR.GetString(SR.net_ftp_server_failed_passive, response.Status);
                    return PipelineInstruction.Abort;
                }
                if (entry.Command == "PASV\r\n")
                {
                    port = GetPortV4(response.StatusDescription);
                } else {
                    port = GetPortV6(response.StatusDescription);
                }

                isPassive = true;
            }

            new SocketPermission(PermissionState.Unrestricted).Assert();

            try {
                if (isPassive)
                {
                    GlobalLog.Assert(port != -1, "FtpControlStream#{0}|'port' not set.", ValidationHelper.HashString(this));

                    try {
                        m_DataSocket = CreateFtpDataSocket((FtpWebRequest)m_Request, Socket);
                    } catch (ObjectDisposedException) {
                        throw ExceptionHelper.RequestAbortedException;
                    }

                    IPEndPoint localEndPoint = new IPEndPoint(((IPEndPoint)Socket.LocalEndPoint).Address, 0);
                    m_DataSocket.Bind(localEndPoint);

                    m_PassiveEndPoint = new IPEndPoint(ServerAddress, port);
                }

                PipelineInstruction result;

                if (m_PassiveEndPoint != null)
                {
                    IPEndPoint passiveEndPoint = m_PassiveEndPoint;
                    m_PassiveEndPoint = null;
                    GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + "starting Connect()");
                    if (m_Async)
                    {
                        m_DataSocket.BeginConnect(passiveEndPoint, m_ConnectCallbackDelegate, this);
                        result = PipelineInstruction.Pause;
                    }
                    else
                    {
                        m_DataSocket.Connect(passiveEndPoint);
                        result = PipelineInstruction.Advance; // for passive mode we end up going to the next command
                    }
                }
                else
                {
                    GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + "starting Accept()");
                    if (m_Async)
                    {
                        m_DataSocket.BeginAccept(m_AcceptCallbackDelegate, this);
                        result = PipelineInstruction.Pause;
                    }
                    else
                    {
                        Socket listenSocket = m_DataSocket;
                        try {
                            m_DataSocket = m_DataSocket.Accept();
                            if (!ServerAddress.Equals(((IPEndPoint)m_DataSocket.RemoteEndPoint).Address))
                            {
                                m_DataSocket.Close();
                                throw new WebException(SR.GetString(SR.net_ftp_active_address_different), WebExceptionStatus.ProtocolError);
                            }
                            isSocketReady = true;   // for active mode we end up creating a stream before advancing the pipeline
                            result = PipelineInstruction.Pause;
                        } finally {
                            listenSocket.Close();
                        }
                    }
                }
                return result;
            } finally {
                SocketPermission.RevertAssert();
            }
        }
Beispiel #15
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;
        }
        //
        //    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);
                
            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 possibe 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
                if (status == FtpStatusCode.OpeningData)
                {
                    FtpWebRequest request = (FtpWebRequest) m_Request;
                    if (request.MethodInfo.ShouldParseForResponseUri)
                    {
                        TryUpdateResponseUri(response.StatusDescription, request);
                    }
                }

                return QueueOrCreateFtpDataStream(ref stream);
            }


            //
            // Parse responses by status code exclusivelly
            //

            //Update our command list if we have an alias
            if (status == FtpStatusCode.LoggedInProceed)
            {
                if(StatusLine.ToLower(CultureInfo.InvariantCulture).IndexOf("alias") > 0){
                    //find start of alias
                    //skip first status code
                    int i = StatusLine.IndexOf("230-",3);
                    if(i > 0)
                    {
                        i+=4;
                        //eat white space
                        while(i<StatusLine.Length && StatusLine[i] == ' '){i++;}

                        //not eol
                        if (i <StatusLine.Length) {
                            //get end of alias
                            int j = StatusLine.IndexOf(' ',i);
                            if(j<0) j=StatusLine.Length;

                            m_Alias = StatusLine.Substring(i,j-i);

                            if (!m_IsRootPath) {
                                //update command list
                                for (i=0;i<m_Commands.Length;i++){
                                    if(m_Commands[i].Command.IndexOf("CWD") == 0) {
                                        string path = m_Alias+m_NewServerPath;
                                        m_Commands[i] = new PipelineEntry(FormatFtpCommand("CWD", path));
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                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();
            }
            // 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);
                    if (!m_IsRootPath &&
                        m_LoginDirectory != "\\" &&
                        m_LoginDirectory != "/" &&
                        m_Alias == null)
                    {
                        //update command list
                        for (int i=0;i<m_Commands.Length;i++){
                            if(m_Commands[i].Command.IndexOf("CWD") == 0) {
                                string path = m_LoginDirectory+m_NewServerPath;
                                m_Commands[i] = new PipelineEntry(FormatFtpCommand("CWD", path));
                                break;
                            }
                        }
                    }
                }
            }
            // Asserting we have some positive response
            else
            {
                // OR update the current path (optimize this pls)
                 if (entry.Command.IndexOf("CWD") != -1)
                {
                    m_PreviousServerPath = m_NewServerPath;
                }
            }

            // Intermidate responses require rereading
            if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n"))
            {
                return PipelineInstruction.Reread;
            }

            return PipelineInstruction.Advance;
        }
 private CommandStream.PipelineInstruction QueueOrCreateDataConection(CommandStream.PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream, out bool isSocketReady)
 {
     CommandStream.PipelineInstruction instruction2;
     isSocketReady = false;
     if (this.m_DataHandshakeStarted)
     {
         isSocketReady = true;
         return CommandStream.PipelineInstruction.Pause;
     }
     this.m_DataHandshakeStarted = true;
     bool flag = false;
     int port = -1;
     if ((entry.Command == "PASV\r\n") || (entry.Command == "EPSV\r\n"))
     {
         if (!response.PositiveCompletion)
         {
             base.m_AbortReason = SR.GetString("net_ftp_server_failed_passive", new object[] { response.Status });
             return CommandStream.PipelineInstruction.Abort;
         }
         if (entry.Command == "PASV\r\n")
         {
             port = this.GetPortV4(response.StatusDescription);
         }
         else
         {
             port = this.GetPortV6(response.StatusDescription);
         }
         flag = true;
     }
     new SocketPermission(PermissionState.Unrestricted).Assert();
     try
     {
         CommandStream.PipelineInstruction pause;
         if (flag)
         {
             try
             {
                 this.m_DataSocket = this.CreateFtpDataSocket((FtpWebRequest) base.m_Request, base.Socket);
             }
             catch (ObjectDisposedException)
             {
                 throw ExceptionHelper.RequestAbortedException;
             }
             this.m_PassiveEndPoint = new IPEndPoint(base.ServerAddress, port);
         }
         if (this.m_PassiveEndPoint != null)
         {
             IPEndPoint passiveEndPoint = this.m_PassiveEndPoint;
             this.m_PassiveEndPoint = null;
             if (base.m_Async)
             {
                 this.m_DataSocket.BeginConnect(passiveEndPoint, m_ConnectCallbackDelegate, this);
                 pause = CommandStream.PipelineInstruction.Pause;
             }
             else
             {
                 this.m_DataSocket.Connect(passiveEndPoint);
                 pause = CommandStream.PipelineInstruction.Advance;
             }
         }
         else if (base.m_Async)
         {
             this.m_DataSocket.BeginAccept(m_AcceptCallbackDelegate, this);
             pause = CommandStream.PipelineInstruction.Pause;
         }
         else
         {
             Socket dataSocket = this.m_DataSocket;
             try
             {
                 this.m_DataSocket = this.m_DataSocket.Accept();
                 if (!base.ServerAddress.Equals(((IPEndPoint) this.m_DataSocket.RemoteEndPoint).Address))
                 {
                     this.m_DataSocket.Close();
                     throw new WebException(SR.GetString("net_ftp_active_address_different"), WebExceptionStatus.ProtocolError);
                 }
                 isSocketReady = true;
                 pause = CommandStream.PipelineInstruction.Pause;
             }
             finally
             {
                 dataSocket.Close();
             }
         }
         instruction2 = pause;
     }
     finally
     {
         CodeAccessPermission.RevertAssert();
     }
     return instruction2;
 }
        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;
        }
        /// <summary>
        /// ReceiveCommandResponseCallback is the main "while loop" of the ReceiveCommandResponse function family.
        /// In general, what is does is perform an EndReceive() to complete the previous retrieval of bytes from the
        /// server (unless it is using a buffered response)  It then processes what is received by using the
        /// implementing class's CheckValid() function, as described above. If the response is complete, it returns the single complete
        /// response in the GeneralResponseDescription created in BeginReceiveComamndResponse, and buffers the rest as described above.
        ///
        /// If the resposne is not complete, it issues another Connection.BeginReceive, with callback ReceiveCommandResponse2,
        /// so the action will continue at the next invocation of ReceiveCommandResponse2.
        /// </summary>
        /// <param name="asyncResult"></param>
        ///
        private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead)
        {
            // completeLength will be set to a nonnegative number by CheckValid if the response is complete:
            // it will set completeLength to the length of a complete response.
            int completeLength = -1;

            while (true)
            {
                int validThrough = state.ValidThrough; // passed to checkvalid


                // If we have a Buffered response (ie data was received with the last response that was past the end of that response)
                // deal with it as if we had just received it now instead of actually doing another receive
                if (m_Buffer.Length > 0)
                {
                    // Append the string we got from the buffer, and flush it out.
                    state.Resp.StatusBuffer.Append(m_Buffer);
                    m_Buffer = string.Empty;

                    // invoke checkvalid.
                    if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
                    {
                        throw GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                    }
                }
                else // we did a Connection.BeginReceive.  Note that in this case, all bytes received are in the receive buffer (because bytes from
                     // the buffer were transferred there if necessary
                {
                    // this indicates the connection was closed.
                    if (bytesRead <= 0)
                    {
                        throw GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                    }

                    // decode the bytes in the receive buffer into a string, append it to the statusbuffer, and invoke checkvalid.
                    // Decoder automatically takes care of caching partial codepoints at the end of a buffer.

                    char[] chars    = new char[m_Decoder.GetCharCount(state.Buffer, 0, bytesRead)];
                    int    numChars = m_Decoder.GetChars(state.Buffer, 0, bytesRead, chars, 0, false);

                    string szResponse = new string(chars, 0, numChars);

                    state.Resp.StatusBuffer.Append(szResponse);
                    if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
                    {
                        throw GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                    }

                    // If the response is complete, then determine how many characters are left over...these bytes need to be set into Buffer.
                    if (completeLength >= 0)
                    {
                        int unusedChars = state.Resp.StatusBuffer.Length - completeLength;
                        if (unusedChars > 0)
                        {
                            m_Buffer = szResponse.Substring(szResponse.Length - unusedChars, unusedChars);
                        }
                    }
                }

                // Now, in general, if the response is not complete, update the "valid through" length for the efficiency of checkValid.
                // and perform the next receive.
                // Note that there may NOT be bytes in the beginning of the receive buffer (even if there were partial characters left over after the
                // last encoding), because they get tracked in the Decoder.
                if (completeLength < 0)
                {
                    state.ValidThrough = validThrough;
                    try {
                        if (m_Async)
                        {
                            BeginRead(state.Buffer, 0, state.Buffer.Length, m_ReadCallbackDelegate, state);
                            return;
                        }
                        else
                        {
                            bytesRead = Read(state.Buffer, 0, state.Buffer.Length);
                            if (bytesRead == 0)
                            {
                                CloseSocket();
                            }
                            continue;
                        }
                    }
                    catch (IOException) {
                        MarkAsRecoverableFailure();
                        throw;
                    }
                    catch {
                        throw;
                    }
                }
                // the response is completed
                break;
            }


            // Otherwise, we have a complete response.
            string responseString = state.Resp.StatusBuffer.ToString();

            state.Resp.StatusDescription = responseString.Substring(0, completeLength);
            // set the StatusDescription to the complete part of the response.  Note that the Buffer has already been taken care of above.

            if (Logging.On)
            {
                Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_received_response, responseString.Substring(0, completeLength - 2)));
            }

            if (m_Async)
            {
                // Tell who is listening what was received.
                if (state.Resp != null)
                {
                    m_CurrentResponseDescription = state.Resp;
                }
                Stream stream = null;
                if (PostReadCommandProcessing(ref stream))
                {
                    return;
                }
                ContinueCommandPipeline();
            }
        }
Beispiel #20
0
 protected void InitCommandPipeline(WebRequest request, PipelineEntry [] commands, bool async) {
     m_Commands = commands;
     m_Index = 0;
     m_Request = request;
     m_Aborted = false;
     m_DoRead = true;
     m_DoSend = true;
     m_CurrentResponseDescription = null;
     m_Async = async;
     m_RecoverableFailure = false;
     m_AbortReason = string.Empty;
 }
 private bool PostSendCommandProcessing(ref Stream stream)
 {
     if (this.m_DoRead)
     {
         bool async = this.m_Async;
         int index = this.m_Index;
         PipelineEntry[] commands = this.m_Commands;
         try
         {
             ResponseDescription description = this.ReceiveCommandResponse();
             if (async)
             {
                 return true;
             }
             this.m_CurrentResponseDescription = description;
         }
         catch
         {
             if (((index < 0) || (index >= commands.Length)) || (commands[index].Command != "QUIT\r\n"))
             {
                 throw;
             }
         }
     }
     return this.PostReadCommandProcessing(ref stream);
 }
Beispiel #22
0
        protected override bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength)
        {
            if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"CheckValid({response.StatusBuffer})");

            // If the response is less than 4 bytes long, it is too short to tell, so return true, valid so far.
            if (response.StatusBuffer.Length < 4)
            {
                return true;
            }
            string responseString = response.StatusBuffer.ToString();

            // Otherwise, if there is no status code for this response yet, get one.
            if (response.Status == ResponseDescription.NoStatus)
            {
                // If the response does not start with three digits, then it is not a valid response from an FTP server.
                if (!(Char.IsDigit(responseString[0]) && Char.IsDigit(responseString[1]) && Char.IsDigit(responseString[2]) && (responseString[3] == ' ' || responseString[3] == '-')))
                {
                    return false;
                }
                else
                {
                    response.StatusCodeString = responseString.Substring(0, 3);
                    response.Status = Convert.ToInt16(response.StatusCodeString, NumberFormatInfo.InvariantInfo);
                }

                // IF a hyphen follows the status code on the first line of the response, then we have a multiline response coming.
                if (responseString[3] == '-')
                {
                    response.Multiline = true;
                }
            }

            // If a complete line of response has been received from the server, then see if the
            // overall response is complete.
            // If this was not a multiline response, then the response is complete at the end of the line.

            // If this was a multiline response (indicated by three digits followed by a '-' in the first line),
            // then we see if the last line received started with the same three digits followed by a space.
            // If it did, then this is the sign of a complete multiline response.
            // If the line contained three other digits followed by the response, then this is a violation of the
            // FTP protocol for multiline responses.
            // All other cases indicate that the response is not yet complete.
            int index = 0;
            while ((index = responseString.IndexOf("\r\n", validThrough)) != -1)  // gets the end line.
            {
                int lineStart = validThrough;
                validThrough = index + 2;  // validThrough now marks the end of the line being examined.
                if (!response.Multiline)
                {
                    completeLength = validThrough;
                    return true;
                }

                if (responseString.Length > lineStart + 4)
                {
                    // If the first three characters of the the response line currently being examined
                    // match the status code, then if they are followed by a space, then we
                    // have reached the end of the reply.
                    if (responseString.Substring(lineStart, 3) == response.StatusCodeString)
                    {
                        if (responseString[lineStart + 3] == ' ')
                        {
                            completeLength = validThrough;
                            return true;
                        }
                    }
                }
            }
            return true;
        }
 private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead)
 {
     int num2;
     int completeLength = -1;
 Label_0002:
     num2 = state.ValidThrough;
     if (this.m_Buffer.Length > 0)
     {
         state.Resp.StatusBuffer.Append(this.m_Buffer);
         this.m_Buffer = string.Empty;
         if (!this.CheckValid(state.Resp, ref num2, ref completeLength))
         {
             throw this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
         }
     }
     else
     {
         if (bytesRead <= 0)
         {
             throw this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
         }
         char[] chars = new char[this.m_Decoder.GetCharCount(state.Buffer, 0, bytesRead)];
         int length = this.m_Decoder.GetChars(state.Buffer, 0, bytesRead, chars, 0, false);
         string str = new string(chars, 0, length);
         state.Resp.StatusBuffer.Append(str);
         if (!this.CheckValid(state.Resp, ref num2, ref completeLength))
         {
             throw this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
         }
         if (completeLength >= 0)
         {
             int num4 = state.Resp.StatusBuffer.Length - completeLength;
             if (num4 > 0)
             {
                 this.m_Buffer = str.Substring(str.Length - num4, num4);
             }
         }
     }
     if (completeLength < 0)
     {
         state.ValidThrough = num2;
         try
         {
             if (this.m_Async)
             {
                 this.BeginRead(state.Buffer, 0, state.Buffer.Length, m_ReadCallbackDelegate, state);
                 return;
             }
             bytesRead = this.Read(state.Buffer, 0, state.Buffer.Length);
             if (bytesRead == 0)
             {
                 base.CloseSocket();
             }
             goto Label_0002;
         }
         catch (IOException)
         {
             this.MarkAsRecoverableFailure();
             throw;
         }
         catch
         {
             throw;
         }
     }
     string str2 = state.Resp.StatusBuffer.ToString();
     state.Resp.StatusDescription = str2.Substring(0, completeLength);
     if (Logging.On)
     {
         Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_received_response", new object[] { str2.Substring(0, completeLength - 2) }));
     }
     if (this.m_Async)
     {
         if (state.Resp != null)
         {
             this.m_CurrentResponseDescription = state.Resp;
         }
         Stream stream = null;
         if (!this.PostReadCommandProcessing(ref stream))
         {
             this.ContinueCommandPipeline();
         }
     }
 }
Beispiel #24
0
        private PipelineInstruction QueueOrCreateDataConection(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream, out bool isSocketReady)
        {
            isSocketReady = false;
            if (_dataHandshakeStarted)
            {
                isSocketReady = true;
                return PipelineInstruction.Pause; //if we already started then this is re-entering into the callback where we proceed with the stream
            }

            _dataHandshakeStarted = true;

            // Handle passive responses by parsing the port and later doing a Connect(...)
            bool isPassive = false;
            int port = -1;
            if (entry.Command == "PASV\r\n" || entry.Command == "EPSV\r\n")
            {
                if (!response.PositiveCompletion)
                {
                    _abortReason = SR.Format(SR.net_ftp_server_failed_passive, response.Status);
                    return PipelineInstruction.Abort;
                }
                if (entry.Command == "PASV\r\n")
                {
                    port = GetPortV4(response.StatusDescription);
                }
                else
                {
                    port = GetPortV6(response.StatusDescription);
                }

                isPassive = true;
            }

            if (isPassive)
            {
                if (port == -1)
                {
                    NetEventSource.Fail(this, "'port' not set.");
                }

                try
                {
                    _dataSocket = CreateFtpDataSocket((FtpWebRequest)_request, Socket);
                }
                catch (ObjectDisposedException)
                {
                    throw ExceptionHelper.RequestAbortedException;
                }

                IPEndPoint localEndPoint = new IPEndPoint(((IPEndPoint)Socket.LocalEndPoint).Address, 0);
                _dataSocket.Bind(localEndPoint);

                _passiveEndPoint = new IPEndPoint(ServerAddress, port);
            }

            PipelineInstruction result;

            if (_passiveEndPoint != null)
            {
                IPEndPoint passiveEndPoint = _passiveEndPoint;
                _passiveEndPoint = null;
                if (NetEventSource.IsEnabled) NetEventSource.Info(this, "starting Connect()");
                if (_isAsync)
                {
                    _dataSocket.BeginConnect(passiveEndPoint, s_connectCallbackDelegate, this);
                    result = PipelineInstruction.Pause;
                }
                else
                {
                    _dataSocket.Connect(passiveEndPoint);
                    result = PipelineInstruction.Advance; // for passive mode we end up going to the next command
                }
            }
            else
            {
                if (NetEventSource.IsEnabled) NetEventSource.Info(this, "starting Accept()");

                if (_isAsync)
                {
                    _dataSocket.BeginAccept(s_acceptCallbackDelegate, this);
                    result = PipelineInstruction.Pause;
                }
                else
                {
                    Socket listenSocket = _dataSocket;
                    try
                    {
                        _dataSocket = _dataSocket.Accept();
                        if (!ServerAddress.Equals(((IPEndPoint)_dataSocket.RemoteEndPoint).Address))
                        {
                            _dataSocket.Close();
                            throw new WebException(SR.net_ftp_active_address_different, WebExceptionStatus.ProtocolError);
                        }
                        isSocketReady = true;   // for active mode we end up creating a stream before advancing the pipeline
                        result = PipelineInstruction.Pause;
                    }
                    finally
                    {
                        listenSocket.Close();
                    }
                }
            }
            return result;
        }
Beispiel #25
0
        private bool PostReadCommandProcessing(ref Stream stream)
        {
            if (this.m_Index < this.m_Commands.Length)
            {
                PipelineInstruction advance;
                PipelineEntry       entry;
                this.m_DoSend = false;
                this.m_DoRead = false;
                if (this.m_Index == -1)
                {
                    entry = null;
                }
                else
                {
                    entry = this.m_Commands[this.m_Index];
                }
                if ((this.m_CurrentResponseDescription == null) && (entry.Command == "QUIT\r\n"))
                {
                    advance = PipelineInstruction.Advance;
                }
                else
                {
                    advance = this.PipelineCallback(entry, this.m_CurrentResponseDescription, false, ref stream);
                }
                switch (advance)
                {
                case PipelineInstruction.Advance:
                    this.m_CurrentResponseDescription = null;
                    this.m_DoSend = true;
                    this.m_DoRead = true;
                    this.m_Index++;
                    break;

                case PipelineInstruction.Pause:
                    return(true);

                case PipelineInstruction.Abort:
                    Exception exception;
                    if (this.m_AbortReason != string.Empty)
                    {
                        exception = new WebException(this.m_AbortReason);
                    }
                    else
                    {
                        exception = this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                    }
                    this.Abort(exception);
                    throw exception;

                case PipelineInstruction.GiveStream:
                    this.m_CurrentResponseDescription = null;
                    this.m_DoRead = true;
                    if (this.m_Async)
                    {
                        this.ContinueCommandPipeline();
                        this.InvokeRequestCallback(stream);
                    }
                    return(true);

                case PipelineInstruction.Reread:
                    this.m_CurrentResponseDescription = null;
                    this.m_DoRead = true;
                    break;
                }
            }
            return(false);
        }
Beispiel #26
0
        private bool PostSendCommandProcessing(ref Stream stream)
        {
            if (_doRead)
            {
                // In async case, the next call can actually result in a
                // series of synchronous completions that eventually close
                // the connection. So we need to save the members that 
                // we need to access, since they may not be valid after the 
                // next call returns
                bool isAsync = _isAsync;
                int index = _index;
                PipelineEntry[] commands = _commands;

                try
                {
                    ResponseDescription response = ReceiveCommandResponse();
                    if (isAsync)
                    {
                        return true;
                    }
                    _currentResponseDescription = response;
                }
                catch
                {
                    // If we get an exception on the QUIT command (which is 
                    // always the last command), ignore the final exception
                    // and continue with the pipeline regardlss of sync/async
                    if (index < 0 || index >= commands.Length ||
                        commands[index].Command != "QUIT\r\n")
                        throw;
                }
            }
            return PostReadCommandProcessing(ref stream);
        }
Beispiel #27
0
 internal ReceiveState(CommandStream connection)
 {
     Connection = connection;
     Resp = new ResponseDescription();
     Buffer = new byte[bufferSize];  //1024
     ValidThrough = 0;
 }
Beispiel #28
0
 protected virtual PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream)
 {
     return PipelineInstruction.Abort;
 }
Beispiel #29
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);
        }
Beispiel #30
0
        /// <summary>
        /// ReceiveCommandResponseCallback is the main "while loop" of the ReceiveCommandResponse function family.
        /// In general, what is does is perform an EndReceive() to complete the previous retrieval of bytes from the
        /// server (unless it is using a buffered response)  It then processes what is received by using the
        /// implementing class's CheckValid() function, as described above. If the response is complete, it returns the single complete
        /// response in the GeneralResponseDescription created in BeginReceiveComamndResponse, and buffers the rest as described above.
        ///
        /// If the response is not complete, it issues another Connection.BeginReceive, with callback ReceiveCommandResponse2,
        /// so the action will continue at the next invocation of ReceiveCommandResponse2.
        /// </summary>
        private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead)
        {
            // completeLength will be set to a nonnegative number by CheckValid if the response is complete:
            // it will set completeLength to the length of a complete response.
            int completeLength = -1;

            while (true)
            {
                int validThrough = state.ValidThrough; // passed to checkvalid

                // If we have a Buffered response (ie data was received with the last response that was past the end of that response)
                // deal with it as if we had just received it now instead of actually doing another receive
                if (_buffer.Length > 0)
                {
                    // Append the string we got from the buffer, and flush it out.
                    state.Resp.StatusBuffer.Append(_buffer);
                    _buffer = string.Empty;

                    // invoke checkvalid.
                    if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
                    {
                        throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
                    }
                }
                else // we did a Connection.BeginReceive.  Note that in this case, all bytes received are in the receive buffer (because bytes from
                     // the buffer were transferred there if necessary
                {
                    // this indicates the connection was closed.
                    if (bytesRead <= 0)
                    {
                        throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
                    }

                    // decode the bytes in the receive buffer into a string, append it to the statusbuffer, and invoke checkvalid.
                    // Decoder automatically takes care of caching partial codepoints at the end of a buffer.

                    char[] chars = new char[_decoder.GetCharCount(state.Buffer, 0, bytesRead)];
                    int numChars = _decoder.GetChars(state.Buffer, 0, bytesRead, chars, 0, false);

                    string szResponse = new string(chars, 0, numChars);

                    state.Resp.StatusBuffer.Append(szResponse);
                    if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
                    {
                        throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
                    }

                    // If the response is complete, then determine how many characters are left over...these bytes need to be set into Buffer.
                    if (completeLength >= 0)
                    {
                        int unusedChars = state.Resp.StatusBuffer.Length - completeLength;
                        if (unusedChars > 0)
                        {
                            _buffer = szResponse.Substring(szResponse.Length - unusedChars, unusedChars);
                        }
                    }
                }

                // Now, in general, if the response is not complete, update the "valid through" length for the efficiency of checkValid,
                // and perform the next receive.
                // Note that there may NOT be bytes in the beginning of the receive buffer (even if there were partial characters left over after the
                // last encoding), because they get tracked in the Decoder.
                if (completeLength < 0)
                {
                    state.ValidThrough = validThrough;
                    try
                    {
                        if (_isAsync)
                        {
                            BeginRead(state.Buffer, 0, state.Buffer.Length, s_readCallbackDelegate, state);
                            return;
                        }
                        else
                        {
                            bytesRead = Read(state.Buffer, 0, state.Buffer.Length);
                            if (bytesRead == 0)
                                CloseSocket();
                            continue;
                        }
                    }
                    catch (IOException)
                    {
                        MarkAsRecoverableFailure();
                        throw;
                    }
                    catch
                    {
                        throw;
                    }
                }

                // The response is completed
                break;
            }

            // Otherwise, we have a complete response.
            string responseString = state.Resp.StatusBuffer.ToString();
            state.Resp.StatusDescription = responseString.Substring(0, completeLength);
            // Set the StatusDescription to the complete part of the response.  Note that the Buffer has already been taken care of above.

            if (NetEventSource.Log.IsEnabled())
                NetEventSource.PrintInfo(NetEventSource.ComponentType.Web, this, string.Format("Received response: {0}", responseString.Substring(0, completeLength - 2)));

            if (_isAsync)
            {
                // Tell who is listening what was received.
                if (state.Resp != null)
                {
                    _currentResponseDescription = state.Resp;
                }
                Stream stream = null;
                if (PostReadCommandProcessing(ref stream))
                    return;
                ContinueCommandPipeline();
            }
        }
Beispiel #31
0
        private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead)
        {
            int num2;
            int completeLength = -1;

Label_0002:
            num2 = state.ValidThrough;
            if (this.m_Buffer.Length > 0)
            {
                state.Resp.StatusBuffer.Append(this.m_Buffer);
                this.m_Buffer = string.Empty;
                if (!this.CheckValid(state.Resp, ref num2, ref completeLength))
                {
                    throw this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                }
            }
            else
            {
                if (bytesRead <= 0)
                {
                    throw this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                }
                char[] chars  = new char[this.m_Decoder.GetCharCount(state.Buffer, 0, bytesRead)];
                int    length = this.m_Decoder.GetChars(state.Buffer, 0, bytesRead, chars, 0, false);
                string str    = new string(chars, 0, length);
                state.Resp.StatusBuffer.Append(str);
                if (!this.CheckValid(state.Resp, ref num2, ref completeLength))
                {
                    throw this.GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                }
                if (completeLength >= 0)
                {
                    int num4 = state.Resp.StatusBuffer.Length - completeLength;
                    if (num4 > 0)
                    {
                        this.m_Buffer = str.Substring(str.Length - num4, num4);
                    }
                }
            }
            if (completeLength < 0)
            {
                state.ValidThrough = num2;
                try
                {
                    if (this.m_Async)
                    {
                        this.BeginRead(state.Buffer, 0, state.Buffer.Length, m_ReadCallbackDelegate, state);
                        return;
                    }
                    bytesRead = this.Read(state.Buffer, 0, state.Buffer.Length);
                    if (bytesRead == 0)
                    {
                        base.CloseSocket();
                    }
                    goto Label_0002;
                }
                catch (IOException)
                {
                    this.MarkAsRecoverableFailure();
                    throw;
                }
                catch
                {
                    throw;
                }
            }
            string str2 = state.Resp.StatusBuffer.ToString();

            state.Resp.StatusDescription = str2.Substring(0, completeLength);
            if (Logging.On)
            {
                Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_received_response", new object[] { str2.Substring(0, completeLength - 2) }));
            }
            if (this.m_Async)
            {
                if (state.Resp != null)
                {
                    this.m_CurrentResponseDescription = state.Resp;
                }
                Stream stream = null;
                if (!this.PostReadCommandProcessing(ref stream))
                {
                    this.ContinueCommandPipeline();
                }
            }
        }
Beispiel #32
0
        protected override bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"CheckValid({response.StatusBuffer})");
            }

            // If the response is less than 4 bytes long, it is too short to tell, so return true, valid so far.
            if (response.StatusBuffer.Length < 4)
            {
                return(true);
            }
            string responseString = response.StatusBuffer.ToString();

            // Otherwise, if there is no status code for this response yet, get one.
            if (response.Status == ResponseDescription.NoStatus)
            {
                // If the response does not start with three digits, then it is not a valid response from an FTP server.
                if (!(char.IsDigit(responseString[0]) && char.IsDigit(responseString[1]) && char.IsDigit(responseString[2]) && (responseString[3] == ' ' || responseString[3] == '-')))
                {
                    return(false);
                }
                else
                {
                    response.StatusCodeString = responseString.Substring(0, 3);
                    response.Status           = Convert.ToInt16(response.StatusCodeString, NumberFormatInfo.InvariantInfo);
                }

                // IF a hyphen follows the status code on the first line of the response, then we have a multiline response coming.
                if (responseString[3] == '-')
                {
                    response.Multiline = true;
                }
            }

            // If a complete line of response has been received from the server, then see if the
            // overall response is complete.
            // If this was not a multiline response, then the response is complete at the end of the line.

            // If this was a multiline response (indicated by three digits followed by a '-' in the first line),
            // then we see if the last line received started with the same three digits followed by a space.
            // If it did, then this is the sign of a complete multiline response.
            // If the line contained three other digits followed by the response, then this is a violation of the
            // FTP protocol for multiline responses.
            // All other cases indicate that the response is not yet complete.
            int index = 0;

            while ((index = responseString.IndexOf("\r\n", validThrough)) != -1)  // gets the end line.
            {
                int lineStart = validThrough;
                validThrough = index + 2;  // validThrough now marks the end of the line being examined.
                if (!response.Multiline)
                {
                    completeLength = validThrough;
                    return(true);
                }

                if (responseString.Length > lineStart + 4)
                {
                    // If the first three characters of the response line currently being examined
                    // match the status code, then if they are followed by a space, then we
                    // have reached the end of the reply.
                    if (responseString.Substring(lineStart, 3) == response.StatusCodeString)
                    {
                        if (responseString[lineStart + 3] == ' ')
                        {
                            completeLength = validThrough;
                            return(true);
                        }
                    }
                }
            }
            return(true);
        }
        //
        private bool PostReadCommandProcessing(ref Stream stream)
        {
            if (m_Index >= m_Commands.Length)
            {
                return(false);
            }

            // Set up front to prevent a race condition on result == PipelineInstruction.Pause
            m_DoSend = false;
            m_DoRead = false;

            PipelineInstruction result;
            PipelineEntry       entry;

            if (m_Index == -1)
            {
                entry = null;
            }
            else
            {
                entry = m_Commands[m_Index];
            }

            // Final QUIT command may get exceptions since the connectin
            // may be already closed by the server. So there is no response
            // to process, just advance the pipeline to continue
            if (m_CurrentResponseDescription == null && entry.Command == "QUIT\r\n")
            {
                result = PipelineInstruction.Advance;
            }
            else
            {
                result = PipelineCallback(entry, m_CurrentResponseDescription, false, ref stream);
            }

            if (result == PipelineInstruction.Abort)
            {
                Exception exception;
                if (m_AbortReason != string.Empty)
                {
                    exception = new WebException(m_AbortReason);
                }
                else
                {
                    exception = GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
                }
                Abort(exception);
                throw exception;
            }
            else if (result == PipelineInstruction.Advance)
            {
                m_CurrentResponseDescription = null;
                m_DoSend = true;
                m_DoRead = true;
                m_Index++;
            }
            else if (result == PipelineInstruction.Pause)
            {
                //
                // PipelineCallback did an async operation and will have to re-enter again
                // Hold on for now
                //
                return(true);
            }
            else if (result == PipelineInstruction.GiveStream)
            {
                //
                // We will have another response coming, don't send
                //
                m_CurrentResponseDescription = null;
                m_DoRead = true;
                if (m_Async)
                {
                    // If they block in the requestcallback we should still continue the pipeline
                    ContinueCommandPipeline();
                    InvokeRequestCallback(stream);
                }
                return(true);
            }
            else if (result == PipelineInstruction.Reread)
            {
                // Another response is expected after this one
                m_CurrentResponseDescription = null;
                m_DoRead = true;
            }
            return(false);
        }
Beispiel #34
0
        private PipelineInstruction QueueOrCreateDataConection(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream, out bool isSocketReady)
        {
            isSocketReady = false;
            if (_dataHandshakeStarted)
            {
                isSocketReady = true;
                return(PipelineInstruction.Pause); //if we already started then this is re-entering into the callback where we proceed with the stream
            }

            _dataHandshakeStarted = true;

            // Handle passive responses by parsing the port and later doing a Connect(...)
            bool isPassive = false;
            int  port      = -1;

            if (entry.Command == "PASV\r\n" || entry.Command == "EPSV\r\n")
            {
                if (!response.PositiveCompletion)
                {
                    _abortReason = SR.Format(SR.net_ftp_server_failed_passive, response.Status);
                    return(PipelineInstruction.Abort);
                }
                if (entry.Command == "PASV\r\n")
                {
                    port = GetPortV4(response.StatusDescription);
                }
                else
                {
                    port = GetPortV6(response.StatusDescription);
                }

                isPassive = true;
            }

            if (isPassive)
            {
                if (port == -1)
                {
                    NetEventSource.Fail(this, "'port' not set.");
                }

                try
                {
                    _dataSocket = CreateFtpDataSocket((FtpWebRequest)_request, Socket);
                }
                catch (ObjectDisposedException)
                {
                    throw ExceptionHelper.RequestAbortedException;
                }

                IPEndPoint localEndPoint = new IPEndPoint(((IPEndPoint)Socket.LocalEndPoint).Address, 0);
                _dataSocket.Bind(localEndPoint);

                _passiveEndPoint = new IPEndPoint(ServerAddress, port);
            }

            PipelineInstruction result;

            if (_passiveEndPoint != null)
            {
                IPEndPoint passiveEndPoint = _passiveEndPoint;
                _passiveEndPoint = null;
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "starting Connect()");
                }
                if (_isAsync)
                {
                    _dataSocket.BeginConnect(passiveEndPoint, s_connectCallbackDelegate, this);
                    result = PipelineInstruction.Pause;
                }
                else
                {
                    _dataSocket.Connect(passiveEndPoint);
                    result = PipelineInstruction.Advance; // for passive mode we end up going to the next command
                }
            }
            else
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "starting Accept()");
                }

                if (_isAsync)
                {
                    _dataSocket.BeginAccept(s_acceptCallbackDelegate, this);
                    result = PipelineInstruction.Pause;
                }
                else
                {
                    Socket listenSocket = _dataSocket;
                    try
                    {
                        _dataSocket = _dataSocket.Accept();
                        if (!ServerAddress.Equals(((IPEndPoint)_dataSocket.RemoteEndPoint).Address))
                        {
                            _dataSocket.Close();
                            throw new WebException(SR.net_ftp_active_address_different, WebExceptionStatus.ProtocolError);
                        }
                        isSocketReady = true;   // for active mode we end up creating a stream before advancing the pipeline
                        result        = PipelineInstruction.Pause;
                    }
                    finally
                    {
                        listenSocket.Close();
                    }
                }
            }
            return(result);
        }
 protected virtual PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream)
 {
     return(PipelineInstruction.Abort);
 }