diff options
Diffstat (limited to 'usr.bin/ssh/ttymodes.c')
-rw-r--r-- | usr.bin/ssh/ttymodes.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/usr.bin/ssh/ttymodes.c b/usr.bin/ssh/ttymodes.c new file mode 100644 index 00000000000..c03e968937c --- /dev/null +++ b/usr.bin/ssh/ttymodes.c @@ -0,0 +1,499 @@ +/* + +ttymodes.c + +Author: Tatu Ylonen <ylo@cs.hut.fi> + +Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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; +} |