ttymodes.c revision 76259
1261909Sluigi/* 2261909Sluigi * Author: Tatu Ylonen <ylo@cs.hut.fi> 3261909Sluigi * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4261909Sluigi * All rights reserved 5261909Sluigi * 6261909Sluigi * As far as I am concerned, the code I have written for this software 7261909Sluigi * can be used freely for any purpose. Any derived versions of this 8261909Sluigi * software must be clearly marked as such, and if the derived work is 9261909Sluigi * incompatible with the protocol description in the RFC file, it must be 10261909Sluigi * called by a name other than "ssh" or "Secure Shell". 11261909Sluigi */ 12261909Sluigi 13261909Sluigi/* 14261909Sluigi * SSH2 tty modes support by Kevin Steves. 15261909Sluigi * Copyright (c) 2001 Kevin Steves. All rights reserved. 16261909Sluigi * 17261909Sluigi * Redistribution and use in source and binary forms, with or without 18261909Sluigi * modification, are permitted provided that the following conditions 19261909Sluigi * are met: 20261909Sluigi * 1. Redistributions of source code must retain the above copyright 21261909Sluigi * notice, this list of conditions and the following disclaimer. 22261909Sluigi * 2. Redistributions in binary form must reproduce the above copyright 23261909Sluigi * notice, this list of conditions and the following disclaimer in the 24261909Sluigi * documentation and/or other materials provided with the distribution. 25261909Sluigi * 26261909Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27261909Sluigi * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28261909Sluigi * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29261909Sluigi * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30261909Sluigi * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31261909Sluigi * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32261909Sluigi * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33261909Sluigi * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34261909Sluigi * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35261909Sluigi * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36261909Sluigi */ 37261909Sluigi 38261909Sluigi/* 39261909Sluigi * Encoding and decoding of terminal modes in a portable way. 40261909Sluigi * Much of the format is defined in ttymodes.h; it is included multiple times 41261909Sluigi * into this file with the appropriate macro definitions to generate the 42261909Sluigi * suitable code. 43261909Sluigi */ 44261909Sluigi 45261909Sluigi#include "includes.h" 46261909SluigiRCSID("$OpenBSD: ttymodes.c,v 1.13 2001/04/15 01:35:22 stevesk Exp $"); 47261909Sluigi 48261909Sluigi#include "packet.h" 49261909Sluigi#include "log.h" 50261909Sluigi#include "ssh1.h" 51261909Sluigi#include "compat.h" 52261909Sluigi#include "buffer.h" 53261909Sluigi#include "bufaux.h" 54261909Sluigi 55261909Sluigi#define TTY_OP_END 0 56261909Sluigi/* 57261909Sluigi * uint32 (u_int) follows speed in SSH1 and SSH2 58261909Sluigi */ 59261909Sluigi#define TTY_OP_ISPEED_PROTO1 192 60261909Sluigi#define TTY_OP_OSPEED_PROTO1 193 61261909Sluigi#define TTY_OP_ISPEED_PROTO2 128 62261909Sluigi#define TTY_OP_OSPEED_PROTO2 129 63261909Sluigi 64261909Sluigi/* 65261909Sluigi * Converts POSIX speed_t to a baud rate. The values of the 66261909Sluigi * constants for speed_t are not themselves portable. 67261909Sluigi */ 68261909Sluigistatic int 69261909Sluigispeed_to_baud(speed_t speed) 70261909Sluigi{ 71261909Sluigi switch (speed) { 72261909Sluigi case B0: 73261909Sluigi return 0; 74261909Sluigi case B50: 75261909Sluigi return 50; 76261909Sluigi case B75: 77261909Sluigi return 75; 78261909Sluigi case B110: 79261909Sluigi return 110; 80261909Sluigi case B134: 81261909Sluigi return 134; 82261909Sluigi case B150: 83261909Sluigi return 150; 84261909Sluigi case B200: 85261909Sluigi return 200; 86261909Sluigi case B300: 87261909Sluigi return 300; 88261909Sluigi case B600: 89261909Sluigi return 600; 90261909Sluigi case B1200: 91261909Sluigi return 1200; 92261909Sluigi case B1800: 93261909Sluigi return 1800; 94261909Sluigi case B2400: 95261909Sluigi return 2400; 96261909Sluigi case B4800: 97261909Sluigi return 4800; 98261909Sluigi case B9600: 99261909Sluigi return 9600; 100261909Sluigi 101261909Sluigi#ifdef B19200 102261909Sluigi case B19200: 103261909Sluigi return 19200; 104261909Sluigi#else /* B19200 */ 105261909Sluigi#ifdef EXTA 106261909Sluigi case EXTA: 107261909Sluigi return 19200; 108261909Sluigi#endif /* EXTA */ 109261909Sluigi#endif /* B19200 */ 110261909Sluigi 111261909Sluigi#ifdef B38400 112261909Sluigi case B38400: 113261909Sluigi return 38400; 114261909Sluigi#else /* B38400 */ 115261909Sluigi#ifdef EXTB 116261909Sluigi case EXTB: 117261909Sluigi return 38400; 118261909Sluigi#endif /* EXTB */ 119261909Sluigi#endif /* B38400 */ 120261909Sluigi 121261909Sluigi#ifdef B7200 122261909Sluigi case B7200: 123261909Sluigi return 7200; 124261909Sluigi#endif /* B7200 */ 125261909Sluigi#ifdef B14400 126261909Sluigi case B14400: 127261909Sluigi return 14400; 128261909Sluigi#endif /* B14400 */ 129261909Sluigi#ifdef B28800 130261909Sluigi case B28800: 131261909Sluigi return 28800; 132261909Sluigi#endif /* B28800 */ 133261909Sluigi#ifdef B57600 134261909Sluigi case B57600: 135261909Sluigi return 57600; 136261909Sluigi#endif /* B57600 */ 137261909Sluigi#ifdef B76800 138261909Sluigi case B76800: 139261909Sluigi return 76800; 140261909Sluigi#endif /* B76800 */ 141261909Sluigi#ifdef B115200 142261909Sluigi case B115200: 143261909Sluigi return 115200; 144261909Sluigi#endif /* B115200 */ 145261909Sluigi#ifdef B230400 146261909Sluigi case B230400: 147261909Sluigi return 230400; 148261909Sluigi#endif /* B230400 */ 149261909Sluigi default: 150261909Sluigi return 9600; 151261909Sluigi } 152261909Sluigi} 153261909Sluigi 154261909Sluigi/* 155261909Sluigi * Converts a numeric baud rate to a POSIX speed_t. 156261909Sluigi */ 157261909Sluigistatic speed_t 158261909Sluigibaud_to_speed(int baud) 159261909Sluigi{ 160261909Sluigi switch (baud) { 161261909Sluigi case 0: 162261909Sluigi return B0; 163261909Sluigi case 50: 164261909Sluigi return B50; 165261909Sluigi case 75: 166261909Sluigi return B75; 167261909Sluigi case 110: 168261909Sluigi return B110; 169261909Sluigi case 134: 170261909Sluigi return B134; 171261909Sluigi case 150: 172261909Sluigi return B150; 173261909Sluigi case 200: 174261909Sluigi return B200; 175261909Sluigi case 300: 176261909Sluigi return B300; 177261909Sluigi case 600: 178261909Sluigi return B600; 179261909Sluigi case 1200: 180261909Sluigi return B1200; 181261909Sluigi case 1800: 182261909Sluigi return B1800; 183261909Sluigi case 2400: 184261909Sluigi return B2400; 185261909Sluigi case 4800: 186261909Sluigi return B4800; 187261909Sluigi case 9600: 188261909Sluigi return B9600; 189261909Sluigi 190261909Sluigi#ifdef B19200 191261909Sluigi case 19200: 192261909Sluigi return B19200; 193261909Sluigi#else /* B19200 */ 194261909Sluigi#ifdef EXTA 195261909Sluigi case 19200: 196261909Sluigi return EXTA; 197261909Sluigi#endif /* EXTA */ 198261909Sluigi#endif /* B19200 */ 199261909Sluigi 200261909Sluigi#ifdef B38400 201261909Sluigi case 38400: 202261909Sluigi return B38400; 203261909Sluigi#else /* B38400 */ 204261909Sluigi#ifdef EXTB 205261909Sluigi case 38400: 206261909Sluigi return EXTB; 207261909Sluigi#endif /* EXTB */ 208261909Sluigi#endif /* B38400 */ 209261909Sluigi 210261909Sluigi#ifdef B7200 211261909Sluigi case 7200: 212261909Sluigi return B7200; 213261909Sluigi#endif /* B7200 */ 214261909Sluigi#ifdef B14400 215261909Sluigi case 14400: 216261909Sluigi return B14400; 217261909Sluigi#endif /* B14400 */ 218261909Sluigi#ifdef B28800 219261909Sluigi case 28800: 220261909Sluigi return B28800; 221261909Sluigi#endif /* B28800 */ 222261909Sluigi#ifdef B57600 223261909Sluigi case 57600: 224261909Sluigi return B57600; 225261909Sluigi#endif /* B57600 */ 226261909Sluigi#ifdef B76800 227261909Sluigi case 76800: 228261909Sluigi return B76800; 229261909Sluigi#endif /* B76800 */ 230261909Sluigi#ifdef B115200 231261909Sluigi case 115200: 232261909Sluigi return B115200; 233261909Sluigi#endif /* B115200 */ 234261909Sluigi#ifdef B230400 235261909Sluigi case 230400: 236261909Sluigi return B230400; 237261909Sluigi#endif /* B230400 */ 238261909Sluigi default: 239261909Sluigi return B9600; 240261909Sluigi } 241261909Sluigi} 242261909Sluigi 243261909Sluigi/* 244261909Sluigi * Encodes terminal modes for the terminal referenced by fd 245261909Sluigi * or tiop in a portable manner, and appends the modes to a packet 246261909Sluigi * being constructed. 247261909Sluigi */ 248261909Sluigivoid 249261909Sluigitty_make_modes(int fd, struct termios *tiop) 250261909Sluigi{ 251261909Sluigi struct termios tio; 252261909Sluigi int baud; 253261909Sluigi Buffer buf; 254261909Sluigi int tty_op_ospeed, tty_op_ispeed; 255261909Sluigi void (*put_arg)(Buffer *, u_int); 256261909Sluigi 257261909Sluigi buffer_init(&buf); 258261909Sluigi if (compat20) { 259261909Sluigi tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 260261909Sluigi tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 261261909Sluigi put_arg = buffer_put_int; 262261909Sluigi } else { 263261909Sluigi tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 264261909Sluigi tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 265261909Sluigi put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 266261909Sluigi } 267261909Sluigi 268261909Sluigi if (tiop == NULL) { 269261909Sluigi if (tcgetattr(fd, &tio) == -1) { 270261909Sluigi log("tcgetattr: %.100s", strerror(errno)); 271261909Sluigi goto end; 272261909Sluigi } 273261909Sluigi } else 274261909Sluigi tio = *tiop; 275261909Sluigi 276261909Sluigi /* Store input and output baud rates. */ 277261909Sluigi baud = speed_to_baud(cfgetospeed(&tio)); 278261909Sluigi debug2("tty_make_modes: ospeed %d", baud); 279261909Sluigi buffer_put_char(&buf, tty_op_ospeed); 280261909Sluigi buffer_put_int(&buf, baud); 281261909Sluigi baud = speed_to_baud(cfgetispeed(&tio)); 282261909Sluigi debug2("tty_make_modes: ispeed %d", baud); 283261909Sluigi buffer_put_char(&buf, tty_op_ispeed); 284261909Sluigi buffer_put_int(&buf, baud); 285261909Sluigi 286261909Sluigi /* Store values of mode flags. */ 287261909Sluigi#define TTYCHAR(NAME, OP) \ 288261909Sluigi debug2("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \ 289261909Sluigi buffer_put_char(&buf, OP); \ 290261909Sluigi put_arg(&buf, tio.c_cc[NAME]); 291261909Sluigi 292261909Sluigi#define TTYMODE(NAME, FIELD, OP) \ 293261909Sluigi debug2("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \ 294261909Sluigi buffer_put_char(&buf, OP); \ 295261909Sluigi put_arg(&buf, ((tio.FIELD & NAME) != 0)); 296261909Sluigi 297261909Sluigi#include "ttymodes.h" 298261909Sluigi 299261909Sluigi#undef TTYCHAR 300261909Sluigi#undef TTYMODE 301261909Sluigi 302261909Sluigiend: 303261909Sluigi /* Mark end of mode data. */ 304261909Sluigi buffer_put_char(&buf, TTY_OP_END); 305261909Sluigi if (compat20) 306261909Sluigi packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 307261909Sluigi else 308261909Sluigi packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 309261909Sluigi buffer_free(&buf); 310261909Sluigi return; 311261909Sluigi} 312261909Sluigi 313261909Sluigi/* 314261909Sluigi * Decodes terminal modes for the terminal referenced by fd in a portable 315261909Sluigi * manner from a packet being read. 316261909Sluigi */ 317261909Sluigivoid 318261909Sluigitty_parse_modes(int fd, int *n_bytes_ptr) 319261909Sluigi{ 320261909Sluigi struct termios tio; 321261909Sluigi int opcode, baud; 322261909Sluigi int n_bytes = 0; 323261909Sluigi int failure = 0; 324261909Sluigi u_int (*get_arg)(void); 325261909Sluigi int arg, arg_size; 326261909Sluigi 327261909Sluigi if (compat20) { 328261909Sluigi *n_bytes_ptr = packet_get_int(); 329261909Sluigi debug2("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); 330261909Sluigi if (*n_bytes_ptr == 0) 331261909Sluigi return; 332261909Sluigi get_arg = packet_get_int; 333261909Sluigi arg_size = 4; 334261909Sluigi } else { 335261909Sluigi get_arg = packet_get_char; 336261909Sluigi arg_size = 1; 337261909Sluigi } 338261909Sluigi 339261909Sluigi /* 340261909Sluigi * Get old attributes for the terminal. We will modify these 341261909Sluigi * flags. I am hoping that if there are any machine-specific 342261909Sluigi * modes, they will initially have reasonable values. 343261909Sluigi */ 344261909Sluigi if (tcgetattr(fd, &tio) == -1) { 345261909Sluigi log("tcgetattr: %.100s", strerror(errno)); 346261909Sluigi failure = -1; 347261909Sluigi } 348261909Sluigi 349261909Sluigi for (;;) { 350261909Sluigi n_bytes += 1; 351261909Sluigi opcode = packet_get_char(); 352261909Sluigi switch (opcode) { 353261909Sluigi case TTY_OP_END: 354261909Sluigi goto set; 355261909Sluigi 356261909Sluigi /* XXX: future conflict possible */ 357261909Sluigi case TTY_OP_ISPEED_PROTO1: 358261909Sluigi case TTY_OP_ISPEED_PROTO2: 359261909Sluigi n_bytes += 4; 360261909Sluigi baud = packet_get_int(); 361261909Sluigi debug2("tty_parse_modes: ispeed %d", baud); 362261909Sluigi if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) == -1) 363261909Sluigi error("cfsetispeed failed for %d", baud); 364261909Sluigi break; 365261909Sluigi 366261909Sluigi /* XXX: future conflict possible */ 367261909Sluigi case TTY_OP_OSPEED_PROTO1: 368261909Sluigi case TTY_OP_OSPEED_PROTO2: 369261909Sluigi n_bytes += 4; 370261909Sluigi baud = packet_get_int(); 371261909Sluigi debug2("tty_parse_modes: ospeed %d", baud); 372261909Sluigi if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) == -1) 373261909Sluigi error("cfsetospeed failed for %d", baud); 374261909Sluigi break; 375261909Sluigi 376261909Sluigi#define TTYCHAR(NAME, OP) \ 377261909Sluigi case OP: \ 378261909Sluigi n_bytes += arg_size; \ 379261909Sluigi tio.c_cc[NAME] = get_arg(); \ 380261909Sluigi debug2("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ 381261909Sluigi break; 382261909Sluigi#define TTYMODE(NAME, FIELD, OP) \ 383261909Sluigi case OP: \ 384261909Sluigi n_bytes += arg_size; \ 385261909Sluigi if ((arg = get_arg())) \ 386261909Sluigi tio.FIELD |= NAME; \ 387261909Sluigi else \ 388261909Sluigi tio.FIELD &= ~NAME; \ 389261909Sluigi debug2("tty_parse_modes: %d %d", OP, arg); \ 390261909Sluigi break; 391261909Sluigi 392261909Sluigi#include "ttymodes.h" 393261909Sluigi 394261909Sluigi#undef TTYCHAR 395261909Sluigi#undef TTYMODE 396261909Sluigi 397261909Sluigi default: 398261909Sluigi debug("Ignoring unsupported tty mode opcode %d (0x%x)", 399261909Sluigi opcode, opcode); 400261909Sluigi if (!compat20) { 401261909Sluigi /* 402261909Sluigi * SSH1: 403261909Sluigi * Opcodes 1 to 127 are defined to have 404261909Sluigi * a one-byte argument. 405261909Sluigi * Opcodes 128 to 159 are defined to have 406261909Sluigi * an integer argument. 407261909Sluigi */ 408261909Sluigi if (opcode > 0 && opcode < 128) { 409261909Sluigi n_bytes += 1; 410261909Sluigi (void) packet_get_char(); 411261909Sluigi break; 412261909Sluigi } else if (opcode >= 128 && opcode < 160) { 413261909Sluigi n_bytes += 4; 414261909Sluigi (void) packet_get_int(); 415261909Sluigi break; 416261909Sluigi } else { 417261909Sluigi /* 418261909Sluigi * It is a truly undefined opcode (160 to 255). 419261909Sluigi * We have no idea about its arguments. So we 420261909Sluigi * must stop parsing. Note that some data may be 421261909Sluigi * left in the packet; hopefully there is nothing 422261909Sluigi * more coming after the mode data. 423261909Sluigi */ 424261909Sluigi log("parse_tty_modes: unknown opcode %d", opcode); 425261909Sluigi packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); 426261909Sluigi goto set; 427261909Sluigi } 428261909Sluigi } else { 429261909Sluigi /* 430261909Sluigi * SSH2: 431261909Sluigi * Opcodes 1 to 159 are defined to have 432261909Sluigi * a uint32 argument. 433261909Sluigi * Opcodes 160 to 255 are undefined and 434261909Sluigi * cause parsing to stop. 435261909Sluigi */ 436261909Sluigi if (opcode > 0 && opcode < 160) { 437261909Sluigi n_bytes += 4; 438261909Sluigi (void) packet_get_int(); 439261909Sluigi break; 440261909Sluigi } else { 441261909Sluigi log("parse_tty_modes: unknown opcode %d", opcode); 442261909Sluigi goto set; 443261909Sluigi } 444261909Sluigi } 445261909Sluigi } 446261909Sluigi } 447261909Sluigi 448261909Sluigiset: 449261909Sluigi if (*n_bytes_ptr != n_bytes) { 450261909Sluigi *n_bytes_ptr = n_bytes; 451261909Sluigi log("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 452261909Sluigi *n_bytes_ptr, n_bytes); 453261909Sluigi return; /* Don't process bytes passed */ 454261909Sluigi } 455261909Sluigi if (failure == -1) 456261909Sluigi return; /* Packet parsed ok but tcgetattr() failed */ 457261909Sluigi 458261909Sluigi /* Set the new modes for the terminal. */ 459261909Sluigi if (tcsetattr(fd, TCSANOW, &tio) == -1) 460261909Sluigi log("Setting tty modes failed: %.100s", strerror(errno)); 461261909Sluigi return; 462261909Sluigi} 463261909Sluigi