1/*	$NetBSD: utility.c,v 1.34 2022/08/26 22:01:20 hgutch Exp $	*/
2
3/*
4 * Copyright (c) 1989, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)utility.c	8.4 (Berkeley) 5/30/95";
36#else
37__RCSID("$NetBSD: utility.c,v 1.34 2022/08/26 22:01:20 hgutch Exp $");
38#endif
39#endif /* not lint */
40
41#include <sys/utsname.h>
42#include <ctype.h>
43#define PRINTOPTIONS
44#include "telnetd.h"
45
46char *nextitem(char *, const char *);
47void putstr(char *);
48
49extern int not42;
50
51/*
52 * utility functions performing io related tasks
53 */
54
55/*
56 * ttloop
57 *
58 *	A small subroutine to flush the network output buffer, get some data
59 * from the network, and pass it through the telnet state machine.  We
60 * also flush the pty input buffer (by dropping its data) if it becomes
61 * too full.
62 */
63
64void
65ttloop(void)
66{
67
68    DIAG(TD_REPORT, {output_data("td: ttloop\r\n");});
69    if (nfrontp - nbackp) {
70	netflush();
71    }
72    ncc = read(net, netibuf, sizeof netibuf);
73    if (ncc < 0) {
74	syslog(LOG_ERR, "ttloop:  read: %m");
75	exit(1);
76    } else if (ncc == 0) {
77	syslog(LOG_INFO, "ttloop:  unexpected EOF from peer");
78	exit(1);
79    }
80    DIAG(TD_REPORT, {output_data("td: ttloop read %d chars\r\n", ncc);});
81    netip = netibuf;
82    telrcv();			/* state machine */
83    if (ncc > 0) {
84	pfrontp = pbackp = ptyobuf;
85	telrcv();
86    }
87}  /* end of ttloop */
88
89/*
90 * Check a descriptor to see if out of band data exists on it.
91 */
92int
93stilloob(int s /* socket number */)
94{
95    struct pollfd set[1];
96    int value;
97
98    set[0].fd = s;
99    set[0].events = POLLPRI;
100    do {
101	value = poll(set, 1, 0);
102    } while ((value == -1) && (errno == EINTR));
103
104    if (value < 0) {
105	fatalperror(pty, "poll");
106    }
107    if (set[0].revents & POLLPRI) {
108	return 1;
109    } else {
110	return 0;
111    }
112}
113
114void
115ptyflush(void)
116{
117	int n;
118
119	if ((n = pfrontp - pbackp) > 0) {
120		DIAG((TD_REPORT | TD_PTYDATA),
121			{ output_data("td: ptyflush %d chars\r\n", n); });
122		DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
123		n = write(pty, pbackp, n);
124	}
125	if (n < 0) {
126		if (errno == EWOULDBLOCK || errno == EINTR)
127			return;
128		cleanup(0);
129	}
130	pbackp += n;
131	if (pbackp == pfrontp)
132		pbackp = pfrontp = ptyobuf;
133}
134
135/*
136 * nextitem()
137 *
138 *	Return the address of the next "item" in the TELNET data
139 * stream.  This will be the address of the next character if
140 * the current address is a user data character, or it will
141 * be the address of the character following the TELNET command
142 * if the current address is a TELNET IAC ("I Am a Command")
143 * character.
144 */
145char *
146nextitem(char *current, const char *endp)
147{
148    if (current >= endp) {
149	return NULL;
150    }
151    if ((*current&0xff) != IAC) {
152	return current+1;
153    }
154    if (current+1 >= endp) {
155	return NULL;
156    }
157    switch (*(current+1)&0xff) {
158    case DO:
159    case DONT:
160    case WILL:
161    case WONT:
162	return current+3 <= endp ? current+3 : NULL;
163    case SB:		/* loop forever looking for the SE */
164	{
165	    char *look = current+2;
166
167	    while (look < endp) {
168		if ((*look++&0xff) == IAC) {
169		    if (look < endp && (*look++&0xff) == SE) {
170			return look;
171		    }
172		}
173	    }
174	    return NULL;
175	}
176    default:
177	return current+2 <= endp ? current+2 : NULL;
178    }
179}  /* end of nextitem */
180
181
182/*
183 * netclear()
184 *
185 *	We are about to do a TELNET SYNCH operation.  Clear
186 * the path to the network.
187 *
188 *	Things are a bit tricky since we may have sent the first
189 * byte or so of a previous TELNET command into the network.
190 * So, we have to scan the network buffer from the beginning
191 * until we are up to where we want to be.
192 *
193 *	A side effect of what we do, just to keep things
194 * simple, is to clear the urgent data pointer.  The principal
195 * caller should be setting the urgent data pointer AFTER calling
196 * us in any case.
197 */
198void
199netclear(void)
200{
201    char *thisitem, *next;
202    char *good;
203#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
204				(nfrontp > p+1) && ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
205
206#ifdef	ENCRYPTION
207    thisitem = nclearto > netobuf ? nclearto : netobuf;
208#else /* ENCRYPTION */
209    thisitem = netobuf;
210#endif	/* ENCRYPTION */
211
212    while ((next = nextitem(thisitem, nbackp)) != NULL && (next <= nbackp)) {
213	thisitem = next;
214    }
215
216    /* Now, thisitem is first before/at boundary. */
217
218#ifdef	ENCRYPTION
219    good = nclearto > netobuf ? nclearto : netobuf;
220#else /* ENCRYPTION */
221    good = netobuf;	/* where the good bytes go */
222#endif	/* ENCRYPTION */
223
224    while ((thisitem != NULL) && (nfrontp > thisitem)) {
225	if (wewant(thisitem)) {
226	    int length;
227
228	    next = thisitem;
229	    do {
230		next = nextitem(next, nfrontp);
231	    } while ((next != NULL) && wewant(next) && (nfrontp > next));
232	    if (next == NULL) {
233		next = nfrontp;
234	    }
235	    length = next-thisitem;
236	    memmove(good, thisitem, length);
237	    good += length;
238	    thisitem = next;
239	} else {
240	    thisitem = nextitem(thisitem, nfrontp);
241	}
242    }
243
244    nbackp = netobuf;
245    nfrontp = good;		/* next byte to be sent */
246    neturg = 0;
247}  /* end of netclear */
248
249/*
250 *  netflush
251 *		Send as much data as possible to the network,
252 *	handling requests for urgent data.
253 */
254void
255netflush(void)
256{
257    int n;
258
259    if ((n = nfrontp - nbackp) > 0) {
260	DIAG(TD_REPORT,
261	    { output_data("td: netflush %d chars\r\n", n);
262	      n = nfrontp - nbackp;	/* re-compute count */
263	    });
264#ifdef	ENCRYPTION
265	if (encrypt_output) {
266		char *s = nclearto ? nclearto : nbackp;
267		if (nfrontp - s > 0) {
268			(*encrypt_output)((unsigned char *)s, nfrontp - s);
269			nclearto = nfrontp;
270		}
271	}
272#endif	/* ENCRYPTION */
273	/*
274	 * if no urgent data, or if the other side appears to be an
275	 * old 4.2 client (and thus unable to survive TCP urgent data),
276	 * write the entire buffer in non-OOB mode.
277	 */
278	if ((neturg == 0) || (not42 == 0)) {
279	    n = write(net, nbackp, n);	/* normal write */
280	} else {
281	    n = neturg - nbackp;
282	    /*
283	     * In 4.2 (and 4.3) systems, there is some question about
284	     * what byte in a sendOOB operation is the "OOB" data.
285	     * To make ourselves compatible, we only send ONE byte
286	     * out of band, the one WE THINK should be OOB (though
287	     * we really have more the TCP philosophy of urgent data
288	     * rather than the Unix philosophy of OOB data).
289	     */
290	    if (n > 1) {
291		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
292	    } else {
293		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
294	    }
295	}
296    }
297    if (n < 0) {
298	if (errno == EWOULDBLOCK || errno == EINTR)
299		return;
300	cleanup(0);
301    }
302    nbackp += n;
303#ifdef	ENCRYPTION
304    if (nbackp > nclearto)
305	nclearto = 0;
306#endif	/* ENCRYPTION */
307    if (nbackp >= neturg) {
308	neturg = 0;
309    }
310    if (nbackp == nfrontp) {
311	nbackp = nfrontp = netobuf;
312#ifdef	ENCRYPTION
313	nclearto = 0;
314#endif	/* ENCRYPTION */
315    }
316    return;
317}  /* end of netflush */
318
319
320/*
321 * writenet
322 *
323 * Just a handy little function to write a bit of raw data to the net.
324 * It will force a transmit of the buffer if necessary
325 *
326 * arguments
327 *    ptr - A pointer to a character string to write
328 *    len - How many bytes to write
329 */
330void
331writenet(unsigned char *ptr, int len)
332{
333	/* flush buffer if no room for new data) */
334	if ((&netobuf[BUFSIZ] - nfrontp) < len) {
335		/* if this fails, don't worry, buffer is a little big */
336		netflush();
337	}
338
339	memmove(nfrontp, ptr, len);
340	nfrontp += len;
341
342}  /* end of writenet */
343
344
345/*
346 * miscellaneous functions doing a variety of little jobs follow ...
347 */
348void
349fatal(int f, const char *msg)
350{
351	char buf[BUFSIZ];
352
353	(void)snprintf(buf, sizeof buf, "telnetd: %s.\r\n", msg);
354#ifdef	ENCRYPTION
355	if (encrypt_output) {
356		/*
357		 * Better turn off encryption first....
358		 * Hope it flushes...
359		 */
360		encrypt_send_end();
361		netflush();
362	}
363#endif	/* ENCRYPTION */
364	(void)write(f, buf, (int)strlen(buf));
365	sleep(1);	/*XXX*/
366	exit(1);
367}
368
369void
370fatalperror(f, msg)
371	int f;
372	const char *msg;
373{
374	char buf[BUFSIZ];
375
376	(void)snprintf(buf, sizeof buf, "%s: %s", msg, strerror(errno));
377	fatal(f, buf);
378}
379
380char editedhost[MAXHOSTNAMELEN];
381
382void
383edithost(const char *pat, const 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		(void) strncpy(res, host,
414		    sizeof editedhost - (res - editedhost) -1);
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
436/*
437 * This is split on two lines so that SCCS will not see the M
438 * between two % signs and expand it...
439 */
440static const char fmtstr[] = { "%l:%M\
441%p on %A, %d %B %Y" };
442
443char *
444putf(const char *cp, char *where)
445{
446	char *slash;
447	time_t t;
448	char db[100];
449	struct utsname utsinfo;
450
451	uname(&utsinfo);
452
453	putlocation = where;
454
455	while (*cp) {
456		if (*cp != '%') {
457			putchr(*cp++);
458			continue;
459		}
460		switch (*++cp) {
461
462		case 't':
463			if ((slash = strstr(line, "/pts/")) == NULL)
464				slash = strrchr(line, '/');
465			if (slash == (char *) 0)
466				putstr(line);
467			else
468				putstr(&slash[1]);
469			break;
470
471		case 'h':
472			putstr(editedhost);
473			break;
474
475		case 'd':
476			(void)time(&t);
477			(void)strftime(db, sizeof(db), fmtstr, localtime(&t));
478			putstr(db);
479			break;
480
481		case '%':
482			putchr('%');
483			break;
484
485		case 's':
486			putstr(utsinfo.sysname);
487			break;
488
489		case 'm':
490			putstr(utsinfo.machine);
491			break;
492
493		case 'r':
494			putstr(utsinfo.release);
495			break;
496
497		case 'v':
498			putstr(utsinfo.version);
499                        break;
500		}
501		cp++;
502	}
503
504	return (putlocation);
505}
506
507#ifdef DIAGNOSTICS
508/*
509 * Print telnet options and commands in plain text, if possible.
510 */
511void
512printoption(const char *fmt, int option)
513{
514	if (TELOPT_OK(option))
515		output_data("%s %s\r\n", fmt, TELOPT(option));
516	else if (TELCMD_OK(option))
517		output_data("%s %s\r\n", fmt, TELCMD(option));
518	else
519		output_data("%s %d\r\n", fmt, option);
520	return;
521}
522
523void
524printsub(
525    int	   direction,	/* '<' or '>' */
526    unsigned char *pointer,	/* where suboption data sits */
527    int		   length)	/* length of suboption data */
528{
529    int i = 0;
530#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
531    u_char buf[512];
532#endif
533
534	if (!(diagnostic & TD_OPTIONS))
535		return;
536
537	if (direction) {
538	    output_data("td: %s suboption ",
539	        direction == '<' ? "recv" : "send");
540	    if (length >= 3) {
541		int j;
542
543		i = pointer[length - 2];
544		j = pointer[length - 1];
545
546		if (i != IAC || j != SE) {
547		    output_data("(terminated by ");
548		    if (TELOPT_OK(i))
549			output_data("%s ", TELOPT(i));
550		    else if (TELCMD_OK(i))
551			output_data("%s ", TELCMD(i));
552		    else
553			output_data("%d ", i);
554		    if (TELOPT_OK(j))
555			output_data("%s", TELOPT(j));
556		    else if (TELCMD_OK(j))
557			output_data("%s", TELCMD(j));
558		    else
559			output_data("%d", j);
560		    output_data(", not IAC SE!) ");
561		}
562	    }
563	    length -= 2;
564	}
565	if (length < 1) {
566	    output_data("(Empty suboption??\?)");
567	    return;
568	}
569	switch (pointer[0]) {
570	case TELOPT_TTYPE:
571	    output_data("TERMINAL-TYPE ");
572	    switch (pointer[1]) {
573	    case TELQUAL_IS:
574		output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
575		break;
576	    case TELQUAL_SEND:
577		output_data("SEND");
578		break;
579	    default:
580		output_data("- unknown qualifier %d (0x%x).",
581		    pointer[1], pointer[1]);
582	    }
583	    break;
584	case TELOPT_TSPEED:
585	    output_data("TERMINAL-SPEED");
586	    if (length < 2) {
587		output_data(" (empty suboption??\?)");
588		break;
589	    }
590	    switch (pointer[1]) {
591	    case TELQUAL_IS:
592		output_data(" IS %.*s", length-2, (char *)pointer+2);
593		break;
594	    default:
595		if (pointer[1] == 1)
596		    output_data(" SEND");
597		else
598		    output_data(" %d (unknown)", pointer[1]);
599		for (i = 2; i < length; i++) {
600		    output_data(" ?%d?", pointer[i]);
601		}
602		break;
603	    }
604	    break;
605
606	case TELOPT_LFLOW:
607	    output_data("TOGGLE-FLOW-CONTROL");
608	    if (length < 2) {
609		output_data(" (empty suboption??\?)");
610		break;
611	    }
612	    switch (pointer[1]) {
613	    case LFLOW_OFF:
614		output_data(" OFF"); break;
615	    case LFLOW_ON:
616		output_data(" ON"); break;
617	    case LFLOW_RESTART_ANY:
618		output_data(" RESTART-ANY"); break;
619	    case LFLOW_RESTART_XON:
620		output_data(" RESTART-XON"); break;
621	    default:
622		output_data(" %d (unknown)", pointer[1]);
623	    }
624	    for (i = 2; i < length; i++)
625		output_data(" ?%d?", pointer[i]);
626	    break;
627
628	case TELOPT_NAWS:
629	    output_data("NAWS");
630	    if (length < 2) {
631		output_data(" (empty suboption??\?)");
632		break;
633	    }
634	    if (length == 2) {
635		output_data(" ?%d?", pointer[1]);
636		break;
637	    }
638	    output_data(" %d %d (%d)",
639		pointer[1], pointer[2],
640		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
641	    if (length == 4) {
642		output_data(" ?%d?", pointer[3]);
643		break;
644	    }
645	    output_data(" %d %d (%d)", pointer[3], pointer[4],
646		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
647	    for (i = 5; i < length; i++) {
648		output_data(" ?%d?", pointer[i]);
649	    }
650	    break;
651
652	case TELOPT_LINEMODE:
653	    output_data("LINEMODE ");
654	    if (length < 2) {
655		output_data(" (empty suboption??\?)");
656		break;
657	    }
658	    switch (pointer[1]) {
659	    case WILL:
660		output_data("WILL ");
661		goto common;
662	    case WONT:
663		output_data("WONT ");
664		goto common;
665	    case DO:
666		output_data("DO ");
667		goto common;
668	    case DONT:
669		output_data("DONT ");
670	    common:
671		if (length < 3) {
672		    output_data("(no option??\?)");
673		    break;
674		}
675		switch (pointer[2]) {
676		case LM_FORWARDMASK:
677		    output_data("Forward Mask");
678		    for (i = 3; i < length; i++)
679			output_data(" %x", pointer[i]);
680		    break;
681		default:
682		    output_data("%d (unknown)", pointer[2]);
683		    for (i = 3; i < length; i++)
684			output_data(" %d", pointer[i]);
685		    break;
686		}
687		break;
688
689	    case LM_SLC:
690		output_data("SLC");
691		for (i = 2; i < length - 2; i += 3) {
692		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
693			output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC]));
694		    else
695			output_data(" %d", pointer[i+SLC_FUNC]);
696		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
697		    case SLC_NOSUPPORT:
698			output_data(" NOSUPPORT"); break;
699		    case SLC_CANTCHANGE:
700			output_data(" CANTCHANGE"); break;
701		    case SLC_VARIABLE:
702			output_data(" VARIABLE"); break;
703		    case SLC_DEFAULT:
704			output_data(" DEFAULT"); break;
705		    }
706		    output_data("%s%s%s",
707			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
708			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
709			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
710		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
711						SLC_FLUSHOUT| SLC_LEVELBITS)) {
712			output_data("(0x%x)", pointer[i+SLC_FLAGS]);
713		    }
714		    output_data(" %d;", pointer[i+SLC_VALUE]);
715		    if ((pointer[i+SLC_VALUE] == IAC) &&
716			(pointer[i+SLC_VALUE+1] == IAC))
717				i++;
718		}
719		for (; i < length; i++)
720		    output_data(" ?%d?", pointer[i]);
721		break;
722
723	    case LM_MODE:
724		output_data("MODE ");
725		if (length < 3) {
726		    output_data("(no mode??\?)");
727		    break;
728		}
729		{
730		    char tbuf[40];
731
732		    (void)snprintf(tbuf, sizeof tbuf, "%s%s%s%s%s",
733			pointer[2]&MODE_EDIT ? "|EDIT" : "",
734			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
735			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
736			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
737			pointer[2]&MODE_ACK ? "|ACK" : "");
738		    output_data("%s", tbuf[1] ? &tbuf[1] : "0");
739		}
740		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK))
741		    output_data(" (0x%x)", pointer[2]);
742		for (i = 3; i < length; i++)
743		    output_data(" ?0x%x?", pointer[i]);
744		break;
745	    default:
746		output_data("%d (unknown)", pointer[1]);
747		for (i = 2; i < length; i++)
748		    output_data(" %d", pointer[i]);
749	    }
750	    break;
751
752	case TELOPT_STATUS: {
753	    const char *cp;
754	    int j, k;
755
756	    output_data("STATUS");
757
758	    switch (pointer[1]) {
759	    default:
760		if (pointer[1] == TELQUAL_SEND)
761		    output_data(" SEND");
762		else
763		    output_data(" %d (unknown)", pointer[1]);
764		for (i = 2; i < length; i++)
765		    output_data(" ?%d?", pointer[i]);
766		break;
767	    case TELQUAL_IS:
768		output_data(" IS\r\n");
769
770		for (i = 2; i < length; i++) {
771		    switch(pointer[i]) {
772		    case DO:	cp = "DO"; goto common2;
773		    case DONT:	cp = "DONT"; goto common2;
774		    case WILL:	cp = "WILL"; goto common2;
775		    case WONT:	cp = "WONT"; goto common2;
776		    common2:
777			i++;
778			if (TELOPT_OK(pointer[i]))
779			    output_data(" %s %s", cp, TELOPT(pointer[i]));
780			else
781			    output_data(" %s %d", cp, pointer[i]);
782
783			output_data("\r\n");
784			break;
785
786		    case SB:
787			output_data(" SB ");
788			i++;
789			j = k = i;
790			while (j < length) {
791			    if (pointer[j] == SE) {
792				if (j+1 == length)
793				    break;
794				if (pointer[j+1] == SE)
795				    j++;
796				else
797				    break;
798			    }
799			    pointer[k++] = pointer[j++];
800			}
801			printsub(0, &pointer[i], k - i);
802			if (i < length) {
803			    output_data(" SE");
804			    i = j;
805			} else
806			    i = j - 1;
807
808			output_data("\r\n");
809
810			break;
811
812		    default:
813			output_data(" %d", pointer[i]);
814			break;
815		    }
816		}
817		break;
818	    }
819	    break;
820	  }
821
822	case TELOPT_XDISPLOC:
823	    output_data("X-DISPLAY-LOCATION ");
824	    switch (pointer[1]) {
825	    case TELQUAL_IS:
826		output_data("IS \"%.*s\"", length - 2, (char *)pointer + 2);
827		break;
828	    case TELQUAL_SEND:
829		output_data("SEND");
830		break;
831	    default:
832		output_data("- unknown qualifier %d (0x%x).",
833		    pointer[1], pointer[1]);
834	    }
835	    break;
836
837	case TELOPT_NEW_ENVIRON:
838	    output_data("NEW-ENVIRON ");
839	    goto env_common1;
840	case TELOPT_OLD_ENVIRON:
841	    output_data("OLD-ENVIRON");
842	env_common1:
843	    switch (pointer[1]) {
844	    case TELQUAL_IS:
845		output_data("IS ");
846		goto env_common;
847	    case TELQUAL_SEND:
848		output_data("SEND ");
849		goto env_common;
850	    case TELQUAL_INFO:
851		output_data("INFO ");
852	    env_common:
853		{
854		    static const char NQ[] = "\" ";
855		    const char *noquote = NQ;
856		    for (i = 2; i < length; i++ ) {
857			switch (pointer[i]) {
858			case NEW_ENV_VAR:
859			    output_data("%sVAR ", noquote);
860			    noquote = NQ;
861			    break;
862
863			case NEW_ENV_VALUE:
864			    output_data("%sVALUE ", noquote);
865			    noquote = NQ;
866			    break;
867
868			case ENV_ESC:
869			    output_data("%sESC ", noquote);
870			    noquote = NQ;
871			    break;
872
873			case ENV_USERVAR:
874			    output_data("%sUSERVAR ", noquote);
875			    noquote = NQ;
876			    break;
877
878			default:
879			    if (isprint(pointer[i]) && pointer[i] != '"') {
880				if (*noquote) {
881				    output_data("\"");
882				    noquote = "";
883				}
884				output_data("%c", pointer[i]);
885			    } else {
886				output_data("%s%03o ", noquote, pointer[i]);
887				noquote = NQ;
888			    }
889			    break;
890			}
891		    }
892		    if (!noquote)
893			output_data("\"");
894		    break;
895		}
896	    }
897	    break;
898
899#ifdef AUTHENTICATION
900	case TELOPT_AUTHENTICATION:
901	    output_data("AUTHENTICATION");
902
903	    if (length < 2) {
904		output_data(" (empty suboption??\?)");
905		break;
906	    }
907	    switch (pointer[1]) {
908	    case TELQUAL_REPLY:
909	    case TELQUAL_IS:
910		output_data(" %s ", (pointer[1] == TELQUAL_IS) ?
911		    "IS" : "REPLY");
912		if (AUTHTYPE_NAME_OK(pointer[2]))
913		    output_data("%s ", AUTHTYPE_NAME(pointer[2]));
914		else
915		    output_data("%d ", pointer[2]);
916		if (length < 3) {
917		    output_data("(partial suboption??\?)");
918		    break;
919		}
920		output_data("%s|%s",
921			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
922			"CLIENT" : "SERVER",
923			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
924			"MUTUAL" : "ONE-WAY");
925
926		auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
927		output_data("%s", buf);
928		break;
929
930	    case TELQUAL_SEND:
931		i = 2;
932		output_data(" SEND ");
933		while (i < length) {
934		    if (AUTHTYPE_NAME_OK(pointer[i]))
935			output_data("%s ", AUTHTYPE_NAME(pointer[i]));
936		    else
937			output_data("%d ", pointer[i]);
938		    if (++i >= length) {
939			output_data("(partial suboption??\?)");
940			break;
941		    }
942		    output_data("%s|%s ",
943			((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
944							"CLIENT" : "SERVER",
945			((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
946							"MUTUAL" : "ONE-WAY");
947		    ++i;
948		}
949		break;
950
951	    case TELQUAL_NAME:
952		i = 2;
953		output_data(" NAME \"");
954		while (i < length) {
955		    if (isprint(pointer[i]))
956			output_data("%c", pointer[i++]);
957		    else
958			output_data("\"%03o\"",pointer[i++]);
959		}
960		output_data("\"");
961		break;
962
963	    default:
964		for (i = 2; i < length; i++)
965		    output_data(" ?%d?", pointer[i]);
966		break;
967	    }
968	    break;
969#endif
970
971#ifdef	ENCRYPTION
972	case TELOPT_ENCRYPT:
973	    output_data("ENCRYPT");
974	    if (length < 2) {
975		output_data(" (empty suboption??\?)");
976		break;
977	    }
978	    switch (pointer[1]) {
979	    case ENCRYPT_START:
980		output_data(" START");
981		break;
982
983	    case ENCRYPT_END:
984		output_data(" END");
985		break;
986
987	    case ENCRYPT_REQSTART:
988		output_data(" REQUEST-START");
989		break;
990
991	    case ENCRYPT_REQEND:
992		output_data(" REQUEST-END");
993		break;
994
995	    case ENCRYPT_IS:
996	    case ENCRYPT_REPLY:
997		output_data(" %s ", (pointer[1] == ENCRYPT_IS) ?
998		    "IS" : "REPLY");
999		if (length < 3) {
1000			output_data(" (partial suboption??\?)");
1001			break;
1002		}
1003		if (ENCTYPE_NAME_OK(pointer[2]))
1004			output_data("%s ", ENCTYPE_NAME(pointer[2]));
1005		else
1006			output_data(" %d (unknown)", pointer[2]);
1007
1008		encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
1009		output_data("%s", buf);
1010		break;
1011
1012	    case ENCRYPT_SUPPORT:
1013		i = 2;
1014		output_data(" SUPPORT ");
1015		while (i < length) {
1016			if (ENCTYPE_NAME_OK(pointer[i]))
1017				output_data("%s ", ENCTYPE_NAME(pointer[i]));
1018			else
1019				output_data("%d ", pointer[i]);
1020			i++;
1021		}
1022		break;
1023
1024	    case ENCRYPT_ENC_KEYID:
1025		output_data(" ENC_KEYID");
1026		goto encommon;
1027
1028	    case ENCRYPT_DEC_KEYID:
1029		output_data(" DEC_KEYID");
1030		goto encommon;
1031
1032	    default:
1033		output_data(" %d (unknown)", pointer[1]);
1034	    encommon:
1035		for (i = 2; i < length; i++)
1036			output_data(" %d", pointer[i]);
1037		break;
1038	    }
1039	    break;
1040#endif	/* ENCRYPTION */
1041
1042	default:
1043	    if (TELOPT_OK(pointer[0]))
1044		output_data("%s (unknown)", TELOPT(pointer[0]));
1045	    else
1046		output_data("%d (unknown)", pointer[i]);
1047	    for (i = 1; i < length; i++)
1048		output_data(" %d", pointer[i]);
1049	    break;
1050	}
1051	output_data("\r\n");
1052}
1053
1054/*
1055 * Dump a data buffer in hex and ascii to the output data stream.
1056 */
1057void
1058printdata(const char *tag, char *ptr, int cnt)
1059{
1060	int i;
1061	char xbuf[30];
1062
1063	while (cnt) {
1064		/* flush net output buffer if no room for new data) */
1065		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
1066			netflush();
1067		}
1068
1069		/* add a line of output */
1070		output_data("%s: ", tag);
1071		for (i = 0; i < 20 && cnt; i++) {
1072			output_data("%02x", *ptr);
1073			if (isprint((unsigned char)*ptr)) {
1074				xbuf[i] = *ptr;
1075			} else {
1076				xbuf[i] = '.';
1077			}
1078			if (i % 2)
1079				output_data(" ");
1080			cnt--;
1081			ptr++;
1082		}
1083		xbuf[i] = '\0';
1084		output_data(" %s\r\n", xbuf);
1085	}
1086}
1087#endif /* DIAGNOSTICS */
1088