telnet.c revision 114911
129088Smarkm/*
229088Smarkm * Copyright (c) 1988, 1990, 1993
329088Smarkm *	The Regents of the University of California.  All rights reserved.
429088Smarkm *
529088Smarkm * Redistribution and use in source and binary forms, with or without
629088Smarkm * modification, are permitted provided that the following conditions
729088Smarkm * are met:
829088Smarkm * 1. Redistributions of source code must retain the above copyright
929088Smarkm *    notice, this list of conditions and the following disclaimer.
1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1129088Smarkm *    notice, this list of conditions and the following disclaimer in the
1229088Smarkm *    documentation and/or other materials provided with the distribution.
1329088Smarkm * 3. All advertising materials mentioning features or use of this software
1429088Smarkm *    must display the following acknowledgement:
1529088Smarkm *	This product includes software developed by the University of
1629088Smarkm *	California, Berkeley and its contributors.
1729088Smarkm * 4. Neither the name of the University nor the names of its contributors
1829088Smarkm *    may be used to endorse or promote products derived from this software
1929088Smarkm *    without specific prior written permission.
2029088Smarkm *
2129088Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2229088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2329088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2429088Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2529088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2629088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2729088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2829088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2929088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3029088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3129088Smarkm * SUCH DAMAGE.
3229088Smarkm */
3329088Smarkm
34114630Sobrien#if 0
3529088Smarkm#ifndef lint
3629181Smarkmstatic const char sccsid[] = "@(#)telnet.c	8.4 (Berkeley) 5/30/95";
3763248Speter#endif
38114630Sobrien#endif
39114630Sobrien#include <sys/cdefs.h>
40114630Sobrien__FBSDID("$FreeBSD: head/contrib/telnet/telnet/telnet.c 114911 2003-05-11 18:17:00Z markm $");
4129088Smarkm
4229088Smarkm#include <sys/types.h>
4329088Smarkm
4429088Smarkm/* By the way, we need to include curses.h before telnet.h since,
4529088Smarkm * among other things, telnet.h #defines 'DO', which is a variable
4629088Smarkm * declared in curses.h.
4729088Smarkm */
4829088Smarkm
4929088Smarkm#include <ctype.h>
5087139Smarkm#include <curses.h>
5187139Smarkm#include <signal.h>
5229181Smarkm#include <stdlib.h>
5387139Smarkm#include <term.h>
5429181Smarkm#include <unistd.h>
5587139Smarkm#include <arpa/telnet.h>
5629181Smarkm
5729088Smarkm#include "ring.h"
5829088Smarkm
5929088Smarkm#include "defines.h"
6029088Smarkm#include "externs.h"
6129088Smarkm#include "types.h"
6229088Smarkm#include "general.h"
6329088Smarkm
6487139Smarkm#ifdef	AUTHENTICATION
6529181Smarkm#include <libtelnet/auth.h>
6629181Smarkm#endif
6787139Smarkm#ifdef	ENCRYPTION
6829181Smarkm#include <libtelnet/encrypt.h>
6929181Smarkm#endif
7029181Smarkm#include <libtelnet/misc.h>
7129088Smarkm
7229088Smarkm#define	strip(x) ((my_want_state_is_wont(TELOPT_BINARY)) ? ((x)&0x7f) : (x))
7329088Smarkm
7429088Smarkmstatic unsigned char	subbuffer[SUBBUFSIZE],
7529088Smarkm			*subpointer, *subend;	 /* buffer for sub-options */
7629088Smarkm#define	SB_CLEAR()	subpointer = subbuffer;
7729088Smarkm#define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
7829088Smarkm#define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
7929088Smarkm				*subpointer++ = (c); \
8029088Smarkm			}
8129088Smarkm
8229088Smarkm#define	SB_GET()	((*subpointer++)&0xff)
8329088Smarkm#define	SB_PEEK()	((*subpointer)&0xff)
8429088Smarkm#define	SB_EOF()	(subpointer >= subend)
8529088Smarkm#define	SB_LEN()	(subend - subpointer)
8629088Smarkm
8729088Smarkmchar	options[256];		/* The combined options */
8829088Smarkmchar	do_dont_resp[256];
8929088Smarkmchar	will_wont_resp[256];
9029088Smarkm
9129088Smarkmint
9229088Smarkm	eight = 0,
9329088Smarkm	autologin = 0,	/* Autologin anyone? */
9429088Smarkm	skiprc = 0,
9529088Smarkm	connected,
9629088Smarkm	showoptions,
9729088Smarkm	ISend,		/* trying to send network data in */
98114911Smarkm	telnet_debug = 0,
9929088Smarkm	crmod,
10029088Smarkm	netdata,	/* Print out network data flow */
10129088Smarkm	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
10229088Smarkm	telnetport,
10329088Smarkm	SYNCHing,	/* we are in TELNET SYNCH mode */
10429088Smarkm	flushout,	/* flush output */
10529088Smarkm	autoflush = 0,	/* flush output when interrupting? */
10629088Smarkm	autosynch,	/* send interrupt characters with SYNCH? */
10729088Smarkm	localflow,	/* we handle flow control locally */
10829088Smarkm	restartany,	/* if flow control enabled, restart on any character */
10929088Smarkm	localchars,	/* we recognize interrupt/quit */
11029088Smarkm	donelclchars,	/* the user has set "localchars" */
11129088Smarkm	donebinarytoggle,	/* the user has put us in binary */
11229088Smarkm	dontlecho,	/* do we suppress local echoing right now? */
11329181Smarkm	globalmode,
11447973Sru	doaddrlookup = 1, /* do a reverse address lookup? */
11529181Smarkm	clienteof = 0;
11629088Smarkm
11729088Smarkmchar *prompt = 0;
11881965Smarkm#ifdef ENCRYPTION
11976616Speterchar *line;		/* hack around breakage in sra.c :-( !! */
12081965Smarkm#endif
12129088Smarkm
12229088Smarkmcc_t escape;
12329088Smarkmcc_t rlogin;
12429088Smarkm#ifdef	KLUDGELINEMODE
12529088Smarkmcc_t echoc;
12629088Smarkm#endif
12729088Smarkm
12829088Smarkm/*
12929088Smarkm * Telnet receiver states for fsm
13029088Smarkm */
13129088Smarkm#define	TS_DATA		0
13229088Smarkm#define	TS_IAC		1
13329088Smarkm#define	TS_WILL		2
13429088Smarkm#define	TS_WONT		3
13529088Smarkm#define	TS_DO		4
13629088Smarkm#define	TS_DONT		5
13729088Smarkm#define	TS_CR		6
13829088Smarkm#define	TS_SB		7		/* sub-option collection */
13929088Smarkm#define	TS_SE		8		/* looking for sub-option end */
14029088Smarkm
14129088Smarkmstatic int	telrcv_state;
14229088Smarkm#ifdef	OLD_ENVIRON
14329088Smarkmunsigned char telopt_environ = TELOPT_NEW_ENVIRON;
14429088Smarkm#else
14529088Smarkm# define telopt_environ TELOPT_NEW_ENVIRON
14629088Smarkm#endif
14729088Smarkm
14887139Smarkmjmp_buf	toplevel;
14929088Smarkmjmp_buf	peerdied;
15029088Smarkm
15129088Smarkmint	flushline;
15229088Smarkmint	linemode;
15329088Smarkm
15429088Smarkm#ifdef	KLUDGELINEMODE
15529088Smarkmint	kludgelinemode = 1;
15629088Smarkm#endif
15729088Smarkm
15887139Smarkmstatic int is_unique(char *, char **, char **);
15987139Smarkm
16029088Smarkm/*
16129088Smarkm * The following are some clocks used to decide how to interpret
16229088Smarkm * the relationship between various variables.
16329088Smarkm */
16429088Smarkm
16529088SmarkmClocks clocks;
16629088Smarkm
16729088Smarkm/*
16829088Smarkm * Initialize telnet environment.
16929088Smarkm */
17029088Smarkm
17187139Smarkmvoid
17287139Smarkminit_telnet(void)
17329088Smarkm{
17429088Smarkm    env_init();
17529088Smarkm
17629088Smarkm    SB_CLEAR();
17729088Smarkm    ClearArray(options);
17829088Smarkm
17987139Smarkm    connected = ISend = localflow = donebinarytoggle = 0;
18087139Smarkm#ifdef	AUTHENTICATION
18187139Smarkm#ifdef	ENCRYPTION
18229088Smarkm    auth_encrypt_connect(connected);
18387139Smarkm#endif
18487139Smarkm#endif
18529088Smarkm    restartany = -1;
18629088Smarkm
18729088Smarkm    SYNCHing = 0;
18829088Smarkm
18929088Smarkm    /* Don't change NetTrace */
19029088Smarkm
19129088Smarkm    escape = CONTROL(']');
19229088Smarkm    rlogin = _POSIX_VDISABLE;
19329088Smarkm#ifdef	KLUDGELINEMODE
19429088Smarkm    echoc = CONTROL('E');
19529088Smarkm#endif
19629088Smarkm
19729088Smarkm    flushline = 1;
19829088Smarkm    telrcv_state = TS_DATA;
19929088Smarkm}
20029088Smarkm
20129088Smarkm
20229088Smarkm/*
20329088Smarkm * These routines are in charge of sending option negotiations
20429088Smarkm * to the other side.
20529088Smarkm *
20629088Smarkm * The basic idea is that we send the negotiation if either side
20729088Smarkm * is in disagreement as to what the current state should be.
20829088Smarkm */
20929088Smarkm
21087139Smarkmvoid
21187139Smarkmsend_do(int c, int init)
21229088Smarkm{
21329088Smarkm    if (init) {
21429088Smarkm	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
21529088Smarkm				my_want_state_is_do(c))
21629088Smarkm	    return;
21729088Smarkm	set_my_want_state_do(c);
21829088Smarkm	do_dont_resp[c]++;
21929088Smarkm    }
22029088Smarkm    NET2ADD(IAC, DO);
22129088Smarkm    NETADD(c);
22229088Smarkm    printoption("SENT", DO, c);
22329088Smarkm}
22429088Smarkm
22587139Smarkmvoid
22687139Smarkmsend_dont(int c, int init)
22729088Smarkm{
22829088Smarkm    if (init) {
22929088Smarkm	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
23029088Smarkm				my_want_state_is_dont(c))
23129088Smarkm	    return;
23229088Smarkm	set_my_want_state_dont(c);
23329088Smarkm	do_dont_resp[c]++;
23429088Smarkm    }
23529088Smarkm    NET2ADD(IAC, DONT);
23629088Smarkm    NETADD(c);
23729088Smarkm    printoption("SENT", DONT, c);
23829088Smarkm}
23929088Smarkm
24087139Smarkmvoid
24187139Smarkmsend_will(int c, int init)
24229088Smarkm{
24329088Smarkm    if (init) {
24429088Smarkm	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
24529088Smarkm				my_want_state_is_will(c))
24629088Smarkm	    return;
24729088Smarkm	set_my_want_state_will(c);
24829088Smarkm	will_wont_resp[c]++;
24929088Smarkm    }
25029088Smarkm    NET2ADD(IAC, WILL);
25129088Smarkm    NETADD(c);
25229088Smarkm    printoption("SENT", WILL, c);
25329088Smarkm}
25429088Smarkm
25587139Smarkmvoid
25687139Smarkmsend_wont(int c, int init)
25729088Smarkm{
25829088Smarkm    if (init) {
25929088Smarkm	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
26029088Smarkm				my_want_state_is_wont(c))
26129088Smarkm	    return;
26229088Smarkm	set_my_want_state_wont(c);
26329088Smarkm	will_wont_resp[c]++;
26429088Smarkm    }
26529088Smarkm    NET2ADD(IAC, WONT);
26629088Smarkm    NETADD(c);
26729088Smarkm    printoption("SENT", WONT, c);
26829088Smarkm}
26929088Smarkm
27087139Smarkmvoid
27187139Smarkmwilloption(int option)
27229088Smarkm{
27329088Smarkm	int new_state_ok = 0;
27429088Smarkm
27529088Smarkm	if (do_dont_resp[option]) {
27629088Smarkm	    --do_dont_resp[option];
27729088Smarkm	    if (do_dont_resp[option] && my_state_is_do(option))
27829088Smarkm		--do_dont_resp[option];
27929088Smarkm	}
28029088Smarkm
28129088Smarkm	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
28229088Smarkm
28329088Smarkm	    switch (option) {
28429088Smarkm
28529088Smarkm	    case TELOPT_ECHO:
28629088Smarkm	    case TELOPT_BINARY:
28729088Smarkm	    case TELOPT_SGA:
28829088Smarkm		settimer(modenegotiated);
289103955Smarkm		/* FALLTHROUGH */
29029088Smarkm	    case TELOPT_STATUS:
29187139Smarkm#ifdef	AUTHENTICATION
29229088Smarkm	    case TELOPT_AUTHENTICATION:
29329088Smarkm#endif
29429088Smarkm#ifdef	ENCRYPTION
29529088Smarkm	    case TELOPT_ENCRYPT:
29629088Smarkm#endif /* ENCRYPTION */
29729088Smarkm		new_state_ok = 1;
29829088Smarkm		break;
29929088Smarkm
30029088Smarkm	    case TELOPT_TM:
30129088Smarkm		if (flushout)
30229088Smarkm		    flushout = 0;
30329088Smarkm		/*
30429088Smarkm		 * Special case for TM.  If we get back a WILL,
30529088Smarkm		 * pretend we got back a WONT.
30629088Smarkm		 */
30729088Smarkm		set_my_want_state_dont(option);
30829088Smarkm		set_my_state_dont(option);
30929088Smarkm		return;			/* Never reply to TM will's/wont's */
31029088Smarkm
31129088Smarkm	    case TELOPT_LINEMODE:
31229088Smarkm	    default:
31329088Smarkm		break;
31429088Smarkm	    }
31529088Smarkm
31629088Smarkm	    if (new_state_ok) {
31729088Smarkm		set_my_want_state_do(option);
31829088Smarkm		send_do(option, 0);
31929088Smarkm		setconnmode(0);		/* possibly set new tty mode */
32029088Smarkm	    } else {
32129088Smarkm		do_dont_resp[option]++;
32229088Smarkm		send_dont(option, 0);
32329088Smarkm	    }
32429088Smarkm	}
32529088Smarkm	set_my_state_do(option);
32629088Smarkm#ifdef	ENCRYPTION
32729088Smarkm	if (option == TELOPT_ENCRYPT)
32829088Smarkm		encrypt_send_support();
32929088Smarkm#endif	/* ENCRYPTION */
33029088Smarkm}
33129088Smarkm
33287139Smarkmvoid
33387139Smarkmwontoption(int option)
33429088Smarkm{
33529088Smarkm	if (do_dont_resp[option]) {
33629088Smarkm	    --do_dont_resp[option];
33729088Smarkm	    if (do_dont_resp[option] && my_state_is_dont(option))
33829088Smarkm		--do_dont_resp[option];
33929088Smarkm	}
34029088Smarkm
34129088Smarkm	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
34229088Smarkm
34329088Smarkm	    switch (option) {
34429088Smarkm
34529088Smarkm#ifdef	KLUDGELINEMODE
34629088Smarkm	    case TELOPT_SGA:
34729088Smarkm		if (!kludgelinemode)
34829088Smarkm		    break;
349103955Smarkm		/* FALLTHROUGH */
35029088Smarkm#endif
35129088Smarkm	    case TELOPT_ECHO:
35229088Smarkm		settimer(modenegotiated);
35329088Smarkm		break;
35429088Smarkm
35529088Smarkm	    case TELOPT_TM:
35629088Smarkm		if (flushout)
35729088Smarkm		    flushout = 0;
35829088Smarkm		set_my_want_state_dont(option);
35929088Smarkm		set_my_state_dont(option);
36029088Smarkm		return;		/* Never reply to TM will's/wont's */
36129088Smarkm
36229088Smarkm	    default:
36329088Smarkm		break;
36429088Smarkm	    }
36529088Smarkm	    set_my_want_state_dont(option);
36629088Smarkm	    if (my_state_is_do(option))
36729088Smarkm		send_dont(option, 0);
36829088Smarkm	    setconnmode(0);			/* Set new tty mode */
36929088Smarkm	} else if (option == TELOPT_TM) {
37029088Smarkm	    /*
37129088Smarkm	     * Special case for TM.
37229088Smarkm	     */
37329088Smarkm	    if (flushout)
37429088Smarkm		flushout = 0;
37529088Smarkm	    set_my_want_state_dont(option);
37629088Smarkm	}
37729088Smarkm	set_my_state_dont(option);
37829088Smarkm}
37929088Smarkm
38087139Smarkmstatic void
38187139Smarkmdooption(int option)
38229088Smarkm{
38329088Smarkm	int new_state_ok = 0;
38429088Smarkm
38529088Smarkm	if (will_wont_resp[option]) {
38629088Smarkm	    --will_wont_resp[option];
38729088Smarkm	    if (will_wont_resp[option] && my_state_is_will(option))
38829088Smarkm		--will_wont_resp[option];
38929088Smarkm	}
39029088Smarkm
39129088Smarkm	if (will_wont_resp[option] == 0) {
39229088Smarkm	  if (my_want_state_is_wont(option)) {
39329088Smarkm
39429088Smarkm	    switch (option) {
39529088Smarkm
39629088Smarkm	    case TELOPT_TM:
39729088Smarkm		/*
39829088Smarkm		 * Special case for TM.  We send a WILL, but pretend
39929088Smarkm		 * we sent WONT.
40029088Smarkm		 */
40129088Smarkm		send_will(option, 0);
40229088Smarkm		set_my_want_state_wont(TELOPT_TM);
40329088Smarkm		set_my_state_wont(TELOPT_TM);
40429088Smarkm		return;
40529088Smarkm
40629088Smarkm	    case TELOPT_BINARY:		/* binary mode */
40729088Smarkm	    case TELOPT_NAWS:		/* window size */
40829088Smarkm	    case TELOPT_TSPEED:		/* terminal speed */
40929088Smarkm	    case TELOPT_LFLOW:		/* local flow control */
41029088Smarkm	    case TELOPT_TTYPE:		/* terminal type option */
41129088Smarkm	    case TELOPT_SGA:		/* no big deal */
41229088Smarkm#ifdef	ENCRYPTION
41329088Smarkm	    case TELOPT_ENCRYPT:	/* encryption variable option */
41429088Smarkm#endif	/* ENCRYPTION */
41529088Smarkm		new_state_ok = 1;
41629088Smarkm		break;
41729088Smarkm
41829088Smarkm	    case TELOPT_NEW_ENVIRON:	/* New environment variable option */
41929088Smarkm#ifdef	OLD_ENVIRON
42029088Smarkm		if (my_state_is_will(TELOPT_OLD_ENVIRON))
42129088Smarkm			send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */
42229088Smarkm		goto env_common;
42329088Smarkm	    case TELOPT_OLD_ENVIRON:	/* Old environment variable option */
42429088Smarkm		if (my_state_is_will(TELOPT_NEW_ENVIRON))
42529088Smarkm			break;		/* Don't enable if new one is in use! */
42629088Smarkm	    env_common:
42729088Smarkm		telopt_environ = option;
42829088Smarkm#endif
42929088Smarkm		new_state_ok = 1;
43029088Smarkm		break;
43129088Smarkm
43287139Smarkm#ifdef	AUTHENTICATION
43329088Smarkm	    case TELOPT_AUTHENTICATION:
43429088Smarkm		if (autologin)
43529088Smarkm			new_state_ok = 1;
43629088Smarkm		break;
43729088Smarkm#endif
43829088Smarkm
43929088Smarkm	    case TELOPT_XDISPLOC:	/* X Display location */
44087139Smarkm		if (env_getvalue("DISPLAY"))
44129088Smarkm		    new_state_ok = 1;
44229088Smarkm		break;
44329088Smarkm
44429088Smarkm	    case TELOPT_LINEMODE:
44529088Smarkm#ifdef	KLUDGELINEMODE
44629088Smarkm		kludgelinemode = 0;
44729088Smarkm		send_do(TELOPT_SGA, 1);
44829088Smarkm#endif
44929088Smarkm		set_my_want_state_will(TELOPT_LINEMODE);
45029088Smarkm		send_will(option, 0);
45129088Smarkm		set_my_state_will(TELOPT_LINEMODE);
45229088Smarkm		slc_init();
45329088Smarkm		return;
45429088Smarkm
45529088Smarkm	    case TELOPT_ECHO:		/* We're never going to echo... */
45629088Smarkm	    default:
45729088Smarkm		break;
45829088Smarkm	    }
45929088Smarkm
46029088Smarkm	    if (new_state_ok) {
46129088Smarkm		set_my_want_state_will(option);
46229088Smarkm		send_will(option, 0);
46329088Smarkm		setconnmode(0);			/* Set new tty mode */
46429088Smarkm	    } else {
46529088Smarkm		will_wont_resp[option]++;
46629088Smarkm		send_wont(option, 0);
46729088Smarkm	    }
46829088Smarkm	  } else {
46929088Smarkm	    /*
47029088Smarkm	     * Handle options that need more things done after the
47129088Smarkm	     * other side has acknowledged the option.
47229088Smarkm	     */
47329088Smarkm	    switch (option) {
47429088Smarkm	    case TELOPT_LINEMODE:
47529088Smarkm#ifdef	KLUDGELINEMODE
47629088Smarkm		kludgelinemode = 0;
47729088Smarkm		send_do(TELOPT_SGA, 1);
47829088Smarkm#endif
47929088Smarkm		set_my_state_will(option);
48029088Smarkm		slc_init();
48129088Smarkm		send_do(TELOPT_SGA, 0);
48229088Smarkm		return;
48329088Smarkm	    }
48429088Smarkm	  }
48529088Smarkm	}
48629088Smarkm	set_my_state_will(option);
48729088Smarkm}
48829088Smarkm
48987139Smarkmstatic void
49087139Smarkmdontoption(int option)
49129088Smarkm{
49229088Smarkm
49329088Smarkm	if (will_wont_resp[option]) {
49429088Smarkm	    --will_wont_resp[option];
49529088Smarkm	    if (will_wont_resp[option] && my_state_is_wont(option))
49629088Smarkm		--will_wont_resp[option];
49729088Smarkm	}
49829088Smarkm
49929088Smarkm	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
50029088Smarkm	    switch (option) {
50129088Smarkm	    case TELOPT_LINEMODE:
50229088Smarkm		linemode = 0;	/* put us back to the default state */
50329088Smarkm		break;
50429088Smarkm#ifdef	OLD_ENVIRON
50529088Smarkm	    case TELOPT_NEW_ENVIRON:
50629088Smarkm		/*
50729088Smarkm		 * The new environ option wasn't recognized, try
50829088Smarkm		 * the old one.
50929088Smarkm		 */
51029088Smarkm		send_will(TELOPT_OLD_ENVIRON, 1);
51129088Smarkm		telopt_environ = TELOPT_OLD_ENVIRON;
51229088Smarkm		break;
51329088Smarkm#endif
51429088Smarkm	    }
51529088Smarkm	    /* we always accept a DONT */
51629088Smarkm	    set_my_want_state_wont(option);
51729088Smarkm	    if (my_state_is_will(option))
51829088Smarkm		send_wont(option, 0);
51929088Smarkm	    setconnmode(0);			/* Set new tty mode */
52029088Smarkm	}
52129088Smarkm	set_my_state_wont(option);
52229088Smarkm}
52329088Smarkm
52429088Smarkm/*
52529088Smarkm * Given a buffer returned by tgetent(), this routine will turn
52672089Sasmodai * the pipe separated list of names in the buffer into an array
52729088Smarkm * of pointers to null terminated names.  We toss out any bad,
52829088Smarkm * duplicate, or verbose names (names with spaces).
52929088Smarkm */
53029088Smarkm
53187139Smarkmstatic const char *name_unknown = "UNKNOWN";
53287139Smarkmstatic const char *unknown[] = { NULL, NULL };
53329088Smarkm
53487139Smarkmstatic const char **
53587139Smarkmmklist(char *buf, char *name)
53629088Smarkm{
53787139Smarkm	int n;
53887139Smarkm	char c, *cp, **argvp, *cp2, **argv, **avt;
53929088Smarkm
54029088Smarkm	if (name) {
54181965Smarkm		if (strlen(name) > 40) {
54229088Smarkm			name = 0;
54329088Smarkm			unknown[0] = name_unknown;
54429088Smarkm		} else {
54529088Smarkm			unknown[0] = name;
54629088Smarkm			upcase(name);
54729088Smarkm		}
54829088Smarkm	} else
54929088Smarkm		unknown[0] = name_unknown;
55029088Smarkm	/*
55129088Smarkm	 * Count up the number of names.
55229088Smarkm	 */
55329088Smarkm	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
55429088Smarkm		if (*cp == '|')
55529088Smarkm			n++;
55629088Smarkm	}
55729088Smarkm	/*
55829088Smarkm	 * Allocate an array to put the name pointers into
55929088Smarkm	 */
56029088Smarkm	argv = (char **)malloc((n+3)*sizeof(char *));
56129088Smarkm	if (argv == 0)
56229088Smarkm		return(unknown);
56329088Smarkm
56429088Smarkm	/*
56529088Smarkm	 * Fill up the array of pointers to names.
56629088Smarkm	 */
56729088Smarkm	*argv = 0;
56829088Smarkm	argvp = argv+1;
56929088Smarkm	n = 0;
57029088Smarkm	for (cp = cp2 = buf; (c = *cp);  cp++) {
57129088Smarkm		if (c == '|' || c == ':') {
57229088Smarkm			*cp++ = '\0';
57329088Smarkm			/*
57429088Smarkm			 * Skip entries that have spaces or are over 40
57529088Smarkm			 * characters long.  If this is our environment
57629088Smarkm			 * name, then put it up front.  Otherwise, as
57729088Smarkm			 * long as this is not a duplicate name (case
57829088Smarkm			 * insensitive) add it to the list.
57929088Smarkm			 */
58029088Smarkm			if (n || (cp - cp2 > 41))
58129088Smarkm				;
58229088Smarkm			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
58329088Smarkm				*argv = cp2;
58429088Smarkm			else if (is_unique(cp2, argv+1, argvp))
58529088Smarkm				*argvp++ = cp2;
58629088Smarkm			if (c == ':')
58729088Smarkm				break;
58829088Smarkm			/*
58929088Smarkm			 * Skip multiple delimiters. Reset cp2 to
59029088Smarkm			 * the beginning of the next name. Reset n,
59129088Smarkm			 * the flag for names with spaces.
59229088Smarkm			 */
59329088Smarkm			while ((c = *cp) == '|')
59429088Smarkm				cp++;
59529088Smarkm			cp2 = cp;
59629088Smarkm			n = 0;
59729088Smarkm		}
59829088Smarkm		/*
59929088Smarkm		 * Skip entries with spaces or non-ascii values.
60029088Smarkm		 * Convert lower case letters to upper case.
60129088Smarkm		 */
60229088Smarkm		if ((c == ' ') || !isascii(c))
60329088Smarkm			n = 1;
60429088Smarkm		else if (islower(c))
60529088Smarkm			*cp = toupper(c);
60629088Smarkm	}
60729088Smarkm
60829088Smarkm	/*
60929088Smarkm	 * Check for an old V6 2 character name.  If the second
61029088Smarkm	 * name points to the beginning of the buffer, and is
61129088Smarkm	 * only 2 characters long, move it to the end of the array.
61229088Smarkm	 */
61329088Smarkm	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
61429088Smarkm		--argvp;
61529088Smarkm		for (avt = &argv[1]; avt < argvp; avt++)
61629088Smarkm			*avt = *(avt+1);
61729088Smarkm		*argvp++ = buf;
61829088Smarkm	}
61929088Smarkm
62029088Smarkm	/*
62129088Smarkm	 * Duplicate last name, for TTYPE option, and null
62229088Smarkm	 * terminate the array.  If we didn't find a match on
62329088Smarkm	 * our terminal name, put that name at the beginning.
62429088Smarkm	 */
62529088Smarkm	cp = *(argvp-1);
62629088Smarkm	*argvp++ = cp;
62729088Smarkm	*argvp = 0;
62829088Smarkm
62929088Smarkm	if (*argv == 0) {
63029088Smarkm		if (name)
63129088Smarkm			*argv = name;
63229088Smarkm		else {
63329088Smarkm			--argvp;
63429088Smarkm			for (avt = argv; avt < argvp; avt++)
63529088Smarkm				*avt = *(avt+1);
63629088Smarkm		}
63729088Smarkm	}
63829088Smarkm	if (*argv)
63987139Smarkm		return((const char **)argv);
64029088Smarkm	else
64129088Smarkm		return(unknown);
64229088Smarkm}
64329088Smarkm
64487139Smarkmstatic int
64587139Smarkmis_unique(char *name, char **as, char **ae)
64629088Smarkm{
64787139Smarkm	char **ap;
64887139Smarkm	int n;
64929088Smarkm
65029088Smarkm	n = strlen(name) + 1;
65129088Smarkm	for (ap = as; ap < ae; ap++)
65229088Smarkm		if (strncasecmp(*ap, name, n) == 0)
65329088Smarkm			return(0);
65429088Smarkm	return (1);
65529088Smarkm}
65629088Smarkm
65729088Smarkm#ifdef	TERMCAP
65829088Smarkmchar termbuf[1024];
65929088Smarkm
66087139Smarkm/*ARGSUSED*/
66187139Smarkmstatic int
66287139Smarkmsetupterm(char *tname, int fd, int *errp)
66329088Smarkm{
66429088Smarkm	if (tgetent(termbuf, tname) == 1) {
66529088Smarkm		termbuf[1023] = '\0';
66629088Smarkm		if (errp)
66729088Smarkm			*errp = 1;
66829088Smarkm		return(0);
66929088Smarkm	}
67029088Smarkm	if (errp)
67129088Smarkm		*errp = 0;
67229088Smarkm	return(-1);
67329088Smarkm}
67429088Smarkm#else
67529088Smarkm#define	termbuf	ttytype
67629088Smarkmextern char ttytype[];
67729088Smarkm#endif
67829088Smarkm
67929088Smarkmint resettermname = 1;
68029088Smarkm
68187139Smarkmstatic const char *
68287139Smarkmgettermname(void)
68329088Smarkm{
68429088Smarkm	char *tname;
68587139Smarkm	static const char **tnamep = 0;
68687139Smarkm	static const char **next;
68729088Smarkm	int err;
68829088Smarkm
68929088Smarkm	if (resettermname) {
69029088Smarkm		resettermname = 0;
69129088Smarkm		if (tnamep && tnamep != unknown)
69229088Smarkm			free(tnamep);
69387139Smarkm		if ((tname = env_getvalue("TERM")) &&
69429088Smarkm				(setupterm(tname, 1, &err) == 0)) {
69529088Smarkm			tnamep = mklist(termbuf, tname);
69629088Smarkm		} else {
69781965Smarkm			if (tname && (strlen(tname) <= 40)) {
69829088Smarkm				unknown[0] = tname;
69929088Smarkm				upcase(tname);
70029088Smarkm			} else
70129088Smarkm				unknown[0] = name_unknown;
70229088Smarkm			tnamep = unknown;
70329088Smarkm		}
70429088Smarkm		next = tnamep;
70529088Smarkm	}
70629088Smarkm	if (*next == 0)
70729088Smarkm		next = tnamep;
70829088Smarkm	return(*next++);
70929088Smarkm}
71029088Smarkm/*
71129088Smarkm * suboption()
71229088Smarkm *
71329088Smarkm *	Look at the sub-option buffer, and try to be helpful to the other
71429088Smarkm * side.
71529088Smarkm *
71629088Smarkm *	Currently we recognize:
71729088Smarkm *
71829088Smarkm *		Terminal type, send request.
71929088Smarkm *		Terminal speed (send request).
72029088Smarkm *		Local flow control (is request).
72129088Smarkm *		Linemode
72229088Smarkm */
72329088Smarkm
72487139Smarkmstatic void
72587139Smarkmsuboption(void)
72629088Smarkm{
72729088Smarkm    unsigned char subchar;
72829088Smarkm
72929088Smarkm    printsub('<', subbuffer, SB_LEN()+2);
73029088Smarkm    switch (subchar = SB_GET()) {
73129088Smarkm    case TELOPT_TTYPE:
73229088Smarkm	if (my_want_state_is_wont(TELOPT_TTYPE))
73329088Smarkm	    return;
73429088Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
73529088Smarkm	    return;
73629088Smarkm	} else {
73787139Smarkm	    const char *name;
73829088Smarkm	    unsigned char temp[50];
73929088Smarkm	    int len;
74029088Smarkm
74129088Smarkm	    name = gettermname();
74229088Smarkm	    len = strlen(name) + 4 + 2;
74329088Smarkm	    if (len < NETROOM()) {
74487139Smarkm		sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
74529088Smarkm				TELQUAL_IS, name, IAC, SE);
74629088Smarkm		ring_supply_data(&netoring, temp, len);
74729088Smarkm		printsub('>', &temp[2], len-2);
74829088Smarkm	    } else {
74929088Smarkm		ExitString("No room in buffer for terminal type.\n", 1);
75029088Smarkm		/*NOTREACHED*/
75129088Smarkm	    }
75229088Smarkm	}
75329088Smarkm	break;
75429088Smarkm    case TELOPT_TSPEED:
75529088Smarkm	if (my_want_state_is_wont(TELOPT_TSPEED))
75629088Smarkm	    return;
75729088Smarkm	if (SB_EOF())
75829088Smarkm	    return;
75929088Smarkm	if (SB_GET() == TELQUAL_SEND) {
76029088Smarkm	    long ospeed, ispeed;
76129088Smarkm	    unsigned char temp[50];
76229088Smarkm	    int len;
76329088Smarkm
76429088Smarkm	    TerminalSpeeds(&ispeed, &ospeed);
76529088Smarkm
76629181Smarkm	    sprintf((char *)temp, "%c%c%c%c%ld,%ld%c%c", IAC, SB, TELOPT_TSPEED,
76729088Smarkm		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
76829088Smarkm	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
76929088Smarkm
77029088Smarkm	    if (len < NETROOM()) {
77129088Smarkm		ring_supply_data(&netoring, temp, len);
77229088Smarkm		printsub('>', temp+2, len - 2);
77329088Smarkm	    }
77429088Smarkm/*@*/	    else printf("lm_will: not enough room in buffer\n");
77529088Smarkm	}
77629088Smarkm	break;
77729088Smarkm    case TELOPT_LFLOW:
77829088Smarkm	if (my_want_state_is_wont(TELOPT_LFLOW))
77929088Smarkm	    return;
78029088Smarkm	if (SB_EOF())
78129088Smarkm	    return;
78229088Smarkm	switch(SB_GET()) {
78329088Smarkm	case LFLOW_RESTART_ANY:
78429088Smarkm	    restartany = 1;
78529088Smarkm	    break;
78629088Smarkm	case LFLOW_RESTART_XON:
78729088Smarkm	    restartany = 0;
78829088Smarkm	    break;
78929088Smarkm	case LFLOW_ON:
79029088Smarkm	    localflow = 1;
79129088Smarkm	    break;
79229088Smarkm	case LFLOW_OFF:
79329088Smarkm	    localflow = 0;
79429088Smarkm	    break;
79529088Smarkm	default:
79629088Smarkm	    return;
79729088Smarkm	}
79829088Smarkm	setcommandmode();
79929088Smarkm	setconnmode(0);
80029088Smarkm	break;
80129088Smarkm
80229088Smarkm    case TELOPT_LINEMODE:
80329088Smarkm	if (my_want_state_is_wont(TELOPT_LINEMODE))
80429088Smarkm	    return;
80529088Smarkm	if (SB_EOF())
80629088Smarkm	    return;
80729088Smarkm	switch (SB_GET()) {
80829088Smarkm	case WILL:
80929088Smarkm	    lm_will(subpointer, SB_LEN());
81029088Smarkm	    break;
81129088Smarkm	case WONT:
81229088Smarkm	    lm_wont(subpointer, SB_LEN());
81329088Smarkm	    break;
81429088Smarkm	case DO:
81529088Smarkm	    lm_do(subpointer, SB_LEN());
81629088Smarkm	    break;
81729088Smarkm	case DONT:
81829088Smarkm	    lm_dont(subpointer, SB_LEN());
81929088Smarkm	    break;
82029088Smarkm	case LM_SLC:
82129088Smarkm	    slc(subpointer, SB_LEN());
82229088Smarkm	    break;
82329088Smarkm	case LM_MODE:
82429088Smarkm	    lm_mode(subpointer, SB_LEN(), 0);
82529088Smarkm	    break;
82629088Smarkm	default:
82729088Smarkm	    break;
82829088Smarkm	}
82929088Smarkm	break;
83029088Smarkm
83129088Smarkm#ifdef	OLD_ENVIRON
83229088Smarkm    case TELOPT_OLD_ENVIRON:
83329088Smarkm#endif
83429088Smarkm    case TELOPT_NEW_ENVIRON:
83529088Smarkm	if (SB_EOF())
83629088Smarkm	    return;
83729088Smarkm	switch(SB_PEEK()) {
83829088Smarkm	case TELQUAL_IS:
83929088Smarkm	case TELQUAL_INFO:
84029088Smarkm	    if (my_want_state_is_dont(subchar))
84129088Smarkm		return;
84229088Smarkm	    break;
84329088Smarkm	case TELQUAL_SEND:
84429088Smarkm	    if (my_want_state_is_wont(subchar)) {
84529088Smarkm		return;
84629088Smarkm	    }
84729088Smarkm	    break;
84829088Smarkm	default:
84929088Smarkm	    return;
85029088Smarkm	}
85129088Smarkm	env_opt(subpointer, SB_LEN());
85229088Smarkm	break;
85329088Smarkm
85429088Smarkm    case TELOPT_XDISPLOC:
85529088Smarkm	if (my_want_state_is_wont(TELOPT_XDISPLOC))
85629088Smarkm	    return;
85729088Smarkm	if (SB_EOF())
85829088Smarkm	    return;
85929088Smarkm	if (SB_GET() == TELQUAL_SEND) {
86029088Smarkm	    unsigned char temp[50], *dp;
86129088Smarkm	    int len;
86229088Smarkm
86387139Smarkm	    if ((dp = env_getvalue("DISPLAY")) == NULL ||
86467827Skris		strlen(dp) > sizeof(temp) - 7) {
86529088Smarkm		/*
86629088Smarkm		 * Something happened, we no longer have a DISPLAY
86767827Skris		 * variable.  Or it is too long.  So, turn off the option.
86829088Smarkm		 */
86929088Smarkm		send_wont(TELOPT_XDISPLOC, 1);
87029088Smarkm		break;
87129088Smarkm	    }
87287139Smarkm	    snprintf(temp, sizeof(temp), "%c%c%c%c%s%c%c", IAC, SB,
87367827Skris		    TELOPT_XDISPLOC, TELQUAL_IS, dp, IAC, SE);
87429088Smarkm	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
87529088Smarkm
87629088Smarkm	    if (len < NETROOM()) {
87729088Smarkm		ring_supply_data(&netoring, temp, len);
87829088Smarkm		printsub('>', temp+2, len - 2);
87929088Smarkm	    }
88029088Smarkm/*@*/	    else printf("lm_will: not enough room in buffer\n");
88129088Smarkm	}
88229088Smarkm	break;
88329088Smarkm
88487139Smarkm#ifdef	AUTHENTICATION
88529088Smarkm	case TELOPT_AUTHENTICATION: {
88629088Smarkm		if (!autologin)
88729088Smarkm			break;
88829088Smarkm		if (SB_EOF())
88929088Smarkm			return;
89029088Smarkm		switch(SB_GET()) {
89129088Smarkm		case TELQUAL_IS:
89229088Smarkm			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
89329088Smarkm				return;
89429088Smarkm			auth_is(subpointer, SB_LEN());
89529088Smarkm			break;
89629088Smarkm		case TELQUAL_SEND:
89729088Smarkm			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
89829088Smarkm				return;
89929088Smarkm			auth_send(subpointer, SB_LEN());
90029088Smarkm			break;
90129088Smarkm		case TELQUAL_REPLY:
90229088Smarkm			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
90329088Smarkm				return;
90429088Smarkm			auth_reply(subpointer, SB_LEN());
90529088Smarkm			break;
90629088Smarkm		case TELQUAL_NAME:
90729088Smarkm			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
90829088Smarkm				return;
90929088Smarkm			auth_name(subpointer, SB_LEN());
91029088Smarkm			break;
91129088Smarkm		}
91229088Smarkm	}
91329088Smarkm	break;
91429088Smarkm#endif
91529088Smarkm#ifdef	ENCRYPTION
91629088Smarkm	case TELOPT_ENCRYPT:
91729088Smarkm		if (SB_EOF())
91829088Smarkm			return;
91929088Smarkm		switch(SB_GET()) {
92029088Smarkm		case ENCRYPT_START:
92129088Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
92229088Smarkm				return;
92329088Smarkm			encrypt_start(subpointer, SB_LEN());
92429088Smarkm			break;
92529088Smarkm		case ENCRYPT_END:
92629088Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
92729088Smarkm				return;
92829088Smarkm			encrypt_end();
92929088Smarkm			break;
93029088Smarkm		case ENCRYPT_SUPPORT:
93129088Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
93229088Smarkm				return;
93329088Smarkm			encrypt_support(subpointer, SB_LEN());
93429088Smarkm			break;
93529088Smarkm		case ENCRYPT_REQSTART:
93629088Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
93729088Smarkm				return;
93829088Smarkm			encrypt_request_start(subpointer, SB_LEN());
93929088Smarkm			break;
94029088Smarkm		case ENCRYPT_REQEND:
94129088Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
94229088Smarkm				return;
94329088Smarkm			/*
94429088Smarkm			 * We can always send an REQEND so that we cannot
94529088Smarkm			 * get stuck encrypting.  We should only get this
94629088Smarkm			 * if we have been able to get in the correct mode
94729088Smarkm			 * anyhow.
94829088Smarkm			 */
94929088Smarkm			encrypt_request_end();
95029088Smarkm			break;
95129088Smarkm		case ENCRYPT_IS:
95229088Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
95329088Smarkm				return;
95429088Smarkm			encrypt_is(subpointer, SB_LEN());
95529088Smarkm			break;
95629088Smarkm		case ENCRYPT_REPLY:
95729088Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
95829088Smarkm				return;
95929088Smarkm			encrypt_reply(subpointer, SB_LEN());
96029088Smarkm			break;
96129088Smarkm		case ENCRYPT_ENC_KEYID:
96229088Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
96329088Smarkm				return;
96429088Smarkm			encrypt_enc_keyid(subpointer, SB_LEN());
96529088Smarkm			break;
96629088Smarkm		case ENCRYPT_DEC_KEYID:
96729088Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
96829088Smarkm				return;
96929088Smarkm			encrypt_dec_keyid(subpointer, SB_LEN());
97029088Smarkm			break;
97129088Smarkm		default:
97229088Smarkm			break;
97329088Smarkm		}
97429088Smarkm		break;
97529088Smarkm#endif	/* ENCRYPTION */
97629088Smarkm    default:
97729088Smarkm	break;
97829088Smarkm    }
97929088Smarkm}
98029088Smarkm
98129088Smarkmstatic unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
98229088Smarkm
98387139Smarkmvoid
98487139Smarkmlm_will(unsigned char *cmd, int len)
98529088Smarkm{
98629088Smarkm    if (len < 1) {
98729088Smarkm/*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
98829088Smarkm	return;
98929088Smarkm    }
99029088Smarkm    switch(cmd[0]) {
99129088Smarkm    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
99229088Smarkm    default:
99329088Smarkm	str_lm[3] = DONT;
99429088Smarkm	str_lm[4] = cmd[0];
99587139Smarkm	if (NETROOM() > (int)sizeof(str_lm)) {
99629088Smarkm	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
99729088Smarkm	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
99829088Smarkm	}
99929088Smarkm/*@*/	else printf("lm_will: not enough room in buffer\n");
100029088Smarkm	break;
100129088Smarkm    }
100229088Smarkm}
100329088Smarkm
100487139Smarkmvoid
100587139Smarkmlm_wont(unsigned char *cmd, int len)
100629088Smarkm{
100729088Smarkm    if (len < 1) {
100829088Smarkm/*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
100929088Smarkm	return;
101029088Smarkm    }
101129088Smarkm    switch(cmd[0]) {
101229088Smarkm    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
101329088Smarkm    default:
101429088Smarkm	/* We are always DONT, so don't respond */
101529088Smarkm	return;
101629088Smarkm    }
101729088Smarkm}
101829088Smarkm
101987139Smarkmvoid
102087139Smarkmlm_do(unsigned char *cmd, int len)
102129088Smarkm{
102229088Smarkm    if (len < 1) {
102329088Smarkm/*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
102429088Smarkm	return;
102529088Smarkm    }
102629088Smarkm    switch(cmd[0]) {
102729088Smarkm    case LM_FORWARDMASK:
102829088Smarkm    default:
102929088Smarkm	str_lm[3] = WONT;
103029088Smarkm	str_lm[4] = cmd[0];
103187139Smarkm	if (NETROOM() > (int)sizeof(str_lm)) {
103229088Smarkm	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
103329088Smarkm	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
103429088Smarkm	}
103529088Smarkm/*@*/	else printf("lm_do: not enough room in buffer\n");
103629088Smarkm	break;
103729088Smarkm    }
103829088Smarkm}
103929088Smarkm
104087139Smarkmvoid
104187139Smarkmlm_dont(unsigned char *cmd, int len)
104229088Smarkm{
104329088Smarkm    if (len < 1) {
104429088Smarkm/*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
104529088Smarkm	return;
104629088Smarkm    }
104729088Smarkm    switch(cmd[0]) {
104829088Smarkm    case LM_FORWARDMASK:
104929088Smarkm    default:
105029088Smarkm	/* we are always WONT, so don't respond */
105129088Smarkm	break;
105229088Smarkm    }
105329088Smarkm}
105429088Smarkm
105529088Smarkmstatic unsigned char str_lm_mode[] = {
105629088Smarkm	IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
105729088Smarkm};
105829088Smarkm
105987139Smarkmvoid
106087139Smarkmlm_mode(unsigned char *cmd, int len, int init)
106129088Smarkm{
106229088Smarkm	if (len != 1)
106329088Smarkm		return;
106429088Smarkm	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
106529088Smarkm		return;
106629088Smarkm	if (*cmd&MODE_ACK)
106729088Smarkm		return;
106829088Smarkm	linemode = *cmd&(MODE_MASK&~MODE_ACK);
106929088Smarkm	str_lm_mode[4] = linemode;
107029088Smarkm	if (!init)
107129088Smarkm	    str_lm_mode[4] |= MODE_ACK;
107287139Smarkm	if (NETROOM() > (int)sizeof(str_lm_mode)) {
107329088Smarkm	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
107429088Smarkm	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
107529088Smarkm	}
107629088Smarkm/*@*/	else printf("lm_mode: not enough room in buffer\n");
107729088Smarkm	setconnmode(0);	/* set changed mode */
107829088Smarkm}
107929088Smarkm
108029088Smarkm
108129088Smarkm
108229088Smarkm/*
108329088Smarkm * slc()
108429088Smarkm * Handle special character suboption of LINEMODE.
108529088Smarkm */
108629088Smarkm
108729088Smarkmstruct spc {
108829088Smarkm	cc_t val;
108929088Smarkm	cc_t *valp;
109029088Smarkm	char flags;	/* Current flags & level */
109129088Smarkm	char mylevel;	/* Maximum level & flags */
109229088Smarkm} spc_data[NSLC+1];
109329088Smarkm
109429088Smarkm#define SLC_IMPORT	0
109529088Smarkm#define	SLC_EXPORT	1
109629088Smarkm#define SLC_RVALUE	2
109729088Smarkmstatic int slc_mode = SLC_EXPORT;
109829088Smarkm
109987139Smarkmvoid
110087139Smarkmslc_init(void)
110129088Smarkm{
110287139Smarkm	struct spc *spcp;
110329088Smarkm
110429088Smarkm	localchars = 1;
110529088Smarkm	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
110629088Smarkm		spcp->val = 0;
110729088Smarkm		spcp->valp = 0;
110829088Smarkm		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
110929088Smarkm	}
111029088Smarkm
111129088Smarkm#define	initfunc(func, flags) { \
111229088Smarkm					spcp = &spc_data[func]; \
111329181Smarkm					if ((spcp->valp = tcval(func))) { \
111429088Smarkm					    spcp->val = *spcp->valp; \
111529088Smarkm					    spcp->mylevel = SLC_VARIABLE|flags; \
111629088Smarkm					} else { \
111729088Smarkm					    spcp->val = 0; \
111829088Smarkm					    spcp->mylevel = SLC_DEFAULT; \
111929088Smarkm					} \
112029088Smarkm				    }
112129088Smarkm
112229088Smarkm	initfunc(SLC_SYNCH, 0);
112329088Smarkm	/* No BRK */
112429088Smarkm	initfunc(SLC_AO, 0);
112529088Smarkm	initfunc(SLC_AYT, 0);
112629088Smarkm	/* No EOR */
112729088Smarkm	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
112829088Smarkm	initfunc(SLC_EOF, 0);
112929088Smarkm#ifndef	SYSV_TERMIO
113029088Smarkm	initfunc(SLC_SUSP, SLC_FLUSHIN);
113129088Smarkm#endif
113229088Smarkm	initfunc(SLC_EC, 0);
113329088Smarkm	initfunc(SLC_EL, 0);
113429088Smarkm#ifndef	SYSV_TERMIO
113529088Smarkm	initfunc(SLC_EW, 0);
113629088Smarkm	initfunc(SLC_RP, 0);
113729088Smarkm	initfunc(SLC_LNEXT, 0);
113829088Smarkm#endif
113929088Smarkm	initfunc(SLC_XON, 0);
114029088Smarkm	initfunc(SLC_XOFF, 0);
114129088Smarkm#ifdef	SYSV_TERMIO
114229088Smarkm	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
114329088Smarkm	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
114429088Smarkm#endif
114529088Smarkm	initfunc(SLC_FORW1, 0);
114629088Smarkm#ifdef	USE_TERMIO
114729088Smarkm	initfunc(SLC_FORW2, 0);
114829088Smarkm	/* No FORW2 */
114929088Smarkm#endif
115029088Smarkm
115129088Smarkm	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
115229088Smarkm#undef	initfunc
115329088Smarkm
115429088Smarkm	if (slc_mode == SLC_EXPORT)
115529088Smarkm		slc_export();
115629088Smarkm	else
115729088Smarkm		slc_import(1);
115829088Smarkm
115929088Smarkm}
116029088Smarkm
116187139Smarkmvoid
116287139Smarkmslcstate(void)
116329088Smarkm{
116429088Smarkm    printf("Special characters are %s values\n",
116529088Smarkm		slc_mode == SLC_IMPORT ? "remote default" :
116629088Smarkm		slc_mode == SLC_EXPORT ? "local" :
116729088Smarkm					 "remote");
116829088Smarkm}
116929088Smarkm
117087139Smarkmvoid
117187139Smarkmslc_mode_export(void)
117229088Smarkm{
117329088Smarkm    slc_mode = SLC_EXPORT;
117429088Smarkm    if (my_state_is_will(TELOPT_LINEMODE))
117529088Smarkm	slc_export();
117629088Smarkm}
117729088Smarkm
117887139Smarkmvoid
117987139Smarkmslc_mode_import(int def)
118029088Smarkm{
118129088Smarkm    slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
118229088Smarkm    if (my_state_is_will(TELOPT_LINEMODE))
118329088Smarkm	slc_import(def);
118429088Smarkm}
118529088Smarkm
118629088Smarkmunsigned char slc_import_val[] = {
118729088Smarkm	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
118829088Smarkm};
118929088Smarkmunsigned char slc_import_def[] = {
119029088Smarkm	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
119129088Smarkm};
119229088Smarkm
119387139Smarkmvoid
119487139Smarkmslc_import(int def)
119529088Smarkm{
119687139Smarkm    if (NETROOM() > (int)sizeof(slc_import_val)) {
119729088Smarkm	if (def) {
119829088Smarkm	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
119929088Smarkm	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
120029088Smarkm	} else {
120129088Smarkm	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
120229088Smarkm	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
120329088Smarkm	}
120429088Smarkm    }
120529088Smarkm/*@*/ else printf("slc_import: not enough room\n");
120629088Smarkm}
120729088Smarkm
120887139Smarkmvoid
120987139Smarkmslc_export(void)
121029088Smarkm{
121187139Smarkm    struct spc *spcp;
121229088Smarkm
121329088Smarkm    TerminalDefaultChars();
121429088Smarkm
121529088Smarkm    slc_start_reply();
121629088Smarkm    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
121729088Smarkm	if (spcp->mylevel != SLC_NOSUPPORT) {
121829088Smarkm	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
121929088Smarkm		spcp->flags = SLC_NOSUPPORT;
122029088Smarkm	    else
122129088Smarkm		spcp->flags = spcp->mylevel;
122229088Smarkm	    if (spcp->valp)
122329088Smarkm		spcp->val = *spcp->valp;
122429088Smarkm	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
122529088Smarkm	}
122629088Smarkm    }
122729088Smarkm    slc_end_reply();
122829088Smarkm    (void)slc_update();
122929088Smarkm    setconnmode(1);	/* Make sure the character values are set */
123029088Smarkm}
123129088Smarkm
123287139Smarkmvoid
123387139Smarkmslc(unsigned char *cp, int len)
123429088Smarkm{
123587139Smarkm	struct spc *spcp;
123687139Smarkm	int func,level;
123729088Smarkm
123829088Smarkm	slc_start_reply();
123929088Smarkm
124029088Smarkm	for (; len >= 3; len -=3, cp +=3) {
124129088Smarkm
124229088Smarkm		func = cp[SLC_FUNC];
124329088Smarkm
124429088Smarkm		if (func == 0) {
124529088Smarkm			/*
124629088Smarkm			 * Client side: always ignore 0 function.
124729088Smarkm			 */
124829088Smarkm			continue;
124929088Smarkm		}
125029088Smarkm		if (func > NSLC) {
125129088Smarkm			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
125229088Smarkm				slc_add_reply(func, SLC_NOSUPPORT, 0);
125329088Smarkm			continue;
125429088Smarkm		}
125529088Smarkm
125629088Smarkm		spcp = &spc_data[func];
125729088Smarkm
125829088Smarkm		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
125929088Smarkm
126029088Smarkm		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
126129088Smarkm		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
126229088Smarkm			continue;
126329088Smarkm		}
126429088Smarkm
126529088Smarkm		if (level == (SLC_DEFAULT|SLC_ACK)) {
126629088Smarkm			/*
126729088Smarkm			 * This is an error condition, the SLC_ACK
126829088Smarkm			 * bit should never be set for the SLC_DEFAULT
126929088Smarkm			 * level.  Our best guess to recover is to
127029088Smarkm			 * ignore the SLC_ACK bit.
127129088Smarkm			 */
127229088Smarkm			cp[SLC_FLAGS] &= ~SLC_ACK;
127329088Smarkm		}
127429088Smarkm
127529088Smarkm		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
127629088Smarkm			spcp->val = (cc_t)cp[SLC_VALUE];
127729088Smarkm			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
127829088Smarkm			continue;
127929088Smarkm		}
128029088Smarkm
128129088Smarkm		level &= ~SLC_ACK;
128229088Smarkm
128329088Smarkm		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
128429088Smarkm			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
128529088Smarkm			spcp->val = (cc_t)cp[SLC_VALUE];
128629088Smarkm		}
128729088Smarkm		if (level == SLC_DEFAULT) {
128829088Smarkm			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
128929088Smarkm				spcp->flags = spcp->mylevel;
129029088Smarkm			else
129129088Smarkm				spcp->flags = SLC_NOSUPPORT;
129229088Smarkm		}
129329088Smarkm		slc_add_reply(func, spcp->flags, spcp->val);
129429088Smarkm	}
129529088Smarkm	slc_end_reply();
129629088Smarkm	if (slc_update())
129729088Smarkm		setconnmode(1);	/* set the  new character values */
129829088Smarkm}
129929088Smarkm
130087139Smarkmvoid
130187139Smarkmslc_check(void)
130229088Smarkm{
130387139Smarkm    struct spc *spcp;
130429088Smarkm
130529088Smarkm    slc_start_reply();
130629088Smarkm    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
130729088Smarkm	if (spcp->valp && spcp->val != *spcp->valp) {
130829088Smarkm	    spcp->val = *spcp->valp;
130929088Smarkm	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
131029088Smarkm		spcp->flags = SLC_NOSUPPORT;
131129088Smarkm	    else
131229088Smarkm		spcp->flags = spcp->mylevel;
131329088Smarkm	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
131429088Smarkm	}
131529088Smarkm    }
131629088Smarkm    slc_end_reply();
131729088Smarkm    setconnmode(1);
131829088Smarkm}
131929088Smarkm
132029088Smarkmunsigned char slc_reply[128];
132129088Smarkmunsigned char *slc_replyp;
132229088Smarkm
132387139Smarkmvoid
132487139Smarkmslc_start_reply(void)
132529088Smarkm{
132629088Smarkm	slc_replyp = slc_reply;
132729088Smarkm	*slc_replyp++ = IAC;
132829088Smarkm	*slc_replyp++ = SB;
132929088Smarkm	*slc_replyp++ = TELOPT_LINEMODE;
133029088Smarkm	*slc_replyp++ = LM_SLC;
133129088Smarkm}
133229088Smarkm
133387139Smarkmvoid
133487139Smarkmslc_add_reply(unsigned char func, unsigned char flags, cc_t value)
133529088Smarkm{
133629088Smarkm	if ((*slc_replyp++ = func) == IAC)
133729088Smarkm		*slc_replyp++ = IAC;
133829088Smarkm	if ((*slc_replyp++ = flags) == IAC)
133929088Smarkm		*slc_replyp++ = IAC;
134029088Smarkm	if ((*slc_replyp++ = (unsigned char)value) == IAC)
134129088Smarkm		*slc_replyp++ = IAC;
134229088Smarkm}
134329088Smarkm
134487139Smarkmvoid
134587139Smarkmslc_end_reply(void)
134629088Smarkm{
134787139Smarkm    int len;
134829088Smarkm
134929088Smarkm    *slc_replyp++ = IAC;
135029088Smarkm    *slc_replyp++ = SE;
135129088Smarkm    len = slc_replyp - slc_reply;
135229088Smarkm    if (len <= 6)
135329088Smarkm	return;
135429088Smarkm    if (NETROOM() > len) {
135529088Smarkm	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
135629088Smarkm	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
135729088Smarkm    }
135829088Smarkm/*@*/else printf("slc_end_reply: not enough room\n");
135929088Smarkm}
136029088Smarkm
136187139Smarkmint
136287139Smarkmslc_update(void)
136329088Smarkm{
136487139Smarkm	struct spc *spcp;
136529088Smarkm	int need_update = 0;
136629088Smarkm
136729088Smarkm	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
136829088Smarkm		if (!(spcp->flags&SLC_ACK))
136929088Smarkm			continue;
137029088Smarkm		spcp->flags &= ~SLC_ACK;
137129088Smarkm		if (spcp->valp && (*spcp->valp != spcp->val)) {
137229088Smarkm			*spcp->valp = spcp->val;
137329088Smarkm			need_update = 1;
137429088Smarkm		}
137529088Smarkm	}
137629088Smarkm	return(need_update);
137729088Smarkm}
137829088Smarkm
137929088Smarkm#ifdef	OLD_ENVIRON
138029088Smarkm# ifdef	ENV_HACK
138129088Smarkm/*
138229088Smarkm * Earlier version of telnet/telnetd from the BSD code had
138329088Smarkm * the definitions of VALUE and VAR reversed.  To ensure
138429088Smarkm * maximum interoperability, we assume that the server is
138529088Smarkm * an older BSD server, until proven otherwise.  The newer
138629088Smarkm * BSD servers should be able to handle either definition,
138729088Smarkm * so it is better to use the wrong values if we don't
138829088Smarkm * know what type of server it is.
138929088Smarkm */
139029088Smarkmint env_auto = 1;
139129088Smarkmint old_env_var = OLD_ENV_VAR;
139229088Smarkmint old_env_value = OLD_ENV_VALUE;
139329088Smarkm# else
139429088Smarkm#  define old_env_var OLD_ENV_VAR
139529088Smarkm#  define old_env_value OLD_ENV_VALUE
139629088Smarkm# endif
139729088Smarkm#endif
139829088Smarkm
139987139Smarkmvoid
140087139Smarkmenv_opt(unsigned char *buf, int len)
140129088Smarkm{
140287139Smarkm	unsigned char *ep = 0, *epc = 0;
140387139Smarkm	int i;
140429088Smarkm
140529088Smarkm	switch(buf[0]&0xff) {
140629088Smarkm	case TELQUAL_SEND:
140729088Smarkm		env_opt_start();
140829088Smarkm		if (len == 1) {
140929088Smarkm			env_opt_add(NULL);
141029088Smarkm		} else for (i = 1; i < len; i++) {
141129088Smarkm			switch (buf[i]&0xff) {
141229088Smarkm#ifdef	OLD_ENVIRON
141329088Smarkm			case OLD_ENV_VAR:
141429088Smarkm# ifdef	ENV_HACK
141529088Smarkm				if (telopt_environ == TELOPT_OLD_ENVIRON
141629088Smarkm				    && env_auto) {
141729088Smarkm					/* Server has the same definitions */
141829088Smarkm					old_env_var = OLD_ENV_VAR;
141929088Smarkm					old_env_value = OLD_ENV_VALUE;
142029088Smarkm				}
1421103955Smarkm				/* FALLTHROUGH */
142229088Smarkm# endif
142329088Smarkm			case OLD_ENV_VALUE:
142429088Smarkm				/*
142529088Smarkm				 * Although OLD_ENV_VALUE is not legal, we will
142629088Smarkm				 * still recognize it, just in case it is an
142729088Smarkm				 * old server that has VAR & VALUE mixed up...
142829088Smarkm				 */
1429103955Smarkm				/* FALLTHROUGH */
143029088Smarkm#else
143129088Smarkm			case NEW_ENV_VAR:
143229088Smarkm#endif
143329088Smarkm			case ENV_USERVAR:
143429088Smarkm				if (ep) {
143529088Smarkm					*epc = 0;
143629088Smarkm					env_opt_add(ep);
143729088Smarkm				}
143829088Smarkm				ep = epc = &buf[i+1];
143929088Smarkm				break;
144029088Smarkm			case ENV_ESC:
144129088Smarkm				i++;
1442103955Smarkm				/*FALLTHROUGH*/
144329088Smarkm			default:
144429088Smarkm				if (epc)
144529088Smarkm					*epc++ = buf[i];
144629088Smarkm				break;
144729088Smarkm			}
144829088Smarkm		}
144929088Smarkm		if (ep) {
145029088Smarkm			*epc = 0;
145129088Smarkm			env_opt_add(ep);
145229088Smarkm		}
145329088Smarkm		env_opt_end(1);
145429088Smarkm		break;
145529088Smarkm
145629088Smarkm	case TELQUAL_IS:
145729088Smarkm	case TELQUAL_INFO:
145829088Smarkm		/* Ignore for now.  We shouldn't get it anyway. */
145929088Smarkm		break;
146029088Smarkm
146129088Smarkm	default:
146229088Smarkm		break;
146329088Smarkm	}
146429088Smarkm}
146529088Smarkm
146629088Smarkm#define	OPT_REPLY_SIZE	256
146729088Smarkmunsigned char *opt_reply;
146829088Smarkmunsigned char *opt_replyp;
146929088Smarkmunsigned char *opt_replyend;
147029088Smarkm
147187139Smarkmvoid
147287139Smarkmenv_opt_start(void)
147329088Smarkm{
147429088Smarkm	if (opt_reply)
147529088Smarkm		opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
147629088Smarkm	else
147729088Smarkm		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
147829088Smarkm	if (opt_reply == NULL) {
147929088Smarkm/*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
148029088Smarkm		opt_reply = opt_replyp = opt_replyend = NULL;
148129088Smarkm		return;
148229088Smarkm	}
148329088Smarkm	opt_replyp = opt_reply;
148429088Smarkm	opt_replyend = opt_reply + OPT_REPLY_SIZE;
148529088Smarkm	*opt_replyp++ = IAC;
148629088Smarkm	*opt_replyp++ = SB;
148729088Smarkm	*opt_replyp++ = telopt_environ;
148829088Smarkm	*opt_replyp++ = TELQUAL_IS;
148929088Smarkm}
149029088Smarkm
149187139Smarkmvoid
149287139Smarkmenv_opt_start_info(void)
149329088Smarkm{
149429088Smarkm	env_opt_start();
149529088Smarkm	if (opt_replyp)
149629088Smarkm	    opt_replyp[-1] = TELQUAL_INFO;
149729088Smarkm}
149829088Smarkm
149987139Smarkmvoid
150087139Smarkmenv_opt_add(unsigned char *ep)
150129088Smarkm{
150287139Smarkm	unsigned char *vp, c;
150329088Smarkm
150429088Smarkm	if (opt_reply == NULL)		/*XXX*/
150529088Smarkm		return;			/*XXX*/
150629088Smarkm
150729088Smarkm	if (ep == NULL || *ep == '\0') {
150829088Smarkm		/* Send user defined variables first. */
150929088Smarkm		env_default(1, 0);
151029181Smarkm		while ((ep = env_default(0, 0)))
151129088Smarkm			env_opt_add(ep);
151229088Smarkm
151329088Smarkm		/* Now add the list of well know variables.  */
151429088Smarkm		env_default(1, 1);
151529181Smarkm		while ((ep = env_default(0, 1)))
151629088Smarkm			env_opt_add(ep);
151729088Smarkm		return;
151829088Smarkm	}
151929088Smarkm	vp = env_getvalue(ep);
152029088Smarkm	if (opt_replyp + (vp ? strlen((char *)vp) : 0) +
152129088Smarkm				strlen((char *)ep) + 6 > opt_replyend)
152229088Smarkm	{
152387139Smarkm		int len;
152429088Smarkm		opt_replyend += OPT_REPLY_SIZE;
152529088Smarkm		len = opt_replyend - opt_reply;
152629088Smarkm		opt_reply = (unsigned char *)realloc(opt_reply, len);
152729088Smarkm		if (opt_reply == NULL) {
152829088Smarkm/*@*/			printf("env_opt_add: realloc() failed!!!\n");
152929088Smarkm			opt_reply = opt_replyp = opt_replyend = NULL;
153029088Smarkm			return;
153129088Smarkm		}
153229088Smarkm		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
153329088Smarkm		opt_replyend = opt_reply + len;
153429088Smarkm	}
153529088Smarkm	if (opt_welldefined(ep))
153629088Smarkm#ifdef	OLD_ENVIRON
153729088Smarkm		if (telopt_environ == TELOPT_OLD_ENVIRON)
153829088Smarkm			*opt_replyp++ = old_env_var;
153929088Smarkm		else
154029088Smarkm#endif
154129088Smarkm			*opt_replyp++ = NEW_ENV_VAR;
154229088Smarkm	else
154329088Smarkm		*opt_replyp++ = ENV_USERVAR;
154429088Smarkm	for (;;) {
154529181Smarkm		while ((c = *ep++)) {
154629088Smarkm			switch(c&0xff) {
154729088Smarkm			case IAC:
154829088Smarkm				*opt_replyp++ = IAC;
154929088Smarkm				break;
155029088Smarkm			case NEW_ENV_VAR:
155129088Smarkm			case NEW_ENV_VALUE:
155229088Smarkm			case ENV_ESC:
155329088Smarkm			case ENV_USERVAR:
155429088Smarkm				*opt_replyp++ = ENV_ESC;
155529088Smarkm				break;
155629088Smarkm			}
155729088Smarkm			*opt_replyp++ = c;
155829088Smarkm		}
155929181Smarkm		if ((ep = vp)) {
156029088Smarkm#ifdef	OLD_ENVIRON
156129088Smarkm			if (telopt_environ == TELOPT_OLD_ENVIRON)
156229088Smarkm				*opt_replyp++ = old_env_value;
156329088Smarkm			else
156429088Smarkm#endif
156529088Smarkm				*opt_replyp++ = NEW_ENV_VALUE;
156629088Smarkm			vp = NULL;
156729088Smarkm		} else
156829088Smarkm			break;
156929088Smarkm	}
157029088Smarkm}
157129088Smarkm
157287139Smarkmint
157387139Smarkmopt_welldefined(const char *ep)
157429088Smarkm{
157529088Smarkm	if ((strcmp(ep, "USER") == 0) ||
157629088Smarkm	    (strcmp(ep, "DISPLAY") == 0) ||
157729088Smarkm	    (strcmp(ep, "PRINTER") == 0) ||
157829088Smarkm	    (strcmp(ep, "SYSTEMTYPE") == 0) ||
157929088Smarkm	    (strcmp(ep, "JOB") == 0) ||
158029088Smarkm	    (strcmp(ep, "ACCT") == 0))
158129088Smarkm		return(1);
158229088Smarkm	return(0);
158329088Smarkm}
158487139Smarkm
158587139Smarkmvoid
158687139Smarkmenv_opt_end(int emptyok)
158729088Smarkm{
158887139Smarkm	int len;
158929088Smarkm
159029088Smarkm	len = opt_replyp - opt_reply + 2;
159129088Smarkm	if (emptyok || len > 6) {
159229088Smarkm		*opt_replyp++ = IAC;
159329088Smarkm		*opt_replyp++ = SE;
159429088Smarkm		if (NETROOM() > len) {
159529088Smarkm			ring_supply_data(&netoring, opt_reply, len);
159629088Smarkm			printsub('>', &opt_reply[2], len - 2);
159729088Smarkm		}
159829088Smarkm/*@*/		else printf("slc_end_reply: not enough room\n");
159929088Smarkm	}
160029088Smarkm	if (opt_reply) {
160129088Smarkm		free(opt_reply);
160229088Smarkm		opt_reply = opt_replyp = opt_replyend = NULL;
160329088Smarkm	}
160429088Smarkm}
160529088Smarkm
160629088Smarkm
160729088Smarkm
160887139Smarkmint
160987139Smarkmtelrcv(void)
161029088Smarkm{
161187139Smarkm    int c;
161287139Smarkm    int scc;
161387139Smarkm    unsigned char *sbp;
161429088Smarkm    int count;
161529088Smarkm    int returnValue = 0;
161629088Smarkm
161729088Smarkm    scc = 0;
161829088Smarkm    count = 0;
161929088Smarkm    while (TTYROOM() > 2) {
162029088Smarkm	if (scc == 0) {
162129088Smarkm	    if (count) {
162229088Smarkm		ring_consumed(&netiring, count);
162329088Smarkm		returnValue = 1;
162429088Smarkm		count = 0;
162529088Smarkm	    }
162629088Smarkm	    sbp = netiring.consume;
162729088Smarkm	    scc = ring_full_consecutive(&netiring);
162829088Smarkm	    if (scc == 0) {
162929088Smarkm		/* No more data coming in */
163029088Smarkm		break;
163129088Smarkm	    }
163229088Smarkm	}
163329088Smarkm
163429088Smarkm	c = *sbp++ & 0xff, scc--; count++;
163529088Smarkm#ifdef	ENCRYPTION
163629088Smarkm	if (decrypt_input)
163729088Smarkm		c = (*decrypt_input)(c);
163829088Smarkm#endif	/* ENCRYPTION */
163929088Smarkm
164029088Smarkm	switch (telrcv_state) {
164129088Smarkm
164229088Smarkm	case TS_CR:
164329088Smarkm	    telrcv_state = TS_DATA;
164429088Smarkm	    if (c == '\0') {
164529088Smarkm		break;	/* Ignore \0 after CR */
164629088Smarkm	    }
164729088Smarkm	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
164829088Smarkm		TTYADD(c);
164929088Smarkm		break;
165029088Smarkm	    }
1651103955Smarkm	    /* FALLTHROUGH */
165229088Smarkm
165329088Smarkm	case TS_DATA:
165429088Smarkm	    if (c == IAC) {
165529088Smarkm		telrcv_state = TS_IAC;
165629088Smarkm		break;
165729088Smarkm	    }
165829088Smarkm		    /*
165929088Smarkm		     * The 'crmod' hack (see following) is needed
166029088Smarkm		     * since we can't * set CRMOD on output only.
166129088Smarkm		     * Machines like MULTICS like to send \r without
166229088Smarkm		     * \n; since we must turn off CRMOD to get proper
166329088Smarkm		     * input, the mapping is done here (sigh).
166429088Smarkm		     */
166529088Smarkm	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
166629088Smarkm		if (scc > 0) {
166729088Smarkm		    c = *sbp&0xff;
166829088Smarkm#ifdef	ENCRYPTION
166929088Smarkm		    if (decrypt_input)
167029088Smarkm			c = (*decrypt_input)(c);
167129088Smarkm#endif	/* ENCRYPTION */
167229088Smarkm		    if (c == 0) {
167329088Smarkm			sbp++, scc--; count++;
167429088Smarkm			/* a "true" CR */
167529088Smarkm			TTYADD('\r');
167629088Smarkm		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
167729088Smarkm					(c == '\n')) {
167829088Smarkm			sbp++, scc--; count++;
167929088Smarkm			TTYADD('\n');
168029088Smarkm		    } else {
168129088Smarkm#ifdef	ENCRYPTION
168229088Smarkm			if (decrypt_input)
168329088Smarkm			    (*decrypt_input)(-1);
168429088Smarkm#endif	/* ENCRYPTION */
168529088Smarkm
168629088Smarkm			TTYADD('\r');
168729088Smarkm			if (crmod) {
168829088Smarkm				TTYADD('\n');
168929088Smarkm			}
169029088Smarkm		    }
169129088Smarkm		} else {
169229088Smarkm		    telrcv_state = TS_CR;
169329088Smarkm		    TTYADD('\r');
169429088Smarkm		    if (crmod) {
169529088Smarkm			    TTYADD('\n');
169629088Smarkm		    }
169729088Smarkm		}
169829088Smarkm	    } else {
169929088Smarkm		TTYADD(c);
170029088Smarkm	    }
170129088Smarkm	    continue;
170229088Smarkm
170329088Smarkm	case TS_IAC:
170429088Smarkmprocess_iac:
170529088Smarkm	    switch (c) {
170629088Smarkm
170729088Smarkm	    case WILL:
170829088Smarkm		telrcv_state = TS_WILL;
170929088Smarkm		continue;
171029088Smarkm
171129088Smarkm	    case WONT:
171229088Smarkm		telrcv_state = TS_WONT;
171329088Smarkm		continue;
171429088Smarkm
171529088Smarkm	    case DO:
171629088Smarkm		telrcv_state = TS_DO;
171729088Smarkm		continue;
171829088Smarkm
171929088Smarkm	    case DONT:
172029088Smarkm		telrcv_state = TS_DONT;
172129088Smarkm		continue;
172229088Smarkm
172329088Smarkm	    case DM:
172429088Smarkm		    /*
172529088Smarkm		     * We may have missed an urgent notification,
172629088Smarkm		     * so make sure we flush whatever is in the
172729088Smarkm		     * buffer currently.
172829088Smarkm		     */
172929088Smarkm		printoption("RCVD", IAC, DM);
173029088Smarkm		SYNCHing = 1;
173129088Smarkm		(void) ttyflush(1);
173229088Smarkm		SYNCHing = stilloob();
173329088Smarkm		settimer(gotDM);
173429088Smarkm		break;
173529088Smarkm
173629088Smarkm	    case SB:
173729088Smarkm		SB_CLEAR();
173829088Smarkm		telrcv_state = TS_SB;
173929088Smarkm		continue;
174029088Smarkm
174129088Smarkm	    case IAC:
174229088Smarkm		TTYADD(IAC);
174329088Smarkm		break;
174429088Smarkm
174529088Smarkm	    case NOP:
174629088Smarkm	    case GA:
174729088Smarkm	    default:
174829088Smarkm		printoption("RCVD", IAC, c);
174929088Smarkm		break;
175029088Smarkm	    }
175129088Smarkm	    telrcv_state = TS_DATA;
175229088Smarkm	    continue;
175329088Smarkm
175429088Smarkm	case TS_WILL:
175529088Smarkm	    printoption("RCVD", WILL, c);
175629088Smarkm	    willoption(c);
175729088Smarkm	    telrcv_state = TS_DATA;
175829088Smarkm	    continue;
175929088Smarkm
176029088Smarkm	case TS_WONT:
176129088Smarkm	    printoption("RCVD", WONT, c);
176229088Smarkm	    wontoption(c);
176329088Smarkm	    telrcv_state = TS_DATA;
176429088Smarkm	    continue;
176529088Smarkm
176629088Smarkm	case TS_DO:
176729088Smarkm	    printoption("RCVD", DO, c);
176829088Smarkm	    dooption(c);
176929088Smarkm	    if (c == TELOPT_NAWS) {
177029088Smarkm		sendnaws();
177129088Smarkm	    } else if (c == TELOPT_LFLOW) {
177229088Smarkm		localflow = 1;
177329088Smarkm		setcommandmode();
177429088Smarkm		setconnmode(0);
177529088Smarkm	    }
177629088Smarkm	    telrcv_state = TS_DATA;
177729088Smarkm	    continue;
177829088Smarkm
177929088Smarkm	case TS_DONT:
178029088Smarkm	    printoption("RCVD", DONT, c);
178129088Smarkm	    dontoption(c);
178229088Smarkm	    flushline = 1;
178329088Smarkm	    setconnmode(0);	/* set new tty mode (maybe) */
178429088Smarkm	    telrcv_state = TS_DATA;
178529088Smarkm	    continue;
178629088Smarkm
178729088Smarkm	case TS_SB:
178829088Smarkm	    if (c == IAC) {
178929088Smarkm		telrcv_state = TS_SE;
179029088Smarkm	    } else {
179129088Smarkm		SB_ACCUM(c);
179229088Smarkm	    }
179329088Smarkm	    continue;
179429088Smarkm
179529088Smarkm	case TS_SE:
179629088Smarkm	    if (c != SE) {
179729088Smarkm		if (c != IAC) {
179829088Smarkm		    /*
179929088Smarkm		     * This is an error.  We only expect to get
180029088Smarkm		     * "IAC IAC" or "IAC SE".  Several things may
180129088Smarkm		     * have happend.  An IAC was not doubled, the
180229088Smarkm		     * IAC SE was left off, or another option got
180329088Smarkm		     * inserted into the suboption are all possibilities.
180429088Smarkm		     * If we assume that the IAC was not doubled,
180529088Smarkm		     * and really the IAC SE was left off, we could
180629088Smarkm		     * get into an infinate loop here.  So, instead,
180729088Smarkm		     * we terminate the suboption, and process the
180829088Smarkm		     * partial suboption if we can.
180929088Smarkm		     */
181029088Smarkm		    SB_ACCUM(IAC);
181129088Smarkm		    SB_ACCUM(c);
181229088Smarkm		    subpointer -= 2;
181329088Smarkm		    SB_TERM();
181429088Smarkm
181529088Smarkm		    printoption("In SUBOPTION processing, RCVD", IAC, c);
181629088Smarkm		    suboption();	/* handle sub-option */
181729088Smarkm		    telrcv_state = TS_IAC;
181829088Smarkm		    goto process_iac;
181929088Smarkm		}
182029088Smarkm		SB_ACCUM(c);
182129088Smarkm		telrcv_state = TS_SB;
182229088Smarkm	    } else {
182329088Smarkm		SB_ACCUM(IAC);
182429088Smarkm		SB_ACCUM(SE);
182529088Smarkm		subpointer -= 2;
182629088Smarkm		SB_TERM();
182729088Smarkm		suboption();	/* handle sub-option */
182829088Smarkm		telrcv_state = TS_DATA;
182929088Smarkm	    }
183029088Smarkm	}
183129088Smarkm    }
183229088Smarkm    if (count)
183329088Smarkm	ring_consumed(&netiring, count);
183429088Smarkm    return returnValue||count;
183529088Smarkm}
183629088Smarkm
183729088Smarkmstatic int bol = 1, local = 0;
183829088Smarkm
183987139Smarkmint
184087139Smarkmrlogin_susp(void)
184129088Smarkm{
184229088Smarkm    if (local) {
184329088Smarkm	local = 0;
184429088Smarkm	bol = 1;
184529088Smarkm	command(0, "z\n", 2);
184629088Smarkm	return(1);
184729088Smarkm    }
184829088Smarkm    return(0);
184929088Smarkm}
185029088Smarkm
185187139Smarkmstatic int
185287139Smarkmtelsnd(void)
185329088Smarkm{
185429088Smarkm    int tcc;
185529088Smarkm    int count;
185629088Smarkm    int returnValue = 0;
185729088Smarkm    unsigned char *tbp;
185829088Smarkm
185929088Smarkm    tcc = 0;
186029088Smarkm    count = 0;
186129088Smarkm    while (NETROOM() > 2) {
186287139Smarkm	int sc;
186387139Smarkm	int c;
186429088Smarkm
186529088Smarkm	if (tcc == 0) {
186629088Smarkm	    if (count) {
186729088Smarkm		ring_consumed(&ttyiring, count);
186829088Smarkm		returnValue = 1;
186929088Smarkm		count = 0;
187029088Smarkm	    }
187129088Smarkm	    tbp = ttyiring.consume;
187229088Smarkm	    tcc = ring_full_consecutive(&ttyiring);
187329088Smarkm	    if (tcc == 0) {
187429088Smarkm		break;
187529088Smarkm	    }
187629088Smarkm	}
187729088Smarkm	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
187829088Smarkm	if (rlogin != _POSIX_VDISABLE) {
187929088Smarkm		if (bol) {
188029088Smarkm			bol = 0;
188129088Smarkm			if (sc == rlogin) {
188229088Smarkm				local = 1;
188329088Smarkm				continue;
188429088Smarkm			}
188529088Smarkm		} else if (local) {
188629088Smarkm			local = 0;
188729088Smarkm			if (sc == '.' || c == termEofChar) {
188829088Smarkm				bol = 1;
188929088Smarkm				command(0, "close\n", 6);
189029088Smarkm				continue;
189129088Smarkm			}
189229088Smarkm			if (sc == termSuspChar) {
189329088Smarkm				bol = 1;
189429088Smarkm				command(0, "z\n", 2);
189529088Smarkm				continue;
189629088Smarkm			}
189729088Smarkm			if (sc == escape) {
189887139Smarkm				command(0, tbp, tcc);
189929088Smarkm				bol = 1;
190029088Smarkm				count += tcc;
190129088Smarkm				tcc = 0;
190229088Smarkm				flushline = 1;
190329088Smarkm				break;
190429088Smarkm			}
190529088Smarkm			if (sc != rlogin) {
190629088Smarkm				++tcc;
190729088Smarkm				--tbp;
190829088Smarkm				--count;
190929088Smarkm				c = sc = rlogin;
191029088Smarkm			}
191129088Smarkm		}
191229088Smarkm		if ((sc == '\n') || (sc == '\r'))
191329088Smarkm			bol = 1;
191447973Sru	} else if (escape != _POSIX_VDISABLE && sc == escape) {
191529088Smarkm	    /*
191629088Smarkm	     * Double escape is a pass through of a single escape character.
191729088Smarkm	     */
191829088Smarkm	    if (tcc && strip(*tbp) == escape) {
191929088Smarkm		tbp++;
192029088Smarkm		tcc--;
192129088Smarkm		count++;
192229088Smarkm		bol = 0;
192329088Smarkm	    } else {
192429088Smarkm		command(0, (char *)tbp, tcc);
192529088Smarkm		bol = 1;
192629088Smarkm		count += tcc;
192729088Smarkm		tcc = 0;
192829088Smarkm		flushline = 1;
192929088Smarkm		break;
193029088Smarkm	    }
193129088Smarkm	} else
193229088Smarkm	    bol = 0;
193329088Smarkm#ifdef	KLUDGELINEMODE
193429088Smarkm	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
193529088Smarkm	    if (tcc > 0 && strip(*tbp) == echoc) {
193629088Smarkm		tcc--; tbp++; count++;
193729088Smarkm	    } else {
193829088Smarkm		dontlecho = !dontlecho;
193929088Smarkm		settimer(echotoggle);
194029088Smarkm		setconnmode(0);
194129088Smarkm		flushline = 1;
194229088Smarkm		break;
194329088Smarkm	    }
194429088Smarkm	}
194529088Smarkm#endif
194629088Smarkm	if (MODE_LOCAL_CHARS(globalmode)) {
194729088Smarkm	    if (TerminalSpecialChars(sc) == 0) {
194829088Smarkm		bol = 1;
194929088Smarkm		break;
195029088Smarkm	    }
195129088Smarkm	}
195229088Smarkm	if (my_want_state_is_wont(TELOPT_BINARY)) {
195329088Smarkm	    switch (c) {
195429088Smarkm	    case '\n':
195529088Smarkm		    /*
195629088Smarkm		     * If we are in CRMOD mode (\r ==> \n)
195729088Smarkm		     * on our local machine, then probably
195829088Smarkm		     * a newline (unix) is CRLF (TELNET).
195929088Smarkm		     */
196029088Smarkm		if (MODE_LOCAL_CHARS(globalmode)) {
196129088Smarkm		    NETADD('\r');
196229088Smarkm		}
196329088Smarkm		NETADD('\n');
196429088Smarkm		bol = flushline = 1;
196529088Smarkm		break;
196629088Smarkm	    case '\r':
196729088Smarkm		if (!crlf) {
196829088Smarkm		    NET2ADD('\r', '\0');
196929088Smarkm		} else {
197029088Smarkm		    NET2ADD('\r', '\n');
197129088Smarkm		}
197229088Smarkm		bol = flushline = 1;
197329088Smarkm		break;
197429088Smarkm	    case IAC:
197529088Smarkm		NET2ADD(IAC, IAC);
197629088Smarkm		break;
197729088Smarkm	    default:
197829088Smarkm		NETADD(c);
197929088Smarkm		break;
198029088Smarkm	    }
198129088Smarkm	} else if (c == IAC) {
198229088Smarkm	    NET2ADD(IAC, IAC);
198329088Smarkm	} else {
198429088Smarkm	    NETADD(c);
198529088Smarkm	}
198629088Smarkm    }
198729088Smarkm    if (count)
198829088Smarkm	ring_consumed(&ttyiring, count);
198929088Smarkm    return returnValue||count;		/* Non-zero if we did anything */
199029088Smarkm}
199129088Smarkm
199229088Smarkm/*
199329088Smarkm * Scheduler()
199429088Smarkm *
199529088Smarkm * Try to do something.
199629088Smarkm *
199729088Smarkm * If we do something useful, return 1; else return 0.
199829088Smarkm *
199929088Smarkm */
200029088Smarkm
200187139Smarkmstatic int
200287139SmarkmScheduler(int block)
200329088Smarkm{
200429088Smarkm		/* One wants to be a bit careful about setting returnValue
200529088Smarkm		 * to one, since a one implies we did some useful work,
200629088Smarkm		 * and therefore probably won't be called to block next
200729088Smarkm		 */
200829088Smarkm    int returnValue;
200929088Smarkm    int netin, netout, netex, ttyin, ttyout;
201029088Smarkm
201129088Smarkm    /* Decide which rings should be processed */
201229088Smarkm
201329088Smarkm    netout = ring_full_count(&netoring) &&
201429088Smarkm	    (flushline ||
201529088Smarkm		(my_want_state_is_wont(TELOPT_LINEMODE)
201629088Smarkm#ifdef	KLUDGELINEMODE
201729088Smarkm			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
201829088Smarkm#endif
201929088Smarkm		) ||
202029088Smarkm			my_want_state_is_will(TELOPT_BINARY));
202129088Smarkm    ttyout = ring_full_count(&ttyoring);
202229088Smarkm
202329181Smarkm    ttyin = ring_empty_count(&ttyiring) && (clienteof == 0);
202429088Smarkm
202529088Smarkm    netin = !ISend && ring_empty_count(&netiring);
202629088Smarkm
202729088Smarkm    netex = !SYNCHing;
202829088Smarkm
202929088Smarkm    /* Call to system code to process rings */
203029088Smarkm
203129088Smarkm    returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
203229088Smarkm
203329088Smarkm    /* Now, look at the input rings, looking for work to do. */
203429088Smarkm
203529088Smarkm    if (ring_full_count(&ttyiring)) {
203629088Smarkm	    returnValue |= telsnd();
203729088Smarkm    }
203829088Smarkm
203929088Smarkm    if (ring_full_count(&netiring)) {
204029088Smarkm	returnValue |= telrcv();
204129088Smarkm    }
204229088Smarkm    return returnValue;
204329088Smarkm}
204429088Smarkm
204587139Smarkm#ifdef	AUTHENTICATION
204687139Smarkm#define __unusedhere
204787139Smarkm#else
204887139Smarkm#define __unusedhere __unused
204987139Smarkm#endif
205029088Smarkm/*
205129088Smarkm * Select from tty and network...
205229088Smarkm */
205387139Smarkmvoid
205487139Smarkmtelnet(char *user __unusedhere)
205529088Smarkm{
205629088Smarkm    sys_telnet_init();
205729088Smarkm
205887139Smarkm#ifdef	AUTHENTICATION
205987139Smarkm#ifdef	ENCRYPTION
206029088Smarkm    {
206129088Smarkm	static char local_host[256] = { 0 };
206229088Smarkm
206329088Smarkm	if (!local_host[0]) {
206429088Smarkm		gethostname(local_host, sizeof(local_host));
206529088Smarkm		local_host[sizeof(local_host)-1] = 0;
206629088Smarkm	}
206729088Smarkm	auth_encrypt_init(local_host, hostname, "TELNET", 0);
206829088Smarkm	auth_encrypt_user(user);
206929088Smarkm    }
207087139Smarkm#endif
207187139Smarkm#endif
207229088Smarkm    if (telnetport) {
207387139Smarkm#ifdef	AUTHENTICATION
207429088Smarkm	if (autologin)
207529088Smarkm		send_will(TELOPT_AUTHENTICATION, 1);
207629088Smarkm#endif
207729088Smarkm#ifdef	ENCRYPTION
207829088Smarkm	send_do(TELOPT_ENCRYPT, 1);
207929088Smarkm	send_will(TELOPT_ENCRYPT, 1);
208029088Smarkm#endif	/* ENCRYPTION */
208129088Smarkm	send_do(TELOPT_SGA, 1);
208229088Smarkm	send_will(TELOPT_TTYPE, 1);
208329088Smarkm	send_will(TELOPT_NAWS, 1);
208429088Smarkm	send_will(TELOPT_TSPEED, 1);
208529088Smarkm	send_will(TELOPT_LFLOW, 1);
208629088Smarkm	send_will(TELOPT_LINEMODE, 1);
208729088Smarkm	send_will(TELOPT_NEW_ENVIRON, 1);
208829088Smarkm	send_do(TELOPT_STATUS, 1);
208987139Smarkm	if (env_getvalue("DISPLAY"))
209029088Smarkm	    send_will(TELOPT_XDISPLOC, 1);
209129088Smarkm	if (eight)
209229088Smarkm	    tel_enter_binary(eight);
209329088Smarkm    }
209429088Smarkm
209529088Smarkm    for (;;) {
209629088Smarkm	int schedValue;
209729088Smarkm
209829088Smarkm	while ((schedValue = Scheduler(0)) != 0) {
209929088Smarkm	    if (schedValue == -1) {
210029088Smarkm		setcommandmode();
210129088Smarkm		return;
210229088Smarkm	    }
210329088Smarkm	}
210429088Smarkm
210529088Smarkm	if (Scheduler(1) == -1) {
210629088Smarkm	    setcommandmode();
210729088Smarkm	    return;
210829088Smarkm	}
210929088Smarkm    }
211029088Smarkm}
211129088Smarkm
211229088Smarkm#if	0	/* XXX - this not being in is a bug */
211329088Smarkm/*
211429088Smarkm * nextitem()
211529088Smarkm *
211629088Smarkm *	Return the address of the next "item" in the TELNET data
211729088Smarkm * stream.  This will be the address of the next character if
211829088Smarkm * the current address is a user data character, or it will
211929088Smarkm * be the address of the character following the TELNET command
212029088Smarkm * if the current address is a TELNET IAC ("I Am a Command")
212129088Smarkm * character.
212229088Smarkm */
212329088Smarkm
212487139Smarkmstatic char *
212587139Smarkmnextitem(char *current)
212629088Smarkm{
212729088Smarkm    if ((*current&0xff) != IAC) {
212829088Smarkm	return current+1;
212929088Smarkm    }
213029088Smarkm    switch (*(current+1)&0xff) {
213129088Smarkm    case DO:
213229088Smarkm    case DONT:
213329088Smarkm    case WILL:
213429088Smarkm    case WONT:
213529088Smarkm	return current+3;
213629088Smarkm    case SB:		/* loop forever looking for the SE */
213729088Smarkm	{
213887139Smarkm	    char *look = current+2;
213929088Smarkm
214029088Smarkm	    for (;;) {
214129088Smarkm		if ((*look++&0xff) == IAC) {
214229088Smarkm		    if ((*look++&0xff) == SE) {
214329088Smarkm			return look;
214429088Smarkm		    }
214529088Smarkm		}
214629088Smarkm	    }
214729088Smarkm	}
214829088Smarkm    default:
214929088Smarkm	return current+2;
215029088Smarkm    }
215129088Smarkm}
215229088Smarkm#endif	/* 0 */
215329088Smarkm
215429088Smarkm/*
215529088Smarkm * netclear()
215629088Smarkm *
215729088Smarkm *	We are about to do a TELNET SYNCH operation.  Clear
215829088Smarkm * the path to the network.
215929088Smarkm *
216029088Smarkm *	Things are a bit tricky since we may have sent the first
216129088Smarkm * byte or so of a previous TELNET command into the network.
216229088Smarkm * So, we have to scan the network buffer from the beginning
216329088Smarkm * until we are up to where we want to be.
216429088Smarkm *
216529088Smarkm *	A side effect of what we do, just to keep things
216629088Smarkm * simple, is to clear the urgent data pointer.  The principal
216729088Smarkm * caller should be setting the urgent data pointer AFTER calling
216829088Smarkm * us in any case.
216929088Smarkm */
217029088Smarkm
217187139Smarkmstatic void
217287139Smarkmnetclear(void)
217329088Smarkm{
217487139Smarkm	/* Deleted */
217529088Smarkm}
217629088Smarkm
217729088Smarkm/*
217829088Smarkm * These routines add various telnet commands to the data stream.
217929088Smarkm */
218029088Smarkm
218187139Smarkmstatic void
218287139Smarkmdoflush(void)
218329088Smarkm{
218429088Smarkm    NET2ADD(IAC, DO);
218529088Smarkm    NETADD(TELOPT_TM);
218629088Smarkm    flushline = 1;
218729088Smarkm    flushout = 1;
218829088Smarkm    (void) ttyflush(1);			/* Flush/drop output */
218929088Smarkm    /* do printoption AFTER flush, otherwise the output gets tossed... */
219029088Smarkm    printoption("SENT", DO, TELOPT_TM);
219129088Smarkm}
219229088Smarkm
219387139Smarkmvoid
219487139SmarkmxmitAO(void)
219529088Smarkm{
219629088Smarkm    NET2ADD(IAC, AO);
219729088Smarkm    printoption("SENT", IAC, AO);
219829088Smarkm    if (autoflush) {
219929088Smarkm	doflush();
220029088Smarkm    }
220129088Smarkm}
220229088Smarkm
220387139Smarkmvoid
220487139SmarkmxmitEL(void)
220529088Smarkm{
220629088Smarkm    NET2ADD(IAC, EL);
220729088Smarkm    printoption("SENT", IAC, EL);
220829088Smarkm}
220929088Smarkm
221087139Smarkmvoid
221187139SmarkmxmitEC(void)
221229088Smarkm{
221329088Smarkm    NET2ADD(IAC, EC);
221429088Smarkm    printoption("SENT", IAC, EC);
221529088Smarkm}
221629088Smarkm
221787139Smarkmint
221887139Smarkmdosynch(char *ch __unused)
221929088Smarkm{
222029088Smarkm    netclear();			/* clear the path to the network */
222129088Smarkm    NETADD(IAC);
222229088Smarkm    setneturg();
222329088Smarkm    NETADD(DM);
222429088Smarkm    printoption("SENT", IAC, DM);
222529088Smarkm    return 1;
222629088Smarkm}
222729088Smarkm
222829088Smarkmint want_status_response = 0;
222929088Smarkm
223087139Smarkmint
223187139Smarkmget_status(char *ch __unused)
223229088Smarkm{
223329088Smarkm    unsigned char tmp[16];
223487139Smarkm    unsigned char *cp;
223529088Smarkm
223629088Smarkm    if (my_want_state_is_dont(TELOPT_STATUS)) {
223729088Smarkm	printf("Remote side does not support STATUS option\n");
223829088Smarkm	return 0;
223929088Smarkm    }
224029088Smarkm    cp = tmp;
224129088Smarkm
224229088Smarkm    *cp++ = IAC;
224329088Smarkm    *cp++ = SB;
224429088Smarkm    *cp++ = TELOPT_STATUS;
224529088Smarkm    *cp++ = TELQUAL_SEND;
224629088Smarkm    *cp++ = IAC;
224729088Smarkm    *cp++ = SE;
224829088Smarkm    if (NETROOM() >= cp - tmp) {
224929088Smarkm	ring_supply_data(&netoring, tmp, cp-tmp);
225029088Smarkm	printsub('>', tmp+2, cp - tmp - 2);
225129088Smarkm    }
225229088Smarkm    ++want_status_response;
225329088Smarkm    return 1;
225429088Smarkm}
225529088Smarkm
225687139Smarkmvoid
225787139Smarkmintp(void)
225829088Smarkm{
225929088Smarkm    NET2ADD(IAC, IP);
226029088Smarkm    printoption("SENT", IAC, IP);
226129088Smarkm    flushline = 1;
226229088Smarkm    if (autoflush) {
226329088Smarkm	doflush();
226429088Smarkm    }
226529088Smarkm    if (autosynch) {
226687139Smarkm	dosynch(NULL);
226729088Smarkm    }
226829088Smarkm}
226929088Smarkm
227087139Smarkmvoid
227187139Smarkmsendbrk(void)
227229088Smarkm{
227329088Smarkm    NET2ADD(IAC, BREAK);
227429088Smarkm    printoption("SENT", IAC, BREAK);
227529088Smarkm    flushline = 1;
227629088Smarkm    if (autoflush) {
227729088Smarkm	doflush();
227829088Smarkm    }
227929088Smarkm    if (autosynch) {
228087139Smarkm	dosynch(NULL);
228129088Smarkm    }
228229088Smarkm}
228329088Smarkm
228487139Smarkmvoid
228587139Smarkmsendabort(void)
228629088Smarkm{
228729088Smarkm    NET2ADD(IAC, ABORT);
228829088Smarkm    printoption("SENT", IAC, ABORT);
228929088Smarkm    flushline = 1;
229029088Smarkm    if (autoflush) {
229129088Smarkm	doflush();
229229088Smarkm    }
229329088Smarkm    if (autosynch) {
229487139Smarkm	dosynch(NULL);
229529088Smarkm    }
229629088Smarkm}
229729088Smarkm
229887139Smarkmvoid
229987139Smarkmsendsusp(void)
230029088Smarkm{
230129088Smarkm    NET2ADD(IAC, SUSP);
230229088Smarkm    printoption("SENT", IAC, SUSP);
230329088Smarkm    flushline = 1;
230429088Smarkm    if (autoflush) {
230529088Smarkm	doflush();
230629088Smarkm    }
230729088Smarkm    if (autosynch) {
230887139Smarkm	dosynch(NULL);
230929088Smarkm    }
231029088Smarkm}
231129088Smarkm
231287139Smarkmvoid
231387139Smarkmsendeof(void)
231429088Smarkm{
231529088Smarkm    NET2ADD(IAC, xEOF);
231629088Smarkm    printoption("SENT", IAC, xEOF);
231729088Smarkm}
231829088Smarkm
231987139Smarkmvoid
232087139Smarkmsendayt(void)
232129088Smarkm{
232229088Smarkm    NET2ADD(IAC, AYT);
232329088Smarkm    printoption("SENT", IAC, AYT);
232429088Smarkm}
232529088Smarkm
232629088Smarkm/*
232729088Smarkm * Send a window size update to the remote system.
232829088Smarkm */
232929088Smarkm
233087139Smarkmvoid
233187139Smarkmsendnaws(void)
233229088Smarkm{
233329088Smarkm    long rows, cols;
233429088Smarkm    unsigned char tmp[16];
233587139Smarkm    unsigned char *cp;
233629088Smarkm
233729088Smarkm    if (my_state_is_wont(TELOPT_NAWS))
233829088Smarkm	return;
233929088Smarkm
234029088Smarkm#define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
234129088Smarkm			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
234229088Smarkm
234329088Smarkm    if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
234429088Smarkm	return;
234529088Smarkm    }
234629088Smarkm
234729088Smarkm    cp = tmp;
234829088Smarkm
234929088Smarkm    *cp++ = IAC;
235029088Smarkm    *cp++ = SB;
235129088Smarkm    *cp++ = TELOPT_NAWS;
235229088Smarkm    PUTSHORT(cp, cols);
235329088Smarkm    PUTSHORT(cp, rows);
235429088Smarkm    *cp++ = IAC;
235529088Smarkm    *cp++ = SE;
235629088Smarkm    if (NETROOM() >= cp - tmp) {
235729088Smarkm	ring_supply_data(&netoring, tmp, cp-tmp);
235829088Smarkm	printsub('>', tmp+2, cp - tmp - 2);
235929088Smarkm    }
236029088Smarkm}
236129088Smarkm
236287139Smarkmvoid
236387139Smarkmtel_enter_binary(int rw)
236429088Smarkm{
236529088Smarkm    if (rw&1)
236629088Smarkm	send_do(TELOPT_BINARY, 1);
236729088Smarkm    if (rw&2)
236829088Smarkm	send_will(TELOPT_BINARY, 1);
236929088Smarkm}
237029088Smarkm
237187139Smarkmvoid
237287139Smarkmtel_leave_binary(int rw)
237329088Smarkm{
237429088Smarkm    if (rw&1)
237529088Smarkm	send_dont(TELOPT_BINARY, 1);
237629088Smarkm    if (rw&2)
237729088Smarkm	send_wont(TELOPT_BINARY, 1);
237829088Smarkm}
2379