xref: /freebsd/crypto/heimdal/appl/telnet/telnetd/utility.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
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$");
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(const void *ptr, size_t 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     if ((&netobuf[BUFSIZ] - nfrontp) < len)
334 	abort();
335 
336     memmove(nfrontp, ptr, len);
337     nfrontp += len;
338 }
339 
340 
341 /*
342  * miscellaneous functions doing a variety of little jobs follow ...
343  */
344 
345 
346 void fatal(int f, char *msg)
347 {
348     char buf[BUFSIZ];
349 
350     snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
351 #ifdef ENCRYPTION
352     if (encrypt_output) {
353 	/*
354 	 * Better turn off encryption first....
355 	 * Hope it flushes...
356 	 */
357 	encrypt_send_end();
358 	netflush();
359     }
360 #endif
361     write(f, buf, (int)strlen(buf));
362     sleep(1);	/*XXX*/
363     exit(1);
364 }
365 
366 void
367 fatalperror_errno(int f, const char *msg, int error)
368 {
369     char buf[BUFSIZ];
370 
371     snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(error));
372     fatal(f, buf);
373 }
374 
375 void
376 fatalperror(int f, const char *msg)
377 {
378     fatalperror_errno(f, msg, errno);
379 }
380 
381 char editedhost[32];
382 
383 void edithost(char *pat, char *host)
384 {
385     char *res = editedhost;
386 
387     if (!pat)
388 	pat = "";
389     while (*pat) {
390 	switch (*pat) {
391 
392 	case '#':
393 	    if (*host)
394 		host++;
395 	    break;
396 
397 	case '@':
398 	    if (*host)
399 		*res++ = *host++;
400 	    break;
401 
402 	default:
403 	    *res++ = *pat;
404 	    break;
405 	}
406 	if (res == &editedhost[sizeof editedhost - 1]) {
407 	    *res = '\0';
408 	    return;
409 	}
410 	pat++;
411     }
412     if (*host)
413 	strlcpy (res, host,
414 			 sizeof editedhost - (res - editedhost));
415     else
416 	*res = '\0';
417     editedhost[sizeof editedhost - 1] = '\0';
418 }
419 
420 static char *putlocation;
421 
422 void
423 putstr(char *s)
424 {
425 
426     while (*s)
427 	putchr(*s++);
428 }
429 
430 void
431 putchr(int cc)
432 {
433     *putlocation++ = cc;
434 }
435 
436 static char fmtstr[] = { "%l:%M%P on %A, %d %B %Y" };
437 
438 void putf(char *cp, char *where)
439 {
440 #ifdef HAVE_UNAME
441     struct utsname name;
442 #endif
443     char *slash;
444     time_t t;
445     char db[100];
446 
447     /* if we don't have uname, set these to sensible values */
448     char *sysname = "Unix",
449 	*machine = "",
450 	*release = "",
451 	*version = "";
452 
453 #ifdef HAVE_UNAME
454     uname(&name);
455     sysname=name.sysname;
456     machine=name.machine;
457     release=name.release;
458     version=name.version;
459 #endif
460 
461     putlocation = where;
462 
463     while (*cp) {
464 	if (*cp != '%') {
465 	    putchr(*cp++);
466 	    continue;
467 	}
468 	switch (*++cp) {
469 
470 	case 't':
471 	    slash = strchr(line+1, '/');
472 	    if (slash == (char *) 0)
473 		putstr(line);
474 	    else
475 		putstr(&slash[1]);
476 	    break;
477 
478 	case 'h':
479 	    putstr(editedhost);
480 	    break;
481 
482 	case 's':
483 	    putstr(sysname);
484 	    break;
485 
486 	case 'm':
487 	    putstr(machine);
488 	    break;
489 
490 	case 'r':
491 	    putstr(release);
492 	    break;
493 
494 	case 'v':
495 	    putstr(version);
496 	    break;
497 
498 	case 'd':
499 	    time(&t);
500 	    strftime(db, sizeof(db), fmtstr, localtime(&t));
501 	    putstr(db);
502 	    break;
503 
504 	case '%':
505 	    putchr('%');
506 	    break;
507 	}
508 	cp++;
509     }
510 }
511 
512 #ifdef DIAGNOSTICS
513 /*
514  * Print telnet options and commands in plain text, if possible.
515  */
516 void
517 printoption(char *fmt, int option)
518 {
519     if (TELOPT_OK(option))
520 	output_data("%s %s\r\n",
521 		    fmt,
522 		    TELOPT(option));
523     else if (TELCMD_OK(option))
524 	output_data("%s %s\r\n",
525 		    fmt,
526 		    TELCMD(option));
527     else
528 	output_data("%s %d\r\n",
529 		    fmt,
530 		    option);
531     return;
532 }
533 
534 void
535 printsub(int direction, unsigned char *pointer, size_t length)
536         		          	/* '<' or '>' */
537                  	         	/* where suboption data sits */
538        			       		/* length of suboption data */
539 {
540     int i = 0;
541     unsigned char buf[512];
542 
543     if (!(diagnostic & TD_OPTIONS))
544 	return;
545 
546     if (direction) {
547 	output_data("td: %s suboption ",
548 		    direction == '<' ? "recv" : "send");
549 	if (length >= 3) {
550 	    int j;
551 
552 	    i = pointer[length-2];
553 	    j = pointer[length-1];
554 
555 	    if (i != IAC || j != SE) {
556 		output_data("(terminated by ");
557 		if (TELOPT_OK(i))
558 		    output_data("%s ",
559 				TELOPT(i));
560 		else if (TELCMD_OK(i))
561 		    output_data("%s ",
562 				TELCMD(i));
563 		else
564 		    output_data("%d ",
565 				i);
566 		if (TELOPT_OK(j))
567 		    output_data("%s",
568 				TELOPT(j));
569 		else if (TELCMD_OK(j))
570 		    output_data("%s",
571 				TELCMD(j));
572 		else
573 		    output_data("%d",
574 				j);
575 		output_data(", not IAC SE!) ");
576 	    }
577 	}
578 	length -= 2;
579     }
580     if (length < 1) {
581 	output_data("(Empty suboption??\?)");
582 	return;
583     }
584     switch (pointer[0]) {
585     case TELOPT_TTYPE:
586 	output_data("TERMINAL-TYPE ");
587 	switch (pointer[1]) {
588 	case TELQUAL_IS:
589 	    output_data("IS \"%.*s\"",
590 			(int)(length-2),
591 			(char *)pointer+2);
592 	    break;
593 	case TELQUAL_SEND:
594 	    output_data("SEND");
595 	    break;
596 	default:
597 	    output_data("- unknown qualifier %d (0x%x).",
598 			pointer[1], pointer[1]);
599 	}
600 	break;
601     case TELOPT_TSPEED:
602 	output_data("TERMINAL-SPEED");
603 	if (length < 2) {
604 	    output_data(" (empty suboption??\?)");
605 	    break;
606 	}
607 	switch (pointer[1]) {
608 	case TELQUAL_IS:
609 	    output_data(" IS %.*s", (int)(length-2), (char *)pointer+2);
610 	    break;
611 	default:
612 	    if (pointer[1] == 1)
613 		output_data(" SEND");
614 	    else
615 		output_data(" %d (unknown)", pointer[1]);
616 	    for (i = 2; i < length; i++) {
617 		output_data(" ?%d?", pointer[i]);
618 	    }
619 	    break;
620 	}
621 	break;
622 
623     case TELOPT_LFLOW:
624 	output_data("TOGGLE-FLOW-CONTROL");
625 	if (length < 2) {
626 	    output_data(" (empty suboption??\?)");
627 	    break;
628 	}
629 	switch (pointer[1]) {
630 	case LFLOW_OFF:
631 	    output_data(" OFF");
632 	    break;
633 	case LFLOW_ON:
634 	    output_data(" ON");
635 	    break;
636 	case LFLOW_RESTART_ANY:
637 	    output_data(" RESTART-ANY");
638 	    break;
639 	case LFLOW_RESTART_XON:
640 	    output_data(" RESTART-XON");
641 	    break;
642 	default:
643 	    output_data(" %d (unknown)",
644 			pointer[1]);
645 	}
646 	for (i = 2; i < length; i++) {
647 	    output_data(" ?%d?",
648 			pointer[i]);
649 	}
650 	break;
651 
652     case TELOPT_NAWS:
653 	output_data("NAWS");
654 	if (length < 2) {
655 	    output_data(" (empty suboption??\?)");
656 	    break;
657 	}
658 	if (length == 2) {
659 	    output_data(" ?%d?",
660 			pointer[1]);
661 	    break;
662 	}
663 	output_data(" %u %u(%u)",
664 		    pointer[1],
665 		    pointer[2],
666 		    (((unsigned int)pointer[1])<<8) + pointer[2]);
667 	if (length == 4) {
668 	    output_data(" ?%d?",
669 			pointer[3]);
670 	    break;
671 	}
672 	output_data(" %u %u(%u)",
673 		    pointer[3],
674 		    pointer[4],
675 		    (((unsigned int)pointer[3])<<8) + pointer[4]);
676 	for (i = 5; i < length; i++) {
677 	    output_data(" ?%d?",
678 			pointer[i]);
679 	}
680 	break;
681 
682     case TELOPT_LINEMODE:
683 	output_data("LINEMODE ");
684 	if (length < 2) {
685 	    output_data(" (empty suboption??\?)");
686 	    break;
687 	}
688 	switch (pointer[1]) {
689 	case WILL:
690 	    output_data("WILL ");
691 	    goto common;
692 	case WONT:
693 	    output_data("WONT ");
694 	    goto common;
695 	case DO:
696 	    output_data("DO ");
697 	    goto common;
698 	case DONT:
699 	    output_data("DONT ");
700 	common:
701 	    if (length < 3) {
702 		output_data("(no option??\?)");
703 		break;
704 	    }
705 	    switch (pointer[2]) {
706 	    case LM_FORWARDMASK:
707 		output_data("Forward Mask");
708 		for (i = 3; i < length; i++) {
709 		    output_data(" %x", pointer[i]);
710 		}
711 		break;
712 	    default:
713 		output_data("%d (unknown)",
714 			    pointer[2]);
715 		for (i = 3; i < length; i++) {
716 		    output_data(" %d",
717 				pointer[i]);
718 		}
719 		break;
720 	    }
721 	    break;
722 
723 	case LM_SLC:
724 	    output_data("SLC");
725 	    for (i = 2; i < length - 2; i += 3) {
726 		if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
727 		    output_data(" %s",
728 				SLC_NAME(pointer[i+SLC_FUNC]));
729 		else
730 		    output_data(" %d",
731 				pointer[i+SLC_FUNC]);
732 		switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
733 		case SLC_NOSUPPORT:
734 		    output_data(" NOSUPPORT");
735 		    break;
736 		case SLC_CANTCHANGE:
737 		    output_data(" CANTCHANGE");
738 		    break;
739 		case SLC_VARIABLE:
740 		    output_data(" VARIABLE");
741 		    break;
742 		case SLC_DEFAULT:
743 		    output_data(" DEFAULT");
744 		    break;
745 		}
746 		output_data("%s%s%s",
747 			    pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
748 			    pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
749 			    pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
750 		if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
751 					    SLC_FLUSHOUT| SLC_LEVELBITS)) {
752 		    output_data("(0x%x)",
753 				pointer[i+SLC_FLAGS]);
754 		}
755 		output_data(" %d;",
756 			    pointer[i+SLC_VALUE]);
757 		if ((pointer[i+SLC_VALUE] == IAC) &&
758 		    (pointer[i+SLC_VALUE+1] == IAC))
759 		    i++;
760 	    }
761 	    for (; i < length; i++) {
762 		output_data(" ?%d?",
763 			    pointer[i]);
764 	    }
765 	    break;
766 
767 	case LM_MODE:
768 	    output_data("MODE ");
769 	    if (length < 3) {
770 		output_data("(no mode??\?)");
771 		break;
772 	    }
773 	    {
774 		char tbuf[32];
775 		snprintf(tbuf,
776 			 sizeof(tbuf),
777 			 "%s%s%s%s%s",
778 			 pointer[2]&MODE_EDIT ? "|EDIT" : "",
779 			 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
780 			 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
781 			 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
782 			 pointer[2]&MODE_ACK ? "|ACK" : "");
783 		output_data("%s",
784 			    tbuf[1] ? &tbuf[1] : "0");
785 	    }
786 	    if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
787 		output_data(" (0x%x)",
788 			    pointer[2]);
789 	    }
790 	    for (i = 3; i < length; i++) {
791 		output_data(" ?0x%x?",
792 			 pointer[i]);
793 	    }
794 	    break;
795 	default:
796 	    output_data("%d (unknown)",
797 			pointer[1]);
798 	    for (i = 2; i < length; i++) {
799 		output_data(" %d", pointer[i]);
800 	    }
801 	}
802 	break;
803 
804     case TELOPT_STATUS: {
805 	char *cp;
806 	int j, k;
807 
808 	output_data("STATUS");
809 
810 	switch (pointer[1]) {
811 	default:
812 	    if (pointer[1] == TELQUAL_SEND)
813 		output_data(" SEND");
814 	    else
815 		output_data(" %d (unknown)",
816 			    pointer[1]);
817 	    for (i = 2; i < length; i++) {
818 		output_data(" ?%d?",
819 			    pointer[i]);
820 	    }
821 	    break;
822 	case TELQUAL_IS:
823 	    output_data(" IS\r\n");
824 
825 	    for (i = 2; i < length; i++) {
826 		switch(pointer[i]) {
827 		case DO:	cp = "DO"; goto common2;
828 		case DONT:	cp = "DONT"; goto common2;
829 		case WILL:	cp = "WILL"; goto common2;
830 		case WONT:	cp = "WONT"; goto common2;
831 		common2:
832 		i++;
833 		if (TELOPT_OK(pointer[i]))
834 		    output_data(" %s %s",
835 				cp,
836 				TELOPT(pointer[i]));
837 		else
838 		    output_data(" %s %d",
839 				cp,
840 				pointer[i]);
841 
842 		output_data("\r\n");
843 		break;
844 
845 		case SB:
846 		    output_data(" SB ");
847 		    i++;
848 		    j = k = i;
849 		    while (j < length) {
850 			if (pointer[j] == SE) {
851 			    if (j+1 == length)
852 				break;
853 			    if (pointer[j+1] == SE)
854 				j++;
855 			    else
856 				break;
857 			}
858 			pointer[k++] = pointer[j++];
859 		    }
860 		    printsub(0, &pointer[i], k - i);
861 		    if (i < length) {
862 			output_data(" SE");
863 			i = j;
864 		    } else
865 			i = j - 1;
866 
867 		    output_data("\r\n");
868 
869 		    break;
870 
871 		default:
872 		    output_data(" %d",
873 				pointer[i]);
874 		    break;
875 		}
876 	    }
877 	    break;
878 	}
879 	break;
880     }
881 
882     case TELOPT_XDISPLOC:
883 	output_data("X-DISPLAY-LOCATION ");
884 	switch (pointer[1]) {
885 	case TELQUAL_IS:
886 	    output_data("IS \"%.*s\"",
887 			(int)(length-2),
888 			(char *)pointer+2);
889 	    break;
890 	case TELQUAL_SEND:
891 	    output_data("SEND");
892 	    break;
893 	default:
894 	    output_data("- unknown qualifier %d (0x%x).",
895 			pointer[1], pointer[1]);
896 	}
897 	break;
898 
899     case TELOPT_NEW_ENVIRON:
900 	output_data("NEW-ENVIRON ");
901 	goto env_common1;
902     case TELOPT_OLD_ENVIRON:
903 	output_data("OLD-ENVIRON");
904     env_common1:
905 	switch (pointer[1]) {
906 	case TELQUAL_IS:
907 	    output_data("IS ");
908 	    goto env_common;
909 	case TELQUAL_SEND:
910 	    output_data("SEND ");
911 	    goto env_common;
912 	case TELQUAL_INFO:
913 	    output_data("INFO ");
914 	env_common:
915 	    {
916 		int quote = 0;
917 		for (i = 2; i < length; i++ ) {
918 		    switch (pointer[i]) {
919 		    case NEW_ENV_VAR:
920 			if (quote)
921 			    output_data("\" ");
922 			output_data("VAR ");
923 			quote = 0;
924 			break;
925 
926 		    case NEW_ENV_VALUE:
927 			if (quote)
928 			    output_data("\" ");
929 			output_data("VALUE ");
930 			quote = 0;
931 			break;
932 
933 		    case ENV_ESC:
934 			if (quote)
935 			    output_data("\" ");
936 			output_data("ESC ");
937 			quote = 0;
938 			break;
939 
940 		    case ENV_USERVAR:
941 			if (quote)
942 			    output_data("\" ");
943 			output_data("USERVAR ");
944 			quote = 0;
945 			break;
946 
947 		    default:
948 			if (isprint(pointer[i]) && pointer[i] != '"') {
949 			    if (!quote) {
950 				output_data("\"");
951 				quote = 1;
952 			    }
953 			    output_data("%c", pointer[i]);
954 			} else {
955 			    output_data("%03o ", pointer[i]);
956 			    quote = 0;
957 			}
958 			break;
959 		    }
960 		}
961 		if (quote)
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 			(int)(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, size_t cnt)
1141 {
1142     size_t 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