-/* $OpenBSD: channels.c,v 1.409 2022/01/01 01:55:30 jsg Exp $ */
+/* $OpenBSD: channels.c,v 1.410 2022/01/06 21:46:23 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#define MAX_DISPLAYS 1000
/* Per-channel callback for pre/post select() actions */
-typedef void chan_fn(struct ssh *, Channel *c,
- fd_set *readset, fd_set *writeset);
+typedef void chan_fn(struct ssh *, Channel *c);
/*
* Data structure for storing which hosts are permitted for forward requests.
}
static void
-channel_pre_listener(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_pre_listener(struct ssh *ssh, Channel *c)
{
- FD_SET(c->sock, readset);
+ c->io_want = SSH_CHAN_IO_SOCK_R;
}
static void
-channel_pre_connecting(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_pre_connecting(struct ssh *ssh, Channel *c)
{
debug3("channel %d: waiting for connection", c->self);
- FD_SET(c->sock, writeset);
+ c->io_want = SSH_CHAN_IO_SOCK_W;
}
static void
-channel_pre_open(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_pre_open(struct ssh *ssh, Channel *c)
{
+ c->io_want = 0;
if (c->istate == CHAN_INPUT_OPEN &&
c->remote_window > 0 &&
sshbuf_len(c->input) < c->remote_window &&
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 ||
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
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) {
if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
debug2("channel %d: "
c->ostate == CHAN_OUTPUT_CLOSED)) {
if (c->extended_usage == CHAN_EXTENDED_WRITE &&
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) &&
(c->extended_usage == CHAN_EXTENDED_READ ||
c->extended_usage == CHAN_EXTENDED_IGNORE) &&
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? */
}
}
static void
-channel_pre_x11_open(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_pre_x11_open(struct ssh *ssh, Channel *c)
{
int ret = x11_open_helper(ssh, c->output);
if (ret == 1) {
c->type = SSH_CHANNEL_OPEN;
- channel_pre_open(ssh, c, readset, writeset);
+ channel_pre_open(ssh, c);
} else if (ret == -1) {
logit("X11 connection rejected because of wrong authentication.");
debug2("X11 rejected %d i%d/o%d",
}
static void
-channel_pre_mux_client(struct ssh *ssh,
- Channel *c, fd_set *readset, fd_set *writeset)
+channel_pre_mux_client(struct ssh *ssh, Channel *c)
{
+ c->io_want = 0;
if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
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) {
/* clear buffer immediately (discard any partial packet) */
sshbuf_reset(c->input);
if (c->ostate == CHAN_OUTPUT_OPEN ||
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
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)
chan_obuf_empty(ssh, c);
}
/* dynamic port forwarding */
static void
-channel_pre_dynamic(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_pre_dynamic(struct ssh *ssh, Channel *c)
{
const u_char *p;
u_int have;
int ret;
+ c->io_want = 0;
have = sshbuf_len(c->input);
debug2("channel %d: pre_dynamic: have %d", c->self, have);
/* sshbuf_dump(c->input, stderr); */
/* check if the fixed size part of the packet is in buffer. */
if (have < 3) {
/* need more */
- FD_SET(c->sock, readset);
+ c->io_want |= SSH_CHAN_IO_RFD;
return;
}
/* try to guess the protocol */
} else if (ret == 0) {
debug2("channel %d: pre_dynamic: need more", c->self);
/* need more */
- FD_SET(c->sock, readset);
+ c->io_want |= SSH_CHAN_IO_RFD;
if (sshbuf_len(c->output))
- FD_SET(c->sock, writeset);
+ c->io_want |= SSH_CHAN_IO_WFD;
} else {
/* switch to the next state */
c->type = SSH_CHANNEL_OPENING;
/* This is our fake X11 server socket. */
static void
-channel_post_x11_listener(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_x11_listener(struct ssh *ssh, Channel *c)
{
Channel *nc;
struct sockaddr_storage addr;
socklen_t addrlen;
char buf[16384], *remote_ipaddr;
- if (!FD_ISSET(c->sock, readset))
+ if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
return;
debug("X11 connection requested.");
* This socket is listening for connections to a forwarded TCP/IP port.
*/
static void
-channel_post_port_listener(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_port_listener(struct ssh *ssh, Channel *c)
{
Channel *nc;
struct sockaddr_storage addr;
socklen_t addrlen;
char *rtype;
- if (!FD_ISSET(c->sock, readset))
+ if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
return;
debug("Connection to port %d forwarding to %.100s port %d requested.",
* clients.
*/
static void
-channel_post_auth_listener(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_auth_listener(struct ssh *ssh, Channel *c)
{
Channel *nc;
int r, newsock;
struct sockaddr_storage addr;
socklen_t addrlen;
- if (!FD_ISSET(c->sock, readset))
+ if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
return;
addrlen = sizeof(addr);
}
static void
-channel_post_connecting(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_connecting(struct ssh *ssh, Channel *c)
{
int err = 0, sock, isopen, r;
socklen_t sz = sizeof(err);
- if (!FD_ISSET(c->sock, writeset))
+ if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0)
return;
if (!c->have_remote_id)
fatal_f("channel %d: no remote id", c->self);
}
static int
-channel_handle_rfd(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_handle_rfd(struct ssh *ssh, Channel *c)
{
char buf[CHAN_RBUF];
ssize_t len;
int r;
- if (c->rfd == -1 || !FD_ISSET(c->rfd, readset))
+ if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
return 1;
len = read(c->rfd, buf, sizeof(buf));
}
static int
-channel_handle_wfd(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_handle_wfd(struct ssh *ssh, Channel *c)
{
struct termios tio;
u_char *data = NULL, *buf; /* XXX const; need filter API change */
size_t dlen, olen = 0;
int r, len;
- if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) ||
- sshbuf_len(c->output) == 0)
+ if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
+ return 1;
+ if (sshbuf_len(c->output) == 0)
return 1;
/* Send buffered output data to the socket. */
}
static int
-channel_handle_efd_write(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_handle_efd_write(struct ssh *ssh, Channel *c)
{
int r;
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;
len = write(c->efd, sshbuf_ptr(c->extended),
}
static int
-channel_handle_efd_read(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_handle_efd_read(struct ssh *ssh, Channel *c)
{
char buf[CHAN_RBUF];
int r;
ssize_t len;
- if (!FD_ISSET(c->efd, readset))
+ if ((c->io_ready & SSH_CHAN_IO_EFD_R) == 0)
return 1;
len = read(c->efd, buf, sizeof(buf));
}
static int
-channel_handle_efd(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_handle_efd(struct ssh *ssh, Channel *c)
{
if (c->efd == -1)
return 1;
/** XXX handle drain efd, too */
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 ||
c->extended_usage == CHAN_EXTENDED_IGNORE)
- return channel_handle_efd_read(ssh, c, readset, writeset);
+ return channel_handle_efd_read(ssh, c);
return 1;
}
}
static void
-channel_post_open(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_open(struct ssh *ssh, Channel *c)
{
- channel_handle_rfd(ssh, c, readset, writeset);
- channel_handle_wfd(ssh, c, readset, writeset);
- channel_handle_efd(ssh, c, readset, writeset);
+ channel_handle_rfd(ssh, c);
+ channel_handle_wfd(ssh, c);
+ channel_handle_efd(ssh, c);
channel_check_window(ssh, c);
}
}
static void
-channel_post_mux_client_read(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_mux_client_read(struct ssh *ssh, Channel *c)
{
u_int need;
- if (c->rfd == -1 || !FD_ISSET(c->rfd, readset))
+ if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
return;
if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
return;
}
static void
-channel_post_mux_client_write(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_mux_client_write(struct ssh *ssh, Channel *c)
{
ssize_t len;
int r;
- if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) ||
- sshbuf_len(c->output) == 0)
+ if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
+ return;
+ if (sshbuf_len(c->output) == 0)
return;
len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
}
static void
-channel_post_mux_client(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_mux_client(struct ssh *ssh, Channel *c)
{
- channel_post_mux_client_read(ssh, c, readset, writeset);
- channel_post_mux_client_write(ssh, c, readset, writeset);
+ channel_post_mux_client_read(ssh, c);
+ channel_post_mux_client_write(ssh, c);
}
static void
-channel_post_mux_listener(struct ssh *ssh, Channel *c,
- fd_set *readset, fd_set *writeset)
+channel_post_mux_listener(struct ssh *ssh, Channel *c)
{
Channel *nc;
struct sockaddr_storage addr;
uid_t euid;
gid_t egid;
- if (!FD_ISSET(c->sock, readset))
+ if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
return;
debug("multiplexing control connection");
enum channel_table { CHAN_PRE, CHAN_POST };
static void
-channel_handler(struct ssh *ssh, int table,
- fd_set *readset, fd_set *writeset, time_t *unpause_secs)
+channel_handler(struct ssh *ssh, int table, time_t *unpause_secs)
{
struct ssh_channels *sc = ssh->chanctxt;
chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
* Run handlers that are not paused.
*/
if (c->notbefore <= now)
- (*ftab[c->type])(ssh, c, readset, writeset);
+ (*ftab[c->type])(ssh, c);
else if (unpause_secs != NULL) {
/*
* Collect the time that the earliest
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;
+ struct ssh_channels *sc = ssh->chanctxt;
+ u_int i, n, sz, nfdset, oalloc = sc->channels_alloc;
+ Channel *c;
channel_before_prepare_select(ssh); /* might update channel_max_fd */
memset(*writesetp, 0, sz);
if (!ssh_packet_is_rekeying(ssh))
- channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp,
- minwait_secs);
+ channel_handler(ssh, CHAN_PRE, minwait_secs);
+
+ /* Convert c->io_want into FD_SET */
+ for (i = 0; i < oalloc; i++) {
+ c = sc->channels[i];
+ if (c == NULL)
+ continue;
+ if ((c->io_want & SSH_CHAN_IO_RFD) != 0) {
+ if (c->rfd == -1)
+ fatal_f("channel %d: no rfd", c->self);
+ FD_SET(c->rfd, *readsetp);
+ }
+ if ((c->io_want & SSH_CHAN_IO_WFD) != 0) {
+ if (c->wfd == -1)
+ fatal_f("channel %d: no wfd", c->self);
+ FD_SET(c->wfd, *writesetp);
+ }
+ if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) {
+ if (c->efd == -1)
+ fatal_f("channel %d: no efd(r)", c->self);
+ FD_SET(c->efd, *readsetp);
+ }
+ if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) {
+ if (c->efd == -1)
+ fatal_f("channel %d: no efd(w)", c->self);
+ FD_SET(c->efd, *writesetp);
+ }
+ if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) {
+ if (c->sock == -1)
+ fatal_f("channel %d: no sock(r)", c->self);
+ FD_SET(c->sock, *readsetp);
+ }
+ if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) {
+ if (c->sock == -1)
+ fatal_f("channel %d: no sock(w)", c->self);
+ FD_SET(c->sock, *writesetp);
+ }
+ }
}
/*
void
channel_after_select(struct ssh *ssh, fd_set *readset, fd_set *writeset)
{
- channel_handler(ssh, CHAN_POST, readset, writeset, NULL);
+ struct ssh_channels *sc = ssh->chanctxt;
+ Channel *c;
+ u_int i, oalloc = sc->channels_alloc;
+
+ /* Convert FD_SET into c->io_ready */
+ for (i = 0; i < oalloc; i++) {
+ c = sc->channels[i];
+ if (c == NULL)
+ continue;
+ c->io_ready = 0;
+ if (c->rfd != -1 && FD_ISSET(c->rfd, readset))
+ c->io_ready |= SSH_CHAN_IO_RFD;
+ if (c->wfd != -1 && FD_ISSET(c->wfd, writeset))
+ c->io_ready |= SSH_CHAN_IO_WFD;
+ if (c->efd != -1 && FD_ISSET(c->efd, readset))
+ c->io_ready |= SSH_CHAN_IO_EFD_R;
+ if (c->efd != -1 && FD_ISSET(c->efd, writeset))
+ c->io_ready |= SSH_CHAN_IO_EFD_W;
+ if (c->sock != -1 && FD_ISSET(c->sock, readset))
+ c->io_ready |= SSH_CHAN_IO_SOCK_R;
+ if (c->sock != -1 && FD_ISSET(c->sock, writeset))
+ c->io_ready |= SSH_CHAN_IO_SOCK_W;
+ }
+ channel_handler(ssh, CHAN_POST, NULL);
}
/*