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:
parent
f35ab0d070
commit
dde2b09496
10
ChangeLog
10
ChangeLog
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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" */
|
||||
|
@ -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...
|
||||
|
@ -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;
|
||||
|
@ -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
165
src/scp.c
@ -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;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
59
src/sftp.c
59
src/sftp.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user