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