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