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