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