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