protected int readRawInput() throws IOException {
   if (buffer.available() > 0) {
     return buffer.getUByte();
   } else {
     return this.in.read();
   }
 }
  @Override
  public synchronized int read(byte[] b, int off, int len) throws IOException {
    if (len == 1) {
      int c = read();
      if (c == -1) {
        return -1;
      }

      b[off] = (byte) c;
      return 1;
    }

    if (buffer.available() == 0) {
      int nb = this.in.read(b, off, len);
      if (nb == -1) {
        return nb;
      }
      buffer.putRawBytes(b, off, nb);
    }

    int nb = 0;
    for (int curPos = off; (nb < len) && (buffer.available() > 0); nb++, curPos++) {
      b[curPos] = (byte) read();
    }

    return nb;
  }
 /**
  * @param extraSize Extra size - besides the extension name
  * @return A {@link Buffer} with the extension name set
  */
 protected Buffer getCommandBuffer(int extraSize) {
   String opcode = getName();
   Buffer buffer =
       new ByteArrayBuffer(
           (Integer.SIZE / Byte.SIZE) + GenericUtils.length(opcode) + extraSize + Byte.SIZE);
   buffer.putString(opcode);
   return buffer;
 }
 @Override
 protected void reply(Buffer buf) throws IOException {
   ByteBuffer b = ByteBuffer.wrap(buf.array(), buf.rpos(), buf.available());
   int result = sessionChannel.write(b);
   if (result < 0) {
     throw new IOException("Could not write response to socket");
   }
 }
    @Override
    public void messageReceived(IoSession session, Readable message) throws Exception {
      TcpipClientChannel channel =
          (TcpipClientChannel) session.getAttribute(TcpipClientChannel.class);
      Buffer buffer = new ByteArrayBuffer();
      buffer.putBuffer(message);
      channel.waitFor(ClientChannel.OPENED | ClientChannel.CLOSED, Long.MAX_VALUE);

      OutputStream outputStream = channel.getInvertedIn();
      outputStream.write(buffer.array(), buffer.rpos(), buffer.available());
      outputStream.flush();
    }
  /**
   * @param buffer The {@link Buffer}
   * @param target A target path {@link String} or {@link Handle} or {@code byte[]} to be encoded in
   *     the buffer
   * @return The updated buffer
   * @throws UnsupportedOperationException If target is not one of the above supported types
   */
  public Buffer putTarget(Buffer buffer, Object target) {
    if (target instanceof CharSequence) {
      buffer.putString(target.toString());
    } else if (target instanceof byte[]) {
      buffer.putBytes((byte[]) target);
    } else if (target instanceof Handle) {
      buffer.putBytes(((Handle) target).getIdentifier());
    } else {
      throw new UnsupportedOperationException("Unknown target type: " + target);
    }

    return buffer;
  }
    @Override
    protected void process(int cmd, Buffer req, Buffer rep) throws Exception {
      switch (cmd) {
        case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
          // stop causing ssh-add -l to log errors
          rep.putByte(SSH_AGENT_RSA_IDENTITIES_ANSWER);
          rep.putInt(0);
          break;

        default:
          super.process(cmd, req, rep);
          break;
      }
    }
 protected void sendHeartBeat() {
   String request =
       FactoryManagerUtils.getStringProperty(
           session,
           ClientFactoryManager.HEARTBEAT_REQUEST,
           ClientFactoryManager.DEFAULT_KEEP_ALIVE_HEARTBEAT_STRING);
   try {
     Buffer buf = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST);
     buf.putString(request);
     buf.putBoolean(false);
     session.writePacket(buf);
   } catch (IOException e) {
     log.info("Error sending keepalive message=" + request, e);
   }
 }
 public synchronized void write(byte[] buf, int off, int len) {
   if (len == 1) {
     write(buf[off] & 0xFF);
   } else {
     buffer.putBytes(buf, off, len);
   }
 }
  /** Test whether onWindowExpanded is called from server session */
  @Test
  public void testHandleWindowAdjust() throws Exception {
    final Buffer buffer = new ByteArrayBuffer();
    buffer.putInt(1234);

    try (ChannelSession channelSession = new ChannelSession()) {
      channelSession.asyncOut =
          new ChannelAsyncOutputStream(new BogusChannel(), (byte) 0) {
            @SuppressWarnings("synthetic-access")
            @Override
            public void onWindowExpanded() throws IOException {
              expanded = true;
              super.onWindowExpanded();
            }
          };
      channelSession.handleWindowAdjust(buffer);
      assertTrue(expanded);
    }
  }
  @Override
  public synchronized OpenFuture open() throws IOException {
    if (isClosing()) {
      throw new SshException("Session has been closed");
    }

    openFuture = new DefaultOpenFuture(lock);
    if (log.isDebugEnabled()) {
      log.debug("open({}) Send SSH_MSG_CHANNEL_OPEN - type={}", this, type);
    }

    Session session = getSession();
    Buffer buffer =
        session.createBuffer(SshConstants.SSH_MSG_CHANNEL_OPEN, type.length() + Integer.SIZE);
    buffer.putString(type);
    buffer.putInt(getId());
    buffer.putInt(localWindow.getSize());
    buffer.putInt(localWindow.getPacketSize());
    writePacket(buffer);
    return openFuture;
  }
  /**
   * @param buffer The {@link Buffer} to check
   * @return The {@link Buffer} if this is an {@link SftpConstants#SSH_FXP_EXTENDED_REPLY}, or
   *     {@code null} if this is a {@link SftpConstants#SSH_FXP_STATUS} carrying an {@link
   *     SftpConstants#SSH_FX_OK} result
   * @throws IOException If a non-{@link SftpConstants#SSH_FX_OK} result or not a {@link
   *     SftpConstants#SSH_FXP_EXTENDED_REPLY} buffer
   */
  protected Buffer checkExtendedReplyBuffer(Buffer buffer) throws IOException {
    int length = buffer.getInt();
    int type = buffer.getUByte();
    int id = buffer.getInt();
    if (type == SftpConstants.SSH_FXP_STATUS) {
      int substatus = buffer.getInt();
      String msg = buffer.getString();
      String lang = buffer.getString();
      if (log.isDebugEnabled()) {
        log.debug(
            "checkStatus({}}[id={}] - status: {} [{}] {}", getName(), id, substatus, lang, msg);
      }

      if (substatus != SftpConstants.SSH_FX_OK) {
        throwStatusException(id, substatus, msg, lang);
      }

      return null;
    } else if (type == SftpConstants.SSH_FXP_EXTENDED_REPLY) {
      return buffer;
    } else {
      throw new SshException(
          "Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
    }
  }
  @Override
  public void handleOpenFailure(Buffer buffer) {
    int reason = buffer.getInt();
    String msg = buffer.getString();
    String lang = buffer.getString();
    if (log.isDebugEnabled()) {
      log.debug(
          "handleOpenFailure({}) reason={}, lang={}, msg={}",
          this,
          SshConstants.getOpenErrorCodeName(reason),
          lang,
          msg);
    }

    this.openFailureReason = reason;
    this.openFailureMsg = msg;
    this.openFailureLang = lang;
    this.openFuture.setException(new SshException(msg));
    this.closeFuture.setClosed();
    this.doCloseImmediately();
    notifyStateChanged();
  }
  @Override
  public Result process(
      ConnectionService connectionService, String request, boolean wantReply, Buffer buffer)
      throws Exception {
    if (!REQUEST.equals(request)) {
      return super.process(connectionService, request, wantReply, buffer);
    }

    String address = buffer.getString();
    int port = buffer.getInt();
    SshdSocketAddress socketAddress = new SshdSocketAddress(address, port);
    TcpipForwarder forwarder =
        Objects.requireNonNull(connectionService.getTcpipForwarder(), "No TCP/IP forwarder");
    SshdSocketAddress bound = forwarder.localPortForwardingRequested(socketAddress);
    if (log.isDebugEnabled()) {
      log.debug(
          "process({})[{}][want-reply-{}] {} => {}",
          connectionService,
          request,
          wantReply,
          socketAddress,
          bound);
    }

    if (bound == null) {
      return Result.ReplyFailure;
    }

    port = bound.getPort();
    if (wantReply) {
      Session session = connectionService.getSession();
      buffer = session.createBuffer(SshConstants.SSH_MSG_REQUEST_SUCCESS, Integer.BYTES);
      buffer.putInt(port);
      session.writePacket(buffer);
    }

    return Result.Replied;
  }
  @Override
  public synchronized void stopRemotePortForwarding(SshdSocketAddress remote) throws IOException {
    SshdSocketAddress bound;
    synchronized (remoteToLocal) {
      bound = remoteToLocal.remove(remote.getPort());
    }

    if (bound != null) {
      if (log.isDebugEnabled()) {
        log.debug("stopRemotePortForwarding(" + remote + ") cancel forwarding to " + bound);
      }

      Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST);
      buffer.putString("cancel-tcpip-forward");
      buffer.putBoolean(false);
      buffer.putString(remote.getHostName());
      buffer.putInt(remote.getPort());
      session.writePacket(buffer);
    } else {
      if (log.isDebugEnabled()) {
        log.debug("stopRemotePortForwarding(" + remote + ") no binding found");
      }
    }
  }
  protected Pair<String, Collection<byte[]>> doGetHash(
      Object target, Collection<String> algorithms, long offset, long length, int blockSize)
      throws IOException {
    Buffer buffer = getCommandBuffer(target, Byte.MAX_VALUE);
    putTarget(buffer, target);
    buffer.putString(GenericUtils.join(algorithms, ','));
    buffer.putLong(offset);
    buffer.putLong(length);
    buffer.putInt(blockSize);

    if (log.isDebugEnabled()) {
      log.debug(
          "doGetHash({})[{}] - offset={}, length={}, block-size={}",
          getName(),
          (target instanceof CharSequence)
              ? target
              : BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, (byte[]) target),
          offset,
          length,
          blockSize);
    }

    buffer = checkExtendedReplyBuffer(receive(sendExtendedCommand(buffer)));
    if (buffer == null) {
      throw new StreamCorruptedException("Missing extended reply data");
    }

    String targetType = buffer.getString();
    if (String.CASE_INSENSITIVE_ORDER.compare(targetType, SftpConstants.EXT_CHECK_FILE) != 0) {
      throw new StreamCorruptedException(
          "Mismatched reply type: expected="
              + SftpConstants.EXT_CHECK_FILE
              + ", actual="
              + targetType);
    }

    String algo = buffer.getString();
    Collection<byte[]> hashes = new LinkedList<>();
    while (buffer.available() > 0) {
      byte[] hashValue = buffer.getBytes();
      hashes.add(hashValue);
    }

    return new Pair<String, Collection<byte[]>>(algo, hashes);
  }
  @Override
  public synchronized SshdSocketAddress startRemotePortForwarding(
      SshdSocketAddress remote, SshdSocketAddress local) throws IOException {
    ValidateUtils.checkNotNull(local, "Local address is null");
    ValidateUtils.checkNotNull(remote, "Remote address is null");

    Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST);
    buffer.putString("tcpip-forward");
    buffer.putBoolean(true);
    buffer.putString(remote.getHostName());
    buffer.putInt(remote.getPort());
    Buffer result = session.request(buffer);
    if (result == null) {
      throw new SshException("Tcpip forwarding request denied by server");
    }
    int port = (remote.getPort() == 0) ? result.getInt() : remote.getPort();
    // TODO: Is it really safe to only store the local address after the request ?
    SshdSocketAddress prev;
    synchronized (remoteToLocal) {
      prev = remoteToLocal.put(port, local);
    }

    if (prev != null) {
      throw new IOException(
          "Multiple remote port forwarding bindings on port="
              + port
              + ": current="
              + remote
              + ", previous="
              + prev);
    }

    SshdSocketAddress bound = new SshdSocketAddress(remote.getHostName(), port);
    if (log.isDebugEnabled()) {
      log.debug("startRemotePortForwarding(" + remote + " -> " + local + "): " + bound);
    }

    return bound;
  }
  @Override
  public Boolean doAuth(Buffer buffer, boolean init) throws Exception {
    ValidateUtils.checkTrue(init, "Instance not initialized");
    boolean hasSig = buffer.getBoolean();
    String alg = buffer.getString();

    int oldLim = buffer.wpos();
    int oldPos = buffer.rpos();
    int len = buffer.getInt();
    buffer.wpos(buffer.rpos() + len);
    PublicKey key = buffer.getRawPublicKey();
    ServerFactoryManager manager = session.getFactoryManager();
    Signature verif =
        ValidateUtils.checkNotNull(
            NamedFactory.Utils.create(manager.getSignatureFactories(), alg),
            "No verifier located for algorithm=%s",
            alg);
    verif.initVerifier(key);
    buffer.wpos(oldLim);

    byte[] sig = hasSig ? buffer.getBytes() : null;

    PublickeyAuthenticator authenticator =
        ValidateUtils.checkNotNull(
            manager.getPublickeyAuthenticator(), "No PublickeyAuthenticator configured");
    if (!authenticator.authenticate(username, key, session)) {
      return Boolean.FALSE;
    }

    if (!hasSig) {
      Buffer buf = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK);
      buf.putString(alg);
      buf.putRawBytes(buffer.array(), oldPos, 4 + len);
      session.writePacket(buf);
      return null;
    } else {
      Buffer buf = new ByteArrayBuffer();
      buf.putBytes(session.getKex().getH());
      buf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
      buf.putString(username);
      buf.putString(service);
      buf.putString(UserAuthPublicKeyFactory.NAME);
      buf.putBoolean(true);
      buf.putString(alg);
      buffer.rpos(oldPos);
      buffer.wpos(oldPos + 4 + len);
      buf.putBuffer(buffer);
      verif.update(buf.array(), buf.rpos(), buf.available());
      if (!verif.verify(sig)) {
        throw new Exception("Key verification failed");
      }
      return Boolean.TRUE;
    }
  }
 public synchronized void write(int c) {
   buffer.putByte((byte) c);
 }
 // TODO add 'insertXXX' methods to the Buffer class
 protected Buffer insertCharacter(Buffer org, int c) {
   int remaining = org.capacity();
   int readPos = org.rpos();
   // see if can accommodate the character in the original buffer
   if ((remaining > 0) && (readPos > 0)) {
     int writePos = org.wpos();
     org.wpos(readPos - 1);
     org.putByte((byte) c);
     org.wpos(writePos);
     org.rpos(readPos - 1);
     return org;
   } else {
     Buffer buf = new ByteArrayBuffer(org.available() + Byte.SIZE, false);
     buf.putByte((byte) c);
     buf.putBuffer(org);
     return buf;
   }
 }
 @Override
 public synchronized int available() throws IOException {
   return super.available() + buffer.available();
 }