xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 69785)
133685Sbostic /*
262313Sbostic  * Copyright (c) 1988, 1990, 1993
362313Sbostic  *	The Regents of the University of California.  All rights reserved.
433685Sbostic  *
542770Sbostic  * %sccs.include.redist.c%
633685Sbostic  */
711758Ssam 
821580Sdist #ifndef lint
9*69785Sdab static char sccsid[] = "@(#)telnet.c	8.4 (Berkeley) 05/30/95";
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 
3468586Sdab #define	strip(x) ((my_want_state_is_wont(TELOPT_BINARY)) ? ((x)&0x7f) : (x))
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;
10565157Sdab #ifdef	OLD_ENVIRON
10665157Sdab unsigned char telopt_environ = TELOPT_NEW_ENVIRON;
10765157Sdab #else
10865157Sdab # define telopt_environ TELOPT_NEW_ENVIRON
10965157Sdab #endif
1106000Sroot 
11132377Sminshall jmp_buf	toplevel = { 0 };
11232377Sminshall jmp_buf	peerdied;
1136000Sroot 
11432377Sminshall int	flushline;
11538811Sborman int	linemode;
11627021Sminshall 
11738689Sborman #ifdef	KLUDGELINEMODE
11838689Sborman int	kludgelinemode = 1;
11938689Sborman #endif
12038689Sborman 
12132377Sminshall /*
12232377Sminshall  * The following are some clocks used to decide how to interpret
12332377Sminshall  * the relationship between various variables.
12432377Sminshall  */
1256000Sroot 
12632377Sminshall Clocks clocks;
12732377Sminshall 
12838689Sborman #ifdef	notdef
12932377Sminshall Modelist modelist[] = {
13032377Sminshall 	{ "telnet command mode", COMMAND_LINE },
13132377Sminshall 	{ "character-at-a-time mode", 0 },
13232377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
13332377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
13432377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
13532377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
13632377Sminshall 	{ "3270 mode", 0 },
13732377Sminshall };
13838689Sborman #endif
1396000Sroot 
14032377Sminshall 
14132377Sminshall /*
14232377Sminshall  * Initialize telnet environment.
14332377Sminshall  */
1446000Sroot 
14546808Sdab     void
init_telnet()14632377Sminshall init_telnet()
14732377Sminshall {
14844360Sborman     env_init();
14944360Sborman 
15032377Sminshall     SB_CLEAR();
15137226Sminshall     ClearArray(options);
1526000Sroot 
15337219Sminshall     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
154*69785Sdab #if	defined(AUTHENTICATION) || defined(ENCRYPTION)
15546808Sdab     auth_encrypt_connect(connected);
15660149Sdab #endif	/* defined(AUTHENTICATION) || defined(ENCRYPTION)  */
15757213Sdab     restartany = -1;
1586000Sroot 
15932377Sminshall     SYNCHing = 0;
1606000Sroot 
16132377Sminshall     /* Don't change NetTrace */
1626000Sroot 
16332377Sminshall     escape = CONTROL(']');
16446808Sdab     rlogin = _POSIX_VDISABLE;
16544360Sborman #ifdef	KLUDGELINEMODE
16632377Sminshall     echoc = CONTROL('E');
16744360Sborman #endif
1686000Sroot 
16932377Sminshall     flushline = 1;
17032377Sminshall     telrcv_state = TS_DATA;
17132377Sminshall }
17232554Sminshall 
1736000Sroot 
17444360Sborman #ifdef	notdef
17532554Sminshall #include <varargs.h>
1766000Sroot 
17746808Sdab     /*VARARGS*/
17846808Sdab     static void
printring(va_alist)17932554Sminshall printring(va_alist)
18046808Sdab     va_dcl
18132554Sminshall {
18232554Sminshall     va_list ap;
18332554Sminshall     char buffer[100];		/* where things go */
18432554Sminshall     char *ptr;
18532554Sminshall     char *format;
18632554Sminshall     char *string;
18732554Sminshall     Ring *ring;
18832554Sminshall     int i;
18932554Sminshall 
19032554Sminshall     va_start(ap);
19132554Sminshall 
19232554Sminshall     ring = va_arg(ap, Ring *);
19332554Sminshall     format = va_arg(ap, char *);
19432554Sminshall     ptr = buffer;
19532554Sminshall 
19632554Sminshall     while ((i = *format++) != 0) {
19732554Sminshall 	if (i == '%') {
19832554Sminshall 	    i = *format++;
19932554Sminshall 	    switch (i) {
20032554Sminshall 	    case 'c':
20132554Sminshall 		*ptr++ = va_arg(ap, int);
20232554Sminshall 		break;
20332554Sminshall 	    case 's':
20432554Sminshall 		string = va_arg(ap, char *);
20532554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
20632554Sminshall 		ring_supply_data(ring, string, strlen(string));
20732554Sminshall 		ptr = buffer;
20832554Sminshall 		break;
20932554Sminshall 	    case 0:
21032554Sminshall 		ExitString("printring: trailing %%.\n", 1);
21132554Sminshall 		/*NOTREACHED*/
21232554Sminshall 	    default:
21332554Sminshall 		ExitString("printring: unknown format character.\n", 1);
21432554Sminshall 		/*NOTREACHED*/
21532554Sminshall 	    }
21632554Sminshall 	} else {
21732554Sminshall 	    *ptr++ = i;
21832554Sminshall 	}
21932554Sminshall     }
22032554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
22132554Sminshall }
22244360Sborman #endif
22332554Sminshall 
22437226Sminshall /*
22537226Sminshall  * These routines are in charge of sending option negotiations
22637226Sminshall  * to the other side.
22737226Sminshall  *
22837226Sminshall  * The basic idea is that we send the negotiation if either side
22937226Sminshall  * is in disagreement as to what the current state should be.
23037226Sminshall  */
23132554Sminshall 
23246808Sdab     void
send_do(c,init)23338689Sborman send_do(c, init)
23446808Sdab     register int c, init;
2356000Sroot {
23638689Sborman     if (init) {
23738689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
23838689Sborman 				my_want_state_is_do(c))
23938689Sborman 	    return;
24038689Sborman 	set_my_want_state_do(c);
24138689Sborman 	do_dont_resp[c]++;
24237226Sminshall     }
24338689Sborman     NET2ADD(IAC, DO);
24438689Sborman     NETADD(c);
24546808Sdab     printoption("SENT", DO, c);
24637226Sminshall }
24737226Sminshall 
24846808Sdab     void
send_dont(c,init)24938689Sborman send_dont(c, init)
25046808Sdab     register int c, init;
25137226Sminshall {
25238689Sborman     if (init) {
25338689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
25438689Sborman 				my_want_state_is_dont(c))
25538689Sborman 	    return;
25638689Sborman 	set_my_want_state_dont(c);
25738689Sborman 	do_dont_resp[c]++;
25837226Sminshall     }
25938689Sborman     NET2ADD(IAC, DONT);
26038689Sborman     NETADD(c);
26146808Sdab     printoption("SENT", DONT, c);
26237226Sminshall }
26337226Sminshall 
26446808Sdab     void
send_will(c,init)26538689Sborman send_will(c, init)
26646808Sdab     register int c, init;
26737226Sminshall {
26838689Sborman     if (init) {
26938689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
27038689Sborman 				my_want_state_is_will(c))
27138689Sborman 	    return;
27238689Sborman 	set_my_want_state_will(c);
27338689Sborman 	will_wont_resp[c]++;
27437226Sminshall     }
27538689Sborman     NET2ADD(IAC, WILL);
27638689Sborman     NETADD(c);
27746808Sdab     printoption("SENT", WILL, c);
27837226Sminshall }
27937226Sminshall 
28046808Sdab     void
send_wont(c,init)28138689Sborman send_wont(c, init)
28246808Sdab     register int c, init;
28337226Sminshall {
28438689Sborman     if (init) {
28538689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
28638689Sborman 				my_want_state_is_wont(c))
28738689Sborman 	    return;
28838689Sborman 	set_my_want_state_wont(c);
28938689Sborman 	will_wont_resp[c]++;
29037226Sminshall     }
29138689Sborman     NET2ADD(IAC, WONT);
29238689Sborman     NETADD(c);
29346808Sdab     printoption("SENT", WONT, c);
29437226Sminshall }
29537226Sminshall 
29637226Sminshall 
29746808Sdab 	void
willoption(option)29837226Sminshall willoption(option)
29937226Sminshall 	int option;
30037226Sminshall {
30138689Sborman 	int new_state_ok = 0;
3026000Sroot 
30338689Sborman 	if (do_dont_resp[option]) {
30438689Sborman 	    --do_dont_resp[option];
30538689Sborman 	    if (do_dont_resp[option] && my_state_is_do(option))
30638689Sborman 		--do_dont_resp[option];
30738689Sborman 	}
30837226Sminshall 
30938689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
3106000Sroot 
31138689Sborman 	    switch (option) {
31238689Sborman 
31338689Sborman 	    case TELOPT_ECHO:
31438689Sborman #	    if defined(TN3270)
31538689Sborman 		/*
31638689Sborman 		 * The following is a pain in the rear-end.
31738689Sborman 		 * Various IBM servers (some versions of Wiscnet,
31838689Sborman 		 * possibly Fibronics/Spartacus, and who knows who
31938689Sborman 		 * else) will NOT allow us to send "DO SGA" too early
32038689Sborman 		 * in the setup proceedings.  On the other hand,
32138689Sborman 		 * 4.2 servers (telnetd) won't set SGA correctly.
32238689Sborman 		 * So, we are stuck.  Empirically (but, based on
32338689Sborman 		 * a VERY small sample), the IBM servers don't send
32438689Sborman 		 * out anything about ECHO, so we postpone our sending
32538689Sborman 		 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
32638689Sborman 		 * DO send).
32738689Sborman 		  */
32838689Sborman 		{
32938689Sborman 		    if (askedSGA == 0) {
33038689Sborman 			askedSGA = 1;
33138689Sborman 			if (my_want_state_is_dont(TELOPT_SGA))
33238689Sborman 			    send_do(TELOPT_SGA, 1);
33332377Sminshall 		    }
33432377Sminshall 		}
33538689Sborman 		    /* Fall through */
33638689Sborman 	    case TELOPT_EOR:
33738908Sborman #endif	    /* defined(TN3270) */
33838689Sborman 	    case TELOPT_BINARY:
33938689Sborman 	    case TELOPT_SGA:
34027110Sminshall 		settimer(modenegotiated);
34138908Sborman 		/* FALL THROUGH */
34238908Sborman 	    case TELOPT_STATUS:
34357213Sdab #if	defined(AUTHENTICATION)
34446808Sdab 	    case TELOPT_AUTHENTICATION:
34546808Sdab #endif
34660149Sdab #ifdef	ENCRYPTION
34746808Sdab 	    case TELOPT_ENCRYPT:
34860149Sdab #endif /* ENCRYPTION */
34938689Sborman 		new_state_ok = 1;
3506000Sroot 		break;
3516000Sroot 
35238689Sborman 	    case TELOPT_TM:
35338689Sborman 		if (flushout)
35438689Sborman 		    flushout = 0;
35538689Sborman 		/*
35638689Sborman 		 * Special case for TM.  If we get back a WILL,
35738689Sborman 		 * pretend we got back a WONT.
35838689Sborman 		 */
35938689Sborman 		set_my_want_state_dont(option);
36038689Sborman 		set_my_state_dont(option);
36127110Sminshall 		return;			/* Never reply to TM will's/wont's */
3626000Sroot 
36338689Sborman 	    case TELOPT_LINEMODE:
36438689Sborman 	    default:
3656000Sroot 		break;
36638689Sborman 	    }
36738689Sborman 
36838689Sborman 	    if (new_state_ok) {
36938689Sborman 		set_my_want_state_do(option);
37038689Sborman 		send_do(option, 0);
37138689Sborman 		setconnmode(0);		/* possibly set new tty mode */
37238689Sborman 	    } else {
37338689Sborman 		do_dont_resp[option]++;
37438689Sborman 		send_dont(option, 0);
37538689Sborman 	    }
3766000Sroot 	}
37738689Sborman 	set_my_state_do(option);
37860149Sdab #ifdef	ENCRYPTION
37946808Sdab 	if (option == TELOPT_ENCRYPT)
38046808Sdab 		encrypt_send_support();
38160149Sdab #endif	/* ENCRYPTION */
3826000Sroot }
3836000Sroot 
38446808Sdab 	void
wontoption(option)38537226Sminshall wontoption(option)
38637226Sminshall 	int option;
3876000Sroot {
38838689Sborman 	if (do_dont_resp[option]) {
38938689Sborman 	    --do_dont_resp[option];
39038689Sborman 	    if (do_dont_resp[option] && my_state_is_dont(option))
39138689Sborman 		--do_dont_resp[option];
39238689Sborman 	}
39337226Sminshall 
39438689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
3956000Sroot 
39638689Sborman 	    switch (option) {
39738689Sborman 
39838689Sborman #ifdef	KLUDGELINEMODE
39938689Sborman 	    case TELOPT_SGA:
40038689Sborman 		if (!kludgelinemode)
40138689Sborman 		    break;
40238689Sborman 		/* FALL THROUGH */
40338689Sborman #endif
40438689Sborman 	    case TELOPT_ECHO:
40527110Sminshall 		settimer(modenegotiated);
4066000Sroot 		break;
4076000Sroot 
40838689Sborman 	    case TELOPT_TM:
40938689Sborman 		if (flushout)
41038689Sborman 		    flushout = 0;
41138689Sborman 		set_my_want_state_dont(option);
41238689Sborman 		set_my_state_dont(option);
41327110Sminshall 		return;		/* Never reply to TM will's/wont's */
41427110Sminshall 
41538689Sborman 	    default:
41638689Sborman 		break;
41738689Sborman 	    }
41838689Sborman 	    set_my_want_state_dont(option);
41944360Sborman 	    if (my_state_is_do(option))
42044360Sborman 		send_dont(option, 0);
42138689Sborman 	    setconnmode(0);			/* Set new tty mode */
42238689Sborman 	} else if (option == TELOPT_TM) {
42338689Sborman 	    /*
42438689Sborman 	     * Special case for TM.
42538689Sborman 	     */
42638689Sborman 	    if (flushout)
42738689Sborman 		flushout = 0;
42838689Sborman 	    set_my_want_state_dont(option);
4296000Sroot 	}
43038689Sborman 	set_my_state_dont(option);
4316000Sroot }
4326000Sroot 
43346808Sdab 	static void
dooption(option)4346000Sroot dooption(option)
4356000Sroot 	int option;
4366000Sroot {
43738689Sborman 	int new_state_ok = 0;
4386000Sroot 
43938689Sborman 	if (will_wont_resp[option]) {
44038689Sborman 	    --will_wont_resp[option];
44138689Sborman 	    if (will_wont_resp[option] && my_state_is_will(option))
44238689Sborman 		--will_wont_resp[option];
44338689Sborman 	}
44437226Sminshall 
44538689Sborman 	if (will_wont_resp[option] == 0) {
44638689Sborman 	  if (my_want_state_is_wont(option)) {
4476000Sroot 
44838689Sborman 	    switch (option) {
44938689Sborman 
45038689Sborman 	    case TELOPT_TM:
45138689Sborman 		/*
45238689Sborman 		 * Special case for TM.  We send a WILL, but pretend
45338689Sborman 		 * we sent WONT.
45438689Sborman 		 */
45538689Sborman 		send_will(option, 0);
45638689Sborman 		set_my_want_state_wont(TELOPT_TM);
45738689Sborman 		set_my_state_wont(TELOPT_TM);
45838689Sborman 		return;
45938689Sborman 
46032377Sminshall #	if defined(TN3270)
46138689Sborman 	    case TELOPT_EOR:		/* end of record */
46238908Sborman #	endif	/* defined(TN3270) */
46338689Sborman 	    case TELOPT_BINARY:		/* binary mode */
46438689Sborman 	    case TELOPT_NAWS:		/* window size */
46538689Sborman 	    case TELOPT_TSPEED:		/* terminal speed */
46638689Sborman 	    case TELOPT_LFLOW:		/* local flow control */
46738689Sborman 	    case TELOPT_TTYPE:		/* terminal type option */
46838689Sborman 	    case TELOPT_SGA:		/* no big deal */
46960149Sdab #ifdef	ENCRYPTION
47046808Sdab 	    case TELOPT_ENCRYPT:	/* encryption variable option */
47160149Sdab #endif	/* ENCRYPTION */
47238689Sborman 		new_state_ok = 1;
4736000Sroot 		break;
47465157Sdab 
47565157Sdab 	    case TELOPT_NEW_ENVIRON:	/* New environment variable option */
47665157Sdab #ifdef	OLD_ENVIRON
47765157Sdab 		if (my_state_is_will(TELOPT_OLD_ENVIRON))
47865157Sdab 			send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */
47965157Sdab 		goto env_common;
48065157Sdab 	    case TELOPT_OLD_ENVIRON:	/* Old environment variable option */
48165157Sdab 		if (my_state_is_will(TELOPT_NEW_ENVIRON))
48265157Sdab 			break;		/* Don't enable if new one is in use! */
48365157Sdab 	    env_common:
48465157Sdab 		telopt_environ = option;
48565157Sdab #endif
48665157Sdab 		new_state_ok = 1;
48765157Sdab 		break;
48865157Sdab 
48957213Sdab #if	defined(AUTHENTICATION)
49046808Sdab 	    case TELOPT_AUTHENTICATION:
49146808Sdab 		if (autologin)
49246808Sdab 			new_state_ok = 1;
49346808Sdab 		break;
49446808Sdab #endif
4956000Sroot 
49644360Sborman 	    case TELOPT_XDISPLOC:	/* X Display location */
49746808Sdab 		if (env_getvalue((unsigned char *)"DISPLAY"))
49844360Sborman 		    new_state_ok = 1;
49944360Sborman 		break;
50044360Sborman 
50138689Sborman 	    case TELOPT_LINEMODE:
50238689Sborman #ifdef	KLUDGELINEMODE
50338689Sborman 		kludgelinemode = 0;
50444360Sborman 		send_do(TELOPT_SGA, 1);
50538689Sborman #endif
50638689Sborman 		set_my_want_state_will(TELOPT_LINEMODE);
50738689Sborman 		send_will(option, 0);
50838689Sborman 		set_my_state_will(TELOPT_LINEMODE);
50938689Sborman 		slc_init();
51038689Sborman 		return;
51138689Sborman 
51238689Sborman 	    case TELOPT_ECHO:		/* We're never going to echo... */
51338689Sborman 	    default:
5146000Sroot 		break;
51538689Sborman 	    }
51638689Sborman 
51738689Sborman 	    if (new_state_ok) {
51838689Sborman 		set_my_want_state_will(option);
51938689Sborman 		send_will(option, 0);
52045232Sborman 		setconnmode(0);			/* Set new tty mode */
52138689Sborman 	    } else {
52238689Sborman 		will_wont_resp[option]++;
52338689Sborman 		send_wont(option, 0);
52438689Sborman 	    }
52538689Sborman 	  } else {
52638689Sborman 	    /*
52738689Sborman 	     * Handle options that need more things done after the
52838689Sborman 	     * other side has acknowledged the option.
52938689Sborman 	     */
53038689Sborman 	    switch (option) {
53138689Sborman 	    case TELOPT_LINEMODE:
53238689Sborman #ifdef	KLUDGELINEMODE
53338689Sborman 		kludgelinemode = 0;
53444360Sborman 		send_do(TELOPT_SGA, 1);
53538689Sborman #endif
53638689Sborman 		set_my_state_will(option);
53738689Sborman 		slc_init();
53844360Sborman 		send_do(TELOPT_SGA, 0);
53938689Sborman 		return;
54038689Sborman 	    }
54138689Sborman 	  }
5426000Sroot 	}
54338689Sborman 	set_my_state_will(option);
5446000Sroot }
54527676Sminshall 
54646808Sdab 	static void
dontoption(option)54738689Sborman dontoption(option)
54838689Sborman 	int option;
54938689Sborman {
55038689Sborman 
55138689Sborman 	if (will_wont_resp[option]) {
55238689Sborman 	    --will_wont_resp[option];
55338689Sborman 	    if (will_wont_resp[option] && my_state_is_wont(option))
55438689Sborman 		--will_wont_resp[option];
55538689Sborman 	}
55638689Sborman 
55738689Sborman 	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
55838811Sborman 	    switch (option) {
55938811Sborman 	    case TELOPT_LINEMODE:
56038811Sborman 		linemode = 0;	/* put us back to the default state */
56138811Sborman 		break;
56265157Sdab #ifdef	OLD_ENVIRON
56365157Sdab 	    case TELOPT_NEW_ENVIRON:
56465157Sdab 		/*
56565157Sdab 		 * The new environ option wasn't recognized, try
56665157Sdab 		 * the old one.
56765157Sdab 		 */
56865157Sdab 		send_will(TELOPT_OLD_ENVIRON, 1);
56965157Sdab 		telopt_environ = TELOPT_OLD_ENVIRON;
57065157Sdab 		break;
57165157Sdab #endif
57238811Sborman 	    }
57338689Sborman 	    /* we always accept a DONT */
57438689Sborman 	    set_my_want_state_wont(option);
57544360Sborman 	    if (my_state_is_will(option))
57644360Sborman 		send_wont(option, 0);
57739529Sborman 	    setconnmode(0);			/* Set new tty mode */
57838689Sborman 	}
57938689Sborman 	set_my_state_wont(option);
58038689Sborman }
58138689Sborman 
58227676Sminshall /*
58338908Sborman  * Given a buffer returned by tgetent(), this routine will turn
58438908Sborman  * the pipe seperated list of names in the buffer into an array
58538908Sborman  * of pointers to null terminated names.  We toss out any bad,
58638908Sborman  * duplicate, or verbose names (names with spaces).
58738908Sborman  */
58838908Sborman 
58946808Sdab static char *name_unknown = "UNKNOWN";
59046808Sdab static char *unknown[] = { 0, 0 };
59138908Sborman 
59246808Sdab 	char **
mklist(buf,name)59338908Sborman mklist(buf, name)
59446808Sdab 	char *buf, *name;
59538908Sborman {
59638908Sborman 	register int n;
59746808Sdab 	register char c, *cp, **argvp, *cp2, **argv, **avt;
59838908Sborman 
59938908Sborman 	if (name) {
600*69785Sdab 		if ((int)strlen(name) > 40) {
60138908Sborman 			name = 0;
60246808Sdab 			unknown[0] = name_unknown;
60346808Sdab 		} else {
60438908Sborman 			unknown[0] = name;
60538908Sborman 			upcase(name);
60638908Sborman 		}
60746808Sdab 	} else
60846808Sdab 		unknown[0] = name_unknown;
60938908Sborman 	/*
61038908Sborman 	 * Count up the number of names.
61138908Sborman 	 */
61238908Sborman 	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
61338908Sborman 		if (*cp == '|')
61438908Sborman 			n++;
61538908Sborman 	}
61638908Sborman 	/*
61738908Sborman 	 * Allocate an array to put the name pointers into
61838908Sborman 	 */
61938908Sborman 	argv = (char **)malloc((n+3)*sizeof(char *));
62038908Sborman 	if (argv == 0)
62138908Sborman 		return(unknown);
62238908Sborman 
62338908Sborman 	/*
62438908Sborman 	 * Fill up the array of pointers to names.
62538908Sborman 	 */
62638908Sborman 	*argv = 0;
62738908Sborman 	argvp = argv+1;
62838908Sborman 	n = 0;
62938908Sborman 	for (cp = cp2 = buf; (c = *cp);  cp++) {
63038908Sborman 		if (c == '|' || c == ':') {
63138908Sborman 			*cp++ = '\0';
63238908Sborman 			/*
63338908Sborman 			 * Skip entries that have spaces or are over 40
63438908Sborman 			 * characters long.  If this is our environment
63538908Sborman 			 * name, then put it up front.  Otherwise, as
63638908Sborman 			 * long as this is not a duplicate name (case
63738908Sborman 			 * insensitive) add it to the list.
63838908Sborman 			 */
63938908Sborman 			if (n || (cp - cp2 > 41))
64038908Sborman 				;
64138908Sborman 			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
64238908Sborman 				*argv = cp2;
64338908Sborman 			else if (is_unique(cp2, argv+1, argvp))
64438908Sborman 				*argvp++ = cp2;
64538908Sborman 			if (c == ':')
64638908Sborman 				break;
64738908Sborman 			/*
64838908Sborman 			 * Skip multiple delimiters. Reset cp2 to
64938908Sborman 			 * the beginning of the next name. Reset n,
65038908Sborman 			 * the flag for names with spaces.
65138908Sborman 			 */
65238908Sborman 			while ((c = *cp) == '|')
65338908Sborman 				cp++;
65438908Sborman 			cp2 = cp;
65538908Sborman 			n = 0;
65638908Sborman 		}
65738908Sborman 		/*
65838908Sborman 		 * Skip entries with spaces or non-ascii values.
65938908Sborman 		 * Convert lower case letters to upper case.
66038908Sborman 		 */
66138908Sborman 		if ((c == ' ') || !isascii(c))
66238908Sborman 			n = 1;
66338908Sborman 		else if (islower(c))
66438908Sborman 			*cp = toupper(c);
66538908Sborman 	}
666*69785Sdab 
66738908Sborman 	/*
66838908Sborman 	 * Check for an old V6 2 character name.  If the second
66938908Sborman 	 * name points to the beginning of the buffer, and is
67038908Sborman 	 * only 2 characters long, move it to the end of the array.
67138908Sborman 	 */
67238908Sborman 	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
67346808Sdab 		--argvp;
67446808Sdab 		for (avt = &argv[1]; avt < argvp; avt++)
67546808Sdab 			*avt = *(avt+1);
67638908Sborman 		*argvp++ = buf;
67738908Sborman 	}
67838908Sborman 
67938908Sborman 	/*
68038908Sborman 	 * Duplicate last name, for TTYPE option, and null
68138908Sborman 	 * terminate the array.  If we didn't find a match on
68238908Sborman 	 * our terminal name, put that name at the beginning.
68338908Sborman 	 */
68438908Sborman 	cp = *(argvp-1);
68538908Sborman 	*argvp++ = cp;
68638908Sborman 	*argvp = 0;
68738908Sborman 
68838908Sborman 	if (*argv == 0) {
68938908Sborman 		if (name)
69038908Sborman 			*argv = name;
69146808Sdab 		else {
69246808Sdab 			--argvp;
69346808Sdab 			for (avt = argv; avt < argvp; avt++)
69446808Sdab 				*avt = *(avt+1);
69546808Sdab 		}
69638908Sborman 	}
69738908Sborman 	if (*argv)
69838908Sborman 		return(argv);
69938908Sborman 	else
70038908Sborman 		return(unknown);
70138908Sborman }
70238908Sborman 
70346808Sdab 	int
is_unique(name,as,ae)70438908Sborman is_unique(name, as, ae)
70546808Sdab 	register char *name, **as, **ae;
70638908Sborman {
70738908Sborman 	register char **ap;
70838908Sborman 	register int n;
70938908Sborman 
71038908Sborman 	n = strlen(name) + 1;
71138908Sborman 	for (ap = as; ap < ae; ap++)
71238908Sborman 		if (strncasecmp(*ap, name, n) == 0)
71338908Sborman 			return(0);
71438908Sborman 	return (1);
71538908Sborman }
71638908Sborman 
71738908Sborman #ifdef	TERMCAP
71839529Sborman char termbuf[1024];
71946808Sdab 
72046808Sdab 	/*ARGSUSED*/
72146808Sdab 	int
setupterm(tname,fd,errp)72238908Sborman setupterm(tname, fd, errp)
72346808Sdab 	char *tname;
72446808Sdab 	int fd, *errp;
72538908Sborman {
72639529Sborman 	if (tgetent(termbuf, tname) == 1) {
72739529Sborman 		termbuf[1023] = '\0';
72838908Sborman 		if (errp)
72938908Sborman 			*errp = 1;
73038908Sborman 		return(0);
73138908Sborman 	}
73238908Sborman 	if (errp)
73338908Sborman 		*errp = 0;
73438908Sborman 	return(-1);
73538908Sborman }
73639529Sborman #else
73739529Sborman #define	termbuf	ttytype
73839529Sborman extern char ttytype[];
73938908Sborman #endif
74038908Sborman 
74146808Sdab int resettermname = 1;
74246808Sdab 
74346808Sdab 	char *
gettermname()74438908Sborman gettermname()
74538908Sborman {
74638908Sborman 	char *tname;
74746808Sdab 	static char **tnamep = 0;
74838908Sborman 	static char **next;
74938908Sborman 	int err;
75038908Sborman 
75146808Sdab 	if (resettermname) {
75246808Sdab 		resettermname = 0;
75346808Sdab 		if (tnamep && tnamep != unknown)
75446808Sdab 			free(tnamep);
75546808Sdab 		if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) &&
75638908Sborman 				(setupterm(tname, 1, &err) == 0)) {
75739529Sborman 			tnamep = mklist(termbuf, tname);
75838908Sborman 		} else {
759*69785Sdab 			if (tname && ((int)strlen(tname) <= 40)) {
76038908Sborman 				unknown[0] = tname;
76138908Sborman 				upcase(tname);
76246808Sdab 			} else
76346808Sdab 				unknown[0] = name_unknown;
76438908Sborman 			tnamep = unknown;
76538908Sborman 		}
76638908Sborman 		next = tnamep;
76738908Sborman 	}
76838908Sborman 	if (*next == 0)
76938908Sborman 		next = tnamep;
77038908Sborman 	return(*next++);
77138908Sborman }
77238908Sborman /*
77327676Sminshall  * suboption()
77427676Sminshall  *
77527676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
77627676Sminshall  * side.
77727676Sminshall  *
77827676Sminshall  *	Currently we recognize:
77927676Sminshall  *
78027676Sminshall  *		Terminal type, send request.
78137219Sminshall  *		Terminal speed (send request).
78237219Sminshall  *		Local flow control (is request).
78338689Sborman  *		Linemode
78427676Sminshall  */
78527676Sminshall 
78646808Sdab     static void
suboption()78727676Sminshall suboption()
78827676Sminshall {
78965157Sdab     unsigned char subchar;
79065157Sdab 
79146808Sdab     printsub('<', subbuffer, SB_LEN()+2);
79265157Sdab     switch (subchar = SB_GET()) {
79327676Sminshall     case TELOPT_TTYPE:
79438689Sborman 	if (my_want_state_is_wont(TELOPT_TTYPE))
79538689Sborman 	    return;
79646808Sdab 	if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
79746808Sdab 	    return;
79827676Sminshall 	} else {
79927676Sminshall 	    char *name;
80046808Sdab 	    unsigned char temp[50];
80127676Sminshall 	    int len;
80227676Sminshall 
80332377Sminshall #if	defined(TN3270)
80432531Sminshall 	    if (tn3270_ttype()) {
80532377Sminshall 		return;
80632377Sminshall 	    }
80732377Sminshall #endif	/* defined(TN3270) */
80838908Sborman 	    name = gettermname();
80938908Sborman 	    len = strlen(name) + 4 + 2;
81038908Sborman 	    if (len < NETROOM()) {
81146808Sdab 		sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
81238908Sborman 				TELQUAL_IS, name, IAC, SE);
81338689Sborman 		ring_supply_data(&netoring, temp, len);
81438908Sborman 		printsub('>', &temp[2], len-2);
81532377Sminshall 	    } else {
81637226Sminshall 		ExitString("No room in buffer for terminal type.\n", 1);
81732377Sminshall 		/*NOTREACHED*/
81827676Sminshall 	    }
81927676Sminshall 	}
82037219Sminshall 	break;
82137219Sminshall     case TELOPT_TSPEED:
82238689Sborman 	if (my_want_state_is_wont(TELOPT_TSPEED))
82338689Sborman 	    return;
82446808Sdab 	if (SB_EOF())
82546808Sdab 	    return;
82646808Sdab 	if (SB_GET() == TELQUAL_SEND) {
82745232Sborman 	    long ospeed, ispeed;
82846808Sdab 	    unsigned char temp[50];
82937219Sminshall 	    int len;
83027676Sminshall 
83137219Sminshall 	    TerminalSpeeds(&ispeed, &ospeed);
83237219Sminshall 
83346808Sdab 	    sprintf((char *)temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
83438689Sborman 		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
83546808Sdab 	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
83637219Sminshall 
83738689Sborman 	    if (len < NETROOM()) {
83838689Sborman 		ring_supply_data(&netoring, temp, len);
83938689Sborman 		printsub('>', temp+2, len - 2);
84037219Sminshall 	    }
84144360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
84237219Sminshall 	}
84337219Sminshall 	break;
84437219Sminshall     case TELOPT_LFLOW:
84538689Sborman 	if (my_want_state_is_wont(TELOPT_LFLOW))
84638689Sborman 	    return;
84746808Sdab 	if (SB_EOF())
84846808Sdab 	    return;
84946808Sdab 	switch(SB_GET()) {
85057213Sdab 	case LFLOW_RESTART_ANY:
85157213Sdab 	    restartany = 1;
85257213Sdab 	    break;
85357213Sdab 	case LFLOW_RESTART_XON:
85457213Sdab 	    restartany = 0;
85557213Sdab 	    break;
85657213Sdab 	case LFLOW_ON:
85737219Sminshall 	    localflow = 1;
85846808Sdab 	    break;
85957213Sdab 	case LFLOW_OFF:
86037219Sminshall 	    localflow = 0;
86146808Sdab 	    break;
86246808Sdab 	default:
86346808Sdab 	    return;
86437219Sminshall 	}
86537219Sminshall 	setcommandmode();
86638689Sborman 	setconnmode(0);
86737219Sminshall 	break;
86838689Sborman 
86938689Sborman     case TELOPT_LINEMODE:
87038689Sborman 	if (my_want_state_is_wont(TELOPT_LINEMODE))
87138689Sborman 	    return;
87246808Sdab 	if (SB_EOF())
87346808Sdab 	    return;
87446808Sdab 	switch (SB_GET()) {
87538689Sborman 	case WILL:
87646808Sdab 	    lm_will(subpointer, SB_LEN());
87738689Sborman 	    break;
87838689Sborman 	case WONT:
87946808Sdab 	    lm_wont(subpointer, SB_LEN());
88038689Sborman 	    break;
88138689Sborman 	case DO:
88246808Sdab 	    lm_do(subpointer, SB_LEN());
88338689Sborman 	    break;
88438689Sborman 	case DONT:
88546808Sdab 	    lm_dont(subpointer, SB_LEN());
88638689Sborman 	    break;
88738689Sborman 	case LM_SLC:
88846808Sdab 	    slc(subpointer, SB_LEN());
88938689Sborman 	    break;
89038689Sborman 	case LM_MODE:
89146808Sdab 	    lm_mode(subpointer, SB_LEN(), 0);
89238689Sborman 	    break;
89338689Sborman 	default:
89444360Sborman 	    break;
89544360Sborman 	}
89644360Sborman 	break;
89744360Sborman 
89865157Sdab #ifdef	OLD_ENVIRON
89965157Sdab     case TELOPT_OLD_ENVIRON:
90065157Sdab #endif
90165157Sdab     case TELOPT_NEW_ENVIRON:
90246808Sdab 	if (SB_EOF())
90346808Sdab 	    return;
90446808Sdab 	switch(SB_PEEK()) {
90544360Sborman 	case TELQUAL_IS:
90644360Sborman 	case TELQUAL_INFO:
90765157Sdab 	    if (my_want_state_is_dont(subchar))
90844360Sborman 		return;
90944360Sborman 	    break;
91044360Sborman 	case TELQUAL_SEND:
91165157Sdab 	    if (my_want_state_is_wont(subchar)) {
91244360Sborman 		return;
91344360Sborman 	    }
91444360Sborman 	    break;
91544360Sborman 	default:
91644360Sborman 	    return;
91744360Sborman 	}
91846808Sdab 	env_opt(subpointer, SB_LEN());
91944360Sborman 	break;
92044360Sborman 
92144360Sborman     case TELOPT_XDISPLOC:
92244360Sborman 	if (my_want_state_is_wont(TELOPT_XDISPLOC))
92344360Sborman 	    return;
92446808Sdab 	if (SB_EOF())
92546808Sdab 	    return;
92646808Sdab 	if (SB_GET() == TELQUAL_SEND) {
92746808Sdab 	    unsigned char temp[50], *dp;
92844360Sborman 	    int len;
92944360Sborman 
93046808Sdab 	    if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) {
93144360Sborman 		/*
93244360Sborman 		 * Something happened, we no longer have a DISPLAY
93344360Sborman 		 * variable.  So, turn off the option.
93444360Sborman 		 */
93544360Sborman 		send_wont(TELOPT_XDISPLOC, 1);
93638689Sborman 		break;
93744360Sborman 	    }
93846808Sdab 	    sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
93944360Sborman 		    TELQUAL_IS, dp, IAC, SE);
94046808Sdab 	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
94144360Sborman 
94244360Sborman 	    if (len < NETROOM()) {
94344360Sborman 		ring_supply_data(&netoring, temp, len);
94444360Sborman 		printsub('>', temp+2, len - 2);
94544360Sborman 	    }
94644360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
94738689Sborman 	}
94844360Sborman 	break;
94943319Skfall 
95057213Sdab #if	defined(AUTHENTICATION)
95146808Sdab 	case TELOPT_AUTHENTICATION: {
95246808Sdab 		if (!autologin)
95346808Sdab 			break;
95446808Sdab 		if (SB_EOF())
95546808Sdab 			return;
95646808Sdab 		switch(SB_GET()) {
95746808Sdab 		case TELQUAL_IS:
95846808Sdab 			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
95946808Sdab 				return;
96046808Sdab 			auth_is(subpointer, SB_LEN());
96146808Sdab 			break;
96246808Sdab 		case TELQUAL_SEND:
96346808Sdab 			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
96446808Sdab 				return;
96546808Sdab 			auth_send(subpointer, SB_LEN());
96646808Sdab 			break;
96746808Sdab 		case TELQUAL_REPLY:
96846808Sdab 			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
96946808Sdab 				return;
97046808Sdab 			auth_reply(subpointer, SB_LEN());
97146808Sdab 			break;
97247608Sdab 		case TELQUAL_NAME:
97347608Sdab 			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
97447608Sdab 				return;
97547608Sdab 			auth_name(subpointer, SB_LEN());
97647608Sdab 			break;
97743319Skfall 		}
97846808Sdab 	}
97946808Sdab 	break;
98043319Skfall #endif
98160149Sdab #ifdef	ENCRYPTION
98246808Sdab 	case TELOPT_ENCRYPT:
98346808Sdab 		if (SB_EOF())
98446808Sdab 			return;
98546808Sdab 		switch(SB_GET()) {
98646808Sdab 		case ENCRYPT_START:
98746808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
98846808Sdab 				return;
98947608Sdab 			encrypt_start(subpointer, SB_LEN());
99046808Sdab 			break;
99146808Sdab 		case ENCRYPT_END:
99246808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
99346808Sdab 				return;
99446808Sdab 			encrypt_end();
99546808Sdab 			break;
99646808Sdab 		case ENCRYPT_SUPPORT:
99746808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
99846808Sdab 				return;
99946808Sdab 			encrypt_support(subpointer, SB_LEN());
100046808Sdab 			break;
100146808Sdab 		case ENCRYPT_REQSTART:
100246808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
100346808Sdab 				return;
100447608Sdab 			encrypt_request_start(subpointer, SB_LEN());
100546808Sdab 			break;
100646808Sdab 		case ENCRYPT_REQEND:
100746808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
100846808Sdab 				return;
100946808Sdab 			/*
101046808Sdab 			 * We can always send an REQEND so that we cannot
101146808Sdab 			 * get stuck encrypting.  We should only get this
101246808Sdab 			 * if we have been able to get in the correct mode
101346808Sdab 			 * anyhow.
101446808Sdab 			 */
101546808Sdab 			encrypt_request_end();
101646808Sdab 			break;
101746808Sdab 		case ENCRYPT_IS:
101846808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
101946808Sdab 				return;
102046808Sdab 			encrypt_is(subpointer, SB_LEN());
102146808Sdab 			break;
102246808Sdab 		case ENCRYPT_REPLY:
102346808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
102446808Sdab 				return;
102546808Sdab 			encrypt_reply(subpointer, SB_LEN());
102646808Sdab 			break;
102747608Sdab 		case ENCRYPT_ENC_KEYID:
102847608Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
102947608Sdab 				return;
103047608Sdab 			encrypt_enc_keyid(subpointer, SB_LEN());
103147608Sdab 			break;
103247608Sdab 		case ENCRYPT_DEC_KEYID:
103347608Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
103447608Sdab 				return;
103547608Sdab 			encrypt_dec_keyid(subpointer, SB_LEN());
103647608Sdab 			break;
103746808Sdab 		default:
103846808Sdab 			break;
103946808Sdab 		}
104046808Sdab 		break;
104160149Sdab #endif	/* ENCRYPTION */
104227676Sminshall     default:
104327676Sminshall 	break;
104427676Sminshall     }
104527676Sminshall }
104638689Sborman 
104746808Sdab static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
104838689Sborman 
104946808Sdab     void
lm_will(cmd,len)105038689Sborman lm_will(cmd, len)
105146808Sdab     unsigned char *cmd;
105246808Sdab     int len;
105338689Sborman {
105444360Sborman     if (len < 1) {
105544360Sborman /*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
105644360Sborman 	return;
105744360Sborman     }
105838689Sborman     switch(cmd[0]) {
105938689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
106038689Sborman     default:
106138689Sborman 	str_lm[3] = DONT;
106238689Sborman 	str_lm[4] = cmd[0];
106338689Sborman 	if (NETROOM() > sizeof(str_lm)) {
106438689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
106538689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
106638689Sborman 	}
106738689Sborman /*@*/	else printf("lm_will: not enough room in buffer\n");
106838689Sborman 	break;
106938689Sborman     }
107038689Sborman }
107138689Sborman 
107246808Sdab     void
lm_wont(cmd,len)107338689Sborman lm_wont(cmd, len)
107446808Sdab     unsigned char *cmd;
107546808Sdab     int len;
107638689Sborman {
107744360Sborman     if (len < 1) {
107844360Sborman /*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
107944360Sborman 	return;
108044360Sborman     }
108138689Sborman     switch(cmd[0]) {
108238689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
108338689Sborman     default:
108438689Sborman 	/* We are always DONT, so don't respond */
108538689Sborman 	return;
108638689Sborman     }
108738689Sborman }
108838689Sborman 
108946808Sdab     void
lm_do(cmd,len)109038689Sborman lm_do(cmd, len)
109146808Sdab     unsigned char *cmd;
109246808Sdab     int len;
109338689Sborman {
109444360Sborman     if (len < 1) {
109544360Sborman /*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
109644360Sborman 	return;
109744360Sborman     }
109838689Sborman     switch(cmd[0]) {
109938689Sborman     case LM_FORWARDMASK:
110038689Sborman     default:
110138689Sborman 	str_lm[3] = WONT;
110238689Sborman 	str_lm[4] = cmd[0];
110338689Sborman 	if (NETROOM() > sizeof(str_lm)) {
110438689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
110538689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
110638689Sborman 	}
110738689Sborman /*@*/	else printf("lm_do: not enough room in buffer\n");
110838689Sborman 	break;
110938689Sborman     }
111038689Sborman }
111138689Sborman 
111246808Sdab     void
lm_dont(cmd,len)111338689Sborman lm_dont(cmd, len)
111446808Sdab     unsigned char *cmd;
111546808Sdab     int len;
111638689Sborman {
111744360Sborman     if (len < 1) {
111844360Sborman /*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
111944360Sborman 	return;
112044360Sborman     }
112138689Sborman     switch(cmd[0]) {
112238689Sborman     case LM_FORWARDMASK:
112338689Sborman     default:
112438689Sborman 	/* we are always WONT, so don't respond */
112538689Sborman 	break;
112638689Sborman     }
112738689Sborman }
112838689Sborman 
112946808Sdab static unsigned char str_lm_mode[] = {
113046808Sdab 	IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
113146808Sdab };
113238689Sborman 
113346808Sdab 	void
lm_mode(cmd,len,init)113438689Sborman lm_mode(cmd, len, init)
113546808Sdab 	unsigned char *cmd;
113646808Sdab 	int len, init;
113738689Sborman {
113838689Sborman 	if (len != 1)
113938689Sborman 		return;
114044360Sborman 	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
114138689Sborman 		return;
114238689Sborman 	if (*cmd&MODE_ACK)
114338689Sborman 		return;
114444360Sborman 	linemode = *cmd&(MODE_MASK&~MODE_ACK);
114538689Sborman 	str_lm_mode[4] = linemode;
114638689Sborman 	if (!init)
114738689Sborman 	    str_lm_mode[4] |= MODE_ACK;
114838689Sborman 	if (NETROOM() > sizeof(str_lm_mode)) {
114938689Sborman 	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
115038689Sborman 	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
115138689Sborman 	}
115238689Sborman /*@*/	else printf("lm_mode: not enough room in buffer\n");
115338689Sborman 	setconnmode(0);	/* set changed mode */
115438689Sborman }
115538689Sborman 
115632377Sminshall 
115727088Sminshall 
115838689Sborman /*
115938689Sborman  * slc()
116038689Sborman  * Handle special character suboption of LINEMODE.
116138689Sborman  */
116238689Sborman 
116338689Sborman struct spc {
116440245Sborman 	cc_t val;
116540245Sborman 	cc_t *valp;
116638689Sborman 	char flags;	/* Current flags & level */
116738689Sborman 	char mylevel;	/* Maximum level & flags */
116838689Sborman } spc_data[NSLC+1];
116938689Sborman 
117038689Sborman #define SLC_IMPORT	0
117138689Sborman #define	SLC_EXPORT	1
117238689Sborman #define SLC_RVALUE	2
117338689Sborman static int slc_mode = SLC_EXPORT;
117438689Sborman 
117546808Sdab 	void
slc_init()117638689Sborman slc_init()
117738689Sborman {
117838689Sborman 	register struct spc *spcp;
117938689Sborman 
118038689Sborman 	localchars = 1;
118138689Sborman 	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
118238689Sborman 		spcp->val = 0;
118338689Sborman 		spcp->valp = 0;
118438689Sborman 		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
118538689Sborman 	}
118638689Sborman 
118738689Sborman #define	initfunc(func, flags) { \
118838689Sborman 					spcp = &spc_data[func]; \
118938689Sborman 					if (spcp->valp = tcval(func)) { \
119038689Sborman 					    spcp->val = *spcp->valp; \
119138689Sborman 					    spcp->mylevel = SLC_VARIABLE|flags; \
119238689Sborman 					} else { \
119338689Sborman 					    spcp->val = 0; \
119438689Sborman 					    spcp->mylevel = SLC_DEFAULT; \
119538689Sborman 					} \
119638689Sborman 				    }
119738689Sborman 
119838689Sborman 	initfunc(SLC_SYNCH, 0);
119938689Sborman 	/* No BRK */
120038689Sborman 	initfunc(SLC_AO, 0);
120138689Sborman 	initfunc(SLC_AYT, 0);
120238689Sborman 	/* No EOR */
120338689Sborman 	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
120438689Sborman 	initfunc(SLC_EOF, 0);
120539529Sborman #ifndef	SYSV_TERMIO
120638689Sborman 	initfunc(SLC_SUSP, SLC_FLUSHIN);
120738689Sborman #endif
120838689Sborman 	initfunc(SLC_EC, 0);
120938689Sborman 	initfunc(SLC_EL, 0);
121039529Sborman #ifndef	SYSV_TERMIO
121138689Sborman 	initfunc(SLC_EW, 0);
121238689Sborman 	initfunc(SLC_RP, 0);
121338689Sborman 	initfunc(SLC_LNEXT, 0);
121438689Sborman #endif
121538689Sborman 	initfunc(SLC_XON, 0);
121638689Sborman 	initfunc(SLC_XOFF, 0);
121739529Sborman #ifdef	SYSV_TERMIO
121838689Sborman 	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
121938689Sborman 	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
122038689Sborman #endif
122144360Sborman 	initfunc(SLC_FORW1, 0);
122244360Sborman #ifdef	USE_TERMIO
122344360Sborman 	initfunc(SLC_FORW2, 0);
122438689Sborman 	/* No FORW2 */
122544360Sborman #endif
122638689Sborman 
122738689Sborman 	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
122838689Sborman #undef	initfunc
122938689Sborman 
123038689Sborman 	if (slc_mode == SLC_EXPORT)
123138689Sborman 		slc_export();
123238689Sborman 	else
123338689Sborman 		slc_import(1);
123438689Sborman 
123538689Sborman }
123638689Sborman 
123746808Sdab     void
slcstate()123838689Sborman slcstate()
123938689Sborman {
124038689Sborman     printf("Special characters are %s values\n",
124138689Sborman 		slc_mode == SLC_IMPORT ? "remote default" :
124238689Sborman 		slc_mode == SLC_EXPORT ? "local" :
124338689Sborman 					 "remote");
124438689Sborman }
124538689Sborman 
124646808Sdab     void
slc_mode_export()124738689Sborman slc_mode_export()
124838689Sborman {
124938689Sborman     slc_mode = SLC_EXPORT;
125038689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
125138689Sborman 	slc_export();
125238689Sborman }
125338689Sborman 
125446808Sdab     void
slc_mode_import(def)125538689Sborman slc_mode_import(def)
125646808Sdab     int def;
125738689Sborman {
125838689Sborman     slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
125938689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
126038689Sborman 	slc_import(def);
126138689Sborman }
126238689Sborman 
126346808Sdab unsigned char slc_import_val[] = {
126438689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
126538689Sborman };
126646808Sdab unsigned char slc_import_def[] = {
126738689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
126838689Sborman };
126938689Sborman 
127046808Sdab     void
slc_import(def)127138689Sborman slc_import(def)
127246808Sdab     int def;
127338689Sborman {
127438689Sborman     if (NETROOM() > sizeof(slc_import_val)) {
127538689Sborman 	if (def) {
127638689Sborman 	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
127738689Sborman 	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
127838689Sborman 	} else {
127938689Sborman 	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
128038689Sborman 	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
128138689Sborman 	}
128238689Sborman     }
128338689Sborman /*@*/ else printf("slc_import: not enough room\n");
128438689Sborman }
128538689Sborman 
128646808Sdab     void
slc_export()128738689Sborman slc_export()
128838689Sborman {
128938689Sborman     register struct spc *spcp;
129038689Sborman 
129138689Sborman     TerminalDefaultChars();
129238689Sborman 
129338689Sborman     slc_start_reply();
129438689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
129538689Sborman 	if (spcp->mylevel != SLC_NOSUPPORT) {
129645232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
129745232Sborman 		spcp->flags = SLC_NOSUPPORT;
129845232Sborman 	    else
129945232Sborman 		spcp->flags = spcp->mylevel;
130038689Sborman 	    if (spcp->valp)
130138689Sborman 		spcp->val = *spcp->valp;
130245232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
130338689Sborman 	}
130438689Sborman     }
130538689Sborman     slc_end_reply();
130646808Sdab     (void)slc_update();
130746808Sdab     setconnmode(1);	/* Make sure the character values are set */
130838689Sborman }
130938689Sborman 
131046808Sdab 	void
slc(cp,len)131138689Sborman slc(cp, len)
131246808Sdab 	register unsigned char *cp;
131346808Sdab 	int len;
131438689Sborman {
131538689Sborman 	register struct spc *spcp;
131638689Sborman 	register int func,level;
131738689Sborman 
131838689Sborman 	slc_start_reply();
131938689Sborman 
132038689Sborman 	for (; len >= 3; len -=3, cp +=3) {
132138689Sborman 
132238689Sborman 		func = cp[SLC_FUNC];
132338689Sborman 
132438689Sborman 		if (func == 0) {
132538689Sborman 			/*
132638689Sborman 			 * Client side: always ignore 0 function.
132738689Sborman 			 */
132838689Sborman 			continue;
132938689Sborman 		}
133038689Sborman 		if (func > NSLC) {
133145232Sborman 			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
133238689Sborman 				slc_add_reply(func, SLC_NOSUPPORT, 0);
133338689Sborman 			continue;
133438689Sborman 		}
133538689Sborman 
133638689Sborman 		spcp = &spc_data[func];
133738689Sborman 
133838689Sborman 		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
133938689Sborman 
134040245Sborman 		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
134138689Sborman 		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
134238689Sborman 			continue;
134338689Sborman 		}
134438689Sborman 
134538689Sborman 		if (level == (SLC_DEFAULT|SLC_ACK)) {
134638689Sborman 			/*
134738689Sborman 			 * This is an error condition, the SLC_ACK
134838689Sborman 			 * bit should never be set for the SLC_DEFAULT
134938689Sborman 			 * level.  Our best guess to recover is to
135038689Sborman 			 * ignore the SLC_ACK bit.
135138689Sborman 			 */
135238689Sborman 			cp[SLC_FLAGS] &= ~SLC_ACK;
135338689Sborman 		}
135438689Sborman 
135538689Sborman 		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
135640245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
135738689Sborman 			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
135838689Sborman 			continue;
135938689Sborman 		}
136038689Sborman 
136138689Sborman 		level &= ~SLC_ACK;
136238689Sborman 
136338689Sborman 		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
136438689Sborman 			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
136540245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
136638689Sborman 		}
136738689Sborman 		if (level == SLC_DEFAULT) {
136838689Sborman 			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
136938689Sborman 				spcp->flags = spcp->mylevel;
137038689Sborman 			else
137138689Sborman 				spcp->flags = SLC_NOSUPPORT;
137238689Sborman 		}
137338689Sborman 		slc_add_reply(func, spcp->flags, spcp->val);
137438689Sborman 	}
137538689Sborman 	slc_end_reply();
137638689Sborman 	if (slc_update())
137738689Sborman 		setconnmode(1);	/* set the  new character values */
137838689Sborman }
137938689Sborman 
138046808Sdab     void
slc_check()138138689Sborman slc_check()
138238689Sborman {
138338689Sborman     register struct spc *spcp;
138438689Sborman 
138538689Sborman     slc_start_reply();
138638689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
138738689Sborman 	if (spcp->valp && spcp->val != *spcp->valp) {
138838689Sborman 	    spcp->val = *spcp->valp;
138945232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
139045232Sborman 		spcp->flags = SLC_NOSUPPORT;
139145232Sborman 	    else
139245232Sborman 		spcp->flags = spcp->mylevel;
139345232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
139438689Sborman 	}
139538689Sborman     }
139638689Sborman     slc_end_reply();
139738689Sborman     setconnmode(1);
139838689Sborman }
139938689Sborman 
140038689Sborman 
140138689Sborman unsigned char slc_reply[128];
140238689Sborman unsigned char *slc_replyp;
140346808Sdab 
140446808Sdab 	void
slc_start_reply()140538689Sborman slc_start_reply()
140638689Sborman {
140738689Sborman 	slc_replyp = slc_reply;
140838689Sborman 	*slc_replyp++ = IAC;
140938689Sborman 	*slc_replyp++ = SB;
141038689Sborman 	*slc_replyp++ = TELOPT_LINEMODE;
141138689Sborman 	*slc_replyp++ = LM_SLC;
141238689Sborman }
141338689Sborman 
141446808Sdab 	void
slc_add_reply(func,flags,value)141538689Sborman slc_add_reply(func, flags, value)
141646808Sdab 	unsigned char func;
141746808Sdab 	unsigned char flags;
141846808Sdab 	cc_t value;
141938689Sborman {
142038689Sborman 	if ((*slc_replyp++ = func) == IAC)
142138689Sborman 		*slc_replyp++ = IAC;
142238689Sborman 	if ((*slc_replyp++ = flags) == IAC)
142338689Sborman 		*slc_replyp++ = IAC;
142440245Sborman 	if ((*slc_replyp++ = (unsigned char)value) == IAC)
142538689Sborman 		*slc_replyp++ = IAC;
142638689Sborman }
142738689Sborman 
142846808Sdab     void
slc_end_reply()142938689Sborman slc_end_reply()
143038689Sborman {
143138689Sborman     register int len;
143238689Sborman 
143338689Sborman     *slc_replyp++ = IAC;
143438689Sborman     *slc_replyp++ = SE;
143538689Sborman     len = slc_replyp - slc_reply;
143638689Sborman     if (len <= 6)
143738689Sborman 	return;
143838689Sborman     if (NETROOM() > len) {
143938689Sborman 	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
144038689Sborman 	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
144138689Sborman     }
144238689Sborman /*@*/else printf("slc_end_reply: not enough room\n");
144338689Sborman }
144438689Sborman 
144546808Sdab 	int
slc_update()144638689Sborman slc_update()
144738689Sborman {
144838689Sborman 	register struct spc *spcp;
144938689Sborman 	int need_update = 0;
145038689Sborman 
145138689Sborman 	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
145238689Sborman 		if (!(spcp->flags&SLC_ACK))
145338689Sborman 			continue;
145438689Sborman 		spcp->flags &= ~SLC_ACK;
145538689Sborman 		if (spcp->valp && (*spcp->valp != spcp->val)) {
145638689Sborman 			*spcp->valp = spcp->val;
145738689Sborman 			need_update = 1;
145838689Sborman 		}
145938689Sborman 	}
146038689Sborman 	return(need_update);
146138689Sborman }
146238689Sborman 
146365157Sdab #ifdef	OLD_ENVIRON
146465157Sdab # ifdef	ENV_HACK
146558972Sdab /*
146658972Sdab  * Earlier version of telnet/telnetd from the BSD code had
146758972Sdab  * the definitions of VALUE and VAR reversed.  To ensure
146858972Sdab  * maximum interoperability, we assume that the server is
146958972Sdab  * an older BSD server, until proven otherwise.  The newer
147058972Sdab  * BSD servers should be able to handle either definition,
147158972Sdab  * so it is better to use the wrong values if we don't
147258972Sdab  * know what type of server it is.
147358972Sdab  */
147458972Sdab int env_auto = 1;
147565157Sdab int old_env_var = OLD_ENV_VAR;
147665157Sdab int old_env_value = OLD_ENV_VALUE;
147765157Sdab # else
147865157Sdab #  define old_env_var OLD_ENV_VAR
147965157Sdab #  define old_env_value OLD_ENV_VALUE
148065157Sdab # endif
148158972Sdab #endif
148258972Sdab 
148346808Sdab 	void
env_opt(buf,len)148444360Sborman env_opt(buf, len)
148546808Sdab 	register unsigned char *buf;
148646808Sdab 	register int len;
148744360Sborman {
148846808Sdab 	register unsigned char *ep = 0, *epc = 0;
148944360Sborman 	register int i;
149044360Sborman 
149145232Sborman 	switch(buf[0]&0xff) {
149244360Sborman 	case TELQUAL_SEND:
149344360Sborman 		env_opt_start();
149444360Sborman 		if (len == 1) {
149544360Sborman 			env_opt_add(NULL);
149644360Sborman 		} else for (i = 1; i < len; i++) {
149745232Sborman 			switch (buf[i]&0xff) {
149865157Sdab #ifdef	OLD_ENVIRON
149965157Sdab 			case OLD_ENV_VAR:
150065157Sdab # ifdef	ENV_HACK
150165157Sdab 				if (telopt_environ == TELOPT_OLD_ENVIRON
150265157Sdab 				    && env_auto) {
150365157Sdab 					/* Server has the same definitions */
150465157Sdab 					old_env_var = OLD_ENV_VAR;
150565157Sdab 					old_env_value = OLD_ENV_VALUE;
150658972Sdab 				}
150758972Sdab 				/* FALL THROUGH */
150865157Sdab # endif
150965157Sdab 			case OLD_ENV_VALUE:
151058972Sdab 				/*
151165157Sdab 				 * Although OLD_ENV_VALUE is not legal, we will
151258972Sdab 				 * still recognize it, just in case it is an
151358972Sdab 				 * old server that has VAR & VALUE mixed up...
151458972Sdab 				 */
151558972Sdab 				/* FALL THROUGH */
151665157Sdab #else
151765157Sdab 			case NEW_ENV_VAR:
151865157Sdab #endif
151958972Sdab 			case ENV_USERVAR:
152044360Sborman 				if (ep) {
152144360Sborman 					*epc = 0;
152244360Sborman 					env_opt_add(ep);
152344360Sborman 				}
152444360Sborman 				ep = epc = &buf[i+1];
152544360Sborman 				break;
152644360Sborman 			case ENV_ESC:
152744360Sborman 				i++;
152844360Sborman 				/*FALL THROUGH*/
152944360Sborman 			default:
153044360Sborman 				if (epc)
153144360Sborman 					*epc++ = buf[i];
153244360Sborman 				break;
153344360Sborman 			}
153444360Sborman 		}
153558972Sdab 		if (ep) {
153658972Sdab 			*epc = 0;
153758972Sdab 			env_opt_add(ep);
153858972Sdab 		}
153944360Sborman 		env_opt_end(1);
154044360Sborman 		break;
154144360Sborman 
154244360Sborman 	case TELQUAL_IS:
154344360Sborman 	case TELQUAL_INFO:
154444360Sborman 		/* Ignore for now.  We shouldn't get it anyway. */
154544360Sborman 		break;
154644360Sborman 
154744360Sborman 	default:
154844360Sborman 		break;
154944360Sborman 	}
155044360Sborman }
155144360Sborman 
155244360Sborman #define	OPT_REPLY_SIZE	256
155344360Sborman unsigned char *opt_reply;
155444360Sborman unsigned char *opt_replyp;
155544360Sborman unsigned char *opt_replyend;
155644360Sborman 
155746808Sdab 	void
env_opt_start()155844360Sborman env_opt_start()
155944360Sborman {
156044360Sborman 	if (opt_reply)
156144360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
156244360Sborman 	else
156344360Sborman 		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
156444360Sborman 	if (opt_reply == NULL) {
156544360Sborman /*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
156644360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
156744360Sborman 		return;
156844360Sborman 	}
156944360Sborman 	opt_replyp = opt_reply;
157044360Sborman 	opt_replyend = opt_reply + OPT_REPLY_SIZE;
157144360Sborman 	*opt_replyp++ = IAC;
157244360Sborman 	*opt_replyp++ = SB;
157365157Sdab 	*opt_replyp++ = telopt_environ;
157444360Sborman 	*opt_replyp++ = TELQUAL_IS;
157544360Sborman }
157644360Sborman 
157746808Sdab 	void
env_opt_start_info()157844360Sborman env_opt_start_info()
157944360Sborman {
158044360Sborman 	env_opt_start();
158144360Sborman 	if (opt_replyp)
158244360Sborman 	    opt_replyp[-1] = TELQUAL_INFO;
158344360Sborman }
158444360Sborman 
158546808Sdab 	void
env_opt_add(ep)158644360Sborman env_opt_add(ep)
158746808Sdab 	register unsigned char *ep;
158844360Sborman {
158946808Sdab 	register unsigned char *vp, c;
159044360Sborman 
159144360Sborman 	if (opt_reply == NULL)		/*XXX*/
159244360Sborman 		return;			/*XXX*/
159344360Sborman 
159444360Sborman 	if (ep == NULL || *ep == '\0') {
159557213Sdab 		/* Send user defined variables first. */
159657213Sdab 		env_default(1, 0);
159757213Sdab 		while (ep = env_default(0, 0))
159844360Sborman 			env_opt_add(ep);
159957213Sdab 
160057213Sdab 		/* Now add the list of well know variables.  */
160157213Sdab 		env_default(1, 1);
160257213Sdab 		while (ep = env_default(0, 1))
160357213Sdab 			env_opt_add(ep);
160444360Sborman 		return;
160544360Sborman 	}
160644360Sborman 	vp = env_getvalue(ep);
160746808Sdab 	if (opt_replyp + (vp ? strlen((char *)vp) : 0) +
160846808Sdab 				strlen((char *)ep) + 6 > opt_replyend)
160946808Sdab 	{
161044360Sborman 		register int len;
161144360Sborman 		opt_replyend += OPT_REPLY_SIZE;
161244360Sborman 		len = opt_replyend - opt_reply;
161344360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, len);
161444360Sborman 		if (opt_reply == NULL) {
161544360Sborman /*@*/			printf("env_opt_add: realloc() failed!!!\n");
161644360Sborman 			opt_reply = opt_replyp = opt_replyend = NULL;
161744360Sborman 			return;
161844360Sborman 		}
161944360Sborman 		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
162044360Sborman 		opt_replyend = opt_reply + len;
162144360Sborman 	}
162257213Sdab 	if (opt_welldefined(ep))
162365157Sdab #ifdef	OLD_ENVIRON
162465157Sdab 		if (telopt_environ == TELOPT_OLD_ENVIRON)
162565157Sdab 			*opt_replyp++ = old_env_var;
162665157Sdab 		else
162765157Sdab #endif
162865157Sdab 			*opt_replyp++ = NEW_ENV_VAR;
162957213Sdab 	else
163057213Sdab 		*opt_replyp++ = ENV_USERVAR;
163144360Sborman 	for (;;) {
163244360Sborman 		while (c = *ep++) {
163345232Sborman 			switch(c&0xff) {
163444360Sborman 			case IAC:
163544360Sborman 				*opt_replyp++ = IAC;
163644360Sborman 				break;
163765157Sdab 			case NEW_ENV_VAR:
163865157Sdab 			case NEW_ENV_VALUE:
163944360Sborman 			case ENV_ESC:
164057213Sdab 			case ENV_USERVAR:
164144360Sborman 				*opt_replyp++ = ENV_ESC;
164244360Sborman 				break;
164344360Sborman 			}
164444360Sborman 			*opt_replyp++ = c;
164544360Sborman 		}
164644360Sborman 		if (ep = vp) {
164765157Sdab #ifdef	OLD_ENVIRON
164865157Sdab 			if (telopt_environ == TELOPT_OLD_ENVIRON)
164965157Sdab 				*opt_replyp++ = old_env_value;
165065157Sdab 			else
165165157Sdab #endif
165265157Sdab 				*opt_replyp++ = NEW_ENV_VALUE;
165344360Sborman 			vp = NULL;
165444360Sborman 		} else
165544360Sborman 			break;
165644360Sborman 	}
165744360Sborman }
165844360Sborman 
165957213Sdab 	int
opt_welldefined(ep)166057213Sdab opt_welldefined(ep)
166157213Sdab 	char *ep;
166257213Sdab {
166357213Sdab 	if ((strcmp(ep, "USER") == 0) ||
166457213Sdab 	    (strcmp(ep, "DISPLAY") == 0) ||
166557213Sdab 	    (strcmp(ep, "PRINTER") == 0) ||
166657213Sdab 	    (strcmp(ep, "SYSTEMTYPE") == 0) ||
166757213Sdab 	    (strcmp(ep, "JOB") == 0) ||
166857213Sdab 	    (strcmp(ep, "ACCT") == 0))
166957213Sdab 		return(1);
167057213Sdab 	return(0);
167157213Sdab }
167246808Sdab 	void
env_opt_end(emptyok)167344360Sborman env_opt_end(emptyok)
167446808Sdab 	register int emptyok;
167544360Sborman {
167644360Sborman 	register int len;
167744360Sborman 
167844360Sborman 	len = opt_replyp - opt_reply + 2;
167944360Sborman 	if (emptyok || len > 6) {
168044360Sborman 		*opt_replyp++ = IAC;
168144360Sborman 		*opt_replyp++ = SE;
168244360Sborman 		if (NETROOM() > len) {
168344360Sborman 			ring_supply_data(&netoring, opt_reply, len);
168444360Sborman 			printsub('>', &opt_reply[2], len - 2);
168544360Sborman 		}
168644360Sborman /*@*/		else printf("slc_end_reply: not enough room\n");
168744360Sborman 	}
168844360Sborman 	if (opt_reply) {
168944360Sborman 		free(opt_reply);
169044360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
169144360Sborman 	}
169244360Sborman }
169344360Sborman 
169438689Sborman 
169538689Sborman 
169646808Sdab     int
telrcv()169732377Sminshall telrcv()
169827110Sminshall {
169932377Sminshall     register int c;
170032385Sminshall     register int scc;
170146808Sdab     register unsigned char *sbp;
170232385Sminshall     int count;
170332385Sminshall     int returnValue = 0;
170427088Sminshall 
170532385Sminshall     scc = 0;
170632385Sminshall     count = 0;
170732385Sminshall     while (TTYROOM() > 2) {
170832385Sminshall 	if (scc == 0) {
170932385Sminshall 	    if (count) {
171032528Sminshall 		ring_consumed(&netiring, count);
171132385Sminshall 		returnValue = 1;
171232385Sminshall 		count = 0;
171332385Sminshall 	    }
171432528Sminshall 	    sbp = netiring.consume;
171532528Sminshall 	    scc = ring_full_consecutive(&netiring);
171632385Sminshall 	    if (scc == 0) {
171732385Sminshall 		/* No more data coming in */
171832385Sminshall 		break;
171932385Sminshall 	    }
172032385Sminshall 	}
172132385Sminshall 
172232385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
172360149Sdab #ifdef	ENCRYPTION
172446808Sdab 	if (decrypt_input)
172546808Sdab 		c = (*decrypt_input)(c);
172660149Sdab #endif	/* ENCRYPTION */
172732385Sminshall 
172832377Sminshall 	switch (telrcv_state) {
172927110Sminshall 
173032377Sminshall 	case TS_CR:
173132377Sminshall 	    telrcv_state = TS_DATA;
173235518Sminshall 	    if (c == '\0') {
173335518Sminshall 		break;	/* Ignore \0 after CR */
173439529Sborman 	    }
173539529Sborman 	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
173635518Sminshall 		TTYADD(c);
173735518Sminshall 		break;
173832377Sminshall 	    }
173935518Sminshall 	    /* Else, fall through */
174027088Sminshall 
174132377Sminshall 	case TS_DATA:
174232377Sminshall 	    if (c == IAC) {
174332377Sminshall 		telrcv_state = TS_IAC;
174433804Sminshall 		break;
174532377Sminshall 	    }
174632377Sminshall #	    if defined(TN3270)
174732377Sminshall 	    if (In3270) {
174832377Sminshall 		*Ifrontp++ = c;
174932385Sminshall 		while (scc > 0) {
175032385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
175160149Sdab #ifdef	ENCRYPTION
175246808Sdab 		    if (decrypt_input)
175346808Sdab 			c = (*decrypt_input)(c);
175460149Sdab #endif	/* ENCRYPTION */
175532377Sminshall 		    if (c == IAC) {
175632377Sminshall 			telrcv_state = TS_IAC;
175734304Sminshall 			break;
175832377Sminshall 		    }
175932377Sminshall 		    *Ifrontp++ = c;
176032377Sminshall 		}
176132377Sminshall 	    } else
176232377Sminshall #	    endif /* defined(TN3270) */
176335518Sminshall 		    /*
176435518Sminshall 		     * The 'crmod' hack (see following) is needed
176535518Sminshall 		     * since we can't * set CRMOD on output only.
176635518Sminshall 		     * Machines like MULTICS like to send \r without
176735518Sminshall 		     * \n; since we must turn off CRMOD to get proper
176835518Sminshall 		     * input, the mapping is done here (sigh).
176935518Sminshall 		     */
177038689Sborman 	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
177135518Sminshall 		if (scc > 0) {
177235518Sminshall 		    c = *sbp&0xff;
177360149Sdab #ifdef	ENCRYPTION
177446808Sdab 		    if (decrypt_input)
177546808Sdab 			c = (*decrypt_input)(c);
177660149Sdab #endif	/* ENCRYPTION */
177735518Sminshall 		    if (c == 0) {
177835518Sminshall 			sbp++, scc--; count++;
177935518Sminshall 			/* a "true" CR */
178032377Sminshall 			TTYADD('\r');
178138689Sborman 		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
178235518Sminshall 					(c == '\n')) {
178335518Sminshall 			sbp++, scc--; count++;
178432377Sminshall 			TTYADD('\n');
178535518Sminshall 		    } else {
178660149Sdab #ifdef	ENCRYPTION
1787*69785Sdab 			if (decrypt_input)
178846808Sdab 			    (*decrypt_input)(-1);
178960149Sdab #endif	/* ENCRYPTION */
179046808Sdab 
179135518Sminshall 			TTYADD('\r');
179235518Sminshall 			if (crmod) {
179335518Sminshall 				TTYADD('\n');
179432377Sminshall 			}
179532377Sminshall 		    }
179635518Sminshall 		} else {
179735518Sminshall 		    telrcv_state = TS_CR;
179835518Sminshall 		    TTYADD('\r');
179935518Sminshall 		    if (crmod) {
180035518Sminshall 			    TTYADD('\n');
180135518Sminshall 		    }
180232377Sminshall 		}
180332377Sminshall 	    } else {
180432377Sminshall 		TTYADD(c);
180532377Sminshall 	    }
180632377Sminshall 	    continue;
180727088Sminshall 
180832377Sminshall 	case TS_IAC:
180938689Sborman process_iac:
181032377Sminshall 	    switch (c) {
1811*69785Sdab 
181232377Sminshall 	    case WILL:
181332377Sminshall 		telrcv_state = TS_WILL;
181432377Sminshall 		continue;
181527261Sminshall 
181632377Sminshall 	    case WONT:
181732377Sminshall 		telrcv_state = TS_WONT;
181832377Sminshall 		continue;
181927261Sminshall 
182032377Sminshall 	    case DO:
182132377Sminshall 		telrcv_state = TS_DO;
182232377Sminshall 		continue;
182327261Sminshall 
182432377Sminshall 	    case DONT:
182532377Sminshall 		telrcv_state = TS_DONT;
182632377Sminshall 		continue;
182727261Sminshall 
182832377Sminshall 	    case DM:
182932377Sminshall 		    /*
183032377Sminshall 		     * We may have missed an urgent notification,
183132377Sminshall 		     * so make sure we flush whatever is in the
183232377Sminshall 		     * buffer currently.
183332377Sminshall 		     */
183446808Sdab 		printoption("RCVD", IAC, DM);
183532377Sminshall 		SYNCHing = 1;
183644360Sborman 		(void) ttyflush(1);
183732554Sminshall 		SYNCHing = stilloob();
183832377Sminshall 		settimer(gotDM);
183932377Sminshall 		break;
184027088Sminshall 
184132377Sminshall 	    case SB:
184232377Sminshall 		SB_CLEAR();
184332377Sminshall 		telrcv_state = TS_SB;
184432377Sminshall 		continue;
184527261Sminshall 
184632377Sminshall #	    if defined(TN3270)
184732377Sminshall 	    case EOR:
184832377Sminshall 		if (In3270) {
184932377Sminshall 		    if (Ibackp == Ifrontp) {
185032377Sminshall 			Ibackp = Ifrontp = Ibuf;
185132377Sminshall 			ISend = 0;	/* should have been! */
185232377Sminshall 		    } else {
185344360Sborman 			Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
185432377Sminshall 			ISend = 1;
185527088Sminshall 		    }
185627088Sminshall 		}
185746808Sdab 		printoption("RCVD", IAC, EOR);
185827088Sminshall 		break;
185932377Sminshall #	    endif /* defined(TN3270) */
186032377Sminshall 
186132377Sminshall 	    case IAC:
186232377Sminshall #	    if !defined(TN3270)
186332377Sminshall 		TTYADD(IAC);
186432377Sminshall #	    else /* !defined(TN3270) */
186532377Sminshall 		if (In3270) {
186632377Sminshall 		    *Ifrontp++ = IAC;
186732377Sminshall 		} else {
186832377Sminshall 		    TTYADD(IAC);
186932377Sminshall 		}
187032377Sminshall #	    endif /* !defined(TN3270) */
187127088Sminshall 		break;
187232377Sminshall 
187344360Sborman 	    case NOP:
187444360Sborman 	    case GA:
187527088Sminshall 	    default:
187646808Sdab 		printoption("RCVD", IAC, c);
187727088Sminshall 		break;
187827088Sminshall 	    }
187932377Sminshall 	    telrcv_state = TS_DATA;
188032377Sminshall 	    continue;
188127088Sminshall 
188232377Sminshall 	case TS_WILL:
188346808Sdab 	    printoption("RCVD", WILL, c);
188438689Sborman 	    willoption(c);
188532377Sminshall 	    SetIn3270();
188632377Sminshall 	    telrcv_state = TS_DATA;
188732377Sminshall 	    continue;
188827110Sminshall 
188932377Sminshall 	case TS_WONT:
189046808Sdab 	    printoption("RCVD", WONT, c);
189138689Sborman 	    wontoption(c);
189232377Sminshall 	    SetIn3270();
189332377Sminshall 	    telrcv_state = TS_DATA;
189432377Sminshall 	    continue;
189527088Sminshall 
189632377Sminshall 	case TS_DO:
189746808Sdab 	    printoption("RCVD", DO, c);
189837226Sminshall 	    dooption(c);
189932377Sminshall 	    SetIn3270();
190037219Sminshall 	    if (c == TELOPT_NAWS) {
190137219Sminshall 		sendnaws();
190237219Sminshall 	    } else if (c == TELOPT_LFLOW) {
190337219Sminshall 		localflow = 1;
190437219Sminshall 		setcommandmode();
190538689Sborman 		setconnmode(0);
190637219Sminshall 	    }
190732377Sminshall 	    telrcv_state = TS_DATA;
190832377Sminshall 	    continue;
190927088Sminshall 
191032377Sminshall 	case TS_DONT:
191146808Sdab 	    printoption("RCVD", DONT, c);
191238689Sborman 	    dontoption(c);
191337226Sminshall 	    flushline = 1;
191438689Sborman 	    setconnmode(0);	/* set new tty mode (maybe) */
191532377Sminshall 	    SetIn3270();
191632377Sminshall 	    telrcv_state = TS_DATA;
191732377Sminshall 	    continue;
191827088Sminshall 
191932377Sminshall 	case TS_SB:
192032377Sminshall 	    if (c == IAC) {
192132377Sminshall 		telrcv_state = TS_SE;
192232377Sminshall 	    } else {
192332377Sminshall 		SB_ACCUM(c);
192432377Sminshall 	    }
192532377Sminshall 	    continue;
192627088Sminshall 
192732377Sminshall 	case TS_SE:
192832377Sminshall 	    if (c != SE) {
192932377Sminshall 		if (c != IAC) {
193038689Sborman 		    /*
193138689Sborman 		     * This is an error.  We only expect to get
193238689Sborman 		     * "IAC IAC" or "IAC SE".  Several things may
193338689Sborman 		     * have happend.  An IAC was not doubled, the
193438689Sborman 		     * IAC SE was left off, or another option got
193538689Sborman 		     * inserted into the suboption are all possibilities.
193638689Sborman 		     * If we assume that the IAC was not doubled,
193738689Sborman 		     * and really the IAC SE was left off, we could
193838689Sborman 		     * get into an infinate loop here.  So, instead,
193938689Sborman 		     * we terminate the suboption, and process the
194038689Sborman 		     * partial suboption if we can.
194138689Sborman 		     */
194232377Sminshall 		    SB_ACCUM(IAC);
194338689Sborman 		    SB_ACCUM(c);
194446808Sdab 		    subpointer -= 2;
194546808Sdab 		    SB_TERM();
194646808Sdab 
194746808Sdab 		    printoption("In SUBOPTION processing, RCVD", IAC, c);
194838689Sborman 		    suboption();	/* handle sub-option */
194938689Sborman 		    SetIn3270();
195038689Sborman 		    telrcv_state = TS_IAC;
195138689Sborman 		    goto process_iac;
195232377Sminshall 		}
195332377Sminshall 		SB_ACCUM(c);
195432377Sminshall 		telrcv_state = TS_SB;
195532377Sminshall 	    } else {
195638689Sborman 		SB_ACCUM(IAC);
195738689Sborman 		SB_ACCUM(SE);
195846808Sdab 		subpointer -= 2;
195946808Sdab 		SB_TERM();
196032377Sminshall 		suboption();	/* handle sub-option */
196132377Sminshall 		SetIn3270();
196232377Sminshall 		telrcv_state = TS_DATA;
196332377Sminshall 	    }
196427088Sminshall 	}
196527088Sminshall     }
196632667Sminshall     if (count)
196732667Sminshall 	ring_consumed(&netiring, count);
196832385Sminshall     return returnValue||count;
196927088Sminshall }
197032385Sminshall 
197146808Sdab static int bol = 1, local = 0;
197246808Sdab 
197346808Sdab     int
rlogin_susp()197446808Sdab rlogin_susp()
197546808Sdab {
197646808Sdab     if (local) {
197746808Sdab 	local = 0;
197846808Sdab 	bol = 1;
197946808Sdab 	command(0, "z\n", 2);
198046808Sdab 	return(1);
198146808Sdab     }
198246808Sdab     return(0);
198346808Sdab }
198446808Sdab 
198546808Sdab     static int
telsnd()198632554Sminshall telsnd()
198732385Sminshall {
198832385Sminshall     int tcc;
198932385Sminshall     int count;
199032385Sminshall     int returnValue = 0;
199146808Sdab     unsigned char *tbp;
199232385Sminshall 
199332385Sminshall     tcc = 0;
199432385Sminshall     count = 0;
199532385Sminshall     while (NETROOM() > 2) {
199632385Sminshall 	register int sc;
199732385Sminshall 	register int c;
199832385Sminshall 
199932385Sminshall 	if (tcc == 0) {
200032385Sminshall 	    if (count) {
200132528Sminshall 		ring_consumed(&ttyiring, count);
200232385Sminshall 		returnValue = 1;
200332385Sminshall 		count = 0;
200432385Sminshall 	    }
200532528Sminshall 	    tbp = ttyiring.consume;
200632528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
200732385Sminshall 	    if (tcc == 0) {
200832385Sminshall 		break;
200932385Sminshall 	    }
201032385Sminshall 	}
201132385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
201246808Sdab 	if (rlogin != _POSIX_VDISABLE) {
201346808Sdab 		if (bol) {
201446808Sdab 			bol = 0;
201546808Sdab 			if (sc == rlogin) {
201646808Sdab 				local = 1;
201746808Sdab 				continue;
201846808Sdab 			}
201946808Sdab 		} else if (local) {
202046808Sdab 			local = 0;
202146808Sdab 			if (sc == '.' || c == termEofChar) {
202246808Sdab 				bol = 1;
202346808Sdab 				command(0, "close\n", 6);
202446808Sdab 				continue;
202546808Sdab 			}
202646808Sdab 			if (sc == termSuspChar) {
202746808Sdab 				bol = 1;
202846808Sdab 				command(0, "z\n", 2);
202946808Sdab 				continue;
203046808Sdab 			}
203146808Sdab 			if (sc == escape) {
203246808Sdab 				command(0, (char *)tbp, tcc);
203346808Sdab 				bol = 1;
203446808Sdab 				count += tcc;
203546808Sdab 				tcc = 0;
203646808Sdab 				flushline = 1;
203746808Sdab 				break;
203846808Sdab 			}
203946808Sdab 			if (sc != rlogin) {
204046808Sdab 				++tcc;
204146808Sdab 				--tbp;
204246808Sdab 				--count;
204346808Sdab 				c = sc = rlogin;
204446808Sdab 			}
204546808Sdab 		}
204646808Sdab 		if ((sc == '\n') || (sc == '\r'))
204746808Sdab 			bol = 1;
204846808Sdab 	} else if (sc == escape) {
204938689Sborman 	    /*
205038689Sborman 	     * Double escape is a pass through of a single escape character.
205138689Sborman 	     */
205238689Sborman 	    if (tcc && strip(*tbp) == escape) {
205338689Sborman 		tbp++;
205438689Sborman 		tcc--;
205538689Sborman 		count++;
205646808Sdab 		bol = 0;
205738689Sborman 	    } else {
205846808Sdab 		command(0, (char *)tbp, tcc);
205946808Sdab 		bol = 1;
206038689Sborman 		count += tcc;
206138689Sborman 		tcc = 0;
206238689Sborman 		flushline = 1;
206338689Sborman 		break;
206438689Sborman 	    }
206546808Sdab 	} else
206646808Sdab 	    bol = 0;
206738689Sborman #ifdef	KLUDGELINEMODE
206838689Sborman 	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
206932385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
207032385Sminshall 		tcc--; tbp++; count++;
207132385Sminshall 	    } else {
207232385Sminshall 		dontlecho = !dontlecho;
207332385Sminshall 		settimer(echotoggle);
207438689Sborman 		setconnmode(0);
207532385Sminshall 		flushline = 1;
207632385Sminshall 		break;
207732385Sminshall 	    }
207832385Sminshall 	}
207938689Sborman #endif
208038689Sborman 	if (MODE_LOCAL_CHARS(globalmode)) {
208132385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
208246808Sdab 		bol = 1;
208332385Sminshall 		break;
208432385Sminshall 	    }
208532385Sminshall 	}
208638689Sborman 	if (my_want_state_is_wont(TELOPT_BINARY)) {
208732385Sminshall 	    switch (c) {
208832385Sminshall 	    case '\n':
208932385Sminshall 		    /*
209032385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
209132385Sminshall 		     * on our local machine, then probably
209232385Sminshall 		     * a newline (unix) is CRLF (TELNET).
209332385Sminshall 		     */
209432385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
209532385Sminshall 		    NETADD('\r');
209632385Sminshall 		}
209732385Sminshall 		NETADD('\n');
209846808Sdab 		bol = flushline = 1;
209932385Sminshall 		break;
210032385Sminshall 	    case '\r':
210132385Sminshall 		if (!crlf) {
210232385Sminshall 		    NET2ADD('\r', '\0');
210332385Sminshall 		} else {
210432385Sminshall 		    NET2ADD('\r', '\n');
210532385Sminshall 		}
210646808Sdab 		bol = flushline = 1;
210732385Sminshall 		break;
210832385Sminshall 	    case IAC:
210932385Sminshall 		NET2ADD(IAC, IAC);
211032385Sminshall 		break;
211132385Sminshall 	    default:
211232385Sminshall 		NETADD(c);
211332385Sminshall 		break;
211432385Sminshall 	    }
211532385Sminshall 	} else if (c == IAC) {
211632385Sminshall 	    NET2ADD(IAC, IAC);
211732385Sminshall 	} else {
211832385Sminshall 	    NETADD(c);
211932385Sminshall 	}
212032385Sminshall     }
212132667Sminshall     if (count)
212232667Sminshall 	ring_consumed(&ttyiring, count);
212332385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
212432385Sminshall }
212532377Sminshall 
212627088Sminshall /*
212732377Sminshall  * Scheduler()
212832377Sminshall  *
212932377Sminshall  * Try to do something.
213032377Sminshall  *
213132377Sminshall  * If we do something useful, return 1; else return 0.
213232377Sminshall  *
213327110Sminshall  */
213427110Sminshall 
213527110Sminshall 
213646808Sdab     int
Scheduler(block)213732377Sminshall Scheduler(block)
213846808Sdab     int	block;			/* should we block in the select ? */
213927110Sminshall {
214032377Sminshall 		/* One wants to be a bit careful about setting returnValue
214132377Sminshall 		 * to one, since a one implies we did some useful work,
214232377Sminshall 		 * and therefore probably won't be called to block next
214332377Sminshall 		 * time (TN3270 mode only).
214432377Sminshall 		 */
214532531Sminshall     int returnValue;
214632531Sminshall     int netin, netout, netex, ttyin, ttyout;
214727110Sminshall 
214832531Sminshall     /* Decide which rings should be processed */
214932531Sminshall 
215032531Sminshall     netout = ring_full_count(&netoring) &&
215138689Sborman 	    (flushline ||
215238689Sborman 		(my_want_state_is_wont(TELOPT_LINEMODE)
215338689Sborman #ifdef	KLUDGELINEMODE
215438689Sborman 			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
215538689Sborman #endif
215638689Sborman 		) ||
215738689Sborman 			my_want_state_is_will(TELOPT_BINARY));
215832531Sminshall     ttyout = ring_full_count(&ttyoring);
215932531Sminshall 
216032377Sminshall #if	defined(TN3270)
216132531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
216232377Sminshall #else	/* defined(TN3270) */
216332531Sminshall     ttyin = ring_empty_count(&ttyiring);
216432377Sminshall #endif	/* defined(TN3270) */
216532531Sminshall 
216632531Sminshall #if	defined(TN3270)
216732531Sminshall     netin = ring_empty_count(&netiring);
216832377Sminshall #   else /* !defined(TN3270) */
216932531Sminshall     netin = !ISend && ring_empty_count(&netiring);
217032377Sminshall #   endif /* !defined(TN3270) */
217132531Sminshall 
217232531Sminshall     netex = !SYNCHing;
217332531Sminshall 
217432531Sminshall     /* If we have seen a signal recently, reset things */
217532377Sminshall #   if defined(TN3270) && defined(unix)
217632377Sminshall     if (HaveInput) {
217732377Sminshall 	HaveInput = 0;
217844360Sborman 	(void) signal(SIGIO, inputAvailable);
217932377Sminshall     }
218032377Sminshall #endif	/* defined(TN3270) && defined(unix) */
218132377Sminshall 
218232531Sminshall     /* Call to system code to process rings */
218327178Sminshall 
218432531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
218527178Sminshall 
218632531Sminshall     /* Now, look at the input rings, looking for work to do. */
218732377Sminshall 
218832531Sminshall     if (ring_full_count(&ttyiring)) {
218932377Sminshall #   if defined(TN3270)
219032377Sminshall 	if (In3270) {
219134848Sminshall 	    int c;
219234848Sminshall 
219333804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
219432528Sminshall 					ring_full_consecutive(&ttyiring));
219532377Sminshall 	    if (c) {
219632377Sminshall 		returnValue = 1;
2197*69785Sdab 		ring_consumed(&ttyiring, c);
219832377Sminshall 	    }
219932377Sminshall 	} else {
220032377Sminshall #   endif /* defined(TN3270) */
220132554Sminshall 	    returnValue |= telsnd();
220232377Sminshall #   if defined(TN3270)
220327178Sminshall 	}
220432531Sminshall #   endif /* defined(TN3270) */
220527178Sminshall     }
220632377Sminshall 
220732528Sminshall     if (ring_full_count(&netiring)) {
220832377Sminshall #	if !defined(TN3270)
220932385Sminshall 	returnValue |= telrcv();
221032377Sminshall #	else /* !defined(TN3270) */
221132377Sminshall 	returnValue = Push3270();
221232377Sminshall #	endif /* !defined(TN3270) */
221332377Sminshall     }
221432377Sminshall     return returnValue;
221527178Sminshall }
221627178Sminshall 
221727178Sminshall /*
221832377Sminshall  * Select from tty and network...
221927088Sminshall  */
222046808Sdab     void
telnet(user)222146808Sdab telnet(user)
222246808Sdab     char *user;
222327088Sminshall {
222432531Sminshall     sys_telnet_init();
222527088Sminshall 
2226*69785Sdab #if	defined(AUTHENTICATION) || defined(ENCRYPTION)
222746808Sdab     {
222846808Sdab 	static char local_host[256] = { 0 };
222946808Sdab 
223046808Sdab 	if (!local_host[0]) {
223157213Sdab 		gethostname(local_host, sizeof(local_host));
223246808Sdab 		local_host[sizeof(local_host)-1] = 0;
223346808Sdab 	}
223446808Sdab 	auth_encrypt_init(local_host, hostname, "TELNET", 0);
223546808Sdab 	auth_encrypt_user(user);
223646808Sdab     }
223760149Sdab #endif	/* defined(AUTHENTICATION) || defined(ENCRYPTION)  */
223832377Sminshall #   if !defined(TN3270)
223932377Sminshall     if (telnetport) {
224057213Sdab #if	defined(AUTHENTICATION)
224146808Sdab 	if (autologin)
224246808Sdab 		send_will(TELOPT_AUTHENTICATION, 1);
224346808Sdab #endif
224460149Sdab #ifdef	ENCRYPTION
224546808Sdab 	send_do(TELOPT_ENCRYPT, 1);
224646808Sdab 	send_will(TELOPT_ENCRYPT, 1);
224760149Sdab #endif	/* ENCRYPTION */
224838689Sborman 	send_do(TELOPT_SGA, 1);
224938689Sborman 	send_will(TELOPT_TTYPE, 1);
225038689Sborman 	send_will(TELOPT_NAWS, 1);
225138689Sborman 	send_will(TELOPT_TSPEED, 1);
225238689Sborman 	send_will(TELOPT_LFLOW, 1);
225338689Sborman 	send_will(TELOPT_LINEMODE, 1);
225465157Sdab 	send_will(TELOPT_NEW_ENVIRON, 1);
225538908Sborman 	send_do(TELOPT_STATUS, 1);
225646808Sdab 	if (env_getvalue((unsigned char *)"DISPLAY"))
225744360Sborman 	    send_will(TELOPT_XDISPLOC, 1);
225846808Sdab 	if (eight)
225946808Sdab 	    tel_enter_binary(eight);
226027178Sminshall     }
226132377Sminshall #   endif /* !defined(TN3270) */
226227088Sminshall 
226332377Sminshall #   if !defined(TN3270)
226432377Sminshall     for (;;) {
226532385Sminshall 	int schedValue;
226632385Sminshall 
226732385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
226832385Sminshall 	    if (schedValue == -1) {
226932385Sminshall 		setcommandmode();
227032385Sminshall 		return;
227132385Sminshall 	    }
227232385Sminshall 	}
227332385Sminshall 
227432531Sminshall 	if (Scheduler(1) == -1) {
227532377Sminshall 	    setcommandmode();
227632377Sminshall 	    return;
227732377Sminshall 	}
227832377Sminshall     }
227932377Sminshall #   else /* !defined(TN3270) */
228032377Sminshall     for (;;) {
228132377Sminshall 	int schedValue;
228227088Sminshall 
228332377Sminshall 	while (!In3270 && !shell_active) {
228432531Sminshall 	    if (Scheduler(1) == -1) {
228532377Sminshall 		setcommandmode();
228632377Sminshall 		return;
228732377Sminshall 	    }
228827088Sminshall 	}
228932377Sminshall 
229032377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
229132377Sminshall 	    if (schedValue == -1) {
229232377Sminshall 		setcommandmode();
229332377Sminshall 		return;
229432377Sminshall 	    }
229527088Sminshall 	}
229632377Sminshall 		/* If there is data waiting to go out to terminal, don't
229732377Sminshall 		 * schedule any more data for the terminal.
229832377Sminshall 		 */
229934304Sminshall 	if (ring_full_count(&ttyoring)) {
230032377Sminshall 	    schedValue = 1;
230127088Sminshall 	} else {
230232377Sminshall 	    if (shell_active) {
230332377Sminshall 		if (shell_continue() == 0) {
230432377Sminshall 		    ConnectScreen();
230527088Sminshall 		}
230632377Sminshall 	    } else if (In3270) {
230732377Sminshall 		schedValue = DoTerminalOutput();
230832377Sminshall 	    }
230927088Sminshall 	}
231032377Sminshall 	if (schedValue && (shell_active == 0)) {
231132531Sminshall 	    if (Scheduler(1) == -1) {
231232377Sminshall 		setcommandmode();
231332377Sminshall 		return;
231432377Sminshall 	    }
231527088Sminshall 	}
231632377Sminshall     }
231732377Sminshall #   endif /* !defined(TN3270) */
231827088Sminshall }
231932377Sminshall 
232034848Sminshall #if	0	/* XXX - this not being in is a bug */
232127088Sminshall /*
232232554Sminshall  * nextitem()
232332554Sminshall  *
232432554Sminshall  *	Return the address of the next "item" in the TELNET data
232532554Sminshall  * stream.  This will be the address of the next character if
232632554Sminshall  * the current address is a user data character, or it will
232732554Sminshall  * be the address of the character following the TELNET command
232832554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
232932554Sminshall  * character.
233032554Sminshall  */
233132554Sminshall 
233246808Sdab     static char *
233332554Sminshall nextitem(current)
233446808Sdab     char *current;
233532554Sminshall {
233632554Sminshall     if ((*current&0xff) != IAC) {
233732554Sminshall 	return current+1;
233832554Sminshall     }
233932554Sminshall     switch (*(current+1)&0xff) {
234032554Sminshall     case DO:
234132554Sminshall     case DONT:
234232554Sminshall     case WILL:
234332554Sminshall     case WONT:
234432554Sminshall 	return current+3;
234532554Sminshall     case SB:		/* loop forever looking for the SE */
234632554Sminshall 	{
234732554Sminshall 	    register char *look = current+2;
234832554Sminshall 
234932554Sminshall 	    for (;;) {
235032554Sminshall 		if ((*look++&0xff) == IAC) {
235132554Sminshall 		    if ((*look++&0xff) == SE) {
235232554Sminshall 			return look;
235332554Sminshall 		    }
235432554Sminshall 		}
235532554Sminshall 	    }
235632554Sminshall 	}
235732554Sminshall     default:
235832554Sminshall 	return current+2;
235932554Sminshall     }
236032554Sminshall }
236134848Sminshall #endif	/* 0 */
236232554Sminshall 
236332554Sminshall /*
236432554Sminshall  * netclear()
236532554Sminshall  *
236632554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
236732554Sminshall  * the path to the network.
236832554Sminshall  *
236932554Sminshall  *	Things are a bit tricky since we may have sent the first
237032554Sminshall  * byte or so of a previous TELNET command into the network.
237132554Sminshall  * So, we have to scan the network buffer from the beginning
237232554Sminshall  * until we are up to where we want to be.
237332554Sminshall  *
237432554Sminshall  *	A side effect of what we do, just to keep things
237532554Sminshall  * simple, is to clear the urgent data pointer.  The principal
237632554Sminshall  * caller should be setting the urgent data pointer AFTER calling
237732554Sminshall  * us in any case.
237832554Sminshall  */
237932554Sminshall 
238046808Sdab     static void
netclear()238132554Sminshall netclear()
238232554Sminshall {
238332554Sminshall #if	0	/* XXX */
238432554Sminshall     register char *thisitem, *next;
238532554Sminshall     char *good;
238632554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
238732554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
238832554Sminshall 
238932554Sminshall     thisitem = netobuf;
239032554Sminshall 
239132554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
239232554Sminshall 	thisitem = next;
239332554Sminshall     }
239432554Sminshall 
239532554Sminshall     /* Now, thisitem is first before/at boundary. */
239632554Sminshall 
239732554Sminshall     good = netobuf;	/* where the good bytes go */
239832554Sminshall 
239932554Sminshall     while (netoring.add > thisitem) {
240032554Sminshall 	if (wewant(thisitem)) {
240132554Sminshall 	    int length;
240232554Sminshall 
240332554Sminshall 	    next = thisitem;
240432554Sminshall 	    do {
240532554Sminshall 		next = nextitem(next);
240632554Sminshall 	    } while (wewant(next) && (nfrontp > next));
240732554Sminshall 	    length = next-thisitem;
2408*69785Sdab 	    memmove(good, thisitem, length);
240932554Sminshall 	    good += length;
241032554Sminshall 	    thisitem = next;
241132554Sminshall 	} else {
241232554Sminshall 	    thisitem = nextitem(thisitem);
241332554Sminshall 	}
241432554Sminshall     }
241532554Sminshall 
241632554Sminshall #endif	/* 0 */
241732554Sminshall }
241832554Sminshall 
241932554Sminshall /*
242032377Sminshall  * These routines add various telnet commands to the data stream.
242127088Sminshall  */
242232377Sminshall 
242346808Sdab     static void
doflush()242432554Sminshall doflush()
242532554Sminshall {
242632554Sminshall     NET2ADD(IAC, DO);
242732554Sminshall     NETADD(TELOPT_TM);
242832554Sminshall     flushline = 1;
242932554Sminshall     flushout = 1;
243044360Sborman     (void) ttyflush(1);			/* Flush/drop output */
243132554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
243246808Sdab     printoption("SENT", DO, TELOPT_TM);
243332554Sminshall }
243432554Sminshall 
243546808Sdab     void
xmitAO()243632377Sminshall xmitAO()
243727088Sminshall {
243832377Sminshall     NET2ADD(IAC, AO);
243946808Sdab     printoption("SENT", IAC, AO);
244032377Sminshall     if (autoflush) {
244132377Sminshall 	doflush();
244232377Sminshall     }
244332377Sminshall }
244427088Sminshall 
244532377Sminshall 
244646808Sdab     void
xmitEL()244732377Sminshall xmitEL()
244827088Sminshall {
244932377Sminshall     NET2ADD(IAC, EL);
245046808Sdab     printoption("SENT", IAC, EL);
245127088Sminshall }
245227088Sminshall 
245346808Sdab     void
xmitEC()245432377Sminshall xmitEC()
245527088Sminshall {
245632377Sminshall     NET2ADD(IAC, EC);
245746808Sdab     printoption("SENT", IAC, EC);
245827088Sminshall }
245927088Sminshall 
246032377Sminshall 
246146808Sdab     int
dosynch()246232377Sminshall dosynch()
246327088Sminshall {
246432377Sminshall     netclear();			/* clear the path to the network */
246533294Sminshall     NETADD(IAC);
246633294Sminshall     setneturg();
246733294Sminshall     NETADD(DM);
246846808Sdab     printoption("SENT", IAC, DM);
246946808Sdab     return 1;
247027088Sminshall }
247127088Sminshall 
247246808Sdab int want_status_response = 0;
247346808Sdab 
247446808Sdab     int
get_status()247538908Sborman get_status()
247638908Sborman {
247746808Sdab     unsigned char tmp[16];
247846808Sdab     register unsigned char *cp;
247938908Sborman 
248038908Sborman     if (my_want_state_is_dont(TELOPT_STATUS)) {
248138908Sborman 	printf("Remote side does not support STATUS option\n");
248246808Sdab 	return 0;
248338908Sborman     }
248438908Sborman     cp = tmp;
248538908Sborman 
248638908Sborman     *cp++ = IAC;
248738908Sborman     *cp++ = SB;
248838908Sborman     *cp++ = TELOPT_STATUS;
248938908Sborman     *cp++ = TELQUAL_SEND;
249038908Sborman     *cp++ = IAC;
249138908Sborman     *cp++ = SE;
249238908Sborman     if (NETROOM() >= cp - tmp) {
249338908Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
249438908Sborman 	printsub('>', tmp+2, cp - tmp - 2);
249538908Sborman     }
249646808Sdab     ++want_status_response;
249746808Sdab     return 1;
249838908Sborman }
249938908Sborman 
250046808Sdab     void
intp()250132377Sminshall intp()
250227088Sminshall {
250332377Sminshall     NET2ADD(IAC, IP);
250446808Sdab     printoption("SENT", IAC, IP);
250532377Sminshall     flushline = 1;
250632377Sminshall     if (autoflush) {
250732377Sminshall 	doflush();
250832377Sminshall     }
250932377Sminshall     if (autosynch) {
251032377Sminshall 	dosynch();
251132377Sminshall     }
251227088Sminshall }
251327186Sminshall 
251446808Sdab     void
sendbrk()251532377Sminshall sendbrk()
251627186Sminshall {
251732377Sminshall     NET2ADD(IAC, BREAK);
251846808Sdab     printoption("SENT", IAC, BREAK);
251932377Sminshall     flushline = 1;
252032377Sminshall     if (autoflush) {
252132377Sminshall 	doflush();
252232377Sminshall     }
252332377Sminshall     if (autosynch) {
252432377Sminshall 	dosynch();
252532377Sminshall     }
252627186Sminshall }
252738689Sborman 
252846808Sdab     void
sendabort()252938689Sborman sendabort()
253038689Sborman {
253138689Sborman     NET2ADD(IAC, ABORT);
253246808Sdab     printoption("SENT", IAC, ABORT);
253338689Sborman     flushline = 1;
253438689Sborman     if (autoflush) {
253538689Sborman 	doflush();
253638689Sborman     }
253738689Sborman     if (autosynch) {
253838689Sborman 	dosynch();
253938689Sborman     }
254038689Sborman }
254138689Sborman 
254246808Sdab     void
sendsusp()254338689Sborman sendsusp()
254438689Sborman {
254538689Sborman     NET2ADD(IAC, SUSP);
254646808Sdab     printoption("SENT", IAC, SUSP);
254738689Sborman     flushline = 1;
254838689Sborman     if (autoflush) {
254938689Sborman 	doflush();
255038689Sborman     }
255138689Sborman     if (autosynch) {
255238689Sborman 	dosynch();
255338689Sborman     }
255438689Sborman }
255538689Sborman 
255646808Sdab     void
sendeof()255738689Sborman sendeof()
255838689Sborman {
255938908Sborman     NET2ADD(IAC, xEOF);
256046808Sdab     printoption("SENT", IAC, xEOF);
256138689Sborman }
256238689Sborman 
256346808Sdab     void
sendayt()256445232Sborman sendayt()
256545232Sborman {
256645232Sborman     NET2ADD(IAC, AYT);
256746808Sdab     printoption("SENT", IAC, AYT);
256845232Sborman }
256945232Sborman 
257037219Sminshall /*
257137219Sminshall  * Send a window size update to the remote system.
257237219Sminshall  */
257337219Sminshall 
257446808Sdab     void
sendnaws()257537219Sminshall sendnaws()
257637219Sminshall {
257737219Sminshall     long rows, cols;
257838689Sborman     unsigned char tmp[16];
257938689Sborman     register unsigned char *cp;
258037219Sminshall 
258138689Sborman     if (my_state_is_wont(TELOPT_NAWS))
258238689Sborman 	return;
258337219Sminshall 
258438689Sborman #define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
258538689Sborman 			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
258638689Sborman 
258737219Sminshall     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
258837219Sminshall 	return;
258937219Sminshall     }
259037219Sminshall 
259138689Sborman     cp = tmp;
259238689Sborman 
259338689Sborman     *cp++ = IAC;
259438689Sborman     *cp++ = SB;
259538689Sborman     *cp++ = TELOPT_NAWS;
259638689Sborman     PUTSHORT(cp, cols);
259738689Sborman     PUTSHORT(cp, rows);
259838689Sborman     *cp++ = IAC;
259938689Sborman     *cp++ = SE;
260038689Sborman     if (NETROOM() >= cp - tmp) {
260138689Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
260238689Sborman 	printsub('>', tmp+2, cp - tmp - 2);
260337219Sminshall     }
260437219Sminshall }
260537226Sminshall 
260646808Sdab     void
tel_enter_binary(rw)260738908Sborman tel_enter_binary(rw)
260846808Sdab     int rw;
260937226Sminshall {
261038908Sborman     if (rw&1)
261138908Sborman 	send_do(TELOPT_BINARY, 1);
261238908Sborman     if (rw&2)
261338908Sborman 	send_will(TELOPT_BINARY, 1);
261437226Sminshall }
261537226Sminshall 
261646808Sdab     void
tel_leave_binary(rw)261738908Sborman tel_leave_binary(rw)
261846808Sdab     int rw;
261937226Sminshall {
262038908Sborman     if (rw&1)
262138908Sborman 	send_dont(TELOPT_BINARY, 1);
262238908Sborman     if (rw&2)
262338908Sborman 	send_wont(TELOPT_BINARY, 1);
262437226Sminshall }
2625