utility.c revision 78527
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
3778527SassarRCSID("$Id: utility.c,v 1.25 2001/05/17 00:34:42 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
36678527Sassarfatalperror_errno(int f, const char *msg, int error)
36757416Smarkm{
36857416Smarkm    char buf[BUFSIZ];
36957416Smarkm
37078527Sassar    snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(error));
37157416Smarkm    fatal(f, buf);
37257416Smarkm}
37357416Smarkm
37478527Sassarvoid
37578527Sassarfatalperror(int f, const char *msg)
37678527Sassar{
37778527Sassar    fatalperror_errno(f, msg, errno);
37878527Sassar}
37978527Sassar
38057416Smarkmchar editedhost[32];
38157416Smarkm
38257416Smarkmvoid edithost(char *pat, char *host)
38357416Smarkm{
38457416Smarkm    char *res = editedhost;
38557416Smarkm
38657416Smarkm    if (!pat)
38757416Smarkm	pat = "";
38857416Smarkm    while (*pat) {
38957416Smarkm	switch (*pat) {
39057416Smarkm
39157416Smarkm	case '#':
39257416Smarkm	    if (*host)
39357416Smarkm		host++;
39457416Smarkm	    break;
39557416Smarkm
39657416Smarkm	case '@':
39757416Smarkm	    if (*host)
39857416Smarkm		*res++ = *host++;
39957416Smarkm	    break;
40057416Smarkm
40157416Smarkm	default:
40257416Smarkm	    *res++ = *pat;
40357416Smarkm	    break;
40457416Smarkm	}
40557416Smarkm	if (res == &editedhost[sizeof editedhost - 1]) {
40657416Smarkm	    *res = '\0';
40757416Smarkm	    return;
40857416Smarkm	}
40957416Smarkm	pat++;
41057416Smarkm    }
41157416Smarkm    if (*host)
41257416Smarkm	strlcpy (res, host,
41357416Smarkm			 sizeof editedhost - (res - editedhost));
41457416Smarkm    else
41557416Smarkm	*res = '\0';
41657416Smarkm    editedhost[sizeof editedhost - 1] = '\0';
41757416Smarkm}
41857416Smarkm
41957416Smarkmstatic char *putlocation;
42057416Smarkm
42157416Smarkmvoid
42257416Smarkmputstr(char *s)
42357416Smarkm{
42457416Smarkm
42557416Smarkm    while (*s)
42657416Smarkm	putchr(*s++);
42757416Smarkm}
42857416Smarkm
42957416Smarkmvoid
43057416Smarkmputchr(int cc)
43157416Smarkm{
43257416Smarkm    *putlocation++ = cc;
43357416Smarkm}
43457416Smarkm
43557416Smarkm/*
43657416Smarkm * This is split on two lines so that SCCS will not see the M
43757416Smarkm * between two % signs and expand it...
43857416Smarkm */
43957416Smarkmstatic char fmtstr[] = { "%l:%M" "%P on %A, %d %B %Y" };
44057416Smarkm
44157416Smarkmvoid putf(char *cp, char *where)
44257416Smarkm{
44357416Smarkm#ifdef HAVE_UNAME
44457416Smarkm    struct utsname name;
44557416Smarkm#endif
44657416Smarkm    char *slash;
44757416Smarkm    time_t t;
44857416Smarkm    char db[100];
44957416Smarkm
45057416Smarkm    /* if we don't have uname, set these to sensible values */
45157416Smarkm    char *sysname = "Unix",
45257416Smarkm	*machine = "",
45357416Smarkm	*release = "",
45457416Smarkm	*version = "";
45557416Smarkm
45657416Smarkm#ifdef HAVE_UNAME
45757416Smarkm    uname(&name);
45857416Smarkm    sysname=name.sysname;
45957416Smarkm    machine=name.machine;
46057416Smarkm    release=name.release;
46157416Smarkm    version=name.version;
46257416Smarkm#endif
46357416Smarkm
46457416Smarkm    putlocation = where;
46557416Smarkm
46657416Smarkm    while (*cp) {
46757416Smarkm	if (*cp != '%') {
46857416Smarkm	    putchr(*cp++);
46957416Smarkm	    continue;
47057416Smarkm	}
47157416Smarkm	switch (*++cp) {
47257416Smarkm
47357416Smarkm	case 't':
47457416Smarkm#ifdef	STREAMSPTY
47557416Smarkm	    /* names are like /dev/pts/2 -- we want pts/2 */
47657416Smarkm	    slash = strchr(line+1, '/');
47757416Smarkm#else
47857416Smarkm	    slash = strrchr(line, '/');
47957416Smarkm#endif
48057416Smarkm	    if (slash == (char *) 0)
48157416Smarkm		putstr(line);
48257416Smarkm	    else
48357416Smarkm		putstr(&slash[1]);
48457416Smarkm	    break;
48557416Smarkm
48657416Smarkm	case 'h':
48757416Smarkm	    putstr(editedhost);
48857416Smarkm	    break;
48957416Smarkm
49057416Smarkm	case 's':
49157416Smarkm	    putstr(sysname);
49257416Smarkm	    break;
49357416Smarkm
49457416Smarkm	case 'm':
49557416Smarkm	    putstr(machine);
49657416Smarkm	    break;
49757416Smarkm
49857416Smarkm	case 'r':
49957416Smarkm	    putstr(release);
50057416Smarkm	    break;
50157416Smarkm
50257416Smarkm	case 'v':
50357416Smarkm	    putstr(version);
50457416Smarkm	    break;
50557416Smarkm
50657416Smarkm	case 'd':
50757416Smarkm	    time(&t);
50857416Smarkm	    strftime(db, sizeof(db), fmtstr, localtime(&t));
50957416Smarkm	    putstr(db);
51057416Smarkm	    break;
51157416Smarkm
51257416Smarkm	case '%':
51357416Smarkm	    putchr('%');
51457416Smarkm	    break;
51557416Smarkm	}
51657416Smarkm	cp++;
51757416Smarkm    }
51857416Smarkm}
51957416Smarkm
52057416Smarkm#ifdef DIAGNOSTICS
52157416Smarkm/*
52257416Smarkm * Print telnet options and commands in plain text, if possible.
52357416Smarkm */
52457416Smarkmvoid
52557416Smarkmprintoption(char *fmt, int option)
52657416Smarkm{
52757416Smarkm    if (TELOPT_OK(option))
52857416Smarkm	output_data("%s %s\r\n",
52957416Smarkm		    fmt,
53057416Smarkm		    TELOPT(option));
53157416Smarkm    else if (TELCMD_OK(option))
53257416Smarkm	output_data("%s %s\r\n",
53357416Smarkm		    fmt,
53457416Smarkm		    TELCMD(option));
53557416Smarkm    else
53657416Smarkm	output_data("%s %d\r\n",
53757416Smarkm		    fmt,
53857416Smarkm		    option);
53957416Smarkm    return;
54057416Smarkm}
54157416Smarkm
54257416Smarkmvoid
54357416Smarkmprintsub(int direction, unsigned char *pointer, int length)
54457416Smarkm        		          	/* '<' or '>' */
54557416Smarkm                 	         	/* where suboption data sits */
54657416Smarkm       			       		/* length of suboption data */
54757416Smarkm{
54857416Smarkm    int i = 0;
54957416Smarkm    unsigned char buf[512];
55057416Smarkm
55157416Smarkm    if (!(diagnostic & TD_OPTIONS))
55257416Smarkm	return;
55357416Smarkm
55457416Smarkm    if (direction) {
55557416Smarkm	output_data("td: %s suboption ",
55657416Smarkm		    direction == '<' ? "recv" : "send");
55757416Smarkm	if (length >= 3) {
55857416Smarkm	    int j;
55957416Smarkm
56057416Smarkm	    i = pointer[length-2];
56157416Smarkm	    j = pointer[length-1];
56257416Smarkm
56357416Smarkm	    if (i != IAC || j != SE) {
56457416Smarkm		output_data("(terminated by ");
56557416Smarkm		if (TELOPT_OK(i))
56657416Smarkm		    output_data("%s ",
56757416Smarkm				TELOPT(i));
56857416Smarkm		else if (TELCMD_OK(i))
56957416Smarkm		    output_data("%s ",
57057416Smarkm				TELCMD(i));
57157416Smarkm		else
57257416Smarkm		    output_data("%d ",
57357416Smarkm				i);
57457416Smarkm		if (TELOPT_OK(j))
57557416Smarkm		    output_data("%s",
57657416Smarkm				TELOPT(j));
57757416Smarkm		else if (TELCMD_OK(j))
57857416Smarkm		    output_data("%s",
57957416Smarkm				TELCMD(j));
58057416Smarkm		else
58157416Smarkm		    output_data("%d",
58257416Smarkm				j);
58357416Smarkm		output_data(", not IAC SE!) ");
58457416Smarkm	    }
58557416Smarkm	}
58657416Smarkm	length -= 2;
58757416Smarkm    }
58857416Smarkm    if (length < 1) {
58957416Smarkm	output_data("(Empty suboption??\?)");
59057416Smarkm	return;
59157416Smarkm    }
59257416Smarkm    switch (pointer[0]) {
59357416Smarkm    case TELOPT_TTYPE:
59457416Smarkm	output_data("TERMINAL-TYPE ");
59557416Smarkm	switch (pointer[1]) {
59657416Smarkm	case TELQUAL_IS:
59757416Smarkm	    output_data("IS \"%.*s\"",
59857416Smarkm			length-2,
59957416Smarkm			(char *)pointer+2);
60057416Smarkm	    break;
60157416Smarkm	case TELQUAL_SEND:
60257416Smarkm	    output_data("SEND");
60357416Smarkm	    break;
60457416Smarkm	default:
60557416Smarkm	    output_data("- unknown qualifier %d (0x%x).",
60657416Smarkm			pointer[1], pointer[1]);
60757416Smarkm	}
60857416Smarkm	break;
60957416Smarkm    case TELOPT_TSPEED:
61057416Smarkm	output_data("TERMINAL-SPEED");
61157416Smarkm	if (length < 2) {
61257416Smarkm	    output_data(" (empty suboption??\?)");
61357416Smarkm	    break;
61457416Smarkm	}
61557416Smarkm	switch (pointer[1]) {
61657416Smarkm	case TELQUAL_IS:
61757416Smarkm	    output_data(" IS %.*s", length-2, (char *)pointer+2);
61857416Smarkm	    break;
61957416Smarkm	default:
62057416Smarkm	    if (pointer[1] == 1)
62157416Smarkm		output_data(" SEND");
62257416Smarkm	    else
62357416Smarkm		output_data(" %d (unknown)", pointer[1]);
62457416Smarkm	    for (i = 2; i < length; i++) {
62557416Smarkm		output_data(" ?%d?", pointer[i]);
62657416Smarkm	    }
62757416Smarkm	    break;
62857416Smarkm	}
62957416Smarkm	break;
63057416Smarkm
63157416Smarkm    case TELOPT_LFLOW:
63257416Smarkm	output_data("TOGGLE-FLOW-CONTROL");
63357416Smarkm	if (length < 2) {
63457416Smarkm	    output_data(" (empty suboption??\?)");
63557416Smarkm	    break;
63657416Smarkm	}
63757416Smarkm	switch (pointer[1]) {
63857416Smarkm	case LFLOW_OFF:
63957416Smarkm	    output_data(" OFF");
64057416Smarkm	    break;
64157416Smarkm	case LFLOW_ON:
64257416Smarkm	    output_data(" ON");
64357416Smarkm	    break;
64457416Smarkm	case LFLOW_RESTART_ANY:
64557416Smarkm	    output_data(" RESTART-ANY");
64657416Smarkm	    break;
64757416Smarkm	case LFLOW_RESTART_XON:
64857416Smarkm	    output_data(" RESTART-XON");
64957416Smarkm	    break;
65057416Smarkm	default:
65157416Smarkm	    output_data(" %d (unknown)",
65257416Smarkm			pointer[1]);
65357416Smarkm	}
65457416Smarkm	for (i = 2; i < length; i++) {
65557416Smarkm	    output_data(" ?%d?",
65657416Smarkm			pointer[i]);
65757416Smarkm	}
65857416Smarkm	break;
65957416Smarkm
66057416Smarkm    case TELOPT_NAWS:
66157416Smarkm	output_data("NAWS");
66257416Smarkm	if (length < 2) {
66357416Smarkm	    output_data(" (empty suboption??\?)");
66457416Smarkm	    break;
66557416Smarkm	}
66657416Smarkm	if (length == 2) {
66757416Smarkm	    output_data(" ?%d?",
66857416Smarkm			pointer[1]);
66957416Smarkm	    break;
67057416Smarkm	}
67157416Smarkm	output_data(" %u %u(%u)",
67257416Smarkm		    pointer[1],
67357416Smarkm		    pointer[2],
67457416Smarkm		    (((unsigned int)pointer[1])<<8) + pointer[2]);
67557416Smarkm	if (length == 4) {
67657416Smarkm	    output_data(" ?%d?",
67757416Smarkm			pointer[3]);
67857416Smarkm	    break;
67957416Smarkm	}
68057416Smarkm	output_data(" %u %u(%u)",
68157416Smarkm		    pointer[3],
68257416Smarkm		    pointer[4],
68357416Smarkm		    (((unsigned int)pointer[3])<<8) + pointer[4]);
68457416Smarkm	for (i = 5; i < length; i++) {
68557416Smarkm	    output_data(" ?%d?",
68657416Smarkm			pointer[i]);
68757416Smarkm	}
68857416Smarkm	break;
68957416Smarkm
69057416Smarkm    case TELOPT_LINEMODE:
69157416Smarkm	output_data("LINEMODE ");
69257416Smarkm	if (length < 2) {
69357416Smarkm	    output_data(" (empty suboption??\?)");
69457416Smarkm	    break;
69557416Smarkm	}
69657416Smarkm	switch (pointer[1]) {
69757416Smarkm	case WILL:
69857416Smarkm	    output_data("WILL ");
69957416Smarkm	    goto common;
70057416Smarkm	case WONT:
70157416Smarkm	    output_data("WONT ");
70257416Smarkm	    goto common;
70357416Smarkm	case DO:
70457416Smarkm	    output_data("DO ");
70557416Smarkm	    goto common;
70657416Smarkm	case DONT:
70757416Smarkm	    output_data("DONT ");
70857416Smarkm	common:
70957416Smarkm	    if (length < 3) {
71057416Smarkm		output_data("(no option??\?)");
71157416Smarkm		break;
71257416Smarkm	    }
71357416Smarkm	    switch (pointer[2]) {
71457416Smarkm	    case LM_FORWARDMASK:
71557416Smarkm		output_data("Forward Mask");
71657416Smarkm		for (i = 3; i < length; i++) {
71757416Smarkm		    output_data(" %x", pointer[i]);
71857416Smarkm		}
71957416Smarkm		break;
72057416Smarkm	    default:
72157416Smarkm		output_data("%d (unknown)",
72257416Smarkm			    pointer[2]);
72357416Smarkm		for (i = 3; i < length; i++) {
72457416Smarkm		    output_data(" %d",
72557416Smarkm				pointer[i]);
72657416Smarkm		}
72757416Smarkm		break;
72857416Smarkm	    }
72957416Smarkm	    break;
73057416Smarkm
73157416Smarkm	case LM_SLC:
73257416Smarkm	    output_data("SLC");
73357416Smarkm	    for (i = 2; i < length - 2; i += 3) {
73457416Smarkm		if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
73557416Smarkm		    output_data(" %s",
73657416Smarkm				SLC_NAME(pointer[i+SLC_FUNC]));
73757416Smarkm		else
73857416Smarkm		    output_data(" %d",
73957416Smarkm				pointer[i+SLC_FUNC]);
74057416Smarkm		switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
74157416Smarkm		case SLC_NOSUPPORT:
74257416Smarkm		    output_data(" NOSUPPORT");
74357416Smarkm		    break;
74457416Smarkm		case SLC_CANTCHANGE:
74557416Smarkm		    output_data(" CANTCHANGE");
74657416Smarkm		    break;
74757416Smarkm		case SLC_VARIABLE:
74857416Smarkm		    output_data(" VARIABLE");
74957416Smarkm		    break;
75057416Smarkm		case SLC_DEFAULT:
75157416Smarkm		    output_data(" DEFAULT");
75257416Smarkm		    break;
75357416Smarkm		}
75457416Smarkm		output_data("%s%s%s",
75557416Smarkm			    pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
75657416Smarkm			    pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
75757416Smarkm			    pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
75857416Smarkm		if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
75957416Smarkm					    SLC_FLUSHOUT| SLC_LEVELBITS)) {
76057416Smarkm		    output_data("(0x%x)",
76157416Smarkm				pointer[i+SLC_FLAGS]);
76257416Smarkm		}
76357416Smarkm		output_data(" %d;",
76457416Smarkm			    pointer[i+SLC_VALUE]);
76557416Smarkm		if ((pointer[i+SLC_VALUE] == IAC) &&
76657416Smarkm		    (pointer[i+SLC_VALUE+1] == IAC))
76757416Smarkm		    i++;
76857416Smarkm	    }
76957416Smarkm	    for (; i < length; i++) {
77057416Smarkm		output_data(" ?%d?",
77157416Smarkm			    pointer[i]);
77257416Smarkm	    }
77357416Smarkm	    break;
77457416Smarkm
77557416Smarkm	case LM_MODE:
77657416Smarkm	    output_data("MODE ");
77757416Smarkm	    if (length < 3) {
77857416Smarkm		output_data("(no mode??\?)");
77957416Smarkm		break;
78057416Smarkm	    }
78157416Smarkm	    {
78257416Smarkm		char tbuf[32];
78357416Smarkm		snprintf(tbuf,
78457416Smarkm			 sizeof(tbuf),
78557416Smarkm			 "%s%s%s%s%s",
78657416Smarkm			 pointer[2]&MODE_EDIT ? "|EDIT" : "",
78757416Smarkm			 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
78857416Smarkm			 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
78957416Smarkm			 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
79057416Smarkm			 pointer[2]&MODE_ACK ? "|ACK" : "");
79157416Smarkm		output_data("%s",
79257416Smarkm			    tbuf[1] ? &tbuf[1] : "0");
79357416Smarkm	    }
79457416Smarkm	    if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
79557416Smarkm		output_data(" (0x%x)",
79657416Smarkm			    pointer[2]);
79757416Smarkm	    }
79857416Smarkm	    for (i = 3; i < length; i++) {
79957416Smarkm		output_data(" ?0x%x?",
80057416Smarkm			 pointer[i]);
80157416Smarkm	    }
80257416Smarkm	    break;
80357416Smarkm	default:
80457416Smarkm	    output_data("%d (unknown)",
80557416Smarkm			pointer[1]);
80657416Smarkm	    for (i = 2; i < length; i++) {
80757416Smarkm		output_data(" %d", pointer[i]);
80857416Smarkm	    }
80957416Smarkm	}
81057416Smarkm	break;
81157416Smarkm
81257416Smarkm    case TELOPT_STATUS: {
81357416Smarkm	char *cp;
81457416Smarkm	int j, k;
81557416Smarkm
81657416Smarkm	output_data("STATUS");
81757416Smarkm
81857416Smarkm	switch (pointer[1]) {
81957416Smarkm	default:
82057416Smarkm	    if (pointer[1] == TELQUAL_SEND)
82157416Smarkm		output_data(" SEND");
82257416Smarkm	    else
82357416Smarkm		output_data(" %d (unknown)",
82457416Smarkm			    pointer[1]);
82557416Smarkm	    for (i = 2; i < length; i++) {
82657416Smarkm		output_data(" ?%d?",
82757416Smarkm			    pointer[i]);
82857416Smarkm	    }
82957416Smarkm	    break;
83057416Smarkm	case TELQUAL_IS:
83157416Smarkm	    output_data(" IS\r\n");
83257416Smarkm
83357416Smarkm	    for (i = 2; i < length; i++) {
83457416Smarkm		switch(pointer[i]) {
83557416Smarkm		case DO:	cp = "DO"; goto common2;
83657416Smarkm		case DONT:	cp = "DONT"; goto common2;
83757416Smarkm		case WILL:	cp = "WILL"; goto common2;
83857416Smarkm		case WONT:	cp = "WONT"; goto common2;
83957416Smarkm		common2:
84057416Smarkm		i++;
84157416Smarkm		if (TELOPT_OK(pointer[i]))
84257416Smarkm		    output_data(" %s %s",
84357416Smarkm				cp,
84457416Smarkm				TELOPT(pointer[i]));
84557416Smarkm		else
84657416Smarkm		    output_data(" %s %d",
84757416Smarkm				cp,
84857416Smarkm				pointer[i]);
84957416Smarkm
85057416Smarkm		output_data("\r\n");
85157416Smarkm		break;
85257416Smarkm
85357416Smarkm		case SB:
85457416Smarkm		    output_data(" SB ");
85557416Smarkm		    i++;
85657416Smarkm		    j = k = i;
85757416Smarkm		    while (j < length) {
85857416Smarkm			if (pointer[j] == SE) {
85957416Smarkm			    if (j+1 == length)
86057416Smarkm				break;
86157416Smarkm			    if (pointer[j+1] == SE)
86257416Smarkm				j++;
86357416Smarkm			    else
86457416Smarkm				break;
86557416Smarkm			}
86657416Smarkm			pointer[k++] = pointer[j++];
86757416Smarkm		    }
86857416Smarkm		    printsub(0, &pointer[i], k - i);
86957416Smarkm		    if (i < length) {
87057416Smarkm			output_data(" SE");
87157416Smarkm			i = j;
87257416Smarkm		    } else
87357416Smarkm			i = j - 1;
87457416Smarkm
87557416Smarkm		    output_data("\r\n");
87657416Smarkm
87757416Smarkm		    break;
87857416Smarkm
87957416Smarkm		default:
88057416Smarkm		    output_data(" %d",
88157416Smarkm				pointer[i]);
88257416Smarkm		    break;
88357416Smarkm		}
88457416Smarkm	    }
88557416Smarkm	    break;
88657416Smarkm	}
88757416Smarkm	break;
88857416Smarkm    }
88957416Smarkm
89057416Smarkm    case TELOPT_XDISPLOC:
89157416Smarkm	output_data("X-DISPLAY-LOCATION ");
89257416Smarkm	switch (pointer[1]) {
89357416Smarkm	case TELQUAL_IS:
89457416Smarkm	    output_data("IS \"%.*s\"",
89557416Smarkm			length-2,
89657416Smarkm			(char *)pointer+2);
89757416Smarkm	    break;
89857416Smarkm	case TELQUAL_SEND:
89957416Smarkm	    output_data("SEND");
90057416Smarkm	    break;
90157416Smarkm	default:
90257416Smarkm	    output_data("- unknown qualifier %d (0x%x).",
90357416Smarkm			pointer[1], pointer[1]);
90457416Smarkm	}
90557416Smarkm	break;
90657416Smarkm
90757416Smarkm    case TELOPT_NEW_ENVIRON:
90857416Smarkm	output_data("NEW-ENVIRON ");
90957416Smarkm	goto env_common1;
91057416Smarkm    case TELOPT_OLD_ENVIRON:
91157416Smarkm	output_data("OLD-ENVIRON");
91257416Smarkm    env_common1:
91357416Smarkm	switch (pointer[1]) {
91457416Smarkm	case TELQUAL_IS:
91557416Smarkm	    output_data("IS ");
91657416Smarkm	    goto env_common;
91757416Smarkm	case TELQUAL_SEND:
91857416Smarkm	    output_data("SEND ");
91957416Smarkm	    goto env_common;
92057416Smarkm	case TELQUAL_INFO:
92157416Smarkm	    output_data("INFO ");
92257416Smarkm	env_common:
92357416Smarkm	    {
92457416Smarkm		int noquote = 2;
92557416Smarkm		for (i = 2; i < length; i++ ) {
92657416Smarkm		    switch (pointer[i]) {
92757416Smarkm		    case NEW_ENV_VAR:
92857416Smarkm			output_data("\" VAR " + noquote);
92957416Smarkm			noquote = 2;
93057416Smarkm			break;
93157416Smarkm
93257416Smarkm		    case NEW_ENV_VALUE:
93357416Smarkm			output_data("\" VALUE " + noquote);
93457416Smarkm			noquote = 2;
93557416Smarkm			break;
93657416Smarkm
93757416Smarkm		    case ENV_ESC:
93857416Smarkm			output_data("\" ESC " + noquote);
93957416Smarkm			noquote = 2;
94057416Smarkm			break;
94157416Smarkm
94257416Smarkm		    case ENV_USERVAR:
94357416Smarkm			output_data("\" USERVAR " + noquote);
94457416Smarkm			noquote = 2;
94557416Smarkm			break;
94657416Smarkm
94757416Smarkm		    default:
94857416Smarkm			if (isprint(pointer[i]) && pointer[i] != '"') {
94957416Smarkm			    if (noquote) {
95057416Smarkm				output_data ("\"");
95157416Smarkm				noquote = 0;
95257416Smarkm			    }
95357416Smarkm			    output_data ("%c", pointer[i]);
95457416Smarkm			} else {
95557416Smarkm			    output_data("\" %03o " + noquote,
95657416Smarkm					pointer[i]);
95757416Smarkm			    noquote = 2;
95857416Smarkm			}
95957416Smarkm			break;
96057416Smarkm		    }
96157416Smarkm		}
96257416Smarkm		if (!noquote)
96357416Smarkm		    output_data ("\"");
96457416Smarkm		break;
96557416Smarkm	    }
96657416Smarkm	}
96757416Smarkm	break;
96857416Smarkm
96957416Smarkm#ifdef AUTHENTICATION
97057416Smarkm    case TELOPT_AUTHENTICATION:
97157416Smarkm	output_data("AUTHENTICATION");
97257416Smarkm
97357416Smarkm	if (length < 2) {
97457416Smarkm	    output_data(" (empty suboption??\?)");
97557416Smarkm	    break;
97657416Smarkm	}
97757416Smarkm	switch (pointer[1]) {
97857416Smarkm	case TELQUAL_REPLY:
97957416Smarkm	case TELQUAL_IS:
98057416Smarkm	    output_data(" %s ",
98157416Smarkm			(pointer[1] == TELQUAL_IS) ?
98257416Smarkm			"IS" : "REPLY");
98357416Smarkm	    if (AUTHTYPE_NAME_OK(pointer[2]))
98457416Smarkm		output_data("%s ",
98557416Smarkm			    AUTHTYPE_NAME(pointer[2]));
98657416Smarkm	    else
98757416Smarkm		output_data("%d ",
98857416Smarkm			    pointer[2]);
98957416Smarkm	    if (length < 3) {
99057416Smarkm		output_data("(partial suboption??\?)");
99157416Smarkm		break;
99257416Smarkm	    }
99357416Smarkm	    output_data("%s|%s",
99457416Smarkm			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
99557416Smarkm			"CLIENT" : "SERVER",
99657416Smarkm			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
99757416Smarkm			"MUTUAL" : "ONE-WAY");
99857416Smarkm
99957416Smarkm	    auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
100057416Smarkm	    output_data("%s",
100157416Smarkm			buf);
100257416Smarkm	    break;
100357416Smarkm
100457416Smarkm	case TELQUAL_SEND:
100557416Smarkm	    i = 2;
100657416Smarkm	    output_data(" SEND ");
100757416Smarkm	    while (i < length) {
100857416Smarkm		if (AUTHTYPE_NAME_OK(pointer[i]))
100957416Smarkm		    output_data("%s ",
101057416Smarkm				AUTHTYPE_NAME(pointer[i]));
101157416Smarkm		else
101257416Smarkm		    output_data("%d ",
101357416Smarkm				pointer[i]);
101457416Smarkm		if (++i >= length) {
101557416Smarkm		    output_data("(partial suboption??\?)");
101657416Smarkm		    break;
101757416Smarkm		}
101857416Smarkm		output_data("%s|%s ",
101957416Smarkm			    ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
102057416Smarkm			    "CLIENT" : "SERVER",
102157416Smarkm			    ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
102257416Smarkm			    "MUTUAL" : "ONE-WAY");
102357416Smarkm		++i;
102457416Smarkm	    }
102557416Smarkm	    break;
102657416Smarkm
102757416Smarkm	case TELQUAL_NAME:
102857416Smarkm	    i = 2;
102957416Smarkm	    output_data(" NAME \"%.*s\"",
103057416Smarkm			length - 2,
103157416Smarkm			pointer);
103257416Smarkm	    break;
103357416Smarkm
103457416Smarkm	default:
103557416Smarkm	    for (i = 2; i < length; i++) {
103657416Smarkm		output_data(" ?%d?",
103757416Smarkm			    pointer[i]);
103857416Smarkm	    }
103957416Smarkm	    break;
104057416Smarkm	}
104157416Smarkm	break;
104257416Smarkm#endif
104357416Smarkm
104457416Smarkm#ifdef ENCRYPTION
104557416Smarkm    case TELOPT_ENCRYPT:
104657416Smarkm	output_data("ENCRYPT");
104757416Smarkm	if (length < 2) {
104857416Smarkm	    output_data(" (empty suboption?)");
104957416Smarkm	    break;
105057416Smarkm	}
105157416Smarkm	switch (pointer[1]) {
105257416Smarkm	case ENCRYPT_START:
105357416Smarkm	    output_data(" START");
105457416Smarkm	    break;
105557416Smarkm
105657416Smarkm	case ENCRYPT_END:
105757416Smarkm	    output_data(" END");
105857416Smarkm	    break;
105957416Smarkm
106057416Smarkm	case ENCRYPT_REQSTART:
106157416Smarkm	    output_data(" REQUEST-START");
106257416Smarkm	    break;
106357416Smarkm
106457416Smarkm	case ENCRYPT_REQEND:
106557416Smarkm	    output_data(" REQUEST-END");
106657416Smarkm	    break;
106757416Smarkm
106857416Smarkm	case ENCRYPT_IS:
106957416Smarkm	case ENCRYPT_REPLY:
107057416Smarkm	    output_data(" %s ",
107157416Smarkm			(pointer[1] == ENCRYPT_IS) ?
107257416Smarkm			"IS" : "REPLY");
107357416Smarkm	    if (length < 3) {
107457416Smarkm		output_data(" (partial suboption?)");
107557416Smarkm		break;
107657416Smarkm	    }
107757416Smarkm	    if (ENCTYPE_NAME_OK(pointer[2]))
107857416Smarkm		output_data("%s ",
107957416Smarkm			    ENCTYPE_NAME(pointer[2]));
108057416Smarkm	    else
108157416Smarkm		output_data(" %d (unknown)",
108257416Smarkm			    pointer[2]);
108357416Smarkm
108457416Smarkm	    encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
108557416Smarkm	    output_data("%s",
108657416Smarkm			buf);
108757416Smarkm	    break;
108857416Smarkm
108957416Smarkm	case ENCRYPT_SUPPORT:
109057416Smarkm	    i = 2;
109157416Smarkm	    output_data(" SUPPORT ");
109257416Smarkm	    while (i < length) {
109357416Smarkm		if (ENCTYPE_NAME_OK(pointer[i]))
109457416Smarkm		    output_data("%s ",
109557416Smarkm				ENCTYPE_NAME(pointer[i]));
109657416Smarkm		else
109757416Smarkm		    output_data("%d ",
109857416Smarkm				pointer[i]);
109957416Smarkm		i++;
110057416Smarkm	    }
110157416Smarkm	    break;
110257416Smarkm
110357416Smarkm	case ENCRYPT_ENC_KEYID:
110457416Smarkm	    output_data(" ENC_KEYID %d", pointer[1]);
110557416Smarkm	    goto encommon;
110657416Smarkm
110757416Smarkm	case ENCRYPT_DEC_KEYID:
110857416Smarkm	    output_data(" DEC_KEYID %d", pointer[1]);
110957416Smarkm	    goto encommon;
111057416Smarkm
111157416Smarkm	default:
111257416Smarkm	    output_data(" %d (unknown)", pointer[1]);
111357416Smarkm	encommon:
111457416Smarkm	    for (i = 2; i < length; i++) {
111557416Smarkm		output_data(" %d", pointer[i]);
111657416Smarkm	    }
111757416Smarkm	    break;
111857416Smarkm	}
111957416Smarkm	break;
112057416Smarkm#endif
112157416Smarkm
112257416Smarkm    default:
112357416Smarkm	if (TELOPT_OK(pointer[0]))
112457416Smarkm	    output_data("%s (unknown)",
112557416Smarkm			TELOPT(pointer[0]));
112657416Smarkm	else
112757416Smarkm	    output_data("%d (unknown)",
112857416Smarkm			pointer[i]);
112957416Smarkm	for (i = 1; i < length; i++) {
113057416Smarkm	    output_data(" %d", pointer[i]);
113157416Smarkm	}
113257416Smarkm	break;
113357416Smarkm    }
113457416Smarkm    output_data("\r\n");
113557416Smarkm}
113657416Smarkm
113757416Smarkm/*
113857416Smarkm * Dump a data buffer in hex and ascii to the output data stream.
113957416Smarkm */
114057416Smarkmvoid
114157416Smarkmprintdata(char *tag, char *ptr, int cnt)
114257416Smarkm{
114357416Smarkm    int i;
114457416Smarkm    char xbuf[30];
114557416Smarkm
114657416Smarkm    while (cnt) {
114757416Smarkm	/* flush net output buffer if no room for new data) */
114857416Smarkm	if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
114957416Smarkm	    netflush();
115057416Smarkm	}
115157416Smarkm
115257416Smarkm	/* add a line of output */
115357416Smarkm	output_data("%s: ", tag);
115457416Smarkm	for (i = 0; i < 20 && cnt; i++) {
115557416Smarkm	    output_data("%02x", *ptr);
115657416Smarkm	    if (isprint(*ptr)) {
115757416Smarkm		xbuf[i] = *ptr;
115857416Smarkm	    } else {
115957416Smarkm		xbuf[i] = '.';
116057416Smarkm	    }
116157416Smarkm	    if (i % 2) {
116257416Smarkm		output_data(" ");
116357416Smarkm	    }
116457416Smarkm	    cnt--;
116557416Smarkm	    ptr++;
116657416Smarkm	}
116757416Smarkm	xbuf[i] = '\0';
116857416Smarkm	output_data(" %s\r\n", xbuf);
116957416Smarkm    }
117057416Smarkm}
117157416Smarkm#endif /* DIAGNOSTICS */
1172