157416Smarkm/*
257416Smarkm * Copyright (c) 1989, 1993
357416Smarkm *	The Regents of the University of California.  All rights reserved.
457416Smarkm *
557416Smarkm * Redistribution and use in source and binary forms, with or without
657416Smarkm * modification, are permitted provided that the following conditions
757416Smarkm * are met:
857416Smarkm * 1. Redistributions of source code must retain the above copyright
957416Smarkm *    notice, this list of conditions and the following disclaimer.
1057416Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1157416Smarkm *    notice, this list of conditions and the following disclaimer in the
1257416Smarkm *    documentation and/or other materials provided with the distribution.
1357416Smarkm * 3. All advertising materials mentioning features or use of this software
1457416Smarkm *    must display the following acknowledgement:
1557416Smarkm *	This product includes software developed by the University of
1657416Smarkm *	California, Berkeley and its contributors.
1757416Smarkm * 4. Neither the name of the University nor the names of its contributors
1857416Smarkm *    may be used to endorse or promote products derived from this software
1957416Smarkm *    without specific prior written permission.
2057416Smarkm *
2157416Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2257416Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2357416Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2457416Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2557416Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2657416Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2757416Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2857416Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2957416Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3057416Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3157416Smarkm * SUCH DAMAGE.
3257416Smarkm */
3357416Smarkm
3457416Smarkm#include "telnetd.h"
3557416Smarkm
36233294SstasRCSID("$Id$");
3757416Smarkm
3857416Smarkmunsigned char	doopt[] = { IAC, DO, '%', 'c', 0 };
3957416Smarkmunsigned char	dont[] = { IAC, DONT, '%', 'c', 0 };
4057416Smarkmunsigned char	will[] = { IAC, WILL, '%', 'c', 0 };
4157416Smarkmunsigned char	wont[] = { IAC, WONT, '%', 'c', 0 };
4257416Smarkmint	not42 = 1;
4357416Smarkm
4457416Smarkm/*
4557416Smarkm * Buffer for sub-options, and macros
4657416Smarkm * for suboptions buffer manipulations
4757416Smarkm */
48142403Snectarunsigned char subbuffer[1024*64], *subpointer= subbuffer, *subend= subbuffer;
4957416Smarkm
5057416Smarkm#define	SB_CLEAR()	subpointer = subbuffer
5157416Smarkm#define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
5257416Smarkm#define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
5357416Smarkm    *subpointer++ = (c); \
5457416Smarkm			     }
5557416Smarkm#define	SB_GET()	((*subpointer++)&0xff)
5657416Smarkm#define	SB_EOF()	(subpointer >= subend)
5757416Smarkm#define	SB_LEN()	(subend - subpointer)
5857416Smarkm
5957416Smarkm#ifdef	ENV_HACK
6057416Smarkmunsigned char *subsave;
6157416Smarkm#define SB_SAVE()	subsave = subpointer;
6257416Smarkm#define	SB_RESTORE()	subpointer = subsave;
6357416Smarkm#endif
6457416Smarkm
6557416Smarkm
6657416Smarkm/*
6757416Smarkm * State for recv fsm
6857416Smarkm */
6957416Smarkm#define	TS_DATA		0	/* base state */
7057416Smarkm#define	TS_IAC		1	/* look for double IAC's */
7157416Smarkm#define	TS_CR		2	/* CR-LF ->'s CR */
7257416Smarkm#define	TS_SB		3	/* throw away begin's... */
7357416Smarkm#define	TS_SE		4	/* ...end's (suboption negotiation) */
7457416Smarkm#define	TS_WILL		5	/* will option negotiation */
7557416Smarkm#define	TS_WONT		6	/* wont -''- */
7657416Smarkm#define	TS_DO		7	/* do -''- */
7757416Smarkm#define	TS_DONT		8	/* dont -''- */
7857416Smarkm
7957416Smarkmvoid
8057416Smarkmtelrcv(void)
8157416Smarkm{
8257416Smarkm    int c;
8357416Smarkm    static int state = TS_DATA;
8457416Smarkm
8557416Smarkm    while (ncc > 0) {
8657416Smarkm	if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
8757416Smarkm	    break;
8857416Smarkm	c = *netip++ & 0377, ncc--;
8957416Smarkm#ifdef ENCRYPTION
9057416Smarkm	if (decrypt_input)
9157416Smarkm	    c = (*decrypt_input)(c);
9257416Smarkm#endif
9357416Smarkm	switch (state) {
9457416Smarkm
9557416Smarkm	case TS_CR:
9657416Smarkm	    state = TS_DATA;
9757416Smarkm	    /* Strip off \n or \0 after a \r */
9857416Smarkm	    if ((c == 0) || (c == '\n')) {
9957416Smarkm		break;
10057416Smarkm	    }
10157416Smarkm	    /* FALL THROUGH */
10257416Smarkm
10357416Smarkm	case TS_DATA:
10457416Smarkm	    if (c == IAC) {
10557416Smarkm		state = TS_IAC;
10657416Smarkm		break;
10757416Smarkm	    }
10857416Smarkm	    /*
10957416Smarkm	     * We now map \r\n ==> \r for pragmatic reasons.
11057416Smarkm	     * Many client implementations send \r\n when
11157416Smarkm	     * the user hits the CarriageReturn key.
11257416Smarkm	     *
11357416Smarkm	     * We USED to map \r\n ==> \n, since \r\n says
11457416Smarkm	     * that we want to be in column 1 of the next
11557416Smarkm	     * printable line, and \n is the standard
11657416Smarkm	     * unix way of saying that (\r is only good
11757416Smarkm	     * if CRMOD is set, which it normally is).
11857416Smarkm	     */
11957416Smarkm	    if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
12057416Smarkm		int nc = *netip;
12157416Smarkm#ifdef ENCRYPTION
12257416Smarkm		if (decrypt_input)
12357416Smarkm		    nc = (*decrypt_input)(nc & 0xff);
12457416Smarkm#endif
12557416Smarkm		{
12657416Smarkm#ifdef ENCRYPTION
12757416Smarkm		    if (decrypt_input)
12857416Smarkm			(void)(*decrypt_input)(-1);
12957416Smarkm#endif
13057416Smarkm		    state = TS_CR;
13157416Smarkm		}
13257416Smarkm	    }
13357416Smarkm	    *pfrontp++ = c;
13457416Smarkm	    break;
13557416Smarkm
13657416Smarkm	case TS_IAC:
13757416Smarkm	gotiac:			switch (c) {
13857416Smarkm
13957416Smarkm	    /*
14057416Smarkm	     * Send the process on the pty side an
14157416Smarkm	     * interrupt.  Do this with a NULL or
14257416Smarkm	     * interrupt char; depending on the tty mode.
14357416Smarkm	     */
14457416Smarkm	case IP:
14557416Smarkm	    DIAG(TD_OPTIONS,
14657416Smarkm		 printoption("td: recv IAC", c));
14757416Smarkm	    interrupt();
14857416Smarkm	    break;
14957416Smarkm
15057416Smarkm	case BREAK:
15157416Smarkm	    DIAG(TD_OPTIONS,
15257416Smarkm		 printoption("td: recv IAC", c));
15357416Smarkm	    sendbrk();
15457416Smarkm	    break;
15557416Smarkm
15657416Smarkm	    /*
15757416Smarkm	     * Are You There?
15857416Smarkm	     */
15957416Smarkm	case AYT:
16057416Smarkm	    DIAG(TD_OPTIONS,
16157416Smarkm		 printoption("td: recv IAC", c));
16257416Smarkm	    recv_ayt();
16357416Smarkm	    break;
16457416Smarkm
16557416Smarkm	    /*
16657416Smarkm	     * Abort Output
16757416Smarkm	     */
16857416Smarkm	case AO:
16957416Smarkm	    {
17057416Smarkm		DIAG(TD_OPTIONS,
17157416Smarkm		     printoption("td: recv IAC", c));
17257416Smarkm		ptyflush();	/* half-hearted */
17357416Smarkm		init_termbuf();
17457416Smarkm
17557416Smarkm		if (slctab[SLC_AO].sptr &&
17657416Smarkm		    *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
17757416Smarkm		    *pfrontp++ =
17857416Smarkm			(unsigned char)*slctab[SLC_AO].sptr;
17957416Smarkm		}
18057416Smarkm
18157416Smarkm		netclear();	/* clear buffer back */
18257416Smarkm		output_data ("%c%c", IAC, DM);
18357416Smarkm		neturg = nfrontp-1; /* off by one XXX */
18457416Smarkm		DIAG(TD_OPTIONS,
18557416Smarkm		     printoption("td: send IAC", DM));
18657416Smarkm		break;
18757416Smarkm	    }
18857416Smarkm
18957416Smarkm	/*
19057416Smarkm	 * Erase Character and
19157416Smarkm	 * Erase Line
19257416Smarkm	 */
19357416Smarkm	case EC:
19457416Smarkm	case EL:
19557416Smarkm	    {
19657416Smarkm		cc_t ch;
19757416Smarkm
19857416Smarkm		DIAG(TD_OPTIONS,
19957416Smarkm		     printoption("td: recv IAC", c));
20057416Smarkm		ptyflush();	/* half-hearted */
20157416Smarkm		init_termbuf();
20257416Smarkm		if (c == EC)
20357416Smarkm		    ch = *slctab[SLC_EC].sptr;
20457416Smarkm		else
20557416Smarkm		    ch = *slctab[SLC_EL].sptr;
20657416Smarkm		if (ch != (cc_t)(_POSIX_VDISABLE))
20757416Smarkm		    *pfrontp++ = (unsigned char)ch;
20857416Smarkm		break;
20957416Smarkm	    }
21057416Smarkm
21157416Smarkm	/*
21257416Smarkm	 * Check for urgent data...
21357416Smarkm	 */
21457416Smarkm	case DM:
21557416Smarkm	    DIAG(TD_OPTIONS,
21657416Smarkm		 printoption("td: recv IAC", c));
21757416Smarkm	    SYNCHing = stilloob(net);
21857416Smarkm	    settimer(gotDM);
21957416Smarkm	    break;
22057416Smarkm
22157416Smarkm
22257416Smarkm	    /*
22357416Smarkm	     * Begin option subnegotiation...
22457416Smarkm	     */
22557416Smarkm	case SB:
22657416Smarkm	    state = TS_SB;
22757416Smarkm	    SB_CLEAR();
22857416Smarkm	    continue;
22957416Smarkm
23057416Smarkm	case WILL:
23157416Smarkm	    state = TS_WILL;
23257416Smarkm	    continue;
23357416Smarkm
23457416Smarkm	case WONT:
23557416Smarkm	    state = TS_WONT;
23657416Smarkm	    continue;
23757416Smarkm
23857416Smarkm	case DO:
23957416Smarkm	    state = TS_DO;
24057416Smarkm	    continue;
24157416Smarkm
24257416Smarkm	case DONT:
24357416Smarkm	    state = TS_DONT;
24457416Smarkm	    continue;
24557416Smarkm	case EOR:
24657416Smarkm	    if (his_state_is_will(TELOPT_EOR))
24757416Smarkm		doeof();
24857416Smarkm	    break;
24957416Smarkm
25057416Smarkm	    /*
25157416Smarkm	     * Handle RFC 10xx Telnet linemode option additions
25257416Smarkm	     * to command stream (EOF, SUSP, ABORT).
25357416Smarkm	     */
25457416Smarkm	case xEOF:
25557416Smarkm	    doeof();
25657416Smarkm	    break;
25757416Smarkm
25857416Smarkm	case SUSP:
25957416Smarkm	    sendsusp();
26057416Smarkm	    break;
26157416Smarkm
26257416Smarkm	case ABORT:
26357416Smarkm	    sendbrk();
26457416Smarkm	    break;
26557416Smarkm
26657416Smarkm	case IAC:
26757416Smarkm	    *pfrontp++ = c;
26857416Smarkm	    break;
26957416Smarkm	}
27057416Smarkm	state = TS_DATA;
27157416Smarkm	break;
27257416Smarkm
27357416Smarkm	case TS_SB:
27457416Smarkm	    if (c == IAC) {
27557416Smarkm		state = TS_SE;
27657416Smarkm	    } else {
27757416Smarkm		SB_ACCUM(c);
27857416Smarkm	    }
27957416Smarkm	    break;
28057416Smarkm
28157416Smarkm	case TS_SE:
28257416Smarkm	    if (c != SE) {
28357416Smarkm		if (c != IAC) {
28457416Smarkm		    /*
28557416Smarkm		     * bad form of suboption negotiation.
28657416Smarkm		     * handle it in such a way as to avoid
28757416Smarkm		     * damage to local state.  Parse
28857416Smarkm		     * suboption buffer found so far,
28957416Smarkm		     * then treat remaining stream as
29057416Smarkm		     * another command sequence.
29157416Smarkm		     */
29257416Smarkm
29357416Smarkm		    /* for DIAGNOSTICS */
29457416Smarkm		    SB_ACCUM(IAC);
29557416Smarkm		    SB_ACCUM(c);
29657416Smarkm		    subpointer -= 2;
29757416Smarkm
29857416Smarkm		    SB_TERM();
29957416Smarkm		    suboption();
30057416Smarkm		    state = TS_IAC;
30157416Smarkm		    goto gotiac;
30257416Smarkm		}
30357416Smarkm		SB_ACCUM(c);
30457416Smarkm		state = TS_SB;
30557416Smarkm	    } else {
30657416Smarkm		/* for DIAGNOSTICS */
30757416Smarkm		SB_ACCUM(IAC);
30857416Smarkm		SB_ACCUM(SE);
30957416Smarkm		subpointer -= 2;
31057416Smarkm
31157416Smarkm		SB_TERM();
31257416Smarkm		suboption();	/* handle sub-option */
31357416Smarkm		state = TS_DATA;
31457416Smarkm	    }
31557416Smarkm	    break;
31657416Smarkm
31757416Smarkm	case TS_WILL:
31857416Smarkm	    willoption(c);
31957416Smarkm	    state = TS_DATA;
32057416Smarkm	    continue;
32157416Smarkm
32257416Smarkm	case TS_WONT:
32357416Smarkm	    wontoption(c);
32457416Smarkm	    if (c==TELOPT_ENCRYPT && his_do_dont_is_changing(TELOPT_ENCRYPT) )
32557416Smarkm                dontoption(c);
32657416Smarkm	    state = TS_DATA;
32757416Smarkm	    continue;
32857416Smarkm
32957416Smarkm	case TS_DO:
33057416Smarkm	    dooption(c);
33157416Smarkm	    state = TS_DATA;
33257416Smarkm	    continue;
33357416Smarkm
33457416Smarkm	case TS_DONT:
33557416Smarkm	    dontoption(c);
33657416Smarkm	    state = TS_DATA;
33757416Smarkm	    continue;
33857416Smarkm
33957416Smarkm	default:
34057416Smarkm	    syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
34157416Smarkm	    printf("telnetd: panic state=%d\n", state);
34257416Smarkm	    exit(1);
34357416Smarkm	}
34457416Smarkm    }
34557416Smarkm}  /* end of telrcv */
34657416Smarkm
34757416Smarkm/*
34857416Smarkm * The will/wont/do/dont state machines are based on Dave Borman's
34957416Smarkm * Telnet option processing state machine.
35057416Smarkm *
35157416Smarkm * These correspond to the following states:
35257416Smarkm *	my_state = the last negotiated state
35357416Smarkm *	want_state = what I want the state to go to
35457416Smarkm *	want_resp = how many requests I have sent
35557416Smarkm * All state defaults are negative, and resp defaults to 0.
35657416Smarkm *
35757416Smarkm * When initiating a request to change state to new_state:
35857416Smarkm *
35957416Smarkm * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
36057416Smarkm *	do nothing;
36157416Smarkm * } else {
36257416Smarkm *	want_state = new_state;
36357416Smarkm *	send new_state;
36457416Smarkm *	want_resp++;
36557416Smarkm * }
36657416Smarkm *
36757416Smarkm * When receiving new_state:
36857416Smarkm *
36957416Smarkm * if (want_resp) {
37057416Smarkm *	want_resp--;
37157416Smarkm *	if (want_resp && (new_state == my_state))
37257416Smarkm *		want_resp--;
37357416Smarkm * }
37457416Smarkm * if ((want_resp == 0) && (new_state != want_state)) {
37557416Smarkm *	if (ok_to_switch_to new_state)
37657416Smarkm *		want_state = new_state;
37757416Smarkm *	else
37857416Smarkm *		want_resp++;
37957416Smarkm *	send want_state;
38057416Smarkm * }
38157416Smarkm * my_state = new_state;
38257416Smarkm *
38357416Smarkm * Note that new_state is implied in these functions by the function itself.
38457416Smarkm * will and do imply positive new_state, wont and dont imply negative.
38557416Smarkm *
38657416Smarkm * Finally, there is one catch.  If we send a negative response to a
38757416Smarkm * positive request, my_state will be the positive while want_state will
38857416Smarkm * remain negative.  my_state will revert to negative when the negative
38957416Smarkm * acknowlegment arrives from the peer.  Thus, my_state generally tells
39057416Smarkm * us not only the last negotiated state, but also tells us what the peer
39157416Smarkm * wants to be doing as well.  It is important to understand this difference
39257416Smarkm * as we may wish to be processing data streams based on our desired state
39357416Smarkm * (want_state) or based on what the peer thinks the state is (my_state).
39457416Smarkm *
39557416Smarkm * This all works fine because if the peer sends a positive request, the data
39657416Smarkm * that we receive prior to negative acknowlegment will probably be affected
39757416Smarkm * by the positive state, and we can process it as such (if we can; if we
39857416Smarkm * can't then it really doesn't matter).  If it is that important, then the
39957416Smarkm * peer probably should be buffering until this option state negotiation
40057416Smarkm * is complete.
40157416Smarkm *
40257416Smarkm */
40357416Smarkmvoid
40457416Smarkmsend_do(int option, int init)
40557416Smarkm{
40657416Smarkm    if (init) {
40757416Smarkm	if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
40857416Smarkm	    his_want_state_is_will(option))
40957416Smarkm	    return;
41057416Smarkm	/*
41157416Smarkm	 * Special case for TELOPT_TM:  We send a DO, but pretend
41257416Smarkm	 * that we sent a DONT, so that we can send more DOs if
41357416Smarkm	 * we want to.
41457416Smarkm	 */
41557416Smarkm	if (option == TELOPT_TM)
41657416Smarkm	    set_his_want_state_wont(option);
41757416Smarkm	else
41857416Smarkm	    set_his_want_state_will(option);
41957416Smarkm	do_dont_resp[option]++;
42057416Smarkm    }
42157416Smarkm    output_data((const char *)doopt, option);
42257416Smarkm
42357416Smarkm    DIAG(TD_OPTIONS, printoption("td: send do", option));
42457416Smarkm}
42557416Smarkm
42657416Smarkm#ifdef	AUTHENTICATION
42757416Smarkmextern void auth_request(void);
42857416Smarkm#endif
42957416Smarkm#ifdef	ENCRYPTION
430178825Sdfrextern void encrypt_send_support(void);
43157416Smarkm#endif
43257416Smarkm
43357416Smarkmvoid
43457416Smarkmwilloption(int option)
43557416Smarkm{
43657416Smarkm    int changeok = 0;
437178825Sdfr    void (*func)(void) = NULL;
43857416Smarkm
43957416Smarkm    /*
44057416Smarkm     * process input from peer.
44157416Smarkm     */
44257416Smarkm
44357416Smarkm    DIAG(TD_OPTIONS, printoption("td: recv will", option));
44457416Smarkm
44557416Smarkm    if (do_dont_resp[option]) {
44657416Smarkm	do_dont_resp[option]--;
44757416Smarkm	if (do_dont_resp[option] && his_state_is_will(option))
44857416Smarkm	    do_dont_resp[option]--;
44957416Smarkm    }
45057416Smarkm    if (do_dont_resp[option] == 0) {
45157416Smarkm	if (his_want_state_is_wont(option)) {
45257416Smarkm	    switch (option) {
45357416Smarkm
45457416Smarkm	    case TELOPT_BINARY:
45557416Smarkm		init_termbuf();
45657416Smarkm		tty_binaryin(1);
45757416Smarkm		set_termbuf();
45857416Smarkm		changeok++;
45957416Smarkm		break;
46057416Smarkm
46157416Smarkm	    case TELOPT_ECHO:
46257416Smarkm		/*
46357416Smarkm		 * See comments below for more info.
46457416Smarkm		 */
46557416Smarkm		not42 = 0;	/* looks like a 4.2 system */
46657416Smarkm		break;
46757416Smarkm
46857416Smarkm	    case TELOPT_TM:
46957416Smarkm		/*
47057416Smarkm		 * We never respond to a WILL TM, and
47157416Smarkm		 * we leave the state WONT.
47257416Smarkm		 */
47357416Smarkm		return;
47457416Smarkm
47557416Smarkm	    case TELOPT_LFLOW:
47657416Smarkm		/*
47757416Smarkm		 * If we are going to support flow control
47857416Smarkm		 * option, then don't worry peer that we can't
47957416Smarkm		 * change the flow control characters.
48057416Smarkm		 */
48157416Smarkm		slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
48257416Smarkm		slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
48357416Smarkm		slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
48457416Smarkm		slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
48557416Smarkm	    case TELOPT_TTYPE:
48657416Smarkm	    case TELOPT_SGA:
48757416Smarkm	    case TELOPT_NAWS:
48857416Smarkm	    case TELOPT_TSPEED:
48957416Smarkm	    case TELOPT_XDISPLOC:
49057416Smarkm	    case TELOPT_NEW_ENVIRON:
49157416Smarkm	    case TELOPT_OLD_ENVIRON:
49257416Smarkm		changeok++;
49357416Smarkm		break;
49457416Smarkm
49557416Smarkm
49657416Smarkm#ifdef	AUTHENTICATION
49757416Smarkm	    case TELOPT_AUTHENTICATION:
49857416Smarkm		func = auth_request;
49957416Smarkm		changeok++;
50057416Smarkm		break;
50157416Smarkm#endif
50257416Smarkm
50357416Smarkm#ifdef	ENCRYPTION
50457416Smarkm	    case TELOPT_ENCRYPT:
50557416Smarkm		func = encrypt_send_support;
50657416Smarkm		changeok++;
50757416Smarkm		break;
50857416Smarkm#endif
509233294Sstas
51057416Smarkm	    default:
51157416Smarkm		break;
51257416Smarkm	    }
51357416Smarkm	    if (changeok) {
51457416Smarkm		set_his_want_state_will(option);
51557416Smarkm		send_do(option, 0);
51657416Smarkm	    } else {
51757416Smarkm		do_dont_resp[option]++;
51857416Smarkm		send_dont(option, 0);
51957416Smarkm	    }
52057416Smarkm	} else {
52157416Smarkm	    /*
52257416Smarkm	     * Option processing that should happen when
52357416Smarkm	     * we receive conformation of a change in
52457416Smarkm	     * state that we had requested.
52557416Smarkm	     */
52657416Smarkm	    switch (option) {
52757416Smarkm	    case TELOPT_ECHO:
52857416Smarkm		not42 = 0;	/* looks like a 4.2 system */
52957416Smarkm		/*
53057416Smarkm		 * Egads, he responded "WILL ECHO".  Turn
53157416Smarkm		 * it off right now!
53257416Smarkm		 */
53357416Smarkm		send_dont(option, 1);
53457416Smarkm		/*
53557416Smarkm		 * "WILL ECHO".  Kludge upon kludge!
53657416Smarkm		 * A 4.2 client is now echoing user input at
53757416Smarkm		 * the tty.  This is probably undesireable and
53857416Smarkm		 * it should be stopped.  The client will
53957416Smarkm		 * respond WONT TM to the DO TM that we send to
54057416Smarkm		 * check for kludge linemode.  When the WONT TM
54157416Smarkm		 * arrives, linemode will be turned off and a
54257416Smarkm		 * change propogated to the pty.  This change
54357416Smarkm		 * will cause us to process the new pty state
54457416Smarkm		 * in localstat(), which will notice that
54557416Smarkm		 * linemode is off and send a WILL ECHO
54657416Smarkm		 * so that we are properly in character mode and
54757416Smarkm		 * all is well.
54857416Smarkm		 */
54957416Smarkm		break;
55057416Smarkm
55157416Smarkm#ifdef	AUTHENTICATION
55257416Smarkm	    case TELOPT_AUTHENTICATION:
55357416Smarkm		func = auth_request;
55457416Smarkm		break;
55557416Smarkm#endif
55657416Smarkm
55757416Smarkm#ifdef	ENCRYPTION
55857416Smarkm	    case TELOPT_ENCRYPT:
55957416Smarkm		func = encrypt_send_support;
56057416Smarkm		break;
56157416Smarkm#endif
56257416Smarkm
56357416Smarkm	    case TELOPT_LFLOW:
56457416Smarkm		func = flowstat;
56557416Smarkm		break;
56657416Smarkm	    }
56757416Smarkm	}
56857416Smarkm    }
56957416Smarkm    set_his_state_will(option);
57057416Smarkm    if (func)
57157416Smarkm	(*func)();
57257416Smarkm}  /* end of willoption */
57357416Smarkm
57457416Smarkmvoid
57557416Smarkmsend_dont(int option, int init)
57657416Smarkm{
57757416Smarkm    if (init) {
57857416Smarkm	if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
57957416Smarkm	    his_want_state_is_wont(option))
58057416Smarkm	    return;
58157416Smarkm	set_his_want_state_wont(option);
58257416Smarkm	do_dont_resp[option]++;
58357416Smarkm    }
58457416Smarkm    output_data((const char *)dont, option);
58557416Smarkm
58657416Smarkm    DIAG(TD_OPTIONS, printoption("td: send dont", option));
58757416Smarkm}
58857416Smarkm
58957416Smarkmvoid
59057416Smarkmwontoption(int option)
59157416Smarkm{
59257416Smarkm    /*
59357416Smarkm     * Process client input.
59457416Smarkm	 */
59557416Smarkm
59657416Smarkm    DIAG(TD_OPTIONS, printoption("td: recv wont", option));
59757416Smarkm
59857416Smarkm    if (do_dont_resp[option]) {
59957416Smarkm	do_dont_resp[option]--;
60057416Smarkm	if (do_dont_resp[option] && his_state_is_wont(option))
60157416Smarkm	    do_dont_resp[option]--;
60257416Smarkm    }
60357416Smarkm    if (do_dont_resp[option] == 0) {
60457416Smarkm	if (his_want_state_is_will(option)) {
60557416Smarkm	    /* it is always ok to change to negative state */
60657416Smarkm	    switch (option) {
60757416Smarkm	    case TELOPT_ECHO:
60857416Smarkm		not42 = 1; /* doesn't seem to be a 4.2 system */
60957416Smarkm		break;
61057416Smarkm
61157416Smarkm	    case TELOPT_BINARY:
61257416Smarkm		init_termbuf();
61357416Smarkm		tty_binaryin(0);
61457416Smarkm		set_termbuf();
61557416Smarkm		break;
61657416Smarkm
61757416Smarkm	    case TELOPT_TM:
61857416Smarkm		/*
61957416Smarkm		 * If we get a WONT TM, and had sent a DO TM,
62057416Smarkm		 * don't respond with a DONT TM, just leave it
62157416Smarkm		 * as is.  Short circut the state machine to
62257416Smarkm		 * achive this.
62357416Smarkm		 */
62457416Smarkm		set_his_want_state_wont(TELOPT_TM);
62557416Smarkm		return;
62657416Smarkm
62757416Smarkm	    case TELOPT_LFLOW:
62857416Smarkm		/*
62957416Smarkm		 * If we are not going to support flow control
63057416Smarkm		 * option, then let peer know that we can't
63157416Smarkm		 * change the flow control characters.
63257416Smarkm		 */
63357416Smarkm		slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
63457416Smarkm		slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
63557416Smarkm		slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
63657416Smarkm		slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
63757416Smarkm		break;
63857416Smarkm
63957416Smarkm#ifdef AUTHENTICATION
64057416Smarkm	    case TELOPT_AUTHENTICATION:
64157416Smarkm		auth_finished(0, AUTH_REJECT);
64257416Smarkm		break;
64357416Smarkm#endif
64457416Smarkm
64557416Smarkm		/*
64657416Smarkm		 * For options that we might spin waiting for
64757416Smarkm		 * sub-negotiation, if the client turns off the
64857416Smarkm		 * option rather than responding to the request,
64957416Smarkm		 * we have to treat it here as if we got a response
65057416Smarkm		 * to the sub-negotiation, (by updating the timers)
65157416Smarkm		 * so that we'll break out of the loop.
65257416Smarkm		 */
65357416Smarkm	    case TELOPT_TTYPE:
65457416Smarkm		settimer(ttypesubopt);
65557416Smarkm		break;
65657416Smarkm
65757416Smarkm	    case TELOPT_TSPEED:
65857416Smarkm		settimer(tspeedsubopt);
65957416Smarkm		break;
66057416Smarkm
66157416Smarkm	    case TELOPT_XDISPLOC:
66257416Smarkm		settimer(xdisplocsubopt);
66357416Smarkm		break;
66457416Smarkm
66557416Smarkm	    case TELOPT_OLD_ENVIRON:
66657416Smarkm		settimer(oenvironsubopt);
66757416Smarkm		break;
66857416Smarkm
66957416Smarkm	    case TELOPT_NEW_ENVIRON:
67057416Smarkm		settimer(environsubopt);
67157416Smarkm		break;
67257416Smarkm
67357416Smarkm	    default:
67457416Smarkm		break;
67557416Smarkm	    }
67657416Smarkm	    set_his_want_state_wont(option);
67757416Smarkm	    if (his_state_is_will(option))
67857416Smarkm		send_dont(option, 0);
67957416Smarkm	} else {
68057416Smarkm	    switch (option) {
68157416Smarkm	    case TELOPT_TM:
68257416Smarkm		break;
68357416Smarkm
68457416Smarkm#ifdef AUTHENTICATION
68557416Smarkm	    case TELOPT_AUTHENTICATION:
68657416Smarkm		auth_finished(0, AUTH_REJECT);
68757416Smarkm		break;
68857416Smarkm#endif
68957416Smarkm	    default:
69057416Smarkm		break;
69157416Smarkm	    }
69257416Smarkm	}
69357416Smarkm    }
69457416Smarkm    set_his_state_wont(option);
69557416Smarkm
69657416Smarkm}  /* end of wontoption */
69757416Smarkm
69857416Smarkmvoid
69957416Smarkmsend_will(int option, int init)
70057416Smarkm{
70157416Smarkm    if (init) {
70257416Smarkm	if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
70357416Smarkm	    my_want_state_is_will(option))
70457416Smarkm	    return;
70557416Smarkm	set_my_want_state_will(option);
70657416Smarkm	will_wont_resp[option]++;
70757416Smarkm    }
70857416Smarkm    output_data ((const char *)will, option);
70957416Smarkm
71057416Smarkm    DIAG(TD_OPTIONS, printoption("td: send will", option));
71157416Smarkm}
71257416Smarkm
71357416Smarkm/*
71457416Smarkm * When we get a DONT SGA, we will try once to turn it
71557416Smarkm * back on.  If the other side responds DONT SGA, we
71657416Smarkm * leave it at that.  This is so that when we talk to
71757416Smarkm * clients that understand KLUDGELINEMODE but not LINEMODE,
71857416Smarkm * we'll keep them in char-at-a-time mode.
71957416Smarkm */
72057416Smarkmint turn_on_sga = 0;
72157416Smarkm
72257416Smarkmvoid
72357416Smarkmdooption(int option)
72457416Smarkm{
72557416Smarkm    int changeok = 0;
72657416Smarkm
72757416Smarkm    /*
72857416Smarkm     * Process client input.
72957416Smarkm     */
73057416Smarkm
73157416Smarkm    DIAG(TD_OPTIONS, printoption("td: recv do", option));
73257416Smarkm
73357416Smarkm    if (will_wont_resp[option]) {
73457416Smarkm	will_wont_resp[option]--;
73557416Smarkm	if (will_wont_resp[option] && my_state_is_will(option))
73657416Smarkm	    will_wont_resp[option]--;
73757416Smarkm    }
73857416Smarkm    if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
73957416Smarkm	switch (option) {
74057416Smarkm	case TELOPT_ECHO:
74157416Smarkm	    {
74257416Smarkm		init_termbuf();
74357416Smarkm		tty_setecho(1);
74457416Smarkm		set_termbuf();
74557416Smarkm	    }
74657416Smarkm	changeok++;
74757416Smarkm	break;
74857416Smarkm
74957416Smarkm	case TELOPT_BINARY:
75057416Smarkm	    init_termbuf();
75157416Smarkm	    tty_binaryout(1);
75257416Smarkm	    set_termbuf();
75357416Smarkm	    changeok++;
75457416Smarkm	    break;
75557416Smarkm
75657416Smarkm	case TELOPT_SGA:
75757416Smarkm	    turn_on_sga = 0;
75857416Smarkm	    changeok++;
75957416Smarkm	    break;
76057416Smarkm
76157416Smarkm	case TELOPT_STATUS:
76257416Smarkm	    changeok++;
76357416Smarkm	    break;
76457416Smarkm
76557416Smarkm	case TELOPT_TM:
76657416Smarkm	    /*
76757416Smarkm	     * Special case for TM.  We send a WILL, but
76857416Smarkm	     * pretend we sent a WONT.
76957416Smarkm	     */
77057416Smarkm	    send_will(option, 0);
77157416Smarkm	    set_my_want_state_wont(option);
77257416Smarkm	    set_my_state_wont(option);
77357416Smarkm	    return;
77457416Smarkm
77557416Smarkm	case TELOPT_LOGOUT:
77657416Smarkm	    /*
77757416Smarkm	     * When we get a LOGOUT option, respond
77857416Smarkm	     * with a WILL LOGOUT, make sure that
77957416Smarkm	     * it gets written out to the network,
78057416Smarkm	     * and then just go away...
78157416Smarkm	     */
78257416Smarkm	    set_my_want_state_will(TELOPT_LOGOUT);
78357416Smarkm	    send_will(TELOPT_LOGOUT, 0);
78457416Smarkm	    set_my_state_will(TELOPT_LOGOUT);
78557416Smarkm	    netflush();
78657416Smarkm	    cleanup(0);
78757416Smarkm	    /* NOT REACHED */
78857416Smarkm	    break;
78957416Smarkm
79057416Smarkm#ifdef ENCRYPTION
79157416Smarkm	case TELOPT_ENCRYPT:
79257416Smarkm	    changeok++;
79357416Smarkm	    break;
79457416Smarkm#endif
79557416Smarkm	case TELOPT_LINEMODE:
79657416Smarkm	case TELOPT_TTYPE:
79757416Smarkm	case TELOPT_NAWS:
79857416Smarkm	case TELOPT_TSPEED:
79957416Smarkm	case TELOPT_LFLOW:
80057416Smarkm	case TELOPT_XDISPLOC:
80157416Smarkm#ifdef	TELOPT_ENVIRON
80257416Smarkm	case TELOPT_NEW_ENVIRON:
80357416Smarkm#endif
80457416Smarkm	case TELOPT_OLD_ENVIRON:
80557416Smarkm	default:
80657416Smarkm	    break;
80757416Smarkm	}
80857416Smarkm	if (changeok) {
80957416Smarkm	    set_my_want_state_will(option);
81057416Smarkm	    send_will(option, 0);
81157416Smarkm	} else {
81257416Smarkm	    will_wont_resp[option]++;
81357416Smarkm	    send_wont(option, 0);
81457416Smarkm	}
81557416Smarkm    }
81657416Smarkm    set_my_state_will(option);
81757416Smarkm
81857416Smarkm}  /* end of dooption */
81957416Smarkm
82057416Smarkmvoid
82157416Smarkmsend_wont(int option, int init)
82257416Smarkm{
82357416Smarkm    if (init) {
82457416Smarkm	if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
82557416Smarkm	    my_want_state_is_wont(option))
82657416Smarkm	    return;
82757416Smarkm	set_my_want_state_wont(option);
82857416Smarkm	will_wont_resp[option]++;
82957416Smarkm    }
83057416Smarkm    output_data ((const char *)wont, option);
83157416Smarkm
83257416Smarkm    DIAG(TD_OPTIONS, printoption("td: send wont", option));
83357416Smarkm}
83457416Smarkm
83557416Smarkmvoid
83657416Smarkmdontoption(int option)
83757416Smarkm{
83857416Smarkm    /*
83957416Smarkm     * Process client input.
84057416Smarkm	 */
84157416Smarkm
84257416Smarkm
84357416Smarkm    DIAG(TD_OPTIONS, printoption("td: recv dont", option));
84457416Smarkm
84557416Smarkm    if (will_wont_resp[option]) {
84657416Smarkm	will_wont_resp[option]--;
84757416Smarkm	if (will_wont_resp[option] && my_state_is_wont(option))
84857416Smarkm	    will_wont_resp[option]--;
84957416Smarkm    }
85057416Smarkm    if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
85157416Smarkm	switch (option) {
85257416Smarkm	case TELOPT_BINARY:
85357416Smarkm	    init_termbuf();
85457416Smarkm	    tty_binaryout(0);
85557416Smarkm	    set_termbuf();
85657416Smarkm	    break;
85757416Smarkm
85857416Smarkm	case TELOPT_ECHO:	/* we should stop echoing */
85957416Smarkm	    {
86057416Smarkm		init_termbuf();
86157416Smarkm		tty_setecho(0);
86257416Smarkm		set_termbuf();
86357416Smarkm	    }
86457416Smarkm	break;
86557416Smarkm
86657416Smarkm	case TELOPT_SGA:
86757416Smarkm	    set_my_want_state_wont(option);
86857416Smarkm	    if (my_state_is_will(option))
86957416Smarkm		send_wont(option, 0);
87057416Smarkm	    set_my_state_wont(option);
87157416Smarkm	    if (turn_on_sga ^= 1)
87257416Smarkm		send_will(option, 1);
87357416Smarkm	    return;
87457416Smarkm
87557416Smarkm	default:
87657416Smarkm	    break;
87757416Smarkm	}
87857416Smarkm
87957416Smarkm	set_my_want_state_wont(option);
88057416Smarkm	if (my_state_is_will(option))
88157416Smarkm	    send_wont(option, 0);
88257416Smarkm    }
88357416Smarkm    set_my_state_wont(option);
88457416Smarkm
88557416Smarkm}  /* end of dontoption */
88657416Smarkm
88757416Smarkm#ifdef	ENV_HACK
88857416Smarkmint env_ovar = -1;
88957416Smarkmint env_ovalue = -1;
89057416Smarkm#else	/* ENV_HACK */
89157416Smarkm# define env_ovar OLD_ENV_VAR
89257416Smarkm# define env_ovalue OLD_ENV_VALUE
89357416Smarkm#endif	/* ENV_HACK */
89457416Smarkm
89557416Smarkm/*
89657416Smarkm * suboption()
89757416Smarkm *
89857416Smarkm *	Look at the sub-option buffer, and try to be helpful to the other
89957416Smarkm * side.
90057416Smarkm *
90157416Smarkm *	Currently we recognize:
90257416Smarkm *
90357416Smarkm *	Terminal type is
90457416Smarkm *	Linemode
90557416Smarkm *	Window size
90657416Smarkm *	Terminal speed
90757416Smarkm */
90857416Smarkmvoid
90957416Smarkmsuboption(void)
91057416Smarkm{
91157416Smarkm    int subchar;
91257416Smarkm
91357416Smarkm    DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
91457416Smarkm
91557416Smarkm    subchar = SB_GET();
91657416Smarkm    switch (subchar) {
91757416Smarkm    case TELOPT_TSPEED: {
91857416Smarkm	int xspeed, rspeed;
91957416Smarkm
92057416Smarkm	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
92157416Smarkm	    break;
92257416Smarkm
92357416Smarkm	settimer(tspeedsubopt);
92457416Smarkm
92557416Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_IS)
92657416Smarkm	    return;
92757416Smarkm
92857416Smarkm	xspeed = atoi((char *)subpointer);
92957416Smarkm
93057416Smarkm	while (SB_GET() != ',' && !SB_EOF());
93157416Smarkm	if (SB_EOF())
93257416Smarkm	    return;
93357416Smarkm
93457416Smarkm	rspeed = atoi((char *)subpointer);
93557416Smarkm	clientstat(TELOPT_TSPEED, xspeed, rspeed);
93657416Smarkm
93757416Smarkm	break;
93857416Smarkm
93957416Smarkm    }  /* end of case TELOPT_TSPEED */
94057416Smarkm
94157416Smarkm    case TELOPT_TTYPE: {		/* Yaaaay! */
942178825Sdfr	char *p;
94357416Smarkm
94457416Smarkm	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
94557416Smarkm	    break;
94657416Smarkm	settimer(ttypesubopt);
94757416Smarkm
94857416Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_IS) {
94957416Smarkm	    return;		/* ??? XXX but, this is the most robust */
95057416Smarkm	}
95157416Smarkm
952178825Sdfr	p = terminaltype;
95357416Smarkm
954178825Sdfr	while ((p < (terminaltype + sizeof terminaltype-1)) &&
95557416Smarkm	       !SB_EOF()) {
95657416Smarkm	    int c;
95757416Smarkm
95857416Smarkm	    c = SB_GET();
95957416Smarkm	    if (isupper(c)) {
96057416Smarkm		c = tolower(c);
96157416Smarkm	    }
962178825Sdfr	    *p++ = c;    /* accumulate name */
96357416Smarkm	}
964178825Sdfr	*p = 0;
96557416Smarkm	break;
96657416Smarkm    }  /* end of case TELOPT_TTYPE */
96757416Smarkm
96857416Smarkm    case TELOPT_NAWS: {
96957416Smarkm	int xwinsize, ywinsize;
97057416Smarkm
97157416Smarkm	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
97257416Smarkm	    break;
97357416Smarkm
97457416Smarkm	if (SB_EOF())
97557416Smarkm	    return;
97657416Smarkm	xwinsize = SB_GET() << 8;
97757416Smarkm	if (SB_EOF())
97857416Smarkm	    return;
97957416Smarkm	xwinsize |= SB_GET();
98057416Smarkm	if (SB_EOF())
98157416Smarkm	    return;
98257416Smarkm	ywinsize = SB_GET() << 8;
98357416Smarkm	if (SB_EOF())
98457416Smarkm	    return;
98557416Smarkm	ywinsize |= SB_GET();
98657416Smarkm	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
98757416Smarkm
98857416Smarkm	break;
98957416Smarkm
99057416Smarkm    }  /* end of case TELOPT_NAWS */
99157416Smarkm
99257416Smarkm    case TELOPT_STATUS: {
99357416Smarkm	int mode;
99457416Smarkm
99557416Smarkm	if (SB_EOF())
99657416Smarkm	    break;
99757416Smarkm	mode = SB_GET();
99857416Smarkm	switch (mode) {
99957416Smarkm	case TELQUAL_SEND:
100057416Smarkm	    if (my_state_is_will(TELOPT_STATUS))
100157416Smarkm		send_status();
100257416Smarkm	    break;
100357416Smarkm
100457416Smarkm	case TELQUAL_IS:
100557416Smarkm	    break;
100657416Smarkm
100757416Smarkm	default:
100857416Smarkm	    break;
100957416Smarkm	}
101057416Smarkm	break;
101157416Smarkm    }  /* end of case TELOPT_STATUS */
101257416Smarkm
101357416Smarkm    case TELOPT_XDISPLOC: {
101457416Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_IS)
101557416Smarkm	    return;
101657416Smarkm	settimer(xdisplocsubopt);
101757416Smarkm	subpointer[SB_LEN()] = '\0';
101872445Sassar	esetenv("DISPLAY", (char *)subpointer, 1);
101957416Smarkm	break;
102057416Smarkm    }  /* end of case TELOPT_XDISPLOC */
102157416Smarkm
102257416Smarkm#ifdef	TELOPT_NEW_ENVIRON
102357416Smarkm    case TELOPT_NEW_ENVIRON:
102457416Smarkm#endif
102557416Smarkm    case TELOPT_OLD_ENVIRON: {
102657416Smarkm	int c;
102757416Smarkm	char *cp, *varp, *valp;
102857416Smarkm
102957416Smarkm	if (SB_EOF())
103057416Smarkm	    return;
103157416Smarkm	c = SB_GET();
103257416Smarkm	if (c == TELQUAL_IS) {
103357416Smarkm	    if (subchar == TELOPT_OLD_ENVIRON)
103457416Smarkm		settimer(oenvironsubopt);
103557416Smarkm	    else
103657416Smarkm		settimer(environsubopt);
103757416Smarkm	} else if (c != TELQUAL_INFO) {
103857416Smarkm	    return;
103957416Smarkm	}
104057416Smarkm
104157416Smarkm#ifdef	TELOPT_NEW_ENVIRON
104257416Smarkm	if (subchar == TELOPT_NEW_ENVIRON) {
104357416Smarkm	    while (!SB_EOF()) {
104457416Smarkm		c = SB_GET();
104557416Smarkm		if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
104657416Smarkm		    break;
104757416Smarkm	    }
104857416Smarkm	} else
104957416Smarkm#endif
105057416Smarkm	    {
105157416Smarkm#ifdef	ENV_HACK
105257416Smarkm		/*
105357416Smarkm		 * We only want to do this if we haven't already decided
105457416Smarkm		 * whether or not the other side has its VALUE and VAR
105557416Smarkm		 * reversed.
105657416Smarkm		 */
105757416Smarkm		if (env_ovar < 0) {
105857416Smarkm		    int last = -1;		/* invalid value */
105957416Smarkm		    int empty = 0;
106057416Smarkm		    int got_var = 0, got_value = 0, got_uservar = 0;
106157416Smarkm
106257416Smarkm		    /*
106357416Smarkm		     * The other side might have its VALUE and VAR values
106457416Smarkm		     * reversed.  To be interoperable, we need to determine
106557416Smarkm		     * which way it is.  If the first recognized character
106657416Smarkm		     * is a VAR or VALUE, then that will tell us what
106757416Smarkm		     * type of client it is.  If the fist recognized
106857416Smarkm		     * character is a USERVAR, then we continue scanning
106957416Smarkm		     * the suboption looking for two consecutive
107057416Smarkm		     * VAR or VALUE fields.  We should not get two
107157416Smarkm		     * consecutive VALUE fields, so finding two
107257416Smarkm		     * consecutive VALUE or VAR fields will tell us
107357416Smarkm		     * what the client is.
107457416Smarkm		     */
107557416Smarkm		    SB_SAVE();
107657416Smarkm		    while (!SB_EOF()) {
107757416Smarkm			c = SB_GET();
107857416Smarkm			switch(c) {
107957416Smarkm			case OLD_ENV_VAR:
108057416Smarkm			    if (last < 0 || last == OLD_ENV_VAR
108157416Smarkm				|| (empty && (last == OLD_ENV_VALUE)))
108257416Smarkm				goto env_ovar_ok;
108357416Smarkm			    got_var++;
108457416Smarkm			    last = OLD_ENV_VAR;
108557416Smarkm			    break;
108657416Smarkm			case OLD_ENV_VALUE:
108757416Smarkm			    if (last < 0 || last == OLD_ENV_VALUE
108857416Smarkm				|| (empty && (last == OLD_ENV_VAR)))
108957416Smarkm				goto env_ovar_wrong;
109057416Smarkm			    got_value++;
109157416Smarkm			    last = OLD_ENV_VALUE;
109257416Smarkm			    break;
109357416Smarkm			case ENV_USERVAR:
109457416Smarkm			    /* count strings of USERVAR as one */
109557416Smarkm			    if (last != ENV_USERVAR)
109657416Smarkm				got_uservar++;
109757416Smarkm			    if (empty) {
109857416Smarkm				if (last == OLD_ENV_VALUE)
109957416Smarkm				    goto env_ovar_ok;
110057416Smarkm				if (last == OLD_ENV_VAR)
110157416Smarkm				    goto env_ovar_wrong;
110257416Smarkm			    }
110357416Smarkm			    last = ENV_USERVAR;
110457416Smarkm			    break;
110557416Smarkm			case ENV_ESC:
110657416Smarkm			    if (!SB_EOF())
110757416Smarkm				c = SB_GET();
110857416Smarkm			    /* FALL THROUGH */
110957416Smarkm			default:
111057416Smarkm			    empty = 0;
111157416Smarkm			    continue;
111257416Smarkm			}
111357416Smarkm			empty = 1;
111457416Smarkm		    }
111557416Smarkm		    if (empty) {
111657416Smarkm			if (last == OLD_ENV_VALUE)
111757416Smarkm			    goto env_ovar_ok;
111857416Smarkm			if (last == OLD_ENV_VAR)
111957416Smarkm			    goto env_ovar_wrong;
112057416Smarkm		    }
112157416Smarkm		    /*
112257416Smarkm		     * Ok, the first thing was a USERVAR, and there
112357416Smarkm		     * are not two consecutive VAR or VALUE commands,
112457416Smarkm		     * and none of the VAR or VALUE commands are empty.
112557416Smarkm		     * If the client has sent us a well-formed option,
112657416Smarkm		     * then the number of VALUEs received should always
112757416Smarkm		     * be less than or equal to the number of VARs and
112857416Smarkm		     * USERVARs received.
112957416Smarkm		     *
113057416Smarkm		     * If we got exactly as many VALUEs as VARs and
113157416Smarkm		     * USERVARs, the client has the same definitions.
113257416Smarkm		     *
113357416Smarkm		     * If we got exactly as many VARs as VALUEs and
113457416Smarkm		     * USERVARS, the client has reversed definitions.
113557416Smarkm		     */
113657416Smarkm		    if (got_uservar + got_var == got_value) {
113757416Smarkm		    env_ovar_ok:
113857416Smarkm			env_ovar = OLD_ENV_VAR;
113957416Smarkm			env_ovalue = OLD_ENV_VALUE;
114057416Smarkm		    } else if (got_uservar + got_value == got_var) {
114157416Smarkm		    env_ovar_wrong:
114257416Smarkm			env_ovar = OLD_ENV_VALUE;
114357416Smarkm			env_ovalue = OLD_ENV_VAR;
114457416Smarkm			DIAG(TD_OPTIONS, {
114557416Smarkm			    output_data("ENVIRON VALUE and VAR are reversed!\r\n");
114657416Smarkm			});
114757416Smarkm
114857416Smarkm		    }
114957416Smarkm		}
115057416Smarkm		SB_RESTORE();
115157416Smarkm#endif
115257416Smarkm
115357416Smarkm		while (!SB_EOF()) {
115457416Smarkm		    c = SB_GET();
115557416Smarkm		    if ((c == env_ovar) || (c == ENV_USERVAR))
115657416Smarkm			break;
115757416Smarkm		}
115857416Smarkm	    }
115957416Smarkm
116057416Smarkm	if (SB_EOF())
116157416Smarkm	    return;
116257416Smarkm
116357416Smarkm	cp = varp = (char *)subpointer;
116457416Smarkm	valp = 0;
116557416Smarkm
116657416Smarkm	while (!SB_EOF()) {
116757416Smarkm	    c = SB_GET();
116857416Smarkm	    if (subchar == TELOPT_OLD_ENVIRON) {
116957416Smarkm		if (c == env_ovar)
117057416Smarkm		    c = NEW_ENV_VAR;
117157416Smarkm		else if (c == env_ovalue)
117257416Smarkm		    c = NEW_ENV_VALUE;
117357416Smarkm	    }
117457416Smarkm	    switch (c) {
117557416Smarkm
117657416Smarkm	    case NEW_ENV_VALUE:
117757416Smarkm		*cp = '\0';
117857416Smarkm		cp = valp = (char *)subpointer;
117957416Smarkm		break;
118057416Smarkm
118157416Smarkm	    case NEW_ENV_VAR:
118257416Smarkm	    case ENV_USERVAR:
118357416Smarkm		*cp = '\0';
118457416Smarkm		if (valp)
118572445Sassar		    esetenv(varp, valp, 1);
118657416Smarkm		else
118757416Smarkm		    unsetenv(varp);
118857416Smarkm		cp = varp = (char *)subpointer;
118957416Smarkm		valp = 0;
119057416Smarkm		break;
119157416Smarkm
119257416Smarkm	    case ENV_ESC:
119357416Smarkm		if (SB_EOF())
119457416Smarkm		    break;
119557416Smarkm		c = SB_GET();
119657416Smarkm		/* FALL THROUGH */
119757416Smarkm	    default:
119857416Smarkm		*cp++ = c;
119957416Smarkm		break;
120057416Smarkm	    }
120157416Smarkm	}
120257416Smarkm	*cp = '\0';
120357416Smarkm	if (valp)
120472445Sassar	    esetenv(varp, valp, 1);
120557416Smarkm	else
120657416Smarkm	    unsetenv(varp);
120757416Smarkm	break;
120857416Smarkm    }  /* end of case TELOPT_NEW_ENVIRON */
120957416Smarkm#ifdef AUTHENTICATION
121057416Smarkm    case TELOPT_AUTHENTICATION:
121157416Smarkm	if (SB_EOF())
121257416Smarkm	    break;
121357416Smarkm	switch(SB_GET()) {
121457416Smarkm	case TELQUAL_SEND:
121557416Smarkm	case TELQUAL_REPLY:
121657416Smarkm	    /*
121757416Smarkm	     * These are sent by us and cannot be sent by
121857416Smarkm	     * the client.
121957416Smarkm	     */
122057416Smarkm	    break;
122157416Smarkm	case TELQUAL_IS:
122257416Smarkm	    auth_is(subpointer, SB_LEN());
122357416Smarkm	    break;
122457416Smarkm	case TELQUAL_NAME:
122557416Smarkm	    auth_name(subpointer, SB_LEN());
122657416Smarkm	    break;
122757416Smarkm	}
122857416Smarkm	break;
122957416Smarkm#endif
123057416Smarkm#ifdef ENCRYPTION
123157416Smarkm    case TELOPT_ENCRYPT:
123257416Smarkm	if (SB_EOF())
123357416Smarkm	    break;
123457416Smarkm	switch(SB_GET()) {
123557416Smarkm	case ENCRYPT_SUPPORT:
123657416Smarkm	    encrypt_support(subpointer, SB_LEN());
123757416Smarkm	    break;
123857416Smarkm	case ENCRYPT_IS:
123957416Smarkm	    encrypt_is(subpointer, SB_LEN());
124057416Smarkm	    break;
124157416Smarkm	case ENCRYPT_REPLY:
124257416Smarkm	    encrypt_reply(subpointer, SB_LEN());
124357416Smarkm	    break;
124457416Smarkm	case ENCRYPT_START:
124557416Smarkm	    encrypt_start(subpointer, SB_LEN());
124657416Smarkm	    break;
124757416Smarkm	case ENCRYPT_END:
1248178825Sdfr	    if (require_encryption)
1249178825Sdfr		fatal(net, "Output encryption is not possible to turn off");
125057416Smarkm	    encrypt_end();
125157416Smarkm	    break;
125257416Smarkm	case ENCRYPT_REQSTART:
125357416Smarkm	    encrypt_request_start(subpointer, SB_LEN());
125457416Smarkm	    break;
125557416Smarkm	case ENCRYPT_REQEND:
125657416Smarkm	    /*
125757416Smarkm	     * We can always send an REQEND so that we cannot
125857416Smarkm	     * get stuck encrypting.  We should only get this
125957416Smarkm	     * if we have been able to get in the correct mode
126057416Smarkm	     * anyhow.
126157416Smarkm	     */
1262178825Sdfr	    if (require_encryption)
1263178825Sdfr		fatal(net, "Input encryption is not possible to turn off");
126457416Smarkm	    encrypt_request_end();
126557416Smarkm	    break;
126657416Smarkm	case ENCRYPT_ENC_KEYID:
126757416Smarkm	    encrypt_enc_keyid(subpointer, SB_LEN());
126857416Smarkm	    break;
126957416Smarkm	case ENCRYPT_DEC_KEYID:
127057416Smarkm	    encrypt_dec_keyid(subpointer, SB_LEN());
127157416Smarkm	    break;
127257416Smarkm	default:
127357416Smarkm	    break;
127457416Smarkm	}
127557416Smarkm	break;
127657416Smarkm#endif
127757416Smarkm
127857416Smarkm    default:
127957416Smarkm	break;
128057416Smarkm    }  /* end of switch */
128157416Smarkm
128257416Smarkm}  /* end of suboption */
128357416Smarkm
128457416Smarkmvoid
128557416Smarkmdoclientstat(void)
128657416Smarkm{
128757416Smarkm    clientstat(TELOPT_LINEMODE, WILL, 0);
128857416Smarkm}
128957416Smarkm
1290142403Snectar#undef ADD
129157416Smarkm#define	ADD(c)	 *ncp++ = c
129257416Smarkm#define	ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
129357416Smarkm
129457416Smarkmvoid
129557416Smarkmsend_status(void)
129657416Smarkm{
129757416Smarkm    unsigned char statusbuf[256];
129857416Smarkm    unsigned char *ncp;
129957416Smarkm    unsigned char i;
130057416Smarkm
130157416Smarkm    ncp = statusbuf;
130257416Smarkm
130357416Smarkm    netflush();	/* get rid of anything waiting to go out */
130457416Smarkm
130557416Smarkm    ADD(IAC);
130657416Smarkm    ADD(SB);
130757416Smarkm    ADD(TELOPT_STATUS);
130857416Smarkm    ADD(TELQUAL_IS);
130957416Smarkm
131057416Smarkm    /*
131157416Smarkm     * We check the want_state rather than the current state,
131257416Smarkm     * because if we received a DO/WILL for an option that we
131357416Smarkm     * don't support, and the other side didn't send a DONT/WONT
131457416Smarkm     * in response to our WONT/DONT, then the "state" will be
131557416Smarkm     * WILL/DO, and the "want_state" will be WONT/DONT.  We
131657416Smarkm     * need to go by the latter.
131757416Smarkm     */
131857416Smarkm    for (i = 0; i < (unsigned char)NTELOPTS; i++) {
131957416Smarkm	if (my_want_state_is_will(i)) {
132057416Smarkm	    ADD(WILL);
132157416Smarkm	    ADD_DATA(i);
132257416Smarkm	}
132357416Smarkm	if (his_want_state_is_will(i)) {
132457416Smarkm	    ADD(DO);
132557416Smarkm	    ADD_DATA(i);
132657416Smarkm	}
132757416Smarkm    }
132857416Smarkm
132957416Smarkm    if (his_want_state_is_will(TELOPT_LFLOW)) {
133057416Smarkm	ADD(SB);
133157416Smarkm	ADD(TELOPT_LFLOW);
133257416Smarkm	if (flowmode) {
133357416Smarkm	    ADD(LFLOW_ON);
133457416Smarkm	} else {
133557416Smarkm	    ADD(LFLOW_OFF);
133657416Smarkm	}
133757416Smarkm	ADD(SE);
133857416Smarkm
133957416Smarkm	if (restartany >= 0) {
134057416Smarkm	    ADD(SB);
134157416Smarkm	    ADD(TELOPT_LFLOW);
134257416Smarkm	    if (restartany) {
134357416Smarkm		ADD(LFLOW_RESTART_ANY);
134457416Smarkm	    } else {
134557416Smarkm		ADD(LFLOW_RESTART_XON);
134657416Smarkm	    }
134757416Smarkm	    ADD(SE);
134857416Smarkm	}
134957416Smarkm    }
135057416Smarkm
135157416Smarkm
135257416Smarkm    ADD(IAC);
135357416Smarkm    ADD(SE);
135457416Smarkm
135557416Smarkm    writenet(statusbuf, ncp - statusbuf);
135657416Smarkm    netflush();	/* Send it on its way */
135757416Smarkm
135857416Smarkm    DIAG(TD_OPTIONS,
135957416Smarkm	 {printsub('>', statusbuf, ncp - statusbuf); netflush();});
136057416Smarkm}
1361