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