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