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