xref: /netbsd-src/libexec/telnetd/utility.c (revision 1c9b56c830954ccf3b57004ac65562e3d6afacf6)
1 /*	$NetBSD: utility.c,v 1.27 2005/02/06 17:30:33 agc Exp $	*/
2 
3 /*
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)utility.c	8.4 (Berkeley) 5/30/95";
36 #else
37 __RCSID("$NetBSD: utility.c,v 1.27 2005/02/06 17:30:33 agc Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/utsname.h>
42 #define PRINTOPTIONS
43 #include "telnetd.h"
44 
45 char *nextitem(char *);
46 void putstr(char *);
47 
48 extern int not42;
49 
50 /*
51  * utility functions performing io related tasks
52  */
53 
54 /*
55  * ttloop
56  *
57  *	A small subroutine to flush the network output buffer, get some data
58  * from the network, and pass it through the telnet state machine.  We
59  * also flush the pty input buffer (by dropping its data) if it becomes
60  * too full.
61  */
62 
63 void
64 ttloop(void)
65 {
66 
67     DIAG(TD_REPORT, {output_data("td: ttloop\r\n");});
68     if (nfrontp - nbackp) {
69 	netflush();
70     }
71     ncc = read(net, netibuf, sizeof netibuf);
72     if (ncc < 0) {
73 	syslog(LOG_ERR, "ttloop:  read: %m");
74 	exit(1);
75     } else if (ncc == 0) {
76 	syslog(LOG_INFO, "ttloop:  peer died: %m");
77 	exit(1);
78     }
79     DIAG(TD_REPORT, {output_data("td: ttloop read %d chars\r\n", ncc);});
80     netip = netibuf;
81     telrcv();			/* state machine */
82     if (ncc > 0) {
83 	pfrontp = pbackp = ptyobuf;
84 	telrcv();
85     }
86 }  /* end of ttloop */
87 
88 /*
89  * Check a descriptor to see if out of band data exists on it.
90  */
91 int
92 stilloob(int s /* socket number */)
93 {
94     struct pollfd set[1];
95     int value;
96 
97     set[0].fd = s;
98     set[0].events = POLLPRI;
99     do {
100 	value = poll(set, 1, 0);
101     } while ((value == -1) && (errno == EINTR));
102 
103     if (value < 0) {
104 	fatalperror(pty, "poll");
105     }
106     if (set[0].revents & POLLPRI) {
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 		DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
122 		n = write(pty, 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:		/* loop forever looking for the SE */
157 	{
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 }  /* end of nextitem */
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 /* ENCRYPTION */
201     thisitem = netobuf;
202 #endif	/* ENCRYPTION */
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 /* ENCRYPTION */
213     good = netobuf;	/* where the good bytes go */
214 #endif	/* ENCRYPTION */
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 
248     if ((n = nfrontp - nbackp) > 0) {
249 	DIAG(TD_REPORT,
250 	    { output_data("td: netflush %d chars\r\n", n);
251 	      n = nfrontp - nbackp;	/* re-compute count */
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	/* ENCRYPTION */
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 ((neturg == 0) || (not42 == 0)) {
268 	    n = write(net, nbackp, n);	/* normal write */
269 	} else {
270 	    n = neturg - nbackp;
271 	    /*
272 	     * In 4.2 (and 4.3) systems, there is some question about
273 	     * what byte in a sendOOB operation is the "OOB" data.
274 	     * To make ourselves compatible, we only send ONE byte
275 	     * out of band, the one WE THINK should be OOB (though
276 	     * we really have more the TCP philosophy of urgent data
277 	     * rather than the Unix philosophy of OOB data).
278 	     */
279 	    if (n > 1) {
280 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
281 	    } else {
282 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
283 	    }
284 	}
285     }
286     if (n < 0) {
287 	if (errno == EWOULDBLOCK || errno == EINTR)
288 		return;
289 	cleanup(0);
290     }
291     nbackp += n;
292 #ifdef	ENCRYPTION
293     if (nbackp > nclearto)
294 	nclearto = 0;
295 #endif	/* ENCRYPTION */
296     if (nbackp >= neturg) {
297 	neturg = 0;
298     }
299     if (nbackp == nfrontp) {
300 	nbackp = nfrontp = netobuf;
301 #ifdef	ENCRYPTION
302 	nclearto = 0;
303 #endif	/* ENCRYPTION */
304     }
305     return;
306 }  /* end of netflush */
307 
308 
309 /*
310  * writenet
311  *
312  * Just a handy little function to write a bit of raw data to the net.
313  * It will force a transmit of the buffer if necessary
314  *
315  * arguments
316  *    ptr - A pointer to a character string to write
317  *    len - How many bytes to write
318  */
319 void
320 writenet(unsigned char *ptr, int len)
321 {
322 	/* flush buffer if no room for new data) */
323 	if ((&netobuf[BUFSIZ] - nfrontp) < len) {
324 		/* if this fails, don't worry, buffer is a little big */
325 		netflush();
326 	}
327 
328 	memmove(nfrontp, ptr, len);
329 	nfrontp += len;
330 
331 }  /* end of writenet */
332 
333 
334 /*
335  * miscellaneous functions doing a variety of little jobs follow ...
336  */
337 void
338 fatal(int f, const char *msg)
339 {
340 	char buf[BUFSIZ];
341 
342 	(void)snprintf(buf, sizeof buf, "telnetd: %s.\r\n", msg);
343 #ifdef	ENCRYPTION
344 	if (encrypt_output) {
345 		/*
346 		 * Better turn off encryption first....
347 		 * Hope it flushes...
348 		 */
349 		encrypt_send_end();
350 		netflush();
351 	}
352 #endif	/* ENCRYPTION */
353 	(void)write(f, buf, (int)strlen(buf));
354 	sleep(1);	/*XXX*/
355 	exit(1);
356 }
357 
358 void
359 fatalperror(f, msg)
360 	int f;
361 	const char *msg;
362 {
363 	char buf[BUFSIZ];
364 
365 	(void)snprintf(buf, sizeof buf, "%s: %s", msg, strerror(errno));
366 	fatal(f, buf);
367 }
368 
369 char editedhost[MAXHOSTNAMELEN];
370 
371 void
372 edithost(char *pat, char *host)
373 {
374 	char *res = editedhost;
375 
376 	if (!pat)
377 		pat = "";
378 	while (*pat) {
379 		switch (*pat) {
380 
381 		case '#':
382 			if (*host)
383 				host++;
384 			break;
385 
386 		case '@':
387 			if (*host)
388 				*res++ = *host++;
389 			break;
390 
391 		default:
392 			*res++ = *pat;
393 			break;
394 		}
395 		if (res == &editedhost[sizeof editedhost - 1]) {
396 			*res = '\0';
397 			return;
398 		}
399 		pat++;
400 	}
401 	if (*host)
402 		(void) strncpy(res, host,
403 		    sizeof editedhost - (res - editedhost) -1);
404 	else
405 		*res = '\0';
406 	editedhost[sizeof editedhost - 1] = '\0';
407 }
408 
409 static char *putlocation;
410 
411 void
412 putstr(char *s)
413 {
414 
415 	while (*s)
416 		putchr(*s++);
417 }
418 
419 void
420 putchr(int cc)
421 {
422 	*putlocation++ = cc;
423 }
424 
425 /*
426  * This is split on two lines so that SCCS will not see the M
427  * between two % signs and expand it...
428  */
429 static char fmtstr[] = { "%l:%M\
430 %p on %A, %d %B %Y" };
431 
432 char *
433 putf(char *cp, char *where)
434 {
435 	char *slash;
436 	time_t t;
437 	char db[100];
438 	struct utsname utsinfo;
439 
440 	uname(&utsinfo);
441 
442 	putlocation = where;
443 
444 	while (*cp) {
445 		if (*cp != '%') {
446 			putchr(*cp++);
447 			continue;
448 		}
449 		switch (*++cp) {
450 
451 		case 't':
452 			slash = strrchr(line, '/');
453 			if (slash == (char *) 0)
454 				putstr(line);
455 			else
456 				putstr(&slash[1]);
457 			break;
458 
459 		case 'h':
460 			putstr(editedhost);
461 			break;
462 
463 		case 'd':
464 			(void)time(&t);
465 			(void)strftime(db, sizeof(db), fmtstr, localtime(&t));
466 			putstr(db);
467 			break;
468 
469 		case '%':
470 			putchr('%');
471 			break;
472 
473 		case 's':
474 			putstr(utsinfo.sysname);
475 			break;
476 
477 		case 'm':
478 			putstr(utsinfo.machine);
479 			break;
480 
481 		case 'r':
482 			putstr(utsinfo.release);
483 			break;
484 
485 		case 'v':
486 			putstr(utsinfo.version);
487                         break;
488 		}
489 		cp++;
490 	}
491 
492 	return (putlocation);
493 }
494 
495 #ifdef DIAGNOSTICS
496 /*
497  * Print telnet options and commands in plain text, if possible.
498  */
499 void
500 printoption(const char *fmt, int option)
501 {
502 	if (TELOPT_OK(option))
503 		output_data("%s %s\r\n", fmt, TELOPT(option));
504 	else if (TELCMD_OK(option))
505 		output_data("%s %s\r\n", fmt, TELCMD(option));
506 	else
507 		output_data("%s %d\r\n", fmt, option);
508 	return;
509 }
510 
511 void
512 printsub(
513     int	   direction,	/* '<' or '>' */
514     unsigned char *pointer,	/* where suboption data sits */
515     int		   length)	/* length of suboption data */
516 {
517     int i = 0;
518 #if	defined(AUTHENTICATION) || defined(ENCRYPTION)
519     char buf[512];
520 #endif
521 
522 	if (!(diagnostic & TD_OPTIONS))
523 		return;
524 
525 	if (direction) {
526 	    output_data("td: %s suboption ",
527 	        direction == '<' ? "recv" : "send");
528 	    if (length >= 3) {
529 		int j;
530 
531 		i = pointer[length - 2];
532 		j = pointer[length - 1];
533 
534 		if (i != IAC || j != SE) {
535 		    output_data("(terminated by ");
536 		    if (TELOPT_OK(i))
537 			output_data("%s ", TELOPT(i));
538 		    else if (TELCMD_OK(i))
539 			output_data("%s ", TELCMD(i));
540 		    else
541 			output_data("%d ", i);
542 		    if (TELOPT_OK(j))
543 			output_data("%s", TELOPT(j));
544 		    else if (TELCMD_OK(j))
545 			output_data("%s", TELCMD(j));
546 		    else
547 			output_data("%d", j);
548 		    output_data(", not IAC SE!) ");
549 		}
550 	    }
551 	    length -= 2;
552 	}
553 	if (length < 1) {
554 	    output_data("(Empty suboption??\?)");
555 	    return;
556 	}
557 	switch (pointer[0]) {
558 	case TELOPT_TTYPE:
559 	    output_data("TERMINAL-TYPE ");
560 	    switch (pointer[1]) {
561 	    case TELQUAL_IS:
562 		output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
563 		break;
564 	    case TELQUAL_SEND:
565 		output_data("SEND");
566 		break;
567 	    default:
568 		output_data("- unknown qualifier %d (0x%x).",
569 		    pointer[1], pointer[1]);
570 	    }
571 	    break;
572 	case TELOPT_TSPEED:
573 	    output_data("TERMINAL-SPEED");
574 	    if (length < 2) {
575 		output_data(" (empty suboption??\?)");
576 		break;
577 	    }
578 	    switch (pointer[1]) {
579 	    case TELQUAL_IS:
580 		output_data(" IS %.*s", length-2, (char *)pointer+2);
581 		break;
582 	    default:
583 		if (pointer[1] == 1)
584 		    output_data(" SEND");
585 		else
586 		    output_data(" %d (unknown)", pointer[1]);
587 		for (i = 2; i < length; i++) {
588 		    output_data(" ?%d?", pointer[i]);
589 		}
590 		break;
591 	    }
592 	    break;
593 
594 	case TELOPT_LFLOW:
595 	    output_data("TOGGLE-FLOW-CONTROL");
596 	    if (length < 2) {
597 		output_data(" (empty suboption??\?)");
598 		break;
599 	    }
600 	    switch (pointer[1]) {
601 	    case LFLOW_OFF:
602 		output_data(" OFF"); break;
603 	    case LFLOW_ON:
604 		output_data(" ON"); break;
605 	    case LFLOW_RESTART_ANY:
606 		output_data(" RESTART-ANY"); break;
607 	    case LFLOW_RESTART_XON:
608 		output_data(" RESTART-XON"); break;
609 	    default:
610 		output_data(" %d (unknown)", pointer[1]);
611 	    }
612 	    for (i = 2; i < length; i++)
613 		output_data(" ?%d?", pointer[i]);
614 	    break;
615 
616 	case TELOPT_NAWS:
617 	    output_data("NAWS");
618 	    if (length < 2) {
619 		output_data(" (empty suboption??\?)");
620 		break;
621 	    }
622 	    if (length == 2) {
623 		output_data(" ?%d?", pointer[1]);
624 		break;
625 	    }
626 	    output_data(" %d %d (%d)",
627 		pointer[1], pointer[2],
628 		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
629 	    if (length == 4) {
630 		output_data(" ?%d?", pointer[3]);
631 		break;
632 	    }
633 	    output_data(" %d %d (%d)", pointer[3], pointer[4],
634 		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
635 	    for (i = 5; i < length; i++) {
636 		output_data(" ?%d?", pointer[i]);
637 	    }
638 	    break;
639 
640 	case TELOPT_LINEMODE:
641 	    output_data("LINEMODE ");
642 	    if (length < 2) {
643 		output_data(" (empty suboption??\?)");
644 		break;
645 	    }
646 	    switch (pointer[1]) {
647 	    case WILL:
648 		output_data("WILL ");
649 		goto common;
650 	    case WONT:
651 		output_data("WONT ");
652 		goto common;
653 	    case DO:
654 		output_data("DO ");
655 		goto common;
656 	    case DONT:
657 		output_data("DONT ");
658 	    common:
659 		if (length < 3) {
660 		    output_data("(no option??\?)");
661 		    break;
662 		}
663 		switch (pointer[2]) {
664 		case LM_FORWARDMASK:
665 		    output_data("Forward Mask");
666 		    for (i = 3; i < length; i++)
667 			output_data(" %x", pointer[i]);
668 		    break;
669 		default:
670 		    output_data("%d (unknown)", pointer[2]);
671 		    for (i = 3; i < length; i++)
672 			output_data(" %d", pointer[i]);
673 		    break;
674 		}
675 		break;
676 
677 	    case LM_SLC:
678 		output_data("SLC");
679 		for (i = 2; i < length - 2; i += 3) {
680 		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
681 			output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC]));
682 		    else
683 			output_data(" %d", pointer[i+SLC_FUNC]);
684 		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
685 		    case SLC_NOSUPPORT:
686 			output_data(" NOSUPPORT"); break;
687 		    case SLC_CANTCHANGE:
688 			output_data(" CANTCHANGE"); break;
689 		    case SLC_VARIABLE:
690 			output_data(" VARIABLE"); break;
691 		    case SLC_DEFAULT:
692 			output_data(" DEFAULT"); break;
693 		    }
694 		    output_data("%s%s%s",
695 			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
696 			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
697 			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
698 		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
699 						SLC_FLUSHOUT| SLC_LEVELBITS)) {
700 			output_data("(0x%x)", pointer[i+SLC_FLAGS]);
701 		    }
702 		    output_data(" %d;", pointer[i+SLC_VALUE]);
703 		    if ((pointer[i+SLC_VALUE] == IAC) &&
704 			(pointer[i+SLC_VALUE+1] == IAC))
705 				i++;
706 		}
707 		for (; i < length; i++)
708 		    output_data(" ?%d?", pointer[i]);
709 		break;
710 
711 	    case LM_MODE:
712 		output_data("MODE ");
713 		if (length < 3) {
714 		    output_data("(no mode??\?)");
715 		    break;
716 		}
717 		{
718 		    char tbuf[32];
719 
720 		    (void)snprintf(tbuf, sizeof tbuf, "%s%s%s%s%s",
721 			pointer[2]&MODE_EDIT ? "|EDIT" : "",
722 			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
723 			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
724 			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
725 			pointer[2]&MODE_ACK ? "|ACK" : "");
726 		    output_data("%s", tbuf[1] ? &tbuf[1] : "0");
727 		}
728 		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK))
729 		    output_data(" (0x%x)", pointer[2]);
730 		for (i = 3; i < length; i++)
731 		    output_data(" ?0x%x?", pointer[i]);
732 		break;
733 	    default:
734 		output_data("%d (unknown)", pointer[1]);
735 		for (i = 2; i < length; i++)
736 		    output_data(" %d", pointer[i]);
737 	    }
738 	    break;
739 
740 	case TELOPT_STATUS: {
741 	    char *cp;
742 	    int j, k;
743 
744 	    output_data("STATUS");
745 
746 	    switch (pointer[1]) {
747 	    default:
748 		if (pointer[1] == TELQUAL_SEND)
749 		    output_data(" SEND");
750 		else
751 		    output_data(" %d (unknown)", pointer[1]);
752 		for (i = 2; i < length; i++)
753 		    output_data(" ?%d?", pointer[i]);
754 		break;
755 	    case TELQUAL_IS:
756 		output_data(" IS\r\n");
757 
758 		for (i = 2; i < length; i++) {
759 		    switch(pointer[i]) {
760 		    case DO:	cp = "DO"; goto common2;
761 		    case DONT:	cp = "DONT"; goto common2;
762 		    case WILL:	cp = "WILL"; goto common2;
763 		    case WONT:	cp = "WONT"; goto common2;
764 		    common2:
765 			i++;
766 			if (TELOPT_OK(pointer[i]))
767 			    output_data(" %s %s", cp, TELOPT(pointer[i]));
768 			else
769 			    output_data(" %s %d", cp, pointer[i]);
770 
771 			output_data("\r\n");
772 			break;
773 
774 		    case SB:
775 			output_data(" SB ");
776 			i++;
777 			j = k = i;
778 			while (j < length) {
779 			    if (pointer[j] == SE) {
780 				if (j+1 == length)
781 				    break;
782 				if (pointer[j+1] == SE)
783 				    j++;
784 				else
785 				    break;
786 			    }
787 			    pointer[k++] = pointer[j++];
788 			}
789 			printsub(0, &pointer[i], k - i);
790 			if (i < length) {
791 			    output_data(" SE");
792 			    i = j;
793 			} else
794 			    i = j - 1;
795 
796 			output_data("\r\n");
797 
798 			break;
799 
800 		    default:
801 			output_data(" %d", pointer[i]);
802 			break;
803 		    }
804 		}
805 		break;
806 	    }
807 	    break;
808 	  }
809 
810 	case TELOPT_XDISPLOC:
811 	    output_data("X-DISPLAY-LOCATION ");
812 	    switch (pointer[1]) {
813 	    case TELQUAL_IS:
814 		output_data("IS \"%.*s\"", length - 2, (char *)pointer + 2);
815 		break;
816 	    case TELQUAL_SEND:
817 		output_data("SEND");
818 		break;
819 	    default:
820 		output_data("- unknown qualifier %d (0x%x).",
821 		    pointer[1], pointer[1]);
822 	    }
823 	    break;
824 
825 	case TELOPT_NEW_ENVIRON:
826 	    output_data("NEW-ENVIRON ");
827 	    goto env_common1;
828 	case TELOPT_OLD_ENVIRON:
829 	    output_data("OLD-ENVIRON");
830 	env_common1:
831 	    switch (pointer[1]) {
832 	    case TELQUAL_IS:
833 		output_data("IS ");
834 		goto env_common;
835 	    case TELQUAL_SEND:
836 		output_data("SEND ");
837 		goto env_common;
838 	    case TELQUAL_INFO:
839 		output_data("INFO ");
840 	    env_common:
841 		{
842 		    int noquote = 2;
843 		    for (i = 2; i < length; i++ ) {
844 			switch (pointer[i]) {
845 			case NEW_ENV_VAR:
846 			    output_data("%s", "\" VAR " + noquote);
847 			    noquote = 2;
848 			    break;
849 
850 			case NEW_ENV_VALUE:
851 			    output_data("%s", "\" VALUE " + noquote);
852 			    noquote = 2;
853 			    break;
854 
855 			case ENV_ESC:
856 			    output_data("%s", "\" ESC " + noquote);
857 			    noquote = 2;
858 			    break;
859 
860 			case ENV_USERVAR:
861 			    output_data("%s", "\" USERVAR " + noquote);
862 			    noquote = 2;
863 			    break;
864 
865 			default:
866 			    if (isprint(pointer[i]) && pointer[i] != '"') {
867 				if (noquote) {
868 				    output_data("\"");
869 				    noquote = 0;
870 				}
871 				output_data("%c", pointer[i]);
872 			    } else {
873 				output_data("\" %03o " + noquote, 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 		auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
914 		output_data("%s", buf);
915 		break;
916 
917 	    case TELQUAL_SEND:
918 		i = 2;
919 		output_data(" SEND ");
920 		while (i < length) {
921 		    if (AUTHTYPE_NAME_OK(pointer[i]))
922 			output_data("%s ", AUTHTYPE_NAME(pointer[i]));
923 		    else
924 			output_data("%d ", pointer[i]);
925 		    if (++i >= length) {
926 			output_data("(partial suboption??\?)");
927 			break;
928 		    }
929 		    output_data("%s|%s ",
930 			((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
931 							"CLIENT" : "SERVER",
932 			((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
933 							"MUTUAL" : "ONE-WAY");
934 		    ++i;
935 		}
936 		break;
937 
938 	    case TELQUAL_NAME:
939 		i = 2;
940 		output_data(" NAME \"");
941 		while (i < length) {
942 		    if (isprint(pointer[i]))
943 			output_data("%c", pointer[i++]);
944 		    else
945 			output_data("\"%03o\"",pointer[i++]);
946 		}
947 		output_data("\"");
948 		break;
949 
950 	    default:
951 		for (i = 2; i < length; i++)
952 		    output_data(" ?%d?", pointer[i]);
953 		break;
954 	    }
955 	    break;
956 #endif
957 
958 #ifdef	ENCRYPTION
959 	case TELOPT_ENCRYPT:
960 	    output_data("ENCRYPT");
961 	    if (length < 2) {
962 		output_data(" (empty suboption??\?)");
963 		break;
964 	    }
965 	    switch (pointer[1]) {
966 	    case ENCRYPT_START:
967 		output_data(" START");
968 		break;
969 
970 	    case ENCRYPT_END:
971 		output_data(" END");
972 		break;
973 
974 	    case ENCRYPT_REQSTART:
975 		output_data(" REQUEST-START");
976 		break;
977 
978 	    case ENCRYPT_REQEND:
979 		output_data(" REQUEST-END");
980 		break;
981 
982 	    case ENCRYPT_IS:
983 	    case ENCRYPT_REPLY:
984 		output_data(" %s ", (pointer[1] == ENCRYPT_IS) ?
985 		    "IS" : "REPLY");
986 		if (length < 3) {
987 			output_data(" (partial suboption??\?)");
988 			break;
989 		}
990 		if (ENCTYPE_NAME_OK(pointer[2]))
991 			output_data("%s ", ENCTYPE_NAME(pointer[2]));
992 		else
993 			output_data(" %d (unknown)", pointer[2]);
994 
995 		encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
996 		output_data("%s", buf);
997 		break;
998 
999 	    case ENCRYPT_SUPPORT:
1000 		i = 2;
1001 		output_data(" SUPPORT ");
1002 		while (i < length) {
1003 			if (ENCTYPE_NAME_OK(pointer[i]))
1004 				output_data("%s ", ENCTYPE_NAME(pointer[i]));
1005 			else
1006 				output_data("%d ", pointer[i]);
1007 			i++;
1008 		}
1009 		break;
1010 
1011 	    case ENCRYPT_ENC_KEYID:
1012 		output_data(" ENC_KEYID");
1013 		goto encommon;
1014 
1015 	    case ENCRYPT_DEC_KEYID:
1016 		output_data(" DEC_KEYID");
1017 		goto encommon;
1018 
1019 	    default:
1020 		output_data(" %d (unknown)", pointer[1]);
1021 	    encommon:
1022 		for (i = 2; i < length; i++)
1023 			output_data(" %d", pointer[i]);
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 	    break;
1037 	}
1038 	output_data("\r\n");
1039 }
1040 
1041 /*
1042  * Dump a data buffer in hex and ascii to the output data stream.
1043  */
1044 void
1045 printdata(char *tag, char *ptr, int cnt)
1046 {
1047 	int i;
1048 	char xbuf[30];
1049 
1050 	while (cnt) {
1051 		/* flush net output buffer if no room for new data) */
1052 		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
1053 			netflush();
1054 		}
1055 
1056 		/* add a line of output */
1057 		output_data("%s: ", tag);
1058 		for (i = 0; i < 20 && cnt; i++) {
1059 			output_data("%02x", *ptr);
1060 			if (isprint((unsigned char)*ptr)) {
1061 				xbuf[i] = *ptr;
1062 			} else {
1063 				xbuf[i] = '.';
1064 			}
1065 			if (i % 2)
1066 				output_data(" ");
1067 			cnt--;
1068 			ptr++;
1069 		}
1070 		xbuf[i] = '\0';
1071 		output_data(" %s\r\n", xbuf);
1072 	}
1073 }
1074 #endif /* DIAGNOSTICS */
1075