xref: /original-bsd/libexec/telnetd/utility.c (revision 9a765c18)
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.6 (Berkeley) 02/26/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 #ifdef DIAGNOSTICS
34     if (diagnostic & TD_REPORT) {
35 	sprintf(nfrontp, "td: ttloop\r\n");
36 	nfrontp += strlen(nfrontp);
37     }
38 #endif /* DIAGNOSTICS */
39     if (nfrontp-nbackp) {
40 	netflush();
41     }
42     ncc = read(net, netibuf, sizeof netibuf);
43     if (ncc < 0) {
44 	syslog(LOG_INFO, "ttloop:  read: %m\n");
45 	exit(1);
46     } else if (ncc == 0) {
47 	syslog(LOG_INFO, "ttloop:  peer died: %m\n");
48 	exit(1);
49     }
50 #ifdef DIAGNOSTICS
51     if (diagnostic & TD_REPORT) {
52 	sprintf(nfrontp, "td: ttloop read %d chars\r\n", ncc);
53 	nfrontp += strlen(nfrontp);
54     }
55 #endif /* DIAGNOSTICS */
56     netip = netibuf;
57     telrcv();			/* state machine */
58     if (ncc > 0) {
59 	pfrontp = pbackp = ptyobuf;
60 	telrcv();
61     }
62 }  /* end of ttloop */
63 
64 /*
65  * Check a descriptor to see if out of band data exists on it.
66  */
67 stilloob(s)
68 int	s;		/* socket number */
69 {
70     static struct timeval timeout = { 0 };
71     fd_set	excepts;
72     int value;
73 
74     do {
75 	FD_ZERO(&excepts);
76 	FD_SET(s, &excepts);
77 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
78     } while ((value == -1) && (errno == EINTR));
79 
80     if (value < 0) {
81 	fatalperror(pty, "select");
82     }
83     if (FD_ISSET(s, &excepts)) {
84 	return 1;
85     } else {
86 	return 0;
87     }
88 }
89 
90 ptyflush()
91 {
92 	int n;
93 
94 	if ((n = pfrontp - pbackp) > 0) {
95 #ifdef DIAGNOSTICS
96 		if (diagnostic & (TD_REPORT | TD_PTYDATA)) {
97 			sprintf(nfrontp, "td: ptyflush %d chars\r\n", n);
98 			nfrontp += strlen(nfrontp);
99 		}
100 		if (diagnostic & TD_PTYDATA) {
101 			printdata("pd", pbackp, n);
102 		}
103 #endif /* DIAGNOSTICS */
104 		n = write(pty, pbackp, n);
105 	}
106 	if (n < 0)
107 		return;
108 	pbackp += n;
109 	if (pbackp == pfrontp)
110 		pbackp = pfrontp = ptyobuf;
111 }
112 
113 /*
114  * nextitem()
115  *
116  *	Return the address of the next "item" in the TELNET data
117  * stream.  This will be the address of the next character if
118  * the current address is a user data character, or it will
119  * be the address of the character following the TELNET command
120  * if the current address is a TELNET IAC ("I Am a Command")
121  * character.
122  */
123 char *
124 nextitem(current)
125 char	*current;
126 {
127     if ((*current&0xff) != IAC) {
128 	return current+1;
129     }
130     switch (*(current+1)&0xff) {
131     case DO:
132     case DONT:
133     case WILL:
134     case WONT:
135 	return current+3;
136     case SB:		/* loop forever looking for the SE */
137 	{
138 	    register char *look = current+2;
139 
140 	    for (;;) {
141 		if ((*look++&0xff) == IAC) {
142 		    if ((*look++&0xff) == SE) {
143 			return look;
144 		    }
145 		}
146 	    }
147 	}
148     default:
149 	return current+2;
150     }
151 }  /* end of nextitem */
152 
153 
154 /*
155  * netclear()
156  *
157  *	We are about to do a TELNET SYNCH operation.  Clear
158  * the path to the network.
159  *
160  *	Things are a bit tricky since we may have sent the first
161  * byte or so of a previous TELNET command into the network.
162  * So, we have to scan the network buffer from the beginning
163  * until we are up to where we want to be.
164  *
165  *	A side effect of what we do, just to keep things
166  * simple, is to clear the urgent data pointer.  The principal
167  * caller should be setting the urgent data pointer AFTER calling
168  * us in any case.
169  */
170 netclear()
171 {
172     register char *thisitem, *next;
173     char *good;
174 #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
175 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
176 
177     thisitem = netobuf;
178 
179     while ((next = nextitem(thisitem)) <= nbackp) {
180 	thisitem = next;
181     }
182 
183     /* Now, thisitem is first before/at boundary. */
184 
185     good = netobuf;	/* where the good bytes go */
186 
187     while (nfrontp > thisitem) {
188 	if (wewant(thisitem)) {
189 	    int length;
190 
191 	    next = thisitem;
192 	    do {
193 		next = nextitem(next);
194 	    } while (wewant(next) && (nfrontp > next));
195 	    length = next-thisitem;
196 	    bcopy(thisitem, good, length);
197 	    good += length;
198 	    thisitem = next;
199 	} else {
200 	    thisitem = nextitem(thisitem);
201 	}
202     }
203 
204     nbackp = netobuf;
205     nfrontp = good;		/* next byte to be sent */
206     neturg = 0;
207 }  /* end of netclear */
208 
209 /*
210  *  netflush
211  *		Send as much data as possible to the network,
212  *	handling requests for urgent data.
213  */
214 void
215 netflush()
216 {
217     int n;
218     extern int not42;
219 
220     if ((n = nfrontp - nbackp) > 0) {
221 #ifdef DIAGNOSTICS
222 	if (diagnostic & 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 #endif /* DIAGNOSTICS */
228 	/*
229 	 * if no urgent data, or if the other side appears to be an
230 	 * old 4.2 client (and thus unable to survive TCP urgent data),
231 	 * write the entire buffer in non-OOB mode.
232 	 */
233 	if ((neturg == 0) || (not42 == 0)) {
234 	    n = write(net, nbackp, n);	/* normal write */
235 	} else {
236 	    n = neturg - nbackp;
237 	    /*
238 	     * In 4.2 (and 4.3) systems, there is some question about
239 	     * what byte in a sendOOB operation is the "OOB" data.
240 	     * To make ourselves compatible, we only send ONE byte
241 	     * out of band, the one WE THINK should be OOB (though
242 	     * we really have more the TCP philosophy of urgent data
243 	     * rather than the Unix philosophy of OOB data).
244 	     */
245 	    if (n > 1) {
246 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
247 	    } else {
248 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
249 	    }
250 	}
251     }
252     if (n < 0) {
253 	if (errno == EWOULDBLOCK || errno == EINTR)
254 		return;
255 	cleanup();
256     }
257     nbackp += n;
258     if (nbackp >= neturg) {
259 	neturg = 0;
260     }
261     if (nbackp == nfrontp) {
262 	nbackp = nfrontp = netobuf;
263     }
264     return;
265 }  /* end of netflush */
266 
267 
268 /*
269  * writenet
270  *
271  * Just a handy little function to write a bit of raw data to the net.
272  * It will force a transmit of the buffer if necessary
273  *
274  * arguments
275  *    ptr - A pointer to a character string to write
276  *    len - How many bytes to write
277  */
278 writenet(ptr, len)
279 register char *ptr;
280 register int len;
281 {
282 	/* flush buffer if no room for new data) */
283 	if ((&netobuf[BUFSIZ] - nfrontp) < len) {
284 		/* if this fails, don't worry, buffer is a little big */
285 		netflush();
286 	}
287 
288 	bcopy(ptr, nfrontp, len);
289 	nfrontp += len;
290 
291 }  /* end of writenet */
292 
293 
294 /*
295  * miscellaneous functions doing a variety of little jobs follow ...
296  */
297 
298 
299 fatal(f, msg)
300 	int f;
301 	char *msg;
302 {
303 	char buf[BUFSIZ];
304 
305 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
306 	(void) write(f, buf, (int)strlen(buf));
307 	sleep(1);	/*XXX*/
308 	exit(1);
309 }
310 
311 fatalperror(f, msg)
312 	int f;
313 	char *msg;
314 {
315 	char buf[BUFSIZ], *strerror();
316 
317 	(void) sprintf(buf, "%s: %s\r\n", msg, strerror(errno));
318 	fatal(f, buf);
319 }
320 
321 char editedhost[32];
322 
323 edithost(pat, host)
324 	register char *pat;
325 	register char *host;
326 {
327 	register char *res = editedhost;
328 	char *strncpy();
329 
330 	if (!pat)
331 		pat = "";
332 	while (*pat) {
333 		switch (*pat) {
334 
335 		case '#':
336 			if (*host)
337 				host++;
338 			break;
339 
340 		case '@':
341 			if (*host)
342 				*res++ = *host++;
343 			break;
344 
345 		default:
346 			*res++ = *pat;
347 			break;
348 		}
349 		if (res == &editedhost[sizeof editedhost - 1]) {
350 			*res = '\0';
351 			return;
352 		}
353 		pat++;
354 	}
355 	if (*host)
356 		(void) strncpy(res, host,
357 				sizeof editedhost - (res - editedhost) -1);
358 	else
359 		*res = '\0';
360 	editedhost[sizeof editedhost - 1] = '\0';
361 }
362 
363 static char *putlocation;
364 
365 putstr(s)
366 register char *s;
367 {
368 
369 	while (*s)
370 		putchr(*s++);
371 }
372 
373 putchr(cc)
374 {
375 	*putlocation++ = cc;
376 }
377 
378 putf(cp, where)
379 register char *cp;
380 char *where;
381 {
382 	time_t t;
383 	char *fmt, *slash, db[100];
384 	extern char *rindex();
385 
386 	putlocation = where;
387 
388 	while (*cp) {
389 		if (*cp != '%') {
390 			putchr(*cp++);
391 			continue;
392 		}
393 		switch (*++cp) {
394 
395 		case 't':
396 			slash = rindex(line, '/');
397 			if (slash == (char *) 0)
398 				putstr(line);
399 			else
400 				putstr(&slash[1]);
401 			break;
402 
403 		case 'h':
404 			putstr(editedhost);
405 			break;
406 
407 		case 'd': {
408 			char fmt[] = "%l:% %P on %A, %d %B %Y";
409 
410 			fmt[4] = 'M';		/* I *hate* SCCS... */
411 			(void)time(&t);
412 			(void)strftime(db, sizeof(db), fmt, localtime(&t));
413 			putstr(db);
414 			break;
415 		}
416 
417 		case '%':
418 			putchr('%');
419 			break;
420 		}
421 		cp++;
422 	}
423 }
424 
425 /*ARGSUSED*/
426 #ifdef	NO_GETTYTAB
427 getent(cp, name)
428 char *cp, *name;
429 {
430 	return(0);
431 }
432 
433 /*ARGSUSED*/
434 char *
435 getstr(cp, cpp)
436 char *cp, **cpp;
437 {
438 	return(0);
439 }
440 #endif	/* NO_GETTYTAB */
441 
442 #ifdef DIAGNOSTICS
443 /*
444  * Print telnet options and commands in plain text, if possible.
445  */
446 void
447 printoption(fmt, option)
448 register char *fmt;
449 register int option;
450 {
451 	if (TELOPT_OK(option))
452 		sprintf(nfrontp, "%s %s\r\n", fmt, TELOPT(option));
453 	else if (TELCMD_OK(option))
454 		sprintf(nfrontp, "%s %s\r\n", fmt, TELCMD(option));
455 	else
456 		sprintf(nfrontp, "%s %d\r\n", fmt, option);
457 	nfrontp += strlen(nfrontp);
458 	return;
459 }
460 
461 char *slcnames[] = { SLC_NAMES };
462 
463 void
464 printsub(dirp, pointer, length)
465 char	*dirp;
466 unsigned char	*pointer;	/* where suboption data sits */
467 int	length;			/* length of suboption data */
468 {
469     register int i;
470 
471 	if (dirp) {
472 	    sprintf(nfrontp, "%s suboption ", dirp);
473 	    nfrontp += strlen(nfrontp);
474 	    if (length >= 3) {
475 		register int j;
476 
477 		i = pointer[length-2];
478 		j = pointer[length-1];
479 
480 		if (i != IAC || j != SE) {
481 		    sprintf(nfrontp, "(terminated by ");
482 		    nfrontp += strlen(nfrontp);
483 		    if (TELOPT_OK(i))
484 			sprintf(nfrontp, "%s ", TELOPT(i));
485 		    else if (TELCMD_OK(i))
486 			sprintf(nfrontp, "%s ", TELCMD(i));
487 		    else
488 			sprintf(nfrontp, "%d ", i);
489 		    nfrontp += strlen(nfrontp);
490 		    if (TELOPT_OK(j))
491 			sprintf(nfrontp, "%s", TELOPT(j));
492 		    else if (TELCMD_OK(j))
493 			sprintf(nfrontp, "%s", TELCMD(j));
494 		    else
495 			sprintf(nfrontp, "%d", j);
496 		    nfrontp += strlen(nfrontp);
497 		    sprintf(nfrontp, ", not IAC SE!) ");
498 		    nfrontp += strlen(nfrontp);
499 		}
500 	    }
501 	    length -= 2;
502 	}
503 	if (length < 1) {
504 	    sprintf(nfrontp, "(Empty suboption???)");
505 	    nfrontp += strlen(nfrontp);
506 	    return;
507 	}
508 	switch (pointer[0]) {
509 	case TELOPT_TTYPE:
510 	    sprintf(nfrontp, "TERMINAL-TYPE ");
511 	    nfrontp += strlen(nfrontp);
512 	    switch (pointer[1]) {
513 	    case TELQUAL_IS:
514 		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
515 		break;
516 	    case TELQUAL_SEND:
517 		sprintf(nfrontp, "SEND");
518 		break;
519 	    default:
520 		sprintf(nfrontp,
521 				"- unknown qualifier %d (0x%x).",
522 				pointer[1], pointer[1]);
523 	    }
524 	    nfrontp += strlen(nfrontp);
525 	    break;
526 	case TELOPT_TSPEED:
527 	    sprintf(nfrontp, "TERMINAL-SPEED");
528 	    nfrontp += strlen(nfrontp);
529 	    if (length < 2) {
530 		sprintf(nfrontp, " (empty suboption???)");
531 		nfrontp += strlen(nfrontp);
532 		break;
533 	    }
534 	    switch (pointer[1]) {
535 	    case TELQUAL_IS:
536 		sprintf(nfrontp, " IS %.*s", length-2, (char *)pointer+2);
537 		nfrontp += strlen(nfrontp);
538 		break;
539 	    default:
540 		if (pointer[1] == 1)
541 		    sprintf(nfrontp, " SEND");
542 		else
543 		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
544 		nfrontp += strlen(nfrontp);
545 		for (i = 2; i < length; i++) {
546 		    sprintf(nfrontp, " ?%d?", pointer[i]);
547 		    nfrontp += strlen(nfrontp);
548 		}
549 		break;
550 	    }
551 	    break;
552 
553 	case TELOPT_LFLOW:
554 	    sprintf(nfrontp, "TOGGLE-FLOW-CONTROL");
555 	    nfrontp += strlen(nfrontp);
556 	    if (length < 2) {
557 		sprintf(nfrontp, " (empty suboption???)");
558 		nfrontp += strlen(nfrontp);
559 		break;
560 	    }
561 	    switch (pointer[1]) {
562 	    case 0:
563 		sprintf(nfrontp, " OFF"); break;
564 	    case 1:
565 		sprintf(nfrontp, " ON"); break;
566 	    default:
567 		sprintf(nfrontp, " %d (unknown)", pointer[1]);
568 	    }
569 	    nfrontp += strlen(nfrontp);
570 	    for (i = 2; i < length; i++) {
571 		sprintf(nfrontp, " ?%d?", pointer[i]);
572 		nfrontp += strlen(nfrontp);
573 	    }
574 	    break;
575 
576 	case TELOPT_NAWS:
577 	    sprintf(nfrontp, "NAWS");
578 	    nfrontp += strlen(nfrontp);
579 	    if (length < 2) {
580 		sprintf(nfrontp, " (empty suboption???)");
581 		nfrontp += strlen(nfrontp);
582 		break;
583 	    }
584 	    if (length == 2) {
585 		sprintf(nfrontp, " ?%d?", pointer[1]);
586 		nfrontp += strlen(nfrontp);
587 		break;
588 	    }
589 	    sprintf(nfrontp, " %d %d (%d)",
590 		pointer[1], pointer[2],
591 		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
592 	    nfrontp += strlen(nfrontp);
593 	    if (length == 4) {
594 		sprintf(nfrontp, " ?%d?", pointer[3]);
595 		nfrontp += strlen(nfrontp);
596 		break;
597 	    }
598 	    sprintf(nfrontp, " %d %d (%d)",
599 		pointer[3], pointer[4],
600 		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
601 	    nfrontp += strlen(nfrontp);
602 	    for (i = 5; i < length; i++) {
603 		sprintf(nfrontp, " ?%d?", pointer[i]);
604 		nfrontp += strlen(nfrontp);
605 	    }
606 	    break;
607 
608 	case TELOPT_LINEMODE:
609 	    sprintf(nfrontp, "LINEMODE ");
610 	    nfrontp += strlen(nfrontp);
611 	    if (length < 2) {
612 		sprintf(nfrontp, " (empty suboption???)");
613 		nfrontp += strlen(nfrontp);
614 		break;
615 	    }
616 	    switch (pointer[1]) {
617 	    case WILL:
618 		sprintf(nfrontp, "WILL ");
619 		goto common;
620 	    case WONT:
621 		sprintf(nfrontp, "WONT ");
622 		goto common;
623 	    case DO:
624 		sprintf(nfrontp, "DO ");
625 		goto common;
626 	    case DONT:
627 		sprintf(nfrontp, "DONT ");
628 	    common:
629 		nfrontp += strlen(nfrontp);
630 		if (length < 3) {
631 		    sprintf(nfrontp, "(no option???)");
632 		    nfrontp += strlen(nfrontp);
633 		    break;
634 		}
635 		switch (pointer[2]) {
636 		case LM_FORWARDMASK:
637 		    sprintf(nfrontp, "Forward Mask");
638 		    nfrontp += strlen(nfrontp);
639 		    for (i = 3; i < length; i++) {
640 			sprintf(nfrontp, " %x", pointer[i]);
641 			nfrontp += strlen(nfrontp);
642 		    }
643 		    break;
644 		default:
645 		    sprintf(nfrontp, "%d (unknown)", pointer[2]);
646 		    nfrontp += strlen(nfrontp);
647 		    for (i = 3; i < length; i++) {
648 			sprintf(nfrontp, " %d", pointer[i]);
649 			nfrontp += strlen(nfrontp);
650 		    }
651 		    break;
652 		}
653 		break;
654 
655 	    case LM_SLC:
656 		sprintf(nfrontp, "SLC");
657 		nfrontp += strlen(nfrontp);
658 		for (i = 2; i < length - 2; i += 3) {
659 		    if (pointer[i+SLC_FUNC] <= NSLC)
660 			sprintf(nfrontp, " %s", slcnames[pointer[i+SLC_FUNC]]);
661 		    else
662 			sprintf(nfrontp, " %d", pointer[i+SLC_FUNC]);
663 		    nfrontp += strlen(nfrontp);
664 		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
665 		    case SLC_NOSUPPORT:
666 			sprintf(nfrontp, " NOSUPPORT"); break;
667 		    case SLC_CANTCHANGE:
668 			sprintf(nfrontp, " CANTCHANGE"); break;
669 		    case SLC_VARIABLE:
670 			sprintf(nfrontp, " VARIABLE"); break;
671 		    case SLC_DEFAULT:
672 			sprintf(nfrontp, " DEFAULT"); break;
673 		    }
674 		    nfrontp += strlen(nfrontp);
675 		    sprintf(nfrontp, "%s%s%s",
676 			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
677 			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
678 			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
679 		    nfrontp += strlen(nfrontp);
680 		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
681 						SLC_FLUSHOUT| SLC_LEVELBITS)) {
682 			sprintf(nfrontp, "(0x%x)", pointer[i+SLC_FLAGS]);
683 			nfrontp += strlen(nfrontp);
684 		    }
685 		    sprintf(nfrontp, " %d;", pointer[i+SLC_VALUE]);
686 		    nfrontp += strlen(nfrontp);
687 		    if ((pointer[i+SLC_VALUE] == IAC) &&
688 			(pointer[i+SLC_VALUE+1] == IAC))
689 				i++;
690 		}
691 		for (; i < length; i++) {
692 		    sprintf(nfrontp, " ?%d?", pointer[i]);
693 		    nfrontp += strlen(nfrontp);
694 		}
695 		break;
696 
697 	    case LM_MODE:
698 		sprintf(nfrontp, "MODE ");
699 		nfrontp += strlen(nfrontp);
700 		if (length < 3) {
701 		    sprintf(nfrontp, "(no mode???)");
702 		    nfrontp += strlen(nfrontp);
703 		    break;
704 		}
705 		{
706 		    char tbuf[32];
707 		    sprintf(tbuf, "%s%s%s%s%s",
708 			pointer[2]&MODE_EDIT ? "|EDIT" : "",
709 			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
710 			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
711 			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
712 			pointer[2]&MODE_ACK ? "|ACK" : "");
713 		    sprintf(nfrontp, "%s", tbuf[1] ? &tbuf[1] : "0");
714 		    nfrontp += strlen(nfrontp);
715 		}
716 		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
717 		    sprintf(nfrontp, " (0x%x)", pointer[2]);
718 		    nfrontp += strlen(nfrontp);
719 		}
720 		for (i = 3; i < length; i++) {
721 		    sprintf(nfrontp, " ?0x%x?", pointer[i]);
722 		    nfrontp += strlen(nfrontp);
723 		}
724 		break;
725 	    default:
726 		sprintf(nfrontp, "%d (unknown)", pointer[1]);
727 		nfrontp += strlen(nfrontp);
728 		for (i = 2; i < length; i++) {
729 		    sprintf(nfrontp, " %d", pointer[i]);
730 		    nfrontp += strlen(nfrontp);
731 		}
732 	    }
733 	    break;
734 
735 	case TELOPT_STATUS: {
736 	    register char *cp;
737 	    register int j, k;
738 
739 	    sprintf(nfrontp, "STATUS");
740 	    nfrontp += strlen(nfrontp);
741 
742 	    switch (pointer[1]) {
743 	    default:
744 		if (pointer[1] == TELQUAL_SEND)
745 		    sprintf(nfrontp, " SEND");
746 		else
747 		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
748 		nfrontp += strlen(nfrontp);
749 		for (i = 2; i < length; i++) {
750 		    sprintf(nfrontp, " ?%d?", pointer[i]);
751 		    nfrontp += strlen(nfrontp);
752 		}
753 		break;
754 	    case TELQUAL_IS:
755 		sprintf(nfrontp, " IS\r\n");
756 		nfrontp += strlen(nfrontp);
757 
758 		for (i = 2; i < length; i++) {
759 		    switch(pointer[i]) {
760 		    case DO:	cp = "DO"; goto common2;
761 		    case DONT:	cp = "DONT"; goto common2;
762 		    case WILL:	cp = "WILL"; goto common2;
763 		    case WONT:	cp = "WONT"; goto common2;
764 		    common2:
765 			i++;
766 			if (TELOPT_OK((int)pointer[i]))
767 			    sprintf(nfrontp, " %s %s", cp, TELOPT(pointer[i]));
768 			else
769 			    sprintf(nfrontp, " %s %d", cp, pointer[i]);
770 			nfrontp += strlen(nfrontp);
771 
772 			sprintf(nfrontp, "\r\n");
773 			nfrontp += strlen(nfrontp);
774 			break;
775 
776 		    case SB:
777 			sprintf(nfrontp, " SB ");
778 			nfrontp += strlen(nfrontp);
779 			i++;
780 			j = k = i;
781 			while (j < length) {
782 			    if (pointer[j] == SE) {
783 				if (j+1 == length)
784 				    break;
785 				if (pointer[j+1] == SE)
786 				    j++;
787 				else
788 				    break;
789 			    }
790 			    pointer[k++] = pointer[j++];
791 			}
792 			printsub(0, &pointer[i], k - i);
793 			if (i < length) {
794 			    sprintf(nfrontp, " SE");
795 			    nfrontp += strlen(nfrontp);
796 			    i = j;
797 			} else
798 			    i = j - 1;
799 
800 			sprintf(nfrontp, "\r\n");
801 			nfrontp += strlen(nfrontp);
802 
803 			break;
804 
805 		    default:
806 			sprintf(nfrontp, " %d", pointer[i]);
807 			nfrontp += strlen(nfrontp);
808 			break;
809 		    }
810 		}
811 		break;
812 	    }
813 	    break;
814 	  }
815 
816 	case TELOPT_XDISPLOC:
817 	    sprintf(nfrontp, "X-DISPLAY-LOCATION ");
818 	    nfrontp += strlen(nfrontp);
819 	    switch (pointer[1]) {
820 	    case TELQUAL_IS:
821 		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
822 		break;
823 	    case TELQUAL_SEND:
824 		sprintf(nfrontp, "SEND");
825 		break;
826 	    default:
827 		sprintf(nfrontp, "- unknown qualifier %d (0x%x).",
828 				pointer[1], pointer[1]);
829 	    }
830 	    nfrontp += strlen(nfrontp);
831 	    break;
832 
833 	case TELOPT_ENVIRON:
834 	    sprintf(nfrontp, "ENVIRON ");
835 	    nfrontp += strlen(nfrontp);
836 	    switch (pointer[1]) {
837 	    case TELQUAL_IS:
838 		sprintf(nfrontp, "IS ");
839 		goto env_common;
840 	    case TELQUAL_SEND:
841 		sprintf(nfrontp, "SEND ");
842 		goto env_common;
843 	    case TELQUAL_INFO:
844 		sprintf(nfrontp, "INFO ");
845 	    env_common:
846 	    nfrontp += strlen(nfrontp);
847 		{
848 		    register int noquote = 2;
849 		    for (i = 2; i < length; i++ ) {
850 			switch (pointer[i]) {
851 			case ENV_VAR:
852 			    if (pointer[1] == TELQUAL_SEND)
853 				goto def_case;
854 			    sprintf(nfrontp, "\" VAR " + noquote);
855 			    nfrontp += strlen(nfrontp);
856 			    noquote = 2;
857 			    break;
858 
859 			case ENV_VALUE:
860 			    sprintf(nfrontp, "\" VALUE " + noquote);
861 			    nfrontp += strlen(nfrontp);
862 			    noquote = 2;
863 			    break;
864 
865 			case ENV_ESC:
866 			    sprintf(nfrontp, "\" ESC " + noquote);
867 			    nfrontp += strlen(nfrontp);
868 			    noquote = 2;
869 			    break;
870 
871 			default:
872 			def_case:
873 			    if (isprint(pointer[i]) && pointer[i] != '"') {
874 				if (noquote) {
875 				    *nfrontp++ = '"';
876 				    noquote = 0;
877 				}
878 				*nfrontp++ = pointer[i];
879 			    } else {
880 				sprintf(nfrontp, "\" %03o " + noquote,
881 							pointer[i]);
882 				nfrontp += strlen(nfrontp);
883 				noquote = 2;
884 			    }
885 			    break;
886 			}
887 		    }
888 		    if (!noquote)
889 			*nfrontp++ = '"';
890 		    break;
891 		}
892 	    }
893 	    break;
894 
895 	default:
896 	    sprintf(nfrontp, "Unknown option ");
897 	    nfrontp += strlen(nfrontp);
898 	    for (i = 0; i < length; i++) {
899 		sprintf(nfrontp, " %d", pointer[i]);
900 		nfrontp += strlen(nfrontp);
901 	    }
902 	    break;
903 	}
904 	sprintf(nfrontp, "\r\n");
905 	nfrontp += strlen(nfrontp);
906 }
907 
908 /*
909  * Dump a data buffer in hex and ascii to the output data stream.
910  */
911 void
912 printdata(tag, ptr, cnt)
913 register char *tag;
914 register char *ptr;
915 register int cnt;
916 {
917 register int i;
918 char xbuf[30];
919 
920 	while (cnt) {
921 		/* flush net output buffer if no room for new data) */
922 		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
923 			netflush();
924 		}
925 
926 		/* add a line of output */
927 		sprintf(nfrontp, "%s: ", tag);
928 		nfrontp += strlen(nfrontp);
929 		for (i = 0; i < 20 && cnt; i++) {
930 			sprintf(nfrontp, "%02x", *ptr);
931 			nfrontp += strlen(nfrontp);
932 			if (isprint(*ptr)) {
933 				xbuf[i] = *ptr;
934 			} else {
935 				xbuf[i] = '.';
936 			}
937 			if (i % 2) {
938 				*nfrontp = ' ';
939 				nfrontp++;
940 			}
941 			cnt--;
942 			ptr++;
943 		}
944 		xbuf[i] = '\0';
945 		sprintf(nfrontp, " %s\r\n", xbuf );
946 		nfrontp += strlen(nfrontp);
947 	}
948 }
949 
950 #endif /* DIAGNOSTICS */
951 
952 #ifdef	NO_STRERROR
953 char *
954 strerror(errno)
955 {
956 	extern char *sys_errlist[];
957 
958 	return(sys_errlist[errno]);
959 }
960 #endif
961