1192595Sdes/* $OpenBSD: ttymodes.c,v 1.29 2008/11/02 00:16:16 stevesk Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 665668Skris * 765668Skris * As far as I am concerned, the code I have written for this software 865668Skris * can be used freely for any purpose. Any derived versions of this 965668Skris * software must be clearly marked as such, and if the derived work is 1065668Skris * incompatible with the protocol description in the RFC file, it must be 1165668Skris * called by a name other than "ssh" or "Secure Shell". 1257429Smarkm */ 1357429Smarkm 1476259Sgreen/* 1576259Sgreen * SSH2 tty modes support by Kevin Steves. 1676259Sgreen * Copyright (c) 2001 Kevin Steves. All rights reserved. 1776259Sgreen * 1876259Sgreen * Redistribution and use in source and binary forms, with or without 1976259Sgreen * modification, are permitted provided that the following conditions 2076259Sgreen * are met: 2176259Sgreen * 1. Redistributions of source code must retain the above copyright 2276259Sgreen * notice, this list of conditions and the following disclaimer. 2376259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 2476259Sgreen * notice, this list of conditions and the following disclaimer in the 2576259Sgreen * documentation and/or other materials provided with the distribution. 2676259Sgreen * 2776259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2876259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2976259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3076259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3176259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3276259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3376259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3476259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3576259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3676259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3776259Sgreen */ 3876259Sgreen 3976259Sgreen/* 4076259Sgreen * Encoding and decoding of terminal modes in a portable way. 4176259Sgreen * Much of the format is defined in ttymodes.h; it is included multiple times 4276259Sgreen * into this file with the appropriate macro definitions to generate the 4376259Sgreen * suitable code. 4476259Sgreen */ 4576259Sgreen 4657429Smarkm#include "includes.h" 4757429Smarkm 48162852Sdes#include <sys/types.h> 49162852Sdes 50162852Sdes#include <errno.h> 51162852Sdes#include <string.h> 52162852Sdes#include <termios.h> 53162852Sdes#include <stdarg.h> 54162852Sdes 5557429Smarkm#include "packet.h" 5676259Sgreen#include "log.h" 5776259Sgreen#include "ssh1.h" 5876259Sgreen#include "compat.h" 5976259Sgreen#include "buffer.h" 6057429Smarkm 6176259Sgreen#define TTY_OP_END 0 6276259Sgreen/* 6376259Sgreen * uint32 (u_int) follows speed in SSH1 and SSH2 6476259Sgreen */ 6576259Sgreen#define TTY_OP_ISPEED_PROTO1 192 6676259Sgreen#define TTY_OP_OSPEED_PROTO1 193 6776259Sgreen#define TTY_OP_ISPEED_PROTO2 128 6876259Sgreen#define TTY_OP_OSPEED_PROTO2 129 6957429Smarkm 7057429Smarkm/* 7157429Smarkm * Converts POSIX speed_t to a baud rate. The values of the 7257429Smarkm * constants for speed_t are not themselves portable. 7357429Smarkm */ 7460573Skrisstatic int 7557429Smarkmspeed_to_baud(speed_t speed) 7657429Smarkm{ 7757429Smarkm switch (speed) { 7857429Smarkm case B0: 7957429Smarkm return 0; 8057429Smarkm case B50: 8157429Smarkm return 50; 8257429Smarkm case B75: 8357429Smarkm return 75; 8457429Smarkm case B110: 8557429Smarkm return 110; 8657429Smarkm case B134: 8757429Smarkm return 134; 8857429Smarkm case B150: 8957429Smarkm return 150; 9057429Smarkm case B200: 9157429Smarkm return 200; 9257429Smarkm case B300: 9357429Smarkm return 300; 9457429Smarkm case B600: 9557429Smarkm return 600; 9657429Smarkm case B1200: 9757429Smarkm return 1200; 9857429Smarkm case B1800: 9957429Smarkm return 1800; 10057429Smarkm case B2400: 10157429Smarkm return 2400; 10257429Smarkm case B4800: 10357429Smarkm return 4800; 10457429Smarkm case B9600: 10557429Smarkm return 9600; 10657429Smarkm 10757429Smarkm#ifdef B19200 10857429Smarkm case B19200: 10957429Smarkm return 19200; 11057429Smarkm#else /* B19200 */ 11157429Smarkm#ifdef EXTA 11257429Smarkm case EXTA: 11357429Smarkm return 19200; 11457429Smarkm#endif /* EXTA */ 11557429Smarkm#endif /* B19200 */ 11657429Smarkm 11757429Smarkm#ifdef B38400 11857429Smarkm case B38400: 11957429Smarkm return 38400; 12057429Smarkm#else /* B38400 */ 12157429Smarkm#ifdef EXTB 12257429Smarkm case EXTB: 12357429Smarkm return 38400; 12457429Smarkm#endif /* EXTB */ 12557429Smarkm#endif /* B38400 */ 12657429Smarkm 12757429Smarkm#ifdef B7200 12857429Smarkm case B7200: 12957429Smarkm return 7200; 13057429Smarkm#endif /* B7200 */ 13157429Smarkm#ifdef B14400 13257429Smarkm case B14400: 13357429Smarkm return 14400; 13457429Smarkm#endif /* B14400 */ 13557429Smarkm#ifdef B28800 13657429Smarkm case B28800: 13757429Smarkm return 28800; 13857429Smarkm#endif /* B28800 */ 13957429Smarkm#ifdef B57600 14057429Smarkm case B57600: 14157429Smarkm return 57600; 14257429Smarkm#endif /* B57600 */ 14357429Smarkm#ifdef B76800 14457429Smarkm case B76800: 14557429Smarkm return 76800; 14657429Smarkm#endif /* B76800 */ 14757429Smarkm#ifdef B115200 14857429Smarkm case B115200: 14957429Smarkm return 115200; 15057429Smarkm#endif /* B115200 */ 15157429Smarkm#ifdef B230400 15257429Smarkm case B230400: 15357429Smarkm return 230400; 15457429Smarkm#endif /* B230400 */ 15557429Smarkm default: 15657429Smarkm return 9600; 15757429Smarkm } 15857429Smarkm} 15957429Smarkm 16057429Smarkm/* 16157429Smarkm * Converts a numeric baud rate to a POSIX speed_t. 16257429Smarkm */ 16360573Skrisstatic speed_t 16457429Smarkmbaud_to_speed(int baud) 16557429Smarkm{ 16657429Smarkm switch (baud) { 16776259Sgreen case 0: 16857429Smarkm return B0; 16957429Smarkm case 50: 17057429Smarkm return B50; 17157429Smarkm case 75: 17257429Smarkm return B75; 17357429Smarkm case 110: 17457429Smarkm return B110; 17557429Smarkm case 134: 17657429Smarkm return B134; 17757429Smarkm case 150: 17857429Smarkm return B150; 17957429Smarkm case 200: 18057429Smarkm return B200; 18157429Smarkm case 300: 18257429Smarkm return B300; 18357429Smarkm case 600: 18457429Smarkm return B600; 18557429Smarkm case 1200: 18657429Smarkm return B1200; 18757429Smarkm case 1800: 18857429Smarkm return B1800; 18957429Smarkm case 2400: 19057429Smarkm return B2400; 19157429Smarkm case 4800: 19257429Smarkm return B4800; 19357429Smarkm case 9600: 19457429Smarkm return B9600; 19557429Smarkm 19657429Smarkm#ifdef B19200 19757429Smarkm case 19200: 19857429Smarkm return B19200; 19957429Smarkm#else /* B19200 */ 20057429Smarkm#ifdef EXTA 20157429Smarkm case 19200: 20257429Smarkm return EXTA; 20357429Smarkm#endif /* EXTA */ 20457429Smarkm#endif /* B19200 */ 20557429Smarkm 20657429Smarkm#ifdef B38400 20757429Smarkm case 38400: 20857429Smarkm return B38400; 20957429Smarkm#else /* B38400 */ 21057429Smarkm#ifdef EXTB 21157429Smarkm case 38400: 21257429Smarkm return EXTB; 21357429Smarkm#endif /* EXTB */ 21457429Smarkm#endif /* B38400 */ 21557429Smarkm 21657429Smarkm#ifdef B7200 21757429Smarkm case 7200: 21857429Smarkm return B7200; 21957429Smarkm#endif /* B7200 */ 22057429Smarkm#ifdef B14400 22157429Smarkm case 14400: 22257429Smarkm return B14400; 22357429Smarkm#endif /* B14400 */ 22457429Smarkm#ifdef B28800 22557429Smarkm case 28800: 22657429Smarkm return B28800; 22757429Smarkm#endif /* B28800 */ 22857429Smarkm#ifdef B57600 22957429Smarkm case 57600: 23057429Smarkm return B57600; 23157429Smarkm#endif /* B57600 */ 23257429Smarkm#ifdef B76800 23357429Smarkm case 76800: 23457429Smarkm return B76800; 23557429Smarkm#endif /* B76800 */ 23657429Smarkm#ifdef B115200 23757429Smarkm case 115200: 23857429Smarkm return B115200; 23957429Smarkm#endif /* B115200 */ 24057429Smarkm#ifdef B230400 24157429Smarkm case 230400: 24257429Smarkm return B230400; 24357429Smarkm#endif /* B230400 */ 24457429Smarkm default: 24557429Smarkm return B9600; 24657429Smarkm } 24757429Smarkm} 24857429Smarkm 24957429Smarkm/* 250149749Sdes * Encode a special character into SSH line format. 251149749Sdes */ 252149749Sdesstatic u_int 253149749Sdesspecial_char_encode(cc_t c) 254149749Sdes{ 255149749Sdes#ifdef _POSIX_VDISABLE 256149749Sdes if (c == _POSIX_VDISABLE) 257149749Sdes return 255; 258149749Sdes#endif /* _POSIX_VDISABLE */ 259149749Sdes return c; 260149749Sdes} 261149749Sdes 262149749Sdes/* 263149749Sdes * Decode a special character from SSH line format. 264149749Sdes */ 265149749Sdesstatic cc_t 266149749Sdesspecial_char_decode(u_int c) 267149749Sdes{ 268149749Sdes#ifdef _POSIX_VDISABLE 269149749Sdes if (c == 255) 270149749Sdes return _POSIX_VDISABLE; 271149749Sdes#endif /* _POSIX_VDISABLE */ 272149749Sdes return c; 273149749Sdes} 274149749Sdes 275149749Sdes/* 27657429Smarkm * Encodes terminal modes for the terminal referenced by fd 27776259Sgreen * or tiop in a portable manner, and appends the modes to a packet 27857429Smarkm * being constructed. 27957429Smarkm */ 28060573Skrisvoid 28176259Sgreentty_make_modes(int fd, struct termios *tiop) 28257429Smarkm{ 28357429Smarkm struct termios tio; 28457429Smarkm int baud; 28576259Sgreen Buffer buf; 28676259Sgreen int tty_op_ospeed, tty_op_ispeed; 28776259Sgreen void (*put_arg)(Buffer *, u_int); 28857429Smarkm 28976259Sgreen buffer_init(&buf); 29076259Sgreen if (compat20) { 29176259Sgreen tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 29276259Sgreen tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 29376259Sgreen put_arg = buffer_put_int; 29476259Sgreen } else { 29576259Sgreen tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 29676259Sgreen tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 29776259Sgreen put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 29857429Smarkm } 29976259Sgreen 30076259Sgreen if (tiop == NULL) { 301181111Sdes if (fd == -1) { 302181111Sdes debug("tty_make_modes: no fd or tio"); 303181111Sdes goto end; 304181111Sdes } 30576259Sgreen if (tcgetattr(fd, &tio) == -1) { 306124208Sdes logit("tcgetattr: %.100s", strerror(errno)); 30776259Sgreen goto end; 30876259Sgreen } 30976259Sgreen } else 31076259Sgreen tio = *tiop; 31176259Sgreen 31257429Smarkm /* Store input and output baud rates. */ 31357429Smarkm baud = speed_to_baud(cfgetospeed(&tio)); 31476259Sgreen buffer_put_char(&buf, tty_op_ospeed); 31576259Sgreen buffer_put_int(&buf, baud); 31657429Smarkm baud = speed_to_baud(cfgetispeed(&tio)); 31776259Sgreen buffer_put_char(&buf, tty_op_ispeed); 31876259Sgreen buffer_put_int(&buf, baud); 31957429Smarkm 32057429Smarkm /* Store values of mode flags. */ 32157429Smarkm#define TTYCHAR(NAME, OP) \ 32276259Sgreen buffer_put_char(&buf, OP); \ 323149749Sdes put_arg(&buf, special_char_encode(tio.c_cc[NAME])); 32476259Sgreen 32557429Smarkm#define TTYMODE(NAME, FIELD, OP) \ 32676259Sgreen buffer_put_char(&buf, OP); \ 32776259Sgreen put_arg(&buf, ((tio.FIELD & NAME) != 0)); 32857429Smarkm 32957429Smarkm#include "ttymodes.h" 33057429Smarkm 33157429Smarkm#undef TTYCHAR 33257429Smarkm#undef TTYMODE 33357429Smarkm 33476259Sgreenend: 33557429Smarkm /* Mark end of mode data. */ 33676259Sgreen buffer_put_char(&buf, TTY_OP_END); 33776259Sgreen if (compat20) 33876259Sgreen packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 33976259Sgreen else 34076259Sgreen packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 34176259Sgreen buffer_free(&buf); 34257429Smarkm} 34357429Smarkm 34457429Smarkm/* 34557429Smarkm * Decodes terminal modes for the terminal referenced by fd in a portable 34657429Smarkm * manner from a packet being read. 34757429Smarkm */ 34860573Skrisvoid 34957429Smarkmtty_parse_modes(int fd, int *n_bytes_ptr) 35057429Smarkm{ 35157429Smarkm struct termios tio; 35257429Smarkm int opcode, baud; 35357429Smarkm int n_bytes = 0; 35457429Smarkm int failure = 0; 35576259Sgreen u_int (*get_arg)(void); 356181111Sdes int arg_size; 35757429Smarkm 35876259Sgreen if (compat20) { 35976259Sgreen *n_bytes_ptr = packet_get_int(); 36076259Sgreen if (*n_bytes_ptr == 0) 36176259Sgreen return; 36276259Sgreen get_arg = packet_get_int; 36376259Sgreen arg_size = 4; 36476259Sgreen } else { 36576259Sgreen get_arg = packet_get_char; 36676259Sgreen arg_size = 1; 36776259Sgreen } 36876259Sgreen 36957429Smarkm /* 37057429Smarkm * Get old attributes for the terminal. We will modify these 37157429Smarkm * flags. I am hoping that if there are any machine-specific 37257429Smarkm * modes, they will initially have reasonable values. 37357429Smarkm */ 37476259Sgreen if (tcgetattr(fd, &tio) == -1) { 375124208Sdes logit("tcgetattr: %.100s", strerror(errno)); 37657429Smarkm failure = -1; 37776259Sgreen } 37857429Smarkm 37957429Smarkm for (;;) { 38057429Smarkm n_bytes += 1; 38157429Smarkm opcode = packet_get_char(); 38257429Smarkm switch (opcode) { 38357429Smarkm case TTY_OP_END: 38457429Smarkm goto set; 38557429Smarkm 38676259Sgreen /* XXX: future conflict possible */ 38776259Sgreen case TTY_OP_ISPEED_PROTO1: 38876259Sgreen case TTY_OP_ISPEED_PROTO2: 38957429Smarkm n_bytes += 4; 39057429Smarkm baud = packet_get_int(); 391162852Sdes if (failure != -1 && 392162852Sdes cfsetispeed(&tio, baud_to_speed(baud)) == -1) 39357429Smarkm error("cfsetispeed failed for %d", baud); 39457429Smarkm break; 39557429Smarkm 39676259Sgreen /* XXX: future conflict possible */ 39776259Sgreen case TTY_OP_OSPEED_PROTO1: 39876259Sgreen case TTY_OP_OSPEED_PROTO2: 39957429Smarkm n_bytes += 4; 40057429Smarkm baud = packet_get_int(); 401162852Sdes if (failure != -1 && 402162852Sdes cfsetospeed(&tio, baud_to_speed(baud)) == -1) 40357429Smarkm error("cfsetospeed failed for %d", baud); 40457429Smarkm break; 40557429Smarkm 40676259Sgreen#define TTYCHAR(NAME, OP) \ 40776259Sgreen case OP: \ 40876259Sgreen n_bytes += arg_size; \ 409149749Sdes tio.c_cc[NAME] = special_char_decode(get_arg()); \ 41057429Smarkm break; 41176259Sgreen#define TTYMODE(NAME, FIELD, OP) \ 41276259Sgreen case OP: \ 41376259Sgreen n_bytes += arg_size; \ 414181111Sdes if (get_arg()) \ 41576259Sgreen tio.FIELD |= NAME; \ 41676259Sgreen else \ 41776259Sgreen tio.FIELD &= ~NAME; \ 41857429Smarkm break; 41957429Smarkm 42057429Smarkm#include "ttymodes.h" 42157429Smarkm 42257429Smarkm#undef TTYCHAR 42357429Smarkm#undef TTYMODE 42457429Smarkm 42557429Smarkm default: 42657429Smarkm debug("Ignoring unsupported tty mode opcode %d (0x%x)", 42792555Sdes opcode, opcode); 42876259Sgreen if (!compat20) { 42976259Sgreen /* 43076259Sgreen * SSH1: 43176259Sgreen * Opcodes 1 to 127 are defined to have 43276259Sgreen * a one-byte argument. 43398675Sdes * Opcodes 128 to 159 are defined to have 43498675Sdes * an integer argument. 43598675Sdes */ 43676259Sgreen if (opcode > 0 && opcode < 128) { 43776259Sgreen n_bytes += 1; 43876259Sgreen (void) packet_get_char(); 43976259Sgreen break; 44076259Sgreen } else if (opcode >= 128 && opcode < 160) { 44198675Sdes n_bytes += 4; 44298675Sdes (void) packet_get_int(); 44398675Sdes break; 44476259Sgreen } else { 44576259Sgreen /* 44676259Sgreen * It is a truly undefined opcode (160 to 255). 44776259Sgreen * We have no idea about its arguments. So we 448162852Sdes * must stop parsing. Note that some data 449162852Sdes * may be left in the packet; hopefully there 450162852Sdes * is nothing more coming after the mode data. 45176259Sgreen */ 452162852Sdes logit("parse_tty_modes: unknown opcode %d", 453162852Sdes opcode); 45476259Sgreen goto set; 45598675Sdes } 45657429Smarkm } else { 45757429Smarkm /* 45876259Sgreen * SSH2: 45976259Sgreen * Opcodes 1 to 159 are defined to have 46076259Sgreen * a uint32 argument. 46176259Sgreen * Opcodes 160 to 255 are undefined and 46276259Sgreen * cause parsing to stop. 46357429Smarkm */ 46476259Sgreen if (opcode > 0 && opcode < 160) { 46557429Smarkm n_bytes += 4; 46657429Smarkm (void) packet_get_int(); 46757429Smarkm break; 46876259Sgreen } else { 469162852Sdes logit("parse_tty_modes: unknown opcode %d", 470162852Sdes opcode); 47176259Sgreen goto set; 47257429Smarkm } 47398675Sdes } 47457429Smarkm } 47557429Smarkm } 47657429Smarkm 47757429Smarkmset: 47857429Smarkm if (*n_bytes_ptr != n_bytes) { 47957429Smarkm *n_bytes_ptr = n_bytes; 480124208Sdes logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 48176259Sgreen *n_bytes_ptr, n_bytes); 48257429Smarkm return; /* Don't process bytes passed */ 48357429Smarkm } 48457429Smarkm if (failure == -1) 48576259Sgreen return; /* Packet parsed ok but tcgetattr() failed */ 48657429Smarkm 48757429Smarkm /* Set the new modes for the terminal. */ 48876259Sgreen if (tcsetattr(fd, TCSANOW, &tio) == -1) 489124208Sdes logit("Setting tty modes failed: %.100s", strerror(errno)); 49057429Smarkm} 491