diff options
Diffstat (limited to 'libs/libssh2/src/channel.c')
-rw-r--r-- | libs/libssh2/src/channel.c | 574 |
1 files changed, 456 insertions, 118 deletions
diff --git a/libs/libssh2/src/channel.c b/libs/libssh2/src/channel.c index 7bbeeb88f6..c35658a04e 100644 --- a/libs/libssh2/src/channel.c +++ b/libs/libssh2/src/channel.c @@ -1,7 +1,6 @@ -/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org> - * Copyright (c) 2005 Mikhail Gusarov <dottedmag@dottedmag.net> - * Copyright (c) 2008-2019 by Daniel Stenberg - * +/* Copyright (C) Sara Golemon <sarag@libssh2.org> + * Copyright (C) Mikhail Gusarov <dottedmag@dottedmag.net> + * Copyright (C) Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, @@ -36,16 +35,19 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-3-Clause */ #include "libssh2_priv.h" + #ifdef HAVE_UNISTD_H #include <unistd.h> #endif -#include <fcntl.h> #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif + #include <assert.h> #include "channel.h" @@ -81,8 +83,8 @@ _libssh2_channel_nextid(LIBSSH2_SESSION * session) * told... */ session->next_channel = id + 1; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Allocated new channel ID#%lu", - id); + _libssh2_debug((session, LIBSSH2_TRACE_CONN, + "Allocated new channel ID#%lu", id)); return id; } @@ -154,9 +156,9 @@ _libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, memset(&session->open_packet_requirev_state, 0, sizeof(session->open_packet_requirev_state)); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Opening Channel - win %d pack %d", window_size, - packet_size); + packet_size)); session->open_channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL)); if(!session->open_channel) { @@ -236,6 +238,7 @@ _libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, return NULL; } else if(rc) { + _libssh2_error(session, rc, "Unexpected error"); goto channel_error; } @@ -261,7 +264,7 @@ _libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, _libssh2_ntohu32(session->open_data + 9); session->open_channel->local.packet_size = _libssh2_ntohu32(session->open_data + 13); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Connection Established - ID: %lu/%lu win: %lu/%lu" " pack: %lu/%lu", session->open_channel->local.id, @@ -269,7 +272,7 @@ _libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, session->open_channel->local.window_size, session->open_channel->remote.window_size, session->open_channel->local.packet_size, - session->open_channel->remote.packet_size); + session->open_channel->remote.packet_size)); LIBSSH2_FREE(session, session->open_packet); session->open_packet = NULL; LIBSSH2_FREE(session, session->open_data); @@ -307,7 +310,7 @@ _libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, } } - channel_error: +channel_error: if(session->open_data) { LIBSSH2_FREE(session, session->open_data); @@ -326,14 +329,14 @@ _libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, /* Clear out packets meant for this channel */ _libssh2_htonu32(channel_id, session->open_channel->local.id); while((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA, - &session->open_data, - &session->open_data_len, 1, - channel_id, 4) >= 0) - || - (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, - &session->open_data, - &session->open_data_len, 1, - channel_id, 4) >= 0)) { + &session->open_data, + &session->open_data_len, 1, + channel_id, 4) >= 0) + || + (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, + &session->open_data, + &session->open_data_len, 1, + channel_id, 4) >= 0)) { LIBSSH2_FREE(session, session->open_data); session->open_data = NULL; } @@ -389,9 +392,9 @@ channel_direct_tcpip(LIBSSH2_SESSION * session, const char *host, session->direct_message_len = session->direct_host_len + session->direct_shost_len + 16; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Requesting direct-tcpip session from %s:%d to %s:%d", - shost, sport, host, port); + shost, sport, host, port)); s = session->direct_message = LIBSSH2_ALLOC(session, session->direct_message_len); @@ -452,6 +455,85 @@ libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host, } /* + * libssh2_channel_direct_streamlocal_ex + * + * Tunnel TCP/IP connect through the SSH session to direct UNIX socket + */ +static LIBSSH2_CHANNEL * +channel_direct_streamlocal(LIBSSH2_SESSION * session, const char *socket_path, + const char *shost, int sport) +{ + LIBSSH2_CHANNEL *channel; + unsigned char *s; + + if(session->direct_state == libssh2_NB_state_idle) { + session->direct_host_len = strlen(socket_path); + session->direct_shost_len = strlen(shost); + session->direct_message_len = + session->direct_host_len + session->direct_shost_len + 12; + + _libssh2_debug((session, LIBSSH2_TRACE_CONN, + "Requesting direct-streamlocal session to %s", + socket_path)); + + s = session->direct_message = + LIBSSH2_ALLOC(session, session->direct_message_len); + if(!session->direct_message) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for direct-streamlocal connection"); + return NULL; + } + _libssh2_store_str(&s, socket_path, session->direct_host_len); + _libssh2_store_str(&s, shost, session->direct_shost_len); + _libssh2_store_u32(&s, sport); + } + + channel = + _libssh2_channel_open(session, "direct-streamlocal@openssh.com", + sizeof("direct-streamlocal@openssh.com") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, + LIBSSH2_CHANNEL_PACKET_DEFAULT, + session->direct_message, + session->direct_message_len); + + if(!channel && + libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { + /* The error code is still set to LIBSSH2_ERROR_EAGAIN, set our state + to created to avoid re-creating the package on next invoke */ + session->direct_state = libssh2_NB_state_created; + return NULL; + } + /* by default we set (keep?) idle state... */ + session->direct_state = libssh2_NB_state_idle; + + LIBSSH2_FREE(session, session->direct_message); + session->direct_message = NULL; + + return channel; +} + +/* + * libssh2_channel_direct_streamlocal_ex + * + * Tunnel TCP/IP connect through the SSH session to direct UNIX socket + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_channel_direct_streamlocal_ex(LIBSSH2_SESSION * session, + const char *socket_path, + const char *shost, int sport) +{ + LIBSSH2_CHANNEL *ptr; + + if(!session) + return NULL; + + BLOCK_ADJUST_ERRNO(ptr, session, + channel_direct_streamlocal(session, + socket_path, shost, sport)); + return ptr; +} + +/* * channel_forward_listen * * Bind a port on the remote host and listen for connections @@ -469,19 +551,20 @@ channel_forward_listen(LIBSSH2_SESSION * session, const char *host, host = "0.0.0.0"; if(session->fwdLstn_state == libssh2_NB_state_idle) { - session->fwdLstn_host_len = strlen(host); + session->fwdLstn_host_len = (uint32_t)strlen(host); /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ session->fwdLstn_packet_len = - session->fwdLstn_host_len + (sizeof("tcpip-forward") - 1) + 14; + session->fwdLstn_host_len + + (uint32_t)(sizeof("tcpip-forward") - 1) + 14; /* Zero the whole thing out */ memset(&session->fwdLstn_packet_requirev_state, 0, sizeof(session->fwdLstn_packet_requirev_state)); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Requesting tcpip-forward session for %s:%d", host, - port); + port)); s = session->fwdLstn_packet = LIBSSH2_ALLOC(session, session->fwdLstn_packet_len); @@ -566,10 +649,10 @@ channel_forward_listen(LIBSSH2_SESSION * session, const char *host, listener->host[session->fwdLstn_host_len] = 0; if(data_len >= 5 && !port) { listener->port = _libssh2_ntohu32(data + 1); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Dynamic tcpip-forward port " "allocated: %d", - listener->port); + listener->port)); } else listener->port = port; @@ -646,9 +729,9 @@ int _libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener) int retcode = 0; if(listener->chanFwdCncl_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Cancelling tcpip-forward session for %s:%d", - listener->host, listener->port); + listener->host, listener->port)); s = packet = LIBSSH2_ALLOC(session, packet_len); if(!packet) { @@ -817,10 +900,10 @@ static int channel_setenv(LIBSSH2_CHANNEL *channel, memset(&channel->setenv_packet_requirev_state, 0, sizeof(channel->setenv_packet_requirev_state)); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Setting remote environment variable: %s=%s on " "channel %lu/%lu", - varname, value, channel->local.id, channel->remote.id); + varname, value, channel->local.id, channel->remote.id)); s = channel->setenv_packet = LIBSSH2_ALLOC(session, channel->setenv_packet_len); @@ -876,7 +959,9 @@ static int channel_setenv(LIBSSH2_CHANNEL *channel, } if(rc) { channel->setenv_state = libssh2_NB_state_idle; - return rc; + return _libssh2_error(session, rc, + "Failed getting response for " + "channel-setenv"); } else if(data_len < 1) { channel->setenv_state = libssh2_NB_state_idle; @@ -950,9 +1035,9 @@ static int channel_request_pty(LIBSSH2_CHANNEL *channel, memset(&channel->reqPTY_packet_requirev_state, 0, sizeof(channel->reqPTY_packet_requirev_state)); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Allocating tty on channel %lu/%lu", channel->local.id, - channel->remote.id); + channel->remote.id)); s = channel->reqPTY_packet; @@ -1021,6 +1106,164 @@ static int channel_request_pty(LIBSSH2_CHANNEL *channel, "channel request-pty"); } +/** + * channel_request_auth_agent + * The actual re-entrant method which requests an auth agent. + * */ +static int channel_request_auth_agent(LIBSSH2_CHANNEL *channel, + const char *request_str, + int request_str_len) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s; + static const unsigned char reply_codes[3] = + { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; + int rc; + + if(channel->req_auth_agent_state == libssh2_NB_state_idle) { + /* Only valid options are "auth-agent-req" and + * "auth-agent-req_at_openssh.com" so we make sure it is not + * actually longer than the longest possible. */ + if(request_str_len > 26) { + return _libssh2_error(session, LIBSSH2_ERROR_INVAL, + "request_str length too large"); + } + + /* + * Length: 24 or 36 = packet_type(1) + channel(4) + req_len(4) + + * request_str (variable) + want_reply (1) */ + channel->req_auth_agent_packet_len = 10 + request_str_len; + + /* Zero out the requireev state to reset */ + memset(&channel->req_auth_agent_requirev_state, 0, + sizeof(channel->req_auth_agent_requirev_state)); + + _libssh2_debug((session, LIBSSH2_TRACE_CONN, + "Requesting auth agent on channel %lu/%lu", + channel->local.id, channel->remote.id)); + + /* + * byte SSH_MSG_CHANNEL_REQUEST + * uint32 recipient channel + * string "auth-agent-req" + * boolean want reply + * */ + s = channel->req_auth_agent_packet; + *(s++) = SSH_MSG_CHANNEL_REQUEST; + _libssh2_store_u32(&s, channel->remote.id); + _libssh2_store_str(&s, (char *)request_str, request_str_len); + *(s++) = 0x01; + + channel->req_auth_agent_state = libssh2_NB_state_created; + } + + if(channel->req_auth_agent_state == libssh2_NB_state_created) { + /* Send the packet, we can use sizeof() on the packet because it + * is always completely filled; there are no variable length fields. */ + rc = _libssh2_transport_send(session, channel->req_auth_agent_packet, + channel->req_auth_agent_packet_len, + NULL, 0); + + if(rc == LIBSSH2_ERROR_EAGAIN) { + _libssh2_error(session, rc, + "Would block sending auth-agent request"); + return rc; + } + else if(rc) { + channel->req_auth_agent_state = libssh2_NB_state_idle; + return _libssh2_error(session, rc, + "Unable to send auth-agent request"); + } + _libssh2_htonu32(channel->req_auth_agent_local_channel, + channel->local.id); + channel->req_auth_agent_state = libssh2_NB_state_sent; + } + + if(channel->req_auth_agent_state == libssh2_NB_state_sent) { + unsigned char *data; + size_t data_len; + unsigned char code; + + rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len, + 1, channel->req_auth_agent_local_channel, + 4, + &channel->req_auth_agent_requirev_state); + if(rc == LIBSSH2_ERROR_EAGAIN) { + return rc; + } + else if(rc) { + channel->req_auth_agent_state = libssh2_NB_state_idle; + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Failed to request auth-agent"); + } + + code = data[0]; + + LIBSSH2_FREE(session, data); + channel->req_auth_agent_state = libssh2_NB_state_idle; + + if(code == SSH_MSG_CHANNEL_SUCCESS) + return 0; + } + + return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, + "Unable to complete request for auth-agent"); +} + +/* + * libssh2_channel_request_auth_agent + * + * Requests that agent forwarding be enabled for the session. The + * request must be sent over a specific channel, which starts the agent + * listener on the remote side. Once the channel is closed, the agent + * listener continues to exist. + */ +LIBSSH2_API int +libssh2_channel_request_auth_agent(LIBSSH2_CHANNEL *channel) +{ + int rc; + + if(!channel) + return LIBSSH2_ERROR_BAD_USE; + + rc = LIBSSH2_ERROR_CHANNEL_UNKNOWN; + + /* The current RFC draft for agent forwarding says you're supposed to + * send "auth-agent-req," but most SSH servers out there right now + * actually expect "auth-agent-req@openssh.com", so we try that + * first. */ + if(channel->req_auth_agent_try_state == libssh2_NB_state_idle) { + BLOCK_ADJUST(rc, channel->session, + channel_request_auth_agent(channel, + "auth-agent-req@openssh.com", + 26)); + + /* If we failed (but not with EAGAIN), then we move onto + * the next step to try another request type. */ + if(rc != LIBSSH2_ERROR_NONE && + rc != LIBSSH2_ERROR_EAGAIN) + channel->req_auth_agent_try_state = libssh2_NB_state_sent; + } + + if(channel->req_auth_agent_try_state == libssh2_NB_state_sent) { + BLOCK_ADJUST(rc, channel->session, + channel_request_auth_agent(channel, + "auth-agent-req", 14)); + + /* If we failed without an EAGAIN, then move on with this + * state machine. */ + if(rc != LIBSSH2_ERROR_NONE && + rc != LIBSSH2_ERROR_EAGAIN) + channel->req_auth_agent_try_state = libssh2_NB_state_sent1; + } + + /* If things are good, reset the try state. */ + if(rc == LIBSSH2_ERROR_NONE) + channel->req_auth_agent_try_state = libssh2_NB_state_idle; + + return rc; +} + /* * libssh2_channel_request_pty_ex * Duh... Request a PTY @@ -1059,10 +1302,10 @@ channel_request_pty_size(LIBSSH2_CHANNEL * channel, int width, memset(&channel->reqPTY_packet_requirev_state, 0, sizeof(channel->reqPTY_packet_requirev_state)); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "changing tty size on channel %lu/%lu", - channel->local.id, - channel->remote.id); + _libssh2_debug((session, LIBSSH2_TRACE_CONN, + "changing tty size on channel %lu/%lu", + channel->local.id, + channel->remote.id)); s = channel->reqPTY_packet; @@ -1148,13 +1391,13 @@ channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection, memset(&channel->reqX11_packet_requirev_state, 0, sizeof(channel->reqX11_packet_requirev_state)); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Requesting x11-req for channel %lu/%lu: single=%d " "proto=%s cookie=%s screen=%d", channel->local.id, channel->remote.id, single_connection, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", - auth_cookie ? auth_cookie : "<random>", screen_number); + auth_cookie ? auth_cookie : "<random>", screen_number)); s = channel->reqX11_packet = LIBSSH2_ALLOC(session, channel->reqX11_packet_len); @@ -1173,7 +1416,7 @@ channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection, _libssh2_store_str(&s, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", proto_len); - _libssh2_store_u32(&s, cookie_len); + _libssh2_store_u32(&s, (uint32_t)cookie_len); if(auth_cookie) { memcpy(s, auth_cookie, cookie_len); } @@ -1185,7 +1428,11 @@ channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection, border */ unsigned char buffer[(LIBSSH2_X11_RANDOM_COOKIE_LEN / 2) + 1]; - _libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); + if(_libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2)) { + return _libssh2_error(session, LIBSSH2_ERROR_RANDGEN, + "Unable to get random bytes " + "for x11-req cookie"); + } for(i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) { snprintf((char *)&s[i*2], 3, "%02X", buffer[i]); } @@ -1302,10 +1549,10 @@ _libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, if(message) channel->process_packet_len += + 4; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "starting request(%s) on channel %lu/%lu, message=%s", request, channel->local.id, channel->remote.id, - message ? message : "<null>"); + message ? message : "<null>")); s = channel->process_packet = LIBSSH2_ALLOC(session, channel->process_packet_len); if(!channel->process_packet) @@ -1319,7 +1566,7 @@ _libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, *(s++) = 0x01; if(message) - _libssh2_store_u32(&s, message_len); + _libssh2_store_u32(&s, (uint32_t)message_len); channel->process_state = libssh2_NB_state_created; } @@ -1410,7 +1657,7 @@ LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL * channel, int blocking) { if(channel) - (void) _libssh2_session_set_blocking(channel->session, blocking); + (void)_libssh2_session_set_blocking(channel->session, blocking); } /* @@ -1434,8 +1681,8 @@ _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid) if(packet->data_len < 1) { packet = next; - _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR, - "Unexpected packet length"); + _libssh2_debug((channel->session, LIBSSH2_TRACE_ERROR, + "Unexpected packet length")); continue; } @@ -1471,11 +1718,11 @@ _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid) size_t bytes_to_flush = packet->data_len - packet->data_head; - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, + _libssh2_debug((channel->session, LIBSSH2_TRACE_CONN, "Flushing %d bytes of data from stream " "%lu on channel %lu/%lu", bytes_to_flush, packet_stream_id, - channel->local.id, channel->remote.id); + channel->local.id, channel->remote.id)); /* It's one of the streams we wanted to flush */ channel->flush_refund_bytes += packet->data_len - 13; @@ -1495,20 +1742,20 @@ _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid) } channel->read_avail -= channel->flush_flush_bytes; - channel->remote.window_size -= channel->flush_flush_bytes; + channel->remote.window_size -= (uint32_t)channel->flush_flush_bytes; if(channel->flush_refund_bytes) { int rc = _libssh2_channel_receive_window_adjust(channel, - channel->flush_refund_bytes, - 1, NULL); + (uint32_t)channel->flush_refund_bytes, + 1, NULL); if(rc == LIBSSH2_ERROR_EAGAIN) return rc; } channel->flush_state = libssh2_NB_state_idle; - return channel->flush_flush_bytes; + return (int)channel->flush_flush_bytes; } /* @@ -1577,7 +1824,7 @@ libssh2_channel_get_exit_signal(LIBSSH2_CHANNEL *channel, *exitsignal = LIBSSH2_ALLOC(session, namelen + 1); if(!*exitsignal) { return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for signal name"); + "Unable to allocate memory for signal name"); } memcpy(*exitsignal, channel->exit_signal, namelen); (*exitsignal)[namelen] = '\0'; @@ -1634,10 +1881,10 @@ _libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, if(!force && (adjustment + channel->adjust_queue < LIBSSH2_CHANNEL_MINADJUST)) { - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, + _libssh2_debug((channel->session, LIBSSH2_TRACE_CONN, "Queueing %lu bytes for receive window adjustment " "for channel %lu/%lu", - adjustment, channel->local.id, channel->remote.id); + adjustment, channel->local.id, channel->remote.id)); channel->adjust_queue += adjustment; return 0; } @@ -1653,10 +1900,10 @@ _libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, channel->adjust_adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST; _libssh2_htonu32(&channel->adjust_adjust[1], channel->remote.id); _libssh2_htonu32(&channel->adjust_adjust[5], adjustment); - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, + _libssh2_debug((channel->session, LIBSSH2_TRACE_CONN, "Adjusting window %lu bytes for data on " "channel %lu/%lu", - adjustment, channel->local.id, channel->remote.id); + adjustment, channel->local.id, channel->remote.id)); channel->adjust_state = libssh2_NB_state_created; } @@ -1708,7 +1955,8 @@ libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, return (unsigned long)LIBSSH2_ERROR_BAD_USE; BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_receive_window_adjust(channel, adj, + _libssh2_channel_receive_window_adjust(channel, + (uint32_t)adj, force, &window)); /* stupid - but this is how it was made to work before and this is just @@ -1739,8 +1987,9 @@ libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL *channel, return LIBSSH2_ERROR_BAD_USE; BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_receive_window_adjust(channel, adj, force, - window)); + _libssh2_channel_receive_window_adjust(channel, + (uint32_t)adj, + force, window)); return rc; } @@ -1748,10 +1997,10 @@ int _libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) { if(channel->extData2_state == libssh2_NB_state_idle) { - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, + _libssh2_debug((channel->session, LIBSSH2_TRACE_CONN, "Setting channel %lu/%lu handle_extended_data" " mode to %d", - channel->local.id, channel->remote.id, ignore_mode); + channel->local.id, channel->remote.id, ignore_mode)); channel->remote.extended_data_ignore_mode = (char)ignore_mode; channel->extData2_state = libssh2_NB_state_created; @@ -1772,7 +2021,7 @@ _libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) } /* - * libssh2_channel_handle_extended_data2() + * libssh2_channel_handle_extended_data2 * */ LIBSSH2_API int @@ -1831,19 +2080,19 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id, LIBSSH2_PACKET *read_packet; LIBSSH2_PACKET *read_next; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "channel_read() wants %d bytes from channel %lu/%lu " "stream #%d", (int) buflen, channel->local.id, channel->remote.id, - stream_id); + stream_id)); /* expand the receiving window first if it has become too narrow */ if((channel->read_state == libssh2_NB_state_jump1) || (channel->remote.window_size < - channel->remote.window_size_initial / 4 * 3 + buflen) ) { + channel->remote.window_size_initial / 4 * 3 + buflen)) { - uint32_t adjustment = channel->remote.window_size_initial + buflen - - channel->remote.window_size; + uint32_t adjustment = (uint32_t)(channel->remote.window_size_initial + + buflen - channel->remote.window_size); if(adjustment < LIBSSH2_CHANNEL_MINADJUST) adjustment = LIBSSH2_CHANNEL_MINADJUST; @@ -1884,8 +2133,13 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id, if(readpkt->data_len < 5) { read_packet = read_next; - _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR, - "Unexpected packet length"); + + if(readpkt->data_len != 1 || + readpkt->data[0] != SSH_MSG_REQUEST_FAILURE) { + _libssh2_debug((channel->session, LIBSSH2_TRACE_ERROR, + "Unexpected packet length")); + } + continue; } @@ -1923,11 +2177,11 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id, unlink_packet = TRUE; } - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "channel_read() got %d of data from %lu/%lu/%d%s", bytes_want, channel->local.id, channel->remote.id, stream_id, - unlink_packet?" [ul]":""); + unlink_packet?" [ul]":"")); /* copy data from this struct to the target buffer */ memcpy(&buf[bytes_read], @@ -1966,7 +2220,7 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id, } channel->read_avail -= bytes_read; - channel->remote.window_size -= bytes_read; + channel->remote.window_size -= (uint32_t)bytes_read; return bytes_read; } @@ -1989,7 +2243,7 @@ LIBSSH2_API ssize_t libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen) { - int rc; + ssize_t rc; unsigned long recv_window; if(!channel) @@ -1999,8 +2253,8 @@ libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, if(buflen > recv_window) { BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_receive_window_adjust(channel, buflen, - 1, NULL)); + _libssh2_channel_receive_window_adjust(channel, + (uint32_t)buflen, 1, NULL)); } BLOCK_ADJUST(rc, channel->session, @@ -2023,7 +2277,7 @@ _libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id) uint32_t read_local_id; read_packet = _libssh2_list_first(&session->packets); - if(read_packet == NULL) + if(!read_packet) return 0; while(read_packet) { @@ -2032,8 +2286,8 @@ _libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id) if(read_packet->data_len < 5) { read_packet = next_packet; - _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR, - "Unexpected packet length"); + _libssh2_debug((channel->session, LIBSSH2_TRACE_ERROR, + "Unexpected packet length")); continue; } @@ -2061,7 +2315,7 @@ _libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id) && (channel->local.id == read_local_id) && (channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) { - return (read_packet->data_len - read_packet->data_head); + return read_packet->data_len - read_packet->data_head; } read_packet = next_packet; @@ -2101,15 +2355,15 @@ _libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id, if(channel->write_state == libssh2_NB_state_idle) { unsigned char *s = channel->write_packet; - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, + _libssh2_debug((channel->session, LIBSSH2_TRACE_CONN, "Writing %d bytes on channel %lu/%lu, stream #%d", (int) buflen, channel->local.id, channel->remote.id, - stream_id); + stream_id)); if(channel->local.close) return _libssh2_error(channel->session, LIBSSH2_ERROR_CHANNEL_CLOSED, - "We've already closed this channel"); + "We have already closed this channel"); else if(channel->local.eof) return _libssh2_error(channel->session, LIBSSH2_ERROR_CHANNEL_EOF_SENT, @@ -2136,7 +2390,7 @@ _libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id, */ session->socket_block_directions = LIBSSH2_SESSION_BLOCK_INBOUND; - return (rc == LIBSSH2_ERROR_EAGAIN?rc:0); + return rc == LIBSSH2_ERROR_EAGAIN ? rc : 0; } channel->write_bufwrite = buflen; @@ -2150,30 +2404,30 @@ _libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id, /* Don't exceed the remote end's limits */ /* REMEMBER local means local as the SOURCE of the data */ if(channel->write_bufwrite > channel->local.window_size) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Splitting write block due to %lu byte " "window_size on %lu/%lu/%d", channel->local.window_size, channel->local.id, - channel->remote.id, stream_id); + channel->remote.id, stream_id)); channel->write_bufwrite = channel->local.window_size; } if(channel->write_bufwrite > channel->local.packet_size) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Splitting write block due to %lu byte " "packet_size on %lu/%lu/%d", channel->local.packet_size, channel->local.id, - channel->remote.id, stream_id); + channel->remote.id, stream_id)); channel->write_bufwrite = channel->local.packet_size; } /* store the size here only, the buffer is passed in as-is to _libssh2_transport_send() */ - _libssh2_store_u32(&s, channel->write_bufwrite); + _libssh2_store_u32(&s, (uint32_t)channel->write_bufwrite); channel->write_packet_len = s - channel->write_packet; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int) channel->write_bufwrite, channel->local.id, - channel->remote.id, stream_id); + channel->remote.id, stream_id)); channel->write_state = libssh2_NB_state_created; } @@ -2192,7 +2446,7 @@ _libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id, "Unable to send channel data"); } /* Shrink local window size */ - channel->local.window_size -= channel->write_bufwrite; + channel->local.window_size -= (uint32_t)channel->write_bufwrite; wrote += channel->write_bufwrite; @@ -2245,9 +2499,9 @@ static int channel_send_eof(LIBSSH2_CHANNEL *channel) unsigned char packet[5]; /* packet_type(1) + channelno(4) */ int rc; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Sending EOF on channel %lu/%lu", - channel->local.id, channel->remote.id); + channel->local.id, channel->remote.id)); packet[0] = SSH_MSG_CHANNEL_EOF; _libssh2_htonu32(packet + 1, channel->remote.id); rc = _libssh2_transport_send(session, packet, 5, NULL, 0); @@ -2306,8 +2560,8 @@ libssh2_channel_eof(LIBSSH2_CHANNEL * channel) if(packet->data_len < 1) { packet = next_packet; - _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR, - "Unexpected packet length"); + _libssh2_debug((channel->session, LIBSSH2_TRACE_ERROR, + "Unexpected packet length")); continue; } @@ -2335,9 +2589,9 @@ static int channel_wait_eof(LIBSSH2_CHANNEL *channel) int rc; if(channel->wait_eof_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Awaiting EOF for channel %lu/%lu", channel->local.id, - channel->remote.id); + channel->remote.id)); channel->wait_eof_state = libssh2_NB_state_created; } @@ -2364,7 +2618,7 @@ static int channel_wait_eof(LIBSSH2_CHANNEL *channel) else if(rc < 0) { channel->wait_eof_state = libssh2_NB_state_idle; return _libssh2_error(session, rc, - "_libssh2_transport_read() bailed out!"); + "_libssh2_transport_read() bailed out"); } } while(1); @@ -2409,7 +2663,7 @@ int _libssh2_channel_close(LIBSSH2_CHANNEL * channel) return rc; } _libssh2_error(session, rc, - "Unable to send EOF, but closing channel anyway"); + "Unable to send EOF, but closing channel anyway"); } } @@ -2417,8 +2671,8 @@ int _libssh2_channel_close(LIBSSH2_CHANNEL * channel) late for us to wait for it. Continue closing! */ if(channel->close_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Closing channel %lu/%lu", - channel->local.id, channel->remote.id); + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Closing channel %lu/%lu", + channel->local.id, channel->remote.id)); channel->close_packet[0] = SSH_MSG_CHANNEL_CLOSE; _libssh2_htonu32(channel->close_packet + 1, channel->remote.id); @@ -2486,7 +2740,7 @@ libssh2_channel_close(LIBSSH2_CHANNEL *channel) if(!channel) return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, channel->session, _libssh2_channel_close(channel) ); + BLOCK_ADJUST(rc, channel->session, _libssh2_channel_close(channel)); return rc; } @@ -2507,9 +2761,9 @@ static int channel_wait_closed(LIBSSH2_CHANNEL *channel) } if(channel->wait_closed_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Awaiting close of channel %lu/%lu", channel->local.id, - channel->remote.id); + channel->remote.id)); channel->wait_closed_state = libssh2_NB_state_created; } @@ -2570,9 +2824,9 @@ int _libssh2_channel_free(LIBSSH2_CHANNEL *channel) assert(session); if(channel->free_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, + _libssh2_debug((session, LIBSSH2_TRACE_CONN, "Freeing channel %lu/%lu resources", channel->local.id, - channel->remote.id); + channel->remote.id)); channel->free_state = libssh2_NB_state_created; } @@ -2604,10 +2858,10 @@ int _libssh2_channel_free(LIBSSH2_CHANNEL *channel) /* Clear out packets meant for this channel */ _libssh2_htonu32(channel_id, channel->local.id); while((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA, &data, - &data_len, 1, channel_id, 4) >= 0) - || - (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, - &data_len, 1, channel_id, 4) >= 0)) { + &data_len, 1, channel_id, 4) >= 0) + || + (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, + &data_len, 1, channel_id, 4) >= 0)) { LIBSSH2_FREE(session, data); } @@ -2667,7 +2921,7 @@ libssh2_channel_free(LIBSSH2_CHANNEL *channel) */ LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, - unsigned long *read_avail, + /* FIXME: -> size_t */ unsigned long *read_avail, unsigned long *window_size_initial) { if(!channel) @@ -2689,8 +2943,8 @@ libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, if(packet->data_len < 1) { packet = next_packet; - _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR, - "Unexpected packet length"); + _libssh2_debug((channel->session, LIBSSH2_TRACE_ERROR, + "Unexpected packet length")); continue; } @@ -2707,7 +2961,7 @@ libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, packet = next_packet; } - *read_avail = bytes_queued; + *read_avail = (unsigned long)bytes_queued; } return channel->remote.window_size; @@ -2736,3 +2990,87 @@ libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, return channel->local.window_size; } + +/* A signal can be delivered to the remote process/service using the + following message. Some systems may not implement signals, in which + case they SHOULD ignore this message. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "signal" + boolean FALSE + string signal name (without the "SIG" prefix) + + 'signal name' values will be encoded as discussed in the passage + describing SSH_MSG_CHANNEL_REQUEST messages using "exit-signal" in + this section. + */ +static int channel_signal(LIBSSH2_CHANNEL *channel, + const char *signame, + size_t signame_len) +{ + LIBSSH2_SESSION *session = channel->session; + int retcode = LIBSSH2_ERROR_PROTO; + + if(channel->sendsignal_state == libssh2_NB_state_idle) { + unsigned char *s; + + /* 20 = packet_type(1) + channel(4) + + signal_len + sizeof(signal) - 1 + want_reply(1) + + signame_len_len(4) */ + channel->sendsignal_packet_len = 20 + signame_len; + + s = channel->sendsignal_packet = + LIBSSH2_ALLOC(session, channel->sendsignal_packet_len); + if(!channel->sendsignal_packet) + return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for " + "signal request"); + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + _libssh2_store_u32(&s, channel->remote.id); + _libssh2_store_str(&s, "signal", sizeof("signal") - 1); + *(s++) = 0x00; /* Don't reply */ + _libssh2_store_str(&s, signame, signame_len); + + channel->sendsignal_state = libssh2_NB_state_created; + } + + if(channel->sendsignal_state == libssh2_NB_state_created) { + int rc; + + rc = _libssh2_transport_send(session, channel->sendsignal_packet, + channel->sendsignal_packet_len, + NULL, 0); + if(rc == LIBSSH2_ERROR_EAGAIN) { + _libssh2_error(session, rc, "Would block sending signal request"); + return rc; + } + else if(rc) { + LIBSSH2_FREE(session, channel->sendsignal_packet); + channel->sendsignal_state = libssh2_NB_state_idle; + return _libssh2_error(session, rc, "Unable to send signal packet"); + } + LIBSSH2_FREE(session, channel->sendsignal_packet); + retcode = LIBSSH2_ERROR_NONE; + } + + channel->sendsignal_state = libssh2_NB_state_idle; + + return retcode; +} + +LIBSSH2_API int +libssh2_channel_signal_ex(LIBSSH2_CHANNEL *channel, + const char *signame, + size_t signame_len) +{ + int rc; + + if(!channel) + return LIBSSH2_ERROR_BAD_USE; + + BLOCK_ADJUST(rc, channel->session, + channel_signal(channel, signame, signame_len)); + return rc; +} |