xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 44360)
133685Sbostic /*
233685Sbostic  * Copyright (c) 1988 Regents of the University of California.
333685Sbostic  * All rights reserved.
433685Sbostic  *
542770Sbostic  * %sccs.include.redist.c%
633685Sbostic  */
711758Ssam 
821580Sdist #ifndef lint
9*44360Sborman static char sccsid[] = "@(#)telnet.c	5.50 (Berkeley) 06/28/90";
1033685Sbostic #endif /* not lint */
1121580Sdist 
129217Ssam #include <sys/types.h>
139217Ssam 
1443319Skfall #ifdef	KERBEROS
1543319Skfall #include <sys/socket.h>
1643319Skfall #include <netinet/in.h>
1743319Skfall #include <kerberosIV/des.h>
1843319Skfall #include <kerberosIV/krb.h>
1943319Skfall #include "krb4-proto.h"
2043319Skfall #endif
2143319Skfall 
2232377Sminshall #if	defined(unix)
2333804Sminshall #include <signal.h>
2432377Sminshall /* By the way, we need to include curses.h before telnet.h since,
2532377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
2632377Sminshall  * declared in curses.h.
2732377Sminshall  */
2832377Sminshall #endif	/* defined(unix) */
2932377Sminshall 
3012212Ssam #include <arpa/telnet.h>
3132377Sminshall 
32*44360Sborman #if	defined(unix)
33*44360Sborman #include <strings.h>
34*44360Sborman #else	/* defined(unix) */
3532377Sminshall #include <string.h>
36*44360Sborman #endif	/* defined(unix) */
379217Ssam 
3838908Sborman #include <ctype.h>
3938908Sborman 
4032381Sminshall #include "ring.h"
4132381Sminshall 
4232377Sminshall #include "defines.h"
4332377Sminshall #include "externs.h"
4432377Sminshall #include "types.h"
4532377Sminshall #include "general.h"
4627178Sminshall 
4727178Sminshall 
4827228Sminshall #define	strip(x)	((x)&0x7f)
496000Sroot 
50*44360Sborman extern char *env_getvalue();
5127088Sminshall 
5232377Sminshall static char	subbuffer[SUBBUFSIZE],
5332377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
5427676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
5527676Sminshall #define	SB_TERM()	subend = subpointer;
5627676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
5727676Sminshall 				*subpointer++ = (c); \
5827676Sminshall 			}
5927676Sminshall 
6037226Sminshall char	options[256];		/* The combined options */
6138689Sborman char	do_dont_resp[256];
6238689Sborman char	will_wont_resp[256];
636000Sroot 
6432377Sminshall int
6532377Sminshall 	connected,
6632377Sminshall 	showoptions,
6732377Sminshall 	In3270,		/* Are we in 3270 mode? */
6832377Sminshall 	ISend,		/* trying to send network data in */
6943319Skfall #ifdef	KERBEROS
7043319Skfall 	kerberized = 0,	/* Are we using Kerberos authentication ? */
7143319Skfall #endif
7232377Sminshall 	debug = 0,
7332377Sminshall 	crmod,
7432377Sminshall 	netdata,	/* Print out network data flow */
7532377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
7634848Sminshall #if	defined(TN3270)
7736241Sminshall 	noasynchtty = 0,/* User specified "-noasynch" on command line */
7836241Sminshall 	noasynchnet = 0,/* User specified "-noasynch" on command line */
7932377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
8034848Sminshall #endif	/* defined(TN3270) */
8133286Sminshall 	telnetport,
8232531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
8332531Sminshall 	flushout,	/* flush output */
8432531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
8532531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
8637219Sminshall 	localflow,	/* we handle flow control locally */
8732531Sminshall 	localchars,	/* we recognize interrupt/quit */
8832531Sminshall 	donelclchars,	/* the user has set "localchars" */
8932531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
9032531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
9132531Sminshall 	globalmode;
9227088Sminshall 
93*44360Sborman char *prompt = 0;
946000Sroot 
95*44360Sborman cc_t escape;
96*44360Sborman #ifdef	KLUDGELINEMODE
97*44360Sborman cc_t echoc;
98*44360Sborman #endif
9927186Sminshall 
10027186Sminshall /*
1016000Sroot  * Telnet receiver states for fsm
1026000Sroot  */
1036000Sroot #define	TS_DATA		0
1046000Sroot #define	TS_IAC		1
1056000Sroot #define	TS_WILL		2
1066000Sroot #define	TS_WONT		3
1076000Sroot #define	TS_DO		4
1086000Sroot #define	TS_DONT		5
10927021Sminshall #define	TS_CR		6
11027676Sminshall #define	TS_SB		7		/* sub-option collection */
11127676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1126000Sroot 
11332377Sminshall static int	telrcv_state;
1146000Sroot 
11532377Sminshall jmp_buf	toplevel = { 0 };
11632377Sminshall jmp_buf	peerdied;
1176000Sroot 
11832377Sminshall int	flushline;
11938811Sborman int	linemode;
12027021Sminshall 
12138689Sborman #ifdef	KLUDGELINEMODE
12238689Sborman int	kludgelinemode = 1;
12338689Sborman #endif
12438689Sborman 
12532377Sminshall /*
12632377Sminshall  * The following are some clocks used to decide how to interpret
12732377Sminshall  * the relationship between various variables.
12832377Sminshall  */
1296000Sroot 
13032377Sminshall Clocks clocks;
13132377Sminshall 
13238689Sborman #ifdef	notdef
13332377Sminshall Modelist modelist[] = {
13432377Sminshall 	{ "telnet command mode", COMMAND_LINE },
13532377Sminshall 	{ "character-at-a-time mode", 0 },
13632377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
13732377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
13832377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
13932377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
14032377Sminshall 	{ "3270 mode", 0 },
14132377Sminshall };
14238689Sborman #endif
1436000Sroot 
14432377Sminshall 
14532377Sminshall /*
14632377Sminshall  * Initialize telnet environment.
14732377Sminshall  */
1486000Sroot 
14932377Sminshall init_telnet()
15032377Sminshall {
151*44360Sborman     env_init();
152*44360Sborman 
15332377Sminshall     SB_CLEAR();
15437226Sminshall     ClearArray(options);
1556000Sroot 
15637219Sminshall     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
1576000Sroot 
15832377Sminshall     SYNCHing = 0;
1596000Sroot 
16032377Sminshall     /* Don't change NetTrace */
1616000Sroot 
16232377Sminshall     escape = CONTROL(']');
163*44360Sborman #ifdef	KLUDGELINEMODE
16432377Sminshall     echoc = CONTROL('E');
165*44360Sborman #endif
1666000Sroot 
16732377Sminshall     flushline = 1;
16832377Sminshall     telrcv_state = TS_DATA;
16932377Sminshall }
17032554Sminshall 
1716000Sroot 
172*44360Sborman #ifdef	notdef
17332554Sminshall #include <varargs.h>
1746000Sroot 
17534848Sminshall /*VARARGS*/
17632554Sminshall static void
17732554Sminshall printring(va_alist)
17832554Sminshall va_dcl
17932554Sminshall {
18032554Sminshall     va_list ap;
18132554Sminshall     char buffer[100];		/* where things go */
18232554Sminshall     char *ptr;
18332554Sminshall     char *format;
18432554Sminshall     char *string;
18532554Sminshall     Ring *ring;
18632554Sminshall     int i;
18732554Sminshall 
18832554Sminshall     va_start(ap);
18932554Sminshall 
19032554Sminshall     ring = va_arg(ap, Ring *);
19132554Sminshall     format = va_arg(ap, char *);
19232554Sminshall     ptr = buffer;
19332554Sminshall 
19432554Sminshall     while ((i = *format++) != 0) {
19532554Sminshall 	if (i == '%') {
19632554Sminshall 	    i = *format++;
19732554Sminshall 	    switch (i) {
19832554Sminshall 	    case 'c':
19932554Sminshall 		*ptr++ = va_arg(ap, int);
20032554Sminshall 		break;
20132554Sminshall 	    case 's':
20232554Sminshall 		string = va_arg(ap, char *);
20332554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
20432554Sminshall 		ring_supply_data(ring, string, strlen(string));
20532554Sminshall 		ptr = buffer;
20632554Sminshall 		break;
20732554Sminshall 	    case 0:
20832554Sminshall 		ExitString("printring: trailing %%.\n", 1);
20932554Sminshall 		/*NOTREACHED*/
21032554Sminshall 	    default:
21132554Sminshall 		ExitString("printring: unknown format character.\n", 1);
21232554Sminshall 		/*NOTREACHED*/
21332554Sminshall 	    }
21432554Sminshall 	} else {
21532554Sminshall 	    *ptr++ = i;
21632554Sminshall 	}
21732554Sminshall     }
21832554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
21932554Sminshall }
220*44360Sborman #endif
22132554Sminshall 
22237226Sminshall /*
22337226Sminshall  * These routines are in charge of sending option negotiations
22437226Sminshall  * to the other side.
22537226Sminshall  *
22637226Sminshall  * The basic idea is that we send the negotiation if either side
22737226Sminshall  * is in disagreement as to what the current state should be.
22837226Sminshall  */
22932554Sminshall 
23038689Sborman send_do(c, init)
23138689Sborman register int c, init;
2326000Sroot {
23338689Sborman     if (init) {
23438689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
23538689Sborman 				my_want_state_is_do(c))
23638689Sborman 	    return;
23738689Sborman 	set_my_want_state_do(c);
23838689Sborman 	do_dont_resp[c]++;
23937226Sminshall     }
24038689Sborman     NET2ADD(IAC, DO);
24138689Sborman     NETADD(c);
24238689Sborman     printoption("SENT", "do", c);
24337226Sminshall }
24437226Sminshall 
24537226Sminshall void
24638689Sborman send_dont(c, init)
24738689Sborman register int c, init;
24837226Sminshall {
24938689Sborman     if (init) {
25038689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
25138689Sborman 				my_want_state_is_dont(c))
25238689Sborman 	    return;
25338689Sborman 	set_my_want_state_dont(c);
25438689Sborman 	do_dont_resp[c]++;
25537226Sminshall     }
25638689Sborman     NET2ADD(IAC, DONT);
25738689Sborman     NETADD(c);
25838689Sborman     printoption("SENT", "dont", c);
25937226Sminshall }
26037226Sminshall 
26137226Sminshall void
26238689Sborman send_will(c, init)
26338689Sborman register int c, init;
26437226Sminshall {
26538689Sborman     if (init) {
26638689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
26738689Sborman 				my_want_state_is_will(c))
26838689Sborman 	    return;
26938689Sborman 	set_my_want_state_will(c);
27038689Sborman 	will_wont_resp[c]++;
27137226Sminshall     }
27238689Sborman     NET2ADD(IAC, WILL);
27338689Sborman     NETADD(c);
27438689Sborman     printoption("SENT", "will", c);
27537226Sminshall }
27637226Sminshall 
27737226Sminshall void
27838689Sborman send_wont(c, init)
27938689Sborman register int c, init;
28037226Sminshall {
28138689Sborman     if (init) {
28238689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
28338689Sborman 				my_want_state_is_wont(c))
28438689Sborman 	    return;
28538689Sborman 	set_my_want_state_wont(c);
28638689Sborman 	will_wont_resp[c]++;
28737226Sminshall     }
28838689Sborman     NET2ADD(IAC, WONT);
28938689Sborman     NETADD(c);
29038689Sborman     printoption("SENT", "wont", c);
29137226Sminshall }
29237226Sminshall 
29337226Sminshall 
29437226Sminshall void
29537226Sminshall willoption(option)
29637226Sminshall 	int option;
29737226Sminshall {
29838689Sborman 	int new_state_ok = 0;
2996000Sroot 
30038689Sborman 	if (do_dont_resp[option]) {
30138689Sborman 	    --do_dont_resp[option];
30238689Sborman 	    if (do_dont_resp[option] && my_state_is_do(option))
30338689Sborman 		--do_dont_resp[option];
30438689Sborman 	}
30537226Sminshall 
30638689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
3076000Sroot 
30838689Sborman 	    switch (option) {
30938689Sborman 
31038689Sborman 	    case TELOPT_ECHO:
31138689Sborman #	    if defined(TN3270)
31238689Sborman 		/*
31338689Sborman 		 * The following is a pain in the rear-end.
31438689Sborman 		 * Various IBM servers (some versions of Wiscnet,
31538689Sborman 		 * possibly Fibronics/Spartacus, and who knows who
31638689Sborman 		 * else) will NOT allow us to send "DO SGA" too early
31738689Sborman 		 * in the setup proceedings.  On the other hand,
31838689Sborman 		 * 4.2 servers (telnetd) won't set SGA correctly.
31938689Sborman 		 * So, we are stuck.  Empirically (but, based on
32038689Sborman 		 * a VERY small sample), the IBM servers don't send
32138689Sborman 		 * out anything about ECHO, so we postpone our sending
32238689Sborman 		 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
32338689Sborman 		 * DO send).
32438689Sborman 		  */
32538689Sborman 		{
32638689Sborman 		    if (askedSGA == 0) {
32738689Sborman 			askedSGA = 1;
32838689Sborman 			if (my_want_state_is_dont(TELOPT_SGA))
32938689Sborman 			    send_do(TELOPT_SGA, 1);
33032377Sminshall 		    }
33132377Sminshall 		}
33238689Sborman 		    /* Fall through */
33338689Sborman 	    case TELOPT_EOR:
33438908Sborman #endif	    /* defined(TN3270) */
33538689Sborman 	    case TELOPT_BINARY:
33638689Sborman 	    case TELOPT_SGA:
33727110Sminshall 		settimer(modenegotiated);
33838908Sborman 		/* FALL THROUGH */
33938908Sborman 	    case TELOPT_STATUS:
34038689Sborman 		new_state_ok = 1;
3416000Sroot 		break;
3426000Sroot 
34338689Sborman 	    case TELOPT_TM:
34438689Sborman 		if (flushout)
34538689Sborman 		    flushout = 0;
34638689Sborman 		/*
34738689Sborman 		 * Special case for TM.  If we get back a WILL,
34838689Sborman 		 * pretend we got back a WONT.
34938689Sborman 		 */
35038689Sborman 		set_my_want_state_dont(option);
35138689Sborman 		set_my_state_dont(option);
35227110Sminshall 		return;			/* Never reply to TM will's/wont's */
3536000Sroot 
35438689Sborman 	    case TELOPT_LINEMODE:
35538689Sborman 	    default:
3566000Sroot 		break;
35738689Sborman 	    }
35838689Sborman 
35938689Sborman 	    if (new_state_ok) {
36038689Sborman 		set_my_want_state_do(option);
36138689Sborman 		send_do(option, 0);
36238689Sborman 		setconnmode(0);		/* possibly set new tty mode */
36338689Sborman 	    } else {
36438689Sborman 		do_dont_resp[option]++;
36538689Sborman 		send_dont(option, 0);
36638689Sborman 	    }
3676000Sroot 	}
36838689Sborman 	set_my_state_do(option);
3696000Sroot }
3706000Sroot 
37132377Sminshall void
37237226Sminshall wontoption(option)
37337226Sminshall 	int option;
3746000Sroot {
37538689Sborman 	if (do_dont_resp[option]) {
37638689Sborman 	    --do_dont_resp[option];
37738689Sborman 	    if (do_dont_resp[option] && my_state_is_dont(option))
37838689Sborman 		--do_dont_resp[option];
37938689Sborman 	}
38037226Sminshall 
38138689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
3826000Sroot 
38338689Sborman 	    switch (option) {
38438689Sborman 
38538689Sborman #ifdef	KLUDGELINEMODE
38638689Sborman 	    case TELOPT_SGA:
38738689Sborman 		if (!kludgelinemode)
38838689Sborman 		    break;
38938689Sborman 		/* FALL THROUGH */
39038689Sborman #endif
39138689Sborman 	    case TELOPT_ECHO:
39227110Sminshall 		settimer(modenegotiated);
3936000Sroot 		break;
3946000Sroot 
39538689Sborman 	    case TELOPT_TM:
39638689Sborman 		if (flushout)
39738689Sborman 		    flushout = 0;
39838689Sborman 		set_my_want_state_dont(option);
39938689Sborman 		set_my_state_dont(option);
40027110Sminshall 		return;		/* Never reply to TM will's/wont's */
40127110Sminshall 
40238689Sborman 	    default:
40338689Sborman 		break;
40438689Sborman 	    }
40538689Sborman 	    set_my_want_state_dont(option);
406*44360Sborman 	    if (my_state_is_do(option))
407*44360Sborman 		send_dont(option, 0);
40838689Sborman 	    setconnmode(0);			/* Set new tty mode */
40938689Sborman 	} else if (option == TELOPT_TM) {
41038689Sborman 	    /*
41138689Sborman 	     * Special case for TM.
41238689Sborman 	     */
41338689Sborman 	    if (flushout)
41438689Sborman 		flushout = 0;
41538689Sborman 	    set_my_want_state_dont(option);
4166000Sroot 	}
41738689Sborman 	set_my_state_dont(option);
4186000Sroot }
4196000Sroot 
42032377Sminshall static void
4216000Sroot dooption(option)
4226000Sroot 	int option;
4236000Sroot {
42438689Sborman 	int new_state_ok = 0;
4256000Sroot 
42638689Sborman 	if (will_wont_resp[option]) {
42738689Sborman 	    --will_wont_resp[option];
42838689Sborman 	    if (will_wont_resp[option] && my_state_is_will(option))
42938689Sborman 		--will_wont_resp[option];
43038689Sborman 	}
43137226Sminshall 
43238689Sborman 	if (will_wont_resp[option] == 0) {
43338689Sborman 	  if (my_want_state_is_wont(option)) {
4346000Sroot 
43538689Sborman 	    switch (option) {
43638689Sborman 
43738689Sborman 	    case TELOPT_TM:
43838689Sborman 		/*
43938689Sborman 		 * Special case for TM.  We send a WILL, but pretend
44038689Sborman 		 * we sent WONT.
44138689Sborman 		 */
44238689Sborman 		send_will(option, 0);
44338689Sborman 		set_my_want_state_wont(TELOPT_TM);
44438689Sborman 		set_my_state_wont(TELOPT_TM);
44538689Sborman 		return;
44638689Sborman 
44743319Skfall #ifdef	KERBEROS
44843319Skfall 	    case TELOPT_AUTHENTICATION:
44943319Skfall 		if (kerberized)
45043319Skfall 			new_state_ok = 1;
45143319Skfall 		break;
45243319Skfall #endif
45332377Sminshall #	if defined(TN3270)
45438689Sborman 	    case TELOPT_EOR:		/* end of record */
45538908Sborman #	endif	/* defined(TN3270) */
45638689Sborman 	    case TELOPT_BINARY:		/* binary mode */
45738689Sborman 	    case TELOPT_NAWS:		/* window size */
45838689Sborman 	    case TELOPT_TSPEED:		/* terminal speed */
45938689Sborman 	    case TELOPT_LFLOW:		/* local flow control */
46038689Sborman 	    case TELOPT_TTYPE:		/* terminal type option */
46138689Sborman 	    case TELOPT_SGA:		/* no big deal */
462*44360Sborman 	    case TELOPT_ENVIRON:	/* environment variable option */
46338689Sborman 		new_state_ok = 1;
4646000Sroot 		break;
4656000Sroot 
466*44360Sborman 	    case TELOPT_XDISPLOC:	/* X Display location */
467*44360Sborman 		if (env_getvalue("DISPLAY"))
468*44360Sborman 		    new_state_ok = 1;
469*44360Sborman 		break;
470*44360Sborman 
47138689Sborman 	    case TELOPT_LINEMODE:
47238689Sborman #ifdef	KLUDGELINEMODE
47338689Sborman 		kludgelinemode = 0;
474*44360Sborman 		send_do(TELOPT_SGA, 1);
47538689Sborman #endif
47638689Sborman 		set_my_want_state_will(TELOPT_LINEMODE);
47738689Sborman 		send_will(option, 0);
47838689Sborman 		set_my_state_will(TELOPT_LINEMODE);
47938689Sborman 		slc_init();
48038689Sborman 		return;
48138689Sborman 
48238689Sborman 	    case TELOPT_ECHO:		/* We're never going to echo... */
48338689Sborman 	    default:
4846000Sroot 		break;
48538689Sborman 	    }
48638689Sborman 
48738689Sborman 	    if (new_state_ok) {
48838689Sborman 		set_my_want_state_will(option);
48938689Sborman 		send_will(option, 0);
49038689Sborman 	    } else {
49138689Sborman 		will_wont_resp[option]++;
49238689Sborman 		send_wont(option, 0);
49338689Sborman 	    }
49438689Sborman 	  } else {
49538689Sborman 	    /*
49638689Sborman 	     * Handle options that need more things done after the
49738689Sborman 	     * other side has acknowledged the option.
49838689Sborman 	     */
49938689Sborman 	    switch (option) {
50038689Sborman 	    case TELOPT_LINEMODE:
50138689Sborman #ifdef	KLUDGELINEMODE
50238689Sborman 		kludgelinemode = 0;
503*44360Sborman 		send_do(TELOPT_SGA, 1);
50438689Sborman #endif
50538689Sborman 		set_my_state_will(option);
50638689Sborman 		slc_init();
507*44360Sborman 		send_do(TELOPT_SGA, 0);
50838689Sborman 		return;
50938689Sborman 	    }
51038689Sborman 	  }
5116000Sroot 	}
51238689Sborman 	set_my_state_will(option);
5136000Sroot }
51427676Sminshall 
51538689Sborman static void
51638689Sborman dontoption(option)
51738689Sborman 	int option;
51838689Sborman {
51938689Sborman 
52038689Sborman 	if (will_wont_resp[option]) {
52138689Sborman 	    --will_wont_resp[option];
52238689Sborman 	    if (will_wont_resp[option] && my_state_is_wont(option))
52338689Sborman 		--will_wont_resp[option];
52438689Sborman 	}
52538689Sborman 
52638689Sborman 	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
52738811Sborman 	    switch (option) {
52838811Sborman 	    case TELOPT_LINEMODE:
52938811Sborman 		linemode = 0;	/* put us back to the default state */
53038811Sborman 		break;
53138811Sborman 	    }
53238689Sborman 	    /* we always accept a DONT */
53338689Sborman 	    set_my_want_state_wont(option);
534*44360Sborman 	    if (my_state_is_will(option))
535*44360Sborman 		send_wont(option, 0);
53639529Sborman 	    setconnmode(0);			/* Set new tty mode */
53738689Sborman 	}
53838689Sborman 	set_my_state_wont(option);
53938689Sborman }
54038689Sborman 
54127676Sminshall /*
54238908Sborman  * Given a buffer returned by tgetent(), this routine will turn
54338908Sborman  * the pipe seperated list of names in the buffer into an array
54438908Sborman  * of pointers to null terminated names.  We toss out any bad,
54538908Sborman  * duplicate, or verbose names (names with spaces).
54638908Sborman  */
54738908Sborman 
54838908Sborman static char *unknown[] = { "UNKNOWN", 0 };
54938908Sborman 
55038908Sborman char **
55138908Sborman mklist(buf, name)
55238908Sborman char *buf, *name;
55338908Sborman {
55438908Sborman 	register int n;
55538908Sborman 	register char c, *cp, **argvp, *cp2, **argv;
55638908Sborman 	char *malloc();
55738908Sborman 
55838908Sborman 	if (name) {
55938908Sborman 		if (strlen(name) > 40)
56038908Sborman 			name = 0;
56138908Sborman 		else {
56238908Sborman 			unknown[0] = name;
56338908Sborman 			upcase(name);
56438908Sborman 		}
56538908Sborman 	}
56638908Sborman 	/*
56738908Sborman 	 * Count up the number of names.
56838908Sborman 	 */
56938908Sborman 	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
57038908Sborman 		if (*cp == '|')
57138908Sborman 			n++;
57238908Sborman 	}
57338908Sborman 	/*
57438908Sborman 	 * Allocate an array to put the name pointers into
57538908Sborman 	 */
57638908Sborman 	argv = (char **)malloc((n+3)*sizeof(char *));
57738908Sborman 	if (argv == 0)
57838908Sborman 		return(unknown);
57938908Sborman 
58038908Sborman 	/*
58138908Sborman 	 * Fill up the array of pointers to names.
58238908Sborman 	 */
58338908Sborman 	*argv = 0;
58438908Sborman 	argvp = argv+1;
58538908Sborman 	n = 0;
58638908Sborman 	for (cp = cp2 = buf; (c = *cp);  cp++) {
58738908Sborman 		if (c == '|' || c == ':') {
58838908Sborman 			*cp++ = '\0';
58938908Sborman 			/*
59038908Sborman 			 * Skip entries that have spaces or are over 40
59138908Sborman 			 * characters long.  If this is our environment
59238908Sborman 			 * name, then put it up front.  Otherwise, as
59338908Sborman 			 * long as this is not a duplicate name (case
59438908Sborman 			 * insensitive) add it to the list.
59538908Sborman 			 */
59638908Sborman 			if (n || (cp - cp2 > 41))
59738908Sborman 				;
59838908Sborman 			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
59938908Sborman 				*argv = cp2;
60038908Sborman 			else if (is_unique(cp2, argv+1, argvp))
60138908Sborman 				*argvp++ = cp2;
60238908Sborman 			if (c == ':')
60338908Sborman 				break;
60438908Sborman 			/*
60538908Sborman 			 * Skip multiple delimiters. Reset cp2 to
60638908Sborman 			 * the beginning of the next name. Reset n,
60738908Sborman 			 * the flag for names with spaces.
60838908Sborman 			 */
60938908Sborman 			while ((c = *cp) == '|')
61038908Sborman 				cp++;
61138908Sborman 			cp2 = cp;
61238908Sborman 			n = 0;
61338908Sborman 		}
61438908Sborman 		/*
61538908Sborman 		 * Skip entries with spaces or non-ascii values.
61638908Sborman 		 * Convert lower case letters to upper case.
61738908Sborman 		 */
61838908Sborman 		if ((c == ' ') || !isascii(c))
61938908Sborman 			n = 1;
62038908Sborman 		else if (islower(c))
62138908Sborman 			*cp = toupper(c);
62238908Sborman 	}
62338908Sborman 
62438908Sborman 	/*
62538908Sborman 	 * Check for an old V6 2 character name.  If the second
62638908Sborman 	 * name points to the beginning of the buffer, and is
62738908Sborman 	 * only 2 characters long, move it to the end of the array.
62838908Sborman 	 */
62938908Sborman 	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
63038908Sborman 		*argvp++ = buf;
63138908Sborman 		cp = *argv++;
63238908Sborman 		*argv = cp;
63338908Sborman 	}
63438908Sborman 
63538908Sborman 	/*
63638908Sborman 	 * Duplicate last name, for TTYPE option, and null
63738908Sborman 	 * terminate the array.  If we didn't find a match on
63838908Sborman 	 * our terminal name, put that name at the beginning.
63938908Sborman 	 */
64038908Sborman 	cp = *(argvp-1);
64138908Sborman 	*argvp++ = cp;
64238908Sborman 	*argvp = 0;
64338908Sborman 
64438908Sborman 	if (*argv == 0) {
64538908Sborman 		if (name)
64638908Sborman 			*argv = name;
64738908Sborman 		else
64838908Sborman 			argv++;
64938908Sborman 	}
65038908Sborman 	if (*argv)
65138908Sborman 		return(argv);
65238908Sborman 	else
65338908Sborman 		return(unknown);
65438908Sborman }
65538908Sborman 
65638908Sborman is_unique(name, as, ae)
65738908Sborman register char *name, **as, **ae;
65838908Sborman {
65938908Sborman 	register char **ap;
66038908Sborman 	register int n;
66138908Sborman 
66238908Sborman 	n = strlen(name) + 1;
66338908Sborman 	for (ap = as; ap < ae; ap++)
66438908Sborman 		if (strncasecmp(*ap, name, n) == 0)
66538908Sborman 			return(0);
66638908Sborman 	return (1);
66738908Sborman }
66838908Sborman 
66938908Sborman #ifdef	TERMCAP
67039529Sborman char termbuf[1024];
671*44360Sborman /*ARGSUSED*/
67238908Sborman setupterm(tname, fd, errp)
67338908Sborman char *tname;
67438908Sborman int fd, *errp;
67538908Sborman {
67639529Sborman 	if (tgetent(termbuf, tname) == 1) {
67739529Sborman 		termbuf[1023] = '\0';
67838908Sborman 		if (errp)
67938908Sborman 			*errp = 1;
68038908Sborman 		return(0);
68138908Sborman 	}
68238908Sborman 	if (errp)
68338908Sborman 		*errp = 0;
68438908Sborman 	return(-1);
68538908Sborman }
68639529Sborman #else
68739529Sborman #define	termbuf	ttytype
68839529Sborman extern char ttytype[];
68938908Sborman #endif
69038908Sborman 
69138908Sborman char *
69238908Sborman gettermname()
69338908Sborman {
69438908Sborman 	char *tname;
69538908Sborman 	static int first = 1;
69638908Sborman 	static char **tnamep;
69738908Sborman 	static char **next;
69838908Sborman 	int err;
69938908Sborman 
70038908Sborman 	if (first) {
70138908Sborman 		first = 0;
702*44360Sborman 		if ((tname = env_getvalue("TERM")) &&
70338908Sborman 				(setupterm(tname, 1, &err) == 0)) {
70439529Sborman 			tnamep = mklist(termbuf, tname);
70538908Sborman 		} else {
70638908Sborman 			if (tname && (strlen(tname) <= 40)) {
70738908Sborman 				unknown[0] = tname;
70838908Sborman 				upcase(tname);
70938908Sborman 			}
71038908Sborman 			tnamep = unknown;
71138908Sborman 		}
71238908Sborman 		next = tnamep;
71338908Sborman 	}
71438908Sborman 	if (*next == 0)
71538908Sborman 		next = tnamep;
71638908Sborman 	return(*next++);
71738908Sborman }
71838908Sborman /*
71927676Sminshall  * suboption()
72027676Sminshall  *
72127676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
72227676Sminshall  * side.
72327676Sminshall  *
72427676Sminshall  *	Currently we recognize:
72527676Sminshall  *
72627676Sminshall  *		Terminal type, send request.
72737219Sminshall  *		Terminal speed (send request).
72837219Sminshall  *		Local flow control (is request).
72938689Sborman  *		Linemode
73027676Sminshall  */
73127676Sminshall 
73232377Sminshall static void
73327676Sminshall suboption()
73427676Sminshall {
73538689Sborman     printsub('<', subbuffer, subend-subbuffer+2);
73627676Sminshall     switch (subbuffer[0]&0xff) {
73727676Sminshall     case TELOPT_TTYPE:
73838689Sborman 	if (my_want_state_is_wont(TELOPT_TTYPE))
73938689Sborman 	    return;
74027676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
74127676Sminshall 	    ;
74227676Sminshall 	} else {
74327676Sminshall 	    char *name;
74438908Sborman 	    char temp[50];
74527676Sminshall 	    int len;
74627676Sminshall 
74732377Sminshall #if	defined(TN3270)
74832531Sminshall 	    if (tn3270_ttype()) {
74932377Sminshall 		return;
75032377Sminshall 	    }
75132377Sminshall #endif	/* defined(TN3270) */
75238908Sborman 	    name = gettermname();
75338908Sborman 	    len = strlen(name) + 4 + 2;
75438908Sborman 	    if (len < NETROOM()) {
75538908Sborman 		sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
75638908Sborman 				TELQUAL_IS, name, IAC, SE);
75738689Sborman 		ring_supply_data(&netoring, temp, len);
75838908Sborman 		printsub('>', &temp[2], len-2);
75932377Sminshall 	    } else {
76037226Sminshall 		ExitString("No room in buffer for terminal type.\n", 1);
76132377Sminshall 		/*NOTREACHED*/
76227676Sminshall 	    }
76327676Sminshall 	}
76437219Sminshall 	break;
76537219Sminshall     case TELOPT_TSPEED:
76638689Sborman 	if (my_want_state_is_wont(TELOPT_TSPEED))
76738689Sborman 	    return;
76837219Sminshall 	if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
769*44360Sborman 	    int ospeed, ispeed;
77038689Sborman 	    char temp[50];
77137219Sminshall 	    int len;
77227676Sminshall 
77337219Sminshall 	    TerminalSpeeds(&ispeed, &ospeed);
77437219Sminshall 
77538689Sborman 	    sprintf(temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
77638689Sborman 		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
77738689Sborman 	    len = strlen(temp+4) + 4;	/* temp[3] is 0 ... */
77837219Sminshall 
77938689Sborman 	    if (len < NETROOM()) {
78038689Sborman 		ring_supply_data(&netoring, temp, len);
78138689Sborman 		printsub('>', temp+2, len - 2);
78237219Sminshall 	    }
783*44360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
78437219Sminshall 	}
78537219Sminshall 	break;
78637219Sminshall     case TELOPT_LFLOW:
78738689Sborman 	if (my_want_state_is_wont(TELOPT_LFLOW))
78838689Sborman 	    return;
78937219Sminshall 	if ((subbuffer[1]&0xff) == 1) {
79037219Sminshall 	    localflow = 1;
79137219Sminshall 	} else if ((subbuffer[1]&0xff) == 0) {
79237219Sminshall 	    localflow = 0;
79337219Sminshall 	}
79437219Sminshall 	setcommandmode();
79538689Sborman 	setconnmode(0);
79637219Sminshall 	break;
79738689Sborman 
79838689Sborman     case TELOPT_LINEMODE:
79938689Sborman 	if (my_want_state_is_wont(TELOPT_LINEMODE))
80038689Sborman 	    return;
80138689Sborman 	switch (subbuffer[1]&0xff) {
80238689Sborman 	case WILL:
80338689Sborman 	    lm_will(&subbuffer[2], subend - &subbuffer[2]);
80438689Sborman 	    break;
80538689Sborman 	case WONT:
80638689Sborman 	    lm_wont(&subbuffer[2], subend - &subbuffer[2]);
80738689Sborman 	    break;
80838689Sborman 	case DO:
80938689Sborman 	    lm_do(&subbuffer[2], subend - &subbuffer[2]);
81038689Sborman 	    break;
81138689Sborman 	case DONT:
81238689Sborman 	    lm_dont(&subbuffer[2], subend - &subbuffer[2]);
81338689Sborman 	    break;
81438689Sborman 	case LM_SLC:
81538689Sborman 	    slc(&subbuffer[2], subend - &subbuffer[2]);
81638689Sborman 	    break;
81738689Sborman 	case LM_MODE:
81838689Sborman 	    lm_mode(&subbuffer[2], subend - &subbuffer[2], 0);
81938689Sborman 	    break;
82038689Sborman 	default:
821*44360Sborman 	    break;
822*44360Sborman 	}
823*44360Sborman 	break;
824*44360Sborman 
825*44360Sborman     case TELOPT_ENVIRON:
826*44360Sborman 	switch(subbuffer[1]&0xff) {
827*44360Sborman 	case TELQUAL_IS:
828*44360Sborman 	case TELQUAL_INFO:
829*44360Sborman 	    if (my_want_state_is_dont(TELOPT_ENVIRON))
830*44360Sborman 		return;
831*44360Sborman 	    break;
832*44360Sborman 	case TELQUAL_SEND:
833*44360Sborman 	    if (my_want_state_is_wont(TELOPT_ENVIRON)) {
834*44360Sborman 		return;
835*44360Sborman 	    }
836*44360Sborman 	    break;
837*44360Sborman 	default:
838*44360Sborman 	    return;
839*44360Sborman 	}
840*44360Sborman 	env_opt(&subbuffer[1], subend - &subbuffer[1]);
841*44360Sborman 	break;
842*44360Sborman 
843*44360Sborman     case TELOPT_XDISPLOC:
844*44360Sborman 	if (my_want_state_is_wont(TELOPT_XDISPLOC))
845*44360Sborman 	    return;
846*44360Sborman 	if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
847*44360Sborman 	    char temp[50], *dp;
848*44360Sborman 	    int len;
849*44360Sborman 
850*44360Sborman 	    if ((dp = env_getvalue("DISPLAY")) == NULL) {
851*44360Sborman 		/*
852*44360Sborman 		 * Something happened, we no longer have a DISPLAY
853*44360Sborman 		 * variable.  So, turn off the option.
854*44360Sborman 		 */
855*44360Sborman 		send_wont(TELOPT_XDISPLOC, 1);
85638689Sborman 		break;
857*44360Sborman 	    }
858*44360Sborman 	    sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
859*44360Sborman 		    TELQUAL_IS, dp, IAC, SE);
860*44360Sborman 	    len = strlen(temp+4) + 4;	/* temp[3] is 0 ... */
861*44360Sborman 
862*44360Sborman 	    if (len < NETROOM()) {
863*44360Sborman 		ring_supply_data(&netoring, temp, len);
864*44360Sborman 		printsub('>', temp+2, len - 2);
865*44360Sborman 	    }
866*44360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
86738689Sborman 	}
868*44360Sborman 	break;
86943319Skfall 
87043319Skfall #ifdef	KERBEROS
87143319Skfall     case TELOPT_AUTHENTICATION:
87243319Skfall 	if ((subbuffer[1] & 0xff) == TELQUAL_SEND) {
87343319Skfall 		register char *cp = &subbuffer[2];
87443320Skfall 		char tmp[256];
87543319Skfall 		int dokrb4 = 0, unknowntypes = 0, noresponse = 1;
87643319Skfall 
87743319Skfall 		while (cp < subend) {
87843319Skfall 			switch (*cp) {
87943319Skfall 			case TELQUAL_AUTHTYPE_KERBEROS_V4:
88043319Skfall 				dokrb4 = 1;
88143319Skfall 				break;
88243319Skfall 			default:
88343319Skfall 				unknowntypes++;
88443319Skfall 			}
88543319Skfall 			cp++;
88643319Skfall 		}
88743319Skfall 
88843319Skfall 		if (noresponse && dokrb4) {
88943319Skfall 			register unsigned char *ucp = (unsigned char *)cp;
89043319Skfall 			char *krb_realm;
89143319Skfall 			char hst_inst[INST_SZ];
89243319Skfall 			KTEXT_ST authent_st;
89343319Skfall 			int space = 0;
89443319Skfall 			int retval;
89543319Skfall 			extern char *krb_realmofhost(), *krb_get_phost();
89643319Skfall 
89743319Skfall 			fprintf(stderr,
89843319Skfall 				"[Trying Kerberos V4 authentication]\n");
89943319Skfall 
90043319Skfall 			krb_realm = krb_get_phost(hostname);
90143319Skfall 			bzero(hst_inst, sizeof(hst_inst));
90243319Skfall 			if (krb_realm)
90343319Skfall 			    strncpy(hst_inst, krb_realm, sizeof(hst_inst));
90443319Skfall 			hst_inst[sizeof(hst_inst)-1] = '\0';
90543319Skfall 			if (!(krb_realm = krb_realmofhost(hst_inst))) {
90643319Skfall 			    fprintf(stderr, "no realm for %s\n", hostname);
90743319Skfall 			    goto cantsend4;
90843319Skfall 			}
90943319Skfall 			if (retval = krb_mk_req(&authent_st, "rcmd", hst_inst,
91043319Skfall 			    krb_realm, 0L)) {
91143319Skfall 				fprintf(stderr, "mk_req failed: %s\n",
91243319Skfall 				    krb_err_txt[retval]);
91343319Skfall 				goto cantsend4;
91443319Skfall 			}
91543319Skfall 			space = authent_st.length;
91643319Skfall 			for (ucp = authent_st.dat; ucp < authent_st.dat +
91743319Skfall 			     authent_st.length; ucp++) {
91843319Skfall 				if (*ucp == IAC)
91943319Skfall 					space++;
92043319Skfall 			}
92143320Skfall 			if (NETROOM() < 6 + 1 + 2 +
92243319Skfall 			    space + 2) {
92343319Skfall 				fprintf(stderr,
92443319Skfall 				   "no room to send V4 ticket/authenticator\n");
92543319Skfall cantsend4:
92643319Skfall 			    if (7 < NETROOM()) {
92743319Skfall 				printring(&netoring, "%c%c%c%c%c%c%c", IAC, SB,
92843319Skfall 				  TELOPT_AUTHENTICATION,
92943319Skfall 				  TELQUAL_IS, TELQUAL_AUTHTYPE_NONE, IAC, SE);
93043319Skfall 				sprintf(tmp, "%c%c%c%c%c", TELOPT_AUTHENTICATION,
93143319Skfall 					TELQUAL_IS, TELQUAL_AUTHTYPE_NONE, IAC, SE);
93243319Skfall 				printsub(">", tmp, 4+2-2-2);
93343319Skfall 			    } else
93443319Skfall 				exit(1);
93543319Skfall 			} else {
93643319Skfall #ifdef notdef
93743320Skfall 		    printring(&netoring, "%c%c%c%c%c%c", IAC, SB,
93843319Skfall 			      TELOPT_AUTHENTICATION,
93943319Skfall 			      TELQUAL_IS, TELQUAL_AUTHTYPE_KERBEROS,
94043320Skfall 			      TELQUAL_AUTHTYPE_KERBEROS_V4);
94143320Skfall 		    sprintf(tmp, "%c%c%c%c%c%c", TELOPT_AUTHENTICATION,
94243319Skfall 			    TELQUAL_IS, TELQUAL_AUTHTYPE_KERBEROS,
94343320Skfall 			    TELQUAL_AUTHTYPE_KERBEROS_V4, IAC, SE);
94443319Skfall #else
94543320Skfall 			    printring(&netoring, "%c%c%c%c%c", IAC, SB,
94643319Skfall 			      TELOPT_AUTHENTICATION,
94743319Skfall 			      TELQUAL_IS,
94843320Skfall 			      TELQUAL_AUTHTYPE_KERBEROS_V4);
94943320Skfall 			    sprintf(tmp, "%c%c%c%c%c", TELOPT_AUTHENTICATION,
95043319Skfall 			      TELQUAL_IS,
95143320Skfall 			      TELQUAL_AUTHTYPE_KERBEROS_V4, IAC, SE);
95243319Skfall #endif
95343320Skfall 			    printsub(">", tmp, 4+2-2-2);
95443319Skfall 			    ring_supply_bindata(&netoring,
95543319Skfall 			        (char *)authent_st.dat, authent_st.length, IAC);
95643319Skfall 			    printring(&netoring, "%c%c", IAC, SE);
95743319Skfall 			}
95843319Skfall 			noresponse = 0;
95943319Skfall 	    	}
96043319Skfall 	    	if (noresponse) {
96143319Skfall 			if (NETROOM() < 7) {
96243319Skfall 			    ExitString("not enough room to reject unhandled authtype\n", 1);
96343319Skfall 			} else {
96443319Skfall 			    fprintf(stderr,"[Sending empty auth info in response to request for %d unknown type(s):\n\t", unknowntypes);
96543319Skfall #ifdef notdef
96643319Skfall 			    cp = &subbuffer[3];
96743319Skfall #else
96843319Skfall 			    cp = &subbuffer[2];
96943319Skfall #endif
97043319Skfall 			    while (cp < subend) {
97143319Skfall 				switch (*cp) {
97243319Skfall 				case TELQUAL_AUTHTYPE_KERBEROS_V4:
97343319Skfall 			    		break;
97443319Skfall 				default:
97543319Skfall 			    		fprintf(stderr, "%d,", *cp);
97643319Skfall 			    		break;
97743319Skfall 				}
97843319Skfall 				cp++;
97943319Skfall 		    	    }
98043319Skfall 		    	    fputs("]\n", stderr);
98143319Skfall 		    	    printring(&netoring, "%c%c%c%c%c%c%c", IAC, SB,
98243319Skfall 			      TELOPT_AUTHENTICATION,
98343319Skfall 			      TELQUAL_IS, TELQUAL_AUTHTYPE_NONE, IAC, SE);
98443319Skfall 			}
98543319Skfall 	    	}
98643319Skfall 	}
98738689Sborman 	break;
98843319Skfall #endif /* KERBEROS */
989*44360Sborman 
99027676Sminshall     default:
99127676Sminshall 	break;
99227676Sminshall     }
99327676Sminshall }
99438689Sborman 
99538689Sborman static char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
99638689Sborman 
99738689Sborman lm_will(cmd, len)
99838689Sborman char *cmd;
99938689Sborman {
1000*44360Sborman     if (len < 1) {
1001*44360Sborman /*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
1002*44360Sborman 	return;
1003*44360Sborman     }
100438689Sborman     switch(cmd[0]) {
100538689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
100638689Sborman     default:
100738689Sborman 	str_lm[3] = DONT;
100838689Sborman 	str_lm[4] = cmd[0];
100938689Sborman 	if (NETROOM() > sizeof(str_lm)) {
101038689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
101138689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
101238689Sborman 	}
101338689Sborman /*@*/	else printf("lm_will: not enough room in buffer\n");
101438689Sborman 	break;
101538689Sborman     }
101638689Sborman }
101738689Sborman 
101838689Sborman lm_wont(cmd, len)
101938689Sborman char *cmd;
102038689Sborman {
1021*44360Sborman     if (len < 1) {
1022*44360Sborman /*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
1023*44360Sborman 	return;
1024*44360Sborman     }
102538689Sborman     switch(cmd[0]) {
102638689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
102738689Sborman     default:
102838689Sborman 	/* We are always DONT, so don't respond */
102938689Sborman 	return;
103038689Sborman     }
103138689Sborman }
103238689Sborman 
103338689Sborman lm_do(cmd, len)
103438689Sborman char *cmd;
103538689Sborman {
1036*44360Sborman     if (len < 1) {
1037*44360Sborman /*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
1038*44360Sborman 	return;
1039*44360Sborman     }
104038689Sborman     switch(cmd[0]) {
104138689Sborman     case LM_FORWARDMASK:
104238689Sborman     default:
104338689Sborman 	str_lm[3] = WONT;
104438689Sborman 	str_lm[4] = cmd[0];
104538689Sborman 	if (NETROOM() > sizeof(str_lm)) {
104638689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
104738689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
104838689Sborman 	}
104938689Sborman /*@*/	else printf("lm_do: not enough room in buffer\n");
105038689Sborman 	break;
105138689Sborman     }
105238689Sborman }
105338689Sborman 
105438689Sborman lm_dont(cmd, len)
105538689Sborman char *cmd;
105638689Sborman {
1057*44360Sborman     if (len < 1) {
1058*44360Sborman /*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
1059*44360Sborman 	return;
1060*44360Sborman     }
106138689Sborman     switch(cmd[0]) {
106238689Sborman     case LM_FORWARDMASK:
106338689Sborman     default:
106438689Sborman 	/* we are always WONT, so don't respond */
106538689Sborman 	break;
106638689Sborman     }
106738689Sborman }
106838689Sborman 
106938689Sborman static char str_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE };
107038689Sborman 
107138689Sborman lm_mode(cmd, len, init)
107238689Sborman char *cmd;
107338689Sborman int len, init;
107438689Sborman {
107538689Sborman 	if (len != 1)
107638689Sborman 		return;
1077*44360Sborman 	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
107838689Sborman 		return;
107938689Sborman 	if (*cmd&MODE_ACK)
108038689Sborman 		return;
1081*44360Sborman 	linemode = *cmd&(MODE_MASK&~MODE_ACK);
108238689Sborman 	str_lm_mode[4] = linemode;
108338689Sborman 	if (!init)
108438689Sborman 	    str_lm_mode[4] |= MODE_ACK;
108538689Sborman 	if (NETROOM() > sizeof(str_lm_mode)) {
108638689Sborman 	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
108738689Sborman 	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
108838689Sborman 	}
108938689Sborman /*@*/	else printf("lm_mode: not enough room in buffer\n");
109038689Sborman 	setconnmode(0);	/* set changed mode */
109138689Sborman }
109238689Sborman 
109332377Sminshall 
109427088Sminshall 
109538689Sborman /*
109638689Sborman  * slc()
109738689Sborman  * Handle special character suboption of LINEMODE.
109838689Sborman  */
109938689Sborman 
110038689Sborman struct spc {
110140245Sborman 	cc_t val;
110240245Sborman 	cc_t *valp;
110338689Sborman 	char flags;	/* Current flags & level */
110438689Sborman 	char mylevel;	/* Maximum level & flags */
110538689Sborman } spc_data[NSLC+1];
110638689Sborman 
110738689Sborman #define SLC_IMPORT	0
110838689Sborman #define	SLC_EXPORT	1
110938689Sborman #define SLC_RVALUE	2
111038689Sborman static int slc_mode = SLC_EXPORT;
111138689Sborman 
111238689Sborman slc_init()
111338689Sborman {
111438689Sborman 	register struct spc *spcp;
111540245Sborman 	extern cc_t *tcval();
111638689Sborman 
111738689Sborman 	localchars = 1;
111838689Sborman 	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
111938689Sborman 		spcp->val = 0;
112038689Sborman 		spcp->valp = 0;
112138689Sborman 		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
112238689Sborman 	}
112338689Sborman 
112438689Sborman #define	initfunc(func, flags) { \
112538689Sborman 					spcp = &spc_data[func]; \
112638689Sborman 					if (spcp->valp = tcval(func)) { \
112738689Sborman 					    spcp->val = *spcp->valp; \
112838689Sborman 					    spcp->mylevel = SLC_VARIABLE|flags; \
112938689Sborman 					} else { \
113038689Sborman 					    spcp->val = 0; \
113138689Sborman 					    spcp->mylevel = SLC_DEFAULT; \
113238689Sborman 					} \
113338689Sborman 				    }
113438689Sborman 
113538689Sborman 	initfunc(SLC_SYNCH, 0);
113638689Sborman 	/* No BRK */
113738689Sborman 	initfunc(SLC_AO, 0);
113838689Sborman 	initfunc(SLC_AYT, 0);
113938689Sborman 	/* No EOR */
114038689Sborman 	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
114138689Sborman 	initfunc(SLC_EOF, 0);
114239529Sborman #ifndef	SYSV_TERMIO
114338689Sborman 	initfunc(SLC_SUSP, SLC_FLUSHIN);
114438689Sborman #endif
114538689Sborman 	initfunc(SLC_EC, 0);
114638689Sborman 	initfunc(SLC_EL, 0);
114739529Sborman #ifndef	SYSV_TERMIO
114838689Sborman 	initfunc(SLC_EW, 0);
114938689Sborman 	initfunc(SLC_RP, 0);
115038689Sborman 	initfunc(SLC_LNEXT, 0);
115138689Sborman #endif
115238689Sborman 	initfunc(SLC_XON, 0);
115338689Sborman 	initfunc(SLC_XOFF, 0);
115439529Sborman #ifdef	SYSV_TERMIO
115538689Sborman 	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
115638689Sborman 	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
115738689Sborman #endif
1158*44360Sborman 	initfunc(SLC_FORW1, 0);
1159*44360Sborman #ifdef	USE_TERMIO
1160*44360Sborman 	initfunc(SLC_FORW2, 0);
116138689Sborman 	/* No FORW2 */
1162*44360Sborman #endif
116338689Sborman 
116438689Sborman 	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
116538689Sborman #undef	initfunc
116638689Sborman 
116738689Sborman 	if (slc_mode == SLC_EXPORT)
116838689Sborman 		slc_export();
116938689Sborman 	else
117038689Sborman 		slc_import(1);
117138689Sborman 
117238689Sborman }
117338689Sborman 
117438689Sborman slcstate()
117538689Sborman {
117638689Sborman     printf("Special characters are %s values\n",
117738689Sborman 		slc_mode == SLC_IMPORT ? "remote default" :
117838689Sborman 		slc_mode == SLC_EXPORT ? "local" :
117938689Sborman 					 "remote");
118038689Sborman }
118138689Sborman 
118238689Sborman slc_mode_export()
118338689Sborman {
118438689Sborman     slc_mode = SLC_EXPORT;
118538689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
118638689Sborman 	slc_export();
118738689Sborman }
118838689Sborman 
118938689Sborman slc_mode_import(def)
119038689Sborman {
119138689Sborman     slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
119238689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
119338689Sborman 	slc_import(def);
119438689Sborman }
119538689Sborman 
119638689Sborman char slc_import_val[] = {
119738689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
119838689Sborman };
119938689Sborman char slc_import_def[] = {
120038689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
120138689Sborman };
120238689Sborman 
120338689Sborman slc_import(def)
120438689Sborman int def;
120538689Sborman {
120638689Sborman     if (NETROOM() > sizeof(slc_import_val)) {
120738689Sborman 	if (def) {
120838689Sborman 	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
120938689Sborman 	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
121038689Sborman 	} else {
121138689Sborman 	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
121238689Sborman 	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
121338689Sborman 	}
121438689Sborman     }
121538689Sborman /*@*/ else printf("slc_import: not enough room\n");
121638689Sborman }
121738689Sborman 
121838689Sborman slc_export()
121938689Sborman {
122038689Sborman     register struct spc *spcp;
122138689Sborman 
122238689Sborman     TerminalDefaultChars();
122338689Sborman 
122438689Sborman     slc_start_reply();
122538689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
122638689Sborman 	if (spcp->mylevel != SLC_NOSUPPORT) {
122738689Sborman 	    spcp->flags = spcp->mylevel;
122838689Sborman 	    if (spcp->valp)
122938689Sborman 		spcp->val = *spcp->valp;
123038689Sborman 	    slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
123138689Sborman 	}
123238689Sborman     }
123338689Sborman     slc_end_reply();
123438689Sborman     if (slc_update())
123538689Sborman 	setconnmode(1);	/* set the  new character values */
123638689Sborman }
123738689Sborman 
123838689Sborman slc(cp, len)
123938689Sborman register char *cp;
124038689Sborman int len;
124138689Sborman {
124238689Sborman 	register struct spc *spcp;
124338689Sborman 	register int func,level;
124438689Sborman 
124538689Sborman 	slc_start_reply();
124638689Sborman 
124738689Sborman 	for (; len >= 3; len -=3, cp +=3) {
124838689Sborman 
124938689Sborman 		func = cp[SLC_FUNC];
125038689Sborman 
125138689Sborman 		if (func == 0) {
125238689Sborman 			/*
125338689Sborman 			 * Client side: always ignore 0 function.
125438689Sborman 			 */
125538689Sborman 			continue;
125638689Sborman 		}
125738689Sborman 		if (func > NSLC) {
125838689Sborman 			if (cp[SLC_FLAGS] & SLC_LEVELBITS != SLC_NOSUPPORT)
125938689Sborman 				slc_add_reply(func, SLC_NOSUPPORT, 0);
126038689Sborman 			continue;
126138689Sborman 		}
126238689Sborman 
126338689Sborman 		spcp = &spc_data[func];
126438689Sborman 
126538689Sborman 		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
126638689Sborman 
126740245Sborman 		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
126838689Sborman 		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
126938689Sborman 			continue;
127038689Sborman 		}
127138689Sborman 
127238689Sborman 		if (level == (SLC_DEFAULT|SLC_ACK)) {
127338689Sborman 			/*
127438689Sborman 			 * This is an error condition, the SLC_ACK
127538689Sborman 			 * bit should never be set for the SLC_DEFAULT
127638689Sborman 			 * level.  Our best guess to recover is to
127738689Sborman 			 * ignore the SLC_ACK bit.
127838689Sborman 			 */
127938689Sborman 			cp[SLC_FLAGS] &= ~SLC_ACK;
128038689Sborman 		}
128138689Sborman 
128238689Sborman 		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
128340245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
128438689Sborman 			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
128538689Sborman 			continue;
128638689Sborman 		}
128738689Sborman 
128838689Sborman 		level &= ~SLC_ACK;
128938689Sborman 
129038689Sborman 		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
129138689Sborman 			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
129240245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
129338689Sborman 		}
129438689Sborman 		if (level == SLC_DEFAULT) {
129538689Sborman 			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
129638689Sborman 				spcp->flags = spcp->mylevel;
129738689Sborman 			else
129838689Sborman 				spcp->flags = SLC_NOSUPPORT;
129938689Sborman 		}
130038689Sborman 		slc_add_reply(func, spcp->flags, spcp->val);
130138689Sborman 	}
130238689Sborman 	slc_end_reply();
130338689Sborman 	if (slc_update())
130438689Sborman 		setconnmode(1);	/* set the  new character values */
130538689Sborman }
130638689Sborman 
130738689Sborman slc_check()
130838689Sborman {
130938689Sborman     register struct spc *spcp;
131038689Sborman 
131138689Sborman     slc_start_reply();
131238689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
131338689Sborman 	if (spcp->valp && spcp->val != *spcp->valp) {
131438689Sborman 	    spcp->val = *spcp->valp;
131538689Sborman 	    slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
131638689Sborman 	}
131738689Sborman     }
131838689Sborman     slc_end_reply();
131938689Sborman     setconnmode(1);
132038689Sborman }
132138689Sborman 
132238689Sborman 
132338689Sborman unsigned char slc_reply[128];
132438689Sborman unsigned char *slc_replyp;
132538689Sborman slc_start_reply()
132638689Sborman {
132738689Sborman 	slc_replyp = slc_reply;
132838689Sborman 	*slc_replyp++ = IAC;
132938689Sborman 	*slc_replyp++ = SB;
133038689Sborman 	*slc_replyp++ = TELOPT_LINEMODE;
133138689Sborman 	*slc_replyp++ = LM_SLC;
133238689Sborman }
133338689Sborman 
133438689Sborman slc_add_reply(func, flags, value)
133538689Sborman char func;
133638689Sborman char flags;
133740245Sborman cc_t value;
133838689Sborman {
133938689Sborman 	if ((*slc_replyp++ = func) == IAC)
134038689Sborman 		*slc_replyp++ = IAC;
134138689Sborman 	if ((*slc_replyp++ = flags) == IAC)
134238689Sborman 		*slc_replyp++ = IAC;
134340245Sborman 	if ((*slc_replyp++ = (unsigned char)value) == IAC)
134438689Sborman 		*slc_replyp++ = IAC;
134538689Sborman }
134638689Sborman 
134738689Sborman slc_end_reply()
134838689Sborman {
134938689Sborman     register int len;
135038689Sborman 
135138689Sborman     *slc_replyp++ = IAC;
135238689Sborman     *slc_replyp++ = SE;
135338689Sborman     len = slc_replyp - slc_reply;
135438689Sborman     if (len <= 6)
135538689Sborman 	return;
135638689Sborman     if (NETROOM() > len) {
135738689Sborman 	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
135838689Sborman 	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
135938689Sborman     }
136038689Sborman /*@*/else printf("slc_end_reply: not enough room\n");
136138689Sborman }
136238689Sborman 
136338689Sborman slc_update()
136438689Sborman {
136538689Sborman 	register struct spc *spcp;
136638689Sborman 	int need_update = 0;
136738689Sborman 
136838689Sborman 	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
136938689Sborman 		if (!(spcp->flags&SLC_ACK))
137038689Sborman 			continue;
137138689Sborman 		spcp->flags &= ~SLC_ACK;
137238689Sborman 		if (spcp->valp && (*spcp->valp != spcp->val)) {
137338689Sborman 			*spcp->valp = spcp->val;
137438689Sborman 			need_update = 1;
137538689Sborman 		}
137638689Sborman 	}
137738689Sborman 	return(need_update);
137838689Sborman }
137938689Sborman 
1380*44360Sborman env_opt(buf, len)
1381*44360Sborman register char *buf;
1382*44360Sborman register int len;
1383*44360Sborman {
1384*44360Sborman 	register char *ep = 0, *epc = 0;
1385*44360Sborman 	register int i;
1386*44360Sborman 
1387*44360Sborman 	switch(buf[0]) {
1388*44360Sborman 	case TELQUAL_SEND:
1389*44360Sborman 		env_opt_start();
1390*44360Sborman 		if (len == 1) {
1391*44360Sborman 			env_opt_add(NULL);
1392*44360Sborman 		} else for (i = 1; i < len; i++) {
1393*44360Sborman 			switch (buf[i]) {
1394*44360Sborman 			case ENV_VALUE:
1395*44360Sborman 				if (ep) {
1396*44360Sborman 					*epc = 0;
1397*44360Sborman 					env_opt_add(ep);
1398*44360Sborman 				}
1399*44360Sborman 				ep = epc = &buf[i+1];
1400*44360Sborman 				break;
1401*44360Sborman 			case ENV_ESC:
1402*44360Sborman 				i++;
1403*44360Sborman 				/*FALL THROUGH*/
1404*44360Sborman 			default:
1405*44360Sborman 				if (epc)
1406*44360Sborman 					*epc++ = buf[i];
1407*44360Sborman 				break;
1408*44360Sborman 			}
1409*44360Sborman 			if (ep) {
1410*44360Sborman 				*epc = 0;
1411*44360Sborman 				env_opt_add(ep);
1412*44360Sborman 			}
1413*44360Sborman 		}
1414*44360Sborman 		env_opt_end(1);
1415*44360Sborman 		break;
1416*44360Sborman 
1417*44360Sborman 	case TELQUAL_IS:
1418*44360Sborman 	case TELQUAL_INFO:
1419*44360Sborman 		/* Ignore for now.  We shouldn't get it anyway. */
1420*44360Sborman 		break;
1421*44360Sborman 
1422*44360Sborman 	default:
1423*44360Sborman 		break;
1424*44360Sborman 	}
1425*44360Sborman }
1426*44360Sborman 
1427*44360Sborman #define	OPT_REPLY_SIZE	256
1428*44360Sborman unsigned char *opt_reply;
1429*44360Sborman unsigned char *opt_replyp;
1430*44360Sborman unsigned char *opt_replyend;
1431*44360Sborman 
1432*44360Sborman env_opt_start()
1433*44360Sborman {
1434*44360Sborman 	extern char *realloc();
1435*44360Sborman 	extern char *malloc();
1436*44360Sborman 
1437*44360Sborman 	if (opt_reply)
1438*44360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
1439*44360Sborman 	else
1440*44360Sborman 		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
1441*44360Sborman 	if (opt_reply == NULL) {
1442*44360Sborman /*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
1443*44360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
1444*44360Sborman 		return;
1445*44360Sborman 	}
1446*44360Sborman 	opt_replyp = opt_reply;
1447*44360Sborman 	opt_replyend = opt_reply + OPT_REPLY_SIZE;
1448*44360Sborman 	*opt_replyp++ = IAC;
1449*44360Sborman 	*opt_replyp++ = SB;
1450*44360Sborman 	*opt_replyp++ = TELOPT_ENVIRON;
1451*44360Sborman 	*opt_replyp++ = TELQUAL_IS;
1452*44360Sborman }
1453*44360Sborman 
1454*44360Sborman env_opt_start_info()
1455*44360Sborman {
1456*44360Sborman 	env_opt_start();
1457*44360Sborman 	if (opt_replyp)
1458*44360Sborman 	    opt_replyp[-1] = TELQUAL_INFO;
1459*44360Sborman }
1460*44360Sborman 
1461*44360Sborman env_opt_add(ep)
1462*44360Sborman register char *ep;
1463*44360Sborman {
1464*44360Sborman 	register char *vp, c;
1465*44360Sborman 	extern char *realloc();
1466*44360Sborman 	extern char *env_default();
1467*44360Sborman 
1468*44360Sborman 	if (opt_reply == NULL)		/*XXX*/
1469*44360Sborman 		return;			/*XXX*/
1470*44360Sborman 
1471*44360Sborman 	if (ep == NULL || *ep == '\0') {
1472*44360Sborman 		env_default(1);
1473*44360Sborman 		while (ep = env_default(0))
1474*44360Sborman 			env_opt_add(ep);
1475*44360Sborman 		return;
1476*44360Sborman 	}
1477*44360Sborman 	vp = env_getvalue(ep);
1478*44360Sborman 	if (opt_replyp + (vp?strlen(vp):0) + strlen(ep) + 6 > opt_replyend) {
1479*44360Sborman 		register int len;
1480*44360Sborman 		opt_replyend += OPT_REPLY_SIZE;
1481*44360Sborman 		len = opt_replyend - opt_reply;
1482*44360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, len);
1483*44360Sborman 		if (opt_reply == NULL) {
1484*44360Sborman /*@*/			printf("env_opt_add: realloc() failed!!!\n");
1485*44360Sborman 			opt_reply = opt_replyp = opt_replyend = NULL;
1486*44360Sborman 			return;
1487*44360Sborman 		}
1488*44360Sborman 		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
1489*44360Sborman 		opt_replyend = opt_reply + len;
1490*44360Sborman 	}
1491*44360Sborman 	*opt_replyp++ = ENV_VAR;
1492*44360Sborman 	for (;;) {
1493*44360Sborman 		while (c = *ep++) {
1494*44360Sborman 			switch(c) {
1495*44360Sborman 			case IAC:
1496*44360Sborman 				*opt_replyp++ = IAC;
1497*44360Sborman 				break;
1498*44360Sborman 			case ENV_VALUE:
1499*44360Sborman 			case ENV_VAR:
1500*44360Sborman 			case ENV_ESC:
1501*44360Sborman 				*opt_replyp++ = ENV_ESC;
1502*44360Sborman 				break;
1503*44360Sborman 			}
1504*44360Sborman 			*opt_replyp++ = c;
1505*44360Sborman 		}
1506*44360Sborman 		if (ep = vp) {
1507*44360Sborman 			*opt_replyp++ = ENV_VALUE;
1508*44360Sborman 			vp = NULL;
1509*44360Sborman 		} else
1510*44360Sborman 			break;
1511*44360Sborman 	}
1512*44360Sborman }
1513*44360Sborman 
1514*44360Sborman env_opt_end(emptyok)
1515*44360Sborman register int emptyok;
1516*44360Sborman {
1517*44360Sborman 	register int len;
1518*44360Sborman 
1519*44360Sborman 	len = opt_replyp - opt_reply + 2;
1520*44360Sborman 	if (emptyok || len > 6) {
1521*44360Sborman 		*opt_replyp++ = IAC;
1522*44360Sborman 		*opt_replyp++ = SE;
1523*44360Sborman 		if (NETROOM() > len) {
1524*44360Sborman 			ring_supply_data(&netoring, opt_reply, len);
1525*44360Sborman 			printsub('>', &opt_reply[2], len - 2);
1526*44360Sborman 		}
1527*44360Sborman /*@*/		else printf("slc_end_reply: not enough room\n");
1528*44360Sborman 	}
1529*44360Sborman 	if (opt_reply) {
1530*44360Sborman 		free(opt_reply);
1531*44360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
1532*44360Sborman 	}
1533*44360Sborman }
1534*44360Sborman 
153538689Sborman 
153638689Sborman 
153733804Sminshall int
153832377Sminshall telrcv()
153927110Sminshall {
154032377Sminshall     register int c;
154132385Sminshall     register int scc;
154232385Sminshall     register char *sbp;
154332385Sminshall     int count;
154432385Sminshall     int returnValue = 0;
154527088Sminshall 
154632385Sminshall     scc = 0;
154732385Sminshall     count = 0;
154832385Sminshall     while (TTYROOM() > 2) {
154932385Sminshall 	if (scc == 0) {
155032385Sminshall 	    if (count) {
155132528Sminshall 		ring_consumed(&netiring, count);
155232385Sminshall 		returnValue = 1;
155332385Sminshall 		count = 0;
155432385Sminshall 	    }
155532528Sminshall 	    sbp = netiring.consume;
155632528Sminshall 	    scc = ring_full_consecutive(&netiring);
155732385Sminshall 	    if (scc == 0) {
155832385Sminshall 		/* No more data coming in */
155932385Sminshall 		break;
156032385Sminshall 	    }
156132385Sminshall 	}
156232385Sminshall 
156332385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
156432385Sminshall 
156532377Sminshall 	switch (telrcv_state) {
156627110Sminshall 
156732377Sminshall 	case TS_CR:
156832377Sminshall 	    telrcv_state = TS_DATA;
156935518Sminshall 	    if (c == '\0') {
157035518Sminshall 		break;	/* Ignore \0 after CR */
157139529Sborman 	    }
157239529Sborman 	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
157335518Sminshall 		TTYADD(c);
157435518Sminshall 		break;
157532377Sminshall 	    }
157635518Sminshall 	    /* Else, fall through */
157727088Sminshall 
157832377Sminshall 	case TS_DATA:
157932377Sminshall 	    if (c == IAC) {
158032377Sminshall 		telrcv_state = TS_IAC;
158133804Sminshall 		break;
158232377Sminshall 	    }
158332377Sminshall #	    if defined(TN3270)
158432377Sminshall 	    if (In3270) {
158532377Sminshall 		*Ifrontp++ = c;
158632385Sminshall 		while (scc > 0) {
158732385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
158832377Sminshall 		    if (c == IAC) {
158932377Sminshall 			telrcv_state = TS_IAC;
159034304Sminshall 			break;
159132377Sminshall 		    }
159232377Sminshall 		    *Ifrontp++ = c;
159332377Sminshall 		}
159432377Sminshall 	    } else
159532377Sminshall #	    endif /* defined(TN3270) */
159635518Sminshall 		    /*
159735518Sminshall 		     * The 'crmod' hack (see following) is needed
159835518Sminshall 		     * since we can't * set CRMOD on output only.
159935518Sminshall 		     * Machines like MULTICS like to send \r without
160035518Sminshall 		     * \n; since we must turn off CRMOD to get proper
160135518Sminshall 		     * input, the mapping is done here (sigh).
160235518Sminshall 		     */
160338689Sborman 	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
160435518Sminshall 		if (scc > 0) {
160535518Sminshall 		    c = *sbp&0xff;
160635518Sminshall 		    if (c == 0) {
160735518Sminshall 			sbp++, scc--; count++;
160835518Sminshall 			/* a "true" CR */
160932377Sminshall 			TTYADD('\r');
161038689Sborman 		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
161135518Sminshall 					(c == '\n')) {
161235518Sminshall 			sbp++, scc--; count++;
161332377Sminshall 			TTYADD('\n');
161435518Sminshall 		    } else {
161535518Sminshall 			TTYADD('\r');
161635518Sminshall 			if (crmod) {
161735518Sminshall 				TTYADD('\n');
161832377Sminshall 			}
161932377Sminshall 		    }
162035518Sminshall 		} else {
162135518Sminshall 		    telrcv_state = TS_CR;
162235518Sminshall 		    TTYADD('\r');
162335518Sminshall 		    if (crmod) {
162435518Sminshall 			    TTYADD('\n');
162535518Sminshall 		    }
162632377Sminshall 		}
162732377Sminshall 	    } else {
162832377Sminshall 		TTYADD(c);
162932377Sminshall 	    }
163032377Sminshall 	    continue;
163127088Sminshall 
163232377Sminshall 	case TS_IAC:
163338689Sborman process_iac:
163432377Sminshall 	    switch (c) {
163532377Sminshall 
163632377Sminshall 	    case WILL:
163732377Sminshall 		telrcv_state = TS_WILL;
163832377Sminshall 		continue;
163927261Sminshall 
164032377Sminshall 	    case WONT:
164132377Sminshall 		telrcv_state = TS_WONT;
164232377Sminshall 		continue;
164327261Sminshall 
164432377Sminshall 	    case DO:
164532377Sminshall 		telrcv_state = TS_DO;
164632377Sminshall 		continue;
164727261Sminshall 
164832377Sminshall 	    case DONT:
164932377Sminshall 		telrcv_state = TS_DONT;
165032377Sminshall 		continue;
165127261Sminshall 
165232377Sminshall 	    case DM:
165332377Sminshall 		    /*
165432377Sminshall 		     * We may have missed an urgent notification,
165532377Sminshall 		     * so make sure we flush whatever is in the
165632377Sminshall 		     * buffer currently.
165732377Sminshall 		     */
165832377Sminshall 		SYNCHing = 1;
1659*44360Sborman 		(void) ttyflush(1);
166032554Sminshall 		SYNCHing = stilloob();
166132377Sminshall 		settimer(gotDM);
166232377Sminshall 		break;
166327088Sminshall 
166432377Sminshall 	    case SB:
166532377Sminshall 		SB_CLEAR();
166632377Sminshall 		telrcv_state = TS_SB;
166738689Sborman 		printoption("RCVD", "IAC", SB);
166832377Sminshall 		continue;
166927261Sminshall 
167032377Sminshall #	    if defined(TN3270)
167132377Sminshall 	    case EOR:
167232377Sminshall 		if (In3270) {
167332377Sminshall 		    if (Ibackp == Ifrontp) {
167432377Sminshall 			Ibackp = Ifrontp = Ibuf;
167532377Sminshall 			ISend = 0;	/* should have been! */
167632377Sminshall 		    } else {
1677*44360Sborman 			Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
167832377Sminshall 			ISend = 1;
167927088Sminshall 		    }
168027088Sminshall 		}
168127088Sminshall 		break;
168232377Sminshall #	    endif /* defined(TN3270) */
168332377Sminshall 
168432377Sminshall 	    case IAC:
168532377Sminshall #	    if !defined(TN3270)
168632377Sminshall 		TTYADD(IAC);
168732377Sminshall #	    else /* !defined(TN3270) */
168832377Sminshall 		if (In3270) {
168932377Sminshall 		    *Ifrontp++ = IAC;
169032377Sminshall 		} else {
169132377Sminshall 		    TTYADD(IAC);
169232377Sminshall 		}
169332377Sminshall #	    endif /* !defined(TN3270) */
169427088Sminshall 		break;
169532377Sminshall 
1696*44360Sborman 	    case NOP:
1697*44360Sborman 	    case GA:
169827088Sminshall 	    default:
1699*44360Sborman 		printoption("RCVD", "IAC", c);
170027088Sminshall 		break;
170127088Sminshall 	    }
170232377Sminshall 	    telrcv_state = TS_DATA;
170332377Sminshall 	    continue;
170427088Sminshall 
170532377Sminshall 	case TS_WILL:
170637226Sminshall 	    printoption("RCVD", "will", c);
170738689Sborman 	    willoption(c);
170832377Sminshall 	    SetIn3270();
170932377Sminshall 	    telrcv_state = TS_DATA;
171032377Sminshall 	    continue;
171127110Sminshall 
171232377Sminshall 	case TS_WONT:
171337226Sminshall 	    printoption("RCVD", "wont", c);
171438689Sborman 	    wontoption(c);
171532377Sminshall 	    SetIn3270();
171632377Sminshall 	    telrcv_state = TS_DATA;
171732377Sminshall 	    continue;
171827088Sminshall 
171932377Sminshall 	case TS_DO:
172037226Sminshall 	    printoption("RCVD", "do", c);
172137226Sminshall 	    dooption(c);
172232377Sminshall 	    SetIn3270();
172337219Sminshall 	    if (c == TELOPT_NAWS) {
172437219Sminshall 		sendnaws();
172537219Sminshall 	    } else if (c == TELOPT_LFLOW) {
172637219Sminshall 		localflow = 1;
172737219Sminshall 		setcommandmode();
172838689Sborman 		setconnmode(0);
172937219Sminshall 	    }
173032377Sminshall 	    telrcv_state = TS_DATA;
173132377Sminshall 	    continue;
173227088Sminshall 
173332377Sminshall 	case TS_DONT:
173437226Sminshall 	    printoption("RCVD", "dont", c);
173538689Sborman 	    dontoption(c);
173637226Sminshall 	    flushline = 1;
173738689Sborman 	    setconnmode(0);	/* set new tty mode (maybe) */
173832377Sminshall 	    SetIn3270();
173932377Sminshall 	    telrcv_state = TS_DATA;
174032377Sminshall 	    continue;
174127088Sminshall 
174232377Sminshall 	case TS_SB:
174332377Sminshall 	    if (c == IAC) {
174432377Sminshall 		telrcv_state = TS_SE;
174532377Sminshall 	    } else {
174632377Sminshall 		SB_ACCUM(c);
174732377Sminshall 	    }
174832377Sminshall 	    continue;
174927088Sminshall 
175032377Sminshall 	case TS_SE:
175132377Sminshall 	    if (c != SE) {
175232377Sminshall 		if (c != IAC) {
175338689Sborman 		    /*
175438689Sborman 		     * This is an error.  We only expect to get
175538689Sborman 		     * "IAC IAC" or "IAC SE".  Several things may
175638689Sborman 		     * have happend.  An IAC was not doubled, the
175738689Sborman 		     * IAC SE was left off, or another option got
175838689Sborman 		     * inserted into the suboption are all possibilities.
175938689Sborman 		     * If we assume that the IAC was not doubled,
176038689Sborman 		     * and really the IAC SE was left off, we could
176138689Sborman 		     * get into an infinate loop here.  So, instead,
176238689Sborman 		     * we terminate the suboption, and process the
176338689Sborman 		     * partial suboption if we can.
176438689Sborman 		     */
176538689Sborman 		    SB_TERM();
176632377Sminshall 		    SB_ACCUM(IAC);
176738689Sborman 		    SB_ACCUM(c);
176838689Sborman 		    printoption("In SUBOPTION processing, RCVD", "IAC", c);
176938689Sborman 		    suboption();	/* handle sub-option */
177038689Sborman 		    SetIn3270();
177138689Sborman 		    telrcv_state = TS_IAC;
177238689Sborman 		    goto process_iac;
177332377Sminshall 		}
177432377Sminshall 		SB_ACCUM(c);
177532377Sminshall 		telrcv_state = TS_SB;
177632377Sminshall 	    } else {
177732377Sminshall 		SB_TERM();
177838689Sborman 		SB_ACCUM(IAC);
177938689Sborman 		SB_ACCUM(SE);
178032377Sminshall 		suboption();	/* handle sub-option */
178132377Sminshall 		SetIn3270();
178232377Sminshall 		telrcv_state = TS_DATA;
178332377Sminshall 	    }
178427088Sminshall 	}
178527088Sminshall     }
178632667Sminshall     if (count)
178732667Sminshall 	ring_consumed(&netiring, count);
178832385Sminshall     return returnValue||count;
178927088Sminshall }
179032385Sminshall 
179132385Sminshall static int
179232554Sminshall telsnd()
179332385Sminshall {
179432385Sminshall     int tcc;
179532385Sminshall     int count;
179632385Sminshall     int returnValue = 0;
179732385Sminshall     char *tbp;
179832385Sminshall 
179932385Sminshall     tcc = 0;
180032385Sminshall     count = 0;
180132385Sminshall     while (NETROOM() > 2) {
180232385Sminshall 	register int sc;
180332385Sminshall 	register int c;
180432385Sminshall 
180532385Sminshall 	if (tcc == 0) {
180632385Sminshall 	    if (count) {
180732528Sminshall 		ring_consumed(&ttyiring, count);
180832385Sminshall 		returnValue = 1;
180932385Sminshall 		count = 0;
181032385Sminshall 	    }
181132528Sminshall 	    tbp = ttyiring.consume;
181232528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
181332385Sminshall 	    if (tcc == 0) {
181432385Sminshall 		break;
181532385Sminshall 	    }
181632385Sminshall 	}
181732385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
181832385Sminshall 	if (sc == escape) {
181938689Sborman 	    /*
182038689Sborman 	     * Double escape is a pass through of a single escape character.
182138689Sborman 	     */
182238689Sborman 	    if (tcc && strip(*tbp) == escape) {
182338689Sborman 		tbp++;
182438689Sborman 		tcc--;
182538689Sborman 		count++;
182638689Sborman 	    } else {
182738689Sborman 		command(0, tbp, tcc);
182838689Sborman 		count += tcc;
182938689Sborman 		tcc = 0;
183038689Sborman 		flushline = 1;
183138689Sborman 		break;
183238689Sborman 	    }
183338689Sborman 	}
183438689Sborman #ifdef	KLUDGELINEMODE
183538689Sborman 	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
183632385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
183732385Sminshall 		tcc--; tbp++; count++;
183832385Sminshall 	    } else {
183932385Sminshall 		dontlecho = !dontlecho;
184032385Sminshall 		settimer(echotoggle);
184138689Sborman 		setconnmode(0);
184232385Sminshall 		flushline = 1;
184332385Sminshall 		break;
184432385Sminshall 	    }
184532385Sminshall 	}
184638689Sborman #endif
184738689Sborman 	if (MODE_LOCAL_CHARS(globalmode)) {
184832385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
184932385Sminshall 		break;
185032385Sminshall 	    }
185132385Sminshall 	}
185238689Sborman 	if (my_want_state_is_wont(TELOPT_BINARY)) {
185332385Sminshall 	    switch (c) {
185432385Sminshall 	    case '\n':
185532385Sminshall 		    /*
185632385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
185732385Sminshall 		     * on our local machine, then probably
185832385Sminshall 		     * a newline (unix) is CRLF (TELNET).
185932385Sminshall 		     */
186032385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
186132385Sminshall 		    NETADD('\r');
186232385Sminshall 		}
186332385Sminshall 		NETADD('\n');
186432385Sminshall 		flushline = 1;
186532385Sminshall 		break;
186632385Sminshall 	    case '\r':
186732385Sminshall 		if (!crlf) {
186832385Sminshall 		    NET2ADD('\r', '\0');
186932385Sminshall 		} else {
187032385Sminshall 		    NET2ADD('\r', '\n');
187132385Sminshall 		}
187232385Sminshall 		flushline = 1;
187332385Sminshall 		break;
187432385Sminshall 	    case IAC:
187532385Sminshall 		NET2ADD(IAC, IAC);
187632385Sminshall 		break;
187732385Sminshall 	    default:
187832385Sminshall 		NETADD(c);
187932385Sminshall 		break;
188032385Sminshall 	    }
188132385Sminshall 	} else if (c == IAC) {
188232385Sminshall 	    NET2ADD(IAC, IAC);
188332385Sminshall 	} else {
188432385Sminshall 	    NETADD(c);
188532385Sminshall 	}
188632385Sminshall     }
188732667Sminshall     if (count)
188832667Sminshall 	ring_consumed(&ttyiring, count);
188932385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
189032385Sminshall }
189132377Sminshall 
189227088Sminshall /*
189332377Sminshall  * Scheduler()
189432377Sminshall  *
189532377Sminshall  * Try to do something.
189632377Sminshall  *
189732377Sminshall  * If we do something useful, return 1; else return 0.
189832377Sminshall  *
189927110Sminshall  */
190027110Sminshall 
190127110Sminshall 
190232377Sminshall int
190332377Sminshall Scheduler(block)
190432377Sminshall int	block;			/* should we block in the select ? */
190527110Sminshall {
190632377Sminshall 		/* One wants to be a bit careful about setting returnValue
190732377Sminshall 		 * to one, since a one implies we did some useful work,
190832377Sminshall 		 * and therefore probably won't be called to block next
190932377Sminshall 		 * time (TN3270 mode only).
191032377Sminshall 		 */
191132531Sminshall     int returnValue;
191232531Sminshall     int netin, netout, netex, ttyin, ttyout;
191327110Sminshall 
191432531Sminshall     /* Decide which rings should be processed */
191532531Sminshall 
191632531Sminshall     netout = ring_full_count(&netoring) &&
191738689Sborman 	    (flushline ||
191838689Sborman 		(my_want_state_is_wont(TELOPT_LINEMODE)
191938689Sborman #ifdef	KLUDGELINEMODE
192038689Sborman 			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
192138689Sborman #endif
192238689Sborman 		) ||
192338689Sborman 			my_want_state_is_will(TELOPT_BINARY));
192432531Sminshall     ttyout = ring_full_count(&ttyoring);
192532531Sminshall 
192632377Sminshall #if	defined(TN3270)
192732531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
192832377Sminshall #else	/* defined(TN3270) */
192932531Sminshall     ttyin = ring_empty_count(&ttyiring);
193032377Sminshall #endif	/* defined(TN3270) */
193132531Sminshall 
193232531Sminshall #if	defined(TN3270)
193332531Sminshall     netin = ring_empty_count(&netiring);
193432377Sminshall #   else /* !defined(TN3270) */
193532531Sminshall     netin = !ISend && ring_empty_count(&netiring);
193632377Sminshall #   endif /* !defined(TN3270) */
193732531Sminshall 
193832531Sminshall     netex = !SYNCHing;
193932531Sminshall 
194032531Sminshall     /* If we have seen a signal recently, reset things */
194132377Sminshall #   if defined(TN3270) && defined(unix)
194232377Sminshall     if (HaveInput) {
194332377Sminshall 	HaveInput = 0;
1944*44360Sborman 	(void) signal(SIGIO, inputAvailable);
194532377Sminshall     }
194632377Sminshall #endif	/* defined(TN3270) && defined(unix) */
194732377Sminshall 
194832531Sminshall     /* Call to system code to process rings */
194927178Sminshall 
195032531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
195127178Sminshall 
195232531Sminshall     /* Now, look at the input rings, looking for work to do. */
195332377Sminshall 
195432531Sminshall     if (ring_full_count(&ttyiring)) {
195532377Sminshall #   if defined(TN3270)
195632377Sminshall 	if (In3270) {
195734848Sminshall 	    int c;
195834848Sminshall 
195933804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
196032528Sminshall 					ring_full_consecutive(&ttyiring));
196132377Sminshall 	    if (c) {
196232377Sminshall 		returnValue = 1;
196332667Sminshall 	        ring_consumed(&ttyiring, c);
196432377Sminshall 	    }
196532377Sminshall 	} else {
196632377Sminshall #   endif /* defined(TN3270) */
196732554Sminshall 	    returnValue |= telsnd();
196832377Sminshall #   if defined(TN3270)
196927178Sminshall 	}
197032531Sminshall #   endif /* defined(TN3270) */
197127178Sminshall     }
197232377Sminshall 
197332528Sminshall     if (ring_full_count(&netiring)) {
197432377Sminshall #	if !defined(TN3270)
197532385Sminshall 	returnValue |= telrcv();
197632377Sminshall #	else /* !defined(TN3270) */
197732377Sminshall 	returnValue = Push3270();
197832377Sminshall #	endif /* !defined(TN3270) */
197932377Sminshall     }
198032377Sminshall     return returnValue;
198127178Sminshall }
198227178Sminshall 
198327178Sminshall /*
198432377Sminshall  * Select from tty and network...
198527088Sminshall  */
198632377Sminshall void
198732377Sminshall telnet()
198827088Sminshall {
198932531Sminshall     sys_telnet_init();
199027088Sminshall 
199132377Sminshall #   if !defined(TN3270)
199232377Sminshall     if (telnetport) {
199338689Sborman 	send_do(TELOPT_SGA, 1);
199438689Sborman 	send_will(TELOPT_TTYPE, 1);
199538689Sborman 	send_will(TELOPT_NAWS, 1);
199638689Sborman 	send_will(TELOPT_TSPEED, 1);
199738689Sborman 	send_will(TELOPT_LFLOW, 1);
199838689Sborman 	send_will(TELOPT_LINEMODE, 1);
199943319Skfall #ifdef	KERBEROS
200043319Skfall 	if (kerberized)
200143319Skfall 		send_will(TELOPT_AUTHENTICATION, 1);
200243319Skfall #endif
200338908Sborman 	send_do(TELOPT_STATUS, 1);
2004*44360Sborman 	if (env_getvalue("DISPLAY"))
2005*44360Sborman 	    send_will(TELOPT_XDISPLOC, 1);
2006*44360Sborman 	send_will(TELOPT_ENVIRON, 1);
200727178Sminshall     }
200832377Sminshall #   endif /* !defined(TN3270) */
200927088Sminshall 
201032377Sminshall #   if !defined(TN3270)
201132377Sminshall     for (;;) {
201232385Sminshall 	int schedValue;
201332385Sminshall 
201432385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
201532385Sminshall 	    if (schedValue == -1) {
201632385Sminshall 		setcommandmode();
201732385Sminshall 		return;
201832385Sminshall 	    }
201932385Sminshall 	}
202032385Sminshall 
202132531Sminshall 	if (Scheduler(1) == -1) {
202232377Sminshall 	    setcommandmode();
202332377Sminshall 	    return;
202432377Sminshall 	}
202532377Sminshall     }
202632377Sminshall #   else /* !defined(TN3270) */
202732377Sminshall     for (;;) {
202832377Sminshall 	int schedValue;
202927088Sminshall 
203032377Sminshall 	while (!In3270 && !shell_active) {
203132531Sminshall 	    if (Scheduler(1) == -1) {
203232377Sminshall 		setcommandmode();
203332377Sminshall 		return;
203432377Sminshall 	    }
203527088Sminshall 	}
203632377Sminshall 
203732377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
203832377Sminshall 	    if (schedValue == -1) {
203932377Sminshall 		setcommandmode();
204032377Sminshall 		return;
204132377Sminshall 	    }
204227088Sminshall 	}
204332377Sminshall 		/* If there is data waiting to go out to terminal, don't
204432377Sminshall 		 * schedule any more data for the terminal.
204532377Sminshall 		 */
204634304Sminshall 	if (ring_full_count(&ttyoring)) {
204732377Sminshall 	    schedValue = 1;
204827088Sminshall 	} else {
204932377Sminshall 	    if (shell_active) {
205032377Sminshall 		if (shell_continue() == 0) {
205132377Sminshall 		    ConnectScreen();
205227088Sminshall 		}
205332377Sminshall 	    } else if (In3270) {
205432377Sminshall 		schedValue = DoTerminalOutput();
205532377Sminshall 	    }
205627088Sminshall 	}
205732377Sminshall 	if (schedValue && (shell_active == 0)) {
205832531Sminshall 	    if (Scheduler(1) == -1) {
205932377Sminshall 		setcommandmode();
206032377Sminshall 		return;
206132377Sminshall 	    }
206227088Sminshall 	}
206332377Sminshall     }
206432377Sminshall #   endif /* !defined(TN3270) */
206527088Sminshall }
206632377Sminshall 
206734848Sminshall #if	0	/* XXX - this not being in is a bug */
206827088Sminshall /*
206932554Sminshall  * nextitem()
207032554Sminshall  *
207132554Sminshall  *	Return the address of the next "item" in the TELNET data
207232554Sminshall  * stream.  This will be the address of the next character if
207332554Sminshall  * the current address is a user data character, or it will
207432554Sminshall  * be the address of the character following the TELNET command
207532554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
207632554Sminshall  * character.
207732554Sminshall  */
207832554Sminshall 
207932554Sminshall static char *
208032554Sminshall nextitem(current)
208132554Sminshall char	*current;
208232554Sminshall {
208332554Sminshall     if ((*current&0xff) != IAC) {
208432554Sminshall 	return current+1;
208532554Sminshall     }
208632554Sminshall     switch (*(current+1)&0xff) {
208732554Sminshall     case DO:
208832554Sminshall     case DONT:
208932554Sminshall     case WILL:
209032554Sminshall     case WONT:
209132554Sminshall 	return current+3;
209232554Sminshall     case SB:		/* loop forever looking for the SE */
209332554Sminshall 	{
209432554Sminshall 	    register char *look = current+2;
209532554Sminshall 
209632554Sminshall 	    for (;;) {
209732554Sminshall 		if ((*look++&0xff) == IAC) {
209832554Sminshall 		    if ((*look++&0xff) == SE) {
209932554Sminshall 			return look;
210032554Sminshall 		    }
210132554Sminshall 		}
210232554Sminshall 	    }
210332554Sminshall 	}
210432554Sminshall     default:
210532554Sminshall 	return current+2;
210632554Sminshall     }
210732554Sminshall }
210834848Sminshall #endif	/* 0 */
210932554Sminshall 
211032554Sminshall /*
211132554Sminshall  * netclear()
211232554Sminshall  *
211332554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
211432554Sminshall  * the path to the network.
211532554Sminshall  *
211632554Sminshall  *	Things are a bit tricky since we may have sent the first
211732554Sminshall  * byte or so of a previous TELNET command into the network.
211832554Sminshall  * So, we have to scan the network buffer from the beginning
211932554Sminshall  * until we are up to where we want to be.
212032554Sminshall  *
212132554Sminshall  *	A side effect of what we do, just to keep things
212232554Sminshall  * simple, is to clear the urgent data pointer.  The principal
212332554Sminshall  * caller should be setting the urgent data pointer AFTER calling
212432554Sminshall  * us in any case.
212532554Sminshall  */
212632554Sminshall 
212732554Sminshall static void
212832554Sminshall netclear()
212932554Sminshall {
213032554Sminshall #if	0	/* XXX */
213132554Sminshall     register char *thisitem, *next;
213232554Sminshall     char *good;
213332554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
213432554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
213532554Sminshall 
213632554Sminshall     thisitem = netobuf;
213732554Sminshall 
213832554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
213932554Sminshall 	thisitem = next;
214032554Sminshall     }
214132554Sminshall 
214232554Sminshall     /* Now, thisitem is first before/at boundary. */
214332554Sminshall 
214432554Sminshall     good = netobuf;	/* where the good bytes go */
214532554Sminshall 
214632554Sminshall     while (netoring.add > thisitem) {
214732554Sminshall 	if (wewant(thisitem)) {
214832554Sminshall 	    int length;
214932554Sminshall 
215032554Sminshall 	    next = thisitem;
215132554Sminshall 	    do {
215232554Sminshall 		next = nextitem(next);
215332554Sminshall 	    } while (wewant(next) && (nfrontp > next));
215432554Sminshall 	    length = next-thisitem;
215532554Sminshall 	    memcpy(good, thisitem, length);
215632554Sminshall 	    good += length;
215732554Sminshall 	    thisitem = next;
215832554Sminshall 	} else {
215932554Sminshall 	    thisitem = nextitem(thisitem);
216032554Sminshall 	}
216132554Sminshall     }
216232554Sminshall 
216332554Sminshall #endif	/* 0 */
216432554Sminshall }
216532554Sminshall 
216632554Sminshall /*
216732377Sminshall  * These routines add various telnet commands to the data stream.
216827088Sminshall  */
216932377Sminshall 
217032554Sminshall static void
217132554Sminshall doflush()
217232554Sminshall {
217332554Sminshall     NET2ADD(IAC, DO);
217432554Sminshall     NETADD(TELOPT_TM);
217532554Sminshall     flushline = 1;
217632554Sminshall     flushout = 1;
2177*44360Sborman     (void) ttyflush(1);			/* Flush/drop output */
217832554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
217937226Sminshall     printoption("SENT", "do", TELOPT_TM);
218032554Sminshall }
218132554Sminshall 
218232377Sminshall void
218332377Sminshall xmitAO()
218427088Sminshall {
218532377Sminshall     NET2ADD(IAC, AO);
218638908Sborman     printoption("SENT", "IAC", AO);
218732377Sminshall     if (autoflush) {
218832377Sminshall 	doflush();
218932377Sminshall     }
219032377Sminshall }
219127088Sminshall 
219232377Sminshall 
219332377Sminshall void
219432377Sminshall xmitEL()
219527088Sminshall {
219632377Sminshall     NET2ADD(IAC, EL);
219738908Sborman     printoption("SENT", "IAC", EL);
219827088Sminshall }
219927088Sminshall 
220032377Sminshall void
220132377Sminshall xmitEC()
220227088Sminshall {
220332377Sminshall     NET2ADD(IAC, EC);
220438908Sborman     printoption("SENT", "IAC", EC);
220527088Sminshall }
220627088Sminshall 
220732377Sminshall 
220832377Sminshall #if	defined(NOT43)
220932377Sminshall int
221032377Sminshall #else	/* defined(NOT43) */
221132377Sminshall void
221232377Sminshall #endif	/* defined(NOT43) */
221332377Sminshall dosynch()
221427088Sminshall {
221532377Sminshall     netclear();			/* clear the path to the network */
221633294Sminshall     NETADD(IAC);
221733294Sminshall     setneturg();
221833294Sminshall     NETADD(DM);
221938908Sborman     printoption("SENT", "IAC", DM);
222027088Sminshall 
222132377Sminshall #if	defined(NOT43)
222232377Sminshall     return 0;
222332377Sminshall #endif	/* defined(NOT43) */
222427088Sminshall }
222527088Sminshall 
222632377Sminshall void
222738908Sborman get_status()
222838908Sborman {
222938908Sborman     char tmp[16];
223038908Sborman     register char *cp;
223138908Sborman 
223238908Sborman     if (my_want_state_is_dont(TELOPT_STATUS)) {
223338908Sborman 	printf("Remote side does not support STATUS option\n");
223438908Sborman 	return;
223538908Sborman     }
223638908Sborman     if (!showoptions)
223738908Sborman 	printf("You will not see the response unless you set \"options\"\n");
223838908Sborman 
223938908Sborman     cp = tmp;
224038908Sborman 
224138908Sborman     *cp++ = IAC;
224238908Sborman     *cp++ = SB;
224338908Sborman     *cp++ = TELOPT_STATUS;
224438908Sborman     *cp++ = TELQUAL_SEND;
224538908Sborman     *cp++ = IAC;
224638908Sborman     *cp++ = SE;
224738908Sborman     if (NETROOM() >= cp - tmp) {
224838908Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
224938908Sborman 	printsub('>', tmp+2, cp - tmp - 2);
225038908Sborman     }
225138908Sborman }
225238908Sborman 
225338908Sborman void
225432377Sminshall intp()
225527088Sminshall {
225632377Sminshall     NET2ADD(IAC, IP);
225738908Sborman     printoption("SENT", "IAC", IP);
225832377Sminshall     flushline = 1;
225932377Sminshall     if (autoflush) {
226032377Sminshall 	doflush();
226132377Sminshall     }
226232377Sminshall     if (autosynch) {
226332377Sminshall 	dosynch();
226432377Sminshall     }
226527088Sminshall }
226627186Sminshall 
226732377Sminshall void
226832377Sminshall sendbrk()
226927186Sminshall {
227032377Sminshall     NET2ADD(IAC, BREAK);
227138908Sborman     printoption("SENT", "IAC", BREAK);
227232377Sminshall     flushline = 1;
227332377Sminshall     if (autoflush) {
227432377Sminshall 	doflush();
227532377Sminshall     }
227632377Sminshall     if (autosynch) {
227732377Sminshall 	dosynch();
227832377Sminshall     }
227927186Sminshall }
228038689Sborman 
228138689Sborman void
228238689Sborman sendabort()
228338689Sborman {
228438689Sborman     NET2ADD(IAC, ABORT);
228538908Sborman     printoption("SENT", "IAC", ABORT);
228638689Sborman     flushline = 1;
228738689Sborman     if (autoflush) {
228838689Sborman 	doflush();
228938689Sborman     }
229038689Sborman     if (autosynch) {
229138689Sborman 	dosynch();
229238689Sborman     }
229338689Sborman }
229438689Sborman 
229538689Sborman void
229638689Sborman sendsusp()
229738689Sborman {
229838689Sborman     NET2ADD(IAC, SUSP);
229938908Sborman     printoption("SENT", "IAC", SUSP);
230038689Sborman     flushline = 1;
230138689Sborman     if (autoflush) {
230238689Sborman 	doflush();
230338689Sborman     }
230438689Sborman     if (autosynch) {
230538689Sborman 	dosynch();
230638689Sborman     }
230738689Sborman }
230838689Sborman 
230938689Sborman void
231038689Sborman sendeof()
231138689Sborman {
231238908Sborman     NET2ADD(IAC, xEOF);
231338908Sborman     printoption("SENT", "IAC", xEOF);
231438689Sborman }
231538689Sborman 
231637219Sminshall /*
231737219Sminshall  * Send a window size update to the remote system.
231837219Sminshall  */
231937219Sminshall 
232037219Sminshall void
232137219Sminshall sendnaws()
232237219Sminshall {
232337219Sminshall     long rows, cols;
232438689Sborman     unsigned char tmp[16];
232538689Sborman     register unsigned char *cp;
232637219Sminshall 
232738689Sborman     if (my_state_is_wont(TELOPT_NAWS))
232838689Sborman 	return;
232937219Sminshall 
233038689Sborman #define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
233138689Sborman 			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
233238689Sborman 
233337219Sminshall     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
233437219Sminshall 	return;
233537219Sminshall     }
233637219Sminshall 
233738689Sborman     cp = tmp;
233838689Sborman 
233938689Sborman     *cp++ = IAC;
234038689Sborman     *cp++ = SB;
234138689Sborman     *cp++ = TELOPT_NAWS;
234238689Sborman     PUTSHORT(cp, cols);
234338689Sborman     PUTSHORT(cp, rows);
234438689Sborman     *cp++ = IAC;
234538689Sborman     *cp++ = SE;
234638689Sborman     if (NETROOM() >= cp - tmp) {
234738689Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
234838689Sborman 	printsub('>', tmp+2, cp - tmp - 2);
234937219Sminshall     }
235037219Sminshall }
235137226Sminshall 
235238908Sborman tel_enter_binary(rw)
235338908Sborman int rw;
235437226Sminshall {
235538908Sborman     if (rw&1)
235638908Sborman 	send_do(TELOPT_BINARY, 1);
235738908Sborman     if (rw&2)
235838908Sborman 	send_will(TELOPT_BINARY, 1);
235937226Sminshall }
236037226Sminshall 
236138908Sborman tel_leave_binary(rw)
236238908Sborman int rw;
236337226Sminshall {
236438908Sborman     if (rw&1)
236538908Sborman 	send_dont(TELOPT_BINARY, 1);
236638908Sborman     if (rw&2)
236738908Sborman 	send_wont(TELOPT_BINARY, 1);
236837226Sminshall }
2369