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