version 1.373, 2022/01/01 01:55:30 |
version 1.374, 2022/01/06 21:48:38 |
|
|
#include <ctype.h> |
#include <ctype.h> |
#include <errno.h> |
#include <errno.h> |
#include <paths.h> |
#include <paths.h> |
|
#include <poll.h> |
#include <signal.h> |
#include <signal.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
|
|
* one of the file descriptors). |
* one of the file descriptors). |
*/ |
*/ |
static void |
static void |
client_wait_until_can_do_something(struct ssh *ssh, |
client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp, |
fd_set **readsetp, fd_set **writesetp, |
u_int *npfd_allocp, u_int *npfd_activep, int rekeying, |
int *maxfdp, u_int *nallocp, int rekeying) |
int *conn_in_readyp, int *conn_out_readyp) |
{ |
{ |
struct timeval tv, *tvp; |
int timeout_secs, pollwait; |
int timeout_secs; |
|
time_t minwait_secs = 0, now = monotime(); |
time_t minwait_secs = 0, now = monotime(); |
int r, ret; |
int r, ret; |
|
u_int p; |
|
|
/* Add any selections by the channel mechanism. */ |
*conn_in_readyp = *conn_out_readyp = 0; |
channel_prepare_select(ssh, readsetp, writesetp, maxfdp, |
|
nallocp, &minwait_secs); |
|
|
|
/* channel_prepare_select could have closed the last channel */ |
/* Prepare channel poll. First two pollfd entries are reserved */ |
|
channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, |
|
&minwait_secs); |
|
if (*npfd_activep < 2) |
|
fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ |
|
|
|
/* channel_prepare_poll could have closed the last channel */ |
if (session_closed && !channel_still_open(ssh) && |
if (session_closed && !channel_still_open(ssh) && |
!ssh_packet_have_data_to_write(ssh)) { |
!ssh_packet_have_data_to_write(ssh)) { |
/* clear mask since we did not call select() */ |
/* clear events since we did not call poll() */ |
memset(*readsetp, 0, *nallocp); |
for (p = 0; p < *npfd_activep; p++) |
memset(*writesetp, 0, *nallocp); |
(*pfdp)[p].revents = 0; |
return; |
return; |
} |
} |
|
|
FD_SET(connection_in, *readsetp); |
/* Monitor server connection on reserved pollfd entries */ |
|
(*pfdp)[0].fd = connection_in; |
|
(*pfdp)[0].events = POLLIN; |
|
(*pfdp)[1].fd = connection_out; |
|
(*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; |
|
|
/* Select server connection if have data to write to the server. */ |
|
if (ssh_packet_have_data_to_write(ssh)) |
|
FD_SET(connection_out, *writesetp); |
|
|
|
/* |
/* |
* Wait for something to happen. This will suspend the process until |
* Wait for something to happen. This will suspend the process until |
* some selected descriptor can be read, written, or has some other |
* some polled descriptor can be read, written, or has some other |
* event pending, or a timeout expires. |
* event pending, or a timeout expires. |
*/ |
*/ |
|
|
|
|
if (minwait_secs != 0) |
if (minwait_secs != 0) |
timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); |
timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); |
if (timeout_secs == INT_MAX) |
if (timeout_secs == INT_MAX) |
tvp = NULL; |
pollwait = -1; |
else { |
else if (timeout_secs >= INT_MAX / 1000) |
tv.tv_sec = timeout_secs; |
pollwait = INT_MAX; |
tv.tv_usec = 0; |
else |
tvp = &tv; |
pollwait = timeout_secs * 1000; |
} |
|
|
|
ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); |
ret = poll(*pfdp, *npfd_activep, pollwait); |
|
|
if (ret == -1) { |
if (ret == -1) { |
/* |
/* |
* We have to clear the select masks, because we return. |
* We have to clear the events because we return. |
* We have to return, because the mainloop checks for the flags |
* We have to return, because the mainloop checks for the flags |
* set by the signal handlers. |
* set by the signal handlers. |
*/ |
*/ |
memset(*readsetp, 0, *nallocp); |
for (p = 0; p < *npfd_activep; p++) |
memset(*writesetp, 0, *nallocp); |
(*pfdp)[p].revents = 0; |
if (errno == EINTR) |
if (errno == EINTR) |
return; |
return; |
/* Note: we might still have data in the buffers. */ |
/* Note: we might still have data in the buffers. */ |
if ((r = sshbuf_putf(stderr_buffer, |
if ((r = sshbuf_putf(stderr_buffer, |
"select: %s\r\n", strerror(errno))) != 0) |
"poll: %s\r\n", strerror(errno))) != 0) |
fatal_fr(r, "sshbuf_putf"); |
fatal_fr(r, "sshbuf_putf"); |
quit_pending = 1; |
quit_pending = 1; |
} else if (options.server_alive_interval > 0 && !FD_ISSET(connection_in, |
return; |
*readsetp) && monotime() >= server_alive_time) |
} |
|
|
|
*conn_in_readyp = (*pfdp)[0].revents != 0; |
|
*conn_out_readyp = (*pfdp)[1].revents != 0; |
|
|
|
if (options.server_alive_interval > 0 && !*conn_in_readyp && |
|
monotime() >= server_alive_time) { |
/* |
/* |
* ServerAlive check is needed. We can't rely on the select |
* ServerAlive check is needed. We can't rely on the poll |
* timing out since traffic on the client side such as port |
* timing out since traffic on the client side such as port |
* forwards can keep waking it up. |
* forwards can keep waking it up. |
*/ |
*/ |
server_alive_check(ssh); |
server_alive_check(ssh); |
|
} |
} |
} |
|
|
static void |
static void |
|
|
} |
} |
|
|
static void |
static void |
client_process_net_input(struct ssh *ssh, fd_set *readset) |
client_process_net_input(struct ssh *ssh) |
{ |
{ |
char buf[8192]; |
char buf[8192]; |
int r, len; |
int r, len; |
|
|
* Read input from the server, and add any such data to the buffer of |
* Read input from the server, and add any such data to the buffer of |
* the packet subsystem. |
* the packet subsystem. |
*/ |
*/ |
if (FD_ISSET(connection_in, readset)) { |
schedule_server_alive_check(); |
schedule_server_alive_check(); |
/* Read as much as possible. */ |
/* Read as much as possible. */ |
len = read(connection_in, buf, sizeof(buf)); |
len = read(connection_in, buf, sizeof(buf)); |
if (len == 0) { |
if (len == 0) { |
/* Received EOF. The remote host has closed the connection. */ |
/* |
if ((r = sshbuf_putf(stderr_buffer, |
* Received EOF. The remote host has closed the |
"Connection to %.300s closed by remote host.\r\n", |
* connection. |
host)) != 0) |
*/ |
fatal_fr(r, "sshbuf_putf"); |
if ((r = sshbuf_putf(stderr_buffer, |
quit_pending = 1; |
"Connection to %.300s closed by remote host.\r\n", |
return; |
host)) != 0) |
} |
fatal_fr(r, "sshbuf_putf"); |
/* |
quit_pending = 1; |
* There is a kernel bug on Solaris that causes poll to |
return; |
* sometimes wake up even though there is no data available. |
} |
*/ |
|
if (len == -1 && (errno == EAGAIN || errno == EINTR)) |
|
len = 0; |
|
|
|
if (len == -1) { |
/* |
/* |
* There is a kernel bug on Solaris that causes select to |
* An error has encountered. Perhaps there is a |
* sometimes wake up even though there is no data available. |
* network problem. |
*/ |
*/ |
if (len == -1 && (errno == EAGAIN || errno == EINTR)) |
if ((r = sshbuf_putf(stderr_buffer, |
len = 0; |
"Read from remote host %.300s: %.100s\r\n", |
|
host, strerror(errno))) != 0) |
if (len == -1) { |
fatal_fr(r, "sshbuf_putf"); |
/* |
quit_pending = 1; |
* An error has encountered. Perhaps there is a |
return; |
* network problem. |
|
*/ |
|
if ((r = sshbuf_putf(stderr_buffer, |
|
"Read from remote host %.300s: %.100s\r\n", |
|
host, strerror(errno))) != 0) |
|
fatal_fr(r, "sshbuf_putf"); |
|
quit_pending = 1; |
|
return; |
|
} |
|
ssh_packet_process_incoming(ssh, buf, len); |
|
} |
} |
|
ssh_packet_process_incoming(ssh, buf, len); |
} |
} |
|
|
static void |
static void |
|
|
client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, |
client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, |
int ssh2_chan_id) |
int ssh2_chan_id) |
{ |
{ |
fd_set *readset = NULL, *writeset = NULL; |
struct pollfd *pfd = NULL; |
|
u_int npfd_alloc = 0, npfd_active = 0; |
double start_time, total_time; |
double start_time, total_time; |
int r, max_fd = 0, max_fd2 = 0, len; |
int r, len; |
u_int64_t ibytes, obytes; |
u_int64_t ibytes, obytes; |
u_int nalloc = 0; |
int conn_in_ready, conn_out_ready; |
|
|
debug("Entering interactive session."); |
debug("Entering interactive session."); |
|
|
|
|
exit_status = -1; |
exit_status = -1; |
connection_in = ssh_packet_get_connection_in(ssh); |
connection_in = ssh_packet_get_connection_in(ssh); |
connection_out = ssh_packet_get_connection_out(ssh); |
connection_out = ssh_packet_get_connection_out(ssh); |
max_fd = MAXIMUM(connection_in, connection_out); |
|
|
|
quit_pending = 0; |
quit_pending = 0; |
|
|
|
|
* Wait until we have something to do (something becomes |
* Wait until we have something to do (something becomes |
* available on one of the descriptors). |
* available on one of the descriptors). |
*/ |
*/ |
max_fd2 = max_fd; |
client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc, |
client_wait_until_can_do_something(ssh, &readset, &writeset, |
&npfd_active, ssh_packet_is_rekeying(ssh), |
&max_fd2, &nalloc, ssh_packet_is_rekeying(ssh)); |
&conn_in_ready, &conn_out_ready); |
|
|
if (quit_pending) |
if (quit_pending) |
break; |
break; |
|
|
/* Do channel operations unless rekeying in progress. */ |
/* Do channel operations unless rekeying in progress. */ |
if (!ssh_packet_is_rekeying(ssh)) |
if (!ssh_packet_is_rekeying(ssh)) |
channel_after_select(ssh, readset, writeset); |
channel_after_poll(ssh, pfd, npfd_active); |
|
|
/* Buffer input from the connection. */ |
/* Buffer input from the connection. */ |
client_process_net_input(ssh, readset); |
if (conn_in_ready) |
|
client_process_net_input(ssh); |
|
|
if (quit_pending) |
if (quit_pending) |
break; |
break; |
|
|
* Send as much buffered packet data as possible to the |
* Send as much buffered packet data as possible to the |
* sender. |
* sender. |
*/ |
*/ |
if (FD_ISSET(connection_out, writeset)) { |
if (conn_out_ready) { |
if ((r = ssh_packet_write_poll(ssh)) != 0) { |
if ((r = ssh_packet_write_poll(ssh)) != 0) { |
sshpkt_fatal(ssh, r, |
sshpkt_fatal(ssh, r, |
"%s: ssh_packet_write_poll", __func__); |
"%s: ssh_packet_write_poll", __func__); |
|
|
} |
} |
} |
} |
} |
} |
free(readset); |
free(pfd); |
free(writeset); |
|
|
|
/* Terminate the session. */ |
/* Terminate the session. */ |
|
|