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