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