=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/channels.c,v retrieving revision 1.409 retrieving revision 1.410 diff -u -r1.409 -r1.410 --- src/usr.bin/ssh/channels.c 2022/01/01 01:55:30 1.409 +++ src/usr.bin/ssh/channels.c 2022/01/06 21:46:23 1.410 @@ -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 * Copyright (c) 1995 Tatu Ylonen , 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 @@ } 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 @@ 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 @@ } 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 @@ 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 @@ } 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 @@ 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 @@ /* 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 @@ } 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 @@ /* 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 @@ 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 @@ * 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 @@ 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 @@ * 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 @@ } 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 @@ } 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,17 +1952,17 @@ } 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. */ olen = sshbuf_len(c->output); @@ -2041,14 +2032,15 @@ } 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), sshbuf_len(c->extended)); @@ -2067,14 +2059,13 @@ } 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 @@ } 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 @@ /** 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 @@ } 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 @@ } 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,15 +2197,15 @@ } 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)); if (len == -1 && (errno == EINTR || errno == EAGAIN)) @@ -2231,16 +2219,14 @@ } 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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); } /*