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