/* ttymodes.c Author: Tatu Ylonen Copyright (c) 1995 Tatu Ylonen , Espoo, Finland All rights reserved Created: Tue Mar 21 15:59:15 1995 ylo Encoding and decoding of terminal modes in a portable way. Much of the format is defined in ttymodes.h; it is included multiple times into this file with the appropriate macro definitions to generate the suitable code. */ #include "includes.h" RCSID("$Id: ttymodes.c,v 1.1 1999/09/26 20:53:38 deraadt Exp $"); #include "packet.h" #include "ssh.h" #define TTY_OP_END 0 #define TTY_OP_ISPEED 192 /* int follows */ #define TTY_OP_OSPEED 193 /* int follows */ /* Speed extraction & setting macros for sgtty. */ #ifdef USING_SGTTY #define cfgetospeed(tio) ((tio)->sg_ospeed) #define cfgetispeed(tio) ((tio)->sg_ispeed) #define cfsetospeed(tio, spd) ((tio)->sg_ospeed = (spd), 0) #define cfsetispeed(tio, spd) ((tio)->sg_ispeed = (spd), 0) #ifndef SPEED_T_IN_STDTYPES_H typedef char speed_t; #endif #endif /* Converts POSIX speed_t to a baud rate. The values of the constants for speed_t are not themselves portable. */ static int speed_to_baud(speed_t speed) { switch (speed) { case B0: return 0; case B50: return 50; case B75: return 75; case B110: return 110; case B134: return 134; case B150: return 150; case B200: return 200; case B300: return 300; case B600: return 600; case B1200: return 1200; case B1800: return 1800; case B2400: return 2400; case B4800: return 4800; case B9600: return 9600; #ifdef B19200 case B19200: return 19200; #else /* B19200 */ #ifdef EXTA case EXTA: return 19200; #endif /* EXTA */ #endif /* B19200 */ #ifdef B38400 case B38400: return 38400; #else /* B38400 */ #ifdef EXTB case EXTB: return 38400; #endif /* EXTB */ #endif /* B38400 */ #ifdef B7200 case B7200: return 7200; #endif /* B7200 */ #ifdef B14400 case B14400: return 14400; #endif /* B14400 */ #ifdef B28800 case B28800: return 28800; #endif /* B28800 */ #ifdef B57600 case B57600: return 57600; #endif /* B57600 */ #ifdef B76800 case B76800: return 76800; #endif /* B76800 */ #ifdef B115200 case B115200: return 115200; #endif /* B115200 */ #ifdef B230400 case B230400: return 230400; #endif /* B230400 */ default: return 9600; } } /* Converts a numeric baud rate to a POSIX speed_t. */ static speed_t baud_to_speed(int baud) { switch (baud) { case 0: return B0; case 50: return B50; case 75: return B75; case 110: return B110; case 134: return B134; case 150: return B150; case 200: return B200; case 300: return B300; case 600: return B600; case 1200: return B1200; case 1800: return B1800; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; #ifdef B19200 case 19200: return B19200; #else /* B19200 */ #ifdef EXTA case 19200: return EXTA; #endif /* EXTA */ #endif /* B19200 */ #ifdef B38400 case 38400: return B38400; #else /* B38400 */ #ifdef EXTB case 38400: return EXTB; #endif /* EXTB */ #endif /* B38400 */ #ifdef B7200 case 7200: return B7200; #endif /* B7200 */ #ifdef B14400 case 14400: return B14400; #endif /* B14400 */ #ifdef B28800 case 28800: return B28800; #endif /* B28800 */ #ifdef B57600 case 57600: return B57600; #endif /* B57600 */ #ifdef B76800 case 76800: return B76800; #endif /* B76800 */ #ifdef B115200 case 115200: return B115200; #endif /* B115200 */ #ifdef B230400 case 230400: return B230400; #endif /* B230400 */ default: return B9600; } } /* Encodes terminal modes for the terminal referenced by fd in a portable manner, and appends the modes to a packet being constructed. */ void tty_make_modes(int fd) { #ifdef USING_TERMIOS struct termios tio; #endif #ifdef USING_SGTTY struct sgttyb tio; struct tchars tiotc; struct ltchars tioltc; int tiolm; #ifdef TIOCGSTAT struct tstatus tiots; #endif /* TIOCGSTAT */ #endif /* USING_SGTTY */ int baud; /* Get the modes. */ #ifdef USING_TERMIOS if (tcgetattr(fd, &tio) < 0) { packet_put_char(TTY_OP_END); log("tcgetattr: %.100s", strerror(errno)); return; } #endif /* USING_TERMIOS */ #ifdef USING_SGTTY if (ioctl(fd, TIOCGETP, &tio) < 0) { packet_put_char(TTY_OP_END); log("ioctl(fd, TIOCGETP, ...): %.100s", strerror(errno)); return; } if (ioctl(fd, TIOCGETC, &tiotc) < 0) { packet_put_char(TTY_OP_END); log("ioctl(fd, TIOCGETC, ...): %.100s", strerror(errno)); return; } if (ioctl(fd, TIOCLGET, &tiolm) < 0) { packet_put_char(TTY_OP_END); log("ioctl(fd, TIOCLGET, ...): %.100s", strerror(errno)); return; } if (ioctl(fd, TIOCGLTC, &tioltc) < 0) { packet_put_char(TTY_OP_END); log("ioctl(fd, TIOCGLTC, ...): %.100s", strerror(errno)); return; } #ifdef TIOCGSTAT if (ioctl(fd, TIOCGSTAT, &tiots) < 0) { packet_put_char(TTY_OP_END); log("ioctl(fd, TIOCGSTAT, ...): %.100s", strerror(errno)); return; } #endif /* TIOCGSTAT */ #endif /* USING_SGTTY */ /* Store input and output baud rates. */ baud = speed_to_baud(cfgetospeed(&tio)); packet_put_char(TTY_OP_OSPEED); packet_put_int(baud); baud = speed_to_baud(cfgetispeed(&tio)); packet_put_char(TTY_OP_ISPEED); packet_put_int(baud); /* Store values of mode flags. */ #ifdef USING_TERMIOS #define TTYCHAR(NAME, OP) \ packet_put_char(OP); packet_put_char(tio.c_cc[NAME]); #define TTYMODE(NAME, FIELD, OP) \ packet_put_char(OP); packet_put_char((tio.FIELD & NAME) != 0); #define SGTTYCHAR(NAME, OP) #define SGTTYMODE(NAME, FIELD, OP) #define SGTTYMODEN(NAME, FIELD, OP) #endif /* USING_TERMIOS */ #ifdef USING_SGTTY #define TTYCHAR(NAME, OP) #define TTYMODE(NAME, FIELD, OP) #define SGTTYCHAR(NAME, OP) \ packet_put_char(OP); packet_put_char(NAME); #define SGTTYMODE(NAME, FIELD, OP) \ packet_put_char(OP); packet_put_char((FIELD & NAME) != 0); #define SGTTYMODEN(NAME, FIELD, OP) \ packet_put_char(OP); packet_put_char((FIELD & NAME) == 0); #endif /* USING_SGTTY */ #include "ttymodes.h" #undef TTYCHAR #undef TTYMODE #undef SGTTYCHAR #undef SGTTYMODE #undef SGTTYMODEN /* Mark end of mode data. */ packet_put_char(TTY_OP_END); } /* Decodes terminal modes for the terminal referenced by fd in a portable manner from a packet being read. */ void tty_parse_modes(int fd, int *n_bytes_ptr) { #ifdef USING_TERMIOS struct termios tio; #endif /* USING_TERMIOS */ #ifdef USING_SGTTY struct sgttyb tio; struct tchars tiotc; struct ltchars tioltc; int tiolm; #ifdef TIOCGSTAT struct tstatus tiots; #endif /* TIOCGSTAT */ #endif int opcode, baud; int n_bytes = 0; int failure = 0; /* Get old attributes for the terminal. We will modify these flags. I am hoping that if there are any machine-specific modes, they will initially have reasonable values. */ #ifdef USING_TERMIOS if (tcgetattr(fd, &tio) < 0) failure = -1; #endif /* USING_TERMIOS */ #ifdef USING_SGTTY if (ioctl(fd, TIOCGETP, &tio) < 0) failure = -1; if (ioctl(fd, TIOCGETC, &tiotc) < 0) failure = -1; if (ioctl(fd, TIOCLGET, &tiolm) < 0) failure = -1; if (ioctl(fd, TIOCGLTC, &tioltc) < 0) failure = -1; #ifdef TIOCGSTAT if (ioctl(fd, TIOCGSTAT, &tiots) < 0) failure = -1; #endif /* TIOCGSTAT */ #endif /* USING_SGTTY */ for (;;) { n_bytes += 1; opcode = packet_get_char(); switch (opcode) { case TTY_OP_END: goto set; case TTY_OP_ISPEED: n_bytes += 4; baud = packet_get_int(); if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) error("cfsetispeed failed for %d", baud); break; case TTY_OP_OSPEED: n_bytes += 4; baud = packet_get_int(); if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) error("cfsetospeed failed for %d", baud); break; #ifdef USING_TERMIOS #define TTYCHAR(NAME, OP) \ case OP: \ n_bytes += 1; \ tio.c_cc[NAME] = packet_get_char(); \ break; #define TTYMODE(NAME, FIELD, OP) \ case OP: \ n_bytes += 1; \ if (packet_get_char()) \ tio.FIELD |= NAME; \ else \ tio.FIELD &= ~NAME; \ break; #define SGTTYCHAR(NAME, OP) #define SGTTYMODE(NAME, FIELD, OP) #define SGTTYMODEN(NAME, FIELD, OP) #endif /* USING_TERMIOS */ #ifdef USING_SGTTY #define TTYCHAR(NAME, OP) #define TTYMODE(NAME, FIELD, OP) #define SGTTYCHAR(NAME, OP) \ case OP: \ n_bytes += 1; \ NAME = packet_get_char(); \ break; #define SGTTYMODE(NAME, FIELD, OP) \ case OP: \ n_bytes += 1; \ if (packet_get_char()) \ FIELD |= NAME; \ else \ FIELD &= ~NAME; \ break; #define SGTTYMODEN(NAME, FIELD, OP) \ case OP: \ n_bytes += 1; \ if (packet_get_char()) \ FIELD &= ~NAME; \ else \ FIELD |= NAME; \ break; #endif /* USING_SGTTY */ #include "ttymodes.h" #undef TTYCHAR #undef TTYMODE #undef SGTTYCHAR #undef SGTTYMODE #undef SGTTYMODEN default: debug("Ignoring unsupported tty mode opcode %d (0x%x)", opcode, opcode); /* Opcodes 0 to 127 are defined to have a one-byte argument. */ if (opcode >= 0 && opcode < 128) { n_bytes += 1; (void)packet_get_char(); break; } else { /* Opcodes 128 to 159 are defined to have an integer argument. */ if (opcode >= 128 && opcode < 160) { n_bytes += 4; (void)packet_get_int(); break; } } /* It is a truly undefined opcode (160 to 255). We have no idea about its arguments. So we must stop parsing. Note that some data may be left in the packet; hopefully there is nothing more coming after the mode data. */ log("parse_tty_modes: unknown opcode %d", opcode); packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); goto set; } } set: if (*n_bytes_ptr != n_bytes) { *n_bytes_ptr = n_bytes; return; /* Don't process bytes passed */ } if (failure == -1) return; /* Packet parsed ok but tty stuff failed */ /* Set the new modes for the terminal. */ #ifdef USING_TERMIOS if (tcsetattr(fd, TCSANOW, &tio) < 0) log("Setting tty modes failed: %.100s", strerror(errno)); #endif /* USING_TERMIOS */ #ifdef USING_SGTTY if (ioctl(fd, TIOCSETP, &tio) < 0 || ioctl(fd, TIOCSETC, &tiotc) < 0 || ioctl(fd, TIOCLSET, &tiolm) < 0 || ioctl(fd, TIOCSLTC, &tioltc) < 0 #ifdef TIOCSSTAT || ioctl(fd, TIOCSSTAT, &tiots) < 0 #endif /* TIOCSSTAT */ ) log("Setting tty modes failed: %.100s", strerror(errno)); #endif /* USING_SGTTY */ return; }