xref: /csrg-svn/libexec/telnetd/state.c (revision 57212)
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*57212Sdab static char sccsid[] = "@(#)state.c	5.11 (Berkeley) 12/18/92";
1038905Sborman #endif /* not lint */
1138905Sborman 
1238905Sborman #include "telnetd.h"
13*57212Sdab #if	defined(AUTHENTICATION)
1446809Sdab #include <libtelnet/auth.h>
1546809Sdab #endif
1638905Sborman 
1738905Sborman char	doopt[] = { IAC, DO, '%', 'c', 0 };
1838905Sborman char	dont[] = { IAC, DONT, '%', 'c', 0 };
1938905Sborman char	will[] = { IAC, WILL, '%', 'c', 0 };
2038905Sborman 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 
2938905Sborman #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 
3838905Sborman 
3938905Sborman 
4038905Sborman /*
4138905Sborman  * State for recv fsm
4238905Sborman  */
4338905Sborman #define	TS_DATA		0	/* base state */
4438905Sborman #define	TS_IAC		1	/* look for double IAC's */
4538905Sborman #define	TS_CR		2	/* CR-LF ->'s CR */
4638905Sborman #define	TS_SB		3	/* throw away begin's... */
4738905Sborman #define	TS_SE		4	/* ...end's (suboption negotiation) */
4838905Sborman #define	TS_WILL		5	/* will option negotiation */
4938905Sborman #define	TS_WONT		6	/* wont " */
5038905Sborman #define	TS_DO		7	/* do " */
5138905Sborman #define	TS_DONT		8	/* dont " */
5238905Sborman 
5346809Sdab 	void
5438905Sborman telrcv()
5538905Sborman {
5638905Sborman 	register int c;
5738905Sborman 	static int state = TS_DATA;
5840242Sborman #if	defined(CRAY2) && defined(UNICOS5)
5938905Sborman 	char *opfrontp = pfrontp;
6038905Sborman #endif
6138905Sborman 
6238905Sborman 	while (ncc > 0) {
6338905Sborman 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6438905Sborman 			break;
6538905Sborman 		c = *netip++ & 0377, ncc--;
66*57212Sdab #if	defined(ENCRYPTION)
6746809Sdab 		if (decrypt_input)
6846809Sdab 			c = (*decrypt_input)(c);
6946809Sdab #endif
7038905Sborman 		switch (state) {
7138905Sborman 
7238905Sborman 		case TS_CR:
7338905Sborman 			state = TS_DATA;
7438905Sborman 			/* Strip off \n or \0 after a \r */
7538905Sborman 			if ((c == 0) || (c == '\n')) {
7638905Sborman 				break;
7738905Sborman 			}
7838905Sborman 			/* FALL THROUGH */
7938905Sborman 
8038905Sborman 		case TS_DATA:
8138905Sborman 			if (c == IAC) {
8238905Sborman 				state = TS_IAC;
8338905Sborman 				break;
8438905Sborman 			}
8538905Sborman 			/*
8638905Sborman 			 * We now map \r\n ==> \r for pragmatic reasons.
8738905Sborman 			 * Many client implementations send \r\n when
8838905Sborman 			 * the user hits the CarriageReturn key.
8938905Sborman 			 *
9038905Sborman 			 * We USED to map \r\n ==> \n, since \r\n says
9138905Sborman 			 * that we want to be in column 1 of the next
9238905Sborman 			 * printable line, and \n is the standard
9338905Sborman 			 * unix way of saying that (\r is only good
9438905Sborman 			 * if CRMOD is set, which it normally is).
9538905Sborman 			 */
9644364Sborman 			if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
9746809Sdab 				int nc = *netip;
98*57212Sdab #if	defined(ENCRYPTION)
9946809Sdab 				if (decrypt_input)
10046809Sdab 					nc = (*decrypt_input)(nc & 0xff);
10146809Sdab #endif
10246809Sdab #ifdef	LINEMODE
10338905Sborman 				/*
10438905Sborman 				 * If we are operating in linemode,
10538905Sborman 				 * convert to local end-of-line.
10638905Sborman 				 */
10746809Sdab 				if (linemode && (ncc > 0) && (('\n' == nc) ||
10846809Sdab 					 ((0 == nc) && tty_iscrnl())) ) {
10938905Sborman 					netip++; ncc--;
11038905Sborman 					c = '\n';
11146809Sdab 				} else
11246809Sdab #endif
11346809Sdab 				{
114*57212Sdab #if	defined(ENCRYPTION)
11546809Sdab 					if (decrypt_input)
11646809Sdab 						(void)(*decrypt_input)(-1);
11746809Sdab #endif
11838905Sborman 					state = TS_CR;
11938905Sborman 				}
12038905Sborman 			}
12138905Sborman 			*pfrontp++ = c;
12238905Sborman 			break;
12338905Sborman 
12438905Sborman 		case TS_IAC:
12538905Sborman gotiac:			switch (c) {
12638905Sborman 
12738905Sborman 			/*
12838905Sborman 			 * Send the process on the pty side an
12938905Sborman 			 * interrupt.  Do this with a NULL or
13038905Sborman 			 * interrupt char; depending on the tty mode.
13138905Sborman 			 */
13238905Sborman 			case IP:
13346809Sdab 				DIAG(TD_OPTIONS,
13446809Sdab 					printoption("td: recv IAC", c));
13538905Sborman 				interrupt();
13638905Sborman 				break;
13738905Sborman 
13838905Sborman 			case BREAK:
13946809Sdab 				DIAG(TD_OPTIONS,
14046809Sdab 					printoption("td: recv IAC", c));
14138905Sborman 				sendbrk();
14238905Sborman 				break;
14338905Sborman 
14438905Sborman 			/*
14538905Sborman 			 * Are You There?
14638905Sborman 			 */
14738905Sborman 			case AYT:
14846809Sdab 				DIAG(TD_OPTIONS,
14946809Sdab 					printoption("td: recv IAC", c));
15045234Sborman 				recv_ayt();
15138905Sborman 				break;
15238905Sborman 
15338905Sborman 			/*
15438905Sborman 			 * Abort Output
15538905Sborman 			 */
15638905Sborman 			case AO:
15738905Sborman 			    {
15846809Sdab 				DIAG(TD_OPTIONS,
15946809Sdab 					printoption("td: recv IAC", c));
16038905Sborman 				ptyflush();	/* half-hearted */
16138905Sborman 				init_termbuf();
16238905Sborman 
16338905Sborman 				if (slctab[SLC_AO].sptr &&
16445234Sborman 				    *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
16540242Sborman 				    *pfrontp++ =
16640242Sborman 					(unsigned char)*slctab[SLC_AO].sptr;
16738905Sborman 				}
16838905Sborman 
16938905Sborman 				netclear();	/* clear buffer back */
17038905Sborman 				*nfrontp++ = IAC;
17138905Sborman 				*nfrontp++ = DM;
17238905Sborman 				neturg = nfrontp-1; /* off by one XXX */
17346809Sdab 				DIAG(TD_OPTIONS,
17446809Sdab 					printoption("td: send IAC", DM));
17538905Sborman 				break;
17638905Sborman 			    }
17738905Sborman 
17838905Sborman 			/*
17938905Sborman 			 * Erase Character and
18038905Sborman 			 * Erase Line
18138905Sborman 			 */
18238905Sborman 			case EC:
18338905Sborman 			case EL:
18438905Sborman 			    {
18540242Sborman 				cc_t ch;
18638905Sborman 
18746809Sdab 				DIAG(TD_OPTIONS,
18846809Sdab 					printoption("td: recv IAC", c));
18938905Sborman 				ptyflush();	/* half-hearted */
19038905Sborman 				init_termbuf();
19144364Sborman 				if (c == EC)
19244364Sborman 					ch = *slctab[SLC_EC].sptr;
19344364Sborman 				else
19444364Sborman 					ch = *slctab[SLC_EL].sptr;
19545234Sborman 				if (ch != (cc_t)(_POSIX_VDISABLE))
19640242Sborman 					*pfrontp++ = (unsigned char)ch;
19738905Sborman 				break;
19838905Sborman 			    }
19938905Sborman 
20038905Sborman 			/*
20138905Sborman 			 * Check for urgent data...
20238905Sborman 			 */
20338905Sborman 			case DM:
20446809Sdab 				DIAG(TD_OPTIONS,
20546809Sdab 					printoption("td: recv IAC", c));
20638905Sborman 				SYNCHing = stilloob(net);
20738905Sborman 				settimer(gotDM);
20838905Sborman 				break;
20938905Sborman 
21038905Sborman 
21138905Sborman 			/*
21238905Sborman 			 * Begin option subnegotiation...
21338905Sborman 			 */
21438905Sborman 			case SB:
21538905Sborman 				state = TS_SB;
21638905Sborman 				SB_CLEAR();
21738905Sborman 				continue;
21838905Sborman 
21938905Sborman 			case WILL:
22038905Sborman 				state = TS_WILL;
22138905Sborman 				continue;
22238905Sborman 
22338905Sborman 			case WONT:
22438905Sborman 				state = TS_WONT;
22538905Sborman 				continue;
22638905Sborman 
22738905Sborman 			case DO:
22838905Sborman 				state = TS_DO;
22938905Sborman 				continue;
23038905Sborman 
23138905Sborman 			case DONT:
23238905Sborman 				state = TS_DONT;
23338905Sborman 				continue;
23438905Sborman 			case EOR:
23544364Sborman 				if (his_state_is_will(TELOPT_EOR))
23638905Sborman 					doeof();
23738905Sborman 				break;
23838905Sborman 
23938905Sborman 			/*
24038905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
24138905Sborman 			 * to command stream (EOF, SUSP, ABORT).
24238905Sborman 			 */
24338905Sborman 			case xEOF:
24438905Sborman 				doeof();
24538905Sborman 				break;
24638905Sborman 
24738905Sborman 			case SUSP:
24838905Sborman 				sendsusp();
24938905Sborman 				break;
25038905Sborman 
25138905Sborman 			case ABORT:
25238905Sborman 				sendbrk();
25338905Sborman 				break;
25438905Sborman 
25538905Sborman 			case IAC:
25638905Sborman 				*pfrontp++ = c;
25738905Sborman 				break;
25838905Sborman 			}
25938905Sborman 			state = TS_DATA;
26038905Sborman 			break;
26138905Sborman 
26238905Sborman 		case TS_SB:
26338905Sborman 			if (c == IAC) {
26438905Sborman 				state = TS_SE;
26538905Sborman 			} else {
26638905Sborman 				SB_ACCUM(c);
26738905Sborman 			}
26838905Sborman 			break;
26938905Sborman 
27038905Sborman 		case TS_SE:
27138905Sborman 			if (c != SE) {
27238905Sborman 				if (c != IAC) {
27338905Sborman 					/*
27438905Sborman 					 * bad form of suboption negotiation.
27538905Sborman 					 * handle it in such a way as to avoid
27638905Sborman 					 * damage to local state.  Parse
27738905Sborman 					 * suboption buffer found so far,
27838905Sborman 					 * then treat remaining stream as
27938905Sborman 					 * another command sequence.
28038905Sborman 					 */
28146809Sdab 
28246809Sdab 					/* for DIAGNOSTICS */
28344364Sborman 					SB_ACCUM(IAC);
28444364Sborman 					SB_ACCUM(c);
28544364Sborman 					subpointer -= 2;
28646809Sdab 
28738905Sborman 					SB_TERM();
28838905Sborman 					suboption();
28938905Sborman 					state = TS_IAC;
29038905Sborman 					goto gotiac;
29138905Sborman 				}
29238905Sborman 				SB_ACCUM(c);
29338905Sborman 				state = TS_SB;
29438905Sborman 			} else {
29546809Sdab 				/* for DIAGNOSTICS */
29644364Sborman 				SB_ACCUM(IAC);
29744364Sborman 				SB_ACCUM(SE);
29844364Sborman 				subpointer -= 2;
29946809Sdab 
30038905Sborman 				SB_TERM();
30138905Sborman 				suboption();	/* handle sub-option */
30238905Sborman 				state = TS_DATA;
30338905Sborman 			}
30438905Sborman 			break;
30538905Sborman 
30638905Sborman 		case TS_WILL:
30739503Sborman 			willoption(c);
30838905Sborman 			state = TS_DATA;
30938905Sborman 			continue;
31038905Sborman 
31138905Sborman 		case TS_WONT:
31239503Sborman 			wontoption(c);
31338905Sborman 			state = TS_DATA;
31438905Sborman 			continue;
31538905Sborman 
31638905Sborman 		case TS_DO:
31739503Sborman 			dooption(c);
31838905Sborman 			state = TS_DATA;
31938905Sborman 			continue;
32038905Sborman 
32138905Sborman 		case TS_DONT:
32239503Sborman 			dontoption(c);
32338905Sborman 			state = TS_DATA;
32438905Sborman 			continue;
32538905Sborman 
32638905Sborman 		default:
32738905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
32838905Sborman 			printf("telnetd: panic state=%d\n", state);
32938905Sborman 			exit(1);
33038905Sborman 		}
33138905Sborman 	}
33240242Sborman #if	defined(CRAY2) && defined(UNICOS5)
33338905Sborman 	if (!linemode) {
33438905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
33538905Sborman 		char	xbuf2[BUFSIZ];
33638905Sborman 		register char *cp;
33738905Sborman 		int n = pfrontp - opfrontp, oc;
33838905Sborman 		bcopy(opfrontp, xptyobuf, n);
33938905Sborman 		pfrontp = opfrontp;
34038905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
34138905Sborman 					xbuf2, &oc, BUFSIZ);
34238905Sborman 		for (cp = xbuf2; oc > 0; --oc)
34338905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
34438905Sborman 				*nfrontp++ = IAC;
34538905Sborman 	}
34640242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
34738905Sborman }  /* end of telrcv */
34838905Sborman 
34938905Sborman /*
35038905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
35144364Sborman  * Telnet option processing state machine.
35238905Sborman  *
35338905Sborman  * These correspond to the following states:
35438905Sborman  *	my_state = the last negotiated state
35538905Sborman  *	want_state = what I want the state to go to
35638905Sborman  *	want_resp = how many requests I have sent
35738905Sborman  * All state defaults are negative, and resp defaults to 0.
35838905Sborman  *
35938905Sborman  * When initiating a request to change state to new_state:
36038905Sborman  *
36138905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
36238905Sborman  *	do nothing;
36338905Sborman  * } else {
36438905Sborman  *	want_state = new_state;
36538905Sborman  *	send new_state;
36638905Sborman  *	want_resp++;
36738905Sborman  * }
36838905Sborman  *
36938905Sborman  * When receiving new_state:
37038905Sborman  *
37138905Sborman  * if (want_resp) {
37238905Sborman  *	want_resp--;
37338905Sborman  *	if (want_resp && (new_state == my_state))
37438905Sborman  *		want_resp--;
37538905Sborman  * }
37638905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
37738905Sborman  *	if (ok_to_switch_to new_state)
37838905Sborman  *		want_state = new_state;
37938905Sborman  *	else
38038905Sborman  *		want_resp++;
38138905Sborman  *	send want_state;
38238905Sborman  * }
38338905Sborman  * my_state = new_state;
38438905Sborman  *
38538905Sborman  * Note that new_state is implied in these functions by the function itself.
38638905Sborman  * will and do imply positive new_state, wont and dont imply negative.
38738905Sborman  *
38838905Sborman  * Finally, there is one catch.  If we send a negative response to a
38938905Sborman  * positive request, my_state will be the positive while want_state will
39038905Sborman  * remain negative.  my_state will revert to negative when the negative
39138905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
39238905Sborman  * us not only the last negotiated state, but also tells us what the peer
39338905Sborman  * wants to be doing as well.  It is important to understand this difference
39438905Sborman  * as we may wish to be processing data streams based on our desired state
39538905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
39638905Sborman  *
39738905Sborman  * This all works fine because if the peer sends a positive request, the data
39838905Sborman  * that we receive prior to negative acknowlegment will probably be affected
39938905Sborman  * by the positive state, and we can process it as such (if we can; if we
40038905Sborman  * can't then it really doesn't matter).  If it is that important, then the
40138905Sborman  * peer probably should be buffering until this option state negotiation
40238905Sborman  * is complete.
40338905Sborman  *
40438905Sborman  */
40546809Sdab 	void
40639503Sborman send_do(option, init)
40739503Sborman 	int option, init;
40838905Sborman {
40939503Sborman 	if (init) {
41044364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
41144364Sborman 		    his_want_state_is_will(option))
41239503Sborman 			return;
41339531Sborman 		/*
41439531Sborman 		 * Special case for TELOPT_TM:  We send a DO, but pretend
41539531Sborman 		 * that we sent a DONT, so that we can send more DOs if
41639531Sborman 		 * we want to.
41739531Sborman 		 */
41839531Sborman 		if (option == TELOPT_TM)
41944364Sborman 			set_his_want_state_wont(option);
42039531Sborman 		else
42144364Sborman 			set_his_want_state_will(option);
42239503Sborman 		do_dont_resp[option]++;
42339503Sborman 	}
42439503Sborman 	(void) sprintf(nfrontp, doopt, option);
42539503Sborman 	nfrontp += sizeof (dont) - 2;
42646809Sdab 
42746809Sdab 	DIAG(TD_OPTIONS, printoption("td: send do", option));
42839503Sborman }
42939503Sborman 
430*57212Sdab #ifdef	AUTHENTICATION
43146809Sdab extern void auth_request();
43246809Sdab #endif
43346809Sdab #ifdef	LINEMODE
43446809Sdab extern void doclientstat();
43546809Sdab #endif
436*57212Sdab #ifdef	ENCRYPTION
43746809Sdab extern void encrypt_send_support();
43846809Sdab #endif
43946809Sdab 
44046809Sdab 	void
44139503Sborman willoption(option)
44239503Sborman 	int option;
44339503Sborman {
44438905Sborman 	int changeok = 0;
44546809Sdab 	void (*func)() = 0;
44638905Sborman 
44739503Sborman 	/*
44839503Sborman 	 * process input from peer.
44939503Sborman 	 */
45039503Sborman 
45146809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv will", option));
45244364Sborman 
45339503Sborman 	if (do_dont_resp[option]) {
45439503Sborman 		do_dont_resp[option]--;
45544364Sborman 		if (do_dont_resp[option] && his_state_is_will(option))
45639503Sborman 			do_dont_resp[option]--;
45739503Sborman 	}
45839531Sborman 	if (do_dont_resp[option] == 0) {
45944364Sborman 	    if (his_want_state_is_wont(option)) {
46038905Sborman 		switch (option) {
46138905Sborman 
46238905Sborman 		case TELOPT_BINARY:
46338905Sborman 			init_termbuf();
46438905Sborman 			tty_binaryin(1);
46538905Sborman 			set_termbuf();
46638905Sborman 			changeok++;
46738905Sborman 			break;
46838905Sborman 
46938905Sborman 		case TELOPT_ECHO:
47038905Sborman 			/*
47144364Sborman 			 * See comments below for more info.
47238905Sborman 			 */
47344364Sborman 			not42 = 0;	/* looks like a 4.2 system */
47438905Sborman 			break;
47538905Sborman 
47638905Sborman 		case TELOPT_TM:
47738905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
47838905Sborman 			/*
47939503Sborman 			 * This telnetd implementation does not really
48039503Sborman 			 * support timing marks, it just uses them to
48139503Sborman 			 * support the kludge linemode stuff.  If we
48239503Sborman 			 * receive a will or wont TM in response to our
48339503Sborman 			 * do TM request that may have been sent to
48439503Sborman 			 * determine kludge linemode support, process
48539503Sborman 			 * it, otherwise TM should get a negative
48639503Sborman 			 * response back.
48738905Sborman 			 */
48838905Sborman 			/*
48938905Sborman 			 * Handle the linemode kludge stuff.
49038905Sborman 			 * If we are not currently supporting any
49138905Sborman 			 * linemode at all, then we assume that this
49238905Sborman 			 * is the client telling us to use kludge
49338905Sborman 			 * linemode in response to our query.  Set the
49438905Sborman 			 * linemode type that is to be supported, note
49538905Sborman 			 * that the client wishes to use linemode, and
49638905Sborman 			 * eat the will TM as though it never arrived.
49738905Sborman 			 */
49838905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
49938905Sborman 				lmodetype = KLUDGE_LINEMODE;
50038905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
50139503Sborman 				send_wont(TELOPT_SGA, 1);
502*57212Sdab 			} else if (lmodetype == NO_AUTOKLUDGE) {
503*57212Sdab 				lmodetype = KLUDGE_OK;
50438905Sborman 			}
50538905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
50638905Sborman 			/*
50739531Sborman 			 * We never respond to a WILL TM, and
50844364Sborman 			 * we leave the state WONT.
50938905Sborman 			 */
51038905Sborman 			return;
51138905Sborman 
51238905Sborman 		case TELOPT_LFLOW:
51338905Sborman 			/*
51439503Sborman 			 * If we are going to support flow control
51539503Sborman 			 * option, then don't worry peer that we can't
51639503Sborman 			 * change the flow control characters.
51738905Sborman 			 */
51838905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
51938905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
52038905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
52138905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
52238905Sborman 		case TELOPT_TTYPE:
52338905Sborman 		case TELOPT_SGA:
52438905Sborman 		case TELOPT_NAWS:
52538905Sborman 		case TELOPT_TSPEED:
52644364Sborman 		case TELOPT_XDISPLOC:
52744364Sborman 		case TELOPT_ENVIRON:
52839531Sborman 			changeok++;
52939531Sborman 			break;
53039531Sborman 
53138905Sborman #ifdef	LINEMODE
53238905Sborman 		case TELOPT_LINEMODE:
53339531Sborman # ifdef	KLUDGELINEMODE
53439531Sborman 			/*
53539531Sborman 			 * Note client's desire to use linemode.
53639531Sborman 			 */
53739531Sborman 			lmodetype = REAL_LINEMODE;
53839531Sborman # endif	/* KLUDGELINEMODE */
53946809Sdab 			func = doclientstat;
54046809Sdab 			changeok++;
54146809Sdab 			break;
54239531Sborman #endif	/* LINEMODE */
54338905Sborman 
544*57212Sdab #ifdef	AUTHENTICATION
54546809Sdab 		case TELOPT_AUTHENTICATION:
54646809Sdab 			func = auth_request;
54746809Sdab 			changeok++;
54846809Sdab 			break;
54946809Sdab #endif
55046809Sdab 
551*57212Sdab #ifdef	ENCRYPTION
55246809Sdab 		case TELOPT_ENCRYPT:
55346809Sdab 			func = encrypt_send_support;
55446809Sdab 			changeok++;
55546809Sdab 			break;
55646809Sdab #endif
55746809Sdab 
55838905Sborman 		default:
55938905Sborman 			break;
56038905Sborman 		}
56139503Sborman 		if (changeok) {
56244364Sborman 			set_his_want_state_will(option);
56339503Sborman 			send_do(option, 0);
56439503Sborman 		} else {
56539503Sborman 			do_dont_resp[option]++;
56639503Sborman 			send_dont(option, 0);
56738905Sborman 		}
56844364Sborman 	    } else {
56944364Sborman 		/*
57044364Sborman 		 * Option processing that should happen when
57144364Sborman 		 * we receive conformation of a change in
57244364Sborman 		 * state that we had requested.
57344364Sborman 		 */
57444364Sborman 		switch (option) {
57544364Sborman 		case TELOPT_ECHO:
57644364Sborman 			not42 = 0;	/* looks like a 4.2 system */
57744364Sborman 			/*
57844364Sborman 			 * Egads, he responded "WILL ECHO".  Turn
57944364Sborman 			 * it off right now!
58044364Sborman 			 */
58144364Sborman 			send_dont(option, 1);
58244364Sborman 			/*
58344364Sborman 			 * "WILL ECHO".  Kludge upon kludge!
58444364Sborman 			 * A 4.2 client is now echoing user input at
58544364Sborman 			 * the tty.  This is probably undesireable and
58644364Sborman 			 * it should be stopped.  The client will
58744364Sborman 			 * respond WONT TM to the DO TM that we send to
58844364Sborman 			 * check for kludge linemode.  When the WONT TM
58944364Sborman 			 * arrives, linemode will be turned off and a
59044364Sborman 			 * change propogated to the pty.  This change
59144364Sborman 			 * will cause us to process the new pty state
59244364Sborman 			 * in localstat(), which will notice that
59344364Sborman 			 * linemode is off and send a WILL ECHO
59444364Sborman 			 * so that we are properly in character mode and
59544364Sborman 			 * all is well.
59644364Sborman 			 */
59744364Sborman 			break;
59844364Sborman #ifdef	LINEMODE
59944364Sborman 		case TELOPT_LINEMODE:
60044364Sborman # ifdef	KLUDGELINEMODE
60144364Sborman 			/*
60244364Sborman 			 * Note client's desire to use linemode.
60344364Sborman 			 */
60444364Sborman 			lmodetype = REAL_LINEMODE;
60544364Sborman # endif	/* KLUDGELINEMODE */
60646809Sdab 			func = doclientstat;
60746809Sdab 			break;
60844364Sborman #endif	/* LINEMODE */
60946809Sdab 
610*57212Sdab #ifdef	AUTHENTICATION
61146809Sdab 		case TELOPT_AUTHENTICATION:
61246809Sdab 			func = auth_request;
61346809Sdab 			break;
61446809Sdab #endif
61546809Sdab 
616*57212Sdab #ifdef	ENCRYPTION
61746809Sdab 		case TELOPT_ENCRYPT:
61846809Sdab 			func = encrypt_send_support;
61946809Sdab 			break;
62046809Sdab #endif
62144364Sborman 		}
62239531Sborman 	    }
62338905Sborman 	}
62444364Sborman 	set_his_state_will(option);
62546809Sdab 	if (func)
62646809Sdab 		(*func)();
62738905Sborman }  /* end of willoption */
62838905Sborman 
62946809Sdab 	void
63039503Sborman send_dont(option, init)
63139503Sborman 	int option, init;
63238905Sborman {
63339503Sborman 	if (init) {
63444364Sborman 		if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
63544364Sborman 		    his_want_state_is_wont(option))
63639503Sborman 			return;
63744364Sborman 		set_his_want_state_wont(option);
63839503Sborman 		do_dont_resp[option]++;
63939503Sborman 	}
64039503Sborman 	(void) sprintf(nfrontp, dont, option);
64139503Sborman 	nfrontp += sizeof (doopt) - 2;
64246809Sdab 
64346809Sdab 	DIAG(TD_OPTIONS, printoption("td: send dont", option));
64439503Sborman }
64539503Sborman 
64646809Sdab 	void
64739503Sborman wontoption(option)
64839503Sborman 	int option;
64939503Sborman {
65038905Sborman 	/*
65138905Sborman 	 * Process client input.
65238905Sborman 	 */
65339503Sborman 
65446809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv wont", option));
65544364Sborman 
65639503Sborman 	if (do_dont_resp[option]) {
65739503Sborman 		do_dont_resp[option]--;
65844364Sborman 		if (do_dont_resp[option] && his_state_is_wont(option))
65939503Sborman 			do_dont_resp[option]--;
66039503Sborman 	}
66139531Sborman 	if (do_dont_resp[option] == 0) {
66244364Sborman 	    if (his_want_state_is_will(option)) {
66339503Sborman 		/* it is always ok to change to negative state */
66438905Sborman 		switch (option) {
66538905Sborman 		case TELOPT_ECHO:
66639503Sborman 			not42 = 1; /* doesn't seem to be a 4.2 system */
66738905Sborman 			break;
66838905Sborman 
66938905Sborman 		case TELOPT_BINARY:
67038905Sborman 			init_termbuf();
67138905Sborman 			tty_binaryin(0);
67238905Sborman 			set_termbuf();
67338905Sborman 			break;
67438905Sborman 
67538905Sborman #ifdef	LINEMODE
67638905Sborman 		case TELOPT_LINEMODE:
67738905Sborman # ifdef	KLUDGELINEMODE
67838905Sborman 			/*
67938905Sborman 			 * If real linemode is supported, then client is
68038905Sborman 			 * asking to turn linemode off.
68138905Sborman 			 */
68244364Sborman 			if (lmodetype != REAL_LINEMODE)
68344364Sborman 				break;
68444364Sborman 			lmodetype = KLUDGE_LINEMODE;
68538905Sborman # endif	/* KLUDGELINEMODE */
68644364Sborman 			clientstat(TELOPT_LINEMODE, WONT, 0);
68738905Sborman 			break;
68846809Sdab #endif	/* LINEMODE */
68938905Sborman 
69038905Sborman 		case TELOPT_TM:
69138905Sborman 			/*
69239503Sborman 			 * If we get a WONT TM, and had sent a DO TM,
69339503Sborman 			 * don't respond with a DONT TM, just leave it
69439503Sborman 			 * as is.  Short circut the state machine to
69539531Sborman 			 * achive this.
69638905Sborman 			 */
69744364Sborman 			set_his_want_state_wont(TELOPT_TM);
69838905Sborman 			return;
69938905Sborman 
70038905Sborman 		case TELOPT_LFLOW:
70138905Sborman 			/*
70239503Sborman 			 * If we are not going to support flow control
70339503Sborman 			 * option, then let peer know that we can't
70439503Sborman 			 * change the flow control characters.
70538905Sborman 			 */
70638905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
70738905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
70838905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
70938905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
71038905Sborman 			break;
71138905Sborman 
712*57212Sdab #if	defined(AUTHENTICATION)
71346809Sdab 		case TELOPT_AUTHENTICATION:
71446809Sdab 			auth_finished(0, AUTH_REJECT);
71546809Sdab 			break;
71646809Sdab #endif
71746809Sdab 
71844364Sborman 		/*
71944364Sborman 		 * For options that we might spin waiting for
72044364Sborman 		 * sub-negotiation, if the client turns off the
72144364Sborman 		 * option rather than responding to the request,
72244364Sborman 		 * we have to treat it here as if we got a response
72344364Sborman 		 * to the sub-negotiation, (by updating the timers)
72444364Sborman 		 * so that we'll break out of the loop.
72544364Sborman 		 */
72644364Sborman 		case TELOPT_TTYPE:
72744364Sborman 			settimer(ttypesubopt);
72844364Sborman 			break;
72944364Sborman 
73044364Sborman 		case TELOPT_TSPEED:
73144364Sborman 			settimer(tspeedsubopt);
73244364Sborman 			break;
73344364Sborman 
73444364Sborman 		case TELOPT_XDISPLOC:
73544364Sborman 			settimer(xdisplocsubopt);
73644364Sborman 			break;
73744364Sborman 
73844364Sborman 		case TELOPT_ENVIRON:
73944364Sborman 			settimer(environsubopt);
74044364Sborman 			break;
74144364Sborman 
74238905Sborman 		default:
74338905Sborman 			break;
74438905Sborman 		}
74544364Sborman 		set_his_want_state_wont(option);
74644364Sborman 		if (his_state_is_will(option))
74744364Sborman 			send_dont(option, 0);
74839531Sborman 	    } else {
74939531Sborman 		switch (option) {
75039531Sborman 		case TELOPT_TM:
75139531Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
752*57212Sdab 			if (lmodetype < NO_AUTOKLUDGE) {
75339531Sborman 				lmodetype = NO_LINEMODE;
75439531Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
75539531Sborman 				send_will(TELOPT_SGA, 1);
75645234Sborman 				send_will(TELOPT_ECHO, 1);
75739531Sborman 			}
75839531Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
75946809Sdab 			break;
76046809Sdab 
761*57212Sdab #if	defined(AUTHENTICATION)
76246809Sdab 		case TELOPT_AUTHENTICATION:
76346809Sdab 			auth_finished(0, AUTH_REJECT);
76446809Sdab 			break;
76546809Sdab #endif
76639531Sborman 		default:
76739531Sborman 			break;
76839531Sborman 		}
76939531Sborman 	    }
77038905Sborman 	}
77144364Sborman 	set_his_state_wont(option);
77238905Sborman 
77339503Sborman }  /* end of wontoption */
77438905Sborman 
77546809Sdab 	void
77639503Sborman send_will(option, init)
77739503Sborman 	int option, init;
77839503Sborman {
77939503Sborman 	if (init) {
78044364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
78144364Sborman 		    my_want_state_is_will(option))
78239503Sborman 			return;
78344364Sborman 		set_my_want_state_will(option);
78439503Sborman 		will_wont_resp[option]++;
78538905Sborman 	}
78639503Sborman 	(void) sprintf(nfrontp, will, option);
78739503Sborman 	nfrontp += sizeof (doopt) - 2;
78846809Sdab 
78946809Sdab 	DIAG(TD_OPTIONS, printoption("td: send will", option));
79039503Sborman }
79138905Sborman 
79245234Sborman #if	!defined(LINEMODE) || !defined(KLUDGELINEMODE)
79345234Sborman /*
79445234Sborman  * When we get a DONT SGA, we will try once to turn it
79545234Sborman  * back on.  If the other side responds DONT SGA, we
79645234Sborman  * leave it at that.  This is so that when we talk to
79745234Sborman  * clients that understand KLUDGELINEMODE but not LINEMODE,
79845234Sborman  * we'll keep them in char-at-a-time mode.
79945234Sborman  */
80045234Sborman int turn_on_sga = 0;
80145234Sborman #endif
80245234Sborman 
80346809Sdab 	void
80439503Sborman dooption(option)
80539503Sborman 	int option;
80638905Sborman {
80738905Sborman 	int changeok = 0;
80838905Sborman 
80938905Sborman 	/*
81038905Sborman 	 * Process client input.
81138905Sborman 	 */
81239503Sborman 
81346809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv do", option));
81444364Sborman 
81539503Sborman 	if (will_wont_resp[option]) {
81639503Sborman 		will_wont_resp[option]--;
81744364Sborman 		if (will_wont_resp[option] && my_state_is_will(option))
81839503Sborman 			will_wont_resp[option]--;
81939503Sborman 	}
82044364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
82138905Sborman 		switch (option) {
82238905Sborman 		case TELOPT_ECHO:
82338905Sborman #ifdef	LINEMODE
82445234Sborman # ifdef	KLUDGELINEMODE
82545234Sborman 			if (lmodetype == NO_LINEMODE)
82645234Sborman # else
82745234Sborman 			if (his_state_is_wont(TELOPT_LINEMODE))
82845234Sborman # endif
82938905Sborman #endif
83045234Sborman 			{
83138905Sborman 				init_termbuf();
83238905Sborman 				tty_setecho(1);
83338905Sborman 				set_termbuf();
83438905Sborman 			}
83538905Sborman 			changeok++;
83638905Sborman 			break;
83738905Sborman 
83838905Sborman 		case TELOPT_BINARY:
83938905Sborman 			init_termbuf();
84038905Sborman 			tty_binaryout(1);
84138905Sborman 			set_termbuf();
84238905Sborman 			changeok++;
84338905Sborman 			break;
84438905Sborman 
84538905Sborman 		case TELOPT_SGA:
84638905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
84738905Sborman 			/*
84839503Sborman 			 * If kludge linemode is in use, then we must
84939503Sborman 			 * process an incoming do SGA for linemode
85039503Sborman 			 * purposes.
85138905Sborman 			 */
85238905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
85338905Sborman 				/*
85439503Sborman 				 * Receipt of "do SGA" in kludge
85539503Sborman 				 * linemode is the peer asking us to
85639503Sborman 				 * turn off linemode.  Make note of
85739503Sborman 				 * the request.
85838905Sborman 				 */
85938905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
86038905Sborman 				/*
86139503Sborman 				 * If linemode did not get turned off
86239503Sborman 				 * then don't tell peer that we did.
86339503Sborman 				 * Breaking here forces a wont SGA to
86439503Sborman 				 * be returned.
86538905Sborman 				 */
86638905Sborman 				if (linemode)
86738905Sborman 					break;
86838905Sborman 			}
86945234Sborman #else
87045234Sborman 			turn_on_sga = 0;
87138905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
87238905Sborman 			changeok++;
87338905Sborman 			break;
87438905Sborman 
87538905Sborman 		case TELOPT_STATUS:
87638905Sborman 			changeok++;
87738905Sborman 			break;
87838905Sborman 
87938905Sborman 		case TELOPT_TM:
88039503Sborman 			/*
88139503Sborman 			 * Special case for TM.  We send a WILL, but
88239503Sborman 			 * pretend we sent a WONT.
88339503Sborman 			 */
88439503Sborman 			send_will(option, 0);
88544364Sborman 			set_my_want_state_wont(option);
88644364Sborman 			set_my_state_wont(option);
88739503Sborman 			return;
88839503Sborman 
88946809Sdab 		case TELOPT_LOGOUT:
89046809Sdab 			/*
89146809Sdab 			 * When we get a LOGOUT option, respond
89246809Sdab 			 * with a WILL LOGOUT, make sure that
89346809Sdab 			 * it gets written out to the network,
89446809Sdab 			 * and then just go away...
89546809Sdab 			 */
89646809Sdab 			set_my_want_state_will(TELOPT_LOGOUT);
89746809Sdab 			send_will(TELOPT_LOGOUT, 0);
89846809Sdab 			set_my_state_will(TELOPT_LOGOUT);
89946809Sdab 			(void)netflush();
90046809Sdab 			cleanup(0);
90146809Sdab 			/* NOT REACHED */
90246809Sdab 			break;
90346809Sdab 
904*57212Sdab #if	defined(ENCRYPTION)
90546809Sdab 		case TELOPT_ENCRYPT:
90646809Sdab 			changeok++;
90746809Sdab 			break;
90846809Sdab #endif
90938905Sborman 		case TELOPT_LINEMODE:
91038905Sborman 		case TELOPT_TTYPE:
91138905Sborman 		case TELOPT_NAWS:
91238905Sborman 		case TELOPT_TSPEED:
91338905Sborman 		case TELOPT_LFLOW:
91444364Sborman 		case TELOPT_XDISPLOC:
91544364Sborman 		case TELOPT_ENVIRON:
91638905Sborman 		default:
91738905Sborman 			break;
91838905Sborman 		}
91939503Sborman 		if (changeok) {
92044364Sborman 			set_my_want_state_will(option);
92139503Sborman 			send_will(option, 0);
92239503Sborman 		} else {
92339503Sborman 			will_wont_resp[option]++;
92439503Sborman 			send_wont(option, 0);
92538905Sborman 		}
92638905Sborman 	}
92744364Sborman 	set_my_state_will(option);
92838905Sborman 
92938905Sborman }  /* end of dooption */
93038905Sborman 
93146809Sdab 	void
93239503Sborman send_wont(option, init)
93339503Sborman 	int option, init;
93439503Sborman {
93539503Sborman 	if (init) {
93644364Sborman 		if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
93744364Sborman 		    my_want_state_is_wont(option))
93839503Sborman 			return;
93944364Sborman 		set_my_want_state_wont(option);
94039503Sborman 		will_wont_resp[option]++;
94139503Sborman 	}
94239503Sborman 	(void) sprintf(nfrontp, wont, option);
94339503Sborman 	nfrontp += sizeof (wont) - 2;
94446809Sdab 
94546809Sdab 	DIAG(TD_OPTIONS, printoption("td: send wont", option));
94639503Sborman }
94738905Sborman 
94846809Sdab 	void
94939503Sborman dontoption(option)
95039503Sborman 	int option;
95138905Sborman {
95238905Sborman 	/*
95338905Sborman 	 * Process client input.
95438905Sborman 	 */
95540242Sborman 
95646809Sdab 
95746809Sdab 	DIAG(TD_OPTIONS, printoption("td: recv dont", option));
95846809Sdab 
95939503Sborman 	if (will_wont_resp[option]) {
96039503Sborman 		will_wont_resp[option]--;
96144364Sborman 		if (will_wont_resp[option] && my_state_is_wont(option))
96239503Sborman 			will_wont_resp[option]--;
96339503Sborman 	}
96444364Sborman 	if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
96538905Sborman 		switch (option) {
96638905Sborman 		case TELOPT_BINARY:
96738905Sborman 			init_termbuf();
96838905Sborman 			tty_binaryout(0);
96938905Sborman 			set_termbuf();
97038905Sborman 			break;
97138905Sborman 
97239503Sborman 		case TELOPT_ECHO:	/* we should stop echoing */
97338905Sborman #ifdef	LINEMODE
97445234Sborman # ifdef	KLUDGELINEMODE
975*57212Sdab 			if ((lmodetype != REAL_LINEMODE) &&
976*57212Sdab 			    (lmodetype != KLUDGE_LINEMODE))
97745234Sborman # else
97845234Sborman 			if (his_state_is_wont(TELOPT_LINEMODE))
97945234Sborman # endif
98038905Sborman #endif
98145234Sborman 			{
98238905Sborman 				init_termbuf();
98338905Sborman 				tty_setecho(0);
98438905Sborman 				set_termbuf();
98538905Sborman 			}
98638905Sborman 			break;
98738905Sborman 
98838905Sborman 		case TELOPT_SGA:
98938905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
99038905Sborman 			/*
99139503Sborman 			 * If kludge linemode is in use, then we
99239503Sborman 			 * must process an incoming do SGA for
99339503Sborman 			 * linemode purposes.
99438905Sborman 			 */
995*57212Sdab 			if ((lmodetype == KLUDGE_LINEMODE) ||
996*57212Sdab 			    (lmodetype == KLUDGE_OK)) {
99738905Sborman 				/*
99839503Sborman 				 * The client is asking us to turn
99939503Sborman 				 * linemode on.
100038905Sborman 				 */
1001*57212Sdab 				lmodetype = KLUDGE_LINEMODE;
100238905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
100338905Sborman 				/*
100439503Sborman 				 * If we did not turn line mode on,
100539503Sborman 				 * then what do we say?  Will SGA?
100639503Sborman 				 * This violates design of telnet.
100739503Sborman 				 * Gross.  Very Gross.
100838905Sborman 				 */
100938905Sborman 			}
101045234Sborman 			break;
101145234Sborman #else
101245234Sborman 			set_my_want_state_wont(option);
101345234Sborman 			if (my_state_is_will(option))
101445234Sborman 				send_wont(option, 0);
101545234Sborman 			set_my_state_wont(option);
101645234Sborman 			if (turn_on_sga ^= 1)
1017*57212Sdab 				send_will(option, 1);
101845234Sborman 			return;
101938905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
102038905Sborman 
102138905Sborman 		default:
102238905Sborman 			break;
102338905Sborman 		}
102438905Sborman 
102544364Sborman 		set_my_want_state_wont(option);
102644364Sborman 		if (my_state_is_will(option))
102744364Sborman 			send_wont(option, 0);
102838905Sborman 	}
102944364Sborman 	set_my_state_wont(option);
103038905Sborman 
103138905Sborman }  /* end of dontoption */
103238905Sborman 
103338905Sborman /*
103438905Sborman  * suboption()
103538905Sborman  *
103638905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
103738905Sborman  * side.
103838905Sborman  *
103938905Sborman  *	Currently we recognize:
104038905Sborman  *
104138905Sborman  *	Terminal type is
104238905Sborman  *	Linemode
104338905Sborman  *	Window size
104438905Sborman  *	Terminal speed
104538905Sborman  */
104646809Sdab 	void
104738905Sborman suboption()
104838905Sborman {
104938905Sborman     register int subchar;
105038905Sborman 
105146809Sdab     DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
105246809Sdab 
105338905Sborman     subchar = SB_GET();
105438905Sborman     switch (subchar) {
105538905Sborman     case TELOPT_TSPEED: {
105638905Sborman 	register int xspeed, rspeed;
105738905Sborman 
105844364Sborman 	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
105938905Sborman 		break;
106038905Sborman 
106138905Sborman 	settimer(tspeedsubopt);
106238905Sborman 
106338905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
106438905Sborman 		return;
106538905Sborman 
106646809Sdab 	xspeed = atoi((char *)subpointer);
106738905Sborman 
106838905Sborman 	while (SB_GET() != ',' && !SB_EOF());
106938905Sborman 	if (SB_EOF())
107038905Sborman 		return;
107138905Sborman 
107246809Sdab 	rspeed = atoi((char *)subpointer);
107338905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
107438905Sborman 
107538905Sborman 	break;
107638905Sborman 
107738905Sborman     }  /* end of case TELOPT_TSPEED */
107838905Sborman 
107938905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
108044364Sborman 	static char terminalname[41];
108138905Sborman 
108244364Sborman 	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
108338905Sborman 		break;
108438905Sborman 	settimer(ttypesubopt);
108538905Sborman 
108646809Sdab 	if (SB_EOF() || SB_GET() != TELQUAL_IS) {
108738905Sborman 	    return;		/* ??? XXX but, this is the most robust */
108838905Sborman 	}
108938905Sborman 
109044364Sborman 	terminaltype = terminalname;
109138905Sborman 
109238905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
109338905Sborman 								    !SB_EOF()) {
109438905Sborman 	    register int c;
109538905Sborman 
109638905Sborman 	    c = SB_GET();
109738905Sborman 	    if (isupper(c)) {
109838905Sborman 		c = tolower(c);
109938905Sborman 	    }
110038905Sborman 	    *terminaltype++ = c;    /* accumulate name */
110138905Sborman 	}
110238905Sborman 	*terminaltype = 0;
110338905Sborman 	terminaltype = terminalname;
110438905Sborman 	break;
110538905Sborman     }  /* end of case TELOPT_TTYPE */
110638905Sborman 
110738905Sborman     case TELOPT_NAWS: {
110838905Sborman 	register int xwinsize, ywinsize;
110938905Sborman 
111044364Sborman 	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
111138905Sborman 		break;
111238905Sborman 
111338905Sborman 	if (SB_EOF())
111438905Sborman 		return;
111538905Sborman 	xwinsize = SB_GET() << 8;
111638905Sborman 	if (SB_EOF())
111738905Sborman 		return;
111838905Sborman 	xwinsize |= SB_GET();
111938905Sborman 	if (SB_EOF())
112038905Sborman 		return;
112138905Sborman 	ywinsize = SB_GET() << 8;
112238905Sborman 	if (SB_EOF())
112338905Sborman 		return;
112438905Sborman 	ywinsize |= SB_GET();
112538905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
112638905Sborman 
112738905Sborman 	break;
112838905Sborman 
112938905Sborman     }  /* end of case TELOPT_NAWS */
113038905Sborman 
113138905Sborman #ifdef	LINEMODE
113238905Sborman     case TELOPT_LINEMODE: {
113338905Sborman 	register int request;
113438905Sborman 
113544364Sborman 	if (his_state_is_wont(TELOPT_LINEMODE))	/* Ignore if option disabled */
113638905Sborman 		break;
113738905Sborman 	/*
113838905Sborman 	 * Process linemode suboptions.
113938905Sborman 	 */
114046809Sdab 	if (SB_EOF())
114146809Sdab 	    break;		/* garbage was sent */
114246809Sdab 	request = SB_GET();	/* get will/wont */
114338905Sborman 
114446809Sdab 	if (SB_EOF())
114546809Sdab 	    break;		/* another garbage check */
114646809Sdab 
114738905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
114838905Sborman 		/*
114938905Sborman 		 * Process suboption buffer of slc's
115038905Sborman 		 */
115138905Sborman 		start_slc(1);
115238905Sborman 		do_opt_slc(subpointer, subend - subpointer);
115346809Sdab 		(void) end_slc(0);
115446809Sdab 		break;
115538905Sborman 	} else if (request == LM_MODE) {
115646809Sdab 		if (SB_EOF())
115746809Sdab 		    return;
115838905Sborman 		useeditmode = SB_GET();  /* get mode flag */
115938905Sborman 		clientstat(LM_MODE, 0, 0);
116046809Sdab 		break;
116138905Sborman 	}
116238905Sborman 
116346809Sdab 	if (SB_EOF())
116446809Sdab 	    break;
116538905Sborman 	switch (SB_GET()) {  /* what suboption? */
116638905Sborman 	case LM_FORWARDMASK:
116738905Sborman 		/*
116838905Sborman 		 * According to spec, only server can send request for
116938905Sborman 		 * forwardmask, and client can only return a positive response.
117038905Sborman 		 * So don't worry about it.
117138905Sborman 		 */
117238905Sborman 
117338905Sborman 	default:
117438905Sborman 		break;
117538905Sborman 	}
117640242Sborman 	break;
117738905Sborman     }  /* end of case TELOPT_LINEMODE */
117838905Sborman #endif
117938905Sborman     case TELOPT_STATUS: {
118038905Sborman 	int mode;
118138905Sborman 
118246809Sdab 	if (SB_EOF())
118346809Sdab 	    break;
118438905Sborman 	mode = SB_GET();
118538905Sborman 	switch (mode) {
118638905Sborman 	case TELQUAL_SEND:
118744364Sborman 	    if (my_state_is_will(TELOPT_STATUS))
118838905Sborman 		send_status();
118938905Sborman 	    break;
119038905Sborman 
119138905Sborman 	case TELQUAL_IS:
119238905Sborman 	    break;
119338905Sborman 
119438905Sborman 	default:
119538905Sborman 	    break;
119638905Sborman 	}
119740242Sborman 	break;
119840242Sborman     }  /* end of case TELOPT_STATUS */
119938905Sborman 
120044364Sborman     case TELOPT_XDISPLOC: {
120144364Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
120244364Sborman 		return;
120344364Sborman 	settimer(xdisplocsubopt);
120444364Sborman 	subpointer[SB_LEN()] = '\0';
120546809Sdab 	(void)setenv("DISPLAY", (char *)subpointer, 1);
120644364Sborman 	break;
120744364Sborman     }  /* end of case TELOPT_XDISPLOC */
120844364Sborman 
120944364Sborman     case TELOPT_ENVIRON: {
121044364Sborman 	register int c;
121144364Sborman 	register char *cp, *varp, *valp;
121244364Sborman 
121344364Sborman 	if (SB_EOF())
121444364Sborman 		return;
121544364Sborman 	c = SB_GET();
121644364Sborman 	if (c == TELQUAL_IS)
121744364Sborman 		settimer(environsubopt);
121844364Sborman 	else if (c != TELQUAL_INFO)
121944364Sborman 		return;
122044364Sborman 
1221*57212Sdab 	while (!SB_EOF()) {
1222*57212Sdab 		c = SB_GET();
1223*57212Sdab 		if ((c == ENV_VAR) || (c == ENV_USERVAR))
1224*57212Sdab 			break;
1225*57212Sdab 	}
122644364Sborman 
122744364Sborman 	if (SB_EOF())
122844364Sborman 		return;
122944364Sborman 
123046809Sdab 	cp = varp = (char *)subpointer;
123144364Sborman 	valp = 0;
123244364Sborman 
123344364Sborman 	while (!SB_EOF()) {
123444364Sborman 		switch (c = SB_GET()) {
123544364Sborman 		case ENV_VALUE:
123644364Sborman 			*cp = '\0';
123746809Sdab 			cp = valp = (char *)subpointer;
123844364Sborman 			break;
123944364Sborman 
124044364Sborman 		case ENV_VAR:
1241*57212Sdab 		case ENV_USERVAR:
124244364Sborman 			*cp = '\0';
124344364Sborman 			if (valp)
124446809Sdab 				(void)setenv(varp, valp, 1);
124544364Sborman 			else
124644364Sborman 				unsetenv(varp);
124746809Sdab 			cp = varp = (char *)subpointer;
124844364Sborman 			valp = 0;
124944364Sborman 			break;
125044364Sborman 
125144364Sborman 		case ENV_ESC:
125244364Sborman 			if (SB_EOF())
125344364Sborman 				break;
125444364Sborman 			c = SB_GET();
125544364Sborman 			/* FALL THROUGH */
125644364Sborman 		default:
125744364Sborman 			*cp++ = c;
125844364Sborman 			break;
125944364Sborman 		}
126044364Sborman 	}
126144364Sborman 	*cp = '\0';
126244364Sborman 	if (valp)
126346809Sdab 		(void)setenv(varp, valp, 1);
126444364Sborman 	else
126544364Sborman 		unsetenv(varp);
126644364Sborman 	break;
126744364Sborman     }  /* end of case TELOPT_ENVIRON */
1268*57212Sdab #if	defined(AUTHENTICATION)
126946809Sdab     case TELOPT_AUTHENTICATION:
127046809Sdab 	if (SB_EOF())
127146809Sdab 		break;
127246809Sdab 	switch(SB_GET()) {
127346809Sdab 	case TELQUAL_SEND:
127446809Sdab 	case TELQUAL_REPLY:
127546809Sdab 		/*
127646809Sdab 		 * These are sent by us and cannot be sent by
127746809Sdab 		 * the client.
127846809Sdab 		 */
127946809Sdab 		break;
128046809Sdab 	case TELQUAL_IS:
128146809Sdab 		auth_is(subpointer, SB_LEN());
128246809Sdab 		break;
128347611Sdab 	case TELQUAL_NAME:
128447611Sdab 		auth_name(subpointer, SB_LEN());
128547611Sdab 		break;
128646809Sdab 	}
128746809Sdab 	break;
128846809Sdab #endif
1289*57212Sdab #if	defined(ENCRYPTION)
129046809Sdab     case TELOPT_ENCRYPT:
129146809Sdab 	if (SB_EOF())
129246809Sdab 		break;
129346809Sdab 	switch(SB_GET()) {
129446809Sdab 	case ENCRYPT_SUPPORT:
129546809Sdab 		encrypt_support(subpointer, SB_LEN());
129646809Sdab 		break;
129746809Sdab 	case ENCRYPT_IS:
129846809Sdab 		encrypt_is(subpointer, SB_LEN());
129946809Sdab 		break;
130046809Sdab 	case ENCRYPT_REPLY:
130146809Sdab 		encrypt_reply(subpointer, SB_LEN());
130246809Sdab 		break;
130346809Sdab 	case ENCRYPT_START:
130447611Sdab 		encrypt_start(subpointer, SB_LEN());
130546809Sdab 		break;
130646809Sdab 	case ENCRYPT_END:
130746809Sdab 		encrypt_end();
130846809Sdab 		break;
130946809Sdab 	case ENCRYPT_REQSTART:
131047611Sdab 		encrypt_request_start(subpointer, SB_LEN());
131146809Sdab 		break;
131246809Sdab 	case ENCRYPT_REQEND:
131346809Sdab 		/*
131446809Sdab 		 * We can always send an REQEND so that we cannot
131546809Sdab 		 * get stuck encrypting.  We should only get this
131646809Sdab 		 * if we have been able to get in the correct mode
131746809Sdab 		 * anyhow.
131846809Sdab 		 */
131946809Sdab 		encrypt_request_end();
132046809Sdab 		break;
132147611Sdab 	case ENCRYPT_ENC_KEYID:
132247611Sdab 		encrypt_enc_keyid(subpointer, SB_LEN());
132347611Sdab 		break;
132447611Sdab 	case ENCRYPT_DEC_KEYID:
132547611Sdab 		encrypt_dec_keyid(subpointer, SB_LEN());
132647611Sdab 		break;
132746809Sdab 	default:
132846809Sdab 		break;
132946809Sdab 	}
133046809Sdab 	break;
133146809Sdab #endif
133244364Sborman 
133338905Sborman     default:
133438905Sborman 	break;
133538905Sborman     }  /* end of switch */
133638905Sborman 
133738905Sborman }  /* end of suboption */
133838905Sborman 
133946809Sdab 	void
134046809Sdab doclientstat()
134146809Sdab {
134246809Sdab 	clientstat(TELOPT_LINEMODE, WILL, 0);
134346809Sdab }
134446809Sdab 
134538905Sborman #define	ADD(c)	 *ncp++ = c;
134638905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
134746809Sdab 	void
134838905Sborman send_status()
134938905Sborman {
135044364Sborman 	unsigned char statusbuf[256];
135144364Sborman 	register unsigned char *ncp;
135244364Sborman 	register unsigned char i;
135338905Sborman 
135438905Sborman 	ncp = statusbuf;
135538905Sborman 
135638905Sborman 	netflush();	/* get rid of anything waiting to go out */
135738905Sborman 
135838905Sborman 	ADD(IAC);
135938905Sborman 	ADD(SB);
136038905Sborman 	ADD(TELOPT_STATUS);
136138905Sborman 	ADD(TELQUAL_IS);
136238905Sborman 
136346809Sdab 	/*
136446809Sdab 	 * We check the want_state rather than the current state,
136546809Sdab 	 * because if we received a DO/WILL for an option that we
136646809Sdab 	 * don't support, and the other side didn't send a DONT/WONT
136746809Sdab 	 * in response to our WONT/DONT, then the "state" will be
136846809Sdab 	 * WILL/DO, and the "want_state" will be WONT/DONT.  We
136946809Sdab 	 * need to go by the latter.
137046809Sdab 	 */
137138905Sborman 	for (i = 0; i < NTELOPTS; i++) {
137246809Sdab 		if (my_want_state_is_will(i)) {
137338905Sborman 			ADD(WILL);
137438905Sborman 			ADD_DATA(i);
137538905Sborman 			if (i == IAC)
137638905Sborman 				ADD(IAC);
137738905Sborman 		}
137846809Sdab 		if (his_want_state_is_will(i)) {
137938905Sborman 			ADD(DO);
138038905Sborman 			ADD_DATA(i);
138138905Sborman 			if (i == IAC)
138238905Sborman 				ADD(IAC);
138338905Sborman 		}
138438905Sborman 	}
138538905Sborman 
138646809Sdab 	if (his_want_state_is_will(TELOPT_LFLOW)) {
138746809Sdab 		ADD(SB);
138846809Sdab 		ADD(TELOPT_LFLOW);
1389*57212Sdab 		if (flowmode) {
1390*57212Sdab 			ADD(LFLOW_ON);
1391*57212Sdab 		} else {
1392*57212Sdab 			ADD(LFLOW_OFF);
1393*57212Sdab 		}
139446809Sdab 		ADD(SE);
1395*57212Sdab 
1396*57212Sdab 		if (restartany >= 0) {
1397*57212Sdab 			ADD(SB)
1398*57212Sdab 			ADD(TELOPT_LFLOW);
1399*57212Sdab 			if (restartany) {
1400*57212Sdab 				ADD(LFLOW_RESTART_ANY);
1401*57212Sdab 			} else {
1402*57212Sdab 				ADD(LFLOW_RESTART_XON);
1403*57212Sdab 			}
1404*57212Sdab 			ADD(SE)
1405*57212Sdab 			ADD(SB);
1406*57212Sdab 		}
140746809Sdab 	}
140846809Sdab 
140938905Sborman #ifdef	LINEMODE
141046809Sdab 	if (his_want_state_is_will(TELOPT_LINEMODE)) {
141144364Sborman 		unsigned char *cp, *cpe;
141238905Sborman 		int len;
141338905Sborman 
141438905Sborman 		ADD(SB);
141538905Sborman 		ADD(TELOPT_LINEMODE);
141638905Sborman 		ADD(LM_MODE);
141738905Sborman 		ADD_DATA(editmode);
141838905Sborman 		if (editmode == IAC)
141938905Sborman 			ADD(IAC);
142038905Sborman 		ADD(SE);
142138905Sborman 
142238905Sborman 		ADD(SB);
142338905Sborman 		ADD(TELOPT_LINEMODE);
142438905Sborman 		ADD(LM_SLC);
142538905Sborman 		start_slc(0);
142638905Sborman 		send_slc();
142738905Sborman 		len = end_slc(&cp);
142838905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
142938905Sborman 			ADD_DATA(*cp);
143038905Sborman 		ADD(SE);
143138905Sborman 	}
143238905Sborman #endif	/* LINEMODE */
143338905Sborman 
143438905Sborman 	ADD(IAC);
143538905Sborman 	ADD(SE);
143638905Sborman 
143738905Sborman 	writenet(statusbuf, ncp - statusbuf);
143838905Sborman 	netflush();	/* Send it on its way */
143946809Sdab 
144046809Sdab 	DIAG(TD_OPTIONS,
144146809Sdab 		{printsub('>', statusbuf, ncp - statusbuf); netflush();});
144238905Sborman }
1443