[BACK]Return to channels.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/channels.c between version 1.410 and 1.411

version 1.410, 2022/01/06 21:46:23 version 1.411, 2022/01/06 21:48:38
Line 54 
Line 54 
 #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 78 
Line 79 
 #include "pathnames.h"  #include "pathnames.h"
 #include "match.h"  #include "match.h"
   
   /* 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 92 
Line 96 
 /* 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);
   
 /*  /*
Line 154 
Line 158 
         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;
Line 298 
Line 296 
 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 412 
Line 403 
         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 444 
Line 416 
            (*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 669 
Line 660 
         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 861 
Line 851 
         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 1588 
Line 1579 
   
 /* 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 1889 
Line 1880 
                 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 2389 
Line 2379 
 }  }
   
 /*  /*
  * 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 2405 
Line 2396 
                 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)
  */  {
   #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
   }
   
   /* 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;
   }
   
   /* * Allocate/prepare poll structure */
 void  void
 channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp,  channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp,
     int *maxfdp, u_int *nallocp, time_t *minwait_secs)      u_int *npfd_activep, u_int npfd_reserved, time_t *minwait_secs)
 {  {
         struct ssh_channels *sc = ssh->chanctxt;          struct ssh_channels *sc = ssh->chanctxt;
         u_int i, n, sz, nfdset, oalloc = sc->channels_alloc;          u_int i, oalloc, p, npfd = npfd_reserved;
         Channel *c;  
   
         channel_before_prepare_select(ssh); /* might update channel_max_fd */          channel_before_prepare_io(ssh); /* might create a new channel */
   
         n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd);          /* 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))
                   npfd += sc->channels_alloc * 4;
           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;
   
         nfdset = howmany(n+1, NFDBITS);          oalloc = sc->channels_alloc;
         /* 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? */          channel_handler(ssh, CHAN_PRE, minwait_secs);
         if (*readsetp == NULL || sz > *nallocp) {  
                 *readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask));          if (oalloc != sc->channels_alloc) {
                 *writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask));                  /* shouldn't happen */
                 *nallocp = sz;                  fatal_f("channels_alloc changed during CHAN_PRE "
                       "(was %u, now %u)", oalloc, sc->channels_alloc);
         }          }
         *maxfdp = n;  
         memset(*readsetp, 0, sz);  
         memset(*writesetp, 0, sz);  
   
         if (!ssh_packet_is_rekeying(ssh))          /* Prepare pollfd */
                 channel_handler(ssh, CHAN_PRE, minwait_secs);          p = npfd_reserved;
           for (i = 0; i < sc->channels_alloc; i++)
                   channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd);
           *npfd_activep = p;
   }
   
         /* Convert c->io_want into FD_SET */  static void
         for (i = 0; i < oalloc; i++) {  fd_ready(Channel *c, u_int p, struct pollfd *pfds, int fd,
                 c = sc->channels[i];      const char *what, u_int revents_mask, u_int ready)
                 if (c == NULL)  {
                         continue;          struct pollfd *pfd = &pfds[p];
                 if ((c->io_want & SSH_CHAN_IO_RFD) != 0) {  
                         if (c->rfd == -1)          if (fd == -1)
                                 fatal_f("channel %d: no rfd", c->self);                  return;
                         FD_SET(c->rfd, *readsetp);          dump_channel_poll(__func__, what, c, p, pfd);
                 }          if (pfd->fd != fd) {
                 if ((c->io_want & SSH_CHAN_IO_WFD) != 0) {                  fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d "
                         if (c->wfd == -1)                      "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd,
                                 fatal_f("channel %d: no wfd", c->self);                      c->rfd, c->wfd, c->efd, c->sock);
                         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);  
                 }  
         }          }
           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)
 {  {
         struct ssh_channels *sc = ssh->chanctxt;          struct ssh_channels *sc = ssh->chanctxt;
           u_int i, p;
         Channel *c;          Channel *c;
         u_int i, oalloc = sc->channels_alloc;  
   
         /* Convert FD_SET into c->io_ready */  #ifdef DEBUG_CHANNEL_POLL
         for (i = 0; i < oalloc; i++) {          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];                  c = sc->channels[i];
                 if (c == NULL)                  if (c == NULL || c->pollfd_offset < 0)
                         continue;                          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;                  c->io_ready = 0;
                 if (c->rfd != -1 && FD_ISSET(c->rfd, readset))                  p = c->pollfd_offset;
                         c->io_ready |= SSH_CHAN_IO_RFD;                  /* rfd, potentially shared with wfd, efd and sock */
                 if (c->wfd != -1 && FD_ISSET(c->wfd, writeset))                  if (c->rfd != -1) {
                         c->io_ready |= SSH_CHAN_IO_WFD;                          fd_ready(c, p, pfd, c->rfd, "rfd", POLLIN,
                 if (c->efd != -1 && FD_ISSET(c->efd, readset))                              SSH_CHAN_IO_RFD);
                         c->io_ready |= SSH_CHAN_IO_EFD_R;                          if (c->rfd == c->wfd) {
                 if (c->efd != -1 && FD_ISSET(c->efd, writeset))                                  fd_ready(c, p, pfd, c->wfd, "wfd/r", POLLOUT,
                         c->io_ready |= SSH_CHAN_IO_EFD_W;                                      SSH_CHAN_IO_WFD);
                 if (c->sock != -1 && FD_ISSET(c->sock, readset))                          }
                         c->io_ready |= SSH_CHAN_IO_SOCK_R;                          if (c->rfd == c->efd) {
                 if (c->sock != -1 && FD_ISSET(c->sock, writeset))                                  fd_ready(c, p, pfd, c->efd, "efdr/r", POLLIN,
                         c->io_ready |= SSH_CHAN_IO_SOCK_W;                                      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);          channel_handler(ssh, CHAN_POST, NULL);
 }  }

Legend:
Removed from v.1.410  
changed lines
  Added in v.1.411