示例#1
0
/* {{{ 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;
}
示例#2
0
/*
 * _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;
}