コード例 #1
0
ファイル: packet.c プロジェクト: kruton/connectbot_native
/*
 * _libssh2_packet_add
 *
 * Create a new packet and attach it to the brigade. Called from the transport
 * layer when it as received a packet.
 */
int
_libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
                    size_t datalen, int macstate)
{
    int rc;

    if (session->packAdd_state == libssh2_NB_state_idle) {
        session->packAdd_data_head = 0;

        /* Zero the whole thing out */
        memset(&session->packAdd_key_state, 0,
               sizeof(session->packAdd_key_state));

        /* Zero the whole thing out */
        memset(&session->packAdd_Qlstn_state, 0,
               sizeof(session->packAdd_Qlstn_state));

        /* Zero the whole thing out */
        memset(&session->packAdd_x11open_state, 0,
               sizeof(session->packAdd_x11open_state));

        _libssh2_debug(session, LIBSSH2_DBG_TRANS,
                       "Packet type %d received, length=%d",
                       (int) data[0], (int) datalen);
        if (macstate == LIBSSH2_MAC_INVALID) {
            if (session->macerror) {
                if (LIBSSH2_MACERROR(session, (char *) data, datalen) == 0) {
                    /* Calling app has given the OK, Process it anyway */
                    macstate = LIBSSH2_MAC_CONFIRMED;
                } else {
                    libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC,
                                  "Invalid Message Authentication Code received",
                                  0);
                    if (session->ssh_msg_disconnect) {
                        LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR,
                                           "Invalid MAC received",
                                           sizeof("Invalid MAC received") - 1,
                                           "", 0);
                    }
                    LIBSSH2_FREE(session, data);
                    return -1;
                }
            } else {
                libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC,
                              "Invalid Message Authentication Code received",
                              0);
                if (session->ssh_msg_disconnect) {
                    LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR,
                                       "Invalid MAC received",
                                       sizeof("Invalid MAC received") - 1,
                                       "", 0);
                }
                LIBSSH2_FREE(session, data);
                return -1;
            }
        }

        session->packAdd_state = libssh2_NB_state_allocated;
    }

    /*
     * =============================== NOTE ===============================
     * I know this is very ugly and not a really good use of "goto", but
     * this case statement would be even uglier to do it any other way
     */
    if (session->packAdd_state == libssh2_NB_state_jump1) {
        goto libssh2_packet_add_jump_point1;
    } else if (session->packAdd_state == libssh2_NB_state_jump2) {
        goto libssh2_packet_add_jump_point2;
    } else if (session->packAdd_state == libssh2_NB_state_jump3) {
        goto libssh2_packet_add_jump_point3;
    }

    if (session->packAdd_state == libssh2_NB_state_allocated) {
        /* A couple exceptions to the packet adding rule: */
        switch (data[0]) {
        case SSH_MSG_DISCONNECT:
            {
                char *message, *language;
                int reason, message_len, language_len;

                reason = _libssh2_ntohu32(data + 1);
                message_len = _libssh2_ntohu32(data + 5);
                /* 9 = packet_type(1) + reason(4) + message_len(4) */
                message = (char *) data + 9;
                language_len = _libssh2_ntohu32(data + 9 + message_len);
                /*
                 * This is where we hack on the data a little,
                 * Use the MSB of language_len to to a terminating NULL
                 * (In all liklihood it is already)
                 * Shift the language tag back a byte (In all likelihood
                 * it's zero length anyway)
                 * Store a NULL in the last byte of the packet to terminate
                 * the language string
                 * With the lengths passed this isn't *REALLY* necessary,
                 * but it's "kind"
                 */
                message[message_len] = '\0';
                language = (char *) data + 9 + message_len + 3;
                if (language_len) {
                    memcpy(language, language + 1, language_len);
                }
                language[language_len] = '\0';

                if (session->ssh_msg_disconnect) {
                    LIBSSH2_DISCONNECT(session, reason, message,
                                       message_len, language, language_len);
                }
                _libssh2_debug(session, LIBSSH2_DBG_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 -1;
            }
            break;

        case SSH_MSG_IGNORE:
            /* As with disconnect, back it up one and add a trailing NULL */
            memcpy(data + 4, data + 5, datalen - 5);
            data[datalen] = '\0';
            if (session->ssh_msg_ignore) {
                LIBSSH2_IGNORE(session, (char *) data + 4, datalen - 5);
            }
            LIBSSH2_FREE(session, data);
            session->packAdd_state = libssh2_NB_state_idle;
            return 0;
            break;

        case SSH_MSG_DEBUG:
            {
                int always_display = data[0];
                char *message, *language;
                int message_len, language_len;

                message_len = _libssh2_ntohu32(data + 2);
                /* 6 = packet_type(1) + display(1) + message_len(4) */
                message = (char *) data + 6;
                language_len = _libssh2_ntohu32(data + 6 + message_len);
                /*
                 * This is where we hack on the data a little,
                 * Use the MSB of language_len to to a terminating NULL
                 * (In all liklihood it is already)
                 * Shift the language tag back a byte (In all likelihood
                 * it's zero length anyway)
                 * Store a NULL in the last byte of the packet to terminate
                 * the language string
                 * With the lengths passed this isn't *REALLY* necessary,
                 * but it's "kind"
                 */
                message[message_len] = '\0';
                language = (char *) data + 6 + message_len + 3;
                if (language_len) {
                    memcpy(language, language + 1, language_len);
                }
                language[language_len] = '\0';

                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_DBG_TRANS,
                               "Debug Packet: %s", message);
                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return 0;
            }
            break;

        case SSH_MSG_CHANNEL_EXTENDED_DATA:
            /* streamid(4) */
            session->packAdd_data_head += 4;
        case SSH_MSG_CHANNEL_DATA:
            /* packet_type(1) + channelno(4) + datalen(4) */
            session->packAdd_data_head += 9;

            session->packAdd_channel =
                _libssh2_channel_locate(session, _libssh2_ntohu32(data + 1));

            if (!session->packAdd_channel) {
                libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN,
                              "Packet received for unknown channel, ignoring",
                              0);
                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return 0;
            }
#ifdef LIBSSH2DEBUG
            {
                unsigned long stream_id = 0;
                if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) {
                    stream_id = _libssh2_ntohu32(data + 5);
                }

                _libssh2_debug(session, LIBSSH2_DBG_CONN,
                               "%d bytes packet_add() for %lu/%lu/%lu",
                               (int) (datalen - session->packAdd_data_head),
                               session->packAdd_channel->local.id,
                               session->packAdd_channel->remote.id,
                               stream_id);
            }
