aboutsummaryrefslogtreecommitdiff
path: root/src/channels.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/channels.c')
-rw-r--r--src/channels.c792
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
-/* @} */
+/** @} */