xref: /freebsd/crypto/heimdal/appl/telnet/telnetd/utility.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
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.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 
54 int
55 ttloop(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  */
87 int
88 stilloob(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 
113 void
114 ptyflush(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  */
145 char *
146 nextitem(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  */
191 void
192 netclear(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 
239 extern int not42;
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 
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  */
325 void
326 writenet(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 
344 void 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 
364 void
365 fatalperror_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 
373 void
374 fatalperror(int f, const char *msg)
375 {
376     fatalperror_errno(f, msg, errno);
377 }
378 
379 char editedhost[32];
380 
381 void 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 
418 static char *putlocation;
419 
420 void
421 putstr(char *s)
422 {
423 
424     while (*s)
425 	putchr(*s++);
426 }
427 
428 void
429 putchr(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  */
438 static char fmtstr[] = { "%l:%M" "%P on %A, %d %B %Y" };
439 
440 void 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  */
523 void
524 printoption(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 
541 void
542 printsub(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  */
1139 void
1140 printdata(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