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