xref: /csrg-svn/libexec/telnetd/state.c (revision 40242)
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*40242Sborman static char sccsid[] = "@(#)state.c	5.5 (Berkeley) 02/28/90";
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;
63*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
6438905Sborman 	char *opfrontp = pfrontp;
6538905Sborman #endif
6638905Sborman 
6738905Sborman 	while (ncc > 0) {
6838905Sborman 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6938905Sborman 			break;
7038905Sborman 		c = *netip++ & 0377, ncc--;
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 &&
145*40242Sborman 				    *slctab[SLC_AO].sptr != (cc_t)-1) {
146*40242Sborman 				    *pfrontp++ =
147*40242Sborman 					(unsigned char)*slctab[SLC_AO].sptr;
14838905Sborman 				}
14938905Sborman 
15038905Sborman 				netclear();	/* clear buffer back */
15138905Sborman 				*nfrontp++ = IAC;
15238905Sborman 				*nfrontp++ = DM;
15338905Sborman 				neturg = nfrontp-1; /* off by one XXX */
15438905Sborman 				break;
15538905Sborman 			    }
15638905Sborman 
15738905Sborman 			/*
15838905Sborman 			 * Erase Character and
15938905Sborman 			 * Erase Line
16038905Sborman 			 */
16138905Sborman 			case EC:
16238905Sborman 			case EL:
16338905Sborman 			    {
164*40242Sborman 				cc_t ch;
16538905Sborman 
16638905Sborman 				ptyflush();	/* half-hearted */
16738905Sborman 				init_termbuf();
16838905Sborman 				ch = (c == EC) ? *slctab[SLC_EC].sptr :
16938905Sborman 						 *slctab[SLC_EL].sptr;
170*40242Sborman 				if (ch != (cc_t)-1)
171*40242Sborman 					*pfrontp++ = (unsigned char)ch;
17238905Sborman 				break;
17338905Sborman 			    }
17438905Sborman 
17538905Sborman 			/*
17638905Sborman 			 * Check for urgent data...
17738905Sborman 			 */
17838905Sborman 			case DM:
17938905Sborman 				SYNCHing = stilloob(net);
18038905Sborman 				settimer(gotDM);
18138905Sborman 				break;
18238905Sborman 
18338905Sborman 
18438905Sborman 			/*
18538905Sborman 			 * Begin option subnegotiation...
18638905Sborman 			 */
18738905Sborman 			case SB:
18838905Sborman 				state = TS_SB;
18938905Sborman 				SB_CLEAR();
19038905Sborman 				continue;
19138905Sborman 
19238905Sborman 			case WILL:
19338905Sborman 				state = TS_WILL;
19438905Sborman 				continue;
19538905Sborman 
19638905Sborman 			case WONT:
19738905Sborman 				state = TS_WONT;
19838905Sborman 				continue;
19938905Sborman 
20038905Sborman 			case DO:
20138905Sborman 				state = TS_DO;
20238905Sborman 				continue;
20338905Sborman 
20438905Sborman 			case DONT:
20538905Sborman 				state = TS_DONT;
20638905Sborman 				continue;
20738905Sborman 			case EOR:
20838905Sborman 				if (hisopts[TELOPT_EOR])
20938905Sborman 					doeof();
21038905Sborman 				break;
21138905Sborman 
21238905Sborman 			/*
21338905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
21438905Sborman 			 * to command stream (EOF, SUSP, ABORT).
21538905Sborman 			 */
21638905Sborman 			case xEOF:
21738905Sborman 				doeof();
21838905Sborman 				break;
21938905Sborman 
22038905Sborman 			case SUSP:
22138905Sborman 				sendsusp();
22238905Sborman 				break;
22338905Sborman 
22438905Sborman 			case ABORT:
22538905Sborman 				sendbrk();
22638905Sborman 				break;
22738905Sborman 
22838905Sborman 			case IAC:
22938905Sborman 				*pfrontp++ = c;
23038905Sborman 				break;
23138905Sborman 			}
23238905Sborman 			state = TS_DATA;
23338905Sborman 			break;
23438905Sborman 
23538905Sborman 		case TS_SB:
23638905Sborman 			if (c == IAC) {
23738905Sborman 				state = TS_SE;
23838905Sborman 			} else {
23938905Sborman 				SB_ACCUM(c);
24038905Sborman 			}
24138905Sborman 			break;
24238905Sborman 
24338905Sborman 		case TS_SE:
24438905Sborman 			if (c != SE) {
24538905Sborman 				if (c != IAC) {
24638905Sborman 					/*
24738905Sborman 					 * bad form of suboption negotiation.
24838905Sborman 					 * handle it in such a way as to avoid
24938905Sborman 					 * damage to local state.  Parse
25038905Sborman 					 * suboption buffer found so far,
25138905Sborman 					 * then treat remaining stream as
25238905Sborman 					 * another command sequence.
25338905Sborman 					 */
25438905Sborman 					SB_TERM();
25538905Sborman 					suboption();
25638905Sborman 					state = TS_IAC;
25738905Sborman 					goto gotiac;
25838905Sborman 				}
25938905Sborman 				SB_ACCUM(c);
26038905Sborman 				state = TS_SB;
26138905Sborman 			} else {
26238905Sborman 				SB_TERM();
26338905Sborman 				suboption();	/* handle sub-option */
26438905Sborman 				state = TS_DATA;
26538905Sborman 			}
26638905Sborman 			break;
26738905Sborman 
26838905Sborman 		case TS_WILL:
26939503Sborman 			willoption(c);
27038905Sborman 			state = TS_DATA;
27138905Sborman 			continue;
27238905Sborman 
27338905Sborman 		case TS_WONT:
27439503Sborman 			wontoption(c);
27538905Sborman 			state = TS_DATA;
27638905Sborman 			continue;
27738905Sborman 
27838905Sborman 		case TS_DO:
27939503Sborman 			dooption(c);
28038905Sborman 			state = TS_DATA;
28138905Sborman 			continue;
28238905Sborman 
28338905Sborman 		case TS_DONT:
28439503Sborman 			dontoption(c);
28538905Sborman 			state = TS_DATA;
28638905Sborman 			continue;
28738905Sborman 
28838905Sborman 		default:
28938905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
29038905Sborman 			printf("telnetd: panic state=%d\n", state);
29138905Sborman 			exit(1);
29238905Sborman 		}
29338905Sborman 	}
294*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
29538905Sborman 	if (!linemode) {
29638905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
29738905Sborman 		char	xbuf2[BUFSIZ];
29838905Sborman 		register char *cp;
29938905Sborman 		int n = pfrontp - opfrontp, oc;
30038905Sborman 		bcopy(opfrontp, xptyobuf, n);
30138905Sborman 		pfrontp = opfrontp;
30238905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
30338905Sborman 					xbuf2, &oc, BUFSIZ);
30438905Sborman 		for (cp = xbuf2; oc > 0; --oc)
30538905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
30638905Sborman 				*nfrontp++ = IAC;
30738905Sborman 	}
308*40242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
30938905Sborman }  /* end of telrcv */
31038905Sborman 
31138905Sborman /*
31238905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
31338905Sborman  * Telnet option processing state machine.  We keep track of the full
31438905Sborman  * state of the option negotiation with the following state variables
31538905Sborman  *	myopts, hisopts - The last fully negotiated state for each
31638905Sborman  *			side of the connection.
31738905Sborman  *	mywants, hiswants - The state we wish to be in after a completed
31838905Sborman  *			negotiation.  (hiswants is slightly misleading,
31938905Sborman  *			this is more precisely the state I want him to
32038905Sborman  *			be in.
32138905Sborman  *	resp - We count the number of requests we have sent out.
32238905Sborman  *
32338905Sborman  * These correspond to the following states:
32438905Sborman  *	my_state = the last negotiated state
32538905Sborman  *	want_state = what I want the state to go to
32638905Sborman  *	want_resp = how many requests I have sent
32738905Sborman  * All state defaults are negative, and resp defaults to 0.
32838905Sborman  *
32938905Sborman  * When initiating a request to change state to new_state:
33038905Sborman  *
33138905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
33238905Sborman  *	do nothing;
33338905Sborman  * } else {
33438905Sborman  *	want_state = new_state;
33538905Sborman  *	send new_state;
33638905Sborman  *	want_resp++;
33738905Sborman  * }
33838905Sborman  *
33938905Sborman  * When receiving new_state:
34038905Sborman  *
34138905Sborman  * if (want_resp) {
34238905Sborman  *	want_resp--;
34338905Sborman  *	if (want_resp && (new_state == my_state))
34438905Sborman  *		want_resp--;
34538905Sborman  * }
34638905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
34738905Sborman  *	if (ok_to_switch_to new_state)
34838905Sborman  *		want_state = new_state;
34938905Sborman  *	else
35038905Sborman  *		want_resp++;
35138905Sborman  *	send want_state;
35238905Sborman  * }
35338905Sborman  * my_state = new_state;
35438905Sborman  *
35538905Sborman  * Note that new_state is implied in these functions by the function itself.
35638905Sborman  * will and do imply positive new_state, wont and dont imply negative.
35738905Sborman  *
35838905Sborman  * Finally, there is one catch.  If we send a negative response to a
35938905Sborman  * positive request, my_state will be the positive while want_state will
36038905Sborman  * remain negative.  my_state will revert to negative when the negative
36138905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
36238905Sborman  * us not only the last negotiated state, but also tells us what the peer
36338905Sborman  * wants to be doing as well.  It is important to understand this difference
36438905Sborman  * as we may wish to be processing data streams based on our desired state
36538905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
36638905Sborman  *
36738905Sborman  * This all works fine because if the peer sends a positive request, the data
36838905Sborman  * that we receive prior to negative acknowlegment will probably be affected
36938905Sborman  * by the positive state, and we can process it as such (if we can; if we
37038905Sborman  * can't then it really doesn't matter).  If it is that important, then the
37138905Sborman  * peer probably should be buffering until this option state negotiation
37238905Sborman  * is complete.
37338905Sborman  *
37438905Sborman  */
37539503Sborman send_do(option, init)
37639503Sborman 	int option, init;
37738905Sborman {
37839503Sborman 	if (init) {
37939503Sborman 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) ||
38039503Sborman 		    hiswants[option] == OPT_YES)
38139503Sborman 			return;
38239531Sborman 		/*
38339531Sborman 		 * Special case for TELOPT_TM:  We send a DO, but pretend
38439531Sborman 		 * that we sent a DONT, so that we can send more DOs if
38539531Sborman 		 * we want to.
38639531Sborman 		 */
38739531Sborman 		if (option == TELOPT_TM)
38839531Sborman 			hiswants[option] = OPT_NO;
38939531Sborman 		else
39039531Sborman 			hiswants[option] = OPT_YES;
39139503Sborman 		do_dont_resp[option]++;
39239503Sborman 	}
39339503Sborman 	(void) sprintf(nfrontp, doopt, option);
39439503Sborman 	nfrontp += sizeof (dont) - 2;
39539503Sborman }
39639503Sborman 
39739503Sborman willoption(option)
39839503Sborman 	int option;
39939503Sborman {
40038905Sborman 	int changeok = 0;
40138905Sborman 
40239503Sborman 	/*
40339503Sborman 	 * process input from peer.
40439503Sborman 	 */
40539503Sborman 
40639503Sborman 	if (do_dont_resp[option]) {
40739503Sborman 		do_dont_resp[option]--;
40839503Sborman 		if (do_dont_resp[option] && hisopts[option] == OPT_YES)
40939503Sborman 			do_dont_resp[option]--;
41039503Sborman 	}
41139531Sborman 	if (do_dont_resp[option] == 0) {
41239531Sborman 	    if (hiswants[option] != OPT_YES) {
41338905Sborman 		switch (option) {
41438905Sborman 
41538905Sborman 		case TELOPT_BINARY:
41638905Sborman 			init_termbuf();
41738905Sborman 			tty_binaryin(1);
41838905Sborman 			set_termbuf();
41938905Sborman 			changeok++;
42038905Sborman 			break;
42138905Sborman 
42238905Sborman 		case TELOPT_ECHO:
42339503Sborman 			not42 = 0;	/* looks like a 4.2 system */
42439503Sborman #ifdef notdef
42538905Sborman 			/*
42639503Sborman 			 * Now, in a 4.2 system, to break them out of
42739503Sborman 			 * ECHOing (to the terminal) mode, we need to
42839503Sborman 			 * send a WILL ECHO.
42938905Sborman 			 */
43038905Sborman 			if (myopts[TELOPT_ECHO] == OPT_YES) {
43139503Sborman 				send_will(TELOPT_ECHO, 1);
43238905Sborman 			}
43339503Sborman #else
43438905Sborman 			/*
43539503Sborman 			 * "WILL ECHO".  Kludge upon kludge!
43639503Sborman 			 * A 4.2 client is now echoing user input at
43739503Sborman 			 * the tty.  This is probably undesireable and
43839503Sborman 			 * it should be stopped.  The client will
43939503Sborman 			 * respond WONT TM to the DO TM that we send to
44039503Sborman 			 * check for kludge linemode.  When the WONT TM
44139503Sborman 			 * arrives, linemode will be turned off and a
44239503Sborman 			 * change propogated to the pty.  This change
44339503Sborman 			 * will cause us to process the new pty state
44439503Sborman 			 * in localstat(), which will notice that
44539503Sborman 			 * linemode is off and send a WILL ECHO
44639503Sborman 			 * so that we are properly in character mode and
44739503Sborman 			 * all is well.
44839503Sborman 			 */
44939503Sborman #endif
45039503Sborman 			/*
45138905Sborman 			 * Fool the state machine into sending a don't.
45239503Sborman 			 * This also allows the initial echo sending
45339503Sborman 			 * code to break out of the loop that it is
45438905Sborman 			 * in.  (Look in telnet())
45538905Sborman 			 */
45638905Sborman 			hiswants[TELOPT_ECHO] = OPT_NO;
45738905Sborman 			break;
45838905Sborman 
45938905Sborman 		case TELOPT_TM:
46038905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
46138905Sborman 			/*
46239503Sborman 			 * This telnetd implementation does not really
46339503Sborman 			 * support timing marks, it just uses them to
46439503Sborman 			 * support the kludge linemode stuff.  If we
46539503Sborman 			 * receive a will or wont TM in response to our
46639503Sborman 			 * do TM request that may have been sent to
46739503Sborman 			 * determine kludge linemode support, process
46839503Sborman 			 * it, otherwise TM should get a negative
46939503Sborman 			 * response back.
47038905Sborman 			 */
47138905Sborman 			/*
47238905Sborman 			 * Handle the linemode kludge stuff.
47338905Sborman 			 * If we are not currently supporting any
47438905Sborman 			 * linemode at all, then we assume that this
47538905Sborman 			 * is the client telling us to use kludge
47638905Sborman 			 * linemode in response to our query.  Set the
47738905Sborman 			 * linemode type that is to be supported, note
47838905Sborman 			 * that the client wishes to use linemode, and
47938905Sborman 			 * eat the will TM as though it never arrived.
48038905Sborman 			 */
48138905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
48238905Sborman 				lmodetype = KLUDGE_LINEMODE;
48338905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
48439503Sborman 				send_wont(TELOPT_SGA, 1);
48538905Sborman 			}
48638905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
48738905Sborman 			/*
48839531Sborman 			 * We never respond to a WILL TM, and
48939531Sborman 			 * we leave the state OPT_NO.
49038905Sborman 			 */
49138905Sborman 			return;
49238905Sborman 
49338905Sborman 		case TELOPT_LFLOW:
49438905Sborman 			/*
49539503Sborman 			 * If we are going to support flow control
49639503Sborman 			 * option, then don't worry peer that we can't
49739503Sborman 			 * change the flow control characters.
49838905Sborman 			 */
49938905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
50038905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
50138905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
50238905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
50338905Sborman 		case TELOPT_TTYPE:
50438905Sborman 		case TELOPT_SGA:
50538905Sborman 		case TELOPT_NAWS:
50638905Sborman 		case TELOPT_TSPEED:
50739531Sborman 			changeok++;
50839531Sborman 			break;
50939531Sborman 
51038905Sborman #ifdef	LINEMODE
51138905Sborman 		case TELOPT_LINEMODE:
51239531Sborman # ifdef	KLUDGELINEMODE
51339531Sborman 			/*
51439531Sborman 			 * Note client's desire to use linemode.
51539531Sborman 			 */
51639531Sborman 			lmodetype = REAL_LINEMODE;
51739531Sborman # endif	/* KLUDGELINEMODE */
51839531Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
51938905Sborman 			changeok++;
52038905Sborman 			break;
52139531Sborman #endif	/* LINEMODE */
52238905Sborman 
52338905Sborman 		default:
52438905Sborman 			break;
52538905Sborman 		}
52639503Sborman 		if (changeok) {
52738905Sborman 			hiswants[option] = OPT_YES;
52839503Sborman 			send_do(option, 0);
52939503Sborman 		} else {
53039503Sborman 			do_dont_resp[option]++;
53139503Sborman 			send_dont(option, 0);
53238905Sborman 		}
53339531Sborman 	    }
53438905Sborman 	}
53539503Sborman 	hisopts[option] = OPT_YES;
53638905Sborman }  /* end of willoption */
53738905Sborman 
53839503Sborman send_dont(option, init)
53939503Sborman 	int option, init;
54038905Sborman {
54139503Sborman 	if (init) {
54239503Sborman 		if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) ||
54339503Sborman 		    hiswants[option] == OPT_NO)
54439503Sborman 			return;
54539503Sborman 		hiswants[option] = OPT_NO;
54639503Sborman 		do_dont_resp[option]++;
54739503Sborman 	}
54839503Sborman 	(void) sprintf(nfrontp, dont, option);
54939503Sborman 	nfrontp += sizeof (doopt) - 2;
55039503Sborman }
55139503Sborman 
55239503Sborman wontoption(option)
55339503Sborman 	int option;
55439503Sborman {
55538905Sborman 	char *fmt = (char *)0;
55638905Sborman 
55738905Sborman 	/*
55838905Sborman 	 * Process client input.
55938905Sborman 	 */
56039503Sborman 
56139503Sborman 	if (do_dont_resp[option]) {
56239503Sborman 		do_dont_resp[option]--;
56339503Sborman 		if (do_dont_resp[option] && hisopts[option] == OPT_NO)
56439503Sborman 			do_dont_resp[option]--;
56539503Sborman 	}
56639531Sborman 	if (do_dont_resp[option] == 0) {
56739531Sborman 	    if (hiswants[option] != OPT_NO) {
56839503Sborman 		/* it is always ok to change to negative state */
56938905Sborman 		switch (option) {
57038905Sborman 		case TELOPT_ECHO:
57139503Sborman 			not42 = 1; /* doesn't seem to be a 4.2 system */
57238905Sborman 			break;
57338905Sborman 
57438905Sborman 		case TELOPT_BINARY:
57538905Sborman 			init_termbuf();
57638905Sborman 			tty_binaryin(0);
57738905Sborman 			set_termbuf();
57838905Sborman 			break;
57938905Sborman 
58038905Sborman #ifdef	LINEMODE
58138905Sborman 		case TELOPT_LINEMODE:
58238905Sborman # ifdef	KLUDGELINEMODE
58338905Sborman 			/*
58438905Sborman 			 * If real linemode is supported, then client is
58538905Sborman 			 * asking to turn linemode off.
58638905Sborman 			 */
58738905Sborman 			if (lmodetype == REAL_LINEMODE)
58838905Sborman # endif	/* KLUDGELINEMODE */
58938905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
59038905Sborman 			break;
59138905Sborman #endif	LINEMODE
59238905Sborman 
59338905Sborman 		case TELOPT_TM:
59438905Sborman 			/*
59539503Sborman 			 * If we get a WONT TM, and had sent a DO TM,
59639503Sborman 			 * don't respond with a DONT TM, just leave it
59739503Sborman 			 * as is.  Short circut the state machine to
59839531Sborman 			 * achive this.
59938905Sborman 			 */
60038905Sborman 			hiswants[TELOPT_TM] = OPT_NO;
60138905Sborman 			return;
60238905Sborman 
60338905Sborman 		case TELOPT_LFLOW:
60438905Sborman 			/*
60539503Sborman 			 * If we are not going to support flow control
60639503Sborman 			 * option, then let peer know that we can't
60739503Sborman 			 * change the flow control characters.
60838905Sborman 			 */
60938905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
61038905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
61138905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
61238905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
61338905Sborman 			break;
61438905Sborman 
61538905Sborman 		default:
61638905Sborman 			break;
61738905Sborman 		}
61839503Sborman 		hiswants[option] = OPT_NO;
61939503Sborman 		fmt = dont;
62039503Sborman 		send_dont(option, 0);
62139531Sborman 	    } else {
62239531Sborman 		switch (option) {
62339531Sborman 		case TELOPT_TM:
62439531Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
62539531Sborman 			if (lmodetype < REAL_LINEMODE) {
62639531Sborman 				lmodetype = NO_LINEMODE;
62739531Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
62839531Sborman 				send_will(TELOPT_SGA, 1);
62939531Sborman /*@*/				send_will(TELOPT_ECHO, 1);
63039531Sborman 			}
63139531Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
63239531Sborman 		default:
63339531Sborman 			break;
63439531Sborman 		}
63539531Sborman 	    }
63638905Sborman 	}
63739503Sborman 	hisopts[option] = OPT_NO;
63838905Sborman 
63939503Sborman }  /* end of wontoption */
64038905Sborman 
64139503Sborman send_will(option, init)
64239503Sborman 	int option, init;
64339503Sborman {
64439503Sborman 	if (init) {
64539503Sborman 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)||
64639503Sborman 		    mywants[option] == OPT_YES)
64739503Sborman 			return;
64839503Sborman 		mywants[option] = OPT_YES;
64939503Sborman 		will_wont_resp[option]++;
65038905Sborman 	}
65139503Sborman 	(void) sprintf(nfrontp, will, option);
65239503Sborman 	nfrontp += sizeof (doopt) - 2;
65339503Sborman }
65438905Sborman 
65539503Sborman dooption(option)
65639503Sborman 	int option;
65738905Sborman {
65838905Sborman 	int changeok = 0;
65938905Sborman 
66038905Sborman 	/*
66138905Sborman 	 * Process client input.
66238905Sborman 	 */
66339503Sborman 
66439503Sborman 	if (will_wont_resp[option]) {
66539503Sborman 		will_wont_resp[option]--;
66639503Sborman 		if (will_wont_resp[option] && myopts[option] == OPT_YES)
66739503Sborman 			will_wont_resp[option]--;
66839503Sborman 	}
66939503Sborman 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) {
67038905Sborman 		switch (option) {
67138905Sborman 		case TELOPT_ECHO:
67238905Sborman #ifdef	LINEMODE
67339503Sborman 			if (lmodetype == NO_LINEMODE) {
67438905Sborman #endif
67538905Sborman 				init_termbuf();
67638905Sborman 				tty_setecho(1);
67738905Sborman 				set_termbuf();
67838905Sborman #ifdef	LINEMODE
67938905Sborman 			}
68038905Sborman #endif
68138905Sborman 			changeok++;
68238905Sborman 			break;
68338905Sborman 
68438905Sborman 		case TELOPT_BINARY:
68538905Sborman 			init_termbuf();
68638905Sborman 			tty_binaryout(1);
68738905Sborman 			set_termbuf();
68838905Sborman 			changeok++;
68938905Sborman 			break;
69038905Sborman 
69138905Sborman 		case TELOPT_SGA:
69238905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
69338905Sborman 			/*
69439503Sborman 			 * If kludge linemode is in use, then we must
69539503Sborman 			 * process an incoming do SGA for linemode
69639503Sborman 			 * purposes.
69738905Sborman 			 */
69838905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
69938905Sborman 				/*
70039503Sborman 				 * Receipt of "do SGA" in kludge
70139503Sborman 				 * linemode is the peer asking us to
70239503Sborman 				 * turn off linemode.  Make note of
70339503Sborman 				 * the request.
70438905Sborman 				 */
70538905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
70638905Sborman 				/*
70739503Sborman 				 * If linemode did not get turned off
70839503Sborman 				 * then don't tell peer that we did.
70939503Sborman 				 * Breaking here forces a wont SGA to
71039503Sborman 				 * be returned.
71138905Sborman 				 */
71238905Sborman 				if (linemode)
71338905Sborman 					break;
71438905Sborman 			}
71538905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
71638905Sborman 			changeok++;
71738905Sborman 			break;
71838905Sborman 
71938905Sborman 		case TELOPT_STATUS:
72038905Sborman 			changeok++;
72138905Sborman 			break;
72238905Sborman 
72338905Sborman 		case TELOPT_TM:
72439503Sborman 			/*
72539503Sborman 			 * Special case for TM.  We send a WILL, but
72639503Sborman 			 * pretend we sent a WONT.
72739503Sborman 			 */
72839503Sborman 			send_will(option, 0);
72939503Sborman 			mywants[option] = OPT_NO;
73039503Sborman 			myopts[option] = OPT_NO;
73139503Sborman 			return;
73239503Sborman 
73338905Sborman 		case TELOPT_LINEMODE:
73438905Sborman 		case TELOPT_TTYPE:
73538905Sborman 		case TELOPT_NAWS:
73638905Sborman 		case TELOPT_TSPEED:
73738905Sborman 		case TELOPT_LFLOW:
73838905Sborman 		default:
73938905Sborman 			break;
74038905Sborman 		}
74139503Sborman 		if (changeok) {
74238905Sborman 			mywants[option] = OPT_YES;
74339503Sborman 			send_will(option, 0);
74439503Sborman 		} else {
74539503Sborman 			will_wont_resp[option]++;
74639503Sborman 			send_wont(option, 0);
74738905Sborman 		}
74838905Sborman 	}
74939503Sborman 	myopts[option] = OPT_YES;
75038905Sborman 
75138905Sborman }  /* end of dooption */
75238905Sborman 
75339503Sborman send_wont(option, init)
75439503Sborman 	int option, init;
75539503Sborman {
75639503Sborman 	if (init) {
75739503Sborman 		if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) ||
75839503Sborman 		    mywants[option] == OPT_NO)
75939503Sborman 			return;
76039503Sborman 		mywants[option] = OPT_NO;
76139503Sborman 		will_wont_resp[option]++;
76239503Sborman 	}
76339503Sborman 	(void) sprintf(nfrontp, wont, option);
76439503Sborman 	nfrontp += sizeof (wont) - 2;
76539503Sborman }
76638905Sborman 
76739503Sborman dontoption(option)
76839503Sborman 	int option;
76938905Sborman {
77038905Sborman 	/*
77138905Sborman 	 * Process client input.
77238905Sborman 	 */
773*40242Sborman 
77439503Sborman 	if (will_wont_resp[option]) {
77539503Sborman 		will_wont_resp[option]--;
77639503Sborman 		if (will_wont_resp[option] && myopts[option] == OPT_NO)
77739503Sborman 			will_wont_resp[option]--;
77839503Sborman 	}
77939503Sborman 	if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) {
78038905Sborman 		switch (option) {
78138905Sborman 		case TELOPT_BINARY:
78238905Sborman 			init_termbuf();
78338905Sborman 			tty_binaryout(0);
78438905Sborman 			set_termbuf();
78538905Sborman 			break;
78638905Sborman 
78739503Sborman 		case TELOPT_ECHO:	/* we should stop echoing */
78838905Sborman #ifdef	LINEMODE
78939503Sborman 			if (lmodetype == NO_LINEMODE) {
79038905Sborman #endif
79138905Sborman 				init_termbuf();
79238905Sborman 				tty_setecho(0);
79338905Sborman 				set_termbuf();
79438905Sborman #ifdef	LINEMODE
79538905Sborman 			}
79638905Sborman #endif
79738905Sborman 			break;
79838905Sborman 
79938905Sborman 		case TELOPT_SGA:
80038905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
80138905Sborman 			/*
80239503Sborman 			 * If kludge linemode is in use, then we
80339503Sborman 			 * must process an incoming do SGA for
80439503Sborman 			 * linemode purposes.
80538905Sborman 			 */
80638905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
80738905Sborman 				/*
80839503Sborman 				 * The client is asking us to turn
80939503Sborman 				 * linemode on.
81038905Sborman 				 */
81138905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
81238905Sborman 				/*
81339503Sborman 				 * If we did not turn line mode on,
81439503Sborman 				 * then what do we say?  Will SGA?
81539503Sborman 				 * This violates design of telnet.
81639503Sborman 				 * Gross.  Very Gross.
81738905Sborman 				 */
81838905Sborman 			}
81938905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
82038905Sborman 
82138905Sborman 		default:
82238905Sborman 			break;
82338905Sborman 		}
82438905Sborman 
82539503Sborman 		mywants[option] = OPT_NO;
82639503Sborman 		send_wont(option, 0);
82738905Sborman 	}
82839503Sborman 	myopts[option] = OPT_NO;
82938905Sborman 
83038905Sborman }  /* end of dontoption */
83138905Sborman 
83238905Sborman /*
83338905Sborman  * suboption()
83438905Sborman  *
83538905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
83638905Sborman  * side.
83738905Sborman  *
83838905Sborman  *	Currently we recognize:
83938905Sborman  *
84038905Sborman  *	Terminal type is
84138905Sborman  *	Linemode
84238905Sborman  *	Window size
84338905Sborman  *	Terminal speed
84438905Sborman  */
84538905Sborman suboption()
84638905Sborman {
84738905Sborman     register int subchar;
84838905Sborman 
84938905Sborman     subchar = SB_GET();
85038905Sborman     switch (subchar) {
85138905Sborman     case TELOPT_TSPEED: {
85238905Sborman 	register int xspeed, rspeed;
85338905Sborman 
85438905Sborman 	if (hisopts[TELOPT_TSPEED] == OPT_NO)	/* Ignore if option disabled */
85538905Sborman 		break;
85638905Sborman 
85738905Sborman 	settimer(tspeedsubopt);
85838905Sborman 
85938905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
86038905Sborman 		return;
86138905Sborman 
86238905Sborman 	xspeed = atoi(subpointer);
86338905Sborman 
86438905Sborman 	while (SB_GET() != ',' && !SB_EOF());
86538905Sborman 	if (SB_EOF())
86638905Sborman 		return;
86738905Sborman 
86838905Sborman 	rspeed = atoi(subpointer);
86938905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
87038905Sborman 
87138905Sborman 	break;
87238905Sborman 
87338905Sborman     }  /* end of case TELOPT_TSPEED */
87438905Sborman 
87538905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
87638905Sborman 	static char terminalname[5+41] = "TERM=";
87738905Sborman 
87838997Sborman 	if (hisopts[TELOPT_TTYPE] == OPT_NO)	/* Ignore if option disabled */
87938905Sborman 		break;
88038905Sborman 	settimer(ttypesubopt);
88138905Sborman 
88238905Sborman 	if (SB_GET() != TELQUAL_IS) {
88338905Sborman 	    return;		/* ??? XXX but, this is the most robust */
88438905Sborman 	}
88538905Sborman 
88638905Sborman 	terminaltype = terminalname+sizeof("TERM=")-1;
88738905Sborman 
88838905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
88938905Sborman 								    !SB_EOF()) {
89038905Sborman 	    register int c;
89138905Sborman 
89238905Sborman 	    c = SB_GET();
89338905Sborman 	    if (isupper(c)) {
89438905Sborman 		c = tolower(c);
89538905Sborman 	    }
89638905Sborman 	    *terminaltype++ = c;    /* accumulate name */
89738905Sborman 	}
89838905Sborman 	*terminaltype = 0;
89938905Sborman 	terminaltype = terminalname;
90038905Sborman 	break;
90138905Sborman     }  /* end of case TELOPT_TTYPE */
90238905Sborman 
90338905Sborman     case TELOPT_NAWS: {
90438905Sborman 	register int xwinsize, ywinsize;
90538905Sborman 
90638905Sborman 	if (hisopts[TELOPT_NAWS] == OPT_NO)	/* Ignore if option disabled */
90738905Sborman 		break;
90838905Sborman 
90938905Sborman 	if (SB_EOF())
91038905Sborman 		return;
91138905Sborman 	xwinsize = SB_GET() << 8;
91238905Sborman 	if (SB_EOF())
91338905Sborman 		return;
91438905Sborman 	xwinsize |= SB_GET();
91538905Sborman 	if (SB_EOF())
91638905Sborman 		return;
91738905Sborman 	ywinsize = SB_GET() << 8;
91838905Sborman 	if (SB_EOF())
91938905Sborman 		return;
92038905Sborman 	ywinsize |= SB_GET();
92138905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
92238905Sborman 
92338905Sborman 	break;
92438905Sborman 
92538905Sborman     }  /* end of case TELOPT_NAWS */
92638905Sborman 
92738905Sborman #ifdef	LINEMODE
92838905Sborman     case TELOPT_LINEMODE: {
92938905Sborman 	register int request;
93038905Sborman 
93138905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO)	/* Ignore if option disabled */
93238905Sborman 		break;
93338905Sborman 	/*
93438905Sborman 	 * Process linemode suboptions.
93538905Sborman 	 */
93638905Sborman 	if (SB_EOF()) break;  /* garbage was sent */
93738905Sborman 	request = SB_GET();  /* get will/wont */
93838905Sborman 	if (SB_EOF()) break;  /* another garbage check */
93938905Sborman 
94038905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
94138905Sborman 		/*
94238905Sborman 		 * Process suboption buffer of slc's
94338905Sborman 		 */
94438905Sborman 		start_slc(1);
94538905Sborman 		do_opt_slc(subpointer, subend - subpointer);
94638905Sborman 		end_slc(0);
94738905Sborman 
94838905Sborman 	} else if (request == LM_MODE) {
94938905Sborman 		useeditmode = SB_GET();  /* get mode flag */
95038905Sborman 		clientstat(LM_MODE, 0, 0);
95138905Sborman 	}
95238905Sborman 
95338905Sborman 	switch (SB_GET()) {  /* what suboption? */
95438905Sborman 	case LM_FORWARDMASK:
95538905Sborman 		/*
95638905Sborman 		 * According to spec, only server can send request for
95738905Sborman 		 * forwardmask, and client can only return a positive response.
95838905Sborman 		 * So don't worry about it.
95938905Sborman 		 */
96038905Sborman 
96138905Sborman 	default:
96238905Sborman 		break;
96338905Sborman 	}
964*40242Sborman 	break;
96538905Sborman     }  /* end of case TELOPT_LINEMODE */
96638905Sborman #endif
96738905Sborman     case TELOPT_STATUS: {
96838905Sborman 	int mode;
96938905Sborman 
97038905Sborman 	mode = SB_GET();
97138905Sborman 	switch (mode) {
97238905Sborman 	case TELQUAL_SEND:
97338905Sborman 	    if (myopts[TELOPT_STATUS] == OPT_YES)
97438905Sborman 		send_status();
97538905Sborman 	    break;
97638905Sborman 
97738905Sborman 	case TELQUAL_IS:
97838905Sborman 	    break;
97938905Sborman 
98038905Sborman 	default:
98138905Sborman 	    break;
98238905Sborman 	}
983*40242Sborman 	break;
984*40242Sborman     }  /* end of case TELOPT_STATUS */
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