[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.140.2.5 and 1.141

version 1.140.2.5, 2002/10/11 14:53:06 version 1.141, 2001/11/29 21:10:51
Line 13 
Line 13 
  * called by a name other than "ssh" or "Secure Shell".   * called by a name other than "ssh" or "Secure Shell".
  *   *
  * SSH2 support added by Markus Friedl.   * SSH2 support added by Markus Friedl.
  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.   * Copyright (c) 1999, 2000, 2001 Markus Friedl.  All rights reserved.
  * Copyright (c) 1999 Dug Song.  All rights reserved.   * Copyright (c) 1999 Dug Song.  All rights reserved.
  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.   * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
  *   *
Line 46 
Line 46 
 #include "ssh2.h"  #include "ssh2.h"
 #include "packet.h"  #include "packet.h"
 #include "xmalloc.h"  #include "xmalloc.h"
   #include "uidswap.h"
 #include "log.h"  #include "log.h"
 #include "misc.h"  #include "misc.h"
 #include "channels.h"  #include "channels.h"
Line 53 
Line 54 
 #include "canohost.h"  #include "canohost.h"
 #include "key.h"  #include "key.h"
 #include "authfd.h"  #include "authfd.h"
 #include "pathnames.h"  
   
   
 /* -- channel core */  /* -- channel core */
Line 128 
Line 128 
   
 #define NUM_SOCKS       10  #define NUM_SOCKS       10
   
   /* Name and directory of socket for authentication agent forwarding. */
   static char *auth_sock_name = NULL;
   static char *auth_sock_dir = NULL;
   
 /* AF_UNSPEC or AF_INET or AF_INET6 */  /* AF_UNSPEC or AF_INET or AF_INET6 */
 static int IPv4or6 = AF_UNSPEC;  static int IPv4or6 = AF_UNSPEC;
   
Line 141 
Line 145 
 {  {
         Channel *c;          Channel *c;
   
         if (id < 0 || id >= channels_alloc) {          if (id < 0 || id > channels_alloc) {
                 log("channel_lookup: %d: bad id", id);                  log("channel_lookup: %d: bad id", id);
                 return NULL;                  return NULL;
         }          }
Line 205 
Line 209 
   
 Channel *  Channel *
 channel_new(char *ctype, int type, int rfd, int wfd, int efd,  channel_new(char *ctype, int type, int rfd, int wfd, int efd,
     u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)      int window, int maxpack, int extusage, char *remote_name, int nonblock)
 {  {
         int i, found;          int i, found;
         Channel *c;          Channel *c;
   
         /* Do initial allocation if this is the first call. */          /* Do initial allocation if this is the first call. */
         if (channels_alloc == 0) {          if (channels_alloc == 0) {
                   chan_init();
                 channels_alloc = 10;                  channels_alloc = 10;
                 channels = xmalloc(channels_alloc * sizeof(Channel *));                  channels = xmalloc(channels_alloc * sizeof(Channel *));
                 for (i = 0; i < channels_alloc; i++)                  for (i = 0; i < channels_alloc; i++)
Line 229 
Line 234 
                 /* There are no free slots.  Take last+1 slot and expand the array.  */                  /* There are no free slots.  Take last+1 slot and expand the array.  */
                 found = channels_alloc;                  found = channels_alloc;
                 channels_alloc += 10;                  channels_alloc += 10;
                 if (channels_alloc > 10000)  
                         fatal("channel_new: internal error: channels_alloc %d "  
                             "too big.", channels_alloc);  
                 debug2("channel: expanding %d", channels_alloc);                  debug2("channel: expanding %d", channels_alloc);
                 channels = xrealloc(channels, channels_alloc * sizeof(Channel *));                  channels = xrealloc(channels, channels_alloc * sizeof(Channel *));
                 for (i = found; i < channels_alloc; i++)                  for (i = found; i < channels_alloc; i++)
Line 243 
Line 245 
         buffer_init(&c->input);          buffer_init(&c->input);
         buffer_init(&c->output);          buffer_init(&c->output);
         buffer_init(&c->extended);          buffer_init(&c->extended);
         c->ostate = CHAN_OUTPUT_OPEN;          chan_init_iostates(c);
         c->istate = CHAN_INPUT_OPEN;  
         c->flags = 0;  
         channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);          channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
         c->self = found;          c->self = found;
         c->type = type;          c->type = type;
Line 258 
Line 258 
         c->remote_name = remote_name;          c->remote_name = remote_name;
         c->remote_window = 0;          c->remote_window = 0;
         c->remote_maxpacket = 0;          c->remote_maxpacket = 0;
           c->cb_fn = NULL;
           c->cb_arg = NULL;
           c->cb_event = 0;
         c->force_drain = 0;          c->force_drain = 0;
         c->single_connection = 0;  
         c->detach_user = NULL;          c->detach_user = NULL;
         c->confirm = NULL;  
         c->input_filter = NULL;          c->input_filter = NULL;
         debug("channel %d: new [%s]", found, remote_name);          debug("channel %d: new [%s]", found, remote_name);
         return c;          return c;
Line 360 
Line 361 
  */   */
   
 void  void
 channel_close_all(void)  channel_close_all()
 {  {
         int i;          int i;
   
Line 401 
Line 402 
  */   */
   
 int  int
 channel_not_very_much_buffered_data(void)  channel_not_very_much_buffered_data()
 {  {
         u_int i;          u_int i;
         Channel *c;          Channel *c;
Line 431 
Line 432 
 /* Returns true if any channel is still open. */  /* Returns true if any channel is still open. */
   
 int  int
 channel_still_open(void)  channel_still_open()
 {  {
         int i;          int i;
         Channel *c;          Channel *c;
Line 474 
Line 475 
 /* Returns the id of an open channel suitable for keepaliving */  /* Returns the id of an open channel suitable for keepaliving */
   
 int  int
 channel_find_open(void)  channel_find_open()
 {  {
         int i;          int i;
         Channel *c;          Channel *c;
Line 519 
Line 520 
  */   */
   
 char *  char *
 channel_open_message(void)  channel_open_message()
 {  {
         Buffer buffer;          Buffer buffer;
         Channel *c;          Channel *c;
Line 572 
Line 573 
 channel_send_open(int id)  channel_send_open(int id)
 {  {
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
   
         if (c == NULL) {          if (c == NULL) {
                 log("channel_send_open: %d: bad id", id);                  log("channel_send_open: %d: bad id", id);
                 return;                  return;
Line 587 
Line 587 
 }  }
   
 void  void
 channel_request_start(int local_id, char *service, int wantconfirm)  channel_request(int id, char *service, int wantconfirm)
 {  {
         Channel *c = channel_lookup(local_id);          channel_request_start(id, service, wantconfirm);
           packet_send();
           debug("channel request %d: %s", id, service) ;
   }
   void
   channel_request_start(int id, char *service, int wantconfirm)
   {
           Channel *c = channel_lookup(id);
         if (c == NULL) {          if (c == NULL) {
                 log("channel_request_start: %d: unknown channel id", local_id);                  log("channel_request: %d: bad id", id);
                 return;                  return;
         }          }
         debug("channel request %d: %s", local_id, service) ;  
         packet_start(SSH2_MSG_CHANNEL_REQUEST);          packet_start(SSH2_MSG_CHANNEL_REQUEST);
         packet_put_int(c->remote_id);          packet_put_int(c->remote_id);
         packet_put_cstring(service);          packet_put_cstring(service);
         packet_put_char(wantconfirm);          packet_put_char(wantconfirm);
 }  }
 void  void
 channel_register_confirm(int id, channel_callback_fn *fn)  channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg)
 {  {
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
   
         if (c == NULL) {          if (c == NULL) {
                 log("channel_register_comfirm: %d: bad id", id);                  log("channel_register_callback: %d: bad id", id);
                 return;                  return;
         }          }
         c->confirm = fn;          c->cb_event = mtype;
           c->cb_fn = fn;
           c->cb_arg = arg;
 }  }
 void  void
 channel_register_cleanup(int id, channel_callback_fn *fn)  channel_register_cleanup(int id, channel_callback_fn *fn)
 {  {
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
   
         if (c == NULL) {          if (c == NULL) {
                 log("channel_register_cleanup: %d: bad id", id);                  log("channel_register_cleanup: %d: bad id", id);
                 return;                  return;
Line 627 
Line 632 
 channel_cancel_cleanup(int id)  channel_cancel_cleanup(int id)
 {  {
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
   
         if (c == NULL) {          if (c == NULL) {
                 log("channel_cancel_cleanup: %d: bad id", id);                  log("channel_cancel_cleanup: %d: bad id", id);
                 return;                  return;
Line 638 
Line 642 
 channel_register_filter(int id, channel_filter_fn *fn)  channel_register_filter(int id, channel_filter_fn *fn)
 {  {
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
   
         if (c == NULL) {          if (c == NULL) {
                 log("channel_register_filter: %d: bad id", id);                  log("channel_register_filter: %d: bad id", id);
                 return;                  return;
Line 648 
Line 651 
   
 void  void
 channel_set_fds(int id, int rfd, int wfd, int efd,  channel_set_fds(int id, int rfd, int wfd, int efd,
     int extusage, int nonblock, u_int window_max)      int extusage, int nonblock)
 {  {
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
   
         if (c == NULL || c->type != SSH_CHANNEL_LARVAL)          if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
                 fatal("channel_activate for non-larval channel %d.", id);                  fatal("channel_activate for non-larval channel %d.", id);
         channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);          channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
         c->type = SSH_CHANNEL_OPEN;          c->type = SSH_CHANNEL_OPEN;
         c->local_window = c->local_window_max = window_max;          /* XXX window size? */
           c->local_window = c->local_window_max = c->local_maxpacket * 2;
         packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);          packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
         packet_put_int(c->remote_id);          packet_put_int(c->remote_id);
         packet_put_int(c->local_window);          packet_put_int(c->local_window);
Line 698 
Line 701 
 }  }
   
 static void  static void
 channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)  channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset)
 {  {
         u_int limit = compat20 ? c->remote_window : packet_get_maxsize();          /* test whether sockets are 'alive' for read/write */
           if (c->istate == CHAN_INPUT_OPEN)
                   if (buffer_len(&c->input) < packet_get_maxsize())
                           FD_SET(c->sock, readset);
           if (c->ostate == CHAN_OUTPUT_OPEN ||
               c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
                   if (buffer_len(&c->output) > 0) {
                           FD_SET(c->sock, writeset);
                   } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
                           chan_obuf_empty(c);
                   }
           }
   }
   
   static void
   channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset)
   {
         if (c->istate == CHAN_INPUT_OPEN &&          if (c->istate == CHAN_INPUT_OPEN &&
             limit > 0 &&              c->remote_window > 0 &&
             buffer_len(&c->input) < limit)              buffer_len(&c->input) < c->remote_window)
                 FD_SET(c->rfd, readset);                  FD_SET(c->rfd, readset);
         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 (buffer_len(&c->output) > 0) {                  if (buffer_len(&c->output) > 0) {
                         FD_SET(c->wfd, writeset);                          FD_SET(c->wfd, writeset);
                 } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {                  } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
                         if (CHANNEL_EFD_OUTPUT_ACTIVE(c))                          chan_obuf_empty(c);
                                debug2("channel %d: obuf_empty delayed efd %d/(%d)",  
                                    c->self, c->efd, buffer_len(&c->extended));  
                         else  
                                 chan_obuf_empty(c);  
                 }                  }
         }          }
         /** XXX check close conditions, too */          /** XXX check close conditions, too */
         if (compat20 && c->efd != -1) {          if (c->efd != -1) {
                 if (c->extended_usage == CHAN_EXTENDED_WRITE &&                  if (c->extended_usage == CHAN_EXTENDED_WRITE &&
                     buffer_len(&c->extended) > 0)                      buffer_len(&c->extended) > 0)
                         FD_SET(c->efd, writeset);                          FD_SET(c->efd, writeset);
                 else if (!(c->flags & CHAN_EOF_SENT) &&                  else if (c->extended_usage == CHAN_EXTENDED_READ &&
                     c->extended_usage == CHAN_EXTENDED_READ &&  
                     buffer_len(&c->extended) < c->remote_window)                      buffer_len(&c->extended) < c->remote_window)
                         FD_SET(c->efd, readset);                          FD_SET(c->efd, readset);
         }          }
Line 771 
Line 784 
                 return 0;                  return 0;
   
         /* Parse the lengths of variable-length fields. */          /* Parse the lengths of variable-length fields. */
         ucp = buffer_ptr(b);          ucp = (u_char *) buffer_ptr(b);
         if (ucp[0] == 0x42) {   /* Byte order MSB first. */          if (ucp[0] == 0x42) {   /* Byte order MSB first. */
                 proto_len = 256 * ucp[6] + ucp[7];                  proto_len = 256 * ucp[6] + ucp[7];
                 data_len = 256 * ucp[8] + ucp[9];                  data_len = 256 * ucp[8] + ucp[9];
Line 780 
Line 793 
                 data_len = ucp[8] + 256 * ucp[9];                  data_len = ucp[8] + 256 * ucp[9];
         } else {          } else {
                 debug("Initial X11 packet contains bad byte order byte: 0x%x",                  debug("Initial X11 packet contains bad byte order byte: 0x%x",
                     ucp[0]);                        ucp[0]);
                 return -1;                  return -1;
         }          }
   
Line 822 
Line 835 
 channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)  channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
 {  {
         int ret = x11_open_helper(&c->output);          int ret = x11_open_helper(&c->output);
   
         if (ret == 1) {          if (ret == 1) {
                 /* Start normal processing for the channel. */                  /* Start normal processing for the channel. */
                 c->type = SSH_CHANNEL_OPEN;                  c->type = SSH_CHANNEL_OPEN;
Line 853 
Line 865 
   
         if (ret == 1) {          if (ret == 1) {
                 c->type = SSH_CHANNEL_OPEN;                  c->type = SSH_CHANNEL_OPEN;
                 channel_pre_open(c, readset, writeset);  
         } else if (ret == -1) {  
                 log("X11 connection rejected because of wrong authentication.");  
                 debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);  
                 chan_read_failed(c);  
                 buffer_clear(&c->input);  
                 chan_ibuf_empty(c);  
                 buffer_clear(&c->output);  
                 /* for proto v1, the peer will send an IEOF */  
                 if (compat20)                  if (compat20)
                         chan_write_failed(c);                          channel_pre_open_20(c, readset, writeset);
                 else                  else
                         c->type = SSH_CHANNEL_OPEN;                          channel_pre_open_15(c, readset, writeset);
           } else if (ret == -1) {
                   debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
                   chan_read_failed(c);    /** force close? */
                   chan_write_failed(c);
                 debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);                  debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
         }          }
 }  }
Line 874 
Line 881 
 static int  static int
 channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)  channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
 {  {
         char *p, *host;          u_char *p, *host;
         int len, have, i, found;          int len, have, i, found;
         char username[256];          char username[256];
         struct {          struct {
                 u_int8_t version;                  u_int8_t version;
                 u_int8_t command;                  u_int8_t command;
Line 923 
Line 930 
         host = inet_ntoa(s4_req.dest_addr);          host = inet_ntoa(s4_req.dest_addr);
         strlcpy(c->path, host, sizeof(c->path));          strlcpy(c->path, host, sizeof(c->path));
         c->host_port = ntohs(s4_req.dest_port);          c->host_port = ntohs(s4_req.dest_port);
   
         debug("channel %d: dynamic request: socks4 host %s port %u command %u",          debug("channel %d: dynamic request: socks4 host %s port %u command %u",
             c->self, host, c->host_port, s4_req.command);              c->self, host, c->host_port, s4_req.command);
   
Line 995 
Line 1002 
                 debug("X11 connection requested.");                  debug("X11 connection requested.");
                 addrlen = sizeof(addr);                  addrlen = sizeof(addr);
                 newsock = accept(c->sock, &addr, &addrlen);                  newsock = accept(c->sock, &addr, &addrlen);
                 if (c->single_connection) {  
                         debug("single_connection: closing X11 listener.");  
                         channel_close_fd(&c->sock);  
                         chan_mark_dead(c);  
                 }  
                 if (newsock < 0) {                  if (newsock < 0) {
                         error("accept: %.100s", strerror(errno));                          error("accept: %.100s", strerror(errno));
                         return;                          return;
                 }                  }
                 set_nodelay(newsock);  
                 remote_ipaddr = get_peer_ipaddr(newsock);                  remote_ipaddr = get_peer_ipaddr(newsock);
                 remote_port = get_peer_port(newsock);                  remote_port = get_peer_port(newsock);
                 snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",                  snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
Line 1014 
Line 1015 
                     SSH_CHANNEL_OPENING, newsock, newsock, -1,                      SSH_CHANNEL_OPENING, newsock, newsock, -1,
                     c->local_window_max, c->local_maxpacket,                      c->local_window_max, c->local_maxpacket,
                     0, xstrdup(buf), 1);                      0, xstrdup(buf), 1);
                   if (nc == NULL) {
                           close(newsock);
                           xfree(remote_ipaddr);
                           return;
                   }
                 if (compat20) {                  if (compat20) {
                         packet_start(SSH2_MSG_CHANNEL_OPEN);                          packet_start(SSH2_MSG_CHANNEL_OPEN);
                         packet_put_cstring("x11");                          packet_put_cstring("x11");
                         packet_put_int(nc->self);                          packet_put_int(nc->self);
                         packet_put_int(nc->local_window_max);                          packet_put_int(c->local_window_max);
                         packet_put_int(nc->local_maxpacket);                          packet_put_int(c->local_maxpacket);
                         /* originator ipaddr and port */                          /* originator ipaddr and port */
                         packet_put_cstring(remote_ipaddr);                          packet_put_cstring(remote_ipaddr);
                         if (datafellows & SSH_BUG_X11FWD) {                          if (datafellows & SSH_BUG_X11FWD) {
Line 1127 
Line 1133 
                         error("accept: %.100s", strerror(errno));                          error("accept: %.100s", strerror(errno));
                         return;                          return;
                 }                  }
                 set_nodelay(newsock);  
                 nc = channel_new(rtype,                  nc = channel_new(rtype,
                     nextstate, newsock, newsock, -1,                      nextstate, newsock, newsock, -1,
                     c->local_window_max, c->local_maxpacket,                      c->local_window_max, c->local_maxpacket,
                     0, xstrdup(rtype), 1);                      0, xstrdup(rtype), 1);
                   if (nc == NULL) {
                           error("channel_post_port_listener: no new channel:");
                           close(newsock);
                           return;
                   }
                 nc->listening_port = c->listening_port;                  nc->listening_port = c->listening_port;
                 nc->host_port = c->host_port;                  nc->host_port = c->host_port;
                 strlcpy(nc->path, c->path, sizeof(nc->path));                  strlcpy(nc->path, c->path, sizeof(nc->path));
Line 1174 
Line 1184 
                     SSH_CHANNEL_OPENING, newsock, newsock, -1,                      SSH_CHANNEL_OPENING, newsock, newsock, -1,
                     c->local_window_max, c->local_maxpacket,                      c->local_window_max, c->local_maxpacket,
                     0, name, 1);                      0, name, 1);
                   if (nc == NULL) {
                           error("channel_post_auth_listener: channel_new failed");
                           xfree(name);
                           close(newsock);
                   }
                 if (compat20) {                  if (compat20) {
                         packet_start(SSH2_MSG_CHANNEL_OPEN);                          packet_start(SSH2_MSG_CHANNEL_OPEN);
                         packet_put_cstring("auth-agent@openssh.com");                          packet_put_cstring("auth-agent@openssh.com");
Line 1195 
Line 1210 
         socklen_t sz = sizeof(err);          socklen_t sz = sizeof(err);
   
         if (FD_ISSET(c->sock, writeset)) {          if (FD_ISSET(c->sock, writeset)) {
                 if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {                  if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, (char *)&err,
                       &sz) < 0) {
                         err = errno;                          err = errno;
                         error("getsockopt SO_ERROR failed");                          error("getsockopt SO_ERROR failed");
                 }                  }
Line 1253 
Line 1269 
                                 chan_mark_dead(c);                                  chan_mark_dead(c);
                                 return -1;                                  return -1;
                         } else if (compat13) {                          } else if (compat13) {
                                 buffer_clear(&c->output);                                  buffer_consume(&c->output, buffer_len(&c->output));
                                 c->type = SSH_CHANNEL_INPUT_DRAINING;                                  c->type = SSH_CHANNEL_INPUT_DRAINING;
                                 debug("channel %d: input draining.", c->self);                                  debug("channel %d: input draining.", c->self);
                         } else {                          } else {
Line 1261 
Line 1277 
                         }                          }
                         return -1;                          return -1;
                 }                  }
                 if (c->input_filter != NULL) {                  if(c->input_filter != NULL) {
                         if (c->input_filter(c, buf, len) == -1) {                          if (c->input_filter(c, buf, len) == -1) {
                                 debug("channel %d: filter stops", c->self);                                  debug("channel %d: filter stops", c->self);
                                 chan_read_failed(c);                                  chan_read_failed(c);
Line 1295 
Line 1311 
                                 chan_mark_dead(c);                                  chan_mark_dead(c);
                                 return -1;                                  return -1;
                         } else if (compat13) {                          } else if (compat13) {
                                 buffer_clear(&c->output);                                  buffer_consume(&c->output, buffer_len(&c->output));
                                 debug("channel %d: input draining.", c->self);                                  debug("channel %d: input draining.", c->self);
                                 c->type = SSH_CHANNEL_INPUT_DRAINING;                                  c->type = SSH_CHANNEL_INPUT_DRAINING;
                         } else {                          } else {
Line 1352 
Line 1368 
                     FD_ISSET(c->efd, readset)) {                      FD_ISSET(c->efd, readset)) {
                         len = read(c->efd, buf, sizeof(buf));                          len = read(c->efd, buf, sizeof(buf));
                         debug2("channel %d: read %d from efd %d",                          debug2("channel %d: read %d from efd %d",
                             c->self, len, c->efd);                               c->self, len, c->efd);
                         if (len < 0 && (errno == EINTR || errno == EAGAIN))                          if (len < 0 && (errno == EINTR || errno == EAGAIN))
                                 return 1;                                  return 1;
                         if (len <= 0) {                          if (len <= 0) {
Line 1387 
Line 1403 
 }  }
   
 static void  static void
 channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)  channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset)
 {  {
         if (c->delayed)          if (c->delayed)
                 return;                  return;
         channel_handle_rfd(c, readset, writeset);          channel_handle_rfd(c, readset, writeset);
         channel_handle_wfd(c, readset, writeset);          channel_handle_wfd(c, readset, writeset);
         if (!compat20)  }
   
   static void
   channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset)
   {
           if (c->delayed)
                 return;                  return;
           channel_handle_rfd(c, readset, writeset);
           channel_handle_wfd(c, readset, writeset);
         channel_handle_efd(c, readset, writeset);          channel_handle_efd(c, readset, writeset);
   
         channel_check_window(c);          channel_check_window(c);
 }  }
   
Line 1403 
Line 1427 
 channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)  channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
 {  {
         int len;          int len;
   
         /* Send buffered output data to the socket. */          /* Send buffered output data to the socket. */
         if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {          if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
                 len = write(c->sock, buffer_ptr(&c->output),                  len = write(c->sock, buffer_ptr(&c->output),
                             buffer_len(&c->output));                              buffer_len(&c->output));
                 if (len <= 0)                  if (len <= 0)
                         buffer_clear(&c->output);                          buffer_consume(&c->output, buffer_len(&c->output));
                 else                  else
                         buffer_consume(&c->output, len);                          buffer_consume(&c->output, len);
         }          }
Line 1418 
Line 1441 
 static void  static void
 channel_handler_init_20(void)  channel_handler_init_20(void)
 {  {
         channel_pre[SSH_CHANNEL_OPEN] =                 &channel_pre_open;          channel_pre[SSH_CHANNEL_OPEN] =                 &channel_pre_open_20;
         channel_pre[SSH_CHANNEL_X11_OPEN] =             &channel_pre_x11_open;          channel_pre[SSH_CHANNEL_X11_OPEN] =             &channel_pre_x11_open;
         channel_pre[SSH_CHANNEL_PORT_LISTENER] =        &channel_pre_listener;          channel_pre[SSH_CHANNEL_PORT_LISTENER] =        &channel_pre_listener;
         channel_pre[SSH_CHANNEL_RPORT_LISTENER] =       &channel_pre_listener;          channel_pre[SSH_CHANNEL_RPORT_LISTENER] =       &channel_pre_listener;
Line 1427 
Line 1450 
         channel_pre[SSH_CHANNEL_CONNECTING] =           &channel_pre_connecting;          channel_pre[SSH_CHANNEL_CONNECTING] =           &channel_pre_connecting;
         channel_pre[SSH_CHANNEL_DYNAMIC] =              &channel_pre_dynamic;          channel_pre[SSH_CHANNEL_DYNAMIC] =              &channel_pre_dynamic;
   
         channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open;          channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open_2;
         channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;          channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;
         channel_post[SSH_CHANNEL_RPORT_LISTENER] =      &channel_post_port_listener;          channel_post[SSH_CHANNEL_RPORT_LISTENER] =      &channel_post_port_listener;
         channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;          channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;
         channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;          channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;
         channel_post[SSH_CHANNEL_CONNECTING] =          &channel_post_connecting;          channel_post[SSH_CHANNEL_CONNECTING] =          &channel_post_connecting;
         channel_post[SSH_CHANNEL_DYNAMIC] =             &channel_post_open;          channel_post[SSH_CHANNEL_DYNAMIC] =             &channel_post_open_2;
 }  }
   
 static void  static void
Line 1449 
Line 1472 
         channel_pre[SSH_CHANNEL_CONNECTING] =           &channel_pre_connecting;          channel_pre[SSH_CHANNEL_CONNECTING] =           &channel_pre_connecting;
         channel_pre[SSH_CHANNEL_DYNAMIC] =              &channel_pre_dynamic;          channel_pre[SSH_CHANNEL_DYNAMIC] =              &channel_pre_dynamic;
   
         channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open;          channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open_1;
         channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;          channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;
         channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;          channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;
         channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;          channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;
         channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =     &channel_post_output_drain_13;          channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =     &channel_post_output_drain_13;
         channel_post[SSH_CHANNEL_CONNECTING] =          &channel_post_connecting;          channel_post[SSH_CHANNEL_CONNECTING] =          &channel_post_connecting;
         channel_post[SSH_CHANNEL_DYNAMIC] =             &channel_post_open;          channel_post[SSH_CHANNEL_DYNAMIC] =             &channel_post_open_1;
 }  }
   
 static void  static void
 channel_handler_init_15(void)  channel_handler_init_15(void)
 {  {
         channel_pre[SSH_CHANNEL_OPEN] =                 &channel_pre_open;          channel_pre[SSH_CHANNEL_OPEN] =                 &channel_pre_open_15;
         channel_pre[SSH_CHANNEL_X11_OPEN] =             &channel_pre_x11_open;          channel_pre[SSH_CHANNEL_X11_OPEN] =             &channel_pre_x11_open;
         channel_pre[SSH_CHANNEL_X11_LISTENER] =         &channel_pre_listener;          channel_pre[SSH_CHANNEL_X11_LISTENER] =         &channel_pre_listener;
         channel_pre[SSH_CHANNEL_PORT_LISTENER] =        &channel_pre_listener;          channel_pre[SSH_CHANNEL_PORT_LISTENER] =        &channel_pre_listener;
Line 1472 
Line 1495 
         channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;          channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;
         channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;          channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;
         channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;          channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;
         channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open;          channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open_1;
         channel_post[SSH_CHANNEL_CONNECTING] =          &channel_post_connecting;          channel_post[SSH_CHANNEL_CONNECTING] =          &channel_post_connecting;
         channel_post[SSH_CHANNEL_DYNAMIC] =             &channel_post_open;          channel_post[SSH_CHANNEL_DYNAMIC] =             &channel_post_open_1;
 }  }
   
 static void  static void
 channel_handler_init(void)  channel_handler_init(void)
 {  {
         int i;          int i;
           for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
         for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {  
                 channel_pre[i] = NULL;                  channel_pre[i] = NULL;
                 channel_post[i] = NULL;                  channel_post[i] = NULL;
         }          }
Line 1579 
Line 1601 
 /* If there is data to send to the connection, enqueue some of it now. */  /* If there is data to send to the connection, enqueue some of it now. */
   
 void  void
 channel_output_poll(void)  channel_output_poll()
 {  {
           int len, i;
         Channel *c;          Channel *c;
         int i;  
         u_int len;  
   
         for (i = 0; i < channels_alloc; i++) {          for (i = 0; i < channels_alloc; i++) {
                 c = channels[i];                  c = channels[i];
Line 1646 
Line 1667 
                                 fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");                                  fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
                         /*                          /*
                          * input-buffer is empty and read-socket shutdown:                           * input-buffer is empty and read-socket shutdown:
                          * tell peer, that we will not send more data: send IEOF.                           * tell peer, that we will not send more data: send IEOF
                          * hack for extended data: delay EOF if EFD still in use.  
                          */                           */
                         if (CHANNEL_EFD_INPUT_ACTIVE(c))                          chan_ibuf_empty(c);
                                debug2("channel %d: ibuf_empty delayed efd %d/(%d)",  
                                    c->self, c->efd, buffer_len(&c->extended));  
                         else  
                                 chan_ibuf_empty(c);  
                 }                  }
                 /* Send extended data, i.e. stderr */                  /* Send extended data, i.e. stderr */
                 if (compat20 &&                  if (compat20 &&
                     !(c->flags & CHAN_EOF_SENT) &&  
                     c->remote_window > 0 &&                      c->remote_window > 0 &&
                     (len = buffer_len(&c->extended)) > 0 &&                      (len = buffer_len(&c->extended)) > 0 &&
                     c->extended_usage == CHAN_EXTENDED_READ) {                      c->extended_usage == CHAN_EXTENDED_READ) {
                         debug2("channel %d: rwin %u elen %u euse %d",                          debug2("channel %d: rwin %d elen %d euse %d",
                             c->self, c->remote_window, buffer_len(&c->extended),                              c->self, c->remote_window, buffer_len(&c->extended),
                             c->extended_usage);                              c->extended_usage);
                         if (len > c->remote_window)                          if (len > c->remote_window)
Line 1684 
Line 1699 
 /* -- protocol input */  /* -- protocol input */
   
 void  void
 channel_input_data(int type, u_int32_t seq, void *ctxt)  channel_input_data(int type, int plen, void *ctxt)
 {  {
         int id;          int id;
         char *data;          char *data;
Line 1708 
Line 1723 
   
         /* Get the data. */          /* Get the data. */
         data = packet_get_string(&data_len);          data = packet_get_string(&data_len);
           packet_done();
   
         if (compat20) {          if (compat20){
                 if (data_len > c->local_maxpacket) {                  if (data_len > c->local_maxpacket) {
                         log("channel %d: rcvd big packet %d, maxpack %d",                          log("channel %d: rcvd big packet %d, maxpack %d",
                             c->self, data_len, c->local_maxpacket);                              c->self, data_len, c->local_maxpacket);
Line 1721 
Line 1737 
                         return;                          return;
                 }                  }
                 c->local_window -= data_len;                  c->local_window -= data_len;
           }else{
                   packet_integrity_check(plen, 4 + 4 + data_len, type);
         }          }
         packet_check_eom();  
         buffer_append(&c->output, data, data_len);          buffer_append(&c->output, data, data_len);
         xfree(data);          xfree(data);
 }  }
   
 void  void
 channel_input_extended_data(int type, u_int32_t seq, void *ctxt)  channel_input_extended_data(int type, int plen, void *ctxt)
 {  {
         int id;          int id;
           int tcode;
         char *data;          char *data;
         u_int data_len, tcode;          u_int data_len;
         Channel *c;          Channel *c;
   
         /* Get the channel number and verify it. */          /* Get the channel number and verify it. */
Line 1745 
Line 1763 
                 log("channel %d: ext data for non open", id);                  log("channel %d: ext data for non open", id);
                 return;                  return;
         }          }
         if (c->flags & CHAN_EOF_RCVD) {  
                 if (datafellows & SSH_BUG_EXTEOF)  
                         debug("channel %d: accepting ext data after eof", id);  
                 else  
                         packet_disconnect("Received extended_data after EOF "  
                             "on channel %d.", id);  
         }  
         tcode = packet_get_int();          tcode = packet_get_int();
         if (c->efd == -1 ||          if (c->efd == -1 ||
             c->extended_usage != CHAN_EXTENDED_WRITE ||              c->extended_usage != CHAN_EXTENDED_WRITE ||
Line 1760 
Line 1771 
                 return;                  return;
         }          }
         data = packet_get_string(&data_len);          data = packet_get_string(&data_len);
         packet_check_eom();          packet_done();
         if (data_len > c->local_window) {          if (data_len > c->local_window) {
                 log("channel %d: rcvd too much extended_data %d, win %d",                  log("channel %d: rcvd too much extended_data %d, win %d",
                     c->self, data_len, c->local_window);                      c->self, data_len, c->local_window);
Line 1774 
Line 1785 
 }  }
   
 void  void
 channel_input_ieof(int type, u_int32_t seq, void *ctxt)  channel_input_ieof(int type, int plen, void *ctxt)
 {  {
         int id;          int id;
         Channel *c;          Channel *c;
   
           packet_integrity_check(plen, 4, type);
   
         id = packet_get_int();          id = packet_get_int();
         packet_check_eom();  
         c = channel_lookup(id);          c = channel_lookup(id);
         if (c == NULL)          if (c == NULL)
                 packet_disconnect("Received ieof for nonexistent channel %d.", id);                  packet_disconnect("Received ieof for nonexistent channel %d.", id);
         chan_rcvd_ieof(c);          chan_rcvd_ieof(c);
   
         /* XXX force input close */          /* XXX force input close */
         if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {          if (c->force_drain) {
                 debug("channel %d: FORCE input drain", c->self);                  debug("channel %d: FORCE input drain", c->self);
                 c->istate = CHAN_INPUT_WAIT_DRAIN;                  c->istate = CHAN_INPUT_WAIT_DRAIN;
                 if (buffer_len(&c->input) == 0)  
                         chan_ibuf_empty(c);  
         }          }
   
 }  }
   
 void  void
 channel_input_close(int type, u_int32_t seq, void *ctxt)  channel_input_close(int type, int plen, void *ctxt)
 {  {
         int id;          int id;
         Channel *c;          Channel *c;
   
           packet_integrity_check(plen, 4, type);
   
         id = packet_get_int();          id = packet_get_int();
         packet_check_eom();  
         c = channel_lookup(id);          c = channel_lookup(id);
         if (c == NULL)          if (c == NULL)
                 packet_disconnect("Received close for nonexistent channel %d.", id);                  packet_disconnect("Received close for nonexistent channel %d.", id);
Line 1828 
Line 1839 
                  * Not a closed channel - mark it as draining, which will                   * Not a closed channel - mark it as draining, which will
                  * cause it to be freed later.                   * cause it to be freed later.
                  */                   */
                 buffer_clear(&c->input);                  buffer_consume(&c->input, buffer_len(&c->input));
                 c->type = SSH_CHANNEL_OUTPUT_DRAINING;                  c->type = SSH_CHANNEL_OUTPUT_DRAINING;
         }          }
 }  }
   
 /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */  /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
 void  void
 channel_input_oclose(int type, u_int32_t seq, void *ctxt)  channel_input_oclose(int type, int plen, void *ctxt)
 {  {
         int id = packet_get_int();          int id = packet_get_int();
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
           packet_integrity_check(plen, 4, type);
         packet_check_eom();  
         if (c == NULL)          if (c == NULL)
                 packet_disconnect("Received oclose for nonexistent channel %d.", id);                  packet_disconnect("Received oclose for nonexistent channel %d.", id);
         chan_rcvd_oclose(c);          chan_rcvd_oclose(c);
 }  }
   
 void  void
 channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)  channel_input_close_confirmation(int type, int plen, void *ctxt)
 {  {
         int id = packet_get_int();          int id = packet_get_int();
         Channel *c = channel_lookup(id);          Channel *c = channel_lookup(id);
   
         packet_check_eom();          packet_done();
         if (c == NULL)          if (c == NULL)
                 packet_disconnect("Received close confirmation for "                  packet_disconnect("Received close confirmation for "
                     "out-of-range channel %d.", id);                      "out-of-range channel %d.", id);
Line 1863 
Line 1873 
 }  }
   
 void  void
 channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)  channel_input_open_confirmation(int type, int plen, void *ctxt)
 {  {
         int id, remote_id;          int id, remote_id;
         Channel *c;          Channel *c;
   
           if (!compat20)
                   packet_integrity_check(plen, 4 + 4, type);
   
         id = packet_get_int();          id = packet_get_int();
         c = channel_lookup(id);          c = channel_lookup(id);
   
Line 1882 
Line 1895 
         if (compat20) {          if (compat20) {
                 c->remote_window = packet_get_int();                  c->remote_window = packet_get_int();
                 c->remote_maxpacket = packet_get_int();                  c->remote_maxpacket = packet_get_int();
                 if (c->confirm) {                  packet_done();
                   if (c->cb_fn != NULL && c->cb_event == type) {
                         debug2("callback start");                          debug2("callback start");
                         c->confirm(c->self, NULL);                          c->cb_fn(c->self, c->cb_arg);
                         debug2("callback done");                          debug2("callback done");
                 }                  }
                 debug("channel %d: open confirm rwindow %u rmax %u", c->self,                  debug("channel %d: open confirm rwindow %d rmax %d", c->self,
                     c->remote_window, c->remote_maxpacket);                      c->remote_window, c->remote_maxpacket);
         }          }
         packet_check_eom();  
 }  }
   
 static char *  static char *
 reason2txt(int reason)  reason2txt(int reason)
 {  {
         switch (reason) {          switch(reason) {
         case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:          case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
                 return "administratively prohibited";                  return "administratively prohibited";
         case SSH2_OPEN_CONNECT_FAILED:          case SSH2_OPEN_CONNECT_FAILED:
Line 1910 
Line 1923 
 }  }
   
 void  void
 channel_input_open_failure(int type, u_int32_t seq, void *ctxt)  channel_input_open_failure(int type, int plen, void *ctxt)
 {  {
         int id, reason;          int id, reason;
         char *msg = NULL, *lang = NULL;          char *msg = NULL, *lang = NULL;
         Channel *c;          Channel *c;
   
           if (!compat20)
                   packet_integrity_check(plen, 4, type);
   
         id = packet_get_int();          id = packet_get_int();
         c = channel_lookup(id);          c = channel_lookup(id);
   
Line 1928 
Line 1944 
                         msg  = packet_get_string(NULL);                          msg  = packet_get_string(NULL);
                         lang = packet_get_string(NULL);                          lang = packet_get_string(NULL);
                 }                  }
                   packet_done();
                 log("channel %d: open failed: %s%s%s", id,                  log("channel %d: open failed: %s%s%s", id,
                     reason2txt(reason), msg ? ": ": "", msg ? msg : "");                      reason2txt(reason), msg ? ": ": "", msg ? msg : "");
                 if (msg != NULL)                  if (msg != NULL)
Line 1935 
Line 1952 
                 if (lang != NULL)                  if (lang != NULL)
                         xfree(lang);                          xfree(lang);
         }          }
         packet_check_eom();  
         /* Free the channel.  This will also close the socket. */          /* Free the channel.  This will also close the socket. */
         channel_free(c);          channel_free(c);
 }  }
   
 void  void
 channel_input_window_adjust(int type, u_int32_t seq, void *ctxt)  channel_input_channel_request(int type, int plen, void *ctxt)
 {  {
         Channel *c;  
         int id;          int id;
         u_int adjust;          Channel *c;
   
           id = packet_get_int();
           c = channel_lookup(id);
   
           if (c == NULL ||
               (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL))
                   packet_disconnect("Received request for "
                       "non-open channel %d.", id);
           if (c->cb_fn != NULL && c->cb_event == type) {
                   debug2("callback start");
                   c->cb_fn(c->self, c->cb_arg);
                   debug2("callback done");
           } else {
                   char *service = packet_get_string(NULL);
                   debug("channel %d: rcvd request for %s", c->self, service);
                   debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event);
                   xfree(service);
           }
   }
   
   void
   channel_input_window_adjust(int type, int plen, void *ctxt)
   {
           Channel *c;
           int id, adjust;
   
         if (!compat20)          if (!compat20)
                 return;                  return;
   
Line 1960 
Line 2000 
                 return;                  return;
         }          }
         adjust = packet_get_int();          adjust = packet_get_int();
         packet_check_eom();          packet_done();
         debug2("channel %d: rcvd adjust %u", id, adjust);          debug2("channel %d: rcvd adjust %d", id, adjust);
         c->remote_window += adjust;          c->remote_window += adjust;
 }  }
   
 void  void
 channel_input_port_open(int type, u_int32_t seq, void *ctxt)  channel_input_port_open(int type, int plen, void *ctxt)
 {  {
         Channel *c = NULL;          Channel *c = NULL;
         u_short host_port;          u_short host_port;
Line 1982 
Line 2022 
         } else {          } else {
                 originator_string = xstrdup("unknown (remote did not supply name)");                  originator_string = xstrdup("unknown (remote did not supply name)");
         }          }
         packet_check_eom();          packet_done();
         sock = channel_connect_to(host, host_port);          sock = channel_connect_to(host, host_port);
         if (sock != -1) {          if (sock != -1) {
                 c = channel_new("connected socket",                  c = channel_new("connected socket",
                     SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,                      SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
                     originator_string, 1);                      originator_string, 1);
                 c->remote_id = remote_id;                  if (c == NULL) {
                           error("channel_input_port_open: channel_new failed");
                           close(sock);
                   } else {
                           c->remote_id = remote_id;
                   }
         }          }
         if (c == NULL) {          if (c == NULL) {
                 packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);                  packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
Line 2007 
Line 2052 
         IPv4or6 = af;          IPv4or6 = af;
 }  }
   
 static int  /*
 channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,   * Initiate forwarding of connections to local port "port" through the secure
     const char *host_to_connect, u_short port_to_connect, int gateway_ports)   * channel to host:port from remote side.
    */
   int
   channel_request_local_forwarding(u_short listen_port, const char *host_to_connect,
       u_short port_to_connect, int gateway_ports)
 {  {
           return channel_request_forwarding(
               NULL, listen_port,
               host_to_connect, port_to_connect,
               gateway_ports, /*remote_fwd*/ 0);
   }
   
   /*
    * If 'remote_fwd' is true we have a '-R style' listener for protocol 2
    * (SSH_CHANNEL_RPORT_LISTENER).
    */
   int
   channel_request_forwarding(
       const char *listen_address, u_short listen_port,
       const char *host_to_connect, u_short port_to_connect,
       int gateway_ports, int remote_fwd)
   {
         Channel *c;          Channel *c;
         int success, sock, on = 1;          int success, sock, on = 1, type;
         struct addrinfo hints, *ai, *aitop;          struct addrinfo hints, *ai, *aitop;
         const char *host;  
         char ntop[NI_MAXHOST], strport[NI_MAXSERV];          char ntop[NI_MAXHOST], strport[NI_MAXSERV];
           const char *host;
           struct linger linger;
   
         success = 0;          success = 0;
         host = (type == SSH_CHANNEL_RPORT_LISTENER) ?  
             listen_addr : host_to_connect;  
   
         if (host == NULL) {          if (remote_fwd) {
                 error("No forward host name.");                  host = listen_address;
                 return success;                  type = SSH_CHANNEL_RPORT_LISTENER;
           } else {
                   host = host_to_connect;
                   type = SSH_CHANNEL_PORT_LISTENER;
         }          }
   
         if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {          if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
                 error("Forward host name too long.");                  error("Forward host name too long.");
                 return success;                  return success;
         }          }
   
           /* XXX listen_address is currently ignored */
         /*          /*
          * getaddrinfo returns a loopback address if the hostname is           * getaddrinfo returns a loopback address if the hostname is
          * set to NULL and hints.ai_flags is not AI_PASSIVE           * set to NULL and hints.ai_flags is not AI_PASSIVE
Line 2047 
Line 2116 
                         continue;                          continue;
                 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),                  if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
                     strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {                      strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
                         error("channel_setup_fwd_listener: getnameinfo failed");                          error("channel_request_forwarding: getnameinfo failed");
                         continue;                          continue;
                 }                  }
                 /* Create a port to listen for the host. */                  /* Create a port to listen for the host. */
Line 2058 
Line 2127 
                         continue;                          continue;
                 }                  }
                 /*                  /*
                  * Set socket options.                   * Set socket options.  We would like the socket to disappear
                  * Allow local port reuse in TIME_WAIT.                   * as soon as it has been closed for whatever reason.
                  */                   */
                 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,                  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
                     sizeof(on)) == -1)                  linger.l_onoff = 1;
                         error("setsockopt SO_REUSEADDR: %s", strerror(errno));                  linger.l_linger = 5;
                   setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
                 debug("Local forwarding listening on %s port %s.", ntop, strport);                  debug("Local forwarding listening on %s port %s.", ntop, strport);
   
                 /* Bind the socket to the address. */                  /* Bind the socket to the address. */
Line 2084 
Line 2153 
                 c = channel_new("port listener", type, sock, sock, -1,                  c = channel_new("port listener", type, sock, sock, -1,
                     CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,                      CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
                     0, xstrdup("port listener"), 1);                      0, xstrdup("port listener"), 1);
                   if (c == NULL) {
                           error("channel_request_forwarding: channel_new failed");
                           close(sock);
                           continue;
                   }
                 strlcpy(c->path, host, sizeof(c->path));                  strlcpy(c->path, host, sizeof(c->path));
                 c->host_port = port_to_connect;                  c->host_port = port_to_connect;
                 c->listening_port = listen_port;                  c->listening_port = listen_port;
                 success = 1;                  success = 1;
         }          }
         if (success == 0)          if (success == 0)
                 error("channel_setup_fwd_listener: cannot listen to port: %d",                  error("channel_request_forwarding: cannot listen to port: %d",
                     listen_port);                      listen_port);
         freeaddrinfo(aitop);          freeaddrinfo(aitop);
         return success;          return success;
 }  }
   
 /* protocol local port fwd, used by ssh (and sshd in v1) */  
 int  
 channel_setup_local_fwd_listener(u_short listen_port,  
     const char *host_to_connect, u_short port_to_connect, int gateway_ports)  
 {  
         return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,  
             NULL, listen_port, host_to_connect, port_to_connect, gateway_ports);  
 }  
   
 /* protocol v2 remote port fwd, used by sshd */  
 int  
 channel_setup_remote_fwd_listener(const char *listen_address,  
     u_short listen_port, int gateway_ports)  
 {  
         return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,  
             listen_address, listen_port, NULL, 0, gateway_ports);  
 }  
   
 /*  /*
  * Initiate forwarding of connections to port "port" on remote host through   * Initiate forwarding of connections to port "port" on remote host through
  * the secure channel to host:port from local side.   * the secure channel to host:port from local side.
Line 2123 
Line 2179 
 channel_request_remote_forwarding(u_short listen_port,  channel_request_remote_forwarding(u_short listen_port,
     const char *host_to_connect, u_short port_to_connect)      const char *host_to_connect, u_short port_to_connect)
 {  {
         int type, success = 0;          int payload_len, type, success = 0;
   
         /* Record locally that connection to this host/port is permitted. */          /* Record locally that connection to this host/port is permitted. */
         if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)          if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
Line 2134 
Line 2190 
                 const char *address_to_bind = "0.0.0.0";                  const char *address_to_bind = "0.0.0.0";
                 packet_start(SSH2_MSG_GLOBAL_REQUEST);                  packet_start(SSH2_MSG_GLOBAL_REQUEST);
                 packet_put_cstring("tcpip-forward");                  packet_put_cstring("tcpip-forward");
                 packet_put_char(1);                     /* boolean: want reply */                  packet_put_char(0);                     /* boolean: want reply */
                 packet_put_cstring(address_to_bind);                  packet_put_cstring(address_to_bind);
                 packet_put_int(listen_port);                  packet_put_int(listen_port);
                 packet_send();                  packet_send();
Line 2150 
Line 2206 
                 packet_write_wait();                  packet_write_wait();
   
                 /* Wait for response from the remote side. */                  /* Wait for response from the remote side. */
                 type = packet_read();                  type = packet_read(&payload_len);
                 switch (type) {                  switch (type) {
                 case SSH_SMSG_SUCCESS:                  case SSH_SMSG_SUCCESS:
                         success = 1;                          success = 1;
Line 2197 
Line 2253 
                 packet_disconnect("Requested forwarding of port %d but user is not root.",                  packet_disconnect("Requested forwarding of port %d but user is not root.",
                                   port);                                    port);
         /* Initiate forwarding */          /* Initiate forwarding */
         channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports);          channel_request_local_forwarding(port, hostname, host_port, gateway_ports);
   
         /* Free the argument string. */          /* Free the argument string. */
         xfree(hostname);          xfree(hostname);
Line 2209 
Line 2265 
  * anyway, and the server has no way to know but to trust the client anyway.   * anyway, and the server has no way to know but to trust the client anyway.
  */   */
 void  void
 channel_permit_all_opens(void)  channel_permit_all_opens()
 {  {
         if (num_permitted_opens == 0)          if (num_permitted_opens == 0)
                 all_opens_permitted = 1;                  all_opens_permitted = 1;
Line 2290 
Line 2346 
                 return -1;                  return -1;
         }          }
         /* success */          /* success */
         set_nodelay(sock);  
         return sock;          return sock;
 }  }
   
Line 2335 
Line 2390 
   
 /*  /*
  * Creates an internet domain socket for listening for X11 connections.   * Creates an internet domain socket for listening for X11 connections.
  * Returns 0 and a suitable display number for the DISPLAY variable   * Returns a suitable display number for the DISPLAY variable, or -1 if
  * stored in display_numberp , or -1 if an error occurs.   * an error occurs.
  */   */
 int  int
 x11_create_display_inet(int x11_display_offset, int x11_use_localhost,  x11_create_display_inet(int x11_display_offset, int gateway_ports)
     int single_connection, u_int *display_numberp)  
 {  {
         Channel *nc = NULL;  
         int display_number, sock;          int display_number, sock;
         u_short port;          u_short port;
         struct addrinfo hints, *ai, *aitop;          struct addrinfo hints, *ai, *aitop;
Line 2350 
Line 2403 
         int gaierr, n, num_socks = 0, socks[NUM_SOCKS];          int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
   
         for (display_number = x11_display_offset;          for (display_number = x11_display_offset;
             display_number < MAX_DISPLAYS;               display_number < MAX_DISPLAYS;
             display_number++) {               display_number++) {
                 port = 6000 + display_number;                  port = 6000 + display_number;
                 memset(&hints, 0, sizeof(hints));                  memset(&hints, 0, sizeof(hints));
                 hints.ai_family = IPv4or6;                  hints.ai_family = IPv4or6;
                 hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;                  hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
                 hints.ai_socktype = SOCK_STREAM;                  hints.ai_socktype = SOCK_STREAM;
                 snprintf(strport, sizeof strport, "%d", port);                  snprintf(strport, sizeof strport, "%d", port);
                 if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {                  if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
Line 2372 
Line 2425 
                         }                          }
                         if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {                          if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
                                 debug("bind port %d: %.100s", port, strerror(errno));                                  debug("bind port %d: %.100s", port, strerror(errno));
                                   shutdown(sock, SHUT_RDWR);
                                 close(sock);                                  close(sock);
   
                                 if (ai->ai_next)  
                                         continue;  
   
                                 for (n = 0; n < num_socks; n++) {                                  for (n = 0; n < num_socks; n++) {
                                           shutdown(socks[n], SHUT_RDWR);
                                         close(socks[n]);                                          close(socks[n]);
                                 }                                  }
                                 num_socks = 0;                                  num_socks = 0;
Line 2400 
Line 2451 
                 sock = socks[n];                  sock = socks[n];
                 if (listen(sock, 5) < 0) {                  if (listen(sock, 5) < 0) {
                         error("listen: %.100s", strerror(errno));                          error("listen: %.100s", strerror(errno));
                           shutdown(sock, SHUT_RDWR);
                         close(sock);                          close(sock);
                         return -1;                          return -1;
                 }                  }
Line 2408 
Line 2460 
         /* Allocate a channel for each socket. */          /* Allocate a channel for each socket. */
         for (n = 0; n < num_socks; n++) {          for (n = 0; n < num_socks; n++) {
                 sock = socks[n];                  sock = socks[n];
                 nc = channel_new("x11 listener",                  (void) channel_new("x11 listener",
                     SSH_CHANNEL_X11_LISTENER, sock, sock, -1,                      SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
                     CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,                      CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
                     0, xstrdup("X11 inet listener"), 1);                      0, xstrdup("X11 inet listener"), 1);
                 nc->single_connection = single_connection;  
         }          }
   
         /* Return the display number for the DISPLAY environment variable. */          /* Return the display number for the DISPLAY environment variable. */
         *display_numberp = display_number;          return display_number;
         return (0);  
 }  }
   
   #ifndef X_UNIX_PATH
   #define X_UNIX_PATH "/tmp/.X11-unix/X"
   #endif
   
 static int  static int
 connect_local_xsocket(u_int dnr)  connect_local_xsocket(u_int dnr)
 {  {
           static const char *const x_sockets[] = {
                   X_UNIX_PATH "%u",
                   "/var/X/.X11-unix/X" "%u",
                   "/usr/spool/sockets/X11/" "%u",
                   NULL
           };
         int sock;          int sock;
         struct sockaddr_un addr;          struct sockaddr_un addr;
           const char *const * path;
   
         sock = socket(AF_UNIX, SOCK_STREAM, 0);          for (path = x_sockets; *path; ++path) {
         if (sock < 0)                  sock = socket(AF_UNIX, SOCK_STREAM, 0);
                 error("socket: %.100s", strerror(errno));                  if (sock < 0)
         memset(&addr, 0, sizeof(addr));                          error("socket: %.100s", strerror(errno));
         addr.sun_family = AF_UNIX;                  memset(&addr, 0, sizeof(addr));
         snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr);                  addr.sun_family = AF_UNIX;
         if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)                  snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr);
                 return sock;                  if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
         close(sock);                          return sock;
                   close(sock);
           }
         error("connect %.100s: %.100s", addr.sun_path, strerror(errno));          error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
         return -1;          return -1;
 }  }
Line 2469 
Line 2532 
                 /* Connect to the unix domain socket. */                  /* Connect to the unix domain socket. */
                 if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {                  if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
                         error("Could not parse display number from DISPLAY: %.100s",                          error("Could not parse display number from DISPLAY: %.100s",
                             display);                                display);
                         return -1;                          return -1;
                 }                  }
                 /* Create a socket. */                  /* Create a socket. */
Line 2484 
Line 2547 
          * Connect to an inet socket.  The DISPLAY value is supposedly           * Connect to an inet socket.  The DISPLAY value is supposedly
          * hostname:d[.s], where hostname may also be numeric IP address.           * hostname:d[.s], where hostname may also be numeric IP address.
          */           */
         strlcpy(buf, display, sizeof(buf));          strncpy(buf, display, sizeof(buf));
           buf[sizeof(buf) - 1] = 0;
         cp = strchr(buf, ':');          cp = strchr(buf, ':');
         if (!cp) {          if (!cp) {
                 error("Could not find ':' in DISPLAY: %.100s", display);                  error("Could not find ':' in DISPLAY: %.100s", display);
Line 2494 
Line 2558 
         /* buf now contains the host name.  But first we parse the display number. */          /* buf now contains the host name.  But first we parse the display number. */
         if (sscanf(cp + 1, "%d", &display_number) != 1) {          if (sscanf(cp + 1, "%d", &display_number) != 1) {
                 error("Could not parse display number from DISPLAY: %.100s",                  error("Could not parse display number from DISPLAY: %.100s",
                     display);                        display);
                 return -1;                  return -1;
         }          }
   
Line 2530 
Line 2594 
                     strerror(errno));                      strerror(errno));
                 return -1;                  return -1;
         }          }
         set_nodelay(sock);  
         return sock;          return sock;
 }  }
   
