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