1 /* $OpenBSD: ttymodes.c,v 1.29 2008/11/02 00:16:16 stevesk Exp $ */ 2 /* 3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5 * All rights reserved 6 * 7 * As far as I am concerned, the code I have written for this software 8 * can be used freely for any purpose. Any derived versions of this 9 * software must be clearly marked as such, and if the derived work is 10 * incompatible with the protocol description in the RFC file, it must be 11 * called by a name other than "ssh" or "Secure Shell". 12 */ 13 14 /* 15 * SSH2 tty modes support by Kevin Steves. 16 * Copyright (c) 2001 Kevin Steves. All rights reserved. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 2. Redistributions in binary form must reproduce the above copyright 24 * notice, this list of conditions and the following disclaimer in the 25 * documentation and/or other materials provided with the distribution. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Encoding and decoding of terminal modes in a portable way. 41 * Much of the format is defined in ttymodes.h; it is included multiple times 42 * into this file with the appropriate macro definitions to generate the 43 * suitable code. 44 */ 45 46 #include <sys/types.h> 47 48 #include <errno.h> 49 #include <string.h> 50 #include <termios.h> 51 #include <stdarg.h> 52 53 #include "packet.h" 54 #include "log.h" 55 #include "ssh1.h" 56 #include "compat.h" 57 #include "buffer.h" 58 59 #define TTY_OP_END 0 60 /* 61 * uint32 (u_int) follows speed in SSH1 and SSH2 62 */ 63 #define TTY_OP_ISPEED_PROTO1 192 64 #define TTY_OP_OSPEED_PROTO1 193 65 #define TTY_OP_ISPEED_PROTO2 128 66 #define TTY_OP_OSPEED_PROTO2 129 67 68 /* 69 * Converts POSIX speed_t to a baud rate. The values of the 70 * constants for speed_t are not themselves portable. 71 */ 72 static int 73 speed_to_baud(speed_t speed) 74 { 75 switch (speed) { 76 case B0: 77 return 0; 78 case B50: 79 return 50; 80 case B75: 81 return 75; 82 case B110: 83 return 110; 84 case B134: 85 return 134; 86 case B150: 87 return 150; 88 case B200: 89 return 200; 90 case B300: 91 return 300; 92 case B600: 93 return 600; 94 case B1200: 95 return 1200; 96 case B1800: 97 return 1800; 98 case B2400: 99 return 2400; 100 case B4800: 101 return 4800; 102 case B9600: 103 return 9600; 104 105 #ifdef B19200 106 case B19200: 107 return 19200; 108 #else /* B19200 */ 109 #ifdef EXTA 110 case EXTA: 111 return 19200; 112 #endif /* EXTA */ 113 #endif /* B19200 */ 114 115 #ifdef B38400 116 case B38400: 117 return 38400; 118 #else /* B38400 */ 119 #ifdef EXTB 120 case EXTB: 121 return 38400; 122 #endif /* EXTB */ 123 #endif /* B38400 */ 124 125 #ifdef B7200 126 case B7200: 127 return 7200; 128 #endif /* B7200 */ 129 #ifdef B14400 130 case B14400: 131 return 14400; 132 #endif /* B14400 */ 133 #ifdef B28800 134 case B28800: 135 return 28800; 136 #endif /* B28800 */ 137 #ifdef B57600 138 case B57600: 139 return 57600; 140 #endif /* B57600 */ 141 #ifdef B76800 142 case B76800: 143 return 76800; 144 #endif /* B76800 */ 145 #ifdef B115200 146 case B115200: 147 return 115200; 148 #endif /* B115200 */ 149 #ifdef B230400 150 case B230400: 151 return 230400; 152 #endif /* B230400 */ 153 default: 154 return 9600; 155 } 156 } 157 158 /* 159 * Converts a numeric baud rate to a POSIX speed_t. 160 */ 161 static speed_t 162 baud_to_speed(int baud) 163 { 164 switch (baud) { 165 case 0: 166 return B0; 167 case 50: 168 return B50; 169 case 75: 170 return B75; 171 case 110: 172 return B110; 173 case 134: 174 return B134; 175 case 150: 176 return B150; 177 case 200: 178 return B200; 179 case 300: 180 return B300; 181 case 600: 182 return B600; 183 case 1200: 184 return B1200; 185 case 1800: 186 return B1800; 187 case 2400: 188 return B2400; 189 case 4800: 190 return B4800; 191 case 9600: 192 return B9600; 193 194 #ifdef B19200 195 case 19200: 196 return B19200; 197 #else /* B19200 */ 198 #ifdef EXTA 199 case 19200: 200 return EXTA; 201 #endif /* EXTA */ 202 #endif /* B19200 */ 203 204 #ifdef B38400 205 case 38400: 206 return B38400; 207 #else /* B38400 */ 208 #ifdef EXTB 209 case 38400: 210 return EXTB; 211 #endif /* EXTB */ 212 #endif /* B38400 */ 213 214 #ifdef B7200 215 case 7200: 216 return B7200; 217 #endif /* B7200 */ 218 #ifdef B14400 219 case 14400: 220 return B14400; 221 #endif /* B14400 */ 222 #ifdef B28800 223 case 28800: 224 return B28800; 225 #endif /* B28800 */ 226 #ifdef B57600 227 case 57600: 228 return B57600; 229 #endif /* B57600 */ 230 #ifdef B76800 231 case 76800: 232 return B76800; 233 #endif /* B76800 */ 234 #ifdef B115200 235 case 115200: 236 return B115200; 237 #endif /* B115200 */ 238 #ifdef B230400 239 case 230400: 240 return B230400; 241 #endif /* B230400 */ 242 default: 243 return B9600; 244 } 245 } 246 247 /* 248 * Encodes terminal modes for the terminal referenced by fd 249 * or tiop in a portable manner, and appends the modes to a packet 250 * being constructed. 251 */ 252 void 253 tty_make_modes(int fd, struct termios *tiop) 254 { 255 struct termios tio; 256 int baud; 257 Buffer buf; 258 int tty_op_ospeed, tty_op_ispeed; 259 void (*put_arg)(Buffer *, u_int); 260 261 buffer_init(&buf); 262 if (compat20) { 263 tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 264 tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 265 put_arg = buffer_put_int; 266 } else { 267 tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 268 tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 269 put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 270 } 271 272 if (tiop == NULL) { 273 if (fd == -1) { 274 debug("tty_make_modes: no fd or tio"); 275 goto end; 276 } 277 if (tcgetattr(fd, &tio) == -1) { 278 logit("tcgetattr: %.100s", strerror(errno)); 279 goto end; 280 } 281 } else 282 tio = *tiop; 283 284 /* Store input and output baud rates. */ 285 baud = speed_to_baud(cfgetospeed(&tio)); 286 buffer_put_char(&buf, tty_op_ospeed); 287 buffer_put_int(&buf, baud); 288 baud = speed_to_baud(cfgetispeed(&tio)); 289 buffer_put_char(&buf, tty_op_ispeed); 290 buffer_put_int(&buf, baud); 291 292 /* Store values of mode flags. */ 293 #define TTYCHAR(NAME, OP) \ 294 buffer_put_char(&buf, OP); \ 295 put_arg(&buf, tio.c_cc[NAME]); 296 297 #define TTYMODE(NAME, FIELD, OP) \ 298 buffer_put_char(&buf, OP); \ 299 put_arg(&buf, ((tio.FIELD & NAME) != 0)); 300 301 #include "ttymodes.h" 302 303 #undef TTYCHAR 304 #undef TTYMODE 305 306 end: 307 /* Mark end of mode data. */ 308 buffer_put_char(&buf, TTY_OP_END); 309 if (compat20) 310 packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 311 else 312 packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 313 buffer_free(&buf); 314 } 315 316 /* 317 * Decodes terminal modes for the terminal referenced by fd in a portable 318 * manner from a packet being read. 319 */ 320 void 321 tty_parse_modes(int fd, int *n_bytes_ptr) 322 { 323 struct termios tio; 324 int opcode, baud; 325 int n_bytes = 0; 326 int failure = 0; 327 u_int (*get_arg)(void); 328 int arg_size; 329 330 if (compat20) { 331 *n_bytes_ptr = packet_get_int(); 332 if (*n_bytes_ptr == 0) 333 return; 334 get_arg = packet_get_int; 335 arg_size = 4; 336 } else { 337 get_arg = packet_get_char; 338 arg_size = 1; 339 } 340 341 /* 342 * Get old attributes for the terminal. We will modify these 343 * flags. I am hoping that if there are any machine-specific 344 * modes, they will initially have reasonable values. 345 */ 346 if (tcgetattr(fd, &tio) == -1) { 347 logit("tcgetattr: %.100s", strerror(errno)); 348 failure = -1; 349 } 350 351 for (;;) { 352 n_bytes += 1; 353 opcode = packet_get_char(); 354 switch (opcode) { 355 case TTY_OP_END: 356 goto set; 357 358 /* XXX: future conflict possible */ 359 case TTY_OP_ISPEED_PROTO1: 360 case TTY_OP_ISPEED_PROTO2: 361 n_bytes += 4; 362 baud = packet_get_int(); 363 if (failure != -1 && 364 cfsetispeed(&tio, baud_to_speed(baud)) == -1) 365 error("cfsetispeed failed for %d", baud); 366 break; 367 368 /* XXX: future conflict possible */ 369 case TTY_OP_OSPEED_PROTO1: 370 case TTY_OP_OSPEED_PROTO2: 371 n_bytes += 4; 372 baud = packet_get_int(); 373 if (failure != -1 && 374 cfsetospeed(&tio, baud_to_speed(baud)) == -1) 375 error("cfsetospeed failed for %d", baud); 376 break; 377 378 #define TTYCHAR(NAME, OP) \ 379 case OP: \ 380 n_bytes += arg_size; \ 381 tio.c_cc[NAME] = get_arg(); \ 382 break; 383 #define TTYMODE(NAME, FIELD, OP) \ 384 case OP: \ 385 n_bytes += arg_size; \ 386 if (get_arg()) \ 387 tio.FIELD |= NAME; \ 388 else \ 389 tio.FIELD &= ~NAME; \ 390 break; 391 392 #include "ttymodes.h" 393 394 #undef TTYCHAR 395 #undef TTYMODE 396 397 default: 398 debug("Ignoring unsupported tty mode opcode %d (0x%x)", 399 opcode, opcode); 400 if (!compat20) { 401 /* 402 * SSH1: 403 * Opcodes 1 to 127 are defined to have 404 * a one-byte argument. 405 * Opcodes 128 to 159 are defined to have 406 * an integer argument. 407 */ 408 if (opcode > 0 && opcode < 128) { 409 n_bytes += 1; 410 (void) packet_get_char(); 411 break; 412 } else if (opcode >= 128 && opcode < 160) { 413 n_bytes += 4; 414 (void) packet_get_int(); 415 break; 416 } else { 417 /* 418 * It is a truly undefined opcode (160 to 255). 419 * We have no idea about its arguments. So we 420 * must stop parsing. Note that some data 421 * may be left in the packet; hopefully there 422 * is nothing more coming after the mode data. 423 */ 424 logit("parse_tty_modes: unknown opcode %d", 425 opcode); 426 goto set; 427 } 428 } else { 429 /* 430 * SSH2: 431 * Opcodes 1 to 159 are defined to have 432 * a uint32 argument. 433 * Opcodes 160 to 255 are undefined and 434 * cause parsing to stop. 435 */ 436 if (opcode > 0 && opcode < 160) { 437 n_bytes += 4; 438 (void) packet_get_int(); 439 break; 440 } else { 441 logit("parse_tty_modes: unknown opcode %d", 442 opcode); 443 goto set; 444 } 445 } 446 } 447 } 448 449 set: 450 if (*n_bytes_ptr != n_bytes) { 451 *n_bytes_ptr = n_bytes; 452 logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 453 *n_bytes_ptr, n_bytes); 454 return; /* Don't process bytes passed */ 455 } 456 if (failure == -1) 457 return; /* Packet parsed ok but tcgetattr() failed */ 458 459 /* Set the new modes for the terminal. */ 460 if (tcsetattr(fd, TCSANOW, &tio) == -1) 461 logit("Setting tty modes failed: %.100s", strerror(errno)); 462 } 463