libssh2_channel_free() actually can return PACKET_EAGAIN. Update all

calling functions to support that with the following API notes:

* libssh2_publickey_shutdown(), libssh2_session_free() changed
  to return an "int" to allow signaling of LIBSSH2_ERROR_EAGAIN.

* libssh2_scp_recv(), libssh2_scp_send_ex() and libssh2_sftp_init()
  will loop in on libssh2_channel_free() when there is an error.
  It is not possible to return LIBSSH2_ERROR_EAGAIN in this condition
  in these 3 functions and not lose the original error code.
This commit is contained in:
James Housley 2007-06-12 18:27:37 +00:00
parent f35ab0d070
commit dde2b09496
9 changed files with 197 additions and 191 deletions

View File

@ -1,3 +1,13 @@
2007-07-12 James Housley <jim@thehousleys.net>
* libssh2_publickey_shutdown(), libssh2_session_free() changed
to return an "int" to allow signaling of LIBSSH2_ERROR_EAGAIN.
* libssh2_scp_recv(), libssh2_scp_send_ex() and libssh2_sftp_init()
will loop in on libssh2_channel_free() when there is an error.
It is not possible to return LIBSSH2_ERROR_EAGAIN in this condition
in these 3 functions and not lose the original error code.
2007-06-10 James Housley <jim@thehousleys.net>
* The list of supported authentication types returned by

View File

@ -273,7 +273,7 @@ LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, const char *banner)
LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket);
LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang);
#define libssh2_session_disconnect(session, description) libssh2_session_disconnect_ex((session), SSH_DISCONNECT_BY_APPLICATION, (description), "")
LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session);
LIBSSH2_API int libssh2_session_free(LIBSSH2_SESSION *session);
LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type);
@ -358,9 +358,7 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const
#define libssh2_channel_exec(channel, command) libssh2_channel_process_startup((channel), "exec", sizeof("exec") - 1, (command), strlen(command))
#define libssh2_channel_subsystem(channel, subsystem) libssh2_channel_process_startup((channel), "subsystem", sizeof("subsystem") - 1, (subsystem), strlen(subsystem))
LIBSSH2_API ssize_t libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel,
int stream_id, char *buf,
size_t buflen);
LIBSSH2_API ssize_t libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen);
#define libssh2_channel_read(channel, buf, buflen) \
libssh2_channel_read_ex((channel), 0, (buf), (buflen))
#define libssh2_channel_read_stderr(channel, buf, buflen) \
@ -374,9 +372,7 @@ LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channe
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force);
LIBSSH2_API size_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
int stream_id, const char *buf,
size_t buflen);
LIBSSH2_API ssize_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen);
#define libssh2_channel_write(channel, buf, buflen) \
libssh2_channel_write_ex((channel), 0, (buf), (buflen))

View File

@ -92,7 +92,7 @@ LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsig
LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list);
LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, libssh2_publickey_list *pkey_list);
LIBSSH2_API void libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey);
LIBSSH2_API int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey);
#ifdef __cplusplus
} /* extern "C" */

View File

