File:  [DragonFly] / src / libexec / telnetd / utility.c
Revision 1.2: download - view: text, annotated - select for diffs
Tue Jun 17 04:27:08 2003 UTC (11 years, 5 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids.  Most
ids have been removed from !lint sections and moved into comment sections.

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