version 1.34, 2021/12/24 21:52:48 |
version 1.35, 2022/02/23 19:07:20 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
/* $OpenBSD: channels.c,v 1.408 2021/09/14 11:04:21 mbuhl Exp $ */ |
/* $OpenBSD: channels.c,v 1.413 2022/02/17 10:58:27 djm Exp $ */ |
|
|
/* |
/* |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
Line 59 __RCSID("$NetBSD$"); |
|
Line 58 __RCSID("$NetBSD$"); |
|
#include <fcntl.h> |
#include <fcntl.h> |
#include <limits.h> |
#include <limits.h> |
#include <netdb.h> |
#include <netdb.h> |
|
#include <poll.h> |
#include <stdarg.h> |
#include <stdarg.h> |
#include <stdint.h> |
#include <stdint.h> |
#include <stdio.h> |
#include <stdio.h> |
Line 87 __RCSID("$NetBSD$"); |
|
Line 87 __RCSID("$NetBSD$"); |
|
static int hpn_disabled = 0; |
static int hpn_disabled = 0; |
static int hpn_buffer_size = 2 * 1024 * 1024; |
static int hpn_buffer_size = 2 * 1024 * 1024; |
|
|
|
/* XXX remove once we're satisfied there's no lurking bugs */ |
|
/* #define DEBUG_CHANNEL_POLL 1 */ |
|
|
/* -- agent forwarding */ |
/* -- agent forwarding */ |
#define NUM_SOCKS 10 |
#define NUM_SOCKS 10 |
|
|
Line 101 static int hpn_buffer_size = 2 * 1024 * |
|
Line 104 static int hpn_buffer_size = 2 * 1024 * |
|
/* Maximum number of fake X11 displays to try. */ |
/* Maximum number of fake X11 displays to try. */ |
#define MAX_DISPLAYS 1000 |
#define MAX_DISPLAYS 1000 |
|
|
/* Per-channel callback for pre/post select() actions */ |
/* Per-channel callback for pre/post IO actions */ |
typedef void chan_fn(struct ssh *, Channel *c, |
typedef void chan_fn(struct ssh *, Channel *c); |
fd_set *readset, fd_set *writeset); |
|
|
|
/* |
/* |
* Data structure for storing which hosts are permitted for forward requests. |
* Data structure for storing which hosts are permitted for forward requests. |
Line 164 struct ssh_channels { |
|
Line 166 struct ssh_channels { |
|
u_int channels_alloc; |
u_int channels_alloc; |
|
|
/* |
/* |
* Maximum file descriptor value used in any of the channels. This is |
* 'channel_pre*' are called just before IO to add any bits |
* updated in channel_new. |
* relevant to channels in the c->io_want bitmasks. |
*/ |
|
int channel_max_fd; |
|
|
|
/* |
|
* 'channel_pre*' are called just before select() to add any bits |
|
* relevant to channels in the select bitmasks. |
|
* |
* |
* 'channel_post*': perform any appropriate operations for |
* 'channel_post*': perform any appropriate operations for |
* channels which have events pending. |
* channels which have c->io_ready events pending. |
*/ |
*/ |
chan_fn **channel_pre; |
chan_fn **channel_pre; |
chan_fn **channel_post; |
chan_fn **channel_post; |
|
|
channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, |
channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, |
int extusage, int nonblock, int is_tty) |
int extusage, int nonblock, int is_tty) |
{ |
{ |
struct ssh_channels *sc = ssh->chanctxt; |
|
|
|
/* Update the maximum file descriptor value. */ |
|
sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, rfd); |
|
sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, wfd); |
|
sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, efd); |
|
|
|
if (rfd != -1) |
if (rfd != -1) |
fcntl(rfd, F_SETFD, FD_CLOEXEC); |
fcntl(rfd, F_SETFD, FD_CLOEXEC); |
if (wfd != -1 && wfd != rfd) |
if (wfd != -1 && wfd != rfd) |
Line 426 channel_new(struct ssh *ssh, const char |
|
Line 415 channel_new(struct ssh *ssh, const char |
|
return c; |
return c; |
} |
} |
|
|
static void |
|
channel_find_maxfd(struct ssh_channels *sc) |
|
{ |
|
u_int i; |
|
int max = 0; |
|
Channel *c; |
|
|
|
for (i = 0; i < sc->channels_alloc; i++) { |
|
c = sc->channels[i]; |
|
if (c != NULL) { |
|
max = MAXIMUM(max, c->rfd); |
|
max = MAXIMUM(max, c->wfd); |
|
max = MAXIMUM(max, c->efd); |
|
} |
|
} |
|
sc->channel_max_fd = max; |
|
} |
|
|
|
int |
int |
channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) |
channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) |
{ |
{ |
struct ssh_channels *sc = ssh->chanctxt; |
|
int ret, fd = *fdp; |
int ret, fd = *fdp; |
|
|
if (fd == -1) |
if (fd == -1) |
Line 458 channel_close_fd(struct ssh *ssh, Channe |
|
Line 428 channel_close_fd(struct ssh *ssh, Channe |
|
(*fdp == c->efd && (c->restore_block & CHANNEL_RESTORE_EFD) != 0)) |
(*fdp == c->efd && (c->restore_block & CHANNEL_RESTORE_EFD) != 0)) |
(void)fcntl(*fdp, F_SETFL, 0); /* restore blocking */ |
(void)fcntl(*fdp, F_SETFL, 0); /* restore blocking */ |
|
|
|
if (*fdp == c->rfd) { |
|
c->io_want &= ~SSH_CHAN_IO_RFD; |
|
c->io_ready &= ~SSH_CHAN_IO_RFD; |
|
c->rfd = -1; |
|
} |
|
if (*fdp == c->wfd) { |
|
c->io_want &= ~SSH_CHAN_IO_WFD; |
|
c->io_ready &= ~SSH_CHAN_IO_WFD; |
|
c->wfd = -1; |
|
} |
|
if (*fdp == c->efd) { |
|
c->io_want &= ~SSH_CHAN_IO_EFD; |
|
c->io_ready &= ~SSH_CHAN_IO_EFD; |
|
c->efd = -1; |
|
} |
|
if (*fdp == c->sock) { |
|
c->io_want &= ~SSH_CHAN_IO_SOCK; |
|
c->io_ready &= ~SSH_CHAN_IO_SOCK; |
|
c->sock = -1; |
|
} |
|
|
ret = close(fd); |
ret = close(fd); |
*fdp = -1; |
*fdp = -1; /* probably redundant */ |
if (fd == sc->channel_max_fd) |
|
channel_find_maxfd(sc); |
|
return ret; |
return ret; |
} |
} |
|
|
Line 546 permission_set_get_array(struct ssh *ssh |
|
Line 535 permission_set_get_array(struct ssh *ssh |
|
} |
} |
} |
} |
|
|
/* Adds an entry to the spcified forwarding list */ |
/* Adds an entry to the specified forwarding list */ |
static int |
static int |
permission_set_add(struct ssh *ssh, int who, int where, |
permission_set_add(struct ssh *ssh, int who, int where, |
const char *host_to_connect, int port_to_connect, |
const char *host_to_connect, int port_to_connect, |
Line 683 channel_free_all(struct ssh *ssh) |
|
Line 672 channel_free_all(struct ssh *ssh) |
|
free(sc->channels); |
free(sc->channels); |
sc->channels = NULL; |
sc->channels = NULL; |
sc->channels_alloc = 0; |
sc->channels_alloc = 0; |
sc->channel_max_fd = 0; |
|
|
|
free(sc->x11_saved_display); |
free(sc->x11_saved_display); |
sc->x11_saved_display = NULL; |
sc->x11_saved_display = NULL; |
Line 875 channel_format_status(const Channel *c) |
|
Line 863 channel_format_status(const Channel *c) |
|
char *ret = NULL; |
char *ret = NULL; |
|
|
xasprintf(&ret, "t%d %s%u i%u/%zu o%u/%zu e[%s]/%zu " |
xasprintf(&ret, "t%d %s%u i%u/%zu o%u/%zu e[%s]/%zu " |
"fd %d/%d/%d sock %d cc %d", |
"fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x", |
c->type, |
c->type, |
c->have_remote_id ? "r" : "nr", c->remote_id, |
c->have_remote_id ? "r" : "nr", c->remote_id, |
c->istate, sshbuf_len(c->input), |
c->istate, sshbuf_len(c->input), |
c->ostate, sshbuf_len(c->output), |
c->ostate, sshbuf_len(c->output), |
channel_format_extended_usage(c), sshbuf_len(c->extended), |
channel_format_extended_usage(c), sshbuf_len(c->extended), |
c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan); |
c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan, |
|
c->io_want, c->io_ready); |
return ret; |
return ret; |
} |
} |
|
|
Line 1100 channel_set_fds(struct ssh *ssh, int id, |
|
Line 1089 channel_set_fds(struct ssh *ssh, int id, |
|
} |
} |
|
|
static void |
static void |
channel_pre_listener(struct ssh *ssh, Channel *c, |
channel_pre_listener(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
FD_SET(c->sock, readset); |
c->io_want = SSH_CHAN_IO_SOCK_R; |
} |
} |
|
|
static void |
static void |
channel_pre_connecting(struct ssh *ssh, Channel *c, |
channel_pre_connecting(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
debug3("channel %d: waiting for connection", c->self); |
debug3("channel %d: waiting for connection", c->self); |
FD_SET(c->sock, writeset); |
c->io_want = SSH_CHAN_IO_SOCK_W; |
} |
} |
|
|
static int |
static int |
Line 1135 channel_tcpwinsz(struct ssh *ssh) |
|
Line 1122 channel_tcpwinsz(struct ssh *ssh) |
|
} |
} |
|
|
static void |
static void |
channel_pre_open(struct ssh *ssh, Channel *c, |
channel_pre_open(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
/* check buffer limits */ |
/* check buffer limits */ |
if ((!c->tcpwinsz) || (c->dynamic_window > 0)) |
if ((!c->tcpwinsz) || (c->dynamic_window > 0)) |
c->tcpwinsz = channel_tcpwinsz(ssh); |
c->tcpwinsz = channel_tcpwinsz(ssh); |
|
|
|
c->io_want = 0; |
if (c->istate == CHAN_INPUT_OPEN && |
if (c->istate == CHAN_INPUT_OPEN && |
c->remote_window > 0 && |
c->remote_window > 0 && |
sshbuf_len(c->input) < c->remote_window && |
sshbuf_len(c->input) < c->remote_window && |
sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) |
sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) |
FD_SET(c->rfd, readset); |
c->io_want |= SSH_CHAN_IO_RFD; |
if (c->ostate == CHAN_OUTPUT_OPEN || |
if (c->ostate == CHAN_OUTPUT_OPEN || |
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
if (sshbuf_len(c->output) > 0) { |
if (sshbuf_len(c->output) > 0) { |
FD_SET(c->wfd, writeset); |
c->io_want |= SSH_CHAN_IO_WFD; |
} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) |
if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) |
debug2("channel %d: " |
debug2("channel %d: " |
Line 1165 channel_pre_open(struct ssh *ssh, Channe |
|
Line 1152 channel_pre_open(struct ssh *ssh, Channe |
|
c->ostate == CHAN_OUTPUT_CLOSED)) { |
c->ostate == CHAN_OUTPUT_CLOSED)) { |
if (c->extended_usage == CHAN_EXTENDED_WRITE && |
if (c->extended_usage == CHAN_EXTENDED_WRITE && |
sshbuf_len(c->extended) > 0) |
sshbuf_len(c->extended) > 0) |
FD_SET(c->efd, writeset); |
c->io_want |= SSH_CHAN_IO_EFD_W; |
else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && |
else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && |
(c->extended_usage == CHAN_EXTENDED_READ || |
(c->extended_usage == CHAN_EXTENDED_READ || |
c->extended_usage == CHAN_EXTENDED_IGNORE) && |
c->extended_usage == CHAN_EXTENDED_IGNORE) && |
sshbuf_len(c->extended) < c->remote_window) |
sshbuf_len(c->extended) < c->remote_window) |
FD_SET(c->efd, readset); |
c->io_want |= SSH_CHAN_IO_EFD_R; |
} |
} |
/* XXX: What about efd? races? */ |
/* XXX: What about efd? races? */ |
} |
} |
Line 1252 x11_open_helper(struct ssh *ssh, struct |
|
Line 1239 x11_open_helper(struct ssh *ssh, struct |
|
} |
} |
|
|
static void |
static void |
channel_pre_x11_open(struct ssh *ssh, Channel *c, |
channel_pre_x11_open(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
int ret = x11_open_helper(ssh, c->output); |
int ret = x11_open_helper(ssh, c->output); |
|
|
Line 1261 channel_pre_x11_open(struct ssh *ssh, Ch |
|
Line 1247 channel_pre_x11_open(struct ssh *ssh, Ch |
|
|
|
if (ret == 1) { |
if (ret == 1) { |
c->type = SSH_CHANNEL_OPEN; |
c->type = SSH_CHANNEL_OPEN; |
channel_pre_open(ssh, c, readset, writeset); |
channel_pre_open(ssh, c); |
} else if (ret == -1) { |
} else if (ret == -1) { |
logit("X11 connection rejected because of wrong authentication."); |
logit("X11 connection rejected because of wrong authentication."); |
debug2("X11 rejected %d i%d/o%d", |
debug2("X11 rejected %d i%d/o%d", |
Line 1276 channel_pre_x11_open(struct ssh *ssh, Ch |
|
Line 1262 channel_pre_x11_open(struct ssh *ssh, Ch |
|
} |
} |
|
|
static void |
static void |
channel_pre_mux_client(struct ssh *ssh, |
channel_pre_mux_client(struct ssh *ssh, Channel *c) |
Channel *c, fd_set *readset, fd_set *writeset) |
|
{ |
{ |
|
c->io_want = 0; |
if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && |
if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && |
sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) |
sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) |
FD_SET(c->rfd, readset); |
c->io_want |= SSH_CHAN_IO_RFD; |
if (c->istate == CHAN_INPUT_WAIT_DRAIN) { |
if (c->istate == CHAN_INPUT_WAIT_DRAIN) { |
/* clear buffer immediately (discard any partial packet) */ |
/* clear buffer immediately (discard any partial packet) */ |
sshbuf_reset(c->input); |
sshbuf_reset(c->input); |
Line 1292 channel_pre_mux_client(struct ssh *ssh, |
|
Line 1278 channel_pre_mux_client(struct ssh *ssh, |
|
if (c->ostate == CHAN_OUTPUT_OPEN || |
if (c->ostate == CHAN_OUTPUT_OPEN || |
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
if (sshbuf_len(c->output) > 0) |
if (sshbuf_len(c->output) > 0) |
FD_SET(c->wfd, writeset); |
c->io_want |= SSH_CHAN_IO_WFD; |
else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) |
else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) |
chan_obuf_empty(ssh, c); |
chan_obuf_empty(ssh, c); |
} |
} |
Line 1571 channel_connect_stdio_fwd(struct ssh *ss |
|
Line 1557 channel_connect_stdio_fwd(struct ssh *ss |
|
|
|
/* dynamic port forwarding */ |
/* dynamic port forwarding */ |
static void |
static void |
channel_pre_dynamic(struct ssh *ssh, Channel *c, |
channel_pre_dynamic(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
const u_char *p; |
const u_char *p; |
u_int have; |
u_int have; |
int ret; |
int ret; |
|
|
|
c->io_want = 0; |
have = sshbuf_len(c->input); |
have = sshbuf_len(c->input); |
debug2("channel %d: pre_dynamic: have %d", c->self, have); |
debug2("channel %d: pre_dynamic: have %d", c->self, have); |
/* sshbuf_dump(c->input, stderr); */ |
/* sshbuf_dump(c->input, stderr); */ |
/* check if the fixed size part of the packet is in buffer. */ |
/* check if the fixed size part of the packet is in buffer. */ |
if (have < 3) { |
if (have < 3) { |
/* need more */ |
/* need more */ |
FD_SET(c->sock, readset); |
c->io_want |= SSH_CHAN_IO_RFD; |
return; |
return; |
} |
} |
/* try to guess the protocol */ |
/* try to guess the protocol */ |
Line 1606 channel_pre_dynamic(struct ssh *ssh, Cha |
|
Line 1592 channel_pre_dynamic(struct ssh *ssh, Cha |
|
} else if (ret == 0) { |
} else if (ret == 0) { |
debug2("channel %d: pre_dynamic: need more", c->self); |
debug2("channel %d: pre_dynamic: need more", c->self); |
/* need more */ |
/* need more */ |
FD_SET(c->sock, readset); |
c->io_want |= SSH_CHAN_IO_RFD; |
if (sshbuf_len(c->output)) |
if (sshbuf_len(c->output)) |
FD_SET(c->sock, writeset); |
c->io_want |= SSH_CHAN_IO_WFD; |
} else { |
} else { |
/* switch to the next state */ |
/* switch to the next state */ |
c->type = SSH_CHANNEL_OPENING; |
c->type = SSH_CHANNEL_OPENING; |
Line 1630 rdynamic_close(struct ssh *ssh, Channel |
|
Line 1616 rdynamic_close(struct ssh *ssh, Channel |
|
|
|
/* reverse dynamic port forwarding */ |
/* reverse dynamic port forwarding */ |
static void |
static void |
channel_before_prepare_select_rdynamic(struct ssh *ssh, Channel *c) |
channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c) |
{ |
{ |
const u_char *p; |
const u_char *p; |
u_int have, len; |
u_int have, len; |
Line 1688 channel_before_prepare_select_rdynamic(s |
|
Line 1674 channel_before_prepare_select_rdynamic(s |
|
|
|
/* This is our fake X11 server socket. */ |
/* This is our fake X11 server socket. */ |
static void |
static void |
channel_post_x11_listener(struct ssh *ssh, Channel *c, |
channel_post_x11_listener(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
Channel *nc; |
Channel *nc; |
struct sockaddr_storage addr; |
struct sockaddr_storage addr; |
Line 1697 channel_post_x11_listener(struct ssh *ss |
|
Line 1682 channel_post_x11_listener(struct ssh *ss |
|
socklen_t addrlen; |
socklen_t addrlen; |
char buf[16384], *remote_ipaddr; |
char buf[16384], *remote_ipaddr; |
|
|
if (!FD_ISSET(c->sock, readset)) |
if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) |
return; |
return; |
|
|
debug("X11 connection requested."); |
debug("X11 connection requested."); |
Line 1806 channel_set_x11_refuse_time(struct ssh * |
|
Line 1791 channel_set_x11_refuse_time(struct ssh * |
|
* This socket is listening for connections to a forwarded TCP/IP port. |
* This socket is listening for connections to a forwarded TCP/IP port. |
*/ |
*/ |
static void |
static void |
channel_post_port_listener(struct ssh *ssh, Channel *c, |
channel_post_port_listener(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
Channel *nc; |
Channel *nc; |
struct sockaddr_storage addr; |
struct sockaddr_storage addr; |
Line 1815 channel_post_port_listener(struct ssh *s |
|
Line 1799 channel_post_port_listener(struct ssh *s |
|
socklen_t addrlen; |
socklen_t addrlen; |
const char *rtype; |
const char *rtype; |
|
|
if (!FD_ISSET(c->sock, readset)) |
if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) |
return; |
return; |
|
|
debug("Connection to port %d forwarding to %.100s port %d requested.", |
debug("Connection to port %d forwarding to %.100s port %d requested.", |
Line 1866 channel_post_port_listener(struct ssh *s |
|
Line 1850 channel_post_port_listener(struct ssh *s |
|
* clients. |
* clients. |
*/ |
*/ |
static void |
static void |
channel_post_auth_listener(struct ssh *ssh, Channel *c, |
channel_post_auth_listener(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
Channel *nc; |
Channel *nc; |
int r, newsock; |
int r, newsock; |
struct sockaddr_storage addr; |
struct sockaddr_storage addr; |
socklen_t addrlen; |
socklen_t addrlen; |
|
|
if (!FD_ISSET(c->sock, readset)) |
if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) |
return; |
return; |
|
|
addrlen = sizeof(addr); |
addrlen = sizeof(addr); |
Line 1895 channel_post_auth_listener(struct ssh *s |
|
Line 1878 channel_post_auth_listener(struct ssh *s |
|
} |
} |
|
|
static void |
static void |
channel_post_connecting(struct ssh *ssh, Channel *c, |
channel_post_connecting(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
int err = 0, sock, isopen, r; |
int err = 0, sock, isopen, r; |
socklen_t sz = sizeof(err); |
socklen_t sz = sizeof(err); |
|
|
if (!FD_ISSET(c->sock, writeset)) |
if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0) |
return; |
return; |
if (!c->have_remote_id) |
if (!c->have_remote_id) |
fatal_f("channel %d: no remote id", c->self); |
fatal_f("channel %d: no remote id", c->self); |
Line 1935 channel_post_connecting(struct ssh *ssh, |
|
Line 1917 channel_post_connecting(struct ssh *ssh, |
|
if ((sock = connect_next(&c->connect_ctx)) > 0) { |
if ((sock = connect_next(&c->connect_ctx)) > 0) { |
close(c->sock); |
close(c->sock); |
c->sock = c->rfd = c->wfd = sock; |
c->sock = c->rfd = c->wfd = sock; |
channel_find_maxfd(ssh->chanctxt); |
|
return; |
return; |
} |
} |
/* Exhausted all addresses */ |
/* Exhausted all addresses */ |
Line 1960 channel_post_connecting(struct ssh *ssh, |
|
Line 1941 channel_post_connecting(struct ssh *ssh, |
|
} |
} |
|
|
static int |
static int |
channel_handle_rfd(struct ssh *ssh, Channel *c, |
channel_handle_rfd(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
char buf[CHAN_RBUF]; |
char buf[CHAN_RBUF]; |
ssize_t len; |
ssize_t len; |
int r; |
int r; |
|
size_t have, avail, maxlen = CHANNEL_MAX_READ; |
|
|
|
if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) |
|
return 1; /* Shouldn't happen */ |
|
if ((avail = sshbuf_avail(c->input)) == 0) |
|
return 1; /* Shouldn't happen */ |
|
|
if (c->rfd == -1 || !FD_ISSET(c->rfd, readset)) |
/* |
|
* For "simple" channels (i.e. not datagram or filtered), we can |
|
* read directly to the channel buffer. |
|
*/ |
|
if (c->input_filter == NULL && !c->datagram) { |
|
/* Only OPEN channels have valid rwin */ |
|
if (c->type == SSH_CHANNEL_OPEN) { |
|
if ((have = sshbuf_len(c->input)) >= c->remote_window) |
|
return 1; /* shouldn't happen */ |
|
if (maxlen > c->remote_window - have) |
|
maxlen = c->remote_window - have; |
|
} |
|
if (maxlen > avail) |
|
maxlen = avail; |
|
if ((r = sshbuf_read(c->rfd, c->input, maxlen, NULL)) != 0) { |
|
if (errno == EINTR || errno == EAGAIN) |
|
return 1; |
|
debug2("channel %d: read failed rfd %d maxlen %zu: %s", |
|
c->self, c->rfd, maxlen, ssh_err(r)); |
|
goto rfail; |
|
} |
return 1; |
return 1; |
|
} |
|
|
len = read(c->rfd, buf, sizeof(buf)); |
len = read(c->rfd, buf, sizeof(buf)); |
if (len == -1 && (errno == EINTR || errno == EAGAIN)) |
if (len == -1 && (errno == EINTR || errno == EAGAIN)) |
return 1; |
return 1; |
if (len <= 0) { |
if (len <= 0) { |
debug2("channel %d: read<=0 rfd %d len %zd", |
debug2("channel %d: read<=0 rfd %d len %zd: %s", |
c->self, c->rfd, len); |
c->self, c->rfd, len, |
|
len == 0 ? "closed" : strerror(errno)); |
|
rfail: |
if (c->type != SSH_CHANNEL_OPEN) { |
if (c->type != SSH_CHANNEL_OPEN) { |
debug2("channel %d: not open", c->self); |
debug2("channel %d: not open", c->self); |
chan_mark_dead(ssh, c); |
chan_mark_dead(ssh, c); |
Line 1993 channel_handle_rfd(struct ssh *ssh, Chan |
|
Line 2002 channel_handle_rfd(struct ssh *ssh, Chan |
|
} else if (c->datagram) { |
} else if (c->datagram) { |
if ((r = sshbuf_put_string(c->input, buf, len)) != 0) |
if ((r = sshbuf_put_string(c->input, buf, len)) != 0) |
fatal_fr(r, "channel %i: put datagram", c->self); |
fatal_fr(r, "channel %i: put datagram", c->self); |
} else if ((r = sshbuf_put(c->input, buf, len)) != 0) |
} |
fatal_fr(r, "channel %i: put data", c->self); |
|
return 1; |
return 1; |
} |
} |
|
|
static int |
static int |
channel_handle_wfd(struct ssh *ssh, Channel *c, |
channel_handle_wfd(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
struct termios tio; |
struct termios tio; |
u_char *data = NULL, *buf; /* XXX const; need filter API change */ |
u_char *data = NULL, *buf; /* XXX const; need filter API change */ |
size_t dlen, olen = 0; |
size_t dlen, olen = 0; |
int r, len; |
int r, len; |
|
|
if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || |
if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) |
sshbuf_len(c->output) == 0) |
return 1; |
|
if (sshbuf_len(c->output) == 0) |
return 1; |
return 1; |
|
|
/* Send buffered output data to the socket. */ |
/* Send buffered output data to the socket. */ |
Line 2079 channel_handle_wfd(struct ssh *ssh, Chan |
|
Line 2087 channel_handle_wfd(struct ssh *ssh, Chan |
|
} |
} |
|
|
static int |
static int |
channel_handle_efd_write(struct ssh *ssh, Channel *c, |
channel_handle_efd_write(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
int r; |
int r; |
ssize_t len; |
ssize_t len; |
|
|
if (!FD_ISSET(c->efd, writeset) || sshbuf_len(c->extended) == 0) |
if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0) |
|
return 1; |
|
if (sshbuf_len(c->extended) == 0) |
return 1; |
return 1; |
|
|
len = write(c->efd, sshbuf_ptr(c->extended), |
len = write(c->efd, sshbuf_ptr(c->extended), |
Line 2105 channel_handle_efd_write(struct ssh *ssh |
|
Line 2114 channel_handle_efd_write(struct ssh *ssh |
|
} |
} |
|
|
static int |
static int |
channel_handle_efd_read(struct ssh *ssh, Channel *c, |
channel_handle_efd_read(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
char buf[CHAN_RBUF]; |
char buf[CHAN_RBUF]; |
int r; |
int r; |
ssize_t len; |
ssize_t len; |
|
|
if (!FD_ISSET(c->efd, readset)) |
if ((c->io_ready & SSH_CHAN_IO_EFD_R) == 0) |
return 1; |
return 1; |
|
|
len = read(c->efd, buf, sizeof(buf)); |
len = read(c->efd, buf, sizeof(buf)); |
Line 2130 channel_handle_efd_read(struct ssh *ssh, |
|
Line 2138 channel_handle_efd_read(struct ssh *ssh, |
|
} |
} |
|
|
static int |
static int |
channel_handle_efd(struct ssh *ssh, Channel *c, |
channel_handle_efd(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
if (c->efd == -1) |
if (c->efd == -1) |
return 1; |
return 1; |
Line 2139 channel_handle_efd(struct ssh *ssh, Chan |
|
Line 2146 channel_handle_efd(struct ssh *ssh, Chan |
|
/** XXX handle drain efd, too */ |
/** XXX handle drain efd, too */ |
|
|
if (c->extended_usage == CHAN_EXTENDED_WRITE) |
if (c->extended_usage == CHAN_EXTENDED_WRITE) |
return channel_handle_efd_write(ssh, c, readset, writeset); |
return channel_handle_efd_write(ssh, c); |
else if (c->extended_usage == CHAN_EXTENDED_READ || |
else if (c->extended_usage == CHAN_EXTENDED_READ || |
c->extended_usage == CHAN_EXTENDED_IGNORE) |
c->extended_usage == CHAN_EXTENDED_IGNORE) |
return channel_handle_efd_read(ssh, c, readset, writeset); |
return channel_handle_efd_read(ssh, c); |
|
|
return 1; |
return 1; |
} |
} |
Line 2186 channel_check_window(struct ssh *ssh, Ch |
|
Line 2193 channel_check_window(struct ssh *ssh, Ch |
|
} |
} |
|
|
static void |
static void |
channel_post_open(struct ssh *ssh, Channel *c, |
channel_post_open(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
channel_handle_rfd(ssh, c, readset, writeset); |
channel_handle_rfd(ssh, c); |
channel_handle_wfd(ssh, c, readset, writeset); |
channel_handle_wfd(ssh, c); |
channel_handle_efd(ssh, c, readset, writeset); |
channel_handle_efd(ssh, c); |
channel_check_window(ssh, c); |
channel_check_window(ssh, c); |
} |
} |
|
|
Line 2220 read_mux(struct ssh *ssh, Channel *c, u_ |
|
Line 2226 read_mux(struct ssh *ssh, Channel *c, u_ |
|
} |
} |
|
|
static void |
static void |
channel_post_mux_client_read(struct ssh *ssh, Channel *c, |
channel_post_mux_client_read(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
u_int need; |
u_int need; |
|
|
if (c->rfd == -1 || !FD_ISSET(c->rfd, readset)) |
if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) |
return; |
return; |
if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) |
if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) |
return; |
return; |
Line 2257 channel_post_mux_client_read(struct ssh |
|
Line 2262 channel_post_mux_client_read(struct ssh |
|
} |
} |
|
|
static void |
static void |
channel_post_mux_client_write(struct ssh *ssh, Channel *c, |
channel_post_mux_client_write(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
ssize_t len; |
ssize_t len; |
int r; |
int r; |
|
|
if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || |
if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) |
sshbuf_len(c->output) == 0) |
return; |
|
if (sshbuf_len(c->output) == 0) |
return; |
return; |
|
|
len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); |
len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); |
Line 2279 channel_post_mux_client_write(struct ssh |
|
Line 2284 channel_post_mux_client_write(struct ssh |
|
} |
} |
|
|
static void |
static void |
channel_post_mux_client(struct ssh *ssh, Channel *c, |
channel_post_mux_client(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
channel_post_mux_client_read(ssh, c, readset, writeset); |
channel_post_mux_client_read(ssh, c); |
channel_post_mux_client_write(ssh, c, readset, writeset); |
channel_post_mux_client_write(ssh, c); |
} |
} |
|
|
static void |
static void |
channel_post_mux_listener(struct ssh *ssh, Channel *c, |
channel_post_mux_listener(struct ssh *ssh, Channel *c) |
fd_set *readset, fd_set *writeset) |
|
{ |
{ |
Channel *nc; |
Channel *nc; |
struct sockaddr_storage addr; |
struct sockaddr_storage addr; |
Line 2297 channel_post_mux_listener(struct ssh *ss |
|
Line 2300 channel_post_mux_listener(struct ssh *ss |
|
uid_t euid; |
uid_t euid; |
gid_t egid; |
gid_t egid; |
|
|
if (!FD_ISSET(c->sock, readset)) |
if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) |
return; |
return; |
|
|
debug("multiplexing control connection"); |
debug("multiplexing control connection"); |
Line 2403 channel_garbage_collect(struct ssh *ssh, |
|
Line 2406 channel_garbage_collect(struct ssh *ssh, |
|
enum channel_table { CHAN_PRE, CHAN_POST }; |
enum channel_table { CHAN_PRE, CHAN_POST }; |
|
|
static void |
static void |
channel_handler(struct ssh *ssh, int table, |
channel_handler(struct ssh *ssh, int table, time_t *unpause_secs) |
fd_set *readset, fd_set *writeset, time_t *unpause_secs) |
|
{ |
{ |
struct ssh_channels *sc = ssh->chanctxt; |
struct ssh_channels *sc = ssh->chanctxt; |
chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; |
chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; |
Line 2430 channel_handler(struct ssh *ssh, int tab |
|
Line 2432 channel_handler(struct ssh *ssh, int tab |
|
* Run handlers that are not paused. |
* Run handlers that are not paused. |
*/ |
*/ |
if (c->notbefore <= now) |
if (c->notbefore <= now) |
(*ftab[c->type])(ssh, c, readset, writeset); |
(*ftab[c->type])(ssh, c); |
else if (unpause_secs != NULL) { |
else if (unpause_secs != NULL) { |
/* |
/* |
* Collect the time that the earliest |
* Collect the time that the earliest |
Line 2452 channel_handler(struct ssh *ssh, int tab |
|
Line 2454 channel_handler(struct ssh *ssh, int tab |
|
} |
} |
|
|
/* |
/* |
* Create sockets before allocating the select bitmasks. |
* Create sockets before preparing IO. |
* This is necessary for things that need to happen after reading |
* This is necessary for things that need to happen after reading |
* the network-input but before channel_prepare_select(). |
* the network-input but need to be completed before IO event setup, e.g. |
|
* because they may create new channels. |
*/ |
*/ |
static void |
static void |
channel_before_prepare_select(struct ssh *ssh) |
channel_before_prepare_io(struct ssh *ssh) |
{ |
{ |
struct ssh_channels *sc = ssh->chanctxt; |
struct ssh_channels *sc = ssh->chanctxt; |
Channel *c; |
Channel *c; |
Line 2468 channel_before_prepare_select(struct ssh |
|
Line 2471 channel_before_prepare_select(struct ssh |
|
if (c == NULL) |
if (c == NULL) |
continue; |
continue; |
if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) |
if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) |
channel_before_prepare_select_rdynamic(ssh, c); |
channel_before_prepare_io_rdynamic(ssh, c); |
} |
} |
} |
} |
|
|
/* |
static void |
* Allocate/update select bitmasks and add any bits relevant to channels in |
dump_channel_poll(const char *func, const char *what, Channel *c, |
* select bitmasks. |
u_int pollfd_offset, struct pollfd *pfd) |
*/ |
|
void |
|
channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp, |
|
int *maxfdp, u_int *nallocp, time_t *minwait_secs) |
|
{ |
{ |
u_int n, sz, nfdset; |
#ifdef DEBUG_CHANNEL_POLL |
|
debug3_f("channel %d: rfd r%d w%d e%d s%d " |
|
"pfd[%u].fd=%d want 0x%02x ev 0x%02x ready 0x%02x rev 0x%02x", |
|
c->self, c->rfd, c->wfd, c->efd, c->sock, pollfd_offset, pfd->fd, |
|
c->io_want, pfd->events, c->io_ready, pfd->revents); |
|
#endif |
|
} |
|
|
channel_before_prepare_select(ssh); /* might update channel_max_fd */ |
/* Prepare pollfd entries for a single channel */ |
|
static void |
|
channel_prepare_pollfd(Channel *c, u_int *next_pollfd, |
|
struct pollfd *pfd, u_int npfd) |
|
{ |
|
u_int p = *next_pollfd; |
|
|
|
if (c == NULL) |
|
return; |
|
if (p + 4 > npfd) { |
|
/* Shouldn't happen */ |
|
fatal_f("channel %d: bad pfd offset %u (max %u)", |
|
c->self, p, npfd); |
|
} |
|
c->pollfd_offset = -1; |
|
/* |
|
* prepare c->rfd |
|
* |
|
* This is a special case, since c->rfd might be the same as |
|
* c->wfd, c->efd and/or c->sock. Handle those here if they want |
|
* IO too. |
|
*/ |
|
if (c->rfd != -1) { |
|
if (c->pollfd_offset == -1) |
|
c->pollfd_offset = p; |
|
pfd[p].fd = c->rfd; |
|
pfd[p].events = 0; |
|
if ((c->io_want & SSH_CHAN_IO_RFD) != 0) |
|
pfd[p].events |= POLLIN; |
|
/* rfd == wfd */ |
|
if (c->wfd == c->rfd && |
|
(c->io_want & SSH_CHAN_IO_WFD) != 0) |
|
pfd[p].events |= POLLOUT; |
|
/* rfd == efd */ |
|
if (c->efd == c->rfd && |
|
(c->io_want & SSH_CHAN_IO_EFD_R) != 0) |
|
pfd[p].events |= POLLIN; |
|
if (c->efd == c->rfd && |
|
(c->io_want & SSH_CHAN_IO_EFD_W) != 0) |
|
pfd[p].events |= POLLOUT; |
|
/* rfd == sock */ |
|
if (c->sock == c->rfd && |
|
(c->io_want & SSH_CHAN_IO_SOCK_R) != 0) |
|
pfd[p].events |= POLLIN; |
|
if (c->sock == c->rfd && |
|
(c->io_want & SSH_CHAN_IO_SOCK_W) != 0) |
|
pfd[p].events |= POLLOUT; |
|
dump_channel_poll(__func__, "rfd", c, p, &pfd[p]); |
|
p++; |
|
} |
|
/* prepare c->wfd (if not already handled above) */ |
|
if (c->wfd != -1 && c->rfd != c->wfd) { |
|
if (c->pollfd_offset == -1) |
|
c->pollfd_offset = p; |
|
pfd[p].fd = c->wfd; |
|
pfd[p].events = 0; |
|
if ((c->io_want & SSH_CHAN_IO_WFD) != 0) |
|
pfd[p].events = POLLOUT; |
|
dump_channel_poll(__func__, "wfd", c, p, &pfd[p]); |
|
p++; |
|
} |
|
/* prepare c->efd (if not already handled above) */ |
|
if (c->efd != -1 && c->rfd != c->efd) { |
|
if (c->pollfd_offset == -1) |
|
c->pollfd_offset = p; |
|
pfd[p].fd = c->efd; |
|
pfd[p].events = 0; |
|
if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) |
|
pfd[p].events |= POLLIN; |
|
if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) |
|
pfd[p].events |= POLLOUT; |
|
dump_channel_poll(__func__, "efd", c, p, &pfd[p]); |
|
p++; |
|
} |
|
/* prepare c->sock (if not already handled above) */ |
|
if (c->sock != -1 && c->rfd != c->sock) { |
|
if (c->pollfd_offset == -1) |
|
c->pollfd_offset = p; |
|
pfd[p].fd = c->sock; |
|
pfd[p].events = 0; |
|
if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) |
|
pfd[p].events |= POLLIN; |
|
if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) |
|
pfd[p].events |= POLLOUT; |
|
dump_channel_poll(__func__, "sock", c, p, &pfd[p]); |
|
p++; |
|
} |
|
*next_pollfd = p; |
|
} |
|
|
n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd); |
/* * Allocate/prepare poll structure */ |
|
void |
|
channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp, |
|
u_int *npfd_activep, u_int npfd_reserved, time_t *minwait_secs) |
|
{ |
|
struct ssh_channels *sc = ssh->chanctxt; |
|
u_int i, oalloc, p, npfd = npfd_reserved; |
|
|
nfdset = howmany(n+1, NFDBITS); |
channel_before_prepare_io(ssh); /* might create a new channel */ |
/* Explicitly test here, because xrealloc isn't always called */ |
|
if (nfdset && SIZE_MAX / nfdset < sizeof(fd_mask)) |
|
fatal("channel_prepare_select: max_fd (%d) is too large", n); |
|
sz = nfdset * sizeof(fd_mask); |
|
|
|
/* perhaps check sz < nalloc/2 and shrink? */ |
|
if (*readsetp == NULL || sz > *nallocp) { |
|
*readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask)); |
|
*writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask)); |
|
*nallocp = sz; |
|
} |
|
*maxfdp = n; |
|
memset(*readsetp, 0, sz); |
|
memset(*writesetp, 0, sz); |
|
|
|
|
/* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */ |
|
if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved) |
|
fatal_f("too many channels"); /* shouldn't happen */ |
if (!ssh_packet_is_rekeying(ssh)) |
if (!ssh_packet_is_rekeying(ssh)) |
channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp, |
npfd += sc->channels_alloc * 4; |
minwait_secs); |
if (npfd > *npfd_allocp) { |
|
*pfdp = xrecallocarray(*pfdp, *npfd_allocp, |
|
npfd, sizeof(**pfdp)); |
|
*npfd_allocp = npfd; |
|
} |
|
*npfd_activep = npfd_reserved; |
|
if (ssh_packet_is_rekeying(ssh)) |
|
return; |
|
|
|
oalloc = sc->channels_alloc; |
|
|
|
channel_handler(ssh, CHAN_PRE, minwait_secs); |
|
|
|
if (oalloc != sc->channels_alloc) { |
|
/* shouldn't happen */ |
|
fatal_f("channels_alloc changed during CHAN_PRE " |
|
"(was %u, now %u)", oalloc, sc->channels_alloc); |
|
} |
|
|
|
/* Prepare pollfd */ |
|
p = npfd_reserved; |
|
for (i = 0; i < sc->channels_alloc; i++) |
|
channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd); |
|
*npfd_activep = p; |
|
} |
|
|
|
static void |
|
fd_ready(Channel *c, u_int p, struct pollfd *pfds, int fd, |
|
const char *what, u_int revents_mask, u_int ready) |
|
{ |
|
struct pollfd *pfd = &pfds[p]; |
|
|
|
if (fd == -1) |
|
return; |
|
dump_channel_poll(__func__, what, c, p, pfd); |
|
if (pfd->fd != fd) { |
|
fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d " |
|
"r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd, |
|
c->rfd, c->wfd, c->efd, c->sock); |
|
} |
|
if ((pfd->revents & POLLNVAL) != 0) { |
|
fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d", |
|
c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock); |
|
} |
|
if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0) |
|
c->io_ready |= ready & c->io_want; |
} |
} |
|
|
/* |
/* |
* After select, perform any appropriate operations for channels which have |
* After poll, perform any appropriate operations for channels which have |
* events pending. |
* events pending. |
*/ |
*/ |
void |
void |
channel_after_select(struct ssh *ssh, fd_set *readset, fd_set *writeset) |
channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd) |
{ |
{ |
channel_handler(ssh, CHAN_POST, readset, writeset, NULL); |
struct ssh_channels *sc = ssh->chanctxt; |
|
u_int i, p; |
|
Channel *c; |
|
|
|
#ifdef DEBUG_CHANNEL_POLL |
|
for (p = 0; p < npfd; p++) { |
|
if (pfd[p].revents == 0) |
|
continue; |
|
debug_f("pfd[%u].fd %d rev 0x%04x", |
|
p, pfd[p].fd, pfd[p].revents); |
|
} |
|
#endif |
|
|
|
/* Convert pollfd into c->io_ready */ |
|
for (i = 0; i < sc->channels_alloc; i++) { |
|
c = sc->channels[i]; |
|
if (c == NULL || c->pollfd_offset < 0) |
|
continue; |
|
if ((u_int)c->pollfd_offset >= npfd) { |
|
/* shouldn't happen */ |
|
fatal_f("channel %d: (before) bad pfd %u (max %u)", |
|
c->self, c->pollfd_offset, npfd); |
|
} |
|
/* if rfd is shared with efd/sock then wfd should be too */ |
|
if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd && |
|
(c->rfd == c->efd || c->rfd == c->sock)) { |
|
/* Shouldn't happen */ |
|
fatal_f("channel %d: unexpected fds r%d w%d e%d s%d", |
|
c->self, c->rfd, c->wfd, c->efd, c->sock); |
|
} |
|
c->io_ready = 0; |
|
p = c->pollfd_offset; |
|
/* rfd, potentially shared with wfd, efd and sock */ |
|
if (c->rfd != -1) { |
|
fd_ready(c, p, pfd, c->rfd, "rfd", POLLIN, |
|
SSH_CHAN_IO_RFD); |
|
if (c->rfd == c->wfd) { |
|
fd_ready(c, p, pfd, c->wfd, "wfd/r", POLLOUT, |
|
SSH_CHAN_IO_WFD); |
|
} |
|
if (c->rfd == c->efd) { |
|
fd_ready(c, p, pfd, c->efd, "efdr/r", POLLIN, |
|
SSH_CHAN_IO_EFD_R); |
|
fd_ready(c, p, pfd, c->efd, "efdw/r", POLLOUT, |
|
SSH_CHAN_IO_EFD_W); |
|
} |
|
if (c->rfd == c->sock) { |
|
fd_ready(c, p, pfd, c->sock, "sockr/r", POLLIN, |
|
SSH_CHAN_IO_SOCK_R); |
|
fd_ready(c, p, pfd, c->sock, "sockw/r", POLLOUT, |
|
SSH_CHAN_IO_SOCK_W); |
|
} |
|
p++; |
|
} |
|
/* wfd */ |
|
if (c->wfd != -1 && c->wfd != c->rfd) { |
|
fd_ready(c, p, pfd, c->wfd, "wfd", POLLOUT, |
|
SSH_CHAN_IO_WFD); |
|
p++; |
|
} |
|
/* efd */ |
|
if (c->efd != -1 && c->efd != c->rfd) { |
|
fd_ready(c, p, pfd, c->efd, "efdr", POLLIN, |
|
SSH_CHAN_IO_EFD_R); |
|
fd_ready(c, p, pfd, c->efd, "efdw", POLLOUT, |
|
SSH_CHAN_IO_EFD_W); |
|
p++; |
|
} |
|
/* sock */ |
|
if (c->sock != -1 && c->sock != c->rfd) { |
|
fd_ready(c, p, pfd, c->sock, "sockr", POLLIN, |
|
SSH_CHAN_IO_SOCK_R); |
|
fd_ready(c, p, pfd, c->sock, "sockw", POLLOUT, |
|
SSH_CHAN_IO_SOCK_W); |
|
p++; |
|
} |
|
|
|
if (p > npfd) { |
|
/* shouldn't happen */ |
|
fatal_f("channel %d: (after) bad pfd %u (max %u)", |
|
c->self, c->pollfd_offset, npfd); |
|
} |
|
} |
|
channel_handler(ssh, CHAN_POST, NULL); |
} |
} |
|
|
/* |
/* |