@ -505,6 +505,7 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
packet = listener->chanFwdCncl_data;
}
if (listener->chanFwdCncl_state == libssh2_NB_state_created) {
rc = libssh2_packet_write(session, packet, packet_len);
if (rc == PACKET_EAGAIN) {
listener->chanFwdCncl_data = packet;
@ -516,12 +517,17 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
return -1;
}
LIBSSH2_FREE(session, packet);
listener->chanFwdCncl_state = libssh2_NB_state_idle;
listener->chanFwdCncl_state = libssh2_NB_state_sent;
}
while (queued) {
LIBSSH2_CHANNEL *next = queued->next;
libssh2_channel_free(queued);
rc = libssh2_channel_free(queued);
if (rc == PACKET_EAGAIN) {
return PACKET_EAGAIN;
}
queued = next;
}
LIBSSH2_FREE(session, listener->host);
@ -537,6 +543,8 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
LIBSSH2_FREE(session, listener);
listener->chanFwdCncl_state = libssh2_NB_state_idle;
return 0;
}
/* }}} */
@ -1319,7 +1327,7 @@ channel_read_ex_point1:
/* {{{ libssh2_channel_write_ex
* Send data to a channel
*/
LIBSSH2_API size_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen)
LIBSSH2_API ssize_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen)
{
LIBSSH2_SESSION *session = channel->session;
libssh2pack_t rc;
@ -1660,12 +1668,27 @@ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
unsigned char channel_id[4];
unsigned char *data;
unsigned long data_len;
int rc;
if (channel->free_state == libssh2_NB_state_idle) {
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Freeing channel %lu/%lu resources", channel->local.id, channel->remote.id);
channel->free_state = libssh2_NB_state_created;
}
/* Allow channel freeing even when the socket has lost its connection */
if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED) && libssh2_channel_close(channel)) {
if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED)) {
while ((rc = libssh2_channel_close(channel)) == PACKET_EAGAIN);
if (rc == PACKET_EAGAIN) {
return PACKET_EAGAIN;
}
else if (rc) {
channel->free_state = libssh2_NB_state_idle;
return -1;
}
}
channel->free_state = libssh2_NB_state_idle;
/*
* channel->remote.close *might* not be set yet, Well...

View File

@ -337,6 +337,9 @@ struct _LIBSSH2_CHANNEL {
/* State variables used in libssh2_channel_wait_closed() */
libssh2_nonblocking_states wait_closed_state;
/* State variables used in libssh2_channel_free() */
libssh2_nonblocking_states free_state;
/* State variables used in libssh2_channel_handle_extended_data2() */
libssh2_nonblocking_states extData2_state;
};
@ -648,6 +651,9 @@ struct _LIBSSH2_SESSION {
packet_require_state_t startup_req_state;
key_exchange_state_t startup_key_state;
/* State variables used in libssh2_session_free() */
libssh2_nonblocking_states free_state;
/* State variables used in libssh2_session_disconnect_ex() */
libssh2_nonblocking_states disconnect_state;
unsigned char *disconnect_data;

View File

@ -860,7 +860,7 @@ LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, libssh2_pu
/* {{{ libssh2_publickey_shutdown
* Shutdown the publickey subsystem
*/
LIBSSH2_API void libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey)
LIBSSH2_API int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey)
{
LIBSSH2_SESSION *session = pkey->channel->session;
@ -869,18 +869,26 @@ LIBSSH2_API void libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey)
*/
if (pkey->receive_packet) {
LIBSSH2_FREE(session, pkey->receive_packet);
pkey->receive_packet = NULL;
}
if (pkey->add_packet) {
LIBSSH2_FREE(session, pkey->add_packet);
pkey->add_packet = NULL;
}
if (pkey->remove_packet) {
LIBSSH2_FREE(session, pkey->remove_packet);
pkey->remove_packet = NULL;
}
if (pkey->listFetch_data) {
LIBSSH2_FREE(session, pkey->listFetch_data);
pkey->listFetch_data = NULL;
}
if (libssh2_channel_free(pkey->channel) == PACKET_EAGAIN) {
return PACKET_EAGAIN;
}
libssh2_channel_free(pkey->channel);
LIBSSH2_FREE(session, pkey);
return 0;
}
/* }}} */

165
src/scp.c
View File

