diff --git a/NEWS b/NEWS index f1d55b9..f08dd7c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,20 @@ + + * Renamed the functions in src/transport.c to be _libssh2_transport_ prefixed + and introduced a transport.h header. + + * Fixed the blocking mode to only change behavior not the actual underlying + socket mode so we now always work with non-blocking sockets. This also + introduces a new rule of thumb in libssh2 code: we don't call the + external function calls internally. We use the internal (non-blocking) + ones! + + * libssh2_channel_receive_window_adjust2 was added and + libssh2_channel_receive_window_adjust is now deprecated + + * Introduced "local" header files with prototypes etc for different parts + instead of cramming everything into libssh2_priv.h. channel.h is the + first. + - (Mar 19 2009) Daniel Stenberg: based on a patch by "E L" we now use errno properly after recv() and send() calls (that internally are now known as _libssh2_recv() and _libssh2_send()) so that the API and more works fine on diff --git a/TODO b/TODO index 74f3783..438b005 100644 --- a/TODO +++ b/TODO @@ -30,7 +30,9 @@ At next SONAME bump * stop using #defined macros as part of the official API. The macros should either be turned into real functions or discarded from the API. -* remove the followign functions from the API/ABI +* remove the following functions from the API/ABI libssh2_base64_decode() libssh2_session_flag() + libssh2_channel_handle_extended_data() + libssh2_channel_receive_window_adjust() diff --git a/docs/Makefile.am b/docs/Makefile.am index f160cd4..5bf0fff 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,4 +1,4 @@ -# $Id: Makefile.am,v 1.36 2009/03/16 15:00:45 bagder Exp $ +# $Id: Makefile.am,v 1.37 2009/03/26 15:41:15 bagder Exp $ EXTRA_DIST = template.3 @@ -19,6 +19,7 @@ dist_man_MANS = \ libssh2_channel_process_startup.3 \ libssh2_channel_read_ex.3 \ libssh2_channel_receive_window_adjust.3 \ + libssh2_channel_receive_window_adjust2.3 \ libssh2_channel_request_pty_ex.3 \ libssh2_channel_send_eof.3 \ libssh2_channel_set_blocking.3 \ diff --git a/docs/libssh2_channel_handle_extended_data.3 b/docs/libssh2_channel_handle_extended_data.3 index 9f556eb..f991167 100644 --- a/docs/libssh2_channel_handle_extended_data.3 +++ b/docs/libssh2_channel_handle_extended_data.3 @@ -1,4 +1,4 @@ -.\" $Id: libssh2_channel_handle_extended_data.3,v 1.1 2007/06/13 20:09:15 jehousley Exp $ +.\" $Id: libssh2_channel_handle_extended_data.3,v 1.2 2009/03/26 15:41:16 bagder Exp $ .\" .TH libssh2_channel_handle_extended_data 3 "1 Jun 2007" "libssh2 0.15" "libssh2 manual" .SH NAME @@ -10,6 +10,9 @@ void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); .SH DESCRIPTION +This function is deprecated. Use the +\fIlibssh2_channel_handle_extended_data2(3)\fP function instead! + \fIchannel\fP - Active channel stream to change extended data handling on. \fIignore_mode\fP - One of the three LIBSSH2_CHANNEL_EXTENDED_DATA_* Constants. @@ -17,21 +20,18 @@ libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); \fBLIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL\fP: Queue extended data for eventual reading .br -\fBLIBSSH2_CHANNEL_EXTENDED_DATA_MERGE\fP: Treat extended data and ordinary -data the same. Merge all substreams such that calls to -.BR libssh2_channel_read(3) -will pull from all substreams on a first-in/first-out basis. +\fBLIBSSH2_CHANNEL_EXTENDED_DATA_MERGE\fP: Treat extended data and ordinary +data the same. Merge all substreams such that calls to +\fIlibssh2_channel_read(3)\fP will pull from all substreams on a +first-in/first-out basis. .br \fBLIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE\fP: Discard all extended data as it arrives. -Change how a channel deals with extended data packets. By default all -extended data is queued until read by -.BR libssh2_channel_read_ex(3) - +Change how a channel deals with extended data packets. By default all extended +data is queued until read by \fIlibssh2_channel_read_ex(3)\fP .SH RETURN VALUE None. - .SH SEE ALSO .BR libssh2_channel_handle_extended_data2(3) .BR libssh2_channel_read_ex(3) diff --git a/docs/libssh2_channel_receive_window_adjust.3 b/docs/libssh2_channel_receive_window_adjust.3 index 361b8cd..708e493 100644 --- a/docs/libssh2_channel_receive_window_adjust.3 +++ b/docs/libssh2_channel_receive_window_adjust.3 @@ -1,4 +1,4 @@ -.\" $Id: libssh2_channel_receive_window_adjust.3,v 1.2 2009/03/16 23:25:14 bagder Exp $ +.\" $Id: libssh2_channel_receive_window_adjust.3,v 1.3 2009/03/26 15:41:16 bagder Exp $ .\" .TH libssh2_channel_receive_window_adjust 3 "15 Mar 2009" "libssh2 0.15" "libssh2 manual" .SH NAME @@ -12,6 +12,9 @@ libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, unsigned char force); .SH DESCRIPTION +This function is deprecated in 1.1. Use +\fIlibssh2_channel_receive_window_adjust2(3)\fP! + Adjust the receive window for a channel by adjustment bytes. If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the adjustment amount will be queued for a later packet. diff --git a/docs/libssh2_channel_receive_window_adjust2.3 b/docs/libssh2_channel_receive_window_adjust2.3 new file mode 100644 index 0000000..884b410 --- /dev/null +++ b/docs/libssh2_channel_receive_window_adjust2.3 @@ -0,0 +1,29 @@ +.\" $Id: libssh2_channel_receive_window_adjust2.3,v 1.1 2009/03/26 15:41:16 bagder Exp $ +.\" +.TH libssh2_channel_receive_window_adjust2 3 "26 Mar 2009" "libssh2 1.1" "libssh2 manual" +.SH NAME +libssh2_channel_receive_window_adjust2 - adjust the channel window +.SH SYNOPSIS +#include + +int +libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL * channel, + unsigned long adjustment, + unsigned char force, + unsigned int *window); + +.SH DESCRIPTION +Adjust the receive window for a channel by adjustment bytes. If the amount to +be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the +adjustment amount will be queued for a later packet. + +This function stores the new size of the receive window (as understood by +remote end) in the variable 'window' points to. +.SH RETURN VALUE +Return 0 on success and a negative value on error. If used in non-blocking +mode it will return LIBSSH2_ERROR_EAGAIN when it would otherwise block. +.SH ERRORS +.SH AVAILABILITY +Added in libssh2 1.1 since the previous API has deficiencies. +.SH SEE ALSO +.BR libssh2_channel_window_read_ex(3) diff --git a/docs/libssh2_channel_set_blocking.3 b/docs/libssh2_channel_set_blocking.3 index db2cc40..5a3bcf0 100644 --- a/docs/libssh2_channel_set_blocking.3 +++ b/docs/libssh2_channel_set_blocking.3 @@ -1,4 +1,4 @@ -.\" $Id: libssh2_channel_set_blocking.3,v 1.5 2007/06/14 17:23:13 jehousley Exp $ +.\" $Id: libssh2_channel_set_blocking.3,v 1.6 2009/03/26 15:41:16 bagder Exp $ .\" .TH libssh2_channel_set_blocking 3 "1 Jun 2007" "libssh2 0.15" "libssh2 manual" .SH NAME @@ -8,7 +8,6 @@ libssh2_channel_set_blocking - set or clear blocking mode on channel void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking); - .SH DESCRIPTION \fIchannel\fP - channel stream to set or clean blocking status on. @@ -18,10 +17,8 @@ make it non-blocking. Currently this is just a short cut call to .BR libssh2_session_set_blocking(3) and therefore will affect the session and all channels. - .SH RETURN VALUE None - .SH SEE ALSO .BR libssh2_session_set_blocking(3) .BR libssh2_channel_read_ex(3) diff --git a/include/libssh2.h b/include/libssh2.h index 5cf7847..a64fa77 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -576,11 +576,18 @@ libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, #define libssh2_channel_window_read(channel) \ libssh2_channel_window_read_ex((channel), NULL, NULL) +/* libssh2_channel_receive_window_adjust is DEPRECATED, do not use! */ LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force); +LIBSSH2_API int +libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL *channel, + unsigned long adjustment, + unsigned char force, + unsigned int *storewindow); + LIBSSH2_API ssize_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen); @@ -603,6 +610,7 @@ LIBSSH2_API int libssh2_session_get_blocking(LIBSSH2_SESSION* session); LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking); +/* libssh2_channel_handle_extended_data is DEPRECATED, do not use! */ LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); LIBSSH2_API int libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel, diff --git a/src/Makefile.am b/src/Makefile.am index eb0ad09..e85b01a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,10 @@ -# $Id: Makefile.am,v 1.16 2009/03/17 10:19:54 jas4711 Exp $ +# $Id: Makefile.am,v 1.17 2009/03/26 15:41:17 bagder Exp $ AUTOMAKE_OPTIONS = foreign nostdinc libssh2_la_SOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c \ misc.c packet.c publickey.c scp.c session.c sftp.c userauth.c \ -libssh2_priv.h openssl.h libgcrypt.h transport.c version.c +libssh2_priv.h openssl.h libgcrypt.h transport.c version.c transport.h \ +channel.h if LIBGCRYPT libssh2_la_SOURCES += libgcrypt.c pem.c diff --git a/src/channel.c b/src/channel.c index 2ba4cd0..e393953 100644 --- a/src/channel.c +++ b/src/channel.c @@ -46,6 +46,8 @@ #include #endif +#include "channel.h" +#include "transport.h" /* * _libssh2_channel_nextid @@ -114,15 +116,15 @@ _libssh2_channel_locate(LIBSSH2_SESSION * session, unsigned long channel_id) } /* - * libssh2_channel_open_ex + * channel_open * * Establish a generic session channel */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_open_ex(LIBSSH2_SESSION * session, const char *channel_type, - unsigned int channel_type_len, - unsigned int window_size, unsigned int packet_size, - const char *message, unsigned int message_len) +static LIBSSH2_CHANNEL * +channel_open(LIBSSH2_SESSION * session, const char *channel_type, + unsigned int channel_type_len, + unsigned int window_size, unsigned int packet_size, + const char *message, unsigned int message_len) { static const unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -210,8 +212,8 @@ libssh2_channel_open_ex(LIBSSH2_SESSION * session, const char *channel_type, } if (session->open_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, session->open_packet, - session->open_packet_len); + rc = _libssh2_transport_write(session, session->open_packet, + session->open_packet_len); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending channel-open request", 0); @@ -328,14 +330,32 @@ libssh2_channel_open_ex(LIBSSH2_SESSION * session, const char *channel_type, return NULL; } +/* + * libssh2_channel_open_ex + * + * Establish a generic session channel + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *type, + unsigned int type_len, + unsigned int window_size, unsigned int packet_size, + const char *msg, unsigned int msg_len) +{ + LIBSSH2_CHANNEL *ptr; + BLOCK_ADJUST_ERRNO(ptr, session, channel_open(session, type, type_len, + window_size, packet_size, + msg, msg_len)); + return ptr; +} + /* * libssh2_channel_direct_tcpip_ex * * Tunnel TCP/IP connect through the SSH session to direct host/port */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION * session, const char *host, - int port, const char *shost, int sport) +static LIBSSH2_CHANNEL * +channel_direct_tcpip(LIBSSH2_SESSION * session, const char *host, + int port, const char *shost, int sport) { LIBSSH2_CHANNEL *channel; unsigned char *s; @@ -406,13 +426,28 @@ libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION * session, const char *host, } /* - * libssh2_channel_forward_listen_ex + * libssh2_channel_direct_tcpip_ex + * + * Tunnel TCP/IP connect through the SSH session to direct host/port + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION * session, const char *host, + int port, const char *shost, int sport) +{ + LIBSSH2_CHANNEL *ptr; + BLOCK_ADJUST_ERRNO(ptr, session, + channel_direct_tcpip(session, host, port, shost, sport)); + return ptr; +} + +/* + * channel_forward_listen * * Bind a port on the remote host and listen for connections */ -LIBSSH2_API LIBSSH2_LISTENER * -libssh2_channel_forward_listen_ex(LIBSSH2_SESSION * session, const char *host, - int port, int *bound_port, int queue_maxsize) +static LIBSSH2_LISTENER * +channel_forward_listen(LIBSSH2_SESSION * session, const char *host, + int port, int *bound_port, int queue_maxsize) { unsigned char *s, *data; static const unsigned char reply_codes[3] = @@ -462,7 +497,7 @@ libssh2_channel_forward_listen_ex(LIBSSH2_SESSION * session, const char *host, } if (session->fwdLstn_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, session->fwdLstn_packet, + rc = _libssh2_transport_write(session, session->fwdLstn_packet, session->fwdLstn_packet_len); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, @@ -570,15 +605,30 @@ libssh2_channel_forward_listen_ex(LIBSSH2_SESSION * session, const char *host, } /* - * libssh2_channel_forward_cancel + * libssh2_channel_forward_listen_ex + * + * Bind a port on the remote host and listen for connections + */ +LIBSSH2_API LIBSSH2_LISTENER * +libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host, + int port, int *bound_port, int queue_maxsize) +{ + LIBSSH2_LISTENER *ptr; + BLOCK_ADJUST_ERRNO(ptr, session, + channel_forward_listen(session, host, port, bound_port, + queue_maxsize)); + return ptr; +} + +/* + * channel_forward_cancel * * Stop listening on a remote port and free the listener * Toss out any pending (un-accept()ed) connections * * Return 0 on success, PACKET_EAGAIN if would block, -1 on error */ -LIBSSH2_API int -libssh2_channel_forward_cancel(LIBSSH2_LISTENER * listener) +static int channel_forward_cancel(LIBSSH2_LISTENER *listener) { LIBSSH2_SESSION *session = listener->session; LIBSSH2_CHANNEL *queued = listener->queue; @@ -622,10 +672,12 @@ libssh2_channel_forward_cancel(LIBSSH2_LISTENER * listener) } if (listener->chanFwdCncl_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, packet, packet_len); + rc = _libssh2_transport_write(session, packet, packet_len); if (rc == PACKET_EAGAIN) { listener->chanFwdCncl_data = packet; - } else if (rc) { + return rc; + } + else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward " "listen request", @@ -667,17 +719,33 @@ libssh2_channel_forward_cancel(LIBSSH2_LISTENER * listener) } /* - * libssh2_channel_forward_accept + * libssh2_channel_forward_cancel + * + * Stop listening on a remote port and free the listener + * Toss out any pending (un-accept()ed) connections + * + * Return 0 on success, PACKET_EAGAIN if would block, -1 on error + */ +LIBSSH2_API int +libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener) +{ + int rc; + BLOCK_ADJUST(rc, listener->session, channel_forward_cancel(listener)); + return rc; +} + +/* + * channel_forward_accept * * Accept a connection */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_forward_accept(LIBSSH2_LISTENER * listener) +static LIBSSH2_CHANNEL * +channel_forward_accept(LIBSSH2_LISTENER *listener) { libssh2pack_t rc; do { - rc = _libssh2_packet_read(listener->session); + rc = _libssh2_transport_read(listener->session); if (rc == PACKET_EAGAIN) { libssh2_error(listener->session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for packet", 0); @@ -714,14 +782,28 @@ libssh2_channel_forward_accept(LIBSSH2_LISTENER * listener) } /* - * libssh2_channel_setenv_ex + * libssh2_channel_forward_accept + * + * Accept a connection + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener) +{ + LIBSSH2_CHANNEL *ptr; + BLOCK_ADJUST_ERRNO(ptr, listener->session, + channel_forward_accept(listener)); + return ptr; + +} + +/* + * channel_setenv * * Set an environment variable prior to requesting a shell/program/subsystem */ -LIBSSH2_API int -libssh2_channel_setenv_ex(LIBSSH2_CHANNEL * channel, const char *varname, - unsigned int varname_len, const char *value, - unsigned int value_len) +static int channel_setenv(LIBSSH2_CHANNEL *channel, + const char *varname, unsigned int varname_len, + const char *value, unsigned int value_len) { LIBSSH2_SESSION *session = channel->session; unsigned char *s, *data; @@ -776,7 +858,7 @@ libssh2_channel_setenv_ex(LIBSSH2_CHANNEL * channel, const char *varname, } if (channel->setenv_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, channel->setenv_packet, + rc = _libssh2_transport_write(session, channel->setenv_packet, channel->setenv_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -827,13 +909,30 @@ libssh2_channel_setenv_ex(LIBSSH2_CHANNEL * channel, const char *varname, } /* - * libssh2_channel_request_pty_ex - * Duh... Request a PTY + * libssh2_channel_setenv_ex + * + * Set an environment variable prior to requesting a shell/program/subsystem */ LIBSSH2_API int -libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL * channel, const char *term, - unsigned int term_len, const char *modes, - unsigned int modes_len, int width, int height, +libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, + const char *varname, unsigned int varname_len, + const char *value, unsigned int value_len) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, + channel_setenv(channel, varname, varname_len, + value, value_len)); + return rc; +} + +/* + * channel_request_pty + * Duh... Request a PTY + */ +static int channel_request_pty(LIBSSH2_CHANNEL *channel, + const char *term, unsigned int term_len, + const char *modes, unsigned int modes_len, + int width, int height, int width_px, int height_px) { LIBSSH2_SESSION *session = channel->session; @@ -902,7 +1001,7 @@ libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL * channel, const char *term, } if (channel->reqPTY_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, channel->reqPTY_packet, + rc = _libssh2_transport_write(session, channel->reqPTY_packet, channel->reqPTY_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -947,9 +1046,27 @@ libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL * channel, const char *term, return -1; } +/* + * libssh2_channel_request_pty_ex + * Duh... Request a PTY + */ LIBSSH2_API int -libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL * channel, int width, - int height, int width_px, int height_px) +libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, const char *term, + unsigned int term_len, const char *modes, + unsigned int modes_len, int width, int height, + int width_px, int height_px) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, + channel_request_pty(channel, term, term_len, modes, + modes_len, width, height, + width_px, height_px)); + return rc; +} + +static int +channel_request_pty_size(LIBSSH2_CHANNEL * channel, int width, + int height, int width_px, int height_px) { LIBSSH2_SESSION *session = channel->session; unsigned char *s; @@ -998,7 +1115,7 @@ libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL * channel, int width, } if (channel->reqPTY_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, channel->reqPTY_packet, + rc = _libssh2_transport_write(session, channel->reqPTY_packet, channel->reqPTY_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -1022,17 +1139,28 @@ libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL * channel, int width, return -1; } +LIBSSH2_API int +libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL *channel, int width, + int height, int width_px, int height_px) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, + channel_request_pty_size(channel, width, height, width_px, + height_px)); + return rc; +} + /* Keep this an even number */ #define LIBSSH2_X11_RANDOM_COOKIE_LEN 32 /* - * libssh2_channel_x11_req_ex + * channel_x11_req * Request X11 forwarding */ -LIBSSH2_API int -libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL * channel, int single_connection, - const char *auth_proto, const char *auth_cookie, - int screen_number) +static int +channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection, + const char *auth_proto, const char *auth_cookie, + int screen_number) { LIBSSH2_SESSION *session = channel->session; unsigned char *s, *data; @@ -1113,7 +1241,7 @@ libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL * channel, int single_connection, } if (channel->reqX11_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, channel->reqX11_packet, + rc = _libssh2_transport_write(session, channel->reqX11_packet, channel->reqX11_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -1158,15 +1286,32 @@ libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL * channel, int single_connection, return -1; } +/* + * libssh2_channel_x11_req_ex + * Request X11 forwarding + */ +LIBSSH2_API int +libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, + const char *auth_proto, const char *auth_cookie, + int screen_number) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, + channel_x11_req(channel, single_connection, auth_proto, + auth_cookie, screen_number)); + return rc; +} + + /* * libssh2_channel_process_startup * * Primitive for libssh2_channel_(shell|exec|subsystem) */ -LIBSSH2_API int -libssh2_channel_process_startup(LIBSSH2_CHANNEL * channel, const char *request, - unsigned int request_len, const char *message, - unsigned int message_len) +static int +channel_process_startup(LIBSSH2_CHANNEL *channel, + const char *request, unsigned int request_len, + const char *message, unsigned int message_len) { LIBSSH2_SESSION *session = channel->session; unsigned char *s, *data; @@ -1221,11 +1366,12 @@ libssh2_channel_process_startup(LIBSSH2_CHANNEL * channel, const char *request, } if (channel->process_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, channel->process_packet, + rc = _libssh2_transport_write(session, channel->process_packet, channel->process_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; - } else if (rc) { + } + else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel request", 0); LIBSSH2_FREE(session, channel->process_packet); @@ -1266,11 +1412,28 @@ libssh2_channel_process_startup(LIBSSH2_CHANNEL * channel, const char *request, return -1; } +/* + * libssh2_channel_process_startup + * + * Primitive for libssh2_channel_(shell|exec|subsystem) + */ +LIBSSH2_API int +libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, + const char *req, unsigned int req_len, + const char *msg, unsigned int msg_len) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, + channel_process_startup(channel, req, req_len, msg, msg_len)); + return rc; +} + + /* * libssh2_channel_set_blocking * - * Set a channel's blocking mode on or off, similar to a socket's - * fcntl(fd, F_SETFL, O_NONBLOCK); type command + * Set a channel's BEHAVIOR blocking on or off. The socket will remain non- + * blocking. */ LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL * channel, int blocking) @@ -1279,13 +1442,13 @@ libssh2_channel_set_blocking(LIBSSH2_CHANNEL * channel, int blocking) } /* - * libssh2_channel_flush_ex + * _libssh2_channel_flush * * Flush data from one (or all) stream - * Returns number of bytes flushed, or -1 on failure + * Returns number of bytes flushed, or negative on failure */ -LIBSSH2_API int -libssh2_channel_flush_ex(LIBSSH2_CHANNEL * channel, int streamid) +int +_libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid) { LIBSSH2_PACKET *packet = channel->session->packets.head; @@ -1346,9 +1509,9 @@ libssh2_channel_flush_ex(LIBSSH2_CHANNEL * channel, int streamid) if (channel->flush_refund_bytes) { int rc; - rc = libssh2_channel_receive_window_adjust(channel, - channel->flush_refund_bytes, - 0); + rc = _libssh2_channel_receive_window_adjust(channel, + channel->flush_refund_bytes, + 0, NULL); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } @@ -1359,6 +1522,21 @@ libssh2_channel_flush_ex(LIBSSH2_CHANNEL * channel, int streamid) return channel->flush_flush_bytes; } +/* + * libssh2_channel_flush_ex + * + * Flush data from one (or all) stream + * Returns number of bytes flushed, or negative on failure + */ +LIBSSH2_API int +libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int stream) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, + _libssh2_channel_flush(channel, stream)); + return rc; +} + /* * libssh2_channel_get_exit_status * @@ -1371,19 +1549,18 @@ libssh2_channel_get_exit_status(LIBSSH2_CHANNEL * channel) } /* - * libssh2_channel_receive_window_adjust + * _libssh2_channel_receive_window_adjust * * Adjust the receive window for a channel by adjustment bytes. If the amount * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the * adjustment amount will be queued for a later packet. * - * Returns the new size of the receive window (as understood by remote end), - * */ -LIBSSH2_API unsigned long -libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, - unsigned long adjustment, - unsigned char force) +int +_libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, + unsigned long adjustment, + unsigned char force, + unsigned int *store) { int rc; @@ -1396,11 +1573,15 @@ libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, "for channel %lu/%lu", adjustment, channel->local.id, channel->remote.id); channel->adjust_queue += adjustment; - return channel->remote.window_size; + if(store) + *store = channel->remote.window_size; + return 0; } if (!adjustment && !channel->adjust_queue) { - return channel->remote.window_size; + if(store) + *store = channel->remote.window_size; + return 0; } adjustment += channel->adjust_queue; @@ -1418,16 +1599,16 @@ libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, channel->adjust_state = libssh2_NB_state_created; } - rc = _libssh2_packet_write(channel->session, channel->adjust_adjust, 9); + rc = _libssh2_transport_write(channel->session, channel->adjust_adjust, 9); if (rc == PACKET_EAGAIN) { - return PACKET_EAGAIN; /* TODO/FIX: this function returns an unsigned - value! */ + return PACKET_EAGAIN; } else if (rc) { libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet, " "deferring", 0); channel->adjust_queue = adjustment; + return rc; } else { channel->remote.window_size += adjustment; @@ -1435,32 +1616,69 @@ libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, channel->adjust_state = libssh2_NB_state_idle; - return channel->remote.window_size; + if(store) + *store = channel->remote.window_size; + return 0; } /* - * libssh2_channel_handle_extended_data + * libssh2_channel_receive_window_adjust + * + * Adjust the receive window for a channel by adjustment bytes. If the amount + * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the + * adjustment amount will be queued for a later packet. + * + * Returns the new size of the receive window (as understood by remote end). + * Note that it might return EAGAIN too which is highly stupid. * - * How should extended data look to the calling app? Keep it in separate - * channels[_read() _read_stdder()]? (NORMAL) Merge the extended data to the - * standard data? [everything via _read()]? (MERGE) Ignore it entirely [toss - * out packets as they come in]? (IGNORE) */ -LIBSSH2_API void -libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL * channel, - int ignore_mode) +LIBSSH2_API unsigned long +libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, + unsigned long adj, + unsigned char force) { - while (libssh2_channel_handle_extended_data2(channel, ignore_mode) == - PACKET_EAGAIN); + unsigned int window; + int rc; + BLOCK_ADJUST(rc, channel->session, + _libssh2_channel_receive_window_adjust(channel, adj, + force, &window)); + + /* stupid - but this is how it was made to work before and this is just + kept for backwards compatibility */ + return rc?(unsigned long)rc:window; } +/* + * libssh2_channel_receive_window_adjust2 + * + * Adjust the receive window for a channel by adjustment bytes. If the amount + * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the + * adjustment amount will be queued for a later packet. + * + * Stores the new size of the receive window in the data 'window' points to. + * + * Returns the "normal" error code: 0 for success, negative for failure. + */ LIBSSH2_API int -libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL * channel, - int ignore_mode) +libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL *channel, + unsigned long adj, + unsigned char force, + unsigned int *window) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, + _libssh2_channel_receive_window_adjust(channel, adj, force, + window)); + return rc; +} + +static int +channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) { if (channel->extData2_state == libssh2_NB_state_idle) { _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, - "Setting channel %lu/%lu handle_extended_data mode to %d", + "Setting channel %lu/%lu handle_extended_data" + " mode to %d", channel->local.id, channel->remote.id, ignore_mode); channel->remote.extended_data_ignore_mode = ignore_mode; @@ -1469,9 +1687,9 @@ libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL * channel, if (channel->extData2_state == libssh2_NB_state_idle) { if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) { - if (libssh2_channel_flush_ex - (channel, - LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) == PACKET_EAGAIN) { + if (_libssh2_channel_flush(channel, + LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) == + PACKET_EAGAIN) { return PACKET_EAGAIN; } } @@ -1482,18 +1700,48 @@ libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL * channel, } /* - * libssh2_channel_read_ex + * libssh2_channel_handle_extended_data2() * - * 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 - * currently read channel is complete. If we read stuff from the wire but it - * was no payload data to fill in the buffer with, we MUST make sure to return - * PACKET_EAGAIN. */ -LIBSSH2_API ssize_t -libssh2_channel_read_ex(LIBSSH2_CHANNEL * channel, int stream_id, char *buf, - size_t buflen) +LIBSSH2_API int +libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel, + int mode) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, channel_extended_data(channel, mode)); + return rc; +} + +/* + * libssh2_channel_handle_extended_data + * + * DEPRECATED DO NOTE USE! + * + * How should extended data look to the calling app? Keep it in separate + * channels[_read() _read_stdder()]? (NORMAL) Merge the extended data to the + * standard data? [everything via _read()]? (MERGE) Ignore it entirely [toss + * out packets as they come in]? (IGNORE) + */ +LIBSSH2_API void +libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, + int ignore_mode) +{ + (void)libssh2_channel_handle_extended_data2(channel, ignore_mode); +} + + + +/* + * channel_read + * + * Read data from a channel + * + * It is important to not return 0 until the currently read channel is + * complete. If we read stuff from the wire but it was no payload data to fill + * in the buffer with, we MUST make sure to return PACKET_EAGAIN. + */ +static ssize_t channel_read(LIBSSH2_CHANNEL *channel, int stream_id, + char *buf, size_t buflen) { LIBSSH2_SESSION *session = channel->session; libssh2pack_t rc; @@ -1512,7 +1760,7 @@ libssh2_channel_read_ex(LIBSSH2_CHANNEL * channel, int stream_id, char *buf, /* process all pending incoming packets */ while (rc > 0) - rc = _libssh2_packet_read(session); + rc = _libssh2_transport_read(session); if ((rc < 0) && (rc != PACKET_EAGAIN)) return -1; @@ -1523,7 +1771,7 @@ libssh2_channel_read_ex(LIBSSH2_CHANNEL * channel, int stream_id, char *buf, /* We're not in the idle state, but in order to "even out" the network readings we do a single shot read here as well. Tests prove that this way produces faster transfers. */ - rc = _libssh2_packet_read(session); + rc = _libssh2_transport_read(session); /* ignore PACKET_EAGAIN but return failure for the rest */ if ((rc < 0) && (rc != PACKET_EAGAIN)) @@ -1620,8 +1868,7 @@ libssh2_channel_read_ex(LIBSSH2_CHANNEL * channel, int stream_id, char *buf, if (bytes_read == 0) { channel->read_state = libssh2_NB_state_idle; - if (channel->remote.close || - channel->session->socket_block) { + if (channel->remote.close) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "Remote end has closed this channel", 0); return 0; @@ -1650,8 +1897,8 @@ libssh2_channel_read_ex(LIBSSH2_CHANNEL * channel, int stream_id, char *buf, channel->read_state = libssh2_NB_state_jump1; /* the actual window adjusting may not finish so we need to deal with this special state here */ - rc = libssh2_channel_receive_window_adjust(channel, - (LIBSSH2_CHANNEL_WINDOW_DEFAULT*600), 0); + rc = _libssh2_channel_receive_window_adjust(channel, + (LIBSSH2_CHANNEL_WINDOW_DEFAULT*600), 0, NULL); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } @@ -1666,6 +1913,26 @@ libssh2_channel_read_ex(LIBSSH2_CHANNEL * channel, int stream_id, char *buf, return bytes_read; } +/* + * 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 + * currently read channel is complete. If we read stuff from the wire but it + * was no payload data to fill in the buffer with, we MUST make sure to return + * PACKET_EAGAIN. + */ +LIBSSH2_API ssize_t +libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, + size_t buflen) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, channel_read(channel, stream_id, + buf, buflen)); + return rc; +} + /* * _libssh2_channel_packet_data_len * @@ -1717,13 +1984,13 @@ _libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id) } /* - * libssh2_channel_write_ex + * channel_write * * Send data to a channel */ -LIBSSH2_API ssize_t -libssh2_channel_write_ex(LIBSSH2_CHANNEL * channel, int stream_id, - const char *buf, size_t buflen) +static ssize_t +channel_write(LIBSSH2_CHANNEL *channel, int stream_id, + const char *buf, size_t buflen) { LIBSSH2_SESSION *session = channel->session; libssh2pack_t rc; @@ -1786,7 +2053,7 @@ libssh2_channel_write_ex(LIBSSH2_CHANNEL * channel, int stream_id, while (channel->local.window_size <= 0) { /* Don't worry -- This is never hit unless it's a blocking channel anyway */ - rc = _libssh2_packet_read(session); + rc = _libssh2_transport_read(session); if (rc < 0) { /* Error or EAGAIN occurred, disconnect? */ @@ -1797,11 +2064,11 @@ libssh2_channel_write_ex(LIBSSH2_CHANNEL * channel, int stream_id, return rc; } - if ((rc == 0) && (session->socket_block == 0)) { + if (rc == 0) { /* - * if rc == 0 and in non-blocking, then fake EAGAIN - * to prevent busyloops until data arriaves on the network - * which seemed like a very bad idea + * if rc == 0, then fake EAGAIN to prevent busyloops until + * data arriaves on the network which seemed like a very + * bad idea */ return PACKET_EAGAIN; } @@ -1839,12 +2106,12 @@ libssh2_channel_write_ex(LIBSSH2_CHANNEL * channel, int stream_id, } if (channel->write_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, channel->write_packet, + rc = _libssh2_transport_write(session, channel->write_packet, channel->write_s - channel->write_packet); if (rc == PACKET_EAGAIN) { _libssh2_debug(session, LIBSSH2_DBG_CONN, - "libssh2_packet_write returned EAGAIN"); + "libssh2_transport_write returned EAGAIN"); return PACKET_EAGAIN; } else if (rc) { @@ -1883,12 +2150,26 @@ libssh2_channel_write_ex(LIBSSH2_CHANNEL * channel, int stream_id, } /* - * libssh2_channel_send_eof + * libssh2_channel_write_ex + * + * Send data to a channel + */ +LIBSSH2_API ssize_t +libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, + const char *buf, size_t buflen) +{ + ssize_t rc; + BLOCK_ADJUST(rc, channel->session, + channel_write(channel, stream_id, buf, buflen)); + return rc; +} + +/* + * channel_send_eof * * Send EOF on channel */ -LIBSSH2_API int -libssh2_channel_send_eof(LIBSSH2_CHANNEL * channel) +static int channel_send_eof(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION *session = channel->session; unsigned char packet[5]; /* packet_type(1) + channelno(4) */ @@ -1898,10 +2179,11 @@ libssh2_channel_send_eof(LIBSSH2_CHANNEL * channel) channel->local.id, channel->remote.id); packet[0] = SSH_MSG_CHANNEL_EOF; _libssh2_htonu32(packet + 1, channel->remote.id); - rc = _libssh2_packet_write(session, packet, 5); + rc = _libssh2_transport_write(session, packet, 5); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; - } else if (rc) { + } + else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send EOF on channel", 0); return -1; @@ -1911,6 +2193,19 @@ libssh2_channel_send_eof(LIBSSH2_CHANNEL * channel) return 0; } +/* + * libssh2_channel_send_eof + * + * Send EOF on channel + */ +LIBSSH2_API int +libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, channel_send_eof(channel)); + return rc; +} + /* * libssh2_channel_eof * @@ -1936,12 +2231,11 @@ libssh2_channel_eof(LIBSSH2_CHANNEL * channel) } /* - * libssh2_channel_wait_eof + * channel_wait_eof * * Awaiting channel EOF */ -LIBSSH2_API int -libssh2_channel_wait_eof(LIBSSH2_CHANNEL * channel) +static int channel_wait_eof(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION *session = channel->session; int rc; @@ -1962,10 +2256,11 @@ libssh2_channel_wait_eof(LIBSSH2_CHANNEL * channel) if (channel->remote.eof) { break; } - rc = _libssh2_packet_read(session); + rc = _libssh2_transport_read(session); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; - } else if (rc < 0) { + } + else if (rc < 0) { channel->wait_eof_state = libssh2_NB_state_idle; return -1; } @@ -1977,12 +2272,20 @@ libssh2_channel_wait_eof(LIBSSH2_CHANNEL * channel) } /* - * libssh2_channel_close + * libssh2_channel_wait_eof * - * Close a channel + * Awaiting channel EOF */ LIBSSH2_API int -libssh2_channel_close(LIBSSH2_CHANNEL * channel) +libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, channel_wait_eof(channel)); + return rc; +} + +static int +channel_close(LIBSSH2_CHANNEL * channel) { LIBSSH2_SESSION *session = channel->session; int rc = 0; @@ -2011,7 +2314,7 @@ libssh2_channel_close(LIBSSH2_CHANNEL * channel) } if (channel->close_state == libssh2_NB_state_created) { - retcode = _libssh2_packet_write(session, channel->close_packet, 5); + retcode = _libssh2_transport_write(session, channel->close_packet, 5); if (retcode == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (retcode) { @@ -2030,7 +2333,7 @@ libssh2_channel_close(LIBSSH2_CHANNEL * channel) libssh2pack_t ret; do { - ret = _libssh2_packet_read(session); + ret = _libssh2_transport_read(session); if (ret == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (ret < 0) { @@ -2046,12 +2349,26 @@ libssh2_channel_close(LIBSSH2_CHANNEL * channel) } /* - * libssh2_channel_wait_closed + * libssh2_channel_close + * + * Close a channel + */ +LIBSSH2_API int +libssh2_channel_close(LIBSSH2_CHANNEL *channel) +{ + int rc; + + BLOCK_ADJUST(rc, channel->session, channel_close(channel) ); + + return rc; +} + +/* + * channel_wait_closed * * Awaiting channel close after EOF */ -LIBSSH2_API int -libssh2_channel_wait_closed(LIBSSH2_CHANNEL * channel) +static int channel_wait_closed(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION *session = channel->session; int rc; @@ -2076,17 +2393,13 @@ libssh2_channel_wait_closed(LIBSSH2_CHANNEL * channel) * While channel is not closed, read more packets from the network. * Either the channel will be closed or network timeout will occur. */ - do { - if (!channel->remote.close) { - break; - } - rc = _libssh2_packet_read(session); - if (rc == PACKET_EAGAIN) { - return PACKET_EAGAIN; - } else if (rc <= 0) { - break; - } - } while (1); + if (!channel->remote.close) { + do { + rc = _libssh2_transport_read(session); + } while (rc > 0); + if(rc < 0) + return rc; + } channel->wait_closed_state = libssh2_NB_state_idle; @@ -2094,15 +2407,27 @@ libssh2_channel_wait_closed(LIBSSH2_CHANNEL * channel) } /* - * libssh2_channel_free + * libssh2_channel_wait_closed + * + * Awaiting channel close after EOF + */ +LIBSSH2_API int +libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, channel_wait_closed(channel)); + return rc; +} + +/* + * channel_free * * Make sure a channel is closed, then remove the channel from the session * and free its resource(s) * - * Returns 0 on success, -1 on failure + * Returns 0 on success, negative on failure */ -LIBSSH2_API int -libssh2_channel_free(LIBSSH2_CHANNEL * channel) +static int channel_free(LIBSSH2_CHANNEL *channel) { LIBSSH2_SESSION *session = channel->session; unsigned char channel_id[4]; @@ -2121,7 +2446,10 @@ libssh2_channel_free(LIBSSH2_CHANNEL * channel) /* Allow channel freeing even when the socket has lost its connection */ if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED)) { - while ((rc = libssh2_channel_close(channel)) == PACKET_EAGAIN); + rc = channel_close(channel); + if(rc == PACKET_EAGAIN) + return rc; + if (rc) { channel->free_state = libssh2_NB_state_idle; return -1; @@ -2187,6 +2515,21 @@ libssh2_channel_free(LIBSSH2_CHANNEL * channel) return 0; } +/* + * libssh2_channel_free + * + * Make sure a channel is closed, then remove the channel from the session + * and free its resource(s) + * + * Returns 0 on success, -1 on failure + */ +LIBSSH2_API int +libssh2_channel_free(LIBSSH2_CHANNEL *channel) +{ + int rc; + BLOCK_ADJUST(rc, channel->session, channel_free(channel)); + return rc; +} /* * libssh2_channel_window_read_ex * diff --git a/src/channel.h b/src/channel.h new file mode 100644 index 0000000..63c89d1 --- /dev/null +++ b/src/channel.h @@ -0,0 +1,64 @@ +#ifndef __LIBSSH2_CHANNEL_H +#define __LIBSSH2_CHANNEL_H +/* Copyright (c) 2008-2009 by Daniel Stenberg + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * _libssh2_channel_receive_window_adjust + * + * Adjust the receive window for a channel by adjustment bytes. If the amount + * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the + * adjustment amount will be queued for a later packet. + * + * Always non-blocking. + */ +int _libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, + unsigned long adjustment, + unsigned char force, + unsigned int *store); + +/* + * _libssh2_channel_flush + * + * Flush data from one (or all) stream + * Returns number of bytes flushed, or negative on failure + */ +int _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid); + +#endif /* __LIBSSH2_CHANNEL_H */ + diff --git a/src/kex.c b/src/kex.c index 6249905..7e5e0f2 100644 --- a/src/kex.c +++ b/src/kex.c @@ -37,6 +37,8 @@ #include "libssh2_priv.h" +#include "transport.h" + /* TODO: Switch this to an inline and handle alloc() failures */ /* Helper macro called from kex_method_diffie_hellman_group1_sha1_key_exchange */ #define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(value, reqlen, version) \ @@ -139,8 +141,8 @@ kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, } if (exchange_state->state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, exchange_state->e_packet, - exchange_state->e_packet_len); + rc = _libssh2_transport_write(session, exchange_state->e_packet, + exchange_state->e_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { @@ -415,7 +417,7 @@ kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, } if (exchange_state->state == libssh2_NB_state_sent2) { - rc = _libssh2_packet_write(session, &exchange_state->c, 1); + rc = _libssh2_transport_write(session, &exchange_state->c, 1); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { @@ -837,7 +839,7 @@ kex_method_diffie_hellman_group_exchange_sha1_key_exchange } if (key_state->state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, key_state->request, + rc = _libssh2_transport_write(session, key_state->request, key_state->request_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -1136,7 +1138,7 @@ static int kexinit(LIBSSH2_SESSION * session) data_len = session->kexinit_data_len; } - rc = _libssh2_packet_write(session, data, data_len); + rc = _libssh2_transport_write(session, data, data_len); if (rc == PACKET_EAGAIN) { session->kexinit_data = data; session->kexinit_data_len = data_len; diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index c610d18..c49522e 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -684,6 +684,9 @@ struct _LIBSSH2_SESSION unsigned char *session_id; unsigned long session_id_len; + /* this is set to TRUE if a blocking API behavior is requested */ + int api_block_mode; + /* Server's public key */ const LIBSSH2_HOSTKEY_METHOD *hostkey; void *server_hostkey_abstract; @@ -716,9 +719,10 @@ struct _LIBSSH2_SESSION /* Actual I/O socket */ int socket_fd; - int socket_block; int socket_state; int socket_block_directions; + int socket_prev_blockstate; /* stores the state of the socket blockiness + when libssh2_session_startup() is called */ /* Error tracking */ char *err_msg; @@ -1145,7 +1149,8 @@ ssize_t _libssh2_send(int socket, const void *buffer, size_t length, int flags); #define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when waiting for more data to arrive */ -int _libssh2_waitsocket(LIBSSH2_SESSION * session, long seconds); + +int _libssh2_wait_socket(LIBSSH2_SESSION *session); /* CAUTION: some of these error codes are returned in the public API and is @@ -1223,4 +1228,40 @@ int _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen); int _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen, unsigned char **i, unsigned int *ilen); + +/* Conveniance-macros to allow code like this; + + int rc = BLOCK_ADJUST(rc, session, session_startup(session, sock) ); + + int rc = BLOCK_ADJUST_ERRNO(ptr, session, session_startup(session, sock) ); + + The point of course being to make sure that while in non-blocking mode + these always return no matter what the return code is, but in blocking mode + it blocks if EAGAIN is the reason for the return from the underlying + function. + +*/ +#define BLOCK_ADJUST(rc,sess,x) \ + do { \ + rc = x; \ + if(!sess->api_block_mode || (rc != LIBSSH2_ERROR_EAGAIN)) \ + break; \ + rc = _libssh2_wait_socket(sess); \ + if(rc) \ + break; \ + } while(1) + +#define BLOCK_ADJUST_ERRNO(ptr,sess,x) \ + do { \ + int rc; \ + ptr = x; \ + if(!sess->api_block_mode || \ + (libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN)) \ + break; \ + rc = _libssh2_wait_socket(sess); \ + if(rc) \ + break; \ + } while(1) + + #endif /* LIBSSH2_H */ diff --git a/src/packet.c b/src/packet.c index 3fc3e34..209c0d4 100644 --- a/src/packet.c +++ b/src/packet.c @@ -59,6 +59,8 @@ #include +#include "transport.h" + /* * libssh2_packet_queue_listener * @@ -202,8 +204,8 @@ packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data, } if (listen_state->state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, listen_state->packet, - 17); + rc = _libssh2_transport_write(session, listen_state->packet, + 17); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { @@ -256,7 +258,8 @@ packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data, p += sizeof(FwdNotReq) - 1; _libssh2_htonu32(p, 0); - rc = _libssh2_packet_write(session, listen_state->packet, packet_len); + rc = _libssh2_transport_write(session, listen_state->packet, + packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { @@ -374,7 +377,7 @@ packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data, } if (x11open_state->state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, x11open_state->packet, 17); + rc = _libssh2_transport_write(session, x11open_state->packet, 17); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { @@ -422,7 +425,7 @@ packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data, p += sizeof(X11FwdUnAvil) - 1; _libssh2_htonu32(p, 0); - rc = _libssh2_packet_write(session, x11open_state->packet, packet_len); + rc = _libssh2_transport_write(session, x11open_state->packet, packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { @@ -922,11 +925,10 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, } /* - * 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_packet_read to read the rest of the key exchange - * conversation. + * 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; @@ -1029,39 +1031,10 @@ _libssh2_packet_askv(LIBSSH2_SESSION * session, return -1; } -/* - * _libssh2_waitsocket - * - * Returns - * negative on error - * >0 on incoming data - * 0 on timeout - * - * FIXME: convert to use poll on systems that have it. - */ -int -_libssh2_waitsocket(LIBSSH2_SESSION * session, long seconds) -{ - struct timeval timeout; - int rc; - fd_set fd; - - timeout.tv_sec = seconds; - timeout.tv_usec = 0; - - FD_ZERO(&fd); - - FD_SET(session->socket_fd, &fd); - - rc = select(session->socket_fd + 1, &fd, NULL, NULL, &timeout); - - return rc; -} - /* * _libssh2_packet_require * - * Loops _libssh2_packet_read() until the packet requested is available + * Loops _libssh2_transport_read() until the packet requested is available * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout * * Returns negative on error @@ -1091,11 +1064,11 @@ _libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type, } while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - libssh2pack_t ret = _libssh2_packet_read(session); + libssh2pack_t ret = _libssh2_transport_read(session); if (ret == PACKET_EAGAIN) { return PACKET_EAGAIN; - } else if ((ret == 0) && (!session->socket_block)) { - /* If we are in non-blocking and there is no data, return that */ + } else if (ret == 0) { + /* There is no data, return that. TODO: is this really correct? */ return PACKET_EAGAIN; } else if (ret < 0) { state->start = 0; @@ -1111,7 +1084,7 @@ _libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type, /* nothing available, wait until data arrives or we time out */ long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); - if ((left <= 0) || (_libssh2_waitsocket(session, left) <= 0)) { + if (left <= 0) { state->start = 0; return PACKET_TIMEOUT; } @@ -1125,7 +1098,7 @@ _libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type, /* * _libssh2_packet_burn * - * Loops _libssh2_packet_read() until any packet is available and promptly + * Loops _libssh2_transport_read() until any packet is available and promptly * discards it. * Used during KEX exchange to discard badly guessed KEX_INIT packets */ @@ -1158,7 +1131,7 @@ _libssh2_packet_burn(LIBSSH2_SESSION * session, } while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - if ((ret = _libssh2_packet_read(session)) == PACKET_EAGAIN) { + if ((ret = _libssh2_transport_read(session)) == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (ret < 0) { *state = libssh2_NB_state_idle; @@ -1185,7 +1158,7 @@ _libssh2_packet_burn(LIBSSH2_SESSION * session, /* * _libssh2_packet_requirev * - * Loops _libssh2_packet_read() until one of a list of packet types requested is + * Loops _libssh2_transport_read() until one of a list of packet types requested is * available * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout * packet_types is a null terminated list of packet_type numbers @@ -1212,7 +1185,7 @@ _libssh2_packet_requirev(LIBSSH2_SESSION * session, } while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { - int ret = _libssh2_packet_read(session); + int ret = _libssh2_transport_read(session); if ((ret < 0) && (ret != PACKET_EAGAIN)) { state->start = 0; return ret; @@ -1220,10 +1193,11 @@ _libssh2_packet_requirev(LIBSSH2_SESSION * session, if (ret <= 0) { long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); - if ((left <= 0) || (_libssh2_waitsocket(session, left) <= 0)) { + if (left <= 0) { state->start = 0; return PACKET_TIMEOUT; - } else if (ret == PACKET_EAGAIN) { + } + else if (ret == PACKET_EAGAIN) { return PACKET_EAGAIN; } } diff --git a/src/session.c b/src/session.c index fef629d..8bd72bb 100644 --- a/src/session.c +++ b/src/session.c @@ -1,4 +1,5 @@ /* Copyright (c) 2004-2007, Sara Golemon + * Copyright (c) 2009 by Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, @@ -396,12 +397,13 @@ libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner) } /* - * proto libssh2_session_init + * libssh2_session_init_ex * - * Allocate and initialize a libssh2 session structure - * Allows for malloc callbacks in case the calling program has its own memory manager - * It's allowable (but unadvisable) to define some but not all of the malloc callbacks - * An additional pointer value may be optionally passed to be sent to the callbacks (so they know who's asking) + * Allocate and initialize a libssh2 session structure. Allows for malloc + * callbacks in case the calling program has its own memory manager It's + * allowable (but unadvisable) to define some but not all of the malloc + * callbacks An additional pointer value may be optionally passed to be sent + * to the callbacks (so they know who's asking) */ LIBSSH2_API LIBSSH2_SESSION * libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), @@ -430,6 +432,7 @@ libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), session->free = local_free; session->realloc = local_realloc; session->abstract = abstract; + session->api_block_mode = 1; /* blocking API by default */ _libssh2_debug(session, LIBSSH2_DBG_TRANS, "New session resource allocated"); libssh2_crypto_init(); @@ -485,16 +488,46 @@ libssh2_session_callback_set(LIBSSH2_SESSION * session, } /* - * proto libssh2_session_startup + * _libssh2_wait_socket() * - * session: LIBSSH2_SESSION struct allocated and owned by the calling program - * Returns: 0 on success, or non-zero on failure - * Any memory allocated by libssh2 will use alloc/realloc/free - * callbacks in session - * socket *must* be populated with an opened and connected socket. + * Utility function that waits for action on the socket. Returns 0 when ready + * to run again or error on timeout. */ -LIBSSH2_API int -libssh2_session_startup(LIBSSH2_SESSION * session, int sock) +int _libssh2_wait_socket(LIBSSH2_SESSION *session) +{ + fd_set fd; + fd_set *writefd = NULL; + fd_set *readfd = NULL; + int dir; + int rc; + + FD_ZERO(&fd); + FD_SET(session->socket_fd, &fd); + + /* now make sure we wait in the correct direction */ + dir = libssh2_session_block_directions(session); + + if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) + readfd = &fd; + + if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) + writefd = &fd; + + /* Note that this COULD be made to use a timeout that perhaps could be + customizable by the app or something... */ + rc = select(session->socket_fd + 1, readfd, writefd, NULL, NULL); + + if(rc <= 0) { + /* timeout (or error), bail out with a timeout error */ + session->err_code = LIBSSH2_ERROR_TIMEOUT; + return LIBSSH2_ERROR_TIMEOUT; + } + + return 0; /* ready to try again */ +} + +static int +session_startup(LIBSSH2_SESSION *session, int sock) { int rc; @@ -510,15 +543,12 @@ libssh2_session_startup(LIBSSH2_SESSION * session, int sock) } session->socket_fd = sock; - session->socket_block = + session->socket_prev_blockstate = !get_socket_nonblocking(session->socket_fd); - if (session->socket_block) { - /* - * Since we can't be sure that we are in blocking or there - * was an error detecting the state, so set to blocking to - * be sure - */ - session_nonblock(session->socket_fd, 0); + + if (session->socket_prev_blockstate) { + /* If in blocking state chang to non-blocking */ + session_nonblock(session->socket_fd, 1); } session->startup_state = libssh2_NB_state_created; @@ -588,8 +618,8 @@ libssh2_session_startup(LIBSSH2_SESSION * session, int sock) } if (session->startup_state == libssh2_NB_state_sent3) { - rc = _libssh2_packet_write(session, session->startup_service, - sizeof("ssh-userauth") + 5 - 1); + rc = _libssh2_transport_write(session, session->startup_service, + sizeof("ssh-userauth") + 5 - 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block asking for ssh-userauth service", 0); @@ -638,14 +668,33 @@ libssh2_session_startup(LIBSSH2_SESSION * session, int sock) return LIBSSH2_ERROR_INVAL; } +/* + * proto libssh2_session_startup + * + * session: LIBSSH2_SESSION struct allocated and owned by the calling program + * Returns: 0 on success, or non-zero on failure + * Any memory allocated by libssh2 will use alloc/realloc/free + * callbacks in session. + * The 'sock' socket *must* be populated with an opened and connected socket. + */ +LIBSSH2_API int +libssh2_session_startup(LIBSSH2_SESSION *session, int sock) +{ + int rc; + + BLOCK_ADJUST(rc, session, session_startup(session, sock) ); + + return rc; +} + /* * libssh2_session_free * * Frees the memory allocated to the session * Also closes and frees any channels attached to this session */ -LIBSSH2_API int -libssh2_session_free(LIBSSH2_SESSION * session) +static int +session_free(LIBSSH2_SESSION *session) { int rc; @@ -869,16 +918,37 @@ libssh2_session_free(LIBSSH2_SESSION * session) LIBSSH2_FREE(session, tmp); } + if(session->socket_prev_blockstate) + /* if the socket was previously blocking, put it back so */ + session_nonblock(session->socket_fd, 0); + LIBSSH2_FREE(session, session); return 0; } -/* libssh2_session_disconnect_ex +/* + * libssh2_session_free + * + * Frees the memory allocated to the session + * Also closes and frees any channels attached to this session */ LIBSSH2_API int -libssh2_session_disconnect_ex(LIBSSH2_SESSION * session, int reason, - const char *description, const char *lang) +libssh2_session_free(LIBSSH2_SESSION * session) +{ + int rc; + + BLOCK_ADJUST(rc, session, session_free(session) ); + + return rc; +} + +/* + * libssh2_session_disconnect_ex + */ +static int +session_disconnect(LIBSSH2_SESSION *session, int reason, + const char *description, const char *lang) { unsigned char *s; unsigned long descr_len = 0, lang_len = 0; @@ -928,8 +998,8 @@ libssh2_session_disconnect_ex(LIBSSH2_SESSION * session, int reason, session->disconnect_state = libssh2_NB_state_created; } - rc = _libssh2_packet_write(session, session->disconnect_data, - session->disconnect_data_len); + rc = _libssh2_transport_write(session, session->disconnect_data, + session->disconnect_data_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } @@ -941,10 +1011,27 @@ libssh2_session_disconnect_ex(LIBSSH2_SESSION * session, int reason, return 0; } +/* + * libssh2_session_disconnect_ex + */ +LIBSSH2_API int +libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, + const char *desc, const char *lang) +{ + int rc; + + BLOCK_ADJUST(rc, session, + session_disconnect(session, reason, desc, lang)); + + return rc; +} + /* libssh2_session_methods + * * Return the currently active methods for method_type - * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string regardless of actual negotiation - * Strings should NOT be freed + * + * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string + * regardless of actual negotiation Strings should NOT be freed */ LIBSSH2_API const char * libssh2_session_methods(LIBSSH2_SESSION * session, int method_type) @@ -1019,9 +1106,10 @@ libssh2_session_abstract(LIBSSH2_SESSION * session) } /* libssh2_session_last_error - * Returns error code and populates an error string into errmsg - * If want_buf is non-zero then the string placed into errmsg must be freed by the calling program - * Otherwise it is assumed to be owned by libssh2 + * + * Returns error code and populates an error string into errmsg If want_buf is + * non-zero then the string placed into errmsg must be freed by the calling + * program. Otherwise it is assumed to be owned by libssh2 */ LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg, @@ -1101,22 +1189,17 @@ libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value) /* _libssh2_session_set_blocking * - * Set a session's blocking mode on or off, return the previous status - * when this function is called. + * Set a session's blocking mode on or off, return the previous status when + * this function is called. Note this function does not alter the state of the + * actual socket involved. */ int -_libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking) +_libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking) { - int bl = session->socket_block; + int bl = session->api_block_mode; _libssh2_debug(session, LIBSSH2_DBG_CONN, - "Setting blocking mode on session %d", blocking); - if (blocking == session->socket_block) { - /* avoid if already correct */ - return bl; - } - session->socket_block = blocking; - - session_nonblock(session->socket_fd, !blocking); + "Setting blocking mode %s", blocking?"ON":"OFF"); + session->api_block_mode = blocking; return bl; } @@ -1139,7 +1222,7 @@ libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking) LIBSSH2_API int libssh2_session_get_blocking(LIBSSH2_SESSION * session) { - return session->socket_block; + return session->api_block_mode; } /* @@ -1418,7 +1501,7 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) case LIBSSH2_POLLFD_CHANNEL: if (sockets[i].events & POLLIN) { /* Spin session until no data available */ - while (_libssh2_packet_read(fds[i].fd.channel->session) + while (_libssh2_transport_read(fds[i].fd.channel->session) > 0); } if (sockets[i].revents & POLLHUP) { @@ -1431,7 +1514,7 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) case LIBSSH2_POLLFD_LISTENER: if (sockets[i].events & POLLIN) { /* Spin session until no data available */ - while (_libssh2_packet_read(fds[i].fd.listener->session) + while (_libssh2_transport_read(fds[i].fd.listener->session) > 0); } if (sockets[i].revents & POLLHUP) { @@ -1484,7 +1567,7 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) case LIBSSH2_POLLFD_CHANNEL: if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) { /* Spin session until no data available */ - while (_libssh2_packet_read(fds[i].fd.channel->session) + while (_libssh2_transport_read(fds[i].fd.channel->session) > 0); } break; @@ -1493,7 +1576,7 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) if (FD_ISSET (fds[i].fd.listener->session->socket_fd, &rfds)) { /* Spin session until no data available */ - while (_libssh2_packet_read(fds[i].fd.listener->session) + while (_libssh2_transport_read(fds[i].fd.listener->session) > 0); } break; diff --git a/src/sftp.c b/src/sftp.c index e79126d..b7ec4a7 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -89,7 +89,7 @@ * Add a packet to the SFTP packet brigade */ static int -sftp_packet_add(LIBSSH2_SFTP * sftp, unsigned char *data, +sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, unsigned long data_len) { LIBSSH2_SESSION *session = sftp->channel->session; @@ -127,7 +127,7 @@ sftp_packet_add(LIBSSH2_SFTP * sftp, unsigned char *data, * Frame an SFTP packet off the channel */ static int -sftp_packet_read(LIBSSH2_SFTP * sftp) +sftp_packet_read(LIBSSH2_SFTP *sftp) { LIBSSH2_CHANNEL *channel = sftp->channel; LIBSSH2_SESSION *session = channel->session; @@ -225,7 +225,7 @@ sftp_packet_read(LIBSSH2_SFTP * sftp) * Checks if there's a matching SFTP packet available. */ static int -sftp_packet_ask(LIBSSH2_SFTP * sftp, unsigned char packet_type, +sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type, unsigned long request_id, unsigned char **data, unsigned long *data_len) { @@ -280,7 +280,7 @@ sftp_packet_ask(LIBSSH2_SFTP * sftp, unsigned char packet_type, * A la libssh2_packet_require */ static int -sftp_packet_require(LIBSSH2_SFTP * sftp, unsigned char packet_type, +sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type, unsigned long request_id, unsigned char **data, unsigned long *data_len) { @@ -322,7 +322,7 @@ sftp_packet_require(LIBSSH2_SFTP * sftp, unsigned char packet_type, * Require one of N possible reponses */ static int -sftp_packet_requirev(LIBSSH2_SFTP * sftp, int num_valid_responses, +sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses, const unsigned char *valid_responses, unsigned long request_id, unsigned char **data, unsigned long *data_len) @@ -360,12 +360,8 @@ sftp_packet_requirev(LIBSSH2_SFTP * sftp, int num_valid_responses, if (left <= 0) { sftp->requirev_start = 0; return PACKET_TIMEOUT; - } else if (sftp->channel->session->socket_block - && (_libssh2_waitsocket(sftp->channel->session, left) <= - 0)) { - sftp->requirev_start = 0; - return PACKET_TIMEOUT; - } else if (ret == PACKET_EAGAIN) { + } + else if (ret == PACKET_EAGAIN) { return PACKET_EAGAIN; } } @@ -521,12 +517,11 @@ LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) } /* - * libssh2_sftp_init + * sftp_init * * Startup an SFTP session */ -LIBSSH2_API LIBSSH2_SFTP * -libssh2_sftp_init(LIBSSH2_SESSION * session) +static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session) { unsigned char *data, *s; unsigned long data_len; @@ -693,11 +688,23 @@ libssh2_sftp_init(LIBSSH2_SESSION * session) return NULL; } +/* + * libssh2_sftp_init + * + * Startup an SFTP session + */ +LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session) +{ + LIBSSH2_SFTP *ptr; + BLOCK_ADJUST_ERRNO(ptr, session, sftp_init(session)); + return ptr; +} + /* libssh2_sftp_shutdown * Shutsdown the SFTP subsystem */ LIBSSH2_API int -libssh2_sftp_shutdown(LIBSSH2_SFTP * sftp) +libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) { /* * Make sure all memory used in the state variables are free @@ -754,12 +761,12 @@ libssh2_sftp_shutdown(LIBSSH2_SFTP * sftp) * SFTP File and Directory Ops * ******************************* */ -/* libssh2_sftp_open_ex +/* sftp_open */ -LIBSSH2_API LIBSSH2_SFTP_HANDLE * -libssh2_sftp_open_ex(LIBSSH2_SFTP * sftp, const char *filename, - unsigned int filename_len, unsigned long flags, long mode, - int open_type) +static LIBSSH2_SFTP_HANDLE * +sftp_open(LIBSSH2_SFTP *sftp, const char *filename, + unsigned int filename_len, unsigned long flags, long mode, + int open_type) { LIBSSH2_CHANNEL *channel = sftp->channel; LIBSSH2_SESSION *session = channel->session; @@ -932,12 +939,25 @@ libssh2_sftp_open_ex(LIBSSH2_SFTP * sftp, const char *filename, return fp; } -/* libssh2_sftp_read +/* libssh2_sftp_open_ex + */ +LIBSSH2_API LIBSSH2_SFTP_HANDLE * +libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const char *filename, + unsigned int filename_len, unsigned long flags, long mode, + int open_type) +{ + LIBSSH2_SFTP_HANDLE *hnd; + BLOCK_ADJUST_ERRNO(hnd, sftp->channel->session, + sftp_open(sftp, filename, filename_len, flags, mode, + open_type)); + return hnd; +} + +/* sftp_read * Read from an SFTP file handle */ -LIBSSH2_API ssize_t -libssh2_sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer, - size_t buffer_maxlen) +static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer, + size_t buffer_maxlen) { LIBSSH2_SFTP *sftp = handle->sftp; LIBSSH2_CHANNEL *channel = sftp->channel; @@ -1108,14 +1128,26 @@ libssh2_sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer, return total_read; } -/* libssh2_sftp_readdir +/* libssh2_sftp_read + * Read from an SFTP file handle + */ +LIBSSH2_API ssize_t +libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *hnd, char *buffer, + size_t buffer_maxlen) +{ + ssize_t rc; + BLOCK_ADJUST(rc, hnd->sftp->channel->session, + sftp_read(hnd, buffer, buffer_maxlen)); + return rc; +} + +/* sftp_readdir * Read from an SFTP directory handle */ -LIBSSH2_API int -libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE * handle, char *buffer, +static int sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, char *longentry, size_t longentry_maxlen, - LIBSSH2_SFTP_ATTRIBUTES * attrs) + LIBSSH2_SFTP_ATTRIBUTES *attrs) { LIBSSH2_SFTP *sftp = handle->sftp; LIBSSH2_CHANNEL *channel = sftp->channel; @@ -1311,12 +1343,27 @@ libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE * handle, char *buffer, longentry_maxlen, attrs); } -/* libssh2_sftp_write +/* libssh2_sftp_readdir_ex + * Read from an SFTP directory handle + */ +LIBSSH2_API int +libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *hnd, char *buffer, + size_t buffer_maxlen, char *longentry, + size_t longentry_maxlen, + LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + int rc; + BLOCK_ADJUST(rc, hnd->sftp->channel->session, + sftp_readdir(hnd, buffer, buffer_maxlen, longentry, + longentry_maxlen, attrs)); + return rc; +} + +/* sftp_write * Write data to a file handle */ -LIBSSH2_API ssize_t -libssh2_sftp_write(LIBSSH2_SFTP_HANDLE * handle, const char *buffer, - size_t count) +static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE * handle, const char *buffer, + size_t count) { LIBSSH2_SFTP *sftp = handle->sftp; LIBSSH2_CHANNEL *channel = sftp->channel; @@ -1405,12 +1452,27 @@ libssh2_sftp_write(LIBSSH2_SFTP_HANDLE * handle, const char *buffer, return -1; } -/* libssh2_sftp_fstat_ex +/* libssh2_sftp_write + * Write data to a file handle + */ +LIBSSH2_API ssize_t +libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *hnd, const char *buffer, + size_t count) +{ + ssize_t rc; + BLOCK_ADJUST(rc, hnd->sftp->channel->session, + sftp_write(hnd, buffer, count)); + return rc; + +} + +/* + * sftp_fstat + * * Get or Set stat on a file */ -LIBSSH2_API int -libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE * handle, - LIBSSH2_SFTP_ATTRIBUTES * attrs, int setstat) +static int sftp_fstat(LIBSSH2_SFTP_HANDLE *handle, + LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat) { LIBSSH2_SFTP *sftp = handle->sftp; LIBSSH2_CHANNEL *channel = sftp->channel; @@ -1506,6 +1568,19 @@ libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE * handle, return 0; } +/* libssh2_sftp_fstat_ex + * Get or Set stat on a file + */ +LIBSSH2_API int +libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE * hnd, + LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat) +{ + int rc; + BLOCK_ADJUST(rc, hnd->sftp->channel->session, + sftp_fstat(hnd, attrs, setstat)); + return rc; +} + /* libssh2_sftp_seek * Set the read/write pointer to an arbitrary position within the file */ @@ -1542,12 +1617,13 @@ libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE * handle) return handle->u.file.offset; } -/* libssh2_sftp_close_handle +/* sftp_close_handle + * * Close a file or directory handle * Also frees handle resource and unlinks it from the SFTP structure */ -LIBSSH2_API int -libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE * handle) +static int +sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle) { LIBSSH2_SFTP *sftp = handle->sftp; LIBSSH2_CHANNEL *channel = sftp->channel; @@ -1647,16 +1723,23 @@ libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE * handle) return 0; } -/* ********************** - * SFTP Miscellaneous * - ********************** */ +/* libssh2_sftp_close_handle + * + * Close a file or directory handle + * Also frees handle resource and unlinks it from the SFTP structure + */ +LIBSSH2_API int +libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *hnd) +{ + int rc; + BLOCK_ADJUST(rc, hnd->sftp->channel->session, sftp_close_handle(hnd)); + return rc; +} -/* libssh2_sftp_unlink_ex +/* sftp_unlink * Delete a file from the remote server */ -/* libssh2_sftp_unlink_ex - NB-UNSAFE?? */ -LIBSSH2_API int -libssh2_sftp_unlink_ex(LIBSSH2_SFTP * sftp, const char *filename, +static int sftp_unlink(LIBSSH2_SFTP *sftp, const char *filename, unsigned int filename_len) { LIBSSH2_CHANNEL *channel = sftp->channel; @@ -1738,11 +1821,25 @@ libssh2_sftp_unlink_ex(LIBSSH2_SFTP * sftp, const char *filename, } } -/* libssh2_sftp_rename_ex - * Rename a file on the remote server +/* libssh2_sftp_unlink_ex + * Delete a file from the remote server */ LIBSSH2_API int -libssh2_sftp_rename_ex(LIBSSH2_SFTP * sftp, const char *source_filename, +libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename, + unsigned int filename_len) +{ + int rc; + BLOCK_ADJUST(rc, sftp->channel->session, + sftp_unlink(sftp, filename, filename_len)); + return rc; +} + +/* + * sftp_rename + * + * Rename a file on the remote server + */ +static int sftp_rename(LIBSSH2_SFTP *sftp, const char *source_filename, unsigned int source_filename_len, const char *dest_filename, unsigned int dest_filename_len, long flags) @@ -1865,11 +1962,28 @@ libssh2_sftp_rename_ex(LIBSSH2_SFTP * sftp, const char *source_filename, return retcode; } -/* libssh2_sftp_mkdir_ex - * Create an SFTP directory +/* libssh2_sftp_rename_ex + * Rename a file on the remote server */ LIBSSH2_API int -libssh2_sftp_mkdir_ex(LIBSSH2_SFTP * sftp, const char *path, +libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename, + unsigned int source_filename_len, + const char *dest_filename, + unsigned int dest_filename_len, long flags) +{ + int rc; + BLOCK_ADJUST(rc, sftp->channel->session, + sftp_rename(sftp, source_filename, source_filename_len, + dest_filename, dest_filename_len, flags)); + return rc; +} + +/* + * sftp_mkdir + * + * Create an SFTP directory + */ +static int sftp_mkdir(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, long mode) { LIBSSH2_CHANNEL *channel = sftp->channel; @@ -1957,12 +2071,25 @@ libssh2_sftp_mkdir_ex(LIBSSH2_SFTP * sftp, const char *path, } } -/* libssh2_sftp_rmdir_ex +/* + * libssh2_sftp_mkdir_ex + * + * Create an SFTP directory + */ +LIBSSH2_API int +libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, + unsigned int path_len, long mode) +{ + int rc; + BLOCK_ADJUST(rc, sftp->channel->session, + sftp_mkdir(sftp, path, path_len, mode)); + return rc; +} + +/* sftp_rmdir * Remove a directory */ -/* libssh2_sftp_rmdir_ex - NB-UNSAFE?? */ -LIBSSH2_API int -libssh2_sftp_rmdir_ex(LIBSSH2_SFTP * sftp, const char *path, +static int sftp_rmdir(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len) { LIBSSH2_CHANNEL *channel = sftp->channel; @@ -2042,12 +2169,23 @@ libssh2_sftp_rmdir_ex(LIBSSH2_SFTP * sftp, const char *path, } } -/* libssh2_sftp_stat_ex +/* libssh2_sftp_rmdir_ex + * Remove a directory + */ +LIBSSH2_API int +libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path, + unsigned int path_len) +{ + int rc; + BLOCK_ADJUST(rc, sftp->channel->session, + sftp_rmdir(sftp, path, path_len)); + return rc; +} + +/* sftp_stat * Stat a file or symbolic link */ -/* libssh2_sftp_stat_ex - NB-UNSAFE?? */ -LIBSSH2_API int -libssh2_sftp_stat_ex(LIBSSH2_SFTP * sftp, const char *path, +static int sftp_stat(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, int stat_type, LIBSSH2_SFTP_ATTRIBUTES * attrs) { @@ -2159,11 +2297,24 @@ libssh2_sftp_stat_ex(LIBSSH2_SFTP * sftp, const char *path, return 0; } -/* libssh2_sftp_symlink_ex - * Read or set a symlink +/* libssh2_sftp_stat_ex + * Stat a file or symbolic link */ LIBSSH2_API int -libssh2_sftp_symlink_ex(LIBSSH2_SFTP * sftp, const char *path, +libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path, + unsigned int path_len, int stat_type, + LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + int rc; + BLOCK_ADJUST(rc, sftp->channel->session, + sftp_stat(sftp, path, path_len, stat_type, attrs)); + return rc; +} + +/* sftp_symlink + * Read or set a symlink + */ +static int sftp_symlink(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, char *target, unsigned int target_len, int link_type) { @@ -2301,11 +2452,26 @@ libssh2_sftp_symlink_ex(LIBSSH2_SFTP * sftp, const char *path, return link_len; } +/* libssh2_sftp_symlink_ex + * Read or set a symlink + */ +LIBSSH2_API int +libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, + unsigned int path_len, char *target, + unsigned int target_len, int link_type) +{ + int rc; + BLOCK_ADJUST(rc, sftp->channel->session, + sftp_symlink(sftp, path, path_len, target, target_len, + link_type)); + return rc; +} + /* libssh2_sftp_last_error * Returns the last error code reported by SFTP */ LIBSSH2_API unsigned long -libssh2_sftp_last_error(LIBSSH2_SFTP * sftp) +libssh2_sftp_last_error(LIBSSH2_SFTP *sftp) { return sftp->last_errno; } diff --git a/src/transport.c b/src/transport.c index cf81b80..92d14b2 100644 --- a/src/transport.c +++ b/src/transport.c @@ -44,6 +44,8 @@ #include +#include "transport.h" + #define MAX_BLOCKSIZE 32 /* MUST fit biggest crypto block size we use/get */ #define MAX_MACSIZE 20 /* MUST fit biggest MAC length we support */ @@ -220,7 +222,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ ) session->fullpacket_packet_type = p->payload[0]; - debugdump(session, "libssh2_packet_read() plain", + debugdump(session, "libssh2_transport_read() plain", p->payload, session->fullpacket_payload_len); session->fullpacket_state = libssh2_NB_state_created; @@ -244,7 +246,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ ) /* - * _libssh2_packet_read + * _libssh2_transport_read * * Collect a packet into the input brigade block only controls whether or not * to wait for a packet to start. @@ -259,7 +261,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ ) * "The Secure Shell (SSH) Transport Layer Protocol" */ libssh2pack_t -_libssh2_packet_read(LIBSSH2_SESSION * session) +_libssh2_transport_read(LIBSSH2_SESSION * session) { libssh2pack_t rc; struct transportpacket *p = &session->packet; @@ -313,7 +315,7 @@ _libssh2_packet_read(LIBSSH2_SESSION * session) if (session->readPack_state == libssh2_NB_state_jump1) { session->readPack_state = libssh2_NB_state_idle; encrypted = session->readPack_encrypted; - goto libssh2_packet_read_point1; + goto libssh2_transport_read_point1; } do { @@ -373,7 +375,7 @@ _libssh2_packet_read(LIBSSH2_SESSION * session) } return PACKET_FAIL; } - debugdump(session, "libssh2_packet_read() raw", + debugdump(session, "libssh2_transport_read() raw", &p->buf[remainbuf], nread); /* advance write pointer */ p->writeidx += nread; @@ -542,7 +544,7 @@ _libssh2_packet_read(LIBSSH2_SESSION * session) if (!remainpack) { /* we have a full packet */ - libssh2_packet_read_point1: + libssh2_transport_read_point1: rc = fullpacket(session, encrypted); if (rc == PACKET_EAGAIN) { @@ -618,7 +620,7 @@ send_existing(LIBSSH2_SESSION * session, unsigned char *data, return PACKET_EAGAIN; } - debugdump(session, "libssh2_packet_write send()", &p->outbuf[p->osent], + debugdump(session, "libssh2_transport_write send()", &p->outbuf[p->osent], length); p->osent += length; /* we sent away this much data */ @@ -626,7 +628,7 @@ send_existing(LIBSSH2_SESSION * session, unsigned char *data, } /* - * libssh2_packet_write + * libssh2_transport_write * * Send a packet, encrypting it and adding a MAC code if necessary * Returns 0 on success, non-zero on failure. @@ -641,8 +643,8 @@ send_existing(LIBSSH2_SESSION * session, unsigned char *data, * (RFC4253 section 6.1) */ int -_libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, - unsigned long data_len) +_libssh2_transport_write(LIBSSH2_SESSION * session, unsigned char *data, + unsigned long data_len) { int blocksize = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt-> @@ -663,7 +665,7 @@ _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, unsigned char *orgdata = data; unsigned long orgdata_len = data_len; - debugdump(session, "libssh2_packet_write plain", data, data_len); + debugdump(session, "libssh2_transport_write plain", data, data_len); /* FIRST, check if we have a pending write to complete */ rc = send_existing(session, data, data_len, &ret); @@ -769,7 +771,7 @@ _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, LIBSSH2_SOCKET_SEND_FLAGS(session)); if (ret != -1) { - debugdump(session, "libssh2_packet_write send()", p->outbuf, ret); + debugdump(session, "libssh2_transport_write send()", p->outbuf, ret); } if (ret != total_length) { if ((ret > 0) || ((ret == -1) && (errno == EAGAIN))) { diff --git a/src/transport.h b/src/transport.h new file mode 100644 index 0000000..3510024 --- /dev/null +++ b/src/transport.h @@ -0,0 +1,80 @@ +#ifndef __LIBSSH2_TRANSPORT_H +#define __LIBSSH2_TRANSPORT_H + +/* Copyright (C) 2007 The Written Word, Inc. All rights reserved. + * Copyright (C) 2009 by Daniel Stenberg + * Author: Daniel Stenberg + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file handles reading and writing to the SECSH transport layer. RFC4253. + */ + +#include "libssh2_priv.h" + +/* + * libssh2_transport_write + * + * Send a packet, encrypting it and adding a MAC code if necessary + * Returns 0 on success, non-zero on failure. + * + * Returns PACKET_EAGAIN if it would block - and if it does so, you should + * call this function again as soon as it is likely that more data can be + * sent, and this function should then be called with the same argument set + * (same data pointer and same data_len) until zero or failure is returned. + * + * NOTE: this function does not verify that 'data_len' is less than ~35000 + * which is what all implementations should support at least as packet size. + * (RFC4253 section 6.1) + */ +int _libssh2_transport_write(LIBSSH2_SESSION * session, unsigned char *data, + unsigned long data_len); +/* + * _libssh2_transport_read + * + * Collect a packet into the input brigade block only controls whether or not + * to wait for a packet to start. + * + * Returns packet type added to input brigade (PACKET_NONE if nothing added), + * or PACKET_FAIL on failure and PACKET_EAGAIN if it couldn't process a full + * packet. + */ + +/* + * This function reads the binary stream as specified in chapter 6 of RFC4253 + * "The Secure Shell (SSH) Transport Layer Protocol" + */ +libssh2pack_t _libssh2_transport_read(LIBSSH2_SESSION * session); + +#endif /* __LIBSSH2_TRANSPORT_H */ diff --git a/src/userauth.c b/src/userauth.c index 47b430e..a91db91 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -1,4 +1,5 @@ /* Copyright (c) 2004-2007, Sara Golemon + * Copyright (c) 2009 by Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, @@ -45,16 +46,17 @@ #include #endif +#include "transport.h" -/* {{{ proto libssh2_userauth_list +/* libssh2_userauth_list + * * List authentication methods * Will yield successful login if "none" happens to be allowable for this user * Not a common configuration for any SSH server though * username should be NULL, or a null terminated string */ -LIBSSH2_API char * -libssh2_userauth_list(LIBSSH2_SESSION * session, const char *username, - unsigned int username_len) +static char *userauth_list(LIBSSH2_SESSION *session, const char *username, + unsigned int username_len) { static const unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; @@ -101,7 +103,7 @@ libssh2_userauth_list(LIBSSH2_SESSION * session, const char *username, } if (session->userauth_list_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, session->userauth_list_data, + rc = _libssh2_transport_write(session, session->userauth_list_data, session->userauth_list_data_len); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, @@ -161,6 +163,23 @@ libssh2_userauth_list(LIBSSH2_SESSION * session, const char *username, return (char *) session->userauth_list_data; } +/* libssh2_userauth_list + * + * List authentication methods + * Will yield successful login if "none" happens to be allowable for this user + * Not a common configuration for any SSH server though + * username should be NULL, or a null terminated string + */ +LIBSSH2_API char * +libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user, + unsigned int user_len) +{ + char *ptr; + BLOCK_ADJUST_ERRNO(ptr, session, + userauth_list(session, user, user_len)); + return ptr; +} + /* * libssh2_userauth_authenticated * @@ -173,16 +192,16 @@ libssh2_userauth_authenticated(LIBSSH2_SESSION * session) return session->state & LIBSSH2_STATE_AUTHENTICATED; } -/* }}} */ -/* {{{ libssh2_userauth_password + +/* userauth_password * Plain ol' login */ -LIBSSH2_API int -libssh2_userauth_password_ex(LIBSSH2_SESSION * session, const char *username, - unsigned int username_len, const char *password, - unsigned int password_len, - LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) +static int +userauth_password(LIBSSH2_SESSION *session, const char *username, + unsigned int username_len, const char *password, + unsigned int password_len, + LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) { unsigned char *s; static const unsigned char reply_codes[4] = @@ -208,8 +227,8 @@ libssh2_userauth_password_ex(LIBSSH2_SESSION * session, const char *username, LIBSSH2_ALLOC(session, session->userauth_pswd_data_len); if (!session->userauth_pswd_data) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for userauth-password request", - 0); + "Unable to allocate memory for userauth-password" + " request", 0); return -1; } @@ -244,7 +263,7 @@ libssh2_userauth_password_ex(LIBSSH2_SESSION * session, const char *username, } if (session->userauth_pswd_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, session->userauth_pswd_data, + rc = _libssh2_transport_write(session, session->userauth_pswd_data, session->userauth_pswd_data_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -378,7 +397,7 @@ libssh2_userauth_password_ex(LIBSSH2_SESSION * session, const char *username, } if (session->userauth_pswd_state == libssh2_NB_state_sent2) { - rc = _libssh2_packet_write(session, + rc = _libssh2_transport_write(session, session->userauth_pswd_data, session-> userauth_pswd_data_len); @@ -425,7 +444,25 @@ libssh2_userauth_password_ex(LIBSSH2_SESSION * session, const char *username, return -1; } -/* }}} */ +/* + * libssh2_userauth_password_ex + * + * Plain ol' login + */ + +LIBSSH2_API int +libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, + unsigned int username_len, const char *password, + unsigned int password_len, + LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) +{ + int rc; + BLOCK_ADJUST(rc, session, + userauth_password(session, username, username_len, + password, password_len, + passwd_change_cb)); + return rc; +} /* * file_read_publickey @@ -529,9 +566,9 @@ file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method, return 0; } -/* }}} */ -/* {{{ libssh2_file_read_privatekey + +/* libssh2_file_read_privatekey * Read a PEM encoded private key from an id_??? style file */ static int @@ -574,22 +611,19 @@ libssh2_file_read_privatekey(LIBSSH2_SESSION * session, return 0; } -/* }}} */ -/* {{{ libssh2_userauth_hostbased_fromfile_ex + +/* userauth_hostbased_fromfile * Authenticate using a keypair found in the named files */ -LIBSSH2_API int -libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION * session, - const char *username, - unsigned int username_len, - const char *publickey, - const char *privatekey, - const char *passphrase, - const char *hostname, - unsigned int hostname_len, - const char *local_username, - unsigned int local_username_len) +static int +userauth_hostbased_fromfile(LIBSSH2_SESSION *session, + const char *username, unsigned int username_len, + const char *publickey, const char *privatekey, + const char *passphrase, const char *hostname, + unsigned int hostname_len, + const char *local_username, + unsigned int local_username_len) { static const unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; @@ -632,10 +666,9 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION * session, */ session->userauth_host_s = session->userauth_host_packet = LIBSSH2_ALLOC(session, - session->userauth_host_packet_len + 4 + (4 + - session-> - userauth_host_method_len) - + (4 + pubkeydata_len)); + session->userauth_host_packet_len + 4 + + (4 + session->userauth_host_method_len) + + (4 + pubkeydata_len)); if (!session->userauth_host_packet) { LIBSSH2_FREE(session, session->userauth_host_method); session->userauth_host_method = NULL; @@ -716,10 +749,14 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION * session, if (sig_len > pubkeydata_len) { unsigned char *newpacket; /* Should *NEVER* happen, but...well.. better safe than sorry */ - newpacket = LIBSSH2_REALLOC(session, session->userauth_host_packet, session->userauth_host_packet_len + 4 + (4 + session->userauth_host_method_len) + (4 + sig_len)); /* PK sigblob */ + newpacket = LIBSSH2_REALLOC(session, session->userauth_host_packet, + session->userauth_host_packet_len + 4 + + (4 + session->userauth_host_method_len) + + (4 + sig_len)); /* PK sigblob */ if (!newpacket) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Failed allocating additional space for userauth-hostbased packet", + "Failed allocating additional space for " + "userauth-hostbased packet", 0); LIBSSH2_FREE(session, sig); LIBSSH2_FREE(session, session->userauth_host_packet); @@ -760,7 +797,7 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION * session, } if (session->userauth_host_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, session->userauth_host_packet, + rc = _libssh2_transport_write(session, session->userauth_host_packet, session->userauth_host_s - session->userauth_host_packet); if (rc == PACKET_EAGAIN) { @@ -809,24 +846,50 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION * session, LIBSSH2_FREE(session, session->userauth_host_data); session->userauth_host_data = NULL; libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Invalid signature for supplied public key, or bad username/public key combination", + "Invalid signature for supplied public key, or bad " + "username/public key combination", 0); session->userauth_host_state = libssh2_NB_state_idle; return -1; } -/* }}} */ - -/* {{{ libssh2_userauth_publickey_fromfile_ex +/* libssh2_userauth_hostbased_fromfile_ex * Authenticate using a keypair found in the named files */ LIBSSH2_API int -libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION * session, - const char *username, - unsigned int username_len, +libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, + const char *user, + unsigned int user_len, const char *publickey, const char *privatekey, - const char *passphrase) + const char *passphrase, + const char *host, + unsigned int host_len, + const char *localuser, + unsigned int localuser_len) +{ + int rc; + BLOCK_ADJUST(rc, session, + userauth_hostbased_fromfile(session, user, user_len, + publickey, privatekey, + passphrase, host, host_len, + localuser, localuser_len)); + return rc; +} + + + +/* + * userauth_publickey_fromfile + * Authenticate using a keypair found in the named files + */ +static int +userauth_publickey_fromfile(LIBSSH2_SESSION *session, + const char *username, + unsigned int username_len, + const char *publickey, + const char *privatekey, + const char *passphrase) { unsigned long pubkeydata_len = 0; unsigned char reply_codes[4] = @@ -865,9 +928,8 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION * session, */ session->userauth_pblc_s = session->userauth_pblc_packet = LIBSSH2_ALLOC(session, - session->userauth_pblc_packet_len + 4 + (4 + - session-> - userauth_pblc_method_len) + session->userauth_pblc_packet_len + 4 + + (4 + session->userauth_pblc_method_len) + (4 + pubkeydata_len)); if (!session->userauth_pblc_packet) { LIBSSH2_FREE(session, session->userauth_pblc_method); @@ -916,7 +978,7 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION * session, } if (session->userauth_pblc_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, session->userauth_pblc_packet, + rc = _libssh2_transport_write(session, session->userauth_pblc_packet, session->userauth_pblc_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -1039,10 +1101,15 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION * session, if (sig_len > pubkeydata_len) { unsigned char *newpacket; /* Should *NEVER* happen, but...well.. better safe than sorry */ - newpacket = LIBSSH2_REALLOC(session, session->userauth_pblc_packet, session->userauth_pblc_packet_len + 4 + (4 + session->userauth_pblc_method_len) + (4 + sig_len)); /* PK sigblob */ + newpacket = LIBSSH2_REALLOC(session, + session->userauth_pblc_packet, + session->userauth_pblc_packet_len + 4 + + (4 + session->userauth_pblc_method_len) + + (4 + sig_len)); /* PK sigblob */ if (!newpacket) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Failed allocating additional space for userauth-publickey packet", + "Failed allocating additional space for " + "userauth-publickey packet", 0); LIBSSH2_FREE(session, sig); LIBSSH2_FREE(session, session->userauth_pblc_packet); @@ -1085,7 +1152,7 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION * session, } if (session->userauth_pblc_state == libssh2_NB_state_sent1) { - rc = _libssh2_packet_write(session, session->userauth_pblc_packet, + rc = _libssh2_transport_write(session, session->userauth_pblc_packet, session->userauth_pblc_s - session->userauth_pblc_packet); if (rc == PACKET_EAGAIN) { @@ -1133,22 +1200,44 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION * session, LIBSSH2_FREE(session, session->userauth_pblc_data); session->userauth_pblc_data = NULL; libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Invalid signature for supplied public key, or bad username/public key combination", + "Invalid signature for supplied public key, or bad " + "username/public key combination", 0); session->userauth_pblc_state = libssh2_NB_state_idle; return -1; } -/* }}} */ - -/* {{{ libssh2_userauth_keyboard_interactive - * Authenticate using a challenge-response authentication +/* libssh2_userauth_publickey_fromfile_ex + * Authenticate using a keypair found in the named files */ LIBSSH2_API int -libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, - const char *username, - unsigned int username_len, - LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) +libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, + const char *user, + unsigned int user_len, + const char *publickey, + const char *privatekey, + const char *passphrase) +{ + int rc; + BLOCK_ADJUST(rc, session, + userauth_publickey_fromfile(session, user, user_len, + publickey, privatekey, + passphrase)); + return rc; +} + + + +/* + * userauth_keyboard_interactive + * + * Authenticate using a challenge-response authentication + */ +static int +userauth_keyboard_interactive(LIBSSH2_SESSION * session, + const char *username, + unsigned int username_len, + LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) { unsigned char *s; int rc; @@ -1171,19 +1260,23 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, memset(&session->userauth_kybd_packet_requirev_state, 0, sizeof(session->userauth_kybd_packet_requirev_state)); - session->userauth_kybd_packet_len = 1 /* byte SSH_MSG_USERAUTH_REQUEST */ - + 4 + username_len /* string user name (ISO-10646 UTF-8, as defined in [RFC-3629]) */ - + 4 + 14 /* string service name (US-ASCII) */ - + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ - + 4 + 0 /* string language tag (as defined in [RFC-3066]) */ - + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ + session->userauth_kybd_packet_len = + 1 /* byte SSH_MSG_USERAUTH_REQUEST */ + + 4 + username_len /* string user name (ISO-10646 UTF-8, as + defined in [RFC-3629]) */ + + 4 + 14 /* string service name (US-ASCII) */ + + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ + + 4 + 0 /* string language tag (as defined in + [RFC-3066]) */ + + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ ; session->userauth_kybd_data = s = LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len); if (!s) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-interactive authentication", + "Unable to allocate memory for " + "keyboard-interactive authentication", 0); return -1; } @@ -1223,7 +1316,7 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, } if (session->userauth_kybd_state == libssh2_NB_state_created) { - rc = _libssh2_packet_write(session, session->userauth_kybd_data, + rc = _libssh2_transport_write(session, session->userauth_kybd_data, session->userauth_kybd_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; @@ -1284,7 +1377,8 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, LIBSSH2_ALLOC(session, session->userauth_kybd_auth_name_len); if (!session->userauth_kybd_auth_name) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-interactive 'name' request field", + "Unable to allocate memory for " + "keyboard-interactive 'name' request field", 0); goto cleanup; } @@ -1300,7 +1394,9 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, session->userauth_kybd_auth_instruction_len); if (!session->userauth_kybd_auth_instruction) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-interactive 'instruction' request field", + "Unable to allocate memory for " + "keyboard-interactive 'instruction' " + "request field", 0); goto cleanup; } @@ -1324,7 +1420,8 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, session->userauth_kybd_num_prompts); if (!session->userauth_kybd_prompts) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-interactive prompts array", + "Unable to allocate memory for " + "keyboard-interactive prompts array", 0); goto cleanup; } @@ -1338,7 +1435,8 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, session->userauth_kybd_num_prompts); if (!session->userauth_kybd_responses) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-interactive responses array", + "Unable to allocate memory for " + "keyboard-interactive responses array", 0); goto cleanup; } @@ -1355,7 +1453,8 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, session->userauth_kybd_prompts[i].length); if (!session->userauth_kybd_prompts[i].text) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-interactive prompt message", + "Unable to allocate memory for " + "keyboard-interactive prompt message", 0); goto cleanup; } @@ -1377,9 +1476,11 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, &session->abstract); _libssh2_debug(session, LIBSSH2_DBG_AUTH, - "Keyboard-interactive response callback function invoked"); + "Keyboard-interactive response callback function" + " invoked"); - session->userauth_kybd_packet_len = 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ + session->userauth_kybd_packet_len = + 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ + 4 /* int num-responses */ ; @@ -1393,7 +1494,8 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len); if (!s) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-interactive response packet", + "Unable to allocate memory for keyboard-" + "interactive response packet", 0); goto cleanup; } @@ -1415,14 +1517,15 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, } if (session->userauth_kybd_state == libssh2_NB_state_sent1) { - rc = _libssh2_packet_write(session, session->userauth_kybd_data, + rc = _libssh2_transport_write(session, session->userauth_kybd_data, session->userauth_kybd_packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth-keyboard-interactive request", + "Unable to send userauth-keyboard-interactive" + " request", 0); goto cleanup; } @@ -1468,4 +1571,21 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, } } -/* }}} */ +/* + * libssh2_userauth_keyboard_interactive_ex + * + * Authenticate using a challenge-response authentication + */ +LIBSSH2_API int +libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session, + const char *user, + unsigned int user_len, + LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) +{ + int rc; + BLOCK_ADJUST(rc, session, + userauth_keyboard_interactive(session, user, user_len, + response_callback)); + return rc; +} +