utility.c revision 29181
129088Smarkm/*
229088Smarkm * Copyright (c) 1989, 1993
329088Smarkm *	The Regents of the University of California.  All rights reserved.
429088Smarkm *
529088Smarkm * Redistribution and use in source and binary forms, with or without
629088Smarkm * modification, are permitted provided that the following conditions
729088Smarkm * are met:
829088Smarkm * 1. Redistributions of source code must retain the above copyright
929088Smarkm *    notice, this list of conditions and the following disclaimer.
1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1129088Smarkm *    notice, this list of conditions and the following disclaimer in the
1229088Smarkm *    documentation and/or other materials provided with the distribution.
1329088Smarkm * 3. All advertising materials mentioning features or use of this software
1429088Smarkm *    must display the following acknowledgement:
1529088Smarkm *	This product includes software developed by the University of
1629088Smarkm *	California, Berkeley and its contributors.
1729088Smarkm * 4. Neither the name of the University nor the names of its contributors
1829088Smarkm *    may be used to endorse or promote products derived from this software
1929088Smarkm *    without specific prior written permission.
2029088Smarkm *
2129088Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2229088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2329088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2429088Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2529088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2629088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2729088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2829088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2929088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3029088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3129088Smarkm * SUCH DAMAGE.
3229088Smarkm */
3329088Smarkm
3429088Smarkm#ifndef lint
3529181Smarkmstatic const char sccsid[] = "@(#)utility.c	8.4 (Berkeley) 5/30/95";
3629088Smarkm#endif /* not lint */
3729088Smarkm
3829181Smarkm#ifdef __FreeBSD__
3929181Smarkm#include <locale.h>
4029181Smarkm#endif
4129088Smarkm#define PRINTOPTIONS
4229088Smarkm#include "telnetd.h"
4329088Smarkm
4429181Smarkm#if	defined(AUTHENTICATION)
4529181Smarkm#include <libtelnet/auth.h>
4629181Smarkm#endif
4729181Smarkm#if	defined(ENCRYPTION)
4829181Smarkm#include <libtelnet/encrypt.h>
4929181Smarkm#endif
5029181Smarkm
5129088Smarkm/*
5229088Smarkm * utility functions performing io related tasks
5329088Smarkm */
5429088Smarkm
5529088Smarkm/*
5629088Smarkm * ttloop
5729088Smarkm *
5829088Smarkm *	A small subroutine to flush the network output buffer, get some data
5929088Smarkm * from the network, and pass it through the telnet state machine.  We
6029088Smarkm * also flush the pty input buffer (by dropping its data) if it becomes
6129088Smarkm * too full.
6229088Smarkm */
6329088Smarkm
6429088Smarkm    void
6529088Smarkmttloop()
6629088Smarkm{
6729088Smarkm    void netflush();
6829088Smarkm
6929088Smarkm    DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop\r\n");
7029088Smarkm		     nfrontp += strlen(nfrontp);});
7129088Smarkm    if (nfrontp-nbackp) {
7229088Smarkm	netflush();
7329088Smarkm    }
7429088Smarkm    ncc = read(net, netibuf, sizeof netibuf);
7529088Smarkm    if (ncc < 0) {
7629088Smarkm	syslog(LOG_INFO, "ttloop:  read: %m\n");
7729088Smarkm	exit(1);
7829088Smarkm    } else if (ncc == 0) {
7929088Smarkm	syslog(LOG_INFO, "ttloop:  peer died: %m\n");
8029088Smarkm	exit(1);
8129088Smarkm    }
8229088Smarkm    DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop read %d chars\r\n", ncc);
8329088Smarkm		     nfrontp += strlen(nfrontp);});
8429088Smarkm    netip = netibuf;
8529088Smarkm    telrcv();			/* state machine */
8629088Smarkm    if (ncc > 0) {
8729088Smarkm	pfrontp = pbackp = ptyobuf;
8829088Smarkm	telrcv();
8929088Smarkm    }
9029088Smarkm}  /* end of ttloop */
9129088Smarkm
9229088Smarkm/*
9329088Smarkm * Check a descriptor to see if out of band data exists on it.
9429088Smarkm */
9529088Smarkm    int
9629088Smarkmstilloob(s)
9729088Smarkm    int	s;		/* socket number */
9829088Smarkm{
9929088Smarkm    static struct timeval timeout = { 0 };
10029088Smarkm    fd_set	excepts;
10129088Smarkm    int value;
10229088Smarkm
10329088Smarkm    do {
10429088Smarkm	FD_ZERO(&excepts);
10529088Smarkm	FD_SET(s, &excepts);
10629181Smarkm	memset((char *)&timeout, 0, sizeof timeout);
10729088Smarkm	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
10829088Smarkm    } while ((value == -1) && (errno == EINTR));
10929088Smarkm
11029088Smarkm    if (value < 0) {
11129088Smarkm	fatalperror(pty, "select");
11229088Smarkm    }
11329088Smarkm    if (FD_ISSET(s, &excepts)) {
11429088Smarkm	return 1;
11529088Smarkm    } else {
11629088Smarkm	return 0;
11729088Smarkm    }
11829088Smarkm}
11929088Smarkm
12029088Smarkm	void
12129088Smarkmptyflush()
12229088Smarkm{
12329088Smarkm	int n;
12429088Smarkm
12529088Smarkm	if ((n = pfrontp - pbackp) > 0) {
12629088Smarkm		DIAG((TD_REPORT | TD_PTYDATA),
12729088Smarkm			{ sprintf(nfrontp, "td: ptyflush %d chars\r\n", n);
12829088Smarkm			  nfrontp += strlen(nfrontp); });
12929088Smarkm		DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
13029088Smarkm		n = write(pty, pbackp, n);
13129088Smarkm	}
13229088Smarkm	if (n < 0) {
13329088Smarkm		if (errno == EWOULDBLOCK || errno == EINTR)
13429088Smarkm			return;
13529088Smarkm		cleanup(0);
13629088Smarkm	}
13729088Smarkm	pbackp += n;
13829088Smarkm	if (pbackp == pfrontp)
13929088Smarkm		pbackp = pfrontp = ptyobuf;
14029088Smarkm}
14129088Smarkm
14229088Smarkm/*
14329088Smarkm * nextitem()
14429088Smarkm *
14529088Smarkm *	Return the address of the next "item" in the TELNET data
14629088Smarkm * stream.  This will be the address of the next character if
14729088Smarkm * the current address is a user data character, or it will
14829088Smarkm * be the address of the character following the TELNET command
14929088Smarkm * if the current address is a TELNET IAC ("I Am a Command")
15029088Smarkm * character.
15129088Smarkm */
15229088Smarkm    char *
15329088Smarkmnextitem(current)
15429088Smarkm    char	*current;
15529088Smarkm{
15629088Smarkm    if ((*current&0xff) != IAC) {
15729088Smarkm	return current+1;
15829088Smarkm    }
15929088Smarkm    switch (*(current+1)&0xff) {
16029088Smarkm    case DO:
16129088Smarkm    case DONT:
16229088Smarkm    case WILL:
16329088Smarkm    case WONT:
16429088Smarkm	return current+3;
16529088Smarkm    case SB:		/* loop forever looking for the SE */
16629088Smarkm	{
16729088Smarkm	    register char *look = current+2;
16829088Smarkm
16929088Smarkm	    for (;;) {
17029088Smarkm		if ((*look++&0xff) == IAC) {
17129088Smarkm		    if ((*look++&0xff) == SE) {
17229088Smarkm			return look;
17329088Smarkm		    }
17429088Smarkm		}
17529088Smarkm	    }
17629088Smarkm	}
17729088Smarkm    default:
17829088Smarkm	return current+2;
17929088Smarkm    }
18029088Smarkm}  /* end of nextitem */
18129088Smarkm
18229088Smarkm
18329088Smarkm/*
18429088Smarkm * netclear()
18529088Smarkm *
18629088Smarkm *	We are about to do a TELNET SYNCH operation.  Clear
18729088Smarkm * the path to the network.
18829088Smarkm *
18929088Smarkm *	Things are a bit tricky since we may have sent the first
19029088Smarkm * byte or so of a previous TELNET command into the network.
19129088Smarkm * So, we have to scan the network buffer from the beginning
19229088Smarkm * until we are up to where we want to be.
19329088Smarkm *
19429088Smarkm *	A side effect of what we do, just to keep things
19529088Smarkm * simple, is to clear the urgent data pointer.  The principal
19629088Smarkm * caller should be setting the urgent data pointer AFTER calling
19729088Smarkm * us in any case.
19829088Smarkm */
19929088Smarkm    void
20029088Smarkmnetclear()
20129088Smarkm{
20229088Smarkm    register char *thisitem, *next;
20329088Smarkm    char *good;
20429088Smarkm#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
20529088Smarkm				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
20629088Smarkm
20729088Smarkm#ifdef	ENCRYPTION
20829088Smarkm    thisitem = nclearto > netobuf ? nclearto : netobuf;
20929088Smarkm#else	/* ENCRYPTION */
21029088Smarkm    thisitem = netobuf;
21129088Smarkm#endif	/* ENCRYPTION */
21229088Smarkm
21329088Smarkm    while ((next = nextitem(thisitem)) <= nbackp) {
21429088Smarkm	thisitem = next;
21529088Smarkm    }
21629088Smarkm
21729088Smarkm    /* Now, thisitem is first before/at boundary. */
21829088Smarkm
21929088Smarkm#ifdef	ENCRYPTION
22029088Smarkm    good = nclearto > netobuf ? nclearto : netobuf;
22129088Smarkm#else	/* ENCRYPTION */
22229088Smarkm    good = netobuf;	/* where the good bytes go */
22329088Smarkm#endif	/* ENCRYPTION */
22429088Smarkm
22529088Smarkm    while (nfrontp > thisitem) {
22629088Smarkm	if (wewant(thisitem)) {
22729088Smarkm	    int length;
22829088Smarkm
22929088Smarkm	    next = thisitem;
23029088Smarkm	    do {
23129088Smarkm		next = nextitem(next);
23229088Smarkm	    } while (wewant(next) && (nfrontp > next));
23329088Smarkm	    length = next-thisitem;
23429088Smarkm	    memmove(good, thisitem, length);
23529088Smarkm	    good += length;
23629088Smarkm	    thisitem = next;
23729088Smarkm	} else {
23829088Smarkm	    thisitem = nextitem(thisitem);
23929088Smarkm	}
24029088Smarkm    }
24129088Smarkm
24229088Smarkm    nbackp = netobuf;
24329088Smarkm    nfrontp = good;		/* next byte to be sent */
24429088Smarkm    neturg = 0;
24529088Smarkm}  /* end of netclear */
24629088Smarkm
24729088Smarkm/*
24829088Smarkm *  netflush
24929088Smarkm *		Send as much data as possible to the network,
25029088Smarkm *	handling requests for urgent data.
25129088Smarkm */
25229088Smarkm    void
25329088Smarkmnetflush()
25429088Smarkm{
25529088Smarkm    int n;
25629088Smarkm    extern int not42;
25729088Smarkm
25829088Smarkm    if ((n = nfrontp - nbackp) > 0) {
25929088Smarkm	DIAG(TD_REPORT,
26029088Smarkm	    { sprintf(nfrontp, "td: netflush %d chars\r\n", n);
26129088Smarkm	      n += strlen(nfrontp);  /* get count first */
26229088Smarkm	      nfrontp += strlen(nfrontp);  /* then move pointer */
26329088Smarkm	    });
26429088Smarkm#ifdef	ENCRYPTION
26529088Smarkm	if (encrypt_output) {
26629088Smarkm		char *s = nclearto ? nclearto : nbackp;
26729088Smarkm		if (nfrontp - s > 0) {
26829088Smarkm			(*encrypt_output)((unsigned char *)s, nfrontp-s);
26929088Smarkm			nclearto = nfrontp;
27029088Smarkm		}
27129088Smarkm	}
27229088Smarkm#endif	/* ENCRYPTION */
27329088Smarkm	/*
27429088Smarkm	 * if no urgent data, or if the other side appears to be an
27529088Smarkm	 * old 4.2 client (and thus unable to survive TCP urgent data),
27629088Smarkm	 * write the entire buffer in non-OOB mode.
27729088Smarkm	 */
27829088Smarkm	if ((neturg == 0) || (not42 == 0)) {
27929088Smarkm	    n = write(net, nbackp, n);	/* normal write */
28029088Smarkm	} else {
28129088Smarkm	    n = neturg - nbackp;
28229088Smarkm	    /*
28329088Smarkm	     * In 4.2 (and 4.3) systems, there is some question about
28429088Smarkm	     * what byte in a sendOOB operation is the "OOB" data.
28529088Smarkm	     * To make ourselves compatible, we only send ONE byte
28629088Smarkm	     * out of band, the one WE THINK should be OOB (though
28729088Smarkm	     * we really have more the TCP philosophy of urgent data
28829088Smarkm	     * rather than the Unix philosophy of OOB data).
28929088Smarkm	     */
29029088Smarkm	    if (n > 1) {
29129088Smarkm		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
29229088Smarkm	    } else {
29329088Smarkm		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
29429088Smarkm	    }
29529088Smarkm	}
29629088Smarkm    }
29729088Smarkm    if (n < 0) {
29829088Smarkm	if (errno == EWOULDBLOCK || errno == EINTR)
29929088Smarkm		return;
30029088Smarkm	cleanup(0);
30129088Smarkm    }
30229088Smarkm    nbackp += n;
30329088Smarkm#ifdef	ENCRYPTION
30429088Smarkm    if (nbackp > nclearto)
30529088Smarkm	nclearto = 0;
30629088Smarkm#endif	/* ENCRYPTION */
30729088Smarkm    if (nbackp >= neturg) {
30829088Smarkm	neturg = 0;
30929088Smarkm    }
31029088Smarkm    if (nbackp == nfrontp) {
31129088Smarkm	nbackp = nfrontp = netobuf;
31229088Smarkm#ifdef	ENCRYPTION
31329088Smarkm	nclearto = 0;
31429088Smarkm#endif	/* ENCRYPTION */
31529088Smarkm    }
31629088Smarkm    return;
31729088Smarkm}  /* end of netflush */
31829088Smarkm
31929088Smarkm
32029088Smarkm/*
32129088Smarkm * writenet
32229088Smarkm *
32329088Smarkm * Just a handy little function to write a bit of raw data to the net.
32429088Smarkm * It will force a transmit of the buffer if necessary
32529088Smarkm *
32629088Smarkm * arguments
32729088Smarkm *    ptr - A pointer to a character string to write
32829088Smarkm *    len - How many bytes to write
32929088Smarkm */
33029088Smarkm	void
33129088Smarkmwritenet(ptr, len)
33229088Smarkm	register unsigned char *ptr;
33329088Smarkm	register int len;
33429088Smarkm{
33529088Smarkm	/* flush buffer if no room for new data) */
33629088Smarkm	if ((&netobuf[BUFSIZ] - nfrontp) < len) {
33729088Smarkm		/* if this fails, don't worry, buffer is a little big */
33829088Smarkm		netflush();
33929088Smarkm	}
34029088Smarkm
34129088Smarkm	memmove(nfrontp, ptr, len);
34229088Smarkm	nfrontp += len;
34329088Smarkm
34429088Smarkm}  /* end of writenet */
34529088Smarkm
34629088Smarkm
34729088Smarkm/*
34829088Smarkm * miscellaneous functions doing a variety of little jobs follow ...
34929088Smarkm */
35029088Smarkm
35129088Smarkm
35229088Smarkm	void
35329088Smarkmfatal(f, msg)
35429088Smarkm	int f;
35529088Smarkm	char *msg;
35629088Smarkm{
35729088Smarkm	char buf[BUFSIZ];
35829088Smarkm
35929088Smarkm	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
36029088Smarkm#ifdef	ENCRYPTION
36129088Smarkm	if (encrypt_output) {
36229088Smarkm		/*
36329088Smarkm		 * Better turn off encryption first....
36429088Smarkm		 * Hope it flushes...
36529088Smarkm		 */
36629088Smarkm		encrypt_send_end();
36729088Smarkm		netflush();
36829088Smarkm	}
36929088Smarkm#endif	/* ENCRYPTION */
37029088Smarkm	(void) write(f, buf, (int)strlen(buf));
37129088Smarkm	sleep(1);	/*XXX*/
37229088Smarkm	exit(1);
37329088Smarkm}
37429088Smarkm
37529088Smarkm	void
37629088Smarkmfatalperror(f, msg)
37729088Smarkm	int f;
37829088Smarkm	char *msg;
37929088Smarkm{
38029088Smarkm	char buf[BUFSIZ], *strerror();
38129088Smarkm
38229088Smarkm	(void) sprintf(buf, "%s: %s", msg, strerror(errno));
38329088Smarkm	fatal(f, buf);
38429088Smarkm}
38529088Smarkm
38629088Smarkmchar editedhost[32];
38729088Smarkm
38829088Smarkm	void
38929088Smarkmedithost(pat, host)
39029088Smarkm	register char *pat;
39129088Smarkm	register char *host;
39229088Smarkm{
39329088Smarkm	register char *res = editedhost;
39429088Smarkm	char *strncpy();
39529088Smarkm
39629088Smarkm	if (!pat)
39729088Smarkm		pat = "";
39829088Smarkm	while (*pat) {
39929088Smarkm		switch (*pat) {
40029088Smarkm
40129088Smarkm		case '#':
40229088Smarkm			if (*host)
40329088Smarkm				host++;
40429088Smarkm			break;
40529088Smarkm
40629088Smarkm		case '@':
40729088Smarkm			if (*host)
40829088Smarkm				*res++ = *host++;
40929088Smarkm			break;
41029088Smarkm
41129088Smarkm		default:
41229088Smarkm			*res++ = *pat;
41329088Smarkm			break;
41429088Smarkm		}
41529088Smarkm		if (res == &editedhost[sizeof editedhost - 1]) {
41629088Smarkm			*res = '\0';
41729088Smarkm			return;
41829088Smarkm		}
41929088Smarkm		pat++;
42029088Smarkm	}
42129088Smarkm	if (*host)
42229088Smarkm		(void) strncpy(res, host,
42329088Smarkm				sizeof editedhost - (res - editedhost) -1);
42429088Smarkm	else
42529088Smarkm		*res = '\0';
42629088Smarkm	editedhost[sizeof editedhost - 1] = '\0';
42729088Smarkm}
42829088Smarkm
42929088Smarkmstatic char *putlocation;
43029088Smarkm
43129088Smarkm	void
43229088Smarkmputstr(s)
43329088Smarkm	register char *s;
43429088Smarkm{
43529088Smarkm
43629088Smarkm	while (*s)
43729088Smarkm		putchr(*s++);
43829088Smarkm}
43929088Smarkm
44029088Smarkm	void
44129088Smarkmputchr(cc)
44229088Smarkm	int cc;
44329088Smarkm{
44429088Smarkm	*putlocation++ = cc;
44529088Smarkm}
44629088Smarkm
44729181Smarkm#ifdef __FreeBSD__
44829181Smarkmstatic char fmtstr[] = { "%+" };
44929181Smarkm#else
45029088Smarkm/*
45129088Smarkm * This is split on two lines so that SCCS will not see the M
45229088Smarkm * between two % signs and expand it...
45329088Smarkm */
45429088Smarkmstatic char fmtstr[] = { "%l:%M\
45529088Smarkm%P on %A, %d %B %Y" };
45629181Smarkm#endif
45729088Smarkm
45829088Smarkm	void
45929088Smarkmputf(cp, where)
46029088Smarkm	register char *cp;
46129088Smarkm	char *where;
46229088Smarkm{
46329088Smarkm	char *slash;
46429088Smarkm	time_t t;
46529088Smarkm	char db[100];
46629088Smarkm#ifdef	STREAMSPTY
46729088Smarkm	extern char *strchr();
46829088Smarkm#else
46929088Smarkm	extern char *strrchr();
47029088Smarkm#endif
47129088Smarkm
47229088Smarkm	putlocation = where;
47329088Smarkm
47429088Smarkm	while (*cp) {
47529088Smarkm		if (*cp != '%') {
47629088Smarkm			putchr(*cp++);
47729088Smarkm			continue;
47829088Smarkm		}
47929088Smarkm		switch (*++cp) {
48029088Smarkm
48129088Smarkm		case 't':
48229088Smarkm#ifdef	STREAMSPTY
48329088Smarkm			/* names are like /dev/pts/2 -- we want pts/2 */
48429088Smarkm			slash = strchr(line+1, '/');
48529088Smarkm#else
48629088Smarkm			slash = strrchr(line, '/');
48729088Smarkm#endif
48829088Smarkm			if (slash == (char *) 0)
48929088Smarkm				putstr(line);
49029088Smarkm			else
49129088Smarkm				putstr(&slash[1]);
49229088Smarkm			break;
49329088Smarkm
49429088Smarkm		case 'h':
49529088Smarkm			putstr(editedhost);
49629088Smarkm			break;
49729088Smarkm
49829088Smarkm		case 'd':
49929181Smarkm#ifdef __FreeBSD__
50029181Smarkm			setlocale(LC_TIME, "");
50129181Smarkm#endif
50229088Smarkm			(void)time(&t);
50329088Smarkm			(void)strftime(db, sizeof(db), fmtstr, localtime(&t));
50429088Smarkm			putstr(db);
50529088Smarkm			break;
50629088Smarkm
50729088Smarkm		case '%':
50829088Smarkm			putchr('%');
50929088Smarkm			break;
51029088Smarkm		}
51129088Smarkm		cp++;
51229088Smarkm	}
51329088Smarkm}
51429088Smarkm
51529088Smarkm#ifdef DIAGNOSTICS
51629088Smarkm/*
51729088Smarkm * Print telnet options and commands in plain text, if possible.
51829088Smarkm */
51929088Smarkm	void
52029088Smarkmprintoption(fmt, option)
52129088Smarkm	register char *fmt;
52229088Smarkm	register int option;
52329088Smarkm{
52429088Smarkm	if (TELOPT_OK(option))
52529088Smarkm		sprintf(nfrontp, "%s %s\r\n", fmt, TELOPT(option));
52629088Smarkm	else if (TELCMD_OK(option))
52729088Smarkm		sprintf(nfrontp, "%s %s\r\n", fmt, TELCMD(option));
52829088Smarkm	else
52929088Smarkm		sprintf(nfrontp, "%s %d\r\n", fmt, option);
53029088Smarkm	nfrontp += strlen(nfrontp);
53129088Smarkm	return;
53229088Smarkm}
53329088Smarkm
53429088Smarkm    void
53529088Smarkmprintsub(direction, pointer, length)
53629088Smarkm    char		direction;	/* '<' or '>' */
53729088Smarkm    unsigned char	*pointer;	/* where suboption data sits */
53829088Smarkm    int			length;		/* length of suboption data */
53929088Smarkm{
54029088Smarkm    register int i;
54129088Smarkm    char buf[512];
54229088Smarkm
54329088Smarkm	if (!(diagnostic & TD_OPTIONS))
54429088Smarkm		return;
54529088Smarkm
54629088Smarkm	if (direction) {
54729088Smarkm	    sprintf(nfrontp, "td: %s suboption ",
54829088Smarkm					direction == '<' ? "recv" : "send");
54929088Smarkm	    nfrontp += strlen(nfrontp);
55029088Smarkm	    if (length >= 3) {
55129088Smarkm		register int j;
55229088Smarkm
55329088Smarkm		i = pointer[length-2];
55429088Smarkm		j = pointer[length-1];
55529088Smarkm
55629088Smarkm		if (i != IAC || j != SE) {
55729088Smarkm		    sprintf(nfrontp, "(terminated by ");
55829088Smarkm		    nfrontp += strlen(nfrontp);
55929088Smarkm		    if (TELOPT_OK(i))
56029088Smarkm			sprintf(nfrontp, "%s ", TELOPT(i));
56129088Smarkm		    else if (TELCMD_OK(i))
56229088Smarkm			sprintf(nfrontp, "%s ", TELCMD(i));
56329088Smarkm		    else
56429088Smarkm			sprintf(nfrontp, "%d ", i);
56529088Smarkm		    nfrontp += strlen(nfrontp);
56629088Smarkm		    if (TELOPT_OK(j))
56729088Smarkm			sprintf(nfrontp, "%s", TELOPT(j));
56829088Smarkm		    else if (TELCMD_OK(j))
56929088Smarkm			sprintf(nfrontp, "%s", TELCMD(j));
57029088Smarkm		    else
57129088Smarkm			sprintf(nfrontp, "%d", j);
57229088Smarkm		    nfrontp += strlen(nfrontp);
57329088Smarkm		    sprintf(nfrontp, ", not IAC SE!) ");
57429088Smarkm		    nfrontp += strlen(nfrontp);
57529088Smarkm		}
57629088Smarkm	    }
57729088Smarkm	    length -= 2;
57829088Smarkm	}
57929088Smarkm	if (length < 1) {
58029088Smarkm	    sprintf(nfrontp, "(Empty suboption??\?)");
58129088Smarkm	    nfrontp += strlen(nfrontp);
58229088Smarkm	    return;
58329088Smarkm	}
58429088Smarkm	switch (pointer[0]) {
58529088Smarkm	case TELOPT_TTYPE:
58629088Smarkm	    sprintf(nfrontp, "TERMINAL-TYPE ");
58729088Smarkm	    nfrontp += strlen(nfrontp);
58829088Smarkm	    switch (pointer[1]) {
58929088Smarkm	    case TELQUAL_IS:
59029088Smarkm		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
59129088Smarkm		break;
59229088Smarkm	    case TELQUAL_SEND:
59329088Smarkm		sprintf(nfrontp, "SEND");
59429088Smarkm		break;
59529088Smarkm	    default:
59629088Smarkm		sprintf(nfrontp,
59729088Smarkm				"- unknown qualifier %d (0x%x).",
59829088Smarkm				pointer[1], pointer[1]);
59929088Smarkm	    }
60029088Smarkm	    nfrontp += strlen(nfrontp);
60129088Smarkm	    break;
60229088Smarkm	case TELOPT_TSPEED:
60329088Smarkm	    sprintf(nfrontp, "TERMINAL-SPEED");
60429088Smarkm	    nfrontp += strlen(nfrontp);
60529088Smarkm	    if (length < 2) {
60629088Smarkm		sprintf(nfrontp, " (empty suboption??\?)");
60729088Smarkm		nfrontp += strlen(nfrontp);
60829088Smarkm		break;
60929088Smarkm	    }
61029088Smarkm	    switch (pointer[1]) {
61129088Smarkm	    case TELQUAL_IS:
61229088Smarkm		sprintf(nfrontp, " IS %.*s", length-2, (char *)pointer+2);
61329088Smarkm		nfrontp += strlen(nfrontp);
61429088Smarkm		break;
61529088Smarkm	    default:
61629088Smarkm		if (pointer[1] == 1)
61729088Smarkm		    sprintf(nfrontp, " SEND");
61829088Smarkm		else
61929088Smarkm		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
62029088Smarkm		nfrontp += strlen(nfrontp);
62129088Smarkm		for (i = 2; i < length; i++) {
62229088Smarkm		    sprintf(nfrontp, " ?%d?", pointer[i]);
62329088Smarkm		    nfrontp += strlen(nfrontp);
62429088Smarkm		}
62529088Smarkm		break;
62629088Smarkm	    }
62729088Smarkm	    break;
62829088Smarkm
62929088Smarkm	case TELOPT_LFLOW:
63029088Smarkm	    sprintf(nfrontp, "TOGGLE-FLOW-CONTROL");
63129088Smarkm	    nfrontp += strlen(nfrontp);
63229088Smarkm	    if (length < 2) {
63329088Smarkm		sprintf(nfrontp, " (empty suboption??\?)");
63429088Smarkm		nfrontp += strlen(nfrontp);
63529088Smarkm		break;
63629088Smarkm	    }
63729088Smarkm	    switch (pointer[1]) {
63829088Smarkm	    case LFLOW_OFF:
63929088Smarkm		sprintf(nfrontp, " OFF"); break;
64029088Smarkm	    case LFLOW_ON:
64129088Smarkm		sprintf(nfrontp, " ON"); break;
64229088Smarkm	    case LFLOW_RESTART_ANY:
64329088Smarkm		sprintf(nfrontp, " RESTART-ANY"); break;
64429088Smarkm	    case LFLOW_RESTART_XON:
64529088Smarkm		sprintf(nfrontp, " RESTART-XON"); break;
64629088Smarkm	    default:
64729088Smarkm		sprintf(nfrontp, " %d (unknown)", pointer[1]);
64829088Smarkm	    }
64929088Smarkm	    nfrontp += strlen(nfrontp);
65029088Smarkm	    for (i = 2; i < length; i++) {
65129088Smarkm		sprintf(nfrontp, " ?%d?", pointer[i]);
65229088Smarkm		nfrontp += strlen(nfrontp);
65329088Smarkm	    }
65429088Smarkm	    break;
65529088Smarkm
65629088Smarkm	case TELOPT_NAWS:
65729088Smarkm	    sprintf(nfrontp, "NAWS");
65829088Smarkm	    nfrontp += strlen(nfrontp);
65929088Smarkm	    if (length < 2) {
66029088Smarkm		sprintf(nfrontp, " (empty suboption??\?)");
66129088Smarkm		nfrontp += strlen(nfrontp);
66229088Smarkm		break;
66329088Smarkm	    }
66429088Smarkm	    if (length == 2) {
66529088Smarkm		sprintf(nfrontp, " ?%d?", pointer[1]);
66629088Smarkm		nfrontp += strlen(nfrontp);
66729088Smarkm		break;
66829088Smarkm	    }
66929088Smarkm	    sprintf(nfrontp, " %d %d (%d)",
67029088Smarkm		pointer[1], pointer[2],
67129088Smarkm		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
67229088Smarkm	    nfrontp += strlen(nfrontp);
67329088Smarkm	    if (length == 4) {
67429088Smarkm		sprintf(nfrontp, " ?%d?", pointer[3]);
67529088Smarkm		nfrontp += strlen(nfrontp);
67629088Smarkm		break;
67729088Smarkm	    }
67829088Smarkm	    sprintf(nfrontp, " %d %d (%d)",
67929088Smarkm		pointer[3], pointer[4],
68029088Smarkm		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
68129088Smarkm	    nfrontp += strlen(nfrontp);
68229088Smarkm	    for (i = 5; i < length; i++) {
68329088Smarkm		sprintf(nfrontp, " ?%d?", pointer[i]);
68429088Smarkm		nfrontp += strlen(nfrontp);
68529088Smarkm	    }
68629088Smarkm	    break;
68729088Smarkm
68829088Smarkm	case TELOPT_LINEMODE:
68929088Smarkm	    sprintf(nfrontp, "LINEMODE ");
69029088Smarkm	    nfrontp += strlen(nfrontp);
69129088Smarkm	    if (length < 2) {
69229088Smarkm		sprintf(nfrontp, " (empty suboption??\?)");
69329088Smarkm		nfrontp += strlen(nfrontp);
69429088Smarkm		break;
69529088Smarkm	    }
69629088Smarkm	    switch (pointer[1]) {
69729088Smarkm	    case WILL:
69829088Smarkm		sprintf(nfrontp, "WILL ");
69929088Smarkm		goto common;
70029088Smarkm	    case WONT:
70129088Smarkm		sprintf(nfrontp, "WONT ");
70229088Smarkm		goto common;
70329088Smarkm	    case DO:
70429088Smarkm		sprintf(nfrontp, "DO ");
70529088Smarkm		goto common;
70629088Smarkm	    case DONT:
70729088Smarkm		sprintf(nfrontp, "DONT ");
70829088Smarkm	    common:
70929088Smarkm		nfrontp += strlen(nfrontp);
71029088Smarkm		if (length < 3) {
71129088Smarkm		    sprintf(nfrontp, "(no option??\?)");
71229088Smarkm		    nfrontp += strlen(nfrontp);
71329088Smarkm		    break;
71429088Smarkm		}
71529088Smarkm		switch (pointer[2]) {
71629088Smarkm		case LM_FORWARDMASK:
71729088Smarkm		    sprintf(nfrontp, "Forward Mask");
71829088Smarkm		    nfrontp += strlen(nfrontp);
71929088Smarkm		    for (i = 3; i < length; i++) {
72029088Smarkm			sprintf(nfrontp, " %x", pointer[i]);
72129088Smarkm			nfrontp += strlen(nfrontp);
72229088Smarkm		    }
72329088Smarkm		    break;
72429088Smarkm		default:
72529088Smarkm		    sprintf(nfrontp, "%d (unknown)", pointer[2]);
72629088Smarkm		    nfrontp += strlen(nfrontp);
72729088Smarkm		    for (i = 3; i < length; i++) {
72829088Smarkm			sprintf(nfrontp, " %d", pointer[i]);
72929088Smarkm			nfrontp += strlen(nfrontp);
73029088Smarkm		    }
73129088Smarkm		    break;
73229088Smarkm		}
73329088Smarkm		break;
73429088Smarkm
73529088Smarkm	    case LM_SLC:
73629088Smarkm		sprintf(nfrontp, "SLC");
73729088Smarkm		nfrontp += strlen(nfrontp);
73829088Smarkm		for (i = 2; i < length - 2; i += 3) {
73929088Smarkm		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
74029088Smarkm			sprintf(nfrontp, " %s", SLC_NAME(pointer[i+SLC_FUNC]));
74129088Smarkm		    else
74229088Smarkm			sprintf(nfrontp, " %d", pointer[i+SLC_FUNC]);
74329088Smarkm		    nfrontp += strlen(nfrontp);
74429088Smarkm		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
74529088Smarkm		    case SLC_NOSUPPORT:
74629088Smarkm			sprintf(nfrontp, " NOSUPPORT"); break;
74729088Smarkm		    case SLC_CANTCHANGE:
74829088Smarkm			sprintf(nfrontp, " CANTCHANGE"); break;
74929088Smarkm		    case SLC_VARIABLE:
75029088Smarkm			sprintf(nfrontp, " VARIABLE"); break;
75129088Smarkm		    case SLC_DEFAULT:
75229088Smarkm			sprintf(nfrontp, " DEFAULT"); break;
75329088Smarkm		    }
75429088Smarkm		    nfrontp += strlen(nfrontp);
75529088Smarkm		    sprintf(nfrontp, "%s%s%s",
75629088Smarkm			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
75729088Smarkm			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
75829088Smarkm			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
75929088Smarkm		    nfrontp += strlen(nfrontp);
76029088Smarkm		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
76129088Smarkm						SLC_FLUSHOUT| SLC_LEVELBITS)) {
76229088Smarkm			sprintf(nfrontp, "(0x%x)", pointer[i+SLC_FLAGS]);
76329088Smarkm			nfrontp += strlen(nfrontp);
76429088Smarkm		    }
76529088Smarkm		    sprintf(nfrontp, " %d;", pointer[i+SLC_VALUE]);
76629088Smarkm		    nfrontp += strlen(nfrontp);
76729088Smarkm		    if ((pointer[i+SLC_VALUE] == IAC) &&
76829088Smarkm			(pointer[i+SLC_VALUE+1] == IAC))
76929088Smarkm				i++;
77029088Smarkm		}
77129088Smarkm		for (; i < length; i++) {
77229088Smarkm		    sprintf(nfrontp, " ?%d?", pointer[i]);
77329088Smarkm		    nfrontp += strlen(nfrontp);
77429088Smarkm		}
77529088Smarkm		break;
77629088Smarkm
77729088Smarkm	    case LM_MODE:
77829088Smarkm		sprintf(nfrontp, "MODE ");
77929088Smarkm		nfrontp += strlen(nfrontp);
78029088Smarkm		if (length < 3) {
78129088Smarkm		    sprintf(nfrontp, "(no mode??\?)");
78229088Smarkm		    nfrontp += strlen(nfrontp);
78329088Smarkm		    break;
78429088Smarkm		}
78529088Smarkm		{
78629088Smarkm		    char tbuf[32];
78729088Smarkm		    sprintf(tbuf, "%s%s%s%s%s",
78829088Smarkm			pointer[2]&MODE_EDIT ? "|EDIT" : "",
78929088Smarkm			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
79029088Smarkm			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
79129088Smarkm			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
79229088Smarkm			pointer[2]&MODE_ACK ? "|ACK" : "");
79329088Smarkm		    sprintf(nfrontp, "%s", tbuf[1] ? &tbuf[1] : "0");
79429088Smarkm		    nfrontp += strlen(nfrontp);
79529088Smarkm		}
79629088Smarkm		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
79729088Smarkm		    sprintf(nfrontp, " (0x%x)", pointer[2]);
79829088Smarkm		    nfrontp += strlen(nfrontp);
79929088Smarkm		}
80029088Smarkm		for (i = 3; i < length; i++) {
80129088Smarkm		    sprintf(nfrontp, " ?0x%x?", pointer[i]);
80229088Smarkm		    nfrontp += strlen(nfrontp);
80329088Smarkm		}
80429088Smarkm		break;
80529088Smarkm	    default:
80629088Smarkm		sprintf(nfrontp, "%d (unknown)", pointer[1]);
80729088Smarkm		nfrontp += strlen(nfrontp);
80829088Smarkm		for (i = 2; i < length; i++) {
80929088Smarkm		    sprintf(nfrontp, " %d", pointer[i]);
81029088Smarkm		    nfrontp += strlen(nfrontp);
81129088Smarkm		}
81229088Smarkm	    }
81329088Smarkm	    break;
81429088Smarkm
81529088Smarkm	case TELOPT_STATUS: {
81629088Smarkm	    register char *cp;
81729088Smarkm	    register int j, k;
81829088Smarkm
81929088Smarkm	    sprintf(nfrontp, "STATUS");
82029088Smarkm	    nfrontp += strlen(nfrontp);
82129088Smarkm
82229088Smarkm	    switch (pointer[1]) {
82329088Smarkm	    default:
82429088Smarkm		if (pointer[1] == TELQUAL_SEND)
82529088Smarkm		    sprintf(nfrontp, " SEND");
82629088Smarkm		else
82729088Smarkm		    sprintf(nfrontp, " %d (unknown)", pointer[1]);
82829088Smarkm		nfrontp += strlen(nfrontp);
82929088Smarkm		for (i = 2; i < length; i++) {
83029088Smarkm		    sprintf(nfrontp, " ?%d?", pointer[i]);
83129088Smarkm		    nfrontp += strlen(nfrontp);
83229088Smarkm		}
83329088Smarkm		break;
83429088Smarkm	    case TELQUAL_IS:
83529088Smarkm		sprintf(nfrontp, " IS\r\n");
83629088Smarkm		nfrontp += strlen(nfrontp);
83729088Smarkm
83829088Smarkm		for (i = 2; i < length; i++) {
83929088Smarkm		    switch(pointer[i]) {
84029088Smarkm		    case DO:	cp = "DO"; goto common2;
84129088Smarkm		    case DONT:	cp = "DONT"; goto common2;
84229088Smarkm		    case WILL:	cp = "WILL"; goto common2;
84329088Smarkm		    case WONT:	cp = "WONT"; goto common2;
84429088Smarkm		    common2:
84529088Smarkm			i++;
84629088Smarkm			if (TELOPT_OK(pointer[i]))
84729088Smarkm			    sprintf(nfrontp, " %s %s", cp, TELOPT(pointer[i]));
84829088Smarkm			else
84929088Smarkm			    sprintf(nfrontp, " %s %d", cp, pointer[i]);
85029088Smarkm			nfrontp += strlen(nfrontp);
85129088Smarkm
85229088Smarkm			sprintf(nfrontp, "\r\n");
85329088Smarkm			nfrontp += strlen(nfrontp);
85429088Smarkm			break;
85529088Smarkm
85629088Smarkm		    case SB:
85729088Smarkm			sprintf(nfrontp, " SB ");
85829088Smarkm			nfrontp += strlen(nfrontp);
85929088Smarkm			i++;
86029088Smarkm			j = k = i;
86129088Smarkm			while (j < length) {
86229088Smarkm			    if (pointer[j] == SE) {
86329088Smarkm				if (j+1 == length)
86429088Smarkm				    break;
86529088Smarkm				if (pointer[j+1] == SE)
86629088Smarkm				    j++;
86729088Smarkm				else
86829088Smarkm				    break;
86929088Smarkm			    }
87029088Smarkm			    pointer[k++] = pointer[j++];
87129088Smarkm			}
87229088Smarkm			printsub(0, &pointer[i], k - i);
87329088Smarkm			if (i < length) {
87429088Smarkm			    sprintf(nfrontp, " SE");
87529088Smarkm			    nfrontp += strlen(nfrontp);
87629088Smarkm			    i = j;
87729088Smarkm			} else
87829088Smarkm			    i = j - 1;
87929088Smarkm
88029088Smarkm			sprintf(nfrontp, "\r\n");
88129088Smarkm			nfrontp += strlen(nfrontp);
88229088Smarkm
88329088Smarkm			break;
88429088Smarkm
88529088Smarkm		    default:
88629088Smarkm			sprintf(nfrontp, " %d", pointer[i]);
88729088Smarkm			nfrontp += strlen(nfrontp);
88829088Smarkm			break;
88929088Smarkm		    }
89029088Smarkm		}
89129088Smarkm		break;
89229088Smarkm	    }
89329088Smarkm	    break;
89429088Smarkm	  }
89529088Smarkm
89629088Smarkm	case TELOPT_XDISPLOC:
89729088Smarkm	    sprintf(nfrontp, "X-DISPLAY-LOCATION ");
89829088Smarkm	    nfrontp += strlen(nfrontp);
89929088Smarkm	    switch (pointer[1]) {
90029088Smarkm	    case TELQUAL_IS:
90129088Smarkm		sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
90229088Smarkm		break;
90329088Smarkm	    case TELQUAL_SEND:
90429088Smarkm		sprintf(nfrontp, "SEND");
90529088Smarkm		break;
90629088Smarkm	    default:
90729088Smarkm		sprintf(nfrontp, "- unknown qualifier %d (0x%x).",
90829088Smarkm				pointer[1], pointer[1]);
90929088Smarkm	    }
91029088Smarkm	    nfrontp += strlen(nfrontp);
91129088Smarkm	    break;
91229088Smarkm
91329088Smarkm	case TELOPT_NEW_ENVIRON:
91429088Smarkm	    sprintf(nfrontp, "NEW-ENVIRON ");
91529088Smarkm	    goto env_common1;
91629088Smarkm	case TELOPT_OLD_ENVIRON:
91729088Smarkm	    sprintf(nfrontp, "OLD-ENVIRON");
91829088Smarkm	env_common1:
91929088Smarkm	    nfrontp += strlen(nfrontp);
92029088Smarkm	    switch (pointer[1]) {
92129088Smarkm	    case TELQUAL_IS:
92229088Smarkm		sprintf(nfrontp, "IS ");
92329088Smarkm		goto env_common;
92429088Smarkm	    case TELQUAL_SEND:
92529088Smarkm		sprintf(nfrontp, "SEND ");
92629088Smarkm		goto env_common;
92729088Smarkm	    case TELQUAL_INFO:
92829088Smarkm		sprintf(nfrontp, "INFO ");
92929088Smarkm	    env_common:
93029088Smarkm		nfrontp += strlen(nfrontp);
93129088Smarkm		{
93229088Smarkm		    register int noquote = 2;
93329088Smarkm		    for (i = 2; i < length; i++ ) {
93429088Smarkm			switch (pointer[i]) {
93529088Smarkm			case NEW_ENV_VAR:
93629088Smarkm			    sprintf(nfrontp, "\" VAR " + noquote);
93729088Smarkm			    nfrontp += strlen(nfrontp);
93829088Smarkm			    noquote = 2;
93929088Smarkm			    break;
94029088Smarkm
94129088Smarkm			case NEW_ENV_VALUE:
94229088Smarkm			    sprintf(nfrontp, "\" VALUE " + noquote);
94329088Smarkm			    nfrontp += strlen(nfrontp);
94429088Smarkm			    noquote = 2;
94529088Smarkm			    break;
94629088Smarkm
94729088Smarkm			case ENV_ESC:
94829088Smarkm			    sprintf(nfrontp, "\" ESC " + noquote);
94929088Smarkm			    nfrontp += strlen(nfrontp);
95029088Smarkm			    noquote = 2;
95129088Smarkm			    break;
95229088Smarkm
95329088Smarkm			case ENV_USERVAR:
95429088Smarkm			    sprintf(nfrontp, "\" USERVAR " + noquote);
95529088Smarkm			    nfrontp += strlen(nfrontp);
95629088Smarkm			    noquote = 2;
95729088Smarkm			    break;
95829088Smarkm
95929088Smarkm			default:
96029088Smarkm			    if (isprint(pointer[i]) && pointer[i] != '"') {
96129088Smarkm				if (noquote) {
96229088Smarkm				    *nfrontp++ = '"';
96329088Smarkm				    noquote = 0;
96429088Smarkm				}
96529088Smarkm				*nfrontp++ = pointer[i];
96629088Smarkm			    } else {
96729088Smarkm				sprintf(nfrontp, "\" %03o " + noquote,
96829088Smarkm							pointer[i]);
96929088Smarkm				nfrontp += strlen(nfrontp);
97029088Smarkm				noquote = 2;
97129088Smarkm			    }
97229088Smarkm			    break;
97329088Smarkm			}
97429088Smarkm		    }
97529088Smarkm		    if (!noquote)
97629088Smarkm			*nfrontp++ = '"';
97729088Smarkm		    break;
97829088Smarkm		}
97929088Smarkm	    }
98029088Smarkm	    break;
98129088Smarkm
98229088Smarkm#if	defined(AUTHENTICATION)
98329088Smarkm	case TELOPT_AUTHENTICATION:
98429088Smarkm	    sprintf(nfrontp, "AUTHENTICATION");
98529088Smarkm	    nfrontp += strlen(nfrontp);
98629088Smarkm
98729088Smarkm	    if (length < 2) {
98829088Smarkm		sprintf(nfrontp, " (empty suboption??\?)");
98929088Smarkm		nfrontp += strlen(nfrontp);
99029088Smarkm		break;
99129088Smarkm	    }
99229088Smarkm	    switch (pointer[1]) {
99329088Smarkm	    case TELQUAL_REPLY:
99429088Smarkm	    case TELQUAL_IS:
99529088Smarkm		sprintf(nfrontp, " %s ", (pointer[1] == TELQUAL_IS) ?
99629088Smarkm							"IS" : "REPLY");
99729088Smarkm		nfrontp += strlen(nfrontp);
99829088Smarkm		if (AUTHTYPE_NAME_OK(pointer[2]))
99929088Smarkm		    sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[2]));
100029088Smarkm		else
100129088Smarkm		    sprintf(nfrontp, "%d ", pointer[2]);
100229088Smarkm		nfrontp += strlen(nfrontp);
100329088Smarkm		if (length < 3) {
100429088Smarkm		    sprintf(nfrontp, "(partial suboption??\?)");
100529088Smarkm		    nfrontp += strlen(nfrontp);
100629088Smarkm		    break;
100729088Smarkm		}
100829088Smarkm		sprintf(nfrontp, "%s|%s",
100929088Smarkm			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
101029088Smarkm			"CLIENT" : "SERVER",
101129088Smarkm			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
101229088Smarkm			"MUTUAL" : "ONE-WAY");
101329088Smarkm		nfrontp += strlen(nfrontp);
101429088Smarkm
101529088Smarkm		auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
101629088Smarkm		sprintf(nfrontp, "%s", buf);
101729088Smarkm		nfrontp += strlen(nfrontp);
101829088Smarkm		break;
101929088Smarkm
102029088Smarkm	    case TELQUAL_SEND:
102129088Smarkm		i = 2;
102229088Smarkm		sprintf(nfrontp, " SEND ");
102329088Smarkm		nfrontp += strlen(nfrontp);
102429088Smarkm		while (i < length) {
102529088Smarkm		    if (AUTHTYPE_NAME_OK(pointer[i]))
102629088Smarkm			sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[i]));
102729088Smarkm		    else
102829088Smarkm			sprintf(nfrontp, "%d ", pointer[i]);
102929088Smarkm		    nfrontp += strlen(nfrontp);
103029088Smarkm		    if (++i >= length) {
103129088Smarkm			sprintf(nfrontp, "(partial suboption??\?)");
103229088Smarkm			nfrontp += strlen(nfrontp);
103329088Smarkm			break;
103429088Smarkm		    }
103529088Smarkm		    sprintf(nfrontp, "%s|%s ",
103629088Smarkm			((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
103729088Smarkm							"CLIENT" : "SERVER",
103829088Smarkm			((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
103929088Smarkm							"MUTUAL" : "ONE-WAY");
104029088Smarkm		    nfrontp += strlen(nfrontp);
104129088Smarkm		    ++i;
104229088Smarkm		}
104329088Smarkm		break;
104429088Smarkm
104529088Smarkm	    case TELQUAL_NAME:
104629088Smarkm		i = 2;
104729088Smarkm		sprintf(nfrontp, " NAME \"");
104829088Smarkm		nfrontp += strlen(nfrontp);
104929088Smarkm		while (i < length)
105029088Smarkm		    *nfrontp += pointer[i++];
105129088Smarkm		*nfrontp += '"';
105229088Smarkm		break;
105329088Smarkm
105429088Smarkm	    default:
105529088Smarkm		    for (i = 2; i < length; i++) {
105629088Smarkm			sprintf(nfrontp, " ?%d?", pointer[i]);
105729088Smarkm			nfrontp += strlen(nfrontp);
105829088Smarkm		    }
105929088Smarkm		    break;
106029088Smarkm	    }
106129088Smarkm	    break;
106229088Smarkm#endif
106329088Smarkm
106429088Smarkm#ifdef	ENCRYPTION
106529088Smarkm	case TELOPT_ENCRYPT:
106629088Smarkm	    sprintf(nfrontp, "ENCRYPT");
106729088Smarkm	    nfrontp += strlen(nfrontp);
106829088Smarkm	    if (length < 2) {
106929088Smarkm		sprintf(nfrontp, " (empty suboption??\?)");
107029088Smarkm		nfrontp += strlen(nfrontp);
107129088Smarkm		break;
107229088Smarkm	    }
107329088Smarkm	    switch (pointer[1]) {
107429088Smarkm	    case ENCRYPT_START:
107529088Smarkm		sprintf(nfrontp, " START");
107629088Smarkm		nfrontp += strlen(nfrontp);
107729088Smarkm		break;
107829088Smarkm
107929088Smarkm	    case ENCRYPT_END:
108029088Smarkm		sprintf(nfrontp, " END");
108129088Smarkm		nfrontp += strlen(nfrontp);
108229088Smarkm		break;
108329088Smarkm
108429088Smarkm	    case ENCRYPT_REQSTART:
108529088Smarkm		sprintf(nfrontp, " REQUEST-START");
108629088Smarkm		nfrontp += strlen(nfrontp);
108729088Smarkm		break;
108829088Smarkm
108929088Smarkm	    case ENCRYPT_REQEND:
109029088Smarkm		sprintf(nfrontp, " REQUEST-END");
109129088Smarkm		nfrontp += strlen(nfrontp);
109229088Smarkm		break;
109329088Smarkm
109429088Smarkm	    case ENCRYPT_IS:
109529088Smarkm	    case ENCRYPT_REPLY:
109629088Smarkm		sprintf(nfrontp, " %s ", (pointer[1] == ENCRYPT_IS) ?
109729088Smarkm							"IS" : "REPLY");
109829088Smarkm		nfrontp += strlen(nfrontp);
109929088Smarkm		if (length < 3) {
110029088Smarkm		    sprintf(nfrontp, " (partial suboption??\?)");
110129088Smarkm		    nfrontp += strlen(nfrontp);
110229088Smarkm		    break;
110329088Smarkm		}
110429088Smarkm		if (ENCTYPE_NAME_OK(pointer[2]))
110529088Smarkm		    sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[2]));
110629088Smarkm		else
110729088Smarkm		    sprintf(nfrontp, " %d (unknown)", pointer[2]);
110829088Smarkm		nfrontp += strlen(nfrontp);
110929088Smarkm
111029088Smarkm		encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
111129088Smarkm		sprintf(nfrontp, "%s", buf);
111229088Smarkm		nfrontp += strlen(nfrontp);
111329088Smarkm		break;
111429088Smarkm
111529088Smarkm	    case ENCRYPT_SUPPORT:
111629088Smarkm		i = 2;
111729088Smarkm		sprintf(nfrontp, " SUPPORT ");
111829088Smarkm		nfrontp += strlen(nfrontp);
111929088Smarkm		while (i < length) {
112029088Smarkm		    if (ENCTYPE_NAME_OK(pointer[i]))
112129088Smarkm			sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[i]));
112229088Smarkm		    else
112329088Smarkm			sprintf(nfrontp, "%d ", pointer[i]);
112429088Smarkm		    nfrontp += strlen(nfrontp);
112529088Smarkm		    i++;
112629088Smarkm		}
112729088Smarkm		break;
112829088Smarkm
112929088Smarkm	    case ENCRYPT_ENC_KEYID:
113029181Smarkm		sprintf(nfrontp, " ENC_KEYID");
113129088Smarkm		nfrontp += strlen(nfrontp);
113229088Smarkm		goto encommon;
113329088Smarkm
113429088Smarkm	    case ENCRYPT_DEC_KEYID:
113529181Smarkm		sprintf(nfrontp, " DEC_KEYID");
113629088Smarkm		nfrontp += strlen(nfrontp);
113729088Smarkm		goto encommon;
113829088Smarkm
113929088Smarkm	    default:
114029088Smarkm		sprintf(nfrontp, " %d (unknown)", pointer[1]);
114129088Smarkm		nfrontp += strlen(nfrontp);
114229088Smarkm	    encommon:
114329088Smarkm		for (i = 2; i < length; i++) {
114429088Smarkm		    sprintf(nfrontp, " %d", pointer[i]);
114529088Smarkm		    nfrontp += strlen(nfrontp);
114629088Smarkm		}
114729088Smarkm		break;
114829088Smarkm	    }
114929088Smarkm	    break;
115029088Smarkm#endif	/* ENCRYPTION */
115129088Smarkm
115229088Smarkm	default:
115329088Smarkm	    if (TELOPT_OK(pointer[0]))
115429088Smarkm		sprintf(nfrontp, "%s (unknown)", TELOPT(pointer[0]));
115529088Smarkm	    else
115629088Smarkm		sprintf(nfrontp, "%d (unknown)", pointer[i]);
115729088Smarkm	    nfrontp += strlen(nfrontp);
115829088Smarkm	    for (i = 1; i < length; i++) {
115929088Smarkm		sprintf(nfrontp, " %d", pointer[i]);
116029088Smarkm		nfrontp += strlen(nfrontp);
116129088Smarkm	    }
116229088Smarkm	    break;
116329088Smarkm	}
116429088Smarkm	sprintf(nfrontp, "\r\n");
116529088Smarkm	nfrontp += strlen(nfrontp);
116629088Smarkm}
116729088Smarkm
116829088Smarkm/*
116929088Smarkm * Dump a data buffer in hex and ascii to the output data stream.
117029088Smarkm */
117129088Smarkm	void
117229088Smarkmprintdata(tag, ptr, cnt)
117329088Smarkm	register char *tag;
117429088Smarkm	register char *ptr;
117529088Smarkm	register int cnt;
117629088Smarkm{
117729088Smarkm	register int i;
117829088Smarkm	char xbuf[30];
117929088Smarkm
118029088Smarkm	while (cnt) {
118129088Smarkm		/* flush net output buffer if no room for new data) */
118229088Smarkm		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
118329088Smarkm			netflush();
118429088Smarkm		}
118529088Smarkm
118629088Smarkm		/* add a line of output */
118729088Smarkm		sprintf(nfrontp, "%s: ", tag);
118829088Smarkm		nfrontp += strlen(nfrontp);
118929088Smarkm		for (i = 0; i < 20 && cnt; i++) {
119029088Smarkm			sprintf(nfrontp, "%02x", *ptr);
119129088Smarkm			nfrontp += strlen(nfrontp);
119229088Smarkm			if (isprint(*ptr)) {
119329088Smarkm				xbuf[i] = *ptr;
119429088Smarkm			} else {
119529088Smarkm				xbuf[i] = '.';
119629088Smarkm			}
119729088Smarkm			if (i % 2) {
119829088Smarkm				*nfrontp = ' ';
119929088Smarkm				nfrontp++;
120029088Smarkm			}
120129088Smarkm			cnt--;
120229088Smarkm			ptr++;
120329088Smarkm		}
120429088Smarkm		xbuf[i] = '\0';
120529088Smarkm		sprintf(nfrontp, " %s\r\n", xbuf );
120629088Smarkm		nfrontp += strlen(nfrontp);
120729088Smarkm	}
120829088Smarkm}
120929088Smarkm#endif /* DIAGNOSTICS */
1210