Line 2541 
Line 2604 
  */   */
   
 void  void
 x11_input_open(int type, u_int32_t seq, void *ctxt)  x11_input_open(int type, int plen, void *ctxt)
 {  {
         Channel *c = NULL;          Channel *c = NULL;
         int remote_id, sock = 0;          int remote_id, sock = 0;
Line 2556 
Line 2619 
         } else {          } else {
                 remote_host = xstrdup("unknown (remote did not supply name)");                  remote_host = xstrdup("unknown (remote did not supply name)");
         }          }
         packet_check_eom();          packet_done();
   
         /* Obtain a connection to the real X display. */          /* Obtain a connection to the real X display. */
         sock = x11_connect_display();          sock = x11_connect_display();
Line 2565 
Line 2628 
                 c = channel_new("connected x11 socket",                  c = channel_new("connected x11 socket",
                     SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,                      SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
                     remote_host, 1);                      remote_host, 1);
                 c->remote_id = remote_id;                  if (c == NULL) {
                 c->force_drain = 1;                          error("x11_input_open: channel_new failed");
                           close(sock);
                   } else {
                           c->remote_id = remote_id;
                           c->force_drain = 1;
                   }
         }          }
         if (c == NULL) {          if (c == NULL) {
                 /* Send refusal to the remote host. */                  /* Send refusal to the remote host. */
Line 2583 
Line 2651 
   
 /* dummy protocol handler that denies SSH-1 requests (agent/x11) */  /* dummy protocol handler that denies SSH-1 requests (agent/x11) */
 void  void
 deny_input_open(int type, u_int32_t seq, void *ctxt)  deny_input_open(int type, int plen, void *ctxt)
 {  {
         int rchan = packet_get_int();          int rchan = packet_get_int();
           switch(type){
         switch (type) {  
         case SSH_SMSG_AGENT_OPEN:          case SSH_SMSG_AGENT_OPEN:
                 error("Warning: ssh server tried agent forwarding.");                  error("Warning: ssh server tried agent forwarding.");
                 break;                  break;
Line 2595 
Line 2662 
                 error("Warning: ssh server tried X11 forwarding.");                  error("Warning: ssh server tried X11 forwarding.");
                 break;                  break;
         default:          default:
                 error("deny_input_open: type %d", type);                  error("deny_input_open: type %d plen %d", type, plen);
                 break;                  break;
         }          }
         error("Warning: this is probably a break in attempt by a malicious server.");          error("Warning: this is probably a break in attempt by a malicious server.");
Line 2679 
Line 2746 
 /* Sends a message to the server to request authentication fd forwarding. */  /* Sends a message to the server to request authentication fd forwarding. */
   
 void  void
 auth_request_forwarding(void)  auth_request_forwarding()
 {  {
         packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);          packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
         packet_send();          packet_send();
         packet_write_wait();          packet_write_wait();
 }  }
   
   /*
    * Returns the name of the forwarded authentication socket.  Returns NULL if
    * there is no forwarded authentication socket.  The returned value points to
    * a static buffer.
    */
   
   char *
   auth_get_socket_name()
   {
           return auth_sock_name;
   }
   
   /* removes the agent forwarding socket */
   
   void
   auth_sock_cleanup_proc(void *_pw)
   {
           struct passwd *pw = _pw;
   
           if (auth_sock_name) {
                   temporarily_use_uid(pw);
                   unlink(auth_sock_name);
                   rmdir(auth_sock_dir);
                   auth_sock_name = NULL;
                   restore_uid();
           }
   }
   
   /*
    * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
    * This starts forwarding authentication requests.
    */
   
   int
   auth_input_request_forwarding(struct passwd * pw)
   {
           Channel *nc;
           int sock;
           struct sockaddr_un sunaddr;
   
           if (auth_get_socket_name() != NULL) {
                   error("authentication forwarding requested twice.");
                   return 0;
           }
   
           /* Temporarily drop privileged uid for mkdir/bind. */
           temporarily_use_uid(pw);
   
           /* Allocate a buffer for the socket name, and format the name. */
           auth_sock_name = xmalloc(MAXPATHLEN);
           auth_sock_dir = xmalloc(MAXPATHLEN);
           strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
   
           /* Create private directory for socket */
           if (mkdtemp(auth_sock_dir) == NULL) {
                   packet_send_debug("Agent forwarding disabled: "
                       "mkdtemp() failed: %.100s", strerror(errno));
                   restore_uid();
                   xfree(auth_sock_name);
                   xfree(auth_sock_dir);
                   auth_sock_name = NULL;
                   auth_sock_dir = NULL;
                   return 0;
           }
           snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%d",
                    auth_sock_dir, (int) getpid());
   
           /* delete agent socket on fatal() */
           fatal_add_cleanup(auth_sock_cleanup_proc, pw);
   
           /* Create the socket. */
           sock = socket(AF_UNIX, SOCK_STREAM, 0);
           if (sock < 0)
                   packet_disconnect("socket: %.100s", strerror(errno));
   
           /* Bind it to the name. */
           memset(&sunaddr, 0, sizeof(sunaddr));
           sunaddr.sun_family = AF_UNIX;
           strncpy(sunaddr.sun_path, auth_sock_name,
                   sizeof(sunaddr.sun_path));
   
           if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0)
                   packet_disconnect("bind: %.100s", strerror(errno));
   
           /* Restore the privileged uid. */
           restore_uid();
   
           /* Start listening on the socket. */
           if (listen(sock, 5) < 0)
                   packet_disconnect("listen: %.100s", strerror(errno));
   
           /* Allocate a channel for the authentication agent socket. */
           nc = channel_new("auth socket",
               SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
               CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
               0, xstrdup("auth socket"), 1);
           if (nc == NULL) {
                   error("auth_input_request_forwarding: channel_new failed");
                   auth_sock_cleanup_proc(pw);
                   fatal_remove_cleanup(auth_sock_cleanup_proc, pw);
                   close(sock);
                   return 0;
           }
           strlcpy(nc->path, auth_sock_name, sizeof(nc->path));
           return 1;
   }
   
 /* This is called to process an SSH_SMSG_AGENT_OPEN message. */  /* This is called to process an SSH_SMSG_AGENT_OPEN message. */
   
 void  void
 auth_input_open_request(int type, u_int32_t seq, void *ctxt)  auth_input_open_request(int type, int plen, void *ctxt)
 {  {
         Channel *c = NULL;          Channel *c = NULL;
         int remote_id, sock;          int remote_id, sock;
         char *name;          char *name;
   
           packet_integrity_check(plen, 4, type);
   
         /* Read the remote channel number from the message. */          /* Read the remote channel number from the message. */
         remote_id = packet_get_int();          remote_id = packet_get_int();
         packet_check_eom();  
   
         /*          /*
          * Get a connection to the local authentication agent (this may again           * Get a connection to the local authentication agent (this may again
Line 2715 
Line 2890 
                 name = xstrdup("authentication agent connection");                  name = xstrdup("authentication agent connection");
                 c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,                  c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
                     -1, 0, 0, 0, name, 1);                      -1, 0, 0, 0, name, 1);
                 c->remote_id = remote_id;                  if (c == NULL) {
                 c->force_drain = 1;                          error("auth_input_open_request: channel_new failed");
                           xfree(name);
                           close(sock);
                   } else {
                           c->remote_id = remote_id;
                           c->force_drain = 1;
                   }
         }          }
         if (c == NULL) {          if (c == NULL) {
                 packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);                  packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);

Legend:
Removed from v.1.140.2.5  
changed lines
  Added in v.1.141