xref: /csrg-svn/libexec/telnetd/state.c (revision 65158)
138905Sborman /*
261451Sbostic  * Copyright (c) 1989, 1993
361451Sbostic  *	The Regents of the University of California.  All rights reserved.
438905Sborman  *
542673Sbostic  * %sccs.include.redist.c%
638905Sborman  */
738905Sborman 
838905Sborman #ifndef lint
9*65158Sdab static char sccsid[] = "@(#)state.c	8.2 (Berkeley) 12/15/93";
1038905Sborman #endif /* not lint */
1138905Sborman 
1238905Sborman #include "telnetd.h"
1357212Sdab #if	defined(AUTHENTICATION)
1446809Sdab #include <libtelnet/auth.h>
1546809Sdab #endif
1638905Sborman 
17*65158Sdab unsigned char	doopt[] = { IAC, DO, '%', 'c', 0 };
18*65158Sdab unsigned char	dont[] = { IAC, DONT, '%', 'c', 0 };
19*65158Sdab unsigned char	will[] = { IAC, WILL, '%', 'c', 0 };
20*65158Sdab unsigned char	wont[] = { IAC, WONT, '%', 'c', 0 };
2138905Sborman int	not42 = 1;
2238905Sborman 
2338905Sborman /*
2438905Sborman  * Buffer for sub-options, and macros
2538905Sborman  * for suboptions buffer manipulations
2638905Sborman  */
2746809Sdab unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer;
2838905Sborman 
2958971Sdab #define	SB_CLEAR()	subpointer = subbuffer
3038905Sborman #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
3138905Sborman #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
3238905Sborman 				*subpointer++ = (c); \
3338905Sborman 			}
3438905Sborman #define	SB_GET()	((*subpointer++)&0xff)
3538905Sborman #define	SB_EOF()	(subpointer >= subend)
3644364Sborman #define	SB_LEN()	(subend - subpointer)
3738905Sborman 
3858971Sdab #ifdef	ENV_HACK
3958971Sdab unsigned char *subsave;
4058971Sdab #define SB_SAVE()	subsave = subpointer;
4158971Sdab #define	SB_RESTORE()	subpointer = subsave;
4258971Sdab #endif
4338905Sborman 
4438905Sborman 
4538905Sborman /*
4638905Sborman  * State for recv fsm
4738905Sborman  */
4838905Sborman #define	TS_DATA		0	/* base state */
4938905Sborman #define	TS_IAC		1	/* look for double IAC's */
5038905Sborman #define	TS_CR		2	/* CR-LF ->'s CR */
5138905Sborman #define	TS_SB		3	/* throw away begin's... */
5238905Sborman #define	TS_SE		4	/* ...end's (suboption negotiation) */
5338905Sborman #define	TS_WILL		5	/* will option negotiation */
5438905Sborman #define	TS_WONT		6	/* wont " */
5538905Sborman #define	TS_DO		7	/* do " */
5638905Sborman #define	TS_DONT		8	/* dont " */
5738905Sborman 
5846809Sdab 	void
5938905Sborman telrcv()
6038905Sborman {
6138905Sborman 	register int c;
6238905Sborman 	static int state = TS_DATA;
6340242Sborman #if	defined(CRAY2) && defined(UNICOS5)
6438905Sborman 	char *opfrontp = pfrontp;
6538905Sborman #endif
6638905Sborman 
6738905Sborman 	while (ncc > 0) {
6838905Sborman 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6938905Sborman 			break;
7038905Sborman 		c = *netip++ & 0377, ncc--;
7160151Sdab #ifdef	ENCRYPTION
7246809Sdab 		if (decrypt_input)
7346809Sdab 			c = (*decrypt_input)(c);
7460151Sdab #endif	/* ENCRYPTION */
7538905Sborman 		switch (state) {
7638905Sborman 
7738905Sborman 		case TS_CR:
7838905Sborman 			state = TS_DATA;
7938905Sborman 			/* Strip off \n or \0 after a \r */
8038905Sborman 			if ((c == 0) || (c == '\n')) {
8138905Sborman 				break;
8238905Sborman 			}
8338905Sborman 			/* FALL THROUGH */
8438905Sborman 
8538905Sborman 		case TS_DATA:
8638905Sborman 			if (c == IAC) {
8738905Sborman 				state = TS_IAC;
8838905Sborman 				break;
8938905Sborman 			}
9038905Sborman 			/*
9138905Sborman 			 * We now map \r\n ==> \r for pragmatic reasons.
9238905Sborman 			 * Many client implementations send \r\n when
9338905Sborman 			 * the user hits the CarriageReturn key.
9438905Sborman 			 *
9538905Sborman 			 * We USED to map \r\n ==> \n, since \r\n says
9638905Sborman 			 * that we want to be in column 1 of the next
9738905Sborman 			 * printable line, and \n is the standard
9838905Sborman 			 * unix way of saying that (\r is only good
9938905Sborman 			 * if CRMOD is set, which it normally is).
10038905Sborman 			 */
10144364Sborman 			if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
10246809Sdab 				int nc = *netip;
10360151Sdab #ifdef	ENCRYPTION
10446809Sdab 				if (decrypt_input)
10546809Sdab 					nc = (*decrypt_input)(nc & 0xff);
10660151Sdab #endif	/* ENCRYPTION */
10746809Sdab #ifdef	LINEMODE
10838905Sborman 				/*
10938905Sborman 				 * If we are operating in linemode,
11038905Sborman 				 * convert to local end-of-line.
11138905Sborman 				 */
11246809Sdab 				if (linemode && (ncc > 0) && (('\n' == nc) ||
11346809Sdab 					 ((0 == nc) && tty_iscrnl())) ) {
11438905Sborman 					netip++; ncc--;
11538905Sborman 					c = '\n';
11646809Sdab 				} else
11746809Sdab #endif
11846809Sdab 				{
11960151Sdab #ifdef	ENCRYPTION
12046809Sdab 					if (decrypt_input)
12146809Sdab 						(void)(*decrypt_input)(-1);
12260151Sdab #endif	/* ENCRYPTION */
12338905Sborman 					state = TS_CR;
12438905Sborman 				}
12538905Sborman 			}
12638905Sborman 			*pfrontp++ = c;
12738905Sborman 			break;
12838905Sborman 
12938905Sborman 		case TS_IAC:
13038905Sborman gotiac:			switch (c) {
13138905Sborman 
13238905Sborman 			/*
13338905Sborman 			 * Send the process on the pty side an
13438905Sborman 			 * interrupt.  Do this with a NULL or
13538905Sborman 			 * interrupt char; depending on the tty mode.
13638905Sborman 			 */
13738905Sborman 			case IP:
13846809Sdab 				DIAG(TD_OPTIONS,
13946809Sdab 					printoption("td: recv IAC", c));
14038905Sborman 				interrupt();
14138905Sborman 				break;
14238905Sborman 
14338905Sborman 			case BREAK:
14446809Sdab 				DIAG(TD_OPTIONS,
14546809Sdab 					printoption("td: recv IAC", c));
14638905Sborman 				sendbrk();
14738905Sborman 				break;
14838905Sborman 
14938905Sborman 			/*
15038905Sborman 			 * Are You There?
15138905Sborman 			 */
15238905Sborman 			case AYT:
15346809Sdab 				DIAG(TD_OPTIONS,
15446809Sdab 					printoption("td: recv IAC", c));
15545234Sborman 				recv_ayt();
15638905Sborman 				break;
15738905Sborman 
15838905Sborman 			/*
15938905Sborman 			 * Abort Output
16038905Sborman 			 */
16138905Sborman 			case AO:
16238905Sborman 			    {
16346809Sdab 				DIAG(TD_OPTIONS,
16446809Sdab 					printoption("td: recv IAC", c));
16538905Sborman 				ptyflush();	/* half-hearted */
16638905Sborman 				init_termbuf();
16738905Sborman 
16838905Sborman 				if (slctab[SLC_AO].sptr &&
16945234Sborman 				    *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
17040242Sborman 				    *pfrontp++ =
17140242Sborman 					(unsigned char)*slctab[SLC_AO].sptr;
17238905Sborman 				}
17338905Sborman 
17438905Sborman 				netclear();	/* clear buffer back */
17538905Sborman 				*nfrontp++ = IAC;
17638905Sborman 				*nfrontp++ = DM;
17738905Sborman 				neturg = nfrontp-1; /* off by one XXX */
17846809Sdab 				DIAG(TD_OPTIONS,
17946809Sdab 					printoption("td: send IAC", DM));
18038905Sborman 				break;
18138905Sborman 			    }
18238905Sborman 
18338905Sborman 			/*
18438905Sborman 			 * Erase Character and
18538905Sborman 			 * Erase Line
18638905Sborman 			 */
18738905Sborman 			case EC:
18838905Sborman 			case EL:
18938905Sborman 			    {
19040242Sborman 				cc_t ch;
19138905Sborman 
19246809Sdab 				DIAG(TD_OPTIONS,
19346809Sdab 					printoption("td: recv IAC", c));
19438905Sborman 				ptyflush();	/* half-hearted */
19538905Sborman 				init_termbuf();
19644364Sborman 				if (c == EC)
19744364Sborman 					ch = *slctab[SLC_EC].sptr;
19844364Sborman 				else
19944364Sborman 					ch = *slctab[SLC_EL].sptr;
20045234Sborman 				if (ch != (cc_t)(_POSIX_VDISABLE))
20140242Sborman 					*pfrontp++ = (unsigned char)ch;
20238905Sborman 				break;
20338905Sborman 			    }
20438905Sborman 
20538905Sborman 			/*
20638905Sborman 			 * Check for urgent data...
20738905Sborman 			 */
20838905Sborman 			case DM:
20946809Sdab 				DIAG(TD_OPTIONS,
21046809Sdab 					printoption("td: recv IAC", c));
21138905Sborman 				SYNCHing = stilloob(net);
21238905Sborman 				settimer(gotDM);
21338905Sborman 				break;
21438905Sborman 
21538905Sborman 
21638905Sborman 			/*
21738905Sborman 			 * Begin option subnegotiation...
21838905Sborman 			 */
21938905Sborman 			case SB:
22038905Sborman 				state = TS_SB;
22138905Sborman 				SB_CLEAR();
22238905Sborman 				continue;
22338905Sborman 
22438905Sborman 			case WILL:
22538905Sborman 				state = TS_WILL;
22638905Sborman 				continue;
22738905Sborman 
22838905Sborman 			case WONT:
22938905Sborman 				state = TS_WONT;
23038905Sborman 				continue;
23138905Sborman 
23238905Sborman 			case DO:
23338905Sborman 				state = TS_DO;
23438905Sborman 				continue;
23538905Sborman 
23638905Sborman 			case DONT:
23738905Sborman 				state = TS_DONT;
23838905Sborman 				continue;
23938905Sborman 			case EOR:
24044364Sborman 				if (his_state_is_will(TELOPT_EOR))
24138905Sborman 					doeof();
24238905Sborman 				break;
24338905Sborman 
24438905Sborman 			/*
24538905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
24638905Sborman 			 * to command stream (EOF, SUSP, ABORT).
24738905Sborman 			 */
24838905Sborman 			case xEOF:
24938905Sborman 				doeof();
25038905Sborman 				break;
25138905Sborman 
25238905Sborman 			case SUSP:
25338905Sborman 				sendsusp();
25438905Sborman 				break;
25538905Sborman 
25638905Sborman 			case ABORT:
25738905Sborman 				sendbrk();
25838905Sborman 				break;
25938905Sborman 
26038905Sborman 			case IAC:
26138905Sborman 				*pfrontp++ = c;
26238905Sborman 				break;
26338905Sborman 			}
26438905Sborman 			state = TS_DATA;
26538905Sborman 			break;
26638905Sborman 
26738905Sborman 		case TS_SB:
26838905Sborman 			if (c == IAC) {
26938905Sborman 				state = TS_SE;
27038905Sborman 			} else {
27138905Sborman 				SB_ACCUM(c);
27238905Sborman 			}
27338905Sborman 			break;
27438905Sborman 
27538905Sborman 		case TS_SE:
27638905Sborman 			if (c != SE) {
27738905Sborman 				if (c != IAC) {
27838905Sborman 					/*
27938905Sborman 					 * bad form of suboption negotiation.
28038905Sborman 					 * handle it in such a way as to avoid
28138905Sborman 					 * damage to local state.  Parse
28238905Sborman 					 * suboption buffer found so far,
28338905Sborman 					 * then treat remaining stream as
28438905Sborman 					 * another command sequence.
28538905Sborman 					 */
28646809Sdab 
28746809Sdab 					/* for DIAGNOSTICS */
28844364Sborman 					SB_ACCUM(IAC);
28944364Sborman 					SB_ACCUM(c);
29044364Sborman 					subpointer -= 2;
29146809Sdab 
29238905Sborman 					SB_TERM();
29338905Sborman 					suboption();
29438905Sborman 					state = TS_IAC;
29538905Sborman 					goto gotiac;
29638905Sborman 				}
29738905Sborman 				SB_ACCUM(c);
29838905Sborman 				state = TS_SB;
29938905Sborman 			} else {
30046809Sdab 				/* for DIAGNOSTICS */
30144364Sborman 				SB_ACCUM(IAC);
30244364Sborman 				SB_ACCUM(SE);
30344364Sborman 				subpointer -= 2;
30446809Sdab 
30538905Sborman 				SB_TERM();
30638905Sborman 				suboption();	/* handle sub-option */
30738905Sborman 				state = TS_DATA;
30838905Sborman 			}
30938905Sborman 			break;
31038905Sborman 
31138905Sborman 		case TS_WILL:
31239503Sborman 			willoption(c);
31338905Sborman 			state = TS_DATA;
31438905Sborman 			continue;
31538905Sborman 
31638905Sborman 		case TS_WONT:
31739503Sborman 			wontoption(c);
31838905Sborman 			state = TS_DATA;
31938905Sborman 			continue;
32038905Sborman 
32138905Sborman 		case TS_DO:
32239503Sborman 			dooption(c);
32338905Sborman 			state = TS_DATA;
32438905Sborman 			continue;
32538905Sborman 
32638905Sborman 		case TS_DONT:
32739503Sborman 			dontoption(c);
32838905Sborman 			state = TS_DATA;
32938905Sborman 			continue;
33038905Sborman 
33138905Sborman 		default:
33238905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
33338905Sborman 			printf("telnetd: panic state=%d\n", state);
33438905Sborman 			exit(1);
33538905Sborman 		}
33638905Sborman 	}
33740242Sborman #if	defined(CRAY2) && defined(UNICOS5)
33838905Sborman 	if (!linemode) {
33938905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
34038905Sborman 		char	xbuf2[BUFSIZ];
34138905Sborman 		register char *cp;
34238905Sborman 		int n = pfrontp - opfrontp, oc;
34338905Sborman 		bcopy(opfrontp, xptyobuf, n);
34438905Sborman 		pfrontp = opfrontp;
34538905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
34638905Sborman 					xbuf2, &oc, BUFSIZ);
34738905Sborman 		for (cp = xbuf2; oc > 0; --oc)
34838905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
34938905Sborman 				*nfrontp++ = IAC;
35038905Sborman 	}
35140242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
35238905Sborman }  /* end of telrcv */
35338905Sborman 
35438905Sborman /*
35538905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
35644364Sborman  * Telnet option processing state machine.
35738905Sborman  *
35838905Sborman  * These correspond to the following states:
35938905Sborman  *	my_state = the last negotiated state
36038905Sborman  *	want_state = what I want the state to go to
36138905Sborman  *	want_resp = how many requests I have sent
36238905Sborman  * All state defaults are negative, and resp defaults to 0.
36338905Sborman  *
36438905Sborman  * When initiating a request to change state to new_state:
36538905Sborman  *
36638905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
36738905Sborman  *	do nothing;
36838905Sborman  * } else {
36938905Sborman  *	want_state = new_state;
37038905Sborman  *	send new_state;
37138905Sborman  *	want_resp++;
37238905Sborman  * }
37338905Sborman  *
37438905Sborman  * When receiving new_state:
37538905Sborman  *
37638905Sborman  * if (want_resp) {
37738905Sborman  *	want_resp--;
37838905Sborman  *	if (want_resp && (new_state == my_state))
37938905Sborman  *		want_resp--;
38038905Sborman  * }
38138905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
38238905Sborman  *	if (ok_to_switch_to new_state)
38338905Sborman  *		want_state = new_state;
38438905Sborman  *	else
38538905Sborman  *		want_resp++;
38638905Sborman  *	send want_state;
38738905Sborman  * }
38838905Sborman  * my_state = new_state;
38938905Sborman  *
39038905Sborman  * Note that new_state is implied in these functions by the function itself.
39138905Sborman  * will and do imply positive new_state, wont and dont imply negative.
39238905Sborman  *
39338905Sborman  * Finally, there is one catch.  If we send a negative response to a
39438905Sborman  * positive request, my_state will be the positive while want_state will
39538905Sborman  * remain negative.  my_state will revert to negative when the negative
39638905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
39738905Sborman  * us not only the last negotiated state, but also tells us what the peer
39838905Sborman  * wants to be doing as well.  It is important to understand this difference
39938905Sborman  * as we may wish to be processing data streams based on our desired state
40038905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
40138905Sborman  *
40238905Sborman  * This all works fine because if the peer sends a positive request, the data
40338905Sborman  * that we receive prior to negative acknowlegment will probably be affected
40438905Sborman  * by the positive state, and we can process it as such (if we can; if we
40538905Sborman  * can't then it really doesn't matter).  If it is that important, then the
40638905Sborman  * peer probably should be buffering until this option state negotiation
40738905Sborman  * is complete.
40838905Sborman  *
40938905Sborman  */
41046809Sdab 	void
41139503Sborman send_do(option, init)
41239503Sborman 	int option, init;
41338905Sborman {
41439503Sborman 	if (init) {
41544364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
41644364Sborman 		    his_want_state_is_will(option))
41739503Sborman 			return;
41839531Sborman 		/*
41939531Sborman 		 * Special case for TELOPT_TM:  We send a DO, but pretend
42039531Sborman 		 * that we sent a DONT, so that we can send more DOs if
42139531Sborman 		 * we want to.
42239531Sborman 		 */
42339531Sborman 		if (option == TELOPT_TM)
42444364Sborman 			set_his_want_state_wont(option);
42539531Sborman 		else
42644364Sborman 			set_his_want_state_will(option);
42739503Sborman 		do_dont_resp[option]++;
42839503Sborman 	}
429*65158Sdab 	(void) sprintf(nfrontp, (char *)doopt, option);
43039503Sborman 	nfrontp += sizeof (dont) - 2;
43146809Sdab 
43246809Sdab 	DIAG(TD_OPTIONS, printoption("td: send do", option));
43339503Sborman }
43439503Sborman 
43557212Sdab #ifdef	AUTHENTICATION
43646809Sdab extern void auth_request();
43746809Sdab #endif
43846809Sdab #ifdef	LINEMODE
43946809Sdab extern void doclientstat();
44046809Sdab #endif
44157212Sdab #ifdef	ENCRYPTION
44246809Sdab extern void encrypt_send_support();
44360151Sdab #endif	/* ENCRYPTION */
44446809Sdab 
44546809Sdab 	void
44639503Sborman willoption(option)
44739503Sborman 	int option;
44839503Sborman {
44938905Sborman 	int changeok = 0;
45046809Sdab 	void (*func)() = 0;
45138905Sborman 
45239503Sborman 	/*
45339503Sborman 	 * process input from peer.
45439503Sborman 	 */
45539503Sborman 
45646809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv will", option));
45744364Sborman 
45839503Sborman 	if (do_dont_resp[option]) {
45939503Sborman 		do_dont_resp[option]--;
46044364Sborman 		if (do_dont_resp[option] && his_state_is_will(option))
46139503Sborman 			do_dont_resp[option]--;
46239503Sborman 	}
46339531Sborman 	if (do_dont_resp[option] == 0) {
46444364Sborman 	    if (his_want_state_is_wont(option)) {
46538905Sborman 		switch (option) {
46638905Sborman 
46738905Sborman 		case TELOPT_BINARY:
46838905Sborman 			init_termbuf();
46938905Sborman 			tty_binaryin(1);
47038905Sborman 			set_termbuf();
47138905Sborman 			changeok++;
47238905Sborman 			break;
47338905Sborman 
47438905Sborman 		case TELOPT_ECHO:
47538905Sborman 			/*
47644364Sborman 			 * See comments below for more info.
47738905Sborman 			 */
47844364Sborman 			not42 = 0;	/* looks like a 4.2 system */
47938905Sborman 			break;
48038905Sborman 
48138905Sborman 		case TELOPT_TM:
48238905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
48338905Sborman 			/*
48439503Sborman 			 * This telnetd implementation does not really
48539503Sborman 			 * support timing marks, it just uses them to
48639503Sborman 			 * support the kludge linemode stuff.  If we
48739503Sborman 			 * receive a will or wont TM in response to our
48839503Sborman 			 * do TM request that may have been sent to
48939503Sborman 			 * determine kludge linemode support, process
49039503Sborman 			 * it, otherwise TM should get a negative
49139503Sborman 			 * response back.
49238905Sborman 			 */
49338905Sborman 			/*
49438905Sborman 			 * Handle the linemode kludge stuff.
49538905Sborman 			 * If we are not currently supporting any
49638905Sborman 			 * linemode at all, then we assume that this
49738905Sborman 			 * is the client telling us to use kludge
49838905Sborman 			 * linemode in response to our query.  Set the
49938905Sborman 			 * linemode type that is to be supported, note
50038905Sborman 			 * that the client wishes to use linemode, and
50138905Sborman 			 * eat the will TM as though it never arrived.
50238905Sborman 			 */
50338905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
50438905Sborman 				lmodetype = KLUDGE_LINEMODE;
50538905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
50639503Sborman 				send_wont(TELOPT_SGA, 1);
50757212Sdab 			} else if (lmodetype == NO_AUTOKLUDGE) {
50857212Sdab 				lmodetype = KLUDGE_OK;
50938905Sborman 			}
51038905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
51138905Sborman 			/*
51239531Sborman 			 * We never respond to a WILL TM, and
51344364Sborman 			 * we leave the state WONT.
51438905Sborman 			 */
51538905Sborman 			return;
51638905Sborman 
51738905Sborman 		case TELOPT_LFLOW:
51838905Sborman 			/*
51939503Sborman 			 * If we are going to support flow control
52039503Sborman 			 * option, then don't worry peer that we can't
52139503Sborman 			 * change the flow control characters.
52238905Sborman 			 */
52338905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
52438905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
52538905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
52638905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
52738905Sborman 		case TELOPT_TTYPE:
52838905Sborman 		case TELOPT_SGA:
52938905Sborman 		case TELOPT_NAWS:
53038905Sborman 		case TELOPT_TSPEED:
53144364Sborman 		case TELOPT_XDISPLOC:
532*65158Sdab 		case TELOPT_NEW_ENVIRON:
533*65158Sdab 		case TELOPT_OLD_ENVIRON:
53439531Sborman 			changeok++;
53539531Sborman 			break;
53639531Sborman 
53738905Sborman #ifdef	LINEMODE
53838905Sborman 		case TELOPT_LINEMODE:
53939531Sborman # ifdef	KLUDGELINEMODE
54039531Sborman 			/*
54139531Sborman 			 * Note client's desire to use linemode.
54239531Sborman 			 */
54339531Sborman 			lmodetype = REAL_LINEMODE;
54439531Sborman # endif	/* KLUDGELINEMODE */
54546809Sdab 			func = doclientstat;
54646809Sdab 			changeok++;
54746809Sdab 			break;
54839531Sborman #endif	/* LINEMODE */
54938905Sborman 
55057212Sdab #ifdef	AUTHENTICATION
55146809Sdab 		case TELOPT_AUTHENTICATION:
55246809Sdab 			func = auth_request;
55346809Sdab 			changeok++;
55446809Sdab 			break;
55546809Sdab #endif
55646809Sdab 
55757212Sdab #ifdef	ENCRYPTION
55846809Sdab 		case TELOPT_ENCRYPT:
55946809Sdab 			func = encrypt_send_support;
56046809Sdab 			changeok++;
56146809Sdab 			break;
56260151Sdab #endif	/* ENCRYPTION */
56346809Sdab 
56438905Sborman 		default:
56538905Sborman 			break;
56638905Sborman 		}
56739503Sborman 		if (changeok) {
56844364Sborman 			set_his_want_state_will(option);
56939503Sborman 			send_do(option, 0);
57039503Sborman 		} else {
57139503Sborman 			do_dont_resp[option]++;
57239503Sborman 			send_dont(option, 0);
57338905Sborman 		}
57444364Sborman 	    } else {
57544364Sborman 		/*
57644364Sborman 		 * Option processing that should happen when
57744364Sborman 		 * we receive conformation of a change in
57844364Sborman 		 * state that we had requested.
57944364Sborman 		 */
58044364Sborman 		switch (option) {
58144364Sborman 		case TELOPT_ECHO:
58244364Sborman 			not42 = 0;	/* looks like a 4.2 system */
58344364Sborman 			/*
58444364Sborman 			 * Egads, he responded "WILL ECHO".  Turn
58544364Sborman 			 * it off right now!
58644364Sborman 			 */
58744364Sborman 			send_dont(option, 1);
58844364Sborman 			/*
58944364Sborman 			 * "WILL ECHO".  Kludge upon kludge!
59044364Sborman 			 * A 4.2 client is now echoing user input at
59144364Sborman 			 * the tty.  This is probably undesireable and
59244364Sborman 			 * it should be stopped.  The client will
59344364Sborman 			 * respond WONT TM to the DO TM that we send to
59444364Sborman 			 * check for kludge linemode.  When the WONT TM
59544364Sborman 			 * arrives, linemode will be turned off and a
59644364Sborman 			 * change propogated to the pty.  This change
59744364Sborman 			 * will cause us to process the new pty state
59844364Sborman 			 * in localstat(), which will notice that
59944364Sborman 			 * linemode is off and send a WILL ECHO
60044364Sborman 			 * so that we are properly in character mode and
60144364Sborman 			 * all is well.
60244364Sborman 			 */
60344364Sborman 			break;
60444364Sborman #ifdef	LINEMODE
60544364Sborman 		case TELOPT_LINEMODE:
60644364Sborman # ifdef	KLUDGELINEMODE
60744364Sborman 			/*
60844364Sborman 			 * Note client's desire to use linemode.
60944364Sborman 			 */
61044364Sborman 			lmodetype = REAL_LINEMODE;
61144364Sborman # endif	/* KLUDGELINEMODE */
61246809Sdab 			func = doclientstat;
61346809Sdab 			break;
61444364Sborman #endif	/* LINEMODE */
61546809Sdab 
61657212Sdab #ifdef	AUTHENTICATION
61746809Sdab 		case TELOPT_AUTHENTICATION:
61846809Sdab 			func = auth_request;
61946809Sdab 			break;
62046809Sdab #endif
62146809Sdab 
62257212Sdab #ifdef	ENCRYPTION
62346809Sdab 		case TELOPT_ENCRYPT:
62446809Sdab 			func = encrypt_send_support;
62546809Sdab 			break;
62660151Sdab #endif	/* ENCRYPTION */
62757597Sdab 		case TELOPT_LFLOW:
62858971Sdab 			func = flowstat;
62957597Sdab 			break;
63044364Sborman 		}
63139531Sborman 	    }
63238905Sborman 	}
63344364Sborman 	set_his_state_will(option);
63446809Sdab 	if (func)
63546809Sdab 		(*func)();
63638905Sborman }  /* end of willoption */
63738905Sborman 
63846809Sdab 	void
63939503Sborman send_dont(option, init)
64039503Sborman 	int option, init;
64138905Sborman {
64239503Sborman 	if (init) {
64344364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
64444364Sborman 		    his_want_state_is_wont(option))
64539503Sborman 			return;
64644364Sborman 		set_his_want_state_wont(option);
64739503Sborman 		do_dont_resp[option]++;
64839503Sborman 	}
649*65158Sdab 	(void) sprintf(nfrontp, (char *)dont, option);
65039503Sborman 	nfrontp += sizeof (doopt) - 2;
65146809Sdab 
65246809Sdab 	DIAG(TD_OPTIONS, printoption("td: send dont", option));
65339503Sborman }
65439503Sborman 
65546809Sdab 	void
65639503Sborman wontoption(option)
65739503Sborman 	int option;
65839503Sborman {
65938905Sborman 	/*
66038905Sborman 	 * Process client input.
66138905Sborman 	 */
66239503Sborman 
66346809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv wont", option));
66444364Sborman 
66539503Sborman 	if (do_dont_resp[option]) {
66639503Sborman 		do_dont_resp[option]--;
66744364Sborman 		if (do_dont_resp[option] && his_state_is_wont(option))
66839503Sborman 			do_dont_resp[option]--;
66939503Sborman 	}
67039531Sborman 	if (do_dont_resp[option] == 0) {
67144364Sborman 	    if (his_want_state_is_will(option)) {
67239503Sborman 		/* it is always ok to change to negative state */
67338905Sborman 		switch (option) {
67438905Sborman 		case TELOPT_ECHO:
67539503Sborman 			not42 = 1; /* doesn't seem to be a 4.2 system */
67638905Sborman 			break;
67738905Sborman 
67838905Sborman 		case TELOPT_BINARY:
67938905Sborman 			init_termbuf();
68038905Sborman 			tty_binaryin(0);
68138905Sborman 			set_termbuf();
68238905Sborman 			break;
68338905Sborman 
68438905Sborman #ifdef	LINEMODE
68538905Sborman 		case TELOPT_LINEMODE:
68638905Sborman # ifdef	KLUDGELINEMODE
68738905Sborman 			/*
68838905Sborman 			 * If real linemode is supported, then client is
68938905Sborman 			 * asking to turn linemode off.
69038905Sborman 			 */
69144364Sborman 			if (lmodetype != REAL_LINEMODE)
69244364Sborman 				break;
69344364Sborman 			lmodetype = KLUDGE_LINEMODE;
69438905Sborman # endif	/* KLUDGELINEMODE */
69544364Sborman 			clientstat(TELOPT_LINEMODE, WONT, 0);
69638905Sborman 			break;
69746809Sdab #endif	/* LINEMODE */
69838905Sborman 
69938905Sborman 		case TELOPT_TM:
70038905Sborman 			/*
70139503Sborman 			 * If we get a WONT TM, and had sent a DO TM,
70239503Sborman 			 * don't respond with a DONT TM, just leave it
70339503Sborman 			 * as is.  Short circut the state machine to
70439531Sborman 			 * achive this.
70538905Sborman 			 */
70644364Sborman 			set_his_want_state_wont(TELOPT_TM);
70738905Sborman 			return;
70838905Sborman 
70938905Sborman 		case TELOPT_LFLOW:
71038905Sborman 			/*
71139503Sborman 			 * If we are not going to support flow control
71239503Sborman 			 * option, then let peer know that we can't
71339503Sborman 			 * change the flow control characters.
71438905Sborman 			 */
71538905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
71638905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
71738905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
71838905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
71938905Sborman 			break;
72038905Sborman 
72157212Sdab #if	defined(AUTHENTICATION)
72246809Sdab 		case TELOPT_AUTHENTICATION:
72346809Sdab 			auth_finished(0, AUTH_REJECT);
72446809Sdab 			break;
72546809Sdab #endif
72646809Sdab 
72744364Sborman 		/*
72844364Sborman 		 * For options that we might spin waiting for
72944364Sborman 		 * sub-negotiation, if the client turns off the
73044364Sborman 		 * option rather than responding to the request,
73144364Sborman 		 * we have to treat it here as if we got a response
73244364Sborman 		 * to the sub-negotiation, (by updating the timers)
73344364Sborman 		 * so that we'll break out of the loop.
73444364Sborman 		 */
73544364Sborman 		case TELOPT_TTYPE:
73644364Sborman 			settimer(ttypesubopt);
73744364Sborman 			break;
73844364Sborman 
73944364Sborman 		case TELOPT_TSPEED:
74044364Sborman 			settimer(tspeedsubopt);
74144364Sborman 			break;
74244364Sborman 
74344364Sborman 		case TELOPT_XDISPLOC:
74444364Sborman 			settimer(xdisplocsubopt);
74544364Sborman 			break;
74644364Sborman 
747*65158Sdab 		case TELOPT_OLD_ENVIRON:
748*65158Sdab 			settimer(oenvironsubopt);
749*65158Sdab 			break;
750*65158Sdab 
751*65158Sdab 		case TELOPT_NEW_ENVIRON:
75244364Sborman 			settimer(environsubopt);
75344364Sborman 			break;
75444364Sborman 
75538905Sborman 		default:
75638905Sborman 			break;
75738905Sborman 		}
75844364Sborman 		set_his_want_state_wont(option);
75944364Sborman 		if (his_state_is_will(option))
76044364Sborman 			send_dont(option, 0);
76139531Sborman 	    } else {
76239531Sborman 		switch (option) {
76339531Sborman 		case TELOPT_TM:
76439531Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
76557212Sdab 			if (lmodetype < NO_AUTOKLUDGE) {
76639531Sborman 				lmodetype = NO_LINEMODE;
76739531Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
76839531Sborman 				send_will(TELOPT_SGA, 1);
76945234Sborman 				send_will(TELOPT_ECHO, 1);
77039531Sborman 			}
77139531Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
77246809Sdab 			break;
77346809Sdab 
77457212Sdab #if	defined(AUTHENTICATION)
77546809Sdab 		case TELOPT_AUTHENTICATION:
77646809Sdab 			auth_finished(0, AUTH_REJECT);
77746809Sdab 			break;
77846809Sdab #endif
77939531Sborman 		default:
78039531Sborman 			break;
78139531Sborman 		}
78239531Sborman 	    }
78338905Sborman 	}
78444364Sborman 	set_his_state_wont(option);
78538905Sborman 
78639503Sborman }  /* end of wontoption */
78738905Sborman 
78846809Sdab 	void
78939503Sborman send_will(option, init)
79039503Sborman 	int option, init;
79139503Sborman {
79239503Sborman 	if (init) {
79344364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
79444364Sborman 		    my_want_state_is_will(option))
79539503Sborman 			return;
79644364Sborman 		set_my_want_state_will(option);
79739503Sborman 		will_wont_resp[option]++;
79838905Sborman 	}
799*65158Sdab 	(void) sprintf(nfrontp, (char *)will, option);
80039503Sborman 	nfrontp += sizeof (doopt) - 2;
80146809Sdab 
80246809Sdab 	DIAG(TD_OPTIONS, printoption("td: send will", option));
80339503Sborman }
80438905Sborman 
80545234Sborman #if	!defined(LINEMODE) || !defined(KLUDGELINEMODE)
80645234Sborman /*
80745234Sborman  * When we get a DONT SGA, we will try once to turn it
80845234Sborman  * back on.  If the other side responds DONT SGA, we
80945234Sborman  * leave it at that.  This is so that when we talk to
81045234Sborman  * clients that understand KLUDGELINEMODE but not LINEMODE,
81145234Sborman  * we'll keep them in char-at-a-time mode.
81245234Sborman  */
81345234Sborman int turn_on_sga = 0;
81445234Sborman #endif
81545234Sborman 
81646809Sdab 	void
81739503Sborman dooption(option)
81839503Sborman 	int option;
81938905Sborman {
82038905Sborman 	int changeok = 0;
82138905Sborman 
82238905Sborman 	/*
82338905Sborman 	 * Process client input.
82438905Sborman 	 */
82539503Sborman 
82646809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv do", option));
82744364Sborman 
82839503Sborman 	if (will_wont_resp[option]) {
82939503Sborman 		will_wont_resp[option]--;
83044364Sborman 		if (will_wont_resp[option] && my_state_is_will(option))
83139503Sborman 			will_wont_resp[option]--;
83239503Sborman 	}
83344364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
83438905Sborman 		switch (option) {
83538905Sborman 		case TELOPT_ECHO:
83638905Sborman #ifdef	LINEMODE
83745234Sborman # ifdef	KLUDGELINEMODE
83845234Sborman 			if (lmodetype == NO_LINEMODE)
83945234Sborman # else
84045234Sborman 			if (his_state_is_wont(TELOPT_LINEMODE))
84145234Sborman # endif
84238905Sborman #endif
84345234Sborman 			{
84438905Sborman 				init_termbuf();
84538905Sborman 				tty_setecho(1);
84638905Sborman 				set_termbuf();
84738905Sborman 			}
84838905Sborman 			changeok++;
84938905Sborman 			break;
85038905Sborman 
85138905Sborman 		case TELOPT_BINARY:
85238905Sborman 			init_termbuf();
85338905Sborman 			tty_binaryout(1);
85438905Sborman 			set_termbuf();
85538905Sborman 			changeok++;
85638905Sborman 			break;
85738905Sborman 
85838905Sborman 		case TELOPT_SGA:
85938905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
86038905Sborman 			/*
86139503Sborman 			 * If kludge linemode is in use, then we must
86239503Sborman 			 * process an incoming do SGA for linemode
86339503Sborman 			 * purposes.
86438905Sborman 			 */
86538905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
86638905Sborman 				/*
86739503Sborman 				 * Receipt of "do SGA" in kludge
86839503Sborman 				 * linemode is the peer asking us to
86939503Sborman 				 * turn off linemode.  Make note of
87039503Sborman 				 * the request.
87138905Sborman 				 */
87238905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
87338905Sborman 				/*
87439503Sborman 				 * If linemode did not get turned off
87539503Sborman 				 * then don't tell peer that we did.
87639503Sborman 				 * Breaking here forces a wont SGA to
87739503Sborman 				 * be returned.
87838905Sborman 				 */
87938905Sborman 				if (linemode)
88038905Sborman 					break;
88138905Sborman 			}
88245234Sborman #else
88345234Sborman 			turn_on_sga = 0;
88438905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
88538905Sborman 			changeok++;
88638905Sborman 			break;
88738905Sborman 
88838905Sborman 		case TELOPT_STATUS:
88938905Sborman 			changeok++;
89038905Sborman 			break;
89138905Sborman 
89238905Sborman 		case TELOPT_TM:
89339503Sborman 			/*
89439503Sborman 			 * Special case for TM.  We send a WILL, but
89539503Sborman 			 * pretend we sent a WONT.
89639503Sborman 			 */
89739503Sborman 			send_will(option, 0);
89844364Sborman 			set_my_want_state_wont(option);
89944364Sborman 			set_my_state_wont(option);
90039503Sborman 			return;
90139503Sborman 
90246809Sdab 		case TELOPT_LOGOUT:
90346809Sdab 			/*
90446809Sdab 			 * When we get a LOGOUT option, respond
90546809Sdab 			 * with a WILL LOGOUT, make sure that
90646809Sdab 			 * it gets written out to the network,
90746809Sdab 			 * and then just go away...
90846809Sdab 			 */
90946809Sdab 			set_my_want_state_will(TELOPT_LOGOUT);
91046809Sdab 			send_will(TELOPT_LOGOUT, 0);
91146809Sdab 			set_my_state_will(TELOPT_LOGOUT);
91246809Sdab 			(void)netflush();
91346809Sdab 			cleanup(0);
91446809Sdab 			/* NOT REACHED */
91546809Sdab 			break;
91646809Sdab 
91760151Sdab #ifdef	ENCRYPTION
91846809Sdab 		case TELOPT_ENCRYPT:
91946809Sdab 			changeok++;
92046809Sdab 			break;
92160151Sdab #endif	/* ENCRYPTION */
92238905Sborman 		case TELOPT_LINEMODE:
92338905Sborman 		case TELOPT_TTYPE:
92438905Sborman 		case TELOPT_NAWS:
92538905Sborman 		case TELOPT_TSPEED:
92638905Sborman 		case TELOPT_LFLOW:
92744364Sborman 		case TELOPT_XDISPLOC:
928*65158Sdab #ifdef	TELOPT_ENVIRON
929*65158Sdab 		case TELOPT_NEW_ENVIRON:
930*65158Sdab #endif
931*65158Sdab 		case TELOPT_OLD_ENVIRON:
93238905Sborman 		default:
93338905Sborman 			break;
93438905Sborman 		}
93539503Sborman 		if (changeok) {
93644364Sborman 			set_my_want_state_will(option);
93739503Sborman 			send_will(option, 0);
93839503Sborman 		} else {
93939503Sborman 			will_wont_resp[option]++;
94039503Sborman 			send_wont(option, 0);
94138905Sborman 		}
94238905Sborman 	}
94344364Sborman 	set_my_state_will(option);
94438905Sborman 
94538905Sborman }  /* end of dooption */
94638905Sborman 
94746809Sdab 	void
94839503Sborman send_wont(option, init)
94939503Sborman 	int option, init;
95039503Sborman {
95139503Sborman 	if (init) {
95244364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
95344364Sborman 		    my_want_state_is_wont(option))
95439503Sborman 			return;
95544364Sborman 		set_my_want_state_wont(option);
95639503Sborman 		will_wont_resp[option]++;
95739503Sborman 	}
958*65158Sdab 	(void) sprintf(nfrontp, (char *)wont, option);
95939503Sborman 	nfrontp += sizeof (wont) - 2;
96046809Sdab 
96146809Sdab 	DIAG(TD_OPTIONS, printoption("td: send wont", option));
96239503Sborman }
96338905Sborman 
96446809Sdab 	void
96539503Sborman dontoption(option)
96639503Sborman 	int option;
96738905Sborman {
96838905Sborman 	/*
96938905Sborman 	 * Process client input.
97038905Sborman 	 */
97140242Sborman 
97246809Sdab 
97346809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv dont", option));
97446809Sdab 
97539503Sborman 	if (will_wont_resp[option]) {
97639503Sborman 		will_wont_resp[option]--;
97744364Sborman 		if (will_wont_resp[option] && my_state_is_wont(option))
97839503Sborman 			will_wont_resp[option]--;
97939503Sborman 	}
98044364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
98138905Sborman 		switch (option) {
98238905Sborman 		case TELOPT_BINARY:
98338905Sborman 			init_termbuf();
98438905Sborman 			tty_binaryout(0);
98538905Sborman 			set_termbuf();
98638905Sborman 			break;
98738905Sborman 
98839503Sborman 		case TELOPT_ECHO:	/* we should stop echoing */
98938905Sborman #ifdef	LINEMODE
99045234Sborman # ifdef	KLUDGELINEMODE
99157212Sdab 			if ((lmodetype != REAL_LINEMODE) &&
99257212Sdab 			    (lmodetype != KLUDGE_LINEMODE))
99345234Sborman # else
99445234Sborman 			if (his_state_is_wont(TELOPT_LINEMODE))
99545234Sborman # endif
99638905Sborman #endif
99745234Sborman 			{
99838905Sborman 				init_termbuf();
99938905Sborman 				tty_setecho(0);
100038905Sborman 				set_termbuf();
100138905Sborman 			}
100238905Sborman 			break;
100338905Sborman 
100438905Sborman 		case TELOPT_SGA:
100538905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
100638905Sborman 			/*
100739503Sborman 			 * If kludge linemode is in use, then we
100839503Sborman 			 * must process an incoming do SGA for
100939503Sborman 			 * linemode purposes.
101038905Sborman 			 */
101157212Sdab 			if ((lmodetype == KLUDGE_LINEMODE) ||
101257212Sdab 			    (lmodetype == KLUDGE_OK)) {
101338905Sborman 				/*
101439503Sborman 				 * The client is asking us to turn
101539503Sborman 				 * linemode on.
101638905Sborman 				 */
101757212Sdab 				lmodetype = KLUDGE_LINEMODE;
101838905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
101938905Sborman 				/*
102039503Sborman 				 * If we did not turn line mode on,
102139503Sborman 				 * then what do we say?  Will SGA?
102239503Sborman 				 * This violates design of telnet.
102339503Sborman 				 * Gross.  Very Gross.
102438905Sborman 				 */
102538905Sborman 			}
102645234Sborman 			break;
102745234Sborman #else
102845234Sborman 			set_my_want_state_wont(option);
102945234Sborman 			if (my_state_is_will(option))
103045234Sborman 				send_wont(option, 0);
103145234Sborman 			set_my_state_wont(option);
103245234Sborman 			if (turn_on_sga ^= 1)
103357212Sdab 				send_will(option, 1);
103445234Sborman 			return;
103538905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
103638905Sborman 
103738905Sborman 		default:
103838905Sborman 			break;
103938905Sborman 		}
104038905Sborman 
104144364Sborman 		set_my_want_state_wont(option);
104244364Sborman 		if (my_state_is_will(option))
104344364Sborman 			send_wont(option, 0);
104438905Sborman 	}
104544364Sborman 	set_my_state_wont(option);
104638905Sborman 
104738905Sborman }  /* end of dontoption */
104838905Sborman 
104958971Sdab #ifdef	ENV_HACK
1050*65158Sdab int env_ovar = -1;
1051*65158Sdab int env_ovalue = -1;
105258971Sdab #else	/* ENV_HACK */
1053*65158Sdab # define env_ovar OLD_ENV_VAR
1054*65158Sdab # define env_ovalue OLD_ENV_VALUE
105558971Sdab #endif	/* ENV_HACK */
105658971Sdab 
105738905Sborman /*
105838905Sborman  * suboption()
105938905Sborman  *
106038905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
106138905Sborman  * side.
106238905Sborman  *
106338905Sborman  *	Currently we recognize:
106438905Sborman  *
106538905Sborman  *	Terminal type is
106638905Sborman  *	Linemode
106738905Sborman  *	Window size
106838905Sborman  *	Terminal speed
106938905Sborman  */
107046809Sdab 	void
107138905Sborman suboption()
107238905Sborman {
107338905Sborman     register int subchar;
107438905Sborman 
107546809Sdab     DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
107646809Sdab 
107738905Sborman     subchar = SB_GET();
107838905Sborman     switch (subchar) {
107938905Sborman     case TELOPT_TSPEED: {
108038905Sborman 	register int xspeed, rspeed;
108138905Sborman 
108244364Sborman 	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
108338905Sborman 		break;
108438905Sborman 
108538905Sborman 	settimer(tspeedsubopt);
108638905Sborman 
108738905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
108838905Sborman 		return;
108938905Sborman 
109046809Sdab 	xspeed = atoi((char *)subpointer);
109138905Sborman 
109238905Sborman 	while (SB_GET() != ',' && !SB_EOF());
109338905Sborman 	if (SB_EOF())
109438905Sborman 		return;
109538905Sborman 
109646809Sdab 	rspeed = atoi((char *)subpointer);
109738905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
109838905Sborman 
109938905Sborman 	break;
110038905Sborman 
110138905Sborman     }  /* end of case TELOPT_TSPEED */
110238905Sborman 
110338905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
110444364Sborman 	static char terminalname[41];
110538905Sborman 
110644364Sborman 	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
110738905Sborman 		break;
110838905Sborman 	settimer(ttypesubopt);
110938905Sborman 
111046809Sdab 	if (SB_EOF() || SB_GET() != TELQUAL_IS) {
111138905Sborman 	    return;		/* ??? XXX but, this is the most robust */
111238905Sborman 	}
111338905Sborman 
111444364Sborman 	terminaltype = terminalname;
111538905Sborman 
111638905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
111738905Sborman 								    !SB_EOF()) {
111838905Sborman 	    register int c;
111938905Sborman 
112038905Sborman 	    c = SB_GET();
112138905Sborman 	    if (isupper(c)) {
112238905Sborman 		c = tolower(c);
112338905Sborman 	    }
112438905Sborman 	    *terminaltype++ = c;    /* accumulate name */
112538905Sborman 	}
112638905Sborman 	*terminaltype = 0;
112738905Sborman 	terminaltype = terminalname;
112838905Sborman 	break;
112938905Sborman     }  /* end of case TELOPT_TTYPE */
113038905Sborman 
113138905Sborman     case TELOPT_NAWS: {
113238905Sborman 	register int xwinsize, ywinsize;
113338905Sborman 
113444364Sborman 	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
113538905Sborman 		break;
113638905Sborman 
113738905Sborman 	if (SB_EOF())
113838905Sborman 		return;
113938905Sborman 	xwinsize = SB_GET() << 8;
114038905Sborman 	if (SB_EOF())
114138905Sborman 		return;
114238905Sborman 	xwinsize |= SB_GET();
114338905Sborman 	if (SB_EOF())
114438905Sborman 		return;
114538905Sborman 	ywinsize = SB_GET() << 8;
114638905Sborman 	if (SB_EOF())
114738905Sborman 		return;
114838905Sborman 	ywinsize |= SB_GET();
114938905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
115038905Sborman 
115138905Sborman 	break;
115238905Sborman 
115338905Sborman     }  /* end of case TELOPT_NAWS */
115438905Sborman 
115538905Sborman #ifdef	LINEMODE
115638905Sborman     case TELOPT_LINEMODE: {
115738905Sborman 	register int request;
115838905Sborman 
115944364Sborman 	if (his_state_is_wont(TELOPT_LINEMODE))	/* Ignore if option disabled */
116038905Sborman 		break;
116138905Sborman 	/*
116238905Sborman 	 * Process linemode suboptions.
116338905Sborman 	 */
116446809Sdab 	if (SB_EOF())
116546809Sdab 	    break;		/* garbage was sent */
116646809Sdab 	request = SB_GET();	/* get will/wont */
116738905Sborman 
116846809Sdab 	if (SB_EOF())
116946809Sdab 	    break;		/* another garbage check */
117046809Sdab 
117138905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
117238905Sborman 		/*
117338905Sborman 		 * Process suboption buffer of slc's
117438905Sborman 		 */
117538905Sborman 		start_slc(1);
117638905Sborman 		do_opt_slc(subpointer, subend - subpointer);
117746809Sdab 		(void) end_slc(0);
117846809Sdab 		break;
117938905Sborman 	} else if (request == LM_MODE) {
118046809Sdab 		if (SB_EOF())
118146809Sdab 		    return;
118238905Sborman 		useeditmode = SB_GET();  /* get mode flag */
118338905Sborman 		clientstat(LM_MODE, 0, 0);
118446809Sdab 		break;
118538905Sborman 	}
118638905Sborman 
118746809Sdab 	if (SB_EOF())
118846809Sdab 	    break;
118938905Sborman 	switch (SB_GET()) {  /* what suboption? */
119038905Sborman 	case LM_FORWARDMASK:
119138905Sborman 		/*
119238905Sborman 		 * According to spec, only server can send request for
119338905Sborman 		 * forwardmask, and client can only return a positive response.
119438905Sborman 		 * So don't worry about it.
119538905Sborman 		 */
119638905Sborman 
119738905Sborman 	default:
119838905Sborman 		break;
119938905Sborman 	}
120040242Sborman 	break;
120138905Sborman     }  /* end of case TELOPT_LINEMODE */
120238905Sborman #endif
120338905Sborman     case TELOPT_STATUS: {
120438905Sborman 	int mode;
120538905Sborman 
120646809Sdab 	if (SB_EOF())
120746809Sdab 	    break;
120838905Sborman 	mode = SB_GET();
120938905Sborman 	switch (mode) {
121038905Sborman 	case TELQUAL_SEND:
121144364Sborman 	    if (my_state_is_will(TELOPT_STATUS))
121238905Sborman 		send_status();
121338905Sborman 	    break;
121438905Sborman 
121538905Sborman 	case TELQUAL_IS:
121638905Sborman 	    break;
121738905Sborman 
121838905Sborman 	default:
121938905Sborman 	    break;
122038905Sborman 	}
122140242Sborman 	break;
122240242Sborman     }  /* end of case TELOPT_STATUS */
122338905Sborman 
122444364Sborman     case TELOPT_XDISPLOC: {
122544364Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
122644364Sborman 		return;
122744364Sborman 	settimer(xdisplocsubopt);
122844364Sborman 	subpointer[SB_LEN()] = '\0';
122946809Sdab 	(void)setenv("DISPLAY", (char *)subpointer, 1);
123044364Sborman 	break;
123144364Sborman     }  /* end of case TELOPT_XDISPLOC */
123244364Sborman 
1233*65158Sdab #ifdef	TELOPT_NEW_ENVIRON
1234*65158Sdab     case TELOPT_NEW_ENVIRON:
1235*65158Sdab #endif
1236*65158Sdab     case TELOPT_OLD_ENVIRON: {
123744364Sborman 	register int c;
123844364Sborman 	register char *cp, *varp, *valp;
123944364Sborman 
124044364Sborman 	if (SB_EOF())
124144364Sborman 		return;
124244364Sborman 	c = SB_GET();
1243*65158Sdab 	if (c == TELQUAL_IS) {
1244*65158Sdab 		if (subchar == TELOPT_OLD_ENVIRON)
1245*65158Sdab 			settimer(oenvironsubopt);
1246*65158Sdab 		else
1247*65158Sdab 			settimer(environsubopt);
1248*65158Sdab 	} else if (c != TELQUAL_INFO) {
124944364Sborman 		return;
1250*65158Sdab 	}
125144364Sborman 
1252*65158Sdab #ifdef	TELOPT_NEW_ENVIRON
1253*65158Sdab 	if (subchar == TELOPT_NEW_ENVIRON) {
1254*65158Sdab 	    while (!SB_EOF()) {
1255*65158Sdab 		c = SB_GET();
1256*65158Sdab 		if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
1257*65158Sdab 			break;
1258*65158Sdab 	    }
1259*65158Sdab 	} else
1260*65158Sdab #endif
1261*65158Sdab 	{
126258971Sdab #ifdef	ENV_HACK
1263*65158Sdab 	    /*
1264*65158Sdab 	     * We only want to do this if we haven't already decided
1265*65158Sdab 	     * whether or not the other side has its VALUE and VAR
1266*65158Sdab 	     * reversed.
1267*65158Sdab 	     */
1268*65158Sdab 	    if (env_ovar < 0) {
126958971Sdab 		register int last = -1;		/* invalid value */
127058971Sdab 		int empty = 0;
127158971Sdab 		int got_var = 0, got_value = 0, got_uservar = 0;
127258971Sdab 
127358971Sdab 		/*
127458971Sdab 		 * The other side might have its VALUE and VAR values
127558971Sdab 		 * reversed.  To be interoperable, we need to determine
127658971Sdab 		 * which way it is.  If the first recognized character
127758971Sdab 		 * is a VAR or VALUE, then that will tell us what
127858971Sdab 		 * type of client it is.  If the fist recognized
127958971Sdab 		 * character is a USERVAR, then we continue scanning
128058971Sdab 		 * the suboption looking for two consecutive
128158971Sdab 		 * VAR or VALUE fields.  We should not get two
128258971Sdab 		 * consecutive VALUE fields, so finding two
128358971Sdab 		 * consecutive VALUE or VAR fields will tell us
128458971Sdab 		 * what the client is.
128558971Sdab 		 */
128658971Sdab 		SB_SAVE();
128758971Sdab 		while (!SB_EOF()) {
128858971Sdab 			c = SB_GET();
128958971Sdab 			switch(c) {
1290*65158Sdab 			case OLD_ENV_VAR:
1291*65158Sdab 				if (last < 0 || last == OLD_ENV_VAR
1292*65158Sdab 				    || (empty && (last == OLD_ENV_VALUE)))
1293*65158Sdab 					goto env_ovar_ok;
129458971Sdab 				got_var++;
1295*65158Sdab 				last = OLD_ENV_VAR;
129658971Sdab 				break;
1297*65158Sdab 			case OLD_ENV_VALUE:
1298*65158Sdab 				if (last < 0 || last == OLD_ENV_VALUE
1299*65158Sdab 				    || (empty && (last == OLD_ENV_VAR)))
1300*65158Sdab 					goto env_ovar_wrong;
130158971Sdab 				got_value++;
1302*65158Sdab 				last = OLD_ENV_VALUE;
130358971Sdab 				break;
130458971Sdab 			case ENV_USERVAR:
130558971Sdab 				/* count strings of USERVAR as one */
130658971Sdab 				if (last != ENV_USERVAR)
130758971Sdab 					got_uservar++;
130858971Sdab 				if (empty) {
1309*65158Sdab 					if (last == OLD_ENV_VALUE)
1310*65158Sdab 						goto env_ovar_ok;
1311*65158Sdab 					if (last == OLD_ENV_VAR)
1312*65158Sdab 						goto env_ovar_wrong;
131358971Sdab 				}
131458971Sdab 				last = ENV_USERVAR;
131558971Sdab 				break;
131658971Sdab 			case ENV_ESC:
131758971Sdab 				if (!SB_EOF())
131858971Sdab 					c = SB_GET();
131958971Sdab 				/* FALL THROUGH */
132058971Sdab 			default:
132158971Sdab 				empty = 0;
132258971Sdab 				continue;
132358971Sdab 			}
132458971Sdab 			empty = 1;
132558971Sdab 		}
132658971Sdab 		if (empty) {
1327*65158Sdab 			if (last == OLD_ENV_VALUE)
1328*65158Sdab 				goto env_ovar_ok;
1329*65158Sdab 			if (last == OLD_ENV_VAR)
1330*65158Sdab 				goto env_ovar_wrong;
133158971Sdab 		}
133258971Sdab 		/*
133358971Sdab 		 * Ok, the first thing was a USERVAR, and there
133458971Sdab 		 * are not two consecutive VAR or VALUE commands,
133558971Sdab 		 * and none of the VAR or VALUE commands are empty.
133658971Sdab 		 * If the client has sent us a well-formed option,
133758971Sdab 		 * then the number of VALUEs received should always
133858971Sdab 		 * be less than or equal to the number of VARs and
133958971Sdab 		 * USERVARs received.
134058971Sdab 		 *
134158971Sdab 		 * If we got exactly as many VALUEs as VARs and
134258971Sdab 		 * USERVARs, the client has the same definitions.
134358971Sdab 		 *
134458971Sdab 		 * If we got exactly as many VARs as VALUEs and
134558971Sdab 		 * USERVARS, the client has reversed definitions.
134658971Sdab 		 */
134760151Sdab 		if (got_uservar + got_var == got_value) {
1348*65158Sdab 	    env_ovar_ok:
1349*65158Sdab 			env_ovar = OLD_ENV_VAR;
1350*65158Sdab 			env_ovalue = OLD_ENV_VALUE;
135160151Sdab 		} else if (got_uservar + got_value == got_var) {
1352*65158Sdab 	    env_ovar_wrong:
1353*65158Sdab 			env_ovar = OLD_ENV_VALUE;
1354*65158Sdab 			env_ovalue = OLD_ENV_VAR;
135558971Sdab 			DIAG(TD_OPTIONS, {sprintf(nfrontp,
135658971Sdab 				"ENVIRON VALUE and VAR are reversed!\r\n");
135758971Sdab 				nfrontp += strlen(nfrontp);});
135858971Sdab 
135958971Sdab 		}
1360*65158Sdab 	    }
1361*65158Sdab 	    SB_RESTORE();
136258971Sdab #endif
136358971Sdab 
1364*65158Sdab 	    while (!SB_EOF()) {
136557212Sdab 		c = SB_GET();
1366*65158Sdab 		if ((c == env_ovar) || (c == ENV_USERVAR))
136757212Sdab 			break;
1368*65158Sdab 	    }
136957212Sdab 	}
137044364Sborman 
137144364Sborman 	if (SB_EOF())
137244364Sborman 		return;
137344364Sborman 
137446809Sdab 	cp = varp = (char *)subpointer;
137544364Sborman 	valp = 0;
137644364Sborman 
137744364Sborman 	while (!SB_EOF()) {
137858971Sdab 		c = SB_GET();
1379*65158Sdab 		if (subchar == TELOPT_OLD_ENVIRON) {
1380*65158Sdab 			if (c == env_ovar)
1381*65158Sdab 				c = NEW_ENV_VAR;
1382*65158Sdab 			else if (c == env_ovalue)
1383*65158Sdab 				c = NEW_ENV_VALUE;
1384*65158Sdab 		}
138558971Sdab 		switch (c) {
138658971Sdab 
1387*65158Sdab 		case NEW_ENV_VALUE:
138844364Sborman 			*cp = '\0';
138946809Sdab 			cp = valp = (char *)subpointer;
139044364Sborman 			break;
139144364Sborman 
1392*65158Sdab 		case NEW_ENV_VAR:
139357212Sdab 		case ENV_USERVAR:
139444364Sborman 			*cp = '\0';
139544364Sborman 			if (valp)
139646809Sdab 				(void)setenv(varp, valp, 1);
139744364Sborman 			else
139844364Sborman 				unsetenv(varp);
139946809Sdab 			cp = varp = (char *)subpointer;
140044364Sborman 			valp = 0;
140144364Sborman 			break;
140244364Sborman 
140344364Sborman 		case ENV_ESC:
140444364Sborman 			if (SB_EOF())
140544364Sborman 				break;
140644364Sborman 			c = SB_GET();
140744364Sborman 			/* FALL THROUGH */
140844364Sborman 		default:
140944364Sborman 			*cp++ = c;
141044364Sborman 			break;
141144364Sborman 		}
141244364Sborman 	}
141344364Sborman 	*cp = '\0';
141444364Sborman 	if (valp)
141546809Sdab 		(void)setenv(varp, valp, 1);
141644364Sborman 	else
141744364Sborman 		unsetenv(varp);
141844364Sborman 	break;
1419*65158Sdab     }  /* end of case TELOPT_NEW_ENVIRON */
142057212Sdab #if	defined(AUTHENTICATION)
142146809Sdab     case TELOPT_AUTHENTICATION:
142246809Sdab 	if (SB_EOF())
142346809Sdab 		break;
142446809Sdab 	switch(SB_GET()) {
142546809Sdab 	case TELQUAL_SEND:
142646809Sdab 	case TELQUAL_REPLY:
142746809Sdab 		/*
142846809Sdab 		 * These are sent by us and cannot be sent by
142946809Sdab 		 * the client.
143046809Sdab 		 */
143146809Sdab 		break;
143246809Sdab 	case TELQUAL_IS:
143346809Sdab 		auth_is(subpointer, SB_LEN());
143446809Sdab 		break;
143547611Sdab 	case TELQUAL_NAME:
143647611Sdab 		auth_name(subpointer, SB_LEN());
143747611Sdab 		break;
143846809Sdab 	}
143946809Sdab 	break;
144046809Sdab #endif
144160151Sdab #ifdef	ENCRYPTION
144246809Sdab     case TELOPT_ENCRYPT:
144346809Sdab 	if (SB_EOF())
144446809Sdab 		break;
144546809Sdab 	switch(SB_GET()) {
144646809Sdab 	case ENCRYPT_SUPPORT:
144746809Sdab 		encrypt_support(subpointer, SB_LEN());
144846809Sdab 		break;
144946809Sdab 	case ENCRYPT_IS:
145046809Sdab 		encrypt_is(subpointer, SB_LEN());
145146809Sdab 		break;
145246809Sdab 	case ENCRYPT_REPLY:
145346809Sdab 		encrypt_reply(subpointer, SB_LEN());
145446809Sdab 		break;
145546809Sdab 	case ENCRYPT_START:
145647611Sdab 		encrypt_start(subpointer, SB_LEN());
145746809Sdab 		break;
145846809Sdab 	case ENCRYPT_END:
145946809Sdab 		encrypt_end();
146046809Sdab 		break;
146146809Sdab 	case ENCRYPT_REQSTART:
146247611Sdab 		encrypt_request_start(subpointer, SB_LEN());
146346809Sdab 		break;
146446809Sdab 	case ENCRYPT_REQEND:
146546809Sdab 		/*
146646809Sdab 		 * We can always send an REQEND so that we cannot
146746809Sdab 		 * get stuck encrypting.  We should only get this
146846809Sdab 		 * if we have been able to get in the correct mode
146946809Sdab 		 * anyhow.
147046809Sdab 		 */
147146809Sdab 		encrypt_request_end();
147246809Sdab 		break;
147347611Sdab 	case ENCRYPT_ENC_KEYID:
147447611Sdab 		encrypt_enc_keyid(subpointer, SB_LEN());
147547611Sdab 		break;
147647611Sdab 	case ENCRYPT_DEC_KEYID:
147747611Sdab 		encrypt_dec_keyid(subpointer, SB_LEN());
147847611Sdab 		break;
147946809Sdab 	default:
148046809Sdab 		break;
148146809Sdab 	}
148246809Sdab 	break;
148360151Sdab #endif	/* ENCRYPTION */
148444364Sborman 
148538905Sborman     default:
148638905Sborman 	break;
148738905Sborman     }  /* end of switch */
148838905Sborman 
148938905Sborman }  /* end of suboption */
149038905Sborman 
149146809Sdab 	void
149246809Sdab doclientstat()
149346809Sdab {
149446809Sdab 	clientstat(TELOPT_LINEMODE, WILL, 0);
149546809Sdab }
149646809Sdab 
149738905Sborman #define	ADD(c)	 *ncp++ = c;
149838905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
149946809Sdab 	void
150038905Sborman send_status()
150138905Sborman {
150244364Sborman 	unsigned char statusbuf[256];
150344364Sborman 	register unsigned char *ncp;
150444364Sborman 	register unsigned char i;
150538905Sborman 
150638905Sborman 	ncp = statusbuf;
150738905Sborman 
150838905Sborman 	netflush();	/* get rid of anything waiting to go out */
150938905Sborman 
151038905Sborman 	ADD(IAC);
151138905Sborman 	ADD(SB);
151238905Sborman 	ADD(TELOPT_STATUS);
151338905Sborman 	ADD(TELQUAL_IS);
151438905Sborman 
151546809Sdab 	/*
151646809Sdab 	 * We check the want_state rather than the current state,
151746809Sdab 	 * because if we received a DO/WILL for an option that we
151846809Sdab 	 * don't support, and the other side didn't send a DONT/WONT
151946809Sdab 	 * in response to our WONT/DONT, then the "state" will be
152046809Sdab 	 * WILL/DO, and the "want_state" will be WONT/DONT.  We
152146809Sdab 	 * need to go by the latter.
152246809Sdab 	 */
1523*65158Sdab 	for (i = 0; i < (unsigned char)NTELOPTS; i++) {
152446809Sdab 		if (my_want_state_is_will(i)) {
152538905Sborman 			ADD(WILL);
152638905Sborman 			ADD_DATA(i);
152738905Sborman 			if (i == IAC)
152838905Sborman 				ADD(IAC);
152938905Sborman 		}
153046809Sdab 		if (his_want_state_is_will(i)) {
153138905Sborman 			ADD(DO);
153238905Sborman 			ADD_DATA(i);
153338905Sborman 			if (i == IAC)
153438905Sborman 				ADD(IAC);
153538905Sborman 		}
153638905Sborman 	}
153738905Sborman 
153846809Sdab 	if (his_want_state_is_will(TELOPT_LFLOW)) {
153946809Sdab 		ADD(SB);
154046809Sdab 		ADD(TELOPT_LFLOW);
154157212Sdab 		if (flowmode) {
154257212Sdab 			ADD(LFLOW_ON);
154357212Sdab 		} else {
154457212Sdab 			ADD(LFLOW_OFF);
154557212Sdab 		}
154646809Sdab 		ADD(SE);
154757212Sdab 
154857212Sdab 		if (restartany >= 0) {
154957212Sdab 			ADD(SB)
155057212Sdab 			ADD(TELOPT_LFLOW);
155157212Sdab 			if (restartany) {
155257212Sdab 				ADD(LFLOW_RESTART_ANY);
155357212Sdab 			} else {
155457212Sdab 				ADD(LFLOW_RESTART_XON);
155557212Sdab 			}
155657212Sdab 			ADD(SE)
155757212Sdab 			ADD(SB);
155857212Sdab 		}
155946809Sdab 	}
156046809Sdab 
156138905Sborman #ifdef	LINEMODE
156246809Sdab 	if (his_want_state_is_will(TELOPT_LINEMODE)) {
156344364Sborman 		unsigned char *cp, *cpe;
156438905Sborman 		int len;
156538905Sborman 
156638905Sborman 		ADD(SB);
156738905Sborman 		ADD(TELOPT_LINEMODE);
156838905Sborman 		ADD(LM_MODE);
156938905Sborman 		ADD_DATA(editmode);
157038905Sborman 		if (editmode == IAC)
157138905Sborman 			ADD(IAC);
157238905Sborman 		ADD(SE);
157338905Sborman 
157438905Sborman 		ADD(SB);
157538905Sborman 		ADD(TELOPT_LINEMODE);
157638905Sborman 		ADD(LM_SLC);
157738905Sborman 		start_slc(0);
157838905Sborman 		send_slc();
157938905Sborman 		len = end_slc(&cp);
158038905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
158138905Sborman 			ADD_DATA(*cp);
158238905Sborman 		ADD(SE);
158338905Sborman 	}
158438905Sborman #endif	/* LINEMODE */
158538905Sborman 
158638905Sborman 	ADD(IAC);
158738905Sborman 	ADD(SE);
158838905Sborman 
158938905Sborman 	writenet(statusbuf, ncp - statusbuf);
159038905Sborman 	netflush();	/* Send it on its way */
159146809Sdab 
159246809Sdab 	DIAG(TD_OPTIONS,
159346809Sdab 		{printsub('>', statusbuf, ncp - statusbuf); netflush();});
159438905Sborman }
1595