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