xref: /csrg-svn/libexec/telnetd/state.c (revision 39531)
138905Sborman /*
238905Sborman  * Copyright (c) 1989 Regents of the University of California.
338905Sborman  * All rights reserved.
438905Sborman  *
538905Sborman  * Redistribution and use in source and binary forms are permitted
638905Sborman  * provided that the above copyright notice and this paragraph are
738905Sborman  * duplicated in all such forms and that any documentation,
838905Sborman  * advertising materials, and other materials related to such
938905Sborman  * distribution and use acknowledge that the software was developed
1038905Sborman  * by the University of California, Berkeley.  The name of the
1138905Sborman  * University may not be used to endorse or promote products derived
1238905Sborman  * from this software without specific prior written permission.
1338905Sborman  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1438905Sborman  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1538905Sborman  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1638905Sborman  */
1738905Sborman 
1838905Sborman #ifndef lint
19*39531Sborman static char sccsid[] = "@(#)state.c	5.4 (Berkeley) 11/14/89";
2038905Sborman #endif /* not lint */
2138905Sborman 
2238905Sborman #include "telnetd.h"
2338905Sborman 
2438905Sborman char	doopt[] = { IAC, DO, '%', 'c', 0 };
2538905Sborman char	dont[] = { IAC, DONT, '%', 'c', 0 };
2638905Sborman char	will[] = { IAC, WILL, '%', 'c', 0 };
2738905Sborman char	wont[] = { IAC, WONT, '%', 'c', 0 };
2838905Sborman int	not42 = 1;
2938905Sborman 
3038905Sborman /*
3138905Sborman  * Buffer for sub-options, and macros
3238905Sborman  * for suboptions buffer manipulations
3338905Sborman  */
3438905Sborman char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
3538905Sborman 
3638905Sborman #define	SB_CLEAR()	subpointer = subbuffer;
3738905Sborman #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
3838905Sborman #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
3938905Sborman 				*subpointer++ = (c); \
4038905Sborman 			}
4138905Sborman #define	SB_GET()	((*subpointer++)&0xff)
4238905Sborman #define	SB_EOF()	(subpointer >= subend)
4338905Sborman 
4438905Sborman 
4538905Sborman 
4638905Sborman /*
4738905Sborman  * State for recv fsm
4838905Sborman  */
4938905Sborman #define	TS_DATA		0	/* base state */
5038905Sborman #define	TS_IAC		1	/* look for double IAC's */
5138905Sborman #define	TS_CR		2	/* CR-LF ->'s CR */
5238905Sborman #define	TS_SB		3	/* throw away begin's... */
5338905Sborman #define	TS_SE		4	/* ...end's (suboption negotiation) */
5438905Sborman #define	TS_WILL		5	/* will option negotiation */
5538905Sborman #define	TS_WONT		6	/* wont " */
5638905Sborman #define	TS_DO		7	/* do " */
5738905Sborman #define	TS_DONT		8	/* dont " */
5838905Sborman 
5938905Sborman telrcv()
6038905Sborman {
6138905Sborman 	register int c;
6238905Sborman 	static int state = TS_DATA;
6338905Sborman #ifdef	CRAY2
6438905Sborman 	char *opfrontp = pfrontp;
6538905Sborman #endif
6638905Sborman 
6738905Sborman 	while (ncc > 0) {
6838905Sborman 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6938905Sborman 			break;
7038905Sborman 		c = *netip++ & 0377, ncc--;
7138905Sborman 		switch (state) {
7238905Sborman 
7338905Sborman 		case TS_CR:
7438905Sborman 			state = TS_DATA;
7538905Sborman 			/* Strip off \n or \0 after a \r */
7638905Sborman 			if ((c == 0) || (c == '\n')) {
7738905Sborman 				break;
7838905Sborman 			}
7938905Sborman 			/* FALL THROUGH */
8038905Sborman 
8138905Sborman 		case TS_DATA:
8238905Sborman 			if (c == IAC) {
8338905Sborman 				state = TS_IAC;
8438905Sborman 				break;
8538905Sborman 			}
8638905Sborman 			/*
8738905Sborman 			 * We now map \r\n ==> \r for pragmatic reasons.
8838905Sborman 			 * Many client implementations send \r\n when
8938905Sborman 			 * the user hits the CarriageReturn key.
9038905Sborman 			 *
9138905Sborman 			 * We USED to map \r\n ==> \n, since \r\n says
9238905Sborman 			 * that we want to be in column 1 of the next
9338905Sborman 			 * printable line, and \n is the standard
9438905Sborman 			 * unix way of saying that (\r is only good
9538905Sborman 			 * if CRMOD is set, which it normally is).
9638905Sborman 			 */
9738905Sborman 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
9838905Sborman 				/*
9938905Sborman 				 * If we are operating in linemode,
10038905Sborman 				 * convert to local end-of-line.
10138905Sborman 				 */
10238905Sborman 				if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
10338905Sborman 					netip++; ncc--;
10438905Sborman 					c = '\n';
10538905Sborman 				} else {
10638905Sborman 					state = TS_CR;
10738905Sborman 				}
10838905Sborman 			}
10938905Sborman 			*pfrontp++ = c;
11038905Sborman 			break;
11138905Sborman 
11238905Sborman 		case TS_IAC:
11338905Sborman gotiac:			switch (c) {
11438905Sborman 
11538905Sborman 			/*
11638905Sborman 			 * Send the process on the pty side an
11738905Sborman 			 * interrupt.  Do this with a NULL or
11838905Sborman 			 * interrupt char; depending on the tty mode.
11938905Sborman 			 */
12038905Sborman 			case IP:
12138905Sborman 				interrupt();
12238905Sborman 				break;
12338905Sborman 
12438905Sborman 			case BREAK:
12538905Sborman 				sendbrk();
12638905Sborman 				break;
12738905Sborman 
12838905Sborman 			/*
12938905Sborman 			 * Are You There?
13038905Sborman 			 */
13138905Sborman 			case AYT:
13238905Sborman 				(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
13338905Sborman 				nfrontp += 9;
13438905Sborman 				break;
13538905Sborman 
13638905Sborman 			/*
13738905Sborman 			 * Abort Output
13838905Sborman 			 */
13938905Sborman 			case AO:
14038905Sborman 			    {
14138905Sborman 				ptyflush();	/* half-hearted */
14238905Sborman 				init_termbuf();
14338905Sborman 
14438905Sborman 				if (slctab[SLC_AO].sptr &&
14538905Sborman 				    *slctab[SLC_AO].sptr != '\377') {
14638905Sborman 					*pfrontp++ = *slctab[SLC_AO].sptr;
14738905Sborman 				}
14838905Sborman 
14938905Sborman 				netclear();	/* clear buffer back */
15038905Sborman 				*nfrontp++ = IAC;
15138905Sborman 				*nfrontp++ = DM;
15238905Sborman 				neturg = nfrontp-1; /* off by one XXX */
15338905Sborman 				break;
15438905Sborman 			    }
15538905Sborman 
15638905Sborman 			/*
15738905Sborman 			 * Erase Character and
15838905Sborman 			 * Erase Line
15938905Sborman 			 */
16038905Sborman 			case EC:
16138905Sborman 			case EL:
16238905Sborman 			    {
16338905Sborman 				unsigned char ch;
16438905Sborman 
16538905Sborman 				ptyflush();	/* half-hearted */
16638905Sborman 				init_termbuf();
16738905Sborman 				ch = (c == EC) ? *slctab[SLC_EC].sptr :
16838905Sborman 						 *slctab[SLC_EL].sptr;
16938905Sborman 				if (ch != '\377')
17038905Sborman 					*pfrontp++ = ch;
17138905Sborman 				break;
17238905Sborman 			    }
17338905Sborman 
17438905Sborman 			/*
17538905Sborman 			 * Check for urgent data...
17638905Sborman 			 */
17738905Sborman 			case DM:
17838905Sborman 				SYNCHing = stilloob(net);
17938905Sborman 				settimer(gotDM);
18038905Sborman 				break;
18138905Sborman 
18238905Sborman 
18338905Sborman 			/*
18438905Sborman 			 * Begin option subnegotiation...
18538905Sborman 			 */
18638905Sborman 			case SB:
18738905Sborman 				state = TS_SB;
18838905Sborman 				SB_CLEAR();
18938905Sborman 				continue;
19038905Sborman 
19138905Sborman 			case WILL:
19238905Sborman 				state = TS_WILL;
19338905Sborman 				continue;
19438905Sborman 
19538905Sborman 			case WONT:
19638905Sborman 				state = TS_WONT;
19738905Sborman 				continue;
19838905Sborman 
19938905Sborman 			case DO:
20038905Sborman 				state = TS_DO;
20138905Sborman 				continue;
20238905Sborman 
20338905Sborman 			case DONT:
20438905Sborman 				state = TS_DONT;
20538905Sborman 				continue;
20638905Sborman 			case EOR:
20738905Sborman 				if (hisopts[TELOPT_EOR])
20838905Sborman 					doeof();
20938905Sborman 				break;
21038905Sborman 
21138905Sborman 			/*
21238905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
21338905Sborman 			 * to command stream (EOF, SUSP, ABORT).
21438905Sborman 			 */
21538905Sborman 			case xEOF:
21638905Sborman 				doeof();
21738905Sborman 				break;
21838905Sborman 
21938905Sborman 			case SUSP:
22038905Sborman 				sendsusp();
22138905Sborman 				break;
22238905Sborman 
22338905Sborman 			case ABORT:
22438905Sborman 				sendbrk();
22538905Sborman 				break;
22638905Sborman 
22738905Sborman 			case IAC:
22838905Sborman 				*pfrontp++ = c;
22938905Sborman 				break;
23038905Sborman 			}
23138905Sborman 			state = TS_DATA;
23238905Sborman 			break;
23338905Sborman 
23438905Sborman 		case TS_SB:
23538905Sborman 			if (c == IAC) {
23638905Sborman 				state = TS_SE;
23738905Sborman 			} else {
23838905Sborman 				SB_ACCUM(c);
23938905Sborman 			}
24038905Sborman 			break;
24138905Sborman 
24238905Sborman 		case TS_SE:
24338905Sborman 			if (c != SE) {
24438905Sborman 				if (c != IAC) {
24538905Sborman 					/*
24638905Sborman 					 * bad form of suboption negotiation.
24738905Sborman 					 * handle it in such a way as to avoid
24838905Sborman 					 * damage to local state.  Parse
24938905Sborman 					 * suboption buffer found so far,
25038905Sborman 					 * then treat remaining stream as
25138905Sborman 					 * another command sequence.
25238905Sborman 					 */
25338905Sborman 					SB_TERM();
25438905Sborman 					suboption();
25538905Sborman 					state = TS_IAC;
25638905Sborman 					goto gotiac;
25738905Sborman 				}
25838905Sborman 				SB_ACCUM(c);
25938905Sborman 				state = TS_SB;
26038905Sborman 			} else {
26138905Sborman 				SB_TERM();
26238905Sborman 				suboption();	/* handle sub-option */
26338905Sborman 				state = TS_DATA;
26438905Sborman 			}
26538905Sborman 			break;
26638905Sborman 
26738905Sborman 		case TS_WILL:
26839503Sborman 			willoption(c);
26938905Sborman 			state = TS_DATA;
27038905Sborman 			continue;
27138905Sborman 
27238905Sborman 		case TS_WONT:
27339503Sborman 			wontoption(c);
27438905Sborman 			state = TS_DATA;
27538905Sborman 			continue;
27638905Sborman 
27738905Sborman 		case TS_DO:
27839503Sborman 			dooption(c);
27938905Sborman 			state = TS_DATA;
28038905Sborman 			continue;
28138905Sborman 
28238905Sborman 		case TS_DONT:
28339503Sborman 			dontoption(c);
28438905Sborman 			state = TS_DATA;
28538905Sborman 			continue;
28638905Sborman 
28738905Sborman 		default:
28838905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
28938905Sborman 			printf("telnetd: panic state=%d\n", state);
29038905Sborman 			exit(1);
29138905Sborman 		}
29238905Sborman 	}
29338905Sborman #if	CRAY2
29438905Sborman 	if (!linemode) {
29538905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
29638905Sborman 		char	xbuf2[BUFSIZ];
29738905Sborman 		register char *cp;
29838905Sborman 		int n = pfrontp - opfrontp, oc;
29938905Sborman 		bcopy(opfrontp, xptyobuf, n);
30038905Sborman 		pfrontp = opfrontp;
30138905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
30238905Sborman 					xbuf2, &oc, BUFSIZ);
30338905Sborman 		for (cp = xbuf2; oc > 0; --oc)
30438905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
30538905Sborman 				*nfrontp++ = IAC;
30638905Sborman 	}
30738905Sborman #endif	/* CRAY2 */
30838905Sborman }  /* end of telrcv */
30938905Sborman 
31038905Sborman /*
31138905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
31238905Sborman  * Telnet option processing state machine.  We keep track of the full
31338905Sborman  * state of the option negotiation with the following state variables
31438905Sborman  *	myopts, hisopts - The last fully negotiated state for each
31538905Sborman  *			side of the connection.
31638905Sborman  *	mywants, hiswants - The state we wish to be in after a completed
31738905Sborman  *			negotiation.  (hiswants is slightly misleading,
31838905Sborman  *			this is more precisely the state I want him to
31938905Sborman  *			be in.
32038905Sborman  *	resp - We count the number of requests we have sent out.
32138905Sborman  *
32238905Sborman  * These correspond to the following states:
32338905Sborman  *	my_state = the last negotiated state
32438905Sborman  *	want_state = what I want the state to go to
32538905Sborman  *	want_resp = how many requests I have sent
32638905Sborman  * All state defaults are negative, and resp defaults to 0.
32738905Sborman  *
32838905Sborman  * When initiating a request to change state to new_state:
32938905Sborman  *
33038905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
33138905Sborman  *	do nothing;
33238905Sborman  * } else {
33338905Sborman  *	want_state = new_state;
33438905Sborman  *	send new_state;
33538905Sborman  *	want_resp++;
33638905Sborman  * }
33738905Sborman  *
33838905Sborman  * When receiving new_state:
33938905Sborman  *
34038905Sborman  * if (want_resp) {
34138905Sborman  *	want_resp--;
34238905Sborman  *	if (want_resp && (new_state == my_state))
34338905Sborman  *		want_resp--;
34438905Sborman  * }
34538905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
34638905Sborman  *	if (ok_to_switch_to new_state)
34738905Sborman  *		want_state = new_state;
34838905Sborman  *	else
34938905Sborman  *		want_resp++;
35038905Sborman  *	send want_state;
35138905Sborman  * }
35238905Sborman  * my_state = new_state;
35338905Sborman  *
35438905Sborman  * Note that new_state is implied in these functions by the function itself.
35538905Sborman  * will and do imply positive new_state, wont and dont imply negative.
35638905Sborman  *
35738905Sborman  * Finally, there is one catch.  If we send a negative response to a
35838905Sborman  * positive request, my_state will be the positive while want_state will
35938905Sborman  * remain negative.  my_state will revert to negative when the negative
36038905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
36138905Sborman  * us not only the last negotiated state, but also tells us what the peer
36238905Sborman  * wants to be doing as well.  It is important to understand this difference
36338905Sborman  * as we may wish to be processing data streams based on our desired state
36438905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
36538905Sborman  *
36638905Sborman  * This all works fine because if the peer sends a positive request, the data
36738905Sborman  * that we receive prior to negative acknowlegment will probably be affected
36838905Sborman  * by the positive state, and we can process it as such (if we can; if we
36938905Sborman  * can't then it really doesn't matter).  If it is that important, then the
37038905Sborman  * peer probably should be buffering until this option state negotiation
37138905Sborman  * is complete.
37238905Sborman  *
37338905Sborman  * In processing options, request signifies whether this is a request
37438905Sborman  * to send or a response.  request is true if this is a request to
37538905Sborman  * send generated locally.
37638905Sborman  */
37739503Sborman send_do(option, init)
37839503Sborman 	int option, init;
37938905Sborman {
38039503Sborman 	if (init) {
38139503Sborman 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) ||
38239503Sborman 		    hiswants[option] == OPT_YES)
38339503Sborman 			return;
384*39531Sborman 		/*
385*39531Sborman 		 * Special case for TELOPT_TM:  We send a DO, but pretend
386*39531Sborman 		 * that we sent a DONT, so that we can send more DOs if
387*39531Sborman 		 * we want to.
388*39531Sborman 		 */
389*39531Sborman 		if (option == TELOPT_TM)
390*39531Sborman 			hiswants[option] = OPT_NO;
391*39531Sborman 		else
392*39531Sborman 			hiswants[option] = OPT_YES;
39339503Sborman 		do_dont_resp[option]++;
39439503Sborman 	}
39539503Sborman 	(void) sprintf(nfrontp, doopt, option);
39639503Sborman 	nfrontp += sizeof (dont) - 2;
39739503Sborman }
39839503Sborman 
39939503Sborman willoption(option)
40039503Sborman 	int option;
40139503Sborman {
40238905Sborman 	int changeok = 0;
40338905Sborman 
40439503Sborman 	/*
40539503Sborman 	 * process input from peer.
40639503Sborman 	 */
40739503Sborman 
40839503Sborman 	if (do_dont_resp[option]) {
40939503Sborman 		do_dont_resp[option]--;
41039503Sborman 		if (do_dont_resp[option] && hisopts[option] == OPT_YES)
41139503Sborman 			do_dont_resp[option]--;
41239503Sborman 	}
413*39531Sborman 	if (do_dont_resp[option] == 0) {
414*39531Sborman 	    if (hiswants[option] != OPT_YES) {
41538905Sborman 		switch (option) {
41638905Sborman 
41738905Sborman 		case TELOPT_BINARY:
41838905Sborman 			init_termbuf();
41938905Sborman 			tty_binaryin(1);
42038905Sborman 			set_termbuf();
42138905Sborman 			changeok++;
42238905Sborman 			break;
42338905Sborman 
42438905Sborman 		case TELOPT_ECHO:
42539503Sborman 			not42 = 0;	/* looks like a 4.2 system */
42639503Sborman #ifdef notdef
42738905Sborman 			/*
42839503Sborman 			 * Now, in a 4.2 system, to break them out of
42939503Sborman 			 * ECHOing (to the terminal) mode, we need to
43039503Sborman 			 * send a WILL ECHO.
43138905Sborman 			 */
43238905Sborman 			if (myopts[TELOPT_ECHO] == OPT_YES) {
43339503Sborman 				send_will(TELOPT_ECHO, 1);
43438905Sborman 			}
43539503Sborman #else
43638905Sborman 			/*
43739503Sborman 			 * "WILL ECHO".  Kludge upon kludge!
43839503Sborman 			 * A 4.2 client is now echoing user input at
43939503Sborman 			 * the tty.  This is probably undesireable and
44039503Sborman 			 * it should be stopped.  The client will
44139503Sborman 			 * respond WONT TM to the DO TM that we send to
44239503Sborman 			 * check for kludge linemode.  When the WONT TM
44339503Sborman 			 * arrives, linemode will be turned off and a
44439503Sborman 			 * change propogated to the pty.  This change
44539503Sborman 			 * will cause us to process the new pty state
44639503Sborman 			 * in localstat(), which will notice that
44739503Sborman 			 * linemode is off and send a WILL ECHO
44839503Sborman 			 * so that we are properly in character mode and
44939503Sborman 			 * all is well.
45039503Sborman 			 */
45139503Sborman #endif
45239503Sborman 			/*
45338905Sborman 			 * Fool the state machine into sending a don't.
45439503Sborman 			 * This also allows the initial echo sending
45539503Sborman 			 * code to break out of the loop that it is
45638905Sborman 			 * in.  (Look in telnet())
45738905Sborman 			 */
45838905Sborman 			hiswants[TELOPT_ECHO] = OPT_NO;
45938905Sborman 			break;
46038905Sborman 
46138905Sborman 		case TELOPT_TM:
46238905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
46338905Sborman 			/*
46439503Sborman 			 * This telnetd implementation does not really
46539503Sborman 			 * support timing marks, it just uses them to
46639503Sborman 			 * support the kludge linemode stuff.  If we
46739503Sborman 			 * receive a will or wont TM in response to our
46839503Sborman 			 * do TM request that may have been sent to
46939503Sborman 			 * determine kludge linemode support, process
47039503Sborman 			 * it, otherwise TM should get a negative
47139503Sborman 			 * response back.
47238905Sborman 			 */
47338905Sborman 			/*
47438905Sborman 			 * Handle the linemode kludge stuff.
47538905Sborman 			 * If we are not currently supporting any
47638905Sborman 			 * linemode at all, then we assume that this
47738905Sborman 			 * is the client telling us to use kludge
47838905Sborman 			 * linemode in response to our query.  Set the
47938905Sborman 			 * linemode type that is to be supported, note
48038905Sborman 			 * that the client wishes to use linemode, and
48138905Sborman 			 * eat the will TM as though it never arrived.
48238905Sborman 			 */
48338905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
48438905Sborman 				lmodetype = KLUDGE_LINEMODE;
48538905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
48639503Sborman 				send_wont(TELOPT_SGA, 1);
48738905Sborman 			}
48838905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
48938905Sborman 			/*
490*39531Sborman 			 * We never respond to a WILL TM, and
491*39531Sborman 			 * we leave the state OPT_NO.
49238905Sborman 			 */
49338905Sborman 			return;
49438905Sborman 
49538905Sborman 		case TELOPT_LFLOW:
49638905Sborman 			/*
49739503Sborman 			 * If we are going to support flow control
49839503Sborman 			 * option, then don't worry peer that we can't
49939503Sborman 			 * change the flow control characters.
50038905Sborman 			 */
50138905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
50238905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
50338905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
50438905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
50538905Sborman 		case TELOPT_TTYPE:
50638905Sborman 		case TELOPT_SGA:
50738905Sborman 		case TELOPT_NAWS:
50838905Sborman 		case TELOPT_TSPEED:
509*39531Sborman 			changeok++;
510*39531Sborman 			break;
511*39531Sborman 
51238905Sborman #ifdef	LINEMODE
51338905Sborman 		case TELOPT_LINEMODE:
514*39531Sborman # ifdef	KLUDGELINEMODE
515*39531Sborman 			/*
516*39531Sborman 			 * Note client's desire to use linemode.
517*39531Sborman 			 */
518*39531Sborman 			lmodetype = REAL_LINEMODE;
519*39531Sborman # endif	/* KLUDGELINEMODE */
520*39531Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
52138905Sborman 			changeok++;
52238905Sborman 			break;
523*39531Sborman #endif	/* LINEMODE */
52438905Sborman 
52538905Sborman 		default:
52638905Sborman 			break;
52738905Sborman 		}
52839503Sborman 		if (changeok) {
52938905Sborman 			hiswants[option] = OPT_YES;
53039503Sborman 			send_do(option, 0);
53139503Sborman 		} else {
53239503Sborman 			do_dont_resp[option]++;
53339503Sborman 			send_dont(option, 0);
53438905Sborman 		}
535*39531Sborman 	    }
53638905Sborman 	}
53739503Sborman 	hisopts[option] = OPT_YES;
53838905Sborman }  /* end of willoption */
53938905Sborman 
54039503Sborman send_dont(option, init)
54139503Sborman 	int option, init;
54238905Sborman {
54339503Sborman 	if (init) {
54439503Sborman 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) ||
54539503Sborman 		    hiswants[option] == OPT_NO)
54639503Sborman 			return;
54739503Sborman 		hiswants[option] = OPT_NO;
54839503Sborman 		do_dont_resp[option]++;
54939503Sborman 	}
55039503Sborman 	(void) sprintf(nfrontp, dont, option);
55139503Sborman 	nfrontp += sizeof (doopt) - 2;
55239503Sborman }
55339503Sborman 
55439503Sborman wontoption(option)
55539503Sborman 	int option;
55639503Sborman {
55738905Sborman 	char *fmt = (char *)0;
55838905Sborman 
55938905Sborman 	/*
56038905Sborman 	 * Process client input.
56138905Sborman 	 */
56239503Sborman 
56339503Sborman 	if (do_dont_resp[option]) {
56439503Sborman 		do_dont_resp[option]--;
56539503Sborman 		if (do_dont_resp[option] && hisopts[option] == OPT_NO)
56639503Sborman 			do_dont_resp[option]--;
56739503Sborman 	}
568*39531Sborman 	if (do_dont_resp[option] == 0) {
569*39531Sborman 	    if (hiswants[option] != OPT_NO) {
57039503Sborman 		/* it is always ok to change to negative state */
57138905Sborman 		switch (option) {
57238905Sborman 		case TELOPT_ECHO:
57339503Sborman 			not42 = 1; /* doesn't seem to be a 4.2 system */
57438905Sborman 			break;
57538905Sborman 
57638905Sborman 		case TELOPT_BINARY:
57738905Sborman 			init_termbuf();
57838905Sborman 			tty_binaryin(0);
57938905Sborman 			set_termbuf();
58038905Sborman 			break;
58138905Sborman 
58238905Sborman #ifdef	LINEMODE
58338905Sborman 		case TELOPT_LINEMODE:
58438905Sborman # ifdef	KLUDGELINEMODE
58538905Sborman 			/*
58638905Sborman 			 * If real linemode is supported, then client is
58738905Sborman 			 * asking to turn linemode off.
58838905Sborman 			 */
58938905Sborman 			if (lmodetype == REAL_LINEMODE)
59038905Sborman # endif	/* KLUDGELINEMODE */
59138905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
59238905Sborman 			break;
59338905Sborman #endif	LINEMODE
59438905Sborman 
59538905Sborman 		case TELOPT_TM:
59638905Sborman 			/*
59739503Sborman 			 * If we get a WONT TM, and had sent a DO TM,
59839503Sborman 			 * don't respond with a DONT TM, just leave it
59939503Sborman 			 * as is.  Short circut the state machine to
600*39531Sborman 			 * achive this.
60138905Sborman 			 */
60238905Sborman 			hiswants[TELOPT_TM] = OPT_NO;
60338905Sborman 			return;
60438905Sborman 
60538905Sborman 		case TELOPT_LFLOW:
60638905Sborman 			/*
60739503Sborman 			 * If we are not going to support flow control
60839503Sborman 			 * option, then let peer know that we can't
60939503Sborman 			 * change the flow control characters.
61038905Sborman 			 */
61138905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
61238905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
61338905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
61438905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
61538905Sborman 			break;
61638905Sborman 
61738905Sborman 		default:
61838905Sborman 			break;
61938905Sborman 		}
62039503Sborman 		hiswants[option] = OPT_NO;
62139503Sborman 		fmt = dont;
62239503Sborman 		send_dont(option, 0);
623*39531Sborman 	    } else {
624*39531Sborman 		switch (option) {
625*39531Sborman 		case TELOPT_TM:
626*39531Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
627*39531Sborman 			if (lmodetype < REAL_LINEMODE) {
628*39531Sborman 				lmodetype = NO_LINEMODE;
629*39531Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
630*39531Sborman 				send_will(TELOPT_SGA, 1);
631*39531Sborman /*@*/				send_will(TELOPT_ECHO, 1);
632*39531Sborman 			}
633*39531Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
634*39531Sborman 		default:
635*39531Sborman 			break;
636*39531Sborman 		}
637*39531Sborman 	    }
63838905Sborman 	}
63939503Sborman 	hisopts[option] = OPT_NO;
64038905Sborman 
64139503Sborman }  /* end of wontoption */
64238905Sborman 
64339503Sborman send_will(option, init)
64439503Sborman 	int option, init;
64539503Sborman {
64639503Sborman 	if (init) {
64739503Sborman 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)||
64839503Sborman 		    mywants[option] == OPT_YES)
64939503Sborman 			return;
65039503Sborman 		mywants[option] = OPT_YES;
65139503Sborman 		will_wont_resp[option]++;
65238905Sborman 	}
65339503Sborman 	(void) sprintf(nfrontp, will, option);
65439503Sborman 	nfrontp += sizeof (doopt) - 2;
65539503Sborman }
65638905Sborman 
65739503Sborman dooption(option)
65839503Sborman 	int option;
65938905Sborman {
66038905Sborman 	int changeok = 0;
66138905Sborman 
66238905Sborman 	/*
66338905Sborman 	 * Process client input.
66438905Sborman 	 */
66539503Sborman 
66639503Sborman 	if (will_wont_resp[option]) {
66739503Sborman 		will_wont_resp[option]--;
66839503Sborman 		if (will_wont_resp[option] && myopts[option] == OPT_YES)
66939503Sborman 			will_wont_resp[option]--;
67039503Sborman 	}
67139503Sborman 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) {
67238905Sborman 		switch (option) {
67338905Sborman 		case TELOPT_ECHO:
67438905Sborman #ifdef	LINEMODE
67539503Sborman 			if (lmodetype == NO_LINEMODE) {
67638905Sborman #endif
67738905Sborman 				init_termbuf();
67838905Sborman 				tty_setecho(1);
67938905Sborman 				set_termbuf();
68038905Sborman #ifdef	LINEMODE
68138905Sborman 			}
68238905Sborman #endif
68338905Sborman 			changeok++;
68438905Sborman 			break;
68538905Sborman 
68638905Sborman 		case TELOPT_BINARY:
68738905Sborman 			init_termbuf();
68838905Sborman 			tty_binaryout(1);
68938905Sborman 			set_termbuf();
69038905Sborman 			changeok++;
69138905Sborman 			break;
69238905Sborman 
69338905Sborman 		case TELOPT_SGA:
69438905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
69538905Sborman 			/*
69639503Sborman 			 * If kludge linemode is in use, then we must
69739503Sborman 			 * process an incoming do SGA for linemode
69839503Sborman 			 * purposes.
69938905Sborman 			 */
70038905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
70138905Sborman 				/*
70239503Sborman 				 * Receipt of "do SGA" in kludge
70339503Sborman 				 * linemode is the peer asking us to
70439503Sborman 				 * turn off linemode.  Make note of
70539503Sborman 				 * the request.
70638905Sborman 				 */
70738905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
70838905Sborman 				/*
70939503Sborman 				 * If linemode did not get turned off
71039503Sborman 				 * then don't tell peer that we did.
71139503Sborman 				 * Breaking here forces a wont SGA to
71239503Sborman 				 * be returned.
71338905Sborman 				 */
71438905Sborman 				if (linemode)
71538905Sborman 					break;
71638905Sborman 			}
71738905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
71838905Sborman 			changeok++;
71938905Sborman 			break;
72038905Sborman 
72138905Sborman 		case TELOPT_STATUS:
72238905Sborman 			changeok++;
72338905Sborman 			break;
72438905Sborman 
72538905Sborman 		case TELOPT_TM:
72639503Sborman 			/*
72739503Sborman 			 * Special case for TM.  We send a WILL, but
72839503Sborman 			 * pretend we sent a WONT.
72939503Sborman 			 */
73039503Sborman 			send_will(option, 0);
73139503Sborman 			mywants[option] = OPT_NO;
73239503Sborman 			myopts[option] = OPT_NO;
73339503Sborman 			return;
73439503Sborman 
73538905Sborman 		case TELOPT_LINEMODE:
73638905Sborman 		case TELOPT_TTYPE:
73738905Sborman 		case TELOPT_NAWS:
73838905Sborman 		case TELOPT_TSPEED:
73938905Sborman 		case TELOPT_LFLOW:
74038905Sborman 		default:
74138905Sborman 			break;
74238905Sborman 		}
74339503Sborman 		if (changeok) {
74438905Sborman 			mywants[option] = OPT_YES;
74539503Sborman 			send_will(option, 0);
74639503Sborman 		} else {
74739503Sborman 			will_wont_resp[option]++;
74839503Sborman 			send_wont(option, 0);
74938905Sborman 		}
75038905Sborman 	}
75139503Sborman 	myopts[option] = OPT_YES;
75238905Sborman 
75338905Sborman }  /* end of dooption */
75438905Sborman 
75539503Sborman send_wont(option, init)
75639503Sborman 	int option, init;
75739503Sborman {
75839503Sborman 	if (init) {
75939503Sborman 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) ||
76039503Sborman 		    mywants[option] == OPT_NO)
76139503Sborman 			return;
76239503Sborman 		mywants[option] = OPT_NO;
76339503Sborman 		will_wont_resp[option]++;
76439503Sborman 	}
76539503Sborman 	(void) sprintf(nfrontp, wont, option);
76639503Sborman 	nfrontp += sizeof (wont) - 2;
76739503Sborman }
76838905Sborman 
76939503Sborman dontoption(option)
77039503Sborman 	int option;
77138905Sborman {
77238905Sborman 	/*
77338905Sborman 	 * Process client input.
77438905Sborman 	 */
77539503Sborman 	if (will_wont_resp[option]) {
77639503Sborman 		will_wont_resp[option]--;
77739503Sborman 		if (will_wont_resp[option] && myopts[option] == OPT_NO)
77839503Sborman 			will_wont_resp[option]--;
77939503Sborman 	}
78039503Sborman 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) {
78138905Sborman 		switch (option) {
78238905Sborman 		case TELOPT_BINARY:
78338905Sborman 			init_termbuf();
78438905Sborman 			tty_binaryout(0);
78538905Sborman 			set_termbuf();
78638905Sborman 			break;
78738905Sborman 
78839503Sborman 		case TELOPT_ECHO:	/* we should stop echoing */
78938905Sborman #ifdef	LINEMODE
79039503Sborman 			if (lmodetype == NO_LINEMODE) {
79138905Sborman #endif
79238905Sborman 				init_termbuf();
79338905Sborman 				tty_setecho(0);
79438905Sborman 				set_termbuf();
79538905Sborman #ifdef	LINEMODE
79638905Sborman 			}
79738905Sborman #endif
79838905Sborman 			break;
79938905Sborman 
80038905Sborman 		case TELOPT_SGA:
80138905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
80238905Sborman 			/*
80339503Sborman 			 * If kludge linemode is in use, then we
80439503Sborman 			 * must process an incoming do SGA for
80539503Sborman 			 * linemode purposes.
80638905Sborman 			 */
80738905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
80838905Sborman 				/*
80939503Sborman 				 * The client is asking us to turn
81039503Sborman 				 * linemode on.
81138905Sborman 				 */
81238905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
81338905Sborman 				/*
81439503Sborman 				 * If we did not turn line mode on,
81539503Sborman 				 * then what do we say?  Will SGA?
81639503Sborman 				 * This violates design of telnet.
81739503Sborman 				 * Gross.  Very Gross.
81838905Sborman 				 */
81938905Sborman 			}
82038905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
82138905Sborman 
82238905Sborman 		default:
82338905Sborman 			break;
82438905Sborman 		}
82538905Sborman 
82639503Sborman 		mywants[option] = OPT_NO;
82739503Sborman 		send_wont(option, 0);
82838905Sborman 	}
82939503Sborman 	myopts[option] = OPT_NO;
83038905Sborman 
83138905Sborman }  /* end of dontoption */
83238905Sborman 
83338905Sborman /*
83438905Sborman  * suboption()
83538905Sborman  *
83638905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
83738905Sborman  * side.
83838905Sborman  *
83938905Sborman  *	Currently we recognize:
84038905Sborman  *
84138905Sborman  *	Terminal type is
84238905Sborman  *	Linemode
84338905Sborman  *	Window size
84438905Sborman  *	Terminal speed
84538905Sborman  */
84638905Sborman suboption()
84738905Sborman {
84838905Sborman     register int subchar;
84938905Sborman 
85038905Sborman     subchar = SB_GET();
85138905Sborman     switch (subchar) {
85238905Sborman     case TELOPT_TSPEED: {
85338905Sborman 	register int xspeed, rspeed;
85438905Sborman 
85538905Sborman 	if (hisopts[TELOPT_TSPEED] == OPT_NO)	/* Ignore if option disabled */
85638905Sborman 		break;
85738905Sborman 
85838905Sborman 	settimer(tspeedsubopt);
85938905Sborman 
86038905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
86138905Sborman 		return;
86238905Sborman 
86338905Sborman 	xspeed = atoi(subpointer);
86438905Sborman 
86538905Sborman 	while (SB_GET() != ',' && !SB_EOF());
86638905Sborman 	if (SB_EOF())
86738905Sborman 		return;
86838905Sborman 
86938905Sborman 	rspeed = atoi(subpointer);
87038905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
87138905Sborman 
87238905Sborman 	break;
87338905Sborman 
87438905Sborman     }  /* end of case TELOPT_TSPEED */
87538905Sborman 
87638905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
87738905Sborman 	static char terminalname[5+41] = "TERM=";
87838905Sborman 
87938997Sborman 	if (hisopts[TELOPT_TTYPE] == OPT_NO)	/* Ignore if option disabled */
88038905Sborman 		break;
88138905Sborman 	settimer(ttypesubopt);
88238905Sborman 
88338905Sborman 	if (SB_GET() != TELQUAL_IS) {
88438905Sborman 	    return;		/* ??? XXX but, this is the most robust */
88538905Sborman 	}
88638905Sborman 
88738905Sborman 	terminaltype = terminalname+sizeof("TERM=")-1;
88838905Sborman 
88938905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
89038905Sborman 								    !SB_EOF()) {
89138905Sborman 	    register int c;
89238905Sborman 
89338905Sborman 	    c = SB_GET();
89438905Sborman 	    if (isupper(c)) {
89538905Sborman 		c = tolower(c);
89638905Sborman 	    }
89738905Sborman 	    *terminaltype++ = c;    /* accumulate name */
89838905Sborman 	}
89938905Sborman 	*terminaltype = 0;
90038905Sborman 	terminaltype = terminalname;
90138905Sborman 	break;
90238905Sborman     }  /* end of case TELOPT_TTYPE */
90338905Sborman 
90438905Sborman     case TELOPT_NAWS: {
90538905Sborman 	register int xwinsize, ywinsize;
90638905Sborman 
90738905Sborman 	if (hisopts[TELOPT_NAWS] == OPT_NO)	/* Ignore if option disabled */
90838905Sborman 		break;
90938905Sborman 
91038905Sborman 	if (SB_EOF())
91138905Sborman 		return;
91238905Sborman 	xwinsize = SB_GET() << 8;
91338905Sborman 	if (SB_EOF())
91438905Sborman 		return;
91538905Sborman 	xwinsize |= SB_GET();
91638905Sborman 	if (SB_EOF())
91738905Sborman 		return;
91838905Sborman 	ywinsize = SB_GET() << 8;
91938905Sborman 	if (SB_EOF())
92038905Sborman 		return;
92138905Sborman 	ywinsize |= SB_GET();
92238905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
92338905Sborman 
92438905Sborman 	break;
92538905Sborman 
92638905Sborman     }  /* end of case TELOPT_NAWS */
92738905Sborman 
92838905Sborman #ifdef	LINEMODE
92938905Sborman     case TELOPT_LINEMODE: {
93038905Sborman 	register int request;
93138905Sborman 
93238905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO)	/* Ignore if option disabled */
93338905Sborman 		break;
93438905Sborman 	/*
93538905Sborman 	 * Process linemode suboptions.
93638905Sborman 	 */
93738905Sborman 	if (SB_EOF()) break;  /* garbage was sent */
93838905Sborman 	request = SB_GET();  /* get will/wont */
93938905Sborman 	if (SB_EOF()) break;  /* another garbage check */
94038905Sborman 
94138905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
94238905Sborman 		/*
94338905Sborman 		 * Process suboption buffer of slc's
94438905Sborman 		 */
94538905Sborman 		start_slc(1);
94638905Sborman 		do_opt_slc(subpointer, subend - subpointer);
94738905Sborman 		end_slc(0);
94838905Sborman 
94938905Sborman 	} else if (request == LM_MODE) {
95038905Sborman 		useeditmode = SB_GET();  /* get mode flag */
95138905Sborman 		clientstat(LM_MODE, 0, 0);
95238905Sborman 	}
95338905Sborman 
95438905Sborman 	switch (SB_GET()) {  /* what suboption? */
95538905Sborman 	case LM_FORWARDMASK:
95638905Sborman 		/*
95738905Sborman 		 * According to spec, only server can send request for
95838905Sborman 		 * forwardmask, and client can only return a positive response.
95938905Sborman 		 * So don't worry about it.
96038905Sborman 		 */
96138905Sborman 
96238905Sborman 	default:
96338905Sborman 		break;
96438905Sborman 	}
96538905Sborman 
96638905Sborman     }  /* end of case TELOPT_LINEMODE */
96738905Sborman #endif
96838905Sborman     case TELOPT_STATUS: {
96938905Sborman 	int mode;
97038905Sborman 
97138905Sborman 	mode = SB_GET();
97238905Sborman 	switch (mode) {
97338905Sborman 	case TELQUAL_SEND:
97438905Sborman 	    if (myopts[TELOPT_STATUS] == OPT_YES)
97538905Sborman 		send_status();
97638905Sborman 	    break;
97738905Sborman 
97838905Sborman 	case TELQUAL_IS:
97938905Sborman 	    break;
98038905Sborman 
98138905Sborman 	default:
98238905Sborman 	    break;
98338905Sborman 	}
98438905Sborman     }
98538905Sborman 
98638905Sborman     default:
98738905Sborman 	break;
98838905Sborman     }  /* end of switch */
98938905Sborman 
99038905Sborman }  /* end of suboption */
99138905Sborman 
99238905Sborman #define	ADD(c)	 *ncp++ = c;
99338905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
99438905Sborman send_status()
99538905Sborman {
99638905Sborman 	char statusbuf[256];
99738905Sborman 	register char *ncp;
99838905Sborman 	register int i;
99938905Sborman 
100038905Sborman 	ncp = statusbuf;
100138905Sborman 
100238905Sborman 	netflush();	/* get rid of anything waiting to go out */
100338905Sborman 
100438905Sborman 	ADD(IAC);
100538905Sborman 	ADD(SB);
100638905Sborman 	ADD(TELOPT_STATUS);
100738905Sborman 	ADD(TELQUAL_IS);
100838905Sborman 
100938905Sborman 	for (i = 0; i < NTELOPTS; i++) {
101038905Sborman 		if (myopts[i] == OPT_YES) {
101138905Sborman 			ADD(WILL);
101238905Sborman 			ADD_DATA(i);
101338905Sborman 			if (i == IAC)
101438905Sborman 				ADD(IAC);
101538905Sborman 		}
101638905Sborman 		if (hisopts[i] == OPT_YES) {
101738905Sborman 			ADD(DO);
101838905Sborman 			ADD_DATA(i);
101938905Sborman 			if (i == IAC)
102038905Sborman 				ADD(IAC);
102138905Sborman 		}
102238905Sborman 	}
102338905Sborman 
102438905Sborman #ifdef	LINEMODE
102538905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
102638905Sborman 		char *cp, *cpe;
102738905Sborman 		int len;
102838905Sborman 
102938905Sborman 		ADD(SB);
103038905Sborman 		ADD(TELOPT_LINEMODE);
103138905Sborman 		ADD(LM_MODE);
103238905Sborman 		ADD_DATA(editmode);
103338905Sborman 		if (editmode == IAC)
103438905Sborman 			ADD(IAC);
103538905Sborman 		ADD(SE);
103638905Sborman 
103738905Sborman 		ADD(SB);
103838905Sborman 		ADD(TELOPT_LINEMODE);
103938905Sborman 		ADD(LM_SLC);
104038905Sborman 		start_slc(0);
104138905Sborman 		send_slc();
104238905Sborman 		len = end_slc(&cp);
104338905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
104438905Sborman 			ADD_DATA(*cp);
104538905Sborman 		ADD(SE);
104638905Sborman 	}
104738905Sborman #endif	/* LINEMODE */
104838905Sborman 
104938905Sborman 	ADD(IAC);
105038905Sborman 	ADD(SE);
105138905Sborman 
105238905Sborman 	writenet(statusbuf, ncp - statusbuf);
105338905Sborman 	netflush();	/* Send it on its way */
105438905Sborman }
1055