xref: /csrg-svn/libexec/telnetd/state.c (revision 38997)
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*38997Sborman static char sccsid[] = "@(#)state.c	5.2 (Berkeley) 09/05/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:
26838905Sborman 			willoption(c, 0);
26938905Sborman 			state = TS_DATA;
27038905Sborman 			continue;
27138905Sborman 
27238905Sborman 		case TS_WONT:
27338905Sborman 			wontoption(c, 0);
27438905Sborman 			state = TS_DATA;
27538905Sborman 			continue;
27638905Sborman 
27738905Sborman 		case TS_DO:
27838905Sborman 			dooption(c, 0);
27938905Sborman 			state = TS_DATA;
28038905Sborman 			continue;
28138905Sborman 
28238905Sborman 		case TS_DONT:
28338905Sborman 			dontoption(c, 0);
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  */
37738905Sborman willoption(option, request)
37838905Sborman 	int option, request;
37938905Sborman {
38038905Sborman 	int changeok = 0;
38138905Sborman 	char *fmt = (char *)0;
38238905Sborman 
38338905Sborman     /*
38438905Sborman      * process input from peer.
38538905Sborman      */
38638905Sborman     if (request == 0) {
38738905Sborman 		switch (option) {
38838905Sborman 
38938905Sborman 		case TELOPT_BINARY:
39038905Sborman 			init_termbuf();
39138905Sborman 			tty_binaryin(1);
39238905Sborman 			set_termbuf();
39338905Sborman 			changeok++;
39438905Sborman 			break;
39538905Sborman 
39638905Sborman 		case TELOPT_ECHO:
39738905Sborman 			not42 = 0;		/* looks like a 4.2 system */
39838905Sborman 			/*
39938905Sborman 			 * Now, in a 4.2 system, to break them out of ECHOing
40038905Sborman 			 * (to the terminal) mode, we need to send a
40138905Sborman 			 * "WILL ECHO".  Kludge upon kludge!
40238905Sborman 			 */
40338905Sborman 			if (myopts[TELOPT_ECHO] == OPT_YES) {
40438905Sborman 				dooption(TELOPT_ECHO, 1);
40538905Sborman 			}
40638905Sborman 			/*
40738905Sborman 			 * Fool the state machine into sending a don't.
40838905Sborman 			 * This also allows the initial echo sending code to
40938905Sborman 			 * break out of the loop that it is
41038905Sborman 			 * in.  (Look in telnet())
41138905Sborman 			 */
41238905Sborman 			hiswants[TELOPT_ECHO] = OPT_NO;
41338905Sborman 			break;
41438905Sborman 
41538905Sborman 		case TELOPT_TM:
41638905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
41738905Sborman 			/*
41838905Sborman 		 	 * This telnetd implementation does not really support
41938905Sborman 			 * timing marks, it just uses them to support the kludge
42038905Sborman 			 * linemode stuff.  If we receive a will or wont TM in
42138905Sborman 			 * response to our do TM request that may have been sent
42238905Sborman 			 * to determine kludge linemode support, process it,
42338905Sborman 			 * otherwise TM should get a negative response back.
42438905Sborman 			 */
42538905Sborman 			/*
42638905Sborman 			 * Handle the linemode kludge stuff.
42738905Sborman 			 * If we are not currently supporting any
42838905Sborman 			 * linemode at all, then we assume that this
42938905Sborman 			 * is the client telling us to use kludge
43038905Sborman 			 * linemode in response to our query.  Set the
43138905Sborman 			 * linemode type that is to be supported, note
43238905Sborman 			 * that the client wishes to use linemode, and
43338905Sborman 			 * eat the will TM as though it never arrived.
43438905Sborman 			 */
43538905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
43638905Sborman 				lmodetype = KLUDGE_LINEMODE;
43738905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
43838905Sborman 				dontoption(TELOPT_SGA, 0);
43938905Sborman 			}
44038905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
44138905Sborman 			/*
44238905Sborman 			 * cheat the state machine so that it
44338905Sborman 			 * looks like we never sent the TM at
44438905Sborman 			 * all.  The bad part of this is that
44538905Sborman 			 * if the client sends a will TM on his
44638905Sborman 			 * own to turn on linemode, then he
44738905Sborman 			 * won't get a response.
44838905Sborman 			 */
44938905Sborman 			hiswants[TELOPT_TM] = OPT_NO;
45038905Sborman 			resp[TELOPT_TM]--;
45138905Sborman 			return;
45238905Sborman 
45338905Sborman 		case TELOPT_LFLOW:
45438905Sborman 			/*
45538905Sborman 			 * If we are going to support flow control option,
45638905Sborman 			 * then don't worry peer that we can't change the
45738905Sborman 			 * flow control characters.
45838905Sborman 			 */
45938905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
46038905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
46138905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
46238905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
46338905Sborman 		case TELOPT_TTYPE:
46438905Sborman 		case TELOPT_SGA:
46538905Sborman 		case TELOPT_NAWS:
46638905Sborman 		case TELOPT_TSPEED:
46738905Sborman #ifdef	LINEMODE
46838905Sborman 		case TELOPT_LINEMODE:
46938905Sborman #endif	LINEMODE
47038905Sborman 			changeok++;
47138905Sborman 			break;
47238905Sborman 
47338905Sborman 		default:
47438905Sborman 			break;
47538905Sborman 		}
47638905Sborman 
47738905Sborman 	}
47838905Sborman 
47938905Sborman 	if (request) {
48038905Sborman 		if (!((resp[option] == 0 && hisopts[option] == OPT_YES) ||
48138905Sborman 		    hiswants[option] == OPT_YES)) {
48238905Sborman 			hiswants[option] = OPT_YES;
48338905Sborman 			fmt = doopt;
48438905Sborman 			resp[option]++;
48538905Sborman 		}
48638905Sborman 	} else {
48738905Sborman 		if (resp[option]) {
48838905Sborman 			resp[option]--;
48938905Sborman 			if (resp[option] && hisopts[option] == OPT_YES)
49038905Sborman 				resp[option]--;
49138905Sborman 		}
49238905Sborman 		if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) {
49338905Sborman 			if (changeok)
49438905Sborman 				hiswants[option] = OPT_YES;
49538905Sborman 			else
49638905Sborman 				resp[option]++;
49738905Sborman 			fmt = (hiswants[option] ? doopt : dont);
49838905Sborman 		}
49938905Sborman 		hisopts[option] = OPT_YES;
50038905Sborman 	}
50138905Sborman 
50238905Sborman 	if (fmt) {
50338905Sborman 		(void) sprintf(nfrontp, fmt, option);
50438905Sborman 		nfrontp += sizeof (dont) - 2;
50538905Sborman 	}
50638905Sborman 
50738905Sborman 	/*
50838905Sborman 	 * Handle other processing that should occur after we have
50938905Sborman 	 * responded to client input.
51038905Sborman 	 */
51138905Sborman 	if (!request) {
51238905Sborman 		switch (option) {
51338905Sborman #ifdef	LINEMODE
51438905Sborman 		case TELOPT_LINEMODE:
51538905Sborman # ifdef	KLUDGELINEMODE
51638905Sborman 			/*
51738905Sborman 			 * Note client's desire to use linemode.
51838905Sborman 			 */
51938905Sborman 			lmodetype = REAL_LINEMODE;
52038905Sborman # endif	/* KLUDGELINEMODE */
52138905Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
52238905Sborman 			break;
52338905Sborman #endif	LINEMODE
52438905Sborman 
52538905Sborman 		default:
52638905Sborman 			break;
52738905Sborman 		}
52838905Sborman 	}
52938905Sborman 
53038905Sborman }  /* end of willoption */
53138905Sborman 
53238905Sborman wontoption(option, request)
53338905Sborman 	int option, request;
53438905Sborman {
53538905Sborman 	char *fmt = (char *)0;
53638905Sborman 
53738905Sborman 	/*
53838905Sborman 	 * Process client input.
53938905Sborman 	 */
54038905Sborman 	if (!request) {
54138905Sborman 		switch (option) {
54238905Sborman 		case TELOPT_ECHO:
54338905Sborman 			not42 = 1;	/* doesn't seem to be a 4.2 system */
54438905Sborman 			break;
54538905Sborman 
54638905Sborman 		case TELOPT_BINARY:
54738905Sborman 			init_termbuf();
54838905Sborman 			tty_binaryin(0);
54938905Sborman 			set_termbuf();
55038905Sborman 			break;
55138905Sborman 
55238905Sborman #ifdef	LINEMODE
55338905Sborman 		case TELOPT_LINEMODE:
55438905Sborman # ifdef	KLUDGELINEMODE
55538905Sborman 			/*
55638905Sborman 			 * If real linemode is supported, then client is
55738905Sborman 			 * asking to turn linemode off.
55838905Sborman 			 */
55938905Sborman 			if (lmodetype == REAL_LINEMODE)
56038905Sborman # endif	/* KLUDGELINEMODE */
56138905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
56238905Sborman 			break;
56338905Sborman #endif	LINEMODE
56438905Sborman 
56538905Sborman 		case TELOPT_TM:
56638905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
56738905Sborman 			if (lmodetype < REAL_LINEMODE) {
56838905Sborman 				lmodetype = NO_LINEMODE;
56938905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
57038905Sborman 				dooption(TELOPT_SGA, 0);
57138905Sborman 			}
57238905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
57338905Sborman 			/*
57438905Sborman 			 * If we get a WONT TM, and had sent a DO TM, don't
57538905Sborman 			 * respond with a DONT TM, just leave it as is.
57638905Sborman 			 * Short circut the state machine to achive this.
57738905Sborman 			 * The bad part of this is that if the client sends
57838905Sborman 			 * a WONT TM on his own to turn off linemode, then he
57938905Sborman 			 * won't get a response.
58038905Sborman 			 */
58138905Sborman 			hiswants[TELOPT_TM] = OPT_NO;
58238905Sborman 			resp[TELOPT_TM]--;
58338905Sborman 			return;
58438905Sborman 
58538905Sborman 		case TELOPT_LFLOW:
58638905Sborman 			/*
58738905Sborman 			 * If we are not going to support flow control option,
58838905Sborman 			 * then let peer know that we can't change the
58938905Sborman 			 * flow control characters.
59038905Sborman 			 */
59138905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
59238905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
59338905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
59438905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
59538905Sborman 			break;
59638905Sborman 
59738905Sborman 		default:
59838905Sborman 			break;
59938905Sborman 		}
60038905Sborman 	}
60138905Sborman 
60238905Sborman 
60338905Sborman 	if (request) {
60438905Sborman 		if (!((resp[option] == 0 && hisopts[option] == OPT_NO) ||
60538905Sborman 			hiswants[option] == OPT_NO)) {
60638905Sborman 			hiswants[option] = OPT_NO;
60738905Sborman 			fmt = dont;
60838905Sborman 			resp[option]++;
60938905Sborman 		}
61038905Sborman 	} else {
61138905Sborman 		if (resp[option]) {
61238905Sborman 			resp[option]--;
61338905Sborman 			if (resp[option] && hisopts[option] == OPT_NO)
61438905Sborman 				resp[option]--;
61538905Sborman 		}
61638905Sborman 		if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) {
61738905Sborman 			/* it is always ok to change to negative state */
61838905Sborman 			hiswants[option] = OPT_NO;
61938905Sborman 			fmt = dont;
62038905Sborman 		}
62138905Sborman 		hisopts[option] = OPT_NO;
62238905Sborman 	}
62338905Sborman 
62438905Sborman 	if (fmt) {
62538905Sborman 		(void) sprintf(nfrontp, fmt, option);
62638905Sborman 		nfrontp += sizeof (doopt) - 2;
62738905Sborman 	}
62838905Sborman 
62938905Sborman }  /* end of wontoption */
63038905Sborman 
63138905Sborman dooption(option, request)
63238905Sborman 	int option, request;
63338905Sborman {
63438905Sborman 	int changeok = 0;
63538905Sborman 	char *fmt = (char *)0;
63638905Sborman 
63738905Sborman 	/*
63838905Sborman 	 * Process client input.
63938905Sborman 	 */
64038905Sborman 	if (!request) {
64138905Sborman 		switch (option) {
64238905Sborman 		case TELOPT_ECHO:
64338905Sborman #ifdef	LINEMODE
64438905Sborman 			if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
64538905Sborman #endif
64638905Sborman 				init_termbuf();
64738905Sborman 				tty_setecho(1);
64838905Sborman 				set_termbuf();
64938905Sborman #ifdef	LINEMODE
65038905Sborman 			}
65138905Sborman #endif
65238905Sborman 			changeok++;
65338905Sborman 			break;
65438905Sborman 
65538905Sborman 		case TELOPT_BINARY:
65638905Sborman 			init_termbuf();
65738905Sborman 			tty_binaryout(1);
65838905Sborman 			set_termbuf();
65938905Sborman 			changeok++;
66038905Sborman 			break;
66138905Sborman 
66238905Sborman 		case TELOPT_SGA:
66338905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
66438905Sborman 			/*
66538905Sborman 			 * If kludge linemode is in use, then we must process
66638905Sborman 			 * an incoming do SGA for linemode purposes.
66738905Sborman 			 */
66838905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
66938905Sborman 				/*
67038905Sborman 				 * Receipt of "do SGA" in kludge linemode
67138905Sborman 				 * is the peer asking us to turn off linemode.
67238905Sborman 				 * Make note of the request.
67338905Sborman 				 */
67438905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
67538905Sborman 				/*
67638905Sborman 				 * If linemode did not get turned off then
67738905Sborman 				 * don't tell peer that we did.  Breaking
67838905Sborman 				 * here forces a wont SGA to be returned.
67938905Sborman 				 */
68038905Sborman 				if (linemode)
68138905Sborman 					break;
68238905Sborman 			}
68338905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
68438905Sborman 			changeok++;
68538905Sborman 			break;
68638905Sborman 
68738905Sborman 		case TELOPT_STATUS:
68838905Sborman 			changeok++;
68938905Sborman 			break;
69038905Sborman 
69138905Sborman 		case TELOPT_TM:
69238905Sborman 		case TELOPT_LINEMODE:
69338905Sborman 		case TELOPT_TTYPE:
69438905Sborman 		case TELOPT_NAWS:
69538905Sborman 		case TELOPT_TSPEED:
69638905Sborman 		case TELOPT_LFLOW:
69738905Sborman 		default:
69838905Sborman 			break;
69938905Sborman 		}
70038905Sborman 	}
70138905Sborman 
70238905Sborman 	if (request) {
70338905Sborman 		if (!((resp[option] == 0 && myopts[option] == OPT_YES) ||
70438905Sborman 		    mywants[option] == OPT_YES)) {
70538905Sborman 			mywants[option] = OPT_YES;
70638905Sborman 			fmt = will;
70738905Sborman 			resp[option]++;
70838905Sborman 		}
70938905Sborman 	} else {
71038905Sborman 		if (resp[option]) {
71138905Sborman 			resp[option]--;
71238905Sborman 			if (resp[option] && myopts[option] == OPT_YES)
71338905Sborman 				resp[option]--;
71438905Sborman 		}
71538905Sborman 		if ((resp[option] == 0) && (mywants[option] != OPT_YES)) {
71638905Sborman 			if (changeok)
71738905Sborman 				mywants[option] = OPT_YES;
71838905Sborman 			else
71938905Sborman 				resp[option]++;
72038905Sborman 			fmt = (mywants[option] ? will : wont);
72138905Sborman 		}
72238905Sborman 		myopts[option] = OPT_YES;
72338905Sborman 	}
72438905Sborman 
72538905Sborman 	if (fmt) {
72638905Sborman 		(void) sprintf(nfrontp, fmt, option);
72738905Sborman 		nfrontp += sizeof (doopt) - 2;
72838905Sborman 	}
72938905Sborman 
73038905Sborman }  /* end of dooption */
73138905Sborman 
73238905Sborman 
73338905Sborman dontoption(option, request)
73438905Sborman 	int option, request;
73538905Sborman {
73638905Sborman 	char *fmt = (char *)0;
73738905Sborman 
73838905Sborman 	/*
73938905Sborman 	 * Process client input.
74038905Sborman 	 */
74138905Sborman 	if (!request) {
74238905Sborman 		switch (option) {
74338905Sborman 		case TELOPT_BINARY:
74438905Sborman 			init_termbuf();
74538905Sborman 			tty_binaryout(0);
74638905Sborman 			set_termbuf();
74738905Sborman 			break;
74838905Sborman 
74938905Sborman 		case TELOPT_ECHO:		/* we should stop echoing */
75038905Sborman #ifdef	LINEMODE
75138905Sborman 			if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
75238905Sborman #endif
75338905Sborman 				init_termbuf();
75438905Sborman 				tty_setecho(0);
75538905Sborman 				set_termbuf();
75638905Sborman #ifdef	LINEMODE
75738905Sborman 			}
75838905Sborman #endif
75938905Sborman 			break;
76038905Sborman 
76138905Sborman 		case TELOPT_SGA:
76238905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
76338905Sborman 			/*
76438905Sborman 			 * If kludge linemode is in use, then we must process an
76538905Sborman 			 * incoming do SGA for linemode purposes.
76638905Sborman 			 */
76738905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
76838905Sborman 				/*
76938905Sborman 				 * The client is asking us to turn linemode
77038905Sborman 				 * on.
77138905Sborman 				 */
77238905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
77338905Sborman 				/*
77438905Sborman 				 * If we did not turn line mode on, then what do
77538905Sborman 				 * we say?  Will SGA?  This violates design of
77638905Sborman 				 * telnet.  Gross.  Very Gross.
77738905Sborman 				 */
77838905Sborman 			}
77938905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
78038905Sborman 
78138905Sborman 		default:
78238905Sborman 			break;
78338905Sborman 		}
78438905Sborman 	}
78538905Sborman 
78638905Sborman 	if (request) {
78738905Sborman 		if (!((resp[option] == 0 && myopts[option] == OPT_NO) ||
78838905Sborman 		    mywants[option] == OPT_NO)) {
78938905Sborman 			mywants[option] = OPT_NO;
79038905Sborman 			fmt = wont;
79138905Sborman 			resp[option]++;
79238905Sborman 		}
79338905Sborman 	} else {
79438905Sborman 		if (resp[option]) {
79538905Sborman 			resp[option]--;
79638905Sborman 			if (resp[option] && myopts[option] == OPT_NO)
79738905Sborman 				resp[option]--;
79838905Sborman 		}
79938905Sborman 		if ((resp[option] == 0) && (mywants[option] != OPT_NO)) {
80038905Sborman 			mywants[option] = OPT_NO;
80138905Sborman 			fmt = wont;
80238905Sborman 		}
80338905Sborman 		myopts[option] = OPT_NO;
80438905Sborman 	}
80538905Sborman 
80638905Sborman 	if (fmt) {
80738905Sborman 	    (void) sprintf(nfrontp, fmt, option);
80838905Sborman 	    nfrontp += sizeof (wont) - 2;
80938905Sborman 	}
81038905Sborman 
81138905Sborman }  /* end of dontoption */
81238905Sborman 
81338905Sborman /*
81438905Sborman  * suboption()
81538905Sborman  *
81638905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
81738905Sborman  * side.
81838905Sborman  *
81938905Sborman  *	Currently we recognize:
82038905Sborman  *
82138905Sborman  *	Terminal type is
82238905Sborman  *	Linemode
82338905Sborman  *	Window size
82438905Sborman  *	Terminal speed
82538905Sborman  */
82638905Sborman suboption()
82738905Sborman {
82838905Sborman     register int subchar;
82938905Sborman 
83038905Sborman     subchar = SB_GET();
83138905Sborman     switch (subchar) {
83238905Sborman     case TELOPT_TSPEED: {
83338905Sborman 	register int xspeed, rspeed;
83438905Sborman 
83538905Sborman 	if (hisopts[TELOPT_TSPEED] == OPT_NO)	/* Ignore if option disabled */
83638905Sborman 		break;
83738905Sborman 
83838905Sborman 	settimer(tspeedsubopt);
83938905Sborman 
84038905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
84138905Sborman 		return;
84238905Sborman 
84338905Sborman 	xspeed = atoi(subpointer);
84438905Sborman 
84538905Sborman 	while (SB_GET() != ',' && !SB_EOF());
84638905Sborman 	if (SB_EOF())
84738905Sborman 		return;
84838905Sborman 
84938905Sborman 	rspeed = atoi(subpointer);
85038905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
85138905Sborman 
85238905Sborman 	break;
85338905Sborman 
85438905Sborman     }  /* end of case TELOPT_TSPEED */
85538905Sborman 
85638905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
85738905Sborman 	static char terminalname[5+41] = "TERM=";
85838905Sborman 
859*38997Sborman 	if (hisopts[TELOPT_TTYPE] == OPT_NO)	/* Ignore if option disabled */
86038905Sborman 		break;
86138905Sborman 	settimer(ttypesubopt);
86238905Sborman 
86338905Sborman 	if (SB_GET() != TELQUAL_IS) {
86438905Sborman 	    return;		/* ??? XXX but, this is the most robust */
86538905Sborman 	}
86638905Sborman 
86738905Sborman 	terminaltype = terminalname+sizeof("TERM=")-1;
86838905Sborman 
86938905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
87038905Sborman 								    !SB_EOF()) {
87138905Sborman 	    register int c;
87238905Sborman 
87338905Sborman 	    c = SB_GET();
87438905Sborman 	    if (isupper(c)) {
87538905Sborman 		c = tolower(c);
87638905Sborman 	    }
87738905Sborman 	    *terminaltype++ = c;    /* accumulate name */
87838905Sborman 	}
87938905Sborman 	*terminaltype = 0;
88038905Sborman 	terminaltype = terminalname;
88138905Sborman 	break;
88238905Sborman     }  /* end of case TELOPT_TTYPE */
88338905Sborman 
88438905Sborman     case TELOPT_NAWS: {
88538905Sborman 	register int xwinsize, ywinsize;
88638905Sborman 
88738905Sborman 	if (hisopts[TELOPT_NAWS] == OPT_NO)	/* Ignore if option disabled */
88838905Sborman 		break;
88938905Sborman 
89038905Sborman 	if (SB_EOF())
89138905Sborman 		return;
89238905Sborman 	xwinsize = SB_GET() << 8;
89338905Sborman 	if (SB_EOF())
89438905Sborman 		return;
89538905Sborman 	xwinsize |= SB_GET();
89638905Sborman 	if (SB_EOF())
89738905Sborman 		return;
89838905Sborman 	ywinsize = SB_GET() << 8;
89938905Sborman 	if (SB_EOF())
90038905Sborman 		return;
90138905Sborman 	ywinsize |= SB_GET();
90238905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
90338905Sborman 
90438905Sborman 	break;
90538905Sborman 
90638905Sborman     }  /* end of case TELOPT_NAWS */
90738905Sborman 
90838905Sborman #ifdef	LINEMODE
90938905Sborman     case TELOPT_LINEMODE: {
91038905Sborman 	register int request;
91138905Sborman 
91238905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO)	/* Ignore if option disabled */
91338905Sborman 		break;
91438905Sborman 	/*
91538905Sborman 	 * Process linemode suboptions.
91638905Sborman 	 */
91738905Sborman 	if (SB_EOF()) break;  /* garbage was sent */
91838905Sborman 	request = SB_GET();  /* get will/wont */
91938905Sborman 	if (SB_EOF()) break;  /* another garbage check */
92038905Sborman 
92138905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
92238905Sborman 		/*
92338905Sborman 		 * Process suboption buffer of slc's
92438905Sborman 		 */
92538905Sborman 		start_slc(1);
92638905Sborman 		do_opt_slc(subpointer, subend - subpointer);
92738905Sborman 		end_slc(0);
92838905Sborman 
92938905Sborman 	} else if (request == LM_MODE) {
93038905Sborman 		useeditmode = SB_GET();  /* get mode flag */
93138905Sborman 		clientstat(LM_MODE, 0, 0);
93238905Sborman 	}
93338905Sborman 
93438905Sborman 	switch (SB_GET()) {  /* what suboption? */
93538905Sborman 	case LM_FORWARDMASK:
93638905Sborman 		/*
93738905Sborman 		 * According to spec, only server can send request for
93838905Sborman 		 * forwardmask, and client can only return a positive response.
93938905Sborman 		 * So don't worry about it.
94038905Sborman 		 */
94138905Sborman 
94238905Sborman 	default:
94338905Sborman 		break;
94438905Sborman 	}
94538905Sborman 
94638905Sborman     }  /* end of case TELOPT_LINEMODE */
94738905Sborman #endif
94838905Sborman     case TELOPT_STATUS: {
94938905Sborman 	int mode;
95038905Sborman 
95138905Sborman 	mode = SB_GET();
95238905Sborman 	switch (mode) {
95338905Sborman 	case TELQUAL_SEND:
95438905Sborman 	    if (myopts[TELOPT_STATUS] == OPT_YES)
95538905Sborman 		send_status();
95638905Sborman 	    break;
95738905Sborman 
95838905Sborman 	case TELQUAL_IS:
95938905Sborman 	    break;
96038905Sborman 
96138905Sborman 	default:
96238905Sborman 	    break;
96338905Sborman 	}
96438905Sborman     }
96538905Sborman 
96638905Sborman     default:
96738905Sborman 	break;
96838905Sborman     }  /* end of switch */
96938905Sborman 
97038905Sborman }  /* end of suboption */
97138905Sborman 
97238905Sborman #define	ADD(c)	 *ncp++ = c;
97338905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
97438905Sborman send_status()
97538905Sborman {
97638905Sborman 	char statusbuf[256];
97738905Sborman 	register char *ncp;
97838905Sborman 	register int i;
97938905Sborman 
98038905Sborman 	ncp = statusbuf;
98138905Sborman 
98238905Sborman 	netflush();	/* get rid of anything waiting to go out */
98338905Sborman 
98438905Sborman 	ADD(IAC);
98538905Sborman 	ADD(SB);
98638905Sborman 	ADD(TELOPT_STATUS);
98738905Sborman 	ADD(TELQUAL_IS);
98838905Sborman 
98938905Sborman 	for (i = 0; i < NTELOPTS; i++) {
99038905Sborman 		if (myopts[i] == OPT_YES) {
99138905Sborman 			ADD(WILL);
99238905Sborman 			ADD_DATA(i);
99338905Sborman 			if (i == IAC)
99438905Sborman 				ADD(IAC);
99538905Sborman 		}
99638905Sborman 		if (hisopts[i] == OPT_YES) {
99738905Sborman 			ADD(DO);
99838905Sborman 			ADD_DATA(i);
99938905Sborman 			if (i == IAC)
100038905Sborman 				ADD(IAC);
100138905Sborman 		}
100238905Sborman 	}
100338905Sborman 
100438905Sborman #ifdef	LINEMODE
100538905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
100638905Sborman 		char *cp, *cpe;
100738905Sborman 		int len;
100838905Sborman 
100938905Sborman 		ADD(SB);
101038905Sborman 		ADD(TELOPT_LINEMODE);
101138905Sborman 		ADD(LM_MODE);
101238905Sborman 		ADD_DATA(editmode);
101338905Sborman 		if (editmode == IAC)
101438905Sborman 			ADD(IAC);
101538905Sborman 		ADD(SE);
101638905Sborman 
101738905Sborman 		ADD(SB);
101838905Sborman 		ADD(TELOPT_LINEMODE);
101938905Sborman 		ADD(LM_SLC);
102038905Sborman 		start_slc(0);
102138905Sborman 		send_slc();
102238905Sborman 		len = end_slc(&cp);
102338905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
102438905Sborman 			ADD_DATA(*cp);
102538905Sborman 		ADD(SE);
102638905Sborman 	}
102738905Sborman #endif	/* LINEMODE */
102838905Sborman 
102938905Sborman 	ADD(IAC);
103038905Sborman 	ADD(SE);
103138905Sborman 
103238905Sborman 	writenet(statusbuf, ncp - statusbuf);
103338905Sborman 	netflush();	/* Send it on its way */
103438905Sborman }
1035