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