Пример #1
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="type">packet type (message number)</param>
 public SSH2Packet(SSH2PacketType type)
 {
     if (_lockFlag.Value)
     {
         throw new InvalidOperationException(
                   "simultaneous editing packet detected: " + typeof(SSH2Packet).FullName);
     }
     _lockFlag.Value = true;
     _payload        = _payloadBuffer.Value;
     _payload.Clear();
     _payload.WriteUInt32(0);    // sequence_number field for computing MAC
     _payload.WriteUInt32(0);    // packet_length field
     _payload.WriteByte(0);      // padding_length field
     _payload.WriteByte((byte)type);
 }
Пример #2
0
 /// <summary>
 /// Process packet additionally.
 /// </summary>
 /// <remarks>
 /// This method will be called repeatedly while <see cref="State"/> is <see cref="State.Established"/> or <see cref="State.Ready"/>.
 /// </remarks>
 /// <param name="packetType">a packet type (message number)</param>
 /// <param name="packetFragment">a packet image except message number and recipient channel.</param>
 /// <returns>result</returns>
 protected virtual SubPacketProcessResult ProcessPacketSub(SSH2PacketType packetType, DataFragment packetFragment)
 {
     // derived class can override this.
     return SubPacketProcessResult.NotConsumed;
 }
Пример #3
0
 //alternative version
 internal void TraceTransmissionEvent(SSH2PacketType pt, string message, params object[] args)
 {
     ISSHEventTracer t = _param.EventTracer;
     if (t != null)
         t.OnTranmission(pt.ToString(), String.Format(message, args));
 }
Пример #4
0
        /// <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;
        }
Пример #5
0
 private void ReceivePortForwardingResponse(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();
             _serverMaxPacketSize = reader.ReadInt32();
             _negotiationStatus = NegotiationStatus.Ready;
             receiver.OnChannelReady();
         }
     }
     else
         throw new SSHException("internal state error");
 }
Пример #6
0
 internal void TraceReceptionEvent(SSH2PacketType pt, byte[] msg)
 {
     TraceReceptionEvent(pt.ToString(), Encoding.ASCII.GetString(msg));
 }
Пример #7
0
 private void ProcessChannelLocalData(ISSHChannelEventReceiver receiver, SSH2PacketType pt, SSH2DataReader re)
 {
     switch (pt) {
         case SSH2PacketType.SSH_MSG_CHANNEL_DATA: {
                 int len = re.ReadInt32();
                 DataFragment frag = re.GetRemainingDataView(len);
                 receiver.OnData(frag.Data, frag.Offset, frag.Length);
             }
             break;
         case SSH2PacketType.SSH_MSG_CHANNEL_EXTENDED_DATA: {
                 int t = re.ReadInt32();
                 byte[] data = re.ReadByteString();
                 receiver.OnExtendedData(t, data);
             }
             break;
         case SSH2PacketType.SSH_MSG_CHANNEL_REQUEST: {
                 string request = re.ReadString();
                 bool reply = re.ReadBool();
                 if (request == "exit-status") {
                     int status = re.ReadInt32();
                 }
                 else if (reply) { //we reject unknown requests including keep-alive check
                     Transmit(
                         0,
                         new SSH2Packet(SSH2PacketType.SSH_MSG_CHANNEL_FAILURE)
                             .WriteInt32(_remoteID)
                     );
                 }
             }
             break;
         case SSH2PacketType.SSH_MSG_CHANNEL_EOF:
             receiver.OnChannelEOF();
             break;
         case SSH2PacketType.SSH_MSG_CHANNEL_CLOSE:
             _connection.ChannelCollection.UnregisterChannelEventReceiver(_localID);
             receiver.OnChannelClosed();
             break;
         case SSH2PacketType.SSH_MSG_CHANNEL_FAILURE: {
                 DataFragment frag = re.GetRemainingDataView();
                 receiver.OnMiscPacket((byte)pt, frag.Data, frag.Offset, frag.Length);
             }
             break;
         default: {
                 DataFragment frag = re.GetRemainingDataView();
                 receiver.OnMiscPacket((byte)pt, frag.Data, frag.Offset, frag.Length);
             }
             Debug.WriteLine("Unknown Packet " + pt);
             break;
     }
 }
Пример #8
0
 //Progress the state of this channel establishment negotiation
 private void ProgressChannelNegotiation(ISSHChannelEventReceiver receiver, SSH2PacketType pt, SSH2DataReader re)
 {
     if (_type == ChannelType.Shell)
         OpenShellOrSubsystem(receiver, pt, re, "shell");
     else if (_type == ChannelType.ForwardedLocalToRemote)
         ReceivePortForwardingResponse(receiver, pt, re);
     else if (_type == ChannelType.Session)
         EstablishSession(receiver, pt, re);
     else if (_type == ChannelType.ExecCommand)  // for SCP
         ExecCommand(receiver, pt, re);
     else if (_type == ChannelType.Subsystem)
         OpenShellOrSubsystem(receiver, pt, re, "subsystem");
 }
Пример #9
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!
                }
            }
        }
Пример #10
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");
        }
Пример #11
0
        private void AdjustWindowSize(SSH2PacketType pt, int dataLength)
        {
            // need not send window size to server when the channel is not opened.
            if (_negotiationStatus != NegotiationStatus.Ready)
                return;

            _leftWindowSize = Math.Max(_leftWindowSize - dataLength, 0);

            if (_leftWindowSize < _windowSize / 2) {
                Transmit(
                    0,
                    new SSH2Packet(SSH2PacketType.SSH_MSG_CHANNEL_WINDOW_ADJUST)
                        .WriteInt32(_remoteID)
                        .WriteInt32(_windowSize - _leftWindowSize)
                );
                if (_connection.IsEventTracerAvailable)
                    _connection.TraceTransmissionEvent("SSH_MSG_CHANNEL_WINDOW_ADJUST", "adjusted window size : {0} --> {1}", _leftWindowSize, _windowSize);
                _leftWindowSize = _windowSize;
            }
        }
Пример #12
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);
        }
Пример #13
0
 private void TraceTransmissionNegotiation(SSH2PacketType pt, string msg)
 {
     _connection.TraceTransmissionEvent(pt.ToString(), msg);
 }