xref: /original-bsd/libexec/telnetd/utility.c (revision 37071c60)
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.2 (Berkeley) 12/15/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 #ifdef	STREAMSPTY
426 	extern char *index();
427 #else
428 	extern char *rindex();
429 #endif
430 
431 	putlocation = where;
432 
433 	while (*cp) {
434 		if (*cp != '%') {
435 			putchr(*cp++);
436 			continue;
437 		}
438 		switch (*++cp) {
439 
440 		case 't':
441 #ifdef	STREAMSPTY
442 			/* names are like /dev/pts/2 -- we want pts/2 */
443 			slash = index(line+1, '/');
444 #else
445 			slash = rindex(line, '/');
446 #endif
447 			if (slash == (char *) 0)
448 				putstr(line);
449 			else
450 				putstr(&slash[1]);
451 			break;
452 
453 		case 'h':
454 			putstr(editedhost);
455 			break;
456 
457 		case 'd':
458 			(void)time(&t);
459 			(void)strftime(db, sizeof(db), fmtstr, localtime(&t));
460 			putstr(db);
461 			break;
462 
463 		case '%':
464 			putchr('%');
465 			break;
466 		}
467 		cp++;
468 	}
469 }
470 
471 #ifdef DIAGNOSTICS
472 /*
473  * Print telnet options and commands in plain text, if possible.
474  */
475 	void
476 printoption(fmt, option)
477 	register char *fmt;
478 	register int option;
479 {
480 	if (TELOPT_OK(option))
481 		sprintf(nfrontp, "%s %s\r\n", fmt, TELOPT(option));
482 	else if (TELCMD_OK(option))
483 		sprintf(nfrontp, "%s %s\r\n", fmt, TELCMD(option));
484 	else
485 		sprintf(nfrontp, "%s %d\r\n", fmt, option);
486 	nfrontp += strlen(nfrontp);
487 	return;
488 }
489 
490     void
491 printsub(direction, pointer, length)
492     char		direction;	/* '<' or '>' */
493     unsigned char	*pointer;	/* where suboption data sits */
494     int			length;		/* length of suboption data */
495 {
496     register int i;
497     char buf[512];
498 
499         if (!(diagnostic & TD_OPTIONS))
500 		return;
501 
502 	if (direction) {
503 	    sprintf(nfrontp, "td: %s suboption ",
504 					direction == '<' ? "recv" : "send");
505 	    nfrontp += strlen(nfrontp);
506 	    if (length >= 3) {
507 		register int j;
508 
509 		i = pointer[length-2];
510 		j = pointer[length-1];
511 
512 		if (i != IAC || j != SE) {
513 		    sprintf(nfrontp, "(terminated by ");
514 		    nfrontp += strlen(nfrontp);
515 		    if (TELOPT_OK(i))
516 			sprintf(nfrontp, "%s ", TELOPT(i));
517 		    else if (TELCMD_OK(i))
518 			sprintf(nfrontp, "%s ", TELCMD(i));
519 		    else
520 			sprintf(nfrontp, "%d ", i);
521 		    nfrontp += strlen(nfrontp);
522 		    if (TELOPT_OK(j))
523 			sprintf(nfrontp, "%s", TELOPT(j));
524 		    else if (TELCMD_OK(j))
525 			sprintf(nfrontp, "%s", TELCMD(j));
526 		    else
527 			sprintf(nfrontp, "%d", j);
528 		    nfrontp += strlen(nfrontp);
529 		    sprintf(nfrontp, ", not IAC SE!) ");
530 		    nfrontp += strlen(nfrontp);
531 		}
532 	    }
533 	    length -= 2;
534 	}
535 	if (length < 1) {
536 	    sprintf(nfrontp, "(Empty suboption??\?)");
537 	    nfrontp += strlen(nfrontp);
538 	    return;
539 	}
540 	switch (pointer[0]) {
541 	case TELOPT_TTYPE:
542 	    sprintf(nfrontp, "TERMINAL-TYPE ");
543 	    nfrontp += strlen(nfrontp);
544 	    switch (pointer[1]) {
545 	    case TELQUAL_IS:
546 		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
547 		break;
548 	    case TELQUAL_SEND:
549 		sprintf(nfrontp, "SEND");
550 		break;
551 	    default:
552 		sprintf(nfrontp,
553 				"- unknown qualifier %d (0x%x).",
554 				pointer[1], pointer[1]);
555 	    }
556 	    nfrontp += strlen(nfrontp);
557 	    break;
558 	case TELOPT_TSPEED:
559 	    sprintf(nfrontp, "TERMINAL-SPEED");
560 	    nfrontp += strlen(nfrontp);
561 	    if (length < 2) {
562 		sprintf(nfrontp, " (empty suboption??\?)");
563 		nfrontp += strlen(nfrontp);
564 		break;
565 	    }
566 	    switch (pointer[1]) {
567 	    case TELQUAL_IS:
568 		sprintf(nfrontp, " IS %.*s", length-2, (char *)pointer+2);
569 		nfrontp += strlen(nfrontp);
570 		break;
571 	    default:
572 		if (pointer[1] == 1)
573 		    sprintf(nfrontp, " SEND");
574 		else
575 		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
576 		nfrontp += strlen(nfrontp);
577 		for (i = 2; i < length; i++) {
578 		    sprintf(nfrontp, " ?%d?", pointer[i]);
579 		    nfrontp += strlen(nfrontp);
580 		}
581 		break;
582 	    }
583 	    break;
584 
585 	case TELOPT_LFLOW:
586 	    sprintf(nfrontp, "TOGGLE-FLOW-CONTROL");
587 	    nfrontp += strlen(nfrontp);
588 	    if (length < 2) {
589 		sprintf(nfrontp, " (empty suboption??\?)");
590 		nfrontp += strlen(nfrontp);
591 		break;
592 	    }
593 	    switch (pointer[1]) {
594 	    case LFLOW_OFF:
595 		sprintf(nfrontp, " OFF"); break;
596 	    case LFLOW_ON:
597 		sprintf(nfrontp, " ON"); break;
598 	    case LFLOW_RESTART_ANY:
599 		sprintf(nfrontp, " RESTART-ANY"); break;
600 	    case LFLOW_RESTART_XON:
601 		sprintf(nfrontp, " RESTART-XON"); break;
602 	    default:
603 		sprintf(nfrontp, " %d (unknown)", pointer[1]);
604 	    }
605 	    nfrontp += strlen(nfrontp);
606 	    for (i = 2; i < length; i++) {
607 		sprintf(nfrontp, " ?%d?", pointer[i]);
608 		nfrontp += strlen(nfrontp);
609 	    }
610 	    break;
611 
612 	case TELOPT_NAWS:
613 	    sprintf(nfrontp, "NAWS");
614 	    nfrontp += strlen(nfrontp);
615 	    if (length < 2) {
616 		sprintf(nfrontp, " (empty suboption??\?)");
617 		nfrontp += strlen(nfrontp);
618 		break;
619 	    }
620 	    if (length == 2) {
621 		sprintf(nfrontp, " ?%d?", pointer[1]);
622 		nfrontp += strlen(nfrontp);
623 		break;
624 	    }
625 	    sprintf(nfrontp, " %d %d (%d)",
626 		pointer[1], pointer[2],
627 		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
628 	    nfrontp += strlen(nfrontp);
629 	    if (length == 4) {
630 		sprintf(nfrontp, " ?%d?", pointer[3]);
631 		nfrontp += strlen(nfrontp);
632 		break;
633 	    }
634 	    sprintf(nfrontp, " %d %d (%d)",
635 		pointer[3], pointer[4],
636 		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
637 	    nfrontp += strlen(nfrontp);
638 	    for (i = 5; i < length; i++) {
639 		sprintf(nfrontp, " ?%d?", pointer[i]);
640 		nfrontp += strlen(nfrontp);
641 	    }
642 	    break;
643 
644 	case TELOPT_LINEMODE:
645 	    sprintf(nfrontp, "LINEMODE ");
646 	    nfrontp += strlen(nfrontp);
647 	    if (length < 2) {
648 		sprintf(nfrontp, " (empty suboption??\?)");
649 		nfrontp += strlen(nfrontp);
650 		break;
651 	    }
652 	    switch (pointer[1]) {
653 	    case WILL:
654 		sprintf(nfrontp, "WILL ");
655 		goto common;
656 	    case WONT:
657 		sprintf(nfrontp, "WONT ");
658 		goto common;
659 	    case DO:
660 		sprintf(nfrontp, "DO ");
661 		goto common;
662 	    case DONT:
663 		sprintf(nfrontp, "DONT ");
664 	    common:
665 		nfrontp += strlen(nfrontp);
666 		if (length < 3) {
667 		    sprintf(nfrontp, "(no option??\?)");
668 		    nfrontp += strlen(nfrontp);
669 		    break;
670 		}
671 		switch (pointer[2]) {
672 		case LM_FORWARDMASK:
673 		    sprintf(nfrontp, "Forward Mask");
674 		    nfrontp += strlen(nfrontp);
675 		    for (i = 3; i < length; i++) {
676 			sprintf(nfrontp, " %x", pointer[i]);
677 			nfrontp += strlen(nfrontp);
678 		    }
679 		    break;
680 		default:
681 		    sprintf(nfrontp, "%d (unknown)", pointer[2]);
682 		    nfrontp += strlen(nfrontp);
683 		    for (i = 3; i < length; i++) {
684 			sprintf(nfrontp, " %d", pointer[i]);
685 			nfrontp += strlen(nfrontp);
686 		    }
687 		    break;
688 		}
689 		break;
690 
691 	    case LM_SLC:
692 		sprintf(nfrontp, "SLC");
693 		nfrontp += strlen(nfrontp);
694 		for (i = 2; i < length - 2; i += 3) {
695 		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
696 			sprintf(nfrontp, " %s", SLC_NAME(pointer[i+SLC_FUNC]));
697 		    else
698 			sprintf(nfrontp, " %d", pointer[i+SLC_FUNC]);
699 		    nfrontp += strlen(nfrontp);
700 		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
701 		    case SLC_NOSUPPORT:
702 			sprintf(nfrontp, " NOSUPPORT"); break;
703 		    case SLC_CANTCHANGE:
704 			sprintf(nfrontp, " CANTCHANGE"); break;
705 		    case SLC_VARIABLE:
706 			sprintf(nfrontp, " VARIABLE"); break;
707 		    case SLC_DEFAULT:
708 			sprintf(nfrontp, " DEFAULT"); break;
709 		    }
710 		    nfrontp += strlen(nfrontp);
711 		    sprintf(nfrontp, "%s%s%s",
712 			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
713 			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
714 			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
715 		    nfrontp += strlen(nfrontp);
716 		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
717 						SLC_FLUSHOUT| SLC_LEVELBITS)) {
718 			sprintf(nfrontp, "(0x%x)", pointer[i+SLC_FLAGS]);
719 			nfrontp += strlen(nfrontp);
720 		    }
721 		    sprintf(nfrontp, " %d;", pointer[i+SLC_VALUE]);
722 		    nfrontp += strlen(nfrontp);
723 		    if ((pointer[i+SLC_VALUE] == IAC) &&
724 			(pointer[i+SLC_VALUE+1] == IAC))
725 				i++;
726 		}
727 		for (; i < length; i++) {
728 		    sprintf(nfrontp, " ?%d?", pointer[i]);
729 		    nfrontp += strlen(nfrontp);
730 		}
731 		break;
732 
733 	    case LM_MODE:
734 		sprintf(nfrontp, "MODE ");
735 		nfrontp += strlen(nfrontp);
736 		if (length < 3) {
737 		    sprintf(nfrontp, "(no mode??\?)");
738 		    nfrontp += strlen(nfrontp);
739 		    break;
740 		}
741 		{
742 		    char tbuf[32];
743 		    sprintf(tbuf, "%s%s%s%s%s",
744 			pointer[2]&MODE_EDIT ? "|EDIT" : "",
745 			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
746 			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
747 			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
748 			pointer[2]&MODE_ACK ? "|ACK" : "");
749 		    sprintf(nfrontp, "%s", tbuf[1] ? &tbuf[1] : "0");
750 		    nfrontp += strlen(nfrontp);
751 		}
752 		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
753 		    sprintf(nfrontp, " (0x%x)", pointer[2]);
754 		    nfrontp += strlen(nfrontp);
755 		}
756 		for (i = 3; i < length; i++) {
757 		    sprintf(nfrontp, " ?0x%x?", pointer[i]);
758 		    nfrontp += strlen(nfrontp);
759 		}
760 		break;
761 	    default:
762 		sprintf(nfrontp, "%d (unknown)", pointer[1]);
763 		nfrontp += strlen(nfrontp);
764 		for (i = 2; i < length; i++) {
765 		    sprintf(nfrontp, " %d", pointer[i]);
766 		    nfrontp += strlen(nfrontp);
767 		}
768 	    }
769 	    break;
770 
771 	case TELOPT_STATUS: {
772 	    register char *cp;
773 	    register int j, k;
774 
775 	    sprintf(nfrontp, "STATUS");
776 	    nfrontp += strlen(nfrontp);
777 
778 	    switch (pointer[1]) {
779 	    default:
780 		if (pointer[1] == TELQUAL_SEND)
781 		    sprintf(nfrontp, " SEND");
782 		else
783 		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
784 		nfrontp += strlen(nfrontp);
785 		for (i = 2; i < length; i++) {
786 		    sprintf(nfrontp, " ?%d?", pointer[i]);
787 		    nfrontp += strlen(nfrontp);
788 		}
789 		break;
790 	    case TELQUAL_IS:
791 		sprintf(nfrontp, " IS\r\n");
792 		nfrontp += strlen(nfrontp);
793 
794 		for (i = 2; i < length; i++) {
795 		    switch(pointer[i]) {
796 		    case DO:	cp = "DO"; goto common2;
797 		    case DONT:	cp = "DONT"; goto common2;
798 		    case WILL:	cp = "WILL"; goto common2;
799 		    case WONT:	cp = "WONT"; goto common2;
800 		    common2:
801 			i++;
802 			if (TELOPT_OK(pointer[i]))
803 			    sprintf(nfrontp, " %s %s", cp, TELOPT(pointer[i]));
804 			else
805 			    sprintf(nfrontp, " %s %d", cp, pointer[i]);
806 			nfrontp += strlen(nfrontp);
807 
808 			sprintf(nfrontp, "\r\n");
809 			nfrontp += strlen(nfrontp);
810 			break;
811 
812 		    case SB:
813 			sprintf(nfrontp, " SB ");
814 			nfrontp += strlen(nfrontp);
815 			i++;
816 			j = k = i;
817 			while (j < length) {
818 			    if (pointer[j] == SE) {
819 				if (j+1 == length)
820 				    break;
821 				if (pointer[j+1] == SE)
822 				    j++;
823 				else
824 				    break;
825 			    }
826 			    pointer[k++] = pointer[j++];
827 			}
828 			printsub(0, &pointer[i], k - i);
829 			if (i < length) {
830 			    sprintf(nfrontp, " SE");
831 			    nfrontp += strlen(nfrontp);
832 			    i = j;
833 			} else
834 			    i = j - 1;
835 
836 			sprintf(nfrontp, "\r\n");
837 			nfrontp += strlen(nfrontp);
838 
839 			break;
840 
841 		    default:
842 			sprintf(nfrontp, " %d", pointer[i]);
843 			nfrontp += strlen(nfrontp);
844 			break;
845 		    }
846 		}
847 		break;
848 	    }
849 	    break;
850 	  }
851 
852 	case TELOPT_XDISPLOC:
853 	    sprintf(nfrontp, "X-DISPLAY-LOCATION ");
854 	    nfrontp += strlen(nfrontp);
855 	    switch (pointer[1]) {
856 	    case TELQUAL_IS:
857 		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
858 		break;
859 	    case TELQUAL_SEND:
860 		sprintf(nfrontp, "SEND");
861 		break;
862 	    default:
863 		sprintf(nfrontp, "- unknown qualifier %d (0x%x).",
864 				pointer[1], pointer[1]);
865 	    }
866 	    nfrontp += strlen(nfrontp);
867 	    break;
868 
869 	case TELOPT_NEW_ENVIRON:
870 	    sprintf(nfrontp, "NEW-ENVIRON ");
871 	    goto env_common1;
872 	case TELOPT_OLD_ENVIRON:
873 	    sprintf(nfrontp, "OLD-ENVIRON");
874 	env_common1:
875 	    nfrontp += strlen(nfrontp);
876 	    switch (pointer[1]) {
877 	    case TELQUAL_IS:
878 		sprintf(nfrontp, "IS ");
879 		goto env_common;
880 	    case TELQUAL_SEND:
881 		sprintf(nfrontp, "SEND ");
882 		goto env_common;
883 	    case TELQUAL_INFO:
884 		sprintf(nfrontp, "INFO ");
885 	    env_common:
886 		nfrontp += strlen(nfrontp);
887 		{
888 		    register int noquote = 2;
889 		    for (i = 2; i < length; i++ ) {
890 			switch (pointer[i]) {
891 			case NEW_ENV_VAR:
892 			    sprintf(nfrontp, "\" VAR " + noquote);
893 			    nfrontp += strlen(nfrontp);
894 			    noquote = 2;
895 			    break;
896 
897 			case NEW_ENV_VALUE:
898 			    sprintf(nfrontp, "\" VALUE " + noquote);
899 			    nfrontp += strlen(nfrontp);
900 			    noquote = 2;
901 			    break;
902 
903 			case ENV_ESC:
904 			    sprintf(nfrontp, "\" ESC " + noquote);
905 			    nfrontp += strlen(nfrontp);
906 			    noquote = 2;
907 			    break;
908 
909 			case ENV_USERVAR:
910 			    sprintf(nfrontp, "\" USERVAR " + noquote);
911 			    nfrontp += strlen(nfrontp);
912 			    noquote = 2;
913 			    break;
914 
915 			default:
916 			def_case:
917 			    if (isprint(pointer[i]) && pointer[i] != '"') {
918 				if (noquote) {
919 				    *nfrontp++ = '"';
920 				    noquote = 0;
921 				}
922 				*nfrontp++ = pointer[i];
923 			    } else {
924 				sprintf(nfrontp, "\" %03o " + noquote,
925 							pointer[i]);
926 				nfrontp += strlen(nfrontp);
927 				noquote = 2;
928 			    }
929 			    break;
930 			}
931 		    }
932 		    if (!noquote)
933 			*nfrontp++ = '"';
934 		    break;
935 		}
936 	    }
937 	    break;
938 
939 #if	defined(AUTHENTICATION)
940 	case TELOPT_AUTHENTICATION:
941 	    sprintf(nfrontp, "AUTHENTICATION");
942 	    nfrontp += strlen(nfrontp);
943 
944 	    if (length < 2) {
945 		sprintf(nfrontp, " (empty suboption??\?)");
946 		nfrontp += strlen(nfrontp);
947 		break;
948 	    }
949 	    switch (pointer[1]) {
950 	    case TELQUAL_REPLY:
951 	    case TELQUAL_IS:
952 		sprintf(nfrontp, " %s ", (pointer[1] == TELQUAL_IS) ?
953 							"IS" : "REPLY");
954 		nfrontp += strlen(nfrontp);
955 		if (AUTHTYPE_NAME_OK(pointer[2]))
956 		    sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[2]));
957 		else
958 		    sprintf(nfrontp, "%d ", pointer[2]);
959 		nfrontp += strlen(nfrontp);
960 		if (length < 3) {
961 		    sprintf(nfrontp, "(partial suboption??\?)");
962 		    nfrontp += strlen(nfrontp);
963 		    break;
964 		}
965 		sprintf(nfrontp, "%s|%s",
966 			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
967 			"CLIENT" : "SERVER",
968 			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
969 			"MUTUAL" : "ONE-WAY");
970 		nfrontp += strlen(nfrontp);
971 
972 		auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
973 		sprintf(nfrontp, "%s", buf);
974 		nfrontp += strlen(nfrontp);
975 		break;
976 
977 	    case TELQUAL_SEND:
978 		i = 2;
979 		sprintf(nfrontp, " SEND ");
980 		nfrontp += strlen(nfrontp);
981 		while (i < length) {
982 		    if (AUTHTYPE_NAME_OK(pointer[i]))
983 			sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[i]));
984 		    else
985 			sprintf(nfrontp, "%d ", pointer[i]);
986 		    nfrontp += strlen(nfrontp);
987 		    if (++i >= length) {
988 			sprintf(nfrontp, "(partial suboption??\?)");
989 			nfrontp += strlen(nfrontp);
990 			break;
991 		    }
992 		    sprintf(nfrontp, "%s|%s ",
993 			((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
994 							"CLIENT" : "SERVER",
995 			((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
996 							"MUTUAL" : "ONE-WAY");
997 		    nfrontp += strlen(nfrontp);
998 		    ++i;
999 		}
1000 		break;
1001 
1002 	    case TELQUAL_NAME:
1003 		i = 2;
1004 		sprintf(nfrontp, " NAME \"");
1005 		nfrontp += strlen(nfrontp);
1006 		while (i < length)
1007 		    *nfrontp += pointer[i++];
1008 		*nfrontp += '"';
1009 		break;
1010 
1011 	    default:
1012 		    for (i = 2; i < length; i++) {
1013 			sprintf(nfrontp, " ?%d?", pointer[i]);
1014 			nfrontp += strlen(nfrontp);
1015 		    }
1016 		    break;
1017 	    }
1018 	    break;
1019 #endif
1020 
1021 #ifdef	ENCRYPTION
1022 	case TELOPT_ENCRYPT:
1023 	    sprintf(nfrontp, "ENCRYPT");
1024 	    nfrontp += strlen(nfrontp);
1025 	    if (length < 2) {
1026 		sprintf(nfrontp, " (empty suboption??\?)");
1027 		nfrontp += strlen(nfrontp);
1028 		break;
1029 	    }
1030 	    switch (pointer[1]) {
1031 	    case ENCRYPT_START:
1032 		sprintf(nfrontp, " START");
1033 		nfrontp += strlen(nfrontp);
1034 		break;
1035 
1036 	    case ENCRYPT_END:
1037 		sprintf(nfrontp, " END");
1038 		nfrontp += strlen(nfrontp);
1039 		break;
1040 
1041 	    case ENCRYPT_REQSTART:
1042 		sprintf(nfrontp, " REQUEST-START");
1043 		nfrontp += strlen(nfrontp);
1044 		break;
1045 
1046 	    case ENCRYPT_REQEND:
1047 		sprintf(nfrontp, " REQUEST-END");
1048 		nfrontp += strlen(nfrontp);
1049 		break;
1050 
1051 	    case ENCRYPT_IS:
1052 	    case ENCRYPT_REPLY:
1053 		sprintf(nfrontp, " %s ", (pointer[1] == ENCRYPT_IS) ?
1054 							"IS" : "REPLY");
1055 		nfrontp += strlen(nfrontp);
1056 		if (length < 3) {
1057 		    sprintf(nfrontp, " (partial suboption??\?)");
1058 		    nfrontp += strlen(nfrontp);
1059 		    break;
1060 		}
1061 		if (ENCTYPE_NAME_OK(pointer[2]))
1062 		    sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[2]));
1063 		else
1064 		    sprintf(nfrontp, " %d (unknown)", pointer[2]);
1065 		nfrontp += strlen(nfrontp);
1066 
1067 		encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
1068 		sprintf(nfrontp, "%s", buf);
1069 		nfrontp += strlen(nfrontp);
1070 		break;
1071 
1072 	    case ENCRYPT_SUPPORT:
1073 		i = 2;
1074 		sprintf(nfrontp, " SUPPORT ");
1075 		nfrontp += strlen(nfrontp);
1076 		while (i < length) {
1077 		    if (ENCTYPE_NAME_OK(pointer[i]))
1078 			sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[i]));
1079 		    else
1080 			sprintf(nfrontp, "%d ", pointer[i]);
1081 		    nfrontp += strlen(nfrontp);
1082 		    i++;
1083 		}
1084 		break;
1085 
1086 	    case ENCRYPT_ENC_KEYID:
1087 		sprintf(nfrontp, " ENC_KEYID", pointer[1]);
1088 		nfrontp += strlen(nfrontp);
1089 		goto encommon;
1090 
1091 	    case ENCRYPT_DEC_KEYID:
1092 		sprintf(nfrontp, " DEC_KEYID", pointer[1]);
1093 		nfrontp += strlen(nfrontp);
1094 		goto encommon;
1095 
1096 	    default:
1097 		sprintf(nfrontp, " %d (unknown)", pointer[1]);
1098 		nfrontp += strlen(nfrontp);
1099 	    encommon:
1100 		for (i = 2; i < length; i++) {
1101 		    sprintf(nfrontp, " %d", pointer[i]);
1102 		    nfrontp += strlen(nfrontp);
1103 		}
1104 		break;
1105 	    }
1106 	    break;
1107 #endif	/* ENCRYPTION */
1108 
1109 	default:
1110 	    if (TELOPT_OK(pointer[0]))
1111 	        sprintf(nfrontp, "%s (unknown)", TELOPT(pointer[0]));
1112 	    else
1113 	        sprintf(nfrontp, "%d (unknown)", pointer[i]);
1114 	    nfrontp += strlen(nfrontp);
1115 	    for (i = 1; i < length; i++) {
1116 		sprintf(nfrontp, " %d", pointer[i]);
1117 		nfrontp += strlen(nfrontp);
1118 	    }
1119 	    break;
1120 	}
1121 	sprintf(nfrontp, "\r\n");
1122 	nfrontp += strlen(nfrontp);
1123 }
1124 
1125 /*
1126  * Dump a data buffer in hex and ascii to the output data stream.
1127  */
1128 	void
1129 printdata(tag, ptr, cnt)
1130 	register char *tag;
1131 	register char *ptr;
1132 	register int cnt;
1133 {
1134 	register int i;
1135 	char xbuf[30];
1136 
1137 	while (cnt) {
1138 		/* flush net output buffer if no room for new data) */
1139 		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
1140 			netflush();
1141 		}
1142 
1143 		/* add a line of output */
1144 		sprintf(nfrontp, "%s: ", tag);
1145 		nfrontp += strlen(nfrontp);
1146 		for (i = 0; i < 20 && cnt; i++) {
1147 			sprintf(nfrontp, "%02x", *ptr);
1148 			nfrontp += strlen(nfrontp);
1149 			if (isprint(*ptr)) {
1150 				xbuf[i] = *ptr;
1151 			} else {
1152 				xbuf[i] = '.';
1153 			}
1154 			if (i % 2) {
1155 				*nfrontp = ' ';
1156 				nfrontp++;
1157 			}
1158 			cnt--;
1159 			ptr++;
1160 		}
1161 		xbuf[i] = '\0';
1162 		sprintf(nfrontp, " %s\r\n", xbuf );
1163 		nfrontp += strlen(nfrontp);
1164 	}
1165 }
1166 #endif /* DIAGNOSTICS */
1167