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