utility.c revision 57416
157416Smarkm/*
257416Smarkm * Copyright (c) 1989, 1993
357416Smarkm *	The Regents of the University of California.  All rights reserved.
457416Smarkm *
557416Smarkm * Redistribution and use in source and binary forms, with or without
657416Smarkm * modification, are permitted provided that the following conditions
757416Smarkm * are met:
857416Smarkm * 1. Redistributions of source code must retain the above copyright
957416Smarkm *    notice, this list of conditions and the following disclaimer.
1057416Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1157416Smarkm *    notice, this list of conditions and the following disclaimer in the
1257416Smarkm *    documentation and/or other materials provided with the distribution.
1357416Smarkm * 3. All advertising materials mentioning features or use of this software
1457416Smarkm *    must display the following acknowledgement:
1557416Smarkm *	This product includes software developed by the University of
1657416Smarkm *	California, Berkeley and its contributors.
1757416Smarkm * 4. Neither the name of the University nor the names of its contributors
1857416Smarkm *    may be used to endorse or promote products derived from this software
1957416Smarkm *    without specific prior written permission.
2057416Smarkm *
2157416Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2257416Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2357416Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2457416Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2557416Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2657416Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2757416Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2857416Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2957416Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3057416Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3157416Smarkm * SUCH DAMAGE.
3257416Smarkm */
3357416Smarkm
3457416Smarkm#define PRINTOPTIONS
3557416Smarkm#include "telnetd.h"
3657416Smarkm
3757416SmarkmRCSID("$Id: utility.c,v 1.22 1999/09/16 20:41:38 assar Exp $");
3857416Smarkm
3957416Smarkm/*
4057416Smarkm * utility functions performing io related tasks
4157416Smarkm */
4257416Smarkm
4357416Smarkm/*
4457416Smarkm * ttloop
4557416Smarkm *
4657416Smarkm * A small subroutine to flush the network output buffer, get some
4757416Smarkm * data from the network, and pass it through the telnet state
4857416Smarkm * machine.  We also flush the pty input buffer (by dropping its data)
4957416Smarkm * if it becomes too full.
5057416Smarkm *
5157416Smarkm * return 0 if OK or 1 if interrupted by a signal.
5257416Smarkm */
5357416Smarkm
5457416Smarkmint
5557416Smarkmttloop(void)
5657416Smarkm{
5757416Smarkm    void netflush(void);
5857416Smarkm
5957416Smarkm    DIAG(TD_REPORT, {
6057416Smarkm	output_data("td: ttloop\r\n");
6157416Smarkm    });
6257416Smarkm    if (nfrontp-nbackp)
6357416Smarkm	netflush();
6457416Smarkm    ncc = read(net, netibuf, sizeof netibuf);
6557416Smarkm    if (ncc < 0) {
6657416Smarkm	if (errno == EINTR)
6757416Smarkm	    return 1;
6857416Smarkm	syslog(LOG_INFO, "ttloop:  read: %m\n");
6957416Smarkm	exit(1);
7057416Smarkm    } else if (ncc == 0) {
7157416Smarkm	syslog(LOG_INFO, "ttloop:  peer died: %m\n");
7257416Smarkm	exit(1);
7357416Smarkm    }
7457416Smarkm    DIAG(TD_REPORT, {
7557416Smarkm	output_data("td: ttloop read %d chars\r\n", ncc);
7657416Smarkm    });
7757416Smarkm    netip = netibuf;
7857416Smarkm    telrcv();			/* state machine */
7957416Smarkm    if (ncc > 0) {
8057416Smarkm	pfrontp = pbackp = ptyobuf;
8157416Smarkm	telrcv();
8257416Smarkm    }
8357416Smarkm    return 0;
8457416Smarkm}  /* end of ttloop */
8557416Smarkm
8657416Smarkm/*
8757416Smarkm * Check a descriptor to see if out of band data exists on it.
8857416Smarkm */
8957416Smarkmint
9057416Smarkmstilloob(int s)
9157416Smarkm{
9257416Smarkm    static struct timeval timeout = { 0 };
9357416Smarkm    fd_set	excepts;
9457416Smarkm    int value;
9557416Smarkm
9657416Smarkm    do {
9757416Smarkm	FD_ZERO(&excepts);
9857416Smarkm	FD_SET(s, &excepts);
9957416Smarkm	value = select(s+1, 0, 0, &excepts, &timeout);
10057416Smarkm    } while ((value == -1) && (errno == EINTR));
10157416Smarkm
10257416Smarkm    if (value < 0) {
10357416Smarkm	fatalperror(ourpty, "select");
10457416Smarkm    }
10557416Smarkm    if (FD_ISSET(s, &excepts)) {
10657416Smarkm	return 1;
10757416Smarkm    } else {
10857416Smarkm	return 0;
10957416Smarkm    }
11057416Smarkm}
11157416Smarkm
11257416Smarkmvoid
11357416Smarkmptyflush(void)
11457416Smarkm{
11557416Smarkm    int n;
11657416Smarkm
11757416Smarkm    if ((n = pfrontp - pbackp) > 0) {
11857416Smarkm	DIAG((TD_REPORT | TD_PTYDATA), {
11957416Smarkm	    output_data("td: ptyflush %d chars\r\n", n);
12057416Smarkm	});
12157416Smarkm	DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
12257416Smarkm	n = write(ourpty, pbackp, n);
12357416Smarkm    }
12457416Smarkm    if (n < 0) {
12557416Smarkm	if (errno == EWOULDBLOCK || errno == EINTR)
12657416Smarkm	    return;
12757416Smarkm	cleanup(0);
12857416Smarkm    }
12957416Smarkm    pbackp += n;
13057416Smarkm    if (pbackp == pfrontp)
13157416Smarkm	pbackp = pfrontp = ptyobuf;
13257416Smarkm}
13357416Smarkm
13457416Smarkm/*
13557416Smarkm * nextitem()
13657416Smarkm *
13757416Smarkm *	Return the address of the next "item" in the TELNET data
13857416Smarkm * stream.  This will be the address of the next character if
13957416Smarkm * the current address is a user data character, or it will
14057416Smarkm * be the address of the character following the TELNET command
14157416Smarkm * if the current address is a TELNET IAC ("I Am a Command")
14257416Smarkm * character.
14357416Smarkm */
14457416Smarkmchar *
14557416Smarkmnextitem(char *current)
14657416Smarkm{
14757416Smarkm    if ((*current&0xff) != IAC) {
14857416Smarkm	return current+1;
14957416Smarkm    }
15057416Smarkm    switch (*(current+1)&0xff) {
15157416Smarkm    case DO:
15257416Smarkm    case DONT:
15357416Smarkm    case WILL:
15457416Smarkm    case WONT:
15557416Smarkm	return current+3;
15657416Smarkm    case SB:{
15757416Smarkm	/* loop forever looking for the SE */
15857416Smarkm	char *look = current+2;
15957416Smarkm
16057416Smarkm	for (;;) {
16157416Smarkm	    if ((*look++&0xff) == IAC) {
16257416Smarkm		if ((*look++&0xff) == SE) {
16357416Smarkm		    return look;
16457416Smarkm		}
16557416Smarkm	    }
16657416Smarkm	}
16757416Smarkm    }
16857416Smarkm    default:
16957416Smarkm	return current+2;
17057416Smarkm    }
17157416Smarkm}
17257416Smarkm
17357416Smarkm
17457416Smarkm/*
17557416Smarkm * netclear()
17657416Smarkm *
17757416Smarkm *	We are about to do a TELNET SYNCH operation.  Clear
17857416Smarkm * the path to the network.
17957416Smarkm *
18057416Smarkm *	Things are a bit tricky since we may have sent the first
18157416Smarkm * byte or so of a previous TELNET command into the network.
18257416Smarkm * So, we have to scan the network buffer from the beginning
18357416Smarkm * until we are up to where we want to be.
18457416Smarkm *
18557416Smarkm *	A side effect of what we do, just to keep things
18657416Smarkm * simple, is to clear the urgent data pointer.  The principal
18757416Smarkm * caller should be setting the urgent data pointer AFTER calling
18857416Smarkm * us in any case.
18957416Smarkm */
19057416Smarkmvoid
19157416Smarkmnetclear(void)
19257416Smarkm{
19357416Smarkm    char *thisitem, *next;
19457416Smarkm    char *good;
19557416Smarkm#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
19657416Smarkm			 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
19757416Smarkm
19857416Smarkm#ifdef ENCRYPTION
19957416Smarkm	thisitem = nclearto > netobuf ? nclearto : netobuf;
20057416Smarkm#else
20157416Smarkm	thisitem = netobuf;
20257416Smarkm#endif
20357416Smarkm
20457416Smarkm	while ((next = nextitem(thisitem)) <= nbackp) {
20557416Smarkm	    thisitem = next;
20657416Smarkm	}
20757416Smarkm
20857416Smarkm	/* Now, thisitem is first before/at boundary. */
20957416Smarkm
21057416Smarkm#ifdef ENCRYPTION
21157416Smarkm	good = nclearto > netobuf ? nclearto : netobuf;
21257416Smarkm#else
21357416Smarkm	good = netobuf;	/* where the good bytes go */
21457416Smarkm#endif
21557416Smarkm
21657416Smarkm	while (nfrontp > thisitem) {
21757416Smarkm	    if (wewant(thisitem)) {
21857416Smarkm		int length;
21957416Smarkm
22057416Smarkm		next = thisitem;
22157416Smarkm		do {
22257416Smarkm		    next = nextitem(next);
22357416Smarkm		} while (wewant(next) && (nfrontp > next));
22457416Smarkm		length = next-thisitem;
22557416Smarkm		memmove(good, thisitem, length);
22657416Smarkm		good += length;
22757416Smarkm		thisitem = next;
22857416Smarkm	    } else {
22957416Smarkm		thisitem = nextitem(thisitem);
23057416Smarkm	    }
23157416Smarkm	}
23257416Smarkm
23357416Smarkm	nbackp = netobuf;
23457416Smarkm	nfrontp = good;		/* next byte to be sent */
23557416Smarkm	neturg = 0;
23657416Smarkm}  /* end of netclear */
23757416Smarkm
23857416Smarkm/*
23957416Smarkm *  netflush
24057416Smarkm *		Send as much data as possible to the network,
24157416Smarkm *	handling requests for urgent data.
24257416Smarkm */
24357416Smarkmvoid
24457416Smarkmnetflush(void)
24557416Smarkm{
24657416Smarkm    int n;
24757416Smarkm    extern int not42;
24857416Smarkm
24957416Smarkm    if ((n = nfrontp - nbackp) > 0) {
25057416Smarkm	DIAG(TD_REPORT,
25157416Smarkm	     { n += output_data("td: netflush %d chars\r\n", n);
25257416Smarkm	     });
25357416Smarkm#ifdef ENCRYPTION
25457416Smarkm	if (encrypt_output) {
25557416Smarkm	    char *s = nclearto ? nclearto : nbackp;
25657416Smarkm	    if (nfrontp - s > 0) {
25757416Smarkm		(*encrypt_output)((unsigned char *)s, nfrontp-s);
25857416Smarkm		nclearto = nfrontp;
25957416Smarkm	    }
26057416Smarkm	}
26157416Smarkm#endif
26257416Smarkm	/*
26357416Smarkm	 * if no urgent data, or if the other side appears to be an
26457416Smarkm	 * old 4.2 client (and thus unable to survive TCP urgent data),
26557416Smarkm	 * write the entire buffer in non-OOB mode.
26657416Smarkm	 */
26757416Smarkm#if 1 /* remove this to make it work between solaris 2.6 and linux */
26857416Smarkm	if ((neturg == 0) || (not42 == 0)) {
26957416Smarkm#endif
27057416Smarkm	    n = write(net, nbackp, n);	/* normal write */
27157416Smarkm#if 1 /* remove this to make it work between solaris 2.6 and linux */
27257416Smarkm	} else {
27357416Smarkm	    n = neturg - nbackp;
27457416Smarkm	    /*
27557416Smarkm	     * In 4.2 (and 4.3) systems, there is some question about
27657416Smarkm	     * what byte in a sendOOB operation is the "OOB" data.
27757416Smarkm	     * To make ourselves compatible, we only send ONE byte
27857416Smarkm	     * out of band, the one WE THINK should be OOB (though
27957416Smarkm	     * we really have more the TCP philosophy of urgent data
28057416Smarkm	     * rather than the Unix philosophy of OOB data).
28157416Smarkm	     */
28257416Smarkm	    if (n > 1) {
28357416Smarkm		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
28457416Smarkm	    } else {
28557416Smarkm		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
28657416Smarkm	    }
28757416Smarkm	}
28857416Smarkm#endif
28957416Smarkm    }
29057416Smarkm    if (n < 0) {
29157416Smarkm	if (errno == EWOULDBLOCK || errno == EINTR)
29257416Smarkm	    return;
29357416Smarkm	cleanup(0);
29457416Smarkm    }
29557416Smarkm    nbackp += n;
29657416Smarkm#ifdef ENCRYPTION
29757416Smarkm    if (nbackp > nclearto)
29857416Smarkm	nclearto = 0;
29957416Smarkm#endif
30057416Smarkm    if (nbackp >= neturg) {
30157416Smarkm	neturg = 0;
30257416Smarkm    }
30357416Smarkm    if (nbackp == nfrontp) {
30457416Smarkm	nbackp = nfrontp = netobuf;
30557416Smarkm#ifdef ENCRYPTION
30657416Smarkm	nclearto = 0;
30757416Smarkm#endif
30857416Smarkm    }
30957416Smarkm    return;
31057416Smarkm}
31157416Smarkm
31257416Smarkm
31357416Smarkm/*
31457416Smarkm * writenet
31557416Smarkm *
31657416Smarkm * Just a handy little function to write a bit of raw data to the net.
31757416Smarkm * It will force a transmit of the buffer if necessary
31857416Smarkm *
31957416Smarkm * arguments
32057416Smarkm *    ptr - A pointer to a character string to write
32157416Smarkm *    len - How many bytes to write
32257416Smarkm */
32357416Smarkmvoid
32457416Smarkmwritenet(unsigned char *ptr, int len)
32557416Smarkm{
32657416Smarkm    /* flush buffer if no room for new data) */
32757416Smarkm    while ((&netobuf[BUFSIZ] - nfrontp) < len) {
32857416Smarkm	/* if this fails, don't worry, buffer is a little big */
32957416Smarkm	netflush();
33057416Smarkm    }
33157416Smarkm
33257416Smarkm    memmove(nfrontp, ptr, len);
33357416Smarkm    nfrontp += len;
33457416Smarkm}
33557416Smarkm
33657416Smarkm
33757416Smarkm/*
33857416Smarkm * miscellaneous functions doing a variety of little jobs follow ...
33957416Smarkm */
34057416Smarkm
34157416Smarkm
34257416Smarkmvoid fatal(int f, char *msg)
34357416Smarkm{
34457416Smarkm    char buf[BUFSIZ];
34557416Smarkm
34657416Smarkm    snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
34757416Smarkm#ifdef ENCRYPTION
34857416Smarkm    if (encrypt_output) {
34957416Smarkm	/*
35057416Smarkm	 * Better turn off encryption first....
35157416Smarkm	 * Hope it flushes...
35257416Smarkm	 */
35357416Smarkm	encrypt_send_end();
35457416Smarkm	netflush();
35557416Smarkm    }
35657416Smarkm#endif
35757416Smarkm    write(f, buf, (int)strlen(buf));
35857416Smarkm    sleep(1);	/*XXX*/
35957416Smarkm    exit(1);
36057416Smarkm}
36157416Smarkm
36257416Smarkmvoid
36357416Smarkmfatalperror(int f, const char *msg)
36457416Smarkm{
36557416Smarkm    char buf[BUFSIZ];
36657416Smarkm
36757416Smarkm    snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
36857416Smarkm    fatal(f, buf);
36957416Smarkm}
37057416Smarkm
37157416Smarkmchar editedhost[32];
37257416Smarkm
37357416Smarkmvoid edithost(char *pat, char *host)
37457416Smarkm{
37557416Smarkm    char *res = editedhost;
37657416Smarkm
37757416Smarkm    if (!pat)
37857416Smarkm	pat = "";
37957416Smarkm    while (*pat) {
38057416Smarkm	switch (*pat) {
38157416Smarkm
38257416Smarkm	case '#':
38357416Smarkm	    if (*host)
38457416Smarkm		host++;
38557416Smarkm	    break;
38657416Smarkm
38757416Smarkm	case '@':
38857416Smarkm	    if (*host)
38957416Smarkm		*res++ = *host++;
39057416Smarkm	    break;
39157416Smarkm
39257416Smarkm	default:
39357416Smarkm	    *res++ = *pat;
39457416Smarkm	    break;
39557416Smarkm	}
39657416Smarkm	if (res == &editedhost[sizeof editedhost - 1]) {
39757416Smarkm	    *res = '\0';
39857416Smarkm	    return;
39957416Smarkm	}
40057416Smarkm	pat++;
40157416Smarkm    }
40257416Smarkm    if (*host)
40357416Smarkm	strlcpy (res, host,
40457416Smarkm			 sizeof editedhost - (res - editedhost));
40557416Smarkm    else
40657416Smarkm	*res = '\0';
40757416Smarkm    editedhost[sizeof editedhost - 1] = '\0';
40857416Smarkm}
40957416Smarkm
41057416Smarkmstatic char *putlocation;
41157416Smarkm
41257416Smarkmvoid
41357416Smarkmputstr(char *s)
41457416Smarkm{
41557416Smarkm
41657416Smarkm    while (*s)
41757416Smarkm	putchr(*s++);
41857416Smarkm}
41957416Smarkm
42057416Smarkmvoid
42157416Smarkmputchr(int cc)
42257416Smarkm{
42357416Smarkm    *putlocation++ = cc;
42457416Smarkm}
42557416Smarkm
42657416Smarkm/*
42757416Smarkm * This is split on two lines so that SCCS will not see the M
42857416Smarkm * between two % signs and expand it...
42957416Smarkm */
43057416Smarkmstatic char fmtstr[] = { "%l:%M" "%P on %A, %d %B %Y" };
43157416Smarkm
43257416Smarkmvoid putf(char *cp, char *where)
43357416Smarkm{
43457416Smarkm#ifdef HAVE_UNAME
43557416Smarkm    struct utsname name;
43657416Smarkm#endif
43757416Smarkm    char *slash;
43857416Smarkm    time_t t;
43957416Smarkm    char db[100];
44057416Smarkm
44157416Smarkm    /* if we don't have uname, set these to sensible values */
44257416Smarkm    char *sysname = "Unix",
44357416Smarkm	*machine = "",
44457416Smarkm	*release = "",
44557416Smarkm	*version = "";
44657416Smarkm
44757416Smarkm#ifdef HAVE_UNAME
44857416Smarkm    uname(&name);
44957416Smarkm    sysname=name.sysname;
45057416Smarkm    machine=name.machine;
45157416Smarkm    release=name.release;
45257416Smarkm    version=name.version;
45357416Smarkm#endif
45457416Smarkm
45557416Smarkm    putlocation = where;
45657416Smarkm
45757416Smarkm    while (*cp) {
45857416Smarkm	if (*cp != '%') {
45957416Smarkm	    putchr(*cp++);
46057416Smarkm	    continue;
46157416Smarkm	}
46257416Smarkm	switch (*++cp) {
46357416Smarkm
46457416Smarkm	case 't':
46557416Smarkm#ifdef	STREAMSPTY
46657416Smarkm	    /* names are like /dev/pts/2 -- we want pts/2 */
46757416Smarkm	    slash = strchr(line+1, '/');
46857416Smarkm#else
46957416Smarkm	    slash = strrchr(line, '/');
47057416Smarkm#endif
47157416Smarkm	    if (slash == (char *) 0)
47257416Smarkm		putstr(line);
47357416Smarkm	    else
47457416Smarkm		putstr(&slash[1]);
47557416Smarkm	    break;
47657416Smarkm
47757416Smarkm	case 'h':
47857416Smarkm	    putstr(editedhost);
47957416Smarkm	    break;
48057416Smarkm
48157416Smarkm	case 's':
48257416Smarkm	    putstr(sysname);
48357416Smarkm	    break;
48457416Smarkm
48557416Smarkm	case 'm':
48657416Smarkm	    putstr(machine);
48757416Smarkm	    break;
48857416Smarkm
48957416Smarkm	case 'r':
49057416Smarkm	    putstr(release);
49157416Smarkm	    break;
49257416Smarkm
49357416Smarkm	case 'v':
49457416Smarkm	    putstr(version);
49557416Smarkm	    break;
49657416Smarkm
49757416Smarkm	case 'd':
49857416Smarkm	    time(&t);
49957416Smarkm	    strftime(db, sizeof(db), fmtstr, localtime(&t));
50057416Smarkm	    putstr(db);
50157416Smarkm	    break;
50257416Smarkm
50357416Smarkm	case '%':
50457416Smarkm	    putchr('%');
50557416Smarkm	    break;
50657416Smarkm	}
50757416Smarkm	cp++;
50857416Smarkm    }
50957416Smarkm}
51057416Smarkm
51157416Smarkm#ifdef DIAGNOSTICS
51257416Smarkm/*
51357416Smarkm * Print telnet options and commands in plain text, if possible.
51457416Smarkm */
51557416Smarkmvoid
51657416Smarkmprintoption(char *fmt, int option)
51757416Smarkm{
51857416Smarkm    if (TELOPT_OK(option))
51957416Smarkm	output_data("%s %s\r\n",
52057416Smarkm		    fmt,
52157416Smarkm		    TELOPT(option));
52257416Smarkm    else if (TELCMD_OK(option))
52357416Smarkm	output_data("%s %s\r\n",
52457416Smarkm		    fmt,
52557416Smarkm		    TELCMD(option));
52657416Smarkm    else
52757416Smarkm	output_data("%s %d\r\n",
52857416Smarkm		    fmt,
52957416Smarkm		    option);
53057416Smarkm    return;
53157416Smarkm}
53257416Smarkm
53357416Smarkmvoid
53457416Smarkmprintsub(int direction, unsigned char *pointer, int length)
53557416Smarkm        		          	/* '<' or '>' */
53657416Smarkm                 	         	/* where suboption data sits */
53757416Smarkm       			       		/* length of suboption data */
53857416Smarkm{
53957416Smarkm    int i = 0;
54057416Smarkm    unsigned char buf[512];
54157416Smarkm
54257416Smarkm    if (!(diagnostic & TD_OPTIONS))
54357416Smarkm	return;
54457416Smarkm
54557416Smarkm    if (direction) {
54657416Smarkm	output_data("td: %s suboption ",
54757416Smarkm		    direction == '<' ? "recv" : "send");
54857416Smarkm	if (length >= 3) {
54957416Smarkm	    int j;
55057416Smarkm
55157416Smarkm	    i = pointer[length-2];
55257416Smarkm	    j = pointer[length-1];
55357416Smarkm
55457416Smarkm	    if (i != IAC || j != SE) {
55557416Smarkm		output_data("(terminated by ");
55657416Smarkm		if (TELOPT_OK(i))
55757416Smarkm		    output_data("%s ",
55857416Smarkm				TELOPT(i));
55957416Smarkm		else if (TELCMD_OK(i))
56057416Smarkm		    output_data("%s ",
56157416Smarkm				TELCMD(i));
56257416Smarkm		else
56357416Smarkm		    output_data("%d ",
56457416Smarkm				i);
56557416Smarkm		if (TELOPT_OK(j))
56657416Smarkm		    output_data("%s",
56757416Smarkm				TELOPT(j));
56857416Smarkm		else if (TELCMD_OK(j))
56957416Smarkm		    output_data("%s",
57057416Smarkm				TELCMD(j));
57157416Smarkm		else
57257416Smarkm		    output_data("%d",
57357416Smarkm				j);
57457416Smarkm		output_data(", not IAC SE!) ");
57557416Smarkm	    }
57657416Smarkm	}
57757416Smarkm	length -= 2;
57857416Smarkm    }
57957416Smarkm    if (length < 1) {
58057416Smarkm	output_data("(Empty suboption??\?)");
58157416Smarkm	return;
58257416Smarkm    }
58357416Smarkm    switch (pointer[0]) {
58457416Smarkm    case TELOPT_TTYPE:
58557416Smarkm	output_data("TERMINAL-TYPE ");
58657416Smarkm	switch (pointer[1]) {
58757416Smarkm	case TELQUAL_IS:
58857416Smarkm	    output_data("IS \"%.*s\"",
58957416Smarkm			length-2,
59057416Smarkm			(char *)pointer+2);
59157416Smarkm	    break;
59257416Smarkm	case TELQUAL_SEND:
59357416Smarkm	    output_data("SEND");
59457416Smarkm	    break;
59557416Smarkm	default:
59657416Smarkm	    output_data("- unknown qualifier %d (0x%x).",
59757416Smarkm			pointer[1], pointer[1]);
59857416Smarkm	}
59957416Smarkm	break;
60057416Smarkm    case TELOPT_TSPEED:
60157416Smarkm	output_data("TERMINAL-SPEED");
60257416Smarkm	if (length < 2) {
60357416Smarkm	    output_data(" (empty suboption??\?)");
60457416Smarkm	    break;
60557416Smarkm	}
60657416Smarkm	switch (pointer[1]) {
60757416Smarkm	case TELQUAL_IS:
60857416Smarkm	    output_data(" IS %.*s", length-2, (char *)pointer+2);
60957416Smarkm	    break;
61057416Smarkm	default:
61157416Smarkm	    if (pointer[1] == 1)
61257416Smarkm		output_data(" SEND");
61357416Smarkm	    else
61457416Smarkm		output_data(" %d (unknown)", pointer[1]);
61557416Smarkm	    for (i = 2; i < length; i++) {
61657416Smarkm		output_data(" ?%d?", pointer[i]);
61757416Smarkm	    }
61857416Smarkm	    break;
61957416Smarkm	}
62057416Smarkm	break;
62157416Smarkm
62257416Smarkm    case TELOPT_LFLOW:
62357416Smarkm	output_data("TOGGLE-FLOW-CONTROL");
62457416Smarkm	if (length < 2) {
62557416Smarkm	    output_data(" (empty suboption??\?)");
62657416Smarkm	    break;
62757416Smarkm	}
62857416Smarkm	switch (pointer[1]) {
62957416Smarkm	case LFLOW_OFF:
63057416Smarkm	    output_data(" OFF");
63157416Smarkm	    break;
63257416Smarkm	case LFLOW_ON:
63357416Smarkm	    output_data(" ON");
63457416Smarkm	    break;
63557416Smarkm	case LFLOW_RESTART_ANY:
63657416Smarkm	    output_data(" RESTART-ANY");
63757416Smarkm	    break;
63857416Smarkm	case LFLOW_RESTART_XON:
63957416Smarkm	    output_data(" RESTART-XON");
64057416Smarkm	    break;
64157416Smarkm	default:
64257416Smarkm	    output_data(" %d (unknown)",
64357416Smarkm			pointer[1]);
64457416Smarkm	}
64557416Smarkm	for (i = 2; i < length; i++) {
64657416Smarkm	    output_data(" ?%d?",
64757416Smarkm			pointer[i]);
64857416Smarkm	}
64957416Smarkm	break;
65057416Smarkm
65157416Smarkm    case TELOPT_NAWS:
65257416Smarkm	output_data("NAWS");
65357416Smarkm	if (length < 2) {
65457416Smarkm	    output_data(" (empty suboption??\?)");
65557416Smarkm	    break;
65657416Smarkm	}
65757416Smarkm	if (length == 2) {
65857416Smarkm	    output_data(" ?%d?",
65957416Smarkm			pointer[1]);
66057416Smarkm	    break;
66157416Smarkm	}
66257416Smarkm	output_data(" %u %u(%u)",
66357416Smarkm		    pointer[1],
66457416Smarkm		    pointer[2],
66557416Smarkm		    (((unsigned int)pointer[1])<<8) + pointer[2]);
66657416Smarkm	if (length == 4) {
66757416Smarkm	    output_data(" ?%d?",
66857416Smarkm			pointer[3]);
66957416Smarkm	    break;
67057416Smarkm	}
67157416Smarkm	output_data(" %u %u(%u)",
67257416Smarkm		    pointer[3],
67357416Smarkm		    pointer[4],
67457416Smarkm		    (((unsigned int)pointer[3])<<8) + pointer[4]);
67557416Smarkm	for (i = 5; i < length; i++) {
67657416Smarkm	    output_data(" ?%d?",
67757416Smarkm			pointer[i]);
67857416Smarkm	}
67957416Smarkm	break;
68057416Smarkm
68157416Smarkm    case TELOPT_LINEMODE:
68257416Smarkm	output_data("LINEMODE ");
68357416Smarkm	if (length < 2) {
68457416Smarkm	    output_data(" (empty suboption??\?)");
68557416Smarkm	    break;
68657416Smarkm	}
68757416Smarkm	switch (pointer[1]) {
68857416Smarkm	case WILL:
68957416Smarkm	    output_data("WILL ");
69057416Smarkm	    goto common;
69157416Smarkm	case WONT:
69257416Smarkm	    output_data("WONT ");
69357416Smarkm	    goto common;
69457416Smarkm	case DO:
69557416Smarkm	    output_data("DO ");
69657416Smarkm	    goto common;
69757416Smarkm	case DONT:
69857416Smarkm	    output_data("DONT ");
69957416Smarkm	common:
70057416Smarkm	    if (length < 3) {
70157416Smarkm		output_data("(no option??\?)");
70257416Smarkm		break;
70357416Smarkm	    }
70457416Smarkm	    switch (pointer[2]) {
70557416Smarkm	    case LM_FORWARDMASK:
70657416Smarkm		output_data("Forward Mask");
70757416Smarkm		for (i = 3; i < length; i++) {
70857416Smarkm		    output_data(" %x", pointer[i]);
70957416Smarkm		}
71057416Smarkm		break;
71157416Smarkm	    default:
71257416Smarkm		output_data("%d (unknown)",
71357416Smarkm			    pointer[2]);
71457416Smarkm		for (i = 3; i < length; i++) {
71557416Smarkm		    output_data(" %d",
71657416Smarkm				pointer[i]);
71757416Smarkm		}
71857416Smarkm		break;
71957416Smarkm	    }
72057416Smarkm	    break;
72157416Smarkm
72257416Smarkm	case LM_SLC:
72357416Smarkm	    output_data("SLC");
72457416Smarkm	    for (i = 2; i < length - 2; i += 3) {
72557416Smarkm		if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
72657416Smarkm		    output_data(" %s",
72757416Smarkm				SLC_NAME(pointer[i+SLC_FUNC]));
72857416Smarkm		else
72957416Smarkm		    output_data(" %d",
73057416Smarkm				pointer[i+SLC_FUNC]);
73157416Smarkm		switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
73257416Smarkm		case SLC_NOSUPPORT:
73357416Smarkm		    output_data(" NOSUPPORT");
73457416Smarkm		    break;
73557416Smarkm		case SLC_CANTCHANGE:
73657416Smarkm		    output_data(" CANTCHANGE");
73757416Smarkm		    break;
73857416Smarkm		case SLC_VARIABLE:
73957416Smarkm		    output_data(" VARIABLE");
74057416Smarkm		    break;
74157416Smarkm		case SLC_DEFAULT:
74257416Smarkm		    output_data(" DEFAULT");
74357416Smarkm		    break;
74457416Smarkm		}
74557416Smarkm		output_data("%s%s%s",
74657416Smarkm			    pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
74757416Smarkm			    pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
74857416Smarkm			    pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
74957416Smarkm		if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
75057416Smarkm					    SLC_FLUSHOUT| SLC_LEVELBITS)) {
75157416Smarkm		    output_data("(0x%x)",
75257416Smarkm				pointer[i+SLC_FLAGS]);
75357416Smarkm		}
75457416Smarkm		output_data(" %d;",
75557416Smarkm			    pointer[i+SLC_VALUE]);
75657416Smarkm		if ((pointer[i+SLC_VALUE] == IAC) &&
75757416Smarkm		    (pointer[i+SLC_VALUE+1] == IAC))
75857416Smarkm		    i++;
75957416Smarkm	    }
76057416Smarkm	    for (; i < length; i++) {
76157416Smarkm		output_data(" ?%d?",
76257416Smarkm			    pointer[i]);
76357416Smarkm	    }
76457416Smarkm	    break;
76557416Smarkm
76657416Smarkm	case LM_MODE:
76757416Smarkm	    output_data("MODE ");
76857416Smarkm	    if (length < 3) {
76957416Smarkm		output_data("(no mode??\?)");
77057416Smarkm		break;
77157416Smarkm	    }
77257416Smarkm	    {
77357416Smarkm		char tbuf[32];
77457416Smarkm		snprintf(tbuf,
77557416Smarkm			 sizeof(tbuf),
77657416Smarkm			 "%s%s%s%s%s",
77757416Smarkm			 pointer[2]&MODE_EDIT ? "|EDIT" : "",
77857416Smarkm			 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
77957416Smarkm			 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
78057416Smarkm			 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
78157416Smarkm			 pointer[2]&MODE_ACK ? "|ACK" : "");
78257416Smarkm		output_data("%s",
78357416Smarkm			    tbuf[1] ? &tbuf[1] : "0");
78457416Smarkm	    }
78557416Smarkm	    if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
78657416Smarkm		output_data(" (0x%x)",
78757416Smarkm			    pointer[2]);
78857416Smarkm	    }
78957416Smarkm	    for (i = 3; i < length; i++) {
79057416Smarkm		output_data(" ?0x%x?",
79157416Smarkm			 pointer[i]);
79257416Smarkm	    }
79357416Smarkm	    break;
79457416Smarkm	default:
79557416Smarkm	    output_data("%d (unknown)",
79657416Smarkm			pointer[1]);
79757416Smarkm	    for (i = 2; i < length; i++) {
79857416Smarkm		output_data(" %d", pointer[i]);
79957416Smarkm	    }
80057416Smarkm	}
80157416Smarkm	break;
80257416Smarkm
80357416Smarkm    case TELOPT_STATUS: {
80457416Smarkm	char *cp;
80557416Smarkm	int j, k;
80657416Smarkm
80757416Smarkm	output_data("STATUS");
80857416Smarkm
80957416Smarkm	switch (pointer[1]) {
81057416Smarkm	default:
81157416Smarkm	    if (pointer[1] == TELQUAL_SEND)
81257416Smarkm		output_data(" SEND");
81357416Smarkm	    else
81457416Smarkm		output_data(" %d (unknown)",
81557416Smarkm			    pointer[1]);
81657416Smarkm	    for (i = 2; i < length; i++) {
81757416Smarkm		output_data(" ?%d?",
81857416Smarkm			    pointer[i]);
81957416Smarkm	    }
82057416Smarkm	    break;
82157416Smarkm	case TELQUAL_IS:
82257416Smarkm	    output_data(" IS\r\n");
82357416Smarkm
82457416Smarkm	    for (i = 2; i < length; i++) {
82557416Smarkm		switch(pointer[i]) {
82657416Smarkm		case DO:	cp = "DO"; goto common2;
82757416Smarkm		case DONT:	cp = "DONT"; goto common2;
82857416Smarkm		case WILL:	cp = "WILL"; goto common2;
82957416Smarkm		case WONT:	cp = "WONT"; goto common2;
83057416Smarkm		common2:
83157416Smarkm		i++;
83257416Smarkm		if (TELOPT_OK(pointer[i]))
83357416Smarkm		    output_data(" %s %s",
83457416Smarkm				cp,
83557416Smarkm				TELOPT(pointer[i]));
83657416Smarkm		else
83757416Smarkm		    output_data(" %s %d",
83857416Smarkm				cp,
83957416Smarkm				pointer[i]);
84057416Smarkm
84157416Smarkm		output_data("\r\n");
84257416Smarkm		break;
84357416Smarkm
84457416Smarkm		case SB:
84557416Smarkm		    output_data(" SB ");
84657416Smarkm		    i++;
84757416Smarkm		    j = k = i;
84857416Smarkm		    while (j < length) {
84957416Smarkm			if (pointer[j] == SE) {
85057416Smarkm			    if (j+1 == length)
85157416Smarkm				break;
85257416Smarkm			    if (pointer[j+1] == SE)
85357416Smarkm				j++;
85457416Smarkm			    else
85557416Smarkm				break;
85657416Smarkm			}
85757416Smarkm			pointer[k++] = pointer[j++];
85857416Smarkm		    }
85957416Smarkm		    printsub(0, &pointer[i], k - i);
86057416Smarkm		    if (i < length) {
86157416Smarkm			output_data(" SE");
86257416Smarkm			i = j;
86357416Smarkm		    } else
86457416Smarkm			i = j - 1;
86557416Smarkm
86657416Smarkm		    output_data("\r\n");
86757416Smarkm
86857416Smarkm		    break;
86957416Smarkm
87057416Smarkm		default:
87157416Smarkm		    output_data(" %d",
87257416Smarkm				pointer[i]);
87357416Smarkm		    break;
87457416Smarkm		}
87557416Smarkm	    }
87657416Smarkm	    break;
87757416Smarkm	}
87857416Smarkm	break;
87957416Smarkm    }
88057416Smarkm
88157416Smarkm    case TELOPT_XDISPLOC:
88257416Smarkm	output_data("X-DISPLAY-LOCATION ");
88357416Smarkm	switch (pointer[1]) {
88457416Smarkm	case TELQUAL_IS:
88557416Smarkm	    output_data("IS \"%.*s\"",
88657416Smarkm			length-2,
88757416Smarkm			(char *)pointer+2);
88857416Smarkm	    break;
88957416Smarkm	case TELQUAL_SEND:
89057416Smarkm	    output_data("SEND");
89157416Smarkm	    break;
89257416Smarkm	default:
89357416Smarkm	    output_data("- unknown qualifier %d (0x%x).",
89457416Smarkm			pointer[1], pointer[1]);
89557416Smarkm	}
89657416Smarkm	break;
89757416Smarkm
89857416Smarkm    case TELOPT_NEW_ENVIRON:
89957416Smarkm	output_data("NEW-ENVIRON ");
90057416Smarkm	goto env_common1;
90157416Smarkm    case TELOPT_OLD_ENVIRON:
90257416Smarkm	output_data("OLD-ENVIRON");
90357416Smarkm    env_common1:
90457416Smarkm	switch (pointer[1]) {
90557416Smarkm	case TELQUAL_IS:
90657416Smarkm	    output_data("IS ");
90757416Smarkm	    goto env_common;
90857416Smarkm	case TELQUAL_SEND:
90957416Smarkm	    output_data("SEND ");
91057416Smarkm	    goto env_common;
91157416Smarkm	case TELQUAL_INFO:
91257416Smarkm	    output_data("INFO ");
91357416Smarkm	env_common:
91457416Smarkm	    {
91557416Smarkm		int noquote = 2;
91657416Smarkm		for (i = 2; i < length; i++ ) {
91757416Smarkm		    switch (pointer[i]) {
91857416Smarkm		    case NEW_ENV_VAR:
91957416Smarkm			output_data("\" VAR " + noquote);
92057416Smarkm			noquote = 2;
92157416Smarkm			break;
92257416Smarkm
92357416Smarkm		    case NEW_ENV_VALUE:
92457416Smarkm			output_data("\" VALUE " + noquote);
92557416Smarkm			noquote = 2;
92657416Smarkm			break;
92757416Smarkm
92857416Smarkm		    case ENV_ESC:
92957416Smarkm			output_data("\" ESC " + noquote);
93057416Smarkm			noquote = 2;
93157416Smarkm			break;
93257416Smarkm
93357416Smarkm		    case ENV_USERVAR:
93457416Smarkm			output_data("\" USERVAR " + noquote);
93557416Smarkm			noquote = 2;
93657416Smarkm			break;
93757416Smarkm
93857416Smarkm		    default:
93957416Smarkm			if (isprint(pointer[i]) && pointer[i] != '"') {
94057416Smarkm			    if (noquote) {
94157416Smarkm				output_data ("\"");
94257416Smarkm				noquote = 0;
94357416Smarkm			    }
94457416Smarkm			    output_data ("%c", pointer[i]);
94557416Smarkm			} else {
94657416Smarkm			    output_data("\" %03o " + noquote,
94757416Smarkm					pointer[i]);
94857416Smarkm			    noquote = 2;
94957416Smarkm			}
95057416Smarkm			break;
95157416Smarkm		    }
95257416Smarkm		}
95357416Smarkm		if (!noquote)
95457416Smarkm		    output_data ("\"");
95557416Smarkm		break;
95657416Smarkm	    }
95757416Smarkm	}
95857416Smarkm	break;
95957416Smarkm
96057416Smarkm#ifdef AUTHENTICATION
96157416Smarkm    case TELOPT_AUTHENTICATION:
96257416Smarkm	output_data("AUTHENTICATION");
96357416Smarkm
96457416Smarkm	if (length < 2) {
96557416Smarkm	    output_data(" (empty suboption??\?)");
96657416Smarkm	    break;
96757416Smarkm	}
96857416Smarkm	switch (pointer[1]) {
96957416Smarkm	case TELQUAL_REPLY:
97057416Smarkm	case TELQUAL_IS:
97157416Smarkm	    output_data(" %s ",
97257416Smarkm			(pointer[1] == TELQUAL_IS) ?
97357416Smarkm			"IS" : "REPLY");
97457416Smarkm	    if (AUTHTYPE_NAME_OK(pointer[2]))
97557416Smarkm		output_data("%s ",
97657416Smarkm			    AUTHTYPE_NAME(pointer[2]));
97757416Smarkm	    else
97857416Smarkm		output_data("%d ",
97957416Smarkm			    pointer[2]);
98057416Smarkm	    if (length < 3) {
98157416Smarkm		output_data("(partial suboption??\?)");
98257416Smarkm		break;
98357416Smarkm	    }
98457416Smarkm	    output_data("%s|%s",
98557416Smarkm			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
98657416Smarkm			"CLIENT" : "SERVER",
98757416Smarkm			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
98857416Smarkm			"MUTUAL" : "ONE-WAY");
98957416Smarkm
99057416Smarkm	    auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
99157416Smarkm	    output_data("%s",
99257416Smarkm			buf);
99357416Smarkm	    break;
99457416Smarkm
99557416Smarkm	case TELQUAL_SEND:
99657416Smarkm	    i = 2;
99757416Smarkm	    output_data(" SEND ");
99857416Smarkm	    while (i < length) {
99957416Smarkm		if (AUTHTYPE_NAME_OK(pointer[i]))
100057416Smarkm		    output_data("%s ",
100157416Smarkm				AUTHTYPE_NAME(pointer[i]));
100257416Smarkm		else
100357416Smarkm		    output_data("%d ",
100457416Smarkm				pointer[i]);
100557416Smarkm		if (++i >= length) {
100657416Smarkm		    output_data("(partial suboption??\?)");
100757416Smarkm		    break;
100857416Smarkm		}
100957416Smarkm		output_data("%s|%s ",
101057416Smarkm			    ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
101157416Smarkm			    "CLIENT" : "SERVER",
101257416Smarkm			    ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
101357416Smarkm			    "MUTUAL" : "ONE-WAY");
101457416Smarkm		++i;
101557416Smarkm	    }
101657416Smarkm	    break;
101757416Smarkm
101857416Smarkm	case TELQUAL_NAME:
101957416Smarkm	    i = 2;
102057416Smarkm	    output_data(" NAME \"%.*s\"",
102157416Smarkm			length - 2,
102257416Smarkm			pointer);
102357416Smarkm	    break;
102457416Smarkm
102557416Smarkm	default:
102657416Smarkm	    for (i = 2; i < length; i++) {
102757416Smarkm		output_data(" ?%d?",
102857416Smarkm			    pointer[i]);
102957416Smarkm	    }
103057416Smarkm	    break;
103157416Smarkm	}
103257416Smarkm	break;
103357416Smarkm#endif
103457416Smarkm
103557416Smarkm#ifdef ENCRYPTION
103657416Smarkm    case TELOPT_ENCRYPT:
103757416Smarkm	output_data("ENCRYPT");
103857416Smarkm	if (length < 2) {
103957416Smarkm	    output_data(" (empty suboption?)");
104057416Smarkm	    break;
104157416Smarkm	}
104257416Smarkm	switch (pointer[1]) {
104357416Smarkm	case ENCRYPT_START:
104457416Smarkm	    output_data(" START");
104557416Smarkm	    break;
104657416Smarkm
104757416Smarkm	case ENCRYPT_END:
104857416Smarkm	    output_data(" END");
104957416Smarkm	    break;
105057416Smarkm
105157416Smarkm	case ENCRYPT_REQSTART:
105257416Smarkm	    output_data(" REQUEST-START");
105357416Smarkm	    break;
105457416Smarkm
105557416Smarkm	case ENCRYPT_REQEND:
105657416Smarkm	    output_data(" REQUEST-END");
105757416Smarkm	    break;
105857416Smarkm
105957416Smarkm	case ENCRYPT_IS:
106057416Smarkm	case ENCRYPT_REPLY:
106157416Smarkm	    output_data(" %s ",
106257416Smarkm			(pointer[1] == ENCRYPT_IS) ?
106357416Smarkm			"IS" : "REPLY");
106457416Smarkm	    if (length < 3) {
106557416Smarkm		output_data(" (partial suboption?)");
106657416Smarkm		break;
106757416Smarkm	    }
106857416Smarkm	    if (ENCTYPE_NAME_OK(pointer[2]))
106957416Smarkm		output_data("%s ",
107057416Smarkm			    ENCTYPE_NAME(pointer[2]));
107157416Smarkm	    else
107257416Smarkm		output_data(" %d (unknown)",
107357416Smarkm			    pointer[2]);
107457416Smarkm
107557416Smarkm	    encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
107657416Smarkm	    output_data("%s",
107757416Smarkm			buf);
107857416Smarkm	    break;
107957416Smarkm
108057416Smarkm	case ENCRYPT_SUPPORT:
108157416Smarkm	    i = 2;
108257416Smarkm	    output_data(" SUPPORT ");
108357416Smarkm	    while (i < length) {
108457416Smarkm		if (ENCTYPE_NAME_OK(pointer[i]))
108557416Smarkm		    output_data("%s ",
108657416Smarkm				ENCTYPE_NAME(pointer[i]));
108757416Smarkm		else
108857416Smarkm		    output_data("%d ",
108957416Smarkm				pointer[i]);
109057416Smarkm		i++;
109157416Smarkm	    }
109257416Smarkm	    break;
109357416Smarkm
109457416Smarkm	case ENCRYPT_ENC_KEYID:
109557416Smarkm	    output_data(" ENC_KEYID %d", pointer[1]);
109657416Smarkm	    goto encommon;
109757416Smarkm
109857416Smarkm	case ENCRYPT_DEC_KEYID:
109957416Smarkm	    output_data(" DEC_KEYID %d", pointer[1]);
110057416Smarkm	    goto encommon;
110157416Smarkm
110257416Smarkm	default:
110357416Smarkm	    output_data(" %d (unknown)", pointer[1]);
110457416Smarkm	encommon:
110557416Smarkm	    for (i = 2; i < length; i++) {
110657416Smarkm		output_data(" %d", pointer[i]);
110757416Smarkm	    }
110857416Smarkm	    break;
110957416Smarkm	}
111057416Smarkm	break;
111157416Smarkm#endif
111257416Smarkm
111357416Smarkm    default:
111457416Smarkm	if (TELOPT_OK(pointer[0]))
111557416Smarkm	    output_data("%s (unknown)",
111657416Smarkm			TELOPT(pointer[0]));
111757416Smarkm	else
111857416Smarkm	    output_data("%d (unknown)",
111957416Smarkm			pointer[i]);
112057416Smarkm	for (i = 1; i < length; i++) {
112157416Smarkm	    output_data(" %d", pointer[i]);
112257416Smarkm	}
112357416Smarkm	break;
112457416Smarkm    }
112557416Smarkm    output_data("\r\n");
112657416Smarkm}
112757416Smarkm
112857416Smarkm/*
112957416Smarkm * Dump a data buffer in hex and ascii to the output data stream.
113057416Smarkm */
113157416Smarkmvoid
113257416Smarkmprintdata(char *tag, char *ptr, int cnt)
113357416Smarkm{
113457416Smarkm    int i;
113557416Smarkm    char xbuf[30];
113657416Smarkm
113757416Smarkm    while (cnt) {
113857416Smarkm	/* flush net output buffer if no room for new data) */
113957416Smarkm	if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
114057416Smarkm	    netflush();
114157416Smarkm	}
114257416Smarkm
114357416Smarkm	/* add a line of output */
114457416Smarkm	output_data("%s: ", tag);
114557416Smarkm	for (i = 0; i < 20 && cnt; i++) {
114657416Smarkm	    output_data("%02x", *ptr);
114757416Smarkm	    if (isprint(*ptr)) {
114857416Smarkm		xbuf[i] = *ptr;
114957416Smarkm	    } else {
115057416Smarkm		xbuf[i] = '.';
115157416Smarkm	    }
115257416Smarkm	    if (i % 2) {
115357416Smarkm		output_data(" ");
115457416Smarkm	    }
115557416Smarkm	    cnt--;
115657416Smarkm	    ptr++;
115757416Smarkm	}
115857416Smarkm	xbuf[i] = '\0';
115957416Smarkm	output_data(" %s\r\n", xbuf);
116057416Smarkm    }
116157416Smarkm}
116257416Smarkm#endif /* DIAGNOSTICS */
1163