public PacketDisconnect(byte payload[], int off, int len) throws IOException {
    this.payload = new byte[len];
    System.arraycopy(payload, off, this.payload, 0, len);

    TypesReader tr = new TypesReader(payload, off, len);

    int packet_type = tr.readByte();

    if (packet_type != Packets.SSH_MSG_DISCONNECT)
      throw new IOException("This is not a Disconnect Packet! (" + packet_type + ")");

    reason = tr.readUINT32();
    desc = tr.readString();
    lang = tr.readString();
  }
Exemple #2
0
  public void receiveLoop() throws IOException {
    byte[] msg = new byte[35000];

    while (true) {
      int msglen;
      try {
        msglen = tc.receiveMessage(msg, 0, msg.length);
      } catch (SocketTimeoutException e) {
        // Timeout in read
        if (idle) {
          log.debug("Ignoring socket timeout");
          continue;
        }
        throw e;
      }
      idle = true;

      int type = msg[0] & 0xff;

      if (type == Packets.SSH_MSG_IGNORE) {
        continue;
      }

      if (type == Packets.SSH_MSG_DEBUG) {
        if (log.isDebugEnabled()) {
          TypesReader tr = new TypesReader(msg, 0, msglen);
          tr.readByte();
          tr.readBoolean();
          StringBuilder debugMessageBuffer = new StringBuilder();
          debugMessageBuffer.append(tr.readString("UTF-8"));

          for (int i = 0; i < debugMessageBuffer.length(); i++) {
            char c = debugMessageBuffer.charAt(i);

            if ((c >= 32) && (c <= 126)) {
              continue;
            }
            debugMessageBuffer.setCharAt(i, '\uFFFD');
          }

          log.debug("DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'");
        }
        continue;
      }

      if (type == Packets.SSH_MSG_UNIMPLEMENTED) {
        throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen.");
      }

      if (type == Packets.SSH_MSG_DISCONNECT) {
        TypesReader tr = new TypesReader(msg, 0, msglen);
        tr.readByte();
        int reason_code = tr.readUINT32();
        StringBuilder reasonBuffer = new StringBuilder();
        reasonBuffer.append(tr.readString("UTF-8"));

        /*
         * Do not get fooled by servers that send abnormal long error
         * messages
         */

        if (reasonBuffer.length() > 255) {
          reasonBuffer.setLength(255);
          reasonBuffer.setCharAt(254, '.');
          reasonBuffer.setCharAt(253, '.');
          reasonBuffer.setCharAt(252, '.');
        }

        /*
         * Also, check that the server did not send characters that may
         * screw up the receiver -> restrict to reasonable US-ASCII
         * subset -> "printable characters" (ASCII 32 - 126). Replace
         * all others with 0xFFFD (UNICODE replacement character).
         */

        for (int i = 0; i < reasonBuffer.length(); i++) {
          char c = reasonBuffer.charAt(i);

          if ((c >= 32) && (c <= 126)) {
            continue;
          }
          reasonBuffer.setCharAt(i, '\uFFFD');
        }

        throw new IOException(
            "Peer sent DISCONNECT message (reason code "
                + reason_code
                + "): "
                + reasonBuffer.toString());
      }

      /*
       * Is it a KEX Packet?
       */

      if ((type == Packets.SSH_MSG_KEXINIT)
          || (type == Packets.SSH_MSG_NEWKEYS)
          || ((type >= 30) && (type <= 49))) {
        km.handleMessage(msg, msglen);
        continue;
      }

      MessageHandler mh = null;

      for (int i = 0; i < messageHandlers.size(); i++) {
        HandlerEntry he = messageHandlers.get(i);
        if ((he.low <= type) && (type <= he.high)) {
          mh = he.mh;
          break;
        }
      }

      if (mh == null) {
        throw new IOException("Unexpected SSH message (type " + type + ")");
      }

      mh.handleMessage(msg, msglen);
    }
  }
  public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException {
    if (msglen < 5)
      throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");

    TypesReader tr = new TypesReader(msg, 0, msglen);

    tr.readByte(); // skip packet type
    int id = tr.readUINT32(); /* sender channel */

    Channel c = getChannel(id);

    if (c == null)
      throw new IOException(
          "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);

    int reasonCode = tr.readUINT32();
    String description = tr.readString("UTF-8");

    String reasonCodeSymbolicName = null;

    switch (reasonCode) {
      case 1:
        reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
        break;
      case 2:
        reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
        break;
      case 3:
        reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
        break;
      case 4:
        reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
        break;
      default:
        reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
    }

    StringBuilder descriptionBuffer = new StringBuilder();
    descriptionBuffer.append(description);

    for (int i = 0; i < descriptionBuffer.length(); i++) {
      char cc = descriptionBuffer.charAt(i);

      if ((cc >= 32) && (cc <= 126)) continue;
      descriptionBuffer.setCharAt(i, '\uFFFD');
    }

    synchronized (c) {
      c.EOF = true;
      c.state = Channel.STATE_CLOSED;
      c.setReasonClosed(
          "The server refused to open the channel ("
              + reasonCodeSymbolicName
              + ", '"
              + descriptionBuffer.toString()
              + "')");
      c.notifyAll();
    }

    log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
  }
  public void msgChannelRequest(byte[] msg, int msglen) throws IOException {
    TypesReader tr = new TypesReader(msg, 0, msglen);

    tr.readByte(); // skip packet type
    int id = tr.readUINT32();

    Channel c = getChannel(id);

    if (c == null)
      throw new IOException(
          "Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);

    ServerSessionImpl server_session = null;

    if (server_state != null) {
      synchronized (c) {
        server_session = c.ss;
      }
    }

    String type = tr.readString("US-ASCII");
    boolean wantReply = tr.readBoolean();

    log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");

    if (type.equals("exit-status")) {
      if (wantReply != false)
        throw new IOException(
            "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status message, 'want reply' is true");

      int exit_status = tr.readUINT32();

      if (tr.remain() != 0)
        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

      synchronized (c) {
        c.exit_status = new Integer(exit_status);
        c.notifyAll();
      }

      log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");

      return;
    }

    if ((server_state == null) && (type.equals("exit-signal"))) {
      if (wantReply != false)
        throw new IOException(
            "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal message, 'want reply' is true");

      String signame = tr.readString("US-ASCII");
      tr.readBoolean();
      tr.readString();
      tr.readString();

      if (tr.remain() != 0)
        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

      synchronized (c) {
        c.exit_signal = signame;
        c.notifyAll();
      }

      log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");

      return;
    }

    if ((server_session != null) && (type.equals("pty-req"))) {
      PtySettings pty = new PtySettings();

      pty.term = tr.readString();
      pty.term_width_characters = tr.readUINT32();
      pty.term_height_characters = tr.readUINT32();
      pty.term_width_pixels = tr.readUINT32();
      pty.term_height_pixels = tr.readUINT32();
      pty.terminal_modes = tr.readByteString();

      if (tr.remain() != 0)
        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

      Runnable run_after_sending_success = null;

      ServerSessionCallback sscb = server_session.getServerSessionCallback();

      if (sscb != null) run_after_sending_success = sscb.requestPtyReq(server_session, pty);

      if (wantReply) {
        if (run_after_sending_success != null) {
          tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
        } else {
          tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
        }
      }

      if (run_after_sending_success != null) {
        runAsync(run_after_sending_success);
      }

      return;
    }

    if ((server_session != null) && (type.equals("shell"))) {
      if (tr.remain() != 0)
        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

      Runnable run_after_sending_success = null;
      ServerSessionCallback sscb = server_session.getServerSessionCallback();

      if (sscb != null) run_after_sending_success = sscb.requestShell(server_session);

      if (wantReply) {
        if (run_after_sending_success != null) {
          tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
        } else {
          tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
        }
      }

      if (run_after_sending_success != null) {
        runAsync(run_after_sending_success);
      }

      return;
    }

    if ((server_session != null) && (type.equals("exec"))) {
      String command = tr.readString();

      if (tr.remain() != 0)
        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

      Runnable run_after_sending_success = null;
      ServerSessionCallback sscb = server_session.getServerSessionCallback();

      if (sscb != null) run_after_sending_success = sscb.requestExec(server_session, command);

      if (wantReply) {
        if (run_after_sending_success != null) {
          tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
        } else {
          tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
        }
      }

      if (run_after_sending_success != null) {
        runAsync(run_after_sending_success);
      }

      return;
    }

    /* We simply ignore unknown channel requests, however, if the server wants a reply,
     * then we signal that we have no idea what it is about.
     */

    if (wantReply) {
      tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
    }

    log.debug("Channel request '" + type + "' is not known, ignoring it");
  }
  public void msgChannelOpen(byte[] msg, int msglen) throws IOException {
    TypesReader tr = new TypesReader(msg, 0, msglen);

    tr.readByte(); // skip packet type
    String channelType = tr.readString();
    int remoteID = tr.readUINT32(); /* sender channel */
    int remoteWindow = tr.readUINT32(); /* initial window size */
    int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */

    if ("x11".equals(channelType)) {
      synchronized (x11_magic_cookies) {
        /* If we did not request X11 forwarding, then simply ignore this bogus request. */

        if (x11_magic_cookies.size() == 0) {
          PacketChannelOpenFailure pcof =
              new PacketChannelOpenFailure(
                  remoteID,
                  Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
                  "X11 forwarding not activated",
                  "");

          tm.sendAsynchronousMessage(pcof.getPayload());

          log.warning("Unexpected X11 request, denying it!");

          return;
        }
      }

      String remoteOriginatorAddress = tr.readString();
      int remoteOriginatorPort = tr.readUINT32();

      Channel c = new Channel(this);

      synchronized (c) {
        c.remoteID = remoteID;
        c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
        c.remoteMaxPacketSize = remoteMaxPacketSize;
        c.localID = addChannel(c);
      }

      /*
       * The open confirmation message will be sent from another thread
       */

      RemoteX11AcceptThread rxat =
          new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
      rxat.setDaemon(true);
      rxat.start();

      return;
    }

    if ("forwarded-tcpip".equals(channelType)) {
      String remoteConnectedAddress = tr.readString(); /* address that was connected */
      int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
      String remoteOriginatorAddress = tr.readString(); /* originator IP address */
      int remoteOriginatorPort = tr.readUINT32(); /* originator port */

      RemoteForwardingData rfd = null;

      synchronized (remoteForwardings) {
        rfd = remoteForwardings.get(new Integer(remoteConnectedPort));
      }

      if (rfd == null) {
        PacketChannelOpenFailure pcof =
            new PacketChannelOpenFailure(
                remoteID,
                Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
                "No thanks, unknown port in forwarded-tcpip request",
                "");

        /* Always try to be polite. */

        tm.sendAsynchronousMessage(pcof.getPayload());

        log.debug("Unexpected forwarded-tcpip request, denying it!");

        return;
      }

      Channel c = new Channel(this);

      synchronized (c) {
        c.remoteID = remoteID;
        c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
        c.remoteMaxPacketSize = remoteMaxPacketSize;
        c.localID = addChannel(c);
      }

      /*
       * The open confirmation message will be sent from another thread.
       */

      RemoteAcceptThread rat =
          new RemoteAcceptThread(
              c,
              remoteConnectedAddress,
              remoteConnectedPort,
              remoteOriginatorAddress,
              remoteOriginatorPort,
              rfd.targetAddress,
              rfd.targetPort);

      rat.setDaemon(true);
      rat.start();

      return;
    }

    if ((server_state != null) && ("session".equals(channelType))) {
      ServerConnectionCallback cb = null;

      synchronized (server_state) {
        cb = server_state.cb_conn;
      }

      if (cb == null) {
        tm.sendAsynchronousMessage(
            new PacketChannelOpenFailure(
                    remoteID,
                    Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
                    "Sessions are currently not enabled",
                    "en")
                .getPayload());

        return;
      }

      final Channel c = new Channel(this);

      synchronized (c) {
        c.remoteID = remoteID;
        c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
        c.remoteMaxPacketSize = remoteMaxPacketSize;
        c.localID = addChannel(c);
        c.state = Channel.STATE_OPEN;
        c.ss = new ServerSessionImpl(c);
      }

      PacketChannelOpenConfirmation pcoc =
          new PacketChannelOpenConfirmation(
              c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);

      tm.sendAsynchronousMessage(pcoc.getPayload());

      c.ss.sscb = cb.acceptSession(c.ss);

      return;
    }

    /* Tell the server that we have no idea what it is talking about */

    PacketChannelOpenFailure pcof =
        new PacketChannelOpenFailure(
            remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE, "Unknown channel type", "");

    tm.sendAsynchronousMessage(pcof.getPayload());

    log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
  }
  public void msgChannelRequest(byte[] msg, int msglen) throws IOException {
    TypesReader tr = new TypesReader(msg, 0, msglen);

    tr.readByte(); // skip packet type
    int id = tr.readUINT32();

    Channel c = getChannel(id);

    if (c == null)
      throw new IOException(
          "Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);

    String type = tr.readString("US-ASCII");
    boolean wantReply = tr.readBoolean();

    log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");

    if (type.equals("exit-status")) {
      if (wantReply != false)
        throw new IOException(
            "Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");

      int exit_status = tr.readUINT32();

      if (tr.remain() != 0)
        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

      synchronized (c) {
        c.exit_status = new Integer(exit_status);
        c.notifyAll();
      }

      log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");

      return;
    }

    if (type.equals("exit-signal")) {
      if (wantReply != false)
        throw new IOException(
            "Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");

      String signame = tr.readString("US-ASCII");
      tr.readBoolean();
      tr.readString();
      tr.readString();

      if (tr.remain() != 0)
        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

      synchronized (c) {
        c.exit_signal = signame;
        c.notifyAll();
      }

      log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");

      return;
    }

    /* We simply ignore unknown channel requests, however, if the server wants a reply,
     * then we signal that we have no idea what it is about.
     */

    if (wantReply) {
      byte[] reply = new byte[5];

      reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
      reply[1] = (byte) (c.remoteID >> 24);
      reply[2] = (byte) (c.remoteID >> 16);
      reply[3] = (byte) (c.remoteID >> 8);
      reply[4] = (byte) (c.remoteID);

      tm.sendAsynchronousMessage(reply);
    }

    log.debug("Channel request '" + type + "' is not known, ignoring it");
  }