1/*	$OpenBSD: telnet.c,v 1.37 2024/04/23 13:34:51 jsg Exp $	*/
2/*	$NetBSD: telnet.c,v 1.7 1996/02/28 21:04:15 thorpej Exp $	*/
3
4/*
5 * Copyright (c) 1988, 1990, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "telnet_locl.h"
34
35#include <arpa/telnet.h>
36#include <ctype.h>
37#include <curses.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include <string.h>
41#include <term.h>
42
43#define        strip(x) (eight ? (x) : ((x) & 0x7f))
44
45static unsigned char	subbuffer[SUBBUFSIZE],
46			*subpointer, *subend;	 /* buffer for sub-options */
47#define	SB_CLEAR()	subpointer = subbuffer;
48#define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
49#define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
50				*subpointer++ = (c); \
51			}
52
53#define	SB_GET()	((*subpointer++)&0xff)
54#define	SB_PEEK()	((*subpointer)&0xff)
55#define	SB_EOF()	(subpointer >= subend)
56#define	SB_LEN()	(subend - subpointer)
57
58static void	lm_will(unsigned char *, int);
59static void	lm_wont(unsigned char *, int);
60static void	lm_do(unsigned char *, int);
61static void	lm_dont(unsigned char *, int);
62
63static void	slc_init(void);
64static void	slc_import(int);
65static void	slc_export(void);
66static void	slc_start_reply(void);
67static void	slc_add_reply(unsigned char, unsigned char, cc_t);
68static void	slc_end_reply(void);
69static void	slc(unsigned char *, int);
70static int	slc_update(void);
71
72static void	env_opt(char *, int);
73static void	env_opt_start(void);
74
75char	options[256];		/* The combined options */
76char	do_dont_resp[256];
77char	will_wont_resp[256];
78
79int
80	eight = 3,
81	binary = 0,
82	autologin = 0,	/* Autologin anyone? */
83	skiprc = 0,
84	connections = 0,
85	connected,
86	showoptions,
87	ISend,		/* trying to send network data in */
88	crmod,
89	netdata,	/* Print out network data flow */
90	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
91	telnetport,
92	SYNCHing,	/* we are in TELNET SYNCH mode */
93	flushout,	/* flush output */
94	autoflush = 0,	/* flush output when interrupting? */
95	autosynch,	/* send interrupt characters with SYNCH? */
96	localflow,	/* we handle flow control locally */
97	restartany,	/* if flow control enabled, restart on any character */
98	localchars,	/* we recognize interrupt/quit */
99	donelclchars,	/* the user has set "localchars" */
100	donebinarytoggle,	/* the user has put us in binary */
101	dontlecho,	/* do we suppress local echoing right now? */
102	globalmode,
103	clienteof = 0;
104
105char *prompt = NULL;
106
107int scheduler_lockout_tty = 0;
108
109cc_t escape;
110cc_t rlogin;
111#ifdef	KLUDGELINEMODE
112cc_t echoc;
113#endif
114
115/*
116 * Telnet receiver states for fsm
117 */
118#define	TS_DATA		0
119#define	TS_IAC		1
120#define	TS_WILL		2
121#define	TS_WONT		3
122#define	TS_DO		4
123#define	TS_DONT		5
124#define	TS_CR		6
125#define	TS_SB		7		/* sub-option collection */
126#define	TS_SE		8		/* looking for sub-option end */
127
128static int	telrcv_state;
129# define telopt_environ TELOPT_NEW_ENVIRON
130
131jmp_buf	toplevel = { 0 };
132jmp_buf	peerdied;
133
134int	flushline;
135int	linemode;
136
137#ifdef	KLUDGELINEMODE
138int	kludgelinemode = 1;
139#endif
140
141/*
142 * The following are some clocks used to decide how to interpret
143 * the relationship between various variables.
144 */
145
146Clocks clocks;
147
148
149/*
150 * Initialize telnet environment.
151 */
152
153void
154init_telnet(void)
155{
156    env_init();
157
158    SB_CLEAR();
159    memset(options, 0, sizeof options);
160
161    connected = ISend = localflow = donebinarytoggle = 0;
162    restartany = -1;
163
164    SYNCHing = 0;
165
166    escape = CONTROL(']');
167    rlogin = _POSIX_VDISABLE;
168#ifdef	KLUDGELINEMODE
169    echoc = CONTROL('E');
170#endif
171
172    flushline = 1;
173    telrcv_state = TS_DATA;
174}
175
176
177/*
178 * These routines are in charge of sending option negotiations
179 * to the other side.
180 *
181 * The basic idea is that we send the negotiation if either side
182 * is in disagreement as to what the current state should be.
183 */
184
185void
186send_do(int c, int init)
187{
188    if (init) {
189	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
190				my_want_state_is_do(c))
191	    return;
192	set_my_want_state_do(c);
193	do_dont_resp[c]++;
194    }
195    NET2ADD(IAC, DO);
196    NETADD(c);
197    printoption("SENT",DO, c);
198}
199
200void
201send_dont(int c, int init)
202{
203    if (init) {
204	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
205				my_want_state_is_dont(c))
206	    return;
207	set_my_want_state_dont(c);
208	do_dont_resp[c]++;
209    }
210    NET2ADD(IAC, DONT);
211    NETADD(c);
212    printoption("SENT", DONT, c);
213}
214
215void
216send_will(int c, int init)
217{
218    if (init) {
219	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
220				my_want_state_is_will(c))
221	    return;
222	set_my_want_state_will(c);
223	will_wont_resp[c]++;
224    }
225    NET2ADD(IAC, WILL);
226    NETADD(c);
227    printoption("SENT", WILL, c);
228}
229
230void
231send_wont(int c, int init)
232{
233    if (init) {
234	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
235				my_want_state_is_wont(c))
236	    return;
237	set_my_want_state_wont(c);
238	will_wont_resp[c]++;
239    }
240    NET2ADD(IAC, WONT);
241    NETADD(c);
242    printoption("SENT", WONT, c);
243}
244
245static void
246willoption(int option)
247{
248	int new_state_ok = 0;
249
250	if (do_dont_resp[option]) {
251	    --do_dont_resp[option];
252	    if (do_dont_resp[option] && my_state_is_do(option))
253		--do_dont_resp[option];
254	}
255
256	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
257
258	    switch (option) {
259
260	    case TELOPT_ECHO:
261	    case TELOPT_BINARY:
262	    case TELOPT_SGA:
263		settimer(modenegotiated);
264		/* FALL THROUGH */
265	    case TELOPT_STATUS:
266		new_state_ok = 1;
267		break;
268
269	    case TELOPT_TM:
270		if (flushout)
271		    flushout = 0;
272		/*
273		 * Special case for TM.  If we get back a WILL,
274		 * pretend we got back a WONT.
275		 */
276		set_my_want_state_dont(option);
277		set_my_state_dont(option);
278		return;			/* Never reply to TM will's/wont's */
279
280	    case TELOPT_LINEMODE:
281	    default:
282		break;
283	    }
284
285	    if (new_state_ok) {
286		set_my_want_state_do(option);
287		send_do(option, 0);
288		setconnmode(0);		/* possibly set new tty mode */
289	    } else {
290		do_dont_resp[option]++;
291		send_dont(option, 0);
292	    }
293	}
294	set_my_state_do(option);
295
296}
297
298static void
299wontoption(int option)
300{
301	if (do_dont_resp[option]) {
302	    --do_dont_resp[option];
303	    if (do_dont_resp[option] && my_state_is_dont(option))
304		--do_dont_resp[option];
305	}
306
307	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
308
309	    switch (option) {
310
311#ifdef	KLUDGELINEMODE
312	    case TELOPT_SGA:
313		if (!kludgelinemode)
314		    break;
315		/* FALL THROUGH */
316#endif
317	    case TELOPT_ECHO:
318		settimer(modenegotiated);
319		break;
320
321	    case TELOPT_TM:
322		if (flushout)
323		    flushout = 0;
324		set_my_want_state_dont(option);
325		set_my_state_dont(option);
326		return;		/* Never reply to TM will's/wont's */
327
328	    default:
329		break;
330	    }
331	    set_my_want_state_dont(option);
332	    if (my_state_is_do(option))
333		send_dont(option, 0);
334	    setconnmode(0);			/* Set new tty mode */
335	} else if (option == TELOPT_TM) {
336	    /*
337	     * Special case for TM.
338	     */
339	    if (flushout)
340		flushout = 0;
341	    set_my_want_state_dont(option);
342	}
343	set_my_state_dont(option);
344}
345
346static void
347dooption(int option)
348{
349	int new_state_ok = 0;
350
351	if (will_wont_resp[option]) {
352	    --will_wont_resp[option];
353	    if (will_wont_resp[option] && my_state_is_will(option))
354		--will_wont_resp[option];
355	}
356
357	if (will_wont_resp[option] == 0) {
358	  if (my_want_state_is_wont(option)) {
359
360	    switch (option) {
361
362	    case TELOPT_TM:
363		/*
364		 * Special case for TM.  We send a WILL, but pretend
365		 * we sent WONT.
366		 */
367		send_will(option, 0);
368		set_my_want_state_wont(TELOPT_TM);
369		set_my_state_wont(TELOPT_TM);
370		return;
371
372	    case TELOPT_BINARY:		/* binary mode */
373	    case TELOPT_NAWS:		/* window size */
374	    case TELOPT_TSPEED:		/* terminal speed */
375	    case TELOPT_LFLOW:		/* local flow control */
376	    case TELOPT_TTYPE:		/* terminal type option */
377	    case TELOPT_SGA:		/* no big deal */
378		new_state_ok = 1;
379		break;
380
381	    case TELOPT_NEW_ENVIRON:	/* New environment variable option */
382		new_state_ok = 1;
383		break;
384
385	    case TELOPT_XDISPLOC:	/* X Display location */
386		if (env_getvalue("DISPLAY", 0))
387		    new_state_ok = 1;
388		break;
389
390	    case TELOPT_LINEMODE:
391#ifdef	KLUDGELINEMODE
392		kludgelinemode = 0;
393		send_do(TELOPT_SGA, 1);
394#endif
395		set_my_want_state_will(TELOPT_LINEMODE);
396		send_will(option, 0);
397		set_my_state_will(TELOPT_LINEMODE);
398		slc_init();
399		return;
400
401	    case TELOPT_ECHO:		/* We're never going to echo... */
402	    default:
403		break;
404	    }
405
406	    if (new_state_ok) {
407		set_my_want_state_will(option);
408		send_will(option, 0);
409		setconnmode(0);			/* Set new tty mode */
410	    } else {
411		will_wont_resp[option]++;
412		send_wont(option, 0);
413	    }
414	  } else {
415	    /*
416	     * Handle options that need more things done after the
417	     * other side has acknowledged the option.
418	     */
419	    switch (option) {
420	    case TELOPT_LINEMODE:
421#ifdef	KLUDGELINEMODE
422		kludgelinemode = 0;
423		send_do(TELOPT_SGA, 1);
424#endif
425		set_my_state_will(option);
426		slc_init();
427		send_do(TELOPT_SGA, 0);
428		return;
429	    }
430	  }
431	}
432	set_my_state_will(option);
433}
434
435static void
436dontoption(int option)
437{
438
439	if (will_wont_resp[option]) {
440	    --will_wont_resp[option];
441	    if (will_wont_resp[option] && my_state_is_wont(option))
442		--will_wont_resp[option];
443	}
444
445	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
446	    switch (option) {
447	    case TELOPT_LINEMODE:
448		linemode = 0;	/* put us back to the default state */
449		break;
450	    }
451	    /* we always accept a DONT */
452	    set_my_want_state_wont(option);
453	    if (my_state_is_will(option))
454		send_wont(option, 0);
455	    setconnmode(0);			/* Set new tty mode */
456	}
457	set_my_state_wont(option);
458}
459
460/*
461 * This routine will turn a pipe separated list of names in the buffer
462 * into an array of pointers to NUL terminated names.  We toss out any
463 * bad, duplicate, or verbose names (names with spaces).
464 */
465
466int is_unique(char *, char **, char **);
467
468static char *name_unknown = "UNKNOWN";
469static char *unknown[] = { NULL, NULL };
470
471char **
472mklist(char *buf, char *name)
473{
474	int n;
475	char c, *cp, **argvp, *cp2, **argv, **avt;
476
477	if (name) {
478		if (strlen(name) > 40) {
479			name = NULL;
480			unknown[0] = name_unknown;
481		} else {
482			unknown[0] = name;
483			upcase(name);
484		}
485	} else
486		unknown[0] = name_unknown;
487	/*
488	 * Count up the number of names.
489	 */
490	for (n = 1, cp = buf; *cp; cp++) {
491		if (*cp == '|')
492			n++;
493	}
494	/*
495	 * Allocate an array to put the name pointers into
496	 */
497	argv = reallocarray(NULL, n+3, sizeof(char *));
498	if (argv == NULL)
499		return(unknown);
500
501	/*
502	 * Fill up the array of pointers to names.
503	 */
504	*argv = NULL;
505	argvp = argv+1;
506	n = 0;
507	for (cp = cp2 = buf; (c = *cp);  cp++) {
508		if (c == '|' || c == ':') {
509			*cp++ = '\0';
510			/*
511			 * Skip entries that have spaces or are over 40
512			 * characters long.  If this is our environment
513			 * name, then put it up front.  Otherwise, as
514			 * long as this is not a duplicate name (case
515			 * insensitive) add it to the list.
516			 */
517			if (n || (cp - cp2 > 41))
518				;
519			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
520				*argv = cp2;
521			else if (is_unique(cp2, argv+1, argvp))
522				*argvp++ = cp2;
523			if (c == ':')
524				break;
525			/*
526			 * Skip multiple delimiters. Reset cp2 to
527			 * the beginning of the next name. Reset n,
528			 * the flag for names with spaces.
529			 */
530			while ((c = *cp) == '|')
531				cp++;
532			cp2 = cp;
533			n = 0;
534		}
535		/*
536		 * Skip entries with spaces or non-ascii values.
537		 * Convert lower case letters to upper case.
538		 */
539#define ISASCII(c) (!((c)&0x80))
540		if ((c == ' ') || !ISASCII(c))
541			n = 1;
542		else
543			*cp = toupper((unsigned char)c);
544	}
545
546	/*
547	 * Check for an old V6 2 character name.  If the second
548	 * name points to the beginning of the buffer, and is
549	 * only 2 characters long, move it to the end of the array.
550	 */
551	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
552		--argvp;
553		for (avt = &argv[1]; avt < argvp; avt++)
554			*avt = *(avt+1);
555		*argvp++ = buf;
556	}
557
558	/*
559	 * Duplicate last name, for TTYPE option, and null
560	 * terminate the array.  If we didn't find a match on
561	 * our terminal name, put that name at the beginning.
562	 */
563	cp = *(argvp-1);
564	*argvp++ = cp;
565	*argvp = NULL;
566
567	if (*argv == NULL) {
568		if (name)
569			*argv = name;
570		else {
571			--argvp;
572			for (avt = argv; avt < argvp; avt++)
573				*avt = *(avt+1);
574		}
575	}
576	if (*argv)
577		return(argv);
578	else
579		return(unknown);
580}
581
582int
583is_unique(char *name, char **as, char **ae)
584{
585	char **ap;
586	int n;
587
588	n = strlen(name) + 1;
589	for (ap = as; ap < ae; ap++)
590		if (strncasecmp(*ap, name, n) == 0)
591			return(0);
592	return (1);
593}
594
595int resettermname = 1;
596
597char *
598gettermname(void)
599{
600	char *tname;
601	static char **tnamep = NULL;
602	static char **next;
603	int errret;
604
605	if (resettermname) {
606		resettermname = 0;
607		if (tnamep && tnamep != unknown)
608			free(tnamep);
609		if ((tname = env_getvalue("TERM", 0)) &&
610		    (setupterm(tname, 1, &errret) == OK)) {
611			tnamep = mklist(ttytype, tname);
612		} else {
613			if (tname && (strlen(tname) <= 40)) {
614				unknown[0] = tname;
615				upcase(tname);
616			} else
617				unknown[0] = name_unknown;
618			tnamep = unknown;
619		}
620		next = tnamep;
621	}
622	if (*next == NULL)
623		next = tnamep;
624	return(*next++);
625}
626
627/*
628 * suboption()
629 *
630 *	Look at the sub-option buffer, and try to be helpful to the other
631 * side.
632 *
633 *	Currently we recognize:
634 *
635 *		Terminal type, send request.
636 *		Terminal speed (send request).
637 *		Local flow control (is request).
638 *		Linemode
639 */
640
641static void
642suboption(void)
643{
644    unsigned char subchar;
645
646    printsub('<', subbuffer, SB_LEN()+2);
647    switch (subchar = SB_GET()) {
648    case TELOPT_TTYPE:
649	if (my_want_state_is_wont(TELOPT_TTYPE))
650	    return;
651	if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
652	    return;
653	} else {
654	    char *name;
655	    unsigned char temp[50];
656	    int len;
657
658	    name = gettermname();
659	    len = strlen(name) + 4 + 2;
660	    if (len < NETROOM()) {
661		snprintf((char *)temp, sizeof(temp),
662			 "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
663			 TELQUAL_IS, name, IAC, SE);
664		ring_supply_data(&netoring, temp, len);
665		printsub('>', &temp[2], len-2);
666	    } else
667		ExitString("No room in buffer for terminal type.\n", 1);
668	}
669	break;
670    case TELOPT_TSPEED:
671	if (my_want_state_is_wont(TELOPT_TSPEED))
672	    return;
673	if (SB_EOF())
674	    return;
675	if (SB_GET() == TELQUAL_SEND) {
676	    long ospeed, ispeed;
677	    unsigned char temp[50];
678	    int len;
679
680	    TerminalSpeeds(&ispeed, &ospeed);
681
682	    snprintf((char *)temp, sizeof(temp),
683		     "%c%c%c%c%ld,%ld%c%c", IAC, SB, TELOPT_TSPEED,
684		     TELQUAL_IS, ospeed, ispeed, IAC, SE);
685	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
686
687	    if (len < NETROOM()) {
688		ring_supply_data(&netoring, temp, len);
689		printsub('>', temp+2, len - 2);
690	    }
691/*@*/	    else printf("lm_will: not enough room in buffer\n");
692	}
693	break;
694    case TELOPT_LFLOW:
695	if (my_want_state_is_wont(TELOPT_LFLOW))
696	    return;
697	if (SB_EOF())
698	    return;
699	switch(SB_GET()) {
700	case LFLOW_RESTART_ANY:
701	    restartany = 1;
702	    break;
703	case LFLOW_RESTART_XON:
704	    restartany = 0;
705	    break;
706	case LFLOW_ON:
707	    localflow = 1;
708	    break;
709	case LFLOW_OFF:
710	    localflow = 0;
711	    break;
712	default:
713	    return;
714	}
715	setcommandmode();
716	setconnmode(0);
717	break;
718
719    case TELOPT_LINEMODE:
720	if (my_want_state_is_wont(TELOPT_LINEMODE))
721	    return;
722	if (SB_EOF())
723	    return;
724	switch (SB_GET()) {
725	case WILL:
726	    lm_will(subpointer, SB_LEN());
727	    break;
728	case WONT:
729	    lm_wont(subpointer, SB_LEN());
730	    break;
731	case DO:
732	    lm_do(subpointer, SB_LEN());
733	    break;
734	case DONT:
735	    lm_dont(subpointer, SB_LEN());
736	    break;
737	case LM_SLC:
738	    slc(subpointer, SB_LEN());
739	    break;
740	case LM_MODE:
741	    lm_mode(subpointer, SB_LEN(), 0);
742	    break;
743	default:
744	    break;
745	}
746	break;
747
748    case TELOPT_NEW_ENVIRON:
749	if (SB_EOF())
750	    return;
751	switch(SB_PEEK()) {
752	case TELQUAL_IS:
753	case TELQUAL_INFO:
754	    if (my_want_state_is_dont(subchar))
755		return;
756	    break;
757	case TELQUAL_SEND:
758	    if (my_want_state_is_wont(subchar)) {
759		return;
760	    }
761	    break;
762	default:
763	    return;
764	}
765	env_opt(subpointer, SB_LEN());
766	break;
767
768    case TELOPT_XDISPLOC:
769	if (my_want_state_is_wont(TELOPT_XDISPLOC))
770	    return;
771	if (SB_EOF())
772	    return;
773	if (SB_GET() == TELQUAL_SEND) {
774	    unsigned char temp[50], *dp;
775	    int len;
776
777	    if ((dp = env_getvalue("DISPLAY", 0)) == NULL) {
778		/*
779		 * Something happened, we no longer have a DISPLAY
780		 * variable.  So, turn off the option.
781		 */
782		send_wont(TELOPT_XDISPLOC, 1);
783		break;
784	    }
785	    snprintf((char *)temp, sizeof(temp),
786		    "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
787		    TELQUAL_IS, dp, IAC, SE);
788	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
789
790	    if (len < NETROOM()) {
791		ring_supply_data(&netoring, temp, len);
792		printsub('>', temp+2, len - 2);
793	    }
794/*@*/	    else printf("lm_will: not enough room in buffer\n");
795	}
796	break;
797
798    default:
799	break;
800    }
801}
802
803static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
804
805static void
806lm_will(unsigned char *cmd, int len)
807{
808    if (len < 1) {
809/*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
810	return;
811    }
812    switch(cmd[0]) {
813    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
814    default:
815	str_lm[3] = DONT;
816	str_lm[4] = cmd[0];
817	if (NETROOM() > sizeof(str_lm)) {
818	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
819	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
820	}
821/*@*/	else printf("lm_will: not enough room in buffer\n");
822	break;
823    }
824}
825
826static void
827lm_wont(unsigned char *cmd, int len)
828{
829    if (len < 1) {
830/*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
831	return;
832    }
833    switch(cmd[0]) {
834    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
835    default:
836	/* We are always DONT, so don't respond */
837	return;
838    }
839}
840
841static void
842lm_do(unsigned char *cmd, int len)
843{
844    if (len < 1) {
845/*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
846	return;
847    }
848    switch(cmd[0]) {
849    case LM_FORWARDMASK:
850    default:
851	str_lm[3] = WONT;
852	str_lm[4] = cmd[0];
853	if (NETROOM() > sizeof(str_lm)) {
854	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
855	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
856	}
857/*@*/	else printf("lm_do: not enough room in buffer\n");
858	break;
859    }
860}
861
862static void
863lm_dont(unsigned char *cmd, int len)
864{
865    if (len < 1) {
866/*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
867	return;
868    }
869    switch(cmd[0]) {
870    case LM_FORWARDMASK:
871    default:
872	/* we are always WONT, so don't respond */
873	break;
874    }
875}
876
877static unsigned char str_lm_mode[] = {
878	IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
879};
880
881void
882lm_mode(unsigned char *cmd, int len, int init)
883{
884	if (len != 1)
885		return;
886	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
887		return;
888	if (*cmd&MODE_ACK)
889		return;
890	linemode = *cmd&(MODE_MASK&~MODE_ACK);
891	str_lm_mode[4] = linemode;
892	if (!init)
893	    str_lm_mode[4] |= MODE_ACK;
894	if (NETROOM() > sizeof(str_lm_mode)) {
895	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
896	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
897	}
898/*@*/	else printf("lm_mode: not enough room in buffer\n");
899	setconnmode(0);	/* set changed mode */
900}
901
902
903
904/*
905 * slc()
906 * Handle special character suboption of LINEMODE.
907 */
908
909struct spc {
910	cc_t val;
911	cc_t *valp;
912	char flags;	/* Current flags & level */
913	char mylevel;	/* Maximum level & flags */
914} spc_data[NSLC+1];
915
916#define SLC_IMPORT	0
917#define	SLC_EXPORT	1
918#define SLC_RVALUE	2
919static int slc_mode = SLC_EXPORT;
920
921static void
922slc_init(void)
923{
924	struct spc *spcp;
925
926	localchars = 1;
927	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
928		spcp->val = 0;
929		spcp->valp = NULL;
930		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
931	}
932
933#define	initfunc(func, flags) { \
934					spcp = &spc_data[func]; \
935					if ((spcp->valp = tcval(func))) { \
936					    spcp->val = *spcp->valp; \
937					    spcp->mylevel = SLC_VARIABLE|flags; \
938					} else { \
939					    spcp->val = 0; \
940					    spcp->mylevel = SLC_DEFAULT; \
941					} \
942				    }
943
944	initfunc(SLC_SYNCH, 0);
945	/* No BRK */
946	initfunc(SLC_AO, 0);
947	initfunc(SLC_AYT, 0);
948	/* No EOR */
949	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
950	initfunc(SLC_EOF, 0);
951	initfunc(SLC_SUSP, SLC_FLUSHIN);
952	initfunc(SLC_EC, 0);
953	initfunc(SLC_EL, 0);
954	initfunc(SLC_EW, 0);
955	initfunc(SLC_RP, 0);
956	initfunc(SLC_LNEXT, 0);
957	initfunc(SLC_XON, 0);
958	initfunc(SLC_XOFF, 0);
959	initfunc(SLC_FORW1, 0);
960	initfunc(SLC_FORW2, 0);
961	/* No FORW2 */
962
963	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
964#undef	initfunc
965
966	if (slc_mode == SLC_EXPORT)
967		slc_export();
968	else
969		slc_import(1);
970
971}
972
973void
974slcstate(void)
975{
976    printf("Special characters are %s values\n",
977		slc_mode == SLC_IMPORT ? "remote default" :
978		slc_mode == SLC_EXPORT ? "local" :
979					 "remote");
980}
981
982void
983slc_mode_export(int unused)
984{
985    slc_mode = SLC_EXPORT;
986    if (my_state_is_will(TELOPT_LINEMODE))
987	slc_export();
988}
989
990void
991slc_mode_import(int def)
992{
993    slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
994    if (my_state_is_will(TELOPT_LINEMODE))
995	slc_import(def);
996}
997
998unsigned char slc_import_val[] = {
999	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
1000};
1001unsigned char slc_import_def[] = {
1002	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
1003};
1004
1005static void
1006slc_import(int def)
1007{
1008    if (NETROOM() > sizeof(slc_import_val)) {
1009	if (def) {
1010	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
1011	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
1012	} else {
1013	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
1014	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
1015	}
1016    }
1017/*@*/ else printf("slc_import: not enough room\n");
1018}
1019
1020static void
1021slc_export(void)
1022{
1023    struct spc *spcp;
1024
1025    TerminalDefaultChars();
1026
1027    slc_start_reply();
1028    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
1029	if (spcp->mylevel != SLC_NOSUPPORT) {
1030	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
1031		spcp->flags = SLC_NOSUPPORT;
1032	    else
1033		spcp->flags = spcp->mylevel;
1034	    if (spcp->valp)
1035		spcp->val = *spcp->valp;
1036	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
1037	}
1038    }
1039    slc_end_reply();
1040    (void)slc_update();
1041    setconnmode(1);	/* Make sure the character values are set */
1042}
1043
1044static void
1045slc(unsigned char *cp, int len)
1046{
1047	struct spc *spcp;
1048	int func,level;
1049
1050	slc_start_reply();
1051
1052	for (; len >= 3; len -=3, cp +=3) {
1053
1054		func = cp[SLC_FUNC];
1055
1056		if (func == 0) {
1057			/*
1058			 * Client side: always ignore 0 function.
1059			 */
1060			continue;
1061		}
1062		if (func > NSLC) {
1063			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
1064				slc_add_reply(func, SLC_NOSUPPORT, 0);
1065			continue;
1066		}
1067
1068		spcp = &spc_data[func];
1069
1070		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
1071
1072		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
1073		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
1074			continue;
1075		}
1076
1077		if (level == (SLC_DEFAULT|SLC_ACK)) {
1078			/*
1079			 * This is an error condition, the SLC_ACK
1080			 * bit should never be set for the SLC_DEFAULT
1081			 * level.  Our best guess to recover is to
1082			 * ignore the SLC_ACK bit.
1083			 */
1084			cp[SLC_FLAGS] &= ~SLC_ACK;
1085		}
1086
1087		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
1088			spcp->val = (cc_t)cp[SLC_VALUE];
1089			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
1090			continue;
1091		}
1092
1093		level &= ~SLC_ACK;
1094
1095		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
1096			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
1097			spcp->val = (cc_t)cp[SLC_VALUE];
1098		}
1099		if (level == SLC_DEFAULT) {
1100			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
1101				spcp->flags = spcp->mylevel;
1102			else
1103				spcp->flags = SLC_NOSUPPORT;
1104		}
1105		slc_add_reply(func, spcp->flags, spcp->val);
1106	}
1107	slc_end_reply();
1108	if (slc_update())
1109		setconnmode(1);	/* set the  new character values */
1110}
1111
1112void
1113slc_check(void)
1114{
1115    struct spc *spcp;
1116
1117    slc_start_reply();
1118    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
1119	if (spcp->valp && spcp->val != *spcp->valp) {
1120	    spcp->val = *spcp->valp;
1121	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
1122		spcp->flags = SLC_NOSUPPORT;
1123	    else
1124		spcp->flags = spcp->mylevel;
1125	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
1126	}
1127    }
1128    slc_end_reply();
1129    setconnmode(1);
1130}
1131
1132
1133static unsigned char slc_reply[2 * SUBBUFSIZE];
1134static unsigned char *slc_replyp;
1135
1136unsigned char
1137slc_add(unsigned char ch)
1138{
1139	if (slc_replyp == slc_reply + sizeof(slc_reply))
1140		return ch;
1141	return *slc_replyp++ = ch;
1142}
1143
1144static void
1145slc_start_reply(void)
1146{
1147	slc_replyp = slc_reply;
1148	slc_add(IAC);
1149	slc_add(SB);
1150	slc_add(TELOPT_LINEMODE);
1151	slc_add(LM_SLC);
1152}
1153
1154static void
1155slc_add_reply(unsigned char func, unsigned char flags, cc_t value)
1156{
1157	if (slc_replyp + 6 >= slc_reply + sizeof(slc_reply)) {
1158		printf("slc_add_reply: not enough room\n");
1159		return;
1160	}
1161	if (slc_add(func) == IAC)
1162		slc_add(IAC);
1163	if (slc_add(flags) == IAC)
1164		slc_add(IAC);
1165	if (slc_add((unsigned char)value) == IAC)
1166		slc_add(IAC);
1167}
1168
1169static void
1170slc_end_reply(void)
1171{
1172    int len;
1173
1174    if (slc_replyp + 2 >= slc_reply + sizeof(slc_reply)) {
1175	printf("slc_end_reply: not enough room\n");
1176	return;
1177    }
1178
1179    slc_add(IAC);
1180    slc_add(SE);
1181    len = slc_replyp - slc_reply;
1182    if (len <= 6)
1183	return;
1184    if (NETROOM() > len) {
1185	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
1186	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
1187    }
1188/*@*/else printf("slc_end_reply: not enough room\n");
1189}
1190
1191static int
1192slc_update(void)
1193{
1194	struct spc *spcp;
1195	int need_update = 0;
1196
1197	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
1198		if (!(spcp->flags&SLC_ACK))
1199			continue;
1200		spcp->flags &= ~SLC_ACK;
1201		if (spcp->valp && (*spcp->valp != spcp->val)) {
1202			*spcp->valp = spcp->val;
1203			need_update = 1;
1204		}
1205	}
1206	return(need_update);
1207}
1208
1209static void
1210env_opt(char *buf, int len)
1211{
1212	char *ep = 0, *epc = 0;
1213	int i;
1214
1215	switch(buf[0]&0xff) {
1216	case TELQUAL_SEND:
1217		env_opt_start();
1218		if (len == 1) {
1219			env_opt_add(NULL);
1220		} else for (i = 1; i < len; i++) {
1221			switch (buf[i]&0xff) {
1222			case NEW_ENV_VAR:
1223			case ENV_USERVAR:
1224				if (ep) {
1225					*epc = 0;
1226					env_opt_add(ep);
1227				}
1228				ep = epc = &buf[i+1];
1229				break;
1230			case ENV_ESC:
1231				i++;
1232				/*FALL THROUGH*/
1233			default:
1234				if (epc)
1235					*epc++ = buf[i];
1236				break;
1237			}
1238		}
1239		if (ep) {
1240			*epc = 0;
1241			env_opt_add(ep);
1242		}
1243		env_opt_end(1);
1244		break;
1245
1246	case TELQUAL_IS:
1247	case TELQUAL_INFO:
1248		/* Ignore for now.  We shouldn't get it anyway. */
1249		break;
1250
1251	default:
1252		break;
1253	}
1254}
1255
1256#define	OPT_REPLY_SIZE	(2 * SUBBUFSIZE)
1257static unsigned char *opt_reply;
1258static unsigned char *opt_replyp;
1259static unsigned char *opt_replyend;
1260
1261void
1262opt_add(unsigned char ch)
1263{
1264	if (opt_replyp == opt_replyend)
1265		return;
1266	*opt_replyp++ = ch;
1267}
1268
1269static void
1270env_opt_start(void)
1271{
1272	unsigned char *p;
1273
1274	p = realloc(opt_reply, OPT_REPLY_SIZE);
1275	if (p == NULL)
1276		free(opt_reply);
1277	opt_reply = p;
1278	if (opt_reply == NULL) {
1279/*@*/		printf("env_opt_start: realloc() failed!!!\n");
1280		opt_reply = opt_replyp = opt_replyend = NULL;
1281		return;
1282	}
1283	opt_replyp = opt_reply;
1284	opt_replyend = opt_reply + OPT_REPLY_SIZE;
1285	opt_add(IAC);
1286	opt_add(SB);
1287	opt_add(telopt_environ);
1288	opt_add(TELQUAL_IS);
1289}
1290
1291void
1292env_opt_start_info(void)
1293{
1294	env_opt_start();
1295	if (opt_replyp)
1296	    opt_replyp[-1] = TELQUAL_INFO;
1297}
1298
1299void
1300env_opt_add(char *ep)
1301{
1302	char *vp, c;
1303
1304	if (opt_reply == NULL)		/*XXX*/
1305		return;			/*XXX*/
1306
1307	if (ep == NULL || *ep == '\0') {
1308		/* Send user defined variables first. */
1309		env_default(1, 0);
1310		while ((ep = env_default(0, 0)))
1311			env_opt_add(ep);
1312
1313		/* Now add the list of well know variables.  */
1314		env_default(1, 1);
1315		while ((ep = env_default(0, 1)))
1316			env_opt_add(ep);
1317		return;
1318	}
1319	vp = env_getvalue(ep, 1);
1320	if (2 * (vp ? strlen(vp) : 0) + 2 * strlen(ep) + 6 >
1321	    opt_replyend - opt_replyp)
1322	{
1323		size_t len;
1324		unsigned char *p;
1325
1326		len = opt_replyend - opt_reply;
1327		len += OPT_REPLY_SIZE + 2 * strlen(ep);
1328		if (vp)
1329			len += 2 * strlen(vp);
1330		p = realloc(opt_reply, len);
1331		if (p == NULL) {
1332			free(opt_reply);
1333/*@*/			printf("env_opt_add: realloc() failed!!!\n");
1334			opt_reply = opt_replyp = opt_replyend = NULL;
1335			return;
1336		}
1337		opt_replyp = p + (opt_replyp - opt_reply);
1338		opt_replyend = p + len;
1339		opt_reply = p;
1340	}
1341	if (opt_welldefined(ep))
1342		opt_add(NEW_ENV_VAR);
1343	else
1344		opt_add(ENV_USERVAR);
1345
1346	for (;;) {
1347		while ((c = *ep++)) {
1348			switch(c&0xff) {
1349			case IAC:
1350				opt_add(IAC);
1351				break;
1352			case NEW_ENV_VAR:
1353			case NEW_ENV_VALUE:
1354			case ENV_ESC:
1355			case ENV_USERVAR:
1356				opt_add(ENV_ESC);
1357				break;
1358			}
1359			opt_add(c);
1360		}
1361		if ((ep = vp)) {
1362			opt_add(NEW_ENV_VALUE);
1363			vp = NULL;
1364		} else
1365			break;
1366	}
1367}
1368
1369int
1370opt_welldefined(const char *ep)
1371{
1372	if ((strcmp(ep, "USER") == 0) ||
1373	    (strcmp(ep, "DISPLAY") == 0) ||
1374	    (strcmp(ep, "PRINTER") == 0) ||
1375	    (strcmp(ep, "SYSTEMTYPE") == 0) ||
1376	    (strcmp(ep, "JOB") == 0) ||
1377	    (strcmp(ep, "ACCT") == 0))
1378		return(1);
1379	return(0);
1380}
1381
1382void
1383env_opt_end(int emptyok)
1384{
1385	int len;
1386
1387	len = opt_replyp - opt_reply + 2;
1388	if (emptyok || len > 6) {
1389		opt_add(IAC);
1390		opt_add(SE);
1391		if (NETROOM() > len) {
1392			ring_supply_data(&netoring, opt_reply, len);
1393			printsub('>', &opt_reply[2], len - 2);
1394		}
1395/*@*/		else printf("slc_end_reply: not enough room\n");
1396	}
1397	if (opt_reply) {
1398		free(opt_reply);
1399		opt_reply = opt_replyp = opt_replyend = NULL;
1400	}
1401}
1402
1403
1404
1405int
1406telrcv(void)
1407{
1408    int c;
1409    int scc;
1410    unsigned char *sbp;
1411    int count;
1412    int returnValue = 0;
1413
1414    scc = 0;
1415    count = 0;
1416    while (TTYROOM() > 2) {
1417	if (scc == 0) {
1418	    if (count) {
1419		ring_consumed(&netiring, count);
1420		returnValue = 1;
1421		count = 0;
1422	    }
1423	    sbp = netiring.consume;
1424	    scc = ring_full_consecutive(&netiring);
1425	    if (scc == 0) {
1426		/* No more data coming in */
1427		break;
1428	    }
1429	}
1430
1431	c = *sbp++ & 0xff, scc--; count++;
1432
1433	switch (telrcv_state) {
1434
1435	case TS_CR:
1436	    telrcv_state = TS_DATA;
1437	    if (c == '\0') {
1438		break;	/* Ignore \0 after CR */
1439	    }
1440	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
1441		TTYADD(c);
1442		break;
1443	    }
1444	    /* Else, fall through */
1445
1446	case TS_DATA:
1447	    if (c == IAC) {
1448		telrcv_state = TS_IAC;
1449		break;
1450	    }
1451		    /*
1452		     * The 'crmod' hack (see following) is needed
1453		     * since we can't set CRMOD on output only.
1454		     * Machines like MULTICS like to send \r without
1455		     * \n; since we must turn off CRMOD to get proper
1456		     * input, the mapping is done here (sigh).
1457		     */
1458	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
1459		if (scc > 0) {
1460		    c = *sbp&0xff;
1461		    if (c == 0) {
1462			sbp++, scc--; count++;
1463			/* a "true" CR */
1464			TTYADD('\r');
1465		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
1466					(c == '\n')) {
1467			sbp++, scc--; count++;
1468			TTYADD('\n');
1469		    } else {
1470			TTYADD('\r');
1471			if (crmod) {
1472				TTYADD('\n');
1473			}
1474		    }
1475		} else {
1476		    telrcv_state = TS_CR;
1477		    TTYADD('\r');
1478		    if (crmod) {
1479			    TTYADD('\n');
1480		    }
1481		}
1482	    } else {
1483		TTYADD(c);
1484	    }
1485	    continue;
1486
1487	case TS_IAC:
1488process_iac:
1489	    switch (c) {
1490
1491	    case WILL:
1492		telrcv_state = TS_WILL;
1493		continue;
1494
1495	    case WONT:
1496		telrcv_state = TS_WONT;
1497		continue;
1498
1499	    case DO:
1500		telrcv_state = TS_DO;
1501		continue;
1502
1503	    case DONT:
1504		telrcv_state = TS_DONT;
1505		continue;
1506
1507	    case DM:
1508		    /*
1509		     * We may have missed an urgent notification,
1510		     * so make sure we flush whatever is in the
1511		     * buffer currently.
1512		     */
1513		printoption("RCVD", IAC, DM);
1514		SYNCHing = 1;
1515		(void) ttyflush(1);
1516		SYNCHing = stilloob();
1517		break;
1518
1519	    case SB:
1520		SB_CLEAR();
1521		telrcv_state = TS_SB;
1522		continue;
1523
1524	    case IAC:
1525		TTYADD(IAC);
1526		break;
1527
1528	    case NOP:
1529	    case GA:
1530	    default:
1531		printoption("RCVD", IAC, c);
1532		break;
1533	    }
1534	    telrcv_state = TS_DATA;
1535	    continue;
1536
1537	case TS_WILL:
1538	    printoption("RCVD", WILL, c);
1539	    willoption(c);
1540	    telrcv_state = TS_DATA;
1541	    continue;
1542
1543	case TS_WONT:
1544	    printoption("RCVD", WONT, c);
1545	    wontoption(c);
1546	    telrcv_state = TS_DATA;
1547	    continue;
1548
1549	case TS_DO:
1550	    printoption("RCVD", DO, c);
1551	    dooption(c);
1552	    if (c == TELOPT_NAWS) {
1553		sendnaws();
1554	    } else if (c == TELOPT_LFLOW) {
1555		localflow = 1;
1556		setcommandmode();
1557		setconnmode(0);
1558	    }
1559	    telrcv_state = TS_DATA;
1560	    continue;
1561
1562	case TS_DONT:
1563	    printoption("RCVD", DONT, c);
1564	    dontoption(c);
1565	    flushline = 1;
1566	    setconnmode(0);	/* set new tty mode (maybe) */
1567	    telrcv_state = TS_DATA;
1568	    continue;
1569
1570	case TS_SB:
1571	    if (c == IAC) {
1572		telrcv_state = TS_SE;
1573	    } else {
1574		SB_ACCUM(c);
1575	    }
1576	    continue;
1577
1578	case TS_SE:
1579	    if (c != SE) {
1580		if (c != IAC) {
1581		    /*
1582		     * This is an error.  We only expect to get
1583		     * "IAC IAC" or "IAC SE".  Several things may
1584		     * have happened.  An IAC was not doubled, the
1585		     * IAC SE was left off, or another option got
1586		     * inserted into the suboption are all possibilities.
1587		     * If we assume that the IAC was not doubled,
1588		     * and really the IAC SE was left off, we could
1589		     * get into an infinite loop here.  So, instead,
1590		     * we terminate the suboption, and process the
1591		     * partial suboption if we can.
1592		     */
1593		    SB_ACCUM(IAC);
1594		    SB_ACCUM(c);
1595		    subpointer -= 2;
1596		    SB_TERM();
1597
1598		    printoption("In SUBOPTION processing, RCVD", IAC, c);
1599		    suboption();	/* handle sub-option */
1600		    telrcv_state = TS_IAC;
1601		    goto process_iac;
1602		}
1603		SB_ACCUM(c);
1604		telrcv_state = TS_SB;
1605	    } else {
1606		SB_ACCUM(IAC);
1607		SB_ACCUM(SE);
1608		subpointer -= 2;
1609		SB_TERM();
1610		suboption();	/* handle sub-option */
1611		telrcv_state = TS_DATA;
1612	    }
1613	}
1614    }
1615    if (count)
1616	ring_consumed(&netiring, count);
1617    return returnValue||count;
1618}
1619
1620static int bol = 1, local = 0;
1621
1622int
1623rlogin_susp(void)
1624{
1625    if (local) {
1626	local = 0;
1627	bol = 1;
1628	command(0, "z\n", 2);
1629	return(1);
1630    }
1631    return(0);
1632}
1633
1634static int
1635telsnd(void)
1636{
1637    int tcc;
1638    int count;
1639    int returnValue = 0;
1640    unsigned char *tbp;
1641
1642    tcc = 0;
1643    count = 0;
1644    while (NETROOM() > 2) {
1645	int sc;
1646	int c;
1647
1648	if (tcc == 0) {
1649	    if (count) {
1650		ring_consumed(&ttyiring, count);
1651		returnValue = 1;
1652		count = 0;
1653	    }
1654	    tbp = ttyiring.consume;
1655	    tcc = ring_full_consecutive(&ttyiring);
1656	    if (tcc == 0) {
1657		break;
1658	    }
1659	}
1660	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
1661	if (rlogin != _POSIX_VDISABLE) {
1662		if (bol) {
1663			bol = 0;
1664			if (sc == rlogin) {
1665				local = 1;
1666				continue;
1667			}
1668		} else if (local) {
1669			local = 0;
1670			if (sc == '.' || c == termEofChar) {
1671				bol = 1;
1672				command(0, "close\n", 6);
1673				continue;
1674			}
1675			if (sc == termSuspChar) {
1676				bol = 1;
1677				command(0, "z\n", 2);
1678				continue;
1679			}
1680			if (sc == escape) {
1681				command(0, (char *)tbp, tcc);
1682				bol = 1;
1683				count += tcc;
1684				tcc = 0;
1685				flushline = 1;
1686				break;
1687			}
1688			if (sc != rlogin) {
1689				++tcc;
1690				--tbp;
1691				--count;
1692				c = sc = rlogin;
1693			}
1694		}
1695		if ((sc == '\n') || (sc == '\r'))
1696			bol = 1;
1697	} else if (escape != _POSIX_VDISABLE && sc == escape) {
1698	    /*
1699	     * Double escape is a pass through of a single escape character.
1700	     */
1701	    if (tcc && strip(*tbp) == escape) {
1702		tbp++;
1703		tcc--;
1704		count++;
1705		bol = 0;
1706	    } else {
1707		command(0, (char *)tbp, tcc);
1708		bol = 1;
1709		count += tcc;
1710		tcc = 0;
1711		flushline = 1;
1712		break;
1713	    }
1714	} else
1715	    bol = 0;
1716#ifdef	KLUDGELINEMODE
1717	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
1718	    if (tcc > 0 && strip(*tbp) == echoc) {
1719		tcc--; tbp++; count++;
1720	    } else {
1721		dontlecho = !dontlecho;
1722		settimer(echotoggle);
1723		setconnmode(0);
1724		flushline = 1;
1725		break;
1726	    }
1727	}
1728#endif
1729	if (sc != _POSIX_VDISABLE && MODE_LOCAL_CHARS(globalmode)) {
1730	    if (TerminalSpecialChars(sc) == 0) {
1731		bol = 1;
1732		break;
1733	    }
1734	}
1735	if (my_want_state_is_wont(TELOPT_BINARY)) {
1736	    switch (c) {
1737	    case '\n':
1738		    /*
1739		     * If we are in CRMOD mode (\r ==> \n)
1740		     * on our local machine, then probably
1741		     * a newline (unix) is CRLF (TELNET).
1742		     */
1743		if (MODE_LOCAL_CHARS(globalmode)) {
1744		    NETADD('\r');
1745		}
1746		NETADD('\n');
1747		bol = flushline = 1;
1748		break;
1749	    case '\r':
1750		if (!crlf) {
1751		    NET2ADD('\r', '\0');
1752		} else {
1753		    NET2ADD('\r', '\n');
1754		}
1755		bol = flushline = 1;
1756		break;
1757	    case IAC:
1758		NET2ADD(IAC, IAC);
1759		break;
1760	    default:
1761		NETADD(c);
1762		break;
1763	    }
1764	} else if (c == IAC) {
1765	    NET2ADD(IAC, IAC);
1766	} else {
1767	    NETADD(c);
1768	}
1769    }
1770    if (count)
1771	ring_consumed(&ttyiring, count);
1772    return returnValue||count;		/* Non-zero if we did anything */
1773}
1774
1775/*
1776 * Scheduler()
1777 *
1778 * Try to do something.
1779 *
1780 * If we do something useful, return 1; else return 0.
1781 *
1782 */
1783
1784int
1785Scheduler(int block)			/* should we block in the select ? */
1786{
1787		/* One wants to be a bit careful about setting returnValue
1788		 * to one, since a one implies we did some useful work,
1789		 * and therefore probably won't be called to block next
1790		 * time (TN3270 mode only).
1791		 */
1792    int returnValue;
1793    int netin, netout, netex, ttyin, ttyout;
1794
1795    /* Decide which rings should be processed */
1796
1797    netout = ring_full_count(&netoring) &&
1798	    (flushline ||
1799		(my_want_state_is_wont(TELOPT_LINEMODE)
1800#ifdef	KLUDGELINEMODE
1801			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
1802#endif
1803		) ||
1804			my_want_state_is_will(TELOPT_BINARY));
1805    ttyout = ring_full_count(&ttyoring);
1806
1807    ttyin = ring_empty_count(&ttyiring) && (clienteof == 0);
1808
1809    netin = !ISend && ring_empty_count(&netiring);
1810
1811    netex = !SYNCHing;
1812
1813    /* If we have seen a signal recently, reset things */
1814
1815    if (scheduler_lockout_tty) {
1816	ttyin = ttyout = 0;
1817    }
1818
1819    /* Call to system code to process rings */
1820
1821    returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
1822
1823    /* Now, look at the input rings, looking for work to do. */
1824
1825    if (ring_full_count(&ttyiring)) {
1826        returnValue |= telsnd();
1827    }
1828
1829    if (ring_full_count(&netiring)) {
1830	returnValue |= telrcv();
1831    }
1832    return returnValue;
1833}
1834
1835/*
1836 * Select from tty and network...
1837 */
1838void
1839telnet(char *user)
1840{
1841    connections++;
1842    sys_telnet_init();
1843
1844    if (pledge("stdio rpath tty", NULL) == -1) {
1845	perror("pledge");
1846	exit(1);
1847    }
1848
1849    if (telnetport) {
1850	send_do(TELOPT_SGA, 1);
1851	send_will(TELOPT_TTYPE, 1);
1852	send_will(TELOPT_NAWS, 1);
1853	send_will(TELOPT_TSPEED, 1);
1854	send_will(TELOPT_LFLOW, 1);
1855	send_will(TELOPT_LINEMODE, 1);
1856	send_will(TELOPT_NEW_ENVIRON, 1);
1857	send_do(TELOPT_STATUS, 1);
1858	if (env_getvalue("DISPLAY", 0))
1859	    send_will(TELOPT_XDISPLOC, 1);
1860	if (binary)
1861	    tel_enter_binary(binary);
1862    }
1863
1864    for (;;) {
1865	int schedValue;
1866
1867	while ((schedValue = Scheduler(0)) != 0) {
1868	    if (schedValue == -1) {
1869		setcommandmode();
1870		return;
1871	    }
1872	}
1873
1874	if (Scheduler(1) == -1) {
1875	    setcommandmode();
1876	    return;
1877	}
1878    }
1879}
1880
1881#if	0	/* XXX - this not being in is a bug */
1882/*
1883 * nextitem()
1884 *
1885 *	Return the address of the next "item" in the TELNET data
1886 * stream.  This will be the address of the next character if
1887 * the current address is a user data character, or it will
1888 * be the address of the character following the TELNET command
1889 * if the current address is a TELNET IAC ("I Am a Command")
1890 * character.
1891 */
1892
1893static char *
1894nextitem(char *current)
1895{
1896    if ((*current&0xff) != IAC) {
1897	return current+1;
1898    }
1899    switch (*(current+1)&0xff) {
1900    case DO:
1901    case DONT:
1902    case WILL:
1903    case WONT:
1904	return current+3;
1905    case SB:		/* loop forever looking for the SE */
1906	{
1907	    char *look = current+2;
1908
1909	    for (;;) {
1910		if ((*look++&0xff) == IAC) {
1911		    if ((*look++&0xff) == SE) {
1912			return look;
1913		    }
1914		}
1915	    }
1916	}
1917    default:
1918	return current+2;
1919    }
1920}
1921#endif	/* 0 */
1922
1923/*
1924 * netclear()
1925 *
1926 *	We are about to do a TELNET SYNCH operation.  Clear
1927 * the path to the network.
1928 *
1929 *	Things are a bit tricky since we may have sent the first
1930 * byte or so of a previous TELNET command into the network.
1931 * So, we have to scan the network buffer from the beginning
1932 * until we are up to where we want to be.
1933 *
1934 *	A side effect of what we do, just to keep things
1935 * simple, is to clear the urgent data pointer.  The principal
1936 * caller should be setting the urgent data pointer AFTER calling
1937 * us in any case.
1938 */
1939
1940static void
1941netclear(void)
1942{
1943#if	0	/* XXX */
1944    char *thisitem, *next;
1945    char *good;
1946#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
1947				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1948
1949    thisitem = netobuf;
1950
1951    while ((next = nextitem(thisitem)) <= netobuf.send) {
1952	thisitem = next;
1953    }
1954
1955    /* Now, thisitem is first before/at boundary. */
1956
1957    good = netobuf;	/* where the good bytes go */
1958
1959    while (netoring.add > thisitem) {
1960	if (wewant(thisitem)) {
1961	    int length;
1962
1963	    next = thisitem;
1964	    do {
1965		next = nextitem(next);
1966	    } while (wewant(next) && (nfrontp > next));
1967	    length = next-thisitem;
1968	    memmove(good, thisitem, length);
1969	    good += length;
1970	    thisitem = next;
1971	} else {
1972	    thisitem = nextitem(thisitem);
1973	}
1974    }
1975
1976#endif	/* 0 */
1977}
1978
1979/*
1980 * These routines add various telnet commands to the data stream.
1981 */
1982
1983static void
1984doflush(void)
1985{
1986    NET2ADD(IAC, DO);
1987    NETADD(TELOPT_TM);
1988    flushline = 1;
1989    flushout = 1;
1990    (void) ttyflush(1);			/* Flush/drop output */
1991    /* do printoption AFTER flush, otherwise the output gets tossed... */
1992    printoption("SENT", DO, TELOPT_TM);
1993}
1994
1995void
1996xmitAO(void)
1997{
1998    NET2ADD(IAC, AO);
1999    printoption("SENT", IAC, AO);
2000    if (autoflush) {
2001	doflush();
2002    }
2003}
2004
2005
2006void
2007xmitEL(void)
2008{
2009    NET2ADD(IAC, EL);
2010    printoption("SENT", IAC, EL);
2011}
2012
2013void
2014xmitEC(void)
2015{
2016    NET2ADD(IAC, EC);
2017    printoption("SENT", IAC, EC);
2018}
2019
2020
2021int
2022dosynch(void)
2023{
2024    netclear();			/* clear the path to the network */
2025    NETADD(IAC);
2026    setneturg();
2027    NETADD(DM);
2028    printoption("SENT", IAC, DM);
2029    return 1;
2030}
2031
2032int want_status_response = 0;
2033
2034int
2035get_status(void)
2036{
2037    unsigned char tmp[16];
2038    unsigned char *cp;
2039
2040    if (my_want_state_is_dont(TELOPT_STATUS)) {
2041	printf("Remote side does not support STATUS option\n");
2042	return 0;
2043    }
2044    cp = tmp;
2045
2046    *cp++ = IAC;
2047    *cp++ = SB;
2048    *cp++ = TELOPT_STATUS;
2049    *cp++ = TELQUAL_SEND;
2050    *cp++ = IAC;
2051    *cp++ = SE;
2052    if (NETROOM() >= cp - tmp) {
2053	ring_supply_data(&netoring, tmp, cp-tmp);
2054	printsub('>', tmp+2, cp - tmp - 2);
2055    }
2056    ++want_status_response;
2057    return 1;
2058}
2059
2060void
2061intp(void)
2062{
2063    NET2ADD(IAC, IP);
2064    printoption("SENT", IAC, IP);
2065    flushline = 1;
2066    if (autoflush) {
2067	doflush();
2068    }
2069    if (autosynch) {
2070	dosynch();
2071    }
2072}
2073
2074void
2075sendbrk(void)
2076{
2077    NET2ADD(IAC, BREAK);
2078    printoption("SENT", IAC, BREAK);
2079    flushline = 1;
2080    if (autoflush) {
2081	doflush();
2082    }
2083    if (autosynch) {
2084	dosynch();
2085    }
2086}
2087
2088void
2089sendabort(void)
2090{
2091    NET2ADD(IAC, ABORT);
2092    printoption("SENT", IAC, ABORT);
2093    flushline = 1;
2094    if (autoflush) {
2095	doflush();
2096    }
2097    if (autosynch) {
2098	dosynch();
2099    }
2100}
2101
2102void
2103sendsusp(void)
2104{
2105    NET2ADD(IAC, SUSP);
2106    printoption("SENT", IAC, SUSP);
2107    flushline = 1;
2108    if (autoflush) {
2109	doflush();
2110    }
2111    if (autosynch) {
2112	dosynch();
2113    }
2114}
2115
2116void
2117sendeof(void)
2118{
2119    NET2ADD(IAC, xEOF);
2120    printoption("SENT", IAC, xEOF);
2121}
2122
2123void
2124sendayt(void)
2125{
2126    NET2ADD(IAC, AYT);
2127    printoption("SENT", IAC, AYT);
2128}
2129
2130/*
2131 * Send a window size update to the remote system.
2132 */
2133
2134void
2135sendnaws(void)
2136{
2137    long rows, cols;
2138    unsigned char tmp[16];
2139    unsigned char *cp;
2140
2141    if (my_state_is_wont(TELOPT_NAWS))
2142	return;
2143
2144#define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
2145			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
2146
2147    if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
2148	return;
2149    }
2150
2151    cp = tmp;
2152
2153    *cp++ = IAC;
2154    *cp++ = SB;
2155    *cp++ = TELOPT_NAWS;
2156    PUTSHORT(cp, cols);
2157    PUTSHORT(cp, rows);
2158    *cp++ = IAC;
2159    *cp++ = SE;
2160    if (NETROOM() >= cp - tmp) {
2161	ring_supply_data(&netoring, tmp, cp-tmp);
2162	printsub('>', tmp+2, cp - tmp - 2);
2163    }
2164}
2165
2166void
2167tel_enter_binary(int rw)
2168{
2169    if (rw&1)
2170	send_do(TELOPT_BINARY, 1);
2171    if (rw&2)
2172	send_will(TELOPT_BINARY, 1);
2173}
2174
2175void
2176tel_leave_binary(int rw)
2177{
2178    if (rw&1)
2179	send_dont(TELOPT_BINARY, 1);
2180    if (rw&2)
2181	send_wont(TELOPT_BINARY, 1);
2182}
2183