From 530e57d4ac43d6969b8f235346891d574d0e9e56 Mon Sep 17 00:00:00 2001 From: James Housley Date: Thu, 5 Jul 2007 11:08:17 +0000 Subject: [PATCH] Gavrie Philipson sumitted a patch to get the actual text of the error on scp upload. --- ChangeLog | 4 ++ example/simple/scp_write_nonblock.c | 7 +++- src/channel.c | 48 ++++++++++++++++++++++- src/libssh2_priv.h | 3 ++ src/scp.c | 61 ++++++++++++++++++++++++----- 5 files changed, 111 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0967c9c..1b9b5dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2007-07-05 James Housley + * Gavrie Philipson sumitted a patch to get the actual text + of the error on scp upload. + 2007-06-23 James Housley * Eberhard Mattes submitted a patch "According to RFC 4251, a boolean value of true is encoded as 1, not as 0xff". Servers diff --git a/example/simple/scp_write_nonblock.c b/example/simple/scp_write_nonblock.c index 245feb9..4705edf 100644 --- a/example/simple/scp_write_nonblock.c +++ b/example/simple/scp_write_nonblock.c @@ -1,5 +1,5 @@ /* - * $Id: scp_write_nonblock.c,v 1.2 2007/06/08 13:33:08 jehousley Exp $ + * $Id: scp_write_nonblock.c,v 1.3 2007/07/05 11:08:17 jehousley Exp $ * * Sample showing how to do a simple SCP transfer. */ @@ -165,7 +165,10 @@ int main(int argc, char *argv[]) channel = libssh2_scp_send(session, scppath, 0x1FF & fileinfo.st_mode, (unsigned long)fileinfo.st_size); if ((!channel) && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { - fprintf(stderr, "Unable to open a session\n"); + char *err_msg; + + libssh2_session_last_error(session, &err_msg, NULL, 0); + fprintf(stderr, "%s\n", err_msg); goto shutdown; } } while (!channel); diff --git a/src/channel.c b/src/channel.c index eb9f7a7..6ef5aca 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1172,7 +1172,7 @@ libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel, int ignore_mode) /* }}} */ /* - * {{{ _libssh2_channel_read_ex + * {{{ libssh2_channel_read_ex * Read data from a channel blocking or non-blocking depending on set state * * When this is done non-blocking, it is important to not return 0 until the @@ -1329,6 +1329,52 @@ channel_read_ex_point1: } /* }}} */ +/* + * {{{ libssh2_channel_packet_data_len + * Return the size of the data block of the current packet, or 0 if there + * isn't a packet. + */ +unsigned long +libssh2_channel_packet_data_len(LIBSSH2_CHANNEL *channel, int stream_id) +{ + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_PACKET *read_packet; + uint32_t read_local_id; + + if ((read_packet = session->packets.head) == NULL) { + return 0; + } + + while (read_packet) { + read_local_id = libssh2_ntohu32(read_packet->data + 1); + + /* + * Either we asked for a specific extended data stream + * (and data was available), + * or the standard stream (and data was available), + * or the standard stream with extended_data_merge + * enabled and data was available + */ + if ((stream_id && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && + (channel->local.id == read_local_id) && + (stream_id == (int)libssh2_ntohu32(read_packet->data + 5))) || + + (!stream_id && (read_packet->data[0] == SSH_MSG_CHANNEL_DATA) && + (channel->local.id == read_local_id)) || + + (!stream_id && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && + (channel->local.id == read_local_id) && + (channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) { + + return (read_packet->data_len - read_packet->data_head); + } + read_packet = read_packet->next; + } + + return 0; +} +/* }}} */ + /* {{{ libssh2_channel_write_ex * Send data to a channel */ diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index a3b1d24..ca433b8 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -798,6 +798,8 @@ struct _LIBSSH2_SESSION { unsigned long scpSend_command_len; unsigned char scpSend_response[LIBSSH2_SCP_RESPONSE_BUFLEN]; unsigned long scpSend_response_len; + char *scpSend_err_msg; + long scpSend_err_len; LIBSSH2_CHANNEL *scpSend_channel; }; @@ -1041,6 +1043,7 @@ int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t dat int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange, key_exchange_state_t *state); unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session); LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id); +unsigned long libssh2_channel_packet_data_len(LIBSSH2_CHANNEL *channel, int stream_id); /* this is the lib-internal set blocking function */ int _libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking); diff --git a/src/scp.c b/src/scp.c index 4c3586b..30a90c0 100644 --- a/src/scp.c +++ b/src/scp.c @@ -419,6 +419,15 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t unsigned const char *base; int rc; + /* + * =============================== 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->scpSend_state == libssh2_NB_state_jump1) { + goto scp_read_ex_point1; + } + if (session->scpSend_state == libssh2_NB_state_idle) { session->scpSend_command_len = path_len + sizeof("scp -t "); @@ -577,15 +586,49 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t session->scpSend_state = libssh2_NB_state_sent6; } - /* Wait for ACK */ - rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *)session->scpSend_response, 1); - if (rc == PACKET_EAGAIN) { - libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response", 0); - return NULL; - } - else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { - libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); - goto scp_send_error; + if (session->scpSend_state == libssh2_NB_state_sent6) { + /* Wait for ACK */ + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *)session->scpSend_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response", 0); + return NULL; + } + else if (rc <= 0) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + goto scp_send_error; + } + else if (session->scpSend_response[0] != 0) { + session->scpSend_state = libssh2_NB_state_jump1; + + /* + * Set this as the default error for here, if + * we are successful it will be replaced + */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + + session->scpSend_err_len = libssh2_channel_packet_data_len(session->scpSend_channel, 0); + session->scpSend_err_msg = LIBSSH2_ALLOC(session, session->scpSend_err_len+1); + if (!session->scpSend_err_msg) { + goto scp_send_error; + } + memset(session->scpSend_err_msg, 0, session->scpSend_err_len+1); + + /* Read the remote error message */ +scp_read_ex_point1: + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, session->scpSend_err_msg, session->scpSend_err_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response", 0); + return NULL; + } + else if (rc <= 0) { + LIBSSH2_FREE(session, session->scpSend_err_msg); + session->scpSend_err_msg = NULL; + goto scp_send_error; + } + + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, session->scpSend_err_msg, 1); + goto scp_send_error; + } } session->scpSend_state = libssh2_NB_state_idle;