/// <summary>
        /// Process packet about this channel.
        /// </summary>
        /// <param name="packetType">a packet type (message number)</param>
        /// <param name="packetFragment">a packet image except message number and recipient channel.</param>
        public void ProcessPacket(SSH2PacketType packetType, DataFragment packetFragment)
        {
            if (_state == State.Closed) {
                return; // ignore
            }

            DataFragment dataFragmentArg;
            uint dataTypeCodeArg;

            lock (_stateSync) {
                switch (_state) {
                    case State.InitiatedByServer:
                        break;
                    case State.InitiatedByClient:
                        if (packetType == SSH2PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
                            SSH2DataReader reader = new SSH2DataReader(packetFragment);
                            RemoteChannel = reader.ReadUInt32();
                            _serverWindowSizeLeft = reader.ReadUInt32();
                            _serverMaxPacketSize = reader.ReadUInt32();

                            _state = State.Established;
                            Monitor.PulseAll(_stateSync);   // notifies state change
                            dataFragmentArg = reader.GetRemainingDataView();
                            goto OnEstablished; // do it out of the lock block
                        }
                        if (packetType == SSH2PacketType.SSH_MSG_CHANNEL_OPEN_FAILURE) {
                            SSH2DataReader reader = new SSH2DataReader(packetFragment);
                            uint reasonCode = reader.ReadUInt32();
                            string description = reader.ReadUTF8String();
                            string lang = reader.ReadString();
                            goto RequestFailed; // do it out of the lock block
                        }
                        break;
                    case State.Closing:
                        if (packetType == SSH2PacketType.SSH_MSG_CHANNEL_CLOSE) {
                            goto SetStateClosedByClient;    // do it out of the lock block
                        }
                        break;
                    case State.Established:
                    case State.Ready:
                        if (ProcessPacketSub(packetType, packetFragment) == SubPacketProcessResult.Consumed) {
                            return;
                        }
                        switch (packetType) {
                            case SSH2PacketType.SSH_MSG_CHANNEL_DATA: {
                                    SSH2DataReader reader = new SSH2DataReader(packetFragment);
                                    int len = reader.ReadInt32();
                                    dataFragmentArg = reader.GetRemainingDataView(len);
                                    AdjustWindowSize(len);
                                }
                                goto OnData;    // do it out of the lock block
                            case SSH2PacketType.SSH_MSG_CHANNEL_EXTENDED_DATA: {
                                    SSH2DataReader reader = new SSH2DataReader(packetFragment);
                                    dataTypeCodeArg = reader.ReadUInt32();
                                    int len = reader.ReadInt32();
                                    dataFragmentArg = reader.GetRemainingDataView(len);
                                    AdjustWindowSize(len);
                                }
                                goto OnExtendedData;    // do it out of the lock block
                            case SSH2PacketType.SSH_MSG_CHANNEL_REQUEST: {
                                    SSH2DataReader reader = new SSH2DataReader(packetFragment);
                                    string request = reader.ReadString();
                                    bool wantReply = reader.ReadBool();
                                    if (wantReply) { //we reject unknown requests including keep-alive check
                                        Transmit(
                                            0,
                                            new SSH2Packet(SSH2PacketType.SSH_MSG_CHANNEL_FAILURE)
                                                .WriteUInt32(RemoteChannel)
                                        );
                                    }
                                }
                                break;
                            case SSH2PacketType.SSH_MSG_CHANNEL_EOF:
                                goto OnEOF; // do it out of the lock block
                            case SSH2PacketType.SSH_MSG_CHANNEL_CLOSE:
                                Transmit(
                                    0,
                                    new SSH2Packet(SSH2PacketType.SSH_MSG_CHANNEL_CLOSE)
                                        .WriteUInt32(RemoteChannel)
                                );
                                goto SetStateClosedByServer;    // do it out of the lock block
                            case SSH2PacketType.SSH_MSG_CHANNEL_WINDOW_ADJUST: {
                                    SSH2DataReader reader = new SSH2DataReader(packetFragment);
                                    uint bytesToAdd = reader.ReadUInt32();
                                    // some servers may not send SSH_MSG_CHANNEL_WINDOW_ADJUST.
                                    // it is dangerous to wait this message in send procedure
                                    _serverWindowSizeLeft += bytesToAdd;
                                }
                                goto OnWindowAdjust;
                            case SSH2PacketType.SSH_MSG_CHANNEL_SUCCESS:
                            case SSH2PacketType.SSH_MSG_CHANNEL_FAILURE: {
                                    _channelRequestResult.TrySet(packetType == SSH2PacketType.SSH_MSG_CHANNEL_SUCCESS, 1000);
                                }
                                break;
                            default:
                                goto OnUnhandledPacket;
                        }
                        break;  // case State.Ready
                }
            }

            return;

            OnEstablished:
            _protocolEventManager.Trace(
                "CH[{0}] remoteCH={1} remoteWindowSize={2} remoteMaxPacketSize={3}",
                LocalChannel, RemoteChannel, _serverWindowSizeLeft, _serverMaxPacketSize);
            _handler.OnEstablished(dataFragmentArg);
            OnChannelEstablished();
            return;

            RequestFailed:
            _protocolEventManager.Trace("CH[{0}] request failed", LocalChannel);
            RequestFailed();
            return;

            SetStateClosedByClient:
            _protocolEventManager.Trace("CH[{0}] closed completely", LocalChannel);
            SetStateClosed(false);
            return;

            SetStateClosedByServer:
            _protocolEventManager.Trace("CH[{0}] closed by server", LocalChannel);
            SetStateClosed(true);
            return;

            OnData:
            _handler.OnData(dataFragmentArg);
            return;

            OnExtendedData:
            _handler.OnExtendedData(dataTypeCodeArg, dataFragmentArg);
            return;

            OnEOF:
            _protocolEventManager.Trace("CH[{0}] caught EOF", LocalChannel);
            _handler.OnEOF();
            return;

            OnWindowAdjust:
            _protocolEventManager.Trace(
                "CH[{0}] adjusted remote window size to {1}",
                LocalChannel, _serverWindowSizeLeft);
            return;

            OnUnhandledPacket:
            _handler.OnUnhandledPacket((byte)packetType, packetFragment);
            return;
        }
