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

Diff for /src/usr.bin/ssh/clientloop.c between version 1.10 and 1.11

version 1.10, 1999/11/19 19:58:18 version 1.11, 1999/11/23 22:25:53
Line 49 
Line 49 
 static int in_non_blocking_mode = 0;  static int in_non_blocking_mode = 0;
   
 /* Common data for the client loop code. */  /* Common data for the client loop code. */
 static int escape_pending;  /* Last character was the escape character */  static int escape_pending;      /* Last character was the escape character */
 static int last_was_cr; /* Last character was a newline. */  static int last_was_cr;         /* Last character was a newline. */
 static int exit_status; /* Used to store the exit status of the command. */  static int exit_status;         /* Used to store the exit status of the command. */
 static int stdin_eof; /* EOF has been encountered on standard error. */  static int stdin_eof;           /* EOF has been encountered on standard error. */
 static Buffer stdin_buffer;  /* Buffer for stdin data. */  static Buffer stdin_buffer;     /* Buffer for stdin data. */
 static Buffer stdout_buffer; /* Buffer for stdout data. */  static Buffer stdout_buffer;    /* Buffer for stdout data. */
 static Buffer stderr_buffer; /* Buffer for stderr data. */  static Buffer stderr_buffer;    /* Buffer for stderr data. */
 static unsigned int buffer_high; /* Soft max buffer size. */  static unsigned int buffer_high;/* Soft max buffer size. */
 static int max_fd; /* Maximum file descriptor number in select(). */  static int max_fd;              /* Maximum file descriptor number in select(). */
 static int connection_in; /* Connection to server (input). */  static int connection_in;       /* Connection to server (input). */
 static int connection_out; /* Connection to server (output). */  static int connection_out;      /* Connection to server (output). */
 static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;  static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
 static int quit_pending; /* Set to non-zero to quit the client loop. */  static int quit_pending;        /* Set to non-zero to quit the client loop. */
 static int escape_char; /* Escape character. */  static int escape_char;         /* Escape character. */
   
 /* Returns the user\'s terminal to normal mode if it had been put in raw  /* Returns the user\'s terminal to normal mode if it had been put in raw
    mode. */     mode. */
   
 void leave_raw_mode()  void
   leave_raw_mode()
 {  {
   if (!in_raw_mode)          if (!in_raw_mode)
     return;                  return;
   in_raw_mode = 0;          in_raw_mode = 0;
   if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)          if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)
     perror("tcsetattr");                  perror("tcsetattr");
   
   fatal_remove_cleanup((void (*)(void *))leave_raw_mode, NULL);          fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL);
 }  }
   
 /* Puts the user\'s terminal in raw mode. */  /* Puts the user\'s terminal in raw mode. */
   
 void enter_raw_mode()  void
   enter_raw_mode()
 {  {
   struct termios tio;          struct termios tio;
   
   if (tcgetattr(fileno(stdin), &tio) < 0)          if (tcgetattr(fileno(stdin), &tio) < 0)
     perror("tcgetattr");                  perror("tcgetattr");
   saved_tio = tio;          saved_tio = tio;
   tio.c_iflag |= IGNPAR;          tio.c_iflag |= IGNPAR;
   tio.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF);          tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
   tio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHONL);          tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
 #ifdef IEXTEN  #ifdef IEXTEN
   tio.c_lflag &= ~IEXTEN;          tio.c_lflag &= ~IEXTEN;
 #endif /* IEXTEN */  #endif                          /* IEXTEN */
   tio.c_oflag &= ~OPOST;          tio.c_oflag &= ~OPOST;
   tio.c_cc[VMIN] = 1;          tio.c_cc[VMIN] = 1;
   tio.c_cc[VTIME] = 0;          tio.c_cc[VTIME] = 0;
   if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)          if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)
     perror("tcsetattr");                  perror("tcsetattr");
   in_raw_mode = 1;          in_raw_mode = 1;
   
   fatal_add_cleanup((void (*)(void *))leave_raw_mode, NULL);          fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL);
 }  }
   
 /* Puts stdin terminal in non-blocking mode. */  
   
 /* Restores stdin to blocking mode. */  /* Restores stdin to blocking mode. */
   
 void leave_non_blocking()  void
   leave_non_blocking()
 {  {
   if (in_non_blocking_mode)          if (in_non_blocking_mode) {
     {                  (void) fcntl(fileno(stdin), F_SETFL, 0);
       (void)fcntl(fileno(stdin), F_SETFL, 0);                  in_non_blocking_mode = 0;
       in_non_blocking_mode = 0;                  fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL);
       fatal_remove_cleanup((void (*)(void *))leave_non_blocking, NULL);          }
     }  
 }  }
   
 void enter_non_blocking()  /* Puts stdin terminal in non-blocking mode. */
   
   void
   enter_non_blocking()
 {  {
   in_non_blocking_mode = 1;          in_non_blocking_mode = 1;
   (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);          (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
   fatal_add_cleanup((void (*)(void *))leave_non_blocking, NULL);          fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL);
 }  }
   
 /* Signal handler for the window change signal (SIGWINCH).  This just  /* Signal handler for the window change signal (SIGWINCH).  This just
    sets a flag indicating that the window has changed. */     sets a flag indicating that the window has changed. */
   
 void window_change_handler(int sig)  void
   window_change_handler(int sig)
 {  {
   received_window_change_signal = 1;          received_window_change_signal = 1;
   signal(SIGWINCH, window_change_handler);          signal(SIGWINCH, window_change_handler);
 }  }
   
 /* Signal handler for signals that cause the program to terminate.  These  /* Signal handler for signals that cause the program to terminate.  These
    signals must be trapped to restore terminal modes. */     signals must be trapped to restore terminal modes. */
   
 void signal_handler(int sig)  void
   signal_handler(int sig)
 {  {
   if (in_raw_mode)          if (in_raw_mode)
     leave_raw_mode();                  leave_raw_mode();
   if (in_non_blocking_mode)          if (in_non_blocking_mode)
     leave_non_blocking();                  leave_non_blocking();
   channel_stop_listening();          channel_stop_listening();
   packet_close();          packet_close();
   fatal("Killed by signal %d.", sig);          fatal("Killed by signal %d.", sig);
 }  }
   
 /* Returns current time in seconds from Jan 1, 1970 with the maximum available  /* Returns current time in seconds from Jan 1, 1970 with the maximum available
    resolution. */     resolution. */
   
 double get_current_time()  double
   get_current_time()
 {  {
   struct timeval tv;          struct timeval tv;
   gettimeofday(&tv, NULL);          gettimeofday(&tv, NULL);
   return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;          return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
 }  }
   
 /* This is called when the interactive is entered.  This checks if there  /* This is called when the interactive is entered.  This checks if there
    is an EOF coming on stdin.  We must check this explicitly, as select()     is an EOF coming on stdin.  We must check this explicitly, as select()
    does not appear to wake up when redirecting from /dev/null. */     does not appear to wake up when redirecting from /dev/null. */
   
 void client_check_initial_eof_on_stdin()  void
   client_check_initial_eof_on_stdin()
 {  {
   int len;          int len;
   char buf[1];          char buf[1];
   
   /* If standard input is to be "redirected from /dev/null", we simply          /* If standard input is to be "redirected from /dev/null", we
      mark that we have seen an EOF and send an EOF message to the server.             simply mark that we have seen an EOF and send an EOF message to
      Otherwise, we try to read a single character; it appears that for some             the server. Otherwise, we try to read a single character; it
      files, such /dev/null, select() never wakes up for read for this             appears that for some files, such /dev/null, select() never
      descriptor, which means that we never get EOF.  This way we will get             wakes up for read for this descriptor, which means that we
      the EOF if stdin comes from /dev/null or similar. */             never get EOF.  This way we will get the EOF if stdin comes
   if (stdin_null_flag)             from /dev/null or similar. */
     {          if (stdin_null_flag) {
       /* Fake EOF on stdin. */                  /* Fake EOF on stdin. */
       debug("Sending eof.");                  debug("Sending eof.");
       stdin_eof = 1;                  stdin_eof = 1;
       packet_start(SSH_CMSG_EOF);                  packet_start(SSH_CMSG_EOF);
       packet_send();                  packet_send();
     }          } else {
   else                  /* Enter non-blocking mode for stdin. */
     {                  enter_non_blocking();
       /* Enter non-blocking mode for stdin. */  
       enter_non_blocking();  
   
       /* Check for immediate EOF on stdin. */                  /* Check for immediate EOF on stdin. */
       len = read(fileno(stdin), buf, 1);                  len = read(fileno(stdin), buf, 1);
       if (len == 0)                  if (len == 0) {
         {                          /* EOF.  Record that we have seen it and send EOF
           /* EOF.  Record that we have seen it and send EOF to server. */                             to server. */
           debug("Sending eof.");                          debug("Sending eof.");
           stdin_eof = 1;                          stdin_eof = 1;
           packet_start(SSH_CMSG_EOF);                          packet_start(SSH_CMSG_EOF);
           packet_send();                          packet_send();
                   } else if (len > 0) {
                           /* Got data.  We must store the data in the
                              buffer, and also process it as an escape
                              character if appropriate. */
                           if ((unsigned char) buf[0] == escape_char)
                                   escape_pending = 1;
                           else {
                                   buffer_append(&stdin_buffer, buf, 1);
                                   stdin_bytes += 1;
                           }
                   }
                   /* Leave non-blocking mode. */
                   leave_non_blocking();
         }          }
       else  
         if (len > 0)  
           {  
             /* Got data.  We must store the data in the buffer, and also  
                process it as an escape character if appropriate. */  
             if ((unsigned char)buf[0] == escape_char)  
               escape_pending = 1;  
             else  
               {  
                 buffer_append(&stdin_buffer, buf, 1);  
                 stdin_bytes += 1;  
               }  
           }  
   
       /* Leave non-blocking mode. */  
       leave_non_blocking();  
     }  
 }  }
   
 /* Get packets from the connection input buffer, and process them as long  /* Get packets from the connection input buffer, and process them as long
    as there are packets available. */     as there are packets available. */
   
 void client_process_buffered_input_packets()  void
   client_process_buffered_input_packets()
 {  {
   int type;          int type;
   char *data;          char *data;
   unsigned int data_len;          unsigned int data_len;
   int payload_len;          int payload_len;
   
   /* Process any buffered packets from the server. */          /* Process any buffered packets from the server. */
   while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE)          while (!quit_pending &&
     {                 (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) {
       switch (type)                  switch (type) {
         {  
   
         case SSH_SMSG_STDOUT_DATA:  
           data = packet_get_string(&data_len);  
           packet_integrity_check(payload_len, 4 + data_len, type);  
           buffer_append(&stdout_buffer, data, data_len);  
           stdout_bytes += data_len;  
           memset(data, 0, data_len);  
           xfree(data);  
           break;  
   
         case SSH_SMSG_STDERR_DATA:                  case SSH_SMSG_STDOUT_DATA:
           data = packet_get_string(&data_len);                          data = packet_get_string(&data_len);
           packet_integrity_check(payload_len, 4 + data_len, type);                          packet_integrity_check(payload_len, 4 + data_len, type);
           buffer_append(&stderr_buffer, data, data_len);                          buffer_append(&stdout_buffer, data, data_len);
           stdout_bytes += data_len;                          stdout_bytes += data_len;
           memset(data, 0, data_len);                          memset(data, 0, data_len);
           xfree(data);                          xfree(data);
           break;                          break;
   
         case SSH_SMSG_EXITSTATUS:                  case SSH_SMSG_STDERR_DATA:
           packet_integrity_check(payload_len, 4, type);                          data = packet_get_string(&data_len);
           exit_status = packet_get_int();                          packet_integrity_check(payload_len, 4 + data_len, type);
           /* Acknowledge the exit. */                          buffer_append(&stderr_buffer, data, data_len);
           packet_start(SSH_CMSG_EXIT_CONFIRMATION);                          stdout_bytes += data_len;
           packet_send();                          memset(data, 0, data_len);
           /* Must wait for packet to be sent since we are exiting the                          xfree(data);
              loop. */                          break;
           packet_write_wait();  
           /* Flag that we want to exit. */  
           quit_pending = 1;  
           break;  
   
         case SSH_SMSG_X11_OPEN:                  case SSH_SMSG_EXITSTATUS:
           x11_input_open(payload_len);                          packet_integrity_check(payload_len, 4, type);
           break;                          exit_status = packet_get_int();
                           /* Acknowledge the exit. */
                           packet_start(SSH_CMSG_EXIT_CONFIRMATION);
                           packet_send();
                           /* Must wait for packet to be sent since we are
                              exiting the loop. */
                           packet_write_wait();
                           /* Flag that we want to exit. */
                           quit_pending = 1;
                           break;
   
         case SSH_MSG_PORT_OPEN:                  case SSH_SMSG_X11_OPEN:
           channel_input_port_open(payload_len);                          x11_input_open(payload_len);
           break;                          break;
   
         case SSH_SMSG_AGENT_OPEN:                  case SSH_MSG_PORT_OPEN:
           packet_integrity_check(payload_len, 4, type);                          channel_input_port_open(payload_len);
           auth_input_open_request();                          break;
           break;  
   
         case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:                  case SSH_SMSG_AGENT_OPEN:
           packet_integrity_check(payload_len, 4 + 4, type);                          packet_integrity_check(payload_len, 4, type);
           channel_input_open_confirmation();                          auth_input_open_request();
           break;                          break;
   
         case SSH_MSG_CHANNEL_OPEN_FAILURE:                  case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
           packet_integrity_check(payload_len, 4, type);                          packet_integrity_check(payload_len, 4 + 4, type);
           channel_input_open_failure();                          channel_input_open_confirmation();
           break;                          break;
   
         case SSH_MSG_CHANNEL_DATA:                  case SSH_MSG_CHANNEL_OPEN_FAILURE:
           channel_input_data(payload_len);                          packet_integrity_check(payload_len, 4, type);
           break;                          channel_input_open_failure();
                           break;
   
         case SSH_MSG_CHANNEL_CLOSE:                  case SSH_MSG_CHANNEL_DATA:
           packet_integrity_check(payload_len, 4, type);                          channel_input_data(payload_len);
           channel_input_close();                          break;
           break;  
   
         case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:                  case SSH_MSG_CHANNEL_CLOSE:
           packet_integrity_check(payload_len, 4, type);                          packet_integrity_check(payload_len, 4, type);
           channel_input_close_confirmation();                          channel_input_close();
           break;                          break;
   
         default:                  case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
           /* Any unknown packets received during the actual session                          packet_integrity_check(payload_len, 4, type);
              cause the session to terminate.  This is intended to make                          channel_input_close_confirmation();
              debugging easier since no confirmations are sent.  Any                          break;
              compatible protocol extensions must be negotiated during  
              the preparatory phase. */                  default:
           packet_disconnect("Protocol error during session: type %d",                          /* Any unknown packets received during the actual
                             type);                             session cause the session to terminate.  This
                              is intended to make debugging easier since no
                              confirmations are sent.  Any compatible
                              protocol extensions must be negotiated during
                              the preparatory phase. */
                           packet_disconnect("Protocol error during session: type %d",
                                             type);
                   }
         }          }
     }  
 }  }
   
 /* Make packets from buffered stdin data, and buffer them for sending to  /* Make packets from buffered stdin data, and buffer them for sending to
    the connection. */     the connection. */
   
 void client_make_packets_from_stdin_data()  void
   client_make_packets_from_stdin_data()
 {  {
   unsigned int len;          unsigned int len;
   
   /* Send buffered stdin data to the server. */          /* Send buffered stdin data to the server. */
   while (buffer_len(&stdin_buffer) > 0 &&          while (buffer_len(&stdin_buffer) > 0 &&
          packet_not_very_much_data_to_write())                 packet_not_very_much_data_to_write()) {
     {                  len = buffer_len(&stdin_buffer);
       len = buffer_len(&stdin_buffer);                  /* Keep the packets at reasonable size. */
       if (len > packet_get_maxsize())                  if (len > packet_get_maxsize())
         len = packet_get_maxsize();  /* Keep the packets at reasonable size. */                          len = packet_get_maxsize();
       packet_start(SSH_CMSG_STDIN_DATA);                  packet_start(SSH_CMSG_STDIN_DATA);
       packet_put_string(buffer_ptr(&stdin_buffer), len);                  packet_put_string(buffer_ptr(&stdin_buffer), len);
       packet_send();                  packet_send();
       buffer_consume(&stdin_buffer, len);                  buffer_consume(&stdin_buffer, len);
       /* If we have a pending EOF, send it now. */                  /* If we have a pending EOF, send it now. */
       if (stdin_eof && buffer_len(&stdin_buffer) == 0)                  if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
         {                          packet_start(SSH_CMSG_EOF);
           packet_start(SSH_CMSG_EOF);                          packet_send();
           packet_send();                  }
         }          }
     }  
 }  }
   
 /* Checks if the client window has changed, and sends a packet about it to  /* Checks if the client window has changed, and sends a packet about it to
Line 342 
Line 344 
    interrupt on Unix); this just checks the flag and sends a message if     interrupt on Unix); this just checks the flag and sends a message if
    appropriate. */     appropriate. */
   
 void client_check_window_change()  void
   client_check_window_change()
 {  {
   /* Send possible window change message to the server. */          /* Send possible window change message to the server. */
   if (received_window_change_signal)          if (received_window_change_signal) {
     {                  struct winsize ws;
       struct winsize ws;  
   
       /* Clear the window change indicator. */                  /* Clear the window change indicator. */
       received_window_change_signal = 0;                  received_window_change_signal = 0;
   
       /* Read new window size. */                  /* Read new window size. */
       if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0)                  if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) {
         {                          /* Successful, send the packet now. */
           /* Successful, send the packet now. */                          packet_start(SSH_CMSG_WINDOW_SIZE);
           packet_start(SSH_CMSG_WINDOW_SIZE);                          packet_put_int(ws.ws_row);
           packet_put_int(ws.ws_row);                          packet_put_int(ws.ws_col);
           packet_put_int(ws.ws_col);                          packet_put_int(ws.ws_xpixel);
           packet_put_int(ws.ws_xpixel);                          packet_put_int(ws.ws_ypixel);
           packet_put_int(ws.ws_ypixel);                          packet_send();
           packet_send();                  }
         }          }
     }  
 }  }
   
 /* Waits until the client can do something (some data becomes available on  /* Waits until the client can do something (some data becomes available on
    one of the file descriptors). */     one of the file descriptors). */
   
 void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset)  void
   client_wait_until_can_do_something(fd_set * readset, fd_set * writeset)
 {  {
   /* Initialize select masks. */          /* Initialize select masks. */
   FD_ZERO(readset);          FD_ZERO(readset);
   
   /* Read from the connection, unless our buffers are full. */  
   if (buffer_len(&stdout_buffer) < buffer_high &&  
       buffer_len(&stderr_buffer) < buffer_high &&  
       channel_not_very_much_buffered_data())  
     FD_SET(connection_in, readset);  
   
   /* Read from stdin, unless we have seen EOF or have very much buffered          /* Read from the connection, unless our buffers are full. */
      data to send to the server. */          if (buffer_len(&stdout_buffer) < buffer_high &&
   if (!stdin_eof && packet_not_very_much_data_to_write())              buffer_len(&stderr_buffer) < buffer_high &&
     FD_SET(fileno(stdin), readset);              channel_not_very_much_buffered_data())
                   FD_SET(connection_in, readset);
   FD_ZERO(writeset);  
   
   /* Add any selections by the channel mechanism. */  
   channel_prepare_select(readset, writeset);  
   
   /* Select server connection if have data to write to the server. */  
   if (packet_have_data_to_write())  
     FD_SET(connection_out, writeset);  
   
   /* Select stdout if have data in buffer. */          /* Read from stdin, unless we have seen EOF or have very much
   if (buffer_len(&stdout_buffer) > 0)             buffered data to send to the server. */
     FD_SET(fileno(stdout), writeset);          if (!stdin_eof && packet_not_very_much_data_to_write())
                   FD_SET(fileno(stdin), readset);
   
   /* Select stderr if have data in buffer. */          FD_ZERO(writeset);
   if (buffer_len(&stderr_buffer) > 0)  
     FD_SET(fileno(stderr), writeset);  
   
   /* Update maximum file descriptor number, if appropriate. */          /* Add any selections by the channel mechanism. */
   if (channel_max_fd() > max_fd)          channel_prepare_select(readset, writeset);
     max_fd = channel_max_fd();  
   
   /* Wait for something to happen.  This will suspend the process until          /* Select server connection if have data to write to the server. */
      some selected descriptor can be read, written, or has some other          if (packet_have_data_to_write())
      event pending.  Note: if you want to implement SSH_MSG_IGNORE                  FD_SET(connection_out, writeset);
      messages to fool traffic analysis, this might be the place to do  
      it: just have a random timeout for the select, and send a random          /* Select stdout if have data in buffer. */
      SSH_MSG_IGNORE packet when the timeout expires. */          if (buffer_len(&stdout_buffer) > 0)
   if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0)                  FD_SET(fileno(stdout), writeset);
     {  
       char buf[100];          /* Select stderr if have data in buffer. */
       /* Some systems fail to clear these automatically. */          if (buffer_len(&stderr_buffer) > 0)
       FD_ZERO(readset);                  FD_SET(fileno(stderr), writeset);
       FD_ZERO(writeset);  
       if (errno == EINTR)          /* Update maximum file descriptor number, if appropriate. */
         return;          if (channel_max_fd() > max_fd)
       /* Note: we might still have data in the buffers. */                  max_fd = channel_max_fd();
       snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));  
       buffer_append(&stderr_buffer, buf, strlen(buf));          /* Wait for something to happen.  This will suspend the process
       stderr_bytes += strlen(buf);             until some selected descriptor can be read, written, or has
       quit_pending = 1;             some other event pending.
     }             Note: if you want to implement SSH_MSG_IGNORE messages to fool
              traffic analysis, this might be the place to do it:
              just have a random timeout for the select, and send a random
              SSH_MSG_IGNORE packet when the timeout expires. */
   
           if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) {
                   char buf[100];
                   /* Some systems fail to clear these automatically. */
                   FD_ZERO(readset);
                   FD_ZERO(writeset);
                   if (errno == EINTR)
                           return;
                   /* Note: we might still have data in the buffers. */
                   snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
                   buffer_append(&stderr_buffer, buf, strlen(buf));
                   stderr_bytes += strlen(buf);
                   quit_pending = 1;
           }
 }  }
   
 void client_suspend_self()  void
   client_suspend_self()
 {  {
   struct winsize oldws, newws;          struct winsize oldws, newws;
   
   /* Flush stdout and stderr buffers. */          /* Flush stdout and stderr buffers. */
   if (buffer_len(&stdout_buffer) > 0)          if (buffer_len(&stdout_buffer) > 0)
     write(fileno(stdout),                  write(fileno(stdout),
           buffer_ptr(&stdout_buffer),                        buffer_ptr(&stdout_buffer),
           buffer_len(&stdout_buffer));                        buffer_len(&stdout_buffer));
   if (buffer_len(&stderr_buffer) > 0)          if (buffer_len(&stderr_buffer) > 0)
     write(fileno(stderr),                  write(fileno(stderr),
           buffer_ptr(&stderr_buffer),                        buffer_ptr(&stderr_buffer),
           buffer_len(&stderr_buffer));                        buffer_len(&stderr_buffer));
   
   /* Leave raw mode. */          /* Leave raw mode. */
   leave_raw_mode();          leave_raw_mode();
   
   /* Free (and clear) the buffer to reduce the          /* Free (and clear) the buffer to reduce the amount of data that
      amount of data that gets written to swap. */             gets written to swap. */
   buffer_free(&stdin_buffer);          buffer_free(&stdin_buffer);
   buffer_free(&stdout_buffer);          buffer_free(&stdout_buffer);
   buffer_free(&stderr_buffer);          buffer_free(&stderr_buffer);
   
   /* Save old window size. */          /* Save old window size. */
   ioctl(fileno(stdin), TIOCGWINSZ, &oldws);          ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
   
   /* Send the suspend signal to the program          /* Send the suspend signal to the program itself. */
      itself. */          kill(getpid(), SIGTSTP);
   kill(getpid(), SIGTSTP);  
   
   /* Check if the window size has changed. */          /* Check if the window size has changed. */
   if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&          if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
       (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col ||              (oldws.ws_row != newws.ws_row ||
        oldws.ws_xpixel != newws.ws_xpixel ||               oldws.ws_col != newws.ws_col ||
        oldws.ws_ypixel != newws.ws_ypixel))               oldws.ws_xpixel != newws.ws_xpixel ||
     received_window_change_signal = 1;               oldws.ws_ypixel != newws.ws_ypixel))
                   received_window_change_signal = 1;
   
   /* OK, we have been continued by the user.          /* OK, we have been continued by the user. Reinitialize buffers. */
      Reinitialize buffers. */          buffer_init(&stdin_buffer);
   buffer_init(&stdin_buffer);          buffer_init(&stdout_buffer);
   buffer_init(&stdout_buffer);          buffer_init(&stderr_buffer);
   buffer_init(&stderr_buffer);  
   
   /* Re-enter raw mode. */          /* Re-enter raw mode. */
   enter_raw_mode();          enter_raw_mode();
 }  }
   
 void client_process_input(fd_set *readset)  void
   client_process_input(fd_set * readset)
 {  {
   int len, pid;          int len, pid;
   char buf[8192], *s;          char buf[8192], *s;
   
   /* Read input from the server, and add any such data to the buffer of the          /* Read input from the server, and add any such data to the buffer
      packet subsystem. */             of the packet subsystem. */
   if (FD_ISSET(connection_in, readset))          if (FD_ISSET(connection_in, readset)) {
     {                  /* Read as much as possible. */
       /* Read as much as possible. */                  len = read(connection_in, buf, sizeof(buf));
       len = read(connection_in, buf, sizeof(buf));                  if (len == 0) {
       if (len == 0)                          /* Received EOF.  The remote host has closed the connection. */
         {                          snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
           /* Received EOF.  The remote host has closed the connection. */                                   host);
           snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",                          buffer_append(&stderr_buffer, buf, strlen(buf));
                   host);                          stderr_bytes += strlen(buf);
           buffer_append(&stderr_buffer, buf, strlen(buf));                          quit_pending = 1;
           stderr_bytes += strlen(buf);                          return;
           quit_pending = 1;                  }
           return;                  /* There is a kernel bug on Solaris that causes select to
         }                     sometimes wake up even though there is no data
                      available. */
                   if (len < 0 && errno == EAGAIN)
                           len = 0;
   
       /* There is a kernel bug on Solaris that causes select to sometimes                  if (len < 0) {
          wake up even though there is no data available. */                          /* An error has encountered.  Perhaps there is a network problem. */
       if (len < 0 && errno == EAGAIN)                          snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
         len = 0;                                   host, strerror(errno));
   
       if (len < 0)  
         {  
           /* An error has encountered.  Perhaps there is a network  
              problem. */  
           snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",  
                   host, strerror(errno));  
           buffer_append(&stderr_buffer, buf, strlen(buf));  
           stderr_bytes += strlen(buf);  
           quit_pending = 1;  
           return;  
         }  
       packet_process_incoming(buf, len);  
     }  
   
   /* Read input from stdin. */  
   if (FD_ISSET(fileno(stdin), readset))  
     {  
       /* Read as much as possible. */  
       len = read(fileno(stdin), buf, sizeof(buf));  
       if (len <= 0)  
         {  
           /* Received EOF or error.  They are treated similarly,  
              except that an error message is printed if it was  
              an error condition. */  
           if (len < 0)  
             {  
               snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));  
               buffer_append(&stderr_buffer, buf, strlen(buf));  
               stderr_bytes += strlen(buf);  
             }  
           /* Mark that we have seen EOF. */  
           stdin_eof = 1;  
           /* Send an EOF message to the server unless there is data  
              in the buffer.  If there is data in the buffer, no message  
              will be sent now.  Code elsewhere will send the EOF  
              when the buffer becomes empty if stdin_eof is set. */  
           if (buffer_len(&stdin_buffer) == 0)  
             {  
               packet_start(SSH_CMSG_EOF);  
               packet_send();  
             }  
         }  
       else  
         if (escape_char == -1)  
           {  
             /* Normal successful read, and no escape character.  Just  
                append the data to buffer. */  
             buffer_append(&stdin_buffer, buf, len);  
             stdin_bytes += len;  
           }  
         else  
           {  
             /* Normal, successful read.  But we have an escape character  
                and have to process the characters one by one. */  
             unsigned int i;  
             for (i = 0; i < len; i++)  
               {  
                 unsigned char ch;  
                 /* Get one character at a time. */  
                 ch = buf[i];  
   
                 /* Check if we have a pending escape character. */  
                 if (escape_pending)  
                   {  
                     /* We have previously seen an escape character. */  
                     /* Clear the flag now. */  
                     escape_pending = 0;  
                     /* Process the escaped character. */  
                     switch (ch)  
                       {  
                       case '.':  
                         /* Terminate the connection. */  
                         snprintf(buf, sizeof buf, "%c.\r\n", escape_char);  
                         buffer_append(&stderr_buffer, buf, strlen(buf));                          buffer_append(&stderr_buffer, buf, strlen(buf));
                         stderr_bytes += strlen(buf);                          stderr_bytes += strlen(buf);
                         quit_pending = 1;                          quit_pending = 1;
                         return;                          return;
                   }
                   packet_process_incoming(buf, len);
           }
           /* Read input from stdin. */
           if (FD_ISSET(fileno(stdin), readset)) {
                   /* Read as much as possible. */
                   len = read(fileno(stdin), buf, sizeof(buf));
                   if (len <= 0) {
                           /* Received EOF or error.  They are treated
                              similarly, except that an error message is
                              printed if it was an error condition. */
                           if (len < 0) {
                                   snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
                                   buffer_append(&stderr_buffer, buf, strlen(buf));
                                   stderr_bytes += strlen(buf);
                           }
                           /* Mark that we have seen EOF. */
                           stdin_eof = 1;
                           /* Send an EOF message to the server unless there
                              is data in the buffer.  If there is data in the
                              buffer, no message will be sent now.  Code
                              elsewhere will send the EOF when the buffer
                              becomes empty if stdin_eof is set. */
                           if (buffer_len(&stdin_buffer) == 0) {
                                   packet_start(SSH_CMSG_EOF);
                                   packet_send();
                           }
                   } else if (escape_char == -1) {
                           /* Normal successful read, and no escape
                              character.  Just append the data to buffer. */
                           buffer_append(&stdin_buffer, buf, len);
                           stdin_bytes += len;
                   } else {
                           /* Normal, successful read.  But we have an escape
                              character and have to process the characters
                              one by one. */
                           unsigned int i;
                           for (i = 0; i < len; i++) {
                                   unsigned char ch;
                                   /* Get one character at a time. */
                                   ch = buf[i];
   
                       case 'Z' - 64:                                  /* Check if we have a pending escape
                           /* Suspend the program. */                                     character. */
                           /* Print a message to that effect to the user. */                                  if (escape_pending) {
                           snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char);                                          /* We have previously seen an escape character. */
                           buffer_append(&stderr_buffer, buf, strlen(buf));                                          /* Clear the flag now. */
                           stderr_bytes += strlen(buf);                                          escape_pending = 0;
                                           /* Process the escaped character. */
                                           switch (ch) {
                                           case '.':
                                                   /* Terminate the connection. */
                                                   snprintf(buf, sizeof buf, "%c.\r\n", escape_char);
                                                   buffer_append(&stderr_buffer, buf, strlen(buf));
                                                   stderr_bytes += strlen(buf);
                                                   quit_pending = 1;
                                                   return;
   
                           /* Restore terminal modes and suspend. */                                          case 'Z' - 64:
                           client_suspend_self();                                                  /* Suspend the program. */
                                                   /* Print a message to that effect to the user. */
                                                   snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char);
                                                   buffer_append(&stderr_buffer, buf, strlen(buf));
                                                   stderr_bytes += strlen(buf);
   
                           /* We have been continued. */                                                  /* Restore terminal modes and suspend. */
                           continue;                                                  client_suspend_self();
   
                       case '&':  
                         /* Detach the program (continue to serve connections,  
                            but put in background and no more new  
                            connections). */  
                         if (!stdin_eof)  
                           {  
                             /* Sending SSH_CMSG_EOF alone does not always  
                                appear to be enough.  So we try to send an  
                                EOF character first. */  
                             packet_start(SSH_CMSG_STDIN_DATA);  
                             packet_put_string("\004", 1);  
                             packet_send();  
                             /* Close stdin. */  
                             stdin_eof = 1;  
                             if (buffer_len(&stdin_buffer) == 0)  
                               {  
                                 packet_start(SSH_CMSG_EOF);  
                                 packet_send();  
                               }  
                           }  
                         /* Restore tty modes. */  
                         leave_raw_mode();  
   
                         /* Stop listening for new connections. */                                                  /* We have been continued. */
                         channel_stop_listening();                                                  continue;
   
                         printf("%c& [backgrounded]\n", escape_char);                                          case '&':
                                                   /* Detach the program (continue to serve connections,
                         /* Fork into background. */                                                     but put in background and no more new connections). */
                         pid = fork();                                                  if (!stdin_eof) {
                         if (pid < 0)                                                          /* Sending SSH_CMSG_EOF alone does not always appear
                           {                                                             to be enough.  So we try to send an EOF character
                             error("fork: %.100s", strerror(errno));                                                             first. */
                             continue;                                                          packet_start(SSH_CMSG_STDIN_DATA);
                           }                                                          packet_put_string("\004", 1);
                         if (pid != 0)                                                          packet_send();
                           { /* This is the parent. */                                                          /* Close stdin. */
                             /* The parent just exits. */                                                          stdin_eof = 1;
                             exit(0);                                                          if (buffer_len(&stdin_buffer) == 0) {
                           }                                                                  packet_start(SSH_CMSG_EOF);
                                                                   packet_send();
                                                           }
                                                   }
                                                   /* Restore tty modes. */
                                                   leave_raw_mode();
   
                         /* The child continues serving connections. */                                                  /* Stop listening for new connections. */
                         continue;                                                  channel_stop_listening();
   
                       case '?':                                                  printf("%c& [backgrounded]\n", escape_char);
                         snprintf(buf, sizeof buf, "%c?\r\n\  
                                                   /* Fork into background. */
                                                   pid = fork();
                                                   if (pid < 0) {
                                                           error("fork: %.100s", strerror(errno));
                                                           continue;
                                                   }
                                                   if (pid != 0) { /* This is the parent. */
                                                           /* The parent just exits. */
                                                           exit(0);
                                                   }
                                                   /* The child continues serving connections. */
                                                   continue;
   
                                           case '?':
                                                   snprintf(buf, sizeof buf,
   "%c?\r\n\
 Supported escape sequences:\r\n\  Supported escape sequences:\r\n\
 ~.  - terminate connection\r\n\  ~.  - terminate connection\r\n\
 ~^Z - suspend ssh\r\n\  ~^Z - suspend ssh\r\n\
Line 647 
Line 632 
 ~?  - this message\r\n\  ~?  - this message\r\n\
 ~~  - send the escape character by typing it twice\r\n\  ~~  - send the escape character by typing it twice\r\n\
 (Note that escapes are only recognized immediately after newline.)\r\n",  (Note that escapes are only recognized immediately after newline.)\r\n",
                                 escape_char);                                                           escape_char);
                         buffer_append(&stderr_buffer, buf, strlen(buf));                                                  buffer_append(&stderr_buffer, buf, strlen(buf));
                         continue;                                                  continue;
   
                       case '#':                                          case '#':
                         snprintf(buf, sizeof buf, "%c#\r\n", escape_char);                                                  snprintf(buf, sizeof buf, "%c#\r\n", escape_char);
                         buffer_append(&stderr_buffer, buf, strlen(buf));                                                  buffer_append(&stderr_buffer, buf, strlen(buf));
                         s = channel_open_message();                                                  s = channel_open_message();
                         buffer_append(&stderr_buffer, s, strlen(s));                                                  buffer_append(&stderr_buffer, s, strlen(s));
                         xfree(s);                                                  xfree(s);
                         continue;                                                  continue;
   
                       default:                                          default:
                         if (ch != escape_char)                                                  if (ch != escape_char) {
                           {                                                          /* Escape character followed by non-special character.
                             /* Escape character followed by non-special                                                             Append both to the input buffer. */
                                character.  Append both to the input                                                          buf[0] = escape_char;
                                buffer. */                                                          buf[1] = ch;
                             buf[0] = escape_char;                                                          buffer_append(&stdin_buffer, buf, 2);
                             buf[1] = ch;                                                          stdin_bytes += 2;
                             buffer_append(&stdin_buffer, buf, 2);                                                          continue;
                             stdin_bytes += 2;                                                  }
                             continue;                                                  /* Note that escape character typed twice
                           }                                                     falls through here; the latter gets processed
                         /* Note that escape character typed twice falls through                                                     as a normal character below. */
                            here; the latter gets processed as a normal                                                  break;
                            character below. */                                          }
                         break;                                  } else {
                       }                                          /* The previous character was not an escape char. Check if this
                   }                                             is an escape. */
                 else                                          if (last_was_cr && ch == escape_char) {
                   {                                                  /* It is. Set the flag and continue to next character. */
                     /* The previous character was not an escape char.                                                  escape_pending = 1;
                        Check if this is an escape. */                                                  continue;
                     if (last_was_cr && ch == escape_char)                                          }
                       {                                  }
                         /* It is. Set the flag and continue to next  
                            character. */  
                         escape_pending = 1;  
                         continue;  
                       }  
                   }  
   
                 /* Normal character.  Record whether it was a newline,                                  /* Normal character.  Record whether it was a newline, and append it to the
                    and append it to the buffer. */                                     buffer. */
                 last_was_cr = (ch == '\r' || ch == '\n');                                  last_was_cr = (ch == '\r' || ch == '\n');
                 buf[0] = ch;                                  buf[0] = ch;
                 buffer_append(&stdin_buffer, buf, 1);                                  buffer_append(&stdin_buffer, buf, 1);
                 stdin_bytes += 1;                                  stdin_bytes += 1;
                 continue;                                  continue;
               }                          }
           }                  }
     }          }
 }  }
   
 void client_process_output(fd_set *writeset)  void
   client_process_output(fd_set * writeset)
 {  {
   int len;          int len;
   char buf[100];          char buf[100];
   
   /* Write buffered output to stdout. */          /* Write buffered output to stdout. */
   if (FD_ISSET(fileno(stdout), writeset))          if (FD_ISSET(fileno(stdout), writeset)) {
     {                  /* Write as much data as possible. */
       /* Write as much data as possible. */                  len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
       len = write(fileno(stdout), buffer_ptr(&stdout_buffer),                              buffer_len(&stdout_buffer));
                   buffer_len(&stdout_buffer));                  if (len <= 0) {
       if (len <= 0)                          if (errno == EAGAIN)
         {                                  len = 0;
           if (errno == EAGAIN)                          else {
             len = 0;                                  /* An error or EOF was encountered.  Put
           else                                     an error message to stderr buffer. */
             {                                  snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
               /* An error or EOF was encountered.  Put an error message                                  buffer_append(&stderr_buffer, buf, strlen(buf));
                  to stderr buffer. */                                  stderr_bytes += strlen(buf);
               snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));                                  quit_pending = 1;
               buffer_append(&stderr_buffer, buf, strlen(buf));                                  return;
               stderr_bytes += strlen(buf);                          }
               quit_pending = 1;                  }
               return;                  /* Consume printed data from the buffer. */
             }                  buffer_consume(&stdout_buffer, len);
         }          }
       /* Consume printed data from the buffer. */          /* Write buffered output to stderr. */
       buffer_consume(&stdout_buffer, len);          if (FD_ISSET(fileno(stderr), writeset)) {
     }                  /* Write as much data as possible. */
                   len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
   /* Write buffered output to stderr. */                              buffer_len(&stderr_buffer));
   if (FD_ISSET(fileno(stderr), writeset))                  if (len <= 0) {
     {                          if (errno == EAGAIN)
       /* Write as much data as possible. */                                  len = 0;
       len = write(fileno(stderr), buffer_ptr(&stderr_buffer),                          else {
                   buffer_len(&stderr_buffer));                                  /* EOF or error, but can't even print
       if (len <= 0) {                                     error message. */
         if (errno == EAGAIN)                                  quit_pending = 1;
           len = 0;                                  return;
         else                          }
           {                  }
             /* EOF or error, but can't even print error message. */                  /* Consume printed characters from the buffer. */
             quit_pending = 1;                  buffer_consume(&stderr_buffer, len);
             return;          }
           }  
       }  
       /* Consume printed characters from the buffer. */  
       buffer_consume(&stderr_buffer, len);  
     }  
 }  }
   
 /* Implements the interactive session with the server.  This is called  /* Implements the interactive session with the server.  This is called
Line 759 
Line 734 
    used as an escape character for terminating or suspending the     used as an escape character for terminating or suspending the
    session. */     session. */
   
 int client_loop(int have_pty, int escape_char_arg)  int
   client_loop(int have_pty, int escape_char_arg)
 {  {
   extern Options options;          extern Options options;
   double start_time, total_time;          double start_time, total_time;
   int len;          int len;
   char buf[100];          char buf[100];
   
   debug("Entering interactive session.");          debug("Entering interactive session.");
   
   start_time = get_current_time();          start_time = get_current_time();
   
   /* Initialize variables. */          /* Initialize variables. */
   escape_pending = 0;          escape_pending = 0;
   last_was_cr = 1;          last_was_cr = 1;
   exit_status = -1;          exit_status = -1;
   stdin_eof = 0;          stdin_eof = 0;
   buffer_high = 64 * 1024;          buffer_high = 64 * 1024;
   connection_in = packet_get_connection_in();          connection_in = packet_get_connection_in();
   connection_out = packet_get_connection_out();          connection_out = packet_get_connection_out();
   max_fd = connection_in;          max_fd = connection_in;
   if (connection_out > max_fd)          if (connection_out > max_fd)
     max_fd = connection_out;                  max_fd = connection_out;
   stdin_bytes = 0;          stdin_bytes = 0;
   stdout_bytes = 0;          stdout_bytes = 0;
   stderr_bytes = 0;          stderr_bytes = 0;
   quit_pending = 0;          quit_pending = 0;
   escape_char = escape_char_arg;          escape_char = escape_char_arg;
   
   /* Initialize buffers. */          /* Initialize buffers. */
   buffer_init(&stdin_buffer);          buffer_init(&stdin_buffer);
   buffer_init(&stdout_buffer);          buffer_init(&stdout_buffer);
   buffer_init(&stderr_buffer);          buffer_init(&stderr_buffer);
   
   /* Set signal handlers to restore non-blocking mode.  */          /* Set signal handlers to restore non-blocking mode.  */
   signal(SIGINT, signal_handler);          signal(SIGINT, signal_handler);
   signal(SIGQUIT, signal_handler);          signal(SIGQUIT, signal_handler);
   signal(SIGTERM, signal_handler);          signal(SIGTERM, signal_handler);
   signal(SIGPIPE, SIG_IGN);          signal(SIGPIPE, SIG_IGN);
   if (have_pty)          if (have_pty)
     signal(SIGWINCH, window_change_handler);                  signal(SIGWINCH, window_change_handler);
   
   /* Enter raw mode if have a pseudo terminal. */          /* Enter raw mode if have a pseudo terminal. */
   if (have_pty)          if (have_pty)
     enter_raw_mode();                  enter_raw_mode();
   
   /* Check if we should immediately send of on stdin. */          /* Check if we should immediately send of on stdin. */
   client_check_initial_eof_on_stdin();          client_check_initial_eof_on_stdin();
   
   /* Main loop of the client for the interactive session mode. */          /* Main loop of the client for the interactive session mode. */
   while (!quit_pending)          while (!quit_pending) {
     {                  fd_set readset, writeset;
       fd_set readset, writeset;  
   
       /* Precess buffered packets sent by the server. */                  /* Precess buffered packets sent by the server. */
       client_process_buffered_input_packets();                  client_process_buffered_input_packets();
   
       /* Make packets of buffered stdin data, and buffer them for sending                  /* Make packets of buffered stdin data, and buffer them
          to the server. */                     for sending to the server. */
       client_make_packets_from_stdin_data();                  client_make_packets_from_stdin_data();
   
       /* Make packets from buffered channel data, and buffer them for sending                  /* Make packets from buffered channel data, and buffer
          to the server. */                     them for sending to the server. */
       if (packet_not_very_much_data_to_write())                  if (packet_not_very_much_data_to_write())
         channel_output_poll();                          channel_output_poll();
   
       /* Check if the window size has changed, and buffer a message about                  /* Check if the window size has changed, and buffer a
          it to the server if so. */                     message about it to the server if so. */
       client_check_window_change();                  client_check_window_change();
   
       if (quit_pending)                  if (quit_pending)
         break;                          break;
   
       /* Wait until we have something to do (something becomes available                  /* Wait until we have something to do (something becomes
          on one of the descriptors). */                     available on one of the descriptors). */
       client_wait_until_can_do_something(&readset, &writeset);                  client_wait_until_can_do_something(&readset, &writeset);
   
       if (quit_pending)                  if (quit_pending)
         break;                          break;
   
       /* Do channel operations. */                  /* Do channel operations. */
       channel_after_select(&readset, &writeset);                  channel_after_select(&readset, &writeset);
   
       /* Process input from the connection and from stdin.  Buffer any data                  /* Process input from the connection and from stdin.
          that is available. */                     Buffer any data that is available. */
       client_process_input(&readset);                  client_process_input(&readset);
   
       /* Process output to stdout and stderr.   Output to the connection                  /* Process output to stdout and stderr.   Output to the
          is processed elsewhere (above). */                     connection is processed elsewhere (above). */
       client_process_output(&writeset);                  client_process_output(&writeset);
   
       /* Send as much buffered packet data as possible to the sender. */                  /* Send as much buffered packet data as possible to the
       if (FD_ISSET(connection_out, &writeset))                     sender. */
         packet_write_poll();                  if (FD_ISSET(connection_out, &writeset))
     }                          packet_write_poll();
           }
   
   /* Terminate the session. */          /* Terminate the session. */
   
   /* Stop watching for window change. */          /* Stop watching for window change. */
   if (have_pty)          if (have_pty)
     signal(SIGWINCH, SIG_DFL);                  signal(SIGWINCH, SIG_DFL);
   
   /* Stop listening for connections. */          /* Stop listening for connections. */
   channel_stop_listening();          channel_stop_listening();
   
   /* In interactive mode (with pseudo tty) display a message indicating that          /* In interactive mode (with pseudo tty) display a message
      the connection has been closed. */             indicating that the connection has been closed. */
   if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET)          if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
     {                  snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
       snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);                  buffer_append(&stderr_buffer, buf, strlen(buf));
       buffer_append(&stderr_buffer, buf, strlen(buf));                  stderr_bytes += strlen(buf);
       stderr_bytes += strlen(buf);  
     }  
   
   /* Output any buffered data for stdout. */  
   while (buffer_len(&stdout_buffer) > 0)  
     {  
       len = write(fileno(stdout), buffer_ptr(&stdout_buffer),  
                   buffer_len(&stdout_buffer));  
       if (len <= 0)  
         {  
           error("Write failed flushing stdout buffer.");  
           break;  
         }          }
       buffer_consume(&stdout_buffer, len);          /* Output any buffered data for stdout. */
     }          while (buffer_len(&stdout_buffer) > 0) {
                   len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
                               buffer_len(&stdout_buffer));
                   if (len <= 0) {
                           error("Write failed flushing stdout buffer.");
                           break;
                   }
                   buffer_consume(&stdout_buffer, len);
           }
   
   /* Output any buffered data for stderr. */          /* Output any buffered data for stderr. */
   while (buffer_len(&stderr_buffer) > 0)          while (buffer_len(&stderr_buffer) > 0) {
     {                  len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
       len = write(fileno(stderr), buffer_ptr(&stderr_buffer),                              buffer_len(&stderr_buffer));
                   buffer_len(&stderr_buffer));                  if (len <= 0) {
       if (len <= 0)                          error("Write failed flushing stderr buffer.");
         {                          break;
           error("Write failed flushing stderr buffer.");                  }
           break;                  buffer_consume(&stderr_buffer, len);
         }          }
       buffer_consume(&stderr_buffer, len);  
     }  
   
   /* Leave raw mode. */          /* Leave raw mode. */
   if (have_pty)          if (have_pty)
     leave_raw_mode();                  leave_raw_mode();
   
   /* Clear and free any buffers. */          /* Clear and free any buffers. */
   memset(buf, 0, sizeof(buf));          memset(buf, 0, sizeof(buf));
   buffer_free(&stdin_buffer);          buffer_free(&stdin_buffer);
   buffer_free(&stdout_buffer);          buffer_free(&stdout_buffer);
   buffer_free(&stderr_buffer);          buffer_free(&stderr_buffer);
   
   /* Report bytes transferred, and transfer rates. */          /* Report bytes transferred, and transfer rates. */
   total_time = get_current_time() - start_time;          total_time = get_current_time() - start_time;
   debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",          debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
         stdin_bytes, stdout_bytes, stderr_bytes, total_time);                stdin_bytes, stdout_bytes, stderr_bytes, total_time);
   if (total_time > 0)          if (total_time > 0)
     debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",                  debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
           stdin_bytes / total_time, stdout_bytes / total_time,                        stdin_bytes / total_time, stdout_bytes / total_time,
           stderr_bytes / total_time);                        stderr_bytes / total_time);
   
   /* Return the exit status of the program. */          /* Return the exit status of the program. */
   debug("Exit status %d", exit_status);          debug("Exit status %d", exit_status);
   return exit_status;          return exit_status;
 }  }

Legend:
Removed from v.1.10  
changed lines
  Added in v.1.11