xref: /original-bsd/libexec/telnetd/termstat.c (revision de436421)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)termstat.c	8.2 (Berkeley) 05/30/95";
10 #endif /* not lint */
11 
12 #include "telnetd.h"
13 
14 /*
15  * local variables
16  */
17 int def_tspeed = -1, def_rspeed = -1;
18 #ifdef	TIOCSWINSZ
19 int def_row = 0, def_col = 0;
20 #endif
21 #ifdef	LINEMODE
22 static int _terminit = 0;
23 #endif	/* LINEMODE */
24 
25 #if	defined(CRAY2) && defined(UNICOS5)
26 int	newmap = 1;	/* nonzero if \n maps to ^M^J */
27 #endif
28 
29 #ifdef	LINEMODE
30 /*
31  * localstat
32  *
33  * This function handles all management of linemode.
34  *
35  * Linemode allows the client to do the local editing of data
36  * and send only complete lines to the server.  Linemode state is
37  * based on the state of the pty driver.  If the pty is set for
38  * external processing, then we can use linemode.  Further, if we
39  * can use real linemode, then we can look at the edit control bits
40  * in the pty to determine what editing the client should do.
41  *
42  * Linemode support uses the following state flags to keep track of
43  * current and desired linemode state.
44  *	alwayslinemode : true if -l was specified on the telnetd
45  * 	command line.  It means to have linemode on as much as
46  *	possible.
47  *
48  * 	lmodetype: signifies whether the client can
49  *	handle real linemode, or if use of kludgeomatic linemode
50  *	is preferred.  It will be set to one of the following:
51  *		REAL_LINEMODE : use linemode option
52  *		NO_KLUDGE : don't initiate kludge linemode.
53  *		KLUDGE_LINEMODE : use kludge linemode
54  *		NO_LINEMODE : client is ignorant of linemode
55  *
56  *	linemode, uselinemode : linemode is true if linemode
57  *	is currently on, uselinemode is the state that we wish
58  *	to be in.  If another function wishes to turn linemode
59  *	on or off, it sets or clears uselinemode.
60  *
61  *	editmode, useeditmode : like linemode/uselinemode, but
62  *	these contain the edit mode states (edit and trapsig).
63  *
64  * The state variables correspond to some of the state information
65  * in the pty.
66  *	linemode:
67  *		In real linemode, this corresponds to whether the pty
68  *		expects external processing of incoming data.
69  *		In kludge linemode, this more closely corresponds to the
70  *		whether normal processing is on or not.  (ICANON in
71  *		system V, or COOKED mode in BSD.)
72  *		If the -l option was specified (alwayslinemode), then
73  *		an attempt is made to force external processing on at
74  *		all times.
75  *
76  * The following heuristics are applied to determine linemode
77  * handling within the server.
78  *	1) Early on in starting up the server, an attempt is made
79  *	   to negotiate the linemode option.  If this succeeds
80  *	   then lmodetype is set to REAL_LINEMODE and all linemode
81  *	   processing occurs in the context of the linemode option.
82  *	2) If the attempt to negotiate the linemode option failed,
83  *	   and the "-k" (don't initiate kludge linemode) isn't set,
84  *	   then we try to use kludge linemode.  We test for this
85  *	   capability by sending "do Timing Mark".  If a positive
86  *	   response comes back, then we assume that the client
87  *	   understands kludge linemode (ech!) and the
88  *	   lmodetype flag is set to KLUDGE_LINEMODE.
89  *	3) Otherwise, linemode is not supported at all and
90  *	   lmodetype remains set to NO_LINEMODE (which happens
91  *	   to be 0 for convenience).
92  *	4) At any time a command arrives that implies a higher
93  *	   state of linemode support in the client, we move to that
94  *	   linemode support.
95  *
96  * A short explanation of kludge linemode is in order here.
97  *	1) The heuristic to determine support for kludge linemode
98  *	   is to send a do timing mark.  We assume that a client
99  *	   that supports timing marks also supports kludge linemode.
100  *	   A risky proposition at best.
101  *	2) Further negotiation of linemode is done by changing the
102  *	   the server's state regarding SGA.  If server will SGA,
103  *	   then linemode is off, if server won't SGA, then linemode
104  *	   is on.
105  */
106 	void
localstat()107 localstat()
108 {
109 	void netflush();
110 	int need_will_echo = 0;
111 
112 #if	defined(CRAY2) && defined(UNICOS5)
113 	/*
114 	 * Keep track of that ol' CR/NL mapping while we're in the
115 	 * neighborhood.
116 	 */
117 	newmap = tty_isnewmap();
118 #endif	/* defined(CRAY2) && defined(UNICOS5) */
119 
120 	/*
121 	 * Check for state of BINARY options.
122 	 */
123 	if (tty_isbinaryin()) {
124 		if (his_want_state_is_wont(TELOPT_BINARY))
125 			send_do(TELOPT_BINARY, 1);
126 	} else {
127 		if (his_want_state_is_will(TELOPT_BINARY))
128 			send_dont(TELOPT_BINARY, 1);
129 	}
130 
131 	if (tty_isbinaryout()) {
132 		if (my_want_state_is_wont(TELOPT_BINARY))
133 			send_will(TELOPT_BINARY, 1);
134 	} else {
135 		if (my_want_state_is_will(TELOPT_BINARY))
136 			send_wont(TELOPT_BINARY, 1);
137 	}
138 
139 	/*
140 	 * Check for changes to flow control if client supports it.
141 	 */
142 	flowstat();
143 
144 	/*
145 	 * Check linemode on/off state
146 	 */
147 	uselinemode = tty_linemode();
148 
149 	/*
150 	 * If alwayslinemode is on, and pty is changing to turn it off, then
151 	 * force linemode back on.
152 	 */
153 	if (alwayslinemode && linemode && !uselinemode) {
154 		uselinemode = 1;
155 		tty_setlinemode(uselinemode);
156 	}
157 
158 #ifdef	ENCRYPTION
159 	/*
160 	 * If the terminal is not echoing, but editing is enabled,
161 	 * something like password input is going to happen, so
162 	 * if we the other side is not currently sending encrypted
163 	 * data, ask the other side to start encrypting.
164 	 */
165 	if (his_state_is_will(TELOPT_ENCRYPT)) {
166 		static int enc_passwd = 0;
167 		if (uselinemode && !tty_isecho() && tty_isediting()
168 		    && (enc_passwd == 0) && !decrypt_input) {
169 			encrypt_send_request_start();
170 			enc_passwd = 1;
171 		} else if (enc_passwd) {
172 			encrypt_send_request_end();
173 			enc_passwd = 0;
174 		}
175 	}
176 #endif	/* ENCRYPTION */
177 
178 	/*
179 	 * Do echo mode handling as soon as we know what the
180 	 * linemode is going to be.
181 	 * If the pty has echo turned off, then tell the client that
182 	 * the server will echo.  If echo is on, then the server
183 	 * will echo if in character mode, but in linemode the
184 	 * client should do local echoing.  The state machine will
185 	 * not send anything if it is unnecessary, so don't worry
186 	 * about that here.
187 	 *
188 	 * If we need to send the WILL ECHO (because echo is off),
189 	 * then delay that until after we have changed the MODE.
190 	 * This way, when the user is turning off both editing
191 	 * and echo, the client will get editing turned off first.
192 	 * This keeps the client from going into encryption mode
193 	 * and then right back out if it is doing auto-encryption
194 	 * when passwords are being typed.
195 	 */
196 	if (uselinemode) {
197 		if (tty_isecho())
198 			send_wont(TELOPT_ECHO, 1);
199 		else
200 			need_will_echo = 1;
201 #ifdef	KLUDGELINEMODE
202 		if (lmodetype == KLUDGE_OK)
203 			lmodetype = KLUDGE_LINEMODE;
204 #endif
205 	}
206 
207 	/*
208 	 * If linemode is being turned off, send appropriate
209 	 * command and then we're all done.
210 	 */
211 	 if (!uselinemode && linemode) {
212 # ifdef	KLUDGELINEMODE
213 		if (lmodetype == REAL_LINEMODE) {
214 # endif	/* KLUDGELINEMODE */
215 			send_dont(TELOPT_LINEMODE, 1);
216 # ifdef	KLUDGELINEMODE
217 		} else if (lmodetype == KLUDGE_LINEMODE)
218 			send_will(TELOPT_SGA, 1);
219 # endif	/* KLUDGELINEMODE */
220 		send_will(TELOPT_ECHO, 1);
221 		linemode = uselinemode;
222 		goto done;
223 	}
224 
225 # ifdef	KLUDGELINEMODE
226 	/*
227 	 * If using real linemode check edit modes for possible later use.
228 	 * If we are in kludge linemode, do the SGA negotiation.
229 	 */
230 	if (lmodetype == REAL_LINEMODE) {
231 # endif	/* KLUDGELINEMODE */
232 		useeditmode = 0;
233 		if (tty_isediting())
234 			useeditmode |= MODE_EDIT;
235 		if (tty_istrapsig())
236 			useeditmode |= MODE_TRAPSIG;
237 		if (tty_issofttab())
238 			useeditmode |= MODE_SOFT_TAB;
239 		if (tty_islitecho())
240 			useeditmode |= MODE_LIT_ECHO;
241 # ifdef	KLUDGELINEMODE
242 	} else if (lmodetype == KLUDGE_LINEMODE) {
243 		if (tty_isediting() && uselinemode)
244 			send_wont(TELOPT_SGA, 1);
245 		else
246 			send_will(TELOPT_SGA, 1);
247 	}
248 # endif	/* KLUDGELINEMODE */
249 
250 	/*
251 	 * Negotiate linemode on if pty state has changed to turn it on.
252 	 * Send appropriate command and send along edit mode, then all done.
253 	 */
254 	if (uselinemode && !linemode) {
255 # ifdef	KLUDGELINEMODE
256 		if (lmodetype == KLUDGE_LINEMODE) {
257 			send_wont(TELOPT_SGA, 1);
258 		} else if (lmodetype == REAL_LINEMODE) {
259 # endif	/* KLUDGELINEMODE */
260 			send_do(TELOPT_LINEMODE, 1);
261 			/* send along edit modes */
262 			(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
263 				TELOPT_LINEMODE, LM_MODE, useeditmode,
264 				IAC, SE);
265 			nfrontp += 7;
266 			editmode = useeditmode;
267 # ifdef	KLUDGELINEMODE
268 		}
269 # endif	/* KLUDGELINEMODE */
270 		linemode = uselinemode;
271 		goto done;
272 	}
273 
274 # ifdef	KLUDGELINEMODE
275 	/*
276 	 * None of what follows is of any value if not using
277 	 * real linemode.
278 	 */
279 	if (lmodetype < REAL_LINEMODE)
280 		goto done;
281 # endif	/* KLUDGELINEMODE */
282 
283 	if (linemode && his_state_is_will(TELOPT_LINEMODE)) {
284 		/*
285 		 * If edit mode changed, send edit mode.
286 		 */
287 		 if (useeditmode != editmode) {
288 			/*
289 			 * Send along appropriate edit mode mask.
290 			 */
291 			(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
292 				TELOPT_LINEMODE, LM_MODE, useeditmode,
293 				IAC, SE);
294 			nfrontp += 7;
295 			editmode = useeditmode;
296 		}
297 
298 
299 		/*
300 		 * Check for changes to special characters in use.
301 		 */
302 		start_slc(0);
303 		check_slc();
304 		(void) end_slc(0);
305 	}
306 
307 done:
308 	if (need_will_echo)
309 		send_will(TELOPT_ECHO, 1);
310 	/*
311 	 * Some things should be deferred until after the pty state has
312 	 * been set by the local process.  Do those things that have been
313 	 * deferred now.  This only happens once.
314 	 */
315 	if (_terminit == 0) {
316 		_terminit = 1;
317 		defer_terminit();
318 	}
319 
320 	netflush();
321 	set_termbuf();
322 	return;
323 
324 }  /* end of localstat */
325 #endif	/* LINEMODE */
326 
327 /*
328  * flowstat
329  *
330  * Check for changes to flow control
331  */
332 	void
flowstat()333 flowstat()
334 {
335 	if (his_state_is_will(TELOPT_LFLOW)) {
336 		if (tty_flowmode() != flowmode) {
337 			flowmode = tty_flowmode();
338 			(void) sprintf(nfrontp, "%c%c%c%c%c%c",
339 					IAC, SB, TELOPT_LFLOW,
340 					flowmode ? LFLOW_ON : LFLOW_OFF,
341 					IAC, SE);
342 			nfrontp += 6;
343 		}
344 		if (tty_restartany() != restartany) {
345 			restartany = tty_restartany();
346 			(void) sprintf(nfrontp, "%c%c%c%c%c%c",
347 					IAC, SB, TELOPT_LFLOW,
348 					restartany ? LFLOW_RESTART_ANY
349 						   : LFLOW_RESTART_XON,
350 					IAC, SE);
351 			nfrontp += 6;
352 		}
353 	}
354 }
355 
356 /*
357  * clientstat
358  *
359  * Process linemode related requests from the client.
360  * Client can request a change to only one of linemode, editmode or slc's
361  * at a time, and if using kludge linemode, then only linemode may be
362  * affected.
363  */
364 	void
clientstat(code,parm1,parm2)365 clientstat(code, parm1, parm2)
366 	register int code, parm1, parm2;
367 {
368 	void netflush();
369 
370 	/*
371 	 * Get a copy of terminal characteristics.
372 	 */
373 	init_termbuf();
374 
375 	/*
376 	 * Process request from client. code tells what it is.
377 	 */
378 	switch (code) {
379 #ifdef	LINEMODE
380 	case TELOPT_LINEMODE:
381 		/*
382 		 * Don't do anything unless client is asking us to change
383 		 * modes.
384 		 */
385 		uselinemode = (parm1 == WILL);
386 		if (uselinemode != linemode) {
387 # ifdef	KLUDGELINEMODE
388 			/*
389 			 * If using kludge linemode, make sure that
390 			 * we can do what the client asks.
391 			 * We can not turn off linemode if alwayslinemode
392 			 * and the ICANON bit is set.
393 			 */
394 			if (lmodetype == KLUDGE_LINEMODE) {
395 				if (alwayslinemode && tty_isediting()) {
396 					uselinemode = 1;
397 				}
398 			}
399 
400 			/*
401 			 * Quit now if we can't do it.
402 			 */
403 			if (uselinemode == linemode)
404 				return;
405 
406 			/*
407 			 * If using real linemode and linemode is being
408 			 * turned on, send along the edit mode mask.
409 			 */
410 			if (lmodetype == REAL_LINEMODE && uselinemode)
411 # else	/* KLUDGELINEMODE */
412 			if (uselinemode)
413 # endif	/* KLUDGELINEMODE */
414 			{
415 				useeditmode = 0;
416 				if (tty_isediting())
417 					useeditmode |= MODE_EDIT;
418 				if (tty_istrapsig)
419 					useeditmode |= MODE_TRAPSIG;
420 				if (tty_issofttab())
421 					useeditmode |= MODE_SOFT_TAB;
422 				if (tty_islitecho())
423 					useeditmode |= MODE_LIT_ECHO;
424 				(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
425 					SB, TELOPT_LINEMODE, LM_MODE,
426 							useeditmode, IAC, SE);
427 				nfrontp += 7;
428 				editmode = useeditmode;
429 			}
430 
431 
432 			tty_setlinemode(uselinemode);
433 
434 			linemode = uselinemode;
435 
436 			if (!linemode)
437 				send_will(TELOPT_ECHO, 1);
438 		}
439 		break;
440 
441 	case LM_MODE:
442 	    {
443 		register int ack, changed;
444 
445 		/*
446 		 * Client has sent along a mode mask.  If it agrees with
447 		 * what we are currently doing, ignore it; if not, it could
448 		 * be viewed as a request to change.  Note that the server
449 		 * will change to the modes in an ack if it is different from
450 		 * what we currently have, but we will not ack the ack.
451 		 */
452 		 useeditmode &= MODE_MASK;
453 		 ack = (useeditmode & MODE_ACK);
454 		 useeditmode &= ~MODE_ACK;
455 
456 		 if (changed = (useeditmode ^ editmode)) {
457 			/*
458 			 * This check is for a timing problem.  If the
459 			 * state of the tty has changed (due to the user
460 			 * application) we need to process that info
461 			 * before we write in the state contained in the
462 			 * ack!!!  This gets out the new MODE request,
463 			 * and when the ack to that command comes back
464 			 * we'll set it and be in the right mode.
465 			 */
466 			if (ack)
467 				localstat();
468 			if (changed & MODE_EDIT)
469 				tty_setedit(useeditmode & MODE_EDIT);
470 
471 			if (changed & MODE_TRAPSIG)
472 				tty_setsig(useeditmode & MODE_TRAPSIG);
473 
474 			if (changed & MODE_SOFT_TAB)
475 				tty_setsofttab(useeditmode & MODE_SOFT_TAB);
476 
477 			if (changed & MODE_LIT_ECHO)
478 				tty_setlitecho(useeditmode & MODE_LIT_ECHO);
479 
480 			set_termbuf();
481 
482  			if (!ack) {
483  				(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
484 					SB, TELOPT_LINEMODE, LM_MODE,
485  					useeditmode|MODE_ACK,
486  					IAC, SE);
487  				nfrontp += 7;
488  			}
489 
490 			editmode = useeditmode;
491 		}
492 
493 		break;
494 
495 	    }  /* end of case LM_MODE */
496 #endif	/* LINEMODE */
497 
498 	case TELOPT_NAWS:
499 #ifdef	TIOCSWINSZ
500 	    {
501 		struct winsize ws;
502 
503 		def_col = parm1;
504 		def_row = parm2;
505 #ifdef	LINEMODE
506 		/*
507 		 * Defer changing window size until after terminal is
508 		 * initialized.
509 		 */
510 		if (terminit() == 0)
511 			return;
512 #endif	/* LINEMODE */
513 
514 		/*
515 		 * Change window size as requested by client.
516 		 */
517 
518 		ws.ws_col = parm1;
519 		ws.ws_row = parm2;
520 		(void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
521 	    }
522 #endif	/* TIOCSWINSZ */
523 
524 		break;
525 
526 	case TELOPT_TSPEED:
527 	    {
528 		def_tspeed = parm1;
529 		def_rspeed = parm2;
530 #ifdef	LINEMODE
531 		/*
532 		 * Defer changing the terminal speed.
533 		 */
534 		if (terminit() == 0)
535 			return;
536 #endif	/* LINEMODE */
537 		/*
538 		 * Change terminal speed as requested by client.
539 		 * We set the receive speed first, so that if we can't
540 		 * store seperate receive and transmit speeds, the transmit
541 		 * speed will take precedence.
542 		 */
543 		tty_rspeed(parm2);
544 		tty_tspeed(parm1);
545 		set_termbuf();
546 
547 		break;
548 
549 	    }  /* end of case TELOPT_TSPEED */
550 
551 	default:
552 		/* What? */
553 		break;
554 	}  /* end of switch */
555 
556 #if	defined(CRAY2) && defined(UNICOS5)
557 	/*
558 	 * Just in case of the likely event that we changed the pty state.
559 	 */
560 	rcv_ioctl();
561 #endif	/* defined(CRAY2) && defined(UNICOS5) */
562 
563 	netflush();
564 
565 }  /* end of clientstat */
566 
567 #if	defined(CRAY2) && defined(UNICOS5)
568 	void
termstat()569 termstat()
570 {
571 	needtermstat = 1;
572 }
573 
574 	void
_termstat()575 _termstat()
576 {
577 	needtermstat = 0;
578 	init_termbuf();
579 	localstat();
580 	rcv_ioctl();
581 }
582 #endif	/* defined(CRAY2) && defined(UNICOS5) */
583 
584 #ifdef	LINEMODE
585 /*
586  * defer_terminit
587  *
588  * Some things should not be done until after the login process has started
589  * and all the pty modes are set to what they are supposed to be.  This
590  * function is called when the pty state has been processed for the first time.
591  * It calls other functions that do things that were deferred in each module.
592  */
593 	void
defer_terminit()594 defer_terminit()
595 {
596 
597 	/*
598 	 * local stuff that got deferred.
599 	 */
600 	if (def_tspeed != -1) {
601 		clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed);
602 		def_tspeed = def_rspeed = 0;
603 	}
604 
605 #ifdef	TIOCSWINSZ
606 	if (def_col || def_row) {
607 		struct winsize ws;
608 
609 		memset((char *)&ws, 0, sizeof(ws));
610 		ws.ws_col = def_col;
611 		ws.ws_row = def_row;
612 		(void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
613 	}
614 #endif
615 
616 	/*
617 	 * The only other module that currently defers anything.
618 	 */
619 	deferslc();
620 
621 }  /* end of defer_terminit */
622 
623 /*
624  * terminit
625  *
626  * Returns true if the pty state has been processed yet.
627  */
628 	int
terminit()629 terminit()
630 {
631 	return(_terminit);
632 
633 }  /* end of terminit */
634 #endif	/* LINEMODE */
635