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