prepare for conversion of ssh, sshd mainloop from select() to poll()
authordjm <djm@openbsd.org>
Thu, 6 Jan 2022 21:46:23 +0000 (21:46 +0000)
committerdjm <djm@openbsd.org>
Thu, 6 Jan 2022 21:46:23 +0000 (21:46 +0000)
by moving FD_SET construction out of channel handlers into separate
functions. ok markus

usr.bin/ssh/channels.c
usr.bin/ssh/channels.h

index c4cce3f..3983989 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
@@ -93,8 +93,7 @@
 #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.
@@ -1086,33 +1085,31 @@ channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
 }
 
 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: "
@@ -1127,12 +1124,12 @@ channel_pre_open(struct ssh *ssh, Channel *c,
            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? */
 }
@@ -1214,8 +1211,7 @@ x11_open_helper(struct ssh *ssh, struct sshbuf *b)
 }
 
 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);
 
@@ -1223,7 +1219,7 @@ channel_pre_x11_open(struct ssh *ssh, Channel *c,
 
        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",
@@ -1238,12 +1234,12 @@ channel_pre_x11_open(struct ssh *ssh, Channel *c,
 }
 
 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);
@@ -1254,7 +1250,7 @@ channel_pre_mux_client(struct ssh *ssh,
        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);
        }
@@ -1533,20 +1529,20 @@ channel_connect_stdio_fwd(struct ssh *ssh,
 
 /* 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 */
@@ -1568,9 +1564,9 @@ channel_pre_dynamic(struct ssh *ssh, Channel *c,
        } 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;
@@ -1650,8 +1646,7 @@ channel_before_prepare_select_rdynamic(struct ssh *ssh, Channel *c)
 
 /* 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;
@@ -1659,7 +1654,7 @@ channel_post_x11_listener(struct ssh *ssh, Channel *c,
        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.");
@@ -1768,8 +1763,7 @@ channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time)
  * 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;
@@ -1777,7 +1771,7 @@ channel_post_port_listener(struct ssh *ssh, Channel *c,
        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.",
@@ -1828,15 +1822,14 @@ channel_post_port_listener(struct ssh *ssh, Channel *c,
  * 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);
@@ -1857,13 +1850,12 @@ channel_post_auth_listener(struct ssh *ssh, Channel *c,
 }
 
 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);
@@ -1922,14 +1914,13 @@ channel_post_connecting(struct ssh *ssh, Channel *c,
 }
 
 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));
@@ -1961,16 +1952,16 @@ channel_handle_rfd(struct ssh *ssh, Channel *c,
 }
 
 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. */
@@ -2041,13 +2032,14 @@ channel_handle_wfd(struct ssh *ssh, Channel *c,
 }
 
 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),
@@ -2067,14 +2059,13 @@ channel_handle_efd_write(struct ssh *ssh, Channel *c,
 }
 
 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));
@@ -2092,8 +2083,7 @@ channel_handle_efd_read(struct ssh *ssh, Channel *c,
 }
 
 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;
@@ -2101,10 +2091,10 @@ channel_handle_efd(struct ssh *ssh, Channel *c,
        /** 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;
 }
@@ -2138,12 +2128,11 @@ channel_check_window(struct ssh *ssh, Channel *c)
 }
 
 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);
 }
 
@@ -2172,12 +2161,11 @@ read_mux(struct ssh *ssh, Channel *c, u_int need)
 }
 
 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;
@@ -2209,14 +2197,14 @@ channel_post_mux_client_read(struct ssh *ssh, Channel *c,
 }
 
 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));
@@ -2231,16 +2219,14 @@ channel_post_mux_client_write(struct ssh *ssh, Channel *c,
 }
 
 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;
@@ -2249,7 +2235,7 @@ channel_post_mux_listener(struct ssh *ssh, Channel *c,
        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");
@@ -2355,8 +2341,7 @@ channel_garbage_collect(struct ssh *ssh, Channel *c)
 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;
@@ -2382,7 +2367,7 @@ channel_handler(struct ssh *ssh, int table,
                         * 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
@@ -2432,7 +2417,9 @@ 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;
+       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 */
 
@@ -2455,8 +2442,44 @@ channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp,
        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);
+               }
+       }
 }
 
 /*
@@ -2466,7 +2489,30 @@ channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **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);
 }
 
 /*
index 633adc3..be4c37e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.138 2021/05/19 01:24:05 djm Exp $ */
+/* $OpenBSD: channels.h,v 1.139 2022/01/06 21:46:23 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -136,6 +136,8 @@ struct Channel {
        int     wfd;            /* write fd */
        int     efd;            /* extended fd */
        int     sock;           /* sock fd */
+       u_int   io_want;        /* bitmask of SSH_CHAN_IO_* */
+       u_int   io_ready;       /* bitmask of SSH_CHAN_IO_* */
        int     ctl_chan;       /* control channel (multiplexed connections) */
        int     isatty;         /* rfd is a tty */
        int     client_tty;     /* (client) TTY has been requested */
@@ -228,6 +230,14 @@ struct Channel {
 #define CHAN_EOF_RCVD                  0x08
 #define CHAN_LOCAL                     0x10
 
+/* file descriptor events */
+#define SSH_CHAN_IO_RFD                        0x01
+#define SSH_CHAN_IO_WFD                        0x02
+#define SSH_CHAN_IO_EFD_R              0x04
+#define SSH_CHAN_IO_EFD_W              0x08
+#define SSH_CHAN_IO_SOCK_R             0x10
+#define SSH_CHAN_IO_SOCK_W             0x20
+
 /* Read buffer size */
 #define CHAN_RBUF      (16*1024)