ttymodes.c revision 162852
154095Ssemenu/* $OpenBSD: ttymodes.c,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */ 247060Ssemenu/* 343552Ssemenu * Author: Tatu Ylonen <ylo@cs.hut.fi> 443552Ssemenu * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 543552Ssemenu * All rights reserved 643552Ssemenu * 743552Ssemenu * As far as I am concerned, the code I have written for this software 843552Ssemenu * can be used freely for any purpose. Any derived versions of this 943552Ssemenu * software must be clearly marked as such, and if the derived work is 1043552Ssemenu * incompatible with the protocol description in the RFC file, it must be 1143552Ssemenu * called by a name other than "ssh" or "Secure Shell". 1243552Ssemenu */ 1343552Ssemenu 1443552Ssemenu/* 1543552Ssemenu * SSH2 tty modes support by Kevin Steves. 1643552Ssemenu * Copyright (c) 2001 Kevin Steves. All rights reserved. 1743552Ssemenu * 1843552Ssemenu * Redistribution and use in source and binary forms, with or without 1943552Ssemenu * modification, are permitted provided that the following conditions 2043552Ssemenu * are met: 2143552Ssemenu * 1. Redistributions of source code must retain the above copyright 2243552Ssemenu * notice, this list of conditions and the following disclaimer. 2343552Ssemenu * 2. Redistributions in binary form must reproduce the above copyright 2443552Ssemenu * notice, this list of conditions and the following disclaimer in the 2543552Ssemenu * documentation and/or other materials provided with the distribution. 2643552Ssemenu * 2743552Ssemenu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2850477Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2943552Ssemenu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3043552Ssemenu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3143552Ssemenu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3243552Ssemenu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3343552Ssemenu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3443552Ssemenu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3543552Ssemenu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3643552Ssemenu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3743552Ssemenu */ 3843552Ssemenu 3943552Ssemenu/* 4043552Ssemenu * Encoding and decoding of terminal modes in a portable way. 4143552Ssemenu * Much of the format is defined in ttymodes.h; it is included multiple times 4243552Ssemenu * into this file with the appropriate macro definitions to generate the 4343552Ssemenu * suitable code. 4443552Ssemenu */ 4543552Ssemenu 4643552Ssemenu#include "includes.h" 4743552Ssemenu 4843552Ssemenu#include <sys/types.h> 4943552Ssemenu 5043552Ssemenu#include <errno.h> 5143552Ssemenu#include <string.h> 5243552Ssemenu#include <termios.h> 5343552Ssemenu#include <stdarg.h> 5443552Ssemenu 5543552Ssemenu#include "packet.h" 5643552Ssemenu#include "log.h" 5743552Ssemenu#include "ssh1.h" 5843552Ssemenu#include "compat.h" 5943552Ssemenu#include "buffer.h" 6043552Ssemenu 6143552Ssemenu#define TTY_OP_END 0 6243552Ssemenu/* 6343552Ssemenu * uint32 (u_int) follows speed in SSH1 and SSH2 6443552Ssemenu */ 6543552Ssemenu#define TTY_OP_ISPEED_PROTO1 192 6643552Ssemenu#define TTY_OP_OSPEED_PROTO1 193 6743552Ssemenu#define TTY_OP_ISPEED_PROTO2 128 6843552Ssemenu#define TTY_OP_OSPEED_PROTO2 129 6943552Ssemenu 7043552Ssemenu/* 7143552Ssemenu * Converts POSIX speed_t to a baud rate. The values of the 7243552Ssemenu * constants for speed_t are not themselves portable. 7343552Ssemenu */ 7443552Ssemenustatic int 7543552Ssemenuspeed_to_baud(speed_t speed) 7643552Ssemenu{ 7743552Ssemenu switch (speed) { 7843552Ssemenu case B0: 7943552Ssemenu return 0; 8043552Ssemenu case B50: 8143552Ssemenu return 50; 8243552Ssemenu case B75: 8343552Ssemenu return 75; 8443552Ssemenu case B110: 8543552Ssemenu return 110; 8643552Ssemenu case B134: 8743552Ssemenu return 134; 8843552Ssemenu case B150: 8943552Ssemenu return 150; 9043552Ssemenu case B200: 9143552Ssemenu return 200; 9243552Ssemenu case B300: 9343552Ssemenu return 300; 9443552Ssemenu case B600: 9543552Ssemenu return 600; 9643552Ssemenu case B1200: 9743552Ssemenu return 1200; 9843552Ssemenu case B1800: 9943552Ssemenu return 1800; 10043552Ssemenu case B2400: 10143552Ssemenu return 2400; 10243552Ssemenu case B4800: 10343552Ssemenu return 4800; 10443552Ssemenu case B9600: 10543552Ssemenu return 9600; 10643552Ssemenu 10743552Ssemenu#ifdef B19200 10843552Ssemenu case B19200: 10943552Ssemenu return 19200; 11043552Ssemenu#else /* B19200 */ 11143552Ssemenu#ifdef EXTA 11243552Ssemenu case EXTA: 11343552Ssemenu return 19200; 11443552Ssemenu#endif /* EXTA */ 11543552Ssemenu#endif /* B19200 */ 11643552Ssemenu 11743552Ssemenu#ifdef B38400 11843552Ssemenu case B38400: 11943552Ssemenu return 38400; 12043552Ssemenu#else /* B38400 */ 12143552Ssemenu#ifdef EXTB 12243552Ssemenu case EXTB: 12343552Ssemenu return 38400; 12443552Ssemenu#endif /* EXTB */ 12543552Ssemenu#endif /* B38400 */ 12643552Ssemenu 12743552Ssemenu#ifdef B7200 12843552Ssemenu case B7200: 12943552Ssemenu return 7200; 13043552Ssemenu#endif /* B7200 */ 13143552Ssemenu#ifdef B14400 13243552Ssemenu case B14400: 13343552Ssemenu return 14400; 13443552Ssemenu#endif /* B14400 */ 13543552Ssemenu#ifdef B28800 13643552Ssemenu case B28800: 13743552Ssemenu return 28800; 13843552Ssemenu#endif /* B28800 */ 13943552Ssemenu#ifdef B57600 14043552Ssemenu case B57600: 14143552Ssemenu return 57600; 14243552Ssemenu#endif /* B57600 */ 14343552Ssemenu#ifdef B76800 14443552Ssemenu case B76800: 14543552Ssemenu return 76800; 14643552Ssemenu#endif /* B76800 */ 14743552Ssemenu#ifdef B115200 14843552Ssemenu case B115200: 14943552Ssemenu return 115200; 15043552Ssemenu#endif /* B115200 */ 15143552Ssemenu#ifdef B230400 15243552Ssemenu case B230400: 15343552Ssemenu return 230400; 15443552Ssemenu#endif /* B230400 */ 15543552Ssemenu default: 15643552Ssemenu return 9600; 15743552Ssemenu } 15843552Ssemenu} 15943552Ssemenu 16043552Ssemenu/* 16143552Ssemenu * Converts a numeric baud rate to a POSIX speed_t. 16243552Ssemenu */ 16343552Ssemenustatic speed_t 16443552Ssemenubaud_to_speed(int baud) 16543552Ssemenu{ 16643552Ssemenu switch (baud) { 16743552Ssemenu case 0: 16843552Ssemenu return B0; 16943552Ssemenu case 50: 17043552Ssemenu return B50; 17143552Ssemenu case 75: 17243552Ssemenu return B75; 17343552Ssemenu case 110: 17443552Ssemenu return B110; 17543552Ssemenu case 134: 17643552Ssemenu return B134; 17743552Ssemenu case 150: 17843552Ssemenu return B150; 17943552Ssemenu case 200: 18043552Ssemenu return B200; 18143552Ssemenu case 300: 18243552Ssemenu return B300; 18343552Ssemenu case 600: 18443552Ssemenu return B600; 18543552Ssemenu case 1200: 18644142Ssemenu return B1200; 18743552Ssemenu case 1800: 18843552Ssemenu return B1800; 18943552Ssemenu case 2400: 19043552Ssemenu return B2400; 19143552Ssemenu case 4800: 19243552Ssemenu return B4800; 19343552Ssemenu case 9600: 19443552Ssemenu return B9600; 19543552Ssemenu 19643552Ssemenu#ifdef B19200 19743552Ssemenu case 19200: 19843552Ssemenu return B19200; 19943552Ssemenu#else /* B19200 */ 20044142Ssemenu#ifdef EXTA 20143552Ssemenu case 19200: 20243552Ssemenu return EXTA; 20343552Ssemenu#endif /* EXTA */ 20444142Ssemenu#endif /* B19200 */ 20543552Ssemenu 20643552Ssemenu#ifdef B38400 20743552Ssemenu case 38400: 20843552Ssemenu return B38400; 20943552Ssemenu#else /* B38400 */ 21043552Ssemenu#ifdef EXTB 21143552Ssemenu case 38400: 21243552Ssemenu return EXTB; 21343552Ssemenu#endif /* EXTB */ 21443552Ssemenu#endif /* B38400 */ 21543552Ssemenu 21643552Ssemenu#ifdef B7200 21743552Ssemenu case 7200: 21845879Ssemenu return B7200; 21945879Ssemenu#endif /* B7200 */ 22043552Ssemenu#ifdef B14400 22143552Ssemenu case 14400: 22243552Ssemenu return B14400; 22343552Ssemenu#endif /* B14400 */ 22443552Ssemenu#ifdef B28800 22543552Ssemenu case 28800: 22643552Ssemenu return B28800; 22743552Ssemenu#endif /* B28800 */ 22843552Ssemenu#ifdef B57600 22943552Ssemenu case 57600: 23043552Ssemenu return B57600; 23143552Ssemenu#endif /* B57600 */ 23243552Ssemenu#ifdef B76800 23343552Ssemenu case 76800: 23443552Ssemenu return B76800; 23543552Ssemenu#endif /* B76800 */ 23643552Ssemenu#ifdef B115200 23743552Ssemenu case 115200: 23843552Ssemenu return B115200; 23943552Ssemenu#endif /* B115200 */ 24043552Ssemenu#ifdef B230400 24143552Ssemenu case 230400: 24254095Ssemenu return B230400; 24343552Ssemenu#endif /* B230400 */ 244137172Sphk default: 245137172Sphk return B9600; 24654095Ssemenu } 24743552Ssemenu} 24843552Ssemenu 24943552Ssemenu/* 25043552Ssemenu * Encode a special character into SSH line format. 25143552Ssemenu */ 252138488Sphkstatic u_int 25345879Ssemenuspecial_char_encode(cc_t c) 25443552Ssemenu{ 25543552Ssemenu#ifdef _POSIX_VDISABLE 25683229Ssemenu if (c == _POSIX_VDISABLE) 25783229Ssemenu return 255; 258120492Sfjoe#endif /* _POSIX_VDISABLE */ 259120492Sfjoe return c; 26043552Ssemenu} 26143552Ssemenu 26243552Ssemenu/* 26343552Ssemenu * Decode a special character from SSH line format. 26443552Ssemenu */ 26543552Ssemenustatic cc_t 26643552Ssemenuspecial_char_decode(u_int c) 26743552Ssemenu{ 26843552Ssemenu#ifdef _POSIX_VDISABLE 26943552Ssemenu if (c == 255) 27043552Ssemenu return _POSIX_VDISABLE; 27143552Ssemenu#endif /* _POSIX_VDISABLE */ 27243552Ssemenu return c; 27343552Ssemenu} 27444142Ssemenu 27544142Ssemenu/* 27644142Ssemenu * Encodes terminal modes for the terminal referenced by fd 27744142Ssemenu * or tiop in a portable manner, and appends the modes to a packet 27843552Ssemenu * being constructed. 27943552Ssemenu */ 28043552Ssemenuvoid 28143552Ssemenutty_make_modes(int fd, struct termios *tiop) 28243552Ssemenu{ 28343552Ssemenu struct termios tio; 28443552Ssemenu int baud; 28543552Ssemenu Buffer buf; 28643552Ssemenu int tty_op_ospeed, tty_op_ispeed; 28755989Sbde void (*put_arg)(Buffer *, u_int); 28844142Ssemenu 28944142Ssemenu buffer_init(&buf); 29043552Ssemenu if (compat20) { 29144142Ssemenu tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 29243552Ssemenu tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 29343552Ssemenu put_arg = buffer_put_int; 29443552Ssemenu } else { 29543552Ssemenu tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 29643552Ssemenu tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 29743552Ssemenu put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 29843552Ssemenu } 299194576Srdivacky 30043552Ssemenu if (tiop == NULL) { 30143552Ssemenu if (tcgetattr(fd, &tio) == -1) { 302194576Srdivacky logit("tcgetattr: %.100s", strerror(errno)); 303194576Srdivacky goto end; 30443552Ssemenu } 30543552Ssemenu } else 306138290Sphk tio = *tiop; 307 308 /* Store input and output baud rates. */ 309 baud = speed_to_baud(cfgetospeed(&tio)); 310 debug3("tty_make_modes: ospeed %d", baud); 311 buffer_put_char(&buf, tty_op_ospeed); 312 buffer_put_int(&buf, baud); 313 baud = speed_to_baud(cfgetispeed(&tio)); 314 debug3("tty_make_modes: ispeed %d", baud); 315 buffer_put_char(&buf, tty_op_ispeed); 316 buffer_put_int(&buf, baud); 317 318 /* Store values of mode flags. */ 319#define TTYCHAR(NAME, OP) \ 320 debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \ 321 buffer_put_char(&buf, OP); \ 322 put_arg(&buf, special_char_encode(tio.c_cc[NAME])); 323 324#define TTYMODE(NAME, FIELD, OP) \ 325 debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \ 326 buffer_put_char(&buf, OP); \ 327 put_arg(&buf, ((tio.FIELD & NAME) != 0)); 328 329#include "ttymodes.h" 330 331#undef TTYCHAR 332#undef TTYMODE 333 334end: 335 /* Mark end of mode data. */ 336 buffer_put_char(&buf, TTY_OP_END); 337 if (compat20) 338 packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 339 else 340 packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 341 buffer_free(&buf); 342} 343 344/* 345 * Decodes terminal modes for the terminal referenced by fd in a portable 346 * manner from a packet being read. 347 */ 348void 349tty_parse_modes(int fd, int *n_bytes_ptr) 350{ 351 struct termios tio; 352 int opcode, baud; 353 int n_bytes = 0; 354 int failure = 0; 355 u_int (*get_arg)(void); 356 int arg, arg_size; 357 358 if (compat20) { 359 *n_bytes_ptr = packet_get_int(); 360 debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); 361 if (*n_bytes_ptr == 0) 362 return; 363 get_arg = packet_get_int; 364 arg_size = 4; 365 } else { 366 get_arg = packet_get_char; 367 arg_size = 1; 368 } 369 370 /* 371 * Get old attributes for the terminal. We will modify these 372 * flags. I am hoping that if there are any machine-specific 373 * modes, they will initially have reasonable values. 374 */ 375 if (tcgetattr(fd, &tio) == -1) { 376 logit("tcgetattr: %.100s", strerror(errno)); 377 failure = -1; 378 } 379 380 for (;;) { 381 n_bytes += 1; 382 opcode = packet_get_char(); 383 switch (opcode) { 384 case TTY_OP_END: 385 goto set; 386 387 /* XXX: future conflict possible */ 388 case TTY_OP_ISPEED_PROTO1: 389 case TTY_OP_ISPEED_PROTO2: 390 n_bytes += 4; 391 baud = packet_get_int(); 392 debug3("tty_parse_modes: ispeed %d", baud); 393 if (failure != -1 && 394 cfsetispeed(&tio, baud_to_speed(baud)) == -1) 395 error("cfsetispeed failed for %d", baud); 396 break; 397 398 /* XXX: future conflict possible */ 399 case TTY_OP_OSPEED_PROTO1: 400 case TTY_OP_OSPEED_PROTO2: 401 n_bytes += 4; 402 baud = packet_get_int(); 403 debug3("tty_parse_modes: ospeed %d", baud); 404 if (failure != -1 && 405 cfsetospeed(&tio, baud_to_speed(baud)) == -1) 406 error("cfsetospeed failed for %d", baud); 407 break; 408 409#define TTYCHAR(NAME, OP) \ 410 case OP: \ 411 n_bytes += arg_size; \ 412 tio.c_cc[NAME] = special_char_decode(get_arg()); \ 413 debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ 414 break; 415#define TTYMODE(NAME, FIELD, OP) \ 416 case OP: \ 417 n_bytes += arg_size; \ 418 if ((arg = get_arg())) \ 419 tio.FIELD |= NAME; \ 420 else \ 421 tio.FIELD &= ~NAME; \ 422 debug3("tty_parse_modes: %d %d", OP, arg); \ 423 break; 424 425#include "ttymodes.h" 426 427#undef TTYCHAR 428#undef TTYMODE 429 430 default: 431 debug("Ignoring unsupported tty mode opcode %d (0x%x)", 432 opcode, opcode); 433 if (!compat20) { 434 /* 435 * SSH1: 436 * Opcodes 1 to 127 are defined to have 437 * a one-byte argument. 438 * Opcodes 128 to 159 are defined to have 439 * an integer argument. 440 */ 441 if (opcode > 0 && opcode < 128) { 442 n_bytes += 1; 443 (void) packet_get_char(); 444 break; 445 } else if (opcode >= 128 && opcode < 160) { 446 n_bytes += 4; 447 (void) packet_get_int(); 448 break; 449 } else { 450 /* 451 * It is a truly undefined opcode (160 to 255). 452 * We have no idea about its arguments. So we 453 * must stop parsing. Note that some data 454 * may be left in the packet; hopefully there 455 * is nothing more coming after the mode data. 456 */ 457 logit("parse_tty_modes: unknown opcode %d", 458 opcode); 459 goto set; 460 } 461 } else { 462 /* 463 * SSH2: 464 * Opcodes 1 to 159 are defined to have 465 * a uint32 argument. 466 * Opcodes 160 to 255 are undefined and 467 * cause parsing to stop. 468 */ 469 if (opcode > 0 && opcode < 160) { 470 n_bytes += 4; 471 (void) packet_get_int(); 472 break; 473 } else { 474 logit("parse_tty_modes: unknown opcode %d", 475 opcode); 476 goto set; 477 } 478 } 479 } 480 } 481 482set: 483 if (*n_bytes_ptr != n_bytes) { 484 *n_bytes_ptr = n_bytes; 485 logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 486 *n_bytes_ptr, n_bytes); 487 return; /* Don't process bytes passed */ 488 } 489 if (failure == -1) 490 return; /* Packet parsed ok but tcgetattr() failed */ 491 492 /* Set the new modes for the terminal. */ 493 if (tcsetattr(fd, TCSANOW, &tio) == -1) 494 logit("Setting tty modes failed: %.100s", strerror(errno)); 495} 496