129088Smarkm/*
229088Smarkm * Copyright (c) 1989, 1993
329088Smarkm *	The Regents of the University of California.  All rights reserved.
429088Smarkm *
529088Smarkm * Redistribution and use in source and binary forms, with or without
629088Smarkm * modification, are permitted provided that the following conditions
729088Smarkm * are met:
829088Smarkm * 1. Redistributions of source code must retain the above copyright
929088Smarkm *    notice, this list of conditions and the following disclaimer.
1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1129088Smarkm *    notice, this list of conditions and the following disclaimer in the
1229088Smarkm *    documentation and/or other materials provided with the distribution.
1329088Smarkm * 3. All advertising materials mentioning features or use of this software
1429088Smarkm *    must display the following acknowledgement:
1529088Smarkm *	This product includes software developed by the University of
1629088Smarkm *	California, Berkeley and its contributors.
1729088Smarkm * 4. Neither the name of the University nor the names of its contributors
1829088Smarkm *    may be used to endorse or promote products derived from this software
1929088Smarkm *    without specific prior written permission.
2029088Smarkm *
2129088Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2229088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2329088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2429088Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2529088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2629088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2729088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2829088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2929088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3029088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3129088Smarkm * SUCH DAMAGE.
3229088Smarkm */
3329088Smarkm
34114630Sobrien#if 0
3529088Smarkm#ifndef lint
3629181Smarkmstatic const char sccsid[] = "@(#)state.c	8.5 (Berkeley) 5/30/95";
3731622Scharnier#endif
38114630Sobrien#endif
39114630Sobrien#include <sys/cdefs.h>
40114630Sobrien__FBSDID("$FreeBSD$");
4129088Smarkm
4279981Sru#include <stdarg.h>
4329088Smarkm#include "telnetd.h"
4487139Smarkm#ifdef	AUTHENTICATION
4529088Smarkm#include <libtelnet/auth.h>
4629088Smarkm#endif
4787139Smarkm#ifdef	ENCRYPTION
4829181Smarkm#include <libtelnet/encrypt.h>
4929181Smarkm#endif
5029088Smarkm
5129088Smarkmunsigned char	doopt[] = { IAC, DO, '%', 'c', 0 };
5229088Smarkmunsigned char	dont[] = { IAC, DONT, '%', 'c', 0 };
5329088Smarkmunsigned char	will[] = { IAC, WILL, '%', 'c', 0 };
5429088Smarkmunsigned char	wont[] = { IAC, WONT, '%', 'c', 0 };
5529088Smarkmint	not42 = 1;
5629088Smarkm
5729088Smarkm/*
5829088Smarkm * Buffer for sub-options, and macros
5929088Smarkm * for suboptions buffer manipulations
6029088Smarkm */
6129088Smarkmunsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer;
6229088Smarkm
6329088Smarkm#define	SB_CLEAR()	subpointer = subbuffer
6429088Smarkm#define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
6529088Smarkm#define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
6629088Smarkm				*subpointer++ = (c); \
6729088Smarkm			}
6829088Smarkm#define	SB_GET()	((*subpointer++)&0xff)
6929088Smarkm#define	SB_EOF()	(subpointer >= subend)
7029088Smarkm#define	SB_LEN()	(subend - subpointer)
7129088Smarkm
7229088Smarkm#ifdef	ENV_HACK
7329088Smarkmunsigned char *subsave;
7429088Smarkm#define SB_SAVE()	subsave = subpointer;
7529088Smarkm#define	SB_RESTORE()	subpointer = subsave;
7629088Smarkm#endif
7729088Smarkm
7829088Smarkm
7929088Smarkm/*
8029088Smarkm * State for recv fsm
8129088Smarkm */
8229088Smarkm#define	TS_DATA		0	/* base state */
8329088Smarkm#define	TS_IAC		1	/* look for double IAC's */
8429088Smarkm#define	TS_CR		2	/* CR-LF ->'s CR */
8529088Smarkm#define	TS_SB		3	/* throw away begin's... */
8629088Smarkm#define	TS_SE		4	/* ...end's (suboption negotiation) */
8729088Smarkm#define	TS_WILL		5	/* will option negotiation */
8829088Smarkm#define	TS_WONT		6	/* wont " */
8929088Smarkm#define	TS_DO		7	/* do " */
9029088Smarkm#define	TS_DONT		8	/* dont " */
9129088Smarkm
9287139Smarkmstatic void doclientstat(void);
9387139Smarkm
9487139Smarkmvoid
9587139Smarkmtelrcv(void)
9629088Smarkm{
9787139Smarkm	int c;
9829088Smarkm	static int state = TS_DATA;
9929088Smarkm
10029088Smarkm	while (ncc > 0) {
10129088Smarkm		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
10229088Smarkm			break;
10329088Smarkm		c = *netip++ & 0377, ncc--;
10429088Smarkm#ifdef	ENCRYPTION
10529088Smarkm		if (decrypt_input)
10629088Smarkm			c = (*decrypt_input)(c);
10729088Smarkm#endif	/* ENCRYPTION */
10829088Smarkm		switch (state) {
10929088Smarkm
11029088Smarkm		case TS_CR:
11129088Smarkm			state = TS_DATA;
11229088Smarkm			/* Strip off \n or \0 after a \r */
11329088Smarkm			if ((c == 0) || (c == '\n')) {
11429088Smarkm				break;
11529088Smarkm			}
116103956Smarkm			/* FALLTHROUGH */
11729088Smarkm
11829088Smarkm		case TS_DATA:
11929088Smarkm			if (c == IAC) {
12029088Smarkm				state = TS_IAC;
12129088Smarkm				break;
12229088Smarkm			}
12329088Smarkm			/*
12429088Smarkm			 * We now map \r\n ==> \r for pragmatic reasons.
12529088Smarkm			 * Many client implementations send \r\n when
12629088Smarkm			 * the user hits the CarriageReturn key.
12729088Smarkm			 *
12829088Smarkm			 * We USED to map \r\n ==> \n, since \r\n says
12929088Smarkm			 * that we want to be in column 1 of the next
13029088Smarkm			 * printable line, and \n is the standard
13129088Smarkm			 * unix way of saying that (\r is only good
13229088Smarkm			 * if CRMOD is set, which it normally is).
13329088Smarkm			 */
13429088Smarkm			if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
13529088Smarkm				int nc = *netip;
13629088Smarkm#ifdef	ENCRYPTION
13729088Smarkm				if (decrypt_input)
13829088Smarkm					nc = (*decrypt_input)(nc & 0xff);
13929088Smarkm#endif	/* ENCRYPTION */
14029088Smarkm#ifdef	LINEMODE
14129088Smarkm				/*
14229088Smarkm				 * If we are operating in linemode,
14329088Smarkm				 * convert to local end-of-line.
14429088Smarkm				 */
14529088Smarkm				if (linemode && (ncc > 0) && (('\n' == nc) ||
14629088Smarkm					 ((0 == nc) && tty_iscrnl())) ) {
14729088Smarkm					netip++; ncc--;
14829088Smarkm					c = '\n';
14929088Smarkm				} else
15029088Smarkm#endif
15129088Smarkm				{
15229088Smarkm#ifdef	ENCRYPTION
15329088Smarkm					if (decrypt_input)
15429088Smarkm						(void)(*decrypt_input)(-1);
15529088Smarkm#endif	/* ENCRYPTION */
15629088Smarkm					state = TS_CR;
15729088Smarkm				}
15829088Smarkm			}
15929088Smarkm			*pfrontp++ = c;
16029088Smarkm			break;
16129088Smarkm
16229088Smarkm		case TS_IAC:
16329088Smarkmgotiac:			switch (c) {
16429088Smarkm
16529088Smarkm			/*
16629088Smarkm			 * Send the process on the pty side an
16729088Smarkm			 * interrupt.  Do this with a NULL or
16829088Smarkm			 * interrupt char; depending on the tty mode.
16929088Smarkm			 */
17029088Smarkm			case IP:
17129088Smarkm				DIAG(TD_OPTIONS,
17229088Smarkm					printoption("td: recv IAC", c));
17329088Smarkm				interrupt();
17429088Smarkm				break;
17529088Smarkm
17629088Smarkm			case BREAK:
17729088Smarkm				DIAG(TD_OPTIONS,
17829088Smarkm					printoption("td: recv IAC", c));
17929088Smarkm				sendbrk();
18029088Smarkm				break;
18129088Smarkm
18229088Smarkm			/*
18329088Smarkm			 * Are You There?
18429088Smarkm			 */
18529088Smarkm			case AYT:
18629088Smarkm				DIAG(TD_OPTIONS,
18729088Smarkm					printoption("td: recv IAC", c));
18829088Smarkm				recv_ayt();
18929088Smarkm				break;
19029088Smarkm
19129088Smarkm			/*
19229088Smarkm			 * Abort Output
19329088Smarkm			 */
19429088Smarkm			case AO:
19529088Smarkm			    {
19629088Smarkm				DIAG(TD_OPTIONS,
19729088Smarkm					printoption("td: recv IAC", c));
19829088Smarkm				ptyflush();	/* half-hearted */
19929088Smarkm				init_termbuf();
20029088Smarkm
20129088Smarkm				if (slctab[SLC_AO].sptr &&
20229088Smarkm				    *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
20329088Smarkm				    *pfrontp++ =
20429088Smarkm					(unsigned char)*slctab[SLC_AO].sptr;
20529088Smarkm				}
20629088Smarkm
20729088Smarkm				netclear();	/* clear buffer back */
20879981Sru				output_data("%c%c", IAC, DM);
20929088Smarkm				neturg = nfrontp-1; /* off by one XXX */
21029088Smarkm				DIAG(TD_OPTIONS,
21129088Smarkm					printoption("td: send IAC", DM));
21229088Smarkm				break;
21329088Smarkm			    }
21429088Smarkm
21529088Smarkm			/*
21629088Smarkm			 * Erase Character and
21729088Smarkm			 * Erase Line
21829088Smarkm			 */
21929088Smarkm			case EC:
22029088Smarkm			case EL:
22129088Smarkm			    {
22229088Smarkm				cc_t ch;
22329088Smarkm
22429088Smarkm				DIAG(TD_OPTIONS,
22529088Smarkm					printoption("td: recv IAC", c));
22629088Smarkm				ptyflush();	/* half-hearted */
22729088Smarkm				init_termbuf();
22829088Smarkm				if (c == EC)
22929088Smarkm					ch = *slctab[SLC_EC].sptr;
23029088Smarkm				else
23129088Smarkm					ch = *slctab[SLC_EL].sptr;
23229088Smarkm				if (ch != (cc_t)(_POSIX_VDISABLE))
23329088Smarkm					*pfrontp++ = (unsigned char)ch;
23429088Smarkm				break;
23529088Smarkm			    }
23629088Smarkm
23729088Smarkm			/*
23829088Smarkm			 * Check for urgent data...
23929088Smarkm			 */
24029088Smarkm			case DM:
24129088Smarkm				DIAG(TD_OPTIONS,
24229088Smarkm					printoption("td: recv IAC", c));
24329088Smarkm				SYNCHing = stilloob(net);
24429088Smarkm				settimer(gotDM);
24529088Smarkm				break;
24629088Smarkm
24729088Smarkm
24829088Smarkm			/*
24929088Smarkm			 * Begin option subnegotiation...
25029088Smarkm			 */
25129088Smarkm			case SB:
25229088Smarkm				state = TS_SB;
25329088Smarkm				SB_CLEAR();
25429088Smarkm				continue;
25529088Smarkm
25629088Smarkm			case WILL:
25729088Smarkm				state = TS_WILL;
25829088Smarkm				continue;
25929088Smarkm
26029088Smarkm			case WONT:
26129088Smarkm				state = TS_WONT;
26229088Smarkm				continue;
26329088Smarkm
26429088Smarkm			case DO:
26529088Smarkm				state = TS_DO;
26629088Smarkm				continue;
26729088Smarkm
26829088Smarkm			case DONT:
26929088Smarkm				state = TS_DONT;
27029088Smarkm				continue;
27129088Smarkm			case EOR:
27229088Smarkm				if (his_state_is_will(TELOPT_EOR))
27329088Smarkm					doeof();
27429088Smarkm				break;
27529088Smarkm
27629088Smarkm			/*
27729088Smarkm			 * Handle RFC 10xx Telnet linemode option additions
27829088Smarkm			 * to command stream (EOF, SUSP, ABORT).
27929088Smarkm			 */
28029088Smarkm			case xEOF:
28129088Smarkm				doeof();
28229088Smarkm				break;
28329088Smarkm
28429088Smarkm			case SUSP:
28529088Smarkm				sendsusp();
28629088Smarkm				break;
28729088Smarkm
28829088Smarkm			case ABORT:
28929088Smarkm				sendbrk();
29029088Smarkm				break;
29129088Smarkm
29229088Smarkm			case IAC:
29329088Smarkm				*pfrontp++ = c;
29429088Smarkm				break;
29529088Smarkm			}
29629088Smarkm			state = TS_DATA;
29729088Smarkm			break;
29829088Smarkm
29929088Smarkm		case TS_SB:
30029088Smarkm			if (c == IAC) {
30129088Smarkm				state = TS_SE;
30229088Smarkm			} else {
30329088Smarkm				SB_ACCUM(c);
30429088Smarkm			}
30529088Smarkm			break;
30629088Smarkm
30729088Smarkm		case TS_SE:
30829088Smarkm			if (c != SE) {
30929088Smarkm				if (c != IAC) {
31029088Smarkm					/*
31129088Smarkm					 * bad form of suboption negotiation.
31229088Smarkm					 * handle it in such a way as to avoid
31329088Smarkm					 * damage to local state.  Parse
31429088Smarkm					 * suboption buffer found so far,
31529088Smarkm					 * then treat remaining stream as
31629088Smarkm					 * another command sequence.
31729088Smarkm					 */
31829088Smarkm
31929088Smarkm					/* for DIAGNOSTICS */
32029088Smarkm					SB_ACCUM(IAC);
32129088Smarkm					SB_ACCUM(c);
32229088Smarkm					subpointer -= 2;
32329088Smarkm
32429088Smarkm					SB_TERM();
32529088Smarkm					suboption();
32629088Smarkm					state = TS_IAC;
32729088Smarkm					goto gotiac;
32829088Smarkm				}
32929088Smarkm				SB_ACCUM(c);
33029088Smarkm				state = TS_SB;
33129088Smarkm			} else {
33229088Smarkm				/* for DIAGNOSTICS */
33329088Smarkm				SB_ACCUM(IAC);
33429088Smarkm				SB_ACCUM(SE);
33529088Smarkm				subpointer -= 2;
33629088Smarkm
33729088Smarkm				SB_TERM();
33829088Smarkm				suboption();	/* handle sub-option */
33929088Smarkm				state = TS_DATA;
34029088Smarkm			}
34129088Smarkm			break;
34229088Smarkm
34329088Smarkm		case TS_WILL:
34429088Smarkm			willoption(c);
34529088Smarkm			state = TS_DATA;
34629088Smarkm			continue;
34729088Smarkm
34829088Smarkm		case TS_WONT:
34929088Smarkm			wontoption(c);
35029088Smarkm			state = TS_DATA;
35129088Smarkm			continue;
35229088Smarkm
35329088Smarkm		case TS_DO:
35429088Smarkm			dooption(c);
35529088Smarkm			state = TS_DATA;
35629088Smarkm			continue;
35729088Smarkm
35829088Smarkm		case TS_DONT:
35929088Smarkm			dontoption(c);
36029088Smarkm			state = TS_DATA;
36129088Smarkm			continue;
36229088Smarkm
36329088Smarkm		default:
36431622Scharnier			syslog(LOG_ERR, "panic state=%d", state);
36529088Smarkm			printf("telnetd: panic state=%d\n", state);
36629088Smarkm			exit(1);
36729088Smarkm		}
36829088Smarkm	}
36929088Smarkm}  /* end of telrcv */
37029088Smarkm
37129088Smarkm/*
37229088Smarkm * The will/wont/do/dont state machines are based on Dave Borman's
37329088Smarkm * Telnet option processing state machine.
37429088Smarkm *
37529088Smarkm * These correspond to the following states:
37629088Smarkm *	my_state = the last negotiated state
37729088Smarkm *	want_state = what I want the state to go to
37829088Smarkm *	want_resp = how many requests I have sent
37929088Smarkm * All state defaults are negative, and resp defaults to 0.
38029088Smarkm *
38129088Smarkm * When initiating a request to change state to new_state:
38229088Smarkm *
38329088Smarkm * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
38429088Smarkm *	do nothing;
38529088Smarkm * } else {
38629088Smarkm *	want_state = new_state;
38729088Smarkm *	send new_state;
38829088Smarkm *	want_resp++;
38929088Smarkm * }
39029088Smarkm *
39129088Smarkm * When receiving new_state:
39229088Smarkm *
39329088Smarkm * if (want_resp) {
39429088Smarkm *	want_resp--;
39529088Smarkm *	if (want_resp && (new_state == my_state))
39629088Smarkm *		want_resp--;
39729088Smarkm * }
39829088Smarkm * if ((want_resp == 0) && (new_state != want_state)) {
39929088Smarkm *	if (ok_to_switch_to new_state)
40029088Smarkm *		want_state = new_state;
40129088Smarkm *	else
40229088Smarkm *		want_resp++;
40329088Smarkm *	send want_state;
40429088Smarkm * }
40529088Smarkm * my_state = new_state;
40629088Smarkm *
40729088Smarkm * Note that new_state is implied in these functions by the function itself.
40829088Smarkm * will and do imply positive new_state, wont and dont imply negative.
40929088Smarkm *
41029088Smarkm * Finally, there is one catch.  If we send a negative response to a
41129088Smarkm * positive request, my_state will be the positive while want_state will
41229088Smarkm * remain negative.  my_state will revert to negative when the negative
41329088Smarkm * acknowlegment arrives from the peer.  Thus, my_state generally tells
41429088Smarkm * us not only the last negotiated state, but also tells us what the peer
41529088Smarkm * wants to be doing as well.  It is important to understand this difference
41629088Smarkm * as we may wish to be processing data streams based on our desired state
41729088Smarkm * (want_state) or based on what the peer thinks the state is (my_state).
41829088Smarkm *
41929088Smarkm * This all works fine because if the peer sends a positive request, the data
42029088Smarkm * that we receive prior to negative acknowlegment will probably be affected
42129088Smarkm * by the positive state, and we can process it as such (if we can; if we
42229088Smarkm * can't then it really doesn't matter).  If it is that important, then the
42329088Smarkm * peer probably should be buffering until this option state negotiation
42429088Smarkm * is complete.
42529088Smarkm *
42629088Smarkm */
42787139Smarkmvoid
42887139Smarkmsend_do(int option, int init)
42929088Smarkm{
43029088Smarkm	if (init) {
43129088Smarkm		if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
43229088Smarkm		    his_want_state_is_will(option))
43329088Smarkm			return;
43429088Smarkm		/*
43529088Smarkm		 * Special case for TELOPT_TM:  We send a DO, but pretend
43629088Smarkm		 * that we sent a DONT, so that we can send more DOs if
43729088Smarkm		 * we want to.
43829088Smarkm		 */
43929088Smarkm		if (option == TELOPT_TM)
44029088Smarkm			set_his_want_state_wont(option);
44129088Smarkm		else
44229088Smarkm			set_his_want_state_will(option);
44329088Smarkm		do_dont_resp[option]++;
44429088Smarkm	}
44579981Sru	output_data((const char *)doopt, option);
44629088Smarkm
44729088Smarkm	DIAG(TD_OPTIONS, printoption("td: send do", option));
44829088Smarkm}
44929088Smarkm
45087139Smarkmvoid
45187139Smarkmwilloption(int option)
45229088Smarkm{
45329088Smarkm	int changeok = 0;
45487139Smarkm	void (*func)(void) = 0;
45529088Smarkm
45629088Smarkm	/*
45729088Smarkm	 * process input from peer.
45829088Smarkm	 */
45929088Smarkm
46029088Smarkm	DIAG(TD_OPTIONS, printoption("td: recv will", option));
46129088Smarkm
46229088Smarkm	if (do_dont_resp[option]) {
46329088Smarkm		do_dont_resp[option]--;
46429088Smarkm		if (do_dont_resp[option] && his_state_is_will(option))
46529088Smarkm			do_dont_resp[option]--;
46629088Smarkm	}
46729088Smarkm	if (do_dont_resp[option] == 0) {
46829088Smarkm	    if (his_want_state_is_wont(option)) {
46929088Smarkm		switch (option) {
47029088Smarkm
47129088Smarkm		case TELOPT_BINARY:
47229088Smarkm			init_termbuf();
47329088Smarkm			tty_binaryin(1);
47429088Smarkm			set_termbuf();
47529088Smarkm			changeok++;
47629088Smarkm			break;
47729088Smarkm
47829088Smarkm		case TELOPT_ECHO:
47929088Smarkm			/*
48029088Smarkm			 * See comments below for more info.
48129088Smarkm			 */
48229088Smarkm			not42 = 0;	/* looks like a 4.2 system */
48329088Smarkm			break;
48429088Smarkm
48529088Smarkm		case TELOPT_TM:
48629088Smarkm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
48729088Smarkm			/*
48829088Smarkm			 * This telnetd implementation does not really
48929088Smarkm			 * support timing marks, it just uses them to
49029088Smarkm			 * support the kludge linemode stuff.  If we
49129088Smarkm			 * receive a will or wont TM in response to our
49229088Smarkm			 * do TM request that may have been sent to
49329088Smarkm			 * determine kludge linemode support, process
49429088Smarkm			 * it, otherwise TM should get a negative
49529088Smarkm			 * response back.
49629088Smarkm			 */
49729088Smarkm			/*
49829088Smarkm			 * Handle the linemode kludge stuff.
49929088Smarkm			 * If we are not currently supporting any
50029088Smarkm			 * linemode at all, then we assume that this
50129088Smarkm			 * is the client telling us to use kludge
50229088Smarkm			 * linemode in response to our query.  Set the
50329088Smarkm			 * linemode type that is to be supported, note
50429088Smarkm			 * that the client wishes to use linemode, and
50529088Smarkm			 * eat the will TM as though it never arrived.
50629088Smarkm			 */
50729088Smarkm			if (lmodetype < KLUDGE_LINEMODE) {
50829088Smarkm				lmodetype = KLUDGE_LINEMODE;
50929088Smarkm				clientstat(TELOPT_LINEMODE, WILL, 0);
51029088Smarkm				send_wont(TELOPT_SGA, 1);
51129088Smarkm			} else if (lmodetype == NO_AUTOKLUDGE) {
51229088Smarkm				lmodetype = KLUDGE_OK;
51329088Smarkm			}
51429088Smarkm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
51529088Smarkm			/*
51629088Smarkm			 * We never respond to a WILL TM, and
51729088Smarkm			 * we leave the state WONT.
51829088Smarkm			 */
51929088Smarkm			return;
52029088Smarkm
52129088Smarkm		case TELOPT_LFLOW:
52229088Smarkm			/*
52329088Smarkm			 * If we are going to support flow control
52429088Smarkm			 * option, then don't worry peer that we can't
52529088Smarkm			 * change the flow control characters.
52629088Smarkm			 */
52729088Smarkm			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
52829088Smarkm			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
52929088Smarkm			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
53029088Smarkm			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
53129088Smarkm		case TELOPT_TTYPE:
53229088Smarkm		case TELOPT_SGA:
53329088Smarkm		case TELOPT_NAWS:
53429088Smarkm		case TELOPT_TSPEED:
53529088Smarkm		case TELOPT_XDISPLOC:
53629088Smarkm		case TELOPT_NEW_ENVIRON:
53729088Smarkm		case TELOPT_OLD_ENVIRON:
53829088Smarkm			changeok++;
53929088Smarkm			break;
54029088Smarkm
54129088Smarkm#ifdef	LINEMODE
54229088Smarkm		case TELOPT_LINEMODE:
54329088Smarkm# ifdef	KLUDGELINEMODE
54429088Smarkm			/*
54529088Smarkm			 * Note client's desire to use linemode.
54629088Smarkm			 */
54729088Smarkm			lmodetype = REAL_LINEMODE;
54829088Smarkm# endif	/* KLUDGELINEMODE */
54929088Smarkm			func = doclientstat;
55029088Smarkm			changeok++;
55129088Smarkm			break;
55229088Smarkm#endif	/* LINEMODE */
55329088Smarkm
55429088Smarkm#ifdef	AUTHENTICATION
55529088Smarkm		case TELOPT_AUTHENTICATION:
556180931Sjhb			if (auth_level >= 0) {
557180931Sjhb				func = auth_request;
558180931Sjhb				changeok++;
559180931Sjhb			}
56029088Smarkm			break;
56129088Smarkm#endif
56229088Smarkm
56329088Smarkm#ifdef	ENCRYPTION
56429088Smarkm		case TELOPT_ENCRYPT:
56529088Smarkm			func = encrypt_send_support;
56629088Smarkm			changeok++;
56729088Smarkm			break;
56829088Smarkm#endif	/* ENCRYPTION */
56929088Smarkm
57029088Smarkm		default:
57129088Smarkm			break;
57229088Smarkm		}
57329088Smarkm		if (changeok) {
57429088Smarkm			set_his_want_state_will(option);
57529088Smarkm			send_do(option, 0);
57629088Smarkm		} else {
57729088Smarkm			do_dont_resp[option]++;
57829088Smarkm			send_dont(option, 0);
57929088Smarkm		}
58029088Smarkm	    } else {
58129088Smarkm		/*
58229088Smarkm		 * Option processing that should happen when
58329088Smarkm		 * we receive conformation of a change in
58429088Smarkm		 * state that we had requested.
58529088Smarkm		 */
58629088Smarkm		switch (option) {
58729088Smarkm		case TELOPT_ECHO:
58829088Smarkm			not42 = 0;	/* looks like a 4.2 system */
58929088Smarkm			/*
59029088Smarkm			 * Egads, he responded "WILL ECHO".  Turn
59129088Smarkm			 * it off right now!
59229088Smarkm			 */
59329088Smarkm			send_dont(option, 1);
59429088Smarkm			/*
59529088Smarkm			 * "WILL ECHO".  Kludge upon kludge!
59629088Smarkm			 * A 4.2 client is now echoing user input at
59729088Smarkm			 * the tty.  This is probably undesireable and
59829088Smarkm			 * it should be stopped.  The client will
59929088Smarkm			 * respond WONT TM to the DO TM that we send to
60029088Smarkm			 * check for kludge linemode.  When the WONT TM
60129088Smarkm			 * arrives, linemode will be turned off and a
60229088Smarkm			 * change propogated to the pty.  This change
60329088Smarkm			 * will cause us to process the new pty state
60429088Smarkm			 * in localstat(), which will notice that
60529088Smarkm			 * linemode is off and send a WILL ECHO
60629088Smarkm			 * so that we are properly in character mode and
60729088Smarkm			 * all is well.
60829088Smarkm			 */
60929088Smarkm			break;
61029088Smarkm#ifdef	LINEMODE
61129088Smarkm		case TELOPT_LINEMODE:
61229088Smarkm# ifdef	KLUDGELINEMODE
61329088Smarkm			/*
61429088Smarkm			 * Note client's desire to use linemode.
61529088Smarkm			 */
61629088Smarkm			lmodetype = REAL_LINEMODE;
61729088Smarkm# endif	/* KLUDGELINEMODE */
61829088Smarkm			func = doclientstat;
61929088Smarkm			break;
62029088Smarkm#endif	/* LINEMODE */
62129088Smarkm
62229088Smarkm#ifdef	AUTHENTICATION
62329088Smarkm		case TELOPT_AUTHENTICATION:
62429088Smarkm			func = auth_request;
62529088Smarkm			break;
62629088Smarkm#endif
62729088Smarkm
62829088Smarkm#ifdef	ENCRYPTION
62929088Smarkm		case TELOPT_ENCRYPT:
63029088Smarkm			func = encrypt_send_support;
63129088Smarkm			break;
63229088Smarkm#endif	/* ENCRYPTION */
63329088Smarkm		case TELOPT_LFLOW:
63429088Smarkm			func = flowstat;
63529088Smarkm			break;
63629088Smarkm		}
63729088Smarkm	    }
63829088Smarkm	}
63929088Smarkm	set_his_state_will(option);
64029088Smarkm	if (func)
64129088Smarkm		(*func)();
64229088Smarkm}  /* end of willoption */
64329088Smarkm
64487139Smarkmvoid
64587139Smarkmsend_dont(int option, int init)
64629088Smarkm{
64729088Smarkm	if (init) {
64829088Smarkm		if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
64929088Smarkm		    his_want_state_is_wont(option))
65029088Smarkm			return;
65129088Smarkm		set_his_want_state_wont(option);
65229088Smarkm		do_dont_resp[option]++;
65329088Smarkm	}
65479981Sru	output_data((const char *)dont, option);
65529088Smarkm
65629088Smarkm	DIAG(TD_OPTIONS, printoption("td: send dont", option));
65729088Smarkm}
65829088Smarkm
65987139Smarkmvoid
66087139Smarkmwontoption(int option)
66129088Smarkm{
66229088Smarkm	/*
66329088Smarkm	 * Process client input.
66429088Smarkm	 */
66529088Smarkm
66629088Smarkm	DIAG(TD_OPTIONS, printoption("td: recv wont", option));
66729088Smarkm
66829088Smarkm	if (do_dont_resp[option]) {
66929088Smarkm		do_dont_resp[option]--;
67029088Smarkm		if (do_dont_resp[option] && his_state_is_wont(option))
67129088Smarkm			do_dont_resp[option]--;
67229088Smarkm	}
67329088Smarkm	if (do_dont_resp[option] == 0) {
67429088Smarkm	    if (his_want_state_is_will(option)) {
67529088Smarkm		/* it is always ok to change to negative state */
67629088Smarkm		switch (option) {
67729088Smarkm		case TELOPT_ECHO:
67829088Smarkm			not42 = 1; /* doesn't seem to be a 4.2 system */
67929088Smarkm			break;
68029088Smarkm
68129088Smarkm		case TELOPT_BINARY:
68229088Smarkm			init_termbuf();
68329088Smarkm			tty_binaryin(0);
68429088Smarkm			set_termbuf();
68529088Smarkm			break;
68629088Smarkm
68729088Smarkm#ifdef	LINEMODE
68829088Smarkm		case TELOPT_LINEMODE:
68929088Smarkm# ifdef	KLUDGELINEMODE
69029088Smarkm			/*
69129088Smarkm			 * If real linemode is supported, then client is
69229088Smarkm			 * asking to turn linemode off.
69329088Smarkm			 */
69429088Smarkm			if (lmodetype != REAL_LINEMODE)
69529088Smarkm				break;
69681965Smarkm			lmodetype = KLUDGE_LINEMODE;
69729088Smarkm# endif	/* KLUDGELINEMODE */
69829088Smarkm			clientstat(TELOPT_LINEMODE, WONT, 0);
69929088Smarkm			break;
70029088Smarkm#endif	/* LINEMODE */
70129088Smarkm
70229088Smarkm		case TELOPT_TM:
70329088Smarkm			/*
70429088Smarkm			 * If we get a WONT TM, and had sent a DO TM,
70529088Smarkm			 * don't respond with a DONT TM, just leave it
70629088Smarkm			 * as is.  Short circut the state machine to
70729088Smarkm			 * achive this.
70829088Smarkm			 */
70929088Smarkm			set_his_want_state_wont(TELOPT_TM);
71029088Smarkm			return;
71129088Smarkm
71229088Smarkm		case TELOPT_LFLOW:
71329088Smarkm			/*
71429088Smarkm			 * If we are not going to support flow control
71529088Smarkm			 * option, then let peer know that we can't
71629088Smarkm			 * change the flow control characters.
71729088Smarkm			 */
71829088Smarkm			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
71929088Smarkm			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
72029088Smarkm			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
72129088Smarkm			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
72229088Smarkm			break;
72329088Smarkm
72487139Smarkm#ifdef	AUTHENTICATION
72529088Smarkm		case TELOPT_AUTHENTICATION:
72629088Smarkm			auth_finished(0, AUTH_REJECT);
72729088Smarkm			break;
72829088Smarkm#endif
72929088Smarkm
73029088Smarkm		/*
73129088Smarkm		 * For options that we might spin waiting for
73229088Smarkm		 * sub-negotiation, if the client turns off the
73329088Smarkm		 * option rather than responding to the request,
73429088Smarkm		 * we have to treat it here as if we got a response
73529088Smarkm		 * to the sub-negotiation, (by updating the timers)
73629088Smarkm		 * so that we'll break out of the loop.
73729088Smarkm		 */
73829088Smarkm		case TELOPT_TTYPE:
73929088Smarkm			settimer(ttypesubopt);
74029088Smarkm			break;
74129088Smarkm
74229088Smarkm		case TELOPT_TSPEED:
74329088Smarkm			settimer(tspeedsubopt);
74429088Smarkm			break;
74529088Smarkm
74629088Smarkm		case TELOPT_XDISPLOC:
74729088Smarkm			settimer(xdisplocsubopt);
74829088Smarkm			break;
74929088Smarkm
75029088Smarkm		case TELOPT_OLD_ENVIRON:
75129088Smarkm			settimer(oenvironsubopt);
75229088Smarkm			break;
75329088Smarkm
75429088Smarkm		case TELOPT_NEW_ENVIRON:
75529088Smarkm			settimer(environsubopt);
75629088Smarkm			break;
75729088Smarkm
75829088Smarkm		default:
75929088Smarkm			break;
76029088Smarkm		}
76129088Smarkm		set_his_want_state_wont(option);
76229088Smarkm		if (his_state_is_will(option))
76329088Smarkm			send_dont(option, 0);
76429088Smarkm	    } else {
76529088Smarkm		switch (option) {
76629088Smarkm		case TELOPT_TM:
76729088Smarkm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
76829088Smarkm			if (lmodetype < NO_AUTOKLUDGE) {
76929088Smarkm				lmodetype = NO_LINEMODE;
77029088Smarkm				clientstat(TELOPT_LINEMODE, WONT, 0);
77129088Smarkm				send_will(TELOPT_SGA, 1);
77229088Smarkm				send_will(TELOPT_ECHO, 1);
77329088Smarkm			}
77429088Smarkm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
77529088Smarkm			break;
77629088Smarkm
77787139Smarkm#ifdef AUTHENTICATION
77829088Smarkm		case TELOPT_AUTHENTICATION:
77929088Smarkm			auth_finished(0, AUTH_REJECT);
78029088Smarkm			break;
78129088Smarkm#endif
78229088Smarkm		default:
78329088Smarkm			break;
78429088Smarkm		}
78529088Smarkm	    }
78629088Smarkm	}
78729088Smarkm	set_his_state_wont(option);
78829088Smarkm
78929088Smarkm}  /* end of wontoption */
79029088Smarkm
79187139Smarkmvoid
79287139Smarkmsend_will(int option, int init)
79329088Smarkm{
79429088Smarkm	if (init) {
79529088Smarkm		if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
79629088Smarkm		    my_want_state_is_will(option))
79729088Smarkm			return;
79829088Smarkm		set_my_want_state_will(option);
79929088Smarkm		will_wont_resp[option]++;
80029088Smarkm	}
80179981Sru	output_data((const char *)will, option);
80229088Smarkm
80329088Smarkm	DIAG(TD_OPTIONS, printoption("td: send will", option));
80429088Smarkm}
80529088Smarkm
80629088Smarkm#if	!defined(LINEMODE) || !defined(KLUDGELINEMODE)
80729088Smarkm/*
80829088Smarkm * When we get a DONT SGA, we will try once to turn it
80929088Smarkm * back on.  If the other side responds DONT SGA, we
81029088Smarkm * leave it at that.  This is so that when we talk to
81129088Smarkm * clients that understand KLUDGELINEMODE but not LINEMODE,
81229088Smarkm * we'll keep them in char-at-a-time mode.
81329088Smarkm */
81429088Smarkmint turn_on_sga = 0;
81529088Smarkm#endif
81629088Smarkm
81787139Smarkmvoid
81887139Smarkmdooption(int option)
81929088Smarkm{
82029088Smarkm	int changeok = 0;
82129088Smarkm
82229088Smarkm	/*
82329088Smarkm	 * Process client input.
82429088Smarkm	 */
82529088Smarkm
82629088Smarkm	DIAG(TD_OPTIONS, printoption("td: recv do", option));
82729088Smarkm
82829088Smarkm	if (will_wont_resp[option]) {
82929088Smarkm		will_wont_resp[option]--;
83029088Smarkm		if (will_wont_resp[option] && my_state_is_will(option))
83129088Smarkm			will_wont_resp[option]--;
83229088Smarkm	}
83329088Smarkm	if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
83429088Smarkm		switch (option) {
83529088Smarkm		case TELOPT_ECHO:
83629088Smarkm#ifdef	LINEMODE
83729088Smarkm# ifdef	KLUDGELINEMODE
83829088Smarkm			if (lmodetype == NO_LINEMODE)
83929088Smarkm# else
84029088Smarkm			if (his_state_is_wont(TELOPT_LINEMODE))
84129088Smarkm# endif
84229088Smarkm#endif
84329088Smarkm			{
84429088Smarkm				init_termbuf();
84529088Smarkm				tty_setecho(1);
84629088Smarkm				set_termbuf();
84729088Smarkm			}
84829088Smarkm			changeok++;
84929088Smarkm			break;
85029088Smarkm
85129088Smarkm		case TELOPT_BINARY:
85229088Smarkm			init_termbuf();
85329088Smarkm			tty_binaryout(1);
85429088Smarkm			set_termbuf();
85529088Smarkm			changeok++;
85629088Smarkm			break;
85729088Smarkm
85829088Smarkm		case TELOPT_SGA:
85929088Smarkm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
86029088Smarkm			/*
86129088Smarkm			 * If kludge linemode is in use, then we must
86229088Smarkm			 * process an incoming do SGA for linemode
86329088Smarkm			 * purposes.
86429088Smarkm			 */
86529088Smarkm			if (lmodetype == KLUDGE_LINEMODE) {
86629088Smarkm				/*
86729088Smarkm				 * Receipt of "do SGA" in kludge
86829088Smarkm				 * linemode is the peer asking us to
86929088Smarkm				 * turn off linemode.  Make note of
87029088Smarkm				 * the request.
87129088Smarkm				 */
87229088Smarkm				clientstat(TELOPT_LINEMODE, WONT, 0);
87329088Smarkm				/*
87429088Smarkm				 * If linemode did not get turned off
87529088Smarkm				 * then don't tell peer that we did.
87629088Smarkm				 * Breaking here forces a wont SGA to
87729088Smarkm				 * be returned.
87829088Smarkm				 */
87929088Smarkm				if (linemode)
88029088Smarkm					break;
88129088Smarkm			}
88229088Smarkm#else
88329088Smarkm			turn_on_sga = 0;
88429088Smarkm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
88529088Smarkm			changeok++;
88629088Smarkm			break;
88729088Smarkm
88829088Smarkm		case TELOPT_STATUS:
88929088Smarkm			changeok++;
89029088Smarkm			break;
89129088Smarkm
89229088Smarkm		case TELOPT_TM:
89329088Smarkm			/*
89429088Smarkm			 * Special case for TM.  We send a WILL, but
89529088Smarkm			 * pretend we sent a WONT.
89629088Smarkm			 */
89729088Smarkm			send_will(option, 0);
89829088Smarkm			set_my_want_state_wont(option);
89929088Smarkm			set_my_state_wont(option);
90029088Smarkm			return;
90129088Smarkm
90229088Smarkm		case TELOPT_LOGOUT:
90329088Smarkm			/*
90429088Smarkm			 * When we get a LOGOUT option, respond
90529088Smarkm			 * with a WILL LOGOUT, make sure that
90629088Smarkm			 * it gets written out to the network,
90729088Smarkm			 * and then just go away...
90829088Smarkm			 */
90929088Smarkm			set_my_want_state_will(TELOPT_LOGOUT);
91029088Smarkm			send_will(TELOPT_LOGOUT, 0);
91129088Smarkm			set_my_state_will(TELOPT_LOGOUT);
91229088Smarkm			(void)netflush();
91329088Smarkm			cleanup(0);
91429088Smarkm			/* NOT REACHED */
91529088Smarkm			break;
91629088Smarkm
91729088Smarkm#ifdef	ENCRYPTION
91829088Smarkm		case TELOPT_ENCRYPT:
91929088Smarkm			changeok++;
92029088Smarkm			break;
92129088Smarkm#endif	/* ENCRYPTION */
92229088Smarkm		case TELOPT_LINEMODE:
92329088Smarkm		case TELOPT_TTYPE:
92429088Smarkm		case TELOPT_NAWS:
92529088Smarkm		case TELOPT_TSPEED:
92629088Smarkm		case TELOPT_LFLOW:
92729088Smarkm		case TELOPT_XDISPLOC:
92829088Smarkm#ifdef	TELOPT_ENVIRON
92929088Smarkm		case TELOPT_NEW_ENVIRON:
93029088Smarkm#endif
93129088Smarkm		case TELOPT_OLD_ENVIRON:
93229088Smarkm		default:
93329088Smarkm			break;
93429088Smarkm		}
93529088Smarkm		if (changeok) {
93629088Smarkm			set_my_want_state_will(option);
93729088Smarkm			send_will(option, 0);
93829088Smarkm		} else {
93929088Smarkm			will_wont_resp[option]++;
94029088Smarkm			send_wont(option, 0);
94129088Smarkm		}
94229088Smarkm	}
94329088Smarkm	set_my_state_will(option);
94429088Smarkm
94529088Smarkm}  /* end of dooption */
94629088Smarkm
94787139Smarkmvoid
94887139Smarkmsend_wont(int option, int init)
94929088Smarkm{
95029088Smarkm	if (init) {
95129088Smarkm		if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
95229088Smarkm		    my_want_state_is_wont(option))
95329088Smarkm			return;
95429088Smarkm		set_my_want_state_wont(option);
95529088Smarkm		will_wont_resp[option]++;
95629088Smarkm	}
95779981Sru	output_data((const char *)wont, option);
95829088Smarkm
95929088Smarkm	DIAG(TD_OPTIONS, printoption("td: send wont", option));
96029088Smarkm}
96129088Smarkm
96287139Smarkmvoid
96387139Smarkmdontoption(int option)
96429088Smarkm{
96529088Smarkm	/*
96629088Smarkm	 * Process client input.
96729088Smarkm	 */
96829088Smarkm
96929088Smarkm
97029088Smarkm	DIAG(TD_OPTIONS, printoption("td: recv dont", option));
97129088Smarkm
97229088Smarkm	if (will_wont_resp[option]) {
97329088Smarkm		will_wont_resp[option]--;
97429088Smarkm		if (will_wont_resp[option] && my_state_is_wont(option))
97529088Smarkm			will_wont_resp[option]--;
97629088Smarkm	}
97729088Smarkm	if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
97829088Smarkm		switch (option) {
97929088Smarkm		case TELOPT_BINARY:
98029088Smarkm			init_termbuf();
98129088Smarkm			tty_binaryout(0);
98229088Smarkm			set_termbuf();
98329088Smarkm			break;
98429088Smarkm
98529088Smarkm		case TELOPT_ECHO:	/* we should stop echoing */
98629088Smarkm#ifdef	LINEMODE
98729088Smarkm# ifdef	KLUDGELINEMODE
98829088Smarkm			if ((lmodetype != REAL_LINEMODE) &&
98929088Smarkm			    (lmodetype != KLUDGE_LINEMODE))
99029088Smarkm# else
99129088Smarkm			if (his_state_is_wont(TELOPT_LINEMODE))
99229088Smarkm# endif
99329088Smarkm#endif
99429088Smarkm			{
99529088Smarkm				init_termbuf();
99629088Smarkm				tty_setecho(0);
99729088Smarkm				set_termbuf();
99829088Smarkm			}
99929088Smarkm			break;
100029088Smarkm
100129088Smarkm		case TELOPT_SGA:
100229088Smarkm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
100329088Smarkm			/*
100429088Smarkm			 * If kludge linemode is in use, then we
100529088Smarkm			 * must process an incoming do SGA for
100629088Smarkm			 * linemode purposes.
100729088Smarkm			 */
100829088Smarkm			if ((lmodetype == KLUDGE_LINEMODE) ||
100929088Smarkm			    (lmodetype == KLUDGE_OK)) {
101029088Smarkm				/*
101129088Smarkm				 * The client is asking us to turn
101229088Smarkm				 * linemode on.
101329088Smarkm				 */
101429088Smarkm				lmodetype = KLUDGE_LINEMODE;
101529088Smarkm				clientstat(TELOPT_LINEMODE, WILL, 0);
101629088Smarkm				/*
101729088Smarkm				 * If we did not turn line mode on,
101829088Smarkm				 * then what do we say?  Will SGA?
101929088Smarkm				 * This violates design of telnet.
102029088Smarkm				 * Gross.  Very Gross.
102129088Smarkm				 */
102229088Smarkm			}
102329088Smarkm			break;
102429088Smarkm#else
102529088Smarkm			set_my_want_state_wont(option);
102629088Smarkm			if (my_state_is_will(option))
102729088Smarkm				send_wont(option, 0);
102829088Smarkm			set_my_state_wont(option);
102929088Smarkm			if (turn_on_sga ^= 1)
103029088Smarkm				send_will(option, 1);
103129088Smarkm			return;
103229088Smarkm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
103329088Smarkm
103429088Smarkm		default:
103529088Smarkm			break;
103629088Smarkm		}
103729088Smarkm
103829088Smarkm		set_my_want_state_wont(option);
103929088Smarkm		if (my_state_is_will(option))
104029088Smarkm			send_wont(option, 0);
104129088Smarkm	}
104229088Smarkm	set_my_state_wont(option);
104329088Smarkm
104429088Smarkm}  /* end of dontoption */
104529088Smarkm
104629088Smarkm#ifdef	ENV_HACK
104729088Smarkmint env_ovar = -1;
104829088Smarkmint env_ovalue = -1;
104929088Smarkm#else	/* ENV_HACK */
105029088Smarkm# define env_ovar OLD_ENV_VAR
105129088Smarkm# define env_ovalue OLD_ENV_VALUE
105229088Smarkm#endif	/* ENV_HACK */
105329088Smarkm
105429088Smarkm/*
105529088Smarkm * suboption()
105629088Smarkm *
105729088Smarkm *	Look at the sub-option buffer, and try to be helpful to the other
105829088Smarkm * side.
105929088Smarkm *
106029088Smarkm *	Currently we recognize:
106129088Smarkm *
106229088Smarkm *	Terminal type is
106329088Smarkm *	Linemode
106429088Smarkm *	Window size
106529088Smarkm *	Terminal speed
106629088Smarkm */
106787139Smarkmvoid
106887139Smarkmsuboption(void)
106929088Smarkm{
107087139Smarkm    int subchar;
107129088Smarkm
107229088Smarkm    DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
107329088Smarkm
107429088Smarkm    subchar = SB_GET();
107529088Smarkm    switch (subchar) {
107629088Smarkm    case TELOPT_TSPEED: {
107787139Smarkm	int xspeed, rspeed;
107829088Smarkm
107929088Smarkm	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
108029088Smarkm		break;
108129088Smarkm
108229088Smarkm	settimer(tspeedsubopt);
108329088Smarkm
108429088Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_IS)
108529088Smarkm		return;
108629088Smarkm
108729088Smarkm	xspeed = atoi((char *)subpointer);
108829088Smarkm
108929088Smarkm	while (SB_GET() != ',' && !SB_EOF());
109029088Smarkm	if (SB_EOF())
109129088Smarkm		return;
109229088Smarkm
109329088Smarkm	rspeed = atoi((char *)subpointer);
109429088Smarkm	clientstat(TELOPT_TSPEED, xspeed, rspeed);
109529088Smarkm
109629088Smarkm	break;
109729088Smarkm
109829088Smarkm    }  /* end of case TELOPT_TSPEED */
109929088Smarkm
110029088Smarkm    case TELOPT_TTYPE: {		/* Yaaaay! */
110129088Smarkm	static char terminalname[41];
110229088Smarkm
110329088Smarkm	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
110429088Smarkm		break;
110529088Smarkm	settimer(ttypesubopt);
110629088Smarkm
110729088Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_IS) {
110829088Smarkm	    return;		/* ??? XXX but, this is the most robust */
110929088Smarkm	}
111029088Smarkm
111129088Smarkm	terminaltype = terminalname;
111229088Smarkm
111329088Smarkm	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
111429088Smarkm								    !SB_EOF()) {
111587139Smarkm	    int c;
111629088Smarkm
111729088Smarkm	    c = SB_GET();
111829088Smarkm	    if (isupper(c)) {
111929088Smarkm		c = tolower(c);
112029088Smarkm	    }
112129088Smarkm	    *terminaltype++ = c;    /* accumulate name */
112229088Smarkm	}
112329088Smarkm	*terminaltype = 0;
112429088Smarkm	terminaltype = terminalname;
112529088Smarkm	break;
112629088Smarkm    }  /* end of case TELOPT_TTYPE */
112729088Smarkm
112829088Smarkm    case TELOPT_NAWS: {
112987139Smarkm	int xwinsize, ywinsize;
113029088Smarkm
113129088Smarkm	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
113229088Smarkm		break;
113329088Smarkm
113429088Smarkm	if (SB_EOF())
113529088Smarkm		return;
113629088Smarkm	xwinsize = SB_GET() << 8;
113729088Smarkm	if (SB_EOF())
113829088Smarkm		return;
113929088Smarkm	xwinsize |= SB_GET();
114029088Smarkm	if (SB_EOF())
114129088Smarkm		return;
114229088Smarkm	ywinsize = SB_GET() << 8;
114329088Smarkm	if (SB_EOF())
114429088Smarkm		return;
114529088Smarkm	ywinsize |= SB_GET();
114629088Smarkm	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
114729088Smarkm
114829088Smarkm	break;
114929088Smarkm
115029088Smarkm    }  /* end of case TELOPT_NAWS */
115129088Smarkm
115229088Smarkm#ifdef	LINEMODE
115329088Smarkm    case TELOPT_LINEMODE: {
115487139Smarkm	int request;
115529088Smarkm
115629088Smarkm	if (his_state_is_wont(TELOPT_LINEMODE))	/* Ignore if option disabled */
115729088Smarkm		break;
115829088Smarkm	/*
115929088Smarkm	 * Process linemode suboptions.
116029088Smarkm	 */
116129088Smarkm	if (SB_EOF())
116229088Smarkm	    break;		/* garbage was sent */
116329088Smarkm	request = SB_GET();	/* get will/wont */
116429088Smarkm
116529088Smarkm	if (SB_EOF())
116629088Smarkm	    break;		/* another garbage check */
116729088Smarkm
116829088Smarkm	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
116929088Smarkm		/*
117029088Smarkm		 * Process suboption buffer of slc's
117129088Smarkm		 */
117229088Smarkm		start_slc(1);
117329088Smarkm		do_opt_slc(subpointer, subend - subpointer);
117429088Smarkm		(void) end_slc(0);
117529088Smarkm		break;
117629088Smarkm	} else if (request == LM_MODE) {
117729088Smarkm		if (SB_EOF())
117829088Smarkm		    return;
117929088Smarkm		useeditmode = SB_GET();  /* get mode flag */
118029088Smarkm		clientstat(LM_MODE, 0, 0);
118129088Smarkm		break;
118229088Smarkm	}
118329088Smarkm
118429088Smarkm	if (SB_EOF())
118529088Smarkm	    break;
118629088Smarkm	switch (SB_GET()) {  /* what suboption? */
118729088Smarkm	case LM_FORWARDMASK:
118829088Smarkm		/*
118929088Smarkm		 * According to spec, only server can send request for
119029088Smarkm		 * forwardmask, and client can only return a positive response.
119129088Smarkm		 * So don't worry about it.
119229088Smarkm		 */
119329088Smarkm
119429088Smarkm	default:
119529088Smarkm		break;
119629088Smarkm	}
119729088Smarkm	break;
119829088Smarkm    }  /* end of case TELOPT_LINEMODE */
119929088Smarkm#endif
120029088Smarkm    case TELOPT_STATUS: {
120129088Smarkm	int mode;
120229088Smarkm
120329088Smarkm	if (SB_EOF())
120429088Smarkm	    break;
120529088Smarkm	mode = SB_GET();
120629088Smarkm	switch (mode) {
120729088Smarkm	case TELQUAL_SEND:
120829088Smarkm	    if (my_state_is_will(TELOPT_STATUS))
120929088Smarkm		send_status();
121029088Smarkm	    break;
121129088Smarkm
121229088Smarkm	case TELQUAL_IS:
121329088Smarkm	    break;
121429088Smarkm
121529088Smarkm	default:
121629088Smarkm	    break;
121729088Smarkm	}
121829088Smarkm	break;
121929088Smarkm    }  /* end of case TELOPT_STATUS */
122029088Smarkm
122129088Smarkm    case TELOPT_XDISPLOC: {
122229088Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_IS)
122329088Smarkm		return;
122429088Smarkm	settimer(xdisplocsubopt);
122529088Smarkm	subpointer[SB_LEN()] = '\0';
122629088Smarkm	(void)setenv("DISPLAY", (char *)subpointer, 1);
122729088Smarkm	break;
122829088Smarkm    }  /* end of case TELOPT_XDISPLOC */
122929088Smarkm
123029088Smarkm#ifdef	TELOPT_NEW_ENVIRON
123129088Smarkm    case TELOPT_NEW_ENVIRON:
123229088Smarkm#endif
123329088Smarkm    case TELOPT_OLD_ENVIRON: {
123487139Smarkm	int c;
123587139Smarkm	char *cp, *varp, *valp;
123629088Smarkm
123729088Smarkm	if (SB_EOF())
123829088Smarkm		return;
123929088Smarkm	c = SB_GET();
124029088Smarkm	if (c == TELQUAL_IS) {
124129088Smarkm		if (subchar == TELOPT_OLD_ENVIRON)
124229088Smarkm			settimer(oenvironsubopt);
124329088Smarkm		else
124429088Smarkm			settimer(environsubopt);
124529088Smarkm	} else if (c != TELQUAL_INFO) {
124629088Smarkm		return;
124729088Smarkm	}
124829088Smarkm
124929088Smarkm#ifdef	TELOPT_NEW_ENVIRON
125029088Smarkm	if (subchar == TELOPT_NEW_ENVIRON) {
125129088Smarkm	    while (!SB_EOF()) {
125229088Smarkm		c = SB_GET();
125329088Smarkm		if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
125429088Smarkm			break;
125529088Smarkm	    }
125629088Smarkm	} else
125729088Smarkm#endif
125829088Smarkm	{
125929088Smarkm#ifdef	ENV_HACK
126029088Smarkm	    /*
126129088Smarkm	     * We only want to do this if we haven't already decided
126229088Smarkm	     * whether or not the other side has its VALUE and VAR
126329088Smarkm	     * reversed.
126429088Smarkm	     */
126529088Smarkm	    if (env_ovar < 0) {
126687139Smarkm		int last = -1;		/* invalid value */
126729088Smarkm		int empty = 0;
126829088Smarkm		int got_var = 0, got_value = 0, got_uservar = 0;
126929088Smarkm
127029088Smarkm		/*
127129088Smarkm		 * The other side might have its VALUE and VAR values
127229088Smarkm		 * reversed.  To be interoperable, we need to determine
127329088Smarkm		 * which way it is.  If the first recognized character
127429088Smarkm		 * is a VAR or VALUE, then that will tell us what
127529088Smarkm		 * type of client it is.  If the fist recognized
127629088Smarkm		 * character is a USERVAR, then we continue scanning
127729088Smarkm		 * the suboption looking for two consecutive
127829088Smarkm		 * VAR or VALUE fields.  We should not get two
127929088Smarkm		 * consecutive VALUE fields, so finding two
128029088Smarkm		 * consecutive VALUE or VAR fields will tell us
128129088Smarkm		 * what the client is.
128229088Smarkm		 */
128329088Smarkm		SB_SAVE();
128429088Smarkm		while (!SB_EOF()) {
128529088Smarkm			c = SB_GET();
128629088Smarkm			switch(c) {
128729088Smarkm			case OLD_ENV_VAR:
128829088Smarkm				if (last < 0 || last == OLD_ENV_VAR
128929088Smarkm				    || (empty && (last == OLD_ENV_VALUE)))
129029088Smarkm					goto env_ovar_ok;
129129088Smarkm				got_var++;
129229088Smarkm				last = OLD_ENV_VAR;
129329088Smarkm				break;
129429088Smarkm			case OLD_ENV_VALUE:
129529088Smarkm				if (last < 0 || last == OLD_ENV_VALUE
129629088Smarkm				    || (empty && (last == OLD_ENV_VAR)))
129729088Smarkm					goto env_ovar_wrong;
129829088Smarkm				got_value++;
129929088Smarkm				last = OLD_ENV_VALUE;
130029088Smarkm				break;
130129088Smarkm			case ENV_USERVAR:
130229088Smarkm				/* count strings of USERVAR as one */
130329088Smarkm				if (last != ENV_USERVAR)
130429088Smarkm					got_uservar++;
130529088Smarkm				if (empty) {
130629088Smarkm					if (last == OLD_ENV_VALUE)
130729088Smarkm						goto env_ovar_ok;
130829088Smarkm					if (last == OLD_ENV_VAR)
130929088Smarkm						goto env_ovar_wrong;
131029088Smarkm				}
131129088Smarkm				last = ENV_USERVAR;
131229088Smarkm				break;
131329088Smarkm			case ENV_ESC:
131429088Smarkm				if (!SB_EOF())
131529088Smarkm					c = SB_GET();
1316103956Smarkm				/* FALLTHROUGH */
131729088Smarkm			default:
131829088Smarkm				empty = 0;
131929088Smarkm				continue;
132029088Smarkm			}
132129088Smarkm			empty = 1;
132229088Smarkm		}
132329088Smarkm		if (empty) {
132429088Smarkm			if (last == OLD_ENV_VALUE)
132529088Smarkm				goto env_ovar_ok;
132629088Smarkm			if (last == OLD_ENV_VAR)
132729088Smarkm				goto env_ovar_wrong;
132829088Smarkm		}
132929088Smarkm		/*
133029088Smarkm		 * Ok, the first thing was a USERVAR, and there
133129088Smarkm		 * are not two consecutive VAR or VALUE commands,
133229088Smarkm		 * and none of the VAR or VALUE commands are empty.
133329088Smarkm		 * If the client has sent us a well-formed option,
133429088Smarkm		 * then the number of VALUEs received should always
133529088Smarkm		 * be less than or equal to the number of VARs and
133629088Smarkm		 * USERVARs received.
133729088Smarkm		 *
133829088Smarkm		 * If we got exactly as many VALUEs as VARs and
133929088Smarkm		 * USERVARs, the client has the same definitions.
134029088Smarkm		 *
134129088Smarkm		 * If we got exactly as many VARs as VALUEs and
134229088Smarkm		 * USERVARS, the client has reversed definitions.
134329088Smarkm		 */
134429088Smarkm		if (got_uservar + got_var == got_value) {
134529088Smarkm	    env_ovar_ok:
134629088Smarkm			env_ovar = OLD_ENV_VAR;
134729088Smarkm			env_ovalue = OLD_ENV_VALUE;
134829088Smarkm		} else if (got_uservar + got_value == got_var) {
134929088Smarkm	    env_ovar_wrong:
135029088Smarkm			env_ovar = OLD_ENV_VALUE;
135129088Smarkm			env_ovalue = OLD_ENV_VAR;
135279981Sru			DIAG(TD_OPTIONS,
135379981Sru			    output_data("ENVIRON VALUE and VAR are reversed!\r\n"));
135429088Smarkm
135529088Smarkm		}
135629088Smarkm	    }
135729088Smarkm	    SB_RESTORE();
135829088Smarkm#endif
135929088Smarkm
136029088Smarkm	    while (!SB_EOF()) {
136129088Smarkm		c = SB_GET();
136229088Smarkm		if ((c == env_ovar) || (c == ENV_USERVAR))
136329088Smarkm			break;
136429088Smarkm	    }
136529088Smarkm	}
136629088Smarkm
136729088Smarkm	if (SB_EOF())
136829088Smarkm		return;
136929088Smarkm
137029088Smarkm	cp = varp = (char *)subpointer;
137129088Smarkm	valp = 0;
137229088Smarkm
137329088Smarkm	while (!SB_EOF()) {
137429088Smarkm		c = SB_GET();
137529088Smarkm		if (subchar == TELOPT_OLD_ENVIRON) {
137629088Smarkm			if (c == env_ovar)
137729088Smarkm				c = NEW_ENV_VAR;
137829088Smarkm			else if (c == env_ovalue)
137929088Smarkm				c = NEW_ENV_VALUE;
138029088Smarkm		}
138129088Smarkm		switch (c) {
138229088Smarkm
138329088Smarkm		case NEW_ENV_VALUE:
138429088Smarkm			*cp = '\0';
138529088Smarkm			cp = valp = (char *)subpointer;
138629088Smarkm			break;
138729088Smarkm
138829088Smarkm		case NEW_ENV_VAR:
138929088Smarkm		case ENV_USERVAR:
139029088Smarkm			*cp = '\0';
139129088Smarkm			if (valp)
139229088Smarkm				(void)setenv(varp, valp, 1);
139329088Smarkm			else
139429088Smarkm				unsetenv(varp);
139529088Smarkm			cp = varp = (char *)subpointer;
139629088Smarkm			valp = 0;
139729088Smarkm			break;
139829088Smarkm
139929088Smarkm		case ENV_ESC:
140029088Smarkm			if (SB_EOF())
140129088Smarkm				break;
140229088Smarkm			c = SB_GET();
1403103956Smarkm			/* FALLTHROUGH */
140429088Smarkm		default:
140529088Smarkm			*cp++ = c;
140629088Smarkm			break;
140729088Smarkm		}
140829088Smarkm	}
140929088Smarkm	*cp = '\0';
141029088Smarkm	if (valp)
141129088Smarkm		(void)setenv(varp, valp, 1);
141229088Smarkm	else
141329088Smarkm		unsetenv(varp);
141429088Smarkm	break;
141529088Smarkm    }  /* end of case TELOPT_NEW_ENVIRON */
141687139Smarkm#ifdef	AUTHENTICATION
141729088Smarkm    case TELOPT_AUTHENTICATION:
141829088Smarkm	if (SB_EOF())
141929088Smarkm		break;
142029088Smarkm	switch(SB_GET()) {
142129088Smarkm	case TELQUAL_SEND:
142229088Smarkm	case TELQUAL_REPLY:
142329088Smarkm		/*
142429088Smarkm		 * These are sent by us and cannot be sent by
142529088Smarkm		 * the client.
142629088Smarkm		 */
142729088Smarkm		break;
142829088Smarkm	case TELQUAL_IS:
142929088Smarkm		auth_is(subpointer, SB_LEN());
143029088Smarkm		break;
143129088Smarkm	case TELQUAL_NAME:
143229088Smarkm		auth_name(subpointer, SB_LEN());
143329088Smarkm		break;
143429088Smarkm	}
143529088Smarkm	break;
143629088Smarkm#endif
143729088Smarkm#ifdef	ENCRYPTION
143829088Smarkm    case TELOPT_ENCRYPT:
143929088Smarkm	if (SB_EOF())
144029088Smarkm		break;
144129088Smarkm	switch(SB_GET()) {
144229088Smarkm	case ENCRYPT_SUPPORT:
144329088Smarkm		encrypt_support(subpointer, SB_LEN());
144429088Smarkm		break;
144529088Smarkm	case ENCRYPT_IS:
144629088Smarkm		encrypt_is(subpointer, SB_LEN());
144729088Smarkm		break;
144829088Smarkm	case ENCRYPT_REPLY:
144929088Smarkm		encrypt_reply(subpointer, SB_LEN());
145029088Smarkm		break;
145129088Smarkm	case ENCRYPT_START:
145229088Smarkm		encrypt_start(subpointer, SB_LEN());
145329088Smarkm		break;
145429088Smarkm	case ENCRYPT_END:
145529088Smarkm		encrypt_end();
145629088Smarkm		break;
145729088Smarkm	case ENCRYPT_REQSTART:
145829088Smarkm		encrypt_request_start(subpointer, SB_LEN());
145929088Smarkm		break;
146029088Smarkm	case ENCRYPT_REQEND:
146129088Smarkm		/*
146229088Smarkm		 * We can always send an REQEND so that we cannot
146329088Smarkm		 * get stuck encrypting.  We should only get this
146429088Smarkm		 * if we have been able to get in the correct mode
146529088Smarkm		 * anyhow.
146629088Smarkm		 */
146729088Smarkm		encrypt_request_end();
146829088Smarkm		break;
146929088Smarkm	case ENCRYPT_ENC_KEYID:
147029088Smarkm		encrypt_enc_keyid(subpointer, SB_LEN());
147129088Smarkm		break;
147229088Smarkm	case ENCRYPT_DEC_KEYID:
147329088Smarkm		encrypt_dec_keyid(subpointer, SB_LEN());
147429088Smarkm		break;
147529088Smarkm	default:
147629088Smarkm		break;
147729088Smarkm	}
147829088Smarkm	break;
147929088Smarkm#endif	/* ENCRYPTION */
148029088Smarkm
148129088Smarkm    default:
148229088Smarkm	break;
148329088Smarkm    }  /* end of switch */
148429088Smarkm
148529088Smarkm}  /* end of suboption */
148629088Smarkm
148787139Smarkmstatic void
148887139Smarkmdoclientstat(void)
148929088Smarkm{
149029088Smarkm	clientstat(TELOPT_LINEMODE, WILL, 0);
149129088Smarkm}
149229088Smarkm
149329088Smarkm#define	ADD(c)	 *ncp++ = c
149429088Smarkm#define	ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
149587139Smarkmvoid
149687139Smarkmsend_status(void)
149729088Smarkm{
149829088Smarkm	unsigned char statusbuf[256];
149987139Smarkm	unsigned char *ncp;
150087139Smarkm	unsigned char i;
150129088Smarkm
150229088Smarkm	ncp = statusbuf;
150329088Smarkm
150429088Smarkm	netflush();	/* get rid of anything waiting to go out */
150529088Smarkm
150629088Smarkm	ADD(IAC);
150729088Smarkm	ADD(SB);
150829088Smarkm	ADD(TELOPT_STATUS);
150929088Smarkm	ADD(TELQUAL_IS);
151029088Smarkm
151129088Smarkm	/*
151229088Smarkm	 * We check the want_state rather than the current state,
151329088Smarkm	 * because if we received a DO/WILL for an option that we
151429088Smarkm	 * don't support, and the other side didn't send a DONT/WONT
151529088Smarkm	 * in response to our WONT/DONT, then the "state" will be
151629088Smarkm	 * WILL/DO, and the "want_state" will be WONT/DONT.  We
151729088Smarkm	 * need to go by the latter.
151829088Smarkm	 */
151929088Smarkm	for (i = 0; i < (unsigned char)NTELOPTS; i++) {
152029088Smarkm		if (my_want_state_is_will(i)) {
152129088Smarkm			ADD(WILL);
152229088Smarkm			ADD_DATA(i);
152381965Smarkm			if (i == IAC)
152481965Smarkm				ADD(IAC);
152529088Smarkm		}
152629088Smarkm		if (his_want_state_is_will(i)) {
152729088Smarkm			ADD(DO);
152829088Smarkm			ADD_DATA(i);
152981965Smarkm			if (i == IAC)
153081965Smarkm				ADD(IAC);
153129088Smarkm		}
153229088Smarkm	}
153329088Smarkm
153429088Smarkm	if (his_want_state_is_will(TELOPT_LFLOW)) {
153529088Smarkm		ADD(SB);
153629088Smarkm		ADD(TELOPT_LFLOW);
153729088Smarkm		if (flowmode) {
153829088Smarkm			ADD(LFLOW_ON);
153929088Smarkm		} else {
154029088Smarkm			ADD(LFLOW_OFF);
154129088Smarkm		}
154229088Smarkm		ADD(SE);
154329088Smarkm
154429088Smarkm		if (restartany >= 0) {
154529088Smarkm			ADD(SB);
154629088Smarkm			ADD(TELOPT_LFLOW);
154729088Smarkm			if (restartany) {
154829088Smarkm				ADD(LFLOW_RESTART_ANY);
154929088Smarkm			} else {
155029088Smarkm				ADD(LFLOW_RESTART_XON);
155129088Smarkm			}
155229088Smarkm			ADD(SE);
155329088Smarkm		}
155429088Smarkm	}
155529088Smarkm
155629088Smarkm#ifdef	LINEMODE
155729088Smarkm	if (his_want_state_is_will(TELOPT_LINEMODE)) {
155829088Smarkm		unsigned char *cp, *cpe;
155929088Smarkm		int len;
156029088Smarkm
156129088Smarkm		ADD(SB);
156229088Smarkm		ADD(TELOPT_LINEMODE);
156329088Smarkm		ADD(LM_MODE);
156429088Smarkm		ADD_DATA(editmode);
156529088Smarkm		ADD(SE);
156629088Smarkm
156729088Smarkm		ADD(SB);
156829088Smarkm		ADD(TELOPT_LINEMODE);
156929088Smarkm		ADD(LM_SLC);
157029088Smarkm		start_slc(0);
157129088Smarkm		send_slc();
157229088Smarkm		len = end_slc(&cp);
157329088Smarkm		for (cpe = cp + len; cp < cpe; cp++)
157429088Smarkm			ADD_DATA(*cp);
157529088Smarkm		ADD(SE);
157629088Smarkm	}
157729088Smarkm#endif	/* LINEMODE */
157829088Smarkm
157929088Smarkm	ADD(IAC);
158029088Smarkm	ADD(SE);
158129088Smarkm
158280038Sru	output_datalen(statusbuf, ncp - statusbuf);
158329088Smarkm	netflush();	/* Send it on its way */
158429088Smarkm
158529088Smarkm	DIAG(TD_OPTIONS,
158629088Smarkm		{printsub('>', statusbuf, ncp - statusbuf); netflush();});
158729088Smarkm}
158879981Sru
158979981Sru/*
159079981Sru * This function appends data to nfrontp and advances nfrontp.
159180224Skris * Returns the number of characters written altogether (the
159280224Skris * buffer may have been flushed in the process).
159379981Sru */
159479981Sru
159579981Sruint
159679981Sruoutput_data(const char *format, ...)
159779981Sru{
159879981Sru	va_list args;
159980224Skris	int len;
160080224Skris	char *buf;
160179981Sru
160279981Sru	va_start(args, format);
160380224Skris	if ((len = vasprintf(&buf, format, args)) == -1)
160480224Skris		return -1;
160580224Skris	output_datalen(buf, len);
160679981Sru	va_end(args);
160780224Skris	free(buf);
160880224Skris	return (len);
160979981Sru}
161079981Sru
161180224Skrisvoid
161280224Skrisoutput_datalen(const char *buf, int len)
161379981Sru{
161480224Skris	int remaining, copied;
161580224Skris
161680224Skris	remaining = BUFSIZ - (nfrontp - netobuf);
161780224Skris	while (len > 0) {
161880224Skris		/* Free up enough space if the room is too low*/
161980224Skris		if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
162080224Skris			netflush();
162180224Skris			remaining = BUFSIZ - (nfrontp - netobuf);
162280224Skris		}
162379981Sru
162480224Skris		/* Copy out as much as will fit */
162580224Skris		copied = remaining > len ? len : remaining;
162680224Skris		memmove(nfrontp, buf, copied);
162780224Skris		nfrontp += copied;
162880224Skris		len -= copied;
162980224Skris		remaining -= copied;
163080224Skris		buf += copied;
163179981Sru	}
163280224Skris	return;
163379981Sru}
1634