xref: /csrg-svn/libexec/telnetd/state.c (revision 45234)
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*45234Sborman static char sccsid[] = "@(#)state.c	5.8 (Berkeley) 09/14/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)
3344364Sborman #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 			 */
8844364Sborman 			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:
11244364Sborman #ifdef DIAGNOSTICS
11344364Sborman 				if (diagnostic & TD_OPTIONS)
11444364Sborman 					printoption("td: recv IAC", c);
11544364Sborman #endif /* DIAGNOSTICS */
11638905Sborman 				interrupt();
11738905Sborman 				break;
11838905Sborman 
11938905Sborman 			case BREAK:
12044364Sborman #ifdef DIAGNOSTICS
12144364Sborman 				if (diagnostic & TD_OPTIONS)
12244364Sborman 					printoption("td: recv IAC", c);
12344364Sborman #endif /* DIAGNOSTICS */
12438905Sborman 				sendbrk();
12538905Sborman 				break;
12638905Sborman 
12738905Sborman 			/*
12838905Sborman 			 * Are You There?
12938905Sborman 			 */
13038905Sborman 			case AYT:
13144364Sborman #ifdef DIAGNOSTICS
13244364Sborman 				if (diagnostic & TD_OPTIONS)
13344364Sborman 					printoption("td: recv IAC", c);
13444364Sborman #endif /* DIAGNOSTICS */
135*45234Sborman 				recv_ayt();
13638905Sborman 				break;
13738905Sborman 
13838905Sborman 			/*
13938905Sborman 			 * Abort Output
14038905Sborman 			 */
14138905Sborman 			case AO:
14238905Sborman 			    {
14344364Sborman #ifdef DIAGNOSTICS
14444364Sborman 				if (diagnostic & TD_OPTIONS)
14544364Sborman 					printoption("td: recv IAC", c);
14644364Sborman #endif /* DIAGNOSTICS */
14738905Sborman 				ptyflush();	/* half-hearted */
14838905Sborman 				init_termbuf();
14938905Sborman 
15038905Sborman 				if (slctab[SLC_AO].sptr &&
151*45234Sborman 				    *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
15240242Sborman 				    *pfrontp++ =
15340242Sborman 					(unsigned char)*slctab[SLC_AO].sptr;
15438905Sborman 				}
15538905Sborman 
15638905Sborman 				netclear();	/* clear buffer back */
15738905Sborman 				*nfrontp++ = IAC;
15838905Sborman 				*nfrontp++ = DM;
15938905Sborman 				neturg = nfrontp-1; /* off by one XXX */
16044364Sborman #ifdef DIAGNOSTICS
16144364Sborman 				if (diagnostic & TD_OPTIONS)
16244364Sborman 					printoption("td: send IAC", DM);
16344364Sborman #endif /* DIAGNOSTICS */
16438905Sborman 				break;
16538905Sborman 			    }
16638905Sborman 
16738905Sborman 			/*
16838905Sborman 			 * Erase Character and
16938905Sborman 			 * Erase Line
17038905Sborman 			 */
17138905Sborman 			case EC:
17238905Sborman 			case EL:
17338905Sborman 			    {
17440242Sborman 				cc_t ch;
17538905Sborman 
17644364Sborman #ifdef DIAGNOSTICS
17744364Sborman 				if (diagnostic & TD_OPTIONS)
17844364Sborman 					printoption("td: recv IAC", c);
17944364Sborman #endif /* DIAGNOSTICS */
18038905Sborman 				ptyflush();	/* half-hearted */
18138905Sborman 				init_termbuf();
18244364Sborman 				if (c == EC)
18344364Sborman 					ch = *slctab[SLC_EC].sptr;
18444364Sborman 				else
18544364Sborman 					ch = *slctab[SLC_EL].sptr;
186*45234Sborman 				if (ch != (cc_t)(_POSIX_VDISABLE))
18740242Sborman 					*pfrontp++ = (unsigned char)ch;
18838905Sborman 				break;
18938905Sborman 			    }
19038905Sborman 
19138905Sborman 			/*
19238905Sborman 			 * Check for urgent data...
19338905Sborman 			 */
19438905Sborman 			case DM:
19544364Sborman #ifdef DIAGNOSTICS
19644364Sborman 				if (diagnostic & TD_OPTIONS)
19744364Sborman 					printoption("td: recv IAC", c);
19844364Sborman #endif /* DIAGNOSTICS */
19938905Sborman 				SYNCHing = stilloob(net);
20038905Sborman 				settimer(gotDM);
20138905Sborman 				break;
20238905Sborman 
20338905Sborman 
20438905Sborman 			/*
20538905Sborman 			 * Begin option subnegotiation...
20638905Sborman 			 */
20738905Sborman 			case SB:
20838905Sborman 				state = TS_SB;
20938905Sborman 				SB_CLEAR();
21038905Sborman 				continue;
21138905Sborman 
21238905Sborman 			case WILL:
21338905Sborman 				state = TS_WILL;
21438905Sborman 				continue;
21538905Sborman 
21638905Sborman 			case WONT:
21738905Sborman 				state = TS_WONT;
21838905Sborman 				continue;
21938905Sborman 
22038905Sborman 			case DO:
22138905Sborman 				state = TS_DO;
22238905Sborman 				continue;
22338905Sborman 
22438905Sborman 			case DONT:
22538905Sborman 				state = TS_DONT;
22638905Sborman 				continue;
22738905Sborman 			case EOR:
22844364Sborman 				if (his_state_is_will(TELOPT_EOR))
22938905Sborman 					doeof();
23038905Sborman 				break;
23138905Sborman 
23238905Sborman 			/*
23338905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
23438905Sborman 			 * to command stream (EOF, SUSP, ABORT).
23538905Sborman 			 */
23638905Sborman 			case xEOF:
23738905Sborman 				doeof();
23838905Sborman 				break;
23938905Sborman 
24038905Sborman 			case SUSP:
24138905Sborman 				sendsusp();
24238905Sborman 				break;
24338905Sborman 
24438905Sborman 			case ABORT:
24538905Sborman 				sendbrk();
24638905Sborman 				break;
24738905Sborman 
24838905Sborman 			case IAC:
24938905Sborman 				*pfrontp++ = c;
25038905Sborman 				break;
25138905Sborman 			}
25238905Sborman 			state = TS_DATA;
25338905Sborman 			break;
25438905Sborman 
25538905Sborman 		case TS_SB:
25638905Sborman 			if (c == IAC) {
25738905Sborman 				state = TS_SE;
25838905Sborman 			} else {
25938905Sborman 				SB_ACCUM(c);
26038905Sborman 			}
26138905Sborman 			break;
26238905Sborman 
26338905Sborman 		case TS_SE:
26438905Sborman 			if (c != SE) {
26538905Sborman 				if (c != IAC) {
26638905Sborman 					/*
26738905Sborman 					 * bad form of suboption negotiation.
26838905Sborman 					 * handle it in such a way as to avoid
26938905Sborman 					 * damage to local state.  Parse
27038905Sborman 					 * suboption buffer found so far,
27138905Sborman 					 * then treat remaining stream as
27238905Sborman 					 * another command sequence.
27338905Sborman 					 */
27444364Sborman #ifdef	DIAGNOSTICS
27544364Sborman 					SB_ACCUM(IAC);
27644364Sborman 					SB_ACCUM(c);
27744364Sborman 					subpointer -= 2;
27844364Sborman #endif
27938905Sborman 					SB_TERM();
28038905Sborman 					suboption();
28138905Sborman 					state = TS_IAC;
28238905Sborman 					goto gotiac;
28338905Sborman 				}
28438905Sborman 				SB_ACCUM(c);
28538905Sborman 				state = TS_SB;
28638905Sborman 			} else {
28744364Sborman #ifdef	DIAGNOSTICS
28844364Sborman 				SB_ACCUM(IAC);
28944364Sborman 				SB_ACCUM(SE);
29044364Sborman 				subpointer -= 2;
29144364Sborman #endif
29238905Sborman 				SB_TERM();
29338905Sborman 				suboption();	/* handle sub-option */
29438905Sborman 				state = TS_DATA;
29538905Sborman 			}
29638905Sborman 			break;
29738905Sborman 
29838905Sborman 		case TS_WILL:
29939503Sborman 			willoption(c);
30038905Sborman 			state = TS_DATA;
30138905Sborman 			continue;
30238905Sborman 
30338905Sborman 		case TS_WONT:
30439503Sborman 			wontoption(c);
30538905Sborman 			state = TS_DATA;
30638905Sborman 			continue;
30738905Sborman 
30838905Sborman 		case TS_DO:
30939503Sborman 			dooption(c);
31038905Sborman 			state = TS_DATA;
31138905Sborman 			continue;
31238905Sborman 
31338905Sborman 		case TS_DONT:
31439503Sborman 			dontoption(c);
31538905Sborman 			state = TS_DATA;
31638905Sborman 			continue;
31738905Sborman 
31838905Sborman 		default:
31938905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
32038905Sborman 			printf("telnetd: panic state=%d\n", state);
32138905Sborman 			exit(1);
32238905Sborman 		}
32338905Sborman 	}
32440242Sborman #if	defined(CRAY2) && defined(UNICOS5)
32538905Sborman 	if (!linemode) {
32638905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
32738905Sborman 		char	xbuf2[BUFSIZ];
32838905Sborman 		register char *cp;
32938905Sborman 		int n = pfrontp - opfrontp, oc;
33038905Sborman 		bcopy(opfrontp, xptyobuf, n);
33138905Sborman 		pfrontp = opfrontp;
33238905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
33338905Sborman 					xbuf2, &oc, BUFSIZ);
33438905Sborman 		for (cp = xbuf2; oc > 0; --oc)
33538905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
33638905Sborman 				*nfrontp++ = IAC;
33738905Sborman 	}
33840242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
33938905Sborman }  /* end of telrcv */
34038905Sborman 
34138905Sborman /*
34238905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
34344364Sborman  * Telnet option processing state machine.
34438905Sborman  *
34538905Sborman  * These correspond to the following states:
34638905Sborman  *	my_state = the last negotiated state
34738905Sborman  *	want_state = what I want the state to go to
34838905Sborman  *	want_resp = how many requests I have sent
34938905Sborman  * All state defaults are negative, and resp defaults to 0.
35038905Sborman  *
35138905Sborman  * When initiating a request to change state to new_state:
35238905Sborman  *
35338905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
35438905Sborman  *	do nothing;
35538905Sborman  * } else {
35638905Sborman  *	want_state = new_state;
35738905Sborman  *	send new_state;
35838905Sborman  *	want_resp++;
35938905Sborman  * }
36038905Sborman  *
36138905Sborman  * When receiving new_state:
36238905Sborman  *
36338905Sborman  * if (want_resp) {
36438905Sborman  *	want_resp--;
36538905Sborman  *	if (want_resp && (new_state == my_state))
36638905Sborman  *		want_resp--;
36738905Sborman  * }
36838905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
36938905Sborman  *	if (ok_to_switch_to new_state)
37038905Sborman  *		want_state = new_state;
37138905Sborman  *	else
37238905Sborman  *		want_resp++;
37338905Sborman  *	send want_state;
37438905Sborman  * }
37538905Sborman  * my_state = new_state;
37638905Sborman  *
37738905Sborman  * Note that new_state is implied in these functions by the function itself.
37838905Sborman  * will and do imply positive new_state, wont and dont imply negative.
37938905Sborman  *
38038905Sborman  * Finally, there is one catch.  If we send a negative response to a
38138905Sborman  * positive request, my_state will be the positive while want_state will
38238905Sborman  * remain negative.  my_state will revert to negative when the negative
38338905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
38438905Sborman  * us not only the last negotiated state, but also tells us what the peer
38538905Sborman  * wants to be doing as well.  It is important to understand this difference
38638905Sborman  * as we may wish to be processing data streams based on our desired state
38738905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
38838905Sborman  *
38938905Sborman  * This all works fine because if the peer sends a positive request, the data
39038905Sborman  * that we receive prior to negative acknowlegment will probably be affected
39138905Sborman  * by the positive state, and we can process it as such (if we can; if we
39238905Sborman  * can't then it really doesn't matter).  If it is that important, then the
39338905Sborman  * peer probably should be buffering until this option state negotiation
39438905Sborman  * is complete.
39538905Sborman  *
39638905Sborman  */
39739503Sborman send_do(option, init)
39839503Sborman 	int option, init;
39938905Sborman {
40039503Sborman 	if (init) {
40144364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
40244364Sborman 		    his_want_state_is_will(option))
40339503Sborman 			return;
40439531Sborman 		/*
40539531Sborman 		 * Special case for TELOPT_TM:  We send a DO, but pretend
40639531Sborman 		 * that we sent a DONT, so that we can send more DOs if
40739531Sborman 		 * we want to.
40839531Sborman 		 */
40939531Sborman 		if (option == TELOPT_TM)
41044364Sborman 			set_his_want_state_wont(option);
41139531Sborman 		else
41244364Sborman 			set_his_want_state_will(option);
41339503Sborman 		do_dont_resp[option]++;
41439503Sborman 	}
41539503Sborman 	(void) sprintf(nfrontp, doopt, option);
41639503Sborman 	nfrontp += sizeof (dont) - 2;
41744364Sborman #ifdef DIAGNOSTICS
41844364Sborman 	/*
41944364Sborman 	 * Report sending option to other side.
42044364Sborman 	 */
42144364Sborman 	if (diagnostic & TD_OPTIONS) {
42244364Sborman 		printoption("td: send do", option);
42344364Sborman 	}
42444364Sborman #endif /* DIAGNOSTICS */
42539503Sborman }
42639503Sborman 
42739503Sborman willoption(option)
42839503Sborman 	int option;
42939503Sborman {
43038905Sborman 	int changeok = 0;
43138905Sborman 
43239503Sborman 	/*
43339503Sborman 	 * process input from peer.
43439503Sborman 	 */
43539503Sborman 
43644364Sborman #ifdef DIAGNOSTICS
43744364Sborman 	/*
43844364Sborman 	 * Report receiving option from other side.
43944364Sborman 	 */
44044364Sborman 	if (diagnostic & TD_OPTIONS) {
44144364Sborman 		printoption("td: recv will", option);
44244364Sborman 	}
44344364Sborman #endif /* DIAGNOSTICS */
44444364Sborman 
44539503Sborman 	if (do_dont_resp[option]) {
44639503Sborman 		do_dont_resp[option]--;
44744364Sborman 		if (do_dont_resp[option] && his_state_is_will(option))
44839503Sborman 			do_dont_resp[option]--;
44939503Sborman 	}
45039531Sborman 	if (do_dont_resp[option] == 0) {
45144364Sborman 	    if (his_want_state_is_wont(option)) {
45238905Sborman 		switch (option) {
45338905Sborman 
45438905Sborman 		case TELOPT_BINARY:
45538905Sborman 			init_termbuf();
45638905Sborman 			tty_binaryin(1);
45738905Sborman 			set_termbuf();
45838905Sborman 			changeok++;
45938905Sborman 			break;
46038905Sborman 
46138905Sborman 		case TELOPT_ECHO:
46238905Sborman 			/*
46344364Sborman 			 * See comments below for more info.
46438905Sborman 			 */
46544364Sborman 			not42 = 0;	/* looks like a 4.2 system */
46638905Sborman 			break;
46738905Sborman 
46838905Sborman 		case TELOPT_TM:
46938905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
47038905Sborman 			/*
47139503Sborman 			 * This telnetd implementation does not really
47239503Sborman 			 * support timing marks, it just uses them to
47339503Sborman 			 * support the kludge linemode stuff.  If we
47439503Sborman 			 * receive a will or wont TM in response to our
47539503Sborman 			 * do TM request that may have been sent to
47639503Sborman 			 * determine kludge linemode support, process
47739503Sborman 			 * it, otherwise TM should get a negative
47839503Sborman 			 * response back.
47938905Sborman 			 */
48038905Sborman 			/*
48138905Sborman 			 * Handle the linemode kludge stuff.
48238905Sborman 			 * If we are not currently supporting any
48338905Sborman 			 * linemode at all, then we assume that this
48438905Sborman 			 * is the client telling us to use kludge
48538905Sborman 			 * linemode in response to our query.  Set the
48638905Sborman 			 * linemode type that is to be supported, note
48738905Sborman 			 * that the client wishes to use linemode, and
48838905Sborman 			 * eat the will TM as though it never arrived.
48938905Sborman 			 */
49038905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
49138905Sborman 				lmodetype = KLUDGE_LINEMODE;
49238905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
49339503Sborman 				send_wont(TELOPT_SGA, 1);
49438905Sborman 			}
49538905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
49638905Sborman 			/*
49739531Sborman 			 * We never respond to a WILL TM, and
49844364Sborman 			 * we leave the state WONT.
49938905Sborman 			 */
50038905Sborman 			return;
50138905Sborman 
50238905Sborman 		case TELOPT_LFLOW:
50338905Sborman 			/*
50439503Sborman 			 * If we are going to support flow control
50539503Sborman 			 * option, then don't worry peer that we can't
50639503Sborman 			 * change the flow control characters.
50738905Sborman 			 */
50838905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
50938905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
51038905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
51138905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
51238905Sborman 		case TELOPT_TTYPE:
51338905Sborman 		case TELOPT_SGA:
51438905Sborman 		case TELOPT_NAWS:
51538905Sborman 		case TELOPT_TSPEED:
51644364Sborman 		case TELOPT_XDISPLOC:
51744364Sborman 		case TELOPT_ENVIRON:
51839531Sborman 			changeok++;
51939531Sborman 			break;
52039531Sborman 
52138905Sborman #ifdef	LINEMODE
52238905Sborman 		case TELOPT_LINEMODE:
52344364Sborman 			/*
52444364Sborman 			 * Local processing of 'will linemode' should
52544364Sborman 			 * occur after placing 'do linemode' in the data
52644364Sborman 			 * stream, because we may wish to send other
52744364Sborman 			 * linemode related messages.  So, we duplicate
52844364Sborman 			 * the other three lines of code here, and then
52944364Sborman 			 * return.
53044364Sborman 			 */
53144364Sborman 			set_his_want_state_will(option);
53244364Sborman 			send_do(option, 0);
53344364Sborman 			set_his_state_will(option);
53439531Sborman # ifdef	KLUDGELINEMODE
53539531Sborman 			/*
53639531Sborman 			 * Note client's desire to use linemode.
53739531Sborman 			 */
53839531Sborman 			lmodetype = REAL_LINEMODE;
53939531Sborman # endif	/* KLUDGELINEMODE */
54039531Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
54144364Sborman 			return;
54239531Sborman #endif	/* LINEMODE */
54338905Sborman 
54438905Sborman 		default:
54538905Sborman 			break;
54638905Sborman 		}
54739503Sborman 		if (changeok) {
54844364Sborman 			set_his_want_state_will(option);
54939503Sborman 			send_do(option, 0);
55039503Sborman 		} else {
55139503Sborman 			do_dont_resp[option]++;
55239503Sborman 			send_dont(option, 0);
55338905Sborman 		}
55444364Sborman 	    } else {
55544364Sborman 		/*
55644364Sborman 		 * Option processing that should happen when
55744364Sborman 		 * we receive conformation of a change in
55844364Sborman 		 * state that we had requested.
55944364Sborman 		 */
56044364Sborman 		switch (option) {
56144364Sborman 		case TELOPT_ECHO:
56244364Sborman 			not42 = 0;	/* looks like a 4.2 system */
56344364Sborman 			/*
56444364Sborman 			 * Egads, he responded "WILL ECHO".  Turn
56544364Sborman 			 * it off right now!
56644364Sborman 			 */
56744364Sborman 			send_dont(option, 1);
56844364Sborman 			/*
56944364Sborman 			 * "WILL ECHO".  Kludge upon kludge!
57044364Sborman 			 * A 4.2 client is now echoing user input at
57144364Sborman 			 * the tty.  This is probably undesireable and
57244364Sborman 			 * it should be stopped.  The client will
57344364Sborman 			 * respond WONT TM to the DO TM that we send to
57444364Sborman 			 * check for kludge linemode.  When the WONT TM
57544364Sborman 			 * arrives, linemode will be turned off and a
57644364Sborman 			 * change propogated to the pty.  This change
57744364Sborman 			 * will cause us to process the new pty state
57844364Sborman 			 * in localstat(), which will notice that
57944364Sborman 			 * linemode is off and send a WILL ECHO
58044364Sborman 			 * so that we are properly in character mode and
58144364Sborman 			 * all is well.
58244364Sborman 			 */
58344364Sborman 			break;
58444364Sborman #ifdef	LINEMODE
58544364Sborman 		case TELOPT_LINEMODE:
58644364Sborman # ifdef	KLUDGELINEMODE
58744364Sborman 			/*
58844364Sborman 			 * Note client's desire to use linemode.
58944364Sborman 			 */
59044364Sborman 			lmodetype = REAL_LINEMODE;
59144364Sborman # endif	/* KLUDGELINEMODE */
59244364Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
59344364Sborman #endif	/* LINEMODE */
59444364Sborman 		}
59539531Sborman 	    }
59638905Sborman 	}
59744364Sborman 	set_his_state_will(option);
59838905Sborman }  /* end of willoption */
59938905Sborman 
60039503Sborman send_dont(option, init)
60139503Sborman 	int option, init;
60238905Sborman {
60339503Sborman 	if (init) {
60444364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
60544364Sborman 		    his_want_state_is_wont(option))
60639503Sborman 			return;
60744364Sborman 		set_his_want_state_wont(option);
60839503Sborman 		do_dont_resp[option]++;
60939503Sborman 	}
61039503Sborman 	(void) sprintf(nfrontp, dont, option);
61139503Sborman 	nfrontp += sizeof (doopt) - 2;
61244364Sborman #ifdef DIAGNOSTICS
61344364Sborman 	/*
61444364Sborman 	 * Report sending option to other side.
61544364Sborman 	 */
61644364Sborman 	if (diagnostic & TD_OPTIONS) {
61744364Sborman 		printoption("td: send dont", option);
61844364Sborman 	}
61944364Sborman #endif /* DIAGNOSTICS */
62039503Sborman }
62139503Sborman 
62239503Sborman wontoption(option)
62339503Sborman 	int option;
62439503Sborman {
62538905Sborman 	/*
62638905Sborman 	 * Process client input.
62738905Sborman 	 */
62839503Sborman 
62944364Sborman #ifdef DIAGNOSTICS
63044364Sborman 	/*
63144364Sborman 	 * Report receiving option from other side.
63244364Sborman 	 */
63344364Sborman 	if (diagnostic & TD_OPTIONS) {
63444364Sborman 		printoption("td: recv wont", option);
63544364Sborman 	}
63644364Sborman #endif /* DIAGNOSTICS */
63744364Sborman 
63839503Sborman 	if (do_dont_resp[option]) {
63939503Sborman 		do_dont_resp[option]--;
64044364Sborman 		if (do_dont_resp[option] && his_state_is_wont(option))
64139503Sborman 			do_dont_resp[option]--;
64239503Sborman 	}
64339531Sborman 	if (do_dont_resp[option] == 0) {
64444364Sborman 	    if (his_want_state_is_will(option)) {
64539503Sborman 		/* it is always ok to change to negative state */
64638905Sborman 		switch (option) {
64738905Sborman 		case TELOPT_ECHO:
64839503Sborman 			not42 = 1; /* doesn't seem to be a 4.2 system */
64938905Sborman 			break;
65038905Sborman 
65138905Sborman 		case TELOPT_BINARY:
65238905Sborman 			init_termbuf();
65338905Sborman 			tty_binaryin(0);
65438905Sborman 			set_termbuf();
65538905Sborman 			break;
65638905Sborman 
65738905Sborman #ifdef	LINEMODE
65838905Sborman 		case TELOPT_LINEMODE:
65938905Sborman # ifdef	KLUDGELINEMODE
66038905Sborman 			/*
66138905Sborman 			 * If real linemode is supported, then client is
66238905Sborman 			 * asking to turn linemode off.
66338905Sborman 			 */
66444364Sborman 			if (lmodetype != REAL_LINEMODE)
66544364Sborman 				break;
66644364Sborman 			lmodetype = KLUDGE_LINEMODE;
66738905Sborman # endif	/* KLUDGELINEMODE */
66844364Sborman 			clientstat(TELOPT_LINEMODE, WONT, 0);
66938905Sborman 			break;
67038905Sborman #endif	LINEMODE
67138905Sborman 
67238905Sborman 		case TELOPT_TM:
67338905Sborman 			/*
67439503Sborman 			 * If we get a WONT TM, and had sent a DO TM,
67539503Sborman 			 * don't respond with a DONT TM, just leave it
67639503Sborman 			 * as is.  Short circut the state machine to
67739531Sborman 			 * achive this.
67838905Sborman 			 */
67944364Sborman 			set_his_want_state_wont(TELOPT_TM);
68038905Sborman 			return;
68138905Sborman 
68238905Sborman 		case TELOPT_LFLOW:
68338905Sborman 			/*
68439503Sborman 			 * If we are not going to support flow control
68539503Sborman 			 * option, then let peer know that we can't
68639503Sborman 			 * change the flow control characters.
68738905Sborman 			 */
68838905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
68938905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
69038905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
69138905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
69238905Sborman 			break;
69338905Sborman 
69444364Sborman 		/*
69544364Sborman 		 * For options that we might spin waiting for
69644364Sborman 		 * sub-negotiation, if the client turns off the
69744364Sborman 		 * option rather than responding to the request,
69844364Sborman 		 * we have to treat it here as if we got a response
69944364Sborman 		 * to the sub-negotiation, (by updating the timers)
70044364Sborman 		 * so that we'll break out of the loop.
70144364Sborman 		 */
70244364Sborman 		case TELOPT_TTYPE:
70344364Sborman 			settimer(ttypesubopt);
70444364Sborman 			break;
70544364Sborman 
70644364Sborman 		case TELOPT_TSPEED:
70744364Sborman 			settimer(tspeedsubopt);
70844364Sborman 			break;
70944364Sborman 
71044364Sborman 		case TELOPT_XDISPLOC:
71144364Sborman 			settimer(xdisplocsubopt);
71244364Sborman 			break;
71344364Sborman 
71444364Sborman 		case TELOPT_ENVIRON:
71544364Sborman 			settimer(environsubopt);
71644364Sborman 			break;
71744364Sborman 
71838905Sborman 		default:
71938905Sborman 			break;
72038905Sborman 		}
72144364Sborman 		set_his_want_state_wont(option);
72244364Sborman 		if (his_state_is_will(option))
72344364Sborman 			send_dont(option, 0);
72439531Sborman 	    } else {
72539531Sborman 		switch (option) {
72639531Sborman 		case TELOPT_TM:
72739531Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
72839531Sborman 			if (lmodetype < REAL_LINEMODE) {
72939531Sborman 				lmodetype = NO_LINEMODE;
73039531Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
73139531Sborman 				send_will(TELOPT_SGA, 1);
732*45234Sborman 				send_will(TELOPT_ECHO, 1);
73339531Sborman 			}
73439531Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
73539531Sborman 		default:
73639531Sborman 			break;
73739531Sborman 		}
73839531Sborman 	    }
73938905Sborman 	}
74044364Sborman 	set_his_state_wont(option);
74138905Sborman 
74239503Sborman }  /* end of wontoption */
74338905Sborman 
74439503Sborman send_will(option, init)
74539503Sborman 	int option, init;
74639503Sborman {
74739503Sborman 	if (init) {
74844364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
74944364Sborman 		    my_want_state_is_will(option))
75039503Sborman 			return;
75144364Sborman 		set_my_want_state_will(option);
75239503Sborman 		will_wont_resp[option]++;
75338905Sborman 	}
75439503Sborman 	(void) sprintf(nfrontp, will, option);
75539503Sborman 	nfrontp += sizeof (doopt) - 2;
75644364Sborman #ifdef DIAGNOSTICS
75744364Sborman 	/*
75844364Sborman 	 * Report sending option to other side.
75944364Sborman 	 */
76044364Sborman 	if (diagnostic & TD_OPTIONS) {
76144364Sborman 		printoption("td: send will", option);
76244364Sborman 	}
76344364Sborman #endif /* DIAGNOSTICS */
76439503Sborman }
76538905Sborman 
766*45234Sborman #if	!defined(LINEMODE) || !defined(KLUDGELINEMODE)
767*45234Sborman /*
768*45234Sborman  * When we get a DONT SGA, we will try once to turn it
769*45234Sborman  * back on.  If the other side responds DONT SGA, we
770*45234Sborman  * leave it at that.  This is so that when we talk to
771*45234Sborman  * clients that understand KLUDGELINEMODE but not LINEMODE,
772*45234Sborman  * we'll keep them in char-at-a-time mode.
773*45234Sborman  */
774*45234Sborman int turn_on_sga = 0;
775*45234Sborman #endif
776*45234Sborman 
77739503Sborman dooption(option)
77839503Sborman 	int option;
77938905Sborman {
78038905Sborman 	int changeok = 0;
78138905Sborman 
78238905Sborman 	/*
78338905Sborman 	 * Process client input.
78438905Sborman 	 */
78539503Sborman 
78644364Sborman #ifdef DIAGNOSTICS
78744364Sborman 	/*
78844364Sborman 	 * Report receiving option from other side.
78944364Sborman 	 */
79044364Sborman 	if (diagnostic & TD_OPTIONS) {
79144364Sborman 		printoption("td: recv do", option);
79244364Sborman 	}
79344364Sborman #endif /* DIAGNOSTICS */
79444364Sborman 
79539503Sborman 	if (will_wont_resp[option]) {
79639503Sborman 		will_wont_resp[option]--;
79744364Sborman 		if (will_wont_resp[option] && my_state_is_will(option))
79839503Sborman 			will_wont_resp[option]--;
79939503Sborman 	}
80044364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
80138905Sborman 		switch (option) {
80238905Sborman 		case TELOPT_ECHO:
80338905Sborman #ifdef	LINEMODE
804*45234Sborman # ifdef	KLUDGELINEMODE
805*45234Sborman 			if (lmodetype == NO_LINEMODE)
806*45234Sborman # else
807*45234Sborman 			if (his_state_is_wont(TELOPT_LINEMODE))
808*45234Sborman # endif
80938905Sborman #endif
810*45234Sborman 			{
81138905Sborman 				init_termbuf();
81238905Sborman 				tty_setecho(1);
81338905Sborman 				set_termbuf();
81438905Sborman 			}
81538905Sborman 			changeok++;
81638905Sborman 			break;
81738905Sborman 
81838905Sborman 		case TELOPT_BINARY:
81938905Sborman 			init_termbuf();
82038905Sborman 			tty_binaryout(1);
82138905Sborman 			set_termbuf();
82238905Sborman 			changeok++;
82338905Sborman 			break;
82438905Sborman 
82538905Sborman 		case TELOPT_SGA:
82638905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
82738905Sborman 			/*
82839503Sborman 			 * If kludge linemode is in use, then we must
82939503Sborman 			 * process an incoming do SGA for linemode
83039503Sborman 			 * purposes.
83138905Sborman 			 */
83238905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
83338905Sborman 				/*
83439503Sborman 				 * Receipt of "do SGA" in kludge
83539503Sborman 				 * linemode is the peer asking us to
83639503Sborman 				 * turn off linemode.  Make note of
83739503Sborman 				 * the request.
83838905Sborman 				 */
83938905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
84038905Sborman 				/*
84139503Sborman 				 * If linemode did not get turned off
84239503Sborman 				 * then don't tell peer that we did.
84339503Sborman 				 * Breaking here forces a wont SGA to
84439503Sborman 				 * be returned.
84538905Sborman 				 */
84638905Sborman 				if (linemode)
84738905Sborman 					break;
84838905Sborman 			}
849*45234Sborman #else
850*45234Sborman 			turn_on_sga = 0;
85138905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
85238905Sborman 			changeok++;
85338905Sborman 			break;
85438905Sborman 
85538905Sborman 		case TELOPT_STATUS:
85638905Sborman 			changeok++;
85738905Sborman 			break;
85838905Sborman 
85938905Sborman 		case TELOPT_TM:
86039503Sborman 			/*
86139503Sborman 			 * Special case for TM.  We send a WILL, but
86239503Sborman 			 * pretend we sent a WONT.
86339503Sborman 			 */
86439503Sborman 			send_will(option, 0);
86544364Sborman 			set_my_want_state_wont(option);
86644364Sborman 			set_my_state_wont(option);
86739503Sborman 			return;
86839503Sborman 
86938905Sborman 		case TELOPT_LINEMODE:
87038905Sborman 		case TELOPT_TTYPE:
87138905Sborman 		case TELOPT_NAWS:
87238905Sborman 		case TELOPT_TSPEED:
87338905Sborman 		case TELOPT_LFLOW:
87444364Sborman 		case TELOPT_XDISPLOC:
87544364Sborman 		case TELOPT_ENVIRON:
87638905Sborman 		default:
87738905Sborman 			break;
87838905Sborman 		}
87939503Sborman 		if (changeok) {
88044364Sborman 			set_my_want_state_will(option);
88139503Sborman 			send_will(option, 0);
88239503Sborman 		} else {
88339503Sborman 			will_wont_resp[option]++;
88439503Sborman 			send_wont(option, 0);
88538905Sborman 		}
88638905Sborman 	}
88744364Sborman 	set_my_state_will(option);
88838905Sborman 
88938905Sborman }  /* end of dooption */
89038905Sborman 
89139503Sborman send_wont(option, init)
89239503Sborman 	int option, init;
89339503Sborman {
89439503Sborman 	if (init) {
89544364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
89644364Sborman 		    my_want_state_is_wont(option))
89739503Sborman 			return;
89844364Sborman 		set_my_want_state_wont(option);
89939503Sborman 		will_wont_resp[option]++;
90039503Sborman 	}
90139503Sborman 	(void) sprintf(nfrontp, wont, option);
90239503Sborman 	nfrontp += sizeof (wont) - 2;
90344364Sborman #ifdef DIAGNOSTICS
90444364Sborman 	/*
90544364Sborman 	 * Report sending option to other side.
90644364Sborman 	 */
90744364Sborman 	if (diagnostic & TD_OPTIONS) {
90844364Sborman 		printoption("td: send wont", option);
90944364Sborman 	}
91044364Sborman #endif /* DIAGNOSTICS */
91139503Sborman }
91238905Sborman 
91339503Sborman dontoption(option)
91439503Sborman 	int option;
91538905Sborman {
91638905Sborman 	/*
91738905Sborman 	 * Process client input.
91838905Sborman 	 */
91944364Sborman #ifdef DIAGNOSTICS
92044364Sborman 	/*
92144364Sborman 	 * Report receiving option from other side.
92244364Sborman 	 */
92344364Sborman 	if (diagnostic & TD_OPTIONS) {
92444364Sborman 		printoption("td: recv dont", option);
92544364Sborman 	}
92644364Sborman #endif /* DIAGNOSTICS */
92740242Sborman 
92839503Sborman 	if (will_wont_resp[option]) {
92939503Sborman 		will_wont_resp[option]--;
93044364Sborman 		if (will_wont_resp[option] && my_state_is_wont(option))
93139503Sborman 			will_wont_resp[option]--;
93239503Sborman 	}
93344364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
93438905Sborman 		switch (option) {
93538905Sborman 		case TELOPT_BINARY:
93638905Sborman 			init_termbuf();
93738905Sborman 			tty_binaryout(0);
93838905Sborman 			set_termbuf();
93938905Sborman 			break;
94038905Sborman 
94139503Sborman 		case TELOPT_ECHO:	/* we should stop echoing */
94238905Sborman #ifdef	LINEMODE
943*45234Sborman # ifdef	KLUDGELINEMODE
944*45234Sborman 			if (lmodetype == NO_LINEMODE)
945*45234Sborman # else
946*45234Sborman 			if (his_state_is_wont(TELOPT_LINEMODE))
947*45234Sborman # endif
94838905Sborman #endif
949*45234Sborman 			{
95038905Sborman 				init_termbuf();
95138905Sborman 				tty_setecho(0);
95238905Sborman 				set_termbuf();
95338905Sborman 			}
95438905Sborman 			break;
95538905Sborman 
95638905Sborman 		case TELOPT_SGA:
95738905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
95838905Sborman 			/*
95939503Sborman 			 * If kludge linemode is in use, then we
96039503Sborman 			 * must process an incoming do SGA for
96139503Sborman 			 * linemode purposes.
96238905Sborman 			 */
96338905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
96438905Sborman 				/*
96539503Sborman 				 * The client is asking us to turn
96639503Sborman 				 * linemode on.
96738905Sborman 				 */
96838905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
96938905Sborman 				/*
97039503Sborman 				 * If we did not turn line mode on,
97139503Sborman 				 * then what do we say?  Will SGA?
97239503Sborman 				 * This violates design of telnet.
97339503Sborman 				 * Gross.  Very Gross.
97438905Sborman 				 */
97538905Sborman 			}
976*45234Sborman 			break;
977*45234Sborman #else
978*45234Sborman 			set_my_want_state_wont(option);
979*45234Sborman 			if (my_state_is_will(option))
980*45234Sborman 				send_wont(option, 0);
981*45234Sborman 			set_my_state_wont(option);
982*45234Sborman 			if (turn_on_sga ^= 1)
983*45234Sborman 				send_will(option);
984*45234Sborman 			return;
98538905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
98638905Sborman 
98738905Sborman 		default:
98838905Sborman 			break;
98938905Sborman 		}
99038905Sborman 
99144364Sborman 		set_my_want_state_wont(option);
99244364Sborman 		if (my_state_is_will(option))
99344364Sborman 			send_wont(option, 0);
99438905Sborman 	}
99544364Sborman 	set_my_state_wont(option);
99638905Sborman 
99738905Sborman }  /* end of dontoption */
99838905Sborman 
99938905Sborman /*
100038905Sborman  * suboption()
100138905Sborman  *
100238905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
100338905Sborman  * side.
100438905Sborman  *
100538905Sborman  *	Currently we recognize:
100638905Sborman  *
100738905Sborman  *	Terminal type is
100838905Sborman  *	Linemode
100938905Sborman  *	Window size
101038905Sborman  *	Terminal speed
101138905Sborman  */
101238905Sborman suboption()
101338905Sborman {
101438905Sborman     register int subchar;
101544364Sborman     extern void unsetenv();
101638905Sborman 
101744364Sborman #ifdef DIAGNOSTICS
101844364Sborman 	/*
101944364Sborman 	 * Report receiving option from other side.
102044364Sborman 	 */
102144364Sborman 	if (diagnostic & TD_OPTIONS) {
102244364Sborman 		netflush();	/* get rid of anything waiting to go out */
102344364Sborman 		printsub("td: recv", subpointer, SB_LEN()+2);
102444364Sborman 	}
102544364Sborman #endif	DIAGNOSTIC
102638905Sborman     subchar = SB_GET();
102738905Sborman     switch (subchar) {
102838905Sborman     case TELOPT_TSPEED: {
102938905Sborman 	register int xspeed, rspeed;
103038905Sborman 
103144364Sborman 	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
103238905Sborman 		break;
103338905Sborman 
103438905Sborman 	settimer(tspeedsubopt);
103538905Sborman 
103638905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
103738905Sborman 		return;
103838905Sborman 
103938905Sborman 	xspeed = atoi(subpointer);
104038905Sborman 
104138905Sborman 	while (SB_GET() != ',' && !SB_EOF());
104238905Sborman 	if (SB_EOF())
104338905Sborman 		return;
104438905Sborman 
104538905Sborman 	rspeed = atoi(subpointer);
104638905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
104738905Sborman 
104838905Sborman 	break;
104938905Sborman 
105038905Sborman     }  /* end of case TELOPT_TSPEED */
105138905Sborman 
105238905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
105344364Sborman 	static char terminalname[41];
105438905Sborman 
105544364Sborman 	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
105638905Sborman 		break;
105738905Sborman 	settimer(ttypesubopt);
105838905Sborman 
105938905Sborman 	if (SB_GET() != TELQUAL_IS) {
106038905Sborman 	    return;		/* ??? XXX but, this is the most robust */
106138905Sborman 	}
106238905Sborman 
106344364Sborman 	terminaltype = terminalname;
106438905Sborman 
106538905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
106638905Sborman 								    !SB_EOF()) {
106738905Sborman 	    register int c;
106838905Sborman 
106938905Sborman 	    c = SB_GET();
107038905Sborman 	    if (isupper(c)) {
107138905Sborman 		c = tolower(c);
107238905Sborman 	    }
107338905Sborman 	    *terminaltype++ = c;    /* accumulate name */
107438905Sborman 	}
107538905Sborman 	*terminaltype = 0;
107638905Sborman 	terminaltype = terminalname;
107738905Sborman 	break;
107838905Sborman     }  /* end of case TELOPT_TTYPE */
107938905Sborman 
108038905Sborman     case TELOPT_NAWS: {
108138905Sborman 	register int xwinsize, ywinsize;
108238905Sborman 
108344364Sborman 	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
108438905Sborman 		break;
108538905Sborman 
108638905Sborman 	if (SB_EOF())
108738905Sborman 		return;
108838905Sborman 	xwinsize = SB_GET() << 8;
108938905Sborman 	if (SB_EOF())
109038905Sborman 		return;
109138905Sborman 	xwinsize |= SB_GET();
109238905Sborman 	if (SB_EOF())
109338905Sborman 		return;
109438905Sborman 	ywinsize = SB_GET() << 8;
109538905Sborman 	if (SB_EOF())
109638905Sborman 		return;
109738905Sborman 	ywinsize |= SB_GET();
109838905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
109938905Sborman 
110038905Sborman 	break;
110138905Sborman 
110238905Sborman     }  /* end of case TELOPT_NAWS */
110338905Sborman 
110438905Sborman #ifdef	LINEMODE
110538905Sborman     case TELOPT_LINEMODE: {
110638905Sborman 	register int request;
110738905Sborman 
110844364Sborman 	if (his_state_is_wont(TELOPT_LINEMODE))	/* Ignore if option disabled */
110938905Sborman 		break;
111038905Sborman 	/*
111138905Sborman 	 * Process linemode suboptions.
111238905Sborman 	 */
111338905Sborman 	if (SB_EOF()) break;  /* garbage was sent */
111438905Sborman 	request = SB_GET();  /* get will/wont */
111538905Sborman 	if (SB_EOF()) break;  /* another garbage check */
111638905Sborman 
111738905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
111838905Sborman 		/*
111938905Sborman 		 * Process suboption buffer of slc's
112038905Sborman 		 */
112138905Sborman 		start_slc(1);
112238905Sborman 		do_opt_slc(subpointer, subend - subpointer);
112338905Sborman 		end_slc(0);
112438905Sborman 
112538905Sborman 	} else if (request == LM_MODE) {
112638905Sborman 		useeditmode = SB_GET();  /* get mode flag */
112738905Sborman 		clientstat(LM_MODE, 0, 0);
112838905Sborman 	}
112938905Sborman 
113038905Sborman 	switch (SB_GET()) {  /* what suboption? */
113138905Sborman 	case LM_FORWARDMASK:
113238905Sborman 		/*
113338905Sborman 		 * According to spec, only server can send request for
113438905Sborman 		 * forwardmask, and client can only return a positive response.
113538905Sborman 		 * So don't worry about it.
113638905Sborman 		 */
113738905Sborman 
113838905Sborman 	default:
113938905Sborman 		break;
114038905Sborman 	}
114140242Sborman 	break;
114238905Sborman     }  /* end of case TELOPT_LINEMODE */
114338905Sborman #endif
114438905Sborman     case TELOPT_STATUS: {
114538905Sborman 	int mode;
114638905Sborman 
114738905Sborman 	mode = SB_GET();
114838905Sborman 	switch (mode) {
114938905Sborman 	case TELQUAL_SEND:
115044364Sborman 	    if (my_state_is_will(TELOPT_STATUS))
115138905Sborman 		send_status();
115238905Sborman 	    break;
115338905Sborman 
115438905Sborman 	case TELQUAL_IS:
115538905Sborman 	    break;
115638905Sborman 
115738905Sborman 	default:
115838905Sborman 	    break;
115938905Sborman 	}
116040242Sborman 	break;
116140242Sborman     }  /* end of case TELOPT_STATUS */
116238905Sborman 
116344364Sborman     case TELOPT_XDISPLOC: {
116444364Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
116544364Sborman 		return;
116644364Sborman 	settimer(xdisplocsubopt);
116744364Sborman 	subpointer[SB_LEN()] = '\0';
116844364Sborman 	setenv("DISPLAY", subpointer, 1);
116944364Sborman 	break;
117044364Sborman     }  /* end of case TELOPT_XDISPLOC */
117144364Sborman 
117244364Sborman     case TELOPT_ENVIRON: {
117344364Sborman 	register int c;
117444364Sborman 	register char *cp, *varp, *valp;
117544364Sborman 
117644364Sborman 	if (SB_EOF())
117744364Sborman 		return;
117844364Sborman 	c = SB_GET();
117944364Sborman 	if (c == TELQUAL_IS)
118044364Sborman 		settimer(environsubopt);
118144364Sborman 	else if (c != TELQUAL_INFO)
118244364Sborman 		return;
118344364Sborman 
118444364Sborman 	while (!SB_EOF() && SB_GET() != ENV_VAR)
118544364Sborman 		;
118644364Sborman 
118744364Sborman 	if (SB_EOF())
118844364Sborman 		return;
118944364Sborman 
119044364Sborman 	cp = varp = subpointer;
119144364Sborman 	valp = 0;
119244364Sborman 
119344364Sborman 	while (!SB_EOF()) {
119444364Sborman 		switch (c = SB_GET()) {
119544364Sborman 		case ENV_VALUE:
119644364Sborman 			*cp = '\0';
119744364Sborman 			cp = valp = subpointer;
119844364Sborman 			break;
119944364Sborman 
120044364Sborman 		case ENV_VAR:
120144364Sborman 			*cp = '\0';
120244364Sborman 			if (valp)
120344364Sborman 				setenv(varp, valp, 1);
120444364Sborman 			else
120544364Sborman 				unsetenv(varp);
120644364Sborman 			cp = varp = subpointer;
120744364Sborman 			valp = 0;
120844364Sborman 			break;
120944364Sborman 
121044364Sborman 		case ENV_ESC:
121144364Sborman 			if (SB_EOF())
121244364Sborman 				break;
121344364Sborman 			c = SB_GET();
121444364Sborman 			/* FALL THROUGH */
121544364Sborman 		default:
121644364Sborman 			*cp++ = c;
121744364Sborman 			break;
121844364Sborman 		}
121944364Sborman 	}
122044364Sborman 	*cp = '\0';
122144364Sborman 	if (valp)
122244364Sborman 		setenv(varp, valp, 1);
122344364Sborman 	else
122444364Sborman 		unsetenv(varp);
122544364Sborman 	break;
122644364Sborman     }  /* end of case TELOPT_ENVIRON */
122744364Sborman 
122838905Sborman     default:
122938905Sborman 	break;
123038905Sborman     }  /* end of switch */
123138905Sborman 
123238905Sborman }  /* end of suboption */
123338905Sborman 
123438905Sborman #define	ADD(c)	 *ncp++ = c;
123538905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
123638905Sborman send_status()
123738905Sborman {
123844364Sborman 	unsigned char statusbuf[256];
123944364Sborman 	register unsigned char *ncp;
124044364Sborman 	register unsigned char i;
124138905Sborman 
124238905Sborman 	ncp = statusbuf;
124338905Sborman 
124438905Sborman 	netflush();	/* get rid of anything waiting to go out */
124538905Sborman 
124638905Sborman 	ADD(IAC);
124738905Sborman 	ADD(SB);
124838905Sborman 	ADD(TELOPT_STATUS);
124938905Sborman 	ADD(TELQUAL_IS);
125038905Sborman 
125138905Sborman 	for (i = 0; i < NTELOPTS; i++) {
125244364Sborman 		if (my_state_is_will(i)) {
125338905Sborman 			ADD(WILL);
125438905Sborman 			ADD_DATA(i);
125538905Sborman 			if (i == IAC)
125638905Sborman 				ADD(IAC);
125738905Sborman 		}
125844364Sborman 		if (his_state_is_will(i)) {
125938905Sborman 			ADD(DO);
126038905Sborman 			ADD_DATA(i);
126138905Sborman 			if (i == IAC)
126238905Sborman 				ADD(IAC);
126338905Sborman 		}
126438905Sborman 	}
126538905Sborman 
126638905Sborman #ifdef	LINEMODE
126744364Sborman 	if (his_state_is_will(TELOPT_LINEMODE)) {
126844364Sborman 		unsigned char *cp, *cpe;
126938905Sborman 		int len;
127038905Sborman 
127138905Sborman 		ADD(SB);
127238905Sborman 		ADD(TELOPT_LINEMODE);
127338905Sborman 		ADD(LM_MODE);
127438905Sborman 		ADD_DATA(editmode);
127538905Sborman 		if (editmode == IAC)
127638905Sborman 			ADD(IAC);
127738905Sborman 		ADD(SE);
127838905Sborman 
127938905Sborman 		ADD(SB);
128038905Sborman 		ADD(TELOPT_LINEMODE);
128138905Sborman 		ADD(LM_SLC);
128238905Sborman 		start_slc(0);
128338905Sborman 		send_slc();
128438905Sborman 		len = end_slc(&cp);
128538905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
128638905Sborman 			ADD_DATA(*cp);
128738905Sborman 		ADD(SE);
128838905Sborman 	}
128938905Sborman #endif	/* LINEMODE */
129038905Sborman 
129138905Sborman 	ADD(IAC);
129238905Sborman 	ADD(SE);
129338905Sborman 
129438905Sborman 	writenet(statusbuf, ncp - statusbuf);
129538905Sborman 	netflush();	/* Send it on its way */
129644364Sborman #ifdef DIAGNOSTICS
129744364Sborman 	/*
129844364Sborman 	 * Report sending status suboption.
129944364Sborman 	 */
130044364Sborman 	if (diagnostic & TD_OPTIONS) {
130144364Sborman 		printsub("td: send", statusbuf, ncp - statusbuf);
130244364Sborman 		netflush();	/* Send it on its way */
130344364Sborman 	}
130444364Sborman #endif	DIAGNOSTIC
130538905Sborman }
130644364Sborman 
130744364Sborman #ifdef	NO_SETENV
130844364Sborman #include "setenv.c"
130944364Sborman #endif
1310