xref: /openbsd-src/usr.bin/ssh/ttymodes.c (revision 8ead0783a05eee83ab02af2c7b14b10fbcdce47d)
1 /* $OpenBSD: ttymodes.c,v 1.32 2017/04/30 23:26:54 djm 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 "compat.h"
56 #include "buffer.h"
57 
58 #define TTY_OP_END		0
59 /*
60  * uint32 (u_int) follows speed.
61  */
62 #define TTY_OP_ISPEED	128
63 #define TTY_OP_OSPEED	129
64 
65 /*
66  * Converts POSIX speed_t to a baud rate.  The values of the
67  * constants for speed_t are not themselves portable.
68  */
69 static int
70 speed_to_baud(speed_t speed)
71 {
72 	switch (speed) {
73 	case B0:
74 		return 0;
75 	case B50:
76 		return 50;
77 	case B75:
78 		return 75;
79 	case B110:
80 		return 110;
81 	case B134:
82 		return 134;
83 	case B150:
84 		return 150;
85 	case B200:
86 		return 200;
87 	case B300:
88 		return 300;
89 	case B600:
90 		return 600;
91 	case B1200:
92 		return 1200;
93 	case B1800:
94 		return 1800;
95 	case B2400:
96 		return 2400;
97 	case B4800:
98 		return 4800;
99 	case B9600:
100 		return 9600;
101 
102 #ifdef B19200
103 	case B19200:
104 		return 19200;
105 #else /* B19200 */
106 #ifdef EXTA
107 	case EXTA:
108 		return 19200;
109 #endif /* EXTA */
110 #endif /* B19200 */
111 
112 #ifdef B38400
113 	case B38400:
114 		return 38400;
115 #else /* B38400 */
116 #ifdef EXTB
117 	case EXTB:
118 		return 38400;
119 #endif /* EXTB */
120 #endif /* B38400 */
121 
122 #ifdef B7200
123 	case B7200:
124 		return 7200;
125 #endif /* B7200 */
126 #ifdef B14400
127 	case B14400:
128 		return 14400;
129 #endif /* B14400 */
130 #ifdef B28800
131 	case B28800:
132 		return 28800;
133 #endif /* B28800 */
134 #ifdef B57600
135 	case B57600:
136 		return 57600;
137 #endif /* B57600 */
138 #ifdef B76800
139 	case B76800:
140 		return 76800;
141 #endif /* B76800 */
142 #ifdef B115200
143 	case B115200:
144 		return 115200;
145 #endif /* B115200 */
146 #ifdef B230400
147 	case B230400:
148 		return 230400;
149 #endif /* B230400 */
150 	default:
151 		return 9600;
152 	}
153 }
154 
155 /*
156  * Converts a numeric baud rate to a POSIX speed_t.
157  */
158 static speed_t
159 baud_to_speed(int baud)
160 {
161 	switch (baud) {
162 	case 0:
163 		return B0;
164 	case 50:
165 		return B50;
166 	case 75:
167 		return B75;
168 	case 110:
169 		return B110;
170 	case 134:
171 		return B134;
172 	case 150:
173 		return B150;
174 	case 200:
175 		return B200;
176 	case 300:
177 		return B300;
178 	case 600:
179 		return B600;
180 	case 1200:
181 		return B1200;
182 	case 1800:
183 		return B1800;
184 	case 2400:
185 		return B2400;
186 	case 4800:
187 		return B4800;
188 	case 9600:
189 		return B9600;
190 
191 #ifdef B19200
192 	case 19200:
193 		return B19200;
194 #else /* B19200 */
195 #ifdef EXTA
196 	case 19200:
197 		return EXTA;
198 #endif /* EXTA */
199 #endif /* B19200 */
200 
201 #ifdef B38400
202 	case 38400:
203 		return B38400;
204 #else /* B38400 */
205 #ifdef EXTB
206 	case 38400:
207 		return EXTB;
208 #endif /* EXTB */
209 #endif /* B38400 */
210 
211 #ifdef B7200
212 	case 7200:
213 		return B7200;
214 #endif /* B7200 */
215 #ifdef B14400
216 	case 14400:
217 		return B14400;
218 #endif /* B14400 */
219 #ifdef B28800
220 	case 28800:
221 		return B28800;
222 #endif /* B28800 */
223 #ifdef B57600
224 	case 57600:
225 		return B57600;
226 #endif /* B57600 */
227 #ifdef B76800
228 	case 76800:
229 		return B76800;
230 #endif /* B76800 */
231 #ifdef B115200
232 	case 115200:
233 		return B115200;
234 #endif /* B115200 */
235 #ifdef B230400
236 	case 230400:
237 		return B230400;
238 #endif /* B230400 */
239 	default:
240 		return B9600;
241 	}
242 }
243 
244 /*
245  * Encodes terminal modes for the terminal referenced by fd
246  * or tiop in a portable manner, and appends the modes to a packet
247  * being constructed.
248  */
249 void
250 tty_make_modes(int fd, struct termios *tiop)
251 {
252 	struct termios tio;
253 	int baud;
254 	Buffer buf;
255 
256 	buffer_init(&buf);
257 
258 	if (tiop == NULL) {
259 		if (fd == -1) {
260 			debug("tty_make_modes: no fd or tio");
261 			goto end;
262 		}
263 		if (tcgetattr(fd, &tio) == -1) {
264 			logit("tcgetattr: %.100s", strerror(errno));
265 			goto end;
266 		}
267 	} else
268 		tio = *tiop;
269 
270 	/* Store input and output baud rates. */
271 	baud = speed_to_baud(cfgetospeed(&tio));
272 	buffer_put_char(&buf, TTY_OP_OSPEED);
273 	buffer_put_int(&buf, baud);
274 	baud = speed_to_baud(cfgetispeed(&tio));
275 	buffer_put_char(&buf, TTY_OP_ISPEED);
276 	buffer_put_int(&buf, baud);
277 
278 	/* Store values of mode flags. */
279 #define TTYCHAR(NAME, OP) \
280 	buffer_put_char(&buf, OP); \
281 	buffer_put_int(&buf, tio.c_cc[NAME]);
282 
283 #define TTYMODE(NAME, FIELD, OP) \
284 	buffer_put_char(&buf, OP); \
285 	buffer_put_int(&buf, ((tio.FIELD & NAME) != 0));
286 
287 #include "ttymodes.h"
288 
289 #undef TTYCHAR
290 #undef TTYMODE
291 
292 end:
293 	/* Mark end of mode data. */
294 	buffer_put_char(&buf, TTY_OP_END);
295 	packet_put_string(buffer_ptr(&buf), buffer_len(&buf));
296 	buffer_free(&buf);
297 }
298 
299 /*
300  * Decodes terminal modes for the terminal referenced by fd in a portable
301  * manner from a packet being read.
302  */
303 void
304 tty_parse_modes(int fd, int *n_bytes_ptr)
305 {
306 	struct termios tio;
307 	int opcode, baud;
308 	int n_bytes = 0;
309 	int failure = 0;
310 
311 	*n_bytes_ptr = packet_get_int();
312 	if (*n_bytes_ptr == 0)
313 		return;
314 
315 	/*
316 	 * Get old attributes for the terminal.  We will modify these
317 	 * flags. I am hoping that if there are any machine-specific
318 	 * modes, they will initially have reasonable values.
319 	 */
320 	if (tcgetattr(fd, &tio) == -1) {
321 		logit("tcgetattr: %.100s", strerror(errno));
322 		failure = -1;
323 	}
324 
325 	for (;;) {
326 		n_bytes += 1;
327 		opcode = packet_get_char();
328 		switch (opcode) {
329 		case TTY_OP_END:
330 			goto set;
331 
332 		case TTY_OP_ISPEED:
333 			n_bytes += 4;
334 			baud = packet_get_int();
335 			if (failure != -1 &&
336 			    cfsetispeed(&tio, baud_to_speed(baud)) == -1)
337 				error("cfsetispeed failed for %d", baud);
338 			break;
339 
340 		case TTY_OP_OSPEED:
341 			n_bytes += 4;
342 			baud = packet_get_int();
343 			if (failure != -1 &&
344 			    cfsetospeed(&tio, baud_to_speed(baud)) == -1)
345 				error("cfsetospeed failed for %d", baud);
346 			break;
347 
348 #define TTYCHAR(NAME, OP) \
349 	case OP: \
350 	  n_bytes += 4; \
351 	  tio.c_cc[NAME] = packet_get_int(); \
352 	  break;
353 #define TTYMODE(NAME, FIELD, OP) \
354 	case OP: \
355 	  n_bytes += 4; \
356 	  if (packet_get_int()) \
357 	    tio.FIELD |= NAME; \
358 	  else \
359 	    tio.FIELD &= ~NAME;	\
360 	  break;
361 
362 #include "ttymodes.h"
363 
364 #undef TTYCHAR
365 #undef TTYMODE
366 
367 		default:
368 			debug("Ignoring unsupported tty mode opcode %d (0x%x)",
369 			    opcode, opcode);
370 			/*
371 			 * SSH2:
372 			 * Opcodes 1 to 159 are defined to have a uint32
373 			 * argument.
374 			 * Opcodes 160 to 255 are undefined and cause parsing
375 			 * to stop.
376 			 */
377 			if (opcode > 0 && opcode < 160) {
378 				n_bytes += 4;
379 				(void) packet_get_int();
380 				break;
381 			} else {
382 				logit("parse_tty_modes: unknown opcode %d",
383 				    opcode);
384 				goto set;
385 			}
386 		}
387 	}
388 
389 set:
390 	if (*n_bytes_ptr != n_bytes) {
391 		*n_bytes_ptr = n_bytes;
392 		logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
393 		    *n_bytes_ptr, n_bytes);
394 		return;		/* Don't process bytes passed */
395 	}
396 	if (failure == -1)
397 		return;		/* Packet parsed ok but tcgetattr() failed */
398 
399 	/* Set the new modes for the terminal. */
400 	if (tcsetattr(fd, TCSANOW, &tio) == -1)
401 		logit("Setting tty modes failed: %.100s", strerror(errno));
402 }
403