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