157416Smarkm/*
257416Smarkm * Copyright (c) 1988, 1990, 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 "telnet_locl.h"
3557416Smarkm
36233294SstasRCSID("$Id$");
3757416Smarkm
3857416Smarkm#define	strip(x) (eight ? (x) : ((x) & 0x7f))
3957416Smarkm
4057416Smarkmstatic unsigned char	subbuffer[SUBBUFSIZE],
4157416Smarkm			*subpointer, *subend;	 /* buffer for sub-options */
4257416Smarkm#define	SB_CLEAR()	subpointer = subbuffer;
4357416Smarkm#define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
4457416Smarkm#define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
4557416Smarkm				*subpointer++ = (c); \
4657416Smarkm			}
4757416Smarkm
4857416Smarkm#define	SB_GET()	((*subpointer++)&0xff)
4957416Smarkm#define	SB_PEEK()	((*subpointer)&0xff)
5057416Smarkm#define	SB_EOF()	(subpointer >= subend)
5157416Smarkm#define	SB_LEN()	(subend - subpointer)
5257416Smarkm
5357416Smarkmchar	options[256];		/* The combined options */
5457416Smarkmchar	do_dont_resp[256];
5557416Smarkmchar	will_wont_resp[256];
5657416Smarkm
5757416Smarkmint
5857416Smarkm	eight = 3,
5957416Smarkm	binary = 0,
6057416Smarkm	autologin = 0,	/* Autologin anyone? */
6157416Smarkm	skiprc = 0,
6257416Smarkm	connected,
6357416Smarkm	showoptions,
6457416Smarkm	ISend,		/* trying to send network data in */
6557416Smarkm	debug = 0,
6657416Smarkm	crmod,
6757416Smarkm	netdata,	/* Print out network data flow */
6857416Smarkm	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
6957416Smarkm	telnetport,
7090926Snectar        wantencryption = 0,
7157416Smarkm	SYNCHing,	/* we are in TELNET SYNCH mode */
7257416Smarkm	flushout,	/* flush output */
7357416Smarkm	autoflush = 0,	/* flush output when interrupting? */
7457416Smarkm	autosynch,	/* send interrupt characters with SYNCH? */
7557416Smarkm	localflow,	/* we handle flow control locally */
7657416Smarkm	restartany,	/* if flow control enabled, restart on any character */
7757416Smarkm	localchars,	/* we recognize interrupt/quit */
7857416Smarkm	donelclchars,	/* the user has set "localchars" */
7957416Smarkm	donebinarytoggle,	/* the user has put us in binary */
8057416Smarkm	dontlecho,	/* do we suppress local echoing right now? */
8157416Smarkm	globalmode;
8257416Smarkm
8357416Smarkmchar *prompt = 0;
8457416Smarkm
8590926Snectarint scheduler_lockout_tty = 0;
8690926Snectar
8757416Smarkmcc_t escape;
8857416Smarkmcc_t rlogin;
8957416Smarkm#ifdef	KLUDGELINEMODE
9057416Smarkmcc_t echoc;
9157416Smarkm#endif
9257416Smarkm
9357416Smarkm/*
9457416Smarkm * Telnet receiver states for fsm
9557416Smarkm */
9657416Smarkm#define	TS_DATA		0
9757416Smarkm#define	TS_IAC		1
9857416Smarkm#define	TS_WILL		2
9957416Smarkm#define	TS_WONT		3
10057416Smarkm#define	TS_DO		4
10157416Smarkm#define	TS_DONT		5
10257416Smarkm#define	TS_CR		6
10357416Smarkm#define	TS_SB		7		/* sub-option collection */
10457416Smarkm#define	TS_SE		8		/* looking for sub-option end */
10557416Smarkm
10657416Smarkmstatic int	telrcv_state;
10757416Smarkm#ifdef	OLD_ENVIRON
10857416Smarkmunsigned char telopt_environ = TELOPT_NEW_ENVIRON;
10957416Smarkm#else
11057416Smarkm# define telopt_environ TELOPT_NEW_ENVIRON
11157416Smarkm#endif
11257416Smarkm
11357416Smarkmjmp_buf	toplevel;
11457416Smarkmjmp_buf	peerdied;
11557416Smarkm
11657416Smarkmint	flushline;
11757416Smarkmint	linemode;
11857416Smarkm
11957416Smarkm#ifdef	KLUDGELINEMODE
12057416Smarkmint	kludgelinemode = 1;
12157416Smarkm#endif
12257416Smarkm
12357416Smarkm/*
12457416Smarkm * The following are some clocks used to decide how to interpret
12557416Smarkm * the relationship between various variables.
12657416Smarkm */
12757416Smarkm
12857416SmarkmClocks clocks;
12957416Smarkm
13057416Smarkmstatic int is_unique(char *name, char **as, char **ae);
13157416Smarkm
13257416Smarkm
13357416Smarkm/*
13457416Smarkm * Initialize telnet environment.
13557416Smarkm */
13657416Smarkm
13757416Smarkmvoid
13857416Smarkminit_telnet(void)
13957416Smarkm{
14057416Smarkm    env_init();
14157416Smarkm
14257416Smarkm    SB_CLEAR();
14357416Smarkm    memset(options, 0, sizeof options);
14457416Smarkm
14557416Smarkm    connected = ISend = localflow = donebinarytoggle = 0;
14657416Smarkm#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
14757416Smarkm    auth_encrypt_connect(connected);
14857416Smarkm#endif	/* defined(AUTHENTICATION) || defined(ENCRYPTION)  */
14957416Smarkm    restartany = -1;
15057416Smarkm
15157416Smarkm    SYNCHing = 0;
15257416Smarkm
15357416Smarkm    /* Don't change NetTrace */
15457416Smarkm
15557416Smarkm    escape = CONTROL(']');
15657416Smarkm    rlogin = _POSIX_VDISABLE;
15757416Smarkm#ifdef	KLUDGELINEMODE
15857416Smarkm    echoc = CONTROL('E');
15957416Smarkm#endif
16057416Smarkm
16157416Smarkm    flushline = 1;
16257416Smarkm    telrcv_state = TS_DATA;
16357416Smarkm}
16457416Smarkm
16557416Smarkm
16657416Smarkm/*
16757416Smarkm * These routines are in charge of sending option negotiations
16857416Smarkm * to the other side.
16957416Smarkm *
17057416Smarkm * The basic idea is that we send the negotiation if either side
17157416Smarkm * is in disagreement as to what the current state should be.
17257416Smarkm */
17357416Smarkm
17457416Smarkmvoid
17557416Smarkmsend_do(int c, int init)
17657416Smarkm{
17757416Smarkm    if (init) {
17857416Smarkm	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
17957416Smarkm				my_want_state_is_do(c))
18057416Smarkm	    return;
18157416Smarkm	set_my_want_state_do(c);
18257416Smarkm	do_dont_resp[c]++;
18357416Smarkm    }
18457416Smarkm    NET2ADD(IAC, DO);
18557416Smarkm    NETADD(c);
18657416Smarkm    printoption("SENT", DO, c);
18757416Smarkm}
18857416Smarkm
18957416Smarkmvoid
19057416Smarkmsend_dont(int c, int init)
19157416Smarkm{
19257416Smarkm    if (init) {
19357416Smarkm	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
19457416Smarkm				my_want_state_is_dont(c))
19557416Smarkm	    return;
19657416Smarkm	set_my_want_state_dont(c);
19757416Smarkm	do_dont_resp[c]++;
19857416Smarkm    }
19957416Smarkm    NET2ADD(IAC, DONT);
20057416Smarkm    NETADD(c);
20157416Smarkm    printoption("SENT", DONT, c);
20257416Smarkm}
20357416Smarkm
20457416Smarkmvoid
20557416Smarkmsend_will(int c, int init)
20657416Smarkm{
20757416Smarkm    if (init) {
20857416Smarkm	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
20957416Smarkm				my_want_state_is_will(c))
21057416Smarkm	    return;
21157416Smarkm	set_my_want_state_will(c);
21257416Smarkm	will_wont_resp[c]++;
21357416Smarkm    }
21457416Smarkm    NET2ADD(IAC, WILL);
21557416Smarkm    NETADD(c);
21657416Smarkm    printoption("SENT", WILL, c);
21757416Smarkm}
21857416Smarkm
21957416Smarkmvoid
22057416Smarkmsend_wont(int c, int init)
22157416Smarkm{
22257416Smarkm    if (init) {
22357416Smarkm	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
22457416Smarkm				my_want_state_is_wont(c))
22557416Smarkm	    return;
22657416Smarkm	set_my_want_state_wont(c);
22757416Smarkm	will_wont_resp[c]++;
22857416Smarkm    }
22957416Smarkm    NET2ADD(IAC, WONT);
23057416Smarkm    NETADD(c);
23157416Smarkm    printoption("SENT", WONT, c);
23257416Smarkm}
23357416Smarkm
23457416Smarkm
23557416Smarkmvoid
23657416Smarkmwilloption(int option)
23757416Smarkm{
23857416Smarkm	int new_state_ok = 0;
23957416Smarkm
24057416Smarkm	if (do_dont_resp[option]) {
24157416Smarkm	    --do_dont_resp[option];
24257416Smarkm	    if (do_dont_resp[option] && my_state_is_do(option))
24357416Smarkm		--do_dont_resp[option];
24457416Smarkm	}
24557416Smarkm
24657416Smarkm	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
24757416Smarkm
24857416Smarkm	    switch (option) {
24957416Smarkm
25057416Smarkm	    case TELOPT_ECHO:
25157416Smarkm	    case TELOPT_BINARY:
25257416Smarkm	    case TELOPT_SGA:
25357416Smarkm		settimer(modenegotiated);
25457416Smarkm		/* FALL THROUGH */
25557416Smarkm	    case TELOPT_STATUS:
25657416Smarkm#if	defined(AUTHENTICATION)
25757416Smarkm	    case TELOPT_AUTHENTICATION:
25857416Smarkm#endif
25957416Smarkm#if	defined(ENCRYPTION)
26057416Smarkm	    case TELOPT_ENCRYPT:
26157416Smarkm#endif
26257416Smarkm		new_state_ok = 1;
26357416Smarkm		break;
26457416Smarkm
26557416Smarkm	    case TELOPT_TM:
26657416Smarkm		if (flushout)
26757416Smarkm		    flushout = 0;
26857416Smarkm		/*
26957416Smarkm		 * Special case for TM.  If we get back a WILL,
27057416Smarkm		 * pretend we got back a WONT.
27157416Smarkm		 */
27257416Smarkm		set_my_want_state_dont(option);
27357416Smarkm		set_my_state_dont(option);
27457416Smarkm		return;			/* Never reply to TM will's/wont's */
27557416Smarkm
27657416Smarkm	    case TELOPT_LINEMODE:
27757416Smarkm	    default:
27857416Smarkm		break;
27957416Smarkm	    }
28057416Smarkm
28157416Smarkm	    if (new_state_ok) {
28257416Smarkm		set_my_want_state_do(option);
28357416Smarkm		send_do(option, 0);
28457416Smarkm		setconnmode(0);		/* possibly set new tty mode */
28557416Smarkm	    } else {
28657416Smarkm		do_dont_resp[option]++;
28757416Smarkm		send_dont(option, 0);
28857416Smarkm	    }
28957416Smarkm	}
29057416Smarkm	set_my_state_do(option);
29157416Smarkm#if	defined(ENCRYPTION)
29257416Smarkm	if (option == TELOPT_ENCRYPT)
29357416Smarkm		encrypt_send_support();
29457416Smarkm#endif
29557416Smarkm}
29657416Smarkm
29757416Smarkmvoid
29857416Smarkmwontoption(int option)
29957416Smarkm{
30057416Smarkm	if (do_dont_resp[option]) {
30157416Smarkm	    --do_dont_resp[option];
30257416Smarkm	    if (do_dont_resp[option] && my_state_is_dont(option))
30357416Smarkm		--do_dont_resp[option];
30457416Smarkm	}
30557416Smarkm
30657416Smarkm	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
30757416Smarkm
30857416Smarkm	    switch (option) {
30957416Smarkm
31057416Smarkm#ifdef	KLUDGELINEMODE
31157416Smarkm	    case TELOPT_SGA:
31257416Smarkm		if (!kludgelinemode)
31357416Smarkm		    break;
31457416Smarkm		/* FALL THROUGH */
31557416Smarkm#endif
31657416Smarkm	    case TELOPT_ECHO:
31757416Smarkm		settimer(modenegotiated);
31857416Smarkm		break;
31957416Smarkm
32057416Smarkm	    case TELOPT_TM:
32157416Smarkm		if (flushout)
32257416Smarkm		    flushout = 0;
32357416Smarkm		set_my_want_state_dont(option);
32457416Smarkm		set_my_state_dont(option);
32557416Smarkm		return;		/* Never reply to TM will's/wont's */
32657416Smarkm
32757416Smarkm#ifdef ENCRYPTION
32857416Smarkm  	    case TELOPT_ENCRYPT:
32957416Smarkm	      encrypt_not();
33057416Smarkm	      break;
33157416Smarkm#endif
33257416Smarkm	    default:
33357416Smarkm		break;
33457416Smarkm	    }
33557416Smarkm	    set_my_want_state_dont(option);
33657416Smarkm	    if (my_state_is_do(option))
33757416Smarkm		send_dont(option, 0);
33857416Smarkm	    setconnmode(0);			/* Set new tty mode */
33957416Smarkm	} else if (option == TELOPT_TM) {
34057416Smarkm	    /*
34157416Smarkm	     * Special case for TM.
34257416Smarkm	     */
34357416Smarkm	    if (flushout)
34457416Smarkm		flushout = 0;
34557416Smarkm	    set_my_want_state_dont(option);
34657416Smarkm	}
34757416Smarkm	set_my_state_dont(option);
34857416Smarkm}
34957416Smarkm
35057416Smarkmstatic void
35157416Smarkmdooption(int option)
35257416Smarkm{
35357416Smarkm	int new_state_ok = 0;
35457416Smarkm
35557416Smarkm	if (will_wont_resp[option]) {
35657416Smarkm	    --will_wont_resp[option];
35757416Smarkm	    if (will_wont_resp[option] && my_state_is_will(option))
35857416Smarkm		--will_wont_resp[option];
35957416Smarkm	}
36057416Smarkm
36157416Smarkm	if (will_wont_resp[option] == 0) {
36257416Smarkm	  if (my_want_state_is_wont(option)) {
36357416Smarkm
36457416Smarkm	    switch (option) {
36557416Smarkm
36657416Smarkm	    case TELOPT_TM:
36757416Smarkm		/*
36857416Smarkm		 * Special case for TM.  We send a WILL, but pretend
36957416Smarkm		 * we sent WONT.
37057416Smarkm		 */
37157416Smarkm		send_will(option, 0);
37257416Smarkm		set_my_want_state_wont(TELOPT_TM);
37357416Smarkm		set_my_state_wont(TELOPT_TM);
37457416Smarkm		return;
37557416Smarkm
37657416Smarkm	    case TELOPT_BINARY:		/* binary mode */
37757416Smarkm	    case TELOPT_NAWS:		/* window size */
37857416Smarkm	    case TELOPT_TSPEED:		/* terminal speed */
37957416Smarkm	    case TELOPT_LFLOW:		/* local flow control */
38057416Smarkm	    case TELOPT_TTYPE:		/* terminal type option */
38157416Smarkm	    case TELOPT_SGA:		/* no big deal */
38257416Smarkm#if	defined(ENCRYPTION)
38357416Smarkm	    case TELOPT_ENCRYPT:	/* encryption variable option */
38457416Smarkm#endif
38557416Smarkm		new_state_ok = 1;
38657416Smarkm		break;
38757416Smarkm
38857416Smarkm	    case TELOPT_NEW_ENVIRON:	/* New environment variable option */
38957416Smarkm#ifdef	OLD_ENVIRON
39057416Smarkm		if (my_state_is_will(TELOPT_OLD_ENVIRON))
39157416Smarkm			send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */
39257416Smarkm		goto env_common;
39357416Smarkm	    case TELOPT_OLD_ENVIRON:	/* Old environment variable option */
39457416Smarkm		if (my_state_is_will(TELOPT_NEW_ENVIRON))
39557416Smarkm			break;		/* Don't enable if new one is in use! */
39657416Smarkm	    env_common:
39757416Smarkm		telopt_environ = option;
39857416Smarkm#endif
39957416Smarkm		new_state_ok = 1;
40057416Smarkm		break;
40157416Smarkm
40257416Smarkm#if	defined(AUTHENTICATION)
40357416Smarkm	    case TELOPT_AUTHENTICATION:
40457416Smarkm		if (autologin)
40557416Smarkm			new_state_ok = 1;
40657416Smarkm		break;
40757416Smarkm#endif
40857416Smarkm
40957416Smarkm	    case TELOPT_XDISPLOC:	/* X Display location */
41057416Smarkm		if (env_getvalue((unsigned char *)"DISPLAY"))
41157416Smarkm		    new_state_ok = 1;
41257416Smarkm		break;
41357416Smarkm
41457416Smarkm	    case TELOPT_LINEMODE:
41557416Smarkm#ifdef	KLUDGELINEMODE
41657416Smarkm		kludgelinemode = 0;
41757416Smarkm		send_do(TELOPT_SGA, 1);
41857416Smarkm#endif
41957416Smarkm		set_my_want_state_will(TELOPT_LINEMODE);
42057416Smarkm		send_will(option, 0);
42157416Smarkm		set_my_state_will(TELOPT_LINEMODE);
42257416Smarkm		slc_init();
42357416Smarkm		return;
42457416Smarkm
42557416Smarkm	    case TELOPT_ECHO:		/* We're never going to echo... */
42657416Smarkm	    default:
42757416Smarkm		break;
42857416Smarkm	    }
42957416Smarkm
43057416Smarkm	    if (new_state_ok) {
43157416Smarkm		set_my_want_state_will(option);
43257416Smarkm		send_will(option, 0);
43357416Smarkm		setconnmode(0);			/* Set new tty mode */
43457416Smarkm	    } else {
43557416Smarkm		will_wont_resp[option]++;
43657416Smarkm		send_wont(option, 0);
43757416Smarkm	    }
43857416Smarkm	  } else {
43957416Smarkm	    /*
44057416Smarkm	     * Handle options that need more things done after the
44157416Smarkm	     * other side has acknowledged the option.
44257416Smarkm	     */
44357416Smarkm	    switch (option) {
44457416Smarkm	    case TELOPT_LINEMODE:
44557416Smarkm#ifdef	KLUDGELINEMODE
44657416Smarkm		kludgelinemode = 0;
44757416Smarkm		send_do(TELOPT_SGA, 1);
44857416Smarkm#endif
44957416Smarkm		set_my_state_will(option);
45057416Smarkm		slc_init();
45157416Smarkm		send_do(TELOPT_SGA, 0);
45257416Smarkm		return;
45357416Smarkm	    }
45457416Smarkm	  }
45557416Smarkm	}
45657416Smarkm	set_my_state_will(option);
45757416Smarkm}
45857416Smarkm
45957416Smarkmstatic void
46057416Smarkmdontoption(int option)
46157416Smarkm{
46257416Smarkm
46357416Smarkm	if (will_wont_resp[option]) {
46457416Smarkm	    --will_wont_resp[option];
46557416Smarkm	    if (will_wont_resp[option] && my_state_is_wont(option))
46657416Smarkm		--will_wont_resp[option];
46757416Smarkm	}
46857416Smarkm
46957416Smarkm	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
47057416Smarkm	    switch (option) {
47157416Smarkm	    case TELOPT_LINEMODE:
47257416Smarkm		linemode = 0;	/* put us back to the default state */
47357416Smarkm		break;
47457416Smarkm#ifdef	OLD_ENVIRON
47557416Smarkm	    case TELOPT_NEW_ENVIRON:
47657416Smarkm		/*
47757416Smarkm		 * The new environ option wasn't recognized, try
47857416Smarkm		 * the old one.
47957416Smarkm		 */
48057416Smarkm		send_will(TELOPT_OLD_ENVIRON, 1);
48157416Smarkm		telopt_environ = TELOPT_OLD_ENVIRON;
48257416Smarkm		break;
48357416Smarkm#endif
48457416Smarkm#if 0
48557416Smarkm#ifdef ENCRYPTION
48657416Smarkm	    case TELOPT_ENCRYPT:
48757416Smarkm	      encrypt_not();
48857416Smarkm	      break;
48957416Smarkm#endif
49057416Smarkm#endif
49157416Smarkm	    }
49257416Smarkm	    /* we always accept a DONT */
49357416Smarkm	    set_my_want_state_wont(option);
49457416Smarkm	    if (my_state_is_will(option))
49557416Smarkm		send_wont(option, 0);
49657416Smarkm	    setconnmode(0);			/* Set new tty mode */
49757416Smarkm	}
49857416Smarkm	set_my_state_wont(option);
49957416Smarkm}
50057416Smarkm
50157416Smarkm/*
50257416Smarkm * Given a buffer returned by tgetent(), this routine will turn
503178825Sdfr * the pipe separated list of names in the buffer into an array
50457416Smarkm * of pointers to null terminated names.  We toss out any bad,
50557416Smarkm * duplicate, or verbose names (names with spaces).
50657416Smarkm */
50757416Smarkm
50857416Smarkmstatic char *name_unknown = "UNKNOWN";
50957416Smarkmstatic char *unknown[] = { 0, 0 };
51057416Smarkm
51157416Smarkmstatic char **
51257416Smarkmmklist(char *buf, char *name)
51357416Smarkm{
51457416Smarkm	int n;
51557416Smarkm	char c, *cp, **argvp, *cp2, **argv, **avt;
51657416Smarkm
51757416Smarkm	if (name) {
51857416Smarkm		if ((int)strlen(name) > 40) {
51957416Smarkm			name = 0;
52057416Smarkm			unknown[0] = name_unknown;
52157416Smarkm		} else {
52257416Smarkm			unknown[0] = name;
52357416Smarkm			strupr(name);
52457416Smarkm		}
52557416Smarkm	} else
52657416Smarkm		unknown[0] = name_unknown;
52757416Smarkm	/*
52857416Smarkm	 * Count up the number of names.
52957416Smarkm	 */
53057416Smarkm	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
53157416Smarkm		if (*cp == '|')
53257416Smarkm			n++;
53357416Smarkm	}
53457416Smarkm	/*
53557416Smarkm	 * Allocate an array to put the name pointers into
53657416Smarkm	 */
53757416Smarkm	argv = (char **)malloc((n+3)*sizeof(char *));
53857416Smarkm	if (argv == 0)
53957416Smarkm		return(unknown);
54057416Smarkm
54157416Smarkm	/*
54257416Smarkm	 * Fill up the array of pointers to names.
54357416Smarkm	 */
54457416Smarkm	*argv = 0;
54557416Smarkm	argvp = argv+1;
54657416Smarkm	n = 0;
54757416Smarkm	for (cp = cp2 = buf; (c = *cp);  cp++) {
54857416Smarkm		if (c == '|' || c == ':') {
54957416Smarkm			*cp++ = '\0';
55057416Smarkm			/*
55157416Smarkm			 * Skip entries that have spaces or are over 40
55257416Smarkm			 * characters long.  If this is our environment
55357416Smarkm			 * name, then put it up front.  Otherwise, as
55457416Smarkm			 * long as this is not a duplicate name (case
55557416Smarkm			 * insensitive) add it to the list.
55657416Smarkm			 */
55757416Smarkm			if (n || (cp - cp2 > 41))
55857416Smarkm				;
55957416Smarkm			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
56057416Smarkm				*argv = cp2;
56157416Smarkm			else if (is_unique(cp2, argv+1, argvp))
56257416Smarkm				*argvp++ = cp2;
56357416Smarkm			if (c == ':')
56457416Smarkm				break;
56557416Smarkm			/*
56657416Smarkm			 * Skip multiple delimiters. Reset cp2 to
56757416Smarkm			 * the beginning of the next name. Reset n,
56857416Smarkm			 * the flag for names with spaces.
56957416Smarkm			 */
57057416Smarkm			while ((c = *cp) == '|')
57157416Smarkm				cp++;
57257416Smarkm			cp2 = cp;
57357416Smarkm			n = 0;
57457416Smarkm		}
57557416Smarkm		/*
57657416Smarkm		 * Skip entries with spaces or non-ascii values.
57757416Smarkm		 * Convert lower case letters to upper case.
57857416Smarkm		 */
579178825Sdfr#undef ISASCII
58057416Smarkm#define ISASCII(c) (!((c)&0x80))
58157416Smarkm		if ((c == ' ') || !ISASCII(c))
58257416Smarkm			n = 1;
58390926Snectar		else if (islower((unsigned char)c))
584178825Sdfr			*cp = toupper((unsigned char)c);
58557416Smarkm	}
58657416Smarkm
58757416Smarkm	/*
58857416Smarkm	 * Check for an old V6 2 character name.  If the second
58957416Smarkm	 * name points to the beginning of the buffer, and is
59057416Smarkm	 * only 2 characters long, move it to the end of the array.
59157416Smarkm	 */
59257416Smarkm	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
59357416Smarkm		--argvp;
59457416Smarkm		for (avt = &argv[1]; avt < argvp; avt++)
59557416Smarkm			*avt = *(avt+1);
59657416Smarkm		*argvp++ = buf;
59757416Smarkm	}
59857416Smarkm
59957416Smarkm	/*
60057416Smarkm	 * Duplicate last name, for TTYPE option, and null
60157416Smarkm	 * terminate the array.  If we didn't find a match on
60257416Smarkm	 * our terminal name, put that name at the beginning.
60357416Smarkm	 */
60457416Smarkm	cp = *(argvp-1);
60557416Smarkm	*argvp++ = cp;
60657416Smarkm	*argvp = 0;
60757416Smarkm
60857416Smarkm	if (*argv == 0) {
60957416Smarkm		if (name)
61057416Smarkm			*argv = name;
61157416Smarkm		else {
61257416Smarkm			--argvp;
61357416Smarkm			for (avt = argv; avt < argvp; avt++)
61457416Smarkm				*avt = *(avt+1);
61557416Smarkm		}
61657416Smarkm	}
61757416Smarkm	if (*argv)
61857416Smarkm		return(argv);
61957416Smarkm	else
62057416Smarkm		return(unknown);
62157416Smarkm}
62257416Smarkm
62357416Smarkmstatic int
62457416Smarkmis_unique(char *name, char **as, char **ae)
62557416Smarkm{
62657416Smarkm	char **ap;
62757416Smarkm	int n;
62857416Smarkm
62957416Smarkm	n = strlen(name) + 1;
63057416Smarkm	for (ap = as; ap < ae; ap++)
63157416Smarkm		if (strncasecmp(*ap, name, n) == 0)
63257416Smarkm			return(0);
63357416Smarkm	return (1);
63457416Smarkm}
63557416Smarkm
63657416Smarkmstatic char termbuf[1024];
63757416Smarkm
63857416Smarkmstatic int
63957416Smarkmtelnet_setupterm(const char *tname, int fd, int *errp)
64057416Smarkm{
64172445Sassar#ifdef HAVE_TGETENT
64272445Sassar    if (tgetent(termbuf, tname) == 1) {
64372445Sassar	termbuf[1023] = '\0';
64457416Smarkm	if (errp)
64572445Sassar	    *errp = 1;
64672445Sassar	return(0);
64772445Sassar    }
64872445Sassar    if (errp)
64972445Sassar	*errp = 0;
65072445Sassar    return(-1);
65172445Sassar#else
65272445Sassar    strlcpy(termbuf, tname, sizeof(termbuf));
65372445Sassar    if(errp) *errp = 1;
65472445Sassar    return 0;
65572445Sassar#endif
65657416Smarkm}
65757416Smarkm
65857416Smarkmint resettermname = 1;
65957416Smarkm
66057416Smarkmstatic char *
66157416Smarkmgettermname()
66257416Smarkm{
66357416Smarkm	char *tname;
66457416Smarkm	static char **tnamep = 0;
66557416Smarkm	static char **next;
66657416Smarkm	int err;
66757416Smarkm
66857416Smarkm	if (resettermname) {
66957416Smarkm		resettermname = 0;
67057416Smarkm		if (tnamep && tnamep != unknown)
67157416Smarkm			free(tnamep);
67257416Smarkm		if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) &&
67357416Smarkm				telnet_setupterm(tname, 1, &err) == 0) {
67457416Smarkm			tnamep = mklist(termbuf, tname);
67557416Smarkm		} else {
67657416Smarkm			if (tname && ((int)strlen(tname) <= 40)) {
67757416Smarkm				unknown[0] = tname;
67857416Smarkm				strupr(tname);
67957416Smarkm			} else
68057416Smarkm				unknown[0] = name_unknown;
68157416Smarkm			tnamep = unknown;
68257416Smarkm		}
68357416Smarkm		next = tnamep;
68457416Smarkm	}
68557416Smarkm	if (*next == 0)
68657416Smarkm		next = tnamep;
68757416Smarkm	return(*next++);
68857416Smarkm}
68957416Smarkm/*
69057416Smarkm * suboption()
69157416Smarkm *
69257416Smarkm *	Look at the sub-option buffer, and try to be helpful to the other
69357416Smarkm * side.
69457416Smarkm *
69557416Smarkm *	Currently we recognize:
69657416Smarkm *
69757416Smarkm *		Terminal type, send request.
69857416Smarkm *		Terminal speed (send request).
69957416Smarkm *		Local flow control (is request).
70057416Smarkm *		Linemode
70157416Smarkm */
70257416Smarkm
70357416Smarkmstatic void
70457416Smarkmsuboption()
70557416Smarkm{
70657416Smarkm    unsigned char subchar;
70757416Smarkm
70857416Smarkm    printsub('<', subbuffer, SB_LEN()+2);
70957416Smarkm    switch (subchar = SB_GET()) {
71057416Smarkm    case TELOPT_TTYPE:
71157416Smarkm	if (my_want_state_is_wont(TELOPT_TTYPE))
71257416Smarkm	    return;
71357416Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
71457416Smarkm	    return;
71557416Smarkm	} else {
71657416Smarkm	    char *name;
71757416Smarkm	    unsigned char temp[50];
71857416Smarkm	    int len;
71957416Smarkm
72057416Smarkm	    name = gettermname();
72157416Smarkm	    len = strlen(name) + 4 + 2;
72257416Smarkm	    if (len < NETROOM()) {
72357416Smarkm		snprintf((char *)temp, sizeof(temp),
72457416Smarkm			 "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
72557416Smarkm			 TELQUAL_IS, name, IAC, SE);
72657416Smarkm		ring_supply_data(&netoring, temp, len);
72757416Smarkm		printsub('>', &temp[2], len-2);
72857416Smarkm	    } else {
72957416Smarkm		ExitString("No room in buffer for terminal type.\n", 1);
73057416Smarkm		/*NOTREACHED*/
73157416Smarkm	    }
73257416Smarkm	}
73357416Smarkm	break;
73457416Smarkm    case TELOPT_TSPEED:
73557416Smarkm	if (my_want_state_is_wont(TELOPT_TSPEED))
73657416Smarkm	    return;
73757416Smarkm	if (SB_EOF())
73857416Smarkm	    return;
73957416Smarkm	if (SB_GET() == TELQUAL_SEND) {
74057416Smarkm	    long output_speed, input_speed;
74157416Smarkm	    unsigned char temp[50];
74257416Smarkm	    int len;
74357416Smarkm
74457416Smarkm	    TerminalSpeeds(&input_speed, &output_speed);
74557416Smarkm
74657416Smarkm	    snprintf((char *)temp, sizeof(temp),
74757416Smarkm		     "%c%c%c%c%u,%u%c%c", IAC, SB, TELOPT_TSPEED,
74857416Smarkm		     TELQUAL_IS,
74957416Smarkm		     (unsigned)output_speed,
75057416Smarkm		     (unsigned)input_speed, IAC, SE);
75157416Smarkm	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
75257416Smarkm
75357416Smarkm	    if (len < NETROOM()) {
75457416Smarkm		ring_supply_data(&netoring, temp, len);
75557416Smarkm		printsub('>', temp+2, len - 2);
75657416Smarkm	    }
75757416Smarkm/*@*/	    else printf("lm_will: not enough room in buffer\n");
75857416Smarkm	}
75957416Smarkm	break;
76057416Smarkm    case TELOPT_LFLOW:
76157416Smarkm	if (my_want_state_is_wont(TELOPT_LFLOW))
76257416Smarkm	    return;
76357416Smarkm	if (SB_EOF())
76457416Smarkm	    return;
76557416Smarkm	switch(SB_GET()) {
76657416Smarkm	case LFLOW_RESTART_ANY:
76757416Smarkm	    restartany = 1;
76857416Smarkm	    break;
76957416Smarkm	case LFLOW_RESTART_XON:
77057416Smarkm	    restartany = 0;
77157416Smarkm	    break;
77257416Smarkm	case LFLOW_ON:
77357416Smarkm	    localflow = 1;
77457416Smarkm	    break;
77557416Smarkm	case LFLOW_OFF:
77657416Smarkm	    localflow = 0;
77757416Smarkm	    break;
77857416Smarkm	default:
77957416Smarkm	    return;
78057416Smarkm	}
78157416Smarkm	setcommandmode();
78257416Smarkm	setconnmode(0);
78357416Smarkm	break;
78457416Smarkm
78557416Smarkm    case TELOPT_LINEMODE:
78657416Smarkm	if (my_want_state_is_wont(TELOPT_LINEMODE))
78757416Smarkm	    return;
78857416Smarkm	if (SB_EOF())
78957416Smarkm	    return;
79057416Smarkm	switch (SB_GET()) {
79157416Smarkm	case WILL:
79257416Smarkm	    lm_will(subpointer, SB_LEN());
79357416Smarkm	    break;
79457416Smarkm	case WONT:
79557416Smarkm	    lm_wont(subpointer, SB_LEN());
79657416Smarkm	    break;
79757416Smarkm	case DO:
79857416Smarkm	    lm_do(subpointer, SB_LEN());
79957416Smarkm	    break;
80057416Smarkm	case DONT:
80157416Smarkm	    lm_dont(subpointer, SB_LEN());
80257416Smarkm	    break;
80357416Smarkm	case LM_SLC:
80457416Smarkm	    slc(subpointer, SB_LEN());
80557416Smarkm	    break;
80657416Smarkm	case LM_MODE:
80757416Smarkm	    lm_mode(subpointer, SB_LEN(), 0);
80857416Smarkm	    break;
80957416Smarkm	default:
81057416Smarkm	    break;
81157416Smarkm	}
81257416Smarkm	break;
81357416Smarkm
81457416Smarkm#ifdef	OLD_ENVIRON
81557416Smarkm    case TELOPT_OLD_ENVIRON:
81657416Smarkm#endif
81757416Smarkm    case TELOPT_NEW_ENVIRON:
81857416Smarkm	if (SB_EOF())
81957416Smarkm	    return;
82057416Smarkm	switch(SB_PEEK()) {
82157416Smarkm	case TELQUAL_IS:
82257416Smarkm	case TELQUAL_INFO:
82357416Smarkm	    if (my_want_state_is_dont(subchar))
82457416Smarkm		return;
82557416Smarkm	    break;
82657416Smarkm	case TELQUAL_SEND:
82757416Smarkm	    if (my_want_state_is_wont(subchar)) {
82857416Smarkm		return;
82957416Smarkm	    }
83057416Smarkm	    break;
83157416Smarkm	default:
83257416Smarkm	    return;
83357416Smarkm	}
83457416Smarkm	env_opt(subpointer, SB_LEN());
83557416Smarkm	break;
83657416Smarkm
83757416Smarkm    case TELOPT_XDISPLOC:
83857416Smarkm	if (my_want_state_is_wont(TELOPT_XDISPLOC))
83957416Smarkm	    return;
84057416Smarkm	if (SB_EOF())
84157416Smarkm	    return;
84257416Smarkm	if (SB_GET() == TELQUAL_SEND) {
84357416Smarkm	    unsigned char temp[50], *dp;
84457416Smarkm	    int len;
84557416Smarkm
84657416Smarkm	    if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) {
84757416Smarkm		/*
84857416Smarkm		 * Something happened, we no longer have a DISPLAY
84957416Smarkm		 * variable.  So, turn off the option.
85057416Smarkm		 */
85157416Smarkm		send_wont(TELOPT_XDISPLOC, 1);
85257416Smarkm		break;
85357416Smarkm	    }
85457416Smarkm	    snprintf((char *)temp, sizeof(temp),
85557416Smarkm		     "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
85657416Smarkm		     TELQUAL_IS, dp, IAC, SE);
85757416Smarkm	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
85857416Smarkm
85957416Smarkm	    if (len < NETROOM()) {
86057416Smarkm		ring_supply_data(&netoring, temp, len);
86157416Smarkm		printsub('>', temp+2, len - 2);
86257416Smarkm	    }
86357416Smarkm/*@*/	    else printf("lm_will: not enough room in buffer\n");
86457416Smarkm	}
86557416Smarkm	break;
86657416Smarkm
86757416Smarkm#if	defined(AUTHENTICATION)
86857416Smarkm	case TELOPT_AUTHENTICATION: {
86957416Smarkm		if (!autologin)
87057416Smarkm			break;
87157416Smarkm		if (SB_EOF())
87257416Smarkm			return;
87357416Smarkm		switch(SB_GET()) {
87457416Smarkm		case TELQUAL_IS:
87557416Smarkm			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
87657416Smarkm				return;
87757416Smarkm			auth_is(subpointer, SB_LEN());
87857416Smarkm			break;
87957416Smarkm		case TELQUAL_SEND:
88057416Smarkm			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
88157416Smarkm				return;
88257416Smarkm			auth_send(subpointer, SB_LEN());
88357416Smarkm			break;
88457416Smarkm		case TELQUAL_REPLY:
88557416Smarkm			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
88657416Smarkm				return;
88757416Smarkm			auth_reply(subpointer, SB_LEN());
88857416Smarkm			break;
88957416Smarkm		case TELQUAL_NAME:
89057416Smarkm			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
89157416Smarkm				return;
89257416Smarkm			auth_name(subpointer, SB_LEN());
89357416Smarkm			break;
89457416Smarkm		}
89557416Smarkm	}
89657416Smarkm	break;
89757416Smarkm#endif
89857416Smarkm#if	defined(ENCRYPTION)
89957416Smarkm	case TELOPT_ENCRYPT:
90057416Smarkm		if (SB_EOF())
90157416Smarkm			return;
90257416Smarkm		switch(SB_GET()) {
90357416Smarkm		case ENCRYPT_START:
90457416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
90557416Smarkm				return;
90657416Smarkm			encrypt_start(subpointer, SB_LEN());
90757416Smarkm			break;
90857416Smarkm		case ENCRYPT_END:
90957416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
91057416Smarkm				return;
91157416Smarkm			encrypt_end();
91257416Smarkm			break;
91357416Smarkm		case ENCRYPT_SUPPORT:
91457416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
91557416Smarkm				return;
91657416Smarkm			encrypt_support(subpointer, SB_LEN());
91757416Smarkm			break;
91857416Smarkm		case ENCRYPT_REQSTART:
91957416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
92057416Smarkm				return;
92157416Smarkm			encrypt_request_start(subpointer, SB_LEN());
92257416Smarkm			break;
92357416Smarkm		case ENCRYPT_REQEND:
92457416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
92557416Smarkm				return;
92657416Smarkm			/*
92757416Smarkm			 * We can always send an REQEND so that we cannot
92857416Smarkm			 * get stuck encrypting.  We should only get this
92957416Smarkm			 * if we have been able to get in the correct mode
93057416Smarkm			 * anyhow.
93157416Smarkm			 */
93257416Smarkm			encrypt_request_end();
93357416Smarkm			break;
93457416Smarkm		case ENCRYPT_IS:
93557416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
93657416Smarkm				return;
93757416Smarkm			encrypt_is(subpointer, SB_LEN());
93857416Smarkm			break;
93957416Smarkm		case ENCRYPT_REPLY:
94057416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
94157416Smarkm				return;
94257416Smarkm			encrypt_reply(subpointer, SB_LEN());
94357416Smarkm			break;
94457416Smarkm		case ENCRYPT_ENC_KEYID:
94557416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
94657416Smarkm				return;
94757416Smarkm			encrypt_enc_keyid(subpointer, SB_LEN());
94857416Smarkm			break;
94957416Smarkm		case ENCRYPT_DEC_KEYID:
95057416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
95157416Smarkm				return;
95257416Smarkm			encrypt_dec_keyid(subpointer, SB_LEN());
95357416Smarkm			break;
95457416Smarkm		default:
95557416Smarkm			break;
95657416Smarkm		}
95757416Smarkm		break;
95857416Smarkm#endif
95957416Smarkm    default:
96057416Smarkm	break;
96157416Smarkm    }
96257416Smarkm}
96357416Smarkm
96457416Smarkmstatic unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
96557416Smarkm
96657416Smarkmvoid
96757416Smarkmlm_will(unsigned char *cmd, int len)
96857416Smarkm{
96957416Smarkm    if (len < 1) {
97057416Smarkm/*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
97157416Smarkm	return;
97257416Smarkm    }
97357416Smarkm    switch(cmd[0]) {
97457416Smarkm    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
97557416Smarkm    default:
97657416Smarkm	str_lm[3] = DONT;
97757416Smarkm	str_lm[4] = cmd[0];
97857416Smarkm	if (NETROOM() > sizeof(str_lm)) {
97957416Smarkm	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
98057416Smarkm	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
98157416Smarkm	}
98257416Smarkm/*@*/	else printf("lm_will: not enough room in buffer\n");
98357416Smarkm	break;
98457416Smarkm    }
98557416Smarkm}
98657416Smarkm
98757416Smarkmvoid
98857416Smarkmlm_wont(unsigned char *cmd, int len)
98957416Smarkm{
99057416Smarkm    if (len < 1) {
99157416Smarkm/*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
99257416Smarkm	return;
99357416Smarkm    }
99457416Smarkm    switch(cmd[0]) {
99557416Smarkm    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
99657416Smarkm    default:
99757416Smarkm	/* We are always DONT, so don't respond */
99857416Smarkm	return;
99957416Smarkm    }
100057416Smarkm}
100157416Smarkm
100257416Smarkmvoid
100357416Smarkmlm_do(unsigned char *cmd, int len)
100457416Smarkm{
100557416Smarkm    if (len < 1) {
100657416Smarkm/*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
100757416Smarkm	return;
100857416Smarkm    }
100957416Smarkm    switch(cmd[0]) {
101057416Smarkm    case LM_FORWARDMASK:
101157416Smarkm    default:
101257416Smarkm	str_lm[3] = WONT;
101357416Smarkm	str_lm[4] = cmd[0];
101457416Smarkm	if (NETROOM() > sizeof(str_lm)) {
101557416Smarkm	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
101657416Smarkm	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
101757416Smarkm	}
101857416Smarkm/*@*/	else printf("lm_do: not enough room in buffer\n");
101957416Smarkm	break;
102057416Smarkm    }
102157416Smarkm}
102257416Smarkm
102357416Smarkmvoid
102457416Smarkmlm_dont(unsigned char *cmd, int len)
102557416Smarkm{
102657416Smarkm    if (len < 1) {
102757416Smarkm/*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
102857416Smarkm	return;
102957416Smarkm    }
103057416Smarkm    switch(cmd[0]) {
103157416Smarkm    case LM_FORWARDMASK:
103257416Smarkm    default:
103357416Smarkm	/* we are always WONT, so don't respond */
103457416Smarkm	break;
103557416Smarkm    }
103657416Smarkm}
103757416Smarkm
103857416Smarkmstatic unsigned char str_lm_mode[] = {
103957416Smarkm	IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
104057416Smarkm};
104157416Smarkm
104257416Smarkmvoid
104357416Smarkmlm_mode(unsigned char *cmd, int len, int init)
104457416Smarkm{
104557416Smarkm	if (len != 1)
104657416Smarkm		return;
104757416Smarkm	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
104857416Smarkm		return;
104957416Smarkm	if (*cmd&MODE_ACK)
105057416Smarkm		return;
105157416Smarkm	linemode = *cmd&(MODE_MASK&~MODE_ACK);
105257416Smarkm	str_lm_mode[4] = linemode;
105357416Smarkm	if (!init)
105457416Smarkm	    str_lm_mode[4] |= MODE_ACK;
105557416Smarkm	if (NETROOM() > sizeof(str_lm_mode)) {
105657416Smarkm	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
105757416Smarkm	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
105857416Smarkm	}
105957416Smarkm/*@*/	else printf("lm_mode: not enough room in buffer\n");
106057416Smarkm	setconnmode(0);	/* set changed mode */
106157416Smarkm}
106257416Smarkm
106357416Smarkm
106457416Smarkm
106557416Smarkm/*
106657416Smarkm * slc()
106757416Smarkm * Handle special character suboption of LINEMODE.
106857416Smarkm */
106957416Smarkm
107057416Smarkmstruct spc {
107157416Smarkm	cc_t val;
107257416Smarkm	cc_t *valp;
107357416Smarkm	char flags;	/* Current flags & level */
107457416Smarkm	char mylevel;	/* Maximum level & flags */
107557416Smarkm} spc_data[NSLC+1];
107657416Smarkm
107757416Smarkm#define SLC_IMPORT	0
107857416Smarkm#define	SLC_EXPORT	1
107957416Smarkm#define SLC_RVALUE	2
108057416Smarkmstatic int slc_mode = SLC_EXPORT;
108157416Smarkm
108257416Smarkmvoid
108357416Smarkmslc_init()
108457416Smarkm{
108557416Smarkm	struct spc *spcp;
108657416Smarkm
108757416Smarkm	localchars = 1;
108857416Smarkm	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
108957416Smarkm		spcp->val = 0;
109057416Smarkm		spcp->valp = 0;
109157416Smarkm		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
109257416Smarkm	}
109357416Smarkm
109457416Smarkm#define	initfunc(func, flags) { \
109557416Smarkm					spcp = &spc_data[func]; \
109657416Smarkm					if ((spcp->valp = tcval(func))) { \
109757416Smarkm					    spcp->val = *spcp->valp; \
109857416Smarkm					    spcp->mylevel = SLC_VARIABLE|flags; \
109957416Smarkm					} else { \
110057416Smarkm					    spcp->val = 0; \
110157416Smarkm					    spcp->mylevel = SLC_DEFAULT; \
110257416Smarkm					} \
110357416Smarkm				    }
110457416Smarkm
110557416Smarkm	initfunc(SLC_SYNCH, 0);
110657416Smarkm	/* No BRK */
110757416Smarkm	initfunc(SLC_AO, 0);
110857416Smarkm	initfunc(SLC_AYT, 0);
110957416Smarkm	/* No EOR */
111057416Smarkm	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
111157416Smarkm	initfunc(SLC_EOF, 0);
111257416Smarkm	initfunc(SLC_SUSP, SLC_FLUSHIN);
111357416Smarkm	initfunc(SLC_EC, 0);
111457416Smarkm	initfunc(SLC_EL, 0);
111557416Smarkm	initfunc(SLC_EW, 0);
111657416Smarkm	initfunc(SLC_RP, 0);
111757416Smarkm	initfunc(SLC_LNEXT, 0);
111857416Smarkm	initfunc(SLC_XON, 0);
111957416Smarkm	initfunc(SLC_XOFF, 0);
112057416Smarkm	initfunc(SLC_FORW1, 0);
112157416Smarkm	initfunc(SLC_FORW2, 0);
112257416Smarkm	/* No FORW2 */
112357416Smarkm
112457416Smarkm	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
112557416Smarkm#undef	initfunc
112657416Smarkm
112757416Smarkm	if (slc_mode == SLC_EXPORT)
112857416Smarkm		slc_export();
112957416Smarkm	else
113057416Smarkm		slc_import(1);
113157416Smarkm
113257416Smarkm}
113357416Smarkm
113457416Smarkmvoid
113557416Smarkmslcstate()
113657416Smarkm{
113757416Smarkm    printf("Special characters are %s values\n",
113857416Smarkm		slc_mode == SLC_IMPORT ? "remote default" :
113957416Smarkm		slc_mode == SLC_EXPORT ? "local" :
114057416Smarkm					 "remote");
114157416Smarkm}
114257416Smarkm
114357416Smarkmvoid
114457416Smarkmslc_mode_export()
114557416Smarkm{
114657416Smarkm    slc_mode = SLC_EXPORT;
114757416Smarkm    if (my_state_is_will(TELOPT_LINEMODE))
114857416Smarkm	slc_export();
114957416Smarkm}
115057416Smarkm
115157416Smarkmvoid
115257416Smarkmslc_mode_import(int def)
115357416Smarkm{
115457416Smarkm    slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
115557416Smarkm    if (my_state_is_will(TELOPT_LINEMODE))
115657416Smarkm	slc_import(def);
115757416Smarkm}
115857416Smarkm
115957416Smarkmunsigned char slc_import_val[] = {
116057416Smarkm	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
116157416Smarkm};
116257416Smarkmunsigned char slc_import_def[] = {
116357416Smarkm	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
116457416Smarkm};
116557416Smarkm
116657416Smarkmvoid
116757416Smarkmslc_import(int def)
116857416Smarkm{
116957416Smarkm    if (NETROOM() > sizeof(slc_import_val)) {
117057416Smarkm	if (def) {
117157416Smarkm	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
117257416Smarkm	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
117357416Smarkm	} else {
117457416Smarkm	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
117557416Smarkm	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
117657416Smarkm	}
117757416Smarkm    }
117857416Smarkm/*@*/ else printf("slc_import: not enough room\n");
117957416Smarkm}
118057416Smarkm
118157416Smarkmvoid
118257416Smarkmslc_export()
118357416Smarkm{
118457416Smarkm    struct spc *spcp;
118557416Smarkm
118657416Smarkm    TerminalDefaultChars();
118757416Smarkm
118857416Smarkm    slc_start_reply();
118957416Smarkm    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
119057416Smarkm	if (spcp->mylevel != SLC_NOSUPPORT) {
119157416Smarkm	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
119257416Smarkm		spcp->flags = SLC_NOSUPPORT;
119357416Smarkm	    else
119457416Smarkm		spcp->flags = spcp->mylevel;
119557416Smarkm	    if (spcp->valp)
119657416Smarkm		spcp->val = *spcp->valp;
119757416Smarkm	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
119857416Smarkm	}
119957416Smarkm    }
120057416Smarkm    slc_end_reply();
120157416Smarkm    slc_update();
120257416Smarkm    setconnmode(1);	/* Make sure the character values are set */
120357416Smarkm}
120457416Smarkm
120557416Smarkmvoid
120657416Smarkmslc(unsigned char *cp, int len)
120757416Smarkm{
120857416Smarkm	struct spc *spcp;
120957416Smarkm	int func,level;
121057416Smarkm
121157416Smarkm	slc_start_reply();
121257416Smarkm
121357416Smarkm	for (; len >= 3; len -=3, cp +=3) {
121457416Smarkm
121557416Smarkm		func = cp[SLC_FUNC];
121657416Smarkm
121757416Smarkm		if (func == 0) {
121857416Smarkm			/*
121957416Smarkm			 * Client side: always ignore 0 function.
122057416Smarkm			 */
122157416Smarkm			continue;
122257416Smarkm		}
122357416Smarkm		if (func > NSLC) {
122457416Smarkm			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
122557416Smarkm				slc_add_reply(func, SLC_NOSUPPORT, 0);
122657416Smarkm			continue;
122757416Smarkm		}
122857416Smarkm
122957416Smarkm		spcp = &spc_data[func];
123057416Smarkm
123157416Smarkm		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
123257416Smarkm
123357416Smarkm		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
123457416Smarkm		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
123557416Smarkm			continue;
123657416Smarkm		}
123757416Smarkm
123857416Smarkm		if (level == (SLC_DEFAULT|SLC_ACK)) {
123957416Smarkm			/*
124057416Smarkm			 * This is an error condition, the SLC_ACK
124157416Smarkm			 * bit should never be set for the SLC_DEFAULT
124257416Smarkm			 * level.  Our best guess to recover is to
124357416Smarkm			 * ignore the SLC_ACK bit.
124457416Smarkm			 */
124557416Smarkm			cp[SLC_FLAGS] &= ~SLC_ACK;
124657416Smarkm		}
124757416Smarkm
124857416Smarkm		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
124957416Smarkm			spcp->val = (cc_t)cp[SLC_VALUE];
125057416Smarkm			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
125157416Smarkm			continue;
125257416Smarkm		}
125357416Smarkm
125457416Smarkm		level &= ~SLC_ACK;
125557416Smarkm
125657416Smarkm		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
125757416Smarkm			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
125857416Smarkm			spcp->val = (cc_t)cp[SLC_VALUE];
125957416Smarkm		}
126057416Smarkm		if (level == SLC_DEFAULT) {
126157416Smarkm			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
126257416Smarkm				spcp->flags = spcp->mylevel;
126357416Smarkm			else
126457416Smarkm				spcp->flags = SLC_NOSUPPORT;
126557416Smarkm		}
126657416Smarkm		slc_add_reply(func, spcp->flags, spcp->val);
126757416Smarkm	}
126857416Smarkm	slc_end_reply();
126957416Smarkm	if (slc_update())
127057416Smarkm		setconnmode(1);	/* set the  new character values */
127157416Smarkm}
127257416Smarkm
127357416Smarkmvoid
127457416Smarkmslc_check()
127557416Smarkm{
127657416Smarkm    struct spc *spcp;
127757416Smarkm
127857416Smarkm    slc_start_reply();
127957416Smarkm    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
128057416Smarkm	if (spcp->valp && spcp->val != *spcp->valp) {
128157416Smarkm	    spcp->val = *spcp->valp;
128257416Smarkm	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
128357416Smarkm		spcp->flags = SLC_NOSUPPORT;
128457416Smarkm	    else
128557416Smarkm		spcp->flags = spcp->mylevel;
128657416Smarkm	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
128757416Smarkm	}
128857416Smarkm    }
128957416Smarkm    slc_end_reply();
129057416Smarkm    setconnmode(1);
129157416Smarkm}
129257416Smarkm
129357416Smarkm
129457416Smarkmunsigned char slc_reply[128];
1295178825Sdfrunsigned char const * const slc_reply_eom = &slc_reply[sizeof(slc_reply)];
129657416Smarkmunsigned char *slc_replyp;
129757416Smarkm
129857416Smarkmvoid
129957416Smarkmslc_start_reply()
130057416Smarkm{
130157416Smarkm	slc_replyp = slc_reply;
130257416Smarkm	*slc_replyp++ = IAC;
130357416Smarkm	*slc_replyp++ = SB;
130457416Smarkm	*slc_replyp++ = TELOPT_LINEMODE;
130557416Smarkm	*slc_replyp++ = LM_SLC;
130657416Smarkm}
130757416Smarkm
130857416Smarkmvoid
130957416Smarkmslc_add_reply(unsigned char func, unsigned char flags, cc_t value)
131057416Smarkm{
1311178825Sdfr	/* A sequence of up to 6 bytes my be written for this member of the SLC
1312178825Sdfr	 * suboption list by this function.  The end of negotiation command,
1313178825Sdfr	 * which is written by slc_end_reply(), will require 2 additional
1314178825Sdfr	 * bytes.  Do not proceed unless there is sufficient space for these
1315178825Sdfr	 * items.
1316178825Sdfr	 */
1317178825Sdfr	if (&slc_replyp[6+2] > slc_reply_eom)
1318178825Sdfr		return;
131957416Smarkm	if ((*slc_replyp++ = func) == IAC)
132057416Smarkm		*slc_replyp++ = IAC;
132157416Smarkm	if ((*slc_replyp++ = flags) == IAC)
132257416Smarkm		*slc_replyp++ = IAC;
132357416Smarkm	if ((*slc_replyp++ = (unsigned char)value) == IAC)
132457416Smarkm		*slc_replyp++ = IAC;
132557416Smarkm}
132657416Smarkm
132757416Smarkmvoid
132857416Smarkmslc_end_reply()
132957416Smarkm{
133057416Smarkm    int len;
133157416Smarkm
1332178825Sdfr    /* The end of negotiation command requires 2 bytes. */
1333178825Sdfr    if (&slc_replyp[2] > slc_reply_eom)
1334178825Sdfr            return;
133557416Smarkm    *slc_replyp++ = IAC;
133657416Smarkm    *slc_replyp++ = SE;
133757416Smarkm    len = slc_replyp - slc_reply;
133857416Smarkm    if (len <= 6)
133957416Smarkm	return;
134057416Smarkm    if (NETROOM() > len) {
134157416Smarkm	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
134257416Smarkm	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
134357416Smarkm    }
134457416Smarkm/*@*/else printf("slc_end_reply: not enough room\n");
134557416Smarkm}
134657416Smarkm
134757416Smarkmint
134857416Smarkmslc_update()
134957416Smarkm{
135057416Smarkm	struct spc *spcp;
135157416Smarkm	int need_update = 0;
135257416Smarkm
135357416Smarkm	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
135457416Smarkm		if (!(spcp->flags&SLC_ACK))
135557416Smarkm			continue;
135657416Smarkm		spcp->flags &= ~SLC_ACK;
135757416Smarkm		if (spcp->valp && (*spcp->valp != spcp->val)) {
135857416Smarkm			*spcp->valp = spcp->val;
135957416Smarkm			need_update = 1;
136057416Smarkm		}
136157416Smarkm	}
136257416Smarkm	return(need_update);
136357416Smarkm}
136457416Smarkm
136557416Smarkm#ifdef	OLD_ENVIRON
136657416Smarkm#  define old_env_var OLD_ENV_VAR
136757416Smarkm#  define old_env_value OLD_ENV_VALUE
136857416Smarkm#endif
136957416Smarkm
137057416Smarkmvoid
137157416Smarkmenv_opt(unsigned char *buf, int len)
137257416Smarkm{
137357416Smarkm	unsigned char *ep = 0, *epc = 0;
137457416Smarkm	int i;
137557416Smarkm
137657416Smarkm	switch(buf[0]&0xff) {
137757416Smarkm	case TELQUAL_SEND:
137857416Smarkm		env_opt_start();
137957416Smarkm		if (len == 1) {
138057416Smarkm			env_opt_add(NULL);
138157416Smarkm		} else for (i = 1; i < len; i++) {
138257416Smarkm			switch (buf[i]&0xff) {
138357416Smarkm#ifdef	OLD_ENVIRON
138457416Smarkm			case OLD_ENV_VAR:
138557416Smarkm			case OLD_ENV_VALUE:
138657416Smarkm				/*
138757416Smarkm				 * Although OLD_ENV_VALUE is not legal, we will
138857416Smarkm				 * still recognize it, just in case it is an
138957416Smarkm				 * old server that has VAR & VALUE mixed up...
139057416Smarkm				 */
139157416Smarkm				/* FALL THROUGH */
139257416Smarkm#else
139357416Smarkm			case NEW_ENV_VAR:
139457416Smarkm#endif
139557416Smarkm			case ENV_USERVAR:
139657416Smarkm				if (ep) {
139757416Smarkm					*epc = 0;
139857416Smarkm					env_opt_add(ep);
139957416Smarkm				}
140057416Smarkm				ep = epc = &buf[i+1];
140157416Smarkm				break;
140257416Smarkm			case ENV_ESC:
140357416Smarkm				i++;
140457416Smarkm				/*FALL THROUGH*/
140557416Smarkm			default:
140657416Smarkm				if (epc)
140757416Smarkm					*epc++ = buf[i];
140857416Smarkm				break;
140957416Smarkm			}
141057416Smarkm		}
141157416Smarkm		if (ep) {
141257416Smarkm			*epc = 0;
141357416Smarkm			env_opt_add(ep);
141457416Smarkm		}
141557416Smarkm		env_opt_end(1);
141657416Smarkm		break;
141757416Smarkm
141857416Smarkm	case TELQUAL_IS:
141957416Smarkm	case TELQUAL_INFO:
142057416Smarkm		/* Ignore for now.  We shouldn't get it anyway. */
142157416Smarkm		break;
142257416Smarkm
142357416Smarkm	default:
142457416Smarkm		break;
142557416Smarkm	}
142657416Smarkm}
142757416Smarkm
1428178825Sdfr#define	OPT_REPLY_SIZE	(2 * SUBBUFSIZE)
142957416Smarkmunsigned char *opt_reply;
143057416Smarkmunsigned char *opt_replyp;
143157416Smarkmunsigned char *opt_replyend;
143257416Smarkm
143357416Smarkmvoid
143457416Smarkmenv_opt_start()
143557416Smarkm{
143657416Smarkm	if (opt_reply) {
143757416Smarkm		void *tmp = realloc (opt_reply, OPT_REPLY_SIZE);
143857416Smarkm		if (tmp != NULL) {
143957416Smarkm			opt_reply = tmp;
144057416Smarkm		} else {
144157416Smarkm			free (opt_reply);
144257416Smarkm			opt_reply = NULL;
144357416Smarkm		}
144457416Smarkm	} else
144557416Smarkm		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
144657416Smarkm	if (opt_reply == NULL) {
144757416Smarkm/*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
144857416Smarkm		opt_reply = opt_replyp = opt_replyend = NULL;
144957416Smarkm		return;
145057416Smarkm	}
145157416Smarkm	opt_replyp = opt_reply;
145257416Smarkm	opt_replyend = opt_reply + OPT_REPLY_SIZE;
145357416Smarkm	*opt_replyp++ = IAC;
145457416Smarkm	*opt_replyp++ = SB;
145557416Smarkm	*opt_replyp++ = telopt_environ;
145657416Smarkm	*opt_replyp++ = TELQUAL_IS;
145757416Smarkm}
145857416Smarkm
145957416Smarkmvoid
146057416Smarkmenv_opt_start_info()
146157416Smarkm{
146257416Smarkm	env_opt_start();
146357416Smarkm	if (opt_replyp)
146457416Smarkm	    opt_replyp[-1] = TELQUAL_INFO;
146557416Smarkm}
146657416Smarkm
146757416Smarkmvoid
146857416Smarkmenv_opt_add(unsigned char *ep)
146957416Smarkm{
147057416Smarkm	unsigned char *vp, c;
147157416Smarkm
147257416Smarkm	if (opt_reply == NULL)		/*XXX*/
147357416Smarkm		return;			/*XXX*/
147457416Smarkm
147557416Smarkm	if (ep == NULL || *ep == '\0') {
147657416Smarkm		/* Send user defined variables first. */
147757416Smarkm		env_default(1, 0);
147857416Smarkm		while ((ep = env_default(0, 0)))
147957416Smarkm			env_opt_add(ep);
148057416Smarkm
148157416Smarkm		/* Now add the list of well know variables.  */
148257416Smarkm		env_default(1, 1);
148357416Smarkm		while ((ep = env_default(0, 1)))
148457416Smarkm			env_opt_add(ep);
148557416Smarkm		return;
148657416Smarkm	}
148757416Smarkm	vp = env_getvalue(ep);
1488178825Sdfr        if (opt_replyp + (vp ? 2 * strlen((char *)vp) : 0) +
1489178825Sdfr                                2 * strlen((char *)ep) + 6 > opt_replyend)
1490178825Sdfr        {
149157416Smarkm		int len;
149257416Smarkm		void *tmp;
149357416Smarkm		opt_replyend += OPT_REPLY_SIZE;
149457416Smarkm		len = opt_replyend - opt_reply;
149557416Smarkm		tmp = realloc(opt_reply, len);
149657416Smarkm		if (tmp == NULL) {
149757416Smarkm/*@*/			printf("env_opt_add: realloc() failed!!!\n");
149857416Smarkm			opt_reply = opt_replyp = opt_replyend = NULL;
149957416Smarkm			return;
150057416Smarkm		}
150157416Smarkm		opt_reply = tmp;
150257416Smarkm		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
150357416Smarkm		opt_replyend = opt_reply + len;
150457416Smarkm	}
150557416Smarkm	if (opt_welldefined((char *)ep)) {
150657416Smarkm#ifdef	OLD_ENVIRON
150757416Smarkm		if (telopt_environ == TELOPT_OLD_ENVIRON)
150857416Smarkm			*opt_replyp++ = old_env_var;
150957416Smarkm		else
151057416Smarkm#endif
151157416Smarkm			*opt_replyp++ = NEW_ENV_VAR;
151257416Smarkm	} else
151357416Smarkm		*opt_replyp++ = ENV_USERVAR;
151457416Smarkm	for (;;) {
151557416Smarkm		while ((c = *ep++)) {
1516178825Sdfr			if (opt_replyp + (2 + 2) > opt_replyend)
1517178825Sdfr				return;
151857416Smarkm			switch(c&0xff) {
151957416Smarkm			case IAC:
152057416Smarkm				*opt_replyp++ = IAC;
152157416Smarkm				break;
152257416Smarkm			case NEW_ENV_VAR:
152357416Smarkm			case NEW_ENV_VALUE:
152457416Smarkm			case ENV_ESC:
152557416Smarkm			case ENV_USERVAR:
152657416Smarkm				*opt_replyp++ = ENV_ESC;
152757416Smarkm				break;
152857416Smarkm			}
152957416Smarkm			*opt_replyp++ = c;
153057416Smarkm		}
153157416Smarkm		if ((ep = vp)) {
1532178825Sdfr			if (opt_replyp + (1 + 2 + 2) > opt_replyend)
1533178825Sdfr				return;
153457416Smarkm#ifdef	OLD_ENVIRON
153557416Smarkm			if (telopt_environ == TELOPT_OLD_ENVIRON)
153657416Smarkm				*opt_replyp++ = old_env_value;
153757416Smarkm			else
153857416Smarkm#endif
153957416Smarkm				*opt_replyp++ = NEW_ENV_VALUE;
154057416Smarkm			vp = NULL;
154157416Smarkm		} else
154257416Smarkm			break;
154357416Smarkm	}
154457416Smarkm}
154557416Smarkm
154657416Smarkmint
154757416Smarkmopt_welldefined(char *ep)
154857416Smarkm{
154957416Smarkm	if ((strcmp(ep, "USER") == 0) ||
155057416Smarkm	    (strcmp(ep, "DISPLAY") == 0) ||
155157416Smarkm	    (strcmp(ep, "PRINTER") == 0) ||
155257416Smarkm	    (strcmp(ep, "SYSTEMTYPE") == 0) ||
155357416Smarkm	    (strcmp(ep, "JOB") == 0) ||
155457416Smarkm	    (strcmp(ep, "ACCT") == 0))
155557416Smarkm		return(1);
155657416Smarkm	return(0);
155757416Smarkm}
155857416Smarkm
155957416Smarkmvoid
156057416Smarkmenv_opt_end(int emptyok)
156157416Smarkm{
156257416Smarkm	int len;
156357416Smarkm
1564178825Sdfr	if (opt_replyp + 2 > opt_replyend)
1565178825Sdfr		return;
1566178825Sdfr	len = opt_replyp + 2 - opt_reply;
156757416Smarkm	if (emptyok || len > 6) {
156857416Smarkm		*opt_replyp++ = IAC;
156957416Smarkm		*opt_replyp++ = SE;
157057416Smarkm		if (NETROOM() > len) {
157157416Smarkm			ring_supply_data(&netoring, opt_reply, len);
157257416Smarkm			printsub('>', &opt_reply[2], len - 2);
157357416Smarkm		}
157457416Smarkm/*@*/		else printf("slc_end_reply: not enough room\n");
157557416Smarkm	}
157657416Smarkm	if (opt_reply) {
157757416Smarkm		free(opt_reply);
157857416Smarkm		opt_reply = opt_replyp = opt_replyend = NULL;
157957416Smarkm	}
158057416Smarkm}
158157416Smarkm
158257416Smarkm
158357416Smarkm
158457416Smarkmint
158557416Smarkmtelrcv(void)
158657416Smarkm{
158757416Smarkm    int c;
158857416Smarkm    int scc;
158957416Smarkm    unsigned char *sbp = NULL;
159057416Smarkm    int count;
159157416Smarkm    int returnValue = 0;
159257416Smarkm
159357416Smarkm    scc = 0;
159457416Smarkm    count = 0;
159557416Smarkm    while (TTYROOM() > 2) {
159657416Smarkm	if (scc == 0) {
159757416Smarkm	    if (count) {
159857416Smarkm		ring_consumed(&netiring, count);
159957416Smarkm		returnValue = 1;
160057416Smarkm		count = 0;
160157416Smarkm	    }
160257416Smarkm	    sbp = netiring.consume;
160357416Smarkm	    scc = ring_full_consecutive(&netiring);
160457416Smarkm	    if (scc == 0) {
160557416Smarkm		/* No more data coming in */
160657416Smarkm		break;
160757416Smarkm	    }
160857416Smarkm	}
160957416Smarkm
161057416Smarkm	c = *sbp++ & 0xff, scc--; count++;
161157416Smarkm#if	defined(ENCRYPTION)
161257416Smarkm	if (decrypt_input)
161357416Smarkm		c = (*decrypt_input)(c);
161457416Smarkm#endif
161557416Smarkm
161657416Smarkm	switch (telrcv_state) {
161757416Smarkm
161857416Smarkm	case TS_CR:
161957416Smarkm	    telrcv_state = TS_DATA;
162057416Smarkm	    if (c == '\0') {
162157416Smarkm		break;	/* Ignore \0 after CR */
162257416Smarkm	    }
162357416Smarkm	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
162457416Smarkm		TTYADD(c);
162557416Smarkm		break;
162657416Smarkm	    }
162757416Smarkm	    /* Else, fall through */
162857416Smarkm
162957416Smarkm	case TS_DATA:
163057416Smarkm	    if (c == IAC) {
163157416Smarkm		telrcv_state = TS_IAC;
163257416Smarkm		break;
163357416Smarkm	    }
1634233294Sstas		    /*
163557416Smarkm		     * The 'crmod' hack (see following) is needed
163657416Smarkm		     * since we can't set CRMOD on output only.
163757416Smarkm		     * Machines like MULTICS like to send \r without
163857416Smarkm		     * \n; since we must turn off CRMOD to get proper
163957416Smarkm		     * input, the mapping is done here (sigh).
164057416Smarkm		     */
164157416Smarkm	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
164257416Smarkm		if (scc > 0) {
164357416Smarkm		    c = *sbp&0xff;
164457416Smarkm#if	defined(ENCRYPTION)
164557416Smarkm		    if (decrypt_input)
164657416Smarkm			c = (*decrypt_input)(c);
164757416Smarkm#endif
164857416Smarkm		    if (c == 0) {
164957416Smarkm			sbp++, scc--; count++;
165057416Smarkm			/* a "true" CR */
165157416Smarkm			TTYADD('\r');
165257416Smarkm		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
165357416Smarkm					(c == '\n')) {
165457416Smarkm			sbp++, scc--; count++;
165557416Smarkm			TTYADD('\n');
165657416Smarkm		    } else {
165757416Smarkm#if	defined(ENCRYPTION)
165857416Smarkm		        if (decrypt_input)
165957416Smarkm			    (*decrypt_input)(-1);
166057416Smarkm#endif
166157416Smarkm
166257416Smarkm			TTYADD('\r');
166357416Smarkm			if (crmod) {
166457416Smarkm				TTYADD('\n');
166557416Smarkm			}
166657416Smarkm		    }
166757416Smarkm		} else {
166857416Smarkm		    telrcv_state = TS_CR;
166957416Smarkm		    TTYADD('\r');
167057416Smarkm		    if (crmod) {
167157416Smarkm			    TTYADD('\n');
167257416Smarkm		    }
167357416Smarkm		}
167457416Smarkm	    } else {
167557416Smarkm		TTYADD(c);
167657416Smarkm	    }
167757416Smarkm	    continue;
167857416Smarkm
167957416Smarkm	case TS_IAC:
168057416Smarkmprocess_iac:
168157416Smarkm	    switch (c) {
168257416Smarkm
168357416Smarkm	    case WILL:
168457416Smarkm		telrcv_state = TS_WILL;
168557416Smarkm		continue;
168657416Smarkm
168757416Smarkm	    case WONT:
168857416Smarkm		telrcv_state = TS_WONT;
168957416Smarkm		continue;
169057416Smarkm
169157416Smarkm	    case DO:
169257416Smarkm		telrcv_state = TS_DO;
169357416Smarkm		continue;
169457416Smarkm
169557416Smarkm	    case DONT:
169657416Smarkm		telrcv_state = TS_DONT;
169757416Smarkm		continue;
169857416Smarkm
169957416Smarkm	    case DM:
170057416Smarkm		    /*
170157416Smarkm		     * We may have missed an urgent notification,
170257416Smarkm		     * so make sure we flush whatever is in the
170357416Smarkm		     * buffer currently.
170457416Smarkm		     */
170557416Smarkm		printoption("RCVD", IAC, DM);
170657416Smarkm		SYNCHing = 1;
170757416Smarkm		ttyflush(1);
170857416Smarkm		SYNCHing = stilloob();
170957416Smarkm		settimer(gotDM);
171057416Smarkm		break;
171157416Smarkm
171257416Smarkm	    case SB:
171357416Smarkm		SB_CLEAR();
171457416Smarkm		telrcv_state = TS_SB;
171557416Smarkm		continue;
171657416Smarkm
171757416Smarkm
171857416Smarkm	    case IAC:
171957416Smarkm		TTYADD(IAC);
172057416Smarkm		break;
172157416Smarkm
172257416Smarkm	    case NOP:
172357416Smarkm	    case GA:
172457416Smarkm	    default:
172557416Smarkm		printoption("RCVD", IAC, c);
172657416Smarkm		break;
172757416Smarkm	    }
172857416Smarkm	    telrcv_state = TS_DATA;
172957416Smarkm	    continue;
173057416Smarkm
173157416Smarkm	case TS_WILL:
173257416Smarkm	    printoption("RCVD", WILL, c);
173357416Smarkm	    willoption(c);
173457416Smarkm	    telrcv_state = TS_DATA;
173557416Smarkm	    continue;
173657416Smarkm
173757416Smarkm	case TS_WONT:
173857416Smarkm	    printoption("RCVD", WONT, c);
173957416Smarkm	    wontoption(c);
174057416Smarkm	    telrcv_state = TS_DATA;
174157416Smarkm	    continue;
174257416Smarkm
174357416Smarkm	case TS_DO:
174457416Smarkm	    printoption("RCVD", DO, c);
174557416Smarkm	    dooption(c);
174657416Smarkm	    if (c == TELOPT_NAWS) {
174757416Smarkm		sendnaws();
174857416Smarkm	    } else if (c == TELOPT_LFLOW) {
174957416Smarkm		localflow = 1;
175057416Smarkm		setcommandmode();
175157416Smarkm		setconnmode(0);
175257416Smarkm	    }
175357416Smarkm	    telrcv_state = TS_DATA;
175457416Smarkm	    continue;
175557416Smarkm
175657416Smarkm	case TS_DONT:
175757416Smarkm	    printoption("RCVD", DONT, c);
175857416Smarkm	    dontoption(c);
175957416Smarkm	    flushline = 1;
176057416Smarkm	    setconnmode(0);	/* set new tty mode (maybe) */
176157416Smarkm	    telrcv_state = TS_DATA;
176257416Smarkm	    continue;
176357416Smarkm
176457416Smarkm	case TS_SB:
176557416Smarkm	    if (c == IAC) {
176657416Smarkm		telrcv_state = TS_SE;
176757416Smarkm	    } else {
176857416Smarkm		SB_ACCUM(c);
176957416Smarkm	    }
177057416Smarkm	    continue;
177157416Smarkm
177257416Smarkm	case TS_SE:
177357416Smarkm	    if (c != SE) {
177457416Smarkm		if (c != IAC) {
177557416Smarkm		    /*
177657416Smarkm		     * This is an error.  We only expect to get
177757416Smarkm		     * "IAC IAC" or "IAC SE".  Several things may
1778178825Sdfr		     * have happened.  An IAC was not doubled, the
177957416Smarkm		     * IAC SE was left off, or another option got
178057416Smarkm		     * inserted into the suboption are all possibilities.
178157416Smarkm		     * If we assume that the IAC was not doubled,
178257416Smarkm		     * and really the IAC SE was left off, we could
1783178825Sdfr		     * get into an infinite loop here.  So, instead,
178457416Smarkm		     * we terminate the suboption, and process the
178557416Smarkm		     * partial suboption if we can.
178657416Smarkm		     */
178757416Smarkm		    SB_ACCUM(IAC);
178857416Smarkm		    SB_ACCUM(c);
178957416Smarkm		    subpointer -= 2;
179057416Smarkm		    SB_TERM();
179157416Smarkm
179257416Smarkm		    printoption("In SUBOPTION processing, RCVD", IAC, c);
179357416Smarkm		    suboption();	/* handle sub-option */
179457416Smarkm		    telrcv_state = TS_IAC;
179557416Smarkm		    goto process_iac;
179657416Smarkm		}
179757416Smarkm		SB_ACCUM(c);
179857416Smarkm		telrcv_state = TS_SB;
179957416Smarkm	    } else {
180057416Smarkm		SB_ACCUM(IAC);
180157416Smarkm		SB_ACCUM(SE);
180257416Smarkm		subpointer -= 2;
180357416Smarkm		SB_TERM();
180457416Smarkm		suboption();	/* handle sub-option */
180557416Smarkm		telrcv_state = TS_DATA;
180657416Smarkm	    }
180757416Smarkm	}
180857416Smarkm    }
180957416Smarkm    if (count)
181057416Smarkm	ring_consumed(&netiring, count);
181157416Smarkm    return returnValue||count;
181257416Smarkm}
181357416Smarkm
181457416Smarkmstatic int bol = 1, local = 0;
181557416Smarkm
181657416Smarkmint
181757416Smarkmrlogin_susp(void)
181857416Smarkm{
181957416Smarkm    if (local) {
182057416Smarkm	local = 0;
182157416Smarkm	bol = 1;
182257416Smarkm	command(0, "z\n", 2);
182357416Smarkm	return(1);
182457416Smarkm    }
182557416Smarkm    return(0);
182657416Smarkm}
182757416Smarkm
182857416Smarkmstatic int
182957416Smarkmtelsnd()
183057416Smarkm{
183157416Smarkm    int tcc;
183257416Smarkm    int count;
183357416Smarkm    int returnValue = 0;
183457416Smarkm    unsigned char *tbp = NULL;
183557416Smarkm
183657416Smarkm    tcc = 0;
183757416Smarkm    count = 0;
183857416Smarkm    while (NETROOM() > 2) {
183957416Smarkm	int sc;
184057416Smarkm	int c;
184157416Smarkm
184257416Smarkm	if (tcc == 0) {
184357416Smarkm	    if (count) {
184457416Smarkm		ring_consumed(&ttyiring, count);
184557416Smarkm		returnValue = 1;
184657416Smarkm		count = 0;
184757416Smarkm	    }
184857416Smarkm	    tbp = ttyiring.consume;
184957416Smarkm	    tcc = ring_full_consecutive(&ttyiring);
185057416Smarkm	    if (tcc == 0) {
185157416Smarkm		break;
185257416Smarkm	    }
185357416Smarkm	}
185457416Smarkm	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
185557416Smarkm	if (rlogin != _POSIX_VDISABLE) {
185657416Smarkm		if (bol) {
185757416Smarkm			bol = 0;
185857416Smarkm			if (sc == rlogin) {
185957416Smarkm				local = 1;
186057416Smarkm				continue;
186157416Smarkm			}
186257416Smarkm		} else if (local) {
186357416Smarkm			local = 0;
186457416Smarkm			if (sc == '.' || c == termEofChar) {
186557416Smarkm				bol = 1;
186657416Smarkm				command(0, "close\n", 6);
186757416Smarkm				continue;
186857416Smarkm			}
186957416Smarkm			if (sc == termSuspChar) {
187057416Smarkm				bol = 1;
187157416Smarkm				command(0, "z\n", 2);
187257416Smarkm				continue;
187357416Smarkm			}
187457416Smarkm			if (sc == escape) {
187557416Smarkm				command(0, (char *)tbp, tcc);
187657416Smarkm				bol = 1;
187757416Smarkm				count += tcc;
187857416Smarkm				tcc = 0;
187957416Smarkm				flushline = 1;
188057416Smarkm				break;
188157416Smarkm			}
188257416Smarkm			if (sc != rlogin) {
188357416Smarkm				++tcc;
188457416Smarkm				--tbp;
188557416Smarkm				--count;
188657416Smarkm				c = sc = rlogin;
188757416Smarkm			}
188857416Smarkm		}
188957416Smarkm		if ((sc == '\n') || (sc == '\r'))
189057416Smarkm			bol = 1;
189157416Smarkm	} else if (sc == escape) {
189257416Smarkm	    /*
189357416Smarkm	     * Double escape is a pass through of a single escape character.
189457416Smarkm	     */
189557416Smarkm	    if (tcc && strip(*tbp) == escape) {
189657416Smarkm		tbp++;
189757416Smarkm		tcc--;
189857416Smarkm		count++;
189957416Smarkm		bol = 0;
190057416Smarkm	    } else {
190157416Smarkm		command(0, (char *)tbp, tcc);
190257416Smarkm		bol = 1;
190357416Smarkm		count += tcc;
190457416Smarkm		tcc = 0;
190557416Smarkm		flushline = 1;
190657416Smarkm		break;
190757416Smarkm	    }
190857416Smarkm	} else
190957416Smarkm	    bol = 0;
191057416Smarkm#ifdef	KLUDGELINEMODE
191157416Smarkm	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
191257416Smarkm	    if (tcc > 0 && strip(*tbp) == echoc) {
191357416Smarkm		tcc--; tbp++; count++;
191457416Smarkm	    } else {
191557416Smarkm		dontlecho = !dontlecho;
191657416Smarkm		settimer(echotoggle);
191757416Smarkm		setconnmode(0);
191857416Smarkm		flushline = 1;
191957416Smarkm		break;
192057416Smarkm	    }
192157416Smarkm	}
192257416Smarkm#endif
192357416Smarkm	if (MODE_LOCAL_CHARS(globalmode)) {
192457416Smarkm	    if (TerminalSpecialChars(sc) == 0) {
192557416Smarkm		bol = 1;
192657416Smarkm		break;
192757416Smarkm	    }
192857416Smarkm	}
192957416Smarkm	if (my_want_state_is_wont(TELOPT_BINARY)) {
193057416Smarkm	    switch (c) {
193157416Smarkm	    case '\n':
193257416Smarkm		    /*
193357416Smarkm		     * If we are in CRMOD mode (\r ==> \n)
193457416Smarkm		     * on our local machine, then probably
193557416Smarkm		     * a newline (unix) is CRLF (TELNET).
193657416Smarkm		     */
193757416Smarkm		if (MODE_LOCAL_CHARS(globalmode)) {
193857416Smarkm		    NETADD('\r');
193957416Smarkm		}
194057416Smarkm		NETADD('\n');
194157416Smarkm		bol = flushline = 1;
194257416Smarkm		break;
194357416Smarkm	    case '\r':
194457416Smarkm		if (!crlf) {
194557416Smarkm		    NET2ADD('\r', '\0');
194657416Smarkm		} else {
194757416Smarkm		    NET2ADD('\r', '\n');
194857416Smarkm		}
194957416Smarkm		bol = flushline = 1;
195057416Smarkm		break;
195157416Smarkm	    case IAC:
195257416Smarkm		NET2ADD(IAC, IAC);
195357416Smarkm		break;
195457416Smarkm	    default:
195557416Smarkm		NETADD(c);
195657416Smarkm		break;
195757416Smarkm	    }
195857416Smarkm	} else if (c == IAC) {
195957416Smarkm	    NET2ADD(IAC, IAC);
196057416Smarkm	} else {
196157416Smarkm	    NETADD(c);
196257416Smarkm	}
196357416Smarkm    }
196457416Smarkm    if (count)
196557416Smarkm	ring_consumed(&ttyiring, count);
196657416Smarkm    return returnValue||count;		/* Non-zero if we did anything */
196757416Smarkm}
196857416Smarkm
196957416Smarkm/*
197057416Smarkm * Scheduler()
197157416Smarkm *
197257416Smarkm * Try to do something.
197357416Smarkm *
197457416Smarkm * If we do something useful, return 1; else return 0.
197557416Smarkm *
197657416Smarkm */
197757416Smarkm
197857416Smarkm
197990926Snectar    int
198057416SmarkmScheduler(int block) /* should we block in the select ? */
198157416Smarkm{
198257416Smarkm		/* One wants to be a bit careful about setting returnValue
198357416Smarkm		 * to one, since a one implies we did some useful work,
198457416Smarkm		 * and therefore probably won't be called to block next
198557416Smarkm		 * time (TN3270 mode only).
198657416Smarkm		 */
198757416Smarkm    int returnValue;
198857416Smarkm    int netin, netout, netex, ttyin, ttyout;
198957416Smarkm
199057416Smarkm    /* Decide which rings should be processed */
199157416Smarkm
199257416Smarkm    netout = ring_full_count(&netoring) &&
199357416Smarkm	    (flushline ||
199457416Smarkm		(my_want_state_is_wont(TELOPT_LINEMODE)
199557416Smarkm#ifdef	KLUDGELINEMODE
199657416Smarkm			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
199757416Smarkm#endif
199857416Smarkm		) ||
199957416Smarkm			my_want_state_is_will(TELOPT_BINARY));
200057416Smarkm    ttyout = ring_full_count(&ttyoring);
200157416Smarkm
200257416Smarkm    ttyin = ring_empty_count(&ttyiring);
200357416Smarkm
200457416Smarkm    netin = !ISend && ring_empty_count(&netiring);
200557416Smarkm
200657416Smarkm    netex = !SYNCHing;
200757416Smarkm
200857416Smarkm    /* If we have seen a signal recently, reset things */
200957416Smarkm
201090926Snectar    if (scheduler_lockout_tty) {
201190926Snectar        ttyin = ttyout = 0;
201290926Snectar    }
201390926Snectar
201457416Smarkm    /* Call to system code to process rings */
201557416Smarkm
201657416Smarkm    returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
201757416Smarkm
201857416Smarkm    /* Now, look at the input rings, looking for work to do. */
201957416Smarkm
202057416Smarkm    if (ring_full_count(&ttyiring)) {
202157416Smarkm	    returnValue |= telsnd();
202257416Smarkm    }
202357416Smarkm
202457416Smarkm    if (ring_full_count(&netiring)) {
202557416Smarkm	returnValue |= telrcv();
202657416Smarkm    }
202757416Smarkm    return returnValue;
202857416Smarkm}
202957416Smarkm
2030178825Sdfrextern int auth_has_failed; /* XXX should be somewhere else */
2031178825Sdfr
203257416Smarkm/*
203357416Smarkm * Select from tty and network...
203457416Smarkm */
203557416Smarkmvoid
203657416Smarkmmy_telnet(char *user)
203757416Smarkm{
203890926Snectar    int printed_encrypt = 0;
2039233294Sstas
204057416Smarkm    sys_telnet_init();
204157416Smarkm
204257416Smarkm#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
204357416Smarkm    {
204457416Smarkm	static char local_host[256] = { 0 };
204557416Smarkm
204657416Smarkm	if (!local_host[0]) {
204757416Smarkm		/* XXX - should be k_gethostname? */
204857416Smarkm		gethostname(local_host, sizeof(local_host));
204957416Smarkm		local_host[sizeof(local_host)-1] = 0;
205057416Smarkm	}
205157416Smarkm	auth_encrypt_init(local_host, hostname, "TELNET", 0);
205257416Smarkm	auth_encrypt_user(user);
205357416Smarkm    }
205457416Smarkm#endif
205557416Smarkm    if (telnetport) {
205657416Smarkm#if	defined(AUTHENTICATION)
205757416Smarkm	if (autologin)
205857416Smarkm		send_will(TELOPT_AUTHENTICATION, 1);
205957416Smarkm#endif
206057416Smarkm#if	defined(ENCRYPTION)
206157416Smarkm	send_do(TELOPT_ENCRYPT, 1);
206257416Smarkm	send_will(TELOPT_ENCRYPT, 1);
206357416Smarkm#endif
206457416Smarkm	send_do(TELOPT_SGA, 1);
206557416Smarkm	send_will(TELOPT_TTYPE, 1);
206657416Smarkm	send_will(TELOPT_NAWS, 1);
206757416Smarkm	send_will(TELOPT_TSPEED, 1);
206857416Smarkm	send_will(TELOPT_LFLOW, 1);
206957416Smarkm	send_will(TELOPT_LINEMODE, 1);
207057416Smarkm	send_will(TELOPT_NEW_ENVIRON, 1);
207157416Smarkm	send_do(TELOPT_STATUS, 1);
207257416Smarkm	if (env_getvalue((unsigned char *)"DISPLAY"))
207357416Smarkm	    send_will(TELOPT_XDISPLOC, 1);
207457416Smarkm	if (binary)
207557416Smarkm	    tel_enter_binary(binary);
207657416Smarkm    }
207757416Smarkm
207890926Snectar#ifdef ENCRYPTION
207990926Snectar    /*
208090926Snectar     * Note: we assume a tie to the authentication option here.  This
208190926Snectar     * is necessary so that authentication fails, we don't spin
2082233294Sstas     * forever.
208390926Snectar     */
2084102644Snectar    if (telnetport && wantencryption) {
208590926Snectar	time_t timeout = time(0) + 60;
208690926Snectar
208790926Snectar	send_do(TELOPT_ENCRYPT, 1);
208890926Snectar	send_will(TELOPT_ENCRYPT, 1);
208990926Snectar	while (1) {
209090926Snectar	    if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) {
2091102644Snectar		if (wantencryption == -1) {
2092102644Snectar		    break;
2093102644Snectar		} else {
2094102644Snectar		    printf("\nServer refused to negotiate authentication,\n");
2095102644Snectar		    printf("which is required for encryption.\n");
2096102644Snectar		    Exit(1);
2097102644Snectar		}
209890926Snectar	    }
209990926Snectar	    if (auth_has_failed) {
2100178825Sdfr		printf("\nAuthentication negotiation has failed,\n");
210190926Snectar		printf("which is required for encryption.\n");
210290926Snectar		Exit(1);
210390926Snectar	    }
210490926Snectar	    if (my_want_state_is_dont(TELOPT_ENCRYPT) ||
210590926Snectar		my_want_state_is_wont(TELOPT_ENCRYPT)) {
210690926Snectar		printf("\nServer refused to negotiate encryption.\n");
210790926Snectar		Exit(1);
210890926Snectar	    }
210990926Snectar	    if (encrypt_is_encrypting())
211090926Snectar		break;
211190926Snectar	    if (time(0) > timeout) {
211290926Snectar		printf("\nEncryption could not be enabled.\n");
211390926Snectar		Exit(1);
211490926Snectar	    }
211590926Snectar	    if (printed_encrypt == 0) {
211690926Snectar		    printed_encrypt = 1;
211790926Snectar		    printf("Waiting for encryption to be negotiated...\n");
211890926Snectar		    /*
2119233294Sstas		     * Turn on MODE_TRAPSIG and then turn off localchars
212090926Snectar		     * so that ^C will cause telnet to exit.
212190926Snectar		     */
212290926Snectar		    TerminalNewMode(getconnmode()|MODE_TRAPSIG);
212390926Snectar		    intr_waiting = 1;
212490926Snectar	    }
212590926Snectar	    if (intr_happened) {
212690926Snectar		    printf("\nUser interrupt.\n");
212790926Snectar		    Exit(1);
212890926Snectar	    }
2129178825Sdfr	    if (telnet_spin()) {
2130178825Sdfr		    printf("\nServer disconnected.\n");
2131178825Sdfr		    Exit(1);
2132178825Sdfr	    }
2133233294Sstas
213490926Snectar	}
213590926Snectar	if (printed_encrypt) {
2136102644Snectar		printf("Encryption negotiated.\n");
213790926Snectar		intr_waiting = 0;
213890926Snectar		setconnmode(0);
213990926Snectar	}
214090926Snectar    }
214190926Snectar#endif
214290926Snectar
214357416Smarkm    for (;;) {
214457416Smarkm	int schedValue;
214557416Smarkm
214657416Smarkm	while ((schedValue = Scheduler(0)) != 0) {
214757416Smarkm	    if (schedValue == -1) {
214857416Smarkm		setcommandmode();
214957416Smarkm		return;
215057416Smarkm	    }
215157416Smarkm	}
215257416Smarkm
215357416Smarkm	if (Scheduler(1) == -1) {
215457416Smarkm	    setcommandmode();
215557416Smarkm	    return;
215657416Smarkm	}
215757416Smarkm    }
215857416Smarkm}
215957416Smarkm
216057416Smarkm/*
216157416Smarkm * netclear()
216257416Smarkm *
216357416Smarkm *	We are about to do a TELNET SYNCH operation.  Clear
216457416Smarkm * the path to the network.
216557416Smarkm *
216657416Smarkm *	Things are a bit tricky since we may have sent the first
216757416Smarkm * byte or so of a previous TELNET command into the network.
216857416Smarkm * So, we have to scan the network buffer from the beginning
216957416Smarkm * until we are up to where we want to be.
217057416Smarkm *
217157416Smarkm *	A side effect of what we do, just to keep things
217257416Smarkm * simple, is to clear the urgent data pointer.  The principal
217357416Smarkm * caller should be setting the urgent data pointer AFTER calling
217457416Smarkm * us in any case.
217557416Smarkm */
217657416Smarkm
217757416Smarkmstatic void
217857416Smarkmnetclear()
217957416Smarkm{
218057416Smarkm#if	0	/* XXX */
218157416Smarkm    char *thisitem, *next;
218257416Smarkm    char *good;
218357416Smarkm#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
218457416Smarkm				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
218557416Smarkm
218657416Smarkm    thisitem = netobuf;
218757416Smarkm
218857416Smarkm    while ((next = nextitem(thisitem)) <= netobuf.send) {
218957416Smarkm	thisitem = next;
219057416Smarkm    }
219157416Smarkm
219257416Smarkm    /* Now, thisitem is first before/at boundary. */
219357416Smarkm
219457416Smarkm    good = netobuf;	/* where the good bytes go */
219557416Smarkm
219657416Smarkm    while (netoring.add > thisitem) {
219757416Smarkm	if (wewant(thisitem)) {
219857416Smarkm	    int length;
219957416Smarkm
220057416Smarkm	    next = thisitem;
220157416Smarkm	    do {
220257416Smarkm		next = nextitem(next);
220357416Smarkm	    } while (wewant(next) && (nfrontp > next));
220457416Smarkm	    length = next-thisitem;
220557416Smarkm	    memmove(good, thisitem, length);
220657416Smarkm	    good += length;
220757416Smarkm	    thisitem = next;
220857416Smarkm	} else {
220957416Smarkm	    thisitem = nextitem(thisitem);
221057416Smarkm	}
221157416Smarkm    }
221257416Smarkm
221357416Smarkm#endif	/* 0 */
221457416Smarkm}
221557416Smarkm
221657416Smarkm/*
221757416Smarkm * These routines add various telnet commands to the data stream.
221857416Smarkm */
221957416Smarkm
222057416Smarkmstatic void
222157416Smarkmdoflush()
222257416Smarkm{
222357416Smarkm    NET2ADD(IAC, DO);
222457416Smarkm    NETADD(TELOPT_TM);
222557416Smarkm    flushline = 1;
222657416Smarkm    flushout = 1;
222757416Smarkm    ttyflush(1);			/* Flush/drop output */
222857416Smarkm    /* do printoption AFTER flush, otherwise the output gets tossed... */
222957416Smarkm    printoption("SENT", DO, TELOPT_TM);
223057416Smarkm}
223157416Smarkm
223257416Smarkmvoid
223357416SmarkmxmitAO(void)
223457416Smarkm{
223557416Smarkm    NET2ADD(IAC, AO);
223657416Smarkm    printoption("SENT", IAC, AO);
223757416Smarkm    if (autoflush) {
223857416Smarkm	doflush();
223957416Smarkm    }
224057416Smarkm}
224157416Smarkm
224257416Smarkm
224357416Smarkmvoid
224457416SmarkmxmitEL(void)
224557416Smarkm{
224657416Smarkm    NET2ADD(IAC, EL);
224757416Smarkm    printoption("SENT", IAC, EL);
224857416Smarkm}
224957416Smarkm
225057416Smarkmvoid
225157416SmarkmxmitEC(void)
225257416Smarkm{
225357416Smarkm    NET2ADD(IAC, EC);
225457416Smarkm    printoption("SENT", IAC, EC);
225557416Smarkm}
225657416Smarkm
225757416Smarkm
225857416Smarkmint
225957416Smarkmdosynch()
226057416Smarkm{
226157416Smarkm    netclear();			/* clear the path to the network */
226257416Smarkm    NETADD(IAC);
226357416Smarkm    setneturg();
226457416Smarkm    NETADD(DM);
226557416Smarkm    printoption("SENT", IAC, DM);
226657416Smarkm    return 1;
226757416Smarkm}
226857416Smarkm
226957416Smarkmint want_status_response = 0;
227057416Smarkm
227157416Smarkmint
227257416Smarkmget_status()
227357416Smarkm{
227457416Smarkm    unsigned char tmp[16];
227557416Smarkm    unsigned char *cp;
227657416Smarkm
227757416Smarkm    if (my_want_state_is_dont(TELOPT_STATUS)) {
227857416Smarkm	printf("Remote side does not support STATUS option\n");
227957416Smarkm	return 0;
228057416Smarkm    }
228157416Smarkm    cp = tmp;
228257416Smarkm
228357416Smarkm    *cp++ = IAC;
228457416Smarkm    *cp++ = SB;
228557416Smarkm    *cp++ = TELOPT_STATUS;
228657416Smarkm    *cp++ = TELQUAL_SEND;
228757416Smarkm    *cp++ = IAC;
228857416Smarkm    *cp++ = SE;
228957416Smarkm    if (NETROOM() >= cp - tmp) {
229057416Smarkm	ring_supply_data(&netoring, tmp, cp-tmp);
229157416Smarkm	printsub('>', tmp+2, cp - tmp - 2);
229257416Smarkm    }
229357416Smarkm    ++want_status_response;
229457416Smarkm    return 1;
229557416Smarkm}
229657416Smarkm
229757416Smarkmvoid
229857416Smarkmintp(void)
229957416Smarkm{
230057416Smarkm    NET2ADD(IAC, IP);
230157416Smarkm    printoption("SENT", IAC, IP);
230257416Smarkm    flushline = 1;
230357416Smarkm    if (autoflush) {
230457416Smarkm	doflush();
230557416Smarkm    }
230657416Smarkm    if (autosynch) {
230757416Smarkm	dosynch();
230857416Smarkm    }
230957416Smarkm}
231057416Smarkm
231157416Smarkmvoid
231257416Smarkmsendbrk(void)
231357416Smarkm{
231457416Smarkm    NET2ADD(IAC, BREAK);
231557416Smarkm    printoption("SENT", IAC, BREAK);
231657416Smarkm    flushline = 1;
231757416Smarkm    if (autoflush) {
231857416Smarkm	doflush();
231957416Smarkm    }
232057416Smarkm    if (autosynch) {
232157416Smarkm	dosynch();
232257416Smarkm    }
232357416Smarkm}
232457416Smarkm
232557416Smarkmvoid
232657416Smarkmsendabort(void)
232757416Smarkm{
232857416Smarkm    NET2ADD(IAC, ABORT);
232957416Smarkm    printoption("SENT", IAC, ABORT);
233057416Smarkm    flushline = 1;
233157416Smarkm    if (autoflush) {
233257416Smarkm	doflush();
233357416Smarkm    }
233457416Smarkm    if (autosynch) {
233557416Smarkm	dosynch();
233657416Smarkm    }
233757416Smarkm}
233857416Smarkm
233957416Smarkmvoid
234057416Smarkmsendsusp(void)
234157416Smarkm{
234257416Smarkm    NET2ADD(IAC, SUSP);
234357416Smarkm    printoption("SENT", IAC, SUSP);
234457416Smarkm    flushline = 1;
234557416Smarkm    if (autoflush) {
234657416Smarkm	doflush();
234757416Smarkm    }
234857416Smarkm    if (autosynch) {
234957416Smarkm	dosynch();
235057416Smarkm    }
235157416Smarkm}
235257416Smarkm
235357416Smarkmvoid
235457416Smarkmsendeof(void)
235557416Smarkm{
235657416Smarkm    NET2ADD(IAC, xEOF);
235757416Smarkm    printoption("SENT", IAC, xEOF);
235857416Smarkm}
235957416Smarkm
236057416Smarkmvoid
236157416Smarkmsendayt(void)
236257416Smarkm{
236357416Smarkm    NET2ADD(IAC, AYT);
236457416Smarkm    printoption("SENT", IAC, AYT);
236557416Smarkm}
236657416Smarkm
236757416Smarkm/*
236857416Smarkm * Send a window size update to the remote system.
236957416Smarkm */
237057416Smarkm
237157416Smarkmvoid
237257416Smarkmsendnaws()
237357416Smarkm{
237457416Smarkm    long rows, cols;
237557416Smarkm    unsigned char tmp[16];
237657416Smarkm    unsigned char *cp;
237757416Smarkm
237857416Smarkm    if (my_state_is_wont(TELOPT_NAWS))
237957416Smarkm	return;
238057416Smarkm
238190926Snectar#undef PUTSHORT
238257416Smarkm#define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
238357416Smarkm			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
238457416Smarkm
238557416Smarkm    if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
238657416Smarkm	return;
238757416Smarkm    }
238857416Smarkm
238957416Smarkm    cp = tmp;
239057416Smarkm
239157416Smarkm    *cp++ = IAC;
239257416Smarkm    *cp++ = SB;
239357416Smarkm    *cp++ = TELOPT_NAWS;
239457416Smarkm    PUTSHORT(cp, cols);
239557416Smarkm    PUTSHORT(cp, rows);
239657416Smarkm    *cp++ = IAC;
239757416Smarkm    *cp++ = SE;
239857416Smarkm    if (NETROOM() >= cp - tmp) {
239957416Smarkm	ring_supply_data(&netoring, tmp, cp-tmp);
240057416Smarkm	printsub('>', tmp+2, cp - tmp - 2);
240157416Smarkm    }
240257416Smarkm}
240357416Smarkm
240457416Smarkmvoid
240557416Smarkmtel_enter_binary(int rw)
240657416Smarkm{
240757416Smarkm    if (rw&1)
240857416Smarkm	send_do(TELOPT_BINARY, 1);
240957416Smarkm    if (rw&2)
241057416Smarkm	send_will(TELOPT_BINARY, 1);
241157416Smarkm}
241257416Smarkm
241357416Smarkmvoid
241457416Smarkmtel_leave_binary(int rw)
241557416Smarkm{
241657416Smarkm    if (rw&1)
241757416Smarkm	send_dont(TELOPT_BINARY, 1);
241857416Smarkm    if (rw&2)
241957416Smarkm	send_wont(TELOPT_BINARY, 1);
242057416Smarkm}
2421