Exemple #2
0
        // sending "exec" service for SCP protocol.
        private void ExecCommand(ISSHChannelEventReceiver receiver, SSH2PacketType pt, SSH2DataReader reader)
        {
            if (_negotiationStatus == NegotiationStatus.WaitingChannelConfirmation) {
                if (pt != SSH2PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
                    if (pt != SSH2PacketType.SSH_MSG_CHANNEL_OPEN_FAILURE)
                        receiver.OnChannelError(new SSHException("opening channel failed; packet type=" + pt));
                    else {
                        int errcode = reader.ReadInt32();
                        string msg = reader.ReadUTF8String();
                        receiver.OnChannelError(new SSHException(msg));
                    }
                    Close();
                }
                else {
                    _remoteID = reader.ReadInt32();
                    _allowedDataSize = reader.ReadUInt32();
                    _serverMaxPacketSize = reader.ReadInt32();

                    // exec command
                    SSHConnectionParameter param = _connection.Param;
                    Transmit(
                        0,
                        new SSH2Packet(SSH2PacketType.SSH_MSG_CHANNEL_REQUEST)
                            .WriteInt32(_remoteID)
                            .WriteString("exec")  // "exec"
                            .WriteBool(false)   // want confirm is disabled. (*)
                            .WriteString(_command)
                    );
                    if (_connection.IsEventTracerAvailable)
                        _connection.TraceTransmissionEvent("exec command", "cmd={0}", _command);

                    //confirmation is omitted
                    receiver.OnChannelReady();
                    _negotiationStatus = NegotiationStatus.Ready; //goal!
                }
            }
            else if (_negotiationStatus == NegotiationStatus.WaitingExecCmdConfirmation) {
                if (pt != SSH2PacketType.SSH_MSG_CHANNEL_DATA) {
                    receiver.OnChannelError(new SSHException("exec command failed"));
                    Close();
                }
                else {
                    receiver.OnChannelReady();
                    _negotiationStatus = NegotiationStatus.Ready; //goal!
                }
            }
            else
                throw new SSHException("internal state error");
        }
