utility.c revision 80224
1/*
2 * Copyright (c) 1989, 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#ifndef lint
35#if 0
36static const char sccsid[] = "@(#)utility.c	8.4 (Berkeley) 5/30/95";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: head/contrib/telnet/telnetd/utility.c 80224 2001-07-23 21:52:26Z kris $";
40#endif /* not lint */
41
42#ifdef __FreeBSD__
43#include <locale.h>
44#include <sys/utsname.h>
45#endif
46#define PRINTOPTIONS
47#include "telnetd.h"
48
49#if	defined(AUTHENTICATION)
50#include <libtelnet/auth.h>
51#endif
52#if	defined(ENCRYPTION)
53#include <libtelnet/encrypt.h>
54#endif
55
56/*
57 * utility functions performing io related tasks
58 */
59
60/*
61 * ttloop
62 *
63 *	A small subroutine to flush the network output buffer, get some data
64 * from the network, and pass it through the telnet state machine.  We
65 * also flush the pty input buffer (by dropping its data) if it becomes
66 * too full.
67 */
68
69    void
70ttloop()
71{
72
73    DIAG(TD_REPORT, output_data("td: ttloop\r\n"));
74    if (nfrontp - nbackp > 0) {
75	netflush();
76    }
77    ncc = read(net, netibuf, sizeof netibuf);
78    if (ncc < 0) {
79	syslog(LOG_INFO, "ttloop:  read: %m");
80	exit(1);
81    } else if (ncc == 0) {
82	syslog(LOG_INFO, "ttloop:  peer died: %m");
83	exit(1);
84    }
85    DIAG(TD_REPORT, output_data("td: ttloop read %d chars\r\n", ncc));
86    netip = netibuf;
87    telrcv();			/* state machine */
88    if (ncc > 0) {
89	pfrontp = pbackp = ptyobuf;
90	telrcv();
91    }
92}  /* end of ttloop */
93
94/*
95 * Check a descriptor to see if out of band data exists on it.
96 */
97    int
98stilloob(s)
99    int	s;		/* socket number */
100{
101    static struct timeval timeout = { 0 };
102    fd_set	excepts;
103    int value;
104
105    do {
106	FD_ZERO(&excepts);
107	FD_SET(s, &excepts);
108	memset((char *)&timeout, 0, sizeof timeout);
109	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
110    } while ((value == -1) && (errno == EINTR));
111
112    if (value < 0) {
113	fatalperror(pty, "select");
114    }
115    if (FD_ISSET(s, &excepts)) {
116	return 1;
117    } else {
118	return 0;
119    }
120}
121
122	void
123ptyflush()
124{
125	int n;
126
127	if ((n = pfrontp - pbackp) > 0) {
128		DIAG(TD_REPORT | TD_PTYDATA,
129		    output_data("td: ptyflush %d chars\r\n", n));
130		DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
131		n = write(pty, pbackp, n);
132	}
133	if (n < 0) {
134		if (errno == EWOULDBLOCK || errno == EINTR)
135			return;
136		cleanup(0);
137	}
138	pbackp += n;
139	if (pbackp == pfrontp)
140		pbackp = pfrontp = ptyobuf;
141}
142
143/*
144 * nextitem()
145 *
146 *	Return the address of the next "item" in the TELNET data
147 * stream.  This will be the address of the next character if
148 * the current address is a user data character, or it will
149 * be the address of the character following the TELNET command
150 * if the current address is a TELNET IAC ("I Am a Command")
151 * character.
152 */
153    char *
154nextitem(current)
155    char	*current;
156{
157    if ((*current&0xff) != IAC) {
158	return current+1;
159    }
160    switch (*(current+1)&0xff) {
161    case DO:
162    case DONT:
163    case WILL:
164    case WONT:
165	return current+3;
166    case SB:		/* loop forever looking for the SE */
167	{
168	    register char *look = current+2;
169
170	    for (;;) {
171		if ((*look++&0xff) == IAC) {
172		    if ((*look++&0xff) == SE) {
173			return look;
174		    }
175		}
176	    }
177	}
178    default:
179	return current+2;
180    }
181}  /* end of nextitem */
182
183
184/*
185 * netclear()
186 *
187 *	We are about to do a TELNET SYNCH operation.  Clear
188 * the path to the network.
189 *
190 *	Things are a bit tricky since we may have sent the first
191 * byte or so of a previous TELNET command into the network.
192 * So, we have to scan the network buffer from the beginning
193 * until we are up to where we want to be.
194 *
195 *	A side effect of what we do, just to keep things
196 * simple, is to clear the urgent data pointer.  The principal
197 * caller should be setting the urgent data pointer AFTER calling
198 * us in any case.
199 */
200    void
201netclear()
202{
203    register char *thisitem, *next;
204    char *good;
205#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
206				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
207
208#ifdef	ENCRYPTION
209    thisitem = nclearto > netobuf ? nclearto : netobuf;
210#else	/* ENCRYPTION */
211    thisitem = netobuf;
212#endif	/* ENCRYPTION */
213
214    while ((next = nextitem(thisitem)) <= nbackp) {
215	thisitem = next;
216    }
217
218    /* Now, thisitem is first before/at boundary. */
219
220#ifdef	ENCRYPTION
221    good = nclearto > netobuf ? nclearto : netobuf;
222#else	/* ENCRYPTION */
223    good = netobuf;	/* where the good bytes go */
224#endif	/* ENCRYPTION */
225
226    while (nfrontp > thisitem) {
227	if (wewant(thisitem)) {
228	    int length;
229
230	    next = thisitem;
231	    do {
232		next = nextitem(next);
233	    } while (wewant(next) && (nfrontp > next));
234	    length = next-thisitem;
235	    memmove(good, thisitem, length);
236	    good += length;
237	    thisitem = next;
238	} else {
239	    thisitem = nextitem(thisitem);
240	}
241    }
242
243    nbackp = netobuf;
244    nfrontp = good;		/* next byte to be sent */
245    neturg = 0;
246}  /* end of netclear */
247
248/*
249 *  netflush
250 *		Send as much data as possible to the network,
251 *	handling requests for urgent data.
252 */
253    void
254netflush()
255{
256    int n;
257    extern int not42;
258
259    while ((n = nfrontp - nbackp) > 0) {
260#if 0
261	/* XXX This causes output_data() to recurse and die */
262	DIAG(TD_REPORT, {
263	    n += output_data("td: netflush %d chars\r\n", n);
264	});
265#endif
266#ifdef	ENCRYPTION
267	if (encrypt_output) {
268		char *s = nclearto ? nclearto : nbackp;
269		if (nfrontp - s > 0) {
270			(*encrypt_output)((unsigned char *)s, nfrontp-s);
271			nclearto = nfrontp;
272		}
273	}
274#endif	/* ENCRYPTION */
275	/*
276	 * if no urgent data, or if the other side appears to be an
277	 * old 4.2 client (and thus unable to survive TCP urgent data),
278	 * write the entire buffer in non-OOB mode.
279	 */
280	if ((neturg == 0) || (not42 == 0)) {
281	    n = write(net, nbackp, n);	/* normal write */
282	} else {
283	    n = neturg - nbackp;
284	    /*
285	     * In 4.2 (and 4.3) systems, there is some question about
286	     * what byte in a sendOOB operation is the "OOB" data.
287	     * To make ourselves compatible, we only send ONE byte
288	     * out of band, the one WE THINK should be OOB (though
289	     * we really have more the TCP philosophy of urgent data
290	     * rather than the Unix philosophy of OOB data).
291	     */
292	    if (n > 1) {
293		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
294	    } else {
295		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
296	    }
297	}
298	if (n == -1) {
299	    if (errno == EWOULDBLOCK || errno == EINTR)
300		continue;
301	    cleanup(0);
302	    /* NOTREACHED */
303	}
304	nbackp += n;
305#ifdef	ENCRYPTION
306	if (nbackp > nclearto)
307	    nclearto = 0;
308#endif	/* ENCRYPTION */
309	if (nbackp >= neturg) {
310	    neturg = 0;
311	}
312	if (nbackp == nfrontp) {
313	    nbackp = nfrontp = netobuf;
314#ifdef	ENCRYPTION
315	    nclearto = 0;
316#endif	/* ENCRYPTION */
317	}
318    }
319    return;
320}  /* end of netflush */
321
322
323/*
324 * miscellaneous functions doing a variety of little jobs follow ...
325 */
326
327
328	void
329fatal(f, msg)
330	int f;
331	char *msg;
332{
333	char buf[BUFSIZ];
334
335	(void) snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
336#ifdef	ENCRYPTION
337	if (encrypt_output) {
338		/*
339		 * Better turn off encryption first....
340		 * Hope it flushes...
341		 */
342		encrypt_send_end();
343		netflush();
344	}
345#endif	/* ENCRYPTION */
346	(void) write(f, buf, (int)strlen(buf));
347	sleep(1);	/*XXX*/
348	exit(1);
349}
350
351	void
352fatalperror(f, msg)
353	int f;
354	char *msg;
355{
356	char buf[BUFSIZ], *strerror();
357
358	(void) snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
359	fatal(f, buf);
360}
361
362char editedhost[32];
363
364	void
365edithost(pat, host)
366	register char *pat;
367	register char *host;
368{
369	register char *res = editedhost;
370
371	if (!pat)
372		pat = "";
373	while (*pat) {
374		switch (*pat) {
375
376		case '#':
377			if (*host)
378				host++;
379			break;
380
381		case '@':
382			if (*host)
383				*res++ = *host++;
384			break;
385
386		default:
387			*res++ = *pat;
388			break;
389		}
390		if (res == &editedhost[sizeof editedhost - 1]) {
391			*res = '\0';
392			return;
393		}
394		pat++;
395	}
396	if (*host)
397		(void) strncpy(res, host,
398				sizeof editedhost - (res - editedhost) -1);
399	else
400		*res = '\0';
401	editedhost[sizeof editedhost - 1] = '\0';
402}
403
404static char *putlocation;
405
406	void
407putstr(s)
408	register char *s;
409{
410
411	while (*s)
412		putchr(*s++);
413}
414
415	void
416putchr(cc)
417	int cc;
418{
419	*putlocation++ = cc;
420}
421
422#ifdef __FreeBSD__
423static char fmtstr[] = { "%+" };
424#else
425/*
426 * This is split on two lines so that SCCS will not see the M
427 * between two % signs and expand it...
428 */
429static char fmtstr[] = { "%l:%M\
430%P on %A, %d %B %Y" };
431#endif
432
433	void
434putf(cp, where)
435	register char *cp;
436	char *where;
437{
438	char *slash;
439	time_t t;
440	char db[100];
441#ifdef	STREAMSPTY
442	extern char *strchr();
443#else
444	extern char *strrchr();
445#endif
446#ifdef __FreeBSD__
447	static struct utsname kerninfo;
448
449	if (!*kerninfo.sysname)
450		uname(&kerninfo);
451#endif
452
453	putlocation = where;
454
455	while (*cp) {
456		if (*cp =='\n') {
457			putstr("\r\n");
458			cp++;
459			continue;
460		} else if (*cp != '%') {
461			putchr(*cp++);
462			continue;
463		}
464		switch (*++cp) {
465
466		case 't':
467#ifdef	STREAMSPTY
468			/* names are like /dev/pts/2 -- we want pts/2 */
469			slash = strchr(line+1, '/');
470#else
471			slash = strrchr(line, '/');
472#endif
473			if (slash == (char *) 0)
474				putstr(line);
475			else
476				putstr(&slash[1]);
477			break;
478
479		case 'h':
480			putstr(editedhost);
481			break;
482
483		case 'd':
484#ifdef __FreeBSD__
485			setlocale(LC_TIME, "");
486#endif
487			(void)time(&t);
488			(void)strftime(db, sizeof(db), fmtstr, localtime(&t));
489			putstr(db);
490			break;
491
492#ifdef __FreeBSD__
493		case 's':
494			putstr(kerninfo.sysname);
495			break;
496
497		case 'm':
498			putstr(kerninfo.machine);
499			break;
500
501		case 'r':
502			putstr(kerninfo.release);
503			break;
504
505		case 'v':
506			putstr(kerninfo.version);
507			break;
508#endif
509
510		case '%':
511			putchr('%');
512			break;
513		}
514		cp++;
515	}
516}
517
518#ifdef DIAGNOSTICS
519/*
520 * Print telnet options and commands in plain text, if possible.
521 */
522	void
523printoption(fmt, option)
524	register char *fmt;
525	register int option;
526{
527	if (TELOPT_OK(option))
528		output_data("%s %s\r\n", fmt, TELOPT(option));
529	else if (TELCMD_OK(option))
530		output_data("%s %s\r\n", fmt, TELCMD(option));
531	else
532		output_data("%s %d\r\n", fmt, option);
533	return;
534}
535
536    void
537printsub(direction, pointer, length)
538    char		direction;	/* '<' or '>' */
539    unsigned char	*pointer;	/* where suboption data sits */
540    int			length;		/* length of suboption data */
541{
542    register int i = 0;
543
544	if (!(diagnostic & TD_OPTIONS))
545		return;
546
547	if (direction) {
548	    output_data("td: %s suboption ",
549					direction == '<' ? "recv" : "send");
550	    if (length >= 3) {
551		register int j;
552
553		i = pointer[length-2];
554		j = pointer[length-1];
555
556		if (i != IAC || j != SE) {
557		    output_data("(terminated by ");
558		    if (TELOPT_OK(i))
559			output_data("%s ", TELOPT(i));
560		    else if (TELCMD_OK(i))
561			output_data("%s ", TELCMD(i));
562		    else
563			output_data("%d ", i);
564		    if (TELOPT_OK(j))
565			output_data("%s", TELOPT(j));
566		    else if (TELCMD_OK(j))
567			output_data("%s", TELCMD(j));
568		    else
569			output_data("%d", j);
570		    output_data(", not IAC SE!) ");
571		}
572	    }
573	    length -= 2;
574	}
575	if (length < 1) {
576	    output_data("(Empty suboption??\?)");
577	    return;
578	}
579	switch (pointer[0]) {
580	case TELOPT_TTYPE:
581	    output_data("TERMINAL-TYPE ");
582	    switch (pointer[1]) {
583	    case TELQUAL_IS:
584		output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
585		break;
586	    case TELQUAL_SEND:
587		output_data("SEND");
588		break;
589	    default:
590		output_data(
591				"- unknown qualifier %d (0x%x).",
592				pointer[1], pointer[1]);
593	    }
594	    break;
595	case TELOPT_TSPEED:
596	    output_data("TERMINAL-SPEED");
597	    if (length < 2) {
598		output_data(" (empty suboption??\?)");
599		break;
600	    }
601	    switch (pointer[1]) {
602	    case TELQUAL_IS:
603		output_data(" IS %.*s", length-2, (char *)pointer+2);
604		break;
605	    default:
606		if (pointer[1] == 1)
607		    output_data(" SEND");
608		else
609		    output_data(" %d (unknown)", pointer[1]);
610		for (i = 2; i < length; i++) {
611		    output_data(" ?%d?", pointer[i]);
612		}
613		break;
614	    }
615	    break;
616
617	case TELOPT_LFLOW:
618	    output_data("TOGGLE-FLOW-CONTROL");
619	    if (length < 2) {
620		output_data(" (empty suboption??\?)");
621		break;
622	    }
623	    switch (pointer[1]) {
624	    case LFLOW_OFF:
625		output_data(" OFF"); break;
626	    case LFLOW_ON:
627		output_data(" ON"); break;
628	    case LFLOW_RESTART_ANY:
629		output_data(" RESTART-ANY"); break;
630	    case LFLOW_RESTART_XON:
631		output_data(" RESTART-XON"); break;
632	    default:
633		output_data(" %d (unknown)", pointer[1]);
634	    }
635	    for (i = 2; i < length; i++) {
636		output_data(" ?%d?", pointer[i]);
637	    }
638	    break;
639
640	case TELOPT_NAWS:
641	    output_data("NAWS");
642	    if (length < 2) {
643		output_data(" (empty suboption??\?)");
644		break;
645	    }
646	    if (length == 2) {
647		output_data(" ?%d?", pointer[1]);
648		break;
649	    }
650	    output_data(" %d %d (%d)",
651		pointer[1], pointer[2],
652		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
653	    if (length == 4) {
654		output_data(" ?%d?", pointer[3]);
655		break;
656	    }
657	    output_data(" %d %d (%d)",
658		pointer[3], pointer[4],
659		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
660	    for (i = 5; i < length; i++) {
661		output_data(" ?%d?", pointer[i]);
662	    }
663	    break;
664
665	case TELOPT_LINEMODE:
666	    output_data("LINEMODE ");
667	    if (length < 2) {
668		output_data(" (empty suboption??\?)");
669		break;
670	    }
671	    switch (pointer[1]) {
672	    case WILL:
673		output_data("WILL ");
674		goto common;
675	    case WONT:
676		output_data("WONT ");
677		goto common;
678	    case DO:
679		output_data("DO ");
680		goto common;
681	    case DONT:
682		output_data("DONT ");
683	    common:
684		if (length < 3) {
685		    output_data("(no option??\?)");
686		    break;
687		}
688		switch (pointer[2]) {
689		case LM_FORWARDMASK:
690		    output_data("Forward Mask");
691		    for (i = 3; i < length; i++) {
692			output_data(" %x", pointer[i]);
693		    }
694		    break;
695		default:
696		    output_data("%d (unknown)", pointer[2]);
697		    for (i = 3; i < length; i++) {
698			output_data(" %d", pointer[i]);
699		    }
700		    break;
701		}
702		break;
703
704	    case LM_SLC:
705		output_data("SLC");
706		for (i = 2; i < length - 2; i += 3) {
707		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
708			output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC]));
709		    else
710			output_data(" %d", pointer[i+SLC_FUNC]);
711		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
712		    case SLC_NOSUPPORT:
713			output_data(" NOSUPPORT"); break;
714		    case SLC_CANTCHANGE:
715			output_data(" CANTCHANGE"); break;
716		    case SLC_VARIABLE:
717			output_data(" VARIABLE"); break;
718		    case SLC_DEFAULT:
719			output_data(" DEFAULT"); break;
720		    }
721		    output_data("%s%s%s",
722			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
723			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
724			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
725		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
726						SLC_FLUSHOUT| SLC_LEVELBITS)) {
727			output_data("(0x%x)", pointer[i+SLC_FLAGS]);
728		    }
729		    output_data(" %d;", pointer[i+SLC_VALUE]);
730		    if ((pointer[i+SLC_VALUE] == IAC) &&
731			(pointer[i+SLC_VALUE+1] == IAC))
732				i++;
733		}
734		for (; i < length; i++) {
735		    output_data(" ?%d?", pointer[i]);
736		}
737		break;
738
739	    case LM_MODE:
740		output_data("MODE ");
741		if (length < 3) {
742		    output_data("(no mode??\?)");
743		    break;
744		}
745		{
746		    char tbuf[32];
747		    sprintf(tbuf, "%s%s%s%s%s",
748			pointer[2]&MODE_EDIT ? "|EDIT" : "",
749			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
750			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
751			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
752			pointer[2]&MODE_ACK ? "|ACK" : "");
753		    output_data("%s", tbuf[1] ? &tbuf[1] : "0");
754		}
755		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
756		    output_data(" (0x%x)", pointer[2]);
757		}
758		for (i = 3; i < length; i++) {
759		    output_data(" ?0x%x?", pointer[i]);
760		}
761		break;
762	    default:
763		output_data("%d (unknown)", pointer[1]);
764		for (i = 2; i < length; i++) {
765		    output_data(" %d", pointer[i]);
766		}
767	    }
768	    break;
769
770	case TELOPT_STATUS: {
771	    register char *cp;
772	    register int j, k;
773
774	    output_data("STATUS");
775
776	    switch (pointer[1]) {
777	    default:
778		if (pointer[1] == TELQUAL_SEND)
779		    output_data(" SEND");
780		else
781		    output_data(" %d (unknown)", pointer[1]);
782		for (i = 2; i < length; i++) {
783		    output_data(" ?%d?", pointer[i]);
784		}
785		break;
786	    case TELQUAL_IS:
787		output_data(" IS\r\n");
788
789		for (i = 2; i < length; i++) {
790		    switch(pointer[i]) {
791		    case DO:	cp = "DO"; goto common2;
792		    case DONT:	cp = "DONT"; goto common2;
793		    case WILL:	cp = "WILL"; goto common2;
794		    case WONT:	cp = "WONT"; goto common2;
795		    common2:
796			i++;
797			if (TELOPT_OK(pointer[i]))
798			    output_data(" %s %s", cp, TELOPT(pointer[i]));
799			else
800			    output_data(" %s %d", cp, pointer[i]);
801
802			output_data("\r\n");
803			break;
804
805		    case SB:
806			output_data(" SB ");
807			i++;
808			j = k = i;
809			while (j < length) {
810			    if (pointer[j] == SE) {
811				if (j+1 == length)
812				    break;
813				if (pointer[j+1] == SE)
814				    j++;
815				else
816				    break;
817			    }
818			    pointer[k++] = pointer[j++];
819			}
820			printsub(0, &pointer[i], k - i);
821			if (i < length) {
822			    output_data(" SE");
823			    i = j;
824			} else
825			    i = j - 1;
826
827			output_data("\r\n");
828
829			break;
830
831		    default:
832			output_data(" %d", pointer[i]);
833			break;
834		    }
835		}
836		break;
837	    }
838	    break;
839	  }
840
841	case TELOPT_XDISPLOC:
842	    output_data("X-DISPLAY-LOCATION ");
843	    switch (pointer[1]) {
844	    case TELQUAL_IS:
845		output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
846		break;
847	    case TELQUAL_SEND:
848		output_data("SEND");
849		break;
850	    default:
851		output_data("- unknown qualifier %d (0x%x).",
852				pointer[1], pointer[1]);
853	    }
854	    break;
855
856	case TELOPT_NEW_ENVIRON:
857	    output_data("NEW-ENVIRON ");
858	    goto env_common1;
859	case TELOPT_OLD_ENVIRON:
860	    output_data("OLD-ENVIRON");
861	env_common1:
862	    switch (pointer[1]) {
863	    case TELQUAL_IS:
864		output_data("IS ");
865		goto env_common;
866	    case TELQUAL_SEND:
867		output_data("SEND ");
868		goto env_common;
869	    case TELQUAL_INFO:
870		output_data("INFO ");
871	    env_common:
872		{
873		    register int noquote = 2;
874		    for (i = 2; i < length; i++ ) {
875			switch (pointer[i]) {
876			case NEW_ENV_VAR:
877			    output_data("\" VAR " + noquote);
878			    noquote = 2;
879			    break;
880
881			case NEW_ENV_VALUE:
882			    output_data("\" VALUE " + noquote);
883			    noquote = 2;
884			    break;
885
886			case ENV_ESC:
887			    output_data("\" ESC " + noquote);
888			    noquote = 2;
889			    break;
890
891			case ENV_USERVAR:
892			    output_data("\" USERVAR " + noquote);
893			    noquote = 2;
894			    break;
895
896			default:
897			    if (isprint(pointer[i]) && pointer[i] != '"') {
898				if (noquote) {
899				    output_data("\"");
900				    noquote = 0;
901				}
902				output_data("%c", pointer[i]);
903			    } else {
904				output_data("\" %03o " + noquote,
905							pointer[i]);
906				noquote = 2;
907			    }
908			    break;
909			}
910		    }
911		    if (!noquote)
912			output_data("\"");
913		    break;
914		}
915	    }
916	    break;
917
918#if	defined(AUTHENTICATION)
919	case TELOPT_AUTHENTICATION:
920	    output_data("AUTHENTICATION");
921
922	    if (length < 2) {
923		output_data(" (empty suboption??\?)");
924		break;
925	    }
926	    switch (pointer[1]) {
927	    case TELQUAL_REPLY:
928	    case TELQUAL_IS:
929		output_data(" %s ", (pointer[1] == TELQUAL_IS) ?
930							"IS" : "REPLY");
931		if (AUTHTYPE_NAME_OK(pointer[2]))
932		    output_data("%s ", AUTHTYPE_NAME(pointer[2]));
933		else
934		    output_data("%d ", pointer[2]);
935		if (length < 3) {
936		    output_data("(partial suboption??\?)");
937		    break;
938		}
939		output_data("%s|%s",
940			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
941			"CLIENT" : "SERVER",
942			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
943			"MUTUAL" : "ONE-WAY");
944
945    		{
946		    char buf[512];
947		    auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
948		    output_data("%s", buf);
949		}
950		break;
951
952	    case TELQUAL_SEND:
953		i = 2;
954		output_data(" SEND ");
955		while (i < length) {
956		    if (AUTHTYPE_NAME_OK(pointer[i]))
957			output_data("%s ", AUTHTYPE_NAME(pointer[i]));
958		    else
959			output_data("%d ", pointer[i]);
960		    if (++i >= length) {
961			output_data("(partial suboption??\?)");
962			break;
963		    }
964		    output_data("%s|%s ",
965			((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
966							"CLIENT" : "SERVER",
967			((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
968							"MUTUAL" : "ONE-WAY");
969		    ++i;
970		}
971		break;
972
973	    case TELQUAL_NAME:
974		output_data(" NAME \"%.*s\"", length - 2, pointer + 2);
975		break;
976
977	    default:
978		    for (i = 2; i < length; i++) {
979			output_data(" ?%d?", pointer[i]);
980		    }
981		    break;
982	    }
983	    break;
984#endif
985
986#ifdef	ENCRYPTION
987	case TELOPT_ENCRYPT:
988	    output_data("ENCRYPT");
989	    if (length < 2) {
990		output_data(" (empty suboption??\?)");
991		break;
992	    }
993	    switch (pointer[1]) {
994	    case ENCRYPT_START:
995		output_data(" START");
996		break;
997
998	    case ENCRYPT_END:
999		output_data(" END");
1000		break;
1001
1002	    case ENCRYPT_REQSTART:
1003		output_data(" REQUEST-START");
1004		break;
1005
1006	    case ENCRYPT_REQEND:
1007		output_data(" REQUEST-END");
1008		break;
1009
1010	    case ENCRYPT_IS:
1011	    case ENCRYPT_REPLY:
1012		output_data(" %s ", (pointer[1] == ENCRYPT_IS) ?
1013							"IS" : "REPLY");
1014		if (length < 3) {
1015		    output_data(" (partial suboption??\?)");
1016		    break;
1017		}
1018		if (ENCTYPE_NAME_OK(pointer[2]))
1019		    output_data("%s ", ENCTYPE_NAME(pointer[2]));
1020		else
1021		    output_data(" %d (unknown)", pointer[2]);
1022
1023		{
1024		    char buf[512];
1025		    encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
1026		    output_data("%s", buf);
1027		}
1028		break;
1029
1030	    case ENCRYPT_SUPPORT:
1031		i = 2;
1032		output_data(" SUPPORT ");
1033		while (i < length) {
1034		    if (ENCTYPE_NAME_OK(pointer[i]))
1035			output_data("%s ", ENCTYPE_NAME(pointer[i]));
1036		    else
1037			output_data("%d ", pointer[i]);
1038		    i++;
1039		}
1040		break;
1041
1042	    case ENCRYPT_ENC_KEYID:
1043		output_data(" ENC_KEYID");
1044		goto encommon;
1045
1046	    case ENCRYPT_DEC_KEYID:
1047		output_data(" DEC_KEYID");
1048		goto encommon;
1049
1050	    default:
1051		output_data(" %d (unknown)", pointer[1]);
1052	    encommon:
1053		for (i = 2; i < length; i++) {
1054		    output_data(" %d", pointer[i]);
1055		}
1056		break;
1057	    }
1058	    break;
1059#endif	/* ENCRYPTION */
1060
1061	default:
1062	    if (TELOPT_OK(pointer[0]))
1063		output_data("%s (unknown)", TELOPT(pointer[0]));
1064	    else
1065		output_data("%d (unknown)", pointer[i]);
1066	    for (i = 1; i < length; i++) {
1067		output_data(" %d", pointer[i]);
1068	    }
1069	    break;
1070	}
1071	output_data("\r\n");
1072}
1073
1074/*
1075 * Dump a data buffer in hex and ascii to the output data stream.
1076 */
1077	void
1078printdata(tag, ptr, cnt)
1079	register char *tag;
1080	register char *ptr;
1081	register int cnt;
1082{
1083	register int i;
1084	char xbuf[30];
1085
1086	while (cnt) {
1087		/* flush net output buffer if no room for new data) */
1088		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
1089			netflush();
1090		}
1091
1092		/* add a line of output */
1093		output_data("%s: ", tag);
1094		for (i = 0; i < 20 && cnt; i++) {
1095			output_data("%02x", *ptr);
1096			if (isprint(*ptr)) {
1097				xbuf[i] = *ptr;
1098			} else {
1099				xbuf[i] = '.';
1100			}
1101			if (i % 2) {
1102				output_data(" ");
1103			}
1104			cnt--;
1105			ptr++;
1106		}
1107		xbuf[i] = '\0';
1108		output_data(" %s\r\n", xbuf );
1109	}
1110}
1111#endif /* DIAGNOSTICS */
1112