xref: /csrg-svn/libexec/telnetd/state.c (revision 44364)
138905Sborman /*
238905Sborman  * Copyright (c) 1989 Regents of the University of California.
338905Sborman  * All rights reserved.
438905Sborman  *
542673Sbostic  * %sccs.include.redist.c%
638905Sborman  */
738905Sborman 
838905Sborman #ifndef lint
9*44364Sborman static char sccsid[] = "@(#)state.c	5.7 (Berkeley) 06/28/90";
1038905Sborman #endif /* not lint */
1138905Sborman 
1238905Sborman #include "telnetd.h"
1338905Sborman 
1438905Sborman char	doopt[] = { IAC, DO, '%', 'c', 0 };
1538905Sborman char	dont[] = { IAC, DONT, '%', 'c', 0 };
1638905Sborman char	will[] = { IAC, WILL, '%', 'c', 0 };
1738905Sborman char	wont[] = { IAC, WONT, '%', 'c', 0 };
1838905Sborman int	not42 = 1;
1938905Sborman 
2038905Sborman /*
2138905Sborman  * Buffer for sub-options, and macros
2238905Sborman  * for suboptions buffer manipulations
2338905Sborman  */
2438905Sborman char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
2538905Sborman 
2638905Sborman #define	SB_CLEAR()	subpointer = subbuffer;
2738905Sborman #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
2838905Sborman #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
2938905Sborman 				*subpointer++ = (c); \
3038905Sborman 			}
3138905Sborman #define	SB_GET()	((*subpointer++)&0xff)
3238905Sborman #define	SB_EOF()	(subpointer >= subend)
33*44364Sborman #define	SB_LEN()	(subend - subpointer)
3438905Sborman 
3538905Sborman 
3638905Sborman 
3738905Sborman /*
3838905Sborman  * State for recv fsm
3938905Sborman  */
4038905Sborman #define	TS_DATA		0	/* base state */
4138905Sborman #define	TS_IAC		1	/* look for double IAC's */
4238905Sborman #define	TS_CR		2	/* CR-LF ->'s CR */
4338905Sborman #define	TS_SB		3	/* throw away begin's... */
4438905Sborman #define	TS_SE		4	/* ...end's (suboption negotiation) */
4538905Sborman #define	TS_WILL		5	/* will option negotiation */
4638905Sborman #define	TS_WONT		6	/* wont " */
4738905Sborman #define	TS_DO		7	/* do " */
4838905Sborman #define	TS_DONT		8	/* dont " */
4938905Sborman 
5038905Sborman telrcv()
5138905Sborman {
5238905Sborman 	register int c;
5338905Sborman 	static int state = TS_DATA;
5440242Sborman #if	defined(CRAY2) && defined(UNICOS5)
5538905Sborman 	char *opfrontp = pfrontp;
5638905Sborman #endif
5738905Sborman 
5838905Sborman 	while (ncc > 0) {
5938905Sborman 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6038905Sborman 			break;
6138905Sborman 		c = *netip++ & 0377, ncc--;
6238905Sborman 		switch (state) {
6338905Sborman 
6438905Sborman 		case TS_CR:
6538905Sborman 			state = TS_DATA;
6638905Sborman 			/* Strip off \n or \0 after a \r */
6738905Sborman 			if ((c == 0) || (c == '\n')) {
6838905Sborman 				break;
6938905Sborman 			}
7038905Sborman 			/* FALL THROUGH */
7138905Sborman 
7238905Sborman 		case TS_DATA:
7338905Sborman 			if (c == IAC) {
7438905Sborman 				state = TS_IAC;
7538905Sborman 				break;
7638905Sborman 			}
7738905Sborman 			/*
7838905Sborman 			 * We now map \r\n ==> \r for pragmatic reasons.
7938905Sborman 			 * Many client implementations send \r\n when
8038905Sborman 			 * the user hits the CarriageReturn key.
8138905Sborman 			 *
8238905Sborman 			 * We USED to map \r\n ==> \n, since \r\n says
8338905Sborman 			 * that we want to be in column 1 of the next
8438905Sborman 			 * printable line, and \n is the standard
8538905Sborman 			 * unix way of saying that (\r is only good
8638905Sborman 			 * if CRMOD is set, which it normally is).
8738905Sborman 			 */
88*44364Sborman 			if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
8938905Sborman 				/*
9038905Sborman 				 * If we are operating in linemode,
9138905Sborman 				 * convert to local end-of-line.
9238905Sborman 				 */
9338905Sborman 				if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
9438905Sborman 					netip++; ncc--;
9538905Sborman 					c = '\n';
9638905Sborman 				} else {
9738905Sborman 					state = TS_CR;
9838905Sborman 				}
9938905Sborman 			}
10038905Sborman 			*pfrontp++ = c;
10138905Sborman 			break;
10238905Sborman 
10338905Sborman 		case TS_IAC:
10438905Sborman gotiac:			switch (c) {
10538905Sborman 
10638905Sborman 			/*
10738905Sborman 			 * Send the process on the pty side an
10838905Sborman 			 * interrupt.  Do this with a NULL or
10938905Sborman 			 * interrupt char; depending on the tty mode.
11038905Sborman 			 */
11138905Sborman 			case IP:
112*44364Sborman #ifdef DIAGNOSTICS
113*44364Sborman 				if (diagnostic & TD_OPTIONS)
114*44364Sborman 					printoption("td: recv IAC", c);
115*44364Sborman #endif /* DIAGNOSTICS */
11638905Sborman 				interrupt();
11738905Sborman 				break;
11838905Sborman 
11938905Sborman 			case BREAK:
120*44364Sborman #ifdef DIAGNOSTICS
121*44364Sborman 				if (diagnostic & TD_OPTIONS)
122*44364Sborman 					printoption("td: recv IAC", c);
123*44364Sborman #endif /* DIAGNOSTICS */
12438905Sborman 				sendbrk();
12538905Sborman 				break;
12638905Sborman 
12738905Sborman 			/*
12838905Sborman 			 * Are You There?
12938905Sborman 			 */
13038905Sborman 			case AYT:
131*44364Sborman #ifdef DIAGNOSTICS
132*44364Sborman 				if (diagnostic & TD_OPTIONS)
133*44364Sborman 					printoption("td: recv IAC", c);
134*44364Sborman #endif /* DIAGNOSTICS */
13538905Sborman 				(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
13638905Sborman 				nfrontp += 9;
13738905Sborman 				break;
13838905Sborman 
13938905Sborman 			/*
14038905Sborman 			 * Abort Output
14138905Sborman 			 */
14238905Sborman 			case AO:
14338905Sborman 			    {
144*44364Sborman #ifdef DIAGNOSTICS
145*44364Sborman 				if (diagnostic & TD_OPTIONS)
146*44364Sborman 					printoption("td: recv IAC", c);
147*44364Sborman #endif /* DIAGNOSTICS */
14838905Sborman 				ptyflush();	/* half-hearted */
14938905Sborman 				init_termbuf();
15038905Sborman 
15138905Sborman 				if (slctab[SLC_AO].sptr &&
15240242Sborman 				    *slctab[SLC_AO].sptr != (cc_t)-1) {
15340242Sborman 				    *pfrontp++ =
15440242Sborman 					(unsigned char)*slctab[SLC_AO].sptr;
15538905Sborman 				}
15638905Sborman 
15738905Sborman 				netclear();	/* clear buffer back */
15838905Sborman 				*nfrontp++ = IAC;
15938905Sborman 				*nfrontp++ = DM;
16038905Sborman 				neturg = nfrontp-1; /* off by one XXX */
161*44364Sborman #ifdef DIAGNOSTICS
162*44364Sborman 				if (diagnostic & TD_OPTIONS)
163*44364Sborman 					printoption("td: send IAC", DM);
164*44364Sborman #endif /* DIAGNOSTICS */
16538905Sborman 				break;
16638905Sborman 			    }
16738905Sborman 
16838905Sborman 			/*
16938905Sborman 			 * Erase Character and
17038905Sborman 			 * Erase Line
17138905Sborman 			 */
17238905Sborman 			case EC:
17338905Sborman 			case EL:
17438905Sborman 			    {
17540242Sborman 				cc_t ch;
17638905Sborman 
177*44364Sborman #ifdef DIAGNOSTICS
178*44364Sborman 				if (diagnostic & TD_OPTIONS)
179*44364Sborman 					printoption("td: recv IAC", c);
180*44364Sborman #endif /* DIAGNOSTICS */
18138905Sborman 				ptyflush();	/* half-hearted */
18238905Sborman 				init_termbuf();
183*44364Sborman 				if (c == EC)
184*44364Sborman 					ch = *slctab[SLC_EC].sptr;
185*44364Sborman 				else
186*44364Sborman 					ch = *slctab[SLC_EL].sptr;
18740242Sborman 				if (ch != (cc_t)-1)
18840242Sborman 					*pfrontp++ = (unsigned char)ch;
18938905Sborman 				break;
19038905Sborman 			    }
19138905Sborman 
19238905Sborman 			/*
19338905Sborman 			 * Check for urgent data...
19438905Sborman 			 */
19538905Sborman 			case DM:
196*44364Sborman #ifdef DIAGNOSTICS
197*44364Sborman 				if (diagnostic & TD_OPTIONS)
198*44364Sborman 					printoption("td: recv IAC", c);
199*44364Sborman #endif /* DIAGNOSTICS */
20038905Sborman 				SYNCHing = stilloob(net);
20138905Sborman 				settimer(gotDM);
20238905Sborman 				break;
20338905Sborman 
20438905Sborman 
20538905Sborman 			/*
20638905Sborman 			 * Begin option subnegotiation...
20738905Sborman 			 */
20838905Sborman 			case SB:
20938905Sborman 				state = TS_SB;
21038905Sborman 				SB_CLEAR();
21138905Sborman 				continue;
21238905Sborman 
21338905Sborman 			case WILL:
21438905Sborman 				state = TS_WILL;
21538905Sborman 				continue;
21638905Sborman 
21738905Sborman 			case WONT:
21838905Sborman 				state = TS_WONT;
21938905Sborman 				continue;
22038905Sborman 
22138905Sborman 			case DO:
22238905Sborman 				state = TS_DO;
22338905Sborman 				continue;
22438905Sborman 
22538905Sborman 			case DONT:
22638905Sborman 				state = TS_DONT;
22738905Sborman 				continue;
22838905Sborman 			case EOR:
229*44364Sborman 				if (his_state_is_will(TELOPT_EOR))
23038905Sborman 					doeof();
23138905Sborman 				break;
23238905Sborman 
23338905Sborman 			/*
23438905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
23538905Sborman 			 * to command stream (EOF, SUSP, ABORT).
23638905Sborman 			 */
23738905Sborman 			case xEOF:
23838905Sborman 				doeof();
23938905Sborman 				break;
24038905Sborman 
24138905Sborman 			case SUSP:
24238905Sborman 				sendsusp();
24338905Sborman 				break;
24438905Sborman 
24538905Sborman 			case ABORT:
24638905Sborman 				sendbrk();
24738905Sborman 				break;
24838905Sborman 
24938905Sborman 			case IAC:
25038905Sborman 				*pfrontp++ = c;
25138905Sborman 				break;
25238905Sborman 			}
25338905Sborman 			state = TS_DATA;
25438905Sborman 			break;
25538905Sborman 
25638905Sborman 		case TS_SB:
25738905Sborman 			if (c == IAC) {
25838905Sborman 				state = TS_SE;
25938905Sborman 			} else {
26038905Sborman 				SB_ACCUM(c);
26138905Sborman 			}
26238905Sborman 			break;
26338905Sborman 
26438905Sborman 		case TS_SE:
26538905Sborman 			if (c != SE) {
26638905Sborman 				if (c != IAC) {
26738905Sborman 					/*
26838905Sborman 					 * bad form of suboption negotiation.
26938905Sborman 					 * handle it in such a way as to avoid
27038905Sborman 					 * damage to local state.  Parse
27138905Sborman 					 * suboption buffer found so far,
27238905Sborman 					 * then treat remaining stream as
27338905Sborman 					 * another command sequence.
27438905Sborman 					 */
275*44364Sborman #ifdef	DIAGNOSTICS
276*44364Sborman 					SB_ACCUM(IAC);
277*44364Sborman 					SB_ACCUM(c);
278*44364Sborman 					subpointer -= 2;
279*44364Sborman #endif
28038905Sborman 					SB_TERM();
28138905Sborman 					suboption();
28238905Sborman 					state = TS_IAC;
28338905Sborman 					goto gotiac;
28438905Sborman 				}
28538905Sborman 				SB_ACCUM(c);
28638905Sborman 				state = TS_SB;
28738905Sborman 			} else {
288*44364Sborman #ifdef	DIAGNOSTICS
289*44364Sborman 				SB_ACCUM(IAC);
290*44364Sborman 				SB_ACCUM(SE);
291*44364Sborman 				subpointer -= 2;
292*44364Sborman #endif
29338905Sborman 				SB_TERM();
29438905Sborman 				suboption();	/* handle sub-option */
29538905Sborman 				state = TS_DATA;
29638905Sborman 			}
29738905Sborman 			break;
29838905Sborman 
29938905Sborman 		case TS_WILL:
30039503Sborman 			willoption(c);
30138905Sborman 			state = TS_DATA;
30238905Sborman 			continue;
30338905Sborman 
30438905Sborman 		case TS_WONT:
30539503Sborman 			wontoption(c);
30638905Sborman 			state = TS_DATA;
30738905Sborman 			continue;
30838905Sborman 
30938905Sborman 		case TS_DO:
31039503Sborman 			dooption(c);
31138905Sborman 			state = TS_DATA;
31238905Sborman 			continue;
31338905Sborman 
31438905Sborman 		case TS_DONT:
31539503Sborman 			dontoption(c);
31638905Sborman 			state = TS_DATA;
31738905Sborman 			continue;
31838905Sborman 
31938905Sborman 		default:
32038905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
32138905Sborman 			printf("telnetd: panic state=%d\n", state);
32238905Sborman 			exit(1);
32338905Sborman 		}
32438905Sborman 	}
32540242Sborman #if	defined(CRAY2) && defined(UNICOS5)
32638905Sborman 	if (!linemode) {
32738905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
32838905Sborman 		char	xbuf2[BUFSIZ];
32938905Sborman 		register char *cp;
33038905Sborman 		int n = pfrontp - opfrontp, oc;
33138905Sborman 		bcopy(opfrontp, xptyobuf, n);
33238905Sborman 		pfrontp = opfrontp;
33338905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
33438905Sborman 					xbuf2, &oc, BUFSIZ);
33538905Sborman 		for (cp = xbuf2; oc > 0; --oc)
33638905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
33738905Sborman 				*nfrontp++ = IAC;
33838905Sborman 	}
33940242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
34038905Sborman }  /* end of telrcv */
34138905Sborman 
34238905Sborman /*
34338905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
344*44364Sborman  * Telnet option processing state machine.
34538905Sborman  *
34638905Sborman  * These correspond to the following states:
34738905Sborman  *	my_state = the last negotiated state
34838905Sborman  *	want_state = what I want the state to go to
34938905Sborman  *	want_resp = how many requests I have sent
35038905Sborman  * All state defaults are negative, and resp defaults to 0.
35138905Sborman  *
35238905Sborman  * When initiating a request to change state to new_state:
35338905Sborman  *
35438905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
35538905Sborman  *	do nothing;
35638905Sborman  * } else {
35738905Sborman  *	want_state = new_state;
35838905Sborman  *	send new_state;
35938905Sborman  *	want_resp++;
36038905Sborman  * }
36138905Sborman  *
36238905Sborman  * When receiving new_state:
36338905Sborman  *
36438905Sborman  * if (want_resp) {
36538905Sborman  *	want_resp--;
36638905Sborman  *	if (want_resp && (new_state == my_state))
36738905Sborman  *		want_resp--;
36838905Sborman  * }
36938905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
37038905Sborman  *	if (ok_to_switch_to new_state)
37138905Sborman  *		want_state = new_state;
37238905Sborman  *	else
37338905Sborman  *		want_resp++;
37438905Sborman  *	send want_state;
37538905Sborman  * }
37638905Sborman  * my_state = new_state;
37738905Sborman  *
37838905Sborman  * Note that new_state is implied in these functions by the function itself.
37938905Sborman  * will and do imply positive new_state, wont and dont imply negative.
38038905Sborman  *
38138905Sborman  * Finally, there is one catch.  If we send a negative response to a
38238905Sborman  * positive request, my_state will be the positive while want_state will
38338905Sborman  * remain negative.  my_state will revert to negative when the negative
38438905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
38538905Sborman  * us not only the last negotiated state, but also tells us what the peer
38638905Sborman  * wants to be doing as well.  It is important to understand this difference
38738905Sborman  * as we may wish to be processing data streams based on our desired state
38838905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
38938905Sborman  *
39038905Sborman  * This all works fine because if the peer sends a positive request, the data
39138905Sborman  * that we receive prior to negative acknowlegment will probably be affected
39238905Sborman  * by the positive state, and we can process it as such (if we can; if we
39338905Sborman  * can't then it really doesn't matter).  If it is that important, then the
39438905Sborman  * peer probably should be buffering until this option state negotiation
39538905Sborman  * is complete.
39638905Sborman  *
39738905Sborman  */
39839503Sborman send_do(option, init)
39939503Sborman 	int option, init;
40038905Sborman {
40139503Sborman 	if (init) {
402*44364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
403*44364Sborman 		    his_want_state_is_will(option))
40439503Sborman 			return;
40539531Sborman 		/*
40639531Sborman 		 * Special case for TELOPT_TM:  We send a DO, but pretend
40739531Sborman 		 * that we sent a DONT, so that we can send more DOs if
40839531Sborman 		 * we want to.
40939531Sborman 		 */
41039531Sborman 		if (option == TELOPT_TM)
411*44364Sborman 			set_his_want_state_wont(option);
41239531Sborman 		else
413*44364Sborman 			set_his_want_state_will(option);
41439503Sborman 		do_dont_resp[option]++;
41539503Sborman 	}
41639503Sborman 	(void) sprintf(nfrontp, doopt, option);
41739503Sborman 	nfrontp += sizeof (dont) - 2;
418*44364Sborman #ifdef DIAGNOSTICS
419*44364Sborman 	/*
420*44364Sborman 	 * Report sending option to other side.
421*44364Sborman 	 */
422*44364Sborman 	if (diagnostic & TD_OPTIONS) {
423*44364Sborman 		printoption("td: send do", option);
424*44364Sborman 	}
425*44364Sborman #endif /* DIAGNOSTICS */
42639503Sborman }
42739503Sborman 
42839503Sborman willoption(option)
42939503Sborman 	int option;
43039503Sborman {
43138905Sborman 	int changeok = 0;
43238905Sborman 
43339503Sborman 	/*
43439503Sborman 	 * process input from peer.
43539503Sborman 	 */
43639503Sborman 
437*44364Sborman #ifdef DIAGNOSTICS
438*44364Sborman 	/*
439*44364Sborman 	 * Report receiving option from other side.
440*44364Sborman 	 */
441*44364Sborman 	if (diagnostic & TD_OPTIONS) {
442*44364Sborman 		printoption("td: recv will", option);
443*44364Sborman 	}
444*44364Sborman #endif /* DIAGNOSTICS */
445*44364Sborman 
44639503Sborman 	if (do_dont_resp[option]) {
44739503Sborman 		do_dont_resp[option]--;
448*44364Sborman 		if (do_dont_resp[option] && his_state_is_will(option))
44939503Sborman 			do_dont_resp[option]--;
45039503Sborman 	}
45139531Sborman 	if (do_dont_resp[option] == 0) {
452*44364Sborman 	    if (his_want_state_is_wont(option)) {
45338905Sborman 		switch (option) {
45438905Sborman 
45538905Sborman 		case TELOPT_BINARY:
45638905Sborman 			init_termbuf();
45738905Sborman 			tty_binaryin(1);
45838905Sborman 			set_termbuf();
45938905Sborman 			changeok++;
46038905Sborman 			break;
46138905Sborman 
46238905Sborman 		case TELOPT_ECHO:
46338905Sborman 			/*
464*44364Sborman 			 * See comments below for more info.
46538905Sborman 			 */
466*44364Sborman 			not42 = 0;	/* looks like a 4.2 system */
46738905Sborman 			break;
46838905Sborman 
46938905Sborman 		case TELOPT_TM:
47038905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
47138905Sborman 			/*
47239503Sborman 			 * This telnetd implementation does not really
47339503Sborman 			 * support timing marks, it just uses them to
47439503Sborman 			 * support the kludge linemode stuff.  If we
47539503Sborman 			 * receive a will or wont TM in response to our
47639503Sborman 			 * do TM request that may have been sent to
47739503Sborman 			 * determine kludge linemode support, process
47839503Sborman 			 * it, otherwise TM should get a negative
47939503Sborman 			 * response back.
48038905Sborman 			 */
48138905Sborman 			/*
48238905Sborman 			 * Handle the linemode kludge stuff.
48338905Sborman 			 * If we are not currently supporting any
48438905Sborman 			 * linemode at all, then we assume that this
48538905Sborman 			 * is the client telling us to use kludge
48638905Sborman 			 * linemode in response to our query.  Set the
48738905Sborman 			 * linemode type that is to be supported, note
48838905Sborman 			 * that the client wishes to use linemode, and
48938905Sborman 			 * eat the will TM as though it never arrived.
49038905Sborman 			 */
49138905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
49238905Sborman 				lmodetype = KLUDGE_LINEMODE;
49338905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
49439503Sborman 				send_wont(TELOPT_SGA, 1);
49538905Sborman 			}
49638905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
49738905Sborman 			/*
49839531Sborman 			 * We never respond to a WILL TM, and
499*44364Sborman 			 * we leave the state WONT.
50038905Sborman 			 */
50138905Sborman 			return;
50238905Sborman 
50338905Sborman 		case TELOPT_LFLOW:
50438905Sborman 			/*
50539503Sborman 			 * If we are going to support flow control
50639503Sborman 			 * option, then don't worry peer that we can't
50739503Sborman 			 * change the flow control characters.
50838905Sborman 			 */
50938905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
51038905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
51138905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
51238905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
51338905Sborman 		case TELOPT_TTYPE:
51438905Sborman 		case TELOPT_SGA:
51538905Sborman 		case TELOPT_NAWS:
51638905Sborman 		case TELOPT_TSPEED:
517*44364Sborman 		case TELOPT_XDISPLOC:
518*44364Sborman 		case TELOPT_ENVIRON:
51939531Sborman 			changeok++;
52039531Sborman 			break;
52139531Sborman 
52238905Sborman #ifdef	LINEMODE
52338905Sborman 		case TELOPT_LINEMODE:
524*44364Sborman 			/*
525*44364Sborman 			 * Local processing of 'will linemode' should
526*44364Sborman 			 * occur after placing 'do linemode' in the data
527*44364Sborman 			 * stream, because we may wish to send other
528*44364Sborman 			 * linemode related messages.  So, we duplicate
529*44364Sborman 			 * the other three lines of code here, and then
530*44364Sborman 			 * return.
531*44364Sborman 			 */
532*44364Sborman 			set_his_want_state_will(option);
533*44364Sborman 			send_do(option, 0);
534*44364Sborman 			set_his_state_will(option);
53539531Sborman # ifdef	KLUDGELINEMODE
53639531Sborman 			/*
53739531Sborman 			 * Note client's desire to use linemode.
53839531Sborman 			 */
53939531Sborman 			lmodetype = REAL_LINEMODE;
54039531Sborman # endif	/* KLUDGELINEMODE */
54139531Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
542*44364Sborman 			return;
54339531Sborman #endif	/* LINEMODE */
54438905Sborman 
54538905Sborman 		default:
54638905Sborman 			break;
54738905Sborman 		}
54839503Sborman 		if (changeok) {
549*44364Sborman 			set_his_want_state_will(option);
55039503Sborman 			send_do(option, 0);
55139503Sborman 		} else {
55239503Sborman 			do_dont_resp[option]++;
55339503Sborman 			send_dont(option, 0);
55438905Sborman 		}
555*44364Sborman 	    } else {
556*44364Sborman 		/*
557*44364Sborman 		 * Option processing that should happen when
558*44364Sborman 		 * we receive conformation of a change in
559*44364Sborman 		 * state that we had requested.
560*44364Sborman 		 */
561*44364Sborman 		switch (option) {
562*44364Sborman 		case TELOPT_ECHO:
563*44364Sborman 			not42 = 0;	/* looks like a 4.2 system */
564*44364Sborman 			/*
565*44364Sborman 			 * Egads, he responded "WILL ECHO".  Turn
566*44364Sborman 			 * it off right now!
567*44364Sborman 			 */
568*44364Sborman 			send_dont(option, 1);
569*44364Sborman 			/*
570*44364Sborman 			 * "WILL ECHO".  Kludge upon kludge!
571*44364Sborman 			 * A 4.2 client is now echoing user input at
572*44364Sborman 			 * the tty.  This is probably undesireable and
573*44364Sborman 			 * it should be stopped.  The client will
574*44364Sborman 			 * respond WONT TM to the DO TM that we send to
575*44364Sborman 			 * check for kludge linemode.  When the WONT TM
576*44364Sborman 			 * arrives, linemode will be turned off and a
577*44364Sborman 			 * change propogated to the pty.  This change
578*44364Sborman 			 * will cause us to process the new pty state
579*44364Sborman 			 * in localstat(), which will notice that
580*44364Sborman 			 * linemode is off and send a WILL ECHO
581*44364Sborman 			 * so that we are properly in character mode and
582*44364Sborman 			 * all is well.
583*44364Sborman 			 */
584*44364Sborman 			break;
585*44364Sborman #ifdef	LINEMODE
586*44364Sborman 		case TELOPT_LINEMODE:
587*44364Sborman # ifdef	KLUDGELINEMODE
588*44364Sborman 			/*
589*44364Sborman 			 * Note client's desire to use linemode.
590*44364Sborman 			 */
591*44364Sborman 			lmodetype = REAL_LINEMODE;
592*44364Sborman # endif	/* KLUDGELINEMODE */
593*44364Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
594*44364Sborman #endif	/* LINEMODE */
595*44364Sborman 		}
59639531Sborman 	    }
59738905Sborman 	}
598*44364Sborman 	set_his_state_will(option);
59938905Sborman }  /* end of willoption */
60038905Sborman 
60139503Sborman send_dont(option, init)
60239503Sborman 	int option, init;
60338905Sborman {
60439503Sborman 	if (init) {
605*44364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
606*44364Sborman 		    his_want_state_is_wont(option))
60739503Sborman 			return;
608*44364Sborman 		set_his_want_state_wont(option);
60939503Sborman 		do_dont_resp[option]++;
61039503Sborman 	}
61139503Sborman 	(void) sprintf(nfrontp, dont, option);
61239503Sborman 	nfrontp += sizeof (doopt) - 2;
613*44364Sborman #ifdef DIAGNOSTICS
614*44364Sborman 	/*
615*44364Sborman 	 * Report sending option to other side.
616*44364Sborman 	 */
617*44364Sborman 	if (diagnostic & TD_OPTIONS) {
618*44364Sborman 		printoption("td: send dont", option);
619*44364Sborman 	}
620*44364Sborman #endif /* DIAGNOSTICS */
62139503Sborman }
62239503Sborman 
62339503Sborman wontoption(option)
62439503Sborman 	int option;
62539503Sborman {
62638905Sborman 	/*
62738905Sborman 	 * Process client input.
62838905Sborman 	 */
62939503Sborman 
630*44364Sborman #ifdef DIAGNOSTICS
631*44364Sborman 	/*
632*44364Sborman 	 * Report receiving option from other side.
633*44364Sborman 	 */
634*44364Sborman 	if (diagnostic & TD_OPTIONS) {
635*44364Sborman 		printoption("td: recv wont", option);
636*44364Sborman 	}
637*44364Sborman #endif /* DIAGNOSTICS */
638*44364Sborman 
63939503Sborman 	if (do_dont_resp[option]) {
64039503Sborman 		do_dont_resp[option]--;
641*44364Sborman 		if (do_dont_resp[option] && his_state_is_wont(option))
64239503Sborman 			do_dont_resp[option]--;
64339503Sborman 	}
64439531Sborman 	if (do_dont_resp[option] == 0) {
645*44364Sborman 	    if (his_want_state_is_will(option)) {
64639503Sborman 		/* it is always ok to change to negative state */
64738905Sborman 		switch (option) {
64838905Sborman 		case TELOPT_ECHO:
64939503Sborman 			not42 = 1; /* doesn't seem to be a 4.2 system */
65038905Sborman 			break;
65138905Sborman 
65238905Sborman 		case TELOPT_BINARY:
65338905Sborman 			init_termbuf();
65438905Sborman 			tty_binaryin(0);
65538905Sborman 			set_termbuf();
65638905Sborman 			break;
65738905Sborman 
65838905Sborman #ifdef	LINEMODE
65938905Sborman 		case TELOPT_LINEMODE:
66038905Sborman # ifdef	KLUDGELINEMODE
66138905Sborman 			/*
66238905Sborman 			 * If real linemode is supported, then client is
66338905Sborman 			 * asking to turn linemode off.
66438905Sborman 			 */
665*44364Sborman 			if (lmodetype != REAL_LINEMODE)
666*44364Sborman 				break;
667*44364Sborman 			lmodetype = KLUDGE_LINEMODE;
66838905Sborman # endif	/* KLUDGELINEMODE */
669*44364Sborman 			clientstat(TELOPT_LINEMODE, WONT, 0);
67038905Sborman 			break;
67138905Sborman #endif	LINEMODE
67238905Sborman 
67338905Sborman 		case TELOPT_TM:
67438905Sborman 			/*
67539503Sborman 			 * If we get a WONT TM, and had sent a DO TM,
67639503Sborman 			 * don't respond with a DONT TM, just leave it
67739503Sborman 			 * as is.  Short circut the state machine to
67839531Sborman 			 * achive this.
67938905Sborman 			 */
680*44364Sborman 			set_his_want_state_wont(TELOPT_TM);
68138905Sborman 			return;
68238905Sborman 
68338905Sborman 		case TELOPT_LFLOW:
68438905Sborman 			/*
68539503Sborman 			 * If we are not going to support flow control
68639503Sborman 			 * option, then let peer know that we can't
68739503Sborman 			 * change the flow control characters.
68838905Sborman 			 */
68938905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
69038905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
69138905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
69238905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
69338905Sborman 			break;
69438905Sborman 
695*44364Sborman 		/*
696*44364Sborman 		 * For options that we might spin waiting for
697*44364Sborman 		 * sub-negotiation, if the client turns off the
698*44364Sborman 		 * option rather than responding to the request,
699*44364Sborman 		 * we have to treat it here as if we got a response
700*44364Sborman 		 * to the sub-negotiation, (by updating the timers)
701*44364Sborman 		 * so that we'll break out of the loop.
702*44364Sborman 		 */
703*44364Sborman 		case TELOPT_TTYPE:
704*44364Sborman 			settimer(ttypesubopt);
705*44364Sborman 			break;
706*44364Sborman 
707*44364Sborman 		case TELOPT_TSPEED:
708*44364Sborman 			settimer(tspeedsubopt);
709*44364Sborman 			break;
710*44364Sborman 
711*44364Sborman 		case TELOPT_XDISPLOC:
712*44364Sborman 			settimer(xdisplocsubopt);
713*44364Sborman 			break;
714*44364Sborman 
715*44364Sborman 		case TELOPT_ENVIRON:
716*44364Sborman 			settimer(environsubopt);
717*44364Sborman 			break;
718*44364Sborman 
71938905Sborman 		default:
72038905Sborman 			break;
72138905Sborman 		}
722*44364Sborman 		set_his_want_state_wont(option);
723*44364Sborman 		if (his_state_is_will(option))
724*44364Sborman 			send_dont(option, 0);
72539531Sborman 	    } else {
72639531Sborman 		switch (option) {
72739531Sborman 		case TELOPT_TM:
72839531Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
72939531Sborman 			if (lmodetype < REAL_LINEMODE) {
73039531Sborman 				lmodetype = NO_LINEMODE;
73139531Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
73239531Sborman 				send_will(TELOPT_SGA, 1);
73339531Sborman /*@*/				send_will(TELOPT_ECHO, 1);
73439531Sborman 			}
73539531Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
73639531Sborman 		default:
73739531Sborman 			break;
73839531Sborman 		}
73939531Sborman 	    }
74038905Sborman 	}
741*44364Sborman 	set_his_state_wont(option);
74238905Sborman 
74339503Sborman }  /* end of wontoption */
74438905Sborman 
74539503Sborman send_will(option, init)
74639503Sborman 	int option, init;
74739503Sborman {
74839503Sborman 	if (init) {
749*44364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
750*44364Sborman 		    my_want_state_is_will(option))
75139503Sborman 			return;
752*44364Sborman 		set_my_want_state_will(option);
75339503Sborman 		will_wont_resp[option]++;
75438905Sborman 	}
75539503Sborman 	(void) sprintf(nfrontp, will, option);
75639503Sborman 	nfrontp += sizeof (doopt) - 2;
757*44364Sborman #ifdef DIAGNOSTICS
758*44364Sborman 	/*
759*44364Sborman 	 * Report sending option to other side.
760*44364Sborman 	 */
761*44364Sborman 	if (diagnostic & TD_OPTIONS) {
762*44364Sborman 		printoption("td: send will", option);
763*44364Sborman 	}
764*44364Sborman #endif /* DIAGNOSTICS */
76539503Sborman }
76638905Sborman 
76739503Sborman dooption(option)
76839503Sborman 	int option;
76938905Sborman {
77038905Sborman 	int changeok = 0;
77138905Sborman 
77238905Sborman 	/*
77338905Sborman 	 * Process client input.
77438905Sborman 	 */
77539503Sborman 
776*44364Sborman #ifdef DIAGNOSTICS
777*44364Sborman 	/*
778*44364Sborman 	 * Report receiving option from other side.
779*44364Sborman 	 */
780*44364Sborman 	if (diagnostic & TD_OPTIONS) {
781*44364Sborman 		printoption("td: recv do", option);
782*44364Sborman 	}
783*44364Sborman #endif /* DIAGNOSTICS */
784*44364Sborman 
78539503Sborman 	if (will_wont_resp[option]) {
78639503Sborman 		will_wont_resp[option]--;
787*44364Sborman 		if (will_wont_resp[option] && my_state_is_will(option))
78839503Sborman 			will_wont_resp[option]--;
78939503Sborman 	}
790*44364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
79138905Sborman 		switch (option) {
79238905Sborman 		case TELOPT_ECHO:
79338905Sborman #ifdef	LINEMODE
79439503Sborman 			if (lmodetype == NO_LINEMODE) {
79538905Sborman #endif
79638905Sborman 				init_termbuf();
79738905Sborman 				tty_setecho(1);
79838905Sborman 				set_termbuf();
79938905Sborman #ifdef	LINEMODE
80038905Sborman 			}
80138905Sborman #endif
80238905Sborman 			changeok++;
80338905Sborman 			break;
80438905Sborman 
80538905Sborman 		case TELOPT_BINARY:
80638905Sborman 			init_termbuf();
80738905Sborman 			tty_binaryout(1);
80838905Sborman 			set_termbuf();
80938905Sborman 			changeok++;
81038905Sborman 			break;
81138905Sborman 
81238905Sborman 		case TELOPT_SGA:
81338905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
81438905Sborman 			/*
81539503Sborman 			 * If kludge linemode is in use, then we must
81639503Sborman 			 * process an incoming do SGA for linemode
81739503Sborman 			 * purposes.
81838905Sborman 			 */
81938905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
82038905Sborman 				/*
82139503Sborman 				 * Receipt of "do SGA" in kludge
82239503Sborman 				 * linemode is the peer asking us to
82339503Sborman 				 * turn off linemode.  Make note of
82439503Sborman 				 * the request.
82538905Sborman 				 */
82638905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
82738905Sborman 				/*
82839503Sborman 				 * If linemode did not get turned off
82939503Sborman 				 * then don't tell peer that we did.
83039503Sborman 				 * Breaking here forces a wont SGA to
83139503Sborman 				 * be returned.
83238905Sborman 				 */
83338905Sborman 				if (linemode)
83438905Sborman 					break;
83538905Sborman 			}
83638905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
83738905Sborman 			changeok++;
83838905Sborman 			break;
83938905Sborman 
84038905Sborman 		case TELOPT_STATUS:
84138905Sborman 			changeok++;
84238905Sborman 			break;
84338905Sborman 
84438905Sborman 		case TELOPT_TM:
84539503Sborman 			/*
84639503Sborman 			 * Special case for TM.  We send a WILL, but
84739503Sborman 			 * pretend we sent a WONT.
84839503Sborman 			 */
84939503Sborman 			send_will(option, 0);
850*44364Sborman 			set_my_want_state_wont(option);
851*44364Sborman 			set_my_state_wont(option);
85239503Sborman 			return;
85339503Sborman 
85438905Sborman 		case TELOPT_LINEMODE:
85538905Sborman 		case TELOPT_TTYPE:
85638905Sborman 		case TELOPT_NAWS:
85738905Sborman 		case TELOPT_TSPEED:
85838905Sborman 		case TELOPT_LFLOW:
859*44364Sborman 		case TELOPT_XDISPLOC:
860*44364Sborman 		case TELOPT_ENVIRON:
86138905Sborman 		default:
86238905Sborman 			break;
86338905Sborman 		}
86439503Sborman 		if (changeok) {
865*44364Sborman 			set_my_want_state_will(option);
86639503Sborman 			send_will(option, 0);
86739503Sborman 		} else {
86839503Sborman 			will_wont_resp[option]++;
86939503Sborman 			send_wont(option, 0);
87038905Sborman 		}
87138905Sborman 	}
872*44364Sborman 	set_my_state_will(option);
87338905Sborman 
87438905Sborman }  /* end of dooption */
87538905Sborman 
87639503Sborman send_wont(option, init)
87739503Sborman 	int option, init;
87839503Sborman {
87939503Sborman 	if (init) {
880*44364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
881*44364Sborman 		    my_want_state_is_wont(option))
88239503Sborman 			return;
883*44364Sborman 		set_my_want_state_wont(option);
88439503Sborman 		will_wont_resp[option]++;
88539503Sborman 	}
88639503Sborman 	(void) sprintf(nfrontp, wont, option);
88739503Sborman 	nfrontp += sizeof (wont) - 2;
888*44364Sborman #ifdef DIAGNOSTICS
889*44364Sborman 	/*
890*44364Sborman 	 * Report sending option to other side.
891*44364Sborman 	 */
892*44364Sborman 	if (diagnostic & TD_OPTIONS) {
893*44364Sborman 		printoption("td: send wont", option);
894*44364Sborman 	}
895*44364Sborman #endif /* DIAGNOSTICS */
89639503Sborman }
89738905Sborman 
89839503Sborman dontoption(option)
89939503Sborman 	int option;
90038905Sborman {
90138905Sborman 	/*
90238905Sborman 	 * Process client input.
90338905Sborman 	 */
904*44364Sborman #ifdef DIAGNOSTICS
905*44364Sborman 	/*
906*44364Sborman 	 * Report receiving option from other side.
907*44364Sborman 	 */
908*44364Sborman 	if (diagnostic & TD_OPTIONS) {
909*44364Sborman 		printoption("td: recv dont", option);
910*44364Sborman 	}
911*44364Sborman #endif /* DIAGNOSTICS */
91240242Sborman 
91339503Sborman 	if (will_wont_resp[option]) {
91439503Sborman 		will_wont_resp[option]--;
915*44364Sborman 		if (will_wont_resp[option] && my_state_is_wont(option))
91639503Sborman 			will_wont_resp[option]--;
91739503Sborman 	}
918*44364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
91938905Sborman 		switch (option) {
92038905Sborman 		case TELOPT_BINARY:
92138905Sborman 			init_termbuf();
92238905Sborman 			tty_binaryout(0);
92338905Sborman 			set_termbuf();
92438905Sborman 			break;
92538905Sborman 
92639503Sborman 		case TELOPT_ECHO:	/* we should stop echoing */
92738905Sborman #ifdef	LINEMODE
92839503Sborman 			if (lmodetype == NO_LINEMODE) {
92938905Sborman #endif
93038905Sborman 				init_termbuf();
93138905Sborman 				tty_setecho(0);
93238905Sborman 				set_termbuf();
93338905Sborman #ifdef	LINEMODE
93438905Sborman 			}
93538905Sborman #endif
93638905Sborman 			break;
93738905Sborman 
93838905Sborman 		case TELOPT_SGA:
93938905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
94038905Sborman 			/*
94139503Sborman 			 * If kludge linemode is in use, then we
94239503Sborman 			 * must process an incoming do SGA for
94339503Sborman 			 * linemode purposes.
94438905Sborman 			 */
94538905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
94638905Sborman 				/*
94739503Sborman 				 * The client is asking us to turn
94839503Sborman 				 * linemode on.
94938905Sborman 				 */
95038905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
95138905Sborman 				/*
95239503Sborman 				 * If we did not turn line mode on,
95339503Sborman 				 * then what do we say?  Will SGA?
95439503Sborman 				 * This violates design of telnet.
95539503Sborman 				 * Gross.  Very Gross.
95638905Sborman 				 */
95738905Sborman 			}
95838905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
95938905Sborman 
96038905Sborman 		default:
96138905Sborman 			break;
96238905Sborman 		}
96338905Sborman 
964*44364Sborman 		set_my_want_state_wont(option);
965*44364Sborman 		if (my_state_is_will(option))
966*44364Sborman 			send_wont(option, 0);
96738905Sborman 	}
968*44364Sborman 	set_my_state_wont(option);
96938905Sborman 
97038905Sborman }  /* end of dontoption */
97138905Sborman 
97238905Sborman /*
97338905Sborman  * suboption()
97438905Sborman  *
97538905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
97638905Sborman  * side.
97738905Sborman  *
97838905Sborman  *	Currently we recognize:
97938905Sborman  *
98038905Sborman  *	Terminal type is
98138905Sborman  *	Linemode
98238905Sborman  *	Window size
98338905Sborman  *	Terminal speed
98438905Sborman  */
98538905Sborman suboption()
98638905Sborman {
98738905Sborman     register int subchar;
988*44364Sborman     extern void unsetenv();
98938905Sborman 
990*44364Sborman #ifdef DIAGNOSTICS
991*44364Sborman 	/*
992*44364Sborman 	 * Report receiving option from other side.
993*44364Sborman 	 */
994*44364Sborman 	if (diagnostic & TD_OPTIONS) {
995*44364Sborman 		netflush();	/* get rid of anything waiting to go out */
996*44364Sborman 		printsub("td: recv", subpointer, SB_LEN()+2);
997*44364Sborman 	}
998*44364Sborman #endif	DIAGNOSTIC
99938905Sborman     subchar = SB_GET();
100038905Sborman     switch (subchar) {
100138905Sborman     case TELOPT_TSPEED: {
100238905Sborman 	register int xspeed, rspeed;
100338905Sborman 
1004*44364Sborman 	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
100538905Sborman 		break;
100638905Sborman 
100738905Sborman 	settimer(tspeedsubopt);
100838905Sborman 
100938905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
101038905Sborman 		return;
101138905Sborman 
101238905Sborman 	xspeed = atoi(subpointer);
101338905Sborman 
101438905Sborman 	while (SB_GET() != ',' && !SB_EOF());
101538905Sborman 	if (SB_EOF())
101638905Sborman 		return;
101738905Sborman 
101838905Sborman 	rspeed = atoi(subpointer);
101938905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
102038905Sborman 
102138905Sborman 	break;
102238905Sborman 
102338905Sborman     }  /* end of case TELOPT_TSPEED */
102438905Sborman 
102538905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
1026*44364Sborman 	static char terminalname[41];
102738905Sborman 
1028*44364Sborman 	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
102938905Sborman 		break;
103038905Sborman 	settimer(ttypesubopt);
103138905Sborman 
103238905Sborman 	if (SB_GET() != TELQUAL_IS) {
103338905Sborman 	    return;		/* ??? XXX but, this is the most robust */
103438905Sborman 	}
103538905Sborman 
1036*44364Sborman 	terminaltype = terminalname;
103738905Sborman 
103838905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
103938905Sborman 								    !SB_EOF()) {
104038905Sborman 	    register int c;
104138905Sborman 
104238905Sborman 	    c = SB_GET();
104338905Sborman 	    if (isupper(c)) {
104438905Sborman 		c = tolower(c);
104538905Sborman 	    }
104638905Sborman 	    *terminaltype++ = c;    /* accumulate name */
104738905Sborman 	}
104838905Sborman 	*terminaltype = 0;
104938905Sborman 	terminaltype = terminalname;
105038905Sborman 	break;
105138905Sborman     }  /* end of case TELOPT_TTYPE */
105238905Sborman 
105338905Sborman     case TELOPT_NAWS: {
105438905Sborman 	register int xwinsize, ywinsize;
105538905Sborman 
1056*44364Sborman 	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
105738905Sborman 		break;
105838905Sborman 
105938905Sborman 	if (SB_EOF())
106038905Sborman 		return;
106138905Sborman 	xwinsize = SB_GET() << 8;
106238905Sborman 	if (SB_EOF())
106338905Sborman 		return;
106438905Sborman 	xwinsize |= SB_GET();
106538905Sborman 	if (SB_EOF())
106638905Sborman 		return;
106738905Sborman 	ywinsize = SB_GET() << 8;
106838905Sborman 	if (SB_EOF())
106938905Sborman 		return;
107038905Sborman 	ywinsize |= SB_GET();
107138905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
107238905Sborman 
107338905Sborman 	break;
107438905Sborman 
107538905Sborman     }  /* end of case TELOPT_NAWS */
107638905Sborman 
107738905Sborman #ifdef	LINEMODE
107838905Sborman     case TELOPT_LINEMODE: {
107938905Sborman 	register int request;
108038905Sborman 
1081*44364Sborman 	if (his_state_is_wont(TELOPT_LINEMODE))	/* Ignore if option disabled */
108238905Sborman 		break;
108338905Sborman 	/*
108438905Sborman 	 * Process linemode suboptions.
108538905Sborman 	 */
108638905Sborman 	if (SB_EOF()) break;  /* garbage was sent */
108738905Sborman 	request = SB_GET();  /* get will/wont */
108838905Sborman 	if (SB_EOF()) break;  /* another garbage check */
108938905Sborman 
109038905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
109138905Sborman 		/*
109238905Sborman 		 * Process suboption buffer of slc's
109338905Sborman 		 */
109438905Sborman 		start_slc(1);
109538905Sborman 		do_opt_slc(subpointer, subend - subpointer);
109638905Sborman 		end_slc(0);
109738905Sborman 
109838905Sborman 	} else if (request == LM_MODE) {
109938905Sborman 		useeditmode = SB_GET();  /* get mode flag */
110038905Sborman 		clientstat(LM_MODE, 0, 0);
110138905Sborman 	}
110238905Sborman 
110338905Sborman 	switch (SB_GET()) {  /* what suboption? */
110438905Sborman 	case LM_FORWARDMASK:
110538905Sborman 		/*
110638905Sborman 		 * According to spec, only server can send request for
110738905Sborman 		 * forwardmask, and client can only return a positive response.
110838905Sborman 		 * So don't worry about it.
110938905Sborman 		 */
111038905Sborman 
111138905Sborman 	default:
111238905Sborman 		break;
111338905Sborman 	}
111440242Sborman 	break;
111538905Sborman     }  /* end of case TELOPT_LINEMODE */
111638905Sborman #endif
111738905Sborman     case TELOPT_STATUS: {
111838905Sborman 	int mode;
111938905Sborman 
112038905Sborman 	mode = SB_GET();
112138905Sborman 	switch (mode) {
112238905Sborman 	case TELQUAL_SEND:
1123*44364Sborman 	    if (my_state_is_will(TELOPT_STATUS))
112438905Sborman 		send_status();
112538905Sborman 	    break;
112638905Sborman 
112738905Sborman 	case TELQUAL_IS:
112838905Sborman 	    break;
112938905Sborman 
113038905Sborman 	default:
113138905Sborman 	    break;
113238905Sborman 	}
113340242Sborman 	break;
113440242Sborman     }  /* end of case TELOPT_STATUS */
113538905Sborman 
1136*44364Sborman     case TELOPT_XDISPLOC: {
1137*44364Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
1138*44364Sborman 		return;
1139*44364Sborman 	settimer(xdisplocsubopt);
1140*44364Sborman 	subpointer[SB_LEN()] = '\0';
1141*44364Sborman 	setenv("DISPLAY", subpointer, 1);
1142*44364Sborman 	break;
1143*44364Sborman     }  /* end of case TELOPT_XDISPLOC */
1144*44364Sborman 
1145*44364Sborman     case TELOPT_ENVIRON: {
1146*44364Sborman 	register int c;
1147*44364Sborman 	register char *cp, *varp, *valp;
1148*44364Sborman 
1149*44364Sborman 	if (SB_EOF())
1150*44364Sborman 		return;
1151*44364Sborman 	c = SB_GET();
1152*44364Sborman 	if (c == TELQUAL_IS)
1153*44364Sborman 		settimer(environsubopt);
1154*44364Sborman 	else if (c != TELQUAL_INFO)
1155*44364Sborman 		return;
1156*44364Sborman 
1157*44364Sborman 	while (!SB_EOF() && SB_GET() != ENV_VAR)
1158*44364Sborman 		;
1159*44364Sborman 
1160*44364Sborman 	if (SB_EOF())
1161*44364Sborman 		return;
1162*44364Sborman 
1163*44364Sborman 	cp = varp = subpointer;
1164*44364Sborman 	valp = 0;
1165*44364Sborman 
1166*44364Sborman 	while (!SB_EOF()) {
1167*44364Sborman 		switch (c = SB_GET()) {
1168*44364Sborman 		case ENV_VALUE:
1169*44364Sborman 			*cp = '\0';
1170*44364Sborman 			cp = valp = subpointer;
1171*44364Sborman 			break;
1172*44364Sborman 
1173*44364Sborman 		case ENV_VAR:
1174*44364Sborman 			*cp = '\0';
1175*44364Sborman 			if (valp)
1176*44364Sborman 				setenv(varp, valp, 1);
1177*44364Sborman 			else
1178*44364Sborman 				unsetenv(varp);
1179*44364Sborman 			cp = varp = subpointer;
1180*44364Sborman 			valp = 0;
1181*44364Sborman 			break;
1182*44364Sborman 
1183*44364Sborman 		case ENV_ESC:
1184*44364Sborman 			if (SB_EOF())
1185*44364Sborman 				break;
1186*44364Sborman 			c = SB_GET();
1187*44364Sborman 			/* FALL THROUGH */
1188*44364Sborman 		default:
1189*44364Sborman 			*cp++ = c;
1190*44364Sborman 			break;
1191*44364Sborman 		}
1192*44364Sborman 	}
1193*44364Sborman 	*cp = '\0';
1194*44364Sborman 	if (valp)
1195*44364Sborman 		setenv(varp, valp, 1);
1196*44364Sborman 	else
1197*44364Sborman 		unsetenv(varp);
1198*44364Sborman 	break;
1199*44364Sborman     }  /* end of case TELOPT_ENVIRON */
1200*44364Sborman 
120138905Sborman     default:
120238905Sborman 	break;
120338905Sborman     }  /* end of switch */
120438905Sborman 
120538905Sborman }  /* end of suboption */
120638905Sborman 
120738905Sborman #define	ADD(c)	 *ncp++ = c;
120838905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
120938905Sborman send_status()
121038905Sborman {
1211*44364Sborman 	unsigned char statusbuf[256];
1212*44364Sborman 	register unsigned char *ncp;
1213*44364Sborman 	register unsigned char i;
121438905Sborman 
121538905Sborman 	ncp = statusbuf;
121638905Sborman 
121738905Sborman 	netflush();	/* get rid of anything waiting to go out */
121838905Sborman 
121938905Sborman 	ADD(IAC);
122038905Sborman 	ADD(SB);
122138905Sborman 	ADD(TELOPT_STATUS);
122238905Sborman 	ADD(TELQUAL_IS);
122338905Sborman 
122438905Sborman 	for (i = 0; i < NTELOPTS; i++) {
1225*44364Sborman 		if (my_state_is_will(i)) {
122638905Sborman 			ADD(WILL);
122738905Sborman 			ADD_DATA(i);
122838905Sborman 			if (i == IAC)
122938905Sborman 				ADD(IAC);
123038905Sborman 		}
1231*44364Sborman 		if (his_state_is_will(i)) {
123238905Sborman 			ADD(DO);
123338905Sborman 			ADD_DATA(i);
123438905Sborman 			if (i == IAC)
123538905Sborman 				ADD(IAC);
123638905Sborman 		}
123738905Sborman 	}
123838905Sborman 
123938905Sborman #ifdef	LINEMODE
1240*44364Sborman 	if (his_state_is_will(TELOPT_LINEMODE)) {
1241*44364Sborman 		unsigned char *cp, *cpe;
124238905Sborman 		int len;
124338905Sborman 
124438905Sborman 		ADD(SB);
124538905Sborman 		ADD(TELOPT_LINEMODE);
124638905Sborman 		ADD(LM_MODE);
124738905Sborman 		ADD_DATA(editmode);
124838905Sborman 		if (editmode == IAC)
124938905Sborman 			ADD(IAC);
125038905Sborman 		ADD(SE);
125138905Sborman 
125238905Sborman 		ADD(SB);
125338905Sborman 		ADD(TELOPT_LINEMODE);
125438905Sborman 		ADD(LM_SLC);
125538905Sborman 		start_slc(0);
125638905Sborman 		send_slc();
125738905Sborman 		len = end_slc(&cp);
125838905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
125938905Sborman 			ADD_DATA(*cp);
126038905Sborman 		ADD(SE);
126138905Sborman 	}
126238905Sborman #endif	/* LINEMODE */
126338905Sborman 
126438905Sborman 	ADD(IAC);
126538905Sborman 	ADD(SE);
126638905Sborman 
126738905Sborman 	writenet(statusbuf, ncp - statusbuf);
126838905Sborman 	netflush();	/* Send it on its way */
1269*44364Sborman #ifdef DIAGNOSTICS
1270*44364Sborman 	/*
1271*44364Sborman 	 * Report sending status suboption.
1272*44364Sborman 	 */
1273*44364Sborman 	if (diagnostic & TD_OPTIONS) {
1274*44364Sborman 		printsub("td: send", statusbuf, ncp - statusbuf);
1275*44364Sborman 		netflush();	/* Send it on its way */
1276*44364Sborman 	}
1277*44364Sborman #endif	DIAGNOSTIC
127838905Sborman }
1279*44364Sborman 
1280*44364Sborman #ifdef	NO_SETENV
1281*44364Sborman #include "setenv.c"
1282*44364Sborman #endif
1283