diff options
Diffstat (limited to 'src/channels.c')
-rw-r--r-- | src/channels.c | 792 |
1 files changed, 477 insertions, 315 deletions
diff --git a/src/channels.c b/src/channels.c index 8d812477..9e613715 100644 --- a/src/channels.c +++ b/src/channels.c @@ -29,6 +29,7 @@ #include <errno.h> #include <time.h> #include <stdbool.h> +#include <string.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif /* HAVE_SYS_TIME_H */ @@ -51,16 +52,19 @@ #include "libssh/server.h" #endif -#define WINDOWBASE 1280000 -#define WINDOWLIMIT (WINDOWBASE/2) - /* * All implementations MUST be able to process packets with an * uncompressed payload length of 32768 bytes or less and a total packet * size of 35000 bytes or less. */ #define CHANNEL_MAX_PACKET 32768 -#define CHANNEL_INITIAL_WINDOW 64000 + +/* + * WINDOW_DEFAULT matches the default OpenSSH session window size. + * This controls how much data the peer can send before needing to receive + * a round-trip SSH2_MSG_CHANNEL_WINDOW_ADJUST message that increases the window. + */ +#define WINDOW_DEFAULT (64*CHANNEL_MAX_PACKET) /** * @defgroup libssh_channel The SSH channel functions @@ -79,6 +83,9 @@ static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet); * @param[in] session The ssh session to use. * * @return A pointer to a newly allocated channel, NULL on error. + * The channel needs to be freed with ssh_channel_free(). + * + * @see ssh_channel_free() */ ssh_channel ssh_channel_new(ssh_session session) { @@ -147,8 +154,9 @@ ssh_channel ssh_channel_new(ssh_session session) * * @return The new channel identifier. */ -uint32_t ssh_channel_new_id(ssh_session session) { - return ++(session->maxchannel); +uint32_t ssh_channel_new_id(ssh_session session) +{ + return ++(session->maxchannel); } /** @@ -173,7 +181,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ channel=ssh_channel_from_local(session,channelid); if(channel==NULL){ ssh_set_error(session, SSH_FATAL, - "Unknown channel id %"PRIu32, + "Unknown channel id %" PRIu32, (uint32_t) channelid); /* TODO: Set error marking in channel object */ @@ -187,8 +195,8 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ if (rc != SSH_OK) goto error; - SSH_LOG(SSH_LOG_PROTOCOL, - "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d", + SSH_LOG(SSH_LOG_DEBUG, + "Received a CHANNEL_OPEN_CONFIRMATION for channel %" PRIu32 ":%" PRIu32, channel->local_channel, channel->remote_channel); @@ -200,13 +208,21 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ goto error; } - SSH_LOG(SSH_LOG_PROTOCOL, - "Remote window : %"PRIu32", maxpacket : %"PRIu32, - (uint32_t) channel->remote_window, - (uint32_t) channel->remote_maxpacket); + SSH_LOG(SSH_LOG_DEBUG, + "Remote window : %" PRIu32 ", maxpacket : %" PRIu32, + channel->remote_window, + channel->remote_maxpacket); channel->state = SSH_CHANNEL_STATE_OPEN; channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND; + + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_open_response_function, + channel->session, + channel, + true /* is_success */); + return SSH_PACKET_USED; error: @@ -245,16 +261,25 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){ "SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel " "state %d", channel->state); + SAFE_FREE(error); goto error; } ssh_set_error(session, SSH_REQUEST_DENIED, - "Channel opening failure: channel %u error (%"PRIu32") %s", + "Channel opening failure: channel %" PRIu32 " error (%" PRIu32 ") %s", channel->local_channel, - (uint32_t) code, + code, error); SAFE_FREE(error); channel->state=SSH_CHANNEL_STATE_OPEN_DENIED; + + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_open_response_function, + channel->session, + channel, + false /* is_success */); + return SSH_PACKET_USED; error: @@ -262,7 +287,8 @@ error: return SSH_PACKET_USED; } -static int ssh_channel_open_termination(void *c){ +static int ssh_channel_open_termination(void *c) +{ ssh_channel channel = (ssh_channel) c; if (channel->state != SSH_CHANNEL_STATE_OPENING || channel->session->session_state == SSH_SESSION_STATE_ERROR) @@ -320,8 +346,8 @@ channel_open(ssh_channel channel, channel->local_maxpacket = maxpacket; channel->local_window = window; - SSH_LOG(SSH_LOG_PROTOCOL, - "Creating a channel %d with %d window and %d max packet", + SSH_LOG(SSH_LOG_DEBUG, + "Creating a channel %" PRIu32 " with %" PRIu32 " window and %" PRIu32 " max packet", channel->local_channel, window, maxpacket); rc = ssh_buffer_pack(session->out_buffer, @@ -349,7 +375,7 @@ channel_open(ssh_channel channel, } SSH_LOG(SSH_LOG_PACKET, - "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", + "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %" PRIu32, type, channel->local_channel); pending: @@ -369,7 +395,7 @@ end: if (channel->state == SSH_CHANNEL_STATE_OPEN) { err = SSH_OK; } else if (err != SSH_AGAIN) { - /* Messages were handled correctly, but he channel state is invalid */ + /* Messages were handled correctly, but the channel state is invalid */ err = SSH_ERROR; } @@ -396,35 +422,46 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) { /** * @internal - * @brief grows the local window and send a packet to the other party + * @brief grows the local window and sends a packet to the other party * @param session SSH session * @param channel SSH channel - * @param minimumsize The minimum acceptable size for the new window. * @return SSH_OK if successful; SSH_ERROR otherwise. */ static int grow_window(ssh_session session, - ssh_channel channel, - uint32_t minimumsize) + ssh_channel channel) { - uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE; + uint32_t used; + uint32_t increment; int rc; - if(new_window <= channel->local_window){ - SSH_LOG(SSH_LOG_PROTOCOL, - "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)", - channel->local_channel, channel->remote_channel, new_window, + /* Calculate the increment taking into account what the peer may still send + * (local_window) and what we've already buffered (stdout_buffer and + * stderr_buffer). + */ + used = channel->local_window; + if (channel->stdout_buffer != NULL) { + used += ssh_buffer_get_len(channel->stdout_buffer); + } + if (channel->stderr_buffer != NULL) { + used += ssh_buffer_get_len(channel->stderr_buffer); + } + /* Avoid a negative increment in case the peer sent more than the window allowed */ + increment = WINDOW_DEFAULT > used ? WINDOW_DEFAULT - used : 0; + /* Don't grow until we can request at least half a window */ + if (increment < (WINDOW_DEFAULT / 2)) { + SSH_LOG(SSH_LOG_DEBUG, + "growing window (channel %" PRIu32 ":%" PRIu32 ") to %" PRIu32 " bytes : not needed (%" PRIu32 " bytes)", + channel->local_channel, channel->remote_channel, WINDOW_DEFAULT, channel->local_window); return SSH_OK; } - /* WINDOW_ADJUST packet needs a relative increment rather than an absolute - * value, so we give here the missing bytes needed to reach new_window - */ + rc = ssh_buffer_pack(session->out_buffer, "bdd", SSH2_MSG_CHANNEL_WINDOW_ADJUST, channel->remote_channel, - new_window - channel->local_window); + increment); if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; @@ -434,13 +471,13 @@ static int grow_window(ssh_session session, goto error; } - SSH_LOG(SSH_LOG_PROTOCOL, - "growing window (channel %d:%d) to %d bytes", + SSH_LOG(SSH_LOG_DEBUG, + "growing window (channel %" PRIu32 ":%" PRIu32 ") by %" PRIu32 " bytes", channel->local_channel, channel->remote_channel, - new_window); + increment); - channel->local_window = new_window; + channel->local_window += increment; return SSH_OK; error: @@ -462,7 +499,8 @@ error: * @return The related ssh_channel, or NULL if the channel is * unknown or the packet is invalid. */ -static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) { +static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) +{ ssh_channel channel; uint32_t chan; int rc; @@ -477,7 +515,7 @@ static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) { channel = ssh_channel_from_local(session, chan); if (channel == NULL) { ssh_set_error(session, SSH_FATAL, - "Server specified invalid channel %"PRIu32, + "Server specified invalid channel %" PRIu32, (uint32_t) chan); } @@ -488,6 +526,8 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) { ssh_channel channel; uint32_t bytes; int rc; + bool was_empty; + (void)user; (void)type; @@ -504,123 +544,153 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) { return SSH_PACKET_USED; } - SSH_LOG(SSH_LOG_PROTOCOL, - "Adding %d bytes to channel (%d:%d) (from %d bytes)", + SSH_LOG(SSH_LOG_DEBUG, + "Adding %" PRIu32 " bytes to channel (%" PRIu32 ":%" PRIu32 ") (from %" PRIu32 " bytes)", bytes, channel->local_channel, channel->remote_channel, channel->remote_window); + was_empty = channel->remote_window == 0; + channel->remote_window += bytes; + /* Writing to the channel is non-blocking until the receive window is empty. + When the receive window becomes non-zero again, call channel_write_wontblock_function. */ + if (was_empty && bytes > 0) { + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_write_wontblock_function, + session, + channel, + channel->remote_window); + } + return SSH_PACKET_USED; } /* is_stderr is set to 1 if the data are extended, ie stderr */ -SSH_PACKET_CALLBACK(channel_rcv_data){ - ssh_channel channel; - ssh_string str; - ssh_buffer buf; - size_t len; - int is_stderr; - int rest; - (void)user; +SSH_PACKET_CALLBACK(channel_rcv_data) +{ + ssh_channel channel = NULL; + ssh_string str = NULL; + ssh_buffer buf = NULL; + void *data = NULL; + uint32_t len; + int extended, is_stderr = 0; + int rest; - if(type==SSH2_MSG_CHANNEL_DATA) - is_stderr=0; - else - is_stderr=1; + (void)user; - channel = channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, - "%s", ssh_get_error(session)); + if (type == SSH2_MSG_CHANNEL_DATA) { + extended = 0; + } else { /* SSH_MSG_CHANNEL_EXTENDED_DATA */ + extended = 1; + } - return SSH_PACKET_USED; - } + channel = channel_from_msg(session, packet); + if (channel == NULL) { + SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - if (is_stderr) { - uint32_t ignore; - /* uint32 data type code. we can ignore it */ - ssh_buffer_get_u32(packet, &ignore); - } + return SSH_PACKET_USED; + } - str = ssh_buffer_get_ssh_string(packet); - if (str == NULL) { - SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!"); + if (extended) { + uint32_t data_type_code, rc; + rc = ssh_buffer_get_u32(packet, &data_type_code); + if (rc != sizeof(uint32_t)) { + SSH_LOG(SSH_LOG_PACKET, + "Failed to read data type code: rc = %" PRIu32, rc); - return SSH_PACKET_USED; - } - len = ssh_string_len(str); + return SSH_PACKET_USED; + } + is_stderr = 1; + data_type_code = ntohl(data_type_code); + if (data_type_code != SSH2_EXTENDED_DATA_STDERR) { + SSH_LOG(SSH_LOG_PACKET, "Invalid data type code %" PRIu32 "!", + data_type_code); + } + } - SSH_LOG(SSH_LOG_PACKET, - "Channel receiving %zu bytes data in %d (local win=%d remote win=%d)", - len, - is_stderr, - channel->local_window, - channel->remote_window); + str = ssh_buffer_get_ssh_string(packet); + if (str == NULL) { + SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!"); - /* What shall we do in this case? Let's accept it anyway */ - if (len > channel->local_window) { - SSH_LOG(SSH_LOG_RARE, - "Data packet too big for our window(%zu vs %d)", - len, - channel->local_window); - } + return SSH_PACKET_USED; + } + len = ssh_string_len(str); - if (channel_default_bufferize(channel, ssh_string_data(str), len, - is_stderr) < 0) { - SSH_STRING_FREE(str); + SSH_LOG(SSH_LOG_PACKET, + "Channel receiving %" PRIu32 " bytes data%s (local win=%" PRIu32 + " remote win=%" PRIu32 ")", + len, + is_stderr ? " in stderr" : "", + channel->local_window, + channel->remote_window); - return SSH_PACKET_USED; - } + if (len > channel->local_window) { + SSH_LOG(SSH_LOG_RARE, + "Data packet too big for our window(%" PRIu32 " vs %" PRIu32 ")", + len, + channel->local_window); + + SSH_STRING_FREE(str); + + ssh_set_error(session, SSH_FATAL, "Window exceeded"); + + return SSH_PACKET_USED; + } + + data = ssh_string_data(str); + if (channel_default_bufferize(channel, data, len, is_stderr) < 0) { + SSH_STRING_FREE(str); + + return SSH_PACKET_USED; + } - if (len <= channel->local_window) { channel->local_window -= len; - } else { - channel->local_window = 0; /* buggy remote */ - } - SSH_LOG(SSH_LOG_PACKET, - "Channel windows are now (local win=%d remote win=%d)", - channel->local_window, - channel->remote_window); + SSH_LOG(SSH_LOG_PACKET, + "Channel windows are now (local win=%" PRIu32 " remote win=%" PRIu32 ")", + channel->local_window, + channel->remote_window); - SSH_STRING_FREE(str); + SSH_STRING_FREE(str); - if (is_stderr) { - buf = channel->stderr_buffer; - } else { - buf = channel->stdout_buffer; - } + if (is_stderr) { + buf = channel->stderr_buffer; + } else { + buf = channel->stdout_buffer; + } - ssh_callbacks_iterate(channel->callbacks, - ssh_channel_callbacks, - channel_data_function) { - if (ssh_buffer_get(buf) == NULL) { - break; - } - rest = ssh_callbacks_iterate_exec(channel_data_function, - channel->session, - channel, - ssh_buffer_get(buf), - ssh_buffer_get_len(buf), - is_stderr); - if (rest > 0) { - if (channel->counter != NULL) { - channel->counter->in_bytes += rest; - } - ssh_buffer_pass_bytes(buf, rest); - } - } - ssh_callbacks_iterate_end(); + ssh_callbacks_iterate(channel->callbacks, + ssh_channel_callbacks, + channel_data_function) { + if (ssh_buffer_get(buf) == NULL) { + break; + } + rest = ssh_callbacks_iterate_exec(channel_data_function, + channel->session, + channel, + ssh_buffer_get(buf), + ssh_buffer_get_len(buf), + is_stderr); + if (rest > 0) { + int rc; + if (channel->counter != NULL) { + channel->counter->in_bytes += rest; + } + ssh_buffer_pass_bytes(buf, rest); - if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) { - if (grow_window(session, channel, 0) < 0) { - return -1; - } - } - return SSH_PACKET_USED; + rc = grow_window(session, channel); + if (rc == SSH_ERROR) { + return -1; + } + } + } + ssh_callbacks_iterate_end(); + + return SSH_PACKET_USED; } SSH_PACKET_CALLBACK(channel_rcv_eof) { @@ -636,7 +706,7 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) { } SSH_LOG(SSH_LOG_PACKET, - "Received eof on channel (%d:%d)", + "Received eof on channel (%" PRIu32 ":%" PRIu32 ")", channel->local_channel, channel->remote_channel); /* channel->remote_window = 0; */ @@ -681,7 +751,7 @@ SSH_PACKET_CALLBACK(channel_rcv_close) { } SSH_LOG(SSH_LOG_PACKET, - "Received close on channel (%d:%d)", + "Received close on channel (%" PRIu32 ":%" PRIu32 ")", channel->local_channel, channel->remote_channel); @@ -823,7 +893,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { } if(strcmp(request,"keepalive@openssh.com")==0){ SAFE_FREE(request); - SSH_LOG(SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive"); + SSH_LOG(SSH_LOG_DEBUG,"Responding to Openssh's keepalive"); rc = ssh_buffer_pack(session->out_buffer, "bd", @@ -841,7 +911,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { int status; SAFE_FREE(request); - SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request"); + SSH_LOG(SSH_LOG_DEBUG, "Received an auth-agent-req request"); status = SSH2_MSG_CHANNEL_FAILURE; ssh_callbacks_iterate(channel->callbacks, @@ -876,9 +946,9 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { */ ssh_message_handle_channel_request(session,channel,packet,request,want_reply); #else - SSH_LOG(SSH_LOG_WARNING, "Unhandled channel request %s", request); + SSH_LOG(SSH_LOG_DEBUG, "Unhandled channel request %s", request); #endif - + SAFE_FREE(request); return SSH_PACKET_USED; @@ -891,7 +961,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { * FIXME is the window changed? */ int channel_default_bufferize(ssh_channel channel, - void *data, size_t len, + void *data, uint32_t len, bool is_stderr) { ssh_session session; @@ -908,7 +978,7 @@ int channel_default_bufferize(ssh_channel channel, } SSH_LOG(SSH_LOG_PACKET, - "placing %zu bytes into channel buffer (%s)", + "placing %" PRIu32 " bytes into channel buffer (%s)", len, is_stderr ? "stderr" : "stdout"); if (!is_stderr) { @@ -963,14 +1033,15 @@ int channel_default_bufferize(ssh_channel channel, * @see ssh_channel_request_shell() * @see ssh_channel_request_exec() */ -int ssh_channel_open_session(ssh_channel channel) { - if(channel == NULL) { +int ssh_channel_open_session(ssh_channel channel) +{ + if (channel == NULL) { return SSH_ERROR; } return channel_open(channel, "session", - CHANNEL_INITIAL_WINDOW, + WINDOW_DEFAULT, CHANNEL_MAX_PACKET, NULL); } @@ -990,14 +1061,15 @@ int ssh_channel_open_session(ssh_channel channel) { * * @see ssh_channel_open_forward() */ -int ssh_channel_open_auth_agent(ssh_channel channel){ - if(channel == NULL) { +int ssh_channel_open_auth_agent(ssh_channel channel) +{ + if (channel == NULL) { return SSH_ERROR; } return channel_open(channel, "auth-agent@openssh.com", - CHANNEL_INITIAL_WINDOW, + WINDOW_DEFAULT, CHANNEL_MAX_PACKET, NULL); } @@ -1026,16 +1098,17 @@ int ssh_channel_open_auth_agent(ssh_channel channel){ * * @warning This function does not bind the local port and does not automatically * forward the content of a socket to the channel. You still have to - * use channel_read and channel_write for this. + * use ssh_channel_read and ssh_channel_write for this. */ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport) { + int remoteport, const char *sourcehost, int localport) +{ ssh_session session; ssh_buffer payload = NULL; ssh_string str = NULL; int rc = SSH_ERROR; - if(channel == NULL) { + if (channel == NULL) { return rc; } @@ -1065,7 +1138,7 @@ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, rc = channel_open(channel, "direct-tcpip", - CHANNEL_INITIAL_WINDOW, + WINDOW_DEFAULT, CHANNEL_MAX_PACKET, payload); @@ -1097,7 +1170,7 @@ error: * * @warning This function does not bind the local port and does not * automatically forward the content of a socket to the channel. - * You still have to use channel_read and channel_write for this. + * You still have to use ssh_channel_read and ssh_channel_write for this. * @warning Requires support of OpenSSH for UNIX domain socket forwarding. */ int ssh_channel_open_forward_unix(ssh_channel channel, @@ -1148,7 +1221,7 @@ int ssh_channel_open_forward_unix(ssh_channel channel, rc = channel_open(channel, "direct-streamlocal@openssh.com", - CHANNEL_INITIAL_WINDOW, + WINDOW_DEFAULT, CHANNEL_MAX_PACKET, payload); @@ -1202,7 +1275,7 @@ void ssh_channel_free(ssh_channel channel) channel->flags |= SSH_CHANNEL_FLAG_FREED_LOCAL; /* The idea behind the flags is the following : it is well possible - * that a client closes a channel that stills exists on the server side. + * that a client closes a channel that still exists on the server side. * We definitively close the channel when we receive a close message *and* * the user closed it. */ @@ -1295,7 +1368,7 @@ int ssh_channel_send_eof(ssh_channel channel) rc = ssh_packet_send(session); SSH_LOG(SSH_LOG_PACKET, - "Sent a EOF on client channel (%d:%d)", + "Sent a EOF on client channel (%" PRIu32 ":%" PRIu32 ")", channel->local_channel, channel->remote_channel); if (rc != SSH_OK) { @@ -1360,7 +1433,7 @@ int ssh_channel_close(ssh_channel channel) rc = ssh_packet_send(session); SSH_LOG(SSH_LOG_PACKET, - "Sent a close on client channel (%d:%d)", + "Sent a close on client channel (%" PRIu32 ":%" PRIu32 ")", channel->local_channel, channel->remote_channel); @@ -1382,7 +1455,8 @@ error: } /* this termination function waits for a window growing condition */ -static int ssh_channel_waitwindow_termination(void *c){ +static int ssh_channel_waitwindow_termination(void *c) +{ ssh_channel channel = (ssh_channel) c; if (channel->remote_window > 0 || channel->session->session_state == SSH_SESSION_STATE_ERROR || @@ -1395,7 +1469,8 @@ static int ssh_channel_waitwindow_termination(void *c){ /* This termination function waits until the session is not in blocked status * anymore, e.g. because of a key re-exchange. */ -static int ssh_waitsession_unblocked(void *s){ +static int ssh_waitsession_unblocked(void *s) +{ ssh_session session = (ssh_session)s; switch (session->session_state){ case SSH_SESSION_STATE_DH: @@ -1415,8 +1490,9 @@ static int ssh_waitsession_unblocked(void *s){ * SSH_ERROR On error. * SSH_AGAIN Timeout elapsed (or in nonblocking mode). */ -int ssh_channel_flush(ssh_channel channel){ - return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT); +int ssh_channel_flush(ssh_channel channel) +{ + return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT); } static int channel_write_common(ssh_channel channel, @@ -1426,7 +1502,6 @@ static int channel_write_common(ssh_channel channel, ssh_session session; uint32_t origlen = len; size_t effectivelen; - size_t maxpacketlen; int rc; if(channel == NULL) { @@ -1439,20 +1514,14 @@ static int channel_write_common(ssh_channel channel, } if (len > INT_MAX) { - SSH_LOG(SSH_LOG_PROTOCOL, - "Length (%u) is bigger than INT_MAX", len); + SSH_LOG(SSH_LOG_TRACE, + "Length (%" PRIu32 ") is bigger than INT_MAX", len); return SSH_ERROR; } - /* - * Handle the max packet len from remote side, be nice - * 10 bytes for the headers - */ - maxpacketlen = channel->remote_maxpacket - 10; - if (channel->local_eof) { ssh_set_error(session, SSH_REQUEST_DENIED, - "Can't write to channel %d:%d after EOF was sent", + "Can't write to channel %" PRIu32 ":%" PRIu32 " after EOF was sent", channel->local_channel, channel->remote_channel); return -1; @@ -1476,14 +1545,14 @@ static int channel_write_common(ssh_channel channel, } while (len > 0) { if (channel->remote_window < len) { - SSH_LOG(SSH_LOG_PROTOCOL, - "Remote window is %d bytes. going to write %d bytes", + SSH_LOG(SSH_LOG_DEBUG, + "Remote window is %" PRIu32 " bytes. going to write %" PRIu32 " bytes", channel->remote_window, len); - /* What happens when the channel window is zero? */ + /* When the window is zero, wait for it to grow */ if(channel->remote_window == 0) { /* nothing can be written */ - SSH_LOG(SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_DEBUG, "Wait for a growing window message..."); rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, ssh_channel_waitwindow_termination,channel); @@ -1494,12 +1563,17 @@ static int channel_write_common(ssh_channel channel, goto out; continue; } + /* When the window is non-zero, accept data up to the window size */ effectivelen = MIN(len, channel->remote_window); } else { effectivelen = len; } - effectivelen = MIN(effectivelen, maxpacketlen);; + /* + * Like OpenSSH, don't subtract bytes for the header fields + * and allow to send a payload of remote_maxpacket length. + */ + effectivelen = MIN(effectivelen, channel->remote_maxpacket); rc = ssh_buffer_pack(session->out_buffer, "bd", @@ -1537,7 +1611,7 @@ static int channel_write_common(ssh_channel channel, } SSH_LOG(SSH_LOG_PACKET, - "channel_write wrote %ld bytes", (long int) effectivelen); + "ssh_channel_write wrote %ld bytes", (long int) effectivelen); channel->remote_window -= effectivelen; len -= effectivelen; @@ -1565,7 +1639,7 @@ error: /** * @brief Get the remote window size. * - * This is the maximum amounts of bytes the remote side expects us to send + * This is the maximum amount of bytes the remote side expects us to send * before growing the window again. * * @param[in] channel The channel to query. @@ -1579,7 +1653,8 @@ error: * @warning A zero return value means ssh_channel_write (default settings) * will block until the window grows back. */ -uint32_t ssh_channel_window_size(ssh_channel channel) { +uint32_t ssh_channel_window_size(ssh_channel channel) +{ return channel->remote_window; } @@ -1596,8 +1671,9 @@ uint32_t ssh_channel_window_size(ssh_channel channel) { * * @see ssh_channel_read() */ -int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) { - return channel_write_common(channel, data, len, 0); +int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) +{ + return channel_write_common(channel, data, len, 0); } /** @@ -1609,8 +1685,9 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) { * * @see ssh_channel_is_closed() */ -int ssh_channel_is_open(ssh_channel channel) { - if(channel == NULL) { +int ssh_channel_is_open(ssh_channel channel) +{ + if (channel == NULL) { return 0; } return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0); @@ -1625,8 +1702,9 @@ int ssh_channel_is_open(ssh_channel channel) { * * @see ssh_channel_is_open() */ -int ssh_channel_is_closed(ssh_channel channel) { - if(channel == NULL) { +int ssh_channel_is_closed(ssh_channel channel) +{ + if (channel == NULL) { return SSH_ERROR; } return (channel->state != SSH_CHANNEL_STATE_OPEN || channel->session->alive == 0); @@ -1639,15 +1717,16 @@ int ssh_channel_is_closed(ssh_channel channel) { * * @return 0 if there is no EOF, nonzero otherwise. */ -int ssh_channel_is_eof(ssh_channel channel) { - if(channel == NULL) { - return SSH_ERROR; - } - if (ssh_channel_has_unread_data(channel)) { - return 0; - } +int ssh_channel_is_eof(ssh_channel channel) +{ + if (channel == NULL) { + return SSH_ERROR; + } + if (ssh_channel_has_unread_data(channel)) { + return 0; + } - return (channel->remote_eof != 0); + return (channel->remote_eof != 0); } /** @@ -1661,11 +1740,12 @@ int ssh_channel_is_eof(ssh_channel channel) { * in non-blocking mode. * @see ssh_set_blocking() */ -void ssh_channel_set_blocking(ssh_channel channel, int blocking) { - if(channel == NULL) { - return; - } - ssh_set_blocking(channel->session,blocking); +void ssh_channel_set_blocking(ssh_channel channel, int blocking) +{ + if (channel == NULL) { + return; + } + ssh_set_blocking(channel->session, blocking); } /** @@ -1685,7 +1765,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){ } SSH_LOG(SSH_LOG_PACKET, - "Received SSH_CHANNEL_SUCCESS on channel (%d:%d)", + "Received SSH_CHANNEL_SUCCESS on channel (%" PRIu32 ":%" PRIu32 ")", channel->local_channel, channel->remote_channel); if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ @@ -1693,6 +1773,12 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){ channel->request_state); } else { channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED; + + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_request_response_function, + channel->session, + channel); } return SSH_PACKET_USED; @@ -1716,7 +1802,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_failure){ } SSH_LOG(SSH_LOG_PACKET, - "Received SSH_CHANNEL_FAILURE on channel (%d:%d)", + "Received SSH_CHANNEL_FAILURE on channel (%" PRIu32 ":%" PRIu32 ")", channel->local_channel, channel->remote_channel); if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ @@ -1724,12 +1810,19 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_failure){ channel->request_state); } else { channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED; + + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_request_response_function, + channel->session, + channel); } return SSH_PACKET_USED; } -static int ssh_channel_request_termination(void *c){ +static int ssh_channel_request_termination(void *c) +{ ssh_channel channel = (ssh_channel)c; if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING || channel->session->session_state == SSH_SESSION_STATE_ERROR) @@ -1739,7 +1832,8 @@ static int ssh_channel_request_termination(void *c){ } static int channel_request(ssh_channel channel, const char *request, - ssh_buffer buffer, int reply) { + ssh_buffer buffer, int reply) +{ ssh_session session = channel->session; int rc = SSH_ERROR; int ret; @@ -1800,7 +1894,7 @@ pending: rc=SSH_ERROR; break; case SSH_CHANNEL_REQ_STATE_ACCEPTED: - SSH_LOG(SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_DEBUG, "Channel request %s success",request); rc=SSH_OK; break; @@ -1825,7 +1919,7 @@ error: /** * @brief Request a pty with a specific type and size. * - * @param[in] channel The channel to sent the request. + * @param[in] channel The channel to send the request. * * @param[in] terminal The terminal type ("vt100, xterm,..."). * @@ -1833,13 +1927,18 @@ error: * * @param[in] row The number of rows. * + * @param[in] modes Encoded SSH terminal modes for the PTY + * + * @param[in] modes_len Number of bytes in 'modes' + * * @return SSH_OK on success, * SSH_ERROR if an error occurred, * SSH_AGAIN if in nonblocking mode and call has * to be done again. */ -int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, - int col, int row) { +int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *terminal, + int col, int row, const unsigned char* modes, size_t modes_len) +{ ssh_session session; ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -1868,14 +1967,14 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, } rc = ssh_buffer_pack(buffer, - "sdddddb", + "sdddddP", terminal, col, row, 0, /* pix */ 0, /* pix */ - 1, /* add a 0byte string */ - 0); + (uint32_t)modes_len, + modes_len, modes); if (rc != SSH_OK) { ssh_set_error_oom(session); @@ -1889,6 +1988,23 @@ error: return rc; } +int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, + int col, int row) +{ + /* use modes from the current TTY */ + unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE]; + int rc = encode_current_tty_opts(modes_buf, sizeof(modes_buf)); + if (rc < 0) { + return rc; + } + return ssh_channel_request_pty_size_modes(channel, + terminal, + col, + row, + modes_buf, + (size_t)rc); +} + /** * @brief Request a PTY. * @@ -1901,7 +2017,8 @@ error: * * @see ssh_channel_request_pty_size() */ -int ssh_channel_request_pty(ssh_channel channel) { +int ssh_channel_request_pty(ssh_channel channel) +{ return ssh_channel_request_pty_size(channel, "xterm", 80, 24); } @@ -1917,10 +2034,11 @@ int ssh_channel_request_pty(ssh_channel channel) { * @return SSH_OK on success, SSH_ERROR if an error occurred. * * @warning Do not call it from a signal handler if you are not sure any other - * libssh function using the same channel/session is running at same - * time (not 100% threadsafe). + * libssh function using the same channel/session is running at the + * same time (not 100% threadsafe). */ -int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) { +int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) +{ ssh_session session = channel->session; ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -1959,8 +2077,9 @@ error: * SSH_AGAIN if in nonblocking mode and call has * to be done again. */ -int ssh_channel_request_shell(ssh_channel channel) { - if(channel == NULL) { +int ssh_channel_request_shell(ssh_channel channel) +{ + if (channel == NULL) { return SSH_ERROR; } @@ -1981,7 +2100,8 @@ int ssh_channel_request_shell(ssh_channel channel) { * * @warning You normally don't have to call it for sftp, see sftp_new(). */ -int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) { +int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) +{ ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -2030,14 +2150,16 @@ error: * * @note You should use sftp_new() which does this for you. */ -int ssh_channel_request_sftp( ssh_channel channel){ +int ssh_channel_request_sftp( ssh_channel channel) +{ if(channel == NULL) { return SSH_ERROR; } return ssh_channel_request_subsystem(channel, "sftp"); } -static char *generate_cookie(void) { +static char *generate_cookie(void) +{ static const char *hex = "0123456789abcdef"; char s[36]; unsigned char rnd[16]; @@ -2061,7 +2183,7 @@ static char *generate_cookie(void) { * @brief Sends the "x11-req" channel request over an existing session channel. * * This will enable redirecting the display of the remote X11 applications to - * local X server over an secure tunnel. + * local X server over a secure tunnel. * * @param[in] channel An existing session channel where the remote X11 * applications are going to be executed. @@ -2083,7 +2205,8 @@ static char *generate_cookie(void) { * to be done again. */ int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, - const char *cookie, int screen_number) { + const char *cookie, int screen_number) +{ ssh_buffer buffer = NULL; char *c = NULL; int rc = SSH_ERROR; @@ -2134,7 +2257,8 @@ error: } static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, - int timeout_ms, int *destination_port) { + int timeout_ms, int *destination_port, char **originator, int *originator_port) +{ #ifndef _WIN32 static const struct timespec ts = { .tv_sec = 0, @@ -2168,6 +2292,12 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, if(destination_port) { *destination_port=msg->channel_request_open.destination_port; } + if(originator) { + *originator=strdup(msg->channel_request_open.originator); + } + if(originator_port) { + *originator_port=msg->channel_request_open.originator_port; + } ssh_message_free(msg); return channel; @@ -2198,8 +2328,9 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, * @return A newly created channel, or NULL if no X11 request from * the server. */ -ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) { - return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL); +ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) +{ + return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL, NULL, NULL); } /** @@ -2268,7 +2399,8 @@ SSH_PACKET_CALLBACK(ssh_request_denied){ } -static int ssh_global_request_termination(void *s){ +static int ssh_global_request_termination(void *s) +{ ssh_session session = (ssh_session) s; if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING || session->session_state == SSH_SESSION_STATE_ERROR) @@ -2357,7 +2489,7 @@ pending: } switch(session->global_req_state){ case SSH_CHANNEL_REQ_STATE_ACCEPTED: - SSH_LOG(SSH_LOG_PROTOCOL, "Global request %s success",request); + SSH_LOG(SSH_LOG_DEBUG, "Global request %s success",request); rc=SSH_OK; break; case SSH_CHANNEL_REQ_STATE_DENIED: @@ -2447,18 +2579,21 @@ error: } /* DEPRECATED */ -int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port) { +int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port) +{ return ssh_channel_listen_forward(session, address, port, bound_port); } /* DEPRECATED */ -ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { - return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL); +ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) +{ + return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL, NULL, NULL); } /** - * @brief Accept an incoming TCP/IP forwarding channel and get information - * about incomming connection + * @brief Accept an incoming TCP/IP forwarding channel and get some information + * about incoming connection + * * @param[in] session The ssh session to use. * * @param[in] timeout_ms A timeout in milliseconds. @@ -2469,7 +2604,31 @@ ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { * the server */ ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int* destination_port) { - return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port); + return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port, NULL, NULL); +} + +/** + * @brief Accept an incoming TCP/IP forwarding channel and get information + * about incoming connection + * + * @param[in] session The ssh session to use. + * + * @param[in] timeout_ms A timeout in milliseconds. + * + * @param[out] destination_port A pointer to destination port or NULL. + * + * @param[out] originator A pointer to a pointer to a string of originator host or NULL. + * That the caller is responsible for to ssh_string_free_char(). + * + * @param[out] originator_port A pointer to originator port or NULL. + * + * @return Newly created channel, or NULL if no incoming channel request from + * the server + * + * @see ssh_string_free_char() + */ +ssh_channel ssh_channel_open_forward_port(ssh_session session, int timeout_ms, int *destination_port, char **originator, int *originator_port) { + return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port, originator, originator_port); } /** @@ -2519,7 +2678,8 @@ error: } /* DEPRECATED */ -int ssh_forward_cancel(ssh_session session, const char *address, int port) { +int ssh_forward_cancel(ssh_session session, const char *address, int port) +{ return ssh_channel_cancel_forward(session, address, port); } @@ -2538,7 +2698,8 @@ int ssh_forward_cancel(ssh_session session, const char *address, int port) { * to be done again. * @warning Some environment variables may be refused by security reasons. */ -int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) { +int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) +{ ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -2608,7 +2769,8 @@ error: * * @see ssh_channel_request_shell() */ -int ssh_channel_request_exec(ssh_channel channel, const char *cmd) { +int ssh_channel_request_exec(ssh_channel channel, const char *cmd) +{ ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -2652,10 +2814,6 @@ error: * Sends a signal 'sig' to the remote process. * Note, that remote system may not support signals concept. * In such a case this request will be silently ignored. - * Only SSH-v2 is supported (I'm not sure about SSH-v1). - * - * OpenSSH doesn't support signals yet, see: - * https://bugzilla.mindrot.org/show_bug.cgi?id=1424 * * @param[in] channel The channel to send signal. * @@ -2675,17 +2833,17 @@ error: * SIGUSR1 -> USR1 \n * SIGUSR2 -> USR2 \n * - * @return SSH_OK on success, SSH_ERROR if an error occurred - * (including attempts to send signal via SSH-v1 session). + * @return SSH_OK on success, SSH_ERROR if an error occurred. */ -int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) { +int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) +{ ssh_buffer buffer = NULL; int rc = SSH_ERROR; - if(channel == NULL) { + if (channel == NULL) { return SSH_ERROR; } - if(sig == NULL) { + if (sig == NULL) { ssh_set_error_invalid(channel->session); return rc; } @@ -2715,16 +2873,15 @@ error: * Sends a break signal to the remote process. * Note, that remote system may not support breaks. * In such a case this request will be silently ignored. - * Only SSH-v2 is supported. * * @param[in] channel The channel to send the break to. * * @param[in] length The break-length in milliseconds to send. * * @return SSH_OK on success, SSH_ERROR if an error occurred - * (including attempts to send signal via SSH-v1 session). */ -int ssh_channel_request_send_break(ssh_channel channel, uint32_t length) { +int ssh_channel_request_send_break(ssh_channel channel, uint32_t length) +{ ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -2757,7 +2914,7 @@ error: * * @param[in] channel The channel to read from. * - * @param[in] buffer The buffer which will get the data. + * @param[out] buffer The buffer which will get the data. * * @param[in] count The count of bytes to be read. If it is bigger than 0, * the exact size will be read, else (bytes=0) it will @@ -2772,7 +2929,8 @@ error: * @see ssh_channel_read */ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, - int is_stderr) { + int is_stderr) +{ ssh_session session; char *buffer_tmp = NULL; int r; @@ -2843,13 +3001,13 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, struct ssh_channel_read_termination_struct { ssh_channel channel; - uint32_t count; ssh_buffer buffer; }; -static int ssh_channel_read_termination(void *s){ +static int ssh_channel_read_termination(void *s) +{ struct ssh_channel_read_termination_struct *ctx = s; - if (ssh_buffer_get_len(ctx->buffer) >= ctx->count || + if (ssh_buffer_get_len(ctx->buffer) >= 1 || ctx->channel->remote_eof || ctx->channel->session->session_state == SSH_SESSION_STATE_ERROR) return 1; @@ -2857,27 +3015,25 @@ static int ssh_channel_read_termination(void *s){ return 0; } -/* TODO FIXME Fix the blocking behaviours */ +/* TODO: FIXME Fix the blocking behaviours */ /** * @brief Reads data from a channel. * * @param[in] channel The channel to read from. * - * @param[in] dest The destination buffer which will get the data. + * @param[out] dest The destination buffer which will get the data. * * @param[in] count The count of bytes to be read. * * @param[in] is_stderr A boolean value to mark reading from the stderr flow. * * @return The number of bytes read, 0 on end of file or SSH_ERROR - * on error. In nonblocking mode it Can return 0 if no data + * on error. In nonblocking mode it can return 0 if no data * is available or SSH_AGAIN. * * @warning This function may return less than count bytes of data, and won't * block until count bytes have been read. - * @warning The read function using a buffer has been renamed to - * channel_read_buffer(). */ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr) { @@ -2893,7 +3049,7 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std * * @param[in] channel The channel to read from. * - * @param[in] dest The destination buffer which will get the data. + * @param[out] dest The destination buffer which will get the data. * * @param[in] count The count of bytes to be read. * @@ -2908,8 +3064,6 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std * * @warning This function may return less than count bytes of data, and won't * block until count bytes have been read. - * @warning The read function using a buffer has been renamed to - * channel_read_buffer(). */ int ssh_channel_read_timeout(ssh_channel channel, void *dest, @@ -2942,28 +3096,17 @@ int ssh_channel_read_timeout(ssh_channel channel, stdbuf=channel->stderr_buffer; } - /* - * We may have problem if the window is too small to accept as much data - * as asked - */ SSH_LOG(SSH_LOG_PACKET, - "Read (%d) buffered : %d bytes. Window: %d", + "Read (%" PRIu32 ") buffered : %" PRIu32 " bytes. Window: %" PRIu32, count, ssh_buffer_get_len(stdbuf), channel->local_window); - if (count > ssh_buffer_get_len(stdbuf) + channel->local_window) { - if (grow_window(session, channel, count - ssh_buffer_get_len(stdbuf)) < 0) { - return -1; - } - } - /* block reading until at least one byte has been read * and ignore the trivial case count=0 */ ctx.channel = channel; ctx.buffer = stdbuf; - ctx.count = 1; if (timeout_ms < SSH_TIMEOUT_DEFAULT) { timeout_ms = SSH_TIMEOUT_INFINITE; @@ -3005,11 +3148,10 @@ int ssh_channel_read_timeout(ssh_channel channel, if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) { channel->state = SSH_CHANNEL_STATE_CLOSED; } - /* Authorize some buffering while userapp is busy */ - if (channel->local_window < WINDOWLIMIT) { - if (grow_window(session, channel, 0) < 0) { - return -1; - } + + rc = grow_window(session, channel); + if (rc == SSH_ERROR) { + return -1; } return len; @@ -3019,20 +3161,18 @@ int ssh_channel_read_timeout(ssh_channel channel, * @brief Do a nonblocking read on the channel. * * A nonblocking read on the specified channel. it will return <= count bytes of - * data read atomically. + * data read atomically. It will also trigger any callbacks set on the channel. * * @param[in] channel The channel to read from. * - * @param[in] dest A pointer to a destination buffer. + * @param[out] dest A pointer to a destination buffer. * * @param[in] count The count of bytes of data to be read. * * @param[in] is_stderr A boolean to select the stderr stream. * - * @return The number of bytes read, 0 if nothing is available or - * SSH_ERROR on error. - * - * @warning Don't forget to check for EOF as it would return 0 here. + * @return The number of bytes read (0 if nothing is available), + * SSH_ERROR on error, and SSH_EOF if the channel is EOF. * * @see ssh_channel_is_eof() */ @@ -3042,7 +3182,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel, int is_stderr) { ssh_session session; - ssize_t to_read; + uint32_t to_read; int rc; int blocking; @@ -3056,22 +3196,24 @@ int ssh_channel_read_nonblocking(ssh_channel channel, session = channel->session; - to_read = ssh_channel_poll(channel, is_stderr); + rc = ssh_channel_poll(channel, is_stderr); - if (to_read <= 0) { + if (rc <= 0) { if (session->session_state == SSH_SESSION_STATE_ERROR){ return SSH_ERROR; } - return to_read; /* may be an error code */ + return rc; /* may be an error code */ } - if ((size_t)to_read > count) { - to_read = (ssize_t)count; + to_read = (unsigned int)rc; + + if (to_read > count) { + to_read = count; } blocking = ssh_is_blocking(session); ssh_set_blocking(session, 0); - rc = ssh_channel_read(channel, dest, (uint32_t)to_read, is_stderr); + rc = ssh_channel_read(channel, dest, to_read, is_stderr); ssh_set_blocking(session,blocking); return rc; @@ -3086,15 +3228,18 @@ int ssh_channel_read_nonblocking(ssh_channel channel, * * @return The number of bytes available for reading, 0 if nothing * is available or SSH_ERROR on error. + * When a channel is freed the function returns + * SSH_ERROR immediately. * * @warning When the channel is in EOF state, the function returns SSH_EOF. * * @see ssh_channel_is_eof() */ -int ssh_channel_poll(ssh_channel channel, int is_stderr){ +int ssh_channel_poll(ssh_channel channel, int is_stderr) +{ ssh_buffer stdbuf; - if(channel == NULL) { + if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) { return SSH_ERROR; } @@ -3140,6 +3285,7 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){ * SSH_ERROR on error. * * @warning When the channel is in EOF state, the function returns SSH_EOF. + * When a channel is freed the function returns SSH_ERROR immediately. * * @see ssh_channel_is_eof() */ @@ -3151,7 +3297,7 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr) size_t len; int rc; - if (channel == NULL) { + if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) { return SSH_ERROR; } @@ -3163,7 +3309,6 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr) } ctx.buffer = stdbuf; ctx.channel = channel; - ctx.count = 1; rc = ssh_handle_packets_termination(channel->session, timeout, ssh_channel_read_termination, @@ -3203,15 +3348,17 @@ out: * * @return The session pointer. */ -ssh_session ssh_channel_get_session(ssh_channel channel) { - if(channel == NULL) { +ssh_session ssh_channel_get_session(ssh_channel channel) +{ + if (channel == NULL) { return NULL; } return channel->session; } -static int ssh_channel_exit_status_termination(void *c){ +static int ssh_channel_exit_status_termination(void *c) +{ ssh_channel channel = c; if(channel->exit_status != -1 || /* When a channel is closed, no exit status message can @@ -3233,15 +3380,18 @@ static int ssh_channel_exit_status_termination(void *c){ * (yet), or SSH_ERROR on error. * @warning This function may block until a timeout (or never) * if the other side is not willing to close the channel. + * When a channel is freed the function returns + * SSH_ERROR immediately. * * If you're looking for an async handling of this register a callback for the * exit status. * * @see ssh_channel_exit_status_callback */ -int ssh_channel_get_exit_status(ssh_channel channel) { +int ssh_channel_get_exit_status(ssh_channel channel) +{ int rc; - if(channel == NULL) { + if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) { return SSH_ERROR; } rc = ssh_handle_packets_termination(channel->session, @@ -3263,8 +3413,11 @@ int ssh_channel_get_exit_status(ssh_channel channel) { * This is made in two parts: protocol select and network select. The protocol * select does not use the network functions at all */ -static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans, - ssh_channel *echans, ssh_channel *rout, ssh_channel *wout, ssh_channel *eout) { +static int +channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans, + ssh_channel *echans, ssh_channel *rout, + ssh_channel *wout, ssh_channel *eout) +{ ssh_channel chan; int i; int j = 0; @@ -3344,7 +3497,8 @@ static size_t count_ptrs(ssh_channel *ptrs) * function, or SSH_ERROR on error. */ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, - ssh_channel *exceptchans, struct timeval * timeout) { + ssh_channel *exceptchans, struct timeval * timeout) +{ ssh_channel *rchans, *wchans, *echans; ssh_channel dummy = NULL; ssh_event event = NULL; @@ -3448,9 +3602,15 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, firstround=0; } while(1); - memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel )); - memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel )); - memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel )); + if (readchans != &dummy) { + memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel)); + } + if (writechans != &dummy) { + memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel)); + } + if (exceptchans != &dummy) { + memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel)); + } SAFE_FREE(rchans); SAFE_FREE(wchans); SAFE_FREE(echans); @@ -3478,7 +3638,8 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, * @param[in] counter Counter for bytes handled by the channel. */ void ssh_channel_set_counter(ssh_channel channel, - ssh_counter counter) { + ssh_counter counter) +{ if (channel != NULL) { channel->counter = counter; } @@ -3497,8 +3658,9 @@ void ssh_channel_set_counter(ssh_channel channel, * * @see ssh_channel_read() */ -int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) { - return channel_write_common(channel, data, len, 1); +int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) +{ + return channel_write_common(channel, data, len, 1); } #if WITH_SERVER @@ -3525,10 +3687,11 @@ int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len * * @warning This function does not bind the local port and does not automatically * forward the content of a socket to the channel. You still have to - * use channel_read and channel_write for this. + * use ssh_channel_read and ssh_channel_write for this. */ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport) { + int remoteport, const char *sourcehost, int localport) +{ ssh_session session; ssh_buffer payload = NULL; int rc = SSH_ERROR; @@ -3563,7 +3726,7 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost pending: rc = channel_open(channel, "forwarded-tcpip", - CHANNEL_INITIAL_WINDOW, + WINDOW_DEFAULT, CHANNEL_MAX_PACKET, payload); @@ -3588,10 +3751,11 @@ error: * to be done again. * @warning This function does not bind the local port and does not automatically * forward the content of a socket to the channel. You still have to - * use channel_read and channel_write for this. + * use shh_channel_read and ssh_channel_write for this. */ -int ssh_channel_open_x11(ssh_channel channel, - const char *orig_addr, int orig_port) { +int ssh_channel_open_x11(ssh_channel channel, + const char *orig_addr, int orig_port) +{ ssh_session session; ssh_buffer payload = NULL; int rc = SSH_ERROR; @@ -3625,7 +3789,7 @@ int ssh_channel_open_x11(ssh_channel channel, pending: rc = channel_open(channel, "x11", - CHANNEL_INITIAL_WINDOW, + WINDOW_DEFAULT, CHANNEL_MAX_PACKET, payload); @@ -3640,16 +3804,15 @@ error: * * Sends the exit status to the remote process (as described in RFC 4254, * section 6.10). - * Only SSH-v2 is supported (I'm not sure about SSH-v1). * * @param[in] channel The channel to send exit status. * * @param[in] exit_status The exit status to send * * @return SSH_OK on success, SSH_ERROR if an error occurred. - * (including attempts to send exit status via SSH-v1 session). */ -int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) { +int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) +{ ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -3681,7 +3844,6 @@ error: * This sends the exit status of the remote process. * Note, that remote system may not support signals concept. * In such a case this request will be silently ignored. - * Only SSH-v2 is supported (I'm not sure about SSH-v1). * * @param[in] channel The channel to send signal. * @@ -3692,10 +3854,10 @@ error: * @param[in] lang The language used in the message (format: RFC 3066) * * @return SSH_OK on success, SSH_ERROR if an error occurred - * (including attempts to send signal via SSH-v1 session). */ int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig, - int core, const char *errmsg, const char *lang) { + int core, const char *errmsg, const char *lang) +{ ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -3732,4 +3894,4 @@ error: #endif -/* @} */ +/** @} */ |