Blame src/channels.c

Packit 6c0a39
/*
Packit 6c0a39
 * channels.c - SSH channel functions
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2003-2013 by Aris Adamantiadis
Packit 6c0a39
 * Copyright (c) 2009-2013 by Andreas Schneider <asn@cryptomilk.org>
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is free software; you can redistribute it and/or modify
Packit 6c0a39
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c0a39
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c0a39
 * option) any later version.
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is distributed in the hope that it will be useful, but
Packit 6c0a39
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c0a39
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c0a39
 * License for more details.
Packit 6c0a39
 *
Packit 6c0a39
 * You should have received a copy of the GNU Lesser General Public License
Packit 6c0a39
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit 6c0a39
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit 6c0a39
 * MA 02111-1307, USA.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
#include "config.h"
Packit 6c0a39
Packit 6c0a39
#include <limits.h>
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
#include <errno.h>
Packit 6c0a39
#include <time.h>
Packit 6c0a39
#include <stdbool.h>
Packit 6c0a39
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
#include <netinet/in.h>
Packit 6c0a39
#include <arpa/inet.h>
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
#include "libssh/priv.h"
Packit 6c0a39
#include "libssh/ssh2.h"
Packit 6c0a39
#include "libssh/buffer.h"
Packit 6c0a39
#include "libssh/packet.h"
Packit 6c0a39
#include "libssh/socket.h"
Packit 6c0a39
#include "libssh/channels.h"
Packit 6c0a39
#include "libssh/session.h"
Packit 6c0a39
#include "libssh/misc.h"
Packit 6c0a39
#include "libssh/messages.h"
Packit 6c0a39
#if WITH_SERVER
Packit 6c0a39
#include "libssh/server.h"
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
#define WINDOWBASE 1280000
Packit 6c0a39
#define WINDOWLIMIT (WINDOWBASE/2)
Packit 6c0a39
Packit 6c0a39
/*
Packit 6c0a39
 * All implementations MUST be able to process packets with an
Packit 6c0a39
 * uncompressed payload length of 32768 bytes or less and a total packet
Packit 6c0a39
 * size of 35000 bytes or less.
Packit 6c0a39
 */
Packit 6c0a39
#define CHANNEL_MAX_PACKET 32768
Packit 6c0a39
#define CHANNEL_INITIAL_WINDOW 64000
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @defgroup libssh_channel The SSH channel functions
Packit 6c0a39
 * @ingroup libssh
Packit 6c0a39
 *
Packit 6c0a39
 * Functions that manage a SSH channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @{
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet);
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Allocate a new channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              A pointer to a newly allocated channel, NULL on error.
Packit 6c0a39
 */
Packit 6c0a39
ssh_channel ssh_channel_new(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    ssh_channel channel = NULL;
Packit 6c0a39
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Check if we have an authenticated session */
Packit 6c0a39
    if (!(session->flags & SSH_SESSION_FLAG_AUTHENTICATED)) {
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    channel = calloc(1, sizeof(struct ssh_channel_struct));
Packit 6c0a39
    if (channel == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    channel->stdout_buffer = ssh_buffer_new();
Packit 6c0a39
    if (channel->stdout_buffer == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        SAFE_FREE(channel);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    channel->stderr_buffer = ssh_buffer_new();
Packit 6c0a39
    if (channel->stderr_buffer == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        ssh_buffer_free(channel->stdout_buffer);
Packit 6c0a39
        SAFE_FREE(channel);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    channel->session = session;
Packit 6c0a39
    channel->exit_status = -1;
Packit 6c0a39
    channel->flags = SSH_CHANNEL_FLAG_NOT_BOUND;
Packit 6c0a39
Packit 6c0a39
    if (session->channels == NULL) {
Packit 6c0a39
        session->channels = ssh_list_new();
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    ssh_list_prepend(session->channels, channel);
Packit 6c0a39
Packit 6c0a39
    return channel;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Create a new channel identifier.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The SSH session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The new channel identifier.
Packit 6c0a39
 */
Packit 6c0a39
uint32_t ssh_channel_new_id(ssh_session session) {
Packit 6c0a39
  return ++(session->maxchannel);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handle a SSH_PACKET_CHANNEL_OPEN_CONFIRMATION packet.
Packit 6c0a39
 *
Packit 6c0a39
 * Constructs the channel object.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
Packit 6c0a39
  uint32_t channelid=0;
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  int rc;
Packit 6c0a39
  (void)type;
Packit 6c0a39
  (void)user;
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION");
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_unpack(packet, "d", &channelid);
Packit 6c0a39
  if (rc != SSH_OK)
Packit 6c0a39
      goto error;
Packit 6c0a39
  channel=ssh_channel_from_local(session,channelid);
Packit 6c0a39
  if(channel==NULL){
Packit 6c0a39
    ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
        "Unknown channel id %"PRIu32,
Packit 6c0a39
        (uint32_t) channelid);
Packit 6c0a39
    /* TODO: Set error marking in channel object */
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_unpack(packet, "ddd",
Packit 6c0a39
          &channel->remote_channel,
Packit 6c0a39
          &channel->remote_window,
Packit 6c0a39
          &channel->remote_maxpacket);
Packit 6c0a39
  if (rc != SSH_OK)
Packit 6c0a39
      goto error;
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
      "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d",
Packit 6c0a39
      channel->local_channel,
Packit 6c0a39
      channel->remote_channel);
Packit 6c0a39
Packit 6c0a39
  if (channel->state != SSH_CHANNEL_STATE_OPENING) {
Packit 6c0a39
      SSH_LOG(SSH_LOG_RARE,
Packit 6c0a39
              "SSH2_MSG_CHANNEL_OPEN_CONFIRMATION received in incorrect "
Packit 6c0a39
              "channel state %d",
Packit 6c0a39
              channel->state);
Packit 6c0a39
      goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
      "Remote window : %"PRIu32", maxpacket : %"PRIu32,
Packit 6c0a39
      (uint32_t) channel->remote_window,
Packit 6c0a39
      (uint32_t) channel->remote_maxpacket);
Packit 6c0a39
Packit 6c0a39
  channel->state = SSH_CHANNEL_STATE_OPEN;
Packit 6c0a39
  channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND;
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_set_error(session, SSH_FATAL, "Invalid packet");
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handle a SSH_CHANNEL_OPEN_FAILURE and set the state of the channel.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){
Packit 6c0a39
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  char *error = NULL;
Packit 6c0a39
  uint32_t code;
Packit 6c0a39
  int rc;
Packit 6c0a39
  (void)user;
Packit 6c0a39
  (void)type;
Packit 6c0a39
Packit 6c0a39
  channel=channel_from_msg(session,packet);
Packit 6c0a39
  if(channel==NULL){
Packit 6c0a39
    SSH_LOG(SSH_LOG_RARE,"Invalid channel in packet");
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_unpack(packet, "ds", &code, &error);
Packit 6c0a39
  if (rc != SSH_OK){
Packit 6c0a39
      ssh_set_error(session, SSH_FATAL, "Invalid packet");
Packit 6c0a39
      return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (channel->state != SSH_CHANNEL_STATE_OPENING) {
Packit 6c0a39
      SSH_LOG(SSH_LOG_RARE,
Packit 6c0a39
              "SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel "
Packit 6c0a39
              "state %d",
Packit 6c0a39
              channel->state);
Packit 6c0a39
      goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
      "Channel opening failure: channel %u error (%"PRIu32") %s",
Packit 6c0a39
      channel->local_channel,
Packit 6c0a39
      (uint32_t) code,
Packit 6c0a39
      error);
Packit 6c0a39
  SAFE_FREE(error);
Packit 6c0a39
  channel->state=SSH_CHANNEL_STATE_OPEN_DENIED;
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_set_error(session, SSH_FATAL, "Invalid packet");
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int ssh_channel_open_termination(void *c){
Packit 6c0a39
  ssh_channel channel = (ssh_channel) c;
Packit 6c0a39
  if (channel->state != SSH_CHANNEL_STATE_OPENING ||
Packit 6c0a39
      channel->session->session_state == SSH_SESSION_STATE_ERROR)
Packit 6c0a39
    return 1;
Packit 6c0a39
  else
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Open a channel by sending a SSH_OPEN_CHANNEL message and
Packit 6c0a39
 *        wait for the reply.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The current channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  type   A C string describing the kind of channel (e.g. "exec").
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  window   The receiving window of the channel. The window is the
Packit 6c0a39
 *                      maximum size of data that can stay in buffers and
Packit 6c0a39
 *                      network.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  maxpacket The maximum packet size allowed (like MTU).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  payload   The buffer containing additional payload for the query.
Packit 6c0a39
 *
Packit 6c0a39
 * @return             SSH_OK if successful; SSH_ERROR otherwise.
Packit 6c0a39
 */
Packit 6c0a39
static int channel_open(ssh_channel channel, const char *type, int window,
Packit 6c0a39
    int maxpacket, ssh_buffer payload) {
Packit 6c0a39
  ssh_session session = channel->session;
Packit 6c0a39
  int err=SSH_ERROR;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  switch(channel->state){
Packit 6c0a39
  case SSH_CHANNEL_STATE_NOT_OPEN:
Packit 6c0a39
    break;
Packit 6c0a39
  case SSH_CHANNEL_STATE_OPENING:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  case SSH_CHANNEL_STATE_OPEN:
Packit 6c0a39
  case SSH_CHANNEL_STATE_CLOSED:
Packit 6c0a39
  case SSH_CHANNEL_STATE_OPEN_DENIED:
Packit 6c0a39
    goto end;
Packit 6c0a39
  default:
Packit 6c0a39
    ssh_set_error(session,SSH_FATAL,"Bad state in channel_open: %d",channel->state);
Packit 6c0a39
  }
Packit 6c0a39
  channel->local_channel = ssh_channel_new_id(session);
Packit 6c0a39
  channel->local_maxpacket = maxpacket;
Packit 6c0a39
  channel->local_window = window;
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
      "Creating a channel %d with %d window and %d max packet",
Packit 6c0a39
      channel->local_channel, window, maxpacket);
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                       "bsddd",
Packit 6c0a39
                       SSH2_MSG_CHANNEL_OPEN,
Packit 6c0a39
                       type,
Packit 6c0a39
                       channel->local_channel,
Packit 6c0a39
                       channel->local_window,
Packit 6c0a39
                       channel->local_maxpacket);
Packit 6c0a39
  if (rc != SSH_OK){
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    return err;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (payload != NULL) {
Packit 6c0a39
    if (ssh_buffer_add_buffer(session->out_buffer, payload) < 0) {
Packit 6c0a39
      ssh_set_error_oom(session);
Packit 6c0a39
Packit 6c0a39
      return err;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
  channel->state = SSH_CHANNEL_STATE_OPENING;
Packit 6c0a39
  if (ssh_packet_send(session) == SSH_ERROR) {
Packit 6c0a39
Packit 6c0a39
    return err;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d",
Packit 6c0a39
      type, channel->local_channel);
Packit 6c0a39
pending:
Packit 6c0a39
  /* wait until channel is opened by server */
Packit 6c0a39
  err = ssh_handle_packets_termination(session,
Packit 6c0a39
                                       SSH_TIMEOUT_DEFAULT,
Packit 6c0a39
                                       ssh_channel_open_termination,
Packit 6c0a39
                                       channel);
Packit 6c0a39
Packit 6c0a39
  if (session->session_state == SSH_SESSION_STATE_ERROR)
Packit 6c0a39
    err = SSH_ERROR;
Packit 6c0a39
end:
Packit 6c0a39
  if(channel->state == SSH_CHANNEL_STATE_OPEN)
Packit 6c0a39
    err=SSH_OK;
Packit 6c0a39
Packit 6c0a39
  return err;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* return channel with corresponding local id, or NULL if not found */
Packit 6c0a39
ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) {
Packit 6c0a39
  struct ssh_iterator *it;
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
Packit 6c0a39
  for (it = ssh_list_get_iterator(session->channels); it != NULL ; it=it->next) {
Packit 6c0a39
    channel = ssh_iterator_value(ssh_channel, it);
Packit 6c0a39
    if (channel == NULL) {
Packit 6c0a39
      continue;
Packit 6c0a39
    }
Packit 6c0a39
    if (channel->local_channel == id) {
Packit 6c0a39
      return channel;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return NULL;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief grows the local window and send a packet to the other party
Packit 6c0a39
 * @param session SSH session
Packit 6c0a39
 * @param channel SSH channel
Packit 6c0a39
 * @param minimumsize The minimum acceptable size for the new window.
Packit 6c0a39
 * @return            SSH_OK if successful; SSH_ERROR otherwise.
Packit 6c0a39
 */
Packit 6c0a39
static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) {
Packit 6c0a39
  uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  if(new_window <= channel->local_window){
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
        "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)",
Packit 6c0a39
        channel->local_channel, channel->remote_channel, new_window,
Packit 6c0a39
        channel->local_window);
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
  }
Packit 6c0a39
  /* WINDOW_ADJUST packet needs a relative increment rather than an absolute
Packit 6c0a39
   * value, so we give here the missing bytes needed to reach new_window
Packit 6c0a39
   */
Packit 6c0a39
  rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                       "bdd",
Packit 6c0a39
                       SSH2_MSG_CHANNEL_WINDOW_ADJUST,
Packit 6c0a39
                       channel->remote_channel,
Packit 6c0a39
                       new_window - channel->local_window);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (ssh_packet_send(session) == SSH_ERROR) {
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
      "growing window (channel %d:%d) to %d bytes",
Packit 6c0a39
      channel->local_channel,
Packit 6c0a39
      channel->remote_channel,
Packit 6c0a39
      new_window);
Packit 6c0a39
Packit 6c0a39
  channel->local_window = new_window;
Packit 6c0a39
Packit 6c0a39
  return SSH_OK;
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
  return SSH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Parse a channel-related packet to resolve it to a ssh_channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The current SSH session.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  packet   The buffer to parse packet from. The read pointer will
Packit 6c0a39
 *                      be moved after the call.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The related ssh_channel, or NULL if the channel is
Packit 6c0a39
 *                      unknown or the packet is invalid.
Packit 6c0a39
 */
Packit 6c0a39
static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) {
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  uint32_t chan;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_unpack(packet,"d",&chan);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
        "Getting channel from message: short read");
Packit 6c0a39
    return NULL;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  channel = ssh_channel_from_local(session, chan);
Packit 6c0a39
  if (channel == NULL) {
Packit 6c0a39
    ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
        "Server specified invalid channel %"PRIu32,
Packit 6c0a39
        (uint32_t) chan);
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return channel;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
SSH_PACKET_CALLBACK(channel_rcv_change_window) {
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  uint32_t bytes;
Packit 6c0a39
  int rc;
Packit 6c0a39
  (void)user;
Packit 6c0a39
  (void)type;
Packit 6c0a39
Packit 6c0a39
  channel = channel_from_msg(session,packet);
Packit 6c0a39
  if (channel == NULL) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_unpack(packet, "d", &bytes);
Packit 6c0a39
  if (channel == NULL || rc != SSH_OK) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
        "Error getting a window adjust message: invalid packet");
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
      "Adding %d bytes to channel (%d:%d) (from %d bytes)",
Packit 6c0a39
      bytes,
Packit 6c0a39
      channel->local_channel,
Packit 6c0a39
      channel->remote_channel,
Packit 6c0a39
      channel->remote_window);
Packit 6c0a39
Packit 6c0a39
  channel->remote_window += bytes;
Packit 6c0a39
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* is_stderr is set to 1 if the data are extended, ie stderr */
Packit 6c0a39
SSH_PACKET_CALLBACK(channel_rcv_data){
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  ssh_string str;
Packit 6c0a39
  ssh_buffer buf;
Packit 6c0a39
  size_t len;
Packit 6c0a39
  int is_stderr;
Packit 6c0a39
  int rest;
Packit 6c0a39
  (void)user;
Packit 6c0a39
Packit 6c0a39
  if(type==SSH2_MSG_CHANNEL_DATA)
Packit 6c0a39
	  is_stderr=0;
Packit 6c0a39
  else
Packit 6c0a39
	  is_stderr=1;
Packit 6c0a39
Packit 6c0a39
  channel = channel_from_msg(session,packet);
Packit 6c0a39
  if (channel == NULL) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_FUNCTIONS,
Packit 6c0a39
        "%s", ssh_get_error(session));
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (is_stderr) {
Packit 6c0a39
    uint32_t ignore;
Packit 6c0a39
    /* uint32 data type code. we can ignore it */
Packit 6c0a39
    ssh_buffer_get_u32(packet, &ignore);
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  str = ssh_buffer_get_ssh_string(packet);
Packit 6c0a39
  if (str == NULL) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!");
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
  len = ssh_string_len(str);
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Channel receiving %" PRIdS " bytes data in %d (local win=%d remote win=%d)",
Packit 6c0a39
      len,
Packit 6c0a39
      is_stderr,
Packit 6c0a39
      channel->local_window,
Packit 6c0a39
      channel->remote_window);
Packit 6c0a39
Packit 6c0a39
  /* What shall we do in this case? Let's accept it anyway */
Packit 6c0a39
  if (len > channel->local_window) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_RARE,
Packit 6c0a39
        "Data packet too big for our window(%" PRIdS " vs %d)",
Packit 6c0a39
        len,
Packit 6c0a39
        channel->local_window);
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (channel_default_bufferize(channel, ssh_string_data(str), len,
Packit 6c0a39
        is_stderr) < 0) {
Packit 6c0a39
    ssh_string_free(str);
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (len <= channel->local_window) {
Packit 6c0a39
    channel->local_window -= len;
Packit 6c0a39
  } else {
Packit 6c0a39
    channel->local_window = 0; /* buggy remote */
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Channel windows are now (local win=%d remote win=%d)",
Packit 6c0a39
      channel->local_window,
Packit 6c0a39
      channel->remote_window);
Packit 6c0a39
Packit 6c0a39
  ssh_string_free(str);
Packit 6c0a39
Packit 6c0a39
  if (is_stderr) {
Packit 6c0a39
      buf = channel->stderr_buffer;
Packit 6c0a39
  } else {
Packit 6c0a39
      buf = channel->stdout_buffer;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  ssh_callbacks_iterate(channel->callbacks,
Packit 6c0a39
                        ssh_channel_callbacks,
Packit 6c0a39
                        channel_data_function) {
Packit 6c0a39
      if (ssh_buffer_get(buf) == NULL) {
Packit 6c0a39
          break;
Packit 6c0a39
      }
Packit 6c0a39
      rest = ssh_callbacks_iterate_exec(channel_data_function,
Packit 6c0a39
                                        channel->session,
Packit 6c0a39
                                        channel,
Packit 6c0a39
                                        ssh_buffer_get(buf),
Packit 6c0a39
                                        ssh_buffer_get_len(buf),
Packit 6c0a39
                                        is_stderr);
Packit 6c0a39
      if (rest > 0) {
Packit 6c0a39
          if (channel->counter != NULL) {
Packit 6c0a39
              channel->counter->in_bytes += rest;
Packit 6c0a39
          }
Packit 6c0a39
          ssh_buffer_pass_bytes(buf, rest);
Packit 6c0a39
      }
Packit 6c0a39
  }
Packit 6c0a39
  ssh_callbacks_iterate_end();
Packit 6c0a39
Packit 6c0a39
  if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) {
Packit 6c0a39
      if (grow_window(session, channel, 0) < 0) {
Packit 6c0a39
          return -1;
Packit 6c0a39
      }
Packit 6c0a39
  }
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
SSH_PACKET_CALLBACK(channel_rcv_eof) {
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  (void)user;
Packit 6c0a39
  (void)type;
Packit 6c0a39
Packit 6c0a39
  channel = channel_from_msg(session,packet);
Packit 6c0a39
  if (channel == NULL) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Received eof on channel (%d:%d)",
Packit 6c0a39
      channel->local_channel,
Packit 6c0a39
      channel->remote_channel);
Packit 6c0a39
  /* channel->remote_window = 0; */
Packit 6c0a39
  channel->remote_eof = 1;
Packit 6c0a39
Packit 6c0a39
  ssh_callbacks_execute_list(channel->callbacks,
Packit 6c0a39
                             ssh_channel_callbacks,
Packit 6c0a39
                             channel_eof_function,
Packit 6c0a39
                             channel->session,
Packit 6c0a39
                             channel);
Packit 6c0a39
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
SSH_PACKET_CALLBACK(channel_rcv_close) {
Packit 6c0a39
	ssh_channel channel;
Packit 6c0a39
	(void)user;
Packit 6c0a39
	(void)type;
Packit 6c0a39
Packit 6c0a39
	channel = channel_from_msg(session,packet);
Packit 6c0a39
	if (channel == NULL) {
Packit 6c0a39
		SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
Packit 6c0a39
Packit 6c0a39
		return SSH_PACKET_USED;
Packit 6c0a39
	}
Packit 6c0a39
Packit 6c0a39
	SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
			"Received close on channel (%d:%d)",
Packit 6c0a39
			channel->local_channel,
Packit 6c0a39
			channel->remote_channel);
Packit 6c0a39
Packit 6c0a39
	if ((channel->stdout_buffer &&
Packit 6c0a39
			ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
Packit 6c0a39
			(channel->stderr_buffer &&
Packit 6c0a39
					ssh_buffer_get_len(channel->stderr_buffer) > 0)) {
Packit 6c0a39
		channel->delayed_close = 1;
Packit 6c0a39
	} else {
Packit 6c0a39
		channel->state = SSH_CHANNEL_STATE_CLOSED;
Packit 6c0a39
	}
Packit 6c0a39
	if (channel->remote_eof == 0) {
Packit 6c0a39
		SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
				"Remote host not polite enough to send an eof before close");
Packit 6c0a39
	}
Packit 6c0a39
	channel->remote_eof = 1;
Packit 6c0a39
	/*
Packit 6c0a39
	 * The remote eof doesn't break things if there was still data into read
Packit 6c0a39
	 * buffer because the eof is ignored until the buffer is empty.
Packit 6c0a39
	 */
Packit 6c0a39
Packit 6c0a39
    ssh_callbacks_execute_list(channel->callbacks,
Packit 6c0a39
                               ssh_channel_callbacks,
Packit 6c0a39
                               channel_close_function,
Packit 6c0a39
                               channel->session,
Packit 6c0a39
                               channel);
Packit 6c0a39
Packit 6c0a39
	channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE;
Packit 6c0a39
	if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)
Packit 6c0a39
	  ssh_channel_do_free(channel);
Packit 6c0a39
Packit 6c0a39
	return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
SSH_PACKET_CALLBACK(channel_rcv_request) {
Packit 6c0a39
	ssh_channel channel;
Packit 6c0a39
	char *request=NULL;
Packit 6c0a39
    uint8_t status;
Packit 6c0a39
    int rc;
Packit 6c0a39
	(void)user;
Packit 6c0a39
	(void)type;
Packit 6c0a39
Packit 6c0a39
	channel = channel_from_msg(session,packet);
Packit 6c0a39
	if (channel == NULL) {
Packit 6c0a39
		SSH_LOG(SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session));
Packit 6c0a39
		return SSH_PACKET_USED;
Packit 6c0a39
	}
Packit 6c0a39
Packit 6c0a39
	rc = ssh_buffer_unpack(packet, "sb",
Packit 6c0a39
	        &request,
Packit 6c0a39
	        &status);
Packit 6c0a39
	if (rc != SSH_OK) {
Packit 6c0a39
		SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
Packit 6c0a39
		return SSH_PACKET_USED;
Packit 6c0a39
	}
Packit 6c0a39
Packit 6c0a39
	if (strcmp(request,"exit-status") == 0) {
Packit 6c0a39
        SAFE_FREE(request);
Packit 6c0a39
        rc = ssh_buffer_unpack(packet, "d", &channel->exit_status);
Packit 6c0a39
        if (rc != SSH_OK) {
Packit 6c0a39
            SSH_LOG(SSH_LOG_PACKET, "Invalid exit-status packet");
Packit 6c0a39
            return SSH_PACKET_USED;
Packit 6c0a39
        }
Packit 6c0a39
        SSH_LOG(SSH_LOG_PACKET, "received exit-status %d", channel->exit_status);
Packit 6c0a39
Packit 6c0a39
        ssh_callbacks_execute_list(channel->callbacks,
Packit 6c0a39
                                   ssh_channel_callbacks,
Packit 6c0a39
                                   channel_exit_status_function,
Packit 6c0a39
                                   channel->session,
Packit 6c0a39
                                   channel,
Packit 6c0a39
                                   channel->exit_status);
Packit 6c0a39
Packit 6c0a39
		return SSH_PACKET_USED;
Packit 6c0a39
	}
Packit 6c0a39
Packit 6c0a39
	if (strcmp(request,"signal") == 0) {
Packit 6c0a39
        char *sig = NULL;
Packit 6c0a39
Packit 6c0a39
		SAFE_FREE(request);
Packit 6c0a39
		SSH_LOG(SSH_LOG_PACKET, "received signal");
Packit 6c0a39
Packit 6c0a39
		rc = ssh_buffer_unpack(packet, "s", &sig);
Packit 6c0a39
		if (rc != SSH_OK) {
Packit 6c0a39
			SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
Packit 6c0a39
			return SSH_PACKET_USED;
Packit 6c0a39
		}
Packit 6c0a39
Packit 6c0a39
		SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
				"Remote connection sent a signal SIG %s", sig);
Packit 6c0a39
        ssh_callbacks_execute_list(channel->callbacks,
Packit 6c0a39
                                   ssh_channel_callbacks,
Packit 6c0a39
                                   channel_signal_function,
Packit 6c0a39
                                   channel->session,
Packit 6c0a39
                                   channel,
Packit 6c0a39
                                   sig);
Packit 6c0a39
		SAFE_FREE(sig);
Packit 6c0a39
Packit 6c0a39
		return SSH_PACKET_USED;
Packit 6c0a39
	}
Packit 6c0a39
Packit 6c0a39
	if (strcmp(request, "exit-signal") == 0) {
Packit 6c0a39
		const char *core = "(core dumped)";
Packit 6c0a39
		char *sig = NULL;
Packit 6c0a39
		char *errmsg = NULL;
Packit 6c0a39
		char *lang = NULL;
Packit 6c0a39
		uint8_t core_dumped;
Packit 6c0a39
Packit 6c0a39
		SAFE_FREE(request);
Packit 6c0a39
Packit 6c0a39
		rc = ssh_buffer_unpack(packet, "sbss",
Packit 6c0a39
		        &sig, /* signal name */
Packit 6c0a39
		        &core_dumped,    /* core dumped */
Packit 6c0a39
		        &errmsg, /* error message */
Packit 6c0a39
		        &lang);
Packit 6c0a39
		if (rc != SSH_OK) {
Packit 6c0a39
			SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
Packit 6c0a39
			return SSH_PACKET_USED;
Packit 6c0a39
		}
Packit 6c0a39
Packit 6c0a39
		if (core_dumped == 0) {
Packit 6c0a39
			core = "";
Packit 6c0a39
		}
Packit 6c0a39
Packit 6c0a39
		SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
				"Remote connection closed by signal SIG %s %s", sig, core);
Packit 6c0a39
		ssh_callbacks_execute_list(channel->callbacks,
Packit 6c0a39
                                   ssh_channel_callbacks,
Packit 6c0a39
                                   channel_exit_signal_function,
Packit 6c0a39
                                   channel->session,
Packit 6c0a39
                                   channel,
Packit 6c0a39
                                   sig,
Packit 6c0a39
                                   core_dumped,
Packit 6c0a39
                                   errmsg,
Packit 6c0a39
                                   lang);
Packit 6c0a39
Packit 6c0a39
        SAFE_FREE(lang);
Packit 6c0a39
        SAFE_FREE(errmsg);
Packit 6c0a39
		SAFE_FREE(sig);
Packit 6c0a39
Packit 6c0a39
		return SSH_PACKET_USED;
Packit 6c0a39
	}
Packit 6c0a39
	if(strcmp(request,"keepalive@openssh.com")==0){
Packit 6c0a39
	  SAFE_FREE(request);
Packit 6c0a39
	  SSH_LOG(SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive");
Packit 6c0a39
Packit 6c0a39
      rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                           "bd",
Packit 6c0a39
                           SSH2_MSG_CHANNEL_FAILURE,
Packit 6c0a39
                           channel->remote_channel);
Packit 6c0a39
      if (rc != SSH_OK) {
Packit 6c0a39
          return SSH_PACKET_USED;
Packit 6c0a39
      }
Packit 6c0a39
	  ssh_packet_send(session);
Packit 6c0a39
Packit 6c0a39
	  return SSH_PACKET_USED;
Packit 6c0a39
	}
Packit 6c0a39
Packit 6c0a39
  if (strcmp(request, "auth-agent-req@openssh.com") == 0) {
Packit 6c0a39
    SAFE_FREE(request);
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request");
Packit 6c0a39
    ssh_callbacks_execute_list(channel->callbacks,
Packit 6c0a39
                               ssh_channel_callbacks,
Packit 6c0a39
                               channel_auth_agent_req_function,
Packit 6c0a39
                               channel->session,
Packit 6c0a39
                               channel);
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
#ifdef WITH_SERVER
Packit 6c0a39
	/* If we are here, that means we have a request that is not in the understood
Packit 6c0a39
	 * client requests. That means we need to create a ssh message to be passed
Packit 6c0a39
	 * to the user code handling ssh messages
Packit 6c0a39
	 */
Packit 6c0a39
	ssh_message_handle_channel_request(session,channel,packet,request,status);
Packit 6c0a39
#else
Packit 6c0a39
    SSH_LOG(SSH_LOG_WARNING, "Unhandled channel request %s", request);
Packit 6c0a39
#endif
Packit 6c0a39
	
Packit 6c0a39
	SAFE_FREE(request);
Packit 6c0a39
Packit 6c0a39
	return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/*
Packit 6c0a39
 * When data has been received from the ssh server, it can be applied to the
Packit 6c0a39
 * known user function, with help of the callback, or inserted here
Packit 6c0a39
 *
Packit 6c0a39
 * FIXME is the window changed?
Packit 6c0a39
 */
Packit 6c0a39
int channel_default_bufferize(ssh_channel channel, void *data, int len,
Packit 6c0a39
    int is_stderr) {
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return -1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
Packit 6c0a39
  if(data == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(session);
Packit 6c0a39
      return -1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr);
Packit 6c0a39
  if (is_stderr == 0) {
Packit 6c0a39
    /* stdout */
Packit 6c0a39
    if (channel->stdout_buffer == NULL) {
Packit 6c0a39
      channel->stdout_buffer = ssh_buffer_new();
Packit 6c0a39
      if (channel->stdout_buffer == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        return -1;
Packit 6c0a39
      }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (ssh_buffer_add_data(channel->stdout_buffer, data, len) < 0) {
Packit 6c0a39
      ssh_set_error_oom(session);
Packit 6c0a39
      ssh_buffer_free(channel->stdout_buffer);
Packit 6c0a39
      channel->stdout_buffer = NULL;
Packit 6c0a39
      return -1;
Packit 6c0a39
    }
Packit 6c0a39
  } else {
Packit 6c0a39
    /* stderr */
Packit 6c0a39
    if (channel->stderr_buffer == NULL) {
Packit 6c0a39
      channel->stderr_buffer = ssh_buffer_new();
Packit 6c0a39
      if (channel->stderr_buffer == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        return -1;
Packit 6c0a39
      }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (ssh_buffer_add_data(channel->stderr_buffer, data, len) < 0) {
Packit 6c0a39
      ssh_set_error_oom(session);
Packit 6c0a39
      ssh_buffer_free(channel->stderr_buffer);
Packit 6c0a39
      channel->stderr_buffer = NULL;
Packit 6c0a39
      return -1;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Open a session channel (suited for a shell, not TCP forwarding).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  An allocated channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_open_forward()
Packit 6c0a39
 * @see ssh_channel_request_env()
Packit 6c0a39
 * @see ssh_channel_request_shell()
Packit 6c0a39
 * @see ssh_channel_request_exec()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_open_session(ssh_channel channel) {
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return channel_open(channel,
Packit 6c0a39
                      "session",
Packit 6c0a39
                      CHANNEL_INITIAL_WINDOW,
Packit 6c0a39
                      CHANNEL_MAX_PACKET,
Packit 6c0a39
                      NULL);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Open an agent authentication forwarding channel. This type of channel
Packit 6c0a39
 * can be opened by a server towards a client in order to provide SSH-Agent services
Packit 6c0a39
 * to the server-side process. This channel can only be opened if the client
Packit 6c0a39
 * claimed support by sending a channel request beforehand.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  An allocated channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_open_forward()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_open_auth_agent(ssh_channel channel){
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return channel_open(channel,
Packit 6c0a39
                      "auth-agent@openssh.com",
Packit 6c0a39
                      CHANNEL_INITIAL_WINDOW,
Packit 6c0a39
                      CHANNEL_MAX_PACKET,
Packit 6c0a39
                      NULL);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Open a TCP/IP forwarding channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  An allocated channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  remotehost The remote host to connected (host name or IP).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  remoteport The remote port.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  sourcehost The numeric IP address of the machine from where the
Packit 6c0a39
 *                        connection request originates. This is mostly for
Packit 6c0a39
 *                        logging purposes.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  localport  The port on the host from where the connection
Packit 6c0a39
 *                        originated. This is mostly for logging purposes.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning This function does not bind the local port and does not automatically
Packit 6c0a39
 *          forward the content of a socket to the channel. You still have to
Packit 6c0a39
 *          use channel_read and channel_write for this.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
Packit 6c0a39
    int remoteport, const char *sourcehost, int localport) {
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  ssh_buffer payload = NULL;
Packit 6c0a39
  ssh_string str = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
Packit 6c0a39
  if(remotehost == NULL || sourcehost == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  payload = ssh_buffer_new();
Packit 6c0a39
  if (payload == NULL) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(payload,
Packit 6c0a39
                       "sdsd",
Packit 6c0a39
                       remotehost,
Packit 6c0a39
                       remoteport,
Packit 6c0a39
                       sourcehost,
Packit 6c0a39
                       localport);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = channel_open(channel,
Packit 6c0a39
                    "direct-tcpip",
Packit 6c0a39
                    CHANNEL_INITIAL_WINDOW,
Packit 6c0a39
                    CHANNEL_MAX_PACKET,
Packit 6c0a39
                    payload);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(payload);
Packit 6c0a39
  ssh_string_free(str);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Open a TCP/IP - UNIX domain socket forwarding channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  An allocated channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  remotepath   The UNIX socket path on the remote machine
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  sourcehost   The numeric IP address of the machine from where the
Packit 6c0a39
 *                          connection request originates. This is mostly for
Packit 6c0a39
 *                          logging purposes.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  localport    The port on the host from where the connection
Packit 6c0a39
 *                          originated. This is mostly for logging purposes.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning This function does not bind the local port and does not
Packit 6c0a39
 *          automatically forward the content of a socket to the channel.
Packit 6c0a39
 *          You still have to use channel_read and channel_write for this.
Packit 6c0a39
 * @warning Requires support of OpenSSH for UNIX domain socket forwarding.
Packit 6c0a39
  */
Packit 6c0a39
int ssh_channel_open_forward_unix(ssh_channel channel,
Packit 6c0a39
                                  const char *remotepath,
Packit 6c0a39
                                  const char *sourcehost,
Packit 6c0a39
                                  int localport)
Packit 6c0a39
{
Packit 6c0a39
    ssh_session session = NULL;
Packit 6c0a39
    ssh_buffer payload = NULL;
Packit 6c0a39
    ssh_string str = NULL;
Packit 6c0a39
    int rc = SSH_ERROR;
Packit 6c0a39
    int version;
Packit 6c0a39
Packit 6c0a39
    if (channel == NULL) {
Packit 6c0a39
        return rc;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session = channel->session;
Packit 6c0a39
Packit 6c0a39
    version = ssh_get_openssh_version(session);
Packit 6c0a39
    if (version == 0) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_REQUEST_DENIED,
Packit 6c0a39
                      "We're not connected to an OpenSSH server!");
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (remotepath == NULL || sourcehost == NULL) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return rc;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    payload = ssh_buffer_new();
Packit 6c0a39
    if (payload == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_pack(payload,
Packit 6c0a39
                         "ssd",
Packit 6c0a39
                         remotepath,
Packit 6c0a39
                         sourcehost,
Packit 6c0a39
                         localport);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = channel_open(channel,
Packit 6c0a39
                      "direct-streamlocal@openssh.com",
Packit 6c0a39
                      CHANNEL_INITIAL_WINDOW,
Packit 6c0a39
                      CHANNEL_MAX_PACKET,
Packit 6c0a39
                      payload);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
    ssh_buffer_free(payload);
Packit 6c0a39
    ssh_string_free(str);
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Close and free a channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to free.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning Any data unread on this channel will be lost.
Packit 6c0a39
 */
Packit 6c0a39
void ssh_channel_free(ssh_channel channel)
Packit 6c0a39
{
Packit 6c0a39
    ssh_session session;
Packit 6c0a39
Packit 6c0a39
    if (channel == NULL) {
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session = channel->session;
Packit 6c0a39
    if (session->alive) {
Packit 6c0a39
        bool send_close = false;
Packit 6c0a39
Packit 6c0a39
        switch (channel->state) {
Packit 6c0a39
        case SSH_CHANNEL_STATE_OPEN:
Packit 6c0a39
            send_close = true;
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_CHANNEL_STATE_CLOSED:
Packit 6c0a39
            if (channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) {
Packit 6c0a39
                send_close = true;
Packit 6c0a39
            }
Packit 6c0a39
            if (channel->flags & SSH_CHANNEL_FLAG_CLOSED_LOCAL) {
Packit 6c0a39
                send_close = false;
Packit 6c0a39
            }
Packit 6c0a39
            break;
Packit 6c0a39
        default:
Packit 6c0a39
            send_close = false;
Packit 6c0a39
            break;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        if (send_close) {
Packit 6c0a39
            ssh_channel_close(channel);
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
    channel->flags |= SSH_CHANNEL_FLAG_FREED_LOCAL;
Packit 6c0a39
Packit 6c0a39
    /* The idea behind the flags is the following : it is well possible
Packit 6c0a39
     * that a client closes a channel that stills exists on the server side.
Packit 6c0a39
     * We definitively close the channel when we receive a close message *and*
Packit 6c0a39
     * the user closed it.
Packit 6c0a39
     */
Packit 6c0a39
    if ((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
Packit 6c0a39
        (channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)) {
Packit 6c0a39
        ssh_channel_do_free(channel);
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief Effectively free a channel, without caring about flags
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
void ssh_channel_do_free(ssh_channel channel)
Packit 6c0a39
{
Packit 6c0a39
    struct ssh_iterator *it = NULL;
Packit 6c0a39
    ssh_session session = channel->session;
Packit 6c0a39
Packit 6c0a39
    it = ssh_list_find(session->channels, channel);
Packit 6c0a39
    if (it != NULL) {
Packit 6c0a39
        ssh_list_remove(session->channels, it);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SSH_BUFFER_FREE(channel->stdout_buffer);
Packit 6c0a39
    SSH_BUFFER_FREE(channel->stderr_buffer);
Packit 6c0a39
Packit 6c0a39
    if (channel->callbacks != NULL) {
Packit 6c0a39
        ssh_list_free(channel->callbacks);
Packit 6c0a39
        channel->callbacks = NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    channel->session = NULL;
Packit 6c0a39
    SAFE_FREE(channel);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Send an end of file on the channel.
Packit 6c0a39
 *
Packit 6c0a39
 * This doesn't close the channel. You may still read from it but not write.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send the eof to.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success, SSH_ERROR if an error occurred.
Packit 6c0a39
 *
Packit 6c0a39
 * Example:
Packit 6c0a39
@code
Packit 6c0a39
   rc = ssh_channel_send_eof(channel);
Packit 6c0a39
   if (rc == SSH_ERROR) {
Packit 6c0a39
       return -1;
Packit 6c0a39
   }
Packit 6c0a39
   while(!ssh_channel_is_eof(channel)) {
Packit 6c0a39
       rc = ssh_channel_read(channel, buf, sizeof(buf), 0);
Packit 6c0a39
       if (rc == SSH_ERROR) {
Packit 6c0a39
           return -1;
Packit 6c0a39
       }
Packit 6c0a39
   }
Packit 6c0a39
   ssh_channel_close(channel);
Packit 6c0a39
@endcode
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_close()
Packit 6c0a39
 * @see ssh_channel_free()
Packit 6c0a39
 * @see ssh_channel_is_eof()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_send_eof(ssh_channel channel)
Packit 6c0a39
{
Packit 6c0a39
    ssh_session session;
Packit 6c0a39
    int rc = SSH_ERROR;
Packit 6c0a39
    int err;
Packit 6c0a39
Packit 6c0a39
    if (channel == NULL || channel->session == NULL) {
Packit 6c0a39
        return rc;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* If the EOF has already been sent we're done here. */
Packit 6c0a39
    if (channel->local_eof != 0) {
Packit 6c0a39
        return SSH_OK;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session = channel->session;
Packit 6c0a39
Packit 6c0a39
    err = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                          "bd",
Packit 6c0a39
                          SSH2_MSG_CHANNEL_EOF,
Packit 6c0a39
                          channel->remote_channel);
Packit 6c0a39
    if (err != SSH_OK) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
        "Sent a EOF on client channel (%d:%d)",
Packit 6c0a39
        channel->local_channel,
Packit 6c0a39
        channel->remote_channel);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_channel_flush(channel);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    channel->local_eof = 1;
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
error:
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Close a channel.
Packit 6c0a39
 *
Packit 6c0a39
 * This sends an end of file and then closes the channel. You won't be able
Packit 6c0a39
 * to recover any data the server was going to send or was in buffers.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to close.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success, SSH_ERROR if an error occurred.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_free()
Packit 6c0a39
 * @see ssh_channel_is_eof()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_close(ssh_channel channel)
Packit 6c0a39
{
Packit 6c0a39
    ssh_session session;
Packit 6c0a39
    int rc = 0;
Packit 6c0a39
Packit 6c0a39
    if(channel == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* If the channel close has already been sent we're done here. */
Packit 6c0a39
    if (channel->flags & SSH_CHANNEL_FLAG_CLOSED_LOCAL) {
Packit 6c0a39
        return SSH_OK;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session = channel->session;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_channel_send_eof(channel);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        return rc;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                         "bd",
Packit 6c0a39
                         SSH2_MSG_CHANNEL_CLOSE,
Packit 6c0a39
                         channel->remote_channel);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
            "Sent a close on client channel (%d:%d)",
Packit 6c0a39
            channel->local_channel,
Packit 6c0a39
            channel->remote_channel);
Packit 6c0a39
Packit 6c0a39
    if (rc == SSH_OK) {
Packit 6c0a39
        channel->state = SSH_CHANNEL_STATE_CLOSED;
Packit 6c0a39
        channel->flags |= SSH_CHANNEL_FLAG_CLOSED_LOCAL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_channel_flush(channel);
Packit 6c0a39
    if(rc == SSH_ERROR) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
error:
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* this termination function waits for a window growing condition */
Packit 6c0a39
static int ssh_channel_waitwindow_termination(void *c){
Packit 6c0a39
  ssh_channel channel = (ssh_channel) c;
Packit 6c0a39
  if (channel->remote_window > 0 ||
Packit 6c0a39
      channel->session->session_state == SSH_SESSION_STATE_ERROR ||
Packit 6c0a39
      channel->state == SSH_CHANNEL_STATE_CLOSED)
Packit 6c0a39
    return 1;
Packit 6c0a39
  else
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* This termination function waits until the session is not in blocked status
Packit 6c0a39
 * anymore, e.g. because of a key re-exchange.
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_waitsession_unblocked(void *s){
Packit 6c0a39
    ssh_session session = (ssh_session)s;
Packit 6c0a39
    switch (session->session_state){
Packit 6c0a39
        case SSH_SESSION_STATE_DH:
Packit 6c0a39
        case SSH_SESSION_STATE_INITIAL_KEX:
Packit 6c0a39
        case SSH_SESSION_STATE_KEXINIT_RECEIVED:
Packit 6c0a39
            return 0;
Packit 6c0a39
        default:
Packit 6c0a39
            return 1;
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief Flushes a channel (and its session) until the output buffer
Packit 6c0a39
 *        is empty, or timeout elapsed.
Packit 6c0a39
 * @param channel SSH channel
Packit 6c0a39
 * @return  SSH_OK On success,
Packit 6c0a39
 *          SSH_ERROR On error.
Packit 6c0a39
 *          SSH_AGAIN Timeout elapsed (or in nonblocking mode).
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_flush(ssh_channel channel){
Packit 6c0a39
  return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int channel_write_common(ssh_channel channel,
Packit 6c0a39
                                const void *data,
Packit 6c0a39
                                uint32_t len, int is_stderr)
Packit 6c0a39
{
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  uint32_t origlen = len;
Packit 6c0a39
  size_t effectivelen;
Packit 6c0a39
  size_t maxpacketlen;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return -1;
Packit 6c0a39
  }
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
  if(data == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(session);
Packit 6c0a39
      return -1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (len > INT_MAX) {
Packit 6c0a39
      SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
              "Length (%u) is bigger than INT_MAX", len);
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /*
Packit 6c0a39
   * Handle the max packet len from remote side, be nice
Packit 6c0a39
   * 10 bytes for the headers
Packit 6c0a39
   */
Packit 6c0a39
  maxpacketlen = channel->remote_maxpacket - 10;
Packit 6c0a39
Packit 6c0a39
  if (channel->local_eof) {
Packit 6c0a39
    ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
        "Can't write to channel %d:%d  after EOF was sent",
Packit 6c0a39
        channel->local_channel,
Packit 6c0a39
        channel->remote_channel);
Packit 6c0a39
    return -1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (channel->state != SSH_CHANNEL_STATE_OPEN || channel->delayed_close != 0) {
Packit 6c0a39
    ssh_set_error(session, SSH_REQUEST_DENIED, "Remote channel is closed");
Packit 6c0a39
Packit 6c0a39
    return -1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (session->session_state == SSH_SESSION_STATE_ERROR) {
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (ssh_waitsession_unblocked(session) == 0){
Packit 6c0a39
    rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT,
Packit 6c0a39
            ssh_waitsession_unblocked, session);
Packit 6c0a39
    if (rc == SSH_ERROR || !ssh_waitsession_unblocked(session))
Packit 6c0a39
        goto out;
Packit 6c0a39
  }
Packit 6c0a39
  while (len > 0) {
Packit 6c0a39
    if (channel->remote_window < len) {
Packit 6c0a39
      SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
          "Remote window is %d bytes. going to write %d bytes",
Packit 6c0a39
          channel->remote_window,
Packit 6c0a39
          len);
Packit 6c0a39
      /* What happens when the channel window is zero? */
Packit 6c0a39
      if(channel->remote_window == 0) {
Packit 6c0a39
          /* nothing can be written */
Packit 6c0a39
          SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
                "Wait for a growing window message...");
Packit 6c0a39
          rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT,
Packit 6c0a39
              ssh_channel_waitwindow_termination,channel);
Packit 6c0a39
          if (rc == SSH_ERROR ||
Packit 6c0a39
              !ssh_channel_waitwindow_termination(channel) ||
Packit 6c0a39
              session->session_state == SSH_SESSION_STATE_ERROR ||
Packit 6c0a39
              channel->state == SSH_CHANNEL_STATE_CLOSED)
Packit 6c0a39
            goto out;
Packit 6c0a39
          continue;
Packit 6c0a39
      }
Packit 6c0a39
      effectivelen = MIN(len, channel->remote_window);
Packit 6c0a39
    } else {
Packit 6c0a39
      effectivelen = len;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    effectivelen = MIN(effectivelen, maxpacketlen);;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                         "bd",
Packit 6c0a39
                         is_stderr ? SSH2_MSG_CHANNEL_EXTENDED_DATA : SSH2_MSG_CHANNEL_DATA,
Packit 6c0a39
                         channel->remote_channel);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* stderr message has an extra field */
Packit 6c0a39
    if (is_stderr) {
Packit 6c0a39
        rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                             "d",
Packit 6c0a39
                             SSH2_EXTENDED_DATA_STDERR);
Packit 6c0a39
        if (rc != SSH_OK) {
Packit 6c0a39
            ssh_set_error_oom(session);
Packit 6c0a39
            goto error;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* append payload data */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                         "dP",
Packit 6c0a39
                         effectivelen,
Packit 6c0a39
                         (size_t)effectivelen, data);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
        "channel_write wrote %ld bytes", (long int) effectivelen);
Packit 6c0a39
Packit 6c0a39
    channel->remote_window -= effectivelen;
Packit 6c0a39
    len -= effectivelen;
Packit 6c0a39
    data = ((uint8_t*)data + effectivelen);
Packit 6c0a39
    if (channel->counter != NULL) {
Packit 6c0a39
        channel->counter->out_bytes += effectivelen;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /* it's a good idea to flush the socket now */
Packit 6c0a39
  rc = ssh_channel_flush(channel);
Packit 6c0a39
  if (rc == SSH_ERROR) {
Packit 6c0a39
      goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
out:
Packit 6c0a39
  return (int)(origlen - len);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
  return SSH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the remote window size.
Packit 6c0a39
 *
Packit 6c0a39
 * This is the maximum amounts of bytes the remote side expects us to send
Packit 6c0a39
 * before growing the window again.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] channel The channel to query.
Packit 6c0a39
 *
Packit 6c0a39
 * @return            The remote window size
Packit 6c0a39
 *
Packit 6c0a39
 * @warning A nonzero return value does not guarantee the socket is ready
Packit 6c0a39
 *          to send that much data. Buffering may happen in the local SSH
Packit 6c0a39
 *          packet buffer, so beware of really big window sizes.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning A zero return value means ssh_channel_write (default settings)
Packit 6c0a39
 *          will block until the window grows back.
Packit 6c0a39
 */
Packit 6c0a39
uint32_t ssh_channel_window_size(ssh_channel channel) {
Packit 6c0a39
    return channel->remote_window;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Blocking write on a channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to write to.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  data     A pointer to the data to write.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  len      The length of the buffer to write to.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes written, SSH_ERROR on error.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_read()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) {
Packit 6c0a39
  return channel_write_common(channel, data, len, 0);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Check if the channel is open or not.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to check.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              0 if channel is closed, nonzero otherwise.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_is_closed()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_is_open(ssh_channel channel) {
Packit 6c0a39
    if(channel == NULL) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
    return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Check if the channel is closed or not.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to check.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              0 if channel is opened, nonzero otherwise.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_is_open()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_is_closed(ssh_channel channel) {
Packit 6c0a39
    if(channel == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    return (channel->state != SSH_CHANNEL_STATE_OPEN || channel->session->alive == 0);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Check if remote has sent an EOF.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to check.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              0 if there is no EOF, nonzero otherwise.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_is_eof(ssh_channel channel) {
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if ((channel->stdout_buffer &&
Packit 6c0a39
        ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
Packit 6c0a39
      (channel->stderr_buffer &&
Packit 6c0a39
       ssh_buffer_get_len(channel->stderr_buffer) > 0)) {
Packit 6c0a39
    return 0;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return (channel->remote_eof != 0);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Put the channel into blocking or nonblocking mode.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  blocking A boolean for blocking or nonblocking.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning    A side-effect of this is to put the whole session
Packit 6c0a39
 *             in non-blocking mode.
Packit 6c0a39
 * @see ssh_set_blocking()
Packit 6c0a39
 */
Packit 6c0a39
void ssh_channel_set_blocking(ssh_channel channel, int blocking) {
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return;
Packit 6c0a39
  }
Packit 6c0a39
  ssh_set_blocking(channel->session,blocking);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief handle a SSH_CHANNEL_SUCCESS packet and set the channel state.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_channel_success){
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  (void)type;
Packit 6c0a39
  (void)user;
Packit 6c0a39
Packit 6c0a39
  channel=channel_from_msg(session,packet);
Packit 6c0a39
  if (channel == NULL) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Received SSH_CHANNEL_SUCCESS on channel (%d:%d)",
Packit 6c0a39
      channel->local_channel,
Packit 6c0a39
      channel->remote_channel);
Packit 6c0a39
  if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
Packit 6c0a39
    SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_SUCCESS received in incorrect state %d",
Packit 6c0a39
        channel->request_state);
Packit 6c0a39
  } else {
Packit 6c0a39
    channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handle a SSH_CHANNEL_FAILURE packet and set the channel state.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_channel_failure){
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  (void)type;
Packit 6c0a39
  (void)user;
Packit 6c0a39
Packit 6c0a39
  channel=channel_from_msg(session,packet);
Packit 6c0a39
  if (channel == NULL) {
Packit 6c0a39
    SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Received SSH_CHANNEL_FAILURE on channel (%d:%d)",
Packit 6c0a39
      channel->local_channel,
Packit 6c0a39
      channel->remote_channel);
Packit 6c0a39
  if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
Packit 6c0a39
    SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_FAILURE received in incorrect state %d",
Packit 6c0a39
        channel->request_state);
Packit 6c0a39
  } else {
Packit 6c0a39
    channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int ssh_channel_request_termination(void *c){
Packit 6c0a39
  ssh_channel channel = (ssh_channel)c;
Packit 6c0a39
  if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING ||
Packit 6c0a39
      channel->session->session_state == SSH_SESSION_STATE_ERROR)
Packit 6c0a39
    return 1;
Packit 6c0a39
  else
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int channel_request(ssh_channel channel, const char *request,
Packit 6c0a39
    ssh_buffer buffer, int reply) {
Packit 6c0a39
  ssh_session session = channel->session;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
  int ret;
Packit 6c0a39
Packit 6c0a39
  switch(channel->request_state){
Packit 6c0a39
  case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
    break;
Packit 6c0a39
  default:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  ret = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                        "bdsb",
Packit 6c0a39
                        SSH2_MSG_CHANNEL_REQUEST,
Packit 6c0a39
                        channel->remote_channel,
Packit 6c0a39
                        request,
Packit 6c0a39
                        reply == 0 ? 0 : 1);
Packit 6c0a39
  if (ret != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (buffer != NULL) {
Packit 6c0a39
    if (ssh_buffer_add_data(session->out_buffer, ssh_buffer_get(buffer),
Packit 6c0a39
        ssh_buffer_get_len(buffer)) < 0) {
Packit 6c0a39
      ssh_set_error_oom(session);
Packit 6c0a39
      goto error;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
  channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING;
Packit 6c0a39
  if (ssh_packet_send(session) == SSH_ERROR) {
Packit 6c0a39
    return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Sent a SSH_MSG_CHANNEL_REQUEST %s", request);
Packit 6c0a39
  if (reply == 0) {
Packit 6c0a39
    channel->request_state = SSH_CHANNEL_REQ_STATE_NONE;
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = ssh_handle_packets_termination(session,
Packit 6c0a39
                                      SSH_TIMEOUT_DEFAULT,
Packit 6c0a39
                                      ssh_channel_request_termination,
Packit 6c0a39
                                      channel);
Packit 6c0a39
Packit 6c0a39
  if(session->session_state == SSH_SESSION_STATE_ERROR || rc == SSH_ERROR) {
Packit 6c0a39
      channel->request_state = SSH_CHANNEL_REQ_STATE_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  /* we received something */
Packit 6c0a39
  switch (channel->request_state){
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_ERROR:
Packit 6c0a39
      rc=SSH_ERROR;
Packit 6c0a39
      break;
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_DENIED:
Packit 6c0a39
      ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
          "Channel request %s failed", request);
Packit 6c0a39
      rc=SSH_ERROR;
Packit 6c0a39
      break;
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_ACCEPTED:
Packit 6c0a39
      SSH_LOG(SSH_LOG_PROTOCOL,
Packit 6c0a39
          "Channel request %s success",request);
Packit 6c0a39
      rc=SSH_OK;
Packit 6c0a39
      break;
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_PENDING:
Packit 6c0a39
      rc = SSH_AGAIN;
Packit 6c0a39
      return rc;
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
      /* Never reached */
Packit 6c0a39
      ssh_set_error(session, SSH_FATAL, "Invalid state in channel_request()");
Packit 6c0a39
      rc=SSH_ERROR;
Packit 6c0a39
      break;
Packit 6c0a39
  }
Packit 6c0a39
  channel->request_state=SSH_CHANNEL_REQ_STATE_NONE;
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Request a pty with a specific type and size.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to sent the request.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  terminal The terminal type ("vt100, xterm,...").
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  col      The number of columns.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  row      The number of rows.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
Packit 6c0a39
    int col, int row) {
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
Packit 6c0a39
  if(terminal == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  switch(channel->request_state){
Packit 6c0a39
  case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
    break;
Packit 6c0a39
  default:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer,
Packit 6c0a39
                       "sdddddb",
Packit 6c0a39
                       terminal,
Packit 6c0a39
                       col,
Packit 6c0a39
                       row,
Packit 6c0a39
                       0, /* pix */
Packit 6c0a39
                       0, /* pix */
Packit 6c0a39
                       1, /* add a 0byte string */
Packit 6c0a39
                       0);
Packit 6c0a39
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = channel_request(channel, "pty-req", buffer, 1);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Request a PTY.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send the request.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_request_pty_size()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_pty(ssh_channel channel) {
Packit 6c0a39
  return ssh_channel_request_pty_size(channel, "xterm", 80, 24);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Change the size of the terminal associated to a channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to change the size.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  cols     The new number of columns.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  rows     The new number of rows.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success, SSH_ERROR if an error occurred.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning Do not call it from a signal handler if you are not sure any other
Packit 6c0a39
 *          libssh function using the same channel/session is running at same
Packit 6c0a39
 *          time (not 100% threadsafe).
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) {
Packit 6c0a39
  ssh_session session = channel->session;
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer,
Packit 6c0a39
                       "dddd",
Packit 6c0a39
                       cols,
Packit 6c0a39
                       rows,
Packit 6c0a39
                       0, /* pix */
Packit 6c0a39
                       0 /* pix */);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = channel_request(channel, "window-change", buffer, 0);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Request a shell.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send the request.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_shell(ssh_channel channel) {
Packit 6c0a39
    if(channel == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return channel_request(channel, "shell", NULL, 1);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Request a subsystem (for example "sftp").
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send the request.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  subsys   The subsystem to request (for example "sftp").
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning You normally don't have to call it for sftp, see sftp_new().
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if(subsys == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
  switch(channel->request_state){
Packit 6c0a39
  case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
    break;
Packit 6c0a39
  default:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer, "s", subsys);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = channel_request(channel, "subsystem", buffer, 1);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
int ssh_channel_request_sftp( ssh_channel channel){
Packit 6c0a39
    if(channel == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    return ssh_channel_request_subsystem(channel, "sftp");
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static char *generate_cookie(void) {
Packit 6c0a39
  static const char *hex = "0123456789abcdef";
Packit 6c0a39
  char s[36];
Packit 6c0a39
  unsigned char rnd[16];
Packit 6c0a39
  int ok;
Packit 6c0a39
  int i;
Packit 6c0a39
Packit 6c0a39
  ok = ssh_get_random(rnd, sizeof(rnd), 0);
Packit 6c0a39
  if (!ok) {
Packit 6c0a39
      return NULL;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  for (i = 0; i < 16; i++) {
Packit 6c0a39
    s[i*2] = hex[rnd[i] & 0x0f];
Packit 6c0a39
    s[i*2+1] = hex[rnd[i] >> 4];
Packit 6c0a39
  }
Packit 6c0a39
  s[32] = '\0';
Packit 6c0a39
  return strdup(s);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Sends the "x11-req" channel request over an existing session channel.
Packit 6c0a39
 *
Packit 6c0a39
 * This will enable redirecting the display of the remote X11 applications to
Packit 6c0a39
 * local X server over an secure tunnel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  An existing session channel where the remote X11
Packit 6c0a39
 *                      applications are going to be executed.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  single_connection A boolean to mark only one X11 app will be
Packit 6c0a39
 *                               redirected.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  protocol A x11 authentication protocol. Pass NULL to use the
Packit 6c0a39
 *                      default value MIT-MAGIC-COOKIE-1.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  cookie   A x11 authentication cookie. Pass NULL to generate
Packit 6c0a39
 *                      a random cookie.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] screen_number The screen number.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol,
Packit 6c0a39
    const char *cookie, int screen_number) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  char *c = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  switch(channel->request_state){
Packit 6c0a39
  case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
    break;
Packit 6c0a39
  default:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (cookie == NULL) {
Packit 6c0a39
    c = generate_cookie();
Packit 6c0a39
    if (c == NULL) {
Packit 6c0a39
      ssh_set_error_oom(channel->session);
Packit 6c0a39
      goto error;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer,
Packit 6c0a39
                       "bssd",
Packit 6c0a39
                       single_connection == 0 ? 0 : 1,
Packit 6c0a39
                       protocol ? protocol : "MIT-MAGIC-COOKIE-1",
Packit 6c0a39
                       cookie ? cookie : c,
Packit 6c0a39
                       screen_number);
Packit 6c0a39
  if (c != NULL){
Packit 6c0a39
      SAFE_FREE(c);
Packit 6c0a39
  }
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = channel_request(channel, "x11-req", buffer, 1);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
Packit 6c0a39
    int timeout_ms, int *destination_port) {
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
  static const struct timespec ts = {
Packit 6c0a39
    .tv_sec = 0,
Packit 6c0a39
    .tv_nsec = 50000000 /* 50ms */
Packit 6c0a39
  };
Packit 6c0a39
#endif
Packit 6c0a39
  ssh_message msg = NULL;
Packit 6c0a39
  ssh_channel channel = NULL;
Packit 6c0a39
  struct ssh_iterator *iterator;
Packit 6c0a39
  int t;
Packit 6c0a39
Packit 6c0a39
  /*
Packit 6c0a39
   * We sleep for 50 ms in ssh_handle_packets() and later sleep for
Packit 6c0a39
   * 50 ms. So we need to decrement by 100 ms.
Packit 6c0a39
   */
Packit 6c0a39
  for (t = timeout_ms; t >= 0; t -= 100) {
Packit 6c0a39
    if (timeout_ms == 0) {
Packit 6c0a39
        ssh_handle_packets(session, 0);
Packit 6c0a39
    } else {
Packit 6c0a39
        ssh_handle_packets(session, 50);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->ssh_message_list) {
Packit 6c0a39
      iterator = ssh_list_get_iterator(session->ssh_message_list);
Packit 6c0a39
      while (iterator) {
Packit 6c0a39
        msg = (ssh_message)iterator->data;
Packit 6c0a39
        if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL_OPEN &&
Packit 6c0a39
            ssh_message_subtype(msg) == channeltype) {
Packit 6c0a39
          ssh_list_remove(session->ssh_message_list, iterator);
Packit 6c0a39
          channel = ssh_message_channel_request_open_reply_accept(msg);
Packit 6c0a39
          if(destination_port) {
Packit 6c0a39
            *destination_port=msg->channel_request_open.destination_port;
Packit 6c0a39
          }
Packit 6c0a39
Packit 6c0a39
          ssh_message_free(msg);
Packit 6c0a39
          return channel;
Packit 6c0a39
        }
Packit 6c0a39
        iterator = iterator->next;
Packit 6c0a39
      }
Packit 6c0a39
    }
Packit 6c0a39
    if(t>0){
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
      Sleep(50); /* 50ms */
Packit 6c0a39
#else
Packit 6c0a39
      nanosleep(&ts, NULL);
Packit 6c0a39
#endif
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  ssh_set_error(session, SSH_NO_ERROR, "No channel request of this type from server");
Packit 6c0a39
  return NULL;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Accept an X11 forwarding channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  An x11-enabled session channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  timeout_ms Timeout in milliseconds.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              A newly created channel, or NULL if no X11 request from
Packit 6c0a39
 *                      the server.
Packit 6c0a39
 */
Packit 6c0a39
ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) {
Packit 6c0a39
  return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Send an "auth-agent-req" channel request over an existing session channel.
Packit 6c0a39
 *
Packit 6c0a39
 * This client-side request will enable forwarding the agent over an secure tunnel.
Packit 6c0a39
 * When the server is ready to open one authentication agent channel, an
Packit 6c0a39
 * ssh_channel_open_request_auth_agent_callback event will be generated.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send signal.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success, SSH_ERROR if an error occurred
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_auth_agent(ssh_channel channel) {
Packit 6c0a39
  if (channel == NULL) {
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return channel_request(channel, "auth-agent-req@openssh.com", NULL, 0);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handle a SSH_REQUEST_SUCCESS packet normally sent after a global
Packit 6c0a39
 * request.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_request_success){
Packit 6c0a39
  (void)type;
Packit 6c0a39
  (void)user;
Packit 6c0a39
  (void)packet;
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Received SSH_REQUEST_SUCCESS");
Packit 6c0a39
  if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){
Packit 6c0a39
    SSH_LOG(SSH_LOG_RARE, "SSH_REQUEST_SUCCESS received in incorrect state %d",
Packit 6c0a39
        session->global_req_state);
Packit 6c0a39
  } else {
Packit 6c0a39
    session->global_req_state=SSH_CHANNEL_REQ_STATE_ACCEPTED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handle a SSH_REQUEST_DENIED packet normally sent after a global
Packit 6c0a39
 * request.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_request_denied){
Packit 6c0a39
  (void)type;
Packit 6c0a39
  (void)user;
Packit 6c0a39
  (void)packet;
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Received SSH_REQUEST_FAILURE");
Packit 6c0a39
  if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){
Packit 6c0a39
    SSH_LOG(SSH_LOG_RARE, "SSH_REQUEST_DENIED received in incorrect state %d",
Packit 6c0a39
        session->global_req_state);
Packit 6c0a39
  } else {
Packit 6c0a39
    session->global_req_state=SSH_CHANNEL_REQ_STATE_DENIED;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int ssh_global_request_termination(void *s){
Packit 6c0a39
  ssh_session session = (ssh_session) s;
Packit 6c0a39
  if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING ||
Packit 6c0a39
      session->session_state == SSH_SESSION_STATE_ERROR)
Packit 6c0a39
    return 1;
Packit 6c0a39
  else
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Send a global request (needed for forward listening) and wait for the
Packit 6c0a39
 * result.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The SSH session handle.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  request  The type of request (defined in RFC).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  buffer   Additional data to put in packet.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  reply    Set if you expect a reply from server.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_global_request(ssh_session session,
Packit 6c0a39
                       const char *request,
Packit 6c0a39
                       ssh_buffer buffer,
Packit 6c0a39
                       int reply)
Packit 6c0a39
{
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  switch (session->global_req_state) {
Packit 6c0a39
  case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
    break;
Packit 6c0a39
  default:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                       "bsb",
Packit 6c0a39
                       SSH2_MSG_GLOBAL_REQUEST,
Packit 6c0a39
                       request,
Packit 6c0a39
                       reply == 0 ? 0 : 1);
Packit 6c0a39
  if (rc != SSH_OK){
Packit 6c0a39
      ssh_set_error_oom(session);
Packit 6c0a39
      rc = SSH_ERROR;
Packit 6c0a39
      goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (buffer != NULL) {
Packit 6c0a39
      rc = ssh_buffer_add_data(session->out_buffer,
Packit 6c0a39
                           ssh_buffer_get(buffer),
Packit 6c0a39
                           ssh_buffer_get_len(buffer));
Packit 6c0a39
      if (rc < 0) {
Packit 6c0a39
          ssh_set_error_oom(session);
Packit 6c0a39
          rc = SSH_ERROR;
Packit 6c0a39
          goto error;
Packit 6c0a39
      }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  session->global_req_state = SSH_CHANNEL_REQ_STATE_PENDING;
Packit 6c0a39
  rc = ssh_packet_send(session);
Packit 6c0a39
  if (rc == SSH_ERROR) {
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Sent a SSH_MSG_GLOBAL_REQUEST %s", request);
Packit 6c0a39
Packit 6c0a39
  if (reply == 0) {
Packit 6c0a39
      session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE;
Packit 6c0a39
Packit 6c0a39
      return SSH_OK;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = ssh_handle_packets_termination(session,
Packit 6c0a39
                                      SSH_TIMEOUT_DEFAULT,
Packit 6c0a39
                                      ssh_global_request_termination,
Packit 6c0a39
                                      session);
Packit 6c0a39
Packit 6c0a39
  if(rc==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){
Packit 6c0a39
    session->global_req_state = SSH_CHANNEL_REQ_STATE_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  switch(session->global_req_state){
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_ACCEPTED:
Packit 6c0a39
      SSH_LOG(SSH_LOG_PROTOCOL, "Global request %s success",request);
Packit 6c0a39
      rc=SSH_OK;
Packit 6c0a39
      break;
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_DENIED:
Packit 6c0a39
      SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
          "Global request %s failed", request);
Packit 6c0a39
      ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
          "Global request %s failed", request);
Packit 6c0a39
      rc=SSH_ERROR;
Packit 6c0a39
      break;
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_ERROR:
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
      rc = SSH_ERROR;
Packit 6c0a39
      break;
Packit 6c0a39
    case SSH_CHANNEL_REQ_STATE_PENDING:
Packit 6c0a39
      return SSH_AGAIN;
Packit 6c0a39
  }
Packit 6c0a39
  session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE;
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Sends the "tcpip-forward" global request to ask the server to begin
Packit 6c0a39
 *        listening for inbound connections.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to send the request.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  address  The address to bind to on the server. Pass NULL to bind
Packit 6c0a39
 *                      to all available addresses on all protocol families
Packit 6c0a39
 *                      supported by the server.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  port     The port to bind to on the server. Pass 0 to ask the
Packit 6c0a39
 *                      server to allocate the next available unprivileged port
Packit 6c0a39
 *                      number
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  bound_port The pointer to get actual bound port. Pass NULL to
Packit 6c0a39
 *                        ignore.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 **/
Packit 6c0a39
int ssh_channel_listen_forward(ssh_session session,
Packit 6c0a39
                               const char *address,
Packit 6c0a39
                               int port,
Packit 6c0a39
                               int *bound_port)
Packit 6c0a39
{
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE)
Packit 6c0a39
    goto pending;
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer,
Packit 6c0a39
                       "sd",
Packit 6c0a39
                       address ? address : "",
Packit 6c0a39
                       port);
Packit 6c0a39
  if (rc != SSH_OK){
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = ssh_global_request(session, "tcpip-forward", buffer, 1);
Packit 6c0a39
Packit 6c0a39
  /* TODO: FIXME no guarantee the last packet we received contains
Packit 6c0a39
   * that info */
Packit 6c0a39
  if (rc == SSH_OK && port == 0 && bound_port != NULL) {
Packit 6c0a39
    rc = ssh_buffer_unpack(session->in_buffer, "d", bound_port);
Packit 6c0a39
    if (rc != SSH_OK)
Packit 6c0a39
        *bound_port = 0;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* DEPRECATED */
Packit 6c0a39
int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port) {
Packit 6c0a39
  return ssh_channel_listen_forward(session, address, port, bound_port);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* DEPRECATED */
Packit 6c0a39
ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) {
Packit 6c0a39
  return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Accept an incoming TCP/IP forwarding channel and get information
Packit 6c0a39
 * about incomming connection
Packit 6c0a39
 * @param[in]  session    The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  timeout_ms A timeout in milliseconds.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  destination_port A pointer to destination port or NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @return Newly created channel, or NULL if no incoming channel request from
Packit 6c0a39
 *         the server
Packit 6c0a39
 */
Packit 6c0a39
ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int* destination_port) {
Packit 6c0a39
  return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Sends the "cancel-tcpip-forward" global request to ask the server to
Packit 6c0a39
 *        cancel the tcpip-forward request.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to send the request.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  address  The bound address on the server.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  port     The bound port on the server.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_cancel_forward(ssh_session session,
Packit 6c0a39
                               const char *address,
Packit 6c0a39
                               int port)
Packit 6c0a39
{
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE)
Packit 6c0a39
    goto pending;
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer, "sd",
Packit 6c0a39
                       address ? address : "",
Packit 6c0a39
                       port);
Packit 6c0a39
  if (rc != SSH_OK){
Packit 6c0a39
      ssh_set_error_oom(session);
Packit 6c0a39
      goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = ssh_global_request(session, "cancel-tcpip-forward", buffer, 1);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* DEPRECATED */
Packit 6c0a39
int ssh_forward_cancel(ssh_session session, const char *address, int port) {
Packit 6c0a39
    return ssh_channel_cancel_forward(session, address, port);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Set environment variables.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to set the environment variables.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  name     The name of the variable.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  value    The value to set.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 * @warning Some environment variables may be refused by security reasons.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if(name == NULL || value == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
  switch(channel->request_state){
Packit 6c0a39
  case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
    break;
Packit 6c0a39
  default:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  }
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer,
Packit 6c0a39
                       "ss",
Packit 6c0a39
                       name,
Packit 6c0a39
                       value);
Packit 6c0a39
  if (rc != SSH_OK){
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = channel_request(channel, "env", buffer,1);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Run a shell command without an interactive shell.
Packit 6c0a39
 *
Packit 6c0a39
 * This is similar to 'sh -c command'.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to execute the command.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  cmd      The command to execute
Packit 6c0a39
 *                      (e.g. "ls ~/ -al | grep -i reports").
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * Example:
Packit 6c0a39
@code
Packit 6c0a39
   rc = channel_request_exec(channel, "ps aux");
Packit 6c0a39
   if (rc > 0) {
Packit 6c0a39
       return -1;
Packit 6c0a39
   }
Packit 6c0a39
Packit 6c0a39
   while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
Packit 6c0a39
       if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) {
Packit 6c0a39
           return -1;
Packit 6c0a39
       }
Packit 6c0a39
   }
Packit 6c0a39
@endcode
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_request_shell()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_exec(ssh_channel channel, const char *cmd) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if(cmd == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  switch(channel->request_state){
Packit 6c0a39
  case SSH_CHANNEL_REQ_STATE_NONE:
Packit 6c0a39
    break;
Packit 6c0a39
  default:
Packit 6c0a39
    goto pending;
Packit 6c0a39
  }
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer, "s", cmd);
Packit 6c0a39
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = channel_request(channel, "exec", buffer, 1);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Send a signal to remote process (as described in RFC 4254, section 6.9).
Packit 6c0a39
 *
Packit 6c0a39
 * Sends a signal 'sig' to the remote process.
Packit 6c0a39
 * Note, that remote system may not support signals concept.
Packit 6c0a39
 * In such a case this request will be silently ignored.
Packit 6c0a39
 * Only SSH-v2 is supported (I'm not sure about SSH-v1).
Packit 6c0a39
 *
Packit 6c0a39
 * OpenSSH doesn't support signals yet, see:
Packit 6c0a39
 * https://bugzilla.mindrot.org/show_bug.cgi?id=1424
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send signal.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  sig      The signal to send (without SIG prefix)
Packit 6c0a39
 *                      \n\n
Packit 6c0a39
 *                      SIGABRT  -> ABRT \n
Packit 6c0a39
 *                      SIGALRM  -> ALRM \n
Packit 6c0a39
 *                      SIGFPE   -> FPE  \n
Packit 6c0a39
 *                      SIGHUP   -> HUP  \n
Packit 6c0a39
 *                      SIGILL   -> ILL  \n
Packit 6c0a39
 *                      SIGINT   -> INT  \n
Packit 6c0a39
 *                      SIGKILL  -> KILL \n
Packit 6c0a39
 *                      SIGPIPE  -> PIPE \n
Packit 6c0a39
 *                      SIGQUIT  -> QUIT \n
Packit 6c0a39
 *                      SIGSEGV  -> SEGV \n
Packit 6c0a39
 *                      SIGTERM  -> TERM \n
Packit 6c0a39
 *                      SIGUSR1  -> USR1 \n
Packit 6c0a39
 *                      SIGUSR2  -> USR2 \n
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success, SSH_ERROR if an error occurred
Packit 6c0a39
 *                      (including attempts to send signal via SSH-v1 session).
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if(sig == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer, "s", sig);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = channel_request(channel, "signal", buffer, 0);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Send a break signal to the server (as described in RFC 4335).
Packit 6c0a39
 *
Packit 6c0a39
 * Sends a break signal to the remote process.
Packit 6c0a39
 * Note, that remote system may not support breaks.
Packit 6c0a39
 * In such a case this request will be silently ignored.
Packit 6c0a39
 * Only SSH-v2 is supported.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send the break to.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  length   The break-length in milliseconds to send.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success, SSH_ERROR if an error occurred
Packit 6c0a39
 *                      (including attempts to send signal via SSH-v1 session).
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_send_break(ssh_channel channel, uint32_t length) {
Packit 6c0a39
    ssh_buffer buffer = NULL;
Packit 6c0a39
    int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
    if (channel == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    buffer = ssh_buffer_new();
Packit 6c0a39
    if (buffer == NULL) {
Packit 6c0a39
        ssh_set_error_oom(channel->session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_pack(buffer, "d", length);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error_oom(channel->session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = channel_request(channel, "break", buffer, 0);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
    ssh_buffer_free(buffer);
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Read data from a channel into a buffer.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to read from.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  buffer   The buffer which will get the data.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  count    The count of bytes to be read. If it is bigger than 0,
Packit 6c0a39
 *                      the exact size will be read, else (bytes=0) it will
Packit 6c0a39
 *                      return once anything is available.
Packit 6c0a39
 *
Packit 6c0a39
 * @param is_stderr     A boolean value to mark reading from the stderr stream.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes read, 0 on end of file or SSH_ERROR
Packit 6c0a39
 *                      on error.
Packit 6c0a39
 * @deprecated          Please use ssh_channel_read instead
Packit 6c0a39
 * @warning             This function doesn't work in nonblocking/timeout mode
Packit 6c0a39
 * @see ssh_channel_read
Packit 6c0a39
 */
Packit 6c0a39
int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
Packit 6c0a39
    int is_stderr) {
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  char buffer_tmp[8192];
Packit 6c0a39
  int r;
Packit 6c0a39
  uint32_t total=0;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
Packit 6c0a39
  if(buffer == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  ssh_buffer_reinit(buffer);
Packit 6c0a39
  if(count==0){
Packit 6c0a39
    do {
Packit 6c0a39
      r=ssh_channel_poll(channel, is_stderr);
Packit 6c0a39
      if(r < 0){
Packit 6c0a39
        return r;
Packit 6c0a39
      }
Packit 6c0a39
      if(r > 0){
Packit 6c0a39
        r=ssh_channel_read(channel, buffer_tmp, r, is_stderr);
Packit 6c0a39
        if(r < 0){
Packit 6c0a39
          return r;
Packit 6c0a39
        }
Packit 6c0a39
        if(ssh_buffer_add_data(buffer,buffer_tmp,r) < 0){
Packit 6c0a39
          ssh_set_error_oom(session);
Packit 6c0a39
          r = SSH_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        return r;
Packit 6c0a39
      }
Packit 6c0a39
      if(ssh_channel_is_eof(channel)){
Packit 6c0a39
        return 0;
Packit 6c0a39
      }
Packit 6c0a39
      ssh_handle_packets(channel->session, SSH_TIMEOUT_INFINITE);
Packit 6c0a39
    } while (r == 0);
Packit 6c0a39
  }
Packit 6c0a39
  while(total < count){
Packit 6c0a39
    r=ssh_channel_read(channel, buffer_tmp, sizeof(buffer_tmp), is_stderr);
Packit 6c0a39
    if(r<0){
Packit 6c0a39
      return r;
Packit 6c0a39
    }
Packit 6c0a39
    if(r==0){
Packit 6c0a39
      return total;
Packit 6c0a39
    }
Packit 6c0a39
    if (ssh_buffer_add_data(buffer,buffer_tmp,r) < 0) {
Packit 6c0a39
      ssh_set_error_oom(session);
Packit 6c0a39
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    total += r;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return total;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
struct ssh_channel_read_termination_struct {
Packit 6c0a39
  ssh_channel channel;
Packit 6c0a39
  uint32_t count;
Packit 6c0a39
  ssh_buffer buffer;
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
static int ssh_channel_read_termination(void *s){
Packit 6c0a39
  struct ssh_channel_read_termination_struct *ctx = s;
Packit 6c0a39
  if (ssh_buffer_get_len(ctx->buffer) >= ctx->count ||
Packit 6c0a39
      ctx->channel->remote_eof ||
Packit 6c0a39
      ctx->channel->session->session_state == SSH_SESSION_STATE_ERROR)
Packit 6c0a39
    return 1;
Packit 6c0a39
  else
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* TODO FIXME Fix the delayed close thing */
Packit 6c0a39
/* TODO FIXME Fix the blocking behaviours */
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Reads data from a channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to read from.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  dest     The destination buffer which will get the data.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  count    The count of bytes to be read.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  is_stderr A boolean value to mark reading from the stderr flow.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes read, 0 on end of file or SSH_ERROR
Packit 6c0a39
 *                      on error. In nonblocking mode it Can return 0 if no data
Packit 6c0a39
 *                      is available or SSH_AGAIN.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning This function may return less than count bytes of data, and won't
Packit 6c0a39
 *          block until count bytes have been read.
Packit 6c0a39
 * @warning The read function using a buffer has been renamed to
Packit 6c0a39
 *          channel_read_buffer().
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
Packit 6c0a39
{
Packit 6c0a39
    return ssh_channel_read_timeout(channel,
Packit 6c0a39
                                    dest,
Packit 6c0a39
                                    count,
Packit 6c0a39
                                    is_stderr,
Packit 6c0a39
                                    SSH_TIMEOUT_DEFAULT);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Reads data from a channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel     The channel to read from.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  dest        The destination buffer which will get the data.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  count       The count of bytes to be read.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  is_stderr   A boolean value to mark reading from the stderr flow.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  timeout_ms  A timeout in milliseconds. A value of -1 means
Packit 6c0a39
 *                         infinite timeout.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes read, 0 on end of file or SSH_ERROR
Packit 6c0a39
 *                      on error. In nonblocking mode it Can return 0 if no data
Packit 6c0a39
 *                      is available or SSH_AGAIN.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning This function may return less than count bytes of data, and won't
Packit 6c0a39
 *          block until count bytes have been read.
Packit 6c0a39
 * @warning The read function using a buffer has been renamed to
Packit 6c0a39
 *          channel_read_buffer().
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_read_timeout(ssh_channel channel,
Packit 6c0a39
                             void *dest,
Packit 6c0a39
                             uint32_t count,
Packit 6c0a39
                             int is_stderr,
Packit 6c0a39
                             int timeout_ms)
Packit 6c0a39
{
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  ssh_buffer stdbuf;
Packit 6c0a39
  uint32_t len;
Packit 6c0a39
  struct ssh_channel_read_termination_struct ctx;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if(dest == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
  stdbuf = channel->stdout_buffer;
Packit 6c0a39
Packit 6c0a39
  if (count == 0) {
Packit 6c0a39
    return 0;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (is_stderr) {
Packit 6c0a39
    stdbuf=channel->stderr_buffer;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /*
Packit 6c0a39
   * We may have problem if the window is too small to accept as much data
Packit 6c0a39
   * as asked
Packit 6c0a39
   */
Packit 6c0a39
  SSH_LOG(SSH_LOG_PACKET,
Packit 6c0a39
      "Read (%d) buffered : %d bytes. Window: %d",
Packit 6c0a39
      count,
Packit 6c0a39
      ssh_buffer_get_len(stdbuf),
Packit 6c0a39
      channel->local_window);
Packit 6c0a39
Packit 6c0a39
  if (count > ssh_buffer_get_len(stdbuf) + channel->local_window) {
Packit 6c0a39
    if (grow_window(session, channel, count - ssh_buffer_get_len(stdbuf)) < 0) {
Packit 6c0a39
      return -1;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /* block reading until at least one byte has been read
Packit 6c0a39
  *  and ignore the trivial case count=0
Packit 6c0a39
  */
Packit 6c0a39
  ctx.channel = channel;
Packit 6c0a39
  ctx.buffer = stdbuf;
Packit 6c0a39
  ctx.count = 1;
Packit 6c0a39
Packit 6c0a39
  if (timeout_ms < SSH_TIMEOUT_DEFAULT) {
Packit 6c0a39
      timeout_ms = SSH_TIMEOUT_INFINITE;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_handle_packets_termination(session,
Packit 6c0a39
                                      timeout_ms,
Packit 6c0a39
                                      ssh_channel_read_termination,
Packit 6c0a39
                                      &ctx;;
Packit 6c0a39
  if (rc == SSH_ERROR){
Packit 6c0a39
    return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /*
Packit 6c0a39
   * If the channel is closed or in an error state, reading from it is an error
Packit 6c0a39
   */
Packit 6c0a39
  if (session->session_state == SSH_SESSION_STATE_ERROR) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if (channel->state == SSH_CHANNEL_STATE_CLOSED) {
Packit 6c0a39
      ssh_set_error(session,
Packit 6c0a39
                    SSH_FATAL,
Packit 6c0a39
                    "Remote channel is closed.");
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if (channel->remote_eof && ssh_buffer_get_len(stdbuf) == 0) {
Packit 6c0a39
    return 0;
Packit 6c0a39
  }
Packit 6c0a39
  len = ssh_buffer_get_len(stdbuf);
Packit 6c0a39
  /* Read count bytes if len is greater, everything otherwise */
Packit 6c0a39
  len = (len > count ? count : len);
Packit 6c0a39
  memcpy(dest, ssh_buffer_get(stdbuf), len);
Packit 6c0a39
  ssh_buffer_pass_bytes(stdbuf,len);
Packit 6c0a39
  if (channel->counter != NULL) {
Packit 6c0a39
      channel->counter->in_bytes += len;
Packit 6c0a39
  }
Packit 6c0a39
  /* Authorize some buffering while userapp is busy */
Packit 6c0a39
  if (channel->local_window < WINDOWLIMIT) {
Packit 6c0a39
    if (grow_window(session, channel, 0) < 0) {
Packit 6c0a39
      return -1;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return len;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Do a nonblocking read on the channel.
Packit 6c0a39
 *
Packit 6c0a39
 * A nonblocking read on the specified channel. it will return <= count bytes of
Packit 6c0a39
 * data read atomically.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to read from.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  dest     A pointer to a destination buffer.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  count    The count of bytes of data to be read.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  is_stderr A boolean to select the stderr stream.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes read, 0 if nothing is available or
Packit 6c0a39
 *                      SSH_ERROR on error.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning Don't forget to check for EOF as it would return 0 here.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_is_eof()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count,
Packit 6c0a39
    int is_stderr) {
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  int to_read;
Packit 6c0a39
  int rc;
Packit 6c0a39
  int blocking;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  if(dest == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
Packit 6c0a39
  to_read = ssh_channel_poll(channel, is_stderr);
Packit 6c0a39
Packit 6c0a39
  if (to_read <= 0) {
Packit 6c0a39
      if (session->session_state == SSH_SESSION_STATE_ERROR){
Packit 6c0a39
          return SSH_ERROR;
Packit 6c0a39
      }
Packit 6c0a39
Packit 6c0a39
      return to_read; /* may be an error code */
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (to_read > (int)count) {
Packit 6c0a39
    to_read = (int)count;
Packit 6c0a39
  }
Packit 6c0a39
  blocking = ssh_is_blocking(session);
Packit 6c0a39
  ssh_set_blocking(session, 0);
Packit 6c0a39
  rc = ssh_channel_read(channel, dest, to_read, is_stderr);
Packit 6c0a39
  ssh_set_blocking(session,blocking);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Polls a channel for data to read.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to poll.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  is_stderr A boolean to select the stderr stream.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes available for reading, 0 if nothing
Packit 6c0a39
 *                      is available or SSH_ERROR on error.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning When the channel is in EOF state, the function returns SSH_EOF.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_is_eof()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_poll(ssh_channel channel, int is_stderr){
Packit 6c0a39
  ssh_buffer stdbuf;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  stdbuf = channel->stdout_buffer;
Packit 6c0a39
Packit 6c0a39
  if (is_stderr) {
Packit 6c0a39
    stdbuf = channel->stderr_buffer;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (ssh_buffer_get_len(stdbuf) == 0 && channel->remote_eof == 0) {
Packit 6c0a39
    if (channel->session->session_state == SSH_SESSION_STATE_ERROR){
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    if (ssh_handle_packets(channel->session, SSH_TIMEOUT_NONBLOCKING)==SSH_ERROR) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (ssh_buffer_get_len(stdbuf) > 0){
Packit 6c0a39
  	return ssh_buffer_get_len(stdbuf);
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (channel->remote_eof) {
Packit 6c0a39
    return SSH_EOF;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return ssh_buffer_get_len(stdbuf);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Polls a channel for data to read, waiting for a certain timeout.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel   The channel to poll.
Packit 6c0a39
 * @param[in]  timeout   Set an upper limit on the time for which this function
Packit 6c0a39
 *                       will block, in milliseconds. Specifying a negative value
Packit 6c0a39
 *                       means an infinite timeout. This parameter is passed to
Packit 6c0a39
 *                       the poll() function.
Packit 6c0a39
 * @param[in]  is_stderr A boolean to select the stderr stream.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes available for reading,
Packit 6c0a39
 *                      0 if nothing is available (timeout elapsed),
Packit 6c0a39
 *                      SSH_EOF on end of file,
Packit 6c0a39
 *                      SSH_ERROR on error.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning When the channel is in EOF state, the function returns SSH_EOF.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_is_eof()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  ssh_buffer stdbuf;
Packit 6c0a39
  struct ssh_channel_read_termination_struct ctx;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
  stdbuf = channel->stdout_buffer;
Packit 6c0a39
Packit 6c0a39
  if (is_stderr) {
Packit 6c0a39
    stdbuf = channel->stderr_buffer;
Packit 6c0a39
  }
Packit 6c0a39
  ctx.buffer = stdbuf;
Packit 6c0a39
  ctx.channel = channel;
Packit 6c0a39
  ctx.count = 1;
Packit 6c0a39
  rc = ssh_handle_packets_termination(channel->session, timeout,
Packit 6c0a39
      ssh_channel_read_termination, &ctx;;
Packit 6c0a39
  if(rc ==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){
Packit 6c0a39
    rc = SSH_ERROR;
Packit 6c0a39
    goto end;
Packit 6c0a39
  }
Packit 6c0a39
  rc = ssh_buffer_get_len(stdbuf);
Packit 6c0a39
  if(rc > 0)
Packit 6c0a39
    goto end;
Packit 6c0a39
  if (channel->remote_eof)
Packit 6c0a39
    rc = SSH_EOF;
Packit 6c0a39
end:
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Recover the session in which belongs a channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to recover the session from.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The session pointer.
Packit 6c0a39
 */
Packit 6c0a39
ssh_session ssh_channel_get_session(ssh_channel channel) {
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return NULL;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return channel->session;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int ssh_channel_exit_status_termination(void *c){
Packit 6c0a39
  ssh_channel channel = c;
Packit 6c0a39
  if(channel->exit_status != -1 ||
Packit 6c0a39
      /* When a channel is closed, no exit status message can
Packit 6c0a39
       * come anymore */
Packit 6c0a39
      (channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
Packit 6c0a39
      channel->session->session_state == SSH_SESSION_STATE_ERROR)
Packit 6c0a39
    return 1;
Packit 6c0a39
  else
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the exit status of the channel (error code from the executed
Packit 6c0a39
 *        instruction).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to get the status from.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The exit status, -1 if no exit status has been returned
Packit 6c0a39
 *                      (yet), or SSH_ERROR on error.
Packit 6c0a39
 * @warning             This function may block until a timeout (or never)
Packit 6c0a39
 *                      if the other side is not willing to close the channel.
Packit 6c0a39
 *
Packit 6c0a39
 * If you're looking for an async handling of this register a callback for the
Packit 6c0a39
 * exit status.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_exit_status_callback
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_get_exit_status(ssh_channel channel) {
Packit 6c0a39
  int rc;
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  rc = ssh_handle_packets_termination(channel->session,
Packit 6c0a39
                                      SSH_TIMEOUT_DEFAULT,
Packit 6c0a39
                                      ssh_channel_exit_status_termination,
Packit 6c0a39
                                      channel);
Packit 6c0a39
  if (rc == SSH_ERROR || channel->session->session_state ==
Packit 6c0a39
      SSH_SESSION_STATE_ERROR)
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  return channel->exit_status;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/*
Packit 6c0a39
 * This function acts as a meta select.
Packit 6c0a39
 *
Packit 6c0a39
 * First, channels are analyzed to seek potential can-write or can-read ones,
Packit 6c0a39
 * then if no channel has been elected, it goes in a loop with the posix
Packit 6c0a39
 * select(2).
Packit 6c0a39
 * This is made in two parts: protocol select and network select. The protocol
Packit 6c0a39
 * select does not use the network functions at all
Packit 6c0a39
 */
Packit 6c0a39
static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans,
Packit 6c0a39
    ssh_channel *echans, ssh_channel *rout, ssh_channel *wout, ssh_channel *eout) {
Packit 6c0a39
  ssh_channel chan;
Packit 6c0a39
  int i;
Packit 6c0a39
  int j = 0;
Packit 6c0a39
Packit 6c0a39
  for (i = 0; rchans[i] != NULL; i++) {
Packit 6c0a39
    chan = rchans[i];
Packit 6c0a39
Packit 6c0a39
    while (ssh_channel_is_open(chan) && ssh_socket_data_available(chan->session->socket)) {
Packit 6c0a39
      ssh_handle_packets(chan->session, SSH_TIMEOUT_NONBLOCKING);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if ((chan->stdout_buffer && ssh_buffer_get_len(chan->stdout_buffer) > 0) ||
Packit 6c0a39
        (chan->stderr_buffer && ssh_buffer_get_len(chan->stderr_buffer) > 0) ||
Packit 6c0a39
        chan->remote_eof) {
Packit 6c0a39
      rout[j] = chan;
Packit 6c0a39
      j++;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
  rout[j] = NULL;
Packit 6c0a39
Packit 6c0a39
  j = 0;
Packit 6c0a39
  for(i = 0; wchans[i] != NULL; i++) {
Packit 6c0a39
    chan = wchans[i];
Packit 6c0a39
    /* It's not our business to seek if the file descriptor is writable */
Packit 6c0a39
    if (ssh_socket_data_writable(chan->session->socket) &&
Packit 6c0a39
        ssh_channel_is_open(chan) && (chan->remote_window > 0)) {
Packit 6c0a39
      wout[j] = chan;
Packit 6c0a39
      j++;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
  wout[j] = NULL;
Packit 6c0a39
Packit 6c0a39
  j = 0;
Packit 6c0a39
  for (i = 0; echans[i] != NULL; i++) {
Packit 6c0a39
    chan = echans[i];
Packit 6c0a39
Packit 6c0a39
    if (!ssh_socket_is_open(chan->session->socket) || ssh_channel_is_closed(chan)) {
Packit 6c0a39
      eout[j] = chan;
Packit 6c0a39
      j++;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
  eout[j] = NULL;
Packit 6c0a39
Packit 6c0a39
  return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* Just count number of pointers in the array */
Packit 6c0a39
static int count_ptrs(ssh_channel *ptrs) {
Packit 6c0a39
  int c;
Packit 6c0a39
  for (c = 0; ptrs[c] != NULL; c++)
Packit 6c0a39
    ;
Packit 6c0a39
Packit 6c0a39
  return c;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Act like the standard select(2) on channels.
Packit 6c0a39
 *
Packit 6c0a39
 * The list of pointers are then actualized and will only contain pointers to
Packit 6c0a39
 * channels that are respectively readable, writable or have an exception to
Packit 6c0a39
 * trap.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  readchans A NULL pointer or an array of channel pointers,
Packit 6c0a39
 *                       terminated by a NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  writechans A NULL pointer or an array of channel pointers,
Packit 6c0a39
 *                        terminated by a NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  exceptchans A NULL pointer or an array of channel pointers,
Packit 6c0a39
 *                         terminated by a NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  timeout  Timeout as defined by select(2).
Packit 6c0a39
 *
Packit 6c0a39
 * @return             SSH_OK on a successful operation, SSH_EINTR if the
Packit 6c0a39
 *                     select(2) syscall was interrupted, then relaunch the
Packit 6c0a39
 *                     function, or SSH_ERROR on error.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
Packit 6c0a39
    ssh_channel *exceptchans, struct timeval * timeout) {
Packit 6c0a39
  ssh_channel *rchans, *wchans, *echans;
Packit 6c0a39
  ssh_channel dummy = NULL;
Packit 6c0a39
  ssh_event event = NULL;
Packit 6c0a39
  int rc;
Packit 6c0a39
  int i;
Packit 6c0a39
  int tm, tm_base;
Packit 6c0a39
  int firstround=1;
Packit 6c0a39
  struct ssh_timestamp ts;
Packit 6c0a39
Packit 6c0a39
  if (timeout != NULL)
Packit 6c0a39
    tm_base = timeout->tv_sec * 1000 + timeout->tv_usec/1000;
Packit 6c0a39
  else
Packit 6c0a39
    tm_base = SSH_TIMEOUT_INFINITE;
Packit 6c0a39
  ssh_timestamp_init(&ts);
Packit 6c0a39
  tm = tm_base;
Packit 6c0a39
  /* don't allow NULL pointers */
Packit 6c0a39
  if (readchans == NULL) {
Packit 6c0a39
    readchans = &dummy;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (writechans == NULL) {
Packit 6c0a39
    writechans = &dummy;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (exceptchans == NULL) {
Packit 6c0a39
    exceptchans = &dummy;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (readchans[0] == NULL && writechans[0] == NULL && exceptchans[0] == NULL) {
Packit 6c0a39
    /* No channel to poll?? Go away! */
Packit 6c0a39
    return 0;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /* Prepare the outgoing temporary arrays */
Packit 6c0a39
  rchans = calloc(count_ptrs(readchans) + 1, sizeof(ssh_channel));
Packit 6c0a39
  if (rchans == NULL) {
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  wchans = calloc(count_ptrs(writechans) + 1, sizeof(ssh_channel));
Packit 6c0a39
  if (wchans == NULL) {
Packit 6c0a39
    SAFE_FREE(rchans);
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  echans = calloc(count_ptrs(exceptchans) + 1, sizeof(ssh_channel));
Packit 6c0a39
  if (echans == NULL) {
Packit 6c0a39
    SAFE_FREE(rchans);
Packit 6c0a39
    SAFE_FREE(wchans);
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /*
Packit 6c0a39
   * First, try without doing network stuff then, use the ssh_poll
Packit 6c0a39
   * infrastructure to poll on all sessions.
Packit 6c0a39
   */
Packit 6c0a39
  do {
Packit 6c0a39
    channel_protocol_select(readchans, writechans, exceptchans,
Packit 6c0a39
        rchans, wchans, echans);
Packit 6c0a39
    if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) {
Packit 6c0a39
      /* At least one channel has an event */
Packit 6c0a39
      break;
Packit 6c0a39
    }
Packit 6c0a39
    /* Add all channels' sessions right into an event object */
Packit 6c0a39
    if (event == NULL) {
Packit 6c0a39
      event = ssh_event_new();
Packit 6c0a39
      if (event == NULL) {
Packit 6c0a39
          SAFE_FREE(rchans);
Packit 6c0a39
          SAFE_FREE(wchans);
Packit 6c0a39
          SAFE_FREE(echans);
Packit 6c0a39
Packit 6c0a39
          return SSH_ERROR;
Packit 6c0a39
      }
Packit 6c0a39
      for (i = 0; readchans[i] != NULL; i++) {
Packit 6c0a39
        ssh_poll_get_default_ctx(readchans[i]->session);
Packit 6c0a39
        ssh_event_add_session(event, readchans[i]->session);
Packit 6c0a39
      }
Packit 6c0a39
      for (i = 0; writechans[i] != NULL; i++) {
Packit 6c0a39
        ssh_poll_get_default_ctx(writechans[i]->session);
Packit 6c0a39
        ssh_event_add_session(event, writechans[i]->session);
Packit 6c0a39
      }
Packit 6c0a39
      for (i = 0; exceptchans[i] != NULL; i++) {
Packit 6c0a39
        ssh_poll_get_default_ctx(exceptchans[i]->session);
Packit 6c0a39
        ssh_event_add_session(event, exceptchans[i]->session);
Packit 6c0a39
      }
Packit 6c0a39
    }
Packit 6c0a39
    /* Get out if the timeout has elapsed */
Packit 6c0a39
    if (!firstround && ssh_timeout_elapsed(&ts, tm_base)){
Packit 6c0a39
      break;
Packit 6c0a39
    }
Packit 6c0a39
    /* Here we go */
Packit 6c0a39
    rc = ssh_event_dopoll(event,tm);
Packit 6c0a39
    if (rc != SSH_OK){
Packit 6c0a39
      SAFE_FREE(rchans);
Packit 6c0a39
      SAFE_FREE(wchans);
Packit 6c0a39
      SAFE_FREE(echans);
Packit 6c0a39
      ssh_event_free(event);
Packit 6c0a39
      return rc;
Packit 6c0a39
    }
Packit 6c0a39
    tm = ssh_timeout_update(&ts, tm_base);
Packit 6c0a39
    firstround=0;
Packit 6c0a39
  } while(1);
Packit 6c0a39
Packit 6c0a39
  memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel ));
Packit 6c0a39
  memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel ));
Packit 6c0a39
  memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel ));
Packit 6c0a39
  SAFE_FREE(rchans);
Packit 6c0a39
  SAFE_FREE(wchans);
Packit 6c0a39
  SAFE_FREE(echans);
Packit 6c0a39
  if(event)
Packit 6c0a39
    ssh_event_free(event);
Packit 6c0a39
  return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Set the channel data counter.
Packit 6c0a39
 *
Packit 6c0a39
 * @code
Packit 6c0a39
 * struct ssh_counter_struct counter = {
Packit 6c0a39
 *     .in_bytes = 0,
Packit 6c0a39
 *     .out_bytes = 0,
Packit 6c0a39
 *     .in_packets = 0,
Packit 6c0a39
 *     .out_packets = 0
Packit 6c0a39
 * };
Packit 6c0a39
 *
Packit 6c0a39
 * ssh_channel_set_counter(channel, &counter);
Packit 6c0a39
 * @endcode
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] channel The SSH channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] counter Counter for bytes handled by the channel.
Packit 6c0a39
 */
Packit 6c0a39
void ssh_channel_set_counter(ssh_channel channel,
Packit 6c0a39
                             ssh_counter counter) {
Packit 6c0a39
    if (channel != NULL) {
Packit 6c0a39
        channel->counter = counter;
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Blocking write on a channel stderr.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to write to.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  data     A pointer to the data to write.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  len      The length of the buffer to write to.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              The number of bytes written, SSH_ERROR on error.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_channel_read()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) {
Packit 6c0a39
  return channel_write_common(channel, data, len, 1);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#if WITH_SERVER
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Open a TCP/IP reverse forwarding channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  An allocated channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  remotehost The remote host to connected (host name or IP).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  remoteport The remote port.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  sourcehost The source host (your local computer). It's optional
Packit 6c0a39
 *                        and for logging purpose.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  localport  The source port (your local computer). It's optional
Packit 6c0a39
 *                        and for logging purpose.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 *
Packit 6c0a39
 * @warning This function does not bind the local port and does not automatically
Packit 6c0a39
 *          forward the content of a socket to the channel. You still have to
Packit 6c0a39
 *          use channel_read and channel_write for this.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost,
Packit 6c0a39
    int remoteport, const char *sourcehost, int localport) {
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  ssh_buffer payload = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
  if(remotehost == NULL || sourcehost == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
Packit 6c0a39
  if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN)
Packit 6c0a39
    goto pending;
Packit 6c0a39
  payload = ssh_buffer_new();
Packit 6c0a39
  if (payload == NULL) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
  rc = ssh_buffer_pack(payload,
Packit 6c0a39
                       "sdsd",
Packit 6c0a39
                       remotehost,
Packit 6c0a39
                       remoteport,
Packit 6c0a39
                       sourcehost,
Packit 6c0a39
                       localport);
Packit 6c0a39
  if (rc != SSH_OK){
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = channel_open(channel,
Packit 6c0a39
                    "forwarded-tcpip",
Packit 6c0a39
                    CHANNEL_INITIAL_WINDOW,
Packit 6c0a39
                    CHANNEL_MAX_PACKET,
Packit 6c0a39
                    payload);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(payload);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Open a X11 channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel      An allocated channel.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  orig_addr    The source host (the local server).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  orig_port    The source port (the local server).
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success,
Packit 6c0a39
 *                      SSH_ERROR if an error occurred,
Packit 6c0a39
 *                      SSH_AGAIN if in nonblocking mode and call has
Packit 6c0a39
 *                      to be done again.
Packit 6c0a39
 * @warning This function does not bind the local port and does not automatically
Packit 6c0a39
 *          forward the content of a socket to the channel. You still have to
Packit 6c0a39
 *          use channel_read and channel_write for this.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_open_x11(ssh_channel channel, 
Packit 6c0a39
        const char *orig_addr, int orig_port) {
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  ssh_buffer payload = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
  if(orig_addr == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
  session = channel->session;
Packit 6c0a39
Packit 6c0a39
  if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN)
Packit 6c0a39
    goto pending;
Packit 6c0a39
Packit 6c0a39
  payload = ssh_buffer_new();
Packit 6c0a39
  if (payload == NULL) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(payload,
Packit 6c0a39
                       "sd",
Packit 6c0a39
                       orig_addr,
Packit 6c0a39
                       orig_port);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
pending:
Packit 6c0a39
  rc = channel_open(channel,
Packit 6c0a39
                    "x11",
Packit 6c0a39
                    CHANNEL_INITIAL_WINDOW,
Packit 6c0a39
                    CHANNEL_MAX_PACKET,
Packit 6c0a39
                    payload);
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(payload);
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Send the exit status to the remote process
Packit 6c0a39
 *
Packit 6c0a39
 * Sends the exit status to the remote process (as described in RFC 4254,
Packit 6c0a39
 * section 6.10).
Packit 6c0a39
 * Only SSH-v2 is supported (I'm not sure about SSH-v1).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send exit status.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  exit_status  The exit status to send
Packit 6c0a39
 *
Packit 6c0a39
 * @return     SSH_OK on success, SSH_ERROR if an error occurred.
Packit 6c0a39
 *             (including attempts to send exit status via SSH-v1 session).
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer, "d", exit_status);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = channel_request(channel, "exit-status", buffer, 0);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Send an exit signal to remote process (RFC 4254, section 6.10).
Packit 6c0a39
 *
Packit 6c0a39
 * This sends the exit status of the remote process.
Packit 6c0a39
 * Note, that remote system may not support signals concept.
Packit 6c0a39
 * In such a case this request will be silently ignored.
Packit 6c0a39
 * Only SSH-v2 is supported (I'm not sure about SSH-v1).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  channel  The channel to send signal.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  sig      The signal to send (without SIG prefix)
Packit 6c0a39
 *                      (e.g. "TERM" or "KILL").
Packit 6c0a39
 * @param[in]  core     A boolean to tell if a core was dumped
Packit 6c0a39
 * @param[in]  errmsg   A CRLF explanation text about the error condition
Packit 6c0a39
 * @param[in]  lang     The language used in the message (format: RFC 3066)
Packit 6c0a39
 *
Packit 6c0a39
 * @return              SSH_OK on success, SSH_ERROR if an error occurred
Packit 6c0a39
 *                      (including attempts to send signal via SSH-v1 session).
Packit 6c0a39
 */
Packit 6c0a39
int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig,
Packit 6c0a39
                            int core, const char *errmsg, const char *lang) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  int rc = SSH_ERROR;
Packit 6c0a39
Packit 6c0a39
  if(channel == NULL) {
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
  if(sig == NULL || errmsg == NULL || lang == NULL) {
Packit 6c0a39
      ssh_set_error_invalid(channel->session);
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = ssh_buffer_pack(buffer,
Packit 6c0a39
                       "sbss",
Packit 6c0a39
                       sig,
Packit 6c0a39
                       core ? 1 : 0,
Packit 6c0a39
                       errmsg,
Packit 6c0a39
                       lang);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    ssh_set_error_oom(channel->session);
Packit 6c0a39
    goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = channel_request(channel, "exit-signal", buffer, 0);
Packit 6c0a39
error:
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
/* @} */