Exemple #3
0
        private void OpenShellOrSubsystem(ISSHChannelEventReceiver receiver, SSH2PacketType pt, SSH2DataReader reader, string scheme)
        {
            if (_negotiationStatus == NegotiationStatus.WaitingChannelConfirmation) {
                if (pt != SSH2PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
                    if (pt != SSH2PacketType.SSH_MSG_CHANNEL_OPEN_FAILURE)
                        receiver.OnChannelError(new SSHException("opening channel failed; packet type=" + pt));
                    else {
                        int errcode = reader.ReadInt32();
                        string msg = reader.ReadUTF8String();
                        receiver.OnChannelError(new SSHException(msg));
                    }
                    // Close() shouldn't be called because remote channel number is not given yet.
                    // We just remove an event receiver from the collection of channels.
                    // FIXME: _negotiationStatus sould be set an error status ?
                    _connection.ChannelCollection.UnregisterChannelEventReceiver(_localID);
                }
                else {
                    _remoteID = reader.ReadInt32();
                    _allowedDataSize = reader.ReadUInt32();
                    _serverMaxPacketSize = reader.ReadInt32();

                    if (_type == ChannelType.Subsystem) {
                        OpenScheme(scheme);
                        _negotiationStatus = NegotiationStatus.WaitingSubsystemConfirmation;
                    }
                    else {
                        //open pty
                        SSHConnectionParameter param = _connection.Param;
                        Transmit(
                            0,
                            new SSH2Packet(SSH2PacketType.SSH_MSG_CHANNEL_REQUEST)
                                .WriteInt32(_remoteID)
                                .WriteString("pty-req")
                                .WriteBool(true)
                                .WriteString(param.TerminalName)
                                .WriteInt32(param.TerminalWidth)
                                .WriteInt32(param.TerminalHeight)
                                .WriteInt32(param.TerminalPixelWidth)
                                .WriteInt32(param.TerminalPixelHeight)
                                .WriteAsString(new byte[0])
                        );

                        if (_connection.IsEventTracerAvailable) {
                            _connection.TraceTransmissionEvent(
                                SSH2PacketType.SSH_MSG_CHANNEL_REQUEST, "pty-req", "terminal={0} width={1} height={2}",
                                param.TerminalName, param.TerminalWidth, param.TerminalHeight);
                        }

                        _negotiationStatus = NegotiationStatus.WaitingPtyReqConfirmation;
                    }
                }
            }
            else if (_negotiationStatus == NegotiationStatus.WaitingPtyReqConfirmation) {
                if (pt != SSH2PacketType.SSH_MSG_CHANNEL_SUCCESS) {
                    receiver.OnChannelError(new SSHException("opening pty failed"));
                    Close();
                }
                else {
                    //agent request (optional)
                    if (_connection.Param.AgentForward != null) {
                        Transmit(
                            0,
                            new SSH2Packet(SSH2PacketType.SSH_MSG_CHANNEL_REQUEST)
                                .WriteInt32(_remoteID)
                                .WriteString("*****@*****.**")
                                .WriteBool(true)
                        );
                        _connection.TraceTransmissionEvent(SSH2PacketType.SSH_MSG_CHANNEL_REQUEST, "auth-agent-req", "");
                        _negotiationStatus = NegotiationStatus.WaitingAuthAgentReqConfirmation;
                    }
                    else {
                        OpenScheme(scheme);
                        _negotiationStatus = NegotiationStatus.WaitingShellConfirmation;
                    }
                }
            }
            else if (_negotiationStatus == NegotiationStatus.WaitingAuthAgentReqConfirmation) {
                if (pt != SSH2PacketType.SSH_MSG_CHANNEL_SUCCESS && pt != SSH2PacketType.SSH_MSG_CHANNEL_FAILURE) {
                    receiver.OnChannelError(new SSHException("auth-agent-req error"));
                    Close();
                }
                else { //auth-agent-req is optional
                    _connection.SetAgentForwardConfirmed(pt == SSH2PacketType.SSH_MSG_CHANNEL_SUCCESS);
                    _connection.TraceReceptionEvent(pt, "auth-agent-req");

                    OpenScheme(scheme);
                    _negotiationStatus = NegotiationStatus.WaitingShellConfirmation;
                }
            }
            else if (_negotiationStatus == NegotiationStatus.WaitingShellConfirmation) {
                if (pt != SSH2PacketType.SSH_MSG_CHANNEL_SUCCESS) {
                    receiver.OnChannelError(new SSHException("Opening shell failed: packet type=" + pt.ToString()));
                    Close();
                }
                else {
                    receiver.OnChannelReady();
                    _negotiationStatus = NegotiationStatus.Ready; //goal!
                }
            }
            else if (_negotiationStatus == NegotiationStatus.WaitingSubsystemConfirmation) {
                if (pt != SSH2PacketType.SSH_MSG_CHANNEL_SUCCESS) {
                    receiver.OnChannelError(new SSHException("Opening subsystem failed: packet type=" + pt.ToString()));
                    Close();
                }
                else {
                    receiver.OnChannelReady();
                    _negotiationStatus = NegotiationStatus.Ready; //goal!
                }
            }
        }
Exemple #4
0
        internal void ProcessPacket(ISSHChannelEventReceiver receiver, SSH2PacketType pt, SSH2DataReader re)
        {
            //NOTE: the offset of 're' is next to 'receipiant channel' field

            if (pt == SSH2PacketType.SSH_MSG_CHANNEL_DATA || pt == SSH2PacketType.SSH_MSG_CHANNEL_EXTENDED_DATA) {
                AdjustWindowSize(pt, re.RemainingDataLength);
            }

            //SSH_MSG_CHANNEL_WINDOW_ADJUST comes before the complete of channel establishment
            if (pt == SSH2PacketType.SSH_MSG_CHANNEL_WINDOW_ADJUST) {
                uint w = re.ReadUInt32();
                //some servers may not send SSH_MSG_CHANNEL_WINDOW_ADJUST.
                //it is dangerous to wait this message in send procedure
                _allowedDataSize += w;
                if (_connection.IsEventTracerAvailable)
                    _connection.TraceReceptionEvent("SSH_MSG_CHANNEL_WINDOW_ADJUST", "adjusted to {0} by increasing {1}", _allowedDataSize, w);
                return;
            }

            // check closing sequence
            if (_waitingChannelClose && pt == SSH2PacketType.SSH_MSG_CHANNEL_CLOSE) {
                _waitingChannelClose = false;
                return; // ignore it
            }

            if (_negotiationStatus != NegotiationStatus.Ready) //when the negotiation is not completed
                ProgressChannelNegotiation(receiver, pt, re);
            else
                ProcessChannelLocalData(receiver, pt, re);
        }