Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/libssh/libssh.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, const char *orig_addr,
LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr);
LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr);
LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr);
LIBSSH_API int ssh_channel_read_buffered(ssh_channel channel, void *dest, uint32_t count,
int is_stderr);
LIBSSH_API int ssh_channel_read_timeout(ssh_channel channel, void *dest, uint32_t count, int is_stderr, int timeout_ms);
LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count,
int is_stderr);
Expand Down Expand Up @@ -624,6 +626,7 @@ LIBSSH_API int ssh_get_random(void *where,int len,int strong);
LIBSSH_API int ssh_get_version(ssh_session session);
LIBSSH_API int ssh_get_status(ssh_session session);
LIBSSH_API int ssh_get_poll_flags(ssh_session session);
LIBSSH_API int ssh_session_handle_poll(ssh_session session, int revents);
LIBSSH_API int ssh_init(void);
LIBSSH_API int ssh_is_blocking(ssh_session session);
LIBSSH_API int ssh_is_connected(ssh_session session);
Expand Down
3 changes: 2 additions & 1 deletion src/ABI/libssh-4.10.0.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ ssh_channel_open_x11
ssh_channel_poll
ssh_channel_poll_timeout
ssh_channel_read
ssh_channel_read_buffered
ssh_channel_read_nonblocking
ssh_channel_read_timeout
ssh_channel_request_auth_agent
Expand Down Expand Up @@ -442,4 +443,4 @@ string_free
string_from_char
string_len
string_new
string_to_char
string_to_char
83 changes: 83 additions & 0 deletions src/channels.c
Original file line number Diff line number Diff line change
Expand Up @@ -3169,6 +3169,89 @@ int ssh_channel_read_timeout(ssh_channel channel,
return len;
}

/**
* @brief Reads buffered data from a channel without polling the socket.
*
* @param[in] channel The channel to read from.
*
* @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, SSH_AGAIN if nothing is
* available, SSH_ERROR on error, and SSH_EOF if the
* channel is EOF.
*/
int ssh_channel_read_buffered(ssh_channel channel,
void *dest,
uint32_t count,
int is_stderr)
{
ssh_session session;
ssh_buffer stdbuf;
uint32_t len;

if (channel == NULL) {
return SSH_ERROR;
}
if (dest == NULL) {
ssh_set_error_invalid(channel->session);
return SSH_ERROR;
}

session = channel->session;
if (count == 0) {
return 0;
}

stdbuf = channel->stdout_buffer;
if (is_stderr) {
stdbuf = channel->stderr_buffer;
}

if (session->session_state == SSH_SESSION_STATE_ERROR) {
return SSH_ERROR;
}

if (channel->state == SSH_CHANNEL_STATE_CLOSED) {
ssh_set_error(session,
SSH_FATAL,
"Remote channel is closed.");
return SSH_ERROR;
Comment on lines +3218 to +3222

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Return EOF before closed-channel error

In ssh_channel_read_buffered, the closed-state branch is evaluated before the EOF branch. A normal remote close path sets remote_eof = 1 and may set state = SSH_CHANNEL_STATE_CLOSED when buffers are empty, so this function reports SSH_ERROR (and a fatal error message) instead of SSH_EOF for graceful shutdown. That contradicts this API's contract and causes event-loop/nonblocking callers to mis-handle normal channel termination as a hard failure.

Useful? React with 👍 / 👎.

}

len = ssh_buffer_get_len(stdbuf);
if (len == 0) {
if (channel->remote_eof) {
return SSH_EOF;
}
return SSH_AGAIN;
}

if (len > count) {
len = count;
}

memcpy(dest, ssh_buffer_get(stdbuf), len);
ssh_buffer_pass_bytes(stdbuf, len);
if (channel->counter != NULL) {
channel->counter->in_bytes += len;
}

/* Try completing the delayed_close */
if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) {
channel->state = SSH_CHANNEL_STATE_CLOSED;
}

if (grow_window(session, channel) == SSH_ERROR) {
return SSH_ERROR;
}

return len;
}

/**
* @brief Do a nonblocking read on the channel.
*
Expand Down
2 changes: 2 additions & 0 deletions src/libssh.map
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ LIBSSH_4_5_0 # Released
ssh_channel_poll;
ssh_channel_poll_timeout;
ssh_channel_read;
ssh_channel_read_buffered;
ssh_channel_read_nonblocking;
ssh_channel_read_timeout;
ssh_channel_request_auth_agent;
Expand Down Expand Up @@ -223,6 +224,7 @@ LIBSSH_4_5_0 # Released
ssh_get_log_userdata;
ssh_get_openssh_version;
ssh_get_poll_flags;
ssh_session_handle_poll;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep ABI symbol manifest in sync with new export

This change adds ssh_session_handle_poll to the public API and linker map, but the current ABI manifest (src/ABI/libssh-4.10.0.symbols) is not updated with that symbol. That leaves ABI bookkeeping inconsistent and can break ABI validation or downstream packaging checks that rely on the manifest matching exported public symbols.

Useful? React with 👍 / 👎.

ssh_get_pubkey;
ssh_get_pubkey_hash;
ssh_get_publickey;
Expand Down
25 changes: 25 additions & 0 deletions src/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,31 @@ int ssh_get_poll_flags(ssh_session session)
return ssh_socket_get_poll_flags (session->socket);
}

int ssh_session_handle_poll(ssh_session session, int revents)
{
ssh_poll_handle ph;
int rc;

if (session == NULL || session->socket == NULL) {
return SSH_ERROR;
}

ph = ssh_socket_get_poll_handle(session->socket);
if (ph == NULL) {
return SSH_ERROR;
}

rc = ssh_socket_pollcallback(ph,
ssh_socket_get_fd(session->socket),
revents,
session->socket);
if (rc < 0) {
return SSH_ERROR;
}

return SSH_OK;
}

/**
* @brief Get the disconnect message from the server.
*
Expand Down