xref: /freebsd/crypto/openssh/ttymodes.c (revision 511b41d2a18c00a5f0db1a67a87ef8d831b19f2a)
1511b41d2SMark Murray /*
2511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
3511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4511b41d2SMark Murray  *                    All rights reserved
5511b41d2SMark Murray  * Created: Tue Mar 21 15:59:15 1995 ylo
6511b41d2SMark Murray  * Encoding and decoding of terminal modes in a portable way.
7511b41d2SMark Murray  * Much of the format is defined in ttymodes.h; it is included multiple times
8511b41d2SMark Murray  * into this file with the appropriate macro definitions to generate the
9511b41d2SMark Murray  * suitable code.
10511b41d2SMark Murray  */
11511b41d2SMark Murray 
12511b41d2SMark Murray #include "includes.h"
13511b41d2SMark Murray RCSID("$Id: ttymodes.c,v 1.5 1999/11/24 19:53:54 markus Exp $");
14511b41d2SMark Murray 
15511b41d2SMark Murray #include "packet.h"
16511b41d2SMark Murray #include "ssh.h"
17511b41d2SMark Murray 
18511b41d2SMark Murray #define TTY_OP_END	0
19511b41d2SMark Murray #define TTY_OP_ISPEED	192	/* int follows */
20511b41d2SMark Murray #define TTY_OP_OSPEED	193	/* int follows */
21511b41d2SMark Murray 
22511b41d2SMark Murray /*
23511b41d2SMark Murray  * Converts POSIX speed_t to a baud rate.  The values of the
24511b41d2SMark Murray  * constants for speed_t are not themselves portable.
25511b41d2SMark Murray  */
26511b41d2SMark Murray static int
27511b41d2SMark Murray speed_to_baud(speed_t speed)
28511b41d2SMark Murray {
29511b41d2SMark Murray 	switch (speed) {
30511b41d2SMark Murray 	case B0:
31511b41d2SMark Murray 		return 0;
32511b41d2SMark Murray 	case B50:
33511b41d2SMark Murray 		return 50;
34511b41d2SMark Murray 	case B75:
35511b41d2SMark Murray 		return 75;
36511b41d2SMark Murray 	case B110:
37511b41d2SMark Murray 		return 110;
38511b41d2SMark Murray 	case B134:
39511b41d2SMark Murray 		return 134;
40511b41d2SMark Murray 	case B150:
41511b41d2SMark Murray 		return 150;
42511b41d2SMark Murray 	case B200:
43511b41d2SMark Murray 		return 200;
44511b41d2SMark Murray 	case B300:
45511b41d2SMark Murray 		return 300;
46511b41d2SMark Murray 	case B600:
47511b41d2SMark Murray 		return 600;
48511b41d2SMark Murray 	case B1200:
49511b41d2SMark Murray 		return 1200;
50511b41d2SMark Murray 	case B1800:
51511b41d2SMark Murray 		return 1800;
52511b41d2SMark Murray 	case B2400:
53511b41d2SMark Murray 		return 2400;
54511b41d2SMark Murray 	case B4800:
55511b41d2SMark Murray 		return 4800;
56511b41d2SMark Murray 	case B9600:
57511b41d2SMark Murray 		return 9600;
58511b41d2SMark Murray 
59511b41d2SMark Murray #ifdef B19200
60511b41d2SMark Murray 	case B19200:
61511b41d2SMark Murray 		return 19200;
62511b41d2SMark Murray #else /* B19200 */
63511b41d2SMark Murray #ifdef EXTA
64511b41d2SMark Murray 	case EXTA:
65511b41d2SMark Murray 		return 19200;
66511b41d2SMark Murray #endif /* EXTA */
67511b41d2SMark Murray #endif /* B19200 */
68511b41d2SMark Murray 
69511b41d2SMark Murray #ifdef B38400
70511b41d2SMark Murray 	case B38400:
71511b41d2SMark Murray 		return 38400;
72511b41d2SMark Murray #else /* B38400 */
73511b41d2SMark Murray #ifdef EXTB
74511b41d2SMark Murray 	case EXTB:
75511b41d2SMark Murray 		return 38400;
76511b41d2SMark Murray #endif /* EXTB */
77511b41d2SMark Murray #endif /* B38400 */
78511b41d2SMark Murray 
79511b41d2SMark Murray #ifdef B7200
80511b41d2SMark Murray 	case B7200:
81511b41d2SMark Murray 		return 7200;
82511b41d2SMark Murray #endif /* B7200 */
83511b41d2SMark Murray #ifdef B14400
84511b41d2SMark Murray 	case B14400:
85511b41d2SMark Murray 		return 14400;
86511b41d2SMark Murray #endif /* B14400 */
87511b41d2SMark Murray #ifdef B28800
88511b41d2SMark Murray 	case B28800:
89511b41d2SMark Murray 		return 28800;
90511b41d2SMark Murray #endif /* B28800 */
91511b41d2SMark Murray #ifdef B57600
92511b41d2SMark Murray 	case B57600:
93511b41d2SMark Murray 		return 57600;
94511b41d2SMark Murray #endif /* B57600 */
95511b41d2SMark Murray #ifdef B76800
96511b41d2SMark Murray 	case B76800:
97511b41d2SMark Murray 		return 76800;
98511b41d2SMark Murray #endif /* B76800 */
99511b41d2SMark Murray #ifdef B115200
100511b41d2SMark Murray 	case B115200:
101511b41d2SMark Murray 		return 115200;
102511b41d2SMark Murray #endif /* B115200 */
103511b41d2SMark Murray #ifdef B230400
104511b41d2SMark Murray 	case B230400:
105511b41d2SMark Murray 		return 230400;
106511b41d2SMark Murray #endif /* B230400 */
107511b41d2SMark Murray 	default:
108511b41d2SMark Murray 		return 9600;
109511b41d2SMark Murray 	}
110511b41d2SMark Murray }
111511b41d2SMark Murray 
112511b41d2SMark Murray /*
113511b41d2SMark Murray  * Converts a numeric baud rate to a POSIX speed_t.
114511b41d2SMark Murray  */
115511b41d2SMark Murray static speed_t
116511b41d2SMark Murray baud_to_speed(int baud)
117511b41d2SMark Murray {
118511b41d2SMark Murray 	switch (baud) {
119511b41d2SMark Murray 		case 0:
120511b41d2SMark Murray 		return B0;
121511b41d2SMark Murray 	case 50:
122511b41d2SMark Murray 		return B50;
123511b41d2SMark Murray 	case 75:
124511b41d2SMark Murray 		return B75;
125511b41d2SMark Murray 	case 110:
126511b41d2SMark Murray 		return B110;
127511b41d2SMark Murray 	case 134:
128511b41d2SMark Murray 		return B134;
129511b41d2SMark Murray 	case 150:
130511b41d2SMark Murray 		return B150;
131511b41d2SMark Murray 	case 200:
132511b41d2SMark Murray 		return B200;
133511b41d2SMark Murray 	case 300:
134511b41d2SMark Murray 		return B300;
135511b41d2SMark Murray 	case 600:
136511b41d2SMark Murray 		return B600;
137511b41d2SMark Murray 	case 1200:
138511b41d2SMark Murray 		return B1200;
139511b41d2SMark Murray 	case 1800:
140511b41d2SMark Murray 		return B1800;
141511b41d2SMark Murray 	case 2400:
142511b41d2SMark Murray 		return B2400;
143511b41d2SMark Murray 	case 4800:
144511b41d2SMark Murray 		return B4800;
145511b41d2SMark Murray 	case 9600:
146511b41d2SMark Murray 		return B9600;
147511b41d2SMark Murray 
148511b41d2SMark Murray #ifdef B19200
149511b41d2SMark Murray 	case 19200:
150511b41d2SMark Murray 		return B19200;
151511b41d2SMark Murray #else /* B19200 */
152511b41d2SMark Murray #ifdef EXTA
153511b41d2SMark Murray 	case 19200:
154511b41d2SMark Murray 		return EXTA;
155511b41d2SMark Murray #endif /* EXTA */
156511b41d2SMark Murray #endif /* B19200 */
157511b41d2SMark Murray 
158511b41d2SMark Murray #ifdef B38400
159511b41d2SMark Murray 	case 38400:
160511b41d2SMark Murray 		return B38400;
161511b41d2SMark Murray #else /* B38400 */
162511b41d2SMark Murray #ifdef EXTB
163511b41d2SMark Murray 	case 38400:
164511b41d2SMark Murray 		return EXTB;
165511b41d2SMark Murray #endif /* EXTB */
166511b41d2SMark Murray #endif /* B38400 */
167511b41d2SMark Murray 
168511b41d2SMark Murray #ifdef B7200
169511b41d2SMark Murray 	case 7200:
170511b41d2SMark Murray 		return B7200;
171511b41d2SMark Murray #endif /* B7200 */
172511b41d2SMark Murray #ifdef B14400
173511b41d2SMark Murray 	case 14400:
174511b41d2SMark Murray 		return B14400;
175511b41d2SMark Murray #endif /* B14400 */
176511b41d2SMark Murray #ifdef B28800
177511b41d2SMark Murray 	case 28800:
178511b41d2SMark Murray 		return B28800;
179511b41d2SMark Murray #endif /* B28800 */
180511b41d2SMark Murray #ifdef B57600
181511b41d2SMark Murray 	case 57600:
182511b41d2SMark Murray 		return B57600;
183511b41d2SMark Murray #endif /* B57600 */
184511b41d2SMark Murray #ifdef B76800
185511b41d2SMark Murray 	case 76800:
186511b41d2SMark Murray 		return B76800;
187511b41d2SMark Murray #endif /* B76800 */
188511b41d2SMark Murray #ifdef B115200
189511b41d2SMark Murray 	case 115200:
190511b41d2SMark Murray 		return B115200;
191511b41d2SMark Murray #endif /* B115200 */
192511b41d2SMark Murray #ifdef B230400
193511b41d2SMark Murray 	case 230400:
194511b41d2SMark Murray 		return B230400;
195511b41d2SMark Murray #endif /* B230400 */
196511b41d2SMark Murray 	default:
197511b41d2SMark Murray 		return B9600;
198511b41d2SMark Murray 	}
199511b41d2SMark Murray }
200511b41d2SMark Murray 
201511b41d2SMark Murray /*
202511b41d2SMark Murray  * Encodes terminal modes for the terminal referenced by fd
203511b41d2SMark Murray  * in a portable manner, and appends the modes to a packet
204511b41d2SMark Murray  * being constructed.
205511b41d2SMark Murray  */
206511b41d2SMark Murray void
207511b41d2SMark Murray tty_make_modes(int fd)
208511b41d2SMark Murray {
209511b41d2SMark Murray 	struct termios tio;
210511b41d2SMark Murray 	int baud;
211511b41d2SMark Murray 
212511b41d2SMark Murray 	if (tcgetattr(fd, &tio) < 0) {
213511b41d2SMark Murray 		packet_put_char(TTY_OP_END);
214511b41d2SMark Murray 		log("tcgetattr: %.100s", strerror(errno));
215511b41d2SMark Murray 		return;
216511b41d2SMark Murray 	}
217511b41d2SMark Murray 	/* Store input and output baud rates. */
218511b41d2SMark Murray 	baud = speed_to_baud(cfgetospeed(&tio));
219511b41d2SMark Murray 	packet_put_char(TTY_OP_OSPEED);
220511b41d2SMark Murray 	packet_put_int(baud);
221511b41d2SMark Murray 	baud = speed_to_baud(cfgetispeed(&tio));
222511b41d2SMark Murray 	packet_put_char(TTY_OP_ISPEED);
223511b41d2SMark Murray 	packet_put_int(baud);
224511b41d2SMark Murray 
225511b41d2SMark Murray 	/* Store values of mode flags. */
226511b41d2SMark Murray #define TTYCHAR(NAME, OP) \
227511b41d2SMark Murray   packet_put_char(OP); packet_put_char(tio.c_cc[NAME]);
228511b41d2SMark Murray #define TTYMODE(NAME, FIELD, OP) \
229511b41d2SMark Murray   packet_put_char(OP); packet_put_char((tio.FIELD & NAME) != 0);
230511b41d2SMark Murray #define SGTTYCHAR(NAME, OP)
231511b41d2SMark Murray #define SGTTYMODE(NAME, FIELD, OP)
232511b41d2SMark Murray #define SGTTYMODEN(NAME, FIELD, OP)
233511b41d2SMark Murray 
234511b41d2SMark Murray #include "ttymodes.h"
235511b41d2SMark Murray 
236511b41d2SMark Murray #undef TTYCHAR
237511b41d2SMark Murray #undef TTYMODE
238511b41d2SMark Murray #undef SGTTYCHAR
239511b41d2SMark Murray #undef SGTTYMODE
240511b41d2SMark Murray #undef SGTTYMODEN
241511b41d2SMark Murray 
242511b41d2SMark Murray 	/* Mark end of mode data. */
243511b41d2SMark Murray 	packet_put_char(TTY_OP_END);
244511b41d2SMark Murray }
245511b41d2SMark Murray 
246511b41d2SMark Murray /*
247511b41d2SMark Murray  * Decodes terminal modes for the terminal referenced by fd in a portable
248511b41d2SMark Murray  * manner from a packet being read.
249511b41d2SMark Murray  */
250511b41d2SMark Murray void
251511b41d2SMark Murray tty_parse_modes(int fd, int *n_bytes_ptr)
252511b41d2SMark Murray {
253511b41d2SMark Murray 	struct termios tio;
254511b41d2SMark Murray 	int opcode, baud;
255511b41d2SMark Murray 	int n_bytes = 0;
256511b41d2SMark Murray 	int failure = 0;
257511b41d2SMark Murray 
258511b41d2SMark Murray 	/*
259511b41d2SMark Murray 	 * Get old attributes for the terminal.  We will modify these
260511b41d2SMark Murray 	 * flags. I am hoping that if there are any machine-specific
261511b41d2SMark Murray 	 * modes, they will initially have reasonable values.
262511b41d2SMark Murray 	 */
263511b41d2SMark Murray 	if (tcgetattr(fd, &tio) < 0)
264511b41d2SMark Murray 		failure = -1;
265511b41d2SMark Murray 
266511b41d2SMark Murray 	for (;;) {
267511b41d2SMark Murray 		n_bytes += 1;
268511b41d2SMark Murray 		opcode = packet_get_char();
269511b41d2SMark Murray 		switch (opcode) {
270511b41d2SMark Murray 		case TTY_OP_END:
271511b41d2SMark Murray 			goto set;
272511b41d2SMark Murray 
273511b41d2SMark Murray 		case TTY_OP_ISPEED:
274511b41d2SMark Murray 			n_bytes += 4;
275511b41d2SMark Murray 			baud = packet_get_int();
276511b41d2SMark Murray 			if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0)
277511b41d2SMark Murray 				error("cfsetispeed failed for %d", baud);
278511b41d2SMark Murray 			break;
279511b41d2SMark Murray 
280511b41d2SMark Murray 		case TTY_OP_OSPEED:
281511b41d2SMark Murray 			n_bytes += 4;
282511b41d2SMark Murray 			baud = packet_get_int();
283511b41d2SMark Murray 			if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0)
284511b41d2SMark Murray 				error("cfsetospeed failed for %d", baud);
285511b41d2SMark Murray 			break;
286511b41d2SMark Murray 
287511b41d2SMark Murray #define TTYCHAR(NAME, OP) 				\
288511b41d2SMark Murray 	case OP:					\
289511b41d2SMark Murray 	  n_bytes += 1;					\
290511b41d2SMark Murray 	  tio.c_cc[NAME] = packet_get_char();		\
291511b41d2SMark Murray 	  break;
292511b41d2SMark Murray #define TTYMODE(NAME, FIELD, OP)		       	\
293511b41d2SMark Murray 	case OP:					\
294511b41d2SMark Murray 	  n_bytes += 1;					\
295511b41d2SMark Murray 	  if (packet_get_char())			\
296511b41d2SMark Murray 	    tio.FIELD |= NAME;				\
297511b41d2SMark Murray 	  else						\
298511b41d2SMark Murray 	    tio.FIELD &= ~NAME;				\
299511b41d2SMark Murray 	  break;
300511b41d2SMark Murray #define SGTTYCHAR(NAME, OP)
301511b41d2SMark Murray #define SGTTYMODE(NAME, FIELD, OP)
302511b41d2SMark Murray #define SGTTYMODEN(NAME, FIELD, OP)
303511b41d2SMark Murray 
304511b41d2SMark Murray #include "ttymodes.h"
305511b41d2SMark Murray 
306511b41d2SMark Murray #undef TTYCHAR
307511b41d2SMark Murray #undef TTYMODE
308511b41d2SMark Murray #undef SGTTYCHAR
309511b41d2SMark Murray #undef SGTTYMODE
310511b41d2SMark Murray #undef SGTTYMODEN
311511b41d2SMark Murray 
312511b41d2SMark Murray 		default:
313511b41d2SMark Murray 			debug("Ignoring unsupported tty mode opcode %d (0x%x)",
314511b41d2SMark Murray 			      opcode, opcode);
315511b41d2SMark Murray 			/*
316511b41d2SMark Murray 			 * Opcodes 0 to 127 are defined to have
317511b41d2SMark Murray 			 * a one-byte argument.
318511b41d2SMark Murray 			 */
319511b41d2SMark Murray 			if (opcode >= 0 && opcode < 128) {
320511b41d2SMark Murray 				n_bytes += 1;
321511b41d2SMark Murray 				(void) packet_get_char();
322511b41d2SMark Murray 				break;
323511b41d2SMark Murray 			} else {
324511b41d2SMark Murray 				/*
325511b41d2SMark Murray 				 * Opcodes 128 to 159 are defined to have
326511b41d2SMark Murray 				 * an integer argument.
327511b41d2SMark Murray 				 */
328511b41d2SMark Murray 				if (opcode >= 128 && opcode < 160) {
329511b41d2SMark Murray 					n_bytes += 4;
330511b41d2SMark Murray 					(void) packet_get_int();
331511b41d2SMark Murray 					break;
332511b41d2SMark Murray 				}
333511b41d2SMark Murray 			}
334511b41d2SMark Murray 			/*
335511b41d2SMark Murray 			 * It is a truly undefined opcode (160 to 255).
336511b41d2SMark Murray 			 * We have no idea about its arguments.  So we
337511b41d2SMark Murray 			 * must stop parsing.  Note that some data may be
338511b41d2SMark Murray 			 * left in the packet; hopefully there is nothing
339511b41d2SMark Murray 			 * more coming after the mode data.
340511b41d2SMark Murray 			 */
341511b41d2SMark Murray 			log("parse_tty_modes: unknown opcode %d", opcode);
342511b41d2SMark Murray 			packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY);
343511b41d2SMark Murray 			goto set;
344511b41d2SMark Murray 		}
345511b41d2SMark Murray 	}
346511b41d2SMark Murray 
347511b41d2SMark Murray set:
348511b41d2SMark Murray 	if (*n_bytes_ptr != n_bytes) {
349511b41d2SMark Murray 		*n_bytes_ptr = n_bytes;
350511b41d2SMark Murray 		return;		/* Don't process bytes passed */
351511b41d2SMark Murray 	}
352511b41d2SMark Murray 	if (failure == -1)
353511b41d2SMark Murray 		return;		/* Packet parsed ok but tty stuff failed */
354511b41d2SMark Murray 
355511b41d2SMark Murray 	/* Set the new modes for the terminal. */
356511b41d2SMark Murray 	if (tcsetattr(fd, TCSANOW, &tio) < 0)
357511b41d2SMark Murray 		log("Setting tty modes failed: %.100s", strerror(errno));
358511b41d2SMark Murray 	return;
359511b41d2SMark Murray }
360