utility.c revision 233294
1219019Sgabor/*
2219019Sgabor * Copyright (c) 1989, 1993
3219019Sgabor *	The Regents of the University of California.  All rights reserved.
4219019Sgabor *
5219019Sgabor * Redistribution and use in source and binary forms, with or without
6219019Sgabor * modification, are permitted provided that the following conditions
7219019Sgabor * are met:
8219019Sgabor * 1. Redistributions of source code must retain the above copyright
9219019Sgabor *    notice, this list of conditions and the following disclaimer.
10219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
11219019Sgabor *    notice, this list of conditions and the following disclaimer in the
12219019Sgabor *    documentation and/or other materials provided with the distribution.
13219019Sgabor * 3. All advertising materials mentioning features or use of this software
14219019Sgabor *    must display the following acknowledgement:
15219019Sgabor *	This product includes software developed by the University of
16219019Sgabor *	California, Berkeley and its contributors.
17219019Sgabor * 4. Neither the name of the University nor the names of its contributors
18219019Sgabor *    may be used to endorse or promote products derived from this software
19219019Sgabor *    without specific prior written permission.
20219019Sgabor *
21219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31219019Sgabor * SUCH DAMAGE.
32219019Sgabor */
33219019Sgabor
34219019Sgabor#define PRINTOPTIONS
35219019Sgabor#include "telnetd.h"
36219019Sgabor
37219019SgaborRCSID("$Id$");
38219019Sgabor
39219019Sgabor/*
40219019Sgabor * utility functions performing io related tasks
41219019Sgabor */
42219019Sgabor
43219019Sgabor/*
44219019Sgabor * ttloop
45219019Sgabor *
46219019Sgabor * A small subroutine to flush the network output buffer, get some
47219019Sgabor * data from the network, and pass it through the telnet state
48219019Sgabor * machine.  We also flush the pty input buffer (by dropping its data)
49219019Sgabor * if it becomes too full.
50219019Sgabor *
51219019Sgabor * return 0 if OK or 1 if interrupted by a signal.
52219019Sgabor */
53219019Sgabor
54219019Sgaborint
55219019Sgaborttloop(void)
56219019Sgabor{
57219019Sgabor    DIAG(TD_REPORT, {
58219019Sgabor	output_data("td: ttloop\r\n");
59219019Sgabor    });
60219019Sgabor    if (nfrontp-nbackp)
61219019Sgabor	netflush();
62219019Sgabor    ncc = read(net, netibuf, sizeof netibuf);
63219019Sgabor    if (ncc < 0) {
64219019Sgabor	if (errno == EINTR)
65219019Sgabor	    return 1;
66219019Sgabor	syslog(LOG_INFO, "ttloop:  read: %m\n");
67219019Sgabor	exit(1);
68219019Sgabor    } else if (ncc == 0) {
69219019Sgabor	syslog(LOG_INFO, "ttloop:  peer died\n");
70219019Sgabor	exit(1);
71219019Sgabor    }
72219019Sgabor    DIAG(TD_REPORT, {
73219019Sgabor	output_data("td: ttloop read %d chars\r\n", ncc);
74219019Sgabor    });
75219019Sgabor    netip = netibuf;
76219019Sgabor    telrcv();			/* state machine */
77219019Sgabor    if (ncc > 0) {
78219019Sgabor	pfrontp = pbackp = ptyobuf;
79219019Sgabor	telrcv();
80219019Sgabor    }
81219019Sgabor    return 0;
82219019Sgabor}  /* end of ttloop */
83219019Sgabor
84219019Sgabor/*
85219019Sgabor * Check a descriptor to see if out of band data exists on it.
86219019Sgabor */
87219019Sgaborint
88219019Sgaborstilloob(int s)
89219019Sgabor{
90219019Sgabor    static struct timeval timeout = { 0 };
91219019Sgabor    fd_set	excepts;
92219019Sgabor    int value;
93219019Sgabor
94219019Sgabor    if (s >= FD_SETSIZE)
95219019Sgabor	fatal(ourpty, "fd too large");
96219019Sgabor
97219019Sgabor    do {
98219019Sgabor	FD_ZERO(&excepts);
99219019Sgabor	FD_SET(s, &excepts);
100219019Sgabor	value = select(s+1, 0, 0, &excepts, &timeout);
101219019Sgabor    } while ((value == -1) && (errno == EINTR));
102219019Sgabor
103219019Sgabor    if (value < 0) {
104219019Sgabor	fatalperror(ourpty, "select");
105219019Sgabor    }
106219019Sgabor    if (FD_ISSET(s, &excepts)) {
107219019Sgabor	return 1;
108219019Sgabor    } else {
109219019Sgabor	return 0;
110219019Sgabor    }
111219019Sgabor}
112219019Sgabor
113219019Sgaborvoid
114219019Sgaborptyflush(void)
115219019Sgabor{
116219019Sgabor    int n;
117219019Sgabor
118219019Sgabor    if ((n = pfrontp - pbackp) > 0) {
119219019Sgabor	DIAG((TD_REPORT | TD_PTYDATA), {
120219019Sgabor	    output_data("td: ptyflush %d chars\r\n", n);
121219019Sgabor	});
122219019Sgabor	DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
123219019Sgabor	n = write(ourpty, pbackp, n);
124219019Sgabor    }
125219019Sgabor    if (n < 0) {
126219019Sgabor	if (errno == EWOULDBLOCK || errno == EINTR)
127219019Sgabor	    return;
128219019Sgabor	cleanup(0);
129219019Sgabor    }
130219019Sgabor    pbackp += n;
131219019Sgabor    if (pbackp == pfrontp)
132219019Sgabor	pbackp = pfrontp = ptyobuf;
133219019Sgabor}
134219019Sgabor
135219019Sgabor/*
136219019Sgabor * nextitem()
137219019Sgabor *
138219019Sgabor *	Return the address of the next "item" in the TELNET data
139219019Sgabor * stream.  This will be the address of the next character if
140219019Sgabor * the current address is a user data character, or it will
141219019Sgabor * be the address of the character following the TELNET command
142219019Sgabor * if the current address is a TELNET IAC ("I Am a Command")
143219019Sgabor * character.
144219019Sgabor */
145219019Sgaborchar *
146219019Sgabornextitem(char *current)
147219019Sgabor{
148219019Sgabor    if ((*current&0xff) != IAC) {
149219019Sgabor	return current+1;
150219019Sgabor    }
151219019Sgabor    switch (*(current+1)&0xff) {
152219019Sgabor    case DO:
153219019Sgabor    case DONT:
154219019Sgabor    case WILL:
155219019Sgabor    case WONT:
156219019Sgabor	return current+3;
157219019Sgabor    case SB:{
158219019Sgabor	/* loop forever looking for the SE */
159260264Sdim	char *look = current+2;
160219019Sgabor
161219019Sgabor	for (;;) {
162219019Sgabor	    if ((*look++&0xff) == IAC) {
163219019Sgabor		if ((*look++&0xff) == SE) {
164219019Sgabor		    return look;
165219019Sgabor		}
166219019Sgabor	    }
167219019Sgabor	}
168219019Sgabor    }
169219019Sgabor    default:
170219019Sgabor	return current+2;
171219019Sgabor    }
172219019Sgabor}
173219019Sgabor
174219019Sgabor
175219019Sgabor/*
176219019Sgabor * netclear()
177260264Sdim *
178219019Sgabor *	We are about to do a TELNET SYNCH operation.  Clear
179219019Sgabor * the path to the network.
180282275Stijl *
181219019Sgabor *	Things are a bit tricky since we may have sent the first
182219019Sgabor * byte or so of a previous TELNET command into the network.
183282275Stijl * So, we have to scan the network buffer from the beginning
184219019Sgabor * until we are up to where we want to be.
185219019Sgabor *
186219019Sgabor *	A side effect of what we do, just to keep things
187219019Sgabor * simple, is to clear the urgent data pointer.  The principal
188219019Sgabor * caller should be setting the urgent data pointer AFTER calling
189219019Sgabor * us in any case.
190219019Sgabor */
191219019Sgaborvoid
192219019Sgabornetclear(void)
193219019Sgabor{
194219019Sgabor    char *thisitem, *next;
195219019Sgabor    char *good;
196219019Sgabor#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
197219019Sgabor			 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
198219019Sgabor
199219019Sgabor#ifdef ENCRYPTION
200219019Sgabor	thisitem = nclearto > netobuf ? nclearto : netobuf;
201219019Sgabor#else
202219019Sgabor	thisitem = netobuf;
203219019Sgabor#endif
204219019Sgabor
205219019Sgabor	while ((next = nextitem(thisitem)) <= nbackp) {
206219019Sgabor	    thisitem = next;
207219019Sgabor	}
208219019Sgabor
209219019Sgabor	/* Now, thisitem is first before/at boundary. */
210219019Sgabor
211219019Sgabor#ifdef ENCRYPTION
212219019Sgabor	good = nclearto > netobuf ? nclearto : netobuf;
213219019Sgabor#else
214219019Sgabor	good = netobuf;	/* where the good bytes go */
215219019Sgabor#endif
216219019Sgabor
217219019Sgabor	while (nfrontp > thisitem) {
218219019Sgabor	    if (wewant(thisitem)) {
219219019Sgabor		int length;
220219019Sgabor
221219019Sgabor		next = thisitem;
222219019Sgabor		do {
223219019Sgabor		    next = nextitem(next);
224219019Sgabor		} while (wewant(next) && (nfrontp > next));
225219019Sgabor		length = next-thisitem;
226219019Sgabor		memmove(good, thisitem, length);
227219019Sgabor		good += length;
228219019Sgabor		thisitem = next;
229219019Sgabor	    } else {
230219019Sgabor		thisitem = nextitem(thisitem);
231219019Sgabor	    }
232219019Sgabor	}
233219019Sgabor
234219019Sgabor	nbackp = netobuf;
235219019Sgabor	nfrontp = good;		/* next byte to be sent */
236219019Sgabor	neturg = 0;
237219019Sgabor}  /* end of netclear */
238219019Sgabor
239219019Sgaborextern int not42;
240219019Sgabor
241219019Sgabor/*
242219019Sgabor *  netflush
243219019Sgabor *		Send as much data as possible to the network,
244219019Sgabor *	handling requests for urgent data.
245219019Sgabor */
246219019Sgaborvoid
247219019Sgabornetflush(void)
248219019Sgabor{
249219019Sgabor    int n;
250219019Sgabor
251219019Sgabor    if ((n = nfrontp - nbackp) > 0) {
252219019Sgabor	DIAG(TD_REPORT,
253219019Sgabor	     { n += output_data("td: netflush %d chars\r\n", n);
254219019Sgabor	     });
255219019Sgabor#ifdef ENCRYPTION
256219019Sgabor	if (encrypt_output) {
257219019Sgabor	    char *s = nclearto ? nclearto : nbackp;
258219019Sgabor	    if (nfrontp - s > 0) {
259219019Sgabor		(*encrypt_output)((unsigned char *)s, nfrontp-s);
260219019Sgabor		nclearto = nfrontp;
261219019Sgabor	    }
262219019Sgabor	}
263219019Sgabor#endif
264219019Sgabor	/*
265219019Sgabor	 * if no urgent data, or if the other side appears to be an
266219019Sgabor	 * old 4.2 client (and thus unable to survive TCP urgent data),
267219019Sgabor	 * write the entire buffer in non-OOB mode.
268219019Sgabor	 */
269219019Sgabor#if 1 /* remove this to make it work between solaris 2.6 and linux */
270219019Sgabor	if ((neturg == 0) || (not42 == 0)) {
271219019Sgabor#endif
272219019Sgabor	    n = write(net, nbackp, n);	/* normal write */
273219019Sgabor#if 1 /* remove this to make it work between solaris 2.6 and linux */
274219019Sgabor	} else {
275219019Sgabor	    n = neturg - nbackp;
276219019Sgabor	    /*
277219019Sgabor	     * In 4.2 (and 4.3) systems, there is some question about
278219019Sgabor	     * what byte in a sendOOB operation is the "OOB" data.
279219019Sgabor	     * To make ourselves compatible, we only send ONE byte
280219019Sgabor	     * out of band, the one WE THINK should be OOB (though
281219019Sgabor	     * we really have more the TCP philosophy of urgent data
282219019Sgabor	     * rather than the Unix philosophy of OOB data).
283219019Sgabor	     */
284219019Sgabor	    if (n > 1) {
285219019Sgabor		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
286219019Sgabor	    } else {
287219019Sgabor		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
288219019Sgabor	    }
289219019Sgabor	}
290219019Sgabor#endif
291219019Sgabor    }
292219019Sgabor    if (n < 0) {
293219019Sgabor	if (errno == EWOULDBLOCK || errno == EINTR)
294219019Sgabor	    return;
295219019Sgabor	cleanup(0);
296219019Sgabor    }
297219019Sgabor    nbackp += n;
298219019Sgabor#ifdef ENCRYPTION
299219019Sgabor    if (nbackp > nclearto)
300219019Sgabor	nclearto = 0;
301219019Sgabor#endif
302219019Sgabor    if (nbackp >= neturg) {
303219019Sgabor	neturg = 0;
304219019Sgabor    }
305219019Sgabor    if (nbackp == nfrontp) {
306219019Sgabor	nbackp = nfrontp = netobuf;
307219019Sgabor#ifdef ENCRYPTION
308219019Sgabor	nclearto = 0;
309219019Sgabor#endif
310219019Sgabor    }
311219019Sgabor    return;
312219019Sgabor}
313219019Sgabor
314219019Sgabor
315219019Sgabor/*
316219019Sgabor * writenet
317219019Sgabor *
318219019Sgabor * Just a handy little function to write a bit of raw data to the net.
319219019Sgabor * It will force a transmit of the buffer if necessary
320219019Sgabor *
321219019Sgabor * arguments
322219019Sgabor *    ptr - A pointer to a character string to write
323219019Sgabor *    len - How many bytes to write
324219019Sgabor */
325219019Sgaborvoid
326219019Sgaborwritenet(const void *ptr, size_t len)
327219019Sgabor{
328219019Sgabor    /* flush buffer if no room for new data) */
329219019Sgabor    while ((&netobuf[BUFSIZ] - nfrontp) < len) {
330219019Sgabor	/* if this fails, don't worry, buffer is a little big */
331219019Sgabor	netflush();
332219019Sgabor    }
333219019Sgabor    if ((&netobuf[BUFSIZ] - nfrontp) < len)
334219019Sgabor	abort();
335219019Sgabor
336219019Sgabor    memmove(nfrontp, ptr, len);
337219019Sgabor    nfrontp += len;
338219019Sgabor}
339219019Sgabor
340219019Sgabor
341219019Sgabor/*
342219019Sgabor * miscellaneous functions doing a variety of little jobs follow ...
343219019Sgabor */
344219019Sgabor
345219019Sgabor
346219019Sgaborvoid fatal(int f, char *msg)
347219019Sgabor{
348219019Sgabor    char buf[BUFSIZ];
349219019Sgabor
350219019Sgabor    snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
351219019Sgabor#ifdef ENCRYPTION
352    if (encrypt_output) {
353	/*
354	 * Better turn off encryption first....
355	 * Hope it flushes...
356	 */
357	encrypt_send_end();
358	netflush();
359    }
360#endif
361    write(f, buf, (int)strlen(buf));
362    sleep(1);	/*XXX*/
363    exit(1);
364}
365
366void
367fatalperror_errno(int f, const char *msg, int error)
368{
369    char buf[BUFSIZ];
370
371    snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(error));
372    fatal(f, buf);
373}
374
375void
376fatalperror(int f, const char *msg)
377{
378    fatalperror_errno(f, msg, errno);
379}
380
381char editedhost[32];
382
383void edithost(char *pat, char *host)
384{
385    char *res = editedhost;
386
387    if (!pat)
388	pat = "";
389    while (*pat) {
390	switch (*pat) {
391
392	case '#':
393	    if (*host)
394		host++;
395	    break;
396
397	case '@':
398	    if (*host)
399		*res++ = *host++;
400	    break;
401
402	default:
403	    *res++ = *pat;
404	    break;
405	}
406	if (res == &editedhost[sizeof editedhost - 1]) {
407	    *res = '\0';
408	    return;
409	}
410	pat++;
411    }
412    if (*host)
413	strlcpy (res, host,
414			 sizeof editedhost - (res - editedhost));
415    else
416	*res = '\0';
417    editedhost[sizeof editedhost - 1] = '\0';
418}
419
420static char *putlocation;
421
422void
423putstr(char *s)
424{
425
426    while (*s)
427	putchr(*s++);
428}
429
430void
431putchr(int cc)
432{
433    *putlocation++ = cc;
434}
435
436static char fmtstr[] = { "%l:%M%P on %A, %d %B %Y" };
437
438void putf(char *cp, char *where)
439{
440#ifdef HAVE_UNAME
441    struct utsname name;
442#endif
443    char *slash;
444    time_t t;
445    char db[100];
446
447    /* if we don't have uname, set these to sensible values */
448    char *sysname = "Unix",
449	*machine = "",
450	*release = "",
451	*version = "";
452
453#ifdef HAVE_UNAME
454    uname(&name);
455    sysname=name.sysname;
456    machine=name.machine;
457    release=name.release;
458    version=name.version;
459#endif
460
461    putlocation = where;
462
463    while (*cp) {
464	if (*cp != '%') {
465	    putchr(*cp++);
466	    continue;
467	}
468	switch (*++cp) {
469
470	case 't':
471	    slash = strchr(line+1, '/');
472	    if (slash == (char *) 0)
473		putstr(line);
474	    else
475		putstr(&slash[1]);
476	    break;
477
478	case 'h':
479	    putstr(editedhost);
480	    break;
481
482	case 's':
483	    putstr(sysname);
484	    break;
485
486	case 'm':
487	    putstr(machine);
488	    break;
489
490	case 'r':
491	    putstr(release);
492	    break;
493
494	case 'v':
495	    putstr(version);
496	    break;
497
498	case 'd':
499	    time(&t);
500	    strftime(db, sizeof(db), fmtstr, localtime(&t));
501	    putstr(db);
502	    break;
503
504	case '%':
505	    putchr('%');
506	    break;
507	}
508	cp++;
509    }
510}
511
512#ifdef DIAGNOSTICS
513/*
514 * Print telnet options and commands in plain text, if possible.
515 */
516void
517printoption(char *fmt, int option)
518{
519    if (TELOPT_OK(option))
520	output_data("%s %s\r\n",
521		    fmt,
522		    TELOPT(option));
523    else if (TELCMD_OK(option))
524	output_data("%s %s\r\n",
525		    fmt,
526		    TELCMD(option));
527    else
528	output_data("%s %d\r\n",
529		    fmt,
530		    option);
531    return;
532}
533
534void
535printsub(int direction, unsigned char *pointer, size_t length)
536        		          	/* '<' or '>' */
537                 	         	/* where suboption data sits */
538       			       		/* length of suboption data */
539{
540    int i = 0;
541    unsigned char buf[512];
542
543    if (!(diagnostic & TD_OPTIONS))
544	return;
545
546    if (direction) {
547	output_data("td: %s suboption ",
548		    direction == '<' ? "recv" : "send");
549	if (length >= 3) {
550	    int j;
551
552	    i = pointer[length-2];
553	    j = pointer[length-1];
554
555	    if (i != IAC || j != SE) {
556		output_data("(terminated by ");
557		if (TELOPT_OK(i))
558		    output_data("%s ",
559				TELOPT(i));
560		else if (TELCMD_OK(i))
561		    output_data("%s ",
562				TELCMD(i));
563		else
564		    output_data("%d ",
565				i);
566		if (TELOPT_OK(j))
567		    output_data("%s",
568				TELOPT(j));
569		else if (TELCMD_OK(j))
570		    output_data("%s",
571				TELCMD(j));
572		else
573		    output_data("%d",
574				j);
575		output_data(", not IAC SE!) ");
576	    }
577	}
578	length -= 2;
579    }
580    if (length < 1) {
581	output_data("(Empty suboption??\?)");
582	return;
583    }
584    switch (pointer[0]) {
585    case TELOPT_TTYPE:
586	output_data("TERMINAL-TYPE ");
587	switch (pointer[1]) {
588	case TELQUAL_IS:
589	    output_data("IS \"%.*s\"",
590			(int)(length-2),
591			(char *)pointer+2);
592	    break;
593	case TELQUAL_SEND:
594	    output_data("SEND");
595	    break;
596	default:
597	    output_data("- unknown qualifier %d (0x%x).",
598			pointer[1], pointer[1]);
599	}
600	break;
601    case TELOPT_TSPEED:
602	output_data("TERMINAL-SPEED");
603	if (length < 2) {
604	    output_data(" (empty suboption??\?)");
605	    break;
606	}
607	switch (pointer[1]) {
608	case TELQUAL_IS:
609	    output_data(" IS %.*s", (int)(length-2), (char *)pointer+2);
610	    break;
611	default:
612	    if (pointer[1] == 1)
613		output_data(" SEND");
614	    else
615		output_data(" %d (unknown)", pointer[1]);
616	    for (i = 2; i < length; i++) {
617		output_data(" ?%d?", pointer[i]);
618	    }
619	    break;
620	}
621	break;
622
623    case TELOPT_LFLOW:
624	output_data("TOGGLE-FLOW-CONTROL");
625	if (length < 2) {
626	    output_data(" (empty suboption??\?)");
627	    break;
628	}
629	switch (pointer[1]) {
630	case LFLOW_OFF:
631	    output_data(" OFF");
632	    break;
633	case LFLOW_ON:
634	    output_data(" ON");
635	    break;
636	case LFLOW_RESTART_ANY:
637	    output_data(" RESTART-ANY");
638	    break;
639	case LFLOW_RESTART_XON:
640	    output_data(" RESTART-XON");
641	    break;
642	default:
643	    output_data(" %d (unknown)",
644			pointer[1]);
645	}
646	for (i = 2; i < length; i++) {
647	    output_data(" ?%d?",
648			pointer[i]);
649	}
650	break;
651
652    case TELOPT_NAWS:
653	output_data("NAWS");
654	if (length < 2) {
655	    output_data(" (empty suboption??\?)");
656	    break;
657	}
658	if (length == 2) {
659	    output_data(" ?%d?",
660			pointer[1]);
661	    break;
662	}
663	output_data(" %u %u(%u)",
664		    pointer[1],
665		    pointer[2],
666		    (((unsigned int)pointer[1])<<8) + pointer[2]);
667	if (length == 4) {
668	    output_data(" ?%d?",
669			pointer[3]);
670	    break;
671	}
672	output_data(" %u %u(%u)",
673		    pointer[3],
674		    pointer[4],
675		    (((unsigned int)pointer[3])<<8) + pointer[4]);
676	for (i = 5; i < length; i++) {
677	    output_data(" ?%d?",
678			pointer[i]);
679	}
680	break;
681
682    case TELOPT_LINEMODE:
683	output_data("LINEMODE ");
684	if (length < 2) {
685	    output_data(" (empty suboption??\?)");
686	    break;
687	}
688	switch (pointer[1]) {
689	case WILL:
690	    output_data("WILL ");
691	    goto common;
692	case WONT:
693	    output_data("WONT ");
694	    goto common;
695	case DO:
696	    output_data("DO ");
697	    goto common;
698	case DONT:
699	    output_data("DONT ");
700	common:
701	    if (length < 3) {
702		output_data("(no option??\?)");
703		break;
704	    }
705	    switch (pointer[2]) {
706	    case LM_FORWARDMASK:
707		output_data("Forward Mask");
708		for (i = 3; i < length; i++) {
709		    output_data(" %x", pointer[i]);
710		}
711		break;
712	    default:
713		output_data("%d (unknown)",
714			    pointer[2]);
715		for (i = 3; i < length; i++) {
716		    output_data(" %d",
717				pointer[i]);
718		}
719		break;
720	    }
721	    break;
722
723	case LM_SLC:
724	    output_data("SLC");
725	    for (i = 2; i < length - 2; i += 3) {
726		if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
727		    output_data(" %s",
728				SLC_NAME(pointer[i+SLC_FUNC]));
729		else
730		    output_data(" %d",
731				pointer[i+SLC_FUNC]);
732		switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
733		case SLC_NOSUPPORT:
734		    output_data(" NOSUPPORT");
735		    break;
736		case SLC_CANTCHANGE:
737		    output_data(" CANTCHANGE");
738		    break;
739		case SLC_VARIABLE:
740		    output_data(" VARIABLE");
741		    break;
742		case SLC_DEFAULT:
743		    output_data(" DEFAULT");
744		    break;
745		}
746		output_data("%s%s%s",
747			    pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
748			    pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
749			    pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
750		if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
751					    SLC_FLUSHOUT| SLC_LEVELBITS)) {
752		    output_data("(0x%x)",
753				pointer[i+SLC_FLAGS]);
754		}
755		output_data(" %d;",
756			    pointer[i+SLC_VALUE]);
757		if ((pointer[i+SLC_VALUE] == IAC) &&
758		    (pointer[i+SLC_VALUE+1] == IAC))
759		    i++;
760	    }
761	    for (; i < length; i++) {
762		output_data(" ?%d?",
763			    pointer[i]);
764	    }
765	    break;
766
767	case LM_MODE:
768	    output_data("MODE ");
769	    if (length < 3) {
770		output_data("(no mode??\?)");
771		break;
772	    }
773	    {
774		char tbuf[32];
775		snprintf(tbuf,
776			 sizeof(tbuf),
777			 "%s%s%s%s%s",
778			 pointer[2]&MODE_EDIT ? "|EDIT" : "",
779			 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
780			 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
781			 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
782			 pointer[2]&MODE_ACK ? "|ACK" : "");
783		output_data("%s",
784			    tbuf[1] ? &tbuf[1] : "0");
785	    }
786	    if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
787		output_data(" (0x%x)",
788			    pointer[2]);
789	    }
790	    for (i = 3; i < length; i++) {
791		output_data(" ?0x%x?",
792			 pointer[i]);
793	    }
794	    break;
795	default:
796	    output_data("%d (unknown)",
797			pointer[1]);
798	    for (i = 2; i < length; i++) {
799		output_data(" %d", pointer[i]);
800	    }
801	}
802	break;
803
804    case TELOPT_STATUS: {
805	char *cp;
806	int j, k;
807
808	output_data("STATUS");
809
810	switch (pointer[1]) {
811	default:
812	    if (pointer[1] == TELQUAL_SEND)
813		output_data(" SEND");
814	    else
815		output_data(" %d (unknown)",
816			    pointer[1]);
817	    for (i = 2; i < length; i++) {
818		output_data(" ?%d?",
819			    pointer[i]);
820	    }
821	    break;
822	case TELQUAL_IS:
823	    output_data(" IS\r\n");
824
825	    for (i = 2; i < length; i++) {
826		switch(pointer[i]) {
827		case DO:	cp = "DO"; goto common2;
828		case DONT:	cp = "DONT"; goto common2;
829		case WILL:	cp = "WILL"; goto common2;
830		case WONT:	cp = "WONT"; goto common2;
831		common2:
832		i++;
833		if (TELOPT_OK(pointer[i]))
834		    output_data(" %s %s",
835				cp,
836				TELOPT(pointer[i]));
837		else
838		    output_data(" %s %d",
839				cp,
840				pointer[i]);
841
842		output_data("\r\n");
843		break;
844
845		case SB:
846		    output_data(" SB ");
847		    i++;
848		    j = k = i;
849		    while (j < length) {
850			if (pointer[j] == SE) {
851			    if (j+1 == length)
852				break;
853			    if (pointer[j+1] == SE)
854				j++;
855			    else
856				break;
857			}
858			pointer[k++] = pointer[j++];
859		    }
860		    printsub(0, &pointer[i], k - i);
861		    if (i < length) {
862			output_data(" SE");
863			i = j;
864		    } else
865			i = j - 1;
866
867		    output_data("\r\n");
868
869		    break;
870
871		default:
872		    output_data(" %d",
873				pointer[i]);
874		    break;
875		}
876	    }
877	    break;
878	}
879	break;
880    }
881
882    case TELOPT_XDISPLOC:
883	output_data("X-DISPLAY-LOCATION ");
884	switch (pointer[1]) {
885	case TELQUAL_IS:
886	    output_data("IS \"%.*s\"",
887			(int)(length-2),
888			(char *)pointer+2);
889	    break;
890	case TELQUAL_SEND:
891	    output_data("SEND");
892	    break;
893	default:
894	    output_data("- unknown qualifier %d (0x%x).",
895			pointer[1], pointer[1]);
896	}
897	break;
898
899    case TELOPT_NEW_ENVIRON:
900	output_data("NEW-ENVIRON ");
901	goto env_common1;
902    case TELOPT_OLD_ENVIRON:
903	output_data("OLD-ENVIRON");
904    env_common1:
905	switch (pointer[1]) {
906	case TELQUAL_IS:
907	    output_data("IS ");
908	    goto env_common;
909	case TELQUAL_SEND:
910	    output_data("SEND ");
911	    goto env_common;
912	case TELQUAL_INFO:
913	    output_data("INFO ");
914	env_common:
915	    {
916		int quote = 0;
917		for (i = 2; i < length; i++ ) {
918		    switch (pointer[i]) {
919		    case NEW_ENV_VAR:
920			if (quote)
921			    output_data("\" ");
922			output_data("VAR ");
923			quote = 0;
924			break;
925
926		    case NEW_ENV_VALUE:
927			if (quote)
928			    output_data("\" ");
929			output_data("VALUE ");
930			quote = 0;
931			break;
932
933		    case ENV_ESC:
934			if (quote)
935			    output_data("\" ");
936			output_data("ESC ");
937			quote = 0;
938			break;
939
940		    case ENV_USERVAR:
941			if (quote)
942			    output_data("\" ");
943			output_data("USERVAR ");
944			quote = 0;
945			break;
946
947		    default:
948			if (isprint(pointer[i]) && pointer[i] != '"') {
949			    if (!quote) {
950				output_data("\"");
951				quote = 1;
952			    }
953			    output_data("%c", pointer[i]);
954			} else {
955			    output_data("%03o ", pointer[i]);
956			    quote = 0;
957			}
958			break;
959		    }
960		}
961		if (quote)
962		    output_data("\"");
963		break;
964	    }
965	}
966	break;
967
968#ifdef AUTHENTICATION
969    case TELOPT_AUTHENTICATION:
970	output_data("AUTHENTICATION");
971
972	if (length < 2) {
973	    output_data(" (empty suboption??\?)");
974	    break;
975	}
976	switch (pointer[1]) {
977	case TELQUAL_REPLY:
978	case TELQUAL_IS:
979	    output_data(" %s ",
980			(pointer[1] == TELQUAL_IS) ?
981			"IS" : "REPLY");
982	    if (AUTHTYPE_NAME_OK(pointer[2]))
983		output_data("%s ",
984			    AUTHTYPE_NAME(pointer[2]));
985	    else
986		output_data("%d ",
987			    pointer[2]);
988	    if (length < 3) {
989		output_data("(partial suboption??\?)");
990		break;
991	    }
992	    output_data("%s|%s",
993			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
994			"CLIENT" : "SERVER",
995			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
996			"MUTUAL" : "ONE-WAY");
997
998	    auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
999	    output_data("%s",
1000			buf);
1001	    break;
1002
1003	case TELQUAL_SEND:
1004	    i = 2;
1005	    output_data(" SEND ");
1006	    while (i < length) {
1007		if (AUTHTYPE_NAME_OK(pointer[i]))
1008		    output_data("%s ",
1009				AUTHTYPE_NAME(pointer[i]));
1010		else
1011		    output_data("%d ",
1012				pointer[i]);
1013		if (++i >= length) {
1014		    output_data("(partial suboption??\?)");
1015		    break;
1016		}
1017		output_data("%s|%s ",
1018			    ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
1019			    "CLIENT" : "SERVER",
1020			    ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
1021			    "MUTUAL" : "ONE-WAY");
1022		++i;
1023	    }
1024	    break;
1025
1026	case TELQUAL_NAME:
1027	    i = 2;
1028	    output_data(" NAME \"%.*s\"",
1029			(int)(length - 2),
1030			pointer);
1031	    break;
1032
1033	default:
1034	    for (i = 2; i < length; i++) {
1035		output_data(" ?%d?",
1036			    pointer[i]);
1037	    }
1038	    break;
1039	}
1040	break;
1041#endif
1042
1043#ifdef ENCRYPTION
1044    case TELOPT_ENCRYPT:
1045	output_data("ENCRYPT");
1046	if (length < 2) {
1047	    output_data(" (empty suboption?)");
1048	    break;
1049	}
1050	switch (pointer[1]) {
1051	case ENCRYPT_START:
1052	    output_data(" START");
1053	    break;
1054
1055	case ENCRYPT_END:
1056	    output_data(" END");
1057	    break;
1058
1059	case ENCRYPT_REQSTART:
1060	    output_data(" REQUEST-START");
1061	    break;
1062
1063	case ENCRYPT_REQEND:
1064	    output_data(" REQUEST-END");
1065	    break;
1066
1067	case ENCRYPT_IS:
1068	case ENCRYPT_REPLY:
1069	    output_data(" %s ",
1070			(pointer[1] == ENCRYPT_IS) ?
1071			"IS" : "REPLY");
1072	    if (length < 3) {
1073		output_data(" (partial suboption?)");
1074		break;
1075	    }
1076	    if (ENCTYPE_NAME_OK(pointer[2]))
1077		output_data("%s ",
1078			    ENCTYPE_NAME(pointer[2]));
1079	    else
1080		output_data(" %d (unknown)",
1081			    pointer[2]);
1082
1083	    encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
1084	    output_data("%s",
1085			buf);
1086	    break;
1087
1088	case ENCRYPT_SUPPORT:
1089	    i = 2;
1090	    output_data(" SUPPORT ");
1091	    while (i < length) {
1092		if (ENCTYPE_NAME_OK(pointer[i]))
1093		    output_data("%s ",
1094				ENCTYPE_NAME(pointer[i]));
1095		else
1096		    output_data("%d ",
1097				pointer[i]);
1098		i++;
1099	    }
1100	    break;
1101
1102	case ENCRYPT_ENC_KEYID:
1103	    output_data(" ENC_KEYID %d", pointer[1]);
1104	    goto encommon;
1105
1106	case ENCRYPT_DEC_KEYID:
1107	    output_data(" DEC_KEYID %d", pointer[1]);
1108	    goto encommon;
1109
1110	default:
1111	    output_data(" %d (unknown)", pointer[1]);
1112	encommon:
1113	    for (i = 2; i < length; i++) {
1114		output_data(" %d", pointer[i]);
1115	    }
1116	    break;
1117	}
1118	break;
1119#endif
1120
1121    default:
1122	if (TELOPT_OK(pointer[0]))
1123	    output_data("%s (unknown)",
1124			TELOPT(pointer[0]));
1125	else
1126	    output_data("%d (unknown)",
1127			pointer[i]);
1128	for (i = 1; i < length; i++) {
1129	    output_data(" %d", pointer[i]);
1130	}
1131	break;
1132    }
1133    output_data("\r\n");
1134}
1135
1136/*
1137 * Dump a data buffer in hex and ascii to the output data stream.
1138 */
1139void
1140printdata(char *tag, char *ptr, size_t cnt)
1141{
1142    size_t i;
1143    char xbuf[30];
1144
1145    while (cnt) {
1146	/* flush net output buffer if no room for new data) */
1147	if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
1148	    netflush();
1149	}
1150
1151	/* add a line of output */
1152	output_data("%s: ", tag);
1153	for (i = 0; i < 20 && cnt; i++) {
1154	    output_data("%02x", *ptr);
1155	    if (isprint((unsigned char)*ptr)) {
1156		xbuf[i] = *ptr;
1157	    } else {
1158		xbuf[i] = '.';
1159	    }
1160	    if (i % 2) {
1161		output_data(" ");
1162	    }
1163	    cnt--;
1164	    ptr++;
1165	}
1166	xbuf[i] = '\0';
1167	output_data(" %s\r\n", xbuf);
1168    }
1169}
1170#endif /* DIAGNOSTICS */
1171