utility.c revision 72445
1261081Sasomers/*
2261081Sasomers * Copyright (c) 1989, 1993
3261081Sasomers *	The Regents of the University of California.  All rights reserved.
4261081Sasomers *
5261081Sasomers * Redistribution and use in source and binary forms, with or without
6261081Sasomers * modification, are permitted provided that the following conditions
7261081Sasomers * are met:
8261081Sasomers * 1. Redistributions of source code must retain the above copyright
9261081Sasomers *    notice, this list of conditions and the following disclaimer.
10261081Sasomers * 2. Redistributions in binary form must reproduce the above copyright
11261081Sasomers *    notice, this list of conditions and the following disclaimer in the
12261081Sasomers *    documentation and/or other materials provided with the distribution.
13261081Sasomers * 3. All advertising materials mentioning features or use of this software
14261081Sasomers *    must display the following acknowledgement:
15261081Sasomers *	This product includes software developed by the University of
16261081Sasomers *	California, Berkeley and its contributors.
17261081Sasomers * 4. Neither the name of the University nor the names of its contributors
18261081Sasomers *    may be used to endorse or promote products derived from this software
19261081Sasomers *    without specific prior written permission.
20261081Sasomers *
21261081Sasomers * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22261081Sasomers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23261081Sasomers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24261081Sasomers * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25261081Sasomers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26261081Sasomers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27261081Sasomers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28261081Sasomers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29261081Sasomers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30261081Sasomers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31261081Sasomers * SUCH DAMAGE.
32261081Sasomers */
33261081Sasomers
34261081Sasomers#define PRINTOPTIONS
35261081Sasomers#include "telnetd.h"
36261081Sasomers
37261081SasomersRCSID("$Id: utility.c,v 1.23 2000/10/08 13:34:27 assar Exp $");
38261081Sasomers
39261081Sasomers/*
40261081Sasomers * utility functions performing io related tasks
41261081Sasomers */
42261081Sasomers
43261081Sasomers/*
44261081Sasomers * ttloop
45261081Sasomers *
46262894Sasomers * A small subroutine to flush the network output buffer, get some
47261081Sasomers * data from the network, and pass it through the telnet state
48261081Sasomers * machine.  We also flush the pty input buffer (by dropping its data)
49261081Sasomers * if it becomes too full.
50261081Sasomers *
51261081Sasomers * return 0 if OK or 1 if interrupted by a signal.
52261081Sasomers */
53261081Sasomers
54261081Sasomersint
55261081Sasomersttloop(void)
56261081Sasomers{
57261081Sasomers    void netflush(void);
58262894Sasomers
59261081Sasomers    DIAG(TD_REPORT, {
60261081Sasomers	output_data("td: ttloop\r\n");
61261081Sasomers    });
62261081Sasomers    if (nfrontp-nbackp)
63261081Sasomers	netflush();
64261081Sasomers    ncc = read(net, netibuf, sizeof netibuf);
65261081Sasomers    if (ncc < 0) {
66261081Sasomers	if (errno == EINTR)
67261081Sasomers	    return 1;
68261081Sasomers	syslog(LOG_INFO, "ttloop:  read: %m\n");
69261081Sasomers	exit(1);
70261081Sasomers    } else if (ncc == 0) {
71261081Sasomers	syslog(LOG_INFO, "ttloop:  peer died\n");
72261081Sasomers	exit(1);
73261081Sasomers    }
74261081Sasomers    DIAG(TD_REPORT, {
75261081Sasomers	output_data("td: ttloop read %d chars\r\n", ncc);
76262894Sasomers    });
77261081Sasomers    netip = netibuf;
78261081Sasomers    telrcv();			/* state machine */
79261081Sasomers    if (ncc > 0) {
80261081Sasomers	pfrontp = pbackp = ptyobuf;
81261081Sasomers	telrcv();
82261081Sasomers    }
83261081Sasomers    return 0;
84261081Sasomers}  /* end of ttloop */
85261081Sasomers
86261081Sasomers/*
87261081Sasomers * Check a descriptor to see if out of band data exists on it.
88261081Sasomers */
89261081Sasomersint
90261081Sasomersstilloob(int s)
91261081Sasomers{
92261081Sasomers    static struct timeval timeout = { 0 };
93261081Sasomers    fd_set	excepts;
94261081Sasomers    int value;
95261081Sasomers
96261081Sasomers    if (s >= FD_SETSIZE)
97261081Sasomers	fatal(ourpty, "fd too large");
98261081Sasomers
99261081Sasomers    do {
100261081Sasomers	FD_ZERO(&excepts);
101261081Sasomers	FD_SET(s, &excepts);
102261081Sasomers	value = select(s+1, 0, 0, &excepts, &timeout);
103261081Sasomers    } while ((value == -1) && (errno == EINTR));
104261081Sasomers
105261081Sasomers    if (value < 0) {
106261081Sasomers	fatalperror(ourpty, "select");
107261081Sasomers    }
108261081Sasomers    if (FD_ISSET(s, &excepts)) {
109261081Sasomers	return 1;
110261081Sasomers    } else {
111261081Sasomers	return 0;
112261081Sasomers    }
113270228Sasomers}
114270228Sasomers
115270228Sasomersvoid
116261081Sasomersptyflush(void)
117261081Sasomers{
118261081Sasomers    int n;
119261081Sasomers
120261081Sasomers    if ((n = pfrontp - pbackp) > 0) {
121262894Sasomers	DIAG((TD_REPORT | TD_PTYDATA), {
122261081Sasomers	    output_data("td: ptyflush %d chars\r\n", n);
123261081Sasomers	});
124261081Sasomers	DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
125261081Sasomers	n = write(ourpty, pbackp, n);
126261081Sasomers    }
127261081Sasomers    if (n < 0) {
128261081Sasomers	if (errno == EWOULDBLOCK || errno == EINTR)
129262894Sasomers	    return;
130261081Sasomers	cleanup(0);
131261081Sasomers    }
132261081Sasomers    pbackp += n;
133261081Sasomers    if (pbackp == pfrontp)
134261081Sasomers	pbackp = pfrontp = ptyobuf;
135261081Sasomers}
136262894Sasomers
137261081Sasomers/*
138262894Sasomers * nextitem()
139261081Sasomers *
140261081Sasomers *	Return the address of the next "item" in the TELNET data
141262133Sasomers * stream.  This will be the address of the next character if
142261081Sasomers * the current address is a user data character, or it will
143261081Sasomers * be the address of the character following the TELNET command
144261081Sasomers * if the current address is a TELNET IAC ("I Am a Command")
145261081Sasomers * character.
146261081Sasomers */
147261081Sasomerschar *
148261081Sasomersnextitem(char *current)
149261081Sasomers{
150262894Sasomers    if ((*current&0xff) != IAC) {
151261081Sasomers	return current+1;
152261081Sasomers    }
153270228Sasomers    switch (*(current+1)&0xff) {
154270228Sasomers    case DO:
155270228Sasomers    case DONT:
156261081Sasomers    case WILL:
157270228Sasomers    case WONT:
158261081Sasomers	return current+3;
159261081Sasomers    case SB:{
160261081Sasomers	/* loop forever looking for the SE */
161261081Sasomers	char *look = current+2;
162261081Sasomers
163261081Sasomers	for (;;) {
164261081Sasomers	    if ((*look++&0xff) == IAC) {
165261081Sasomers		if ((*look++&0xff) == SE) {
166261081Sasomers		    return look;
167262894Sasomers		}
168261081Sasomers	    }
169261081Sasomers	}
170261081Sasomers    }
171262894Sasomers    default:
172261081Sasomers	return current+2;
173261081Sasomers    }
174261081Sasomers}
175261081Sasomers
176261081Sasomers
177261081Sasomers/*
178261081Sasomers * netclear()
179261081Sasomers *
180261081Sasomers *	We are about to do a TELNET SYNCH operation.  Clear
181261081Sasomers * the path to the network.
182261081Sasomers *
183261081Sasomers *	Things are a bit tricky since we may have sent the first
184261081Sasomers * byte or so of a previous TELNET command into the network.
185261081Sasomers * So, we have to scan the network buffer from the beginning
186261081Sasomers * until we are up to where we want to be.
187261081Sasomers *
188261081Sasomers *	A side effect of what we do, just to keep things
189261081Sasomers * simple, is to clear the urgent data pointer.  The principal
190261081Sasomers * caller should be setting the urgent data pointer AFTER calling
191261081Sasomers * us in any case.
192261081Sasomers */
193261081Sasomersvoid
194261081Sasomersnetclear(void)
195261081Sasomers{
196261081Sasomers    char *thisitem, *next;
197261081Sasomers    char *good;
198261081Sasomers#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
199261081Sasomers			 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
200261081Sasomers
201261081Sasomers#ifdef ENCRYPTION
202261081Sasomers	thisitem = nclearto > netobuf ? nclearto : netobuf;
203261081Sasomers#else
204261081Sasomers	thisitem = netobuf;
205261081Sasomers#endif
206261081Sasomers
207270228Sasomers	while ((next = nextitem(thisitem)) <= nbackp) {
208270228Sasomers	    thisitem = next;
209261081Sasomers	}
210261081Sasomers
211262894Sasomers	/* Now, thisitem is first before/at boundary. */
212261081Sasomers
213261081Sasomers#ifdef ENCRYPTION
214262894Sasomers	good = nclearto > netobuf ? nclearto : netobuf;
215261081Sasomers#else
216262894Sasomers	good = netobuf;	/* where the good bytes go */
217261081Sasomers#endif
218261081Sasomers
219261081Sasomers	while (nfrontp > thisitem) {
220261081Sasomers	    if (wewant(thisitem)) {
221261081Sasomers		int length;
222261081Sasomers
223261081Sasomers		next = thisitem;
224261081Sasomers		do {
225261081Sasomers		    next = nextitem(next);
226261081Sasomers		} while (wewant(next) && (nfrontp > next));
227261081Sasomers		length = next-thisitem;
228261081Sasomers		memmove(good, thisitem, length);
229261081Sasomers		good += length;
230261081Sasomers		thisitem = next;
231261081Sasomers	    } else {
232261081Sasomers		thisitem = nextitem(thisitem);
233261081Sasomers	    }
234261081Sasomers	}
235261081Sasomers
236261081Sasomers	nbackp = netobuf;
237261081Sasomers	nfrontp = good;		/* next byte to be sent */
238261081Sasomers	neturg = 0;
239261081Sasomers}  /* end of netclear */
240261081Sasomers
241261081Sasomers/*
242261081Sasomers *  netflush
243261081Sasomers *		Send as much data as possible to the network,
244261081Sasomers *	handling requests for urgent data.
245261081Sasomers */
246261081Sasomersvoid
247261081Sasomersnetflush(void)
248261081Sasomers{
249261081Sasomers    int n;
250261081Sasomers    extern int not42;
251261081Sasomers
252261081Sasomers    if ((n = nfrontp - nbackp) > 0) {
253261081Sasomers	DIAG(TD_REPORT,
254261081Sasomers	     { n += output_data("td: netflush %d chars\r\n", n);
255261081Sasomers	     });
256261081Sasomers#ifdef ENCRYPTION
257261081Sasomers	if (encrypt_output) {
258261081Sasomers	    char *s = nclearto ? nclearto : nbackp;
259261081Sasomers	    if (nfrontp - s > 0) {
260261081Sasomers		(*encrypt_output)((unsigned char *)s, nfrontp-s);
261261081Sasomers		nclearto = nfrontp;
262261081Sasomers	    }
263261081Sasomers	}
264261081Sasomers#endif
265261081Sasomers	/*
266261081Sasomers	 * if no urgent data, or if the other side appears to be an
267261081Sasomers	 * old 4.2 client (and thus unable to survive TCP urgent data),
268261081Sasomers	 * write the entire buffer in non-OOB mode.
269261081Sasomers	 */
270261081Sasomers#if 1 /* remove this to make it work between solaris 2.6 and linux */
271261081Sasomers	if ((neturg == 0) || (not42 == 0)) {
272261081Sasomers#endif
273261081Sasomers	    n = write(net, nbackp, n);	/* normal write */
274261081Sasomers#if 1 /* remove this to make it work between solaris 2.6 and linux */
275261081Sasomers	} else {
276261081Sasomers	    n = neturg - nbackp;
277261081Sasomers	    /*
278261081Sasomers	     * In 4.2 (and 4.3) systems, there is some question about
279261081Sasomers	     * what byte in a sendOOB operation is the "OOB" data.
280261081Sasomers	     * To make ourselves compatible, we only send ONE byte
281261081Sasomers	     * out of band, the one WE THINK should be OOB (though
282261081Sasomers	     * we really have more the TCP philosophy of urgent data
283261081Sasomers	     * rather than the Unix philosophy of OOB data).
284270228Sasomers	     */
285270228Sasomers	    if (n > 1) {
286261081Sasomers		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
287261081Sasomers	    } else {
288261081Sasomers		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
289261081Sasomers	    }
290261081Sasomers	}
291261081Sasomers#endif
292261081Sasomers    }
293261081Sasomers    if (n < 0) {
294261081Sasomers	if (errno == EWOULDBLOCK || errno == EINTR)
295261081Sasomers	    return;
296261081Sasomers	cleanup(0);
297261081Sasomers    }
298261081Sasomers    nbackp += n;
299261081Sasomers#ifdef ENCRYPTION
300261081Sasomers    if (nbackp > nclearto)
301261081Sasomers	nclearto = 0;
302261081Sasomers#endif
303261081Sasomers    if (nbackp >= neturg) {
304261081Sasomers	neturg = 0;
305261081Sasomers    }
306261081Sasomers    if (nbackp == nfrontp) {
307261081Sasomers	nbackp = nfrontp = netobuf;
308261081Sasomers#ifdef ENCRYPTION
309261081Sasomers	nclearto = 0;
310261081Sasomers#endif
311261081Sasomers    }
312261081Sasomers    return;
313261081Sasomers}
314261081Sasomers
315261081Sasomers
316261081Sasomers/*
317261081Sasomers * writenet
318261081Sasomers *
319261081Sasomers * Just a handy little function to write a bit of raw data to the net.
320261081Sasomers * It will force a transmit of the buffer if necessary
321261081Sasomers *
322261081Sasomers * arguments
323261081Sasomers *    ptr - A pointer to a character string to write
324261081Sasomers *    len - How many bytes to write
325261081Sasomers */
326261081Sasomersvoid
327261081Sasomerswritenet(unsigned char *ptr, int len)
328261081Sasomers{
329261081Sasomers    /* flush buffer if no room for new data) */
330261081Sasomers    while ((&netobuf[BUFSIZ] - nfrontp) < len) {
331261081Sasomers	/* if this fails, don't worry, buffer is a little big */
332261081Sasomers	netflush();
333261081Sasomers    }
334261081Sasomers
335261081Sasomers    memmove(nfrontp, ptr, len);
336261081Sasomers    nfrontp += len;
337261081Sasomers}
338261081Sasomers
339261081Sasomers
340261081Sasomers/*
341261081Sasomers * miscellaneous functions doing a variety of little jobs follow ...
342261081Sasomers */
343262894Sasomers
344261081Sasomers
345261081Sasomersvoid fatal(int f, char *msg)
346261081Sasomers{
347261081Sasomers    char buf[BUFSIZ];
348261081Sasomers
349261081Sasomers    snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
350261081Sasomers#ifdef ENCRYPTION
351261081Sasomers    if (encrypt_output) {
352261081Sasomers	/*
353261081Sasomers	 * Better turn off encryption first....
354261081Sasomers	 * Hope it flushes...
355261081Sasomers	 */
356261081Sasomers	encrypt_send_end();
357261081Sasomers	netflush();
358261081Sasomers    }
359261081Sasomers#endif
360261081Sasomers    write(f, buf, (int)strlen(buf));
361261081Sasomers    sleep(1);	/*XXX*/
362261081Sasomers    exit(1);
363261081Sasomers}
364261081Sasomers
365261081Sasomersvoid
366261081Sasomersfatalperror(int f, const char *msg)
367261081Sasomers{
368261081Sasomers    char buf[BUFSIZ];
369261081Sasomers
370261081Sasomers    snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
371261081Sasomers    fatal(f, buf);
372262867Sasomers}
373262867Sasomers
374262867Sasomerschar editedhost[32];
375262867Sasomers
376262867Sasomersvoid edithost(char *pat, char *host)
377262867Sasomers{
378261081Sasomers    char *res = editedhost;
379261081Sasomers
380261081Sasomers    if (!pat)
381261081Sasomers	pat = "";
382261081Sasomers    while (*pat) {
383261081Sasomers	switch (*pat) {
384270228Sasomers
385270228Sasomers	case '#':
386261081Sasomers	    if (*host)
387261081Sasomers		host++;
388261081Sasomers	    break;
389261081Sasomers
390261081Sasomers	case '@':
391261081Sasomers	    if (*host)
392261081Sasomers		*res++ = *host++;
393261081Sasomers	    break;
394261081Sasomers
395261081Sasomers	default:
396261081Sasomers	    *res++ = *pat;
397261081Sasomers	    break;
398261081Sasomers	}
399261081Sasomers	if (res == &editedhost[sizeof editedhost - 1]) {
400261081Sasomers	    *res = '\0';
401270228Sasomers	    return;
402261081Sasomers	}
403261081Sasomers	pat++;
404261081Sasomers    }
405261081Sasomers    if (*host)
406261081Sasomers	strlcpy (res, host,
407261081Sasomers			 sizeof editedhost - (res - editedhost));
408261081Sasomers    else
409261081Sasomers	*res = '\0';
410261081Sasomers    editedhost[sizeof editedhost - 1] = '\0';
411261081Sasomers}
412261081Sasomers
413261081Sasomersstatic char *putlocation;
414261081Sasomers
415261081Sasomersvoid
416270228Sasomersputstr(char *s)
417270228Sasomers{
418261081Sasomers
419261081Sasomers    while (*s)
420261081Sasomers	putchr(*s++);
421261081Sasomers}
422261081Sasomers
423261081Sasomersvoid
424261081Sasomersputchr(int cc)
425261081Sasomers{
426261081Sasomers    *putlocation++ = cc;
427261081Sasomers}
428261081Sasomers
429261081Sasomers/*
430261081Sasomers * This is split on two lines so that SCCS will not see the M
431270228Sasomers * between two % signs and expand it...
432261081Sasomers */
433261081Sasomersstatic char fmtstr[] = { "%l:%M" "%P on %A, %d %B %Y" };
434261081Sasomers
435261081Sasomersvoid putf(char *cp, char *where)
436261081Sasomers{
437261081Sasomers#ifdef HAVE_UNAME
438261081Sasomers    struct utsname name;
439261081Sasomers#endif
440261081Sasomers    char *slash;
441261081Sasomers    time_t t;
442261081Sasomers    char db[100];
443261081Sasomers
444261081Sasomers    /* if we don't have uname, set these to sensible values */
445261081Sasomers    char *sysname = "Unix",
446261081Sasomers	*machine = "",
447261081Sasomers	*release = "",
448261081Sasomers	*version = "";
449261081Sasomers
450261081Sasomers#ifdef HAVE_UNAME
451261081Sasomers    uname(&name);
452270228Sasomers    sysname=name.sysname;
453261081Sasomers    machine=name.machine;
454261081Sasomers    release=name.release;
455261081Sasomers    version=name.version;
456261081Sasomers#endif
457261081Sasomers
458261081Sasomers    putlocation = where;
459261081Sasomers
460261081Sasomers    while (*cp) {
461261081Sasomers	if (*cp != '%') {
462261081Sasomers	    putchr(*cp++);
463261081Sasomers	    continue;
464261081Sasomers	}
465261081Sasomers	switch (*++cp) {
466261081Sasomers
467261081Sasomers	case 't':
468261081Sasomers#ifdef	STREAMSPTY
469261081Sasomers	    /* names are like /dev/pts/2 -- we want pts/2 */
470261081Sasomers	    slash = strchr(line+1, '/');
471261081Sasomers#else
472261081Sasomers	    slash = strrchr(line, '/');
473261081Sasomers#endif
474261081Sasomers	    if (slash == (char *) 0)
475270228Sasomers		putstr(line);
476261081Sasomers	    else
477261081Sasomers		putstr(&slash[1]);
478261081Sasomers	    break;
479261081Sasomers
480261081Sasomers	case 'h':
481261081Sasomers	    putstr(editedhost);
482261081Sasomers	    break;
483261081Sasomers
484261081Sasomers	case 's':
485261081Sasomers	    putstr(sysname);
486261081Sasomers	    break;
487261081Sasomers
488261081Sasomers	case 'm':
489261081Sasomers	    putstr(machine);
490261081Sasomers	    break;
491261081Sasomers
492261081Sasomers	case 'r':
493261081Sasomers	    putstr(release);
494261081Sasomers	    break;
495261081Sasomers
496261081Sasomers	case 'v':
497261081Sasomers	    putstr(version);
498261081Sasomers	    break;
499261081Sasomers
500261081Sasomers	case 'd':
501261081Sasomers	    time(&t);
502261081Sasomers	    strftime(db, sizeof(db), fmtstr, localtime(&t));
503261081Sasomers	    putstr(db);
504261081Sasomers	    break;
505261081Sasomers
506261081Sasomers	case '%':
507270228Sasomers	    putchr('%');
508270228Sasomers	    break;
509261081Sasomers	}
510261081Sasomers	cp++;
511261081Sasomers    }
512261081Sasomers}
513261081Sasomers
514261081Sasomers#ifdef DIAGNOSTICS
515261081Sasomers/*
516261081Sasomers * Print telnet options and commands in plain text, if possible.
517261081Sasomers */
518270228Sasomersvoid
519270228Sasomersprintoption(char *fmt, int option)
520261081Sasomers{
521261081Sasomers    if (TELOPT_OK(option))
522261081Sasomers	output_data("%s %s\r\n",
523261081Sasomers		    fmt,
524261081Sasomers		    TELOPT(option));
525261081Sasomers    else if (TELCMD_OK(option))
526261081Sasomers	output_data("%s %s\r\n",
527261081Sasomers		    fmt,
528261081Sasomers		    TELCMD(option));
529261081Sasomers    else
530261081Sasomers	output_data("%s %d\r\n",
531261081Sasomers		    fmt,
532261081Sasomers		    option);
533261081Sasomers    return;
534261081Sasomers}
535270228Sasomers
536261081Sasomersvoid
537261081Sasomersprintsub(int direction, unsigned char *pointer, int length)
538261081Sasomers        		          	/* '<' or '>' */
539261081Sasomers                 	         	/* where suboption data sits */
540261081Sasomers       			       		/* length of suboption data */
541261081Sasomers{
542261081Sasomers    int i = 0;
543261081Sasomers    unsigned char buf[512];
544261081Sasomers
545261081Sasomers    if (!(diagnostic & TD_OPTIONS))
546261081Sasomers	return;
547261081Sasomers
548261081Sasomers    if (direction) {
549261081Sasomers	output_data("td: %s suboption ",
550261081Sasomers		    direction == '<' ? "recv" : "send");
551261081Sasomers	if (length >= 3) {
552261081Sasomers	    int j;
553261081Sasomers
554261081Sasomers	    i = pointer[length-2];
555261081Sasomers	    j = pointer[length-1];
556261081Sasomers
557261081Sasomers	    if (i != IAC || j != SE) {
558261081Sasomers		output_data("(terminated by ");
559261081Sasomers		if (TELOPT_OK(i))
560261081Sasomers		    output_data("%s ",
561261081Sasomers				TELOPT(i));
562261081Sasomers		else if (TELCMD_OK(i))
563261081Sasomers		    output_data("%s ",
564261081Sasomers				TELCMD(i));
565261081Sasomers		else
566261081Sasomers		    output_data("%d ",
567261081Sasomers				i);
568261081Sasomers		if (TELOPT_OK(j))
569261081Sasomers		    output_data("%s",
570261081Sasomers				TELOPT(j));
571261081Sasomers		else if (TELCMD_OK(j))
572270228Sasomers		    output_data("%s",
573261081Sasomers				TELCMD(j));
574261081Sasomers		else
575261081Sasomers		    output_data("%d",
576261081Sasomers				j);
577261081Sasomers		output_data(", not IAC SE!) ");
578261081Sasomers	    }
579261081Sasomers	}
580261081Sasomers	length -= 2;
581261081Sasomers    }
582261081Sasomers    if (length < 1) {
583261081Sasomers	output_data("(Empty suboption??\?)");
584261081Sasomers	return;
585261081Sasomers    }
586261081Sasomers    switch (pointer[0]) {
587261081Sasomers    case TELOPT_TTYPE:
588261081Sasomers	output_data("TERMINAL-TYPE ");
589261081Sasomers	switch (pointer[1]) {
590261081Sasomers	case TELQUAL_IS:
591261081Sasomers	    output_data("IS \"%.*s\"",
592261081Sasomers			length-2,
593261081Sasomers			(char *)pointer+2);
594261081Sasomers	    break;
595261081Sasomers	case TELQUAL_SEND:
596261081Sasomers	    output_data("SEND");
597261081Sasomers	    break;
598261081Sasomers	default:
599261081Sasomers	    output_data("- unknown qualifier %d (0x%x).",
600261081Sasomers			pointer[1], pointer[1]);
601261081Sasomers	}
602261081Sasomers	break;
603261081Sasomers    case TELOPT_TSPEED:
604261081Sasomers	output_data("TERMINAL-SPEED");
605261081Sasomers	if (length < 2) {
606261081Sasomers	    output_data(" (empty suboption??\?)");
607261081Sasomers	    break;
608261081Sasomers	}
609261081Sasomers	switch (pointer[1]) {
610261081Sasomers	case TELQUAL_IS:
611261081Sasomers	    output_data(" IS %.*s", length-2, (char *)pointer+2);
612261081Sasomers	    break;
613261081Sasomers	default:
614261081Sasomers	    if (pointer[1] == 1)
615261081Sasomers		output_data(" SEND");
616261081Sasomers	    else
617261081Sasomers		output_data(" %d (unknown)", pointer[1]);
618261081Sasomers	    for (i = 2; i < length; i++) {
619261081Sasomers		output_data(" ?%d?", pointer[i]);
620261081Sasomers	    }
621261081Sasomers	    break;
622261081Sasomers	}
623261081Sasomers	break;
624261081Sasomers
625261081Sasomers    case TELOPT_LFLOW:
626261081Sasomers	output_data("TOGGLE-FLOW-CONTROL");
627261081Sasomers	if (length < 2) {
628261081Sasomers	    output_data(" (empty suboption??\?)");
629270228Sasomers	    break;
630270228Sasomers	}
631261081Sasomers	switch (pointer[1]) {
632261081Sasomers	case LFLOW_OFF:
633261081Sasomers	    output_data(" OFF");
634261081Sasomers	    break;
635261081Sasomers	case LFLOW_ON:
636261081Sasomers	    output_data(" ON");
637261081Sasomers	    break;
638261081Sasomers	case LFLOW_RESTART_ANY:
639261081Sasomers	    output_data(" RESTART-ANY");
640261081Sasomers	    break;
641261081Sasomers	case LFLOW_RESTART_XON:
642262894Sasomers	    output_data(" RESTART-XON");
643261081Sasomers	    break;
644261081Sasomers	default:
645261081Sasomers	    output_data(" %d (unknown)",
646261081Sasomers			pointer[1]);
647261081Sasomers	}
648261081Sasomers	for (i = 2; i < length; i++) {
649261081Sasomers	    output_data(" ?%d?",
650261081Sasomers			pointer[i]);
651261081Sasomers	}
652261081Sasomers	break;
653261081Sasomers
654261081Sasomers    case TELOPT_NAWS:
655261081Sasomers	output_data("NAWS");
656261081Sasomers	if (length < 2) {
657261081Sasomers	    output_data(" (empty suboption??\?)");
658261081Sasomers	    break;
659261081Sasomers	}
660270228Sasomers	if (length == 2) {
661270228Sasomers	    output_data(" ?%d?",
662261081Sasomers			pointer[1]);
663261081Sasomers	    break;
664261081Sasomers	}
665261081Sasomers	output_data(" %u %u(%u)",
666261081Sasomers		    pointer[1],
667261081Sasomers		    pointer[2],
668261081Sasomers		    (((unsigned int)pointer[1])<<8) + pointer[2]);
669261081Sasomers	if (length == 4) {
670261081Sasomers	    output_data(" ?%d?",
671261081Sasomers			pointer[3]);
672261081Sasomers	    break;
673261081Sasomers	}
674261081Sasomers	output_data(" %u %u(%u)",
675261081Sasomers		    pointer[3],
676261081Sasomers		    pointer[4],
677261081Sasomers		    (((unsigned int)pointer[3])<<8) + pointer[4]);
678261081Sasomers	for (i = 5; i < length; i++) {
679261081Sasomers	    output_data(" ?%d?",
680262894Sasomers			pointer[i]);
681261081Sasomers	}
682261081Sasomers	break;
683261081Sasomers
684261081Sasomers    case TELOPT_LINEMODE:
685261081Sasomers	output_data("LINEMODE ");
686261081Sasomers	if (length < 2) {
687261081Sasomers	    output_data(" (empty suboption??\?)");
688261081Sasomers	    break;
689261081Sasomers	}
690261081Sasomers	switch (pointer[1]) {
691261081Sasomers	case WILL:
692261081Sasomers	    output_data("WILL ");
693261081Sasomers	    goto common;
694261081Sasomers	case WONT:
695261081Sasomers	    output_data("WONT ");
696261081Sasomers	    goto common;
697261081Sasomers	case DO:
698261081Sasomers	    output_data("DO ");
699261081Sasomers	    goto common;
700261081Sasomers	case DONT:
701261081Sasomers	    output_data("DONT ");
702261081Sasomers	common:
703261081Sasomers	    if (length < 3) {
704261081Sasomers		output_data("(no option??\?)");
705261081Sasomers		break;
706261081Sasomers	    }
707261081Sasomers	    switch (pointer[2]) {
708261081Sasomers	    case LM_FORWARDMASK:
709261081Sasomers		output_data("Forward Mask");
710261081Sasomers		for (i = 3; i < length; i++) {
711261081Sasomers		    output_data(" %x", pointer[i]);
712261081Sasomers		}
713261081Sasomers		break;
714270228Sasomers	    default:
715270228Sasomers		output_data("%d (unknown)",
716261081Sasomers			    pointer[2]);
717261081Sasomers		for (i = 3; i < length; i++) {
718261081Sasomers		    output_data(" %d",
719261081Sasomers				pointer[i]);
720261081Sasomers		}
721261081Sasomers		break;
722261081Sasomers	    }
723261081Sasomers	    break;
724261081Sasomers
725261081Sasomers	case LM_SLC:
726261081Sasomers	    output_data("SLC");
727261081Sasomers	    for (i = 2; i < length - 2; i += 3) {
728261081Sasomers		if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
729262894Sasomers		    output_data(" %s",
730261081Sasomers				SLC_NAME(pointer[i+SLC_FUNC]));
731261081Sasomers		else
732261081Sasomers		    output_data(" %d",
733261081Sasomers				pointer[i+SLC_FUNC]);
734261081Sasomers		switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
735261081Sasomers		case SLC_NOSUPPORT:
736261081Sasomers		    output_data(" NOSUPPORT");
737261081Sasomers		    break;
738261081Sasomers		case SLC_CANTCHANGE:
739261081Sasomers		    output_data(" CANTCHANGE");
740261081Sasomers		    break;
741261081Sasomers		case SLC_VARIABLE:
742261081Sasomers		    output_data(" VARIABLE");
743261081Sasomers		    break;
744261081Sasomers		case SLC_DEFAULT:
745261081Sasomers		    output_data(" DEFAULT");
746270228Sasomers		    break;
747270228Sasomers		}
748261081Sasomers		output_data("%s%s%s",
749261081Sasomers			    pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
750261081Sasomers			    pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
751261081Sasomers			    pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
752261081Sasomers		if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
753261081Sasomers					    SLC_FLUSHOUT| SLC_LEVELBITS)) {
754261081Sasomers		    output_data("(0x%x)",
755270228Sasomers				pointer[i+SLC_FLAGS]);
756261081Sasomers		}
757261081Sasomers		output_data(" %d;",
758261081Sasomers			    pointer[i+SLC_VALUE]);
759270228Sasomers		if ((pointer[i+SLC_VALUE] == IAC) &&
760261081Sasomers		    (pointer[i+SLC_VALUE+1] == IAC))
761261081Sasomers		    i++;
762261081Sasomers	    }
763261081Sasomers	    for (; i < length; i++) {
764261081Sasomers		output_data(" ?%d?",
765270228Sasomers			    pointer[i]);
766261081Sasomers	    }
767261081Sasomers	    break;
768261081Sasomers
769261081Sasomers	case LM_MODE:
770261081Sasomers	    output_data("MODE ");
771261081Sasomers	    if (length < 3) {
772261081Sasomers		output_data("(no mode??\?)");
773270228Sasomers		break;
774261081Sasomers	    }
775261081Sasomers	    {
776261081Sasomers		char tbuf[32];
777270228Sasomers		snprintf(tbuf,
778261081Sasomers			 sizeof(tbuf),
779261081Sasomers			 "%s%s%s%s%s",
780261081Sasomers			 pointer[2]&MODE_EDIT ? "|EDIT" : "",
781261081Sasomers			 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
782270228Sasomers			 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
783261081Sasomers			 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
784261081Sasomers			 pointer[2]&MODE_ACK ? "|ACK" : "");
785261081Sasomers		output_data("%s",
786261081Sasomers			    tbuf[1] ? &tbuf[1] : "0");
787261081Sasomers	    }
788261081Sasomers	    if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
789261081Sasomers		output_data(" (0x%x)",
790261081Sasomers			    pointer[2]);
791261081Sasomers	    }
792261081Sasomers	    for (i = 3; i < length; i++) {
793262894Sasomers		output_data(" ?0x%x?",
794261081Sasomers			 pointer[i]);
795261081Sasomers	    }
796261081Sasomers	    break;
797261081Sasomers	default:
798261081Sasomers	    output_data("%d (unknown)",
799261081Sasomers			pointer[1]);
800261081Sasomers	    for (i = 2; i < length; i++) {
801261081Sasomers		output_data(" %d", pointer[i]);
802261081Sasomers	    }
803261081Sasomers	}
804261081Sasomers	break;
805261081Sasomers
806261081Sasomers    case TELOPT_STATUS: {
807261081Sasomers	char *cp;
808261081Sasomers	int j, k;
809261081Sasomers
810261081Sasomers	output_data("STATUS");
811261081Sasomers
812261081Sasomers	switch (pointer[1]) {
813261081Sasomers	default:
814261081Sasomers	    if (pointer[1] == TELQUAL_SEND)
815261081Sasomers		output_data(" SEND");
816270228Sasomers	    else
817270228Sasomers		output_data(" %d (unknown)",
818261081Sasomers			    pointer[1]);
819261081Sasomers	    for (i = 2; i < length; i++) {
820261081Sasomers		output_data(" ?%d?",
821261081Sasomers			    pointer[i]);
822261081Sasomers	    }
823261081Sasomers	    break;
824261081Sasomers	case TELQUAL_IS:
825261081Sasomers	    output_data(" IS\r\n");
826261081Sasomers
827261081Sasomers	    for (i = 2; i < length; i++) {
828261081Sasomers		switch(pointer[i]) {
829261081Sasomers		case DO:	cp = "DO"; goto common2;
830261081Sasomers		case DONT:	cp = "DONT"; goto common2;
831261081Sasomers		case WILL:	cp = "WILL"; goto common2;
832262894Sasomers		case WONT:	cp = "WONT"; goto common2;
833261081Sasomers		common2:
834261081Sasomers		i++;
835261081Sasomers		if (TELOPT_OK(pointer[i]))
836261081Sasomers		    output_data(" %s %s",
837261081Sasomers				cp,
838261081Sasomers				TELOPT(pointer[i]));
839261081Sasomers		else
840261081Sasomers		    output_data(" %s %d",
841261081Sasomers				cp,
842261081Sasomers				pointer[i]);
843261081Sasomers
844261081Sasomers		output_data("\r\n");
845270228Sasomers		break;
846270228Sasomers
847261081Sasomers		case SB:
848261081Sasomers		    output_data(" SB ");
849261081Sasomers		    i++;
850261081Sasomers		    j = k = i;
851261081Sasomers		    while (j < length) {
852261081Sasomers			if (pointer[j] == SE) {
853261081Sasomers			    if (j+1 == length)
854261081Sasomers				break;
855261081Sasomers			    if (pointer[j+1] == SE)
856261081Sasomers				j++;
857261081Sasomers			    else
858261081Sasomers				break;
859261081Sasomers			}
860261081Sasomers			pointer[k++] = pointer[j++];
861262894Sasomers		    }
862261081Sasomers		    printsub(0, &pointer[i], k - i);
863261081Sasomers		    if (i < length) {
864261081Sasomers			output_data(" SE");
865261081Sasomers			i = j;
866261081Sasomers		    } else
867261081Sasomers			i = j - 1;
868261081Sasomers
869261081Sasomers		    output_data("\r\n");
870261081Sasomers
871261081Sasomers		    break;
872261081Sasomers
873261081Sasomers		default:
874270228Sasomers		    output_data(" %d",
875270228Sasomers				pointer[i]);
876261081Sasomers		    break;
877261081Sasomers		}
878261081Sasomers	    }
879261081Sasomers	    break;
880261081Sasomers	}
881261081Sasomers	break;
882261081Sasomers    }
883261081Sasomers
884261081Sasomers    case TELOPT_XDISPLOC:
885261081Sasomers	output_data("X-DISPLAY-LOCATION ");
886261081Sasomers	switch (pointer[1]) {
887261081Sasomers	case TELQUAL_IS:
888261081Sasomers	    output_data("IS \"%.*s\"",
889261081Sasomers			length-2,
890261081Sasomers			(char *)pointer+2);
891261081Sasomers	    break;
892261081Sasomers	case TELQUAL_SEND:
893261081Sasomers	    output_data("SEND");
894261081Sasomers	    break;
895261081Sasomers	default:
896261081Sasomers	    output_data("- unknown qualifier %d (0x%x).",
897261081Sasomers			pointer[1], pointer[1]);
898261081Sasomers	}
899261081Sasomers	break;
900261081Sasomers
901261081Sasomers    case TELOPT_NEW_ENVIRON:
902261081Sasomers	output_data("NEW-ENVIRON ");
903261081Sasomers	goto env_common1;
904261081Sasomers    case TELOPT_OLD_ENVIRON:
905261081Sasomers	output_data("OLD-ENVIRON");
906261081Sasomers    env_common1:
907261081Sasomers	switch (pointer[1]) {
908261081Sasomers	case TELQUAL_IS:
909261081Sasomers	    output_data("IS ");
910261081Sasomers	    goto env_common;
911261081Sasomers	case TELQUAL_SEND:
912262894Sasomers	    output_data("SEND ");
913261081Sasomers	    goto env_common;
914264133Sjmmv	case TELQUAL_INFO:
915261081Sasomers	    output_data("INFO ");
916261081Sasomers	env_common:
917262894Sasomers	    {
918262894Sasomers		int noquote = 2;
919261081Sasomers		for (i = 2; i < length; i++ ) {
920261081Sasomers		    switch (pointer[i]) {
921261081Sasomers		    case NEW_ENV_VAR:
922261081Sasomers			output_data("\" VAR " + noquote);
923261081Sasomers			noquote = 2;
924261081Sasomers			break;
925262894Sasomers
926262894Sasomers		    case NEW_ENV_VALUE:
927262894Sasomers			output_data("\" VALUE " + noquote);
928262894Sasomers			noquote = 2;
929261081Sasomers			break;
930261081Sasomers
931261081Sasomers		    case ENV_ESC:
932261081Sasomers			output_data("\" ESC " + noquote);
933261081Sasomers			noquote = 2;
934262894Sasomers			break;
935261081Sasomers
936261081Sasomers		    case ENV_USERVAR:
937261081Sasomers			output_data("\" USERVAR " + noquote);
938261081Sasomers			noquote = 2;
939261081Sasomers			break;
940261081Sasomers
941261081Sasomers		    default:
942261081Sasomers			if (isprint(pointer[i]) && pointer[i] != '"') {
943261081Sasomers			    if (noquote) {
944261081Sasomers				output_data ("\"");
945261081Sasomers				noquote = 0;
946261081Sasomers			    }
947261081Sasomers			    output_data ("%c", pointer[i]);
948261081Sasomers			} else {
949261081Sasomers			    output_data("\" %03o " + noquote,
950261081Sasomers					pointer[i]);
951261081Sasomers			    noquote = 2;
952261081Sasomers			}
953261081Sasomers			break;
954261081Sasomers		    }
955261081Sasomers		}
956261081Sasomers		if (!noquote)
957261081Sasomers		    output_data ("\"");
958261081Sasomers		break;
959261081Sasomers	    }
960261081Sasomers	}
961261081Sasomers	break;
962261081Sasomers
963261081Sasomers#ifdef AUTHENTICATION
964261081Sasomers    case TELOPT_AUTHENTICATION:
965261081Sasomers	output_data("AUTHENTICATION");
966270228Sasomers
967270228Sasomers	if (length < 2) {
968261081Sasomers	    output_data(" (empty suboption??\?)");
969261081Sasomers	    break;
970261081Sasomers	}
971261081Sasomers	switch (pointer[1]) {
972261081Sasomers	case TELQUAL_REPLY:
973261081Sasomers	case TELQUAL_IS:
974261081Sasomers	    output_data(" %s ",
975261081Sasomers			(pointer[1] == TELQUAL_IS) ?
976261081Sasomers			"IS" : "REPLY");
977261081Sasomers	    if (AUTHTYPE_NAME_OK(pointer[2]))
978261081Sasomers		output_data("%s ",
979261081Sasomers			    AUTHTYPE_NAME(pointer[2]));
980261081Sasomers	    else
981261081Sasomers		output_data("%d ",
982261081Sasomers			    pointer[2]);
983261081Sasomers	    if (length < 3) {
984261081Sasomers		output_data("(partial suboption??\?)");
985261081Sasomers		break;
986261081Sasomers	    }
987261081Sasomers	    output_data("%s|%s",
988261081Sasomers			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
989261081Sasomers			"CLIENT" : "SERVER",
990261081Sasomers			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
991261081Sasomers			"MUTUAL" : "ONE-WAY");
992261081Sasomers
993261081Sasomers	    auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
994261081Sasomers	    output_data("%s",
995261081Sasomers			buf);
996261081Sasomers	    break;
997261081Sasomers
998261081Sasomers	case TELQUAL_SEND:
999261081Sasomers	    i = 2;
1000261081Sasomers	    output_data(" SEND ");
1001261081Sasomers	    while (i < length) {
1002261081Sasomers		if (AUTHTYPE_NAME_OK(pointer[i]))
1003261081Sasomers		    output_data("%s ",
1004261081Sasomers				AUTHTYPE_NAME(pointer[i]));
1005261081Sasomers		else
1006261081Sasomers		    output_data("%d ",
1007261081Sasomers				pointer[i]);
1008261081Sasomers		if (++i >= length) {
1009261081Sasomers		    output_data("(partial suboption??\?)");
1010261081Sasomers		    break;
1011261081Sasomers		}
1012261081Sasomers		output_data("%s|%s ",
1013261081Sasomers			    ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
1014261081Sasomers			    "CLIENT" : "SERVER",
1015261081Sasomers			    ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
1016261081Sasomers			    "MUTUAL" : "ONE-WAY");
1017261081Sasomers		++i;
1018261081Sasomers	    }
1019261081Sasomers	    break;
1020261081Sasomers
1021261081Sasomers	case TELQUAL_NAME:
1022261081Sasomers	    i = 2;
1023261081Sasomers	    output_data(" NAME \"%.*s\"",
1024261081Sasomers			length - 2,
1025261081Sasomers			pointer);
1026261081Sasomers	    break;
1027261081Sasomers
1028261081Sasomers	default:
1029261081Sasomers	    for (i = 2; i < length; i++) {
1030261081Sasomers		output_data(" ?%d?",
1031261081Sasomers			    pointer[i]);
1032261081Sasomers	    }
1033261081Sasomers	    break;
1034261081Sasomers	}
1035261081Sasomers	break;
1036261081Sasomers#endif
1037261081Sasomers
1038261081Sasomers#ifdef ENCRYPTION
1039261081Sasomers    case TELOPT_ENCRYPT:
1040261081Sasomers	output_data("ENCRYPT");
1041261081Sasomers	if (length < 2) {
1042261081Sasomers	    output_data(" (empty suboption?)");
1043261081Sasomers	    break;
1044261081Sasomers	}
1045261081Sasomers	switch (pointer[1]) {
1046261081Sasomers	case ENCRYPT_START:
1047261081Sasomers	    output_data(" START");
1048261081Sasomers	    break;
1049261081Sasomers
1050261081Sasomers	case ENCRYPT_END:
1051261081Sasomers	    output_data(" END");
1052261081Sasomers	    break;
1053261081Sasomers
1054261081Sasomers	case ENCRYPT_REQSTART:
1055261081Sasomers	    output_data(" REQUEST-START");
1056261081Sasomers	    break;
1057261081Sasomers
1058261081Sasomers	case ENCRYPT_REQEND:
1059261081Sasomers	    output_data(" REQUEST-END");
1060261081Sasomers	    break;
1061261081Sasomers
1062261081Sasomers	case ENCRYPT_IS:
1063261081Sasomers	case ENCRYPT_REPLY:
1064261081Sasomers	    output_data(" %s ",
1065261081Sasomers			(pointer[1] == ENCRYPT_IS) ?
1066261081Sasomers			"IS" : "REPLY");
1067261081Sasomers	    if (length < 3) {
1068261081Sasomers		output_data(" (partial suboption?)");
1069261081Sasomers		break;
1070261081Sasomers	    }
1071261081Sasomers	    if (ENCTYPE_NAME_OK(pointer[2]))
1072261081Sasomers		output_data("%s ",
1073261081Sasomers			    ENCTYPE_NAME(pointer[2]));
1074261081Sasomers	    else
1075261081Sasomers		output_data(" %d (unknown)",
1076261081Sasomers			    pointer[2]);
1077261081Sasomers
1078261081Sasomers	    encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
1079261081Sasomers	    output_data("%s",
1080261081Sasomers			buf);
1081261081Sasomers	    break;
1082261081Sasomers
1083261081Sasomers	case ENCRYPT_SUPPORT:
1084261081Sasomers	    i = 2;
1085261081Sasomers	    output_data(" SUPPORT ");
1086261081Sasomers	    while (i < length) {
1087261081Sasomers		if (ENCTYPE_NAME_OK(pointer[i]))
1088261081Sasomers		    output_data("%s ",
1089261081Sasomers				ENCTYPE_NAME(pointer[i]));
1090261081Sasomers		else
1091261081Sasomers		    output_data("%d ",
1092261081Sasomers				pointer[i]);
1093261081Sasomers		i++;
1094261081Sasomers	    }
1095261081Sasomers	    break;
1096261081Sasomers
1097261081Sasomers	case ENCRYPT_ENC_KEYID:
1098261081Sasomers	    output_data(" ENC_KEYID %d", pointer[1]);
1099261081Sasomers	    goto encommon;
1100261081Sasomers
1101261081Sasomers	case ENCRYPT_DEC_KEYID:
1102261081Sasomers	    output_data(" DEC_KEYID %d", pointer[1]);
1103261081Sasomers	    goto encommon;
1104261081Sasomers
1105261081Sasomers	default:
1106261081Sasomers	    output_data(" %d (unknown)", pointer[1]);
1107261081Sasomers	encommon:
1108261081Sasomers	    for (i = 2; i < length; i++) {
1109261081Sasomers		output_data(" %d", pointer[i]);
1110261081Sasomers	    }
1111261081Sasomers	    break;
1112261081Sasomers	}
1113261081Sasomers	break;
1114261081Sasomers#endif
1115261081Sasomers
1116261081Sasomers    default:
1117261081Sasomers	if (TELOPT_OK(pointer[0]))
1118261081Sasomers	    output_data("%s (unknown)",
1119261081Sasomers			TELOPT(pointer[0]));
1120261081Sasomers	else
1121261081Sasomers	    output_data("%d (unknown)",
1122261081Sasomers			pointer[i]);
1123261081Sasomers	for (i = 1; i < length; i++) {
1124261081Sasomers	    output_data(" %d", pointer[i]);
1125261081Sasomers	}
1126261081Sasomers	break;
1127261081Sasomers    }
1128261081Sasomers    output_data("\r\n");
1129261081Sasomers}
1130261081Sasomers
1131261081Sasomers/*
1132261081Sasomers * Dump a data buffer in hex and ascii to the output data stream.
1133261081Sasomers */
1134261081Sasomersvoid
1135261081Sasomersprintdata(char *tag, char *ptr, int cnt)
1136261081Sasomers{
1137261081Sasomers    int i;
1138261081Sasomers    char xbuf[30];
1139261081Sasomers
1140261081Sasomers    while (cnt) {
1141261081Sasomers	/* flush net output buffer if no room for new data) */
1142	if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
1143	    netflush();
1144	}
1145
1146	/* add a line of output */
1147	output_data("%s: ", tag);
1148	for (i = 0; i < 20 && cnt; i++) {
1149	    output_data("%02x", *ptr);
1150	    if (isprint(*ptr)) {
1151		xbuf[i] = *ptr;
1152	    } else {
1153		xbuf[i] = '.';
1154	    }
1155	    if (i % 2) {
1156		output_data(" ");
1157	    }
1158	    cnt--;
1159	    ptr++;
1160	}
1161	xbuf[i] = '\0';
1162	output_data(" %s\r\n", xbuf);
1163    }
1164}
1165#endif /* DIAGNOSTICS */
1166