xref: /csrg-svn/libexec/telnetd/state.c (revision 42673)
138905Sborman /*
238905Sborman  * Copyright (c) 1989 Regents of the University of California.
338905Sborman  * All rights reserved.
438905Sborman  *
5*42673Sbostic  * %sccs.include.redist.c%
638905Sborman  */
738905Sborman 
838905Sborman #ifndef lint
9*42673Sbostic static char sccsid[] = "@(#)state.c	5.6 (Berkeley) 06/01/90";
1038905Sborman #endif /* not lint */
1138905Sborman 
1238905Sborman #include "telnetd.h"
1338905Sborman 
1438905Sborman char	doopt[] = { IAC, DO, '%', 'c', 0 };
1538905Sborman char	dont[] = { IAC, DONT, '%', 'c', 0 };
1638905Sborman char	will[] = { IAC, WILL, '%', 'c', 0 };
1738905Sborman char	wont[] = { IAC, WONT, '%', 'c', 0 };
1838905Sborman int	not42 = 1;
1938905Sborman 
2038905Sborman /*
2138905Sborman  * Buffer for sub-options, and macros
2238905Sborman  * for suboptions buffer manipulations
2338905Sborman  */
2438905Sborman char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
2538905Sborman 
2638905Sborman #define	SB_CLEAR()	subpointer = subbuffer;
2738905Sborman #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
2838905Sborman #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
2938905Sborman 				*subpointer++ = (c); \
3038905Sborman 			}
3138905Sborman #define	SB_GET()	((*subpointer++)&0xff)
3238905Sborman #define	SB_EOF()	(subpointer >= subend)
3338905Sborman 
3438905Sborman 
3538905Sborman 
3638905Sborman /*
3738905Sborman  * State for recv fsm
3838905Sborman  */
3938905Sborman #define	TS_DATA		0	/* base state */
4038905Sborman #define	TS_IAC		1	/* look for double IAC's */
4138905Sborman #define	TS_CR		2	/* CR-LF ->'s CR */
4238905Sborman #define	TS_SB		3	/* throw away begin's... */
4338905Sborman #define	TS_SE		4	/* ...end's (suboption negotiation) */
4438905Sborman #define	TS_WILL		5	/* will option negotiation */
4538905Sborman #define	TS_WONT		6	/* wont " */
4638905Sborman #define	TS_DO		7	/* do " */
4738905Sborman #define	TS_DONT		8	/* dont " */
4838905Sborman 
4938905Sborman telrcv()
5038905Sborman {
5138905Sborman 	register int c;
5238905Sborman 	static int state = TS_DATA;
5340242Sborman #if	defined(CRAY2) && defined(UNICOS5)
5438905Sborman 	char *opfrontp = pfrontp;
5538905Sborman #endif
5638905Sborman 
5738905Sborman 	while (ncc > 0) {
5838905Sborman 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
5938905Sborman 			break;
6038905Sborman 		c = *netip++ & 0377, ncc--;
6138905Sborman 		switch (state) {
6238905Sborman 
6338905Sborman 		case TS_CR:
6438905Sborman 			state = TS_DATA;
6538905Sborman 			/* Strip off \n or \0 after a \r */
6638905Sborman 			if ((c == 0) || (c == '\n')) {
6738905Sborman 				break;
6838905Sborman 			}
6938905Sborman 			/* FALL THROUGH */
7038905Sborman 
7138905Sborman 		case TS_DATA:
7238905Sborman 			if (c == IAC) {
7338905Sborman 				state = TS_IAC;
7438905Sborman 				break;
7538905Sborman 			}
7638905Sborman 			/*
7738905Sborman 			 * We now map \r\n ==> \r for pragmatic reasons.
7838905Sborman 			 * Many client implementations send \r\n when
7938905Sborman 			 * the user hits the CarriageReturn key.
8038905Sborman 			 *
8138905Sborman 			 * We USED to map \r\n ==> \n, since \r\n says
8238905Sborman 			 * that we want to be in column 1 of the next
8338905Sborman 			 * printable line, and \n is the standard
8438905Sborman 			 * unix way of saying that (\r is only good
8538905Sborman 			 * if CRMOD is set, which it normally is).
8638905Sborman 			 */
8738905Sborman 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
8838905Sborman 				/*
8938905Sborman 				 * If we are operating in linemode,
9038905Sborman 				 * convert to local end-of-line.
9138905Sborman 				 */
9238905Sborman 				if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
9338905Sborman 					netip++; ncc--;
9438905Sborman 					c = '\n';
9538905Sborman 				} else {
9638905Sborman 					state = TS_CR;
9738905Sborman 				}
9838905Sborman 			}
9938905Sborman 			*pfrontp++ = c;
10038905Sborman 			break;
10138905Sborman 
10238905Sborman 		case TS_IAC:
10338905Sborman gotiac:			switch (c) {
10438905Sborman 
10538905Sborman 			/*
10638905Sborman 			 * Send the process on the pty side an
10738905Sborman 			 * interrupt.  Do this with a NULL or
10838905Sborman 			 * interrupt char; depending on the tty mode.
10938905Sborman 			 */
11038905Sborman 			case IP:
11138905Sborman 				interrupt();
11238905Sborman 				break;
11338905Sborman 
11438905Sborman 			case BREAK:
11538905Sborman 				sendbrk();
11638905Sborman 				break;
11738905Sborman 
11838905Sborman 			/*
11938905Sborman 			 * Are You There?
12038905Sborman 			 */
12138905Sborman 			case AYT:
12238905Sborman 				(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
12338905Sborman 				nfrontp += 9;
12438905Sborman 				break;
12538905Sborman 
12638905Sborman 			/*
12738905Sborman 			 * Abort Output
12838905Sborman 			 */
12938905Sborman 			case AO:
13038905Sborman 			    {
13138905Sborman 				ptyflush();	/* half-hearted */
13238905Sborman 				init_termbuf();
13338905Sborman 
13438905Sborman 				if (slctab[SLC_AO].sptr &&
13540242Sborman 				    *slctab[SLC_AO].sptr != (cc_t)-1) {
13640242Sborman 				    *pfrontp++ =
13740242Sborman 					(unsigned char)*slctab[SLC_AO].sptr;
13838905Sborman 				}
13938905Sborman 
14038905Sborman 				netclear();	/* clear buffer back */
14138905Sborman 				*nfrontp++ = IAC;
14238905Sborman 				*nfrontp++ = DM;
14338905Sborman 				neturg = nfrontp-1; /* off by one XXX */
14438905Sborman 				break;
14538905Sborman 			    }
14638905Sborman 
14738905Sborman 			/*
14838905Sborman 			 * Erase Character and
14938905Sborman 			 * Erase Line
15038905Sborman 			 */
15138905Sborman 			case EC:
15238905Sborman 			case EL:
15338905Sborman 			    {
15440242Sborman 				cc_t ch;
15538905Sborman 
15638905Sborman 				ptyflush();	/* half-hearted */
15738905Sborman 				init_termbuf();
15838905Sborman 				ch = (c == EC) ? *slctab[SLC_EC].sptr :
15938905Sborman 						 *slctab[SLC_EL].sptr;
16040242Sborman 				if (ch != (cc_t)-1)
16140242Sborman 					*pfrontp++ = (unsigned char)ch;
16238905Sborman 				break;
16338905Sborman 			    }
16438905Sborman 
16538905Sborman 			/*
16638905Sborman 			 * Check for urgent data...
16738905Sborman 			 */
16838905Sborman 			case DM:
16938905Sborman 				SYNCHing = stilloob(net);
17038905Sborman 				settimer(gotDM);
17138905Sborman 				break;
17238905Sborman 
17338905Sborman 
17438905Sborman 			/*
17538905Sborman 			 * Begin option subnegotiation...
17638905Sborman 			 */
17738905Sborman 			case SB:
17838905Sborman 				state = TS_SB;
17938905Sborman 				SB_CLEAR();
18038905Sborman 				continue;
18138905Sborman 
18238905Sborman 			case WILL:
18338905Sborman 				state = TS_WILL;
18438905Sborman 				continue;
18538905Sborman 
18638905Sborman 			case WONT:
18738905Sborman 				state = TS_WONT;
18838905Sborman 				continue;
18938905Sborman 
19038905Sborman 			case DO:
19138905Sborman 				state = TS_DO;
19238905Sborman 				continue;
19338905Sborman 
19438905Sborman 			case DONT:
19538905Sborman 				state = TS_DONT;
19638905Sborman 				continue;
19738905Sborman 			case EOR:
19838905Sborman 				if (hisopts[TELOPT_EOR])
19938905Sborman 					doeof();
20038905Sborman 				break;
20138905Sborman 
20238905Sborman 			/*
20338905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
20438905Sborman 			 * to command stream (EOF, SUSP, ABORT).
20538905Sborman 			 */
20638905Sborman 			case xEOF:
20738905Sborman 				doeof();
20838905Sborman 				break;
20938905Sborman 
21038905Sborman 			case SUSP:
21138905Sborman 				sendsusp();
21238905Sborman 				break;
21338905Sborman 
21438905Sborman 			case ABORT:
21538905Sborman 				sendbrk();
21638905Sborman 				break;
21738905Sborman 
21838905Sborman 			case IAC:
21938905Sborman 				*pfrontp++ = c;
22038905Sborman 				break;
22138905Sborman 			}
22238905Sborman 			state = TS_DATA;
22338905Sborman 			break;
22438905Sborman 
22538905Sborman 		case TS_SB:
22638905Sborman 			if (c == IAC) {
22738905Sborman 				state = TS_SE;
22838905Sborman 			} else {
22938905Sborman 				SB_ACCUM(c);
23038905Sborman 			}
23138905Sborman 			break;
23238905Sborman 
23338905Sborman 		case TS_SE:
23438905Sborman 			if (c != SE) {
23538905Sborman 				if (c != IAC) {
23638905Sborman 					/*
23738905Sborman 					 * bad form of suboption negotiation.
23838905Sborman 					 * handle it in such a way as to avoid
23938905Sborman 					 * damage to local state.  Parse
24038905Sborman 					 * suboption buffer found so far,
24138905Sborman 					 * then treat remaining stream as
24238905Sborman 					 * another command sequence.
24338905Sborman 					 */
24438905Sborman 					SB_TERM();
24538905Sborman 					suboption();
24638905Sborman 					state = TS_IAC;
24738905Sborman 					goto gotiac;
24838905Sborman 				}
24938905Sborman 				SB_ACCUM(c);
25038905Sborman 				state = TS_SB;
25138905Sborman 			} else {
25238905Sborman 				SB_TERM();
25338905Sborman 				suboption();	/* handle sub-option */
25438905Sborman 				state = TS_DATA;
25538905Sborman 			}
25638905Sborman 			break;
25738905Sborman 
25838905Sborman 		case TS_WILL:
25939503Sborman 			willoption(c);
26038905Sborman 			state = TS_DATA;
26138905Sborman 			continue;
26238905Sborman 
26338905Sborman 		case TS_WONT:
26439503Sborman 			wontoption(c);
26538905Sborman 			state = TS_DATA;
26638905Sborman 			continue;
26738905Sborman 
26838905Sborman 		case TS_DO:
26939503Sborman 			dooption(c);
27038905Sborman 			state = TS_DATA;
27138905Sborman 			continue;
27238905Sborman 
27338905Sborman 		case TS_DONT:
27439503Sborman 			dontoption(c);
27538905Sborman 			state = TS_DATA;
27638905Sborman 			continue;
27738905Sborman 
27838905Sborman 		default:
27938905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
28038905Sborman 			printf("telnetd: panic state=%d\n", state);
28138905Sborman 			exit(1);
28238905Sborman 		}
28338905Sborman 	}
28440242Sborman #if	defined(CRAY2) && defined(UNICOS5)
28538905Sborman 	if (!linemode) {
28638905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
28738905Sborman 		char	xbuf2[BUFSIZ];
28838905Sborman 		register char *cp;
28938905Sborman 		int n = pfrontp - opfrontp, oc;
29038905Sborman 		bcopy(opfrontp, xptyobuf, n);
29138905Sborman 		pfrontp = opfrontp;
29238905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
29338905Sborman 					xbuf2, &oc, BUFSIZ);
29438905Sborman 		for (cp = xbuf2; oc > 0; --oc)
29538905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
29638905Sborman 				*nfrontp++ = IAC;
29738905Sborman 	}
29840242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
29938905Sborman }  /* end of telrcv */
30038905Sborman 
30138905Sborman /*
30238905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
30338905Sborman  * Telnet option processing state machine.  We keep track of the full
30438905Sborman  * state of the option negotiation with the following state variables
30538905Sborman  *	myopts, hisopts - The last fully negotiated state for each
30638905Sborman  *			side of the connection.
30738905Sborman  *	mywants, hiswants - The state we wish to be in after a completed
30838905Sborman  *			negotiation.  (hiswants is slightly misleading,
30938905Sborman  *			this is more precisely the state I want him to
31038905Sborman  *			be in.
31138905Sborman  *	resp - We count the number of requests we have sent out.
31238905Sborman  *
31338905Sborman  * These correspond to the following states:
31438905Sborman  *	my_state = the last negotiated state
31538905Sborman  *	want_state = what I want the state to go to
31638905Sborman  *	want_resp = how many requests I have sent
31738905Sborman  * All state defaults are negative, and resp defaults to 0.
31838905Sborman  *
31938905Sborman  * When initiating a request to change state to new_state:
32038905Sborman  *
32138905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
32238905Sborman  *	do nothing;
32338905Sborman  * } else {
32438905Sborman  *	want_state = new_state;
32538905Sborman  *	send new_state;
32638905Sborman  *	want_resp++;
32738905Sborman  * }
32838905Sborman  *
32938905Sborman  * When receiving new_state:
33038905Sborman  *
33138905Sborman  * if (want_resp) {
33238905Sborman  *	want_resp--;
33338905Sborman  *	if (want_resp && (new_state == my_state))
33438905Sborman  *		want_resp--;
33538905Sborman  * }
33638905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
33738905Sborman  *	if (ok_to_switch_to new_state)
33838905Sborman  *		want_state = new_state;
33938905Sborman  *	else
34038905Sborman  *		want_resp++;
34138905Sborman  *	send want_state;
34238905Sborman  * }
34338905Sborman  * my_state = new_state;
34438905Sborman  *
34538905Sborman  * Note that new_state is implied in these functions by the function itself.
34638905Sborman  * will and do imply positive new_state, wont and dont imply negative.
34738905Sborman  *
34838905Sborman  * Finally, there is one catch.  If we send a negative response to a
34938905Sborman  * positive request, my_state will be the positive while want_state will
35038905Sborman  * remain negative.  my_state will revert to negative when the negative
35138905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
35238905Sborman  * us not only the last negotiated state, but also tells us what the peer
35338905Sborman  * wants to be doing as well.  It is important to understand this difference
35438905Sborman  * as we may wish to be processing data streams based on our desired state
35538905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
35638905Sborman  *
35738905Sborman  * This all works fine because if the peer sends a positive request, the data
35838905Sborman  * that we receive prior to negative acknowlegment will probably be affected
35938905Sborman  * by the positive state, and we can process it as such (if we can; if we
36038905Sborman  * can't then it really doesn't matter).  If it is that important, then the
36138905Sborman  * peer probably should be buffering until this option state negotiation
36238905Sborman  * is complete.
36338905Sborman  *
36438905Sborman  */
36539503Sborman send_do(option, init)
36639503Sborman 	int option, init;
36738905Sborman {
36839503Sborman 	if (init) {
36939503Sborman 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) ||
37039503Sborman 		    hiswants[option] == OPT_YES)
37139503Sborman 			return;
37239531Sborman 		/*
37339531Sborman 		 * Special case for TELOPT_TM:  We send a DO, but pretend
37439531Sborman 		 * that we sent a DONT, so that we can send more DOs if
37539531Sborman 		 * we want to.
37639531Sborman 		 */
37739531Sborman 		if (option == TELOPT_TM)
37839531Sborman 			hiswants[option] = OPT_NO;
37939531Sborman 		else
38039531Sborman 			hiswants[option] = OPT_YES;
38139503Sborman 		do_dont_resp[option]++;
38239503Sborman 	}
38339503Sborman 	(void) sprintf(nfrontp, doopt, option);
38439503Sborman 	nfrontp += sizeof (dont) - 2;
38539503Sborman }
38639503Sborman 
38739503Sborman willoption(option)
38839503Sborman 	int option;
38939503Sborman {
39038905Sborman 	int changeok = 0;
39138905Sborman 
39239503Sborman 	/*
39339503Sborman 	 * process input from peer.
39439503Sborman 	 */
39539503Sborman 
39639503Sborman 	if (do_dont_resp[option]) {
39739503Sborman 		do_dont_resp[option]--;
39839503Sborman 		if (do_dont_resp[option] && hisopts[option] == OPT_YES)
39939503Sborman 			do_dont_resp[option]--;
40039503Sborman 	}
40139531Sborman 	if (do_dont_resp[option] == 0) {
40239531Sborman 	    if (hiswants[option] != OPT_YES) {
40338905Sborman 		switch (option) {
40438905Sborman 
40538905Sborman 		case TELOPT_BINARY:
40638905Sborman 			init_termbuf();
40738905Sborman 			tty_binaryin(1);
40838905Sborman 			set_termbuf();
40938905Sborman 			changeok++;
41038905Sborman 			break;
41138905Sborman 
41238905Sborman 		case TELOPT_ECHO:
41339503Sborman 			not42 = 0;	/* looks like a 4.2 system */
41439503Sborman #ifdef notdef
41538905Sborman 			/*
41639503Sborman 			 * Now, in a 4.2 system, to break them out of
41739503Sborman 			 * ECHOing (to the terminal) mode, we need to
41839503Sborman 			 * send a WILL ECHO.
41938905Sborman 			 */
42038905Sborman 			if (myopts[TELOPT_ECHO] == OPT_YES) {
42139503Sborman 				send_will(TELOPT_ECHO, 1);
42238905Sborman 			}
42339503Sborman #else
42438905Sborman 			/*
42539503Sborman 			 * "WILL ECHO".  Kludge upon kludge!
42639503Sborman 			 * A 4.2 client is now echoing user input at
42739503Sborman 			 * the tty.  This is probably undesireable and
42839503Sborman 			 * it should be stopped.  The client will
42939503Sborman 			 * respond WONT TM to the DO TM that we send to
43039503Sborman 			 * check for kludge linemode.  When the WONT TM
43139503Sborman 			 * arrives, linemode will be turned off and a
43239503Sborman 			 * change propogated to the pty.  This change
43339503Sborman 			 * will cause us to process the new pty state
43439503Sborman 			 * in localstat(), which will notice that
43539503Sborman 			 * linemode is off and send a WILL ECHO
43639503Sborman 			 * so that we are properly in character mode and
43739503Sborman 			 * all is well.
43839503Sborman 			 */
43939503Sborman #endif
44039503Sborman 			/*
44138905Sborman 			 * Fool the state machine into sending a don't.
44239503Sborman 			 * This also allows the initial echo sending
44339503Sborman 			 * code to break out of the loop that it is
44438905Sborman 			 * in.  (Look in telnet())
44538905Sborman 			 */
44638905Sborman 			hiswants[TELOPT_ECHO] = OPT_NO;
44738905Sborman 			break;
44838905Sborman 
44938905Sborman 		case TELOPT_TM:
45038905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
45138905Sborman 			/*
45239503Sborman 			 * This telnetd implementation does not really
45339503Sborman 			 * support timing marks, it just uses them to
45439503Sborman 			 * support the kludge linemode stuff.  If we
45539503Sborman 			 * receive a will or wont TM in response to our
45639503Sborman 			 * do TM request that may have been sent to
45739503Sborman 			 * determine kludge linemode support, process
45839503Sborman 			 * it, otherwise TM should get a negative
45939503Sborman 			 * response back.
46038905Sborman 			 */
46138905Sborman 			/*
46238905Sborman 			 * Handle the linemode kludge stuff.
46338905Sborman 			 * If we are not currently supporting any
46438905Sborman 			 * linemode at all, then we assume that this
46538905Sborman 			 * is the client telling us to use kludge
46638905Sborman 			 * linemode in response to our query.  Set the
46738905Sborman 			 * linemode type that is to be supported, note
46838905Sborman 			 * that the client wishes to use linemode, and
46938905Sborman 			 * eat the will TM as though it never arrived.
47038905Sborman 			 */
47138905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
47238905Sborman 				lmodetype = KLUDGE_LINEMODE;
47338905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
47439503Sborman 				send_wont(TELOPT_SGA, 1);
47538905Sborman 			}
47638905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
47738905Sborman 			/*
47839531Sborman 			 * We never respond to a WILL TM, and
47939531Sborman 			 * we leave the state OPT_NO.
48038905Sborman 			 */
48138905Sborman 			return;
48238905Sborman 
48338905Sborman 		case TELOPT_LFLOW:
48438905Sborman 			/*
48539503Sborman 			 * If we are going to support flow control
48639503Sborman 			 * option, then don't worry peer that we can't
48739503Sborman 			 * change the flow control characters.
48838905Sborman 			 */
48938905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
49038905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
49138905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
49238905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
49338905Sborman 		case TELOPT_TTYPE:
49438905Sborman 		case TELOPT_SGA:
49538905Sborman 		case TELOPT_NAWS:
49638905Sborman 		case TELOPT_TSPEED:
49739531Sborman 			changeok++;
49839531Sborman 			break;
49939531Sborman 
50038905Sborman #ifdef	LINEMODE
50138905Sborman 		case TELOPT_LINEMODE:
50239531Sborman # ifdef	KLUDGELINEMODE
50339531Sborman 			/*
50439531Sborman 			 * Note client's desire to use linemode.
50539531Sborman 			 */
50639531Sborman 			lmodetype = REAL_LINEMODE;
50739531Sborman # endif	/* KLUDGELINEMODE */
50839531Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
50938905Sborman 			changeok++;
51038905Sborman 			break;
51139531Sborman #endif	/* LINEMODE */
51238905Sborman 
51338905Sborman 		default:
51438905Sborman 			break;
51538905Sborman 		}
51639503Sborman 		if (changeok) {
51738905Sborman 			hiswants[option] = OPT_YES;
51839503Sborman 			send_do(option, 0);
51939503Sborman 		} else {
52039503Sborman 			do_dont_resp[option]++;
52139503Sborman 			send_dont(option, 0);
52238905Sborman 		}
52339531Sborman 	    }
52438905Sborman 	}
52539503Sborman 	hisopts[option] = OPT_YES;
52638905Sborman }  /* end of willoption */
52738905Sborman 
52839503Sborman send_dont(option, init)
52939503Sborman 	int option, init;
53038905Sborman {
53139503Sborman 	if (init) {
53239503Sborman 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) ||
53339503Sborman 		    hiswants[option] == OPT_NO)
53439503Sborman 			return;
53539503Sborman 		hiswants[option] = OPT_NO;
53639503Sborman 		do_dont_resp[option]++;
53739503Sborman 	}
53839503Sborman 	(void) sprintf(nfrontp, dont, option);
53939503Sborman 	nfrontp += sizeof (doopt) - 2;
54039503Sborman }
54139503Sborman 
54239503Sborman wontoption(option)
54339503Sborman 	int option;
54439503Sborman {
54538905Sborman 	char *fmt = (char *)0;
54638905Sborman 
54738905Sborman 	/*
54838905Sborman 	 * Process client input.
54938905Sborman 	 */
55039503Sborman 
55139503Sborman 	if (do_dont_resp[option]) {
55239503Sborman 		do_dont_resp[option]--;
55339503Sborman 		if (do_dont_resp[option] && hisopts[option] == OPT_NO)
55439503Sborman 			do_dont_resp[option]--;
55539503Sborman 	}
55639531Sborman 	if (do_dont_resp[option] == 0) {
55739531Sborman 	    if (hiswants[option] != OPT_NO) {
55839503Sborman 		/* it is always ok to change to negative state */
55938905Sborman 		switch (option) {
56038905Sborman 		case TELOPT_ECHO:
56139503Sborman 			not42 = 1; /* doesn't seem to be a 4.2 system */
56238905Sborman 			break;
56338905Sborman 
56438905Sborman 		case TELOPT_BINARY:
56538905Sborman 			init_termbuf();
56638905Sborman 			tty_binaryin(0);
56738905Sborman 			set_termbuf();
56838905Sborman 			break;
56938905Sborman 
57038905Sborman #ifdef	LINEMODE
57138905Sborman 		case TELOPT_LINEMODE:
57238905Sborman # ifdef	KLUDGELINEMODE
57338905Sborman 			/*
57438905Sborman 			 * If real linemode is supported, then client is
57538905Sborman 			 * asking to turn linemode off.
57638905Sborman 			 */
57738905Sborman 			if (lmodetype == REAL_LINEMODE)
57838905Sborman # endif	/* KLUDGELINEMODE */
57938905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
58038905Sborman 			break;
58138905Sborman #endif	LINEMODE
58238905Sborman 
58338905Sborman 		case TELOPT_TM:
58438905Sborman 			/*
58539503Sborman 			 * If we get a WONT TM, and had sent a DO TM,
58639503Sborman 			 * don't respond with a DONT TM, just leave it
58739503Sborman 			 * as is.  Short circut the state machine to
58839531Sborman 			 * achive this.
58938905Sborman 			 */
59038905Sborman 			hiswants[TELOPT_TM] = OPT_NO;
59138905Sborman 			return;
59238905Sborman 
59338905Sborman 		case TELOPT_LFLOW:
59438905Sborman 			/*
59539503Sborman 			 * If we are not going to support flow control
59639503Sborman 			 * option, then let peer know that we can't
59739503Sborman 			 * change the flow control characters.
59838905Sborman 			 */
59938905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
60038905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
60138905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
60238905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
60338905Sborman 			break;
60438905Sborman 
60538905Sborman 		default:
60638905Sborman 			break;
60738905Sborman 		}
60839503Sborman 		hiswants[option] = OPT_NO;
60939503Sborman 		fmt = dont;
61039503Sborman 		send_dont(option, 0);
61139531Sborman 	    } else {
61239531Sborman 		switch (option) {
61339531Sborman 		case TELOPT_TM:
61439531Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
61539531Sborman 			if (lmodetype < REAL_LINEMODE) {
61639531Sborman 				lmodetype = NO_LINEMODE;
61739531Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
61839531Sborman 				send_will(TELOPT_SGA, 1);
61939531Sborman /*@*/				send_will(TELOPT_ECHO, 1);
62039531Sborman 			}
62139531Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
62239531Sborman 		default:
62339531Sborman 			break;
62439531Sborman 		}
62539531Sborman 	    }
62638905Sborman 	}
62739503Sborman 	hisopts[option] = OPT_NO;
62838905Sborman 
62939503Sborman }  /* end of wontoption */
63038905Sborman 
63139503Sborman send_will(option, init)
63239503Sborman 	int option, init;
63339503Sborman {
63439503Sborman 	if (init) {
63539503Sborman 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)||
63639503Sborman 		    mywants[option] == OPT_YES)
63739503Sborman 			return;
63839503Sborman 		mywants[option] = OPT_YES;
63939503Sborman 		will_wont_resp[option]++;
64038905Sborman 	}
64139503Sborman 	(void) sprintf(nfrontp, will, option);
64239503Sborman 	nfrontp += sizeof (doopt) - 2;
64339503Sborman }
64438905Sborman 
64539503Sborman dooption(option)
64639503Sborman 	int option;
64738905Sborman {
64838905Sborman 	int changeok = 0;
64938905Sborman 
65038905Sborman 	/*
65138905Sborman 	 * Process client input.
65238905Sborman 	 */
65339503Sborman 
65439503Sborman 	if (will_wont_resp[option]) {
65539503Sborman 		will_wont_resp[option]--;
65639503Sborman 		if (will_wont_resp[option] && myopts[option] == OPT_YES)
65739503Sborman 			will_wont_resp[option]--;
65839503Sborman 	}
65939503Sborman 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) {
66038905Sborman 		switch (option) {
66138905Sborman 		case TELOPT_ECHO:
66238905Sborman #ifdef	LINEMODE
66339503Sborman 			if (lmodetype == NO_LINEMODE) {
66438905Sborman #endif
66538905Sborman 				init_termbuf();
66638905Sborman 				tty_setecho(1);
66738905Sborman 				set_termbuf();
66838905Sborman #ifdef	LINEMODE
66938905Sborman 			}
67038905Sborman #endif
67138905Sborman 			changeok++;
67238905Sborman 			break;
67338905Sborman 
67438905Sborman 		case TELOPT_BINARY:
67538905Sborman 			init_termbuf();
67638905Sborman 			tty_binaryout(1);
67738905Sborman 			set_termbuf();
67838905Sborman 			changeok++;
67938905Sborman 			break;
68038905Sborman 
68138905Sborman 		case TELOPT_SGA:
68238905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
68338905Sborman 			/*
68439503Sborman 			 * If kludge linemode is in use, then we must
68539503Sborman 			 * process an incoming do SGA for linemode
68639503Sborman 			 * purposes.
68738905Sborman 			 */
68838905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
68938905Sborman 				/*
69039503Sborman 				 * Receipt of "do SGA" in kludge
69139503Sborman 				 * linemode is the peer asking us to
69239503Sborman 				 * turn off linemode.  Make note of
69339503Sborman 				 * the request.
69438905Sborman 				 */
69538905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
69638905Sborman 				/*
69739503Sborman 				 * If linemode did not get turned off
69839503Sborman 				 * then don't tell peer that we did.
69939503Sborman 				 * Breaking here forces a wont SGA to
70039503Sborman 				 * be returned.
70138905Sborman 				 */
70238905Sborman 				if (linemode)
70338905Sborman 					break;
70438905Sborman 			}
70538905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
70638905Sborman 			changeok++;
70738905Sborman 			break;
70838905Sborman 
70938905Sborman 		case TELOPT_STATUS:
71038905Sborman 			changeok++;
71138905Sborman 			break;
71238905Sborman 
71338905Sborman 		case TELOPT_TM:
71439503Sborman 			/*
71539503Sborman 			 * Special case for TM.  We send a WILL, but
71639503Sborman 			 * pretend we sent a WONT.
71739503Sborman 			 */
71839503Sborman 			send_will(option, 0);
71939503Sborman 			mywants[option] = OPT_NO;
72039503Sborman 			myopts[option] = OPT_NO;
72139503Sborman 			return;
72239503Sborman 
72338905Sborman 		case TELOPT_LINEMODE:
72438905Sborman 		case TELOPT_TTYPE:
72538905Sborman 		case TELOPT_NAWS:
72638905Sborman 		case TELOPT_TSPEED:
72738905Sborman 		case TELOPT_LFLOW:
72838905Sborman 		default:
72938905Sborman 			break;
73038905Sborman 		}
73139503Sborman 		if (changeok) {
73238905Sborman 			mywants[option] = OPT_YES;
73339503Sborman 			send_will(option, 0);
73439503Sborman 		} else {
73539503Sborman 			will_wont_resp[option]++;
73639503Sborman 			send_wont(option, 0);
73738905Sborman 		}
73838905Sborman 	}
73939503Sborman 	myopts[option] = OPT_YES;
74038905Sborman 
74138905Sborman }  /* end of dooption */
74238905Sborman 
74339503Sborman send_wont(option, init)
74439503Sborman 	int option, init;
74539503Sborman {
74639503Sborman 	if (init) {
74739503Sborman 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) ||
74839503Sborman 		    mywants[option] == OPT_NO)
74939503Sborman 			return;
75039503Sborman 		mywants[option] = OPT_NO;
75139503Sborman 		will_wont_resp[option]++;
75239503Sborman 	}
75339503Sborman 	(void) sprintf(nfrontp, wont, option);
75439503Sborman 	nfrontp += sizeof (wont) - 2;
75539503Sborman }
75638905Sborman 
75739503Sborman dontoption(option)
75839503Sborman 	int option;
75938905Sborman {
76038905Sborman 	/*
76138905Sborman 	 * Process client input.
76238905Sborman 	 */
76340242Sborman 
76439503Sborman 	if (will_wont_resp[option]) {
76539503Sborman 		will_wont_resp[option]--;
76639503Sborman 		if (will_wont_resp[option] && myopts[option] == OPT_NO)
76739503Sborman 			will_wont_resp[option]--;
76839503Sborman 	}
76939503Sborman 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) {
77038905Sborman 		switch (option) {
77138905Sborman 		case TELOPT_BINARY:
77238905Sborman 			init_termbuf();
77338905Sborman 			tty_binaryout(0);
77438905Sborman 			set_termbuf();
77538905Sborman 			break;
77638905Sborman 
77739503Sborman 		case TELOPT_ECHO:	/* we should stop echoing */
77838905Sborman #ifdef	LINEMODE
77939503Sborman 			if (lmodetype == NO_LINEMODE) {
78038905Sborman #endif
78138905Sborman 				init_termbuf();
78238905Sborman 				tty_setecho(0);
78338905Sborman 				set_termbuf();
78438905Sborman #ifdef	LINEMODE
78538905Sborman 			}
78638905Sborman #endif
78738905Sborman 			break;
78838905Sborman 
78938905Sborman 		case TELOPT_SGA:
79038905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
79138905Sborman 			/*
79239503Sborman 			 * If kludge linemode is in use, then we
79339503Sborman 			 * must process an incoming do SGA for
79439503Sborman 			 * linemode purposes.
79538905Sborman 			 */
79638905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
79738905Sborman 				/*
79839503Sborman 				 * The client is asking us to turn
79939503Sborman 				 * linemode on.
80038905Sborman 				 */
80138905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
80238905Sborman 				/*
80339503Sborman 				 * If we did not turn line mode on,
80439503Sborman 				 * then what do we say?  Will SGA?
80539503Sborman 				 * This violates design of telnet.
80639503Sborman 				 * Gross.  Very Gross.
80738905Sborman 				 */
80838905Sborman 			}
80938905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
81038905Sborman 
81138905Sborman 		default:
81238905Sborman 			break;
81338905Sborman 		}
81438905Sborman 
81539503Sborman 		mywants[option] = OPT_NO;
81639503Sborman 		send_wont(option, 0);
81738905Sborman 	}
81839503Sborman 	myopts[option] = OPT_NO;
81938905Sborman 
82038905Sborman }  /* end of dontoption */
82138905Sborman 
82238905Sborman /*
82338905Sborman  * suboption()
82438905Sborman  *
82538905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
82638905Sborman  * side.
82738905Sborman  *
82838905Sborman  *	Currently we recognize:
82938905Sborman  *
83038905Sborman  *	Terminal type is
83138905Sborman  *	Linemode
83238905Sborman  *	Window size
83338905Sborman  *	Terminal speed
83438905Sborman  */
83538905Sborman suboption()
83638905Sborman {
83738905Sborman     register int subchar;
83838905Sborman 
83938905Sborman     subchar = SB_GET();
84038905Sborman     switch (subchar) {
84138905Sborman     case TELOPT_TSPEED: {
84238905Sborman 	register int xspeed, rspeed;
84338905Sborman 
84438905Sborman 	if (hisopts[TELOPT_TSPEED] == OPT_NO)	/* Ignore if option disabled */
84538905Sborman 		break;
84638905Sborman 
84738905Sborman 	settimer(tspeedsubopt);
84838905Sborman 
84938905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
85038905Sborman 		return;
85138905Sborman 
85238905Sborman 	xspeed = atoi(subpointer);
85338905Sborman 
85438905Sborman 	while (SB_GET() != ',' && !SB_EOF());
85538905Sborman 	if (SB_EOF())
85638905Sborman 		return;
85738905Sborman 
85838905Sborman 	rspeed = atoi(subpointer);
85938905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
86038905Sborman 
86138905Sborman 	break;
86238905Sborman 
86338905Sborman     }  /* end of case TELOPT_TSPEED */
86438905Sborman 
86538905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
86638905Sborman 	static char terminalname[5+41] = "TERM=";
86738905Sborman 
86838997Sborman 	if (hisopts[TELOPT_TTYPE] == OPT_NO)	/* Ignore if option disabled */
86938905Sborman 		break;
87038905Sborman 	settimer(ttypesubopt);
87138905Sborman 
87238905Sborman 	if (SB_GET() != TELQUAL_IS) {
87338905Sborman 	    return;		/* ??? XXX but, this is the most robust */
87438905Sborman 	}
87538905Sborman 
87638905Sborman 	terminaltype = terminalname+sizeof("TERM=")-1;
87738905Sborman 
87838905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
87938905Sborman 								    !SB_EOF()) {
88038905Sborman 	    register int c;
88138905Sborman 
88238905Sborman 	    c = SB_GET();
88338905Sborman 	    if (isupper(c)) {
88438905Sborman 		c = tolower(c);
88538905Sborman 	    }
88638905Sborman 	    *terminaltype++ = c;    /* accumulate name */
88738905Sborman 	}
88838905Sborman 	*terminaltype = 0;
88938905Sborman 	terminaltype = terminalname;
89038905Sborman 	break;
89138905Sborman     }  /* end of case TELOPT_TTYPE */
89238905Sborman 
89338905Sborman     case TELOPT_NAWS: {
89438905Sborman 	register int xwinsize, ywinsize;
89538905Sborman 
89638905Sborman 	if (hisopts[TELOPT_NAWS] == OPT_NO)	/* Ignore if option disabled */
89738905Sborman 		break;
89838905Sborman 
89938905Sborman 	if (SB_EOF())
90038905Sborman 		return;
90138905Sborman 	xwinsize = SB_GET() << 8;
90238905Sborman 	if (SB_EOF())
90338905Sborman 		return;
90438905Sborman 	xwinsize |= SB_GET();
90538905Sborman 	if (SB_EOF())
90638905Sborman 		return;
90738905Sborman 	ywinsize = SB_GET() << 8;
90838905Sborman 	if (SB_EOF())
90938905Sborman 		return;
91038905Sborman 	ywinsize |= SB_GET();
91138905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
91238905Sborman 
91338905Sborman 	break;
91438905Sborman 
91538905Sborman     }  /* end of case TELOPT_NAWS */
91638905Sborman 
91738905Sborman #ifdef	LINEMODE
91838905Sborman     case TELOPT_LINEMODE: {
91938905Sborman 	register int request;
92038905Sborman 
92138905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO)	/* Ignore if option disabled */
92238905Sborman 		break;
92338905Sborman 	/*
92438905Sborman 	 * Process linemode suboptions.
92538905Sborman 	 */
92638905Sborman 	if (SB_EOF()) break;  /* garbage was sent */
92738905Sborman 	request = SB_GET();  /* get will/wont */
92838905Sborman 	if (SB_EOF()) break;  /* another garbage check */
92938905Sborman 
93038905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
93138905Sborman 		/*
93238905Sborman 		 * Process suboption buffer of slc's
93338905Sborman 		 */
93438905Sborman 		start_slc(1);
93538905Sborman 		do_opt_slc(subpointer, subend - subpointer);
93638905Sborman 		end_slc(0);
93738905Sborman 
93838905Sborman 	} else if (request == LM_MODE) {
93938905Sborman 		useeditmode = SB_GET();  /* get mode flag */
94038905Sborman 		clientstat(LM_MODE, 0, 0);
94138905Sborman 	}
94238905Sborman 
94338905Sborman 	switch (SB_GET()) {  /* what suboption? */
94438905Sborman 	case LM_FORWARDMASK:
94538905Sborman 		/*
94638905Sborman 		 * According to spec, only server can send request for
94738905Sborman 		 * forwardmask, and client can only return a positive response.
94838905Sborman 		 * So don't worry about it.
94938905Sborman 		 */
95038905Sborman 
95138905Sborman 	default:
95238905Sborman 		break;
95338905Sborman 	}
95440242Sborman 	break;
95538905Sborman     }  /* end of case TELOPT_LINEMODE */
95638905Sborman #endif
95738905Sborman     case TELOPT_STATUS: {
95838905Sborman 	int mode;
95938905Sborman 
96038905Sborman 	mode = SB_GET();
96138905Sborman 	switch (mode) {
96238905Sborman 	case TELQUAL_SEND:
96338905Sborman 	    if (myopts[TELOPT_STATUS] == OPT_YES)
96438905Sborman 		send_status();
96538905Sborman 	    break;
96638905Sborman 
96738905Sborman 	case TELQUAL_IS:
96838905Sborman 	    break;
96938905Sborman 
97038905Sborman 	default:
97138905Sborman 	    break;
97238905Sborman 	}
97340242Sborman 	break;
97440242Sborman     }  /* end of case TELOPT_STATUS */
97538905Sborman 
97638905Sborman     default:
97738905Sborman 	break;
97838905Sborman     }  /* end of switch */
97938905Sborman 
98038905Sborman }  /* end of suboption */
98138905Sborman 
98238905Sborman #define	ADD(c)	 *ncp++ = c;
98338905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
98438905Sborman send_status()
98538905Sborman {
98638905Sborman 	char statusbuf[256];
98738905Sborman 	register char *ncp;
98838905Sborman 	register int i;
98938905Sborman 
99038905Sborman 	ncp = statusbuf;
99138905Sborman 
99238905Sborman 	netflush();	/* get rid of anything waiting to go out */
99338905Sborman 
99438905Sborman 	ADD(IAC);
99538905Sborman 	ADD(SB);
99638905Sborman 	ADD(TELOPT_STATUS);
99738905Sborman 	ADD(TELQUAL_IS);
99838905Sborman 
99938905Sborman 	for (i = 0; i < NTELOPTS; i++) {
100038905Sborman 		if (myopts[i] == OPT_YES) {
100138905Sborman 			ADD(WILL);
100238905Sborman 			ADD_DATA(i);
100338905Sborman 			if (i == IAC)
100438905Sborman 				ADD(IAC);
100538905Sborman 		}
100638905Sborman 		if (hisopts[i] == OPT_YES) {
100738905Sborman 			ADD(DO);
100838905Sborman 			ADD_DATA(i);
100938905Sborman 			if (i == IAC)
101038905Sborman 				ADD(IAC);
101138905Sborman 		}
101238905Sborman 	}
101338905Sborman 
101438905Sborman #ifdef	LINEMODE
101538905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
101638905Sborman 		char *cp, *cpe;
101738905Sborman 		int len;
101838905Sborman 
101938905Sborman 		ADD(SB);
102038905Sborman 		ADD(TELOPT_LINEMODE);
102138905Sborman 		ADD(LM_MODE);
102238905Sborman 		ADD_DATA(editmode);
102338905Sborman 		if (editmode == IAC)
102438905Sborman 			ADD(IAC);
102538905Sborman 		ADD(SE);
102638905Sborman 
102738905Sborman 		ADD(SB);
102838905Sborman 		ADD(TELOPT_LINEMODE);
102938905Sborman 		ADD(LM_SLC);
103038905Sborman 		start_slc(0);
103138905Sborman 		send_slc();
103238905Sborman 		len = end_slc(&cp);
103338905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
103438905Sborman 			ADD_DATA(*cp);
103538905Sborman 		ADD(SE);
103638905Sborman 	}
103738905Sborman #endif	/* LINEMODE */
103838905Sborman 
103938905Sborman 	ADD(IAC);
104038905Sborman 	ADD(SE);
104138905Sborman 
104238905Sborman 	writenet(statusbuf, ncp - statusbuf);
104338905Sborman 	netflush();	/* Send it on its way */
104438905Sborman }
1045