xref: /csrg-svn/libexec/telnetd/utility.c (revision 44364)
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.4 (Berkeley) 06/28/90";
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 	char *slash;
383 #ifndef	NO_GETTYTAB
384 	char datebuffer[60];
385 #endif	/* NO_GETTYTAB */
386 	extern char *rindex();
387 
388 	putlocation = where;
389 
390 	while (*cp) {
391 		if (*cp != '%') {
392 			putchr(*cp++);
393 			continue;
394 		}
395 		switch (*++cp) {
396 
397 		case 't':
398 			slash = rindex(line, '/');
399 			if (slash == (char *) 0)
400 				putstr(line);
401 			else
402 				putstr(&slash[1]);
403 			break;
404 
405 		case 'h':
406 			putstr(editedhost);
407 			break;
408 
409 #ifndef	NO_GETTYTAB
410 		case 'd':
411 			get_date(datebuffer);
412 			putstr(datebuffer);
413 			break;
414 #endif	/* NO_GETTYTAB */
415 
416 		case '%':
417 			putchr('%');
418 			break;
419 		}
420 		cp++;
421 	}
422 }
423 
424 /*ARGSUSED*/
425 #ifdef	NO_GETTYTAB
426 getent(cp, name)
427 char *cp, *name;
428 {
429 	return(0);
430 }
431 
432 /*ARGSUSED*/
433 char *
434 getstr(cp, cpp)
435 char *cp, **cpp;
436 {
437 	return(0);
438 }
439 #endif	/* NO_GETTYTAB */
440 
441 #ifdef DIAGNOSTICS
442 /*
443  * Print telnet options and commands in plain text, if possible.
444  */
445 void
446 printoption(fmt, option)
447 register char *fmt;
448 register int option;
449 {
450 	if (TELOPT_OK(option))
451 		sprintf(nfrontp, "%s %s\r\n", fmt, TELOPT(option));
452 	else if (TELCMD_OK(option))
453 		sprintf(nfrontp, "%s %s\r\n", fmt, TELCMD(option));
454 	else
455 		sprintf(nfrontp, "%s %d\r\n", fmt, option);
456 	nfrontp += strlen(nfrontp);
457 	return;
458 }
459 
460 char *slcnames[] = { SLC_NAMES };
461 
462 void
463 printsub(dirp, pointer, length)
464 char	*dirp;
465 unsigned char	*pointer;	/* where suboption data sits */
466 int	length;			/* length of suboption data */
467 {
468     register int i;
469 
470 	if (dirp) {
471 	    sprintf(nfrontp, "%s suboption ", dirp);
472 	    nfrontp += strlen(nfrontp);
473 	    if (length >= 3) {
474 		register int j;
475 
476 		i = pointer[length-2];
477 		j = pointer[length-1];
478 
479 		if (i != IAC || j != SE) {
480 		    sprintf(nfrontp, "(terminated by ");
481 		    nfrontp += strlen(nfrontp);
482 		    if (TELOPT_OK(i))
483 			sprintf(nfrontp, "%s ", TELOPT(i));
484 		    else if (TELCMD_OK(i))
485 			sprintf(nfrontp, "%s ", TELCMD(i));
486 		    else
487 			sprintf(nfrontp, "%d ", i);
488 		    nfrontp += strlen(nfrontp);
489 		    if (TELOPT_OK(j))
490 			sprintf(nfrontp, "%s", TELOPT(j));
491 		    else if (TELCMD_OK(j))
492 			sprintf(nfrontp, "%s", TELCMD(j));
493 		    else
494 			sprintf(nfrontp, "%d", j);
495 		    nfrontp += strlen(nfrontp);
496 		    sprintf(nfrontp, ", not IAC SE!) ");
497 		    nfrontp += strlen(nfrontp);
498 		}
499 	    }
500 	    length -= 2;
501 	}
502 	if (length < 1) {
503 	    sprintf(nfrontp, "(Empty suboption???)");
504 	    nfrontp += strlen(nfrontp);
505 	    return;
506 	}
507 	switch (pointer[0]) {
508 	case TELOPT_TTYPE:
509 	    sprintf(nfrontp, "TERMINAL-TYPE ");
510 	    nfrontp += strlen(nfrontp);
511 	    switch (pointer[1]) {
512 	    case TELQUAL_IS:
513 		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
514 		break;
515 	    case TELQUAL_SEND:
516 		sprintf(nfrontp, "SEND");
517 		break;
518 	    default:
519 		sprintf(nfrontp,
520 				"- unknown qualifier %d (0x%x).",
521 				pointer[1], pointer[1]);
522 	    }
523 	    nfrontp += strlen(nfrontp);
524 	    break;
525 	case TELOPT_TSPEED:
526 	    sprintf(nfrontp, "TERMINAL-SPEED");
527 	    nfrontp += strlen(nfrontp);
528 	    if (length < 2) {
529 		sprintf(nfrontp, " (empty suboption???)");
530 		nfrontp += strlen(nfrontp);
531 		break;
532 	    }
533 	    switch (pointer[1]) {
534 	    case TELQUAL_IS:
535 		sprintf(nfrontp, " IS %.*s", length-2, (char *)pointer+2);
536 		nfrontp += strlen(nfrontp);
537 		break;
538 	    default:
539 		if (pointer[1] == 1)
540 		    sprintf(nfrontp, " SEND");
541 		else
542 		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
543 		nfrontp += strlen(nfrontp);
544 		for (i = 2; i < length; i++) {
545 		    sprintf(nfrontp, " ?%d?", pointer[i]);
546 		    nfrontp += strlen(nfrontp);
547 		}
548 		break;
549 	    }
550 	    break;
551 
552 	case TELOPT_LFLOW:
553 	    sprintf(nfrontp, "TOGGLE-FLOW-CONTROL");
554 	    nfrontp += strlen(nfrontp);
555 	    if (length < 2) {
556 		sprintf(nfrontp, " (empty suboption???)");
557 		nfrontp += strlen(nfrontp);
558 		break;
559 	    }
560 	    switch (pointer[1]) {
561 	    case 0:
562 		sprintf(nfrontp, " OFF"); break;
563 	    case 1:
564 		sprintf(nfrontp, " ON"); break;
565 	    default:
566 		sprintf(nfrontp, " %d (unknown)", pointer[1]);
567 	    }
568 	    nfrontp += strlen(nfrontp);
569 	    for (i = 2; i < length; i++) {
570 		sprintf(nfrontp, " ?%d?", pointer[i]);
571 		nfrontp += strlen(nfrontp);
572 	    }
573 	    break;
574 
575 	case TELOPT_NAWS:
576 	    sprintf(nfrontp, "NAWS");
577 	    nfrontp += strlen(nfrontp);
578 	    if (length < 2) {
579 		sprintf(nfrontp, " (empty suboption???)");
580 		nfrontp += strlen(nfrontp);
581 		break;
582 	    }
583 	    if (length == 2) {
584 		sprintf(nfrontp, " ?%d?", pointer[1]);
585 		nfrontp += strlen(nfrontp);
586 		break;
587 	    }
588 	    sprintf(nfrontp, " %d %d (%d)",
589 		pointer[1], pointer[2],
590 		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
591 	    nfrontp += strlen(nfrontp);
592 	    if (length == 4) {
593 		sprintf(nfrontp, " ?%d?", pointer[3]);
594 		nfrontp += strlen(nfrontp);
595 		break;
596 	    }
597 	    sprintf(nfrontp, " %d %d (%d)",
598 		pointer[3], pointer[4],
599 		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
600 	    nfrontp += strlen(nfrontp);
601 	    for (i = 5; i < length; i++) {
602 		sprintf(nfrontp, " ?%d?", pointer[i]);
603 		nfrontp += strlen(nfrontp);
604 	    }
605 	    break;
606 
607 	case TELOPT_LINEMODE:
608 	    sprintf(nfrontp, "LINEMODE ");
609 	    nfrontp += strlen(nfrontp);
610 	    if (length < 2) {
611 		sprintf(nfrontp, " (empty suboption???)");
612 		nfrontp += strlen(nfrontp);
613 		break;
614 	    }
615 	    switch (pointer[1]) {
616 	    case WILL:
617 		sprintf(nfrontp, "WILL ");
618 		goto common;
619 	    case WONT:
620 		sprintf(nfrontp, "WONT ");
621 		goto common;
622 	    case DO:
623 		sprintf(nfrontp, "DO ");
624 		goto common;
625 	    case DONT:
626 		sprintf(nfrontp, "DONT ");
627 	    common:
628 		nfrontp += strlen(nfrontp);
629 		if (length < 3) {
630 		    sprintf(nfrontp, "(no option???)");
631 		    nfrontp += strlen(nfrontp);
632 		    break;
633 		}
634 		switch (pointer[2]) {
635 		case LM_FORWARDMASK:
636 		    sprintf(nfrontp, "Forward Mask");
637 		    nfrontp += strlen(nfrontp);
638 		    for (i = 3; i < length; i++) {
639 			sprintf(nfrontp, " %x", pointer[i]);
640 			nfrontp += strlen(nfrontp);
641 		    }
642 		    break;
643 		default:
644 		    sprintf(nfrontp, "%d (unknown)", pointer[2]);
645 		    nfrontp += strlen(nfrontp);
646 		    for (i = 3; i < length; i++) {
647 			sprintf(nfrontp, " %d", pointer[i]);
648 			nfrontp += strlen(nfrontp);
649 		    }
650 		    break;
651 		}
652 		break;
653 
654 	    case LM_SLC:
655 		sprintf(nfrontp, "SLC");
656 		nfrontp += strlen(nfrontp);
657 		for (i = 2; i < length - 2; i += 3) {
658 		    if (pointer[i+SLC_FUNC] <= NSLC)
659 			sprintf(nfrontp, " %s", slcnames[pointer[i+SLC_FUNC]]);
660 		    else
661 			sprintf(nfrontp, " %d", pointer[i+SLC_FUNC]);
662 		    nfrontp += strlen(nfrontp);
663 		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
664 		    case SLC_NOSUPPORT:
665 			sprintf(nfrontp, " NOSUPPORT"); break;
666 		    case SLC_CANTCHANGE:
667 			sprintf(nfrontp, " CANTCHANGE"); break;
668 		    case SLC_VARIABLE:
669 			sprintf(nfrontp, " VARIABLE"); break;
670 		    case SLC_DEFAULT:
671 			sprintf(nfrontp, " DEFAULT"); break;
672 		    }
673 		    nfrontp += strlen(nfrontp);
674 		    sprintf(nfrontp, "%s%s%s",
675 			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
676 			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
677 			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
678 		    nfrontp += strlen(nfrontp);
679 		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
680 						SLC_FLUSHOUT| SLC_LEVELBITS)) {
681 			sprintf(nfrontp, "(0x%x)", pointer[i+SLC_FLAGS]);
682 			nfrontp += strlen(nfrontp);
683 		    }
684 		    sprintf(nfrontp, " %d;", pointer[i+SLC_VALUE]);
685 		    nfrontp += strlen(nfrontp);
686 		    if ((pointer[i+SLC_VALUE] == IAC) &&
687 			(pointer[i+SLC_VALUE+1] == IAC))
688 				i++;
689 		}
690 		for (; i < length; i++) {
691 		    sprintf(nfrontp, " ?%d?", pointer[i]);
692 		    nfrontp += strlen(nfrontp);
693 		}
694 		break;
695 
696 	    case LM_MODE:
697 		sprintf(nfrontp, "MODE ");
698 		nfrontp += strlen(nfrontp);
699 		if (length < 3) {
700 		    sprintf(nfrontp, "(no mode???)");
701 		    nfrontp += strlen(nfrontp);
702 		    break;
703 		}
704 		{
705 		    char tbuf[32];
706 		    sprintf(tbuf, "%s%s%s%s%s",
707 			pointer[2]&MODE_EDIT ? "|EDIT" : "",
708 			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
709 			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
710 			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
711 			pointer[2]&MODE_ACK ? "|ACK" : "");
712 		    sprintf(nfrontp, "%s", tbuf[1] ? &tbuf[1] : "0");
713 		    nfrontp += strlen(nfrontp);
714 		}
715 		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
716 		    sprintf(nfrontp, " (0x%x)", pointer[2]);
717 		    nfrontp += strlen(nfrontp);
718 		}
719 		for (i = 3; i < length; i++) {
720 		    sprintf(nfrontp, " ?0x%x?", pointer[i]);
721 		    nfrontp += strlen(nfrontp);
722 		}
723 		break;
724 	    default:
725 		sprintf(nfrontp, "%d (unknown)", pointer[1]);
726 		nfrontp += strlen(nfrontp);
727 		for (i = 2; i < length; i++) {
728 		    sprintf(nfrontp, " %d", pointer[i]);
729 		    nfrontp += strlen(nfrontp);
730 		}
731 	    }
732 	    break;
733 
734 	case TELOPT_STATUS: {
735 	    register char *cp;
736 	    register int j, k;
737 
738 	    sprintf(nfrontp, "STATUS");
739 	    nfrontp += strlen(nfrontp);
740 
741 	    switch (pointer[1]) {
742 	    default:
743 		if (pointer[1] == TELQUAL_SEND)
744 		    sprintf(nfrontp, " SEND");
745 		else
746 		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
747 		nfrontp += strlen(nfrontp);
748 		for (i = 2; i < length; i++) {
749 		    sprintf(nfrontp, " ?%d?", pointer[i]);
750 		    nfrontp += strlen(nfrontp);
751 		}
752 		break;
753 	    case TELQUAL_IS:
754 		sprintf(nfrontp, " IS\r\n");
755 		nfrontp += strlen(nfrontp);
756 
757 		for (i = 2; i < length; i++) {
758 		    switch(pointer[i]) {
759 		    case DO:	cp = "DO"; goto common2;
760 		    case DONT:	cp = "DONT"; goto common2;
761 		    case WILL:	cp = "WILL"; goto common2;
762 		    case WONT:	cp = "WONT"; goto common2;
763 		    common2:
764 			i++;
765 			if (TELOPT_OK((int)pointer[i]))
766 			    sprintf(nfrontp, " %s %s", cp, TELOPT(pointer[i]));
767 			else
768 			    sprintf(nfrontp, " %s %d", cp, pointer[i]);
769 			nfrontp += strlen(nfrontp);
770 
771 			sprintf(nfrontp, "\r\n");
772 			nfrontp += strlen(nfrontp);
773 			break;
774 
775 		    case SB:
776 			sprintf(nfrontp, " SB ");
777 			nfrontp += strlen(nfrontp);
778 			i++;
779 			j = k = i;
780 			while (j < length) {
781 			    if (pointer[j] == SE) {
782 				if (j+1 == length)
783 				    break;
784 				if (pointer[j+1] == SE)
785 				    j++;
786 				else
787 				    break;
788 			    }
789 			    pointer[k++] = pointer[j++];
790 			}
791 			printsub(0, &pointer[i], k - i);
792 			if (i < length) {
793 			    sprintf(nfrontp, " SE");
794 			    nfrontp += strlen(nfrontp);
795 			    i = j;
796 			} else
797 			    i = j - 1;
798 
799 			sprintf(nfrontp, "\r\n");
800 			nfrontp += strlen(nfrontp);
801 
802 			break;
803 
804 		    default:
805 			sprintf(nfrontp, " %d", pointer[i]);
806 			nfrontp += strlen(nfrontp);
807 			break;
808 		    }
809 		}
810 		break;
811 	    }
812 	    break;
813 	  }
814 
815 	case TELOPT_XDISPLOC:
816 	    sprintf(nfrontp, "X-DISPLAY-LOCATION ");
817 	    nfrontp += strlen(nfrontp);
818 	    switch (pointer[1]) {
819 	    case TELQUAL_IS:
820 		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
821 		break;
822 	    case TELQUAL_SEND:
823 		sprintf(nfrontp, "SEND");
824 		break;
825 	    default:
826 		sprintf(nfrontp, "- unknown qualifier %d (0x%x).",
827 				pointer[1], pointer[1]);
828 	    }
829 	    nfrontp += strlen(nfrontp);
830 	    break;
831 
832 	case TELOPT_ENVIRON:
833 	    sprintf(nfrontp, "ENVIRON ");
834 	    nfrontp += strlen(nfrontp);
835 	    switch (pointer[1]) {
836 	    case TELQUAL_IS:
837 		sprintf(nfrontp, "IS ");
838 		goto env_common;
839 	    case TELQUAL_SEND:
840 		sprintf(nfrontp, "SEND ");
841 		goto env_common;
842 	    case TELQUAL_INFO:
843 		sprintf(nfrontp, "INFO ");
844 	    env_common:
845 	    nfrontp += strlen(nfrontp);
846 		{
847 		    register int noquote = 2;
848 		    for (i = 2; i < length; i++ ) {
849 			switch (pointer[i]) {
850 			case ENV_VAR:
851 			    if (pointer[1] == TELQUAL_SEND)
852 				goto def_case;
853 			    sprintf(nfrontp, "\" VAR " + noquote);
854 			    nfrontp += strlen(nfrontp);
855 			    noquote = 2;
856 			    break;
857 
858 			case ENV_VALUE:
859 			    sprintf(nfrontp, "\" VALUE " + noquote);
860 			    nfrontp += strlen(nfrontp);
861 			    noquote = 2;
862 			    break;
863 
864 			case ENV_ESC:
865 			    sprintf(nfrontp, "\" ESC " + noquote);
866 			    nfrontp += strlen(nfrontp);
867 			    noquote = 2;
868 			    break;
869 
870 			default:
871 			def_case:
872 			    if (isprint(pointer[i]) && pointer[i] != '"') {
873 				if (noquote) {
874 				    *nfrontp++ = '"';
875 				    noquote = 0;
876 				}
877 				*nfrontp++ = pointer[i];
878 			    } else {
879 				sprintf(nfrontp, "\" %03o " + noquote,
880 							pointer[i]);
881 				nfrontp += strlen(nfrontp);
882 				noquote = 2;
883 			    }
884 			    break;
885 			}
886 		    }
887 		    if (!noquote)
888 			*nfrontp++ = '"';
889 		    break;
890 		}
891 	    }
892 	    break;
893 
894 	default:
895 	    sprintf(nfrontp, "Unknown option ");
896 	    nfrontp += strlen(nfrontp);
897 	    for (i = 0; i < length; i++) {
898 		sprintf(nfrontp, " %d", pointer[i]);
899 		nfrontp += strlen(nfrontp);
900 	    }
901 	    break;
902 	}
903 	sprintf(nfrontp, "\r\n");
904 	nfrontp += strlen(nfrontp);
905 }
906 
907 /*
908  * Dump a data buffer in hex and ascii to the output data stream.
909  */
910 void
911 printdata(tag, ptr, cnt)
912 register char *tag;
913 register char *ptr;
914 register int cnt;
915 {
916 register int i;
917 char xbuf[30];
918 
919 	while (cnt) {
920 		/* flush net output buffer if no room for new data) */
921 		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
922 			netflush();
923 		}
924 
925 		/* add a line of output */
926 		sprintf(nfrontp, "%s: ", tag);
927 		nfrontp += strlen(nfrontp);
928 		for (i = 0; i < 20 && cnt; i++) {
929 			sprintf(nfrontp, "%02x", *ptr);
930 			nfrontp += strlen(nfrontp);
931 			if (isprint(*ptr)) {
932 				xbuf[i] = *ptr;
933 			} else {
934 				xbuf[i] = '.';
935 			}
936 			if (i % 2) {
937 				*nfrontp = ' ';
938 				nfrontp++;
939 			}
940 			cnt--;
941 			ptr++;
942 		}
943 		xbuf[i] = '\0';
944 		sprintf(nfrontp, " %s\r\n", xbuf );
945 		nfrontp += strlen(nfrontp);
946 	}
947 }
948 
949 #endif /* DIAGNOSTICS */
950 
951 #ifdef	NO_STRERROR
952 char *
953 strerror(errno)
954 {
955 	extern char *sys_errlist[];
956 
957 	return(sys_errlist[errno]);
958 }
959 #endif
960