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