#endif
            if ((session->packAdd_channel->remote.extended_data_ignore_mode ==
                 LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) &&
                (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) {
                /* Pretend we didn't receive this */
                LIBSSH2_FREE(session, data);

                _libssh2_debug(session, LIBSSH2_DBG_CONN,
                               "Ignoring extended data and refunding %d bytes",
                               (int) (datalen - 13));
                /* 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_channel,
                                                           datalen - 13,
                                                           0);
                if (rc == PACKET_EAGAIN) {
                    session->socket_block_directions =
                        LIBSSH2_SESSION_BLOCK_OUTBOUND;
                    return PACKET_EAGAIN;
                }
                session->packAdd_state = libssh2_NB_state_idle;
                return 0;
            }

            /*
             * REMEMBER! remote means remote as source of data,
             * NOT remote window!
             */
            if (session->packAdd_channel->remote.packet_size <
                (datalen - session->packAdd_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", 0);
                datalen =
                    session->packAdd_channel->remote.packet_size +
                    session->packAdd_data_head;
            }
            if (session->packAdd_channel->remote.window_size <= 0) {
                /*
                 * 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",
                              0);
                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return 0;
            }
            /* Reset EOF status */
            session->packAdd_channel->remote.eof = 0;

            if ((datalen - session->packAdd_data_head) >
                session->packAdd_channel->remote.window_size) {
                libssh2_error(session,
                              LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
                              "Remote sent more data than current "
                              "window allows, truncating",
                              0);
                datalen =
                    session->packAdd_channel->remote.window_size +
                    session->packAdd_data_head;
            }
            else {
                /* Now that we've received it, shrink our window */
                session->packAdd_channel->remote.window_size -=
                    datalen - session->packAdd_data_head;
            }

            break;

        case SSH_MSG_CHANNEL_EOF:
            {
                session->packAdd_channel =
                    _libssh2_channel_locate(session, _libssh2_ntohu32(data + 1));

                if (!session->packAdd_channel) {
                    /* 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_DBG_CONN,
                               "EOF received for channel %lu/%lu",
                               session->packAdd_channel->local.id,
                               session->packAdd_channel->remote.id);
                session->packAdd_channel->remote.eof = 1;

                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return 0;
            }
            break;

        case SSH_MSG_CHANNEL_REQUEST:
            {
                if (_libssh2_ntohu32(data + 5) == sizeof("exit-status") - 1
                    && !memcmp("exit-status", data + 9,
                               sizeof("exit-status") - 1)) {

                    /* we've got "exit-status" packet. Set the session value */
                    session->packAdd_channel =
                        _libssh2_channel_locate(session,
                                                _libssh2_ntohu32(data + 1));

                    if (session->packAdd_channel) {
                        session->packAdd_channel->exit_status =
                            _libssh2_ntohu32(data + 9 + sizeof("exit-status"));
                        _libssh2_debug(session, LIBSSH2_DBG_CONN,
                                       "Exit status %lu received for channel %lu/%lu",
                                       session->packAdd_channel->exit_status,
                                       session->packAdd_channel->local.id,
                                       session->packAdd_channel->remote.id);
                    }

                    LIBSSH2_FREE(session, data);
                    session->packAdd_state = libssh2_NB_state_idle;
                    return 0;
                }
            }
            break;

        case SSH_MSG_CHANNEL_CLOSE:
            {
                session->packAdd_channel =
                    _libssh2_channel_locate(session, _libssh2_ntohu32(data + 1));

                if (!session->packAdd_channel) {
                    /* 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_DBG_CONN,
                               "Close received for channel %lu/%lu",
                               session->packAdd_channel->local.id,
                               session->packAdd_channel->remote.id);

                session->packAdd_channel->remote.close = 1;
                session->packAdd_channel->remote.eof = 1;
                /* TODO: Add a callback for this */

                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return 0;
            }
            break;

        case SSH_MSG_CHANNEL_OPEN:
            if ((datalen >= (sizeof("forwarded-tcpip") + 4)) &&
                ((sizeof("forwarded-tcpip") - 1) == _libssh2_ntohu32(data + 1))
                &&
                (memcmp
                 (data + 5, "forwarded-tcpip",
                  sizeof("forwarded-tcpip") - 1) == 0)) {

              libssh2_packet_add_jump_point2:
                session->packAdd_state = libssh2_NB_state_jump2;
                rc = packet_queue_listener(session, data, datalen,
                                           &session->packAdd_Qlstn_state);
                if (rc == PACKET_EAGAIN) {
                    session->socket_block_directions =
                        LIBSSH2_SESSION_BLOCK_OUTBOUND;
                    return PACKET_EAGAIN;
                }

                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return rc;
            }
            if ((datalen >= (sizeof("x11") + 4)) &&
                ((sizeof("x11") - 1) == _libssh2_ntohu32(data + 1)) &&
                (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) {

              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 == PACKET_EAGAIN) {
                    session->socket_block_directions =
                        LIBSSH2_SESSION_BLOCK_OUTBOUND;
                    return PACKET_EAGAIN;
                }

                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return rc;
            }
            break;

        case SSH_MSG_CHANNEL_WINDOW_ADJUST:
            {
                unsigned long bytestoadd = _libssh2_ntohu32(data + 5);
                session->packAdd_channel =
                    _libssh2_channel_locate(session,
                                            _libssh2_ntohu32(data + 1));

                if (session->packAdd_channel && bytestoadd) {
                    session->packAdd_channel->local.window_size += bytestoadd;
                }
                _libssh2_debug(session, LIBSSH2_DBG_CONN,
                               "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu",
                               session->packAdd_channel->local.id,
                               session->packAdd_channel->remote.id,
                               bytestoadd,
                               session->packAdd_channel->local.window_size);

                LIBSSH2_FREE(session, data);
                session->packAdd_state = libssh2_NB_state_idle;
                return 0;
            }
            break;
        }

        session->packAdd_state = libssh2_NB_state_sent;
    }

    if (session->packAdd_state == libssh2_NB_state_sent) {
        session->packAdd_packet =
            LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
        if (!session->packAdd_packet) {
            _libssh2_debug(session, LIBSSH2_ERROR_ALLOC,
                           "Unable to allocate memory for LIBSSH2_PACKET");
            LIBSSH2_FREE(session, data);
            session->packAdd_state = libssh2_NB_state_idle;
            return -1;
        }
        memset(session->packAdd_packet, 0, sizeof(LIBSSH2_PACKET));

        session->packAdd_packet->data = data;
        session->packAdd_packet->data_len = datalen;
        session->packAdd_packet->data_head = session->packAdd_data_head;
        session->packAdd_packet->mac = macstate;
        session->packAdd_packet->brigade = &session->packets;
        session->packAdd_packet->next = NULL;

        if (session->packets.tail) {
            session->packAdd_packet->prev = session->packets.tail;
            session->packAdd_packet->prev->next = session->packAdd_packet;
            session->packets.tail = session->packAdd_packet;
        } else {
            session->packets.head = session->packAdd_packet;
            session->packets.tail = session->packAdd_packet;
            session->packAdd_packet->prev = NULL;
        }

        session->packAdd_state = libssh2_NB_state_sent1;
    }

    if ((data[0] == 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_DBG_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;

        /*
         * Also, don't use packAdd_key_state for key re-exchange,
         * as it will be wiped out in the middle of the exchange.
         * How about re-using the startup_key_state?
         */
        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 == PACKET_EAGAIN) {
            return PACKET_EAGAIN;
        }
    }

    session->packAdd_state = libssh2_NB_state_idle;
    return 0;
}
コード例 #2
0
ファイル: packet.c プロジェクト: Zarzuelo/pandorafms
/* {{{ libssh2_packet_new
 * Create a new packet and attach it to the brigade
 */
static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate)
{
	LIBSSH2_PACKET *packet;
	unsigned long data_head = 0;

#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen);
#endif
	if (macstate == LIBSSH2_MAC_INVALID) {
		if (session->macerror) {
			if (LIBSSH2_MACERROR(session, data, datalen) == 0) {
				/* Calling app has given the OK, Process it anyway */
				macstate = LIBSSH2_MAC_CONFIRMED;
			} else {
				libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0);
				if (session->ssh_msg_disconnect) {
					LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0);
				}
				return -1;
			}
		} else {
			libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0);
			if (session->ssh_msg_disconnect) {
				LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0);
			}
			return -1;
		}
	}

	/* A couple exceptions to the packet adding rule: */
	switch (data[0]) {
		case SSH_MSG_DISCONNECT:
		{
			char *message, *language;
			int reason, message_len, language_len;

			reason = libssh2_ntohu32(data + 1);
			message_len = libssh2_ntohu32(data + 5);
			message = data + 9; /* packet_type(1) + reason(4) + message_len(4) */
			language_len = libssh2_ntohu32(data + 9 + message_len);
			/* This is where we hack on the data a little,
			 * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already)
			 * Shift the language tag back a byte (In all likelihood it's zero length anyway
			 * Store a NULL in the last byte of the packet to terminate the language string
			 * With the lengths passed this isn't *REALLY* necessary, but it's "kind"
			 */
			message[message_len] = '\0';
			language = data + 9 + message_len + 3;
			if (language_len) {
				memcpy(language, language + 1, language_len);
			}
			language[language_len] = '\0';

			if (session->ssh_msg_disconnect) {
				LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len);
			}
#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language);
#endif
			LIBSSH2_FREE(session, data);
			session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
			return -1;
		}
			break;
		case SSH_MSG_IGNORE:
			/* As with disconnect, back it up one and add a trailing NULL */
			memcpy(data + 4, data + 5, datalen - 5);
			data[datalen] = '\0';
			if (session->ssh_msg_ignore) {
				LIBSSH2_IGNORE(session, data + 4, datalen - 5);
			}
			LIBSSH2_FREE(session, data);
			return 0;
			break;
		case SSH_MSG_DEBUG:
		{
			int always_display = data[0];
			char *message, *language;
			int message_len, language_len;

			message_len = libssh2_ntohu32(data + 2);
			message = data + 6; /* packet_type(1) + display(1) + message_len(4) */
			language_len = libssh2_ntohu32(data + 6 + message_len);
			/* This is where we hack on the data a little,
			 * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already)
			 * Shift the language tag back a byte (In all likelihood it's zero length anyway
			 * Store a NULL in the last byte of the packet to terminate the language string
			 * With the lengths passed this isn't *REALLY* necessary, but it's "kind"
			 */
			message[message_len] = '\0';
			language = data + 6 + message_len + 3;
			if (language_len) {
				memcpy(language, language + 1, language_len);
			}
			language[language_len] = '\0';

			if (session->ssh_msg_debug) {
				LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len);
			}