@ -41,6 +41,10 @@
/* {{{ libssh2_scp_recv
* Open a channel and request a remote file via SCP
*
* NOTE: Will block in a busy loop on error. This has to be done,
* otherwise the blocking error code would erase the true
* cause of the error.
*/
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb)
{
@ -111,10 +115,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
else if (rc) {
LIBSSH2_FREE(session, session->scpRecv_command);
session->scpRecv_command = NULL;
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
LIBSSH2_FREE(session, session->scpRecv_command);
session->scpRecv_command = NULL;
@ -133,10 +134,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
return NULL;
}
else if (rc != 1) {
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
/* Parse SCP response */
@ -159,19 +157,13 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
else if (rc <= 0) {
/* Timeout, give up */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
session->scpRecv_response_len++;
if (session->scpRecv_response[0] != 'T') {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response, missing Time data", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
if ((session->scpRecv_response_len > 1) &&
@ -181,20 +173,14 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
(session->scpRecv_response[session->scpRecv_response_len-1] != '\r') &&
(session->scpRecv_response[session->scpRecv_response_len-1] != '\n')) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
if ((session->scpRecv_response_len < 9) || (session->scpRecv_response[session->scpRecv_response_len-1] != '\n')) {
if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) {
/* You had your chance */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
/* Way too short to be an SCP response, or not done yet, short circuit */
continue;
@ -207,10 +193,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
if (session->scpRecv_response_len < 8) {
/* EOL came too soon */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
s = session->scpRecv_response + 1;
@ -219,10 +202,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
if (!p || ((p - s) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
*(p++) = '\0';
@ -231,19 +211,13 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
session->scpRecv_mtime = strtol((char *)s, NULL, 10);
if (errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mtime", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
s = (unsigned char *)strchr((char *)p, ' ');
if (!s || ((s - p) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime.usec", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
/* Ignore mtime.usec */
@ -252,10 +226,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
if (!p || ((p - s) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
*(p++) = '\0';
@ -264,10 +235,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
session->scpRecv_atime = strtol((char *)s, NULL, 10);
if (errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid atime", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
/* SCP ACK */
@ -283,10 +251,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
return NULL;
}
else if (rc != 1) {
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
_libssh2_debug(session, LIBSSH2_DBG_SCP, "mtime = %ld, atime = %ld", session->scpRecv_mtime, session->scpRecv_atime);
@ -319,19 +284,13 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
else if (rc <= 0) {
/* Timeout, give up */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
session->scpRecv_response_len++;
if (session->scpRecv_response[0] != 'C') {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
if ((session->scpRecv_response_len > 1) &&
@ -340,20 +299,14 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
((session->scpRecv_response[session->scpRecv_response_len-1] < 32) ||
(session->scpRecv_response[session->scpRecv_response_len-1] > 126))) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
if ((session->scpRecv_response_len < 7) || (session->scpRecv_response[session->scpRecv_response_len-1] != '\n')) {
if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) {
/* You had your chance */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
/* Way too short to be an SCP response, or not done yet, short circuit */
continue;
@ -369,10 +322,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
if (session->scpRecv_response_len < 6) {
/* EOL came too soon */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
s = (char *)session->scpRecv_response + 1;
@ -381,10 +331,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
if (!p || ((p - s) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mode", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
*(p++) = '\0';
@ -393,10 +340,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
session->scpRecv_mode = strtol(s, &e, 8);
if ((e && *e) || errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mode", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
s = strchr(p, ' ');
@ -404,10 +348,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed",
0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
*(s++) = '\0';
@ -416,10 +357,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
session->scpRecv_size = strtol(p, &e, 10);
if ((e && *e) || errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid size", 0);
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
/* SCP ACK */
@ -435,10 +373,7 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
return NULL;
}
else if (rc != 1) {
libssh2_channel_free(session->scpRecv_channel);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
goto scp_recv_error;
}
_libssh2_debug(session, LIBSSH2_DBG_SCP, "mode = 0%lo size = %ld", session->scpRecv_mode, session->scpRecv_size);
@ -461,11 +396,21 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const ch
session->scpRecv_state = libssh2_NB_state_idle;
return session->scpRecv_channel;
scp_recv_error:
while (libssh2_channel_free(session->scpRecv_channel) == PACKET_EAGAIN);
session->scpRecv_channel = NULL;
session->scpRecv_state = libssh2_NB_state_idle;
return NULL;
}
/* }}} */
/* {{{ libssh2_scp_send_ex
* Send a file using SCP
*
* NOTE: Will block in a busy loop on error. This has to be done,
* otherwise the blocking error code would erase the true
* cause of the error.
*/
LIBSSH2_API LIBSSH2_CHANNEL *
libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime)
@ -534,10 +479,7 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t
/* previous call set libssh2_session_last_error(), pass it through */
LIBSSH2_FREE(session, session->scpSend_command);
session->scpSend_command = NULL;
libssh2_channel_free(session->scpSend_channel);
session->scpSend_channel = NULL;
session->scpSend_state = libssh2_NB_state_idle;
return NULL;
goto scp_send_error;
}
LIBSSH2_FREE(session, session->scpSend_command);
session->scpSend_command = NULL;
@ -554,10 +496,7 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t
}
else if ((rc <= 0) || (session->scpSend_response[0] != 0)) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
libssh2_channel_free(session->scpSend_channel);
session->scpSend_channel = NULL;
session->scpSend_state = libssh2_NB_state_idle;
return NULL;
goto scp_send_error;
}
if (mtime || atime) {
@ -581,10 +520,7 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t
}
else if (rc != session->scpSend_response_len) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0);
libssh2_channel_free(session->scpSend_channel);
session->scpSend_channel = NULL;
session->scpSend_state = libssh2_NB_state_idle;
return NULL;
goto scp_send_error;
}
session->scpSend_state = libssh2_NB_state_sent3;
@ -599,10 +535,7 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t
}
else if ((rc <= 0) || (session->scpSend_response[0] != 0)) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
libssh2_channel_free(session->scpSend_channel);
session->scpSend_channel = NULL;
session->scpSend_state = libssh2_NB_state_idle;
return NULL;
goto scp_send_error;
}
session->scpSend_state = libssh2_NB_state_sent4;
@ -638,10 +571,7 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t
}
else if (rc != session->scpSend_response_len) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0);
libssh2_channel_free(session->scpSend_channel);
session->scpSend_channel = NULL;
session->scpSend_state = libssh2_NB_state_idle;
return NULL;
goto scp_send_error;
}
session->scpSend_state = libssh2_NB_state_sent6;
@ -655,15 +585,18 @@ libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t
}
else if ((rc <= 0) || (session->scpSend_response[0] != 0)) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
libssh2_channel_free(session->scpSend_channel);
session->scpSend_channel = NULL;
session->scpSend_state = libssh2_NB_state_idle;
return NULL;
goto scp_send_error;
}
session->scpSend_state = libssh2_NB_state_idle;
return session->scpSend_channel;
scp_send_error:
while (libssh2_channel_free(session->scpSend_channel) == PACKET_EAGAIN);
session->scpSend_channel = NULL;
session->scpSend_state = libssh2_NB_state_idle;
return NULL;
}
/* }}} */

View File

@ -597,13 +597,24 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
* Frees the memory allocated to the session
* Also closes and frees any channels attached to this session
*/
LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
LIBSSH2_API int libssh2_session_free(LIBSSH2_SESSION *session)
{
int rc;
if (session->free_state == libssh2_NB_state_idle) {
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Freeing session resource", session->remote.banner);
session->state = libssh2_NB_state_created;
}
if (session->free_state == libssh2_NB_state_created) {
while (session->channels.head) {
LIBSSH2_CHANNEL *tmp = session->channels.head;
libssh2_channel_free(session->channels.head);
rc = libssh2_channel_free(session->channels.head);
if (rc == PACKET_EAGAIN) {
return PACKET_EAGAIN;
}
if (tmp == session->channels.head) {
/* channel_free couldn't do it's job, perform a messy cleanup */
tmp = session->channels.head;
@ -618,8 +629,18 @@ LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
}
}
session->state = libssh2_NB_state_sent;
}
if (session->state == libssh2_NB_state_sent) {
while (session->listeners) {
libssh2_channel_forward_cancel(session->listeners);
rc = libssh2_channel_forward_cancel(session->listeners);
if (rc == PACKET_EAGAIN) {
return PACKET_EAGAIN;
}
}
session->state = libssh2_NB_state_sent1;
}
if (session->state & LIBSSH2_STATE_NEWKEYS) {
@ -784,6 +805,8 @@ LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
}
LIBSSH2_FREE(session, session);
return 0;
}
/* }}} */

View File

@ -498,6 +498,10 @@ LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor)
/* {{{ libssh2_sftp_init
* Startup an SFTP session
*
* NOTE: Will block in a busy loop on error. This has to be done,
* otherwise the blocking error code would erase the true
* cause of the error.
*/
LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
{
@ -508,6 +512,8 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
if (session->sftpInit_state == libssh2_NB_state_idle) {
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem");
session->sftpInit_sftp = NULL;
session->sftpInit_state = libssh2_NB_state_created;
}
@ -539,10 +545,7 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
}
else if (rc) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request SFTP subsystem", 0);
libssh2_channel_free(session->sftpInit_channel);
session->sftpInit_channel = NULL;
session->sftpInit_state = libssh2_NB_state_idle;
return NULL;
goto sftp_init_error;
}
session->sftpInit_state = libssh2_NB_state_sent1;
@ -558,10 +561,7 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
session->sftpInit_sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP));
if (!session->sftpInit_sftp) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0);
libssh2_channel_free(session->sftpInit_channel);
session->sftpInit_channel = NULL;
session->sftpInit_state = libssh2_NB_state_idle;
return NULL;
goto sftp_init_error;
}
memset(session->sftpInit_sftp, 0, sizeof(LIBSSH2_SFTP));
session->sftpInit_sftp->channel = session->sftpInit_channel;
@ -585,12 +585,7 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
}
else if (9 != rc) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0);
libssh2_channel_free(session->sftpInit_channel);
session->sftpInit_channel = NULL;
LIBSSH2_FREE(session, session->sftpInit_sftp);
session->sftpInit_sftp = NULL;
session->sftpInit_state = libssh2_NB_state_idle;
return NULL;
goto sftp_init_error;
}
session->sftpInit_state = libssh2_NB_state_sent3;
@ -604,21 +599,11 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
}
else if (rc) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0);
libssh2_channel_free(session->sftpInit_channel);
session->sftpInit_channel = NULL;
LIBSSH2_FREE(session, session->sftpInit_sftp);
session->sftpInit_sftp = NULL;
session->sftpInit_state = libssh2_NB_state_idle;
return NULL;
goto sftp_init_error;
}
if (data_len < 5) {
libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Invalid SSH_FXP_VERSION response", 0);
libssh2_channel_free(session->sftpInit_channel);
session->sftpInit_channel = NULL;
LIBSSH2_FREE(session, session->sftpInit_sftp);
session->sftpInit_sftp = NULL;
session->sftpInit_state = libssh2_NB_state_idle;
return NULL;
goto sftp_init_error;
}
s = data + 1;
@ -648,6 +633,16 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
session->sftpInit_state = libssh2_NB_state_idle;
return session->sftpInit_sftp;
sftp_init_error:
while (libssh2_channel_free(session->sftpInit_channel) == PACKET_EAGAIN);
session->sftpInit_channel = NULL;
if (session->sftpInit_sftp) {
LIBSSH2_FREE(session, session->sftpInit_sftp);
session->sftpInit_sftp = NULL;
}
session->sftpInit_state = libssh2_NB_state_idle;
return NULL;
}
/* }}} */
@ -661,39 +656,51 @@ LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp)
*/
if (sftp->partial_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->partial_packet);
sftp->partial_packet = NULL;
}
if (sftp->open_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->open_packet);
sftp->open_packet = NULL;
}
if (sftp->read_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->read_packet);
sftp->read_packet = NULL;
}
if (sftp->readdir_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->readdir_packet);
sftp->readdir_packet = NULL;
}
if (sftp->write_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->write_packet);
sftp->write_packet = NULL;
}
if (sftp->fstat_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->fstat_packet);
sftp->fstat_packet = NULL;
}
if (sftp->unlink_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->unlink_packet);
sftp->unlink_packet = NULL;
}
if (sftp->rename_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->rename_packet);
sftp->rename_packet = NULL;
}
if (sftp->mkdir_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->mkdir_packet);
sftp->mkdir_packet = NULL;
}
if (sftp->rmdir_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->rmdir_packet);
sftp->rmdir_packet = NULL;
}
if (sftp->stat_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->stat_packet);
sftp->stat_packet = NULL;
}
if (sftp->symlink_packet) {
LIBSSH2_FREE(sftp->channel->session, sftp->symlink_packet);
sftp->symlink_packet = NULL;
}
return libssh2_channel_free(sftp->channel);