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