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