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