xref: /csrg-svn/libexec/telnetd/state.c (revision 42673)
1 /*
2  * Copyright (c) 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)state.c	5.6 (Berkeley) 06/01/90";
10 #endif /* not lint */
11 
12 #include "telnetd.h"
13 
14 char	doopt[] = { IAC, DO, '%', 'c', 0 };
15 char	dont[] = { IAC, DONT, '%', 'c', 0 };
16 char	will[] = { IAC, WILL, '%', 'c', 0 };
17 char	wont[] = { IAC, WONT, '%', 'c', 0 };
18 int	not42 = 1;
19 
20 /*
21  * Buffer for sub-options, and macros
22  * for suboptions buffer manipulations
23  */
24 char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
25 
26 #define	SB_CLEAR()	subpointer = subbuffer;
27 #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
28 #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
29 				*subpointer++ = (c); \
30 			}
31 #define	SB_GET()	((*subpointer++)&0xff)
32 #define	SB_EOF()	(subpointer >= subend)
33 
34 
35 
36 /*
37  * State for recv fsm
38  */
39 #define	TS_DATA		0	/* base state */
40 #define	TS_IAC		1	/* look for double IAC's */
41 #define	TS_CR		2	/* CR-LF ->'s CR */
42 #define	TS_SB		3	/* throw away begin's... */
43 #define	TS_SE		4	/* ...end's (suboption negotiation) */
44 #define	TS_WILL		5	/* will option negotiation */
45 #define	TS_WONT		6	/* wont " */
46 #define	TS_DO		7	/* do " */
47 #define	TS_DONT		8	/* dont " */
48 
49 telrcv()
50 {
51 	register int c;
52 	static int state = TS_DATA;
53 #if	defined(CRAY2) && defined(UNICOS5)
54 	char *opfrontp = pfrontp;
55 #endif
56 
57 	while (ncc > 0) {
58 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
59 			break;
60 		c = *netip++ & 0377, ncc--;
61 		switch (state) {
62 
63 		case TS_CR:
64 			state = TS_DATA;
65 			/* Strip off \n or \0 after a \r */
66 			if ((c == 0) || (c == '\n')) {
67 				break;
68 			}
69 			/* FALL THROUGH */
70 
71 		case TS_DATA:
72 			if (c == IAC) {
73 				state = TS_IAC;
74 				break;
75 			}
76 			/*
77 			 * We now map \r\n ==> \r for pragmatic reasons.
78 			 * Many client implementations send \r\n when
79 			 * the user hits the CarriageReturn key.
80 			 *
81 			 * We USED to map \r\n ==> \n, since \r\n says
82 			 * that we want to be in column 1 of the next
83 			 * printable line, and \n is the standard
84 			 * unix way of saying that (\r is only good
85 			 * if CRMOD is set, which it normally is).
86 			 */
87 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
88 				/*
89 				 * If we are operating in linemode,
90 				 * convert to local end-of-line.
91 				 */
92 				if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
93 					netip++; ncc--;
94 					c = '\n';
95 				} else {
96 					state = TS_CR;
97 				}
98 			}
99 			*pfrontp++ = c;
100 			break;
101 
102 		case TS_IAC:
103 gotiac:			switch (c) {
104 
105 			/*
106 			 * Send the process on the pty side an
107 			 * interrupt.  Do this with a NULL or
108 			 * interrupt char; depending on the tty mode.
109 			 */
110 			case IP:
111 				interrupt();
112 				break;
113 
114 			case BREAK:
115 				sendbrk();
116 				break;
117 
118 			/*
119 			 * Are You There?
120 			 */
121 			case AYT:
122 				(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
123 				nfrontp += 9;
124 				break;
125 
126 			/*
127 			 * Abort Output
128 			 */
129 			case AO:
130 			    {
131 				ptyflush();	/* half-hearted */
132 				init_termbuf();
133 
134 				if (slctab[SLC_AO].sptr &&
135 				    *slctab[SLC_AO].sptr != (cc_t)-1) {
136 				    *pfrontp++ =
137 					(unsigned char)*slctab[SLC_AO].sptr;
138 				}
139 
140 				netclear();	/* clear buffer back */
141 				*nfrontp++ = IAC;
142 				*nfrontp++ = DM;
143 				neturg = nfrontp-1; /* off by one XXX */
144 				break;
145 			    }
146 
147 			/*
148 			 * Erase Character and
149 			 * Erase Line
150 			 */
151 			case EC:
152 			case EL:
153 			    {
154 				cc_t ch;
155 
156 				ptyflush();	/* half-hearted */
157 				init_termbuf();
158 				ch = (c == EC) ? *slctab[SLC_EC].sptr :
159 						 *slctab[SLC_EL].sptr;
160 				if (ch != (cc_t)-1)
161 					*pfrontp++ = (unsigned char)ch;
162 				break;
163 			    }
164 
165 			/*
166 			 * Check for urgent data...
167 			 */
168 			case DM:
169 				SYNCHing = stilloob(net);
170 				settimer(gotDM);
171 				break;
172 
173 
174 			/*
175 			 * Begin option subnegotiation...
176 			 */
177 			case SB:
178 				state = TS_SB;
179 				SB_CLEAR();
180 				continue;
181 
182 			case WILL:
183 				state = TS_WILL;
184 				continue;
185 
186 			case WONT:
187 				state = TS_WONT;
188 				continue;
189 
190 			case DO:
191 				state = TS_DO;
192 				continue;
193 
194 			case DONT:
195 				state = TS_DONT;
196 				continue;
197 			case EOR:
198 				if (hisopts[TELOPT_EOR])
199 					doeof();
200 				break;
201 
202 			/*
203 			 * Handle RFC 10xx Telnet linemode option additions
204 			 * to command stream (EOF, SUSP, ABORT).
205 			 */
206 			case xEOF:
207 				doeof();
208 				break;
209 
210 			case SUSP:
211 				sendsusp();
212 				break;
213 
214 			case ABORT:
215 				sendbrk();
216 				break;
217 
218 			case IAC:
219 				*pfrontp++ = c;
220 				break;
221 			}
222 			state = TS_DATA;
223 			break;
224 
225 		case TS_SB:
226 			if (c == IAC) {
227 				state = TS_SE;
228 			} else {
229 				SB_ACCUM(c);
230 			}
231 			break;
232 
233 		case TS_SE:
234 			if (c != SE) {
235 				if (c != IAC) {
236 					/*
237 					 * bad form of suboption negotiation.
238 					 * handle it in such a way as to avoid
239 					 * damage to local state.  Parse
240 					 * suboption buffer found so far,
241 					 * then treat remaining stream as
242 					 * another command sequence.
243 					 */
244 					SB_TERM();
245 					suboption();
246 					state = TS_IAC;
247 					goto gotiac;
248 				}
249 				SB_ACCUM(c);
250 				state = TS_SB;
251 			} else {
252 				SB_TERM();
253 				suboption();	/* handle sub-option */
254 				state = TS_DATA;
255 			}
256 			break;
257 
258 		case TS_WILL:
259 			willoption(c);
260 			state = TS_DATA;
261 			continue;
262 
263 		case TS_WONT:
264 			wontoption(c);
265 			state = TS_DATA;
266 			continue;
267 
268 		case TS_DO:
269 			dooption(c);
270 			state = TS_DATA;
271 			continue;
272 
273 		case TS_DONT:
274 			dontoption(c);
275 			state = TS_DATA;
276 			continue;
277 
278 		default:
279 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
280 			printf("telnetd: panic state=%d\n", state);
281 			exit(1);
282 		}
283 	}
284 #if	defined(CRAY2) && defined(UNICOS5)
285 	if (!linemode) {
286 		char	xptyobuf[BUFSIZ+NETSLOP];
287 		char	xbuf2[BUFSIZ];
288 		register char *cp;
289 		int n = pfrontp - opfrontp, oc;
290 		bcopy(opfrontp, xptyobuf, n);
291 		pfrontp = opfrontp;
292 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
293 					xbuf2, &oc, BUFSIZ);
294 		for (cp = xbuf2; oc > 0; --oc)
295 			if ((*nfrontp++ = *cp++) == IAC)
296 				*nfrontp++ = IAC;
297 	}
298 #endif	/* defined(CRAY2) && defined(UNICOS5) */
299 }  /* end of telrcv */
300 
301 /*
302  * The will/wont/do/dont state machines are based on Dave Borman's
303  * Telnet option processing state machine.  We keep track of the full
304  * state of the option negotiation with the following state variables
305  *	myopts, hisopts - The last fully negotiated state for each
306  *			side of the connection.
307  *	mywants, hiswants - The state we wish to be in after a completed
308  *			negotiation.  (hiswants is slightly misleading,
309  *			this is more precisely the state I want him to
310  *			be in.
311  *	resp - We count the number of requests we have sent out.
312  *
313  * These correspond to the following states:
314  *	my_state = the last negotiated state
315  *	want_state = what I want the state to go to
316  *	want_resp = how many requests I have sent
317  * All state defaults are negative, and resp defaults to 0.
318  *
319  * When initiating a request to change state to new_state:
320  *
321  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
322  *	do nothing;
323  * } else {
324  *	want_state = new_state;
325  *	send new_state;
326  *	want_resp++;
327  * }
328  *
329  * When receiving new_state:
330  *
331  * if (want_resp) {
332  *	want_resp--;
333  *	if (want_resp && (new_state == my_state))
334  *		want_resp--;
335  * }
336  * if ((want_resp == 0) && (new_state != want_state)) {
337  *	if (ok_to_switch_to new_state)
338  *		want_state = new_state;
339  *	else
340  *		want_resp++;
341  *	send want_state;
342  * }
343  * my_state = new_state;
344  *
345  * Note that new_state is implied in these functions by the function itself.
346  * will and do imply positive new_state, wont and dont imply negative.
347  *
348  * Finally, there is one catch.  If we send a negative response to a
349  * positive request, my_state will be the positive while want_state will
350  * remain negative.  my_state will revert to negative when the negative
351  * acknowlegment arrives from the peer.  Thus, my_state generally tells
352  * us not only the last negotiated state, but also tells us what the peer
353  * wants to be doing as well.  It is important to understand this difference
354  * as we may wish to be processing data streams based on our desired state
355  * (want_state) or based on what the peer thinks the state is (my_state).
356  *
357  * This all works fine because if the peer sends a positive request, the data
358  * that we receive prior to negative acknowlegment will probably be affected
359  * by the positive state, and we can process it as such (if we can; if we
360  * can't then it really doesn't matter).  If it is that important, then the
361  * peer probably should be buffering until this option state negotiation
362  * is complete.
363  *
364  */
365 send_do(option, init)
366 	int option, init;
367 {
368 	if (init) {
369 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) ||
370 		    hiswants[option] == OPT_YES)
371 			return;
372 		/*
373 		 * Special case for TELOPT_TM:  We send a DO, but pretend
374 		 * that we sent a DONT, so that we can send more DOs if
375 		 * we want to.
376 		 */
377 		if (option == TELOPT_TM)
378 			hiswants[option] = OPT_NO;
379 		else
380 			hiswants[option] = OPT_YES;
381 		do_dont_resp[option]++;
382 	}
383 	(void) sprintf(nfrontp, doopt, option);
384 	nfrontp += sizeof (dont) - 2;
385 }
386 
387 willoption(option)
388 	int option;
389 {
390 	int changeok = 0;
391 
392 	/*
393 	 * process input from peer.
394 	 */
395 
396 	if (do_dont_resp[option]) {
397 		do_dont_resp[option]--;
398 		if (do_dont_resp[option] && hisopts[option] == OPT_YES)
399 			do_dont_resp[option]--;
400 	}
401 	if (do_dont_resp[option] == 0) {
402 	    if (hiswants[option] != OPT_YES) {
403 		switch (option) {
404 
405 		case TELOPT_BINARY:
406 			init_termbuf();
407 			tty_binaryin(1);
408 			set_termbuf();
409 			changeok++;
410 			break;
411 
412 		case TELOPT_ECHO:
413 			not42 = 0;	/* looks like a 4.2 system */
414 #ifdef notdef
415 			/*
416 			 * Now, in a 4.2 system, to break them out of
417 			 * ECHOing (to the terminal) mode, we need to
418 			 * send a WILL ECHO.
419 			 */
420 			if (myopts[TELOPT_ECHO] == OPT_YES) {
421 				send_will(TELOPT_ECHO, 1);
422 			}
423 #else
424 			/*
425 			 * "WILL ECHO".  Kludge upon kludge!
426 			 * A 4.2 client is now echoing user input at
427 			 * the tty.  This is probably undesireable and
428 			 * it should be stopped.  The client will
429 			 * respond WONT TM to the DO TM that we send to
430 			 * check for kludge linemode.  When the WONT TM
431 			 * arrives, linemode will be turned off and a
432 			 * change propogated to the pty.  This change
433 			 * will cause us to process the new pty state
434 			 * in localstat(), which will notice that
435 			 * linemode is off and send a WILL ECHO
436 			 * so that we are properly in character mode and
437 			 * all is well.
438 			 */
439 #endif
440 			/*
441 			 * Fool the state machine into sending a don't.
442 			 * This also allows the initial echo sending
443 			 * code to break out of the loop that it is
444 			 * in.  (Look in telnet())
445 			 */
446 			hiswants[TELOPT_ECHO] = OPT_NO;
447 			break;
448 
449 		case TELOPT_TM:
450 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
451 			/*
452 			 * This telnetd implementation does not really
453 			 * support timing marks, it just uses them to
454 			 * support the kludge linemode stuff.  If we
455 			 * receive a will or wont TM in response to our
456 			 * do TM request that may have been sent to
457 			 * determine kludge linemode support, process
458 			 * it, otherwise TM should get a negative
459 			 * response back.
460 			 */
461 			/*
462 			 * Handle the linemode kludge stuff.
463 			 * If we are not currently supporting any
464 			 * linemode at all, then we assume that this
465 			 * is the client telling us to use kludge
466 			 * linemode in response to our query.  Set the
467 			 * linemode type that is to be supported, note
468 			 * that the client wishes to use linemode, and
469 			 * eat the will TM as though it never arrived.
470 			 */
471 			if (lmodetype < KLUDGE_LINEMODE) {
472 				lmodetype = KLUDGE_LINEMODE;
473 				clientstat(TELOPT_LINEMODE, WILL, 0);
474 				send_wont(TELOPT_SGA, 1);
475 			}
476 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
477 			/*
478 			 * We never respond to a WILL TM, and
479 			 * we leave the state OPT_NO.
480 			 */
481 			return;
482 
483 		case TELOPT_LFLOW:
484 			/*
485 			 * If we are going to support flow control
486 			 * option, then don't worry peer that we can't
487 			 * change the flow control characters.
488 			 */
489 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
490 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
491 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
492 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
493 		case TELOPT_TTYPE:
494 		case TELOPT_SGA:
495 		case TELOPT_NAWS:
496 		case TELOPT_TSPEED:
497 			changeok++;
498 			break;
499 
500 #ifdef	LINEMODE
501 		case TELOPT_LINEMODE:
502 # ifdef	KLUDGELINEMODE
503 			/*
504 			 * Note client's desire to use linemode.
505 			 */
506 			lmodetype = REAL_LINEMODE;
507 # endif	/* KLUDGELINEMODE */
508 			clientstat(TELOPT_LINEMODE, WILL, 0);
509 			changeok++;
510 			break;
511 #endif	/* LINEMODE */
512 
513 		default:
514 			break;
515 		}
516 		if (changeok) {
517 			hiswants[option] = OPT_YES;
518 			send_do(option, 0);
519 		} else {
520 			do_dont_resp[option]++;
521 			send_dont(option, 0);
522 		}
523 	    }
524 	}
525 	hisopts[option] = OPT_YES;
526 }  /* end of willoption */
527 
528 send_dont(option, init)
529 	int option, init;
530 {
531 	if (init) {
532 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) ||
533 		    hiswants[option] == OPT_NO)
534 			return;
535 		hiswants[option] = OPT_NO;
536 		do_dont_resp[option]++;
537 	}
538 	(void) sprintf(nfrontp, dont, option);
539 	nfrontp += sizeof (doopt) - 2;
540 }
541 
542 wontoption(option)
543 	int option;
544 {
545 	char *fmt = (char *)0;
546 
547 	/*
548 	 * Process client input.
549 	 */
550 
551 	if (do_dont_resp[option]) {
552 		do_dont_resp[option]--;
553 		if (do_dont_resp[option] && hisopts[option] == OPT_NO)
554 			do_dont_resp[option]--;
555 	}
556 	if (do_dont_resp[option] == 0) {
557 	    if (hiswants[option] != OPT_NO) {
558 		/* it is always ok to change to negative state */
559 		switch (option) {
560 		case TELOPT_ECHO:
561 			not42 = 1; /* doesn't seem to be a 4.2 system */
562 			break;
563 
564 		case TELOPT_BINARY:
565 			init_termbuf();
566 			tty_binaryin(0);
567 			set_termbuf();
568 			break;
569 
570 #ifdef	LINEMODE
571 		case TELOPT_LINEMODE:
572 # ifdef	KLUDGELINEMODE
573 			/*
574 			 * If real linemode is supported, then client is
575 			 * asking to turn linemode off.
576 			 */
577 			if (lmodetype == REAL_LINEMODE)
578 # endif	/* KLUDGELINEMODE */
579 				clientstat(TELOPT_LINEMODE, WONT, 0);
580 			break;
581 #endif	LINEMODE
582 
583 		case TELOPT_TM:
584 			/*
585 			 * If we get a WONT TM, and had sent a DO TM,
586 			 * don't respond with a DONT TM, just leave it
587 			 * as is.  Short circut the state machine to
588 			 * achive this.
589 			 */
590 			hiswants[TELOPT_TM] = OPT_NO;
591 			return;
592 
593 		case TELOPT_LFLOW:
594 			/*
595 			 * If we are not going to support flow control
596 			 * option, then let peer know that we can't
597 			 * change the flow control characters.
598 			 */
599 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
600 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
601 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
602 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
603 			break;
604 
605 		default:
606 			break;
607 		}
608 		hiswants[option] = OPT_NO;
609 		fmt = dont;
610 		send_dont(option, 0);
611 	    } else {
612 		switch (option) {
613 		case TELOPT_TM:
614 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
615 			if (lmodetype < REAL_LINEMODE) {
616 				lmodetype = NO_LINEMODE;
617 				clientstat(TELOPT_LINEMODE, WONT, 0);
618 				send_will(TELOPT_SGA, 1);
619 /*@*/				send_will(TELOPT_ECHO, 1);
620 			}
621 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
622 		default:
623 			break;
624 		}
625 	    }
626 	}
627 	hisopts[option] = OPT_NO;
628 
629 }  /* end of wontoption */
630 
631 send_will(option, init)
632 	int option, init;
633 {
634 	if (init) {
635 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)||
636 		    mywants[option] == OPT_YES)
637 			return;
638 		mywants[option] = OPT_YES;
639 		will_wont_resp[option]++;
640 	}
641 	(void) sprintf(nfrontp, will, option);
642 	nfrontp += sizeof (doopt) - 2;
643 }
644 
645 dooption(option)
646 	int option;
647 {
648 	int changeok = 0;
649 
650 	/*
651 	 * Process client input.
652 	 */
653 
654 	if (will_wont_resp[option]) {
655 		will_wont_resp[option]--;
656 		if (will_wont_resp[option] && myopts[option] == OPT_YES)
657 			will_wont_resp[option]--;
658 	}
659 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) {
660 		switch (option) {
661 		case TELOPT_ECHO:
662 #ifdef	LINEMODE
663 			if (lmodetype == NO_LINEMODE) {
664 #endif
665 				init_termbuf();
666 				tty_setecho(1);
667 				set_termbuf();
668 #ifdef	LINEMODE
669 			}
670 #endif
671 			changeok++;
672 			break;
673 
674 		case TELOPT_BINARY:
675 			init_termbuf();
676 			tty_binaryout(1);
677 			set_termbuf();
678 			changeok++;
679 			break;
680 
681 		case TELOPT_SGA:
682 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
683 			/*
684 			 * If kludge linemode is in use, then we must
685 			 * process an incoming do SGA for linemode
686 			 * purposes.
687 			 */
688 			if (lmodetype == KLUDGE_LINEMODE) {
689 				/*
690 				 * Receipt of "do SGA" in kludge
691 				 * linemode is the peer asking us to
692 				 * turn off linemode.  Make note of
693 				 * the request.
694 				 */
695 				clientstat(TELOPT_LINEMODE, WONT, 0);
696 				/*
697 				 * If linemode did not get turned off
698 				 * then don't tell peer that we did.
699 				 * Breaking here forces a wont SGA to
700 				 * be returned.
701 				 */
702 				if (linemode)
703 					break;
704 			}
705 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
706 			changeok++;
707 			break;
708 
709 		case TELOPT_STATUS:
710 			changeok++;
711 			break;
712 
713 		case TELOPT_TM:
714 			/*
715 			 * Special case for TM.  We send a WILL, but
716 			 * pretend we sent a WONT.
717 			 */
718 			send_will(option, 0);
719 			mywants[option] = OPT_NO;
720 			myopts[option] = OPT_NO;
721 			return;
722 
723 		case TELOPT_LINEMODE:
724 		case TELOPT_TTYPE:
725 		case TELOPT_NAWS:
726 		case TELOPT_TSPEED:
727 		case TELOPT_LFLOW:
728 		default:
729 			break;
730 		}
731 		if (changeok) {
732 			mywants[option] = OPT_YES;
733 			send_will(option, 0);
734 		} else {
735 			will_wont_resp[option]++;
736 			send_wont(option, 0);
737 		}
738 	}
739 	myopts[option] = OPT_YES;
740 
741 }  /* end of dooption */
742 
743 send_wont(option, init)
744 	int option, init;
745 {
746 	if (init) {
747 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) ||
748 		    mywants[option] == OPT_NO)
749 			return;
750 		mywants[option] = OPT_NO;
751 		will_wont_resp[option]++;
752 	}
753 	(void) sprintf(nfrontp, wont, option);
754 	nfrontp += sizeof (wont) - 2;
755 }
756 
757 dontoption(option)
758 	int option;
759 {
760 	/*
761 	 * Process client input.
762 	 */
763 
764 	if (will_wont_resp[option]) {
765 		will_wont_resp[option]--;
766 		if (will_wont_resp[option] && myopts[option] == OPT_NO)
767 			will_wont_resp[option]--;
768 	}
769 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) {
770 		switch (option) {
771 		case TELOPT_BINARY:
772 			init_termbuf();
773 			tty_binaryout(0);
774 			set_termbuf();
775 			break;
776 
777 		case TELOPT_ECHO:	/* we should stop echoing */
778 #ifdef	LINEMODE
779 			if (lmodetype == NO_LINEMODE) {
780 #endif
781 				init_termbuf();
782 				tty_setecho(0);
783 				set_termbuf();
784 #ifdef	LINEMODE
785 			}
786 #endif
787 			break;
788 
789 		case TELOPT_SGA:
790 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
791 			/*
792 			 * If kludge linemode is in use, then we
793 			 * must process an incoming do SGA for
794 			 * linemode purposes.
795 			 */
796 			if (lmodetype == KLUDGE_LINEMODE) {
797 				/*
798 				 * The client is asking us to turn
799 				 * linemode on.
800 				 */
801 				clientstat(TELOPT_LINEMODE, WILL, 0);
802 				/*
803 				 * If we did not turn line mode on,
804 				 * then what do we say?  Will SGA?
805 				 * This violates design of telnet.
806 				 * Gross.  Very Gross.
807 				 */
808 			}
809 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
810 
811 		default:
812 			break;
813 		}
814 
815 		mywants[option] = OPT_NO;
816 		send_wont(option, 0);
817 	}
818 	myopts[option] = OPT_NO;
819 
820 }  /* end of dontoption */
821 
822 /*
823  * suboption()
824  *
825  *	Look at the sub-option buffer, and try to be helpful to the other
826  * side.
827  *
828  *	Currently we recognize:
829  *
830  *	Terminal type is
831  *	Linemode
832  *	Window size
833  *	Terminal speed
834  */
835 suboption()
836 {
837     register int subchar;
838 
839     subchar = SB_GET();
840     switch (subchar) {
841     case TELOPT_TSPEED: {
842 	register int xspeed, rspeed;
843 
844 	if (hisopts[TELOPT_TSPEED] == OPT_NO)	/* Ignore if option disabled */
845 		break;
846 
847 	settimer(tspeedsubopt);
848 
849 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
850 		return;
851 
852 	xspeed = atoi(subpointer);
853 
854 	while (SB_GET() != ',' && !SB_EOF());
855 	if (SB_EOF())
856 		return;
857 
858 	rspeed = atoi(subpointer);
859 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
860 
861 	break;
862 
863     }  /* end of case TELOPT_TSPEED */
864 
865     case TELOPT_TTYPE: {		/* Yaaaay! */
866 	static char terminalname[5+41] = "TERM=";
867 
868 	if (hisopts[TELOPT_TTYPE] == OPT_NO)	/* Ignore if option disabled */
869 		break;
870 	settimer(ttypesubopt);
871 
872 	if (SB_GET() != TELQUAL_IS) {
873 	    return;		/* ??? XXX but, this is the most robust */
874 	}
875 
876 	terminaltype = terminalname+sizeof("TERM=")-1;
877 
878 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
879 								    !SB_EOF()) {
880 	    register int c;
881 
882 	    c = SB_GET();
883 	    if (isupper(c)) {
884 		c = tolower(c);
885 	    }
886 	    *terminaltype++ = c;    /* accumulate name */
887 	}
888 	*terminaltype = 0;
889 	terminaltype = terminalname;
890 	break;
891     }  /* end of case TELOPT_TTYPE */
892 
893     case TELOPT_NAWS: {
894 	register int xwinsize, ywinsize;
895 
896 	if (hisopts[TELOPT_NAWS] == OPT_NO)	/* Ignore if option disabled */
897 		break;
898 
899 	if (SB_EOF())
900 		return;
901 	xwinsize = SB_GET() << 8;
902 	if (SB_EOF())
903 		return;
904 	xwinsize |= SB_GET();
905 	if (SB_EOF())
906 		return;
907 	ywinsize = SB_GET() << 8;
908 	if (SB_EOF())
909 		return;
910 	ywinsize |= SB_GET();
911 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
912 
913 	break;
914 
915     }  /* end of case TELOPT_NAWS */
916 
917 #ifdef	LINEMODE
918     case TELOPT_LINEMODE: {
919 	register int request;
920 
921 	if (hisopts[TELOPT_LINEMODE] == OPT_NO)	/* Ignore if option disabled */
922 		break;
923 	/*
924 	 * Process linemode suboptions.
925 	 */
926 	if (SB_EOF()) break;  /* garbage was sent */
927 	request = SB_GET();  /* get will/wont */
928 	if (SB_EOF()) break;  /* another garbage check */
929 
930 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
931 		/*
932 		 * Process suboption buffer of slc's
933 		 */
934 		start_slc(1);
935 		do_opt_slc(subpointer, subend - subpointer);
936 		end_slc(0);
937 
938 	} else if (request == LM_MODE) {
939 		useeditmode = SB_GET();  /* get mode flag */
940 		clientstat(LM_MODE, 0, 0);
941 	}
942 
943 	switch (SB_GET()) {  /* what suboption? */
944 	case LM_FORWARDMASK:
945 		/*
946 		 * According to spec, only server can send request for
947 		 * forwardmask, and client can only return a positive response.
948 		 * So don't worry about it.
949 		 */
950 
951 	default:
952 		break;
953 	}
954 	break;
955     }  /* end of case TELOPT_LINEMODE */
956 #endif
957     case TELOPT_STATUS: {
958 	int mode;
959 
960 	mode = SB_GET();
961 	switch (mode) {
962 	case TELQUAL_SEND:
963 	    if (myopts[TELOPT_STATUS] == OPT_YES)
964 		send_status();
965 	    break;
966 
967 	case TELQUAL_IS:
968 	    break;
969 
970 	default:
971 	    break;
972 	}
973 	break;
974     }  /* end of case TELOPT_STATUS */
975 
976     default:
977 	break;
978     }  /* end of switch */
979 
980 }  /* end of suboption */
981 
982 #define	ADD(c)	 *ncp++ = c;
983 #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
984 send_status()
985 {
986 	char statusbuf[256];
987 	register char *ncp;
988 	register int i;
989 
990 	ncp = statusbuf;
991 
992 	netflush();	/* get rid of anything waiting to go out */
993 
994 	ADD(IAC);
995 	ADD(SB);
996 	ADD(TELOPT_STATUS);
997 	ADD(TELQUAL_IS);
998 
999 	for (i = 0; i < NTELOPTS; i++) {
1000 		if (myopts[i] == OPT_YES) {
1001 			ADD(WILL);
1002 			ADD_DATA(i);
1003 			if (i == IAC)
1004 				ADD(IAC);
1005 		}
1006 		if (hisopts[i] == OPT_YES) {
1007 			ADD(DO);
1008 			ADD_DATA(i);
1009 			if (i == IAC)
1010 				ADD(IAC);
1011 		}
1012 	}
1013 
1014 #ifdef	LINEMODE
1015 	if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
1016 		char *cp, *cpe;
1017 		int len;
1018 
1019 		ADD(SB);
1020 		ADD(TELOPT_LINEMODE);
1021 		ADD(LM_MODE);
1022 		ADD_DATA(editmode);
1023 		if (editmode == IAC)
1024 			ADD(IAC);
1025 		ADD(SE);
1026 
1027 		ADD(SB);
1028 		ADD(TELOPT_LINEMODE);
1029 		ADD(LM_SLC);
1030 		start_slc(0);
1031 		send_slc();
1032 		len = end_slc(&cp);
1033 		for (cpe = cp + len; cp < cpe; cp++)
1034 			ADD_DATA(*cp);
1035 		ADD(SE);
1036 	}
1037 #endif	/* LINEMODE */
1038 
1039 	ADD(IAC);
1040 	ADD(SE);
1041 
1042 	writenet(statusbuf, ncp - statusbuf);
1043 	netflush();	/* Send it on its way */
1044 }
1045