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