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