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