xref: /freebsd/crypto/heimdal/appl/telnet/telnetd/state.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
113e3f4d6SMark Murray /*
213e3f4d6SMark Murray  * Copyright (c) 1989, 1993
313e3f4d6SMark Murray  *	The Regents of the University of California.  All rights reserved.
413e3f4d6SMark Murray  *
513e3f4d6SMark Murray  * Redistribution and use in source and binary forms, with or without
613e3f4d6SMark Murray  * modification, are permitted provided that the following conditions
713e3f4d6SMark Murray  * are met:
813e3f4d6SMark Murray  * 1. Redistributions of source code must retain the above copyright
913e3f4d6SMark Murray  *    notice, this list of conditions and the following disclaimer.
1013e3f4d6SMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
1113e3f4d6SMark Murray  *    notice, this list of conditions and the following disclaimer in the
1213e3f4d6SMark Murray  *    documentation and/or other materials provided with the distribution.
1313e3f4d6SMark Murray  * 3. All advertising materials mentioning features or use of this software
1413e3f4d6SMark Murray  *    must display the following acknowledgement:
1513e3f4d6SMark Murray  *	This product includes software developed by the University of
1613e3f4d6SMark Murray  *	California, Berkeley and its contributors.
1713e3f4d6SMark Murray  * 4. Neither the name of the University nor the names of its contributors
1813e3f4d6SMark Murray  *    may be used to endorse or promote products derived from this software
1913e3f4d6SMark Murray  *    without specific prior written permission.
2013e3f4d6SMark Murray  *
2113e3f4d6SMark Murray  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2213e3f4d6SMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2313e3f4d6SMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2413e3f4d6SMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2513e3f4d6SMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2613e3f4d6SMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2713e3f4d6SMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2813e3f4d6SMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2913e3f4d6SMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3013e3f4d6SMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3113e3f4d6SMark Murray  * SUCH DAMAGE.
3213e3f4d6SMark Murray  */
3313e3f4d6SMark Murray 
3413e3f4d6SMark Murray #include "telnetd.h"
3513e3f4d6SMark Murray 
36*ae771770SStanislav Sedov RCSID("$Id$");
3713e3f4d6SMark Murray 
3813e3f4d6SMark Murray unsigned char	doopt[] = { IAC, DO, '%', 'c', 0 };
3913e3f4d6SMark Murray unsigned char	dont[] = { IAC, DONT, '%', 'c', 0 };
4013e3f4d6SMark Murray unsigned char	will[] = { IAC, WILL, '%', 'c', 0 };
4113e3f4d6SMark Murray unsigned char	wont[] = { IAC, WONT, '%', 'c', 0 };
4213e3f4d6SMark Murray int	not42 = 1;
4313e3f4d6SMark Murray 
4413e3f4d6SMark Murray /*
4513e3f4d6SMark Murray  * Buffer for sub-options, and macros
4613e3f4d6SMark Murray  * for suboptions buffer manipulations
4713e3f4d6SMark Murray  */
488d4ba808SJacques Vidrine unsigned char subbuffer[1024*64], *subpointer= subbuffer, *subend= subbuffer;
4913e3f4d6SMark Murray 
5013e3f4d6SMark Murray #define	SB_CLEAR()	subpointer = subbuffer
5113e3f4d6SMark Murray #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
5213e3f4d6SMark Murray #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
5313e3f4d6SMark Murray     *subpointer++ = (c); \
5413e3f4d6SMark Murray 			     }
5513e3f4d6SMark Murray #define	SB_GET()	((*subpointer++)&0xff)
5613e3f4d6SMark Murray #define	SB_EOF()	(subpointer >= subend)
5713e3f4d6SMark Murray #define	SB_LEN()	(subend - subpointer)
5813e3f4d6SMark Murray 
5913e3f4d6SMark Murray #ifdef	ENV_HACK
6013e3f4d6SMark Murray unsigned char *subsave;
6113e3f4d6SMark Murray #define SB_SAVE()	subsave = subpointer;
6213e3f4d6SMark Murray #define	SB_RESTORE()	subpointer = subsave;
6313e3f4d6SMark Murray #endif
6413e3f4d6SMark Murray 
6513e3f4d6SMark Murray 
6613e3f4d6SMark Murray /*
6713e3f4d6SMark Murray  * State for recv fsm
6813e3f4d6SMark Murray  */
6913e3f4d6SMark Murray #define	TS_DATA		0	/* base state */
7013e3f4d6SMark Murray #define	TS_IAC		1	/* look for double IAC's */
7113e3f4d6SMark Murray #define	TS_CR		2	/* CR-LF ->'s CR */
7213e3f4d6SMark Murray #define	TS_SB		3	/* throw away begin's... */
7313e3f4d6SMark Murray #define	TS_SE		4	/* ...end's (suboption negotiation) */
7413e3f4d6SMark Murray #define	TS_WILL		5	/* will option negotiation */
7513e3f4d6SMark Murray #define	TS_WONT		6	/* wont -''- */
7613e3f4d6SMark Murray #define	TS_DO		7	/* do -''- */
7713e3f4d6SMark Murray #define	TS_DONT		8	/* dont -''- */
7813e3f4d6SMark Murray 
7913e3f4d6SMark Murray void
telrcv(void)8013e3f4d6SMark Murray telrcv(void)
8113e3f4d6SMark Murray {
8213e3f4d6SMark Murray     int c;
8313e3f4d6SMark Murray     static int state = TS_DATA;
8413e3f4d6SMark Murray 
8513e3f4d6SMark Murray     while (ncc > 0) {
8613e3f4d6SMark Murray 	if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
8713e3f4d6SMark Murray 	    break;
8813e3f4d6SMark Murray 	c = *netip++ & 0377, ncc--;
8913e3f4d6SMark Murray #ifdef ENCRYPTION
9013e3f4d6SMark Murray 	if (decrypt_input)
9113e3f4d6SMark Murray 	    c = (*decrypt_input)(c);
9213e3f4d6SMark Murray #endif
9313e3f4d6SMark Murray 	switch (state) {
9413e3f4d6SMark Murray 
9513e3f4d6SMark Murray 	case TS_CR:
9613e3f4d6SMark Murray 	    state = TS_DATA;
9713e3f4d6SMark Murray 	    /* Strip off \n or \0 after a \r */
9813e3f4d6SMark Murray 	    if ((c == 0) || (c == '\n')) {
9913e3f4d6SMark Murray 		break;
10013e3f4d6SMark Murray 	    }
10113e3f4d6SMark Murray 	    /* FALL THROUGH */
10213e3f4d6SMark Murray 
10313e3f4d6SMark Murray 	case TS_DATA:
10413e3f4d6SMark Murray 	    if (c == IAC) {
10513e3f4d6SMark Murray 		state = TS_IAC;
10613e3f4d6SMark Murray 		break;
10713e3f4d6SMark Murray 	    }
10813e3f4d6SMark Murray 	    /*
10913e3f4d6SMark Murray 	     * We now map \r\n ==> \r for pragmatic reasons.
11013e3f4d6SMark Murray 	     * Many client implementations send \r\n when
11113e3f4d6SMark Murray 	     * the user hits the CarriageReturn key.
11213e3f4d6SMark Murray 	     *
11313e3f4d6SMark Murray 	     * We USED to map \r\n ==> \n, since \r\n says
11413e3f4d6SMark Murray 	     * that we want to be in column 1 of the next
11513e3f4d6SMark Murray 	     * printable line, and \n is the standard
11613e3f4d6SMark Murray 	     * unix way of saying that (\r is only good
11713e3f4d6SMark Murray 	     * if CRMOD is set, which it normally is).
11813e3f4d6SMark Murray 	     */
11913e3f4d6SMark Murray 	    if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
12013e3f4d6SMark Murray 		int nc = *netip;
12113e3f4d6SMark Murray #ifdef ENCRYPTION
12213e3f4d6SMark Murray 		if (decrypt_input)
12313e3f4d6SMark Murray 		    nc = (*decrypt_input)(nc & 0xff);
12413e3f4d6SMark Murray #endif
12513e3f4d6SMark Murray 		{
12613e3f4d6SMark Murray #ifdef ENCRYPTION
12713e3f4d6SMark Murray 		    if (decrypt_input)
12813e3f4d6SMark Murray 			(void)(*decrypt_input)(-1);
12913e3f4d6SMark Murray #endif
13013e3f4d6SMark Murray 		    state = TS_CR;
13113e3f4d6SMark Murray 		}
13213e3f4d6SMark Murray 	    }
13313e3f4d6SMark Murray 	    *pfrontp++ = c;
13413e3f4d6SMark Murray 	    break;
13513e3f4d6SMark Murray 
13613e3f4d6SMark Murray 	case TS_IAC:
13713e3f4d6SMark Murray 	gotiac:			switch (c) {
13813e3f4d6SMark Murray 
13913e3f4d6SMark Murray 	    /*
14013e3f4d6SMark Murray 	     * Send the process on the pty side an
14113e3f4d6SMark Murray 	     * interrupt.  Do this with a NULL or
14213e3f4d6SMark Murray 	     * interrupt char; depending on the tty mode.
14313e3f4d6SMark Murray 	     */
14413e3f4d6SMark Murray 	case IP:
14513e3f4d6SMark Murray 	    DIAG(TD_OPTIONS,
14613e3f4d6SMark Murray 		 printoption("td: recv IAC", c));
14713e3f4d6SMark Murray 	    interrupt();
14813e3f4d6SMark Murray 	    break;
14913e3f4d6SMark Murray 
15013e3f4d6SMark Murray 	case BREAK:
15113e3f4d6SMark Murray 	    DIAG(TD_OPTIONS,
15213e3f4d6SMark Murray 		 printoption("td: recv IAC", c));
15313e3f4d6SMark Murray 	    sendbrk();
15413e3f4d6SMark Murray 	    break;
15513e3f4d6SMark Murray 
15613e3f4d6SMark Murray 	    /*
15713e3f4d6SMark Murray 	     * Are You There?
15813e3f4d6SMark Murray 	     */
15913e3f4d6SMark Murray 	case AYT:
16013e3f4d6SMark Murray 	    DIAG(TD_OPTIONS,
16113e3f4d6SMark Murray 		 printoption("td: recv IAC", c));
16213e3f4d6SMark Murray 	    recv_ayt();
16313e3f4d6SMark Murray 	    break;
16413e3f4d6SMark Murray 
16513e3f4d6SMark Murray 	    /*
16613e3f4d6SMark Murray 	     * Abort Output
16713e3f4d6SMark Murray 	     */
16813e3f4d6SMark Murray 	case AO:
16913e3f4d6SMark Murray 	    {
17013e3f4d6SMark Murray 		DIAG(TD_OPTIONS,
17113e3f4d6SMark Murray 		     printoption("td: recv IAC", c));
17213e3f4d6SMark Murray 		ptyflush();	/* half-hearted */
17313e3f4d6SMark Murray 		init_termbuf();
17413e3f4d6SMark Murray 
17513e3f4d6SMark Murray 		if (slctab[SLC_AO].sptr &&
17613e3f4d6SMark Murray 		    *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
17713e3f4d6SMark Murray 		    *pfrontp++ =
17813e3f4d6SMark Murray 			(unsigned char)*slctab[SLC_AO].sptr;
17913e3f4d6SMark Murray 		}
18013e3f4d6SMark Murray 
18113e3f4d6SMark Murray 		netclear();	/* clear buffer back */
18213e3f4d6SMark Murray 		output_data ("%c%c", IAC, DM);
18313e3f4d6SMark Murray 		neturg = nfrontp-1; /* off by one XXX */
18413e3f4d6SMark Murray 		DIAG(TD_OPTIONS,
18513e3f4d6SMark Murray 		     printoption("td: send IAC", DM));
18613e3f4d6SMark Murray 		break;
18713e3f4d6SMark Murray 	    }
18813e3f4d6SMark Murray 
18913e3f4d6SMark Murray 	/*
19013e3f4d6SMark Murray 	 * Erase Character and
19113e3f4d6SMark Murray 	 * Erase Line
19213e3f4d6SMark Murray 	 */
19313e3f4d6SMark Murray 	case EC:
19413e3f4d6SMark Murray 	case EL:
19513e3f4d6SMark Murray 	    {
19613e3f4d6SMark Murray 		cc_t ch;
19713e3f4d6SMark Murray 
19813e3f4d6SMark Murray 		DIAG(TD_OPTIONS,
19913e3f4d6SMark Murray 		     printoption("td: recv IAC", c));
20013e3f4d6SMark Murray 		ptyflush();	/* half-hearted */
20113e3f4d6SMark Murray 		init_termbuf();
20213e3f4d6SMark Murray 		if (c == EC)
20313e3f4d6SMark Murray 		    ch = *slctab[SLC_EC].sptr;
20413e3f4d6SMark Murray 		else
20513e3f4d6SMark Murray 		    ch = *slctab[SLC_EL].sptr;
20613e3f4d6SMark Murray 		if (ch != (cc_t)(_POSIX_VDISABLE))
20713e3f4d6SMark Murray 		    *pfrontp++ = (unsigned char)ch;
20813e3f4d6SMark Murray 		break;
20913e3f4d6SMark Murray 	    }
21013e3f4d6SMark Murray 
21113e3f4d6SMark Murray 	/*
21213e3f4d6SMark Murray 	 * Check for urgent data...
21313e3f4d6SMark Murray 	 */
21413e3f4d6SMark Murray 	case DM:
21513e3f4d6SMark Murray 	    DIAG(TD_OPTIONS,
21613e3f4d6SMark Murray 		 printoption("td: recv IAC", c));
21713e3f4d6SMark Murray 	    SYNCHing = stilloob(net);
21813e3f4d6SMark Murray 	    settimer(gotDM);
21913e3f4d6SMark Murray 	    break;
22013e3f4d6SMark Murray 
22113e3f4d6SMark Murray 
22213e3f4d6SMark Murray 	    /*
22313e3f4d6SMark Murray 	     * Begin option subnegotiation...
22413e3f4d6SMark Murray 	     */
22513e3f4d6SMark Murray 	case SB:
22613e3f4d6SMark Murray 	    state = TS_SB;
22713e3f4d6SMark Murray 	    SB_CLEAR();
22813e3f4d6SMark Murray 	    continue;
22913e3f4d6SMark Murray 
23013e3f4d6SMark Murray 	case WILL:
23113e3f4d6SMark Murray 	    state = TS_WILL;
23213e3f4d6SMark Murray 	    continue;
23313e3f4d6SMark Murray 
23413e3f4d6SMark Murray 	case WONT:
23513e3f4d6SMark Murray 	    state = TS_WONT;
23613e3f4d6SMark Murray 	    continue;
23713e3f4d6SMark Murray 
23813e3f4d6SMark Murray 	case DO:
23913e3f4d6SMark Murray 	    state = TS_DO;
24013e3f4d6SMark Murray 	    continue;
24113e3f4d6SMark Murray 
24213e3f4d6SMark Murray 	case DONT:
24313e3f4d6SMark Murray 	    state = TS_DONT;
24413e3f4d6SMark Murray 	    continue;
24513e3f4d6SMark Murray 	case EOR:
24613e3f4d6SMark Murray 	    if (his_state_is_will(TELOPT_EOR))
24713e3f4d6SMark Murray 		doeof();
24813e3f4d6SMark Murray 	    break;
24913e3f4d6SMark Murray 
25013e3f4d6SMark Murray 	    /*
25113e3f4d6SMark Murray 	     * Handle RFC 10xx Telnet linemode option additions
25213e3f4d6SMark Murray 	     * to command stream (EOF, SUSP, ABORT).
25313e3f4d6SMark Murray 	     */
25413e3f4d6SMark Murray 	case xEOF:
25513e3f4d6SMark Murray 	    doeof();
25613e3f4d6SMark Murray 	    break;
25713e3f4d6SMark Murray 
25813e3f4d6SMark Murray 	case SUSP:
25913e3f4d6SMark Murray 	    sendsusp();
26013e3f4d6SMark Murray 	    break;
26113e3f4d6SMark Murray 
26213e3f4d6SMark Murray 	case ABORT:
26313e3f4d6SMark Murray 	    sendbrk();
26413e3f4d6SMark Murray 	    break;
26513e3f4d6SMark Murray 
26613e3f4d6SMark Murray 	case IAC:
26713e3f4d6SMark Murray 	    *pfrontp++ = c;
26813e3f4d6SMark Murray 	    break;
26913e3f4d6SMark Murray 	}
27013e3f4d6SMark Murray 	state = TS_DATA;
27113e3f4d6SMark Murray 	break;
27213e3f4d6SMark Murray 
27313e3f4d6SMark Murray 	case TS_SB:
27413e3f4d6SMark Murray 	    if (c == IAC) {
27513e3f4d6SMark Murray 		state = TS_SE;
27613e3f4d6SMark Murray 	    } else {
27713e3f4d6SMark Murray 		SB_ACCUM(c);
27813e3f4d6SMark Murray 	    }
27913e3f4d6SMark Murray 	    break;
28013e3f4d6SMark Murray 
28113e3f4d6SMark Murray 	case TS_SE:
28213e3f4d6SMark Murray 	    if (c != SE) {
28313e3f4d6SMark Murray 		if (c != IAC) {
28413e3f4d6SMark Murray 		    /*
28513e3f4d6SMark Murray 		     * bad form of suboption negotiation.
28613e3f4d6SMark Murray 		     * handle it in such a way as to avoid
28713e3f4d6SMark Murray 		     * damage to local state.  Parse
28813e3f4d6SMark Murray 		     * suboption buffer found so far,
28913e3f4d6SMark Murray 		     * then treat remaining stream as
29013e3f4d6SMark Murray 		     * another command sequence.
29113e3f4d6SMark Murray 		     */
29213e3f4d6SMark Murray 
29313e3f4d6SMark Murray 		    /* for DIAGNOSTICS */
29413e3f4d6SMark Murray 		    SB_ACCUM(IAC);
29513e3f4d6SMark Murray 		    SB_ACCUM(c);
29613e3f4d6SMark Murray 		    subpointer -= 2;
29713e3f4d6SMark Murray 
29813e3f4d6SMark Murray 		    SB_TERM();
29913e3f4d6SMark Murray 		    suboption();
30013e3f4d6SMark Murray 		    state = TS_IAC;
30113e3f4d6SMark Murray 		    goto gotiac;
30213e3f4d6SMark Murray 		}
30313e3f4d6SMark Murray 		SB_ACCUM(c);
30413e3f4d6SMark Murray 		state = TS_SB;
30513e3f4d6SMark Murray 	    } else {
30613e3f4d6SMark Murray 		/* for DIAGNOSTICS */
30713e3f4d6SMark Murray 		SB_ACCUM(IAC);
30813e3f4d6SMark Murray 		SB_ACCUM(SE);
30913e3f4d6SMark Murray 		subpointer -= 2;
31013e3f4d6SMark Murray 
31113e3f4d6SMark Murray 		SB_TERM();
31213e3f4d6SMark Murray 		suboption();	/* handle sub-option */
31313e3f4d6SMark Murray 		state = TS_DATA;
31413e3f4d6SMark Murray 	    }
31513e3f4d6SMark Murray 	    break;
31613e3f4d6SMark Murray 
31713e3f4d6SMark Murray 	case TS_WILL:
31813e3f4d6SMark Murray 	    willoption(c);
31913e3f4d6SMark Murray 	    state = TS_DATA;
32013e3f4d6SMark Murray 	    continue;
32113e3f4d6SMark Murray 
32213e3f4d6SMark Murray 	case TS_WONT:
32313e3f4d6SMark Murray 	    wontoption(c);
32413e3f4d6SMark Murray 	    if (c==TELOPT_ENCRYPT && his_do_dont_is_changing(TELOPT_ENCRYPT) )
32513e3f4d6SMark Murray                 dontoption(c);
32613e3f4d6SMark Murray 	    state = TS_DATA;
32713e3f4d6SMark Murray 	    continue;
32813e3f4d6SMark Murray 
32913e3f4d6SMark Murray 	case TS_DO:
33013e3f4d6SMark Murray 	    dooption(c);
33113e3f4d6SMark Murray 	    state = TS_DATA;
33213e3f4d6SMark Murray 	    continue;
33313e3f4d6SMark Murray 
33413e3f4d6SMark Murray 	case TS_DONT:
33513e3f4d6SMark Murray 	    dontoption(c);
33613e3f4d6SMark Murray 	    state = TS_DATA;
33713e3f4d6SMark Murray 	    continue;
33813e3f4d6SMark Murray 
33913e3f4d6SMark Murray 	default:
34013e3f4d6SMark Murray 	    syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
34113e3f4d6SMark Murray 	    printf("telnetd: panic state=%d\n", state);
34213e3f4d6SMark Murray 	    exit(1);
34313e3f4d6SMark Murray 	}
34413e3f4d6SMark Murray     }
34513e3f4d6SMark Murray }  /* end of telrcv */
34613e3f4d6SMark Murray 
34713e3f4d6SMark Murray /*
34813e3f4d6SMark Murray  * The will/wont/do/dont state machines are based on Dave Borman's
34913e3f4d6SMark Murray  * Telnet option processing state machine.
35013e3f4d6SMark Murray  *
35113e3f4d6SMark Murray  * These correspond to the following states:
35213e3f4d6SMark Murray  *	my_state = the last negotiated state
35313e3f4d6SMark Murray  *	want_state = what I want the state to go to
35413e3f4d6SMark Murray  *	want_resp = how many requests I have sent
35513e3f4d6SMark Murray  * All state defaults are negative, and resp defaults to 0.
35613e3f4d6SMark Murray  *
35713e3f4d6SMark Murray  * When initiating a request to change state to new_state:
35813e3f4d6SMark Murray  *
35913e3f4d6SMark Murray  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
36013e3f4d6SMark Murray  *	do nothing;
36113e3f4d6SMark Murray  * } else {
36213e3f4d6SMark Murray  *	want_state = new_state;
36313e3f4d6SMark Murray  *	send new_state;
36413e3f4d6SMark Murray  *	want_resp++;
36513e3f4d6SMark Murray  * }
36613e3f4d6SMark Murray  *
36713e3f4d6SMark Murray  * When receiving new_state:
36813e3f4d6SMark Murray  *
36913e3f4d6SMark Murray  * if (want_resp) {
37013e3f4d6SMark Murray  *	want_resp--;
37113e3f4d6SMark Murray  *	if (want_resp && (new_state == my_state))
37213e3f4d6SMark Murray  *		want_resp--;
37313e3f4d6SMark Murray  * }
37413e3f4d6SMark Murray  * if ((want_resp == 0) && (new_state != want_state)) {
37513e3f4d6SMark Murray  *	if (ok_to_switch_to new_state)
37613e3f4d6SMark Murray  *		want_state = new_state;
37713e3f4d6SMark Murray  *	else
37813e3f4d6SMark Murray  *		want_resp++;
37913e3f4d6SMark Murray  *	send want_state;
38013e3f4d6SMark Murray  * }
38113e3f4d6SMark Murray  * my_state = new_state;
38213e3f4d6SMark Murray  *
38313e3f4d6SMark Murray  * Note that new_state is implied in these functions by the function itself.
38413e3f4d6SMark Murray  * will and do imply positive new_state, wont and dont imply negative.
38513e3f4d6SMark Murray  *
38613e3f4d6SMark Murray  * Finally, there is one catch.  If we send a negative response to a
38713e3f4d6SMark Murray  * positive request, my_state will be the positive while want_state will
38813e3f4d6SMark Murray  * remain negative.  my_state will revert to negative when the negative
38913e3f4d6SMark Murray  * acknowlegment arrives from the peer.  Thus, my_state generally tells
39013e3f4d6SMark Murray  * us not only the last negotiated state, but also tells us what the peer
39113e3f4d6SMark Murray  * wants to be doing as well.  It is important to understand this difference
39213e3f4d6SMark Murray  * as we may wish to be processing data streams based on our desired state
39313e3f4d6SMark Murray  * (want_state) or based on what the peer thinks the state is (my_state).
39413e3f4d6SMark Murray  *
39513e3f4d6SMark Murray  * This all works fine because if the peer sends a positive request, the data
39613e3f4d6SMark Murray  * that we receive prior to negative acknowlegment will probably be affected
39713e3f4d6SMark Murray  * by the positive state, and we can process it as such (if we can; if we
39813e3f4d6SMark Murray  * can't then it really doesn't matter).  If it is that important, then the
39913e3f4d6SMark Murray  * peer probably should be buffering until this option state negotiation
40013e3f4d6SMark Murray  * is complete.
40113e3f4d6SMark Murray  *
40213e3f4d6SMark Murray  */
40313e3f4d6SMark Murray void
send_do(int option,int init)40413e3f4d6SMark Murray send_do(int option, int init)
40513e3f4d6SMark Murray {
40613e3f4d6SMark Murray     if (init) {
40713e3f4d6SMark Murray 	if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
40813e3f4d6SMark Murray 	    his_want_state_is_will(option))
40913e3f4d6SMark Murray 	    return;
41013e3f4d6SMark Murray 	/*
41113e3f4d6SMark Murray 	 * Special case for TELOPT_TM:  We send a DO, but pretend
41213e3f4d6SMark Murray 	 * that we sent a DONT, so that we can send more DOs if
41313e3f4d6SMark Murray 	 * we want to.
41413e3f4d6SMark Murray 	 */
41513e3f4d6SMark Murray 	if (option == TELOPT_TM)
41613e3f4d6SMark Murray 	    set_his_want_state_wont(option);
41713e3f4d6SMark Murray 	else
41813e3f4d6SMark Murray 	    set_his_want_state_will(option);
41913e3f4d6SMark Murray 	do_dont_resp[option]++;
42013e3f4d6SMark Murray     }
42113e3f4d6SMark Murray     output_data((const char *)doopt, option);
42213e3f4d6SMark Murray 
42313e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: send do", option));
42413e3f4d6SMark Murray }
42513e3f4d6SMark Murray 
42613e3f4d6SMark Murray #ifdef	AUTHENTICATION
42713e3f4d6SMark Murray extern void auth_request(void);
42813e3f4d6SMark Murray #endif
42913e3f4d6SMark Murray #ifdef	ENCRYPTION
430c19800e8SDoug Rabson extern void encrypt_send_support(void);
43113e3f4d6SMark Murray #endif
43213e3f4d6SMark Murray 
43313e3f4d6SMark Murray void
willoption(int option)43413e3f4d6SMark Murray willoption(int option)
43513e3f4d6SMark Murray {
43613e3f4d6SMark Murray     int changeok = 0;
437c19800e8SDoug Rabson     void (*func)(void) = NULL;
43813e3f4d6SMark Murray 
43913e3f4d6SMark Murray     /*
44013e3f4d6SMark Murray      * process input from peer.
44113e3f4d6SMark Murray      */
44213e3f4d6SMark Murray 
44313e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: recv will", option));
44413e3f4d6SMark Murray 
44513e3f4d6SMark Murray     if (do_dont_resp[option]) {
44613e3f4d6SMark Murray 	do_dont_resp[option]--;
44713e3f4d6SMark Murray 	if (do_dont_resp[option] && his_state_is_will(option))
44813e3f4d6SMark Murray 	    do_dont_resp[option]--;
44913e3f4d6SMark Murray     }
45013e3f4d6SMark Murray     if (do_dont_resp[option] == 0) {
45113e3f4d6SMark Murray 	if (his_want_state_is_wont(option)) {
45213e3f4d6SMark Murray 	    switch (option) {
45313e3f4d6SMark Murray 
45413e3f4d6SMark Murray 	    case TELOPT_BINARY:
45513e3f4d6SMark Murray 		init_termbuf();
45613e3f4d6SMark Murray 		tty_binaryin(1);
45713e3f4d6SMark Murray 		set_termbuf();
45813e3f4d6SMark Murray 		changeok++;
45913e3f4d6SMark Murray 		break;
46013e3f4d6SMark Murray 
46113e3f4d6SMark Murray 	    case TELOPT_ECHO:
46213e3f4d6SMark Murray 		/*
46313e3f4d6SMark Murray 		 * See comments below for more info.
46413e3f4d6SMark Murray 		 */
46513e3f4d6SMark Murray 		not42 = 0;	/* looks like a 4.2 system */
46613e3f4d6SMark Murray 		break;
46713e3f4d6SMark Murray 
46813e3f4d6SMark Murray 	    case TELOPT_TM:
46913e3f4d6SMark Murray 		/*
47013e3f4d6SMark Murray 		 * We never respond to a WILL TM, and
47113e3f4d6SMark Murray 		 * we leave the state WONT.
47213e3f4d6SMark Murray 		 */
47313e3f4d6SMark Murray 		return;
47413e3f4d6SMark Murray 
47513e3f4d6SMark Murray 	    case TELOPT_LFLOW:
47613e3f4d6SMark Murray 		/*
47713e3f4d6SMark Murray 		 * If we are going to support flow control
47813e3f4d6SMark Murray 		 * option, then don't worry peer that we can't
47913e3f4d6SMark Murray 		 * change the flow control characters.
48013e3f4d6SMark Murray 		 */
48113e3f4d6SMark Murray 		slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
48213e3f4d6SMark Murray 		slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
48313e3f4d6SMark Murray 		slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
48413e3f4d6SMark Murray 		slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
48513e3f4d6SMark Murray 	    case TELOPT_TTYPE:
48613e3f4d6SMark Murray 	    case TELOPT_SGA:
48713e3f4d6SMark Murray 	    case TELOPT_NAWS:
48813e3f4d6SMark Murray 	    case TELOPT_TSPEED:
48913e3f4d6SMark Murray 	    case TELOPT_XDISPLOC:
49013e3f4d6SMark Murray 	    case TELOPT_NEW_ENVIRON:
49113e3f4d6SMark Murray 	    case TELOPT_OLD_ENVIRON:
49213e3f4d6SMark Murray 		changeok++;
49313e3f4d6SMark Murray 		break;
49413e3f4d6SMark Murray 
49513e3f4d6SMark Murray 
49613e3f4d6SMark Murray #ifdef	AUTHENTICATION
49713e3f4d6SMark Murray 	    case TELOPT_AUTHENTICATION:
49813e3f4d6SMark Murray 		func = auth_request;
49913e3f4d6SMark Murray 		changeok++;
50013e3f4d6SMark Murray 		break;
50113e3f4d6SMark Murray #endif
50213e3f4d6SMark Murray 
50313e3f4d6SMark Murray #ifdef	ENCRYPTION
50413e3f4d6SMark Murray 	    case TELOPT_ENCRYPT:
50513e3f4d6SMark Murray 		func = encrypt_send_support;
50613e3f4d6SMark Murray 		changeok++;
50713e3f4d6SMark Murray 		break;
50813e3f4d6SMark Murray #endif
50913e3f4d6SMark Murray 
51013e3f4d6SMark Murray 	    default:
51113e3f4d6SMark Murray 		break;
51213e3f4d6SMark Murray 	    }
51313e3f4d6SMark Murray 	    if (changeok) {
51413e3f4d6SMark Murray 		set_his_want_state_will(option);
51513e3f4d6SMark Murray 		send_do(option, 0);
51613e3f4d6SMark Murray 	    } else {
51713e3f4d6SMark Murray 		do_dont_resp[option]++;
51813e3f4d6SMark Murray 		send_dont(option, 0);
51913e3f4d6SMark Murray 	    }
52013e3f4d6SMark Murray 	} else {
52113e3f4d6SMark Murray 	    /*
52213e3f4d6SMark Murray 	     * Option processing that should happen when
52313e3f4d6SMark Murray 	     * we receive conformation of a change in
52413e3f4d6SMark Murray 	     * state that we had requested.
52513e3f4d6SMark Murray 	     */
52613e3f4d6SMark Murray 	    switch (option) {
52713e3f4d6SMark Murray 	    case TELOPT_ECHO:
52813e3f4d6SMark Murray 		not42 = 0;	/* looks like a 4.2 system */
52913e3f4d6SMark Murray 		/*
53013e3f4d6SMark Murray 		 * Egads, he responded "WILL ECHO".  Turn
53113e3f4d6SMark Murray 		 * it off right now!
53213e3f4d6SMark Murray 		 */
53313e3f4d6SMark Murray 		send_dont(option, 1);
53413e3f4d6SMark Murray 		/*
53513e3f4d6SMark Murray 		 * "WILL ECHO".  Kludge upon kludge!
53613e3f4d6SMark Murray 		 * A 4.2 client is now echoing user input at
53713e3f4d6SMark Murray 		 * the tty.  This is probably undesireable and
53813e3f4d6SMark Murray 		 * it should be stopped.  The client will
53913e3f4d6SMark Murray 		 * respond WONT TM to the DO TM that we send to
54013e3f4d6SMark Murray 		 * check for kludge linemode.  When the WONT TM
54113e3f4d6SMark Murray 		 * arrives, linemode will be turned off and a
54213e3f4d6SMark Murray 		 * change propogated to the pty.  This change
54313e3f4d6SMark Murray 		 * will cause us to process the new pty state
54413e3f4d6SMark Murray 		 * in localstat(), which will notice that
54513e3f4d6SMark Murray 		 * linemode is off and send a WILL ECHO
54613e3f4d6SMark Murray 		 * so that we are properly in character mode and
54713e3f4d6SMark Murray 		 * all is well.
54813e3f4d6SMark Murray 		 */
54913e3f4d6SMark Murray 		break;
55013e3f4d6SMark Murray 
55113e3f4d6SMark Murray #ifdef	AUTHENTICATION
55213e3f4d6SMark Murray 	    case TELOPT_AUTHENTICATION:
55313e3f4d6SMark Murray 		func = auth_request;
55413e3f4d6SMark Murray 		break;
55513e3f4d6SMark Murray #endif
55613e3f4d6SMark Murray 
55713e3f4d6SMark Murray #ifdef	ENCRYPTION
55813e3f4d6SMark Murray 	    case TELOPT_ENCRYPT:
55913e3f4d6SMark Murray 		func = encrypt_send_support;
56013e3f4d6SMark Murray 		break;
56113e3f4d6SMark Murray #endif
56213e3f4d6SMark Murray 
56313e3f4d6SMark Murray 	    case TELOPT_LFLOW:
56413e3f4d6SMark Murray 		func = flowstat;
56513e3f4d6SMark Murray 		break;
56613e3f4d6SMark Murray 	    }
56713e3f4d6SMark Murray 	}
56813e3f4d6SMark Murray     }
56913e3f4d6SMark Murray     set_his_state_will(option);
57013e3f4d6SMark Murray     if (func)
57113e3f4d6SMark Murray 	(*func)();
57213e3f4d6SMark Murray }  /* end of willoption */
57313e3f4d6SMark Murray 
57413e3f4d6SMark Murray void
send_dont(int option,int init)57513e3f4d6SMark Murray send_dont(int option, int init)
57613e3f4d6SMark Murray {
57713e3f4d6SMark Murray     if (init) {
57813e3f4d6SMark Murray 	if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
57913e3f4d6SMark Murray 	    his_want_state_is_wont(option))
58013e3f4d6SMark Murray 	    return;
58113e3f4d6SMark Murray 	set_his_want_state_wont(option);
58213e3f4d6SMark Murray 	do_dont_resp[option]++;
58313e3f4d6SMark Murray     }
58413e3f4d6SMark Murray     output_data((const char *)dont, option);
58513e3f4d6SMark Murray 
58613e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: send dont", option));
58713e3f4d6SMark Murray }
58813e3f4d6SMark Murray 
58913e3f4d6SMark Murray void
wontoption(int option)59013e3f4d6SMark Murray wontoption(int option)
59113e3f4d6SMark Murray {
59213e3f4d6SMark Murray     /*
59313e3f4d6SMark Murray      * Process client input.
59413e3f4d6SMark Murray 	 */
59513e3f4d6SMark Murray 
59613e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: recv wont", option));
59713e3f4d6SMark Murray 
59813e3f4d6SMark Murray     if (do_dont_resp[option]) {
59913e3f4d6SMark Murray 	do_dont_resp[option]--;
60013e3f4d6SMark Murray 	if (do_dont_resp[option] && his_state_is_wont(option))
60113e3f4d6SMark Murray 	    do_dont_resp[option]--;
60213e3f4d6SMark Murray     }
60313e3f4d6SMark Murray     if (do_dont_resp[option] == 0) {
60413e3f4d6SMark Murray 	if (his_want_state_is_will(option)) {
60513e3f4d6SMark Murray 	    /* it is always ok to change to negative state */
60613e3f4d6SMark Murray 	    switch (option) {
60713e3f4d6SMark Murray 	    case TELOPT_ECHO:
60813e3f4d6SMark Murray 		not42 = 1; /* doesn't seem to be a 4.2 system */
60913e3f4d6SMark Murray 		break;
61013e3f4d6SMark Murray 
61113e3f4d6SMark Murray 	    case TELOPT_BINARY:
61213e3f4d6SMark Murray 		init_termbuf();
61313e3f4d6SMark Murray 		tty_binaryin(0);
61413e3f4d6SMark Murray 		set_termbuf();
61513e3f4d6SMark Murray 		break;
61613e3f4d6SMark Murray 
61713e3f4d6SMark Murray 	    case TELOPT_TM:
61813e3f4d6SMark Murray 		/*
61913e3f4d6SMark Murray 		 * If we get a WONT TM, and had sent a DO TM,
62013e3f4d6SMark Murray 		 * don't respond with a DONT TM, just leave it
62113e3f4d6SMark Murray 		 * as is.  Short circut the state machine to
62213e3f4d6SMark Murray 		 * achive this.
62313e3f4d6SMark Murray 		 */
62413e3f4d6SMark Murray 		set_his_want_state_wont(TELOPT_TM);
62513e3f4d6SMark Murray 		return;
62613e3f4d6SMark Murray 
62713e3f4d6SMark Murray 	    case TELOPT_LFLOW:
62813e3f4d6SMark Murray 		/*
62913e3f4d6SMark Murray 		 * If we are not going to support flow control
63013e3f4d6SMark Murray 		 * option, then let peer know that we can't
63113e3f4d6SMark Murray 		 * change the flow control characters.
63213e3f4d6SMark Murray 		 */
63313e3f4d6SMark Murray 		slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
63413e3f4d6SMark Murray 		slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
63513e3f4d6SMark Murray 		slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
63613e3f4d6SMark Murray 		slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
63713e3f4d6SMark Murray 		break;
63813e3f4d6SMark Murray 
63913e3f4d6SMark Murray #ifdef AUTHENTICATION
64013e3f4d6SMark Murray 	    case TELOPT_AUTHENTICATION:
64113e3f4d6SMark Murray 		auth_finished(0, AUTH_REJECT);
64213e3f4d6SMark Murray 		break;
64313e3f4d6SMark Murray #endif
64413e3f4d6SMark Murray 
64513e3f4d6SMark Murray 		/*
64613e3f4d6SMark Murray 		 * For options that we might spin waiting for
64713e3f4d6SMark Murray 		 * sub-negotiation, if the client turns off the
64813e3f4d6SMark Murray 		 * option rather than responding to the request,
64913e3f4d6SMark Murray 		 * we have to treat it here as if we got a response
65013e3f4d6SMark Murray 		 * to the sub-negotiation, (by updating the timers)
65113e3f4d6SMark Murray 		 * so that we'll break out of the loop.
65213e3f4d6SMark Murray 		 */
65313e3f4d6SMark Murray 	    case TELOPT_TTYPE:
65413e3f4d6SMark Murray 		settimer(ttypesubopt);
65513e3f4d6SMark Murray 		break;
65613e3f4d6SMark Murray 
65713e3f4d6SMark Murray 	    case TELOPT_TSPEED:
65813e3f4d6SMark Murray 		settimer(tspeedsubopt);
65913e3f4d6SMark Murray 		break;
66013e3f4d6SMark Murray 
66113e3f4d6SMark Murray 	    case TELOPT_XDISPLOC:
66213e3f4d6SMark Murray 		settimer(xdisplocsubopt);
66313e3f4d6SMark Murray 		break;
66413e3f4d6SMark Murray 
66513e3f4d6SMark Murray 	    case TELOPT_OLD_ENVIRON:
66613e3f4d6SMark Murray 		settimer(oenvironsubopt);
66713e3f4d6SMark Murray 		break;
66813e3f4d6SMark Murray 
66913e3f4d6SMark Murray 	    case TELOPT_NEW_ENVIRON:
67013e3f4d6SMark Murray 		settimer(environsubopt);
67113e3f4d6SMark Murray 		break;
67213e3f4d6SMark Murray 
67313e3f4d6SMark Murray 	    default:
67413e3f4d6SMark Murray 		break;
67513e3f4d6SMark Murray 	    }
67613e3f4d6SMark Murray 	    set_his_want_state_wont(option);
67713e3f4d6SMark Murray 	    if (his_state_is_will(option))
67813e3f4d6SMark Murray 		send_dont(option, 0);
67913e3f4d6SMark Murray 	} else {
68013e3f4d6SMark Murray 	    switch (option) {
68113e3f4d6SMark Murray 	    case TELOPT_TM:
68213e3f4d6SMark Murray 		break;
68313e3f4d6SMark Murray 
68413e3f4d6SMark Murray #ifdef AUTHENTICATION
68513e3f4d6SMark Murray 	    case TELOPT_AUTHENTICATION:
68613e3f4d6SMark Murray 		auth_finished(0, AUTH_REJECT);
68713e3f4d6SMark Murray 		break;
68813e3f4d6SMark Murray #endif
68913e3f4d6SMark Murray 	    default:
69013e3f4d6SMark Murray 		break;
69113e3f4d6SMark Murray 	    }
69213e3f4d6SMark Murray 	}
69313e3f4d6SMark Murray     }
69413e3f4d6SMark Murray     set_his_state_wont(option);
69513e3f4d6SMark Murray 
69613e3f4d6SMark Murray }  /* end of wontoption */
69713e3f4d6SMark Murray 
69813e3f4d6SMark Murray void
send_will(int option,int init)69913e3f4d6SMark Murray send_will(int option, int init)
70013e3f4d6SMark Murray {
70113e3f4d6SMark Murray     if (init) {
70213e3f4d6SMark Murray 	if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
70313e3f4d6SMark Murray 	    my_want_state_is_will(option))
70413e3f4d6SMark Murray 	    return;
70513e3f4d6SMark Murray 	set_my_want_state_will(option);
70613e3f4d6SMark Murray 	will_wont_resp[option]++;
70713e3f4d6SMark Murray     }
70813e3f4d6SMark Murray     output_data ((const char *)will, option);
70913e3f4d6SMark Murray 
71013e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: send will", option));
71113e3f4d6SMark Murray }
71213e3f4d6SMark Murray 
71313e3f4d6SMark Murray /*
71413e3f4d6SMark Murray  * When we get a DONT SGA, we will try once to turn it
71513e3f4d6SMark Murray  * back on.  If the other side responds DONT SGA, we
71613e3f4d6SMark Murray  * leave it at that.  This is so that when we talk to
71713e3f4d6SMark Murray  * clients that understand KLUDGELINEMODE but not LINEMODE,
71813e3f4d6SMark Murray  * we'll keep them in char-at-a-time mode.
71913e3f4d6SMark Murray  */
72013e3f4d6SMark Murray int turn_on_sga = 0;
72113e3f4d6SMark Murray 
72213e3f4d6SMark Murray void
dooption(int option)72313e3f4d6SMark Murray dooption(int option)
72413e3f4d6SMark Murray {
72513e3f4d6SMark Murray     int changeok = 0;
72613e3f4d6SMark Murray 
72713e3f4d6SMark Murray     /*
72813e3f4d6SMark Murray      * Process client input.
72913e3f4d6SMark Murray      */
73013e3f4d6SMark Murray 
73113e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: recv do", option));
73213e3f4d6SMark Murray 
73313e3f4d6SMark Murray     if (will_wont_resp[option]) {
73413e3f4d6SMark Murray 	will_wont_resp[option]--;
73513e3f4d6SMark Murray 	if (will_wont_resp[option] && my_state_is_will(option))
73613e3f4d6SMark Murray 	    will_wont_resp[option]--;
73713e3f4d6SMark Murray     }
73813e3f4d6SMark Murray     if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
73913e3f4d6SMark Murray 	switch (option) {
74013e3f4d6SMark Murray 	case TELOPT_ECHO:
74113e3f4d6SMark Murray 	    {
74213e3f4d6SMark Murray 		init_termbuf();
74313e3f4d6SMark Murray 		tty_setecho(1);
74413e3f4d6SMark Murray 		set_termbuf();
74513e3f4d6SMark Murray 	    }
74613e3f4d6SMark Murray 	changeok++;
74713e3f4d6SMark Murray 	break;
74813e3f4d6SMark Murray 
74913e3f4d6SMark Murray 	case TELOPT_BINARY:
75013e3f4d6SMark Murray 	    init_termbuf();
75113e3f4d6SMark Murray 	    tty_binaryout(1);
75213e3f4d6SMark Murray 	    set_termbuf();
75313e3f4d6SMark Murray 	    changeok++;
75413e3f4d6SMark Murray 	    break;
75513e3f4d6SMark Murray 
75613e3f4d6SMark Murray 	case TELOPT_SGA:
75713e3f4d6SMark Murray 	    turn_on_sga = 0;
75813e3f4d6SMark Murray 	    changeok++;
75913e3f4d6SMark Murray 	    break;
76013e3f4d6SMark Murray 
76113e3f4d6SMark Murray 	case TELOPT_STATUS:
76213e3f4d6SMark Murray 	    changeok++;
76313e3f4d6SMark Murray 	    break;
76413e3f4d6SMark Murray 
76513e3f4d6SMark Murray 	case TELOPT_TM:
76613e3f4d6SMark Murray 	    /*
76713e3f4d6SMark Murray 	     * Special case for TM.  We send a WILL, but
76813e3f4d6SMark Murray 	     * pretend we sent a WONT.
76913e3f4d6SMark Murray 	     */
77013e3f4d6SMark Murray 	    send_will(option, 0);
77113e3f4d6SMark Murray 	    set_my_want_state_wont(option);
77213e3f4d6SMark Murray 	    set_my_state_wont(option);
77313e3f4d6SMark Murray 	    return;
77413e3f4d6SMark Murray 
77513e3f4d6SMark Murray 	case TELOPT_LOGOUT:
77613e3f4d6SMark Murray 	    /*
77713e3f4d6SMark Murray 	     * When we get a LOGOUT option, respond
77813e3f4d6SMark Murray 	     * with a WILL LOGOUT, make sure that
77913e3f4d6SMark Murray 	     * it gets written out to the network,
78013e3f4d6SMark Murray 	     * and then just go away...
78113e3f4d6SMark Murray 	     */
78213e3f4d6SMark Murray 	    set_my_want_state_will(TELOPT_LOGOUT);
78313e3f4d6SMark Murray 	    send_will(TELOPT_LOGOUT, 0);
78413e3f4d6SMark Murray 	    set_my_state_will(TELOPT_LOGOUT);
78513e3f4d6SMark Murray 	    netflush();
78613e3f4d6SMark Murray 	    cleanup(0);
78713e3f4d6SMark Murray 	    /* NOT REACHED */
78813e3f4d6SMark Murray 	    break;
78913e3f4d6SMark Murray 
79013e3f4d6SMark Murray #ifdef ENCRYPTION
79113e3f4d6SMark Murray 	case TELOPT_ENCRYPT:
79213e3f4d6SMark Murray 	    changeok++;
79313e3f4d6SMark Murray 	    break;
79413e3f4d6SMark Murray #endif
79513e3f4d6SMark Murray 	case TELOPT_LINEMODE:
79613e3f4d6SMark Murray 	case TELOPT_TTYPE:
79713e3f4d6SMark Murray 	case TELOPT_NAWS:
79813e3f4d6SMark Murray 	case TELOPT_TSPEED:
79913e3f4d6SMark Murray 	case TELOPT_LFLOW:
80013e3f4d6SMark Murray 	case TELOPT_XDISPLOC:
80113e3f4d6SMark Murray #ifdef	TELOPT_ENVIRON
80213e3f4d6SMark Murray 	case TELOPT_NEW_ENVIRON:
80313e3f4d6SMark Murray #endif
80413e3f4d6SMark Murray 	case TELOPT_OLD_ENVIRON:
80513e3f4d6SMark Murray 	default:
80613e3f4d6SMark Murray 	    break;
80713e3f4d6SMark Murray 	}
80813e3f4d6SMark Murray 	if (changeok) {
80913e3f4d6SMark Murray 	    set_my_want_state_will(option);
81013e3f4d6SMark Murray 	    send_will(option, 0);
81113e3f4d6SMark Murray 	} else {
81213e3f4d6SMark Murray 	    will_wont_resp[option]++;
81313e3f4d6SMark Murray 	    send_wont(option, 0);
81413e3f4d6SMark Murray 	}
81513e3f4d6SMark Murray     }
81613e3f4d6SMark Murray     set_my_state_will(option);
81713e3f4d6SMark Murray 
81813e3f4d6SMark Murray }  /* end of dooption */
81913e3f4d6SMark Murray 
82013e3f4d6SMark Murray void
send_wont(int option,int init)82113e3f4d6SMark Murray send_wont(int option, int init)
82213e3f4d6SMark Murray {
82313e3f4d6SMark Murray     if (init) {
82413e3f4d6SMark Murray 	if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
82513e3f4d6SMark Murray 	    my_want_state_is_wont(option))
82613e3f4d6SMark Murray 	    return;
82713e3f4d6SMark Murray 	set_my_want_state_wont(option);
82813e3f4d6SMark Murray 	will_wont_resp[option]++;
82913e3f4d6SMark Murray     }
83013e3f4d6SMark Murray     output_data ((const char *)wont, option);
83113e3f4d6SMark Murray 
83213e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: send wont", option));
83313e3f4d6SMark Murray }
83413e3f4d6SMark Murray 
83513e3f4d6SMark Murray void
dontoption(int option)83613e3f4d6SMark Murray dontoption(int option)
83713e3f4d6SMark Murray {
83813e3f4d6SMark Murray     /*
83913e3f4d6SMark Murray      * Process client input.
84013e3f4d6SMark Murray 	 */
84113e3f4d6SMark Murray 
84213e3f4d6SMark Murray 
84313e3f4d6SMark Murray     DIAG(TD_OPTIONS, printoption("td: recv dont", option));
84413e3f4d6SMark Murray 
84513e3f4d6SMark Murray     if (will_wont_resp[option]) {
84613e3f4d6SMark Murray 	will_wont_resp[option]--;
84713e3f4d6SMark Murray 	if (will_wont_resp[option] && my_state_is_wont(option))
84813e3f4d6SMark Murray 	    will_wont_resp[option]--;
84913e3f4d6SMark Murray     }
85013e3f4d6SMark Murray     if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
85113e3f4d6SMark Murray 	switch (option) {
85213e3f4d6SMark Murray 	case TELOPT_BINARY:
85313e3f4d6SMark Murray 	    init_termbuf();
85413e3f4d6SMark Murray 	    tty_binaryout(0);
85513e3f4d6SMark Murray 	    set_termbuf();
85613e3f4d6SMark Murray 	    break;
85713e3f4d6SMark Murray 
85813e3f4d6SMark Murray 	case TELOPT_ECHO:	/* we should stop echoing */
85913e3f4d6SMark Murray 	    {
86013e3f4d6SMark Murray 		init_termbuf();
86113e3f4d6SMark Murray 		tty_setecho(0);
86213e3f4d6SMark Murray 		set_termbuf();
86313e3f4d6SMark Murray 	    }
86413e3f4d6SMark Murray 	break;
86513e3f4d6SMark Murray 
86613e3f4d6SMark Murray 	case TELOPT_SGA:
86713e3f4d6SMark Murray 	    set_my_want_state_wont(option);
86813e3f4d6SMark Murray 	    if (my_state_is_will(option))
86913e3f4d6SMark Murray 		send_wont(option, 0);
87013e3f4d6SMark Murray 	    set_my_state_wont(option);
87113e3f4d6SMark Murray 	    if (turn_on_sga ^= 1)
87213e3f4d6SMark Murray 		send_will(option, 1);
87313e3f4d6SMark Murray 	    return;
87413e3f4d6SMark Murray 
87513e3f4d6SMark Murray 	default:
87613e3f4d6SMark Murray 	    break;
87713e3f4d6SMark Murray 	}
87813e3f4d6SMark Murray 
87913e3f4d6SMark Murray 	set_my_want_state_wont(option);
88013e3f4d6SMark Murray 	if (my_state_is_will(option))
88113e3f4d6SMark Murray 	    send_wont(option, 0);
88213e3f4d6SMark Murray     }
88313e3f4d6SMark Murray     set_my_state_wont(option);
88413e3f4d6SMark Murray 
88513e3f4d6SMark Murray }  /* end of dontoption */
88613e3f4d6SMark Murray 
88713e3f4d6SMark Murray #ifdef	ENV_HACK
88813e3f4d6SMark Murray int env_ovar = -1;
88913e3f4d6SMark Murray int env_ovalue = -1;
89013e3f4d6SMark Murray #else	/* ENV_HACK */
89113e3f4d6SMark Murray # define env_ovar OLD_ENV_VAR
89213e3f4d6SMark Murray # define env_ovalue OLD_ENV_VALUE
89313e3f4d6SMark Murray #endif	/* ENV_HACK */
89413e3f4d6SMark Murray 
89513e3f4d6SMark Murray /*
89613e3f4d6SMark Murray  * suboption()
89713e3f4d6SMark Murray  *
89813e3f4d6SMark Murray  *	Look at the sub-option buffer, and try to be helpful to the other
89913e3f4d6SMark Murray  * side.
90013e3f4d6SMark Murray  *
90113e3f4d6SMark Murray  *	Currently we recognize:
90213e3f4d6SMark Murray  *
90313e3f4d6SMark Murray  *	Terminal type is
90413e3f4d6SMark Murray  *	Linemode
90513e3f4d6SMark Murray  *	Window size
90613e3f4d6SMark Murray  *	Terminal speed
90713e3f4d6SMark Murray  */
90813e3f4d6SMark Murray void
suboption(void)90913e3f4d6SMark Murray suboption(void)
91013e3f4d6SMark Murray {
91113e3f4d6SMark Murray     int subchar;
91213e3f4d6SMark Murray 
91313e3f4d6SMark Murray     DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
91413e3f4d6SMark Murray 
91513e3f4d6SMark Murray     subchar = SB_GET();
91613e3f4d6SMark Murray     switch (subchar) {
91713e3f4d6SMark Murray     case TELOPT_TSPEED: {
91813e3f4d6SMark Murray 	int xspeed, rspeed;
91913e3f4d6SMark Murray 
92013e3f4d6SMark Murray 	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
92113e3f4d6SMark Murray 	    break;
92213e3f4d6SMark Murray 
92313e3f4d6SMark Murray 	settimer(tspeedsubopt);
92413e3f4d6SMark Murray 
92513e3f4d6SMark Murray 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
92613e3f4d6SMark Murray 	    return;
92713e3f4d6SMark Murray 
92813e3f4d6SMark Murray 	xspeed = atoi((char *)subpointer);
92913e3f4d6SMark Murray 
93013e3f4d6SMark Murray 	while (SB_GET() != ',' && !SB_EOF());
93113e3f4d6SMark Murray 	if (SB_EOF())
93213e3f4d6SMark Murray 	    return;
93313e3f4d6SMark Murray 
93413e3f4d6SMark Murray 	rspeed = atoi((char *)subpointer);
93513e3f4d6SMark Murray 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
93613e3f4d6SMark Murray 
93713e3f4d6SMark Murray 	break;
93813e3f4d6SMark Murray 
93913e3f4d6SMark Murray     }  /* end of case TELOPT_TSPEED */
94013e3f4d6SMark Murray 
94113e3f4d6SMark Murray     case TELOPT_TTYPE: {		/* Yaaaay! */
942c19800e8SDoug Rabson 	char *p;
94313e3f4d6SMark Murray 
94413e3f4d6SMark Murray 	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
94513e3f4d6SMark Murray 	    break;
94613e3f4d6SMark Murray 	settimer(ttypesubopt);
94713e3f4d6SMark Murray 
94813e3f4d6SMark Murray 	if (SB_EOF() || SB_GET() != TELQUAL_IS) {
94913e3f4d6SMark Murray 	    return;		/* ??? XXX but, this is the most robust */
95013e3f4d6SMark Murray 	}
95113e3f4d6SMark Murray 
952c19800e8SDoug Rabson 	p = terminaltype;
95313e3f4d6SMark Murray 
954c19800e8SDoug Rabson 	while ((p < (terminaltype + sizeof terminaltype-1)) &&
95513e3f4d6SMark Murray 	       !SB_EOF()) {
95613e3f4d6SMark Murray 	    int c;
95713e3f4d6SMark Murray 
95813e3f4d6SMark Murray 	    c = SB_GET();
95913e3f4d6SMark Murray 	    if (isupper(c)) {
96013e3f4d6SMark Murray 		c = tolower(c);
96113e3f4d6SMark Murray 	    }
962c19800e8SDoug Rabson 	    *p++ = c;    /* accumulate name */
96313e3f4d6SMark Murray 	}
964c19800e8SDoug Rabson 	*p = 0;
96513e3f4d6SMark Murray 	break;
96613e3f4d6SMark Murray     }  /* end of case TELOPT_TTYPE */
96713e3f4d6SMark Murray 
96813e3f4d6SMark Murray     case TELOPT_NAWS: {
96913e3f4d6SMark Murray 	int xwinsize, ywinsize;
97013e3f4d6SMark Murray 
97113e3f4d6SMark Murray 	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
97213e3f4d6SMark Murray 	    break;
97313e3f4d6SMark Murray 
97413e3f4d6SMark Murray 	if (SB_EOF())
97513e3f4d6SMark Murray 	    return;
97613e3f4d6SMark Murray 	xwinsize = SB_GET() << 8;
97713e3f4d6SMark Murray 	if (SB_EOF())
97813e3f4d6SMark Murray 	    return;
97913e3f4d6SMark Murray 	xwinsize |= SB_GET();
98013e3f4d6SMark Murray 	if (SB_EOF())
98113e3f4d6SMark Murray 	    return;
98213e3f4d6SMark Murray 	ywinsize = SB_GET() << 8;
98313e3f4d6SMark Murray 	if (SB_EOF())
98413e3f4d6SMark Murray 	    return;
98513e3f4d6SMark Murray 	ywinsize |= SB_GET();
98613e3f4d6SMark Murray 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
98713e3f4d6SMark Murray 
98813e3f4d6SMark Murray 	break;
98913e3f4d6SMark Murray 
99013e3f4d6SMark Murray     }  /* end of case TELOPT_NAWS */
99113e3f4d6SMark Murray 
99213e3f4d6SMark Murray     case TELOPT_STATUS: {
99313e3f4d6SMark Murray 	int mode;
99413e3f4d6SMark Murray 
99513e3f4d6SMark Murray 	if (SB_EOF())
99613e3f4d6SMark Murray 	    break;
99713e3f4d6SMark Murray 	mode = SB_GET();
99813e3f4d6SMark Murray 	switch (mode) {
99913e3f4d6SMark Murray 	case TELQUAL_SEND:
100013e3f4d6SMark Murray 	    if (my_state_is_will(TELOPT_STATUS))
100113e3f4d6SMark Murray 		send_status();
100213e3f4d6SMark Murray 	    break;
100313e3f4d6SMark Murray 
100413e3f4d6SMark Murray 	case TELQUAL_IS:
100513e3f4d6SMark Murray 	    break;
100613e3f4d6SMark Murray 
100713e3f4d6SMark Murray 	default:
100813e3f4d6SMark Murray 	    break;
100913e3f4d6SMark Murray 	}
101013e3f4d6SMark Murray 	break;
101113e3f4d6SMark Murray     }  /* end of case TELOPT_STATUS */
101213e3f4d6SMark Murray 
101313e3f4d6SMark Murray     case TELOPT_XDISPLOC: {
101413e3f4d6SMark Murray 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
101513e3f4d6SMark Murray 	    return;
101613e3f4d6SMark Murray 	settimer(xdisplocsubopt);
101713e3f4d6SMark Murray 	subpointer[SB_LEN()] = '\0';
10185e9cd1aeSAssar Westerlund 	esetenv("DISPLAY", (char *)subpointer, 1);
101913e3f4d6SMark Murray 	break;
102013e3f4d6SMark Murray     }  /* end of case TELOPT_XDISPLOC */
102113e3f4d6SMark Murray 
102213e3f4d6SMark Murray #ifdef	TELOPT_NEW_ENVIRON
102313e3f4d6SMark Murray     case TELOPT_NEW_ENVIRON:
102413e3f4d6SMark Murray #endif
102513e3f4d6SMark Murray     case TELOPT_OLD_ENVIRON: {
102613e3f4d6SMark Murray 	int c;
102713e3f4d6SMark Murray 	char *cp, *varp, *valp;
102813e3f4d6SMark Murray 
102913e3f4d6SMark Murray 	if (SB_EOF())
103013e3f4d6SMark Murray 	    return;
103113e3f4d6SMark Murray 	c = SB_GET();
103213e3f4d6SMark Murray 	if (c == TELQUAL_IS) {
103313e3f4d6SMark Murray 	    if (subchar == TELOPT_OLD_ENVIRON)
103413e3f4d6SMark Murray 		settimer(oenvironsubopt);
103513e3f4d6SMark Murray 	    else
103613e3f4d6SMark Murray 		settimer(environsubopt);
103713e3f4d6SMark Murray 	} else if (c != TELQUAL_INFO) {
103813e3f4d6SMark Murray 	    return;
103913e3f4d6SMark Murray 	}
104013e3f4d6SMark Murray 
104113e3f4d6SMark Murray #ifdef	TELOPT_NEW_ENVIRON
104213e3f4d6SMark Murray 	if (subchar == TELOPT_NEW_ENVIRON) {
104313e3f4d6SMark Murray 	    while (!SB_EOF()) {
104413e3f4d6SMark Murray 		c = SB_GET();
104513e3f4d6SMark Murray 		if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
104613e3f4d6SMark Murray 		    break;
104713e3f4d6SMark Murray 	    }
104813e3f4d6SMark Murray 	} else
104913e3f4d6SMark Murray #endif
105013e3f4d6SMark Murray 	    {
105113e3f4d6SMark Murray #ifdef	ENV_HACK
105213e3f4d6SMark Murray 		/*
105313e3f4d6SMark Murray 		 * We only want to do this if we haven't already decided
105413e3f4d6SMark Murray 		 * whether or not the other side has its VALUE and VAR
105513e3f4d6SMark Murray 		 * reversed.
105613e3f4d6SMark Murray 		 */
105713e3f4d6SMark Murray 		if (env_ovar < 0) {
105813e3f4d6SMark Murray 		    int last = -1;		/* invalid value */
105913e3f4d6SMark Murray 		    int empty = 0;
106013e3f4d6SMark Murray 		    int got_var = 0, got_value = 0, got_uservar = 0;
106113e3f4d6SMark Murray 
106213e3f4d6SMark Murray 		    /*
106313e3f4d6SMark Murray 		     * The other side might have its VALUE and VAR values
106413e3f4d6SMark Murray 		     * reversed.  To be interoperable, we need to determine
106513e3f4d6SMark Murray 		     * which way it is.  If the first recognized character
106613e3f4d6SMark Murray 		     * is a VAR or VALUE, then that will tell us what
106713e3f4d6SMark Murray 		     * type of client it is.  If the fist recognized
106813e3f4d6SMark Murray 		     * character is a USERVAR, then we continue scanning
106913e3f4d6SMark Murray 		     * the suboption looking for two consecutive
107013e3f4d6SMark Murray 		     * VAR or VALUE fields.  We should not get two
107113e3f4d6SMark Murray 		     * consecutive VALUE fields, so finding two
107213e3f4d6SMark Murray 		     * consecutive VALUE or VAR fields will tell us
107313e3f4d6SMark Murray 		     * what the client is.
107413e3f4d6SMark Murray 		     */
107513e3f4d6SMark Murray 		    SB_SAVE();
107613e3f4d6SMark Murray 		    while (!SB_EOF()) {
107713e3f4d6SMark Murray 			c = SB_GET();
107813e3f4d6SMark Murray 			switch(c) {
107913e3f4d6SMark Murray 			case OLD_ENV_VAR:
108013e3f4d6SMark Murray 			    if (last < 0 || last == OLD_ENV_VAR
108113e3f4d6SMark Murray 				|| (empty && (last == OLD_ENV_VALUE)))
108213e3f4d6SMark Murray 				goto env_ovar_ok;
108313e3f4d6SMark Murray 			    got_var++;
108413e3f4d6SMark Murray 			    last = OLD_ENV_VAR;
108513e3f4d6SMark Murray 			    break;
108613e3f4d6SMark Murray 			case OLD_ENV_VALUE:
108713e3f4d6SMark Murray 			    if (last < 0 || last == OLD_ENV_VALUE
108813e3f4d6SMark Murray 				|| (empty && (last == OLD_ENV_VAR)))
108913e3f4d6SMark Murray 				goto env_ovar_wrong;
109013e3f4d6SMark Murray 			    got_value++;
109113e3f4d6SMark Murray 			    last = OLD_ENV_VALUE;
109213e3f4d6SMark Murray 			    break;
109313e3f4d6SMark Murray 			case ENV_USERVAR:
109413e3f4d6SMark Murray 			    /* count strings of USERVAR as one */
109513e3f4d6SMark Murray 			    if (last != ENV_USERVAR)
109613e3f4d6SMark Murray 				got_uservar++;
109713e3f4d6SMark Murray 			    if (empty) {
109813e3f4d6SMark Murray 				if (last == OLD_ENV_VALUE)
109913e3f4d6SMark Murray 				    goto env_ovar_ok;
110013e3f4d6SMark Murray 				if (last == OLD_ENV_VAR)
110113e3f4d6SMark Murray 				    goto env_ovar_wrong;
110213e3f4d6SMark Murray 			    }
110313e3f4d6SMark Murray 			    last = ENV_USERVAR;
110413e3f4d6SMark Murray 			    break;
110513e3f4d6SMark Murray 			case ENV_ESC:
110613e3f4d6SMark Murray 			    if (!SB_EOF())
110713e3f4d6SMark Murray 				c = SB_GET();
110813e3f4d6SMark Murray 			    /* FALL THROUGH */
110913e3f4d6SMark Murray 			default:
111013e3f4d6SMark Murray 			    empty = 0;
111113e3f4d6SMark Murray 			    continue;
111213e3f4d6SMark Murray 			}
111313e3f4d6SMark Murray 			empty = 1;
111413e3f4d6SMark Murray 		    }
111513e3f4d6SMark Murray 		    if (empty) {
111613e3f4d6SMark Murray 			if (last == OLD_ENV_VALUE)
111713e3f4d6SMark Murray 			    goto env_ovar_ok;
111813e3f4d6SMark Murray 			if (last == OLD_ENV_VAR)
111913e3f4d6SMark Murray 			    goto env_ovar_wrong;
112013e3f4d6SMark Murray 		    }
112113e3f4d6SMark Murray 		    /*
112213e3f4d6SMark Murray 		     * Ok, the first thing was a USERVAR, and there
112313e3f4d6SMark Murray 		     * are not two consecutive VAR or VALUE commands,
112413e3f4d6SMark Murray 		     * and none of the VAR or VALUE commands are empty.
112513e3f4d6SMark Murray 		     * If the client has sent us a well-formed option,
112613e3f4d6SMark Murray 		     * then the number of VALUEs received should always
112713e3f4d6SMark Murray 		     * be less than or equal to the number of VARs and
112813e3f4d6SMark Murray 		     * USERVARs received.
112913e3f4d6SMark Murray 		     *
113013e3f4d6SMark Murray 		     * If we got exactly as many VALUEs as VARs and
113113e3f4d6SMark Murray 		     * USERVARs, the client has the same definitions.
113213e3f4d6SMark Murray 		     *
113313e3f4d6SMark Murray 		     * If we got exactly as many VARs as VALUEs and
113413e3f4d6SMark Murray 		     * USERVARS, the client has reversed definitions.
113513e3f4d6SMark Murray 		     */
113613e3f4d6SMark Murray 		    if (got_uservar + got_var == got_value) {
113713e3f4d6SMark Murray 		    env_ovar_ok:
113813e3f4d6SMark Murray 			env_ovar = OLD_ENV_VAR;
113913e3f4d6SMark Murray 			env_ovalue = OLD_ENV_VALUE;
114013e3f4d6SMark Murray 		    } else if (got_uservar + got_value == got_var) {
114113e3f4d6SMark Murray 		    env_ovar_wrong:
114213e3f4d6SMark Murray 			env_ovar = OLD_ENV_VALUE;
114313e3f4d6SMark Murray 			env_ovalue = OLD_ENV_VAR;
114413e3f4d6SMark Murray 			DIAG(TD_OPTIONS, {
114513e3f4d6SMark Murray 			    output_data("ENVIRON VALUE and VAR are reversed!\r\n");
114613e3f4d6SMark Murray 			});
114713e3f4d6SMark Murray 
114813e3f4d6SMark Murray 		    }
114913e3f4d6SMark Murray 		}
115013e3f4d6SMark Murray 		SB_RESTORE();
115113e3f4d6SMark Murray #endif
115213e3f4d6SMark Murray 
115313e3f4d6SMark Murray 		while (!SB_EOF()) {
115413e3f4d6SMark Murray 		    c = SB_GET();
115513e3f4d6SMark Murray 		    if ((c == env_ovar) || (c == ENV_USERVAR))
115613e3f4d6SMark Murray 			break;
115713e3f4d6SMark Murray 		}
115813e3f4d6SMark Murray 	    }
115913e3f4d6SMark Murray 
116013e3f4d6SMark Murray 	if (SB_EOF())
116113e3f4d6SMark Murray 	    return;
116213e3f4d6SMark Murray 
116313e3f4d6SMark Murray 	cp = varp = (char *)subpointer;
116413e3f4d6SMark Murray 	valp = 0;
116513e3f4d6SMark Murray 
116613e3f4d6SMark Murray 	while (!SB_EOF()) {
116713e3f4d6SMark Murray 	    c = SB_GET();
116813e3f4d6SMark Murray 	    if (subchar == TELOPT_OLD_ENVIRON) {
116913e3f4d6SMark Murray 		if (c == env_ovar)
117013e3f4d6SMark Murray 		    c = NEW_ENV_VAR;
117113e3f4d6SMark Murray 		else if (c == env_ovalue)
117213e3f4d6SMark Murray 		    c = NEW_ENV_VALUE;
117313e3f4d6SMark Murray 	    }
117413e3f4d6SMark Murray 	    switch (c) {
117513e3f4d6SMark Murray 
117613e3f4d6SMark Murray 	    case NEW_ENV_VALUE:
117713e3f4d6SMark Murray 		*cp = '\0';
117813e3f4d6SMark Murray 		cp = valp = (char *)subpointer;
117913e3f4d6SMark Murray 		break;
118013e3f4d6SMark Murray 
118113e3f4d6SMark Murray 	    case NEW_ENV_VAR:
118213e3f4d6SMark Murray 	    case ENV_USERVAR:
118313e3f4d6SMark Murray 		*cp = '\0';
118413e3f4d6SMark Murray 		if (valp)
11855e9cd1aeSAssar Westerlund 		    esetenv(varp, valp, 1);
118613e3f4d6SMark Murray 		else
118713e3f4d6SMark Murray 		    unsetenv(varp);
118813e3f4d6SMark Murray 		cp = varp = (char *)subpointer;
118913e3f4d6SMark Murray 		valp = 0;
119013e3f4d6SMark Murray 		break;
119113e3f4d6SMark Murray 
119213e3f4d6SMark Murray 	    case ENV_ESC:
119313e3f4d6SMark Murray 		if (SB_EOF())
119413e3f4d6SMark Murray 		    break;
119513e3f4d6SMark Murray 		c = SB_GET();
119613e3f4d6SMark Murray 		/* FALL THROUGH */
119713e3f4d6SMark Murray 	    default:
119813e3f4d6SMark Murray 		*cp++ = c;
119913e3f4d6SMark Murray 		break;
120013e3f4d6SMark Murray 	    }
120113e3f4d6SMark Murray 	}
120213e3f4d6SMark Murray 	*cp = '\0';
120313e3f4d6SMark Murray 	if (valp)
12045e9cd1aeSAssar Westerlund 	    esetenv(varp, valp, 1);
120513e3f4d6SMark Murray 	else
120613e3f4d6SMark Murray 	    unsetenv(varp);
120713e3f4d6SMark Murray 	break;
120813e3f4d6SMark Murray     }  /* end of case TELOPT_NEW_ENVIRON */
120913e3f4d6SMark Murray #ifdef AUTHENTICATION
121013e3f4d6SMark Murray     case TELOPT_AUTHENTICATION:
121113e3f4d6SMark Murray 	if (SB_EOF())
121213e3f4d6SMark Murray 	    break;
121313e3f4d6SMark Murray 	switch(SB_GET()) {
121413e3f4d6SMark Murray 	case TELQUAL_SEND:
121513e3f4d6SMark Murray 	case TELQUAL_REPLY:
121613e3f4d6SMark Murray 	    /*
121713e3f4d6SMark Murray 	     * These are sent by us and cannot be sent by
121813e3f4d6SMark Murray 	     * the client.
121913e3f4d6SMark Murray 	     */
122013e3f4d6SMark Murray 	    break;
122113e3f4d6SMark Murray 	case TELQUAL_IS:
122213e3f4d6SMark Murray 	    auth_is(subpointer, SB_LEN());
122313e3f4d6SMark Murray 	    break;
122413e3f4d6SMark Murray 	case TELQUAL_NAME:
122513e3f4d6SMark Murray 	    auth_name(subpointer, SB_LEN());
122613e3f4d6SMark Murray 	    break;
122713e3f4d6SMark Murray 	}
122813e3f4d6SMark Murray 	break;
122913e3f4d6SMark Murray #endif
123013e3f4d6SMark Murray #ifdef ENCRYPTION
123113e3f4d6SMark Murray     case TELOPT_ENCRYPT:
123213e3f4d6SMark Murray 	if (SB_EOF())
123313e3f4d6SMark Murray 	    break;
123413e3f4d6SMark Murray 	switch(SB_GET()) {
123513e3f4d6SMark Murray 	case ENCRYPT_SUPPORT:
123613e3f4d6SMark Murray 	    encrypt_support(subpointer, SB_LEN());
123713e3f4d6SMark Murray 	    break;
123813e3f4d6SMark Murray 	case ENCRYPT_IS:
123913e3f4d6SMark Murray 	    encrypt_is(subpointer, SB_LEN());
124013e3f4d6SMark Murray 	    break;
124113e3f4d6SMark Murray 	case ENCRYPT_REPLY:
124213e3f4d6SMark Murray 	    encrypt_reply(subpointer, SB_LEN());
124313e3f4d6SMark Murray 	    break;
124413e3f4d6SMark Murray 	case ENCRYPT_START:
124513e3f4d6SMark Murray 	    encrypt_start(subpointer, SB_LEN());
124613e3f4d6SMark Murray 	    break;
124713e3f4d6SMark Murray 	case ENCRYPT_END:
1248c19800e8SDoug Rabson 	    if (require_encryption)
1249c19800e8SDoug Rabson 		fatal(net, "Output encryption is not possible to turn off");
125013e3f4d6SMark Murray 	    encrypt_end();
125113e3f4d6SMark Murray 	    break;
125213e3f4d6SMark Murray 	case ENCRYPT_REQSTART:
125313e3f4d6SMark Murray 	    encrypt_request_start(subpointer, SB_LEN());
125413e3f4d6SMark Murray 	    break;
125513e3f4d6SMark Murray 	case ENCRYPT_REQEND:
125613e3f4d6SMark Murray 	    /*
125713e3f4d6SMark Murray 	     * We can always send an REQEND so that we cannot
125813e3f4d6SMark Murray 	     * get stuck encrypting.  We should only get this
125913e3f4d6SMark Murray 	     * if we have been able to get in the correct mode
126013e3f4d6SMark Murray 	     * anyhow.
126113e3f4d6SMark Murray 	     */
1262c19800e8SDoug Rabson 	    if (require_encryption)
1263c19800e8SDoug Rabson 		fatal(net, "Input encryption is not possible to turn off");
126413e3f4d6SMark Murray 	    encrypt_request_end();
126513e3f4d6SMark Murray 	    break;
126613e3f4d6SMark Murray 	case ENCRYPT_ENC_KEYID:
126713e3f4d6SMark Murray 	    encrypt_enc_keyid(subpointer, SB_LEN());
126813e3f4d6SMark Murray 	    break;
126913e3f4d6SMark Murray 	case ENCRYPT_DEC_KEYID:
127013e3f4d6SMark Murray 	    encrypt_dec_keyid(subpointer, SB_LEN());
127113e3f4d6SMark Murray 	    break;
127213e3f4d6SMark Murray 	default:
127313e3f4d6SMark Murray 	    break;
127413e3f4d6SMark Murray 	}
127513e3f4d6SMark Murray 	break;
127613e3f4d6SMark Murray #endif
127713e3f4d6SMark Murray 
127813e3f4d6SMark Murray     default:
127913e3f4d6SMark Murray 	break;
128013e3f4d6SMark Murray     }  /* end of switch */
128113e3f4d6SMark Murray 
128213e3f4d6SMark Murray }  /* end of suboption */
128313e3f4d6SMark Murray 
128413e3f4d6SMark Murray void
doclientstat(void)128513e3f4d6SMark Murray doclientstat(void)
128613e3f4d6SMark Murray {
128713e3f4d6SMark Murray     clientstat(TELOPT_LINEMODE, WILL, 0);
128813e3f4d6SMark Murray }
128913e3f4d6SMark Murray 
12908d4ba808SJacques Vidrine #undef ADD
129113e3f4d6SMark Murray #define	ADD(c)	 *ncp++ = c
129213e3f4d6SMark Murray #define	ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
129313e3f4d6SMark Murray 
129413e3f4d6SMark Murray void
send_status(void)129513e3f4d6SMark Murray send_status(void)
129613e3f4d6SMark Murray {
129713e3f4d6SMark Murray     unsigned char statusbuf[256];
129813e3f4d6SMark Murray     unsigned char *ncp;
129913e3f4d6SMark Murray     unsigned char i;
130013e3f4d6SMark Murray 
130113e3f4d6SMark Murray     ncp = statusbuf;
130213e3f4d6SMark Murray 
130313e3f4d6SMark Murray     netflush();	/* get rid of anything waiting to go out */
130413e3f4d6SMark Murray 
130513e3f4d6SMark Murray     ADD(IAC);
130613e3f4d6SMark Murray     ADD(SB);
130713e3f4d6SMark Murray     ADD(TELOPT_STATUS);
130813e3f4d6SMark Murray     ADD(TELQUAL_IS);
130913e3f4d6SMark Murray 
131013e3f4d6SMark Murray     /*
131113e3f4d6SMark Murray      * We check the want_state rather than the current state,
131213e3f4d6SMark Murray      * because if we received a DO/WILL for an option that we
131313e3f4d6SMark Murray      * don't support, and the other side didn't send a DONT/WONT
131413e3f4d6SMark Murray      * in response to our WONT/DONT, then the "state" will be
131513e3f4d6SMark Murray      * WILL/DO, and the "want_state" will be WONT/DONT.  We
131613e3f4d6SMark Murray      * need to go by the latter.
131713e3f4d6SMark Murray      */
131813e3f4d6SMark Murray     for (i = 0; i < (unsigned char)NTELOPTS; i++) {
131913e3f4d6SMark Murray 	if (my_want_state_is_will(i)) {
132013e3f4d6SMark Murray 	    ADD(WILL);
132113e3f4d6SMark Murray 	    ADD_DATA(i);
132213e3f4d6SMark Murray 	}
132313e3f4d6SMark Murray 	if (his_want_state_is_will(i)) {
132413e3f4d6SMark Murray 	    ADD(DO);
132513e3f4d6SMark Murray 	    ADD_DATA(i);
132613e3f4d6SMark Murray 	}
132713e3f4d6SMark Murray     }
132813e3f4d6SMark Murray 
132913e3f4d6SMark Murray     if (his_want_state_is_will(TELOPT_LFLOW)) {
133013e3f4d6SMark Murray 	ADD(SB);
133113e3f4d6SMark Murray 	ADD(TELOPT_LFLOW);
133213e3f4d6SMark Murray 	if (flowmode) {
133313e3f4d6SMark Murray 	    ADD(LFLOW_ON);
133413e3f4d6SMark Murray 	} else {
133513e3f4d6SMark Murray 	    ADD(LFLOW_OFF);
133613e3f4d6SMark Murray 	}
133713e3f4d6SMark Murray 	ADD(SE);
133813e3f4d6SMark Murray 
133913e3f4d6SMark Murray 	if (restartany >= 0) {
134013e3f4d6SMark Murray 	    ADD(SB);
134113e3f4d6SMark Murray 	    ADD(TELOPT_LFLOW);
134213e3f4d6SMark Murray 	    if (restartany) {
134313e3f4d6SMark Murray 		ADD(LFLOW_RESTART_ANY);
134413e3f4d6SMark Murray 	    } else {
134513e3f4d6SMark Murray 		ADD(LFLOW_RESTART_XON);
134613e3f4d6SMark Murray 	    }
134713e3f4d6SMark Murray 	    ADD(SE);
134813e3f4d6SMark Murray 	}
134913e3f4d6SMark Murray     }
135013e3f4d6SMark Murray 
135113e3f4d6SMark Murray 
135213e3f4d6SMark Murray     ADD(IAC);
135313e3f4d6SMark Murray     ADD(SE);
135413e3f4d6SMark Murray 
135513e3f4d6SMark Murray     writenet(statusbuf, ncp - statusbuf);
135613e3f4d6SMark Murray     netflush();	/* Send it on its way */
135713e3f4d6SMark Murray 
135813e3f4d6SMark Murray     DIAG(TD_OPTIONS,
135913e3f4d6SMark Murray 	 {printsub('>', statusbuf, ncp - statusbuf); netflush();});
136013e3f4d6SMark Murray }
1361