xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 60149)
133685Sbostic /*
245232Sborman  * Copyright (c) 1988, 1990 Regents of the University of California.
333685Sbostic  * All rights reserved.
433685Sbostic  *
542770Sbostic  * %sccs.include.redist.c%
633685Sbostic  */
711758Ssam 
821580Sdist #ifndef lint
9*60149Sdab static char sccsid[] = "@(#)telnet.c	5.56 (Berkeley) 05/20/93";
1033685Sbostic #endif /* not lint */
1121580Sdist 
129217Ssam #include <sys/types.h>
139217Ssam 
1432377Sminshall #if	defined(unix)
1533804Sminshall #include <signal.h>
1632377Sminshall /* By the way, we need to include curses.h before telnet.h since,
1732377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
1832377Sminshall  * declared in curses.h.
1932377Sminshall  */
2032377Sminshall #endif	/* defined(unix) */
2132377Sminshall 
2212212Ssam #include <arpa/telnet.h>
2332377Sminshall 
2438908Sborman #include <ctype.h>
2538908Sborman 
2632381Sminshall #include "ring.h"
2732381Sminshall 
2832377Sminshall #include "defines.h"
2932377Sminshall #include "externs.h"
3032377Sminshall #include "types.h"
3132377Sminshall #include "general.h"
3227178Sminshall 
3327178Sminshall 
3427228Sminshall #define	strip(x)	((x)&0x7f)
356000Sroot 
3646808Sdab static unsigned char	subbuffer[SUBBUFSIZE],
3746808Sdab 			*subpointer, *subend;	 /* buffer for sub-options */
3827676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
3946808Sdab #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
4027676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
4127676Sminshall 				*subpointer++ = (c); \
4227676Sminshall 			}
4327676Sminshall 
4446808Sdab #define	SB_GET()	((*subpointer++)&0xff)
4546808Sdab #define	SB_PEEK()	((*subpointer)&0xff)
4646808Sdab #define	SB_EOF()	(subpointer >= subend)
4746808Sdab #define	SB_LEN()	(subend - subpointer)
4846808Sdab 
4937226Sminshall char	options[256];		/* The combined options */
5038689Sborman char	do_dont_resp[256];
5138689Sborman char	will_wont_resp[256];
526000Sroot 
5332377Sminshall int
5446808Sdab 	eight = 0,
5546808Sdab 	autologin = 0,	/* Autologin anyone? */
5647608Sdab 	skiprc = 0,
5732377Sminshall 	connected,
5832377Sminshall 	showoptions,
5932377Sminshall 	In3270,		/* Are we in 3270 mode? */
6032377Sminshall 	ISend,		/* trying to send network data in */
6132377Sminshall 	debug = 0,
6232377Sminshall 	crmod,
6332377Sminshall 	netdata,	/* Print out network data flow */
6432377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
6534848Sminshall #if	defined(TN3270)
6636241Sminshall 	noasynchtty = 0,/* User specified "-noasynch" on command line */
6736241Sminshall 	noasynchnet = 0,/* User specified "-noasynch" on command line */
6832377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
6934848Sminshall #endif	/* defined(TN3270) */
7033286Sminshall 	telnetport,
7132531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
7232531Sminshall 	flushout,	/* flush output */
7332531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
7432531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
7537219Sminshall 	localflow,	/* we handle flow control locally */
7657213Sdab 	restartany,	/* if flow control enabled, restart on any character */
7732531Sminshall 	localchars,	/* we recognize interrupt/quit */
7832531Sminshall 	donelclchars,	/* the user has set "localchars" */
7932531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
8032531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
8132531Sminshall 	globalmode;
8227088Sminshall 
8344360Sborman char *prompt = 0;
846000Sroot 
8544360Sborman cc_t escape;
8646808Sdab cc_t rlogin;
8744360Sborman #ifdef	KLUDGELINEMODE
8844360Sborman cc_t echoc;
8944360Sborman #endif
9027186Sminshall 
9127186Sminshall /*
926000Sroot  * Telnet receiver states for fsm
936000Sroot  */
946000Sroot #define	TS_DATA		0
956000Sroot #define	TS_IAC		1
966000Sroot #define	TS_WILL		2
976000Sroot #define	TS_WONT		3
986000Sroot #define	TS_DO		4
996000Sroot #define	TS_DONT		5
10027021Sminshall #define	TS_CR		6
10127676Sminshall #define	TS_SB		7		/* sub-option collection */
10227676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1036000Sroot 
10432377Sminshall static int	telrcv_state;
1056000Sroot 
10632377Sminshall jmp_buf	toplevel = { 0 };
10732377Sminshall jmp_buf	peerdied;
1086000Sroot 
10932377Sminshall int	flushline;
11038811Sborman int	linemode;
11127021Sminshall 
11238689Sborman #ifdef	KLUDGELINEMODE
11338689Sborman int	kludgelinemode = 1;
11438689Sborman #endif
11538689Sborman 
11632377Sminshall /*
11732377Sminshall  * The following are some clocks used to decide how to interpret
11832377Sminshall  * the relationship between various variables.
11932377Sminshall  */
1206000Sroot 
12132377Sminshall Clocks clocks;
12232377Sminshall 
12338689Sborman #ifdef	notdef
12432377Sminshall Modelist modelist[] = {
12532377Sminshall 	{ "telnet command mode", COMMAND_LINE },
12632377Sminshall 	{ "character-at-a-time mode", 0 },
12732377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
12832377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
12932377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
13032377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
13132377Sminshall 	{ "3270 mode", 0 },
13232377Sminshall };
13338689Sborman #endif
1346000Sroot 
13532377Sminshall 
13632377Sminshall /*
13732377Sminshall  * Initialize telnet environment.
13832377Sminshall  */
1396000Sroot 
14046808Sdab     void
14132377Sminshall init_telnet()
14232377Sminshall {
14344360Sborman     env_init();
14444360Sborman 
14532377Sminshall     SB_CLEAR();
14637226Sminshall     ClearArray(options);
1476000Sroot 
14837219Sminshall     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
149*60149Sdab #if	defined(AUTHENTICATION) || defined(ENCRYPTION)
15046808Sdab     auth_encrypt_connect(connected);
151*60149Sdab #endif	/* defined(AUTHENTICATION) || defined(ENCRYPTION)  */
15257213Sdab     restartany = -1;
1536000Sroot 
15432377Sminshall     SYNCHing = 0;
1556000Sroot 
15632377Sminshall     /* Don't change NetTrace */
1576000Sroot 
15832377Sminshall     escape = CONTROL(']');
15946808Sdab     rlogin = _POSIX_VDISABLE;
16044360Sborman #ifdef	KLUDGELINEMODE
16132377Sminshall     echoc = CONTROL('E');
16244360Sborman #endif
1636000Sroot 
16432377Sminshall     flushline = 1;
16532377Sminshall     telrcv_state = TS_DATA;
16632377Sminshall }
16732554Sminshall 
1686000Sroot 
16944360Sborman #ifdef	notdef
17032554Sminshall #include <varargs.h>
1716000Sroot 
17246808Sdab     /*VARARGS*/
17346808Sdab     static void
17432554Sminshall printring(va_alist)
17546808Sdab     va_dcl
17632554Sminshall {
17732554Sminshall     va_list ap;
17832554Sminshall     char buffer[100];		/* where things go */
17932554Sminshall     char *ptr;
18032554Sminshall     char *format;
18132554Sminshall     char *string;
18232554Sminshall     Ring *ring;
18332554Sminshall     int i;
18432554Sminshall 
18532554Sminshall     va_start(ap);
18632554Sminshall 
18732554Sminshall     ring = va_arg(ap, Ring *);
18832554Sminshall     format = va_arg(ap, char *);
18932554Sminshall     ptr = buffer;
19032554Sminshall 
19132554Sminshall     while ((i = *format++) != 0) {
19232554Sminshall 	if (i == '%') {
19332554Sminshall 	    i = *format++;
19432554Sminshall 	    switch (i) {
19532554Sminshall 	    case 'c':
19632554Sminshall 		*ptr++ = va_arg(ap, int);
19732554Sminshall 		break;
19832554Sminshall 	    case 's':
19932554Sminshall 		string = va_arg(ap, char *);
20032554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
20132554Sminshall 		ring_supply_data(ring, string, strlen(string));
20232554Sminshall 		ptr = buffer;
20332554Sminshall 		break;
20432554Sminshall 	    case 0:
20532554Sminshall 		ExitString("printring: trailing %%.\n", 1);
20632554Sminshall 		/*NOTREACHED*/
20732554Sminshall 	    default:
20832554Sminshall 		ExitString("printring: unknown format character.\n", 1);
20932554Sminshall 		/*NOTREACHED*/
21032554Sminshall 	    }
21132554Sminshall 	} else {
21232554Sminshall 	    *ptr++ = i;
21332554Sminshall 	}
21432554Sminshall     }
21532554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
21632554Sminshall }
21744360Sborman #endif
21832554Sminshall 
21937226Sminshall /*
22037226Sminshall  * These routines are in charge of sending option negotiations
22137226Sminshall  * to the other side.
22237226Sminshall  *
22337226Sminshall  * The basic idea is that we send the negotiation if either side
22437226Sminshall  * is in disagreement as to what the current state should be.
22537226Sminshall  */
22632554Sminshall 
22746808Sdab     void
22838689Sborman send_do(c, init)
22946808Sdab     register int c, init;
2306000Sroot {
23138689Sborman     if (init) {
23238689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
23338689Sborman 				my_want_state_is_do(c))
23438689Sborman 	    return;
23538689Sborman 	set_my_want_state_do(c);
23638689Sborman 	do_dont_resp[c]++;
23737226Sminshall     }
23838689Sborman     NET2ADD(IAC, DO);
23938689Sborman     NETADD(c);
24046808Sdab     printoption("SENT", DO, c);
24137226Sminshall }
24237226Sminshall 
24346808Sdab     void
24438689Sborman send_dont(c, init)
24546808Sdab     register int c, init;
24637226Sminshall {
24738689Sborman     if (init) {
24838689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
24938689Sborman 				my_want_state_is_dont(c))
25038689Sborman 	    return;
25138689Sborman 	set_my_want_state_dont(c);
25238689Sborman 	do_dont_resp[c]++;
25337226Sminshall     }
25438689Sborman     NET2ADD(IAC, DONT);
25538689Sborman     NETADD(c);
25646808Sdab     printoption("SENT", DONT, c);
25737226Sminshall }
25837226Sminshall 
25946808Sdab     void
26038689Sborman send_will(c, init)
26146808Sdab     register int c, init;
26237226Sminshall {
26338689Sborman     if (init) {
26438689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
26538689Sborman 				my_want_state_is_will(c))
26638689Sborman 	    return;
26738689Sborman 	set_my_want_state_will(c);
26838689Sborman 	will_wont_resp[c]++;
26937226Sminshall     }
27038689Sborman     NET2ADD(IAC, WILL);
27138689Sborman     NETADD(c);
27246808Sdab     printoption("SENT", WILL, c);
27337226Sminshall }
27437226Sminshall 
27546808Sdab     void
27638689Sborman send_wont(c, init)
27746808Sdab     register int c, init;
27837226Sminshall {
27938689Sborman     if (init) {
28038689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
28138689Sborman 				my_want_state_is_wont(c))
28238689Sborman 	    return;
28338689Sborman 	set_my_want_state_wont(c);
28438689Sborman 	will_wont_resp[c]++;
28537226Sminshall     }
28638689Sborman     NET2ADD(IAC, WONT);
28738689Sborman     NETADD(c);
28846808Sdab     printoption("SENT", WONT, c);
28937226Sminshall }
29037226Sminshall 
29137226Sminshall 
29246808Sdab 	void
29337226Sminshall willoption(option)
29437226Sminshall 	int option;
29537226Sminshall {
29638689Sborman 	int new_state_ok = 0;
2976000Sroot 
29838689Sborman 	if (do_dont_resp[option]) {
29938689Sborman 	    --do_dont_resp[option];
30038689Sborman 	    if (do_dont_resp[option] && my_state_is_do(option))
30138689Sborman 		--do_dont_resp[option];
30238689Sborman 	}
30337226Sminshall 
30438689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
3056000Sroot 
30638689Sborman 	    switch (option) {
30738689Sborman 
30838689Sborman 	    case TELOPT_ECHO:
30938689Sborman #	    if defined(TN3270)
31038689Sborman 		/*
31138689Sborman 		 * The following is a pain in the rear-end.
31238689Sborman 		 * Various IBM servers (some versions of Wiscnet,
31338689Sborman 		 * possibly Fibronics/Spartacus, and who knows who
31438689Sborman 		 * else) will NOT allow us to send "DO SGA" too early
31538689Sborman 		 * in the setup proceedings.  On the other hand,
31638689Sborman 		 * 4.2 servers (telnetd) won't set SGA correctly.
31738689Sborman 		 * So, we are stuck.  Empirically (but, based on
31838689Sborman 		 * a VERY small sample), the IBM servers don't send
31938689Sborman 		 * out anything about ECHO, so we postpone our sending
32038689Sborman 		 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
32138689Sborman 		 * DO send).
32238689Sborman 		  */
32338689Sborman 		{
32438689Sborman 		    if (askedSGA == 0) {
32538689Sborman 			askedSGA = 1;
32638689Sborman 			if (my_want_state_is_dont(TELOPT_SGA))
32738689Sborman 			    send_do(TELOPT_SGA, 1);
32832377Sminshall 		    }
32932377Sminshall 		}
33038689Sborman 		    /* Fall through */
33138689Sborman 	    case TELOPT_EOR:
33238908Sborman #endif	    /* defined(TN3270) */
33338689Sborman 	    case TELOPT_BINARY:
33438689Sborman 	    case TELOPT_SGA:
33527110Sminshall 		settimer(modenegotiated);
33638908Sborman 		/* FALL THROUGH */
33738908Sborman 	    case TELOPT_STATUS:
33857213Sdab #if	defined(AUTHENTICATION)
33946808Sdab 	    case TELOPT_AUTHENTICATION:
34046808Sdab #endif
341*60149Sdab #ifdef	ENCRYPTION
34246808Sdab 	    case TELOPT_ENCRYPT:
343*60149Sdab #endif /* ENCRYPTION */
34438689Sborman 		new_state_ok = 1;
3456000Sroot 		break;
3466000Sroot 
34738689Sborman 	    case TELOPT_TM:
34838689Sborman 		if (flushout)
34938689Sborman 		    flushout = 0;
35038689Sborman 		/*
35138689Sborman 		 * Special case for TM.  If we get back a WILL,
35238689Sborman 		 * pretend we got back a WONT.
35338689Sborman 		 */
35438689Sborman 		set_my_want_state_dont(option);
35538689Sborman 		set_my_state_dont(option);
35627110Sminshall 		return;			/* Never reply to TM will's/wont's */
3576000Sroot 
35838689Sborman 	    case TELOPT_LINEMODE:
35938689Sborman 	    default:
3606000Sroot 		break;
36138689Sborman 	    }
36238689Sborman 
36338689Sborman 	    if (new_state_ok) {
36438689Sborman 		set_my_want_state_do(option);
36538689Sborman 		send_do(option, 0);
36638689Sborman 		setconnmode(0);		/* possibly set new tty mode */
36738689Sborman 	    } else {
36838689Sborman 		do_dont_resp[option]++;
36938689Sborman 		send_dont(option, 0);
37038689Sborman 	    }
3716000Sroot 	}
37238689Sborman 	set_my_state_do(option);
373*60149Sdab #ifdef	ENCRYPTION
37446808Sdab 	if (option == TELOPT_ENCRYPT)
37546808Sdab 		encrypt_send_support();
376*60149Sdab #endif	/* ENCRYPTION */
3776000Sroot }
3786000Sroot 
37946808Sdab 	void
38037226Sminshall wontoption(option)
38137226Sminshall 	int option;
3826000Sroot {
38338689Sborman 	if (do_dont_resp[option]) {
38438689Sborman 	    --do_dont_resp[option];
38538689Sborman 	    if (do_dont_resp[option] && my_state_is_dont(option))
38638689Sborman 		--do_dont_resp[option];
38738689Sborman 	}
38837226Sminshall 
38938689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
3906000Sroot 
39138689Sborman 	    switch (option) {
39238689Sborman 
39338689Sborman #ifdef	KLUDGELINEMODE
39438689Sborman 	    case TELOPT_SGA:
39538689Sborman 		if (!kludgelinemode)
39638689Sborman 		    break;
39738689Sborman 		/* FALL THROUGH */
39838689Sborman #endif
39938689Sborman 	    case TELOPT_ECHO:
40027110Sminshall 		settimer(modenegotiated);
4016000Sroot 		break;
4026000Sroot 
40338689Sborman 	    case TELOPT_TM:
40438689Sborman 		if (flushout)
40538689Sborman 		    flushout = 0;
40638689Sborman 		set_my_want_state_dont(option);
40738689Sborman 		set_my_state_dont(option);
40827110Sminshall 		return;		/* Never reply to TM will's/wont's */
40927110Sminshall 
41038689Sborman 	    default:
41138689Sborman 		break;
41238689Sborman 	    }
41338689Sborman 	    set_my_want_state_dont(option);
41444360Sborman 	    if (my_state_is_do(option))
41544360Sborman 		send_dont(option, 0);
41638689Sborman 	    setconnmode(0);			/* Set new tty mode */
41738689Sborman 	} else if (option == TELOPT_TM) {
41838689Sborman 	    /*
41938689Sborman 	     * Special case for TM.
42038689Sborman 	     */
42138689Sborman 	    if (flushout)
42238689Sborman 		flushout = 0;
42338689Sborman 	    set_my_want_state_dont(option);
4246000Sroot 	}
42538689Sborman 	set_my_state_dont(option);
4266000Sroot }
4276000Sroot 
42846808Sdab 	static void
4296000Sroot dooption(option)
4306000Sroot 	int option;
4316000Sroot {
43238689Sborman 	int new_state_ok = 0;
4336000Sroot 
43438689Sborman 	if (will_wont_resp[option]) {
43538689Sborman 	    --will_wont_resp[option];
43638689Sborman 	    if (will_wont_resp[option] && my_state_is_will(option))
43738689Sborman 		--will_wont_resp[option];
43838689Sborman 	}
43937226Sminshall 
44038689Sborman 	if (will_wont_resp[option] == 0) {
44138689Sborman 	  if (my_want_state_is_wont(option)) {
4426000Sroot 
44338689Sborman 	    switch (option) {
44438689Sborman 
44538689Sborman 	    case TELOPT_TM:
44638689Sborman 		/*
44738689Sborman 		 * Special case for TM.  We send a WILL, but pretend
44838689Sborman 		 * we sent WONT.
44938689Sborman 		 */
45038689Sborman 		send_will(option, 0);
45138689Sborman 		set_my_want_state_wont(TELOPT_TM);
45238689Sborman 		set_my_state_wont(TELOPT_TM);
45338689Sborman 		return;
45438689Sborman 
45532377Sminshall #	if defined(TN3270)
45638689Sborman 	    case TELOPT_EOR:		/* end of record */
45738908Sborman #	endif	/* defined(TN3270) */
45838689Sborman 	    case TELOPT_BINARY:		/* binary mode */
45938689Sborman 	    case TELOPT_NAWS:		/* window size */
46038689Sborman 	    case TELOPT_TSPEED:		/* terminal speed */
46138689Sborman 	    case TELOPT_LFLOW:		/* local flow control */
46238689Sborman 	    case TELOPT_TTYPE:		/* terminal type option */
46338689Sborman 	    case TELOPT_SGA:		/* no big deal */
46444360Sborman 	    case TELOPT_ENVIRON:	/* environment variable option */
465*60149Sdab #ifdef	ENCRYPTION
46646808Sdab 	    case TELOPT_ENCRYPT:	/* encryption variable option */
467*60149Sdab #endif	/* ENCRYPTION */
46838689Sborman 		new_state_ok = 1;
4696000Sroot 		break;
47057213Sdab #if	defined(AUTHENTICATION)
47146808Sdab 	    case TELOPT_AUTHENTICATION:
47246808Sdab 		if (autologin)
47346808Sdab 			new_state_ok = 1;
47446808Sdab 		break;
47546808Sdab #endif
4766000Sroot 
47744360Sborman 	    case TELOPT_XDISPLOC:	/* X Display location */
47846808Sdab 		if (env_getvalue((unsigned char *)"DISPLAY"))
47944360Sborman 		    new_state_ok = 1;
48044360Sborman 		break;
48144360Sborman 
48238689Sborman 	    case TELOPT_LINEMODE:
48338689Sborman #ifdef	KLUDGELINEMODE
48438689Sborman 		kludgelinemode = 0;
48544360Sborman 		send_do(TELOPT_SGA, 1);
48638689Sborman #endif
48738689Sborman 		set_my_want_state_will(TELOPT_LINEMODE);
48838689Sborman 		send_will(option, 0);
48938689Sborman 		set_my_state_will(TELOPT_LINEMODE);
49038689Sborman 		slc_init();
49138689Sborman 		return;
49238689Sborman 
49338689Sborman 	    case TELOPT_ECHO:		/* We're never going to echo... */
49438689Sborman 	    default:
4956000Sroot 		break;
49638689Sborman 	    }
49738689Sborman 
49838689Sborman 	    if (new_state_ok) {
49938689Sborman 		set_my_want_state_will(option);
50038689Sborman 		send_will(option, 0);
50145232Sborman 		setconnmode(0);			/* Set new tty mode */
50238689Sborman 	    } else {
50338689Sborman 		will_wont_resp[option]++;
50438689Sborman 		send_wont(option, 0);
50538689Sborman 	    }
50638689Sborman 	  } else {
50738689Sborman 	    /*
50838689Sborman 	     * Handle options that need more things done after the
50938689Sborman 	     * other side has acknowledged the option.
51038689Sborman 	     */
51138689Sborman 	    switch (option) {
51238689Sborman 	    case TELOPT_LINEMODE:
51338689Sborman #ifdef	KLUDGELINEMODE
51438689Sborman 		kludgelinemode = 0;
51544360Sborman 		send_do(TELOPT_SGA, 1);
51638689Sborman #endif
51738689Sborman 		set_my_state_will(option);
51838689Sborman 		slc_init();
51944360Sborman 		send_do(TELOPT_SGA, 0);
52038689Sborman 		return;
52138689Sborman 	    }
52238689Sborman 	  }
5236000Sroot 	}
52438689Sborman 	set_my_state_will(option);
5256000Sroot }
52627676Sminshall 
52746808Sdab 	static void
52838689Sborman dontoption(option)
52938689Sborman 	int option;
53038689Sborman {
53138689Sborman 
53238689Sborman 	if (will_wont_resp[option]) {
53338689Sborman 	    --will_wont_resp[option];
53438689Sborman 	    if (will_wont_resp[option] && my_state_is_wont(option))
53538689Sborman 		--will_wont_resp[option];
53638689Sborman 	}
53738689Sborman 
53838689Sborman 	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
53938811Sborman 	    switch (option) {
54038811Sborman 	    case TELOPT_LINEMODE:
54138811Sborman 		linemode = 0;	/* put us back to the default state */
54238811Sborman 		break;
54338811Sborman 	    }
54438689Sborman 	    /* we always accept a DONT */
54538689Sborman 	    set_my_want_state_wont(option);
54644360Sborman 	    if (my_state_is_will(option))
54744360Sborman 		send_wont(option, 0);
54839529Sborman 	    setconnmode(0);			/* Set new tty mode */
54938689Sborman 	}
55038689Sborman 	set_my_state_wont(option);
55138689Sborman }
55238689Sborman 
55327676Sminshall /*
55438908Sborman  * Given a buffer returned by tgetent(), this routine will turn
55538908Sborman  * the pipe seperated list of names in the buffer into an array
55638908Sborman  * of pointers to null terminated names.  We toss out any bad,
55738908Sborman  * duplicate, or verbose names (names with spaces).
55838908Sborman  */
55938908Sborman 
56046808Sdab static char *name_unknown = "UNKNOWN";
56146808Sdab static char *unknown[] = { 0, 0 };
56238908Sborman 
56346808Sdab 	char **
56438908Sborman mklist(buf, name)
56546808Sdab 	char *buf, *name;
56638908Sborman {
56738908Sborman 	register int n;
56846808Sdab 	register char c, *cp, **argvp, *cp2, **argv, **avt;
56938908Sborman 
57038908Sborman 	if (name) {
57146808Sdab 		if (strlen(name) > 40) {
57238908Sborman 			name = 0;
57346808Sdab 			unknown[0] = name_unknown;
57446808Sdab 		} else {
57538908Sborman 			unknown[0] = name;
57638908Sborman 			upcase(name);
57738908Sborman 		}
57846808Sdab 	} else
57946808Sdab 		unknown[0] = name_unknown;
58038908Sborman 	/*
58138908Sborman 	 * Count up the number of names.
58238908Sborman 	 */
58338908Sborman 	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
58438908Sborman 		if (*cp == '|')
58538908Sborman 			n++;
58638908Sborman 	}
58738908Sborman 	/*
58838908Sborman 	 * Allocate an array to put the name pointers into
58938908Sborman 	 */
59038908Sborman 	argv = (char **)malloc((n+3)*sizeof(char *));
59138908Sborman 	if (argv == 0)
59238908Sborman 		return(unknown);
59338908Sborman 
59438908Sborman 	/*
59538908Sborman 	 * Fill up the array of pointers to names.
59638908Sborman 	 */
59738908Sborman 	*argv = 0;
59838908Sborman 	argvp = argv+1;
59938908Sborman 	n = 0;
60038908Sborman 	for (cp = cp2 = buf; (c = *cp);  cp++) {
60138908Sborman 		if (c == '|' || c == ':') {
60238908Sborman 			*cp++ = '\0';
60338908Sborman 			/*
60438908Sborman 			 * Skip entries that have spaces or are over 40
60538908Sborman 			 * characters long.  If this is our environment
60638908Sborman 			 * name, then put it up front.  Otherwise, as
60738908Sborman 			 * long as this is not a duplicate name (case
60838908Sborman 			 * insensitive) add it to the list.
60938908Sborman 			 */
61038908Sborman 			if (n || (cp - cp2 > 41))
61138908Sborman 				;
61238908Sborman 			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
61338908Sborman 				*argv = cp2;
61438908Sborman 			else if (is_unique(cp2, argv+1, argvp))
61538908Sborman 				*argvp++ = cp2;
61638908Sborman 			if (c == ':')
61738908Sborman 				break;
61838908Sborman 			/*
61938908Sborman 			 * Skip multiple delimiters. Reset cp2 to
62038908Sborman 			 * the beginning of the next name. Reset n,
62138908Sborman 			 * the flag for names with spaces.
62238908Sborman 			 */
62338908Sborman 			while ((c = *cp) == '|')
62438908Sborman 				cp++;
62538908Sborman 			cp2 = cp;
62638908Sborman 			n = 0;
62738908Sborman 		}
62838908Sborman 		/*
62938908Sborman 		 * Skip entries with spaces or non-ascii values.
63038908Sborman 		 * Convert lower case letters to upper case.
63138908Sborman 		 */
63238908Sborman 		if ((c == ' ') || !isascii(c))
63338908Sborman 			n = 1;
63438908Sborman 		else if (islower(c))
63538908Sborman 			*cp = toupper(c);
63638908Sborman 	}
63738908Sborman 
63838908Sborman 	/*
63938908Sborman 	 * Check for an old V6 2 character name.  If the second
64038908Sborman 	 * name points to the beginning of the buffer, and is
64138908Sborman 	 * only 2 characters long, move it to the end of the array.
64238908Sborman 	 */
64338908Sborman 	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
64446808Sdab 		--argvp;
64546808Sdab 		for (avt = &argv[1]; avt < argvp; avt++)
64646808Sdab 			*avt = *(avt+1);
64738908Sborman 		*argvp++ = buf;
64838908Sborman 	}
64938908Sborman 
65038908Sborman 	/*
65138908Sborman 	 * Duplicate last name, for TTYPE option, and null
65238908Sborman 	 * terminate the array.  If we didn't find a match on
65338908Sborman 	 * our terminal name, put that name at the beginning.
65438908Sborman 	 */
65538908Sborman 	cp = *(argvp-1);
65638908Sborman 	*argvp++ = cp;
65738908Sborman 	*argvp = 0;
65838908Sborman 
65938908Sborman 	if (*argv == 0) {
66038908Sborman 		if (name)
66138908Sborman 			*argv = name;
66246808Sdab 		else {
66346808Sdab 			--argvp;
66446808Sdab 			for (avt = argv; avt < argvp; avt++)
66546808Sdab 				*avt = *(avt+1);
66646808Sdab 		}
66738908Sborman 	}
66838908Sborman 	if (*argv)
66938908Sborman 		return(argv);
67038908Sborman 	else
67138908Sborman 		return(unknown);
67238908Sborman }
67338908Sborman 
67446808Sdab 	int
67538908Sborman is_unique(name, as, ae)
67646808Sdab 	register char *name, **as, **ae;
67738908Sborman {
67838908Sborman 	register char **ap;
67938908Sborman 	register int n;
68038908Sborman 
68138908Sborman 	n = strlen(name) + 1;
68238908Sborman 	for (ap = as; ap < ae; ap++)
68338908Sborman 		if (strncasecmp(*ap, name, n) == 0)
68438908Sborman 			return(0);
68538908Sborman 	return (1);
68638908Sborman }
68738908Sborman 
68838908Sborman #ifdef	TERMCAP
68939529Sborman char termbuf[1024];
69046808Sdab 
69146808Sdab 	/*ARGSUSED*/
69246808Sdab 	int
69338908Sborman setupterm(tname, fd, errp)
69446808Sdab 	char *tname;
69546808Sdab 	int fd, *errp;
69638908Sborman {
69739529Sborman 	if (tgetent(termbuf, tname) == 1) {
69839529Sborman 		termbuf[1023] = '\0';
69938908Sborman 		if (errp)
70038908Sborman 			*errp = 1;
70138908Sborman 		return(0);
70238908Sborman 	}
70338908Sborman 	if (errp)
70438908Sborman 		*errp = 0;
70538908Sborman 	return(-1);
70638908Sborman }
70739529Sborman #else
70839529Sborman #define	termbuf	ttytype
70939529Sborman extern char ttytype[];
71038908Sborman #endif
71138908Sborman 
71246808Sdab int resettermname = 1;
71346808Sdab 
71446808Sdab 	char *
71538908Sborman gettermname()
71638908Sborman {
71738908Sborman 	char *tname;
71846808Sdab 	static char **tnamep = 0;
71938908Sborman 	static char **next;
72038908Sborman 	int err;
72138908Sborman 
72246808Sdab 	if (resettermname) {
72346808Sdab 		resettermname = 0;
72446808Sdab 		if (tnamep && tnamep != unknown)
72546808Sdab 			free(tnamep);
72646808Sdab 		if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) &&
72738908Sborman 				(setupterm(tname, 1, &err) == 0)) {
72839529Sborman 			tnamep = mklist(termbuf, tname);
72938908Sborman 		} else {
73038908Sborman 			if (tname && (strlen(tname) <= 40)) {
73138908Sborman 				unknown[0] = tname;
73238908Sborman 				upcase(tname);
73346808Sdab 			} else
73446808Sdab 				unknown[0] = name_unknown;
73538908Sborman 			tnamep = unknown;
73638908Sborman 		}
73738908Sborman 		next = tnamep;
73838908Sborman 	}
73938908Sborman 	if (*next == 0)
74038908Sborman 		next = tnamep;
74138908Sborman 	return(*next++);
74238908Sborman }
74338908Sborman /*
74427676Sminshall  * suboption()
74527676Sminshall  *
74627676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
74727676Sminshall  * side.
74827676Sminshall  *
74927676Sminshall  *	Currently we recognize:
75027676Sminshall  *
75127676Sminshall  *		Terminal type, send request.
75237219Sminshall  *		Terminal speed (send request).
75337219Sminshall  *		Local flow control (is request).
75438689Sborman  *		Linemode
75527676Sminshall  */
75627676Sminshall 
75746808Sdab     static void
75827676Sminshall suboption()
75927676Sminshall {
76046808Sdab     printsub('<', subbuffer, SB_LEN()+2);
76146808Sdab     switch (SB_GET()) {
76227676Sminshall     case TELOPT_TTYPE:
76338689Sborman 	if (my_want_state_is_wont(TELOPT_TTYPE))
76438689Sborman 	    return;
76546808Sdab 	if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
76646808Sdab 	    return;
76727676Sminshall 	} else {
76827676Sminshall 	    char *name;
76946808Sdab 	    unsigned char temp[50];
77027676Sminshall 	    int len;
77127676Sminshall 
77232377Sminshall #if	defined(TN3270)
77332531Sminshall 	    if (tn3270_ttype()) {
77432377Sminshall 		return;
77532377Sminshall 	    }
77632377Sminshall #endif	/* defined(TN3270) */
77738908Sborman 	    name = gettermname();
77838908Sborman 	    len = strlen(name) + 4 + 2;
77938908Sborman 	    if (len < NETROOM()) {
78046808Sdab 		sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
78138908Sborman 				TELQUAL_IS, name, IAC, SE);
78238689Sborman 		ring_supply_data(&netoring, temp, len);
78338908Sborman 		printsub('>', &temp[2], len-2);
78432377Sminshall 	    } else {
78537226Sminshall 		ExitString("No room in buffer for terminal type.\n", 1);
78632377Sminshall 		/*NOTREACHED*/
78727676Sminshall 	    }
78827676Sminshall 	}
78937219Sminshall 	break;
79037219Sminshall     case TELOPT_TSPEED:
79138689Sborman 	if (my_want_state_is_wont(TELOPT_TSPEED))
79238689Sborman 	    return;
79346808Sdab 	if (SB_EOF())
79446808Sdab 	    return;
79546808Sdab 	if (SB_GET() == TELQUAL_SEND) {
79645232Sborman 	    long ospeed, ispeed;
79746808Sdab 	    unsigned char temp[50];
79837219Sminshall 	    int len;
79927676Sminshall 
80037219Sminshall 	    TerminalSpeeds(&ispeed, &ospeed);
80137219Sminshall 
80246808Sdab 	    sprintf((char *)temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
80338689Sborman 		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
80446808Sdab 	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
80537219Sminshall 
80638689Sborman 	    if (len < NETROOM()) {
80738689Sborman 		ring_supply_data(&netoring, temp, len);
80838689Sborman 		printsub('>', temp+2, len - 2);
80937219Sminshall 	    }
81044360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
81137219Sminshall 	}
81237219Sminshall 	break;
81337219Sminshall     case TELOPT_LFLOW:
81438689Sborman 	if (my_want_state_is_wont(TELOPT_LFLOW))
81538689Sborman 	    return;
81646808Sdab 	if (SB_EOF())
81746808Sdab 	    return;
81846808Sdab 	switch(SB_GET()) {
81957213Sdab 	case LFLOW_RESTART_ANY:
82057213Sdab 	    restartany = 1;
82157213Sdab 	    break;
82257213Sdab 	case LFLOW_RESTART_XON:
82357213Sdab 	    restartany = 0;
82457213Sdab 	    break;
82557213Sdab 	case LFLOW_ON:
82637219Sminshall 	    localflow = 1;
82746808Sdab 	    break;
82857213Sdab 	case LFLOW_OFF:
82937219Sminshall 	    localflow = 0;
83046808Sdab 	    break;
83146808Sdab 	default:
83246808Sdab 	    return;
83337219Sminshall 	}
83437219Sminshall 	setcommandmode();
83538689Sborman 	setconnmode(0);
83637219Sminshall 	break;
83738689Sborman 
83838689Sborman     case TELOPT_LINEMODE:
83938689Sborman 	if (my_want_state_is_wont(TELOPT_LINEMODE))
84038689Sborman 	    return;
84146808Sdab 	if (SB_EOF())
84246808Sdab 	    return;
84346808Sdab 	switch (SB_GET()) {
84438689Sborman 	case WILL:
84546808Sdab 	    lm_will(subpointer, SB_LEN());
84638689Sborman 	    break;
84738689Sborman 	case WONT:
84846808Sdab 	    lm_wont(subpointer, SB_LEN());
84938689Sborman 	    break;
85038689Sborman 	case DO:
85146808Sdab 	    lm_do(subpointer, SB_LEN());
85238689Sborman 	    break;
85338689Sborman 	case DONT:
85446808Sdab 	    lm_dont(subpointer, SB_LEN());
85538689Sborman 	    break;
85638689Sborman 	case LM_SLC:
85746808Sdab 	    slc(subpointer, SB_LEN());
85838689Sborman 	    break;
85938689Sborman 	case LM_MODE:
86046808Sdab 	    lm_mode(subpointer, SB_LEN(), 0);
86138689Sborman 	    break;
86238689Sborman 	default:
86344360Sborman 	    break;
86444360Sborman 	}
86544360Sborman 	break;
86644360Sborman 
86744360Sborman     case TELOPT_ENVIRON:
86846808Sdab 	if (SB_EOF())
86946808Sdab 	    return;
87046808Sdab 	switch(SB_PEEK()) {
87144360Sborman 	case TELQUAL_IS:
87244360Sborman 	case TELQUAL_INFO:
87344360Sborman 	    if (my_want_state_is_dont(TELOPT_ENVIRON))
87444360Sborman 		return;
87544360Sborman 	    break;
87644360Sborman 	case TELQUAL_SEND:
87744360Sborman 	    if (my_want_state_is_wont(TELOPT_ENVIRON)) {
87844360Sborman 		return;
87944360Sborman 	    }
88044360Sborman 	    break;
88144360Sborman 	default:
88244360Sborman 	    return;
88344360Sborman 	}
88446808Sdab 	env_opt(subpointer, SB_LEN());
88544360Sborman 	break;
88644360Sborman 
88744360Sborman     case TELOPT_XDISPLOC:
88844360Sborman 	if (my_want_state_is_wont(TELOPT_XDISPLOC))
88944360Sborman 	    return;
89046808Sdab 	if (SB_EOF())
89146808Sdab 	    return;
89246808Sdab 	if (SB_GET() == TELQUAL_SEND) {
89346808Sdab 	    unsigned char temp[50], *dp;
89444360Sborman 	    int len;
89544360Sborman 
89646808Sdab 	    if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) {
89744360Sborman 		/*
89844360Sborman 		 * Something happened, we no longer have a DISPLAY
89944360Sborman 		 * variable.  So, turn off the option.
90044360Sborman 		 */
90144360Sborman 		send_wont(TELOPT_XDISPLOC, 1);
90238689Sborman 		break;
90344360Sborman 	    }
90446808Sdab 	    sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
90544360Sborman 		    TELQUAL_IS, dp, IAC, SE);
90646808Sdab 	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
90744360Sborman 
90844360Sborman 	    if (len < NETROOM()) {
90944360Sborman 		ring_supply_data(&netoring, temp, len);
91044360Sborman 		printsub('>', temp+2, len - 2);
91144360Sborman 	    }
91244360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
91338689Sborman 	}
91444360Sborman 	break;
91543319Skfall 
91657213Sdab #if	defined(AUTHENTICATION)
91746808Sdab 	case TELOPT_AUTHENTICATION: {
91846808Sdab 		if (!autologin)
91946808Sdab 			break;
92046808Sdab 		if (SB_EOF())
92146808Sdab 			return;
92246808Sdab 		switch(SB_GET()) {
92346808Sdab 		case TELQUAL_IS:
92446808Sdab 			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
92546808Sdab 				return;
92646808Sdab 			auth_is(subpointer, SB_LEN());
92746808Sdab 			break;
92846808Sdab 		case TELQUAL_SEND:
92946808Sdab 			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
93046808Sdab 				return;
93146808Sdab 			auth_send(subpointer, SB_LEN());
93246808Sdab 			break;
93346808Sdab 		case TELQUAL_REPLY:
93446808Sdab 			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
93546808Sdab 				return;
93646808Sdab 			auth_reply(subpointer, SB_LEN());
93746808Sdab 			break;
93847608Sdab 		case TELQUAL_NAME:
93947608Sdab 			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
94047608Sdab 				return;
94147608Sdab 			auth_name(subpointer, SB_LEN());
94247608Sdab 			break;
94343319Skfall 		}
94446808Sdab 	}
94546808Sdab 	break;
94643319Skfall #endif
947*60149Sdab #ifdef	ENCRYPTION
94846808Sdab 	case TELOPT_ENCRYPT:
94946808Sdab 		if (SB_EOF())
95046808Sdab 			return;
95146808Sdab 		switch(SB_GET()) {
95246808Sdab 		case ENCRYPT_START:
95346808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
95446808Sdab 				return;
95547608Sdab 			encrypt_start(subpointer, SB_LEN());
95646808Sdab 			break;
95746808Sdab 		case ENCRYPT_END:
95846808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
95946808Sdab 				return;
96046808Sdab 			encrypt_end();
96146808Sdab 			break;
96246808Sdab 		case ENCRYPT_SUPPORT:
96346808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
96446808Sdab 				return;
96546808Sdab 			encrypt_support(subpointer, SB_LEN());
96646808Sdab 			break;
96746808Sdab 		case ENCRYPT_REQSTART:
96846808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
96946808Sdab 				return;
97047608Sdab 			encrypt_request_start(subpointer, SB_LEN());
97146808Sdab 			break;
97246808Sdab 		case ENCRYPT_REQEND:
97346808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
97446808Sdab 				return;
97546808Sdab 			/*
97646808Sdab 			 * We can always send an REQEND so that we cannot
97746808Sdab 			 * get stuck encrypting.  We should only get this
97846808Sdab 			 * if we have been able to get in the correct mode
97946808Sdab 			 * anyhow.
98046808Sdab 			 */
98146808Sdab 			encrypt_request_end();
98246808Sdab 			break;
98346808Sdab 		case ENCRYPT_IS:
98446808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
98546808Sdab 				return;
98646808Sdab 			encrypt_is(subpointer, SB_LEN());
98746808Sdab 			break;
98846808Sdab 		case ENCRYPT_REPLY:
98946808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
99046808Sdab 				return;
99146808Sdab 			encrypt_reply(subpointer, SB_LEN());
99246808Sdab 			break;
99347608Sdab 		case ENCRYPT_ENC_KEYID:
99447608Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
99547608Sdab 				return;
99647608Sdab 			encrypt_enc_keyid(subpointer, SB_LEN());
99747608Sdab 			break;
99847608Sdab 		case ENCRYPT_DEC_KEYID:
99947608Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
100047608Sdab 				return;
100147608Sdab 			encrypt_dec_keyid(subpointer, SB_LEN());
100247608Sdab 			break;
100346808Sdab 		default:
100446808Sdab 			break;
100546808Sdab 		}
100646808Sdab 		break;
1007*60149Sdab #endif	/* ENCRYPTION */
100827676Sminshall     default:
100927676Sminshall 	break;
101027676Sminshall     }
101127676Sminshall }
101238689Sborman 
101346808Sdab static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
101438689Sborman 
101546808Sdab     void
101638689Sborman lm_will(cmd, len)
101746808Sdab     unsigned char *cmd;
101846808Sdab     int len;
101938689Sborman {
102044360Sborman     if (len < 1) {
102144360Sborman /*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
102244360Sborman 	return;
102344360Sborman     }
102438689Sborman     switch(cmd[0]) {
102538689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
102638689Sborman     default:
102738689Sborman 	str_lm[3] = DONT;
102838689Sborman 	str_lm[4] = cmd[0];
102938689Sborman 	if (NETROOM() > sizeof(str_lm)) {
103038689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
103138689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
103238689Sborman 	}
103338689Sborman /*@*/	else printf("lm_will: not enough room in buffer\n");
103438689Sborman 	break;
103538689Sborman     }
103638689Sborman }
103738689Sborman 
103846808Sdab     void
103938689Sborman lm_wont(cmd, len)
104046808Sdab     unsigned char *cmd;
104146808Sdab     int len;
104238689Sborman {
104344360Sborman     if (len < 1) {
104444360Sborman /*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
104544360Sborman 	return;
104644360Sborman     }
104738689Sborman     switch(cmd[0]) {
104838689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
104938689Sborman     default:
105038689Sborman 	/* We are always DONT, so don't respond */
105138689Sborman 	return;
105238689Sborman     }
105338689Sborman }
105438689Sborman 
105546808Sdab     void
105638689Sborman lm_do(cmd, len)
105746808Sdab     unsigned char *cmd;
105846808Sdab     int len;
105938689Sborman {
106044360Sborman     if (len < 1) {
106144360Sborman /*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
106244360Sborman 	return;
106344360Sborman     }
106438689Sborman     switch(cmd[0]) {
106538689Sborman     case LM_FORWARDMASK:
106638689Sborman     default:
106738689Sborman 	str_lm[3] = WONT;
106838689Sborman 	str_lm[4] = cmd[0];
106938689Sborman 	if (NETROOM() > sizeof(str_lm)) {
107038689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
107138689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
107238689Sborman 	}
107338689Sborman /*@*/	else printf("lm_do: not enough room in buffer\n");
107438689Sborman 	break;
107538689Sborman     }
107638689Sborman }
107738689Sborman 
107846808Sdab     void
107938689Sborman lm_dont(cmd, len)
108046808Sdab     unsigned char *cmd;
108146808Sdab     int len;
108238689Sborman {
108344360Sborman     if (len < 1) {
108444360Sborman /*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
108544360Sborman 	return;
108644360Sborman     }
108738689Sborman     switch(cmd[0]) {
108838689Sborman     case LM_FORWARDMASK:
108938689Sborman     default:
109038689Sborman 	/* we are always WONT, so don't respond */
109138689Sborman 	break;
109238689Sborman     }
109338689Sborman }
109438689Sborman 
109546808Sdab static unsigned char str_lm_mode[] = {
109646808Sdab 	IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
109746808Sdab };
109838689Sborman 
109946808Sdab 	void
110038689Sborman lm_mode(cmd, len, init)
110146808Sdab 	unsigned char *cmd;
110246808Sdab 	int len, init;
110338689Sborman {
110438689Sborman 	if (len != 1)
110538689Sborman 		return;
110644360Sborman 	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
110738689Sborman 		return;
110838689Sborman 	if (*cmd&MODE_ACK)
110938689Sborman 		return;
111044360Sborman 	linemode = *cmd&(MODE_MASK&~MODE_ACK);
111138689Sborman 	str_lm_mode[4] = linemode;
111238689Sborman 	if (!init)
111338689Sborman 	    str_lm_mode[4] |= MODE_ACK;
111438689Sborman 	if (NETROOM() > sizeof(str_lm_mode)) {
111538689Sborman 	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
111638689Sborman 	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
111738689Sborman 	}
111838689Sborman /*@*/	else printf("lm_mode: not enough room in buffer\n");
111938689Sborman 	setconnmode(0);	/* set changed mode */
112038689Sborman }
112138689Sborman 
112232377Sminshall 
112327088Sminshall 
112438689Sborman /*
112538689Sborman  * slc()
112638689Sborman  * Handle special character suboption of LINEMODE.
112738689Sborman  */
112838689Sborman 
112938689Sborman struct spc {
113040245Sborman 	cc_t val;
113140245Sborman 	cc_t *valp;
113238689Sborman 	char flags;	/* Current flags & level */
113338689Sborman 	char mylevel;	/* Maximum level & flags */
113438689Sborman } spc_data[NSLC+1];
113538689Sborman 
113638689Sborman #define SLC_IMPORT	0
113738689Sborman #define	SLC_EXPORT	1
113838689Sborman #define SLC_RVALUE	2
113938689Sborman static int slc_mode = SLC_EXPORT;
114038689Sborman 
114146808Sdab 	void
114238689Sborman slc_init()
114338689Sborman {
114438689Sborman 	register struct spc *spcp;
114538689Sborman 
114638689Sborman 	localchars = 1;
114738689Sborman 	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
114838689Sborman 		spcp->val = 0;
114938689Sborman 		spcp->valp = 0;
115038689Sborman 		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
115138689Sborman 	}
115238689Sborman 
115338689Sborman #define	initfunc(func, flags) { \
115438689Sborman 					spcp = &spc_data[func]; \
115538689Sborman 					if (spcp->valp = tcval(func)) { \
115638689Sborman 					    spcp->val = *spcp->valp; \
115738689Sborman 					    spcp->mylevel = SLC_VARIABLE|flags; \
115838689Sborman 					} else { \
115938689Sborman 					    spcp->val = 0; \
116038689Sborman 					    spcp->mylevel = SLC_DEFAULT; \
116138689Sborman 					} \
116238689Sborman 				    }
116338689Sborman 
116438689Sborman 	initfunc(SLC_SYNCH, 0);
116538689Sborman 	/* No BRK */
116638689Sborman 	initfunc(SLC_AO, 0);
116738689Sborman 	initfunc(SLC_AYT, 0);
116838689Sborman 	/* No EOR */
116938689Sborman 	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
117038689Sborman 	initfunc(SLC_EOF, 0);
117139529Sborman #ifndef	SYSV_TERMIO
117238689Sborman 	initfunc(SLC_SUSP, SLC_FLUSHIN);
117338689Sborman #endif
117438689Sborman 	initfunc(SLC_EC, 0);
117538689Sborman 	initfunc(SLC_EL, 0);
117639529Sborman #ifndef	SYSV_TERMIO
117738689Sborman 	initfunc(SLC_EW, 0);
117838689Sborman 	initfunc(SLC_RP, 0);
117938689Sborman 	initfunc(SLC_LNEXT, 0);
118038689Sborman #endif
118138689Sborman 	initfunc(SLC_XON, 0);
118238689Sborman 	initfunc(SLC_XOFF, 0);
118339529Sborman #ifdef	SYSV_TERMIO
118438689Sborman 	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
118538689Sborman 	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
118638689Sborman #endif
118744360Sborman 	initfunc(SLC_FORW1, 0);
118844360Sborman #ifdef	USE_TERMIO
118944360Sborman 	initfunc(SLC_FORW2, 0);
119038689Sborman 	/* No FORW2 */
119144360Sborman #endif
119238689Sborman 
119338689Sborman 	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
119438689Sborman #undef	initfunc
119538689Sborman 
119638689Sborman 	if (slc_mode == SLC_EXPORT)
119738689Sborman 		slc_export();
119838689Sborman 	else
119938689Sborman 		slc_import(1);
120038689Sborman 
120138689Sborman }
120238689Sborman 
120346808Sdab     void
120438689Sborman slcstate()
120538689Sborman {
120638689Sborman     printf("Special characters are %s values\n",
120738689Sborman 		slc_mode == SLC_IMPORT ? "remote default" :
120838689Sborman 		slc_mode == SLC_EXPORT ? "local" :
120938689Sborman 					 "remote");
121038689Sborman }
121138689Sborman 
121246808Sdab     void
121338689Sborman slc_mode_export()
121438689Sborman {
121538689Sborman     slc_mode = SLC_EXPORT;
121638689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
121738689Sborman 	slc_export();
121838689Sborman }
121938689Sborman 
122046808Sdab     void
122138689Sborman slc_mode_import(def)
122246808Sdab     int def;
122338689Sborman {
122438689Sborman     slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
122538689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
122638689Sborman 	slc_import(def);
122738689Sborman }
122838689Sborman 
122946808Sdab unsigned char slc_import_val[] = {
123038689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
123138689Sborman };
123246808Sdab unsigned char slc_import_def[] = {
123338689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
123438689Sborman };
123538689Sborman 
123646808Sdab     void
123738689Sborman slc_import(def)
123846808Sdab     int def;
123938689Sborman {
124038689Sborman     if (NETROOM() > sizeof(slc_import_val)) {
124138689Sborman 	if (def) {
124238689Sborman 	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
124338689Sborman 	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
124438689Sborman 	} else {
124538689Sborman 	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
124638689Sborman 	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
124738689Sborman 	}
124838689Sborman     }
124938689Sborman /*@*/ else printf("slc_import: not enough room\n");
125038689Sborman }
125138689Sborman 
125246808Sdab     void
125338689Sborman slc_export()
125438689Sborman {
125538689Sborman     register struct spc *spcp;
125638689Sborman 
125738689Sborman     TerminalDefaultChars();
125838689Sborman 
125938689Sborman     slc_start_reply();
126038689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
126138689Sborman 	if (spcp->mylevel != SLC_NOSUPPORT) {
126245232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
126345232Sborman 		spcp->flags = SLC_NOSUPPORT;
126445232Sborman 	    else
126545232Sborman 		spcp->flags = spcp->mylevel;
126638689Sborman 	    if (spcp->valp)
126738689Sborman 		spcp->val = *spcp->valp;
126845232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
126938689Sborman 	}
127038689Sborman     }
127138689Sborman     slc_end_reply();
127246808Sdab     (void)slc_update();
127346808Sdab     setconnmode(1);	/* Make sure the character values are set */
127438689Sborman }
127538689Sborman 
127646808Sdab 	void
127738689Sborman slc(cp, len)
127846808Sdab 	register unsigned char *cp;
127946808Sdab 	int len;
128038689Sborman {
128138689Sborman 	register struct spc *spcp;
128238689Sborman 	register int func,level;
128338689Sborman 
128438689Sborman 	slc_start_reply();
128538689Sborman 
128638689Sborman 	for (; len >= 3; len -=3, cp +=3) {
128738689Sborman 
128838689Sborman 		func = cp[SLC_FUNC];
128938689Sborman 
129038689Sborman 		if (func == 0) {
129138689Sborman 			/*
129238689Sborman 			 * Client side: always ignore 0 function.
129338689Sborman 			 */
129438689Sborman 			continue;
129538689Sborman 		}
129638689Sborman 		if (func > NSLC) {
129745232Sborman 			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
129838689Sborman 				slc_add_reply(func, SLC_NOSUPPORT, 0);
129938689Sborman 			continue;
130038689Sborman 		}
130138689Sborman 
130238689Sborman 		spcp = &spc_data[func];
130338689Sborman 
130438689Sborman 		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
130538689Sborman 
130640245Sborman 		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
130738689Sborman 		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
130838689Sborman 			continue;
130938689Sborman 		}
131038689Sborman 
131138689Sborman 		if (level == (SLC_DEFAULT|SLC_ACK)) {
131238689Sborman 			/*
131338689Sborman 			 * This is an error condition, the SLC_ACK
131438689Sborman 			 * bit should never be set for the SLC_DEFAULT
131538689Sborman 			 * level.  Our best guess to recover is to
131638689Sborman 			 * ignore the SLC_ACK bit.
131738689Sborman 			 */
131838689Sborman 			cp[SLC_FLAGS] &= ~SLC_ACK;
131938689Sborman 		}
132038689Sborman 
132138689Sborman 		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
132240245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
132338689Sborman 			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
132438689Sborman 			continue;
132538689Sborman 		}
132638689Sborman 
132738689Sborman 		level &= ~SLC_ACK;
132838689Sborman 
132938689Sborman 		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
133038689Sborman 			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
133140245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
133238689Sborman 		}
133338689Sborman 		if (level == SLC_DEFAULT) {
133438689Sborman 			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
133538689Sborman 				spcp->flags = spcp->mylevel;
133638689Sborman 			else
133738689Sborman 				spcp->flags = SLC_NOSUPPORT;
133838689Sborman 		}
133938689Sborman 		slc_add_reply(func, spcp->flags, spcp->val);
134038689Sborman 	}
134138689Sborman 	slc_end_reply();
134238689Sborman 	if (slc_update())
134338689Sborman 		setconnmode(1);	/* set the  new character values */
134438689Sborman }
134538689Sborman 
134646808Sdab     void
134738689Sborman slc_check()
134838689Sborman {
134938689Sborman     register struct spc *spcp;
135038689Sborman 
135138689Sborman     slc_start_reply();
135238689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
135338689Sborman 	if (spcp->valp && spcp->val != *spcp->valp) {
135438689Sborman 	    spcp->val = *spcp->valp;
135545232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
135645232Sborman 		spcp->flags = SLC_NOSUPPORT;
135745232Sborman 	    else
135845232Sborman 		spcp->flags = spcp->mylevel;
135945232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
136038689Sborman 	}
136138689Sborman     }
136238689Sborman     slc_end_reply();
136338689Sborman     setconnmode(1);
136438689Sborman }
136538689Sborman 
136638689Sborman 
136738689Sborman unsigned char slc_reply[128];
136838689Sborman unsigned char *slc_replyp;
136946808Sdab 
137046808Sdab 	void
137138689Sborman slc_start_reply()
137238689Sborman {
137338689Sborman 	slc_replyp = slc_reply;
137438689Sborman 	*slc_replyp++ = IAC;
137538689Sborman 	*slc_replyp++ = SB;
137638689Sborman 	*slc_replyp++ = TELOPT_LINEMODE;
137738689Sborman 	*slc_replyp++ = LM_SLC;
137838689Sborman }
137938689Sborman 
138046808Sdab 	void
138138689Sborman slc_add_reply(func, flags, value)
138246808Sdab 	unsigned char func;
138346808Sdab 	unsigned char flags;
138446808Sdab 	cc_t value;
138538689Sborman {
138638689Sborman 	if ((*slc_replyp++ = func) == IAC)
138738689Sborman 		*slc_replyp++ = IAC;
138838689Sborman 	if ((*slc_replyp++ = flags) == IAC)
138938689Sborman 		*slc_replyp++ = IAC;
139040245Sborman 	if ((*slc_replyp++ = (unsigned char)value) == IAC)
139138689Sborman 		*slc_replyp++ = IAC;
139238689Sborman }
139338689Sborman 
139446808Sdab     void
139538689Sborman slc_end_reply()
139638689Sborman {
139738689Sborman     register int len;
139838689Sborman 
139938689Sborman     *slc_replyp++ = IAC;
140038689Sborman     *slc_replyp++ = SE;
140138689Sborman     len = slc_replyp - slc_reply;
140238689Sborman     if (len <= 6)
140338689Sborman 	return;
140438689Sborman     if (NETROOM() > len) {
140538689Sborman 	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
140638689Sborman 	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
140738689Sborman     }
140838689Sborman /*@*/else printf("slc_end_reply: not enough room\n");
140938689Sborman }
141038689Sborman 
141146808Sdab 	int
141238689Sborman slc_update()
141338689Sborman {
141438689Sborman 	register struct spc *spcp;
141538689Sborman 	int need_update = 0;
141638689Sborman 
141738689Sborman 	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
141838689Sborman 		if (!(spcp->flags&SLC_ACK))
141938689Sborman 			continue;
142038689Sborman 		spcp->flags &= ~SLC_ACK;
142138689Sborman 		if (spcp->valp && (*spcp->valp != spcp->val)) {
142238689Sborman 			*spcp->valp = spcp->val;
142338689Sborman 			need_update = 1;
142438689Sborman 		}
142538689Sborman 	}
142638689Sborman 	return(need_update);
142738689Sborman }
142838689Sborman 
142958972Sdab #ifdef	ENV_HACK
143058972Sdab /*
143158972Sdab  * Earlier version of telnet/telnetd from the BSD code had
143258972Sdab  * the definitions of VALUE and VAR reversed.  To ensure
143358972Sdab  * maximum interoperability, we assume that the server is
143458972Sdab  * an older BSD server, until proven otherwise.  The newer
143558972Sdab  * BSD servers should be able to handle either definition,
143658972Sdab  * so it is better to use the wrong values if we don't
143758972Sdab  * know what type of server it is.
143858972Sdab  */
143958972Sdab int env_auto = 1;
144058972Sdab int env_var = ENV_VALUE;
144158972Sdab int env_value = ENV_VAR;
144258972Sdab #else
144358972Sdab #define env_var ENV_VAR
144458972Sdab #define env_value ENV_VALUE
144558972Sdab #endif
144658972Sdab 
144746808Sdab 	void
144844360Sborman env_opt(buf, len)
144946808Sdab 	register unsigned char *buf;
145046808Sdab 	register int len;
145144360Sborman {
145246808Sdab 	register unsigned char *ep = 0, *epc = 0;
145344360Sborman 	register int i;
145444360Sborman 
145545232Sborman 	switch(buf[0]&0xff) {
145644360Sborman 	case TELQUAL_SEND:
145744360Sborman 		env_opt_start();
145844360Sborman 		if (len == 1) {
145944360Sborman 			env_opt_add(NULL);
146044360Sborman 		} else for (i = 1; i < len; i++) {
146145232Sborman 			switch (buf[i]&0xff) {
146258972Sdab 			case ENV_VAR:
146358972Sdab #ifdef	ENV_HACK
146458972Sdab 				if (env_auto) {
146558972Sdab 					/* The server has correct definitions */
146658972Sdab 					env_var = ENV_VAR;
146758972Sdab 					env_value = ENV_VALUE;
146858972Sdab 				}
146958972Sdab 				/* FALL THROUGH */
147058972Sdab #endif
147144360Sborman 			case ENV_VALUE:
147258972Sdab 				/*
147358972Sdab 				 * Although ENV_VALUE is not legal, we will
147458972Sdab 				 * still recognize it, just in case it is an
147558972Sdab 				 * old server that has VAR & VALUE mixed up...
147658972Sdab 				 */
147758972Sdab 				/* FALL THROUGH */
147858972Sdab 			case ENV_USERVAR:
147944360Sborman 				if (ep) {
148044360Sborman 					*epc = 0;
148144360Sborman 					env_opt_add(ep);
148244360Sborman 				}
148344360Sborman 				ep = epc = &buf[i+1];
148444360Sborman 				break;
148544360Sborman 			case ENV_ESC:
148644360Sborman 				i++;
148744360Sborman 				/*FALL THROUGH*/
148844360Sborman 			default:
148944360Sborman 				if (epc)
149044360Sborman 					*epc++ = buf[i];
149144360Sborman 				break;
149244360Sborman 			}
149344360Sborman 		}
149458972Sdab 		if (ep) {
149558972Sdab 			*epc = 0;
149658972Sdab 			env_opt_add(ep);
149758972Sdab 		}
149844360Sborman 		env_opt_end(1);
149944360Sborman 		break;
150044360Sborman 
150144360Sborman 	case TELQUAL_IS:
150244360Sborman 	case TELQUAL_INFO:
150344360Sborman 		/* Ignore for now.  We shouldn't get it anyway. */
150444360Sborman 		break;
150544360Sborman 
150644360Sborman 	default:
150744360Sborman 		break;
150844360Sborman 	}
150944360Sborman }
151044360Sborman 
151144360Sborman #define	OPT_REPLY_SIZE	256
151244360Sborman unsigned char *opt_reply;
151344360Sborman unsigned char *opt_replyp;
151444360Sborman unsigned char *opt_replyend;
151544360Sborman 
151646808Sdab 	void
151744360Sborman env_opt_start()
151844360Sborman {
151944360Sborman 	if (opt_reply)
152044360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
152144360Sborman 	else
152244360Sborman 		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
152344360Sborman 	if (opt_reply == NULL) {
152444360Sborman /*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
152544360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
152644360Sborman 		return;
152744360Sborman 	}
152844360Sborman 	opt_replyp = opt_reply;
152944360Sborman 	opt_replyend = opt_reply + OPT_REPLY_SIZE;
153044360Sborman 	*opt_replyp++ = IAC;
153144360Sborman 	*opt_replyp++ = SB;
153244360Sborman 	*opt_replyp++ = TELOPT_ENVIRON;
153344360Sborman 	*opt_replyp++ = TELQUAL_IS;
153444360Sborman }
153544360Sborman 
153646808Sdab 	void
153744360Sborman env_opt_start_info()
153844360Sborman {
153944360Sborman 	env_opt_start();
154044360Sborman 	if (opt_replyp)
154144360Sborman 	    opt_replyp[-1] = TELQUAL_INFO;
154244360Sborman }
154344360Sborman 
154446808Sdab 	void
154544360Sborman env_opt_add(ep)
154646808Sdab 	register unsigned char *ep;
154744360Sborman {
154846808Sdab 	register unsigned char *vp, c;
154944360Sborman 
155044360Sborman 	if (opt_reply == NULL)		/*XXX*/
155144360Sborman 		return;			/*XXX*/
155244360Sborman 
155344360Sborman 	if (ep == NULL || *ep == '\0') {
155457213Sdab 		/* Send user defined variables first. */
155557213Sdab 		env_default(1, 0);
155657213Sdab 		while (ep = env_default(0, 0))
155744360Sborman 			env_opt_add(ep);
155857213Sdab 
155957213Sdab 		/* Now add the list of well know variables.  */
156057213Sdab 		env_default(1, 1);
156157213Sdab 		while (ep = env_default(0, 1))
156257213Sdab 			env_opt_add(ep);
156344360Sborman 		return;
156444360Sborman 	}
156544360Sborman 	vp = env_getvalue(ep);
156646808Sdab 	if (opt_replyp + (vp ? strlen((char *)vp) : 0) +
156746808Sdab 				strlen((char *)ep) + 6 > opt_replyend)
156846808Sdab 	{
156944360Sborman 		register int len;
157044360Sborman 		opt_replyend += OPT_REPLY_SIZE;
157144360Sborman 		len = opt_replyend - opt_reply;
157244360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, len);
157344360Sborman 		if (opt_reply == NULL) {
157444360Sborman /*@*/			printf("env_opt_add: realloc() failed!!!\n");
157544360Sborman 			opt_reply = opt_replyp = opt_replyend = NULL;
157644360Sborman 			return;
157744360Sborman 		}
157844360Sborman 		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
157944360Sborman 		opt_replyend = opt_reply + len;
158044360Sborman 	}
158157213Sdab 	if (opt_welldefined(ep))
158258972Sdab 		*opt_replyp++ = env_var;
158357213Sdab 	else
158457213Sdab 		*opt_replyp++ = ENV_USERVAR;
158544360Sborman 	for (;;) {
158644360Sborman 		while (c = *ep++) {
158745232Sborman 			switch(c&0xff) {
158844360Sborman 			case IAC:
158944360Sborman 				*opt_replyp++ = IAC;
159044360Sborman 				break;
159144360Sborman 			case ENV_VALUE:
159244360Sborman 			case ENV_VAR:
159344360Sborman 			case ENV_ESC:
159457213Sdab 			case ENV_USERVAR:
159544360Sborman 				*opt_replyp++ = ENV_ESC;
159644360Sborman 				break;
159744360Sborman 			}
159844360Sborman 			*opt_replyp++ = c;
159944360Sborman 		}
160044360Sborman 		if (ep = vp) {
160158972Sdab 			*opt_replyp++ = env_value;
160244360Sborman 			vp = NULL;
160344360Sborman 		} else
160444360Sborman 			break;
160544360Sborman 	}
160644360Sborman }
160744360Sborman 
160857213Sdab 	int
160957213Sdab opt_welldefined(ep)
161057213Sdab 	char *ep;
161157213Sdab {
161257213Sdab 	if ((strcmp(ep, "USER") == 0) ||
161357213Sdab 	    (strcmp(ep, "DISPLAY") == 0) ||
161457213Sdab 	    (strcmp(ep, "PRINTER") == 0) ||
161557213Sdab 	    (strcmp(ep, "SYSTEMTYPE") == 0) ||
161657213Sdab 	    (strcmp(ep, "JOB") == 0) ||
161757213Sdab 	    (strcmp(ep, "ACCT") == 0))
161857213Sdab 		return(1);
161957213Sdab 	return(0);
162057213Sdab }
162146808Sdab 	void
162244360Sborman env_opt_end(emptyok)
162346808Sdab 	register int emptyok;
162444360Sborman {
162544360Sborman 	register int len;
162644360Sborman 
162744360Sborman 	len = opt_replyp - opt_reply + 2;
162844360Sborman 	if (emptyok || len > 6) {
162944360Sborman 		*opt_replyp++ = IAC;
163044360Sborman 		*opt_replyp++ = SE;
163144360Sborman 		if (NETROOM() > len) {
163244360Sborman 			ring_supply_data(&netoring, opt_reply, len);
163344360Sborman 			printsub('>', &opt_reply[2], len - 2);
163444360Sborman 		}
163544360Sborman /*@*/		else printf("slc_end_reply: not enough room\n");
163644360Sborman 	}
163744360Sborman 	if (opt_reply) {
163844360Sborman 		free(opt_reply);
163944360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
164044360Sborman 	}
164144360Sborman }
164244360Sborman 
164338689Sborman 
164438689Sborman 
164546808Sdab     int
164632377Sminshall telrcv()
164727110Sminshall {
164832377Sminshall     register int c;
164932385Sminshall     register int scc;
165046808Sdab     register unsigned char *sbp;
165132385Sminshall     int count;
165232385Sminshall     int returnValue = 0;
165327088Sminshall 
165432385Sminshall     scc = 0;
165532385Sminshall     count = 0;
165632385Sminshall     while (TTYROOM() > 2) {
165732385Sminshall 	if (scc == 0) {
165832385Sminshall 	    if (count) {
165932528Sminshall 		ring_consumed(&netiring, count);
166032385Sminshall 		returnValue = 1;
166132385Sminshall 		count = 0;
166232385Sminshall 	    }
166332528Sminshall 	    sbp = netiring.consume;
166432528Sminshall 	    scc = ring_full_consecutive(&netiring);
166532385Sminshall 	    if (scc == 0) {
166632385Sminshall 		/* No more data coming in */
166732385Sminshall 		break;
166832385Sminshall 	    }
166932385Sminshall 	}
167032385Sminshall 
167132385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
1672*60149Sdab #ifdef	ENCRYPTION
167346808Sdab 	if (decrypt_input)
167446808Sdab 		c = (*decrypt_input)(c);
1675*60149Sdab #endif	/* ENCRYPTION */
167632385Sminshall 
167732377Sminshall 	switch (telrcv_state) {
167827110Sminshall 
167932377Sminshall 	case TS_CR:
168032377Sminshall 	    telrcv_state = TS_DATA;
168135518Sminshall 	    if (c == '\0') {
168235518Sminshall 		break;	/* Ignore \0 after CR */
168339529Sborman 	    }
168439529Sborman 	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
168535518Sminshall 		TTYADD(c);
168635518Sminshall 		break;
168732377Sminshall 	    }
168835518Sminshall 	    /* Else, fall through */
168927088Sminshall 
169032377Sminshall 	case TS_DATA:
169132377Sminshall 	    if (c == IAC) {
169232377Sminshall 		telrcv_state = TS_IAC;
169333804Sminshall 		break;
169432377Sminshall 	    }
169532377Sminshall #	    if defined(TN3270)
169632377Sminshall 	    if (In3270) {
169732377Sminshall 		*Ifrontp++ = c;
169832385Sminshall 		while (scc > 0) {
169932385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
1700*60149Sdab #ifdef	ENCRYPTION
170146808Sdab 		    if (decrypt_input)
170246808Sdab 			c = (*decrypt_input)(c);
1703*60149Sdab #endif	/* ENCRYPTION */
170432377Sminshall 		    if (c == IAC) {
170532377Sminshall 			telrcv_state = TS_IAC;
170634304Sminshall 			break;
170732377Sminshall 		    }
170832377Sminshall 		    *Ifrontp++ = c;
170932377Sminshall 		}
171032377Sminshall 	    } else
171132377Sminshall #	    endif /* defined(TN3270) */
171235518Sminshall 		    /*
171335518Sminshall 		     * The 'crmod' hack (see following) is needed
171435518Sminshall 		     * since we can't * set CRMOD on output only.
171535518Sminshall 		     * Machines like MULTICS like to send \r without
171635518Sminshall 		     * \n; since we must turn off CRMOD to get proper
171735518Sminshall 		     * input, the mapping is done here (sigh).
171835518Sminshall 		     */
171938689Sborman 	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
172035518Sminshall 		if (scc > 0) {
172135518Sminshall 		    c = *sbp&0xff;
1722*60149Sdab #ifdef	ENCRYPTION
172346808Sdab 		    if (decrypt_input)
172446808Sdab 			c = (*decrypt_input)(c);
1725*60149Sdab #endif	/* ENCRYPTION */
172635518Sminshall 		    if (c == 0) {
172735518Sminshall 			sbp++, scc--; count++;
172835518Sminshall 			/* a "true" CR */
172932377Sminshall 			TTYADD('\r');
173038689Sborman 		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
173135518Sminshall 					(c == '\n')) {
173235518Sminshall 			sbp++, scc--; count++;
173332377Sminshall 			TTYADD('\n');
173435518Sminshall 		    } else {
1735*60149Sdab #ifdef	ENCRYPTION
173646808Sdab 		        if (decrypt_input)
173746808Sdab 			    (*decrypt_input)(-1);
1738*60149Sdab #endif	/* ENCRYPTION */
173946808Sdab 
174035518Sminshall 			TTYADD('\r');
174135518Sminshall 			if (crmod) {
174235518Sminshall 				TTYADD('\n');
174332377Sminshall 			}
174432377Sminshall 		    }
174535518Sminshall 		} else {
174635518Sminshall 		    telrcv_state = TS_CR;
174735518Sminshall 		    TTYADD('\r');
174835518Sminshall 		    if (crmod) {
174935518Sminshall 			    TTYADD('\n');
175035518Sminshall 		    }
175132377Sminshall 		}
175232377Sminshall 	    } else {
175332377Sminshall 		TTYADD(c);
175432377Sminshall 	    }
175532377Sminshall 	    continue;
175627088Sminshall 
175732377Sminshall 	case TS_IAC:
175838689Sborman process_iac:
175932377Sminshall 	    switch (c) {
176032377Sminshall 
176132377Sminshall 	    case WILL:
176232377Sminshall 		telrcv_state = TS_WILL;
176332377Sminshall 		continue;
176427261Sminshall 
176532377Sminshall 	    case WONT:
176632377Sminshall 		telrcv_state = TS_WONT;
176732377Sminshall 		continue;
176827261Sminshall 
176932377Sminshall 	    case DO:
177032377Sminshall 		telrcv_state = TS_DO;
177132377Sminshall 		continue;
177227261Sminshall 
177332377Sminshall 	    case DONT:
177432377Sminshall 		telrcv_state = TS_DONT;
177532377Sminshall 		continue;
177627261Sminshall 
177732377Sminshall 	    case DM:
177832377Sminshall 		    /*
177932377Sminshall 		     * We may have missed an urgent notification,
178032377Sminshall 		     * so make sure we flush whatever is in the
178132377Sminshall 		     * buffer currently.
178232377Sminshall 		     */
178346808Sdab 		printoption("RCVD", IAC, DM);
178432377Sminshall 		SYNCHing = 1;
178544360Sborman 		(void) ttyflush(1);
178632554Sminshall 		SYNCHing = stilloob();
178732377Sminshall 		settimer(gotDM);
178832377Sminshall 		break;
178927088Sminshall 
179032377Sminshall 	    case SB:
179132377Sminshall 		SB_CLEAR();
179232377Sminshall 		telrcv_state = TS_SB;
179332377Sminshall 		continue;
179427261Sminshall 
179532377Sminshall #	    if defined(TN3270)
179632377Sminshall 	    case EOR:
179732377Sminshall 		if (In3270) {
179832377Sminshall 		    if (Ibackp == Ifrontp) {
179932377Sminshall 			Ibackp = Ifrontp = Ibuf;
180032377Sminshall 			ISend = 0;	/* should have been! */
180132377Sminshall 		    } else {
180244360Sborman 			Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
180332377Sminshall 			ISend = 1;
180427088Sminshall 		    }
180527088Sminshall 		}
180646808Sdab 		printoption("RCVD", IAC, EOR);
180727088Sminshall 		break;
180832377Sminshall #	    endif /* defined(TN3270) */
180932377Sminshall 
181032377Sminshall 	    case IAC:
181132377Sminshall #	    if !defined(TN3270)
181232377Sminshall 		TTYADD(IAC);
181332377Sminshall #	    else /* !defined(TN3270) */
181432377Sminshall 		if (In3270) {
181532377Sminshall 		    *Ifrontp++ = IAC;
181632377Sminshall 		} else {
181732377Sminshall 		    TTYADD(IAC);
181832377Sminshall 		}
181932377Sminshall #	    endif /* !defined(TN3270) */
182027088Sminshall 		break;
182132377Sminshall 
182244360Sborman 	    case NOP:
182344360Sborman 	    case GA:
182427088Sminshall 	    default:
182546808Sdab 		printoption("RCVD", IAC, c);
182627088Sminshall 		break;
182727088Sminshall 	    }
182832377Sminshall 	    telrcv_state = TS_DATA;
182932377Sminshall 	    continue;
183027088Sminshall 
183132377Sminshall 	case TS_WILL:
183246808Sdab 	    printoption("RCVD", WILL, c);
183338689Sborman 	    willoption(c);
183432377Sminshall 	    SetIn3270();
183532377Sminshall 	    telrcv_state = TS_DATA;
183632377Sminshall 	    continue;
183727110Sminshall 
183832377Sminshall 	case TS_WONT:
183946808Sdab 	    printoption("RCVD", WONT, c);
184038689Sborman 	    wontoption(c);
184132377Sminshall 	    SetIn3270();
184232377Sminshall 	    telrcv_state = TS_DATA;
184332377Sminshall 	    continue;
184427088Sminshall 
184532377Sminshall 	case TS_DO:
184646808Sdab 	    printoption("RCVD", DO, c);
184737226Sminshall 	    dooption(c);
184832377Sminshall 	    SetIn3270();
184937219Sminshall 	    if (c == TELOPT_NAWS) {
185037219Sminshall 		sendnaws();
185137219Sminshall 	    } else if (c == TELOPT_LFLOW) {
185237219Sminshall 		localflow = 1;
185337219Sminshall 		setcommandmode();
185438689Sborman 		setconnmode(0);
185537219Sminshall 	    }
185632377Sminshall 	    telrcv_state = TS_DATA;
185732377Sminshall 	    continue;
185827088Sminshall 
185932377Sminshall 	case TS_DONT:
186046808Sdab 	    printoption("RCVD", DONT, c);
186138689Sborman 	    dontoption(c);
186237226Sminshall 	    flushline = 1;
186338689Sborman 	    setconnmode(0);	/* set new tty mode (maybe) */
186432377Sminshall 	    SetIn3270();
186532377Sminshall 	    telrcv_state = TS_DATA;
186632377Sminshall 	    continue;
186727088Sminshall 
186832377Sminshall 	case TS_SB:
186932377Sminshall 	    if (c == IAC) {
187032377Sminshall 		telrcv_state = TS_SE;
187132377Sminshall 	    } else {
187232377Sminshall 		SB_ACCUM(c);
187332377Sminshall 	    }
187432377Sminshall 	    continue;
187527088Sminshall 
187632377Sminshall 	case TS_SE:
187732377Sminshall 	    if (c != SE) {
187832377Sminshall 		if (c != IAC) {
187938689Sborman 		    /*
188038689Sborman 		     * This is an error.  We only expect to get
188138689Sborman 		     * "IAC IAC" or "IAC SE".  Several things may
188238689Sborman 		     * have happend.  An IAC was not doubled, the
188338689Sborman 		     * IAC SE was left off, or another option got
188438689Sborman 		     * inserted into the suboption are all possibilities.
188538689Sborman 		     * If we assume that the IAC was not doubled,
188638689Sborman 		     * and really the IAC SE was left off, we could
188738689Sborman 		     * get into an infinate loop here.  So, instead,
188838689Sborman 		     * we terminate the suboption, and process the
188938689Sborman 		     * partial suboption if we can.
189038689Sborman 		     */
189132377Sminshall 		    SB_ACCUM(IAC);
189238689Sborman 		    SB_ACCUM(c);
189346808Sdab 		    subpointer -= 2;
189446808Sdab 		    SB_TERM();
189546808Sdab 
189646808Sdab 		    printoption("In SUBOPTION processing, RCVD", IAC, c);
189738689Sborman 		    suboption();	/* handle sub-option */
189838689Sborman 		    SetIn3270();
189938689Sborman 		    telrcv_state = TS_IAC;
190038689Sborman 		    goto process_iac;
190132377Sminshall 		}
190232377Sminshall 		SB_ACCUM(c);
190332377Sminshall 		telrcv_state = TS_SB;
190432377Sminshall 	    } else {
190538689Sborman 		SB_ACCUM(IAC);
190638689Sborman 		SB_ACCUM(SE);
190746808Sdab 		subpointer -= 2;
190846808Sdab 		SB_TERM();
190932377Sminshall 		suboption();	/* handle sub-option */
191032377Sminshall 		SetIn3270();
191132377Sminshall 		telrcv_state = TS_DATA;
191232377Sminshall 	    }
191327088Sminshall 	}
191427088Sminshall     }
191532667Sminshall     if (count)
191632667Sminshall 	ring_consumed(&netiring, count);
191732385Sminshall     return returnValue||count;
191827088Sminshall }
191932385Sminshall 
192046808Sdab static int bol = 1, local = 0;
192146808Sdab 
192246808Sdab     int
192346808Sdab rlogin_susp()
192446808Sdab {
192546808Sdab     if (local) {
192646808Sdab 	local = 0;
192746808Sdab 	bol = 1;
192846808Sdab 	command(0, "z\n", 2);
192946808Sdab 	return(1);
193046808Sdab     }
193146808Sdab     return(0);
193246808Sdab }
193346808Sdab 
193446808Sdab     static int
193532554Sminshall telsnd()
193632385Sminshall {
193732385Sminshall     int tcc;
193832385Sminshall     int count;
193932385Sminshall     int returnValue = 0;
194046808Sdab     unsigned char *tbp;
194132385Sminshall 
194232385Sminshall     tcc = 0;
194332385Sminshall     count = 0;
194432385Sminshall     while (NETROOM() > 2) {
194532385Sminshall 	register int sc;
194632385Sminshall 	register int c;
194732385Sminshall 
194832385Sminshall 	if (tcc == 0) {
194932385Sminshall 	    if (count) {
195032528Sminshall 		ring_consumed(&ttyiring, count);
195132385Sminshall 		returnValue = 1;
195232385Sminshall 		count = 0;
195332385Sminshall 	    }
195432528Sminshall 	    tbp = ttyiring.consume;
195532528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
195632385Sminshall 	    if (tcc == 0) {
195732385Sminshall 		break;
195832385Sminshall 	    }
195932385Sminshall 	}
196032385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
196146808Sdab 	if (rlogin != _POSIX_VDISABLE) {
196246808Sdab 		if (bol) {
196346808Sdab 			bol = 0;
196446808Sdab 			if (sc == rlogin) {
196546808Sdab 				local = 1;
196646808Sdab 				continue;
196746808Sdab 			}
196846808Sdab 		} else if (local) {
196946808Sdab 			local = 0;
197046808Sdab 			if (sc == '.' || c == termEofChar) {
197146808Sdab 				bol = 1;
197246808Sdab 				command(0, "close\n", 6);
197346808Sdab 				continue;
197446808Sdab 			}
197546808Sdab 			if (sc == termSuspChar) {
197646808Sdab 				bol = 1;
197746808Sdab 				command(0, "z\n", 2);
197846808Sdab 				continue;
197946808Sdab 			}
198046808Sdab 			if (sc == escape) {
198146808Sdab 				command(0, (char *)tbp, tcc);
198246808Sdab 				bol = 1;
198346808Sdab 				count += tcc;
198446808Sdab 				tcc = 0;
198546808Sdab 				flushline = 1;
198646808Sdab 				break;
198746808Sdab 			}
198846808Sdab 			if (sc != rlogin) {
198946808Sdab 				++tcc;
199046808Sdab 				--tbp;
199146808Sdab 				--count;
199246808Sdab 				c = sc = rlogin;
199346808Sdab 			}
199446808Sdab 		}
199546808Sdab 		if ((sc == '\n') || (sc == '\r'))
199646808Sdab 			bol = 1;
199746808Sdab 	} else if (sc == escape) {
199838689Sborman 	    /*
199938689Sborman 	     * Double escape is a pass through of a single escape character.
200038689Sborman 	     */
200138689Sborman 	    if (tcc && strip(*tbp) == escape) {
200238689Sborman 		tbp++;
200338689Sborman 		tcc--;
200438689Sborman 		count++;
200546808Sdab 		bol = 0;
200638689Sborman 	    } else {
200746808Sdab 		command(0, (char *)tbp, tcc);
200846808Sdab 		bol = 1;
200938689Sborman 		count += tcc;
201038689Sborman 		tcc = 0;
201138689Sborman 		flushline = 1;
201238689Sborman 		break;
201338689Sborman 	    }
201446808Sdab 	} else
201546808Sdab 	    bol = 0;
201638689Sborman #ifdef	KLUDGELINEMODE
201738689Sborman 	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
201832385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
201932385Sminshall 		tcc--; tbp++; count++;
202032385Sminshall 	    } else {
202132385Sminshall 		dontlecho = !dontlecho;
202232385Sminshall 		settimer(echotoggle);
202338689Sborman 		setconnmode(0);
202432385Sminshall 		flushline = 1;
202532385Sminshall 		break;
202632385Sminshall 	    }
202732385Sminshall 	}
202838689Sborman #endif
202938689Sborman 	if (MODE_LOCAL_CHARS(globalmode)) {
203032385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
203146808Sdab 		bol = 1;
203232385Sminshall 		break;
203332385Sminshall 	    }
203432385Sminshall 	}
203538689Sborman 	if (my_want_state_is_wont(TELOPT_BINARY)) {
203632385Sminshall 	    switch (c) {
203732385Sminshall 	    case '\n':
203832385Sminshall 		    /*
203932385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
204032385Sminshall 		     * on our local machine, then probably
204132385Sminshall 		     * a newline (unix) is CRLF (TELNET).
204232385Sminshall 		     */
204332385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
204432385Sminshall 		    NETADD('\r');
204532385Sminshall 		}
204632385Sminshall 		NETADD('\n');
204746808Sdab 		bol = flushline = 1;
204832385Sminshall 		break;
204932385Sminshall 	    case '\r':
205032385Sminshall 		if (!crlf) {
205132385Sminshall 		    NET2ADD('\r', '\0');
205232385Sminshall 		} else {
205332385Sminshall 		    NET2ADD('\r', '\n');
205432385Sminshall 		}
205546808Sdab 		bol = flushline = 1;
205632385Sminshall 		break;
205732385Sminshall 	    case IAC:
205832385Sminshall 		NET2ADD(IAC, IAC);
205932385Sminshall 		break;
206032385Sminshall 	    default:
206132385Sminshall 		NETADD(c);
206232385Sminshall 		break;
206332385Sminshall 	    }
206432385Sminshall 	} else if (c == IAC) {
206532385Sminshall 	    NET2ADD(IAC, IAC);
206632385Sminshall 	} else {
206732385Sminshall 	    NETADD(c);
206832385Sminshall 	}
206932385Sminshall     }
207032667Sminshall     if (count)
207132667Sminshall 	ring_consumed(&ttyiring, count);
207232385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
207332385Sminshall }
207432377Sminshall 
207527088Sminshall /*
207632377Sminshall  * Scheduler()
207732377Sminshall  *
207832377Sminshall  * Try to do something.
207932377Sminshall  *
208032377Sminshall  * If we do something useful, return 1; else return 0.
208132377Sminshall  *
208227110Sminshall  */
208327110Sminshall 
208427110Sminshall 
208546808Sdab     int
208632377Sminshall Scheduler(block)
208746808Sdab     int	block;			/* should we block in the select ? */
208827110Sminshall {
208932377Sminshall 		/* One wants to be a bit careful about setting returnValue
209032377Sminshall 		 * to one, since a one implies we did some useful work,
209132377Sminshall 		 * and therefore probably won't be called to block next
209232377Sminshall 		 * time (TN3270 mode only).
209332377Sminshall 		 */
209432531Sminshall     int returnValue;
209532531Sminshall     int netin, netout, netex, ttyin, ttyout;
209627110Sminshall 
209732531Sminshall     /* Decide which rings should be processed */
209832531Sminshall 
209932531Sminshall     netout = ring_full_count(&netoring) &&
210038689Sborman 	    (flushline ||
210138689Sborman 		(my_want_state_is_wont(TELOPT_LINEMODE)
210238689Sborman #ifdef	KLUDGELINEMODE
210338689Sborman 			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
210438689Sborman #endif
210538689Sborman 		) ||
210638689Sborman 			my_want_state_is_will(TELOPT_BINARY));
210732531Sminshall     ttyout = ring_full_count(&ttyoring);
210832531Sminshall 
210932377Sminshall #if	defined(TN3270)
211032531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
211132377Sminshall #else	/* defined(TN3270) */
211232531Sminshall     ttyin = ring_empty_count(&ttyiring);
211332377Sminshall #endif	/* defined(TN3270) */
211432531Sminshall 
211532531Sminshall #if	defined(TN3270)
211632531Sminshall     netin = ring_empty_count(&netiring);
211732377Sminshall #   else /* !defined(TN3270) */
211832531Sminshall     netin = !ISend && ring_empty_count(&netiring);
211932377Sminshall #   endif /* !defined(TN3270) */
212032531Sminshall 
212132531Sminshall     netex = !SYNCHing;
212232531Sminshall 
212332531Sminshall     /* If we have seen a signal recently, reset things */
212432377Sminshall #   if defined(TN3270) && defined(unix)
212532377Sminshall     if (HaveInput) {
212632377Sminshall 	HaveInput = 0;
212744360Sborman 	(void) signal(SIGIO, inputAvailable);
212832377Sminshall     }
212932377Sminshall #endif	/* defined(TN3270) && defined(unix) */
213032377Sminshall 
213132531Sminshall     /* Call to system code to process rings */
213227178Sminshall 
213332531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
213427178Sminshall 
213532531Sminshall     /* Now, look at the input rings, looking for work to do. */
213632377Sminshall 
213732531Sminshall     if (ring_full_count(&ttyiring)) {
213832377Sminshall #   if defined(TN3270)
213932377Sminshall 	if (In3270) {
214034848Sminshall 	    int c;
214134848Sminshall 
214233804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
214332528Sminshall 					ring_full_consecutive(&ttyiring));
214432377Sminshall 	    if (c) {
214532377Sminshall 		returnValue = 1;
214632667Sminshall 	        ring_consumed(&ttyiring, c);
214732377Sminshall 	    }
214832377Sminshall 	} else {
214932377Sminshall #   endif /* defined(TN3270) */
215032554Sminshall 	    returnValue |= telsnd();
215132377Sminshall #   if defined(TN3270)
215227178Sminshall 	}
215332531Sminshall #   endif /* defined(TN3270) */
215427178Sminshall     }
215532377Sminshall 
215632528Sminshall     if (ring_full_count(&netiring)) {
215732377Sminshall #	if !defined(TN3270)
215832385Sminshall 	returnValue |= telrcv();
215932377Sminshall #	else /* !defined(TN3270) */
216032377Sminshall 	returnValue = Push3270();
216132377Sminshall #	endif /* !defined(TN3270) */
216232377Sminshall     }
216332377Sminshall     return returnValue;
216427178Sminshall }
216527178Sminshall 
216627178Sminshall /*
216732377Sminshall  * Select from tty and network...
216827088Sminshall  */
216946808Sdab     void
217046808Sdab telnet(user)
217146808Sdab     char *user;
217227088Sminshall {
217332531Sminshall     sys_telnet_init();
217427088Sminshall 
2175*60149Sdab #if	defined(AUTHENTICATION) || defined(ENCRYPTION)
217646808Sdab     {
217746808Sdab 	static char local_host[256] = { 0 };
217846808Sdab 
217946808Sdab 	if (!local_host[0]) {
218057213Sdab 		gethostname(local_host, sizeof(local_host));
218146808Sdab 		local_host[sizeof(local_host)-1] = 0;
218246808Sdab 	}
218346808Sdab 	auth_encrypt_init(local_host, hostname, "TELNET", 0);
218446808Sdab 	auth_encrypt_user(user);
218546808Sdab     }
2186*60149Sdab #endif	/* defined(AUTHENTICATION) || defined(ENCRYPTION)  */
218732377Sminshall #   if !defined(TN3270)
218832377Sminshall     if (telnetport) {
218957213Sdab #if	defined(AUTHENTICATION)
219046808Sdab 	if (autologin)
219146808Sdab 		send_will(TELOPT_AUTHENTICATION, 1);
219246808Sdab #endif
2193*60149Sdab #ifdef	ENCRYPTION
219446808Sdab 	send_do(TELOPT_ENCRYPT, 1);
219546808Sdab 	send_will(TELOPT_ENCRYPT, 1);
2196*60149Sdab #endif	/* ENCRYPTION */
219738689Sborman 	send_do(TELOPT_SGA, 1);
219838689Sborman 	send_will(TELOPT_TTYPE, 1);
219938689Sborman 	send_will(TELOPT_NAWS, 1);
220038689Sborman 	send_will(TELOPT_TSPEED, 1);
220138689Sborman 	send_will(TELOPT_LFLOW, 1);
220238689Sborman 	send_will(TELOPT_LINEMODE, 1);
220346808Sdab 	send_will(TELOPT_ENVIRON, 1);
220438908Sborman 	send_do(TELOPT_STATUS, 1);
220546808Sdab 	if (env_getvalue((unsigned char *)"DISPLAY"))
220644360Sborman 	    send_will(TELOPT_XDISPLOC, 1);
220746808Sdab 	if (eight)
220846808Sdab 	    tel_enter_binary(eight);
220927178Sminshall     }
221032377Sminshall #   endif /* !defined(TN3270) */
221127088Sminshall 
221232377Sminshall #   if !defined(TN3270)
221332377Sminshall     for (;;) {
221432385Sminshall 	int schedValue;
221532385Sminshall 
221632385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
221732385Sminshall 	    if (schedValue == -1) {
221832385Sminshall 		setcommandmode();
221932385Sminshall 		return;
222032385Sminshall 	    }
222132385Sminshall 	}
222232385Sminshall 
222332531Sminshall 	if (Scheduler(1) == -1) {
222432377Sminshall 	    setcommandmode();
222532377Sminshall 	    return;
222632377Sminshall 	}
222732377Sminshall     }
222832377Sminshall #   else /* !defined(TN3270) */
222932377Sminshall     for (;;) {
223032377Sminshall 	int schedValue;
223127088Sminshall 
223232377Sminshall 	while (!In3270 && !shell_active) {
223332531Sminshall 	    if (Scheduler(1) == -1) {
223432377Sminshall 		setcommandmode();
223532377Sminshall 		return;
223632377Sminshall 	    }
223727088Sminshall 	}
223832377Sminshall 
223932377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
224032377Sminshall 	    if (schedValue == -1) {
224132377Sminshall 		setcommandmode();
224232377Sminshall 		return;
224332377Sminshall 	    }
224427088Sminshall 	}
224532377Sminshall 		/* If there is data waiting to go out to terminal, don't
224632377Sminshall 		 * schedule any more data for the terminal.
224732377Sminshall 		 */
224834304Sminshall 	if (ring_full_count(&ttyoring)) {
224932377Sminshall 	    schedValue = 1;
225027088Sminshall 	} else {
225132377Sminshall 	    if (shell_active) {
225232377Sminshall 		if (shell_continue() == 0) {
225332377Sminshall 		    ConnectScreen();
225427088Sminshall 		}
225532377Sminshall 	    } else if (In3270) {
225632377Sminshall 		schedValue = DoTerminalOutput();
225732377Sminshall 	    }
225827088Sminshall 	}
225932377Sminshall 	if (schedValue && (shell_active == 0)) {
226032531Sminshall 	    if (Scheduler(1) == -1) {
226132377Sminshall 		setcommandmode();
226232377Sminshall 		return;
226332377Sminshall 	    }
226427088Sminshall 	}
226532377Sminshall     }
226632377Sminshall #   endif /* !defined(TN3270) */
226727088Sminshall }
226832377Sminshall 
226934848Sminshall #if	0	/* XXX - this not being in is a bug */
227027088Sminshall /*
227132554Sminshall  * nextitem()
227232554Sminshall  *
227332554Sminshall  *	Return the address of the next "item" in the TELNET data
227432554Sminshall  * stream.  This will be the address of the next character if
227532554Sminshall  * the current address is a user data character, or it will
227632554Sminshall  * be the address of the character following the TELNET command
227732554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
227832554Sminshall  * character.
227932554Sminshall  */
228032554Sminshall 
228146808Sdab     static char *
228232554Sminshall nextitem(current)
228346808Sdab     char *current;
228432554Sminshall {
228532554Sminshall     if ((*current&0xff) != IAC) {
228632554Sminshall 	return current+1;
228732554Sminshall     }
228832554Sminshall     switch (*(current+1)&0xff) {
228932554Sminshall     case DO:
229032554Sminshall     case DONT:
229132554Sminshall     case WILL:
229232554Sminshall     case WONT:
229332554Sminshall 	return current+3;
229432554Sminshall     case SB:		/* loop forever looking for the SE */
229532554Sminshall 	{
229632554Sminshall 	    register char *look = current+2;
229732554Sminshall 
229832554Sminshall 	    for (;;) {
229932554Sminshall 		if ((*look++&0xff) == IAC) {
230032554Sminshall 		    if ((*look++&0xff) == SE) {
230132554Sminshall 			return look;
230232554Sminshall 		    }
230332554Sminshall 		}
230432554Sminshall 	    }
230532554Sminshall 	}
230632554Sminshall     default:
230732554Sminshall 	return current+2;
230832554Sminshall     }
230932554Sminshall }
231034848Sminshall #endif	/* 0 */
231132554Sminshall 
231232554Sminshall /*
231332554Sminshall  * netclear()
231432554Sminshall  *
231532554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
231632554Sminshall  * the path to the network.
231732554Sminshall  *
231832554Sminshall  *	Things are a bit tricky since we may have sent the first
231932554Sminshall  * byte or so of a previous TELNET command into the network.
232032554Sminshall  * So, we have to scan the network buffer from the beginning
232132554Sminshall  * until we are up to where we want to be.
232232554Sminshall  *
232332554Sminshall  *	A side effect of what we do, just to keep things
232432554Sminshall  * simple, is to clear the urgent data pointer.  The principal
232532554Sminshall  * caller should be setting the urgent data pointer AFTER calling
232632554Sminshall  * us in any case.
232732554Sminshall  */
232832554Sminshall 
232946808Sdab     static void
233032554Sminshall netclear()
233132554Sminshall {
233232554Sminshall #if	0	/* XXX */
233332554Sminshall     register char *thisitem, *next;
233432554Sminshall     char *good;
233532554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
233632554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
233732554Sminshall 
233832554Sminshall     thisitem = netobuf;
233932554Sminshall 
234032554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
234132554Sminshall 	thisitem = next;
234232554Sminshall     }
234332554Sminshall 
234432554Sminshall     /* Now, thisitem is first before/at boundary. */
234532554Sminshall 
234632554Sminshall     good = netobuf;	/* where the good bytes go */
234732554Sminshall 
234832554Sminshall     while (netoring.add > thisitem) {
234932554Sminshall 	if (wewant(thisitem)) {
235032554Sminshall 	    int length;
235132554Sminshall 
235232554Sminshall 	    next = thisitem;
235332554Sminshall 	    do {
235432554Sminshall 		next = nextitem(next);
235532554Sminshall 	    } while (wewant(next) && (nfrontp > next));
235632554Sminshall 	    length = next-thisitem;
235732554Sminshall 	    memcpy(good, thisitem, length);
235832554Sminshall 	    good += length;
235932554Sminshall 	    thisitem = next;
236032554Sminshall 	} else {
236132554Sminshall 	    thisitem = nextitem(thisitem);
236232554Sminshall 	}
236332554Sminshall     }
236432554Sminshall 
236532554Sminshall #endif	/* 0 */
236632554Sminshall }
236732554Sminshall 
236832554Sminshall /*
236932377Sminshall  * These routines add various telnet commands to the data stream.
237027088Sminshall  */
237132377Sminshall 
237246808Sdab     static void
237332554Sminshall doflush()
237432554Sminshall {
237532554Sminshall     NET2ADD(IAC, DO);
237632554Sminshall     NETADD(TELOPT_TM);
237732554Sminshall     flushline = 1;
237832554Sminshall     flushout = 1;
237944360Sborman     (void) ttyflush(1);			/* Flush/drop output */
238032554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
238146808Sdab     printoption("SENT", DO, TELOPT_TM);
238232554Sminshall }
238332554Sminshall 
238446808Sdab     void
238532377Sminshall xmitAO()
238627088Sminshall {
238732377Sminshall     NET2ADD(IAC, AO);
238846808Sdab     printoption("SENT", IAC, AO);
238932377Sminshall     if (autoflush) {
239032377Sminshall 	doflush();
239132377Sminshall     }
239232377Sminshall }
239327088Sminshall 
239432377Sminshall 
239546808Sdab     void
239632377Sminshall xmitEL()
239727088Sminshall {
239832377Sminshall     NET2ADD(IAC, EL);
239946808Sdab     printoption("SENT", IAC, EL);
240027088Sminshall }
240127088Sminshall 
240246808Sdab     void
240332377Sminshall xmitEC()
240427088Sminshall {
240532377Sminshall     NET2ADD(IAC, EC);
240646808Sdab     printoption("SENT", IAC, EC);
240727088Sminshall }
240827088Sminshall 
240932377Sminshall 
241046808Sdab     int
241132377Sminshall dosynch()
241227088Sminshall {
241332377Sminshall     netclear();			/* clear the path to the network */
241433294Sminshall     NETADD(IAC);
241533294Sminshall     setneturg();
241633294Sminshall     NETADD(DM);
241746808Sdab     printoption("SENT", IAC, DM);
241846808Sdab     return 1;
241927088Sminshall }
242027088Sminshall 
242146808Sdab int want_status_response = 0;
242246808Sdab 
242346808Sdab     int
242438908Sborman get_status()
242538908Sborman {
242646808Sdab     unsigned char tmp[16];
242746808Sdab     register unsigned char *cp;
242838908Sborman 
242938908Sborman     if (my_want_state_is_dont(TELOPT_STATUS)) {
243038908Sborman 	printf("Remote side does not support STATUS option\n");
243146808Sdab 	return 0;
243238908Sborman     }
243338908Sborman     cp = tmp;
243438908Sborman 
243538908Sborman     *cp++ = IAC;
243638908Sborman     *cp++ = SB;
243738908Sborman     *cp++ = TELOPT_STATUS;
243838908Sborman     *cp++ = TELQUAL_SEND;
243938908Sborman     *cp++ = IAC;
244038908Sborman     *cp++ = SE;
244138908Sborman     if (NETROOM() >= cp - tmp) {
244238908Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
244338908Sborman 	printsub('>', tmp+2, cp - tmp - 2);
244438908Sborman     }
244546808Sdab     ++want_status_response;
244646808Sdab     return 1;
244738908Sborman }
244838908Sborman 
244946808Sdab     void
245032377Sminshall intp()
245127088Sminshall {
245232377Sminshall     NET2ADD(IAC, IP);
245346808Sdab     printoption("SENT", IAC, IP);
245432377Sminshall     flushline = 1;
245532377Sminshall     if (autoflush) {
245632377Sminshall 	doflush();
245732377Sminshall     }
245832377Sminshall     if (autosynch) {
245932377Sminshall 	dosynch();
246032377Sminshall     }
246127088Sminshall }
246227186Sminshall 
246346808Sdab     void
246432377Sminshall sendbrk()
246527186Sminshall {
246632377Sminshall     NET2ADD(IAC, BREAK);
246746808Sdab     printoption("SENT", IAC, BREAK);
246832377Sminshall     flushline = 1;
246932377Sminshall     if (autoflush) {
247032377Sminshall 	doflush();
247132377Sminshall     }
247232377Sminshall     if (autosynch) {
247332377Sminshall 	dosynch();
247432377Sminshall     }
247527186Sminshall }
247638689Sborman 
247746808Sdab     void
247838689Sborman sendabort()
247938689Sborman {
248038689Sborman     NET2ADD(IAC, ABORT);
248146808Sdab     printoption("SENT", IAC, ABORT);
248238689Sborman     flushline = 1;
248338689Sborman     if (autoflush) {
248438689Sborman 	doflush();
248538689Sborman     }
248638689Sborman     if (autosynch) {
248738689Sborman 	dosynch();
248838689Sborman     }
248938689Sborman }
249038689Sborman 
249146808Sdab     void
249238689Sborman sendsusp()
249338689Sborman {
249438689Sborman     NET2ADD(IAC, SUSP);
249546808Sdab     printoption("SENT", IAC, SUSP);
249638689Sborman     flushline = 1;
249738689Sborman     if (autoflush) {
249838689Sborman 	doflush();
249938689Sborman     }
250038689Sborman     if (autosynch) {
250138689Sborman 	dosynch();
250238689Sborman     }
250338689Sborman }
250438689Sborman 
250546808Sdab     void
250638689Sborman sendeof()
250738689Sborman {
250838908Sborman     NET2ADD(IAC, xEOF);
250946808Sdab     printoption("SENT", IAC, xEOF);
251038689Sborman }
251138689Sborman 
251246808Sdab     void
251345232Sborman sendayt()
251445232Sborman {
251545232Sborman     NET2ADD(IAC, AYT);
251646808Sdab     printoption("SENT", IAC, AYT);
251745232Sborman }
251845232Sborman 
251937219Sminshall /*
252037219Sminshall  * Send a window size update to the remote system.
252137219Sminshall  */
252237219Sminshall 
252346808Sdab     void
252437219Sminshall sendnaws()
252537219Sminshall {
252637219Sminshall     long rows, cols;
252738689Sborman     unsigned char tmp[16];
252838689Sborman     register unsigned char *cp;
252937219Sminshall 
253038689Sborman     if (my_state_is_wont(TELOPT_NAWS))
253138689Sborman 	return;
253237219Sminshall 
253338689Sborman #define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
253438689Sborman 			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
253538689Sborman 
253637219Sminshall     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
253737219Sminshall 	return;
253837219Sminshall     }
253937219Sminshall 
254038689Sborman     cp = tmp;
254138689Sborman 
254238689Sborman     *cp++ = IAC;
254338689Sborman     *cp++ = SB;
254438689Sborman     *cp++ = TELOPT_NAWS;
254538689Sborman     PUTSHORT(cp, cols);
254638689Sborman     PUTSHORT(cp, rows);
254738689Sborman     *cp++ = IAC;
254838689Sborman     *cp++ = SE;
254938689Sborman     if (NETROOM() >= cp - tmp) {
255038689Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
255138689Sborman 	printsub('>', tmp+2, cp - tmp - 2);
255237219Sminshall     }
255337219Sminshall }
255437226Sminshall 
255546808Sdab     void
255638908Sborman tel_enter_binary(rw)
255746808Sdab     int rw;
255837226Sminshall {
255938908Sborman     if (rw&1)
256038908Sborman 	send_do(TELOPT_BINARY, 1);
256138908Sborman     if (rw&2)
256238908Sborman 	send_will(TELOPT_BINARY, 1);
256337226Sminshall }
256437226Sminshall 
256546808Sdab     void
256638908Sborman tel_leave_binary(rw)
256746808Sdab     int rw;
256837226Sminshall {
256938908Sborman     if (rw&1)
257038908Sborman 	send_dont(TELOPT_BINARY, 1);
257138908Sborman     if (rw&2)
257238908Sborman 	send_wont(TELOPT_BINARY, 1);
257337226Sminshall }
2574