Annotation of src/usr.bin/ssh/clientloop.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2:
! 3: clientloop.c
! 4:
! 5: Author: Tatu Ylonen <ylo@cs.hut.fi>
! 6:
! 7: Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
! 8: All rights reserved
! 9:
! 10:
! 11: Created: Sat Sep 23 12:23:57 1995 ylo
! 12:
! 13: The main loop for the interactive session (client side).
! 14:
! 15: */
! 16:
! 17: #include "includes.h"
! 18: RCSID("$Id: clientloop.c,v 1.4 1999/05/04 11:58:37 bg Exp $");
! 19:
! 20: #include "xmalloc.h"
! 21: #include "randoms.h"
! 22: #include "ssh.h"
! 23: #include "packet.h"
! 24: #include "buffer.h"
! 25: #include "authfd.h"
! 26:
! 27: /* Flag indicating whether quiet mode is on. */
! 28: extern int quiet_flag;
! 29:
! 30: /* Flag indicating that stdin should be redirected from /dev/null. */
! 31: extern int stdin_null_flag;
! 32:
! 33: /* Name of the host we are connecting to. This is the name given on the
! 34: command line, or the HostName specified for the user-supplied name
! 35: in a configuration file. */
! 36: extern char *host;
! 37:
! 38: #ifdef SIGWINCH
! 39: /* Flag to indicate that we have received a window change signal which has
! 40: not yet been processed. This will cause a message indicating the new
! 41: window size to be sent to the server a little later. This is volatile
! 42: because this is updated in a signal handler. */
! 43: static volatile int received_window_change_signal = 0;
! 44: #endif /* SIGWINCH */
! 45:
! 46: /* Terminal modes, as saved by enter_raw_mode. */
! 47: #ifdef USING_TERMIOS
! 48: static struct termios saved_tio;
! 49: #endif
! 50: #ifdef USING_SGTTY
! 51: static struct sgttyb saved_tio;
! 52: #endif
! 53:
! 54: /* Flag indicating whether we are in raw mode. This is used by enter_raw_mode
! 55: and leave_raw_mode. */
! 56: static int in_raw_mode = 0;
! 57:
! 58: /* Flag indicating whether the user\'s terminal is in non-blocking mode. */
! 59: static int in_non_blocking_mode = 0;
! 60:
! 61: /* Common data for the client loop code. */
! 62: static int escape_pending; /* Last character was the escape character */
! 63: static int last_was_cr; /* Last character was a newline. */
! 64: static int exit_status; /* Used to store the exit status of the command. */
! 65: static int stdin_eof; /* EOF has been encountered on standard error. */
! 66: static Buffer stdin_buffer; /* Buffer for stdin data. */
! 67: static Buffer stdout_buffer; /* Buffer for stdout data. */
! 68: static Buffer stderr_buffer; /* Buffer for stderr data. */
! 69: static unsigned int buffer_high; /* Soft max buffer size. */
! 70: static int max_fd; /* Maximum file descriptor number in select(). */
! 71: static int connection_in; /* Connection to server (input). */
! 72: static int connection_out; /* Connection to server (output). */
! 73: static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
! 74: static int quit_pending; /* Set to non-zero to quit the client loop. */
! 75: static int escape_char; /* Escape character. */
! 76:
! 77: /* Returns the user\'s terminal to normal mode if it had been put in raw
! 78: mode. */
! 79:
! 80: void leave_raw_mode()
! 81: {
! 82: if (!in_raw_mode)
! 83: return;
! 84: in_raw_mode = 0;
! 85: #ifdef USING_TERMIOS
! 86: if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)
! 87: perror("tcsetattr");
! 88: #endif /* USING_TERMIOS */
! 89: #ifdef USING_SGTTY
! 90: if (ioctl(fileno(stdin), TIOCSETP, &saved_tio) < 0)
! 91: perror("ioctl(stdin, TIOCSETP, ...)");
! 92: #endif /* USING_SGTTY */
! 93:
! 94: fatal_remove_cleanup((void (*)(void *))leave_raw_mode, NULL);
! 95: }
! 96:
! 97: /* Puts the user\'s terminal in raw mode. */
! 98:
! 99: void enter_raw_mode()
! 100: {
! 101: #ifdef USING_TERMIOS
! 102: struct termios tio;
! 103:
! 104: if (tcgetattr(fileno(stdin), &tio) < 0)
! 105: perror("tcgetattr");
! 106: saved_tio = tio;
! 107: tio.c_iflag |= IGNPAR;
! 108: tio.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF);
! 109: tio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHONL);
! 110: #ifdef IEXTEN
! 111: tio.c_lflag &= ~IEXTEN;
! 112: #endif /* IEXTEN */
! 113: tio.c_oflag &= ~OPOST;
! 114: tio.c_cc[VMIN] = 1;
! 115: tio.c_cc[VTIME] = 0;
! 116: if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)
! 117: perror("tcsetattr");
! 118: in_raw_mode = 1;
! 119: #endif /* USING_TERMIOS */
! 120: #ifdef USING_SGTTY
! 121: struct sgttyb tio;
! 122:
! 123: if (ioctl(fileno(stdin), TIOCGETP, &tio) < 0)
! 124: perror("ioctl(stdin, TIOCGETP, ...)");
! 125: saved_tio = tio;
! 126: tio.sg_flags &= ~(CBREAK | ECHO | CRMOD | LCASE | TANDEM);
! 127: tio.sg_flags |= (RAW | ANYP);
! 128: if (ioctl(fileno(stdin), TIOCSETP, &tio) < 0)
! 129: perror("ioctl(stdin, TIOCSETP, ...)");
! 130: in_raw_mode = 1;
! 131: #endif /* USING_SGTTY */
! 132:
! 133: fatal_add_cleanup((void (*)(void *))leave_raw_mode, NULL);
! 134: }
! 135:
! 136: /* Puts stdin terminal in non-blocking mode. */
! 137:
! 138: /* Restores stdin to blocking mode. */
! 139:
! 140: void leave_non_blocking()
! 141: {
! 142: if (in_non_blocking_mode)
! 143: {
! 144: (void)fcntl(fileno(stdin), F_SETFL, 0);
! 145: in_non_blocking_mode = 0;
! 146: fatal_remove_cleanup((void (*)(void *))leave_non_blocking, NULL);
! 147: }
! 148: }
! 149:
! 150: void enter_non_blocking()
! 151: {
! 152: in_non_blocking_mode = 1;
! 153: #if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN)
! 154: (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
! 155: #else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
! 156: (void)fcntl(fileno(stdin), F_SETFL, O_NDELAY);
! 157: #endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
! 158: fatal_add_cleanup((void (*)(void *))leave_non_blocking, NULL);
! 159: }
! 160:
! 161: #ifdef SIGWINCH
! 162: /* Signal handler for the window change signal (SIGWINCH). This just
! 163: sets a flag indicating that the window has changed. */
! 164:
! 165: RETSIGTYPE window_change_handler(int sig)
! 166: {
! 167: received_window_change_signal = 1;
! 168: signal(SIGWINCH, window_change_handler);
! 169: }
! 170: #endif /* SIGWINCH */
! 171:
! 172: /* Signal handler for signals that cause the program to terminate. These
! 173: signals must be trapped to restore terminal modes. */
! 174:
! 175: RETSIGTYPE signal_handler(int sig)
! 176: {
! 177: if (in_raw_mode)
! 178: leave_raw_mode();
! 179: if (in_non_blocking_mode)
! 180: leave_non_blocking();
! 181: channel_stop_listening();
! 182: packet_close();
! 183: fatal("Killed by signal %d.", sig);
! 184: }
! 185:
! 186: /* Returns current time in seconds from Jan 1, 1970 with the maximum available
! 187: resolution. */
! 188:
! 189: double get_current_time()
! 190: {
! 191: #ifdef HAVE_GETTIMEOFDAY
! 192: struct timeval tv;
! 193: gettimeofday(&tv, NULL);
! 194: return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
! 195: #else /* HAVE_GETTIMEOFDAY */
! 196: return (double)time(NULL);
! 197: #endif /* HAVE_GETTIMEOFDAY */
! 198: }
! 199:
! 200: /* This is called when the interactive is entered. This checks if there
! 201: is an EOF coming on stdin. We must check this explicitly, as select()
! 202: does not appear to wake up when redirecting from /dev/null. */
! 203:
! 204: void client_check_initial_eof_on_stdin()
! 205: {
! 206: int len;
! 207: char buf[1];
! 208:
! 209: /* If standard input is to be "redirected from /dev/null", we simply
! 210: mark that we have seen an EOF and send an EOF message to the server.
! 211: Otherwise, we try to read a single character; it appears that for some
! 212: files, such /dev/null, select() never wakes up for read for this
! 213: descriptor, which means that we never get EOF. This way we will get
! 214: the EOF if stdin comes from /dev/null or similar. */
! 215: if (stdin_null_flag)
! 216: {
! 217: /* Fake EOF on stdin. */
! 218: debug("Sending eof.");
! 219: stdin_eof = 1;
! 220: packet_start(SSH_CMSG_EOF);
! 221: packet_send();
! 222: }
! 223: else
! 224: {
! 225: /* Enter non-blocking mode for stdin. */
! 226: enter_non_blocking();
! 227:
! 228: /* Check for immediate EOF on stdin. */
! 229: len = read(fileno(stdin), buf, 1);
! 230: if (len == 0)
! 231: {
! 232: /* EOF. Record that we have seen it and send EOF to server. */
! 233: debug("Sending eof.");
! 234: stdin_eof = 1;
! 235: packet_start(SSH_CMSG_EOF);
! 236: packet_send();
! 237: }
! 238: else
! 239: if (len > 0)
! 240: {
! 241: /* Got data. We must store the data in the buffer, and also
! 242: process it as an escape character if appropriate. */
! 243: if ((unsigned char)buf[0] == escape_char)
! 244: escape_pending = 1;
! 245: else
! 246: {
! 247: buffer_append(&stdin_buffer, buf, 1);
! 248: stdin_bytes += 1;
! 249: }
! 250: }
! 251:
! 252: /* Leave non-blocking mode. */
! 253: leave_non_blocking();
! 254: }
! 255: }
! 256:
! 257: /* Get packets from the connection input buffer, and process them as long
! 258: as there are packets available. */
! 259:
! 260: void client_process_buffered_input_packets()
! 261: {
! 262: int type;
! 263: char *data;
! 264: unsigned int data_len;
! 265: int payload_len;
! 266:
! 267: /* Process any buffered packets from the server. */
! 268: while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE)
! 269: {
! 270: switch (type)
! 271: {
! 272:
! 273: case SSH_SMSG_STDOUT_DATA:
! 274: data = packet_get_string(&data_len);
! 275: packet_integrity_check(payload_len, 4 + data_len, type);
! 276: buffer_append(&stdout_buffer, data, data_len);
! 277: stdout_bytes += data_len;
! 278: memset(data, 0, data_len);
! 279: xfree(data);
! 280: break;
! 281:
! 282: case SSH_SMSG_STDERR_DATA:
! 283: data = packet_get_string(&data_len);
! 284: packet_integrity_check(payload_len, 4 + data_len, type);
! 285: buffer_append(&stderr_buffer, data, data_len);
! 286: stdout_bytes += data_len;
! 287: memset(data, 0, data_len);
! 288: xfree(data);
! 289: break;
! 290:
! 291: case SSH_SMSG_EXITSTATUS:
! 292: packet_integrity_check(payload_len, 4, type);
! 293: exit_status = packet_get_int();
! 294: /* Acknowledge the exit. */
! 295: packet_start(SSH_CMSG_EXIT_CONFIRMATION);
! 296: packet_send();
! 297: /* Must wait for packet to be sent since we are exiting the
! 298: loop. */
! 299: packet_write_wait();
! 300: /* Flag that we want to exit. */
! 301: quit_pending = 1;
! 302: break;
! 303:
! 304: case SSH_SMSG_X11_OPEN:
! 305: x11_input_open(payload_len);
! 306: break;
! 307:
! 308: case SSH_MSG_PORT_OPEN:
! 309: channel_input_port_open(payload_len);
! 310: break;
! 311:
! 312: case SSH_SMSG_AGENT_OPEN:
! 313: packet_integrity_check(payload_len, 4, type);
! 314: auth_input_open_request();
! 315: break;
! 316:
! 317: case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
! 318: packet_integrity_check(payload_len, 4 + 4, type);
! 319: channel_input_open_confirmation();
! 320: break;
! 321:
! 322: case SSH_MSG_CHANNEL_OPEN_FAILURE:
! 323: packet_integrity_check(payload_len, 4, type);
! 324: channel_input_open_failure();
! 325: break;
! 326:
! 327: case SSH_MSG_CHANNEL_DATA:
! 328: channel_input_data(payload_len);
! 329: break;
! 330:
! 331: case SSH_MSG_CHANNEL_CLOSE:
! 332: packet_integrity_check(payload_len, 4, type);
! 333: channel_input_close();
! 334: break;
! 335:
! 336: case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
! 337: packet_integrity_check(payload_len, 4, type);
! 338: channel_input_close_confirmation();
! 339: break;
! 340:
! 341: default:
! 342: /* Any unknown packets received during the actual session
! 343: cause the session to terminate. This is intended to make
! 344: debugging easier since no confirmations are sent. Any
! 345: compatible protocol extensions must be negotiated during
! 346: the preparatory phase. */
! 347: packet_disconnect("Protocol error during session: type %d",
! 348: type);
! 349: }
! 350: }
! 351: }
! 352:
! 353: /* Make packets from buffered stdin data, and buffer them for sending to
! 354: the connection. */
! 355:
! 356: void client_make_packets_from_stdin_data()
! 357: {
! 358: unsigned int len;
! 359:
! 360: /* Send buffered stdin data to the server. */
! 361: while (buffer_len(&stdin_buffer) > 0 &&
! 362: packet_not_very_much_data_to_write())
! 363: {
! 364: len = buffer_len(&stdin_buffer);
! 365: if (len > 32768)
! 366: len = 32768; /* Keep the packets at reasonable size. */
! 367: packet_start(SSH_CMSG_STDIN_DATA);
! 368: packet_put_string(buffer_ptr(&stdin_buffer), len);
! 369: packet_send();
! 370: buffer_consume(&stdin_buffer, len);
! 371: /* If we have a pending EOF, send it now. */
! 372: if (stdin_eof && buffer_len(&stdin_buffer) == 0)
! 373: {
! 374: packet_start(SSH_CMSG_EOF);
! 375: packet_send();
! 376: }
! 377: }
! 378: }
! 379:
! 380: /* Checks if the client window has changed, and sends a packet about it to
! 381: the server if so. The actual change is detected elsewhere (by a software
! 382: interrupt on Unix); this just checks the flag and sends a message if
! 383: appropriate. */
! 384:
! 385: void client_check_window_change()
! 386: {
! 387: #ifdef SIGWINCH
! 388: /* Send possible window change message to the server. */
! 389: if (received_window_change_signal)
! 390: {
! 391: struct winsize ws;
! 392:
! 393: /* Clear the window change indicator. */
! 394: received_window_change_signal = 0;
! 395:
! 396: /* Read new window size. */
! 397: if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0)
! 398: {
! 399: /* Successful, send the packet now. */
! 400: packet_start(SSH_CMSG_WINDOW_SIZE);
! 401: packet_put_int(ws.ws_row);
! 402: packet_put_int(ws.ws_col);
! 403: packet_put_int(ws.ws_xpixel);
! 404: packet_put_int(ws.ws_ypixel);
! 405: packet_send();
! 406: }
! 407: }
! 408: #endif /* SIGWINCH */
! 409: }
! 410:
! 411: /* Waits until the client can do something (some data becomes available on
! 412: one of the file descriptors). */
! 413:
! 414: void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset)
! 415: {
! 416: /* Initialize select masks. */
! 417: FD_ZERO(readset);
! 418:
! 419: /* Read from the connection, unless our buffers are full. */
! 420: if (buffer_len(&stdout_buffer) < buffer_high &&
! 421: buffer_len(&stderr_buffer) < buffer_high &&
! 422: channel_not_very_much_buffered_data())
! 423: FD_SET(connection_in, readset);
! 424:
! 425: /* Read from stdin, unless we have seen EOF or have very much buffered
! 426: data to send to the server. */
! 427: if (!stdin_eof && packet_not_very_much_data_to_write())
! 428: FD_SET(fileno(stdin), readset);
! 429:
! 430: FD_ZERO(writeset);
! 431:
! 432: /* Add any selections by the channel mechanism. */
! 433: channel_prepare_select(readset, writeset);
! 434:
! 435: /* Select server connection if have data to write to the server. */
! 436: if (packet_have_data_to_write())
! 437: FD_SET(connection_out, writeset);
! 438:
! 439: /* Select stdout if have data in buffer. */
! 440: if (buffer_len(&stdout_buffer) > 0)
! 441: FD_SET(fileno(stdout), writeset);
! 442:
! 443: /* Select stderr if have data in buffer. */
! 444: if (buffer_len(&stderr_buffer) > 0)
! 445: FD_SET(fileno(stderr), writeset);
! 446:
! 447: /* Update maximum file descriptor number, if appropriate. */
! 448: if (channel_max_fd() > max_fd)
! 449: max_fd = channel_max_fd();
! 450:
! 451: /* Wait for something to happen. This will suspend the process until
! 452: some selected descriptor can be read, written, or has some other
! 453: event pending. Note: if you want to implement SSH_MSG_IGNORE
! 454: messages to fool traffic analysis, this might be the place to do
! 455: it: just have a random timeout for the select, and send a random
! 456: SSH_MSG_IGNORE packet when the timeout expires. */
! 457: if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0)
! 458: {
! 459: char buf[100];
! 460: /* Some systems fail to clear these automatically. */
! 461: FD_ZERO(readset);
! 462: FD_ZERO(writeset);
! 463: if (errno == EINTR)
! 464: return;
! 465: /* Note: we might still have data in the buffers. */
! 466: sprintf(buf, "select: %.100s\r\n", strerror(errno));
! 467: buffer_append(&stderr_buffer, buf, strlen(buf));
! 468: stderr_bytes += strlen(buf);
! 469: quit_pending = 1;
! 470: }
! 471: }
! 472:
! 473: void client_suspend_self()
! 474: {
! 475: #ifdef SIGWINCH
! 476: struct winsize oldws, newws;
! 477: #endif /* SIGWINCH */
! 478:
! 479: /* Flush stdout and stderr buffers. */
! 480: if (buffer_len(&stdout_buffer) > 0)
! 481: write(fileno(stdout),
! 482: buffer_ptr(&stdout_buffer),
! 483: buffer_len(&stdout_buffer));
! 484: if (buffer_len(&stderr_buffer) > 0)
! 485: write(fileno(stderr),
! 486: buffer_ptr(&stderr_buffer),
! 487: buffer_len(&stderr_buffer));
! 488:
! 489: /* Leave raw mode. */
! 490: leave_raw_mode();
! 491:
! 492: /* Free (and clear) the buffer to reduce the
! 493: amount of data that gets written to swap. */
! 494: buffer_free(&stdin_buffer);
! 495: buffer_free(&stdout_buffer);
! 496: buffer_free(&stderr_buffer);
! 497:
! 498: #ifdef SIGWINCH
! 499: /* Save old window size. */
! 500: ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
! 501: #endif /* SIGWINCH */
! 502:
! 503: /* Send the suspend signal to the program
! 504: itself. */
! 505: kill(getpid(), SIGTSTP);
! 506:
! 507: #ifdef SIGWINCH
! 508: /* Check if the window size has changed. */
! 509: if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
! 510: (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col ||
! 511: oldws.ws_xpixel != newws.ws_xpixel ||
! 512: oldws.ws_ypixel != newws.ws_ypixel))
! 513: received_window_change_signal = 1;
! 514: #endif /* SIGWINCH */
! 515:
! 516: /* OK, we have been continued by the user.
! 517: Reinitialize buffers. */
! 518: buffer_init(&stdin_buffer);
! 519: buffer_init(&stdout_buffer);
! 520: buffer_init(&stderr_buffer);
! 521:
! 522: /* Re-enter raw mode. */
! 523: enter_raw_mode();
! 524: }
! 525:
! 526: void client_process_input(fd_set *readset)
! 527: {
! 528: int len, pid;
! 529: char buf[8192], *s;
! 530:
! 531: /* Read input from the server, and add any such data to the buffer of the
! 532: packet subsystem. */
! 533: if (FD_ISSET(connection_in, readset))
! 534: {
! 535: /* Read as much as possible. */
! 536: len = read(connection_in, buf, sizeof(buf));
! 537: if (len == 0)
! 538: {
! 539: /* Received EOF. The remote host has closed the connection. */
! 540: sprintf(buf, "Connection to %.300s closed by remote host.\r\n",
! 541: host);
! 542: buffer_append(&stderr_buffer, buf, strlen(buf));
! 543: stderr_bytes += strlen(buf);
! 544: quit_pending = 1;
! 545: return;
! 546: }
! 547:
! 548: /* There is a kernel bug on Solaris that causes select to sometimes
! 549: wake up even though there is no data available. */
! 550: if (len < 0 && errno == EAGAIN)
! 551: len = 0;
! 552:
! 553: if (len < 0)
! 554: {
! 555: /* An error has encountered. Perhaps there is a network
! 556: problem. */
! 557: sprintf(buf, "Read from remote host %.300s: %.100s\r\n",
! 558: host, strerror(errno));
! 559: buffer_append(&stderr_buffer, buf, strlen(buf));
! 560: stderr_bytes += strlen(buf);
! 561: quit_pending = 1;
! 562: return;
! 563: }
! 564: packet_process_incoming(buf, len);
! 565: }
! 566:
! 567: /* Read input from stdin. */
! 568: if (FD_ISSET(fileno(stdin), readset))
! 569: {
! 570: /* Read as much as possible. */
! 571: len = read(fileno(stdin), buf, sizeof(buf));
! 572: if (len <= 0)
! 573: {
! 574: /* Received EOF or error. They are treated similarly,
! 575: except that an error message is printed if it was
! 576: an error condition. */
! 577: if (len < 0)
! 578: {
! 579: sprintf(buf, "read: %.100s\r\n", strerror(errno));
! 580: buffer_append(&stderr_buffer, buf, strlen(buf));
! 581: stderr_bytes += strlen(buf);
! 582: }
! 583: /* Mark that we have seen EOF. */
! 584: stdin_eof = 1;
! 585: /* Send an EOF message to the server unless there is data
! 586: in the buffer. If there is data in the buffer, no message
! 587: will be sent now. Code elsewhere will send the EOF
! 588: when the buffer becomes empty if stdin_eof is set. */
! 589: if (buffer_len(&stdin_buffer) == 0)
! 590: {
! 591: packet_start(SSH_CMSG_EOF);
! 592: packet_send();
! 593: }
! 594: }
! 595: else
! 596: if (escape_char == -1)
! 597: {
! 598: /* Normal successful read, and no escape character. Just
! 599: append the data to buffer. */
! 600: buffer_append(&stdin_buffer, buf, len);
! 601: stdin_bytes += len;
! 602: }
! 603: else
! 604: {
! 605: /* Normal, successful read. But we have an escape character
! 606: and have to process the characters one by one. */
! 607: unsigned int i;
! 608: for (i = 0; i < len; i++)
! 609: {
! 610: unsigned char ch;
! 611: /* Get one character at a time. */
! 612: ch = buf[i];
! 613:
! 614: /* Check if we have a pending escape character. */
! 615: if (escape_pending)
! 616: {
! 617: /* We have previously seen an escape character. */
! 618: /* Clear the flag now. */
! 619: escape_pending = 0;
! 620: /* Process the escaped character. */
! 621: switch (ch)
! 622: {
! 623: case '.':
! 624: /* Terminate the connection. */
! 625: sprintf(buf, "%c.\r\n", escape_char);
! 626: buffer_append(&stderr_buffer, buf, strlen(buf));
! 627: stderr_bytes += strlen(buf);
! 628: quit_pending = 1;
! 629: return;
! 630:
! 631: case 'Z' - 64:
! 632: /* Suspend the program. */
! 633: /* Print a message to that effect to the user. */
! 634: sprintf(buf, "%c^Z\r\n", escape_char);
! 635: buffer_append(&stderr_buffer, buf, strlen(buf));
! 636: stderr_bytes += strlen(buf);
! 637:
! 638: /* Restore terminal modes and suspend. */
! 639: client_suspend_self();
! 640:
! 641: /* We have been continued. */
! 642: continue;
! 643:
! 644: case '&':
! 645: /* Detach the program (continue to serve connections,
! 646: but put in background and no more new
! 647: connections). */
! 648: if (!stdin_eof)
! 649: {
! 650: /* Sending SSH_CMSG_EOF alone does not always
! 651: appear to be enough. So we try to send an
! 652: EOF character first. */
! 653: packet_start(SSH_CMSG_STDIN_DATA);
! 654: packet_put_string("\004", 1);
! 655: packet_send();
! 656: /* Close stdin. */
! 657: stdin_eof = 1;
! 658: if (buffer_len(&stdin_buffer) == 0)
! 659: {
! 660: packet_start(SSH_CMSG_EOF);
! 661: packet_send();
! 662: }
! 663: }
! 664: /* Restore tty modes. */
! 665: leave_raw_mode();
! 666:
! 667: /* Stop listening for new connections. */
! 668: channel_stop_listening();
! 669:
! 670: printf("%c& [backgrounded]\n", escape_char);
! 671:
! 672: /* Fork into background. */
! 673: pid = fork();
! 674: if (pid < 0)
! 675: {
! 676: error("fork: %.100s", strerror(errno));
! 677: continue;
! 678: }
! 679: if (pid != 0)
! 680: { /* This is the parent. */
! 681: /* The parent just exits. */
! 682: exit(0);
! 683: }
! 684:
! 685: /* The child continues serving connections. */
! 686: continue;
! 687:
! 688: case '?':
! 689: sprintf(buf, "%c?\r\n\
! 690: Supported escape sequences:\r\n\
! 691: ~. - terminate connection\r\n\
! 692: ~^Z - suspend ssh\r\n\
! 693: ~# - list forwarded connections\r\n\
! 694: ~& - background ssh (when waiting for connections to terminate)\r\n\
! 695: ~? - this message\r\n\
! 696: ~~ - send the escape character by typing it twice\r\n\
! 697: (Note that escapes are only recognized immediately after newline.)\r\n",
! 698: escape_char);
! 699: buffer_append(&stderr_buffer, buf, strlen(buf));
! 700: continue;
! 701:
! 702: case '#':
! 703: sprintf(buf, "%c#\r\n", escape_char);
! 704: buffer_append(&stderr_buffer, buf, strlen(buf));
! 705: s = channel_open_message();
! 706: buffer_append(&stderr_buffer, s, strlen(s));
! 707: xfree(s);
! 708: continue;
! 709:
! 710: default:
! 711: if (ch != escape_char)
! 712: {
! 713: /* Escape character followed by non-special
! 714: character. Append both to the input
! 715: buffer. */
! 716: buf[0] = escape_char;
! 717: buf[1] = ch;
! 718: buffer_append(&stdin_buffer, buf, 2);
! 719: stdin_bytes += 2;
! 720: continue;
! 721: }
! 722: /* Note that escape character typed twice falls through
! 723: here; the latter gets processed as a normal
! 724: character below. */
! 725: break;
! 726: }
! 727: }
! 728: else
! 729: {
! 730: /* The previous character was not an escape char.
! 731: Check if this is an escape. */
! 732: if (last_was_cr && ch == escape_char)
! 733: {
! 734: /* It is. Set the flag and continue to next
! 735: character. */
! 736: escape_pending = 1;
! 737: continue;
! 738: }
! 739: }
! 740:
! 741: /* Normal character. Record whether it was a newline,
! 742: and append it to the buffer. */
! 743: last_was_cr = (ch == '\r' || ch == '\n');
! 744: buf[0] = ch;
! 745: buffer_append(&stdin_buffer, buf, 1);
! 746: stdin_bytes += 1;
! 747: continue;
! 748: }
! 749: }
! 750: }
! 751: }
! 752:
! 753: void client_process_output(fd_set *writeset)
! 754: {
! 755: int len;
! 756: char buf[100];
! 757:
! 758: /* Write buffered output to stdout. */
! 759: if (FD_ISSET(fileno(stdout), writeset))
! 760: {
! 761: /* Write as much data as possible. */
! 762: len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
! 763: buffer_len(&stdout_buffer));
! 764: if (len <= 0)
! 765: {
! 766: if (errno == EAGAIN)
! 767: len = 0;
! 768: else
! 769: {
! 770: /* An error or EOF was encountered. Put an error message
! 771: to stderr buffer. */
! 772: sprintf(buf, "write stdout: %.50s\r\n", strerror(errno));
! 773: buffer_append(&stderr_buffer, buf, strlen(buf));
! 774: stderr_bytes += strlen(buf);
! 775: quit_pending = 1;
! 776: return;
! 777: }
! 778: }
! 779: /* Consume printed data from the buffer. */
! 780: buffer_consume(&stdout_buffer, len);
! 781: }
! 782:
! 783: /* Write buffered output to stderr. */
! 784: if (FD_ISSET(fileno(stderr), writeset))
! 785: {
! 786: /* Write as much data as possible. */
! 787: len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
! 788: buffer_len(&stderr_buffer));
! 789: if (len <= 0)
! 790: if (errno == EAGAIN)
! 791: len = 0;
! 792: else
! 793: {
! 794: /* EOF or error, but can't even print error message. */
! 795: quit_pending = 1;
! 796: return;
! 797: }
! 798: /* Consume printed characters from the buffer. */
! 799: buffer_consume(&stderr_buffer, len);
! 800: }
! 801: }
! 802:
! 803: /* Implements the interactive session with the server. This is called
! 804: after the user has been authenticated, and a command has been
! 805: started on the remote host. If escape_char != -1, it is the character
! 806: used as an escape character for terminating or suspending the
! 807: session. */
! 808:
! 809: int client_loop(int have_pty, int escape_char_arg)
! 810: {
! 811: double start_time, total_time;
! 812: int len;
! 813: char buf[100];
! 814:
! 815: debug("Entering interactive session.");
! 816:
! 817: start_time = get_current_time();
! 818:
! 819: /* Initialize variables. */
! 820: escape_pending = 0;
! 821: last_was_cr = 1;
! 822: exit_status = -1;
! 823: stdin_eof = 0;
! 824: buffer_high = 64 * 1024;
! 825: connection_in = packet_get_connection_in();
! 826: connection_out = packet_get_connection_out();
! 827: max_fd = connection_in;
! 828: if (connection_out > max_fd)
! 829: max_fd = connection_out;
! 830: stdin_bytes = 0;
! 831: stdout_bytes = 0;
! 832: stderr_bytes = 0;
! 833: quit_pending = 0;
! 834: escape_char = escape_char_arg;
! 835:
! 836: /* Initialize buffers. */
! 837: buffer_init(&stdin_buffer);
! 838: buffer_init(&stdout_buffer);
! 839: buffer_init(&stderr_buffer);
! 840:
! 841: /* Set signal handlers to restore non-blocking mode. */
! 842: signal(SIGINT, signal_handler);
! 843: signal(SIGQUIT, signal_handler);
! 844: signal(SIGTERM, signal_handler);
! 845: signal(SIGPIPE, SIG_IGN);
! 846: #ifdef SIGWINCH
! 847: if (have_pty)
! 848: signal(SIGWINCH, window_change_handler);
! 849: #endif /* SIGWINCH */
! 850:
! 851: /* Enter raw mode if have a pseudo terminal. */
! 852: if (have_pty)
! 853: enter_raw_mode();
! 854:
! 855: /* Check if we should immediately send of on stdin. */
! 856: client_check_initial_eof_on_stdin();
! 857:
! 858: /* Main loop of the client for the interactive session mode. */
! 859: while (!quit_pending)
! 860: {
! 861: fd_set readset, writeset;
! 862:
! 863: /* Precess buffered packets sent by the server. */
! 864: client_process_buffered_input_packets();
! 865:
! 866: /* Make packets of buffered stdin data, and buffer them for sending
! 867: to the server. */
! 868: client_make_packets_from_stdin_data();
! 869:
! 870: /* Make packets from buffered channel data, and buffer them for sending
! 871: to the server. */
! 872: if (packet_not_very_much_data_to_write())
! 873: channel_output_poll();
! 874:
! 875: /* Check if the window size has changed, and buffer a message about
! 876: it to the server if so. */
! 877: client_check_window_change();
! 878:
! 879: if (quit_pending)
! 880: break;
! 881:
! 882: /* Wait until we have something to do (something becomes available
! 883: on one of the descriptors). */
! 884: client_wait_until_can_do_something(&readset, &writeset);
! 885:
! 886: if (quit_pending)
! 887: break;
! 888:
! 889: /* Do channel operations. */
! 890: channel_after_select(&readset, &writeset);
! 891:
! 892: /* Process input from the connection and from stdin. Buffer any data
! 893: that is available. */
! 894: client_process_input(&readset);
! 895:
! 896: /* Process output to stdout and stderr. Output to the connection
! 897: is processed elsewhere (above). */
! 898: client_process_output(&writeset);
! 899:
! 900: /* Send as much buffered packet data as possible to the sender. */
! 901: if (FD_ISSET(connection_out, &writeset))
! 902: packet_write_poll();
! 903: }
! 904:
! 905: /* Terminate the session. */
! 906:
! 907: #ifdef SIGWINCH
! 908: /* Stop watching for window change. */
! 909: if (have_pty)
! 910: signal(SIGWINCH, SIG_DFL);
! 911: #endif /* SIGWINCH */
! 912:
! 913: /* Stop listening for connections. */
! 914: channel_stop_listening();
! 915:
! 916: /* In interactive mode (with pseudo tty) display a message indicating that
! 917: the connection has been closed. */
! 918: if (have_pty && !quiet_flag)
! 919: {
! 920: sprintf(buf, "Connection to %.64s closed.\r\n", host);
! 921: buffer_append(&stderr_buffer, buf, strlen(buf));
! 922: stderr_bytes += strlen(buf);
! 923: }
! 924:
! 925: /* Output any buffered data for stdout. */
! 926: while (buffer_len(&stdout_buffer) > 0)
! 927: {
! 928: len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
! 929: buffer_len(&stdout_buffer));
! 930: if (len <= 0)
! 931: {
! 932: error("Write failed flushing stdout buffer.");
! 933: break;
! 934: }
! 935: buffer_consume(&stdout_buffer, len);
! 936: }
! 937:
! 938: /* Output any buffered data for stderr. */
! 939: while (buffer_len(&stderr_buffer) > 0)
! 940: {
! 941: len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
! 942: buffer_len(&stderr_buffer));
! 943: if (len <= 0)
! 944: {
! 945: error("Write failed flushing stderr buffer.");
! 946: break;
! 947: }
! 948: buffer_consume(&stderr_buffer, len);
! 949: }
! 950:
! 951: /* Leave raw mode. */
! 952: if (have_pty)
! 953: leave_raw_mode();
! 954:
! 955: /* Clear and free any buffers. */
! 956: memset(buf, 0, sizeof(buf));
! 957: buffer_free(&stdin_buffer);
! 958: buffer_free(&stdout_buffer);
! 959: buffer_free(&stderr_buffer);
! 960:
! 961: /* Report bytes transferred, and transfer rates. */
! 962: total_time = get_current_time() - start_time;
! 963: debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
! 964: stdin_bytes, stdout_bytes, stderr_bytes, total_time);
! 965: if (total_time > 0)
! 966: debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
! 967: stdin_bytes / total_time, stdout_bytes / total_time,
! 968: stderr_bytes / total_time);
! 969:
! 970: /* Return the exit status of the program. */
! 971: debug("Exit status %d", exit_status);
! 972: return exit_status;
! 973: }