Branch data Line data Source code
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 "includes.h"
47 : :
48 : : #include <sys/types.h>
49 : :
50 : : #include <errno.h>
51 : : #include <string.h>
52 : : #include <termios.h>
53 : : #include <stdarg.h>
54 : :
55 : : #include "packet.h"
56 : : #include "log.h"
57 : : #include "ssh1.h"
58 : : #include "compat.h"
59 : : #include "buffer.h"
60 : :
61 : : #define TTY_OP_END 0
62 : : /*
63 : : * uint32 (u_int) follows speed in SSH1 and SSH2
64 : : */
65 : : #define TTY_OP_ISPEED_PROTO1 192
66 : : #define TTY_OP_OSPEED_PROTO1 193
67 : : #define TTY_OP_ISPEED_PROTO2 128
68 : : #define TTY_OP_OSPEED_PROTO2 129
69 : :
70 : : /*
71 : : * Converts POSIX speed_t to a baud rate. The values of the
72 : : * constants for speed_t are not themselves portable.
73 : : */
74 : : static int
75 : 0 : speed_to_baud(speed_t speed)
76 : : {
77 [ # # # # : 0 : switch (speed) {
# # # # #
# # # # #
# # # # #
# ]
78 : : case B0:
79 : : return 0;
80 : : case B50:
81 : 0 : return 50;
82 : : case B75:
83 : 0 : return 75;
84 : : case B110:
85 : 0 : return 110;
86 : : case B134:
87 : 0 : return 134;
88 : : case B150:
89 : 0 : return 150;
90 : : case B200:
91 : 0 : return 200;
92 : : case B300:
93 : 0 : return 300;
94 : : case B600:
95 : 0 : return 600;
96 : : case B1200:
97 : 0 : return 1200;
98 : : case B1800:
99 : 0 : return 1800;
100 : : case B2400:
101 : 0 : return 2400;
102 : : case B4800:
103 : 0 : return 4800;
104 : : case B9600:
105 : 0 : return 9600;
106 : :
107 : : #ifdef B19200
108 : : case B19200:
109 : 0 : return 19200;
110 : : #else /* B19200 */
111 : : #ifdef EXTA
112 : : case EXTA:
113 : : return 19200;
114 : : #endif /* EXTA */
115 : : #endif /* B19200 */
116 : :
117 : : #ifdef B38400
118 : : case B38400:
119 : 0 : return 38400;
120 : : #else /* B38400 */
121 : : #ifdef EXTB
122 : : case EXTB:
123 : : return 38400;
124 : : #endif /* EXTB */
125 : : #endif /* B38400 */
126 : :
127 : : #ifdef B7200
128 : : case B7200:
129 : : return 7200;
130 : : #endif /* B7200 */
131 : : #ifdef B14400
132 : : case B14400:
133 : : return 14400;
134 : : #endif /* B14400 */
135 : : #ifdef B28800
136 : : case B28800:
137 : : return 28800;
138 : : #endif /* B28800 */
139 : : #ifdef B57600
140 : : case B57600:
141 : 0 : return 57600;
142 : : #endif /* B57600 */
143 : : #ifdef B76800
144 : : case B76800:
145 : : return 76800;
146 : : #endif /* B76800 */
147 : : #ifdef B115200
148 : : case B115200:
149 : 0 : return 115200;
150 : : #endif /* B115200 */
151 : : #ifdef B230400
152 : : case B230400:
153 : 0 : return 230400;
154 : : #endif /* B230400 */
155 : : default:
156 : 0 : return 9600;
157 : : }
158 : : }
159 : :
160 : : /*
161 : : * Converts a numeric baud rate to a POSIX speed_t.
162 : : */
163 : : static speed_t
164 : 0 : baud_to_speed(int baud)
165 : : {
166 [ # # # # : 0 : switch (baud) {
# # # # #
# # # # #
# # # # #
# ]
167 : : case 0:
168 : : return B0;
169 : : case 50:
170 : 0 : return B50;
171 : : case 75:
172 : 0 : return B75;
173 : : case 110:
174 : 0 : return B110;
175 : : case 134:
176 : 0 : return B134;
177 : : case 150:
178 : 0 : return B150;
179 : : case 200:
180 : 0 : return B200;
181 : : case 300:
182 : 0 : return B300;
183 : : case 600:
184 : 0 : return B600;
185 : : case 1200:
186 : 0 : return B1200;
187 : : case 1800:
188 : 0 : return B1800;
189 : : case 2400:
190 : 0 : return B2400;
191 : : case 4800:
192 : 0 : return B4800;
193 : : case 9600:
194 : 0 : return B9600;
195 : :
196 : : #ifdef B19200
197 : : case 19200:
198 : 0 : return B19200;
199 : : #else /* B19200 */
200 : : #ifdef EXTA
201 : : case 19200:
202 : : return EXTA;
203 : : #endif /* EXTA */
204 : : #endif /* B19200 */
205 : :
206 : : #ifdef B38400
207 : : case 38400:
208 : 0 : return B38400;
209 : : #else /* B38400 */
210 : : #ifdef EXTB
211 : : case 38400:
212 : : return EXTB;
213 : : #endif /* EXTB */
214 : : #endif /* B38400 */
215 : :
216 : : #ifdef B7200
217 : : case 7200:
218 : : return B7200;
219 : : #endif /* B7200 */
220 : : #ifdef B14400
221 : : case 14400:
222 : : return B14400;
223 : : #endif /* B14400 */
224 : : #ifdef B28800
225 : : case 28800:
226 : : return B28800;
227 : : #endif /* B28800 */
228 : : #ifdef B57600
229 : : case 57600:
230 : 0 : return B57600;
231 : : #endif /* B57600 */
232 : : #ifdef B76800
233 : : case 76800:
234 : : return B76800;
235 : : #endif /* B76800 */
236 : : #ifdef B115200
237 : : case 115200:
238 : 0 : return B115200;
239 : : #endif /* B115200 */
240 : : #ifdef B230400
241 : : case 230400:
242 : 0 : return B230400;
243 : : #endif /* B230400 */
244 : : default:
245 : 0 : return B9600;
246 : : }
247 : : }
248 : :
249 : : /*
250 : : * Encode a special character into SSH line format.
251 : : */
252 : : static u_int
253 : : special_char_encode(cc_t c)
254 : : {
255 : : #ifdef _POSIX_VDISABLE
256 : : if (c == _POSIX_VDISABLE)
257 : : return 255;
258 : : #endif /* _POSIX_VDISABLE */
259 : 0 : return c;
260 : : }
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 : 0 : 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 : 0 : 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 : 0 : buffer_init(&buf);
290 [ # # ]: 0 : 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 : 0 : tty_op_ospeed = TTY_OP_OSPEED_PROTO1;
296 : 0 : tty_op_ispeed = TTY_OP_ISPEED_PROTO1;
297 : 0 : put_arg = (void (*)(Buffer *, u_int)) buffer_put_char;
298 : : }
299 : :
300 [ # # ]: 0 : if (tiop == NULL) {
301 [ # # ]: 0 : if (fd == -1) {
302 : 0 : debug("tty_make_modes: no fd or tio");
303 : 0 : goto end;
304 : : }
305 [ # # ]: 0 : if (tcgetattr(fd, &tio) == -1) {
306 : 0 : logit("tcgetattr: %.100s", strerror(errno));
307 : 0 : goto end;
308 : : }
309 : : } else
310 : 0 : tio = *tiop;
311 : :
312 : : /* Store input and output baud rates. */
313 : 0 : baud = speed_to_baud(cfgetospeed(&tio));
314 : 0 : buffer_put_char(&buf, tty_op_ospeed);
315 : 0 : buffer_put_int(&buf, baud);
316 : 0 : baud = speed_to_baud(cfgetispeed(&tio));
317 : 0 : buffer_put_char(&buf, tty_op_ispeed);
318 : 0 : 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 : 0 : buffer_put_char(&buf, TTY_OP_END);
337 [ # # ]: 0 : if (compat20)
338 : 0 : packet_put_string(buffer_ptr(&buf), buffer_len(&buf));
339 : : else
340 : 0 : packet_put_raw(buffer_ptr(&buf), buffer_len(&buf));
341 : 0 : buffer_free(&buf);
342 : 0 : }
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 : 0 : tty_parse_modes(int fd, int *n_bytes_ptr)
350 : : {
351 : : struct termios tio;
352 : : int opcode, baud;
353 : 0 : int n_bytes = 0;
354 : 0 : int failure = 0;
355 : : u_int (*get_arg)(void);
356 : : int arg_size;
357 : :
358 [ # # ]: 0 : if (compat20) {
359 : 0 : *n_bytes_ptr = packet_get_int();
360 [ # # ]: 0 : if (*n_bytes_ptr == 0)
361 : 0 : 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 [ # # ]: 0 : if (tcgetattr(fd, &tio) == -1) {
375 : 0 : logit("tcgetattr: %.100s", strerror(errno));
376 : 0 : failure = -1;
377 : : }
378 : :
379 : : for (;;) {
380 : 0 : n_bytes += 1;
381 : 0 : opcode = packet_get_char();
382 [ # # # # : 0 : 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 : 0 : n_bytes += 4;
390 : 0 : baud = packet_get_int();
391 [ # # # # ]: 0 : if (failure != -1 &&
392 : 0 : cfsetispeed(&tio, baud_to_speed(baud)) == -1)
393 : 0 : 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 : 0 : n_bytes += 4;
400 : 0 : baud = packet_get_int();
401 [ # # # # ]: 0 : if (failure != -1 &&
402 : 0 : cfsetospeed(&tio, baud_to_speed(baud)) == -1)
403 : 0 : 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 : 0 : debug("Ignoring unsupported tty mode opcode %d (0x%x)",
427 : : opcode, opcode);
428 [ # # ]: 0 : 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 [ # # ]: 0 : if (opcode > 0 && opcode < 128) {
437 : 0 : n_bytes += 1;
438 : 0 : (void) packet_get_char();
439 : 0 : break;
440 [ # # ]: 0 : } else if (opcode >= 128 && opcode < 160) {
441 : 0 : n_bytes += 4;
442 : 0 : (void) packet_get_int();
443 : 0 : 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 : 0 : logit("parse_tty_modes: unknown opcode %d",
453 : : opcode);
454 : 0 : 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 [ # # ]: 0 : if (opcode > 0 && opcode < 160) {
465 : 0 : n_bytes += 4;
466 : 0 : (void) packet_get_int();
467 : 0 : break;
468 : : } else {
469 : 0 : logit("parse_tty_modes: unknown opcode %d",
470 : : opcode);
471 : 0 : goto set;
472 : : }
473 : : }
474 : : }
475 : : }
476 : :
477 : : set:
478 [ # # ]: 0 : if (*n_bytes_ptr != n_bytes) {
479 : 0 : *n_bytes_ptr = n_bytes;
480 : 0 : logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
481 : : *n_bytes_ptr, n_bytes);
482 : 0 : return; /* Don't process bytes passed */
483 : : }
484 [ # # ]: 0 : if (failure == -1)
485 : : return; /* Packet parsed ok but tcgetattr() failed */
486 : :
487 : : /* Set the new modes for the terminal. */
488 [ # # ]: 0 : if (tcsetattr(fd, TCSANOW, &tio) == -1)
489 : 0 : logit("Setting tty modes failed: %.100s", strerror(errno));
490 : : }
|