#ifdef LIBSSH2_DEBUG_TRANSPORT
	/* _libssh2_debug will actually truncate this for us so that it's not an inordinate about of data */
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Debug Packet: %s", message);
#endif
			LIBSSH2_FREE(session, data);
			return 0;
		}
			break;
		case SSH_MSG_CHANNEL_EXTENDED_DATA:
			data_head += 4; /* streamid(4) */
		case SSH_MSG_CHANNEL_DATA:
			data_head += 9; /* packet_type(1) + channelno(4) + datalen(4) */
			{
				LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));

				if (!channel) {
					libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, "Packet received for unknown channel, ignoring", 0);
					LIBSSH2_FREE(session, data);
					return 0;
				}
#ifdef LIBSSH2_DEBUG_CONNECTION
{
	unsigned long stream_id = 0;

	if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) {
		stream_id = libssh2_ntohu32(data + 5);
	}

	_libssh2_debug(session, LIBSSH2_DBG_CONN, "%d bytes received for channel %lu/%lu stream #%lu", (int)(datalen - data_head), channel->local.id, channel->remote.id, stream_id);
}
#endif
				if ((channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) {
					/* Pretend we didn't receive this */
					LIBSSH2_FREE(session, data);

#ifdef LIBSSH2_DEBUG_CONNECTION
	_libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13));
#endif
					/* Adjust the window based on the block we just freed */
					libssh2_channel_receive_window_adjust(channel, datalen - 13, 0);

					return 0;
				}

				/* REMEMBER! remote means remote as source of data, NOT remote window! */
				if (channel->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", 0);
					datalen = channel->remote.packet_size + data_head;
				}
				if (channel->remote.window_size <= 0) {
					/* 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", 0);
					LIBSSH2_FREE(session, data);
					return 0;
				}
				/* Reset EOF status */
				channel->remote.eof = 0;

				if ((datalen - data_head) > channel->remote.window_size) {
					libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0);
					datalen = channel->remote.window_size + data_head;
				} else {
					/* Now that we've received it, shrink our window */
					channel->remote.window_size -= datalen - data_head;
				}
			}
			break;
		case SSH_MSG_CHANNEL_EOF:
			{
				LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));

				if (!channel) {
					/* We may have freed already, just quietly ignore this... */
					LIBSSH2_FREE(session, data);
					return 0;
				}

#ifdef LIBSSH2_DEBUG_CONNECTION
	_libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id);
#endif
				channel->remote.eof = 1;

				LIBSSH2_FREE(session, data);
				return 0;
			}
			break;
	    case SSH_MSG_CHANNEL_REQUEST:
		    {
				if (libssh2_ntohu32(data+5) == sizeof("exit-status") - 1
					&& !memcmp("exit-status", data + 9, sizeof("exit-status") - 1)) {

					/* we've got "exit-status" packet. Set the session value */
					LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data+1));

					if (channel) {
						channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status"));
#ifdef LIBSSH2_DEBUG_CONNECTION
						_libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", channel->exit_status, channel->local.id, channel->remote.id);
#endif
					}

					LIBSSH2_FREE(session, data);
					return 0;
				}
			}
			break;
		case SSH_MSG_CHANNEL_CLOSE:
			{
				LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));

				if (!channel) {
					/* We may have freed already, just quietly ignore this... */
					LIBSSH2_FREE(session, data);
					return 0;
				}
#ifdef LIBSSH2_DEBUG_CONNECTION
	_libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id);
#endif

				channel->remote.close = 1;
				/* TODO: Add a callback for this */

				LIBSSH2_FREE(session, data);
				return 0;
			}
			break;
		case SSH_MSG_CHANNEL_OPEN:
			if ((datalen >= (sizeof("forwarded-tcpip") + 4)) &&
				((sizeof("forwarded-tcpip")-1) == libssh2_ntohu32(data + 1)) &&
				(memcmp(data + 5, "forwarded-tcpip", sizeof("forwarded-tcpip") - 1) == 0)) {
				int retval = libssh2_packet_queue_listener(session, data, datalen);

				LIBSSH2_FREE(session, data);
				return retval;
			}
			if ((datalen >= (sizeof("x11") + 4)) &&
				((sizeof("x11")-1) == libssh2_ntohu32(data + 1)) &&
				(memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) {
				int retval = libssh2_packet_x11_open(session, data, datalen);

				LIBSSH2_FREE(session, data);
				return retval;
			}
			break;
		case SSH_MSG_CHANNEL_WINDOW_ADJUST:
			{
				LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));
				unsigned long bytestoadd = libssh2_ntohu32(data + 5);

				if (channel && bytestoadd) {
					channel->local.window_size += bytestoadd;
				}
#ifdef LIBSSH2_DEBUG_CONNECTION
	_libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", channel->local.id, channel->remote.id, bytestoadd, channel->local.window_size);
#endif

				LIBSSH2_FREE(session, data);
				return 0;
			}
			break;
	}

	packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
	memset(packet, 0, sizeof(LIBSSH2_PACKET));

	packet->data = data;
	packet->data_len = datalen;
	packet->data_head = data_head;
	packet->mac = macstate;
	packet->brigade = &session->packets;
	packet->next = NULL;

	if (session->packets.tail) {
		packet->prev = session->packets.tail;
		packet->prev->next = packet;
		session->packets.tail = packet;
	} else {
		session->packets.head = packet;
		session->packets.tail = packet;
		packet->prev = NULL;
	}

	if (data[0] == SSH_MSG_KEXINIT && !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) {
		/* Remote wants new keys
		 * Well, it's already in the brigade,
		 * let's just call back into ourselves
		 */
#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys");
#endif
		libssh2_kex_exchange(session, 1);
		/* 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 */
	}

	return 0;
}
コード例 #3
0
ファイル: packet.c プロジェクト: Aahanbhatt/robomongo
/*
 * _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;
}