private boolean initialize(String user) throws IOException {
    if (initDone == false) {
      tm.registerMessageHandler(this, 0, 255);

      PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
      tm.sendMessage(sr.getPayload());

      byte[] msg = getNextMessage();
      new PacketServiceAccept(msg, 0, msg.length);

      PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
      tm.sendMessage(urn.getPayload());

      msg = getNextMessage();

      initDone = true;

      if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
        authenticated = true;
        tm.removeMessageHandler(this, 0, 255);
        return true;
      }

      if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
        PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);

        remainingMethods = puf.getAuthThatCanContinue();
        isPartialSuccess = puf.isPartialSuccess();
        return false;
      }

      throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
    }
    return authenticated;
  }
  public void requestSubSystem(Channel c, String subSystemName) throws IOException {
    PacketSessionSubsystemRequest ssr;

    synchronized (c) {
      if (c.state != Channel.STATE_OPEN)
        throw new IOException(
            "Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");

      ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);

      c.successCounter = c.failedCounter = 0;
    }

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent)
        throw new IOException(
            "Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
      tm.sendMessage(ssr.getPayload());
    }

    try {
      waitForChannelSuccessOrFailure(c);
    } catch (IOException e) {
      throw (IOException) new IOException("The subsystem request failed.").initCause(e);
    }
  }
  public void requestCancelGlobalForward(int bindPort) throws IOException {
    RemoteForwardingData rfd = null;

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

      if (rfd == null)
        throw new IOException(
            "Sorry, there is no known remote forwarding for remote port " + bindPort);
    }

    synchronized (channels) {
      globalSuccessCounter = globalFailedCounter = 0;
    }

    PacketGlobalCancelForwardRequest pgcf =
        new PacketGlobalCancelForwardRequest(true, rfd.bindAddress, rfd.bindPort);
    tm.sendMessage(pgcf.getPayload());

    log.debug(
        "Requesting cancelation of remote forward ('"
            + rfd.bindAddress
            + "', "
            + rfd.bindPort
            + ")");

    waitForGlobalSuccessOrFailure();

    /* Only now we are sure that no more forwarded connections will arrive */

    synchronized (remoteForwardings) {
      remoteForwardings.remove(rfd);
    }
  }
  public Channel openDirectTCPIPChannel(
      String host_to_connect,
      int port_to_connect,
      String originator_IP_address,
      int originator_port)
      throws IOException {
    Channel c = new Channel(this);

    synchronized (c) {
      c.localID = addChannel(c);
      // end of synchronized block forces writing out to main memory
    }

    PacketOpenDirectTCPIPChannel dtc =
        new PacketOpenDirectTCPIPChannel(
            c.localID,
            c.localWindow,
            c.localMaxPacketSize,
            host_to_connect,
            port_to_connect,
            originator_IP_address,
            originator_port);

    tm.sendMessage(dtc.getPayload());

    waitUntilChannelOpen(c);

    return c;
  }
  public void closeChannel(Channel c, String reason, boolean force) throws IOException {
    byte msg[] = new byte[5];

    synchronized (c) {
      if (force) {
        c.state = Channel.STATE_CLOSED;
        c.EOF = true;
      }

      c.setReasonClosed(reason);

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

      c.notifyAll();
    }

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent == true) return;
      tm.sendMessage(msg);
      c.closeMessageSent = true;
    }

    log.debug("Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
  }
  /**
   * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
   */
  public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException {
    PacketSessionExecCommand sm;

    synchronized (c) {
      if (c.state != Channel.STATE_OPEN)
        throw new IOException(
            "Cannot execute command on this channel (" + c.getReasonClosed() + ")");

      sm = new PacketSessionExecCommand(c.remoteID, true, cmd);

      c.successCounter = c.failedCounter = 0;
    }

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent)
        throw new IOException(
            "Cannot execute command on this channel (" + c.getReasonClosed() + ")");
      tm.sendMessage(sm.getPayload(charsetName));
    }

    log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");

    try {
      waitForChannelSuccessOrFailure(c);
    } catch (IOException e) {
      throw (IOException) new IOException("The execute request failed.").initCause(e);
    }
  }
 public boolean authenticateNone(String user) throws IOException {
   try {
     initialize(user);
     return authenticated;
   } catch (IOException e) {
     tm.close(e, false);
     throw (IOException) new IOException("None authentication failed.").initCause(e);
   }
 }
  public boolean authenticatePassword(String user, String pass) throws IOException {
    try {
      initialize(user);

      if (methodPossible("password") == false)
        throw new IOException(
            "Authentication method password not supported by the server at this stage.");

      PacketUserauthRequestPassword ua =
          new PacketUserauthRequestPassword("ssh-connection", user, pass);
      tm.sendMessage(ua.getPayload());

      byte[] ar = getNextMessage();

      if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
        authenticated = true;
        tm.removeMessageHandler(this, 0, 255);
        return true;
      }

      if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
        PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

        remainingMethods = puf.getAuthThatCanContinue();
        isPartialSuccess = puf.isPartialSuccess();

        return false;
      }

      throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

    } catch (IOException e) {
      tm.close(e, false);
      throw (IOException) new IOException("Password authentication failed.").initCause(e);
    }
  }
  public Channel openSessionChannel() throws IOException {
    Channel c = new Channel(this);

    synchronized (c) {
      c.localID = addChannel(c);
      // end of synchronized block forces the writing out to main memory
    }

    log.debug("Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");

    PacketOpenSessionChannel smo =
        new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
    tm.sendMessage(smo.getPayload());

    waitUntilChannelOpen(c);

    return c;
  }
  public void sendOpenConfirmation(Channel c) throws IOException {
    PacketChannelOpenConfirmation pcoc = null;

    synchronized (c) {
      if (c.state != Channel.STATE_OPENING) return;

      c.state = Channel.STATE_OPEN;

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

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent == true) return;
      tm.sendMessage(pcoc.getPayload());
    }
  }
  byte[] deQueue() throws IOException {
    synchronized (packets) {
      while (packets.size() == 0) {
        if (connectionClosed)
          throw (IOException)
              new IOException("The connection is closed.").initCause(tm.getReasonClosedCause());

        try {
          packets.wait();
        } catch (InterruptedException ign) {
          Thread.currentThread().interrupt();
        }
      }
      /* This sequence works with J2ME */
      byte[] res = (byte[]) packets.firstElement();
      packets.removeElementAt(0);
      return res;
    }
  }
  public void sendEOF(Channel c) throws IOException {
    byte[] msg = new byte[5];

    synchronized (c) {
      if (c.state != Channel.STATE_OPEN) return;

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

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent == true) return;
      tm.sendMessage(msg);
    }

    log.debug("Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
  }
  public void msgGlobalRequest(byte[] msg, int msglen) throws IOException {
    /* Currently we do not support any kind of global request */

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

    tr.readByte(); // skip packet type
    String requestName = tr.readString();
    boolean wantReply = tr.readBoolean();

    if (wantReply) {
      byte[] reply_failure = new byte[1];
      reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;

      tm.sendAsynchronousMessage(reply_failure);
    }

    /* We do not clean up the requestName String - that is OK for debug */

    log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
  }
  public void requestPTY(
      Channel c,
      String term,
      int term_width_characters,
      int term_height_characters,
      int term_width_pixels,
      int term_height_pixels,
      byte[] terminal_modes)
      throws IOException {
    PacketSessionPtyRequest spr;

    synchronized (c) {
      if (c.state != Channel.STATE_OPEN)
        throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");

      spr =
          new PacketSessionPtyRequest(
              c.remoteID,
              true,
              term,
              term_width_characters,
              term_height_characters,
              term_width_pixels,
              term_height_pixels,
              terminal_modes);

      c.successCounter = c.failedCounter = 0;
    }

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent)
        throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
      tm.sendMessage(spr.getPayload());
    }

    try {
      waitForChannelSuccessOrFailure(c);
    } catch (IOException e) {
      throw (IOException) new IOException("PTY request failed").initCause(e);
    }
  }
  public int requestGlobalForward(
      String bindAddress, int bindPort, String targetAddress, int targetPort) throws IOException {
    RemoteForwardingData rfd = new RemoteForwardingData();

    rfd.bindAddress = bindAddress;
    rfd.bindPort = bindPort;
    rfd.targetAddress = targetAddress;
    rfd.targetPort = targetPort;

    synchronized (remoteForwardings) {
      Integer key = new Integer(bindPort);

      if (remoteForwardings.get(key) != null) {
        throw new IOException("There is already a forwarding for remote port " + bindPort);
      }

      remoteForwardings.put(key, rfd);
    }

    synchronized (channels) {
      globalSuccessCounter = globalFailedCounter = 0;
    }

    PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
    tm.sendMessage(pgf.getPayload());

    log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");

    try {
      waitForGlobalSuccessOrFailure();
    } catch (IOException e) {
      synchronized (remoteForwardings) {
        remoteForwardings.remove(rfd);
      }
      throw e;
    }

    return bindPort;
  }
  public void requestX11(
      Channel c,
      boolean singleConnection,
      String x11AuthenticationProtocol,
      String x11AuthenticationCookie,
      int x11ScreenNumber)
      throws IOException {
    PacketSessionX11Request psr;

    synchronized (c) {
      if (c.state != Channel.STATE_OPEN)
        throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");

      psr =
          new PacketSessionX11Request(
              c.remoteID,
              true,
              singleConnection,
              x11AuthenticationProtocol,
              x11AuthenticationCookie,
              x11ScreenNumber);

      c.successCounter = c.failedCounter = 0;
    }

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent)
        throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
      tm.sendMessage(psr.getPayload());
    }

    log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");

    try {
      waitForChannelSuccessOrFailure(c);
    } catch (IOException e) {
      throw (IOException) new IOException("The X11 request failed.").initCause(e);
    }
  }
  public void requestShell(Channel c) throws IOException {
    PacketSessionStartShell sm;

    synchronized (c) {
      if (c.state != Channel.STATE_OPEN)
        throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");

      sm = new PacketSessionStartShell(c.remoteID, true);

      c.successCounter = c.failedCounter = 0;
    }

    synchronized (c.channelSendLock) {
      if (c.closeMessageSent)
        throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
      tm.sendMessage(sm.getPayload());
    }

    try {
      waitForChannelSuccessOrFailure(c);
    } catch (IOException e) {
      throw (IOException) new IOException("The shell request failed.").initCause(e);
    }
  }
  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");
  }
  public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb)
      throws IOException {
    try {
      initialize(user);

      if (methodPossible("keyboard-interactive") == false)
        throw new IOException(
            "Authentication method keyboard-interactive not supported by the server at this stage.");

      if (submethods == null) submethods = new String[0];

      PacketUserauthRequestInteractive ua =
          new PacketUserauthRequestInteractive("ssh-connection", user, submethods);

      tm.sendMessage(ua.getPayload());

      while (true) {
        byte[] ar = getNextMessage();

        if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
          authenticated = true;
          tm.removeMessageHandler(this, 0, 255);
          return true;
        }

        if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
          PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

          remainingMethods = puf.getAuthThatCanContinue();
          isPartialSuccess = puf.isPartialSuccess();

          return false;
        }

        if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST) {
          PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);

          String[] responses;

          try {
            responses =
                cb.replyToChallenge(
                    pui.getName(),
                    pui.getInstruction(),
                    pui.getNumPrompts(),
                    pui.getPrompt(),
                    pui.getEcho());
          } catch (Exception e) {
            throw (IOException) new IOException("Exception in callback.").initCause(e);
          }

          if (responses == null) throw new IOException("Your callback may not return NULL!");

          PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
          tm.sendMessage(puir.getPayload());

          continue;
        }

        throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
      }
    } catch (IOException e) {
      tm.close(e, false);
      throw (IOException)
          new IOException("Keyboard-interactive authentication failed.").initCause(e);
    }
  }
  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 sendData(Channel c, byte[] buffer, int pos, int len) throws IOException {
    boolean wasInterrupted = false;

    try {
      while (len > 0) {
        int thislen = 0;
        byte[] msg;

        synchronized (c) {
          while (true) {
            if (c.state == Channel.STATE_CLOSED)
              throw new ChannelClosedException(
                  "SSH channel is closed. (" + c.getReasonClosed() + ")");

            if (c.state != Channel.STATE_OPEN)
              throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");

            if (c.remoteWindow != 0) break;

            try {
              c.wait();
            } catch (InterruptedException ignore) {
              wasInterrupted = true;
            }
          }

          /* len > 0, no sign extension can happen when comparing */

          thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;

          int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);

          /* The worst case scenario =) a true bottleneck */

          if (estimatedMaxDataLen <= 0) {
            estimatedMaxDataLen = 1;
          }

          if (thislen > estimatedMaxDataLen) thislen = estimatedMaxDataLen;

          c.remoteWindow -= thislen;

          msg = new byte[1 + 8 + thislen];

          msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
          msg[1] = (byte) (c.remoteID >> 24);
          msg[2] = (byte) (c.remoteID >> 16);
          msg[3] = (byte) (c.remoteID >> 8);
          msg[4] = (byte) (c.remoteID);
          msg[5] = (byte) (thislen >> 24);
          msg[6] = (byte) (thislen >> 16);
          msg[7] = (byte) (thislen >> 8);
          msg[8] = (byte) (thislen);

          System.arraycopy(buffer, pos, msg, 9, thislen);
        }

        synchronized (c.channelSendLock) {
          if (c.closeMessageSent == true)
            throw new ChannelClosedException(
                "SSH channel is closed. (" + c.getReasonClosed() + ")");

          tm.sendMessage(msg);
        }

        pos += thislen;
        len -= thislen;
      }
    } finally {
      if (wasInterrupted) Thread.currentThread().interrupt();
    }
  }
  public boolean authenticatePublicKey(
      String user, char[] PEMPrivateKey, String password, SecureRandom rnd) throws IOException {
    try {
      initialize(user);

      if (methodPossible("publickey") == false)
        throw new IOException(
            "Authentication method publickey not supported by the server at this stage.");

      Object key = PEMDecoder.decode(PEMPrivateKey, password);

      if (key instanceof DSAPrivateKey) {
        DSAPrivateKey pk = (DSAPrivateKey) key;

        byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());

        TypesWriter tw = new TypesWriter();

        byte[] H = tm.getSessionIdentifier();

        tw.writeString(H, 0, H.length);
        tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
        tw.writeString(user);
        tw.writeString("ssh-connection");
        tw.writeString("publickey");
        tw.writeBoolean(true);
        tw.writeString("ssh-dss");
        tw.writeString(pk_enc, 0, pk_enc.length);

        byte[] msg = tw.getBytes();

        DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);

        byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);

        PacketUserauthRequestPublicKey ua =
            new PacketUserauthRequestPublicKey("ssh-connection", user, "ssh-dss", pk_enc, ds_enc);
        tm.sendMessage(ua.getPayload());
      } else if (key instanceof RSAPrivateKey) {
        RSAPrivateKey pk = (RSAPrivateKey) key;

        byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());

        TypesWriter tw = new TypesWriter();
        {
          byte[] H = tm.getSessionIdentifier();

          tw.writeString(H, 0, H.length);
          tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
          tw.writeString(user);
          tw.writeString("ssh-connection");
          tw.writeString("publickey");
          tw.writeBoolean(true);
          tw.writeString("ssh-rsa");
          tw.writeString(pk_enc, 0, pk_enc.length);
        }

        byte[] msg = tw.getBytes();

        RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);

        byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);

        PacketUserauthRequestPublicKey ua =
            new PacketUserauthRequestPublicKey(
                "ssh-connection", user, "ssh-rsa", pk_enc, rsa_sig_enc);
        tm.sendMessage(ua.getPayload());
      } else {
        throw new IOException("Unknown private key type returned by the PEM decoder.");
      }

      byte[] ar = getNextMessage();

      if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
        authenticated = true;
        tm.removeMessageHandler(this, 0, 255);
        return true;
      }

      if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
        PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

        remainingMethods = puf.getAuthThatCanContinue();
        isPartialSuccess = puf.isPartialSuccess();

        return false;
      }

      throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

    } catch (IOException e) {
      tm.close(e, false);
      throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
    }
  }
  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 int getChannelData(Channel c, boolean extended, byte[] target, int off, int len)
      throws IOException {
    boolean wasInterrupted = false;

    try {
      int copylen = 0;
      int increment = 0;
      int remoteID = 0;
      int localID = 0;

      synchronized (c) {
        int stdoutAvail = 0;
        int stderrAvail = 0;

        while (true) {
          /*
           * Data available? We have to return remaining data even if the
           * channel is already closed.
           */

          stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
          stderrAvail = c.stderrWritepos - c.stderrReadpos;

          if ((!extended) && (stdoutAvail != 0)) break;

          if ((extended) && (stderrAvail != 0)) break;

          /* Do not wait if more data will never arrive (EOF or CLOSED) */

          if ((c.EOF) || (c.state != Channel.STATE_OPEN)) return -1;

          try {
            c.wait();
          } catch (InterruptedException ignore) {
            wasInterrupted = true;
          }
        }

        /* OK, there is some data. Return it. */

        if (!extended) {
          copylen = (stdoutAvail > len) ? len : stdoutAvail;
          System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
          c.stdoutReadpos += copylen;

          if (c.stdoutReadpos != c.stdoutWritepos)
            System.arraycopy(
                c.stdoutBuffer,
                c.stdoutReadpos,
                c.stdoutBuffer,
                0,
                c.stdoutWritepos - c.stdoutReadpos);

          c.stdoutWritepos -= c.stdoutReadpos;
          c.stdoutReadpos = 0;
        } else {
          copylen = (stderrAvail > len) ? len : stderrAvail;
          System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
          c.stderrReadpos += copylen;

          if (c.stderrReadpos != c.stderrWritepos)
            System.arraycopy(
                c.stderrBuffer,
                c.stderrReadpos,
                c.stderrBuffer,
                0,
                c.stderrWritepos - c.stderrReadpos);

          c.stderrWritepos -= c.stderrReadpos;
          c.stderrReadpos = 0;
        }

        if (c.state != Channel.STATE_OPEN) return copylen;

        if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2)) {
          int minFreeSpace =
              Math.min(
                  Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
                  Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);

          increment = minFreeSpace - c.localWindow;
          c.localWindow = minFreeSpace;
        }

        remoteID = c.remoteID; /* read while holding the lock */
        localID = c.localID; /* read while holding the lock */
      }

      /*
       * If a consumer reads stdout and stdin in parallel, we may end up with
       * sending two msgWindowAdjust messages. Luckily, it
       * does not matter in which order they arrive at the server.
       */

      if (increment > 0) {
        log.debug(
            "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");

        synchronized (c.channelSendLock) {
          byte[] msg = c.msgWindowAdjust;

          msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
          msg[1] = (byte) (remoteID >> 24);
          msg[2] = (byte) (remoteID >> 16);
          msg[3] = (byte) (remoteID >> 8);
          msg[4] = (byte) (remoteID);
          msg[5] = (byte) (increment >> 24);
          msg[6] = (byte) (increment >> 16);
          msg[7] = (byte) (increment >> 8);
          msg[8] = (byte) (increment);

          if (c.closeMessageSent == false) tm.sendMessage(msg);
        }
      }

      return copylen;
    } finally {
      if (wasInterrupted) Thread.currentThread().interrupt();
    }
  }
 /**
  * Constructor for server-mode.
  *
  * @param state
  */
 public ChannelManager(ServerConnectionState state) {
   this.server_state = state;
   this.tm = state.tm;
   tm.registerMessageHandler(this, 80, 100);
 }
 /**
  * Constructor for client-mode.
  *
  * @param tm
  */
 public ChannelManager(TransportManager tm) {
   this.server_state = null;
   this.tm = tm;
   tm.registerMessageHandler(this, 80, 100);
 }