summaryrefslogtreecommitdiff
path: root/libssh2-sys/libssh2-1.5.0/src/packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'libssh2-sys/libssh2-1.5.0/src/packet.c')
-rw-r--r--libssh2-sys/libssh2-1.5.0/src/packet.c1267
1 files changed, 1267 insertions, 0 deletions
diff --git a/libssh2-sys/libssh2-1.5.0/src/packet.c b/libssh2-sys/libssh2-1.5.0/src/packet.c
new file mode 100644
index 0000000..5f1feb8
--- /dev/null
+++ b/libssh2-sys/libssh2-1.5.0/src/packet.c
@@ -0,0 +1,1267 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2005,2006 Mikhail Gusarov
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include <sys/types.h>
+
+#include "transport.h"
+#include "channel.h"
+#include "packet.h"
+
+/*
+ * libssh2_packet_queue_listener
+ *
+ * Queue a connection request for a listener
+ */
+static inline int
+packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long datalen,
+ packet_queue_listener_state_t *listen_state)
+{
+ /*
+ * Look for a matching listener
+ */
+ /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
+ unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1);
+ unsigned char *p;
+ LIBSSH2_LISTENER *listn = _libssh2_list_first(&session->listeners);
+ char failure_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+ int rc;
+
+ (void) datalen;
+
+ if (listen_state->state == libssh2_NB_state_idle) {
+ unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5;
+ listen_state->sender_channel = _libssh2_ntohu32(s);
+ s += 4;
+
+ listen_state->initial_window_size = _libssh2_ntohu32(s);
+ s += 4;
+ listen_state->packet_size = _libssh2_ntohu32(s);
+ s += 4;
+
+ listen_state->host_len = _libssh2_ntohu32(s);
+ s += 4;
+ listen_state->host = s;
+ s += listen_state->host_len;
+ listen_state->port = _libssh2_ntohu32(s);
+ s += 4;
+
+ listen_state->shost_len = _libssh2_ntohu32(s);
+ s += 4;
+ listen_state->shost = s;
+ s += listen_state->shost_len;
+ listen_state->sport = _libssh2_ntohu32(s);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Remote received connection from %s:%ld to %s:%ld",
+ listen_state->shost, listen_state->sport,
+ listen_state->host, listen_state->port);
+
+ listen_state->state = libssh2_NB_state_allocated;
+ }
+
+ if (listen_state->state != libssh2_NB_state_sent) {
+ while (listn) {
+ if ((listn->port == (int) listen_state->port) &&
+ (strlen(listn->host) == listen_state->host_len) &&
+ (memcmp (listn->host, listen_state->host,
+ listen_state->host_len) == 0)) {
+ /* This is our listener */
+ LIBSSH2_CHANNEL *channel = NULL;
+ listen_state->channel = NULL;
+
+ if (listen_state->state == libssh2_NB_state_allocated) {
+ if (listn->queue_maxsize &&
+ (listn->queue_maxsize <= listn->queue_size)) {
+ /* Queue is full */
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Listener queue full, ignoring");
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+
+ channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if (!channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a channel for "
+ "new connection");
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+ listen_state->channel = channel;
+
+ channel->session = session;
+ channel->channel_type_len = sizeof("forwarded-tcpip") - 1;
+ channel->channel_type = LIBSSH2_ALLOC(session,
+ channel->
+ channel_type_len +
+ 1);
+ if (!channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a channel for new"
+ " connection");
+ LIBSSH2_FREE(session, channel);
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+ memcpy(channel->channel_type, "forwarded-tcpip",
+ channel->channel_type_len + 1);
+
+ channel->remote.id = listen_state->sender_channel;
+ channel->remote.window_size_initial =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.window_size =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.packet_size =
+ LIBSSH2_CHANNEL_PACKET_DEFAULT;
+
+ channel->local.id = _libssh2_channel_nextid(session);
+ channel->local.window_size_initial =
+ listen_state->initial_window_size;
+ channel->local.window_size =
+ listen_state->initial_window_size;
+ channel->local.packet_size = listen_state->packet_size;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Connection queued: channel %lu/%lu "
+ "win %lu/%lu packet %lu/%lu",
+ channel->local.id, channel->remote.id,
+ channel->local.window_size,
+ channel->remote.window_size,
+ channel->local.packet_size,
+ channel->remote.packet_size);
+
+ p = listen_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
+ _libssh2_store_u32(&p, channel->remote.id);
+ _libssh2_store_u32(&p, channel->local.id);
+ _libssh2_store_u32(&p,
+ channel->remote.window_size_initial);
+ _libssh2_store_u32(&p, channel->remote.packet_size);
+
+ listen_state->state = libssh2_NB_state_created;
+ }
+
+ if (listen_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, listen_state->packet,
+ 17, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if (rc) {
+ listen_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send channel "
+ "open confirmation");
+ }
+
+ /* Link the channel into the end of the queue list */
+ if (listen_state->channel) {
+ _libssh2_list_add(&listn->queue,
+ &listen_state->channel->node);
+ listn->queue_size++;
+ }
+
+ listen_state->state = libssh2_NB_state_idle;
+ return 0;
+ }
+ }
+
+ listn = _libssh2_list_next(&listn->node);
+ }
+
+ listen_state->state = libssh2_NB_state_sent;
+ }
+
+ /* We're not listening to you */
+ p = listen_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
+ _libssh2_store_u32(&p, listen_state->sender_channel);
+ _libssh2_store_u32(&p, failure_code);
+ _libssh2_store_str(&p, FwdNotReq, sizeof(FwdNotReq) - 1);
+ _libssh2_htonu32(p, 0);
+
+ rc = _libssh2_transport_send(session, listen_state->packet,
+ packet_len, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ listen_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc, "Unable to send open failure");
+
+ }
+ listen_state->state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * packet_x11_open
+ *
+ * Accept a forwarded X11 connection
+ */
+static inline int
+packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long datalen,
+ packet_x11_open_state_t *x11open_state)
+{
+ int failure_code = SSH_OPEN_CONNECT_FAILED;
+ /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
+ unsigned long packet_len = 17 + (sizeof(X11FwdUnAvil) - 1);
+ unsigned char *p;
+ LIBSSH2_CHANNEL *channel = x11open_state->channel;
+ int rc;
+
+ (void) datalen;
+
+ if (x11open_state->state == libssh2_NB_state_idle) {
+ unsigned char *s = data + (sizeof("x11") - 1) + 5;
+ x11open_state->sender_channel = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->initial_window_size = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->packet_size = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->shost_len = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->shost = s;
+ s += x11open_state->shost_len;
+ x11open_state->sport = _libssh2_ntohu32(s);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "X11 Connection Received from %s:%ld on channel %lu",
+ x11open_state->shost, x11open_state->sport,
+ x11open_state->sender_channel);
+
+ x11open_state->state = libssh2_NB_state_allocated;
+ }
+
+ if (session->x11) {
+ if (x11open_state->state == libssh2_NB_state_allocated) {
+ channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if (!channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "allocate a channel for new connection");
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ goto x11_exit;
+ }
+
+ channel->session = session;
+ channel->channel_type_len = sizeof("x11") - 1;
+ channel->channel_type = LIBSSH2_ALLOC(session,
+ channel->channel_type_len +
+ 1);
+ if (!channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "allocate a channel for new connection");
+ LIBSSH2_FREE(session, channel);
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ goto x11_exit;
+ }
+ memcpy(channel->channel_type, "x11",
+ channel->channel_type_len + 1);
+
+ channel->remote.id = x11open_state->sender_channel;
+ channel->remote.window_size_initial =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT;
+
+ channel->local.id = _libssh2_channel_nextid(session);
+ channel->local.window_size_initial =
+ x11open_state->initial_window_size;
+ channel->local.window_size = x11open_state->initial_window_size;
+ channel->local.packet_size = x11open_state->packet_size;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "X11 Connection established: channel %lu/%lu "
+ "win %lu/%lu packet %lu/%lu",
+ channel->local.id, channel->remote.id,
+ channel->local.window_size,
+ channel->remote.window_size,
+ channel->local.packet_size,
+ channel->remote.packet_size);
+ p = x11open_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
+ _libssh2_store_u32(&p, channel->remote.id);
+ _libssh2_store_u32(&p, channel->local.id);
+ _libssh2_store_u32(&p, channel->remote.window_size_initial);
+ _libssh2_store_u32(&p, channel->remote.packet_size);
+
+ x11open_state->state = libssh2_NB_state_created;
+ }
+
+ if (x11open_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, x11open_state->packet, 17,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ x11open_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send channel open "
+ "confirmation");
+ }
+
+ /* Link the channel into the session */
+ _libssh2_list_add(&session->channels, &channel->node);
+
+ /*
+ * Pass control to the callback, they may turn right around and
+ * free the channel, or actually use it
+ */
+ LIBSSH2_X11_OPEN(channel, (char *)x11open_state->shost,
+ x11open_state->sport);
+
+ x11open_state->state = libssh2_NB_state_idle;
+ return 0;
+ }
+ }
+ else
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ /* fall-trough */
+ x11_exit:
+ p = x11open_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
+ _libssh2_store_u32(&p, x11open_state->sender_channel);
+ _libssh2_store_u32(&p, failure_code);
+ _libssh2_store_str(&p, X11FwdUnAvil, sizeof(X11FwdUnAvil) - 1);
+ _libssh2_htonu32(p, 0);
+
+ rc = _libssh2_transport_send(session, x11open_state->packet, packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ x11open_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc, "Unable to send open failure");
+ }
+ x11open_state->state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * _libssh2_packet_add
+ *
+ * Create a new packet and attach it to the brigade. Called from the transport
+ * layer when it has received a packet.
+ *
+ * The input pointer 'data' is pointing to allocated data that this function
+ * is asked to deal with so on failure OR success, it must be freed fine.
+ * The only exception is when the return code is LIBSSH2_ERROR_EAGAIN.
+ *
+ * This function will always be called with 'datalen' greater than zero.
+ */
+int
+_libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
+ size_t datalen, int macstate)
+{
+ int rc = 0;
+ char *message=NULL;
+ char *language=NULL;
+ size_t message_len=0;
+ size_t language_len=0;
+ LIBSSH2_CHANNEL *channelp = NULL;
+ size_t data_head = 0;
+ unsigned char msg = data[0];
+
+ switch(session->packAdd_state) {
+ case libssh2_NB_state_idle:
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Packet type %d received, length=%d",
+ (int) msg, (int) datalen);
+
+ if ((macstate == LIBSSH2_MAC_INVALID) &&
+ (!session->macerror ||
+ LIBSSH2_MACERROR(session, (char *) data, datalen))) {
+ /* Bad MAC input, but no callback set or non-zero return from the
+ callback */
+
+ LIBSSH2_FREE(session, data);
+ return _libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC,
+ "Invalid MAC received");
+ }
+ session->packAdd_state = libssh2_NB_state_allocated;
+ break;
+ case libssh2_NB_state_jump1:
+ goto libssh2_packet_add_jump_point1;
+ case libssh2_NB_state_jump2:
+ goto libssh2_packet_add_jump_point2;
+ case libssh2_NB_state_jump3:
+ goto libssh2_packet_add_jump_point3;
+ case libssh2_NB_state_jump4:
+ goto libssh2_packet_add_jump_point4;
+ case libssh2_NB_state_jump5:
+ goto libssh2_packet_add_jump_point5;
+ default: /* nothing to do */
+ break;
+ }
+
+ if (session->packAdd_state == libssh2_NB_state_allocated) {
+ /* A couple exceptions to the packet adding rule: */
+ switch (msg) {
+
+ /*
+ byte SSH_MSG_DISCONNECT
+ uint32 reason code
+ string description in ISO-10646 UTF-8 encoding [RFC3629]
+ string language tag [RFC3066]
+ */
+
+ case SSH_MSG_DISCONNECT:
+ if(datalen >= 5) {
+ size_t reason = _libssh2_ntohu32(data + 1);
+
+ if(datalen >= 9) {
+ message_len = _libssh2_ntohu32(data + 5);
+
+ if(message_len < datalen-13) {
+ /* 9 = packet_type(1) + reason(4) + message_len(4) */
+ message = (char *) data + 9;
+
+ language_len = _libssh2_ntohu32(data + 9 + message_len);
+ language = (char *) data + 9 + message_len + 4;
+
+ if(language_len > (datalen-13-message_len)) {
+ /* bad input, clear info */
+ language = message = NULL;
+ language_len = message_len = 0;
+ }
+ }
+ else
+ /* bad size, clear it */
+ message_len=0;
+ }
+ if (session->ssh_msg_disconnect) {
+ LIBSSH2_DISCONNECT(session, reason, message,
+ message_len, language, language_len);
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Disconnect(%d): %s(%s)", reason,
+ message, language);
+ }
+
+ LIBSSH2_FREE(session, data);
+ session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
+ session->packAdd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "socket disconnect");
+ /*
+ byte SSH_MSG_IGNORE
+ string data
+ */
+
+ case SSH_MSG_IGNORE:
+ if (datalen >= 2) {
+ if (session->ssh_msg_ignore) {
+ LIBSSH2_IGNORE(session, (char *) data + 1, datalen - 1);
+ }
+ } else if (session->ssh_msg_ignore) {
+ LIBSSH2_IGNORE(session, "", 0);
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_DEBUG
+ boolean always_display
+ string message in ISO-10646 UTF-8 encoding [RFC3629]
+ string language tag [RFC3066]
+ */
+
+ case SSH_MSG_DEBUG:
+ if(datalen >= 2) {
+ int always_display= data[1];
+
+ if(datalen >= 6) {
+ message_len = _libssh2_ntohu32(data + 2);
+
+ if(message_len <= (datalen - 10)) {
+ /* 6 = packet_type(1) + display(1) + message_len(4) */
+ message = (char *) data + 6;
+ language_len = _libssh2_ntohu32(data + 6 + message_len);
+
+ if(language_len <= (datalen - 10 - message_len))
+ language = (char *) data + 10 + message_len;
+ }
+ }
+
+ if (session->ssh_msg_debug) {
+ LIBSSH2_DEBUG(session, always_display, message,
+ message_len, language, language_len);
+ }
+ }
+ /*
+ * _libssh2_debug will actually truncate this for us so
+ * that it's not an inordinate about of data
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Debug Packet: %s", message);
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_GLOBAL_REQUEST
+ string request name in US-ASCII only
+ boolean want reply
+ .... request-specific data follows
+ */
+
+ case SSH_MSG_GLOBAL_REQUEST:
+ if(datalen >= 5) {
+ uint32_t len =0;
+ unsigned char want_reply=0;
+ len = _libssh2_ntohu32(data + 1);
+ if(datalen >= (6 + len)) {
+ want_reply = data[5 + len];
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "Received global request type %.*s (wr %X)",
+ len, data + 5, want_reply);
+ }
+
+
+ if (want_reply) {
+ static const unsigned char packet =
+ SSH_MSG_REQUEST_FAILURE;
+ libssh2_packet_add_jump_point5:
+ session->packAdd_state = libssh2_NB_state_jump5;
+ rc = _libssh2_transport_send(session, &packet, 1, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_EXTENDED_DATA
+ uint32 recipient channel
+ uint32 data_type_code
+ string data
+ */
+
+ case SSH_MSG_CHANNEL_EXTENDED_DATA:
+ /* streamid(4) */
+ data_head += 4;
+
+ /* fall-through */
+
+ /*
+ byte SSH_MSG_CHANNEL_DATA
+ uint32 recipient channel
+ string data
+ */
+
+ case SSH_MSG_CHANNEL_DATA:
+ /* packet_type(1) + channelno(4) + datalen(4) */
+ data_head += 9;
+
+ if(datalen >= data_head)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+
+ if (!channelp) {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN,
+ "Packet received for unknown channel");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ uint32_t stream_id = 0;
+ if (msg == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ stream_id = _libssh2_ntohu32(data + 5);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "%d bytes packet_add() for %lu/%lu/%lu",
+ (int) (datalen - data_head),
+ channelp->local.id,
+ channelp->remote.id,
+ stream_id);
+ }
+#endif
+ if ((channelp->remote.extended_data_ignore_mode ==
+ LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) &&
+ (msg == SSH_MSG_CHANNEL_EXTENDED_DATA)) {
+ /* Pretend we didn't receive this */
+ LIBSSH2_FREE(session, data);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Ignoring extended data and refunding %d bytes",
+ (int) (datalen - 13));
+ if (channelp->read_avail + datalen - data_head >=
+ channelp->remote.window_size)
+ datalen = channelp->remote.window_size -
+ channelp->read_avail + data_head;
+
+ channelp->remote.window_size -= datalen - data_head;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "shrinking window size by %lu bytes to %lu, read_avail %lu",
+ datalen - data_head,
+ channelp->remote.window_size,
+ channelp->read_avail);
+
+ session->packAdd_channelp = channelp;
+
+ /* Adjust the window based on the block we just freed */
+ libssh2_packet_add_jump_point1:
+ session->packAdd_state = libssh2_NB_state_jump1;
+ rc = _libssh2_channel_receive_window_adjust(session->
+ packAdd_channelp,
+ datalen - 13,
+ 1, NULL);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ /*
+ * REMEMBER! remote means remote as source of data,
+ * NOT remote window!
+ */
+ if (channelp->remote.packet_size < (datalen - data_head)) {
+ /*
+ * Spec says we MAY ignore bytes sent beyond
+ * packet_size
+ */
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
+ "Packet contains more data than we offered"
+ " to receive, truncating");
+ datalen = channelp->remote.packet_size + data_head;
+ }
+ if (channelp->remote.window_size <= channelp->read_avail) {
+ /*
+ * Spec says we MAY ignore bytes sent beyond
+ * window_size
+ */
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
+ "The current receive window is full,"
+ " data ignored");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ /* Reset EOF status */
+ channelp->remote.eof = 0;
+
+ if (channelp->read_avail + datalen - data_head >
+ channelp->remote.window_size) {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
+ "Remote sent more data than current "
+ "window allows, truncating");
+ datalen = channelp->remote.window_size -
+ channelp->read_avail + data_head;
+ }
+
+ /* Update the read_avail counter. The window size will be
+ * updated once the data is actually read from the queue
+ * from an upper layer */
+ channelp->read_avail += datalen - data_head;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "increasing read_avail by %lu bytes to %lu/%lu",
+ (long)(datalen - data_head),
+ (long)channelp->read_avail,
+ (long)channelp->remote.window_size);
+
+ break;
+
+ /*
+ byte SSH_MSG_CHANNEL_EOF
+ uint32 recipient channel
+ */
+
+ case SSH_MSG_CHANNEL_EOF:
+ if(datalen >= 5)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if (!channelp)
+ /* We may have freed already, just quietly ignore this... */
+ ;
+ else {
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "EOF received for channel %lu/%lu",
+ channelp->local.id,
+ channelp->remote.id);
+ channelp->remote.eof = 1;
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_REQUEST
+ uint32 recipient channel
+ string request type in US-ASCII characters only
+ boolean want reply
+ .... type-specific data follows
+ */
+
+ case SSH_MSG_CHANNEL_REQUEST:
+ if(datalen >= 9) {
+ uint32_t channel = _libssh2_ntohu32(data + 1);
+ uint32_t len = _libssh2_ntohu32(data + 5);
+ unsigned char want_reply = 1;
+
+ if(len < (datalen - 10))
+ want_reply = data[9 + len];
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "Channel %d received request type %.*s (wr %X)",
+ channel, len, data + 9, want_reply);
+
+ if (len == sizeof("exit-status") - 1
+ && !memcmp("exit-status", data + 9,
+ sizeof("exit-status") - 1)) {
+
+ /* we've got "exit-status" packet. Set the session value */
+ if(datalen >= 20)
+ channelp =
+ _libssh2_channel_locate(session, channel);
+
+ if (channelp) {
+ channelp->exit_status =
+ _libssh2_ntohu32(data + 9 + sizeof("exit-status"));
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Exit status %lu received for "
+ "channel %lu/%lu",
+ channelp->exit_status,
+ channelp->local.id,
+ channelp->remote.id);
+ }
+
+ }
+ else if (len == sizeof("exit-signal") - 1
+ && !memcmp("exit-signal", data + 9,
+ sizeof("exit-signal") - 1)) {
+ /* command terminated due to signal */
+ if(datalen >= 20)
+ channelp = _libssh2_channel_locate(session, channel);
+
+ if (channelp) {
+ /* set signal name (without SIG prefix) */
+ uint32_t namelen =
+ _libssh2_ntohu32(data + 9 + sizeof("exit-signal"));
+ channelp->exit_signal =
+ LIBSSH2_ALLOC(session, namelen + 1);
+ if (!channelp->exit_signal)
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "memory for signal name");
+ else {
+ memcpy(channelp->exit_signal,
+ data + 13 + sizeof("exit_signal"), namelen);
+ channelp->exit_signal[namelen] = '\0';
+ /* TODO: save error message and language tag */
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Exit signal %s received for "
+ "channel %lu/%lu",
+ channelp->exit_signal,
+ channelp->local.id,
+ channelp->remote.id);
+ }
+ }
+ }
+
+
+ if (want_reply) {
+ unsigned char packet[5];
+ libssh2_packet_add_jump_point4:
+ session->packAdd_state = libssh2_NB_state_jump4;
+ packet[0] = SSH_MSG_CHANNEL_FAILURE;
+ memcpy(&packet[1], data+1, 4);
+ rc = _libssh2_transport_send(session, packet, 5, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return rc;
+
+ /*
+ byte SSH_MSG_CHANNEL_CLOSE
+ uint32 recipient channel
+ */
+
+ case SSH_MSG_CHANNEL_CLOSE:
+ if(datalen >= 5)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if (!channelp) {
+ /* We may have freed already, just quietly ignore this... */
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Close received for channel %lu/%lu",
+ channelp->local.id,
+ channelp->remote.id);
+
+ channelp->remote.close = 1;
+ channelp->remote.eof = 1;
+
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_OPEN
+ string "session"
+ uint32 sender channel
+ uint32 initial window size
+ uint32 maximum packet size
+ */
+
+ case SSH_MSG_CHANNEL_OPEN:
+ if(datalen < 17)
+ ;
+ else if ((datalen >= (sizeof("forwarded-tcpip") + 4)) &&
+ ((sizeof("forwarded-tcpip") - 1) ==
+ _libssh2_ntohu32(data + 1))
+ &&
+ (memcmp(data + 5, "forwarded-tcpip",
+ sizeof("forwarded-tcpip") - 1) == 0)) {
+
+ /* init the state struct */
+ memset(&session->packAdd_Qlstn_state, 0,
+ sizeof(session->packAdd_Qlstn_state));
+
+ libssh2_packet_add_jump_point2:
+ session->packAdd_state = libssh2_NB_state_jump2;
+ rc = packet_queue_listener(session, data, datalen,
+ &session->packAdd_Qlstn_state);
+ }
+ else if ((datalen >= (sizeof("x11") + 4)) &&
+ ((sizeof("x11") - 1) == _libssh2_ntohu32(data + 1)) &&
+ (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) {
+
+ /* init the state struct */
+ memset(&session->packAdd_x11open_state, 0,
+ sizeof(session->packAdd_x11open_state));
+
+ libssh2_packet_add_jump_point3:
+ session->packAdd_state = libssh2_NB_state_jump3;
+ rc = packet_x11_open(session, data, datalen,
+ &session->packAdd_x11open_state);
+ }
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return rc;
+
+ /*
+ byte SSH_MSG_CHANNEL_WINDOW_ADJUST
+ uint32 recipient channel
+ uint32 bytes to add
+ */
+ case SSH_MSG_CHANNEL_WINDOW_ADJUST:
+ if(datalen < 9)
+ ;
+ else {
+ uint32_t bytestoadd = _libssh2_ntohu32(data + 5);
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if(channelp) {
+ channelp->local.window_size += bytestoadd;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Window adjust for channel %lu/%lu, "
+ "adding %lu bytes, new window_size=%lu",
+ channelp->local.id,
+ channelp->remote.id,
+ bytestoadd,
+ channelp->local.window_size);
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ default:
+ break;
+ }
+
+ session->packAdd_state = libssh2_NB_state_sent;
+ }
+
+ if (session->packAdd_state == libssh2_NB_state_sent) {
+ LIBSSH2_PACKET *packetp =
+ LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
+ if (!packetp) {
+ _libssh2_debug(session, LIBSSH2_ERROR_ALLOC,
+ "memory for packet");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return LIBSSH2_ERROR_ALLOC;
+ }
+ packetp->data = data;
+ packetp->data_len = datalen;
+ packetp->data_head = data_head;
+
+ _libssh2_list_add(&session->packets, &packetp->node);
+
+ session->packAdd_state = libssh2_NB_state_sent1;
+ }
+
+ if ((msg == SSH_MSG_KEXINIT &&
+ !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) ||
+ (session->packAdd_state == libssh2_NB_state_sent2)) {
+ if (session->packAdd_state == libssh2_NB_state_sent1) {
+ /*
+ * Remote wants new keys
+ * Well, it's already in the brigade,
+ * let's just call back into ourselves
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Renegotiating Keys");
+
+ session->packAdd_state = libssh2_NB_state_sent2;
+ }
+
+ /*
+ * The KEXINIT message has been added to the queue. The packAdd and
+ * readPack states need to be reset because _libssh2_kex_exchange
+ * (eventually) calls upon _libssh2_transport_read to read the rest of
+ * the key exchange conversation.
+ */
+ session->readPack_state = libssh2_NB_state_idle;
+ session->packet.total_num = 0;
+ session->packAdd_state = libssh2_NB_state_idle;
+ session->fullpacket_state = libssh2_NB_state_idle;
+
+ memset(&session->startup_key_state, 0, sizeof(key_exchange_state_t));
+
+ /*
+ * If there was a key reexchange failure, let's just hope we didn't
+ * send NEWKEYS yet, otherwise remote will drop us like a rock
+ */
+ rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * _libssh2_packet_ask
+ *
+ * Scan the brigade for a matching packet type, optionally poll the socket for
+ * a packet first
+ */
+int
+_libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs, const unsigned char *match_buf,
+ size_t match_len)
+{
+ LIBSSH2_PACKET *packet = _libssh2_list_first(&session->packets);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Looking for packet of type: %d", (int) packet_type);
+
+ while (packet) {
+ if (packet->data[0] == packet_type
+ && (packet->data_len >= (match_ofs + match_len))
+ && (!match_buf ||
+ (memcmp(packet->data + match_ofs, match_buf,
+ match_len) == 0))) {
+ *data = packet->data;
+ *data_len = packet->data_len;
+
+ /* unlink struct from session->packets */
+ _libssh2_list_remove(&packet->node);
+
+ LIBSSH2_FREE(session, packet);
+
+ return 0;
+ }
+ packet = _libssh2_list_next(&packet->node);
+ }
+ return -1;
+}
+
+/*
+ * libssh2_packet_askv
+ *
+ * Scan for any of a list of packet types in the brigade, optionally poll the
+ * socket for a packet first
+ */
+int
+_libssh2_packet_askv(LIBSSH2_SESSION * session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len)
+{
+ int i, packet_types_len = strlen((char *) packet_types);
+
+ for(i = 0; i < packet_types_len; i++) {
+ if (0 == _libssh2_packet_ask(session, packet_types[i], data,
+ data_len, match_ofs,
+ match_buf, match_len)) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * _libssh2_packet_require
+ *
+ * Loops _libssh2_transport_read() until the packet requested is available
+ * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
+ *
+ * Returns negative on error
+ * Returns 0 when it has taken care of the requested packet.
+ */
+int
+_libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len,
+ packet_require_state_t *state)
+{
+ if (state->start == 0) {
+ if (_libssh2_packet_ask(session, packet_type, data, data_len,
+ match_ofs, match_buf,
+ match_len) == 0) {
+ /* A packet was available in the packet brigade */
+ return 0;
+ }
+
+ state->start = time(NULL);
+ }
+
+ while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ int ret = _libssh2_transport_read(session);
+ if (ret == LIBSSH2_ERROR_EAGAIN)
+ return ret;
+ else if (ret < 0) {
+ state->start = 0;
+ /* an error which is not just because of blocking */
+ return ret;
+ } else if (ret == packet_type) {
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ ret = _libssh2_packet_ask(session, packet_type, data, data_len,
+ match_ofs, match_buf, match_len);
+ state->start = 0;
+ return ret;
+ } else if (ret == 0) {
+ /* nothing available, wait until data arrives or we time out */
+ long left = LIBSSH2_READ_TIMEOUT - (long)(time(NULL) -
+ state->start);
+
+ if (left <= 0) {
+ state->start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ return -1; /* no packet available yet */
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/*
+ * _libssh2_packet_burn
+ *
+ * Loops _libssh2_transport_read() until any packet is available and promptly
+ * discards it.
+ * Used during KEX exchange to discard badly guessed KEX_INIT packets
+ */
+int
+_libssh2_packet_burn(LIBSSH2_SESSION * session,
+ libssh2_nonblocking_states * state)
+{
+ unsigned char *data;
+ size_t data_len;
+ unsigned char i, all_packets[255];
+ int ret;
+
+ if (*state == libssh2_NB_state_idle) {
+ for(i = 1; i < 255; i++) {
+ all_packets[i - 1] = i;
+ }
+ all_packets[254] = 0;
+
+ if (_libssh2_packet_askv(session, all_packets, &data, &data_len, 0,
+ NULL, 0) == 0) {
+ i = data[0];
+ /* A packet was available in the packet brigade, burn it */
+ LIBSSH2_FREE(session, data);
+ return i;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Blocking until packet becomes available to burn");
+ *state = libssh2_NB_state_created;
+ }
+
+ while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ ret = _libssh2_transport_read(session);
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ } else if (ret < 0) {
+ *state = libssh2_NB_state_idle;
+ return ret;
+ } else if (ret == 0) {
+ /* FIXME: this might busyloop */
+ continue;
+ }
+
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ if (0 ==
+ _libssh2_packet_ask(session, (unsigned char)ret,
+ &data, &data_len, 0, NULL, 0)) {
+ /* Smoke 'em if you got 'em */
+ LIBSSH2_FREE(session, data);
+ *state = libssh2_NB_state_idle;
+ return ret;
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/*
+ * _libssh2_packet_requirev
+ *
+ * Loops _libssh2_transport_read() until one of a list of packet types
+ * requested is available. SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause
+ * a bailout. packet_types is a null terminated list of packet_type numbers
+ */
+
+int
+_libssh2_packet_requirev(LIBSSH2_SESSION *session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf, size_t match_len,
+ packet_requirev_state_t * state)
+{
+ if (_libssh2_packet_askv(session, packet_types, data, data_len, match_ofs,
+ match_buf, match_len) == 0) {
+ /* One of the packets listed was available in the packet brigade */
+ state->start = 0;
+ return 0;
+ }
+
+ if (state->start == 0) {
+ state->start = time(NULL);
+ }
+
+ while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) {
+ int ret = _libssh2_transport_read(session);
+ if ((ret < 0) && (ret != LIBSSH2_ERROR_EAGAIN)) {
+ state->start = 0;
+ return ret;
+ }
+ if (ret <= 0) {
+ long left = LIBSSH2_READ_TIMEOUT -
+ (long)(time(NULL) - state->start);
+
+ if (left <= 0) {
+ state->start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ else if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+ }
+
+ if (strchr((char *) packet_types, ret)) {
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ return _libssh2_packet_askv(session, packet_types, data,
+ data_len, match_ofs, match_buf,
+ match_len);
+ }
+ }
+
+ /* Only reached if the socket died */
+ state->start = 0;
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+