Stop building multiple versions of telnet.
[dragonfly.git] / usr.bin / telnet / commands.c
1 /*
2  * Copyright (c) 1988, 1990, 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  * @(#)commands.c       8.4 (Berkeley) 5/30/95
34  * $FreeBSD: src/crypto/telnet/telnet/commands.c,v 1.12.2.7 2003/04/23 07:16:32 ru Exp $
35  * $DragonFly: src/crypto/telnet/telnet/commands.c,v 1.3 2008/09/04 11:54:24 hasso Exp $
36  */
37
38 #include <sys/param.h>
39 #include <sys/un.h>
40 #include <sys/file.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <netdb.h>
48 #include <pwd.h>
49 #include <signal.h>
50 #include <stdarg.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54
55 #include <arpa/telnet.h>
56 #include <arpa/inet.h>
57
58 #include "general.h"
59
60 #include "ring.h"
61
62 #include "externs.h"
63 #include "defines.h"
64 #include "types.h"
65 #include "misc.h"
66
67 #ifdef  AUTHENTICATION
68 #include <libtelnet/auth.h>
69 #endif
70 #ifdef  ENCRYPTION
71 #include <libtelnet/encrypt.h>
72 #endif
73
74 #include <netinet/in_systm.h>
75 #include <netinet/ip.h>
76 #include <netinet/ip6.h>
77
78 #ifndef       MAXHOSTNAMELEN
79 #define       MAXHOSTNAMELEN 256
80 #endif
81
82 typedef int (*intrtn_t)(int, char **);
83
84 #ifdef  AUTHENTICATION
85 extern int auth_togdebug(int);
86 #endif
87 #ifdef  ENCRYPTION
88 extern int EncryptAutoEnc(int);
89 extern int EncryptAutoDec(int);
90 extern int EncryptDebug(int);
91 extern int EncryptVerbose(int);
92 #endif  /* ENCRYPTION */
93 #if     defined(IPPROTO_IP) && defined(IP_TOS)
94 int tos = -1;
95 #endif  /* defined(IPPROTO_IP) && defined(IP_TOS) */
96
97 char    *hostname;
98 static char _hostname[MAXHOSTNAMELEN];
99
100 static int help(int, char **);
101 static int call(intrtn_t, ...);
102 static void cmdrc(char *, char *);
103 #ifdef INET6
104 static int switch_af(struct addrinfo **);
105 #endif
106 static int togglehelp(void);
107 static int send_tncmd(void (*)(int, int), const char *, char *);
108 static int setmod(int);
109 static int clearmode(int);
110 static int modehelp(void);
111 static int sourceroute(struct addrinfo *, char *, char **, int *, int *, int *);
112
113 typedef struct {
114         const char *name;       /* command name */
115         const char *help;       /* help string (NULL for no help) */
116         int     (*handler)(int, char **); /* routine which executes command */
117         int     needconnect;    /* Do we need to be connected to execute? */
118 } Command;
119
120 static char line[256];
121 static char saveline[256];
122 static int margc;
123 static char *margv[20];
124
125 #ifdef OPIE
126 #include <sys/wait.h>
127 #define PATH_OPIEKEY    "/usr/bin/opiekey"
128 static int
129 opie_calc(int argc, char *argv[])
130 {
131         int status;
132
133         if(argc != 3) {
134                 printf("%s sequence challenge\n", argv[0]);
135                 return (0);
136         }
137
138         switch(fork()) {
139         case 0:
140                 execv(PATH_OPIEKEY, argv);
141                 exit (1);
142         case -1:
143                 perror("fork");
144                 break;
145         default:
146                 (void) wait(&status);
147                 if (WIFEXITED(status))
148                         return (WEXITSTATUS(status));
149         }
150         return (0);
151 }
152 #endif
153
154 static void
155 makeargv(void)
156 {
157     char *cp, *cp2, c;
158     char **argp = margv;
159
160     margc = 0;
161     cp = line;
162     if (*cp == '!') {           /* Special case shell escape */
163         strcpy(saveline, line); /* save for shell command */
164         *argp++ = strdup("!");          /* No room in string to get this */
165         margc++;
166         cp++;
167     }
168     while ((c = *cp)) {
169         int inquote = 0;
170         while (isspace(c))
171             c = *++cp;
172         if (c == '\0')
173             break;
174         *argp++ = cp;
175         margc += 1;
176         for (cp2 = cp; c != '\0'; c = *++cp) {
177             if (inquote) {
178                 if (c == inquote) {
179                     inquote = 0;
180                     continue;
181                 }
182             } else {
183                 if (c == '\\') {
184                     if ((c = *++cp) == '\0')
185                         break;
186                 } else if (c == '"') {
187                     inquote = '"';
188                     continue;
189                 } else if (c == '\'') {
190                     inquote = '\'';
191                     continue;
192                 } else if (isspace(c))
193                     break;
194             }
195             *cp2++ = c;
196         }
197         *cp2 = '\0';
198         if (c == '\0')
199             break;
200         cp++;
201     }
202     *argp++ = 0;
203 }
204
205 /*
206  * Make a character string into a number.
207  *
208  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
209  */
210
211 static int
212 special(char *s)
213 {
214         char c;
215         char b;
216
217         switch (*s) {
218         case '^':
219                 b = *++s;
220                 if (b == '?') {
221                     c = b | 0x40;               /* DEL */
222                 } else {
223                     c = b & 0x1f;
224                 }
225                 break;
226         default:
227                 c = *s;
228                 break;
229         }
230         return c;
231 }
232
233 /*
234  * Construct a control character sequence
235  * for a special character.
236  */
237 static const char *
238 control(cc_t c)
239 {
240         static char buf[5];
241         /*
242          * The only way I could get the Sun 3.5 compiler
243          * to shut up about
244          *      if ((unsigned int)c >= 0x80)
245          * was to assign "c" to an unsigned int variable...
246          * Arggg....
247          */
248         unsigned int uic = (unsigned int)c;
249
250         if (uic == 0x7f)
251                 return ("^?");
252         if (c == (cc_t)_POSIX_VDISABLE) {
253                 return "off";
254         }
255         if (uic >= 0x80) {
256                 buf[0] = '\\';
257                 buf[1] = ((c>>6)&07) + '0';
258                 buf[2] = ((c>>3)&07) + '0';
259                 buf[3] = (c&07) + '0';
260                 buf[4] = 0;
261         } else if (uic >= 0x20) {
262                 buf[0] = c;
263                 buf[1] = 0;
264         } else {
265                 buf[0] = '^';
266                 buf[1] = '@'+c;
267                 buf[2] = 0;
268         }
269         return (buf);
270 }
271
272 /*
273  *      The following are data structures and routines for
274  *      the "send" command.
275  *
276  */
277
278 struct sendlist {
279     const char  *name;          /* How user refers to it (case independent) */
280     const char  *help;          /* Help information (0 ==> no help) */
281     int         needconnect;    /* Need to be connected */
282     int         narg;           /* Number of arguments */
283     int         (*handler)(char *, ...); /* Routine to perform (for special ops) */
284     int         nbyte;          /* Number of bytes to send this command */
285     int         what;           /* Character to be sent (<0 ==> special) */
286 };
287 \f
288
289 static int
290         send_esc(void),
291         send_help(void),
292         send_docmd(char *),
293         send_dontcmd(char *),
294         send_willcmd(char *),
295         send_wontcmd(char *);
296
297 static struct sendlist Sendlist[] = {
298     { "ao",     "Send Telnet Abort output",     1, 0, NULL, 2, AO },
299     { "ayt",    "Send Telnet 'Are You There'",  1, 0, NULL, 2, AYT },
300     { "brk",    "Send Telnet Break",            1, 0, NULL, 2, BREAK },
301     { "break",  NULL,                           1, 0, NULL, 2, BREAK },
302     { "ec",     "Send Telnet Erase Character",  1, 0, NULL, 2, EC },
303     { "el",     "Send Telnet Erase Line",       1, 0, NULL, 2, EL },
304     { "escape", "Send current escape character",1, 0, (int (*)(char *, ...))send_esc, 1, 0 },
305     { "ga",     "Send Telnet 'Go Ahead' sequence", 1, 0, NULL, 2, GA },
306     { "ip",     "Send Telnet Interrupt Process",1, 0, NULL, 2, IP },
307     { "intp",   NULL,                           1, 0, NULL, 2, IP },
308     { "interrupt", NULL,                        1, 0, NULL, 2, IP },
309     { "intr",   NULL,                           1, 0, NULL, 2, IP },
310     { "nop",    "Send Telnet 'No operation'",   1, 0, NULL, 2, NOP },
311     { "eor",    "Send Telnet 'End of Record'",  1, 0, NULL, 2, EOR },
312     { "abort",  "Send Telnet 'Abort Process'",  1, 0, NULL, 2, ABORT },
313     { "susp",   "Send Telnet 'Suspend Process'",1, 0, NULL, 2, SUSP },
314     { "eof",    "Send Telnet End of File Character", 1, 0, NULL, 2, xEOF },
315     { "synch",  "Perform Telnet 'Synch operation'", 1, 0, (int (*)(char *, ...))dosynch, 2, 0 },
316     { "getstatus", "Send request for STATUS",   1, 0, (int (*)(char *, ...))get_status, 6, 0 },
317     { "?",      "Display send options",         0, 0, (int (*)(char *, ...))send_help, 0, 0 },
318     { "help",   NULL,                           0, 0, (int (*)(char *, ...))send_help, 0, 0 },
319     { "do",     NULL,                           0, 1, (int (*)(char *, ...))send_docmd, 3, 0 },
320     { "dont",   NULL,                           0, 1, (int (*)(char *, ...))send_dontcmd, 3, 0 },
321     { "will",   NULL,                           0, 1, (int (*)(char *, ...))send_willcmd, 3, 0 },
322     { "wont",   NULL,                           0, 1, (int (*)(char *, ...))send_wontcmd, 3, 0 },
323     { NULL,     NULL,                           0, 0, NULL, 0, 0 }
324 };
325
326 #define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
327                                 sizeof(struct sendlist)))
328
329 static int
330 sendcmd(int argc, char *argv[])
331 {
332     int count;          /* how many bytes we are going to need to send */
333     int i;
334     struct sendlist *s; /* pointer to current command */
335     int success = 0;
336     int needconnect = 0;
337
338     if (argc < 2) {
339         printf("need at least one argument for 'send' command\n");
340         printf("'send ?' for help\n");
341         return 0;
342     }
343     /*
344      * First, validate all the send arguments.
345      * In addition, we see how much space we are going to need, and
346      * whether or not we will be doing a "SYNCH" operation (which
347      * flushes the network queue).
348      */
349     count = 0;
350     for (i = 1; i < argc; i++) {
351         s = GETSEND(argv[i]);
352         if (s == 0) {
353             printf("Unknown send argument '%s'\n'send ?' for help.\n",
354                         argv[i]);
355             return 0;
356         } else if (Ambiguous((void *)s)) {
357             printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
358                         argv[i]);
359             return 0;
360         }
361         if (i + s->narg >= argc) {
362             fprintf(stderr,
363             "Need %d argument%s to 'send %s' command.  'send %s ?' for help.\n",
364                 s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
365             return 0;
366         }
367         count += s->nbyte;
368         if ((void *)s->handler == (void *)send_help) {
369             send_help();
370             return 0;
371         }
372
373         i += s->narg;
374         needconnect += s->needconnect;
375     }
376     if (!connected && needconnect) {
377         printf("?Need to be connected first.\n");
378         printf("'send ?' for help\n");
379         return 0;
380     }
381     /* Now, do we have enough room? */
382     if (NETROOM() < count) {
383         printf("There is not enough room in the buffer TO the network\n");
384         printf("to process your request.  Nothing will be done.\n");
385         printf("('send synch' will throw away most data in the network\n");
386         printf("buffer, if this might help.)\n");
387         return 0;
388     }
389     /* OK, they are all OK, now go through again and actually send */
390     count = 0;
391     for (i = 1; i < argc; i++) {
392         if ((s = GETSEND(argv[i])) == 0) {
393             fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
394             quit();
395             /*NOTREACHED*/
396         }
397         if (s->handler) {
398             count++;
399             success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
400                                   (s->narg > 1) ? argv[i+2] : 0);
401             i += s->narg;
402         } else {
403             NET2ADD(IAC, s->what);
404             printoption("SENT", IAC, s->what);
405         }
406     }
407     return (count == success);
408 }
409
410 static int
411 send_esc(void)
412 {
413     NETADD(escape);
414     return 1;
415 }
416
417 static int
418 send_docmd(char *name)
419 {
420     return(send_tncmd(send_do, "do", name));
421 }
422
423 static int
424 send_dontcmd(char *name)
425 {
426     return(send_tncmd(send_dont, "dont", name));
427 }
428
429 static int
430 send_willcmd(char *name)
431 {
432     return(send_tncmd(send_will, "will", name));
433 }
434
435 static int
436 send_wontcmd(char *name)
437 {
438     return(send_tncmd(send_wont, "wont", name));
439 }
440
441 static int
442 send_tncmd(void (*func)(int, int), const char *cmd, char *name)
443 {
444     char **cpp;
445     extern char *telopts[];
446     int val = 0;
447
448     if (isprefix(name, "help") || isprefix(name, "?")) {
449         int col, len;
450
451         printf("Usage: send %s <value|option>\n", cmd);
452         printf("\"value\" must be from 0 to 255\n");
453         printf("Valid options are:\n\t");
454
455         col = 8;
456         for (cpp = telopts; *cpp; cpp++) {
457             len = strlen(*cpp) + 3;
458             if (col + len > 65) {
459                 printf("\n\t");
460                 col = 8;
461             }
462             printf(" \"%s\"", *cpp);
463             col += len;
464         }
465         printf("\n");
466         return 0;
467     }
468     cpp = (char **)genget(name, telopts, sizeof(char *));
469     if (Ambiguous(cpp)) {
470         fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n",
471                                         name, cmd);
472         return 0;
473     }
474     if (cpp) {
475         val = cpp - telopts;
476     } else {
477         char *cp = name;
478
479         while (*cp >= '0' && *cp <= '9') {
480             val *= 10;
481             val += *cp - '0';
482             cp++;
483         }
484         if (*cp != 0) {
485             fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n",
486                                         name, cmd);
487             return 0;
488         } else if (val < 0 || val > 255) {
489             fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n",
490                                         name, cmd);
491             return 0;
492         }
493     }
494     if (!connected) {
495         printf("?Need to be connected first.\n");
496         return 0;
497     }
498     (*func)(val, 1);
499     return 1;
500 }
501
502 static int
503 send_help(void)
504 {
505     struct sendlist *s; /* pointer to current command */
506     for (s = Sendlist; s->name; s++) {
507         if (s->help)
508             printf("%-15s %s\n", s->name, s->help);
509     }
510     return(0);
511 }
512 \f
513 /*
514  * The following are the routines and data structures referred
515  * to by the arguments to the "toggle" command.
516  */
517
518 static int
519 lclchars(void)
520 {
521     donelclchars = 1;
522     return 1;
523 }
524
525 static int
526 togdebug(void)
527 {
528 #ifndef NOT43
529     if (net > 0 &&
530         (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
531             perror("setsockopt (SO_DEBUG)");
532     }
533 #else   /* NOT43 */
534     if (debug) {
535         if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0)
536             perror("setsockopt (SO_DEBUG)");
537     } else
538         printf("Cannot turn off socket debugging\n");
539 #endif  /* NOT43 */
540     return 1;
541 }
542
543
544 static int
545 togcrlf(void)
546 {
547     if (crlf) {
548         printf("Will send carriage returns as telnet <CR><LF>.\n");
549     } else {
550         printf("Will send carriage returns as telnet <CR><NUL>.\n");
551     }
552     return 1;
553 }
554
555 int binmode;
556
557 static int
558 togbinary(int val)
559 {
560     donebinarytoggle = 1;
561
562     if (val >= 0) {
563         binmode = val;
564     } else {
565         if (my_want_state_is_will(TELOPT_BINARY) &&
566                                 my_want_state_is_do(TELOPT_BINARY)) {
567             binmode = 1;
568         } else if (my_want_state_is_wont(TELOPT_BINARY) &&
569                                 my_want_state_is_dont(TELOPT_BINARY)) {
570             binmode = 0;
571         }
572         val = binmode ? 0 : 1;
573     }
574
575     if (val == 1) {
576         if (my_want_state_is_will(TELOPT_BINARY) &&
577                                         my_want_state_is_do(TELOPT_BINARY)) {
578             printf("Already operating in binary mode with remote host.\n");
579         } else {
580             printf("Negotiating binary mode with remote host.\n");
581             tel_enter_binary(3);
582         }
583     } else {
584         if (my_want_state_is_wont(TELOPT_BINARY) &&
585                                         my_want_state_is_dont(TELOPT_BINARY)) {
586             printf("Already in network ascii mode with remote host.\n");
587         } else {
588             printf("Negotiating network ascii mode with remote host.\n");
589             tel_leave_binary(3);
590         }
591     }
592     return 1;
593 }
594
595 static int
596 togrbinary(int val)
597 {
598     donebinarytoggle = 1;
599
600     if (val == -1)
601         val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
602
603     if (val == 1) {
604         if (my_want_state_is_do(TELOPT_BINARY)) {
605             printf("Already receiving in binary mode.\n");
606         } else {
607             printf("Negotiating binary mode on input.\n");
608             tel_enter_binary(1);
609         }
610     } else {
611         if (my_want_state_is_dont(TELOPT_BINARY)) {
612             printf("Already receiving in network ascii mode.\n");
613         } else {
614             printf("Negotiating network ascii mode on input.\n");
615             tel_leave_binary(1);
616         }
617     }
618     return 1;
619 }
620
621 static int
622 togxbinary(int val)
623 {
624     donebinarytoggle = 1;
625
626     if (val == -1)
627         val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
628
629     if (val == 1) {
630         if (my_want_state_is_will(TELOPT_BINARY)) {
631             printf("Already transmitting in binary mode.\n");
632         } else {
633             printf("Negotiating binary mode on output.\n");
634             tel_enter_binary(2);
635         }
636     } else {
637         if (my_want_state_is_wont(TELOPT_BINARY)) {
638             printf("Already transmitting in network ascii mode.\n");
639         } else {
640             printf("Negotiating network ascii mode on output.\n");
641             tel_leave_binary(2);
642         }
643     }
644     return 1;
645 }
646
647 struct togglelist {
648     const char  *name;          /* name of toggle */
649     const char  *help;          /* help message */
650     int         (*handler)(int); /* routine to do actual setting */
651     int         *variable;
652     const char  *actionexplanation;
653 };
654
655 static struct togglelist Togglelist[] = {
656     { "autoflush",
657         "flushing of output when sending interrupt characters",
658             0,
659                 &autoflush,
660                     "flush output when sending interrupt characters" },
661     { "autosynch",
662         "automatic sending of interrupt characters in urgent mode",
663             0,
664                 &autosynch,
665                     "send interrupt characters in urgent mode" },
666 #ifdef  AUTHENTICATION
667     { "autologin",
668         "automatic sending of login and/or authentication info",
669             0,
670                 &autologin,
671                     "send login name and/or authentication information" },
672     { "authdebug",
673         "Toggle authentication debugging",
674             auth_togdebug,
675                 0,
676                      "print authentication debugging information" },
677 #endif
678 #ifdef  ENCRYPTION
679     { "autoencrypt",
680         "automatic encryption of data stream",
681             EncryptAutoEnc,
682                 0,
683                     "automatically encrypt output" },
684     { "autodecrypt",
685         "automatic decryption of data stream",
686             EncryptAutoDec,
687                 0,
688                     "automatically decrypt input" },
689     { "verbose_encrypt",
690         "Toggle verbose encryption output",
691             EncryptVerbose,
692                 0,
693                     "print verbose encryption output" },
694     { "encdebug",
695         "Toggle encryption debugging",
696             EncryptDebug,
697                 0,
698                     "print encryption debugging information" },
699 #endif  /* ENCRYPTION */
700     { "skiprc",
701         "don't read ~/.telnetrc file",
702             0,
703                 &skiprc,
704                     "skip reading of ~/.telnetrc file" },
705     { "binary",
706         "sending and receiving of binary data",
707             togbinary,
708                 0,
709                     0 },
710     { "inbinary",
711         "receiving of binary data",
712             togrbinary,
713                 0,
714                     0 },
715     { "outbinary",
716         "sending of binary data",
717             togxbinary,
718                 0,
719                     0 },
720     { "crlf",
721         "sending carriage returns as telnet <CR><LF>",
722             (int (*)(int))togcrlf,
723                 &crlf,
724                     0 },
725     { "crmod",
726         "mapping of received carriage returns",
727             0,
728                 &crmod,
729                     "map carriage return on output" },
730     { "localchars",
731         "local recognition of certain control characters",
732             (int (*)(int))lclchars,
733                 &localchars,
734                     "recognize certain control characters" },
735     { " ", "", NULL, NULL, NULL },              /* empty line */
736     { "debug",
737         "debugging",
738             (int (*)(int))togdebug,
739                 &debug,
740                     "turn on socket level debugging" },
741     { "netdata",
742         "printing of hexadecimal network data (debugging)",
743             0,
744                 &netdata,
745                     "print hexadecimal representation of network traffic" },
746     { "prettydump",
747         "output of \"netdata\" to user readable format (debugging)",
748             0,
749                 &prettydump,
750                     "print user readable output for \"netdata\"" },
751     { "options",
752         "viewing of options processing (debugging)",
753             0,
754                 &showoptions,
755                     "show option processing" },
756     { "termdata",
757         "(debugging) toggle printing of hexadecimal terminal data",
758             0,
759                 &termdata,
760                     "print hexadecimal representation of terminal traffic" },
761     { "?",
762         NULL,
763             (int (*)(int))togglehelp,
764                 NULL,
765                     NULL },
766     { NULL, NULL, NULL, NULL, NULL },
767     { "help",
768         NULL,
769             (int (*)(int))togglehelp,
770                 NULL,
771                     NULL },
772     { NULL, NULL, NULL, NULL, NULL }
773 };
774
775 static int
776 togglehelp(void)
777 {
778     struct togglelist *c;
779
780     for (c = Togglelist; c->name; c++) {
781         if (c->help) {
782             if (*c->help)
783                 printf("%-15s toggle %s\n", c->name, c->help);
784             else
785                 printf("\n");
786         }
787     }
788     printf("\n");
789     printf("%-15s %s\n", "?", "display help information");
790     return 0;
791 }
792
793 static void
794 settogglehelp(int set)
795 {
796     struct togglelist *c;
797
798     for (c = Togglelist; c->name; c++) {
799         if (c->help) {
800             if (*c->help)
801                 printf("%-15s %s %s\n", c->name, set ? "enable" : "disable",
802                                                 c->help);
803             else
804                 printf("\n");
805         }
806     }
807 }
808
809 #define GETTOGGLE(name) (struct togglelist *) \
810                 genget(name, (char **) Togglelist, sizeof(struct togglelist))
811
812 static int
813 toggle(int argc, char *argv[])
814 {
815     int retval = 1;
816     char *name;
817     struct togglelist *c;
818
819     if (argc < 2) {
820         fprintf(stderr,
821             "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
822         return 0;
823     }
824     argc--;
825     argv++;
826     while (argc--) {
827         name = *argv++;
828         c = GETTOGGLE(name);
829         if (Ambiguous((void *)c)) {
830             fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
831                                         name);
832             return 0;
833         } else if (c == 0) {
834             fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
835                                         name);
836             return 0;
837         } else {
838             if (c->variable) {
839                 *c->variable = !*c->variable;           /* invert it */
840                 if (c->actionexplanation) {
841                     printf("%s %s.\n", *c->variable? "Will" : "Won't",
842                                                         c->actionexplanation);
843                 }
844             }
845             if (c->handler) {
846                 retval &= (*c->handler)(-1);
847             }
848         }
849     }
850     return retval;
851 }
852 \f
853 /*
854  * The following perform the "set" command.
855  */
856
857 #ifdef  USE_TERMIO
858 struct termio new_tc = { 0, 0, 0, 0, {}, 0, 0 };
859 #endif
860
861 struct setlist {
862     const char *name;                   /* name */
863     const char *help;                   /* help information */
864     void (*handler)(char *);
865     cc_t *charp;                        /* where it is located at */
866 };
867
868 static struct setlist Setlist[] = {
869 #ifdef  KLUDGELINEMODE
870     { "echo",   "character to toggle local echoing on/off", NULL, &echoc },
871 #endif
872     { "escape", "character to escape back to telnet command mode", NULL, &escape },
873     { "rlogin", "rlogin escape character", 0, &rlogin },
874     { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
875     { " ", "", NULL, NULL },
876     { " ", "The following need 'localchars' to be toggled true", NULL, NULL },
877     { "flushoutput", "character to cause an Abort Output", NULL, termFlushCharp },
878     { "interrupt", "character to cause an Interrupt Process", NULL, termIntCharp },
879     { "quit",   "character to cause an Abort process", NULL, termQuitCharp },
880     { "eof",    "character to cause an EOF ", NULL, termEofCharp },
881     { " ", "", NULL, NULL },
882     { " ", "The following are for local editing in linemode", NULL, NULL },
883     { "erase",  "character to use to erase a character", NULL, termEraseCharp },
884     { "kill",   "character to use to erase a line", NULL, termKillCharp },
885     { "lnext",  "character to use for literal next", NULL, termLiteralNextCharp },
886     { "susp",   "character to cause a Suspend Process", NULL, termSuspCharp },
887     { "reprint", "character to use for line reprint", NULL, termRprntCharp },
888     { "worderase", "character to use to erase a word", NULL, termWerasCharp },
889     { "start",  "character to use for XON", NULL, termStartCharp },
890     { "stop",   "character to use for XOFF", NULL, termStopCharp },
891     { "forw1",  "alternate end of line character", NULL, termForw1Charp },
892     { "forw2",  "alternate end of line character", NULL, termForw2Charp },
893     { "ayt",    "alternate AYT character", NULL, termAytCharp },
894     { NULL, NULL, NULL, NULL }
895 };
896
897 static struct setlist *
898 getset(char *name)
899 {
900     return (struct setlist *)
901                 genget(name, (char **) Setlist, sizeof(struct setlist));
902 }
903
904 void
905 set_escape_char(char *s)
906 {
907         if (rlogin != _POSIX_VDISABLE) {
908                 rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
909                 printf("Telnet rlogin escape character is '%s'.\n",
910                                         control(rlogin));
911         } else {
912                 escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
913                 printf("Telnet escape character is '%s'.\n", control(escape));
914         }
915 }
916
917 static int
918 setcmd(int argc, char *argv[])
919 {
920     int value;
921     struct setlist *ct;
922     struct togglelist *c;
923
924     if (argc < 2 || argc > 3) {
925         printf("Format is 'set Name Value'\n'set ?' for help.\n");
926         return 0;
927     }
928     if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
929         for (ct = Setlist; ct->name; ct++)
930             printf("%-15s %s\n", ct->name, ct->help);
931         printf("\n");
932         settogglehelp(1);
933         printf("%-15s %s\n", "?", "display help information");
934         return 0;
935     }
936
937     ct = getset(argv[1]);
938     if (ct == 0) {
939         c = GETTOGGLE(argv[1]);
940         if (c == 0) {
941             fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
942                         argv[1]);
943             return 0;
944         } else if (Ambiguous((void *)c)) {
945             fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
946                         argv[1]);
947             return 0;
948         }
949         if (c->variable) {
950             if ((argc == 2) || (strcmp("on", argv[2]) == 0))
951                 *c->variable = 1;
952             else if (strcmp("off", argv[2]) == 0)
953                 *c->variable = 0;
954             else {
955                 printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n");
956                 return 0;
957             }
958             if (c->actionexplanation) {
959                 printf("%s %s.\n", *c->variable? "Will" : "Won't",
960                                                         c->actionexplanation);
961             }
962         }
963         if (c->handler)
964             (*c->handler)(1);
965     } else if (argc != 3) {
966         printf("Format is 'set Name Value'\n'set ?' for help.\n");
967         return 0;
968     } else if (Ambiguous((void *)ct)) {
969         fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
970                         argv[1]);
971         return 0;
972     } else if (ct->handler) {
973         (*ct->handler)(argv[2]);
974         printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp);
975     } else {
976         if (strcmp("off", argv[2])) {
977             value = special(argv[2]);
978         } else {
979             value = _POSIX_VDISABLE;
980         }
981         *(ct->charp) = (cc_t)value;
982         printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
983     }
984     slc_check();
985     return 1;
986 }
987
988 static int
989 unsetcmd(int argc, char *argv[])
990 {
991     struct setlist *ct;
992     struct togglelist *c;
993     char *name;
994
995     if (argc < 2) {
996         fprintf(stderr,
997             "Need an argument to 'unset' command.  'unset ?' for help.\n");
998         return 0;
999     }
1000     if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
1001         for (ct = Setlist; ct->name; ct++)
1002             printf("%-15s %s\n", ct->name, ct->help);
1003         printf("\n");
1004         settogglehelp(0);
1005         printf("%-15s %s\n", "?", "display help information");
1006         return 0;
1007     }
1008
1009     argc--;
1010     argv++;
1011     while (argc--) {
1012         name = *argv++;
1013         ct = getset(name);
1014         if (ct == 0) {
1015             c = GETTOGGLE(name);
1016             if (c == 0) {
1017                 fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n",
1018                         name);
1019                 return 0;
1020             } else if (Ambiguous((void *)c)) {
1021                 fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
1022                         name);
1023                 return 0;
1024             }
1025             if (c->variable) {
1026                 *c->variable = 0;
1027                 if (c->actionexplanation) {
1028                     printf("%s %s.\n", *c->variable? "Will" : "Won't",
1029                                                         c->actionexplanation);
1030                 }
1031             }
1032             if (c->handler)
1033                 (*c->handler)(0);
1034         } else if (Ambiguous((void *)ct)) {
1035             fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
1036                         name);
1037             return 0;
1038         } else if (ct->handler) {
1039             (*ct->handler)(0);
1040             printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp);
1041         } else {
1042             *(ct->charp) = _POSIX_VDISABLE;
1043             printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
1044         }
1045     }
1046     return 1;
1047 }
1048 \f
1049 /*
1050  * The following are the data structures and routines for the
1051  * 'mode' command.
1052  */
1053 #ifdef  KLUDGELINEMODE
1054 extern int kludgelinemode;
1055
1056 static int
1057 dokludgemode(void)
1058 {
1059     kludgelinemode = 1;
1060     send_wont(TELOPT_LINEMODE, 1);
1061     send_dont(TELOPT_SGA, 1);
1062     send_dont(TELOPT_ECHO, 1);
1063     return 1;
1064 }
1065 #endif
1066
1067 static int
1068 dolinemode(void)
1069 {
1070 #ifdef  KLUDGELINEMODE
1071     if (kludgelinemode)
1072         send_dont(TELOPT_SGA, 1);
1073 #endif
1074     send_will(TELOPT_LINEMODE, 1);
1075     send_dont(TELOPT_ECHO, 1);
1076     return 1;
1077 }
1078
1079 static int
1080 docharmode(void)
1081 {
1082 #ifdef  KLUDGELINEMODE
1083     if (kludgelinemode)
1084         send_do(TELOPT_SGA, 1);
1085     else
1086 #endif
1087     send_wont(TELOPT_LINEMODE, 1);
1088     send_do(TELOPT_ECHO, 1);
1089     return 1;
1090 }
1091
1092 static int
1093 dolmmode(int bit, int on)
1094 {
1095     unsigned char c;
1096     extern int linemode;
1097
1098     if (my_want_state_is_wont(TELOPT_LINEMODE)) {
1099         printf("?Need to have LINEMODE option enabled first.\n");
1100         printf("'mode ?' for help.\n");
1101         return 0;
1102     }
1103
1104     if (on)
1105         c = (linemode | bit);
1106     else
1107         c = (linemode & ~bit);
1108     lm_mode(&c, 1, 1);
1109     return 1;
1110 }
1111
1112 static int
1113 setmod(int bit)
1114 {
1115     return dolmmode(bit, 1);
1116 }
1117
1118 static int
1119 clearmode(int bit)
1120 {
1121     return dolmmode(bit, 0);
1122 }
1123
1124 struct modelist {
1125         const char      *name;  /* command name */
1126         const char      *help;  /* help string */
1127         int     (*handler)(int);/* routine which executes command */
1128         int     needconnect;    /* Do we need to be connected to execute? */
1129         int     arg1;
1130 };
1131
1132 static struct modelist ModeList[] = {
1133     { "character", "Disable LINEMODE option",   (int (*)(int))docharmode, 1, 0 },
1134 #ifdef  KLUDGELINEMODE
1135     { "",       "(or disable obsolete line-by-line mode)", NULL, 0, 0 },
1136 #endif
1137     { "line",   "Enable LINEMODE option",       (int (*)(int))dolinemode, 1, 0 },
1138 #ifdef  KLUDGELINEMODE
1139     { "",       "(or enable obsolete line-by-line mode)", NULL, 0, 0 },
1140 #endif
1141     { "", "", NULL, 0, 0 },
1142     { "",       "These require the LINEMODE option to be enabled", NULL, 0, 0 },
1143     { "isig",   "Enable signal trapping",       setmod, 1, MODE_TRAPSIG },
1144     { "+isig",  0,                              setmod, 1, MODE_TRAPSIG },
1145     { "-isig",  "Disable signal trapping",      clearmode, 1, MODE_TRAPSIG },
1146     { "edit",   "Enable character editing",     setmod, 1, MODE_EDIT },
1147     { "+edit",  0,                              setmod, 1, MODE_EDIT },
1148     { "-edit",  "Disable character editing",    clearmode, 1, MODE_EDIT },
1149     { "softtabs", "Enable tab expansion",       setmod, 1, MODE_SOFT_TAB },
1150     { "+softtabs", 0,                           setmod, 1, MODE_SOFT_TAB },
1151     { "-softtabs", "Disable character editing", clearmode, 1, MODE_SOFT_TAB },
1152     { "litecho", "Enable literal character echo", setmod, 1, MODE_LIT_ECHO },
1153     { "+litecho", 0,                            setmod, 1, MODE_LIT_ECHO },
1154     { "-litecho", "Disable literal character echo", clearmode, 1, MODE_LIT_ECHO },
1155     { "help",   0,                              (int (*)(int))modehelp, 0, 0 },
1156 #ifdef  KLUDGELINEMODE
1157     { "kludgeline", 0,                          (int (*)(int))dokludgemode, 1, 0 },
1158 #endif
1159     { "", "", NULL, 0, 0 },
1160     { "?",      "Print help information",       (int (*)(int))modehelp, 0, 0 },
1161     { NULL, NULL, NULL, 0, 0 },
1162 };
1163
1164
1165 static int
1166 modehelp(void)
1167 {
1168     struct modelist *mt;
1169
1170     printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
1171     for (mt = ModeList; mt->name; mt++) {
1172         if (mt->help) {
1173             if (*mt->help)
1174                 printf("%-15s %s\n", mt->name, mt->help);
1175             else
1176                 printf("\n");
1177         }
1178     }
1179     return 0;
1180 }
1181
1182 #define GETMODECMD(name) (struct modelist *) \
1183                 genget(name, (char **) ModeList, sizeof(struct modelist))
1184
1185 static int
1186 modecmd(int argc, char *argv[])
1187 {
1188     struct modelist *mt;
1189
1190     if (argc != 2) {
1191         printf("'mode' command requires an argument\n");
1192         printf("'mode ?' for help.\n");
1193     } else if ((mt = GETMODECMD(argv[1])) == 0) {
1194         fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
1195     } else if (Ambiguous((void *)mt)) {
1196         fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
1197     } else if (mt->needconnect && !connected) {
1198         printf("?Need to be connected first.\n");
1199         printf("'mode ?' for help.\n");
1200     } else if (mt->handler) {
1201         return (*mt->handler)(mt->arg1);
1202     }
1203     return 0;
1204 }
1205 \f
1206 /*
1207  * The following data structures and routines implement the
1208  * "display" command.
1209  */
1210
1211 static int
1212 display(int argc, char *argv[])
1213 {
1214     struct togglelist *tl;
1215     struct setlist *sl;
1216
1217 #define dotog(tl)       if (tl->variable && tl->actionexplanation) { \
1218                             if (*tl->variable) { \
1219                                 printf("will"); \
1220                             } else { \
1221                                 printf("won't"); \
1222                             } \
1223                             printf(" %s.\n", tl->actionexplanation); \
1224                         }
1225
1226 #define doset(sl)   if (sl->name && *sl->name != ' ') { \
1227                         if (sl->handler == 0) \
1228                             printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \
1229                         else \
1230                             printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \
1231                     }
1232
1233     if (argc == 1) {
1234         for (tl = Togglelist; tl->name; tl++) {
1235             dotog(tl);
1236         }
1237         printf("\n");
1238         for (sl = Setlist; sl->name; sl++) {
1239             doset(sl);
1240         }
1241     } else {
1242         int i;
1243
1244         for (i = 1; i < argc; i++) {
1245             sl = getset(argv[i]);
1246             tl = GETTOGGLE(argv[i]);
1247             if (Ambiguous((void *)sl) || Ambiguous((void *)tl)) {
1248                 printf("?Ambiguous argument '%s'.\n", argv[i]);
1249                 return 0;
1250             } else if (!sl && !tl) {
1251                 printf("?Unknown argument '%s'.\n", argv[i]);
1252                 return 0;
1253             } else {
1254                 if (tl) {
1255                     dotog(tl);
1256                 }
1257                 if (sl) {
1258                     doset(sl);
1259                 }
1260             }
1261         }
1262     }
1263 /*@*/optionstatus();
1264 #ifdef  ENCRYPTION
1265     EncryptStatus();
1266 #endif  /* ENCRYPTION */
1267     return 1;
1268 #undef  doset
1269 #undef  dotog
1270 }
1271 \f
1272 /*
1273  * The following are the data structures, and many of the routines,
1274  * relating to command processing.
1275  */
1276
1277 /*
1278  * Set the escape character.
1279  */
1280 static int
1281 setescape(int argc, char *argv[])
1282 {
1283         char *arg;
1284         char buf[50];
1285
1286         printf(
1287             "Deprecated usage - please use 'set escape%s%s' in the future.\n",
1288                                 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
1289         if (argc > 2)
1290                 arg = argv[1];
1291         else {
1292                 printf("new escape character: ");
1293                 (void) fgets(buf, sizeof(buf), stdin);
1294                 arg = buf;
1295         }
1296         if (arg[0] != '\0')
1297                 escape = arg[0];
1298         (void) fflush(stdout);
1299         return 1;
1300 }
1301
1302 static int
1303 togcrmod(void)
1304 {
1305     crmod = !crmod;
1306     printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
1307     printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
1308     (void) fflush(stdout);
1309     return 1;
1310 }
1311
1312 static int
1313 suspend(void)
1314 {
1315 #ifdef  SIGTSTP
1316     setcommandmode();
1317     {
1318         long oldrows, oldcols, newrows, newcols, err_;
1319
1320         err_ = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1321         (void) kill(0, SIGTSTP);
1322         /*
1323          * If we didn't get the window size before the SUSPEND, but we
1324          * can get them now (?), then send the NAWS to make sure that
1325          * we are set up for the right window size.
1326          */
1327         if (TerminalWindowSize(&newrows, &newcols) && connected &&
1328             (err_ || ((oldrows != newrows) || (oldcols != newcols)))) {
1329                 sendnaws();
1330         }
1331     }
1332     /* reget parameters in case they were changed */
1333     TerminalSaveState();
1334     setconnmode(0);
1335 #else
1336     printf("Suspend is not supported.  Try the '!' command instead\n");
1337 #endif
1338     return 1;
1339 }
1340
1341 static int
1342 shell(int argc, char *argv[] __unused)
1343 {
1344     long oldrows, oldcols, newrows, newcols, err_;
1345
1346     setcommandmode();
1347
1348     err_ = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1349     switch(vfork()) {
1350     case -1:
1351         perror("Fork failed\n");
1352         break;
1353
1354     case 0:
1355         {
1356             /*
1357              * Fire up the shell in the child.
1358              */
1359             const char *shellp, *shellname;
1360
1361             shellp = getenv("SHELL");
1362             if (shellp == NULL)
1363                 shellp = "/bin/sh";
1364             if ((shellname = strrchr(shellp, '/')) == 0)
1365                 shellname = shellp;
1366             else
1367                 shellname++;
1368             if (argc > 1)
1369                 execl(shellp, shellname, "-c", &saveline[1], NULL);
1370             else
1371                 execl(shellp, shellname, NULL);
1372             perror("Execl");
1373             _exit(1);
1374         }
1375     default:
1376             (void)wait(NULL);   /* Wait for the shell to complete */
1377
1378             if (TerminalWindowSize(&newrows, &newcols) && connected &&
1379                 (err_ || ((oldrows != newrows) || (oldcols != newcols)))) {
1380                     sendnaws();
1381             }
1382             break;
1383     }
1384     return 1;
1385 }
1386
1387 static int
1388 bye(int argc, char *argv[])
1389 {
1390     extern int resettermname;
1391
1392     if (connected) {
1393         (void) shutdown(net, SHUT_RDWR);
1394         printf("Connection closed.\n");
1395         (void) NetClose(net);
1396         connected = 0;
1397         resettermname = 1;
1398 #ifdef  AUTHENTICATION
1399 #ifdef  ENCRYPTION
1400         auth_encrypt_connect(connected);
1401 #endif
1402 #endif
1403         /* reset options */
1404         tninit();
1405     }
1406     if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
1407         longjmp(toplevel, 1);
1408         /* NOTREACHED */
1409     }
1410     return 1;                   /* Keep lint, etc., happy */
1411 }
1412
1413 void
1414 quit(void)
1415 {
1416         (void) call(bye, "bye", "fromquit", 0);
1417         Exit(0);
1418 }
1419
1420 static int
1421 logout(void)
1422 {
1423         send_do(TELOPT_LOGOUT, 1);
1424         (void) netflush();
1425         return 1;
1426 }
1427
1428 \f
1429 /*
1430  * The SLC command.
1431  */
1432
1433 struct slclist {
1434         const char      *name;
1435         const char      *help;
1436         void    (*handler)(int);
1437         int     arg;
1438 };
1439
1440 static void slc_help(void);
1441
1442 struct slclist SlcList[] = {
1443     { "export", "Use local special character definitions",
1444                                                 (void (*)(int))slc_mode_export, 0 },
1445     { "import", "Use remote special character definitions",
1446                                                 slc_mode_import,        1 },
1447     { "check",  "Verify remote special character definitions",
1448                                                 slc_mode_import,        0 },
1449     { "help",   NULL,                           (void (*)(int))slc_help,                0 },
1450     { "?",      "Print help information",       (void (*)(int))slc_help,                0 },
1451     { NULL, NULL, NULL, 0 },
1452 };
1453
1454 static void
1455 slc_help(void)
1456 {
1457     struct slclist *c;
1458
1459     for (c = SlcList; c->name; c++) {
1460         if (c->help) {
1461             if (*c->help)
1462                 printf("%-15s %s\n", c->name, c->help);
1463             else
1464                 printf("\n");
1465         }
1466     }
1467 }
1468
1469 static struct slclist *
1470 getslc(char *name)
1471 {
1472     return (struct slclist *)
1473                 genget(name, (char **) SlcList, sizeof(struct slclist));
1474 }
1475
1476 static int
1477 slccmd(int argc, char *argv[])
1478 {
1479     struct slclist *c;
1480
1481     if (argc != 2) {
1482         fprintf(stderr,
1483             "Need an argument to 'slc' command.  'slc ?' for help.\n");
1484         return 0;
1485     }
1486     c = getslc(argv[1]);
1487     if (c == 0) {
1488         fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n",
1489                                 argv[1]);
1490         return 0;
1491     }
1492     if (Ambiguous((void *)c)) {
1493         fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n",
1494                                 argv[1]);
1495         return 0;
1496     }
1497     (*c->handler)(c->arg);
1498     slcstate();
1499     return 1;
1500 }
1501 \f
1502 /*
1503  * The ENVIRON command.
1504  */
1505
1506 struct envlist {
1507         const char      *name;
1508         const char      *help;
1509         void    (*handler)(unsigned char *, unsigned char *);
1510         int     narg;
1511 };
1512
1513 extern struct env_lst *
1514         env_define(const unsigned char *, unsigned char *);
1515 extern void
1516         env_undefine(unsigned char *),
1517         env_export(const unsigned char *),
1518         env_unexport(const unsigned char *),
1519         env_send(unsigned char *),
1520 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
1521         env_varval(unsigned char *),
1522 #endif
1523         env_list(void);
1524 static void
1525         env_help(void);
1526
1527 struct envlist EnvList[] = {
1528     { "define", "Define an environment variable",
1529                                                 (void (*)(unsigned char *, unsigned char *))env_define, 2 },
1530     { "undefine", "Undefine an environment variable",
1531                                                 (void (*)(unsigned char *, unsigned char *))env_undefine,       1 },
1532     { "export", "Mark an environment variable for automatic export",
1533                                                 (void (*)(unsigned char *, unsigned char *))env_export, 1 },
1534     { "unexport", "Don't mark an environment variable for automatic export",
1535                                                 (void (*)(unsigned char *, unsigned char *))env_unexport,       1 },
1536     { "send",   "Send an environment variable", (void (*)(unsigned char *, unsigned char *))env_send,   1 },
1537     { "list",   "List the current environment variables",
1538                                                 (void (*)(unsigned char *, unsigned char *))env_list,   0 },
1539 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
1540     { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)",
1541                                                 (void (*)(unsigned char *, unsigned char *))env_varval,    1 },
1542 #endif
1543     { "help",   NULL,                           (void (*)(unsigned char *, unsigned char *))env_help,           0 },
1544     { "?",      "Print help information",       (void (*)(unsigned char *, unsigned char *))env_help,           0 },
1545     { NULL, NULL, NULL, 0 },
1546 };
1547
1548 static void
1549 env_help(void)
1550 {
1551     struct envlist *c;
1552
1553     for (c = EnvList; c->name; c++) {
1554         if (c->help) {
1555             if (*c->help)
1556                 printf("%-15s %s\n", c->name, c->help);
1557             else
1558                 printf("\n");
1559         }
1560     }
1561 }
1562
1563 static struct envlist *
1564 getenvcmd(char *name)
1565 {
1566     return (struct envlist *)
1567                 genget(name, (char **) EnvList, sizeof(struct envlist));
1568 }
1569
1570 static int
1571 env_cmd(int argc, char *argv[])
1572 {
1573     struct envlist *c;
1574
1575     if (argc < 2) {
1576         fprintf(stderr,
1577             "Need an argument to 'environ' command.  'environ ?' for help.\n");
1578         return 0;
1579     }
1580     c = getenvcmd(argv[1]);
1581     if (c == 0) {
1582         fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n",
1583                                 argv[1]);
1584         return 0;
1585     }
1586     if (Ambiguous((void *)c)) {
1587         fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n",
1588                                 argv[1]);
1589         return 0;
1590     }
1591     if (c->narg + 2 != argc) {
1592         fprintf(stderr,
1593             "Need %s%d argument%s to 'environ %s' command.  'environ ?' for help.\n",
1594                 c->narg < argc + 2 ? "only " : "",
1595                 c->narg, c->narg == 1 ? "" : "s", c->name);
1596         return 0;
1597     }
1598     (*c->handler)(argv[2], argv[3]);
1599     return 1;
1600 }
1601
1602 struct env_lst {
1603         struct env_lst *next;   /* pointer to next structure */
1604         struct env_lst *prev;   /* pointer to previous structure */
1605         unsigned char *var;     /* pointer to variable name */
1606         unsigned char *value;   /* pointer to variable value */
1607         int export;             /* 1 -> export with default list of variables */
1608         int welldefined;        /* A well defined variable */
1609 };
1610
1611 struct env_lst envlisthead;
1612
1613 static struct env_lst *
1614 env_find(const unsigned char *var)
1615 {
1616         struct env_lst *ep;
1617
1618         for (ep = envlisthead.next; ep; ep = ep->next) {
1619                 if (strcmp(ep->var, var) == 0)
1620                         return(ep);
1621         }
1622         return(NULL);
1623 }
1624
1625 void
1626 env_init(void)
1627 {
1628         extern char **environ;
1629         char **epp, *cp;
1630         struct env_lst *ep;
1631
1632         for (epp = environ; *epp; epp++) {
1633                 if ((cp = strchr(*epp, '='))) {
1634                         *cp = '\0';
1635                         ep = env_define((unsigned char *)*epp,
1636                                         (unsigned char *)cp+1);
1637                         ep->export = 0;
1638                         *cp = '=';
1639                 }
1640         }
1641         /*
1642          * Special case for DISPLAY variable.  If it is ":0.0" or
1643          * "unix:0.0", we have to get rid of "unix" and insert our
1644          * hostname.
1645          */
1646         if ((ep = env_find("DISPLAY"))
1647             && ((*ep->value == ':')
1648                 || (strncmp((char *)ep->value, "unix:", 5) == 0))) {
1649                 char hbuf[256+1];
1650                 char *cp2 = strchr((char *)ep->value, ':');
1651
1652                 gethostname(hbuf, 256);
1653                 hbuf[256] = '\0';
1654                 cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1);
1655                 sprintf((char *)cp, "%s%s", hbuf, cp2);
1656                 free(ep->value);
1657                 ep->value = (unsigned char *)cp;
1658         }
1659         /*
1660          * If USER is not defined, but LOGNAME is, then add
1661          * USER with the value from LOGNAME.  By default, we
1662          * don't export the USER variable.
1663          */
1664         if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) {
1665                 env_define("USER", ep->value);
1666                 env_unexport("USER");
1667         }
1668         env_export("DISPLAY");
1669         env_export("PRINTER");
1670 }
1671
1672 struct env_lst *
1673 env_define(const unsigned char *var, unsigned char *value)
1674 {
1675         struct env_lst *ep;
1676
1677         if ((ep = env_find(var))) {
1678                 if (ep->var)
1679                         free(ep->var);
1680                 if (ep->value)
1681                         free(ep->value);
1682         } else {
1683                 ep = (struct env_lst *)malloc(sizeof(struct env_lst));
1684                 ep->next = envlisthead.next;
1685                 envlisthead.next = ep;
1686                 ep->prev = &envlisthead;
1687                 if (ep->next)
1688                         ep->next->prev = ep;
1689         }
1690         ep->welldefined = opt_welldefined(var);
1691         ep->export = 1;
1692         ep->var = strdup(var);
1693         ep->value = strdup(value);
1694         return(ep);
1695 }
1696
1697 void
1698 env_undefine(unsigned char *var)
1699 {
1700         struct env_lst *ep;
1701
1702         if ((ep = env_find(var))) {
1703                 ep->prev->next = ep->next;
1704                 if (ep->next)
1705                         ep->next->prev = ep->prev;
1706                 if (ep->var)
1707                         free(ep->var);
1708                 if (ep->value)
1709                         free(ep->value);
1710                 free(ep);
1711         }
1712 }
1713
1714 void
1715 env_export(const unsigned char *var)
1716 {
1717         struct env_lst *ep;
1718
1719         if ((ep = env_find(var)))
1720                 ep->export = 1;
1721 }
1722
1723 void
1724 env_unexport(const unsigned char *var)
1725 {
1726         struct env_lst *ep;
1727
1728         if ((ep = env_find(var)))
1729                 ep->export = 0;
1730 }
1731
1732 void
1733 env_send(unsigned char *var)
1734 {
1735         struct env_lst *ep;
1736
1737         if (my_state_is_wont(TELOPT_NEW_ENVIRON)
1738 #ifdef  OLD_ENVIRON
1739             && my_state_is_wont(TELOPT_OLD_ENVIRON)
1740 #endif
1741                 ) {
1742                 fprintf(stderr,
1743                     "Cannot send '%s': Telnet ENVIRON option not enabled\n",
1744                                                                         var);
1745                 return;
1746         }
1747         ep = env_find(var);
1748         if (ep == 0) {
1749                 fprintf(stderr, "Cannot send '%s': variable not defined\n",
1750                                                                         var);
1751                 return;
1752         }
1753         env_opt_start_info();
1754         env_opt_add(ep->var);
1755         env_opt_end(0);
1756 }
1757
1758 void
1759 env_list(void)
1760 {
1761         struct env_lst *ep;
1762
1763         for (ep = envlisthead.next; ep; ep = ep->next) {
1764                 printf("%c %-20s %s\n", ep->export ? '*' : ' ',
1765                                         ep->var, ep->value);
1766         }
1767 }
1768
1769 unsigned char *
1770 env_default(int init, int welldefined)
1771 {
1772         static struct env_lst *nep = NULL;
1773
1774         if (init) {
1775                 nep = &envlisthead;
1776                 return(NULL);
1777         }
1778         if (nep) {
1779                 while ((nep = nep->next)) {
1780                         if (nep->export && (nep->welldefined == welldefined))
1781                                 return(nep->var);
1782                 }
1783         }
1784         return(NULL);
1785 }
1786
1787 unsigned char *
1788 env_getvalue(const unsigned char *var)
1789 {
1790         struct env_lst *ep;
1791
1792         if ((ep = env_find(var)))
1793                 return(ep->value);
1794         return(NULL);
1795 }
1796
1797 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
1798 void
1799 env_varval(unsigned char *what)
1800 {
1801         extern int old_env_var, old_env_value, env_auto;
1802         int len = strlen((char *)what);
1803
1804         if (len == 0)
1805                 goto unknown;
1806
1807         if (strncasecmp((char *)what, "status", len) == 0) {
1808                 if (env_auto)
1809                         printf("%s%s", "VAR and VALUE are/will be ",
1810                                         "determined automatically\n");
1811                 if (old_env_var == OLD_ENV_VAR)
1812                         printf("VAR and VALUE set to correct definitions\n");
1813                 else
1814                         printf("VAR and VALUE definitions are reversed\n");
1815         } else if (strncasecmp((char *)what, "auto", len) == 0) {
1816                 env_auto = 1;
1817                 old_env_var = OLD_ENV_VALUE;
1818                 old_env_value = OLD_ENV_VAR;
1819         } else if (strncasecmp((char *)what, "right", len) == 0) {
1820                 env_auto = 0;
1821                 old_env_var = OLD_ENV_VAR;
1822                 old_env_value = OLD_ENV_VALUE;
1823         } else if (strncasecmp((char *)what, "wrong", len) == 0) {
1824                 env_auto = 0;
1825                 old_env_var = OLD_ENV_VALUE;
1826                 old_env_value = OLD_ENV_VAR;
1827         } else {
1828 unknown:
1829                 printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n");
1830         }
1831 }
1832 #endif
1833
1834 #ifdef  AUTHENTICATION
1835 /*
1836  * The AUTHENTICATE command.
1837  */
1838
1839 struct authlist {
1840         const char      *name;
1841         const char      *help;
1842         int     (*handler)(char *);
1843         int     narg;
1844 };
1845
1846 extern int
1847         auth_enable(char *),
1848         auth_disable(char *),
1849         auth_status(void);
1850 static int
1851         auth_help(void);
1852
1853 struct authlist AuthList[] = {
1854     { "status", "Display current status of authentication information",
1855                                                 (int (*)(char *))auth_status,   0 },
1856     { "disable", "Disable an authentication type ('auth disable ?' for more)",
1857                                                 auth_disable,   1 },
1858     { "enable", "Enable an authentication type ('auth enable ?' for more)",
1859                                                 auth_enable,    1 },
1860     { "help",   NULL,                           (int (*)(char *))auth_help,             0 },
1861     { "?",      "Print help information",       (int (*)(char *))auth_help,             0 },
1862     { NULL, NULL, NULL, 0 },
1863 };
1864
1865 static int
1866 auth_help(void)
1867 {
1868     struct authlist *c;
1869
1870     for (c = AuthList; c->name; c++) {
1871         if (c->help) {
1872             if (*c->help)
1873                 printf("%-15s %s\n", c->name, c->help);
1874             else
1875                 printf("\n");
1876         }
1877     }
1878     return 0;
1879 }
1880
1881 int
1882 auth_cmd(int argc, char *argv[])
1883 {
1884     struct authlist *c;
1885
1886     if (argc < 2) {
1887         fprintf(stderr,
1888             "Need an argument to 'auth' command.  'auth ?' for help.\n");
1889         return 0;
1890     }
1891
1892     c = (struct authlist *)
1893                 genget(argv[1], (char **) AuthList, sizeof(struct authlist));
1894     if (c == 0) {
1895         fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n",
1896                                 argv[1]);
1897         return 0;
1898     }
1899     if (Ambiguous((void *)c)) {
1900         fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n",
1901                                 argv[1]);
1902         return 0;
1903     }
1904     if (c->narg + 2 != argc) {
1905         fprintf(stderr,
1906             "Need %s%d argument%s to 'auth %s' command.  'auth ?' for help.\n",
1907                 c->narg < argc + 2 ? "only " : "",
1908                 c->narg, c->narg == 1 ? "" : "s", c->name);
1909         return 0;
1910     }
1911     return((*c->handler)(argv[2]));
1912 }
1913 #endif
1914
1915 #ifdef  ENCRYPTION
1916 /*
1917  * The ENCRYPT command.
1918  */
1919
1920 struct encryptlist {
1921         const char      *name;
1922         const char      *help;
1923         int     (*handler)(char *, char *);
1924         int     needconnect;
1925         int     minarg;
1926         int     maxarg;
1927 };
1928
1929 extern int
1930         EncryptEnable(char *, char *),
1931         EncryptDisable(char *, char *),
1932         EncryptType(char *, char *),
1933         EncryptStart(char *),
1934         EncryptStartInput(void),
1935         EncryptStartOutput(void),
1936         EncryptStop(char *),
1937         EncryptStopInput(void),
1938         EncryptStopOutput(void),
1939         EncryptStatus(void);
1940 static int
1941         EncryptHelp(void);
1942
1943 struct encryptlist EncryptList[] = {
1944     { "enable", "Enable encryption. ('encrypt enable ?' for more)",
1945                                                 EncryptEnable, 1, 1, 2 },
1946     { "disable", "Disable encryption. ('encrypt enable ?' for more)",
1947                                                 EncryptDisable, 0, 1, 2 },
1948     { "type", "Set encryption type. ('encrypt type ?' for more)",
1949                                                 EncryptType, 0, 1, 1 },
1950     { "start", "Start encryption. ('encrypt start ?' for more)",
1951                                                 (int (*)(char *, char *))EncryptStart, 1, 0, 1 },
1952     { "stop", "Stop encryption. ('encrypt stop ?' for more)",
1953                                                 (int (*)(char *, char *))EncryptStop, 1, 0, 1 },
1954     { "input", "Start encrypting the input stream",
1955                                                 (int (*)(char *, char *))EncryptStartInput, 1, 0, 0 },
1956     { "-input", "Stop encrypting the input stream",
1957                                                 (int (*)(char *, char *))EncryptStopInput, 1, 0, 0 },
1958     { "output", "Start encrypting the output stream",
1959                                                 (int (*)(char *, char *))EncryptStartOutput, 1, 0, 0 },
1960     { "-output", "Stop encrypting the output stream",
1961                                                 (int (*)(char *, char *))EncryptStopOutput, 1, 0, 0 },
1962
1963     { "status", "Display current status of authentication information",
1964                                                 (int (*)(char *, char *))EncryptStatus, 0, 0, 0 },
1965     { "help",   NULL,                           (int (*)(char *, char *))EncryptHelp,   0, 0, 0 },
1966     { "?",      "Print help information",       (int (*)(char *, char *))EncryptHelp,   0, 0, 0 },
1967     { NULL, NULL, NULL, 0, 0, 0 },
1968 };
1969
1970 static int
1971 EncryptHelp(void)
1972 {
1973     struct encryptlist *c;
1974
1975     for (c = EncryptList; c->name; c++) {
1976         if (c->help) {
1977             if (*c->help)
1978                 printf("%-15s %s\n", c->name, c->help);
1979             else
1980                 printf("\n");
1981         }
1982     }
1983     return 0;
1984 }
1985
1986 static int
1987 encrypt_cmd(int argc, char *argv[])
1988 {
1989     struct encryptlist *c;
1990
1991     if (argc < 2) {
1992         fprintf(stderr,
1993             "Need an argument to 'encrypt' command.  'encrypt ?' for help.\n");
1994         return 0;
1995     }
1996
1997     c = (struct encryptlist *)
1998                 genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist));
1999     if (c == 0) {
2000         fprintf(stderr, "'%s': unknown argument ('encrypt ?' for help).\n",
2001                                 argv[1]);
2002         return 0;
2003     }
2004     if (Ambiguous((void *)c)) {
2005         fprintf(stderr, "'%s': ambiguous argument ('encrypt ?' for help).\n",
2006                                 argv[1]);
2007         return 0;
2008     }
2009     argc -= 2;
2010     if (argc < c->minarg || argc > c->maxarg) {
2011         if (c->minarg == c->maxarg) {
2012             fprintf(stderr, "Need %s%d argument%s ",
2013                 c->minarg < argc ? "only " : "", c->minarg,
2014                 c->minarg == 1 ? "" : "s");
2015         } else {
2016             fprintf(stderr, "Need %s%d-%d arguments ",
2017                 c->maxarg < argc ? "only " : "", c->minarg, c->maxarg);
2018         }
2019         fprintf(stderr, "to 'encrypt %s' command.  'encrypt ?' for help.\n",
2020                 c->name);
2021         return 0;
2022     }
2023     if (c->needconnect && !connected) {
2024         if (!(argc && (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) {
2025             printf("?Need to be connected first.\n");
2026             return 0;
2027         }
2028     }
2029     return ((*c->handler)(argc > 0 ? argv[2] : 0,
2030                         argc > 1 ? argv[3] : 0));
2031 }
2032 #endif  /* ENCRYPTION */
2033
2034 /*
2035  * Print status about the connection.
2036  */
2037 /*ARGSUSED*/
2038 static int
2039 status(int argc, char *argv[])
2040 {
2041     if (connected) {
2042         printf("Connected to %s.\n", hostname);
2043         if ((argc < 2) || strcmp(argv[1], "notmuch")) {
2044             int mode = getconnmode();
2045
2046             if (my_want_state_is_will(TELOPT_LINEMODE)) {
2047                 printf("Operating with LINEMODE option\n");
2048                 printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No");
2049                 printf("%s catching of signals\n",
2050                                         (mode&MODE_TRAPSIG) ? "Local" : "No");
2051                 slcstate();
2052 #ifdef  KLUDGELINEMODE
2053             } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) {
2054                 printf("Operating in obsolete linemode\n");
2055 #endif
2056             } else {
2057                 printf("Operating in single character mode\n");
2058                 if (localchars)
2059                     printf("Catching signals locally\n");
2060             }
2061             printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote");
2062             if (my_want_state_is_will(TELOPT_LFLOW))
2063                 printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No");
2064 #ifdef  ENCRYPTION
2065             encrypt_display();
2066 #endif  /* ENCRYPTION */
2067         }
2068     } else {
2069         printf("No connection.\n");
2070     }
2071     printf("Escape character is '%s'.\n", control(escape));
2072     (void) fflush(stdout);
2073     return 1;
2074 }
2075
2076 #ifdef  SIGINFO
2077 /*
2078  * Function that gets called when SIGINFO is received.
2079  */
2080 void
2081 ayt_status(void)
2082 {
2083     (void) call(status, "status", "notmuch", 0);
2084 }
2085 #endif
2086
2087 static const char *
2088 sockaddr_ntop(struct sockaddr *sa)
2089 {
2090     void *addr;
2091     static char addrbuf[INET6_ADDRSTRLEN];
2092
2093     switch (sa->sa_family) {
2094     case AF_INET:
2095         addr = &((struct sockaddr_in *)sa)->sin_addr;
2096         break;
2097     case AF_UNIX:
2098         addr = &((struct sockaddr_un *)sa)->sun_path;
2099         break;
2100 #ifdef INET6
2101     case AF_INET6:
2102         addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
2103         break;
2104 #endif
2105     default:
2106         return NULL;
2107     }
2108     inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf));
2109     return addrbuf;
2110 }
2111
2112 #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
2113 static int
2114 setpolicy(int lnet, struct addrinfo *res, char *policy)
2115 {
2116         char *buf;
2117         int level;
2118         int optname;
2119
2120         if (policy == NULL)
2121                 return 0;
2122
2123         buf = ipsec_set_policy(policy, strlen(policy));
2124         if (buf == NULL) {
2125                 printf("%s\n", ipsec_strerror());
2126                 return -1;
2127         }
2128         level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
2129         optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY;
2130         if (setsockopt(lnet, level, optname, buf, ipsec_get_policylen(buf)) < 0){
2131                 perror("setsockopt");
2132                 return -1;
2133         }
2134
2135         free(buf);
2136         return 0;
2137 }
2138 #endif
2139
2140 #ifdef INET6
2141 /*
2142  * When an Address Family related error happend, check if retry with
2143  * another AF is possible or not.
2144  * Return 1, if retry with another af is OK. Else, return 0.
2145  */
2146 static int
2147 switch_af(struct addrinfo **aip)
2148 {
2149     int nextaf;
2150     struct addrinfo *ai;
2151
2152     ai = *aip;
2153     nextaf = (ai->ai_family == AF_INET) ? AF_INET6 : AF_INET;
2154     do
2155         ai=ai->ai_next;
2156     while (ai != NULL && ai->ai_family != nextaf);
2157     *aip = ai;
2158     if (*aip != NULL) {
2159         return 1;
2160     }
2161     return 0;
2162 }
2163 #endif
2164
2165 int
2166 tn(int argc, char *argv[])
2167 {
2168     char *srp = 0;
2169     int proto, opt;
2170     int srlen;
2171     int srcroute = 0, result;
2172     char *cmd, *hostp = 0, *portp = 0, *user = 0;
2173     char *src_addr = NULL;
2174     struct addrinfo hints, *res, *res0 = NULL, *src_res, *src_res0 = NULL;
2175     int error = 0, af_error = 0;
2176
2177     if (connected) {
2178         printf("?Already connected to %s\n", hostname);
2179         setuid(getuid());
2180         return 0;
2181     }
2182     if (argc < 2) {
2183         (void) strcpy(line, "open ");
2184         printf("(to) ");
2185         (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin);
2186         makeargv();
2187         argc = margc;
2188         argv = margv;
2189     }
2190     cmd = *argv;
2191     --argc; ++argv;
2192     while (argc) {
2193         if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?"))
2194             goto usage;
2195         if (strcmp(*argv, "-l") == 0) {
2196             --argc; ++argv;
2197             if (argc == 0)
2198                 goto usage;
2199             user = *argv++;
2200             --argc;
2201             continue;
2202         }
2203         if (strcmp(*argv, "-a") == 0) {
2204             --argc; ++argv;
2205             autologin = 1;
2206             continue;
2207         }
2208         if (strcmp(*argv, "-s") == 0) {
2209             --argc; ++argv;
2210             if (argc == 0)
2211                 goto usage;
2212             src_addr = *argv++;
2213             --argc;
2214             continue;
2215         }
2216         if (hostp == 0) {
2217             hostp = *argv++;
2218             --argc;
2219             continue;
2220         }
2221         if (portp == 0) {
2222             portp = *argv++;
2223             --argc;
2224             continue;
2225         }
2226     usage:
2227         printf("usage: %s [-l user] [-a] [-s src_addr] host-name [port]\n", cmd);
2228         setuid(getuid());
2229         return 0;
2230     }
2231     if (hostp == 0)
2232         goto usage;
2233
2234     if (src_addr != NULL) {
2235         memset(&hints, 0, sizeof(hints));
2236         hints.ai_flags = AI_NUMERICHOST;
2237         hints.ai_family = family;
2238         hints.ai_socktype = SOCK_STREAM;
2239         error = getaddrinfo(src_addr, 0, &hints, &src_res);
2240         if (error == EAI_NODATA) {
2241                 hints.ai_flags = 0;
2242                 error = getaddrinfo(src_addr, 0, &hints, &src_res);
2243         }
2244         if (error != 0) {
2245                 fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error));
2246                 if (error == EAI_SYSTEM)
2247                         fprintf(stderr, "%s: %s\n", src_addr, strerror(errno));
2248                 setuid(getuid());
2249                 return 0;
2250         }
2251         src_res0 = src_res;
2252     }
2253     if (hostp[0] == '/') {
2254         struct sockaddr_un su;
2255         
2256         if (strlen(hostp) >= sizeof(su.sun_path)) {
2257             fprintf(stderr, "hostname too long for unix domain socket: %s",
2258                     hostp);
2259                 goto fail;
2260         }
2261         memset(&su, 0, sizeof su);
2262         su.sun_family = AF_UNIX;
2263         strncpy(su.sun_path, hostp, sizeof su.sun_path);
2264         printf("Trying %s...\n", hostp);
2265         net = socket(PF_UNIX, SOCK_STREAM, 0);
2266         if ( net < 0) {
2267             perror("socket");
2268             goto fail;
2269         }
2270         if (connect(net, (struct sockaddr *)&su, sizeof su) == -1) {
2271             perror(su.sun_path);
2272             (void) NetClose(net);
2273             goto fail;
2274         }
2275         goto af_unix;
2276     } else if (hostp[0] == '@' || hostp[0] == '!') {
2277         if (
2278 #ifdef INET6
2279             family == AF_INET6 ||
2280 #endif
2281             (hostname = strrchr(hostp, ':')) == NULL)
2282             hostname = strrchr(hostp, '@');
2283         if (hostname == NULL) {
2284             hostname = hostp;
2285         } else {
2286             hostname++;
2287             srcroute = 1;
2288         }
2289     } else
2290         hostname = hostp;
2291     if (!portp) {
2292       telnetport = 1;
2293       portp = strdup("telnet");
2294     } else if (*portp == '-') {
2295       portp++;
2296       telnetport = 1;
2297     } else
2298       telnetport = 0;
2299
2300     memset(&hints, 0, sizeof(hints));
2301     hints.ai_flags = AI_NUMERICHOST;
2302     hints.ai_family = family;
2303     hints.ai_socktype = SOCK_STREAM;
2304     error = getaddrinfo(hostname, portp, &hints, &res);
2305     if (error) {
2306         hints.ai_flags = AI_CANONNAME;
2307         error = getaddrinfo(hostname, portp, &hints, &res);
2308     }
2309     if (error != 0) {
2310         fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error));
2311         if (error == EAI_SYSTEM)
2312             fprintf(stderr, "%s: %s\n", hostname, strerror(errno));
2313         setuid(getuid());
2314         goto fail;
2315     }
2316     if (hints.ai_flags == AI_NUMERICHOST) {
2317         /* hostname has numeric */
2318         int gni_err = 1;
2319
2320         if (doaddrlookup)
2321             gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len,
2322                                   _hostname, sizeof(_hostname) - 1, NULL, 0,
2323                                   NI_NAMEREQD);
2324         if (gni_err != 0)
2325             (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1);
2326         _hostname[sizeof(_hostname)-1] = '\0';
2327         hostname = _hostname;
2328     } else {
2329         /* hostname has FQDN */
2330         if (srcroute != 0)
2331             (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1);
2332         else if (res->ai_canonname != NULL)
2333           strcpy(_hostname, res->ai_canonname);
2334         else
2335           (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1);
2336         _hostname[sizeof(_hostname)-1] = '\0';
2337         hostname = _hostname;
2338     }
2339     res0 = res;
2340  #ifdef INET6
2341  af_again:
2342  #endif
2343     if (srcroute != 0) {
2344         static char hostbuf[BUFSIZ];
2345
2346         if (af_error == 0) { /* save intermediate hostnames for retry */
2347                 strncpy(hostbuf, hostp, BUFSIZ - 1);
2348                 hostbuf[BUFSIZ - 1] = '\0';
2349         } else
2350                 hostp = hostbuf;
2351         srp = 0;
2352         result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt);
2353         if (result == 0) {
2354 #ifdef INET6
2355             if (family == AF_UNSPEC && af_error == 0 &&
2356                 switch_af(&res) == 1) {
2357                 af_error = 1;
2358                 goto af_again;
2359             }
2360 #endif
2361             setuid(getuid());
2362             goto fail;
2363         } else if (result == -1) {
2364             printf("Bad source route option: %s\n", hostp);
2365             setuid(getuid());
2366             goto fail;
2367         }
2368     }
2369     do {
2370         printf("Trying %s...\n", sockaddr_ntop(res->ai_addr));
2371         net = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
2372         setuid(getuid());
2373         if (net < 0) {
2374 #ifdef INET6
2375             if (family == AF_UNSPEC && af_error == 0 &&
2376                 switch_af(&res) == 1) {
2377                 af_error = 1;
2378                 goto af_again;
2379             }
2380 #endif
2381             perror("telnet: socket");
2382             goto fail;
2383         }
2384         if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0)
2385                 perror("setsockopt (source route)");
2386 #if     defined(IPPROTO_IP) && defined(IP_TOS)
2387         if (res->ai_family == PF_INET) {
2388 # if    defined(HAS_GETTOS)
2389             struct tosent *tp;
2390             if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
2391                 tos = tp->t_tos;
2392 # endif
2393             if (tos < 0)
2394                 tos = IPTOS_LOWDELAY;
2395             if (tos
2396                 && (setsockopt(net, IPPROTO_IP, IP_TOS,
2397                     (char *)&tos, sizeof(int)) < 0)
2398                 && (errno != ENOPROTOOPT))
2399                     perror("telnet: setsockopt (IP_TOS) (ignored)");
2400         }
2401 #endif  /* defined(IPPROTO_IP) && defined(IP_TOS) */
2402
2403         if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
2404                 perror("setsockopt (SO_DEBUG)");
2405         }
2406
2407         if (src_addr != NULL) {
2408             for (src_res = src_res0; src_res != 0; src_res = src_res->ai_next)
2409                 if (src_res->ai_family == res->ai_family)
2410                     break;
2411             if (src_res == NULL)
2412                 src_res = src_res0;
2413             if (bind(net, src_res->ai_addr, src_res->ai_addrlen) == -1) {
2414 #ifdef INET6
2415                 if (family == AF_UNSPEC && af_error == 0 &&
2416                     switch_af(&res) == 1) {
2417                     af_error = 1;
2418                     (void) NetClose(net);
2419                     goto af_again;
2420                 }
2421 #endif
2422                 perror("bind");
2423                 (void) NetClose(net);
2424                 goto fail;
2425             }
2426         }
2427 #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
2428         if (setpolicy(net, res, ipsec_policy_in) < 0) {
2429                 (void) NetClose(net);
2430                 goto fail;
2431         }
2432         if (setpolicy(net, res, ipsec_policy_out) < 0) {
2433                 (void) NetClose(net);
2434                 goto fail;
2435         }
2436 #endif
2437
2438         if (connect(net, res->ai_addr, res->ai_addrlen) < 0) {
2439             struct addrinfo *next;
2440
2441             next = res->ai_next;
2442             /* If already an af failed, only try same af. */
2443             if (af_error != 0)
2444                 while (next != NULL && next->ai_family != res->ai_family)
2445                     next = next->ai_next;
2446             warn("connect to address %s", sockaddr_ntop(res->ai_addr));
2447             if (next != NULL) {
2448                 res = next;
2449                 (void) NetClose(net);
2450                 continue;
2451             }
2452             warnx("Unable to connect to remote host");
2453             (void) NetClose(net);
2454             goto fail;
2455         }
2456         connected++;
2457 #ifdef  AUTHENTICATION
2458 #ifdef  ENCRYPTION
2459         auth_encrypt_connect(connected);
2460 #endif
2461 #endif
2462     } while (connected == 0);
2463     freeaddrinfo(res0);
2464     if (src_res0 != NULL)
2465         freeaddrinfo(src_res0);
2466     cmdrc(hostp, hostname);
2467  af_unix:    
2468     if (autologin && user == NULL) {
2469         struct passwd *pw;
2470
2471         user = getenv("USER");
2472         if (user == NULL ||
2473             ((pw = getpwnam(user)) && pw->pw_uid != getuid())) {
2474                 if ((pw = getpwuid(getuid())))
2475                         user = pw->pw_name;
2476                 else
2477                         user = NULL;
2478         }
2479     }
2480     if (user) {
2481         env_define("USER", user);
2482         env_export("USER");
2483     }
2484     (void) call(status, "status", "notmuch", 0);
2485     if (setjmp(peerdied) == 0)
2486         telnet(user);
2487     (void) NetClose(net);
2488     ExitString("Connection closed by foreign host.\n",1);
2489     /*NOTREACHED*/
2490  fail:
2491     if (res0 != NULL)
2492         freeaddrinfo(res0);
2493     if (src_res0 != NULL)
2494         freeaddrinfo(src_res0);
2495     return 0;
2496 }
2497
2498 #define HELPINDENT (sizeof ("connect"))
2499
2500 static char
2501         openhelp[] =    "connect to a site",
2502         closehelp[] =   "close current connection",
2503         logouthelp[] =  "forcibly logout remote user and close the connection",
2504         quithelp[] =    "exit telnet",
2505         statushelp[] =  "print status information",
2506         helphelp[] =    "print help information",
2507         sendhelp[] =    "transmit special characters ('send ?' for more)",
2508         sethelp[] =     "set operating parameters ('set ?' for more)",
2509         unsethelp[] =   "unset operating parameters ('unset ?' for more)",
2510         togglestring[] ="toggle operating parameters ('toggle ?' for more)",
2511         slchelp[] =     "change state of special characters ('slc ?' for more)",
2512         displayhelp[] = "display operating parameters",
2513 #ifdef  AUTHENTICATION
2514         authhelp[] =    "turn on (off) authentication ('auth ?' for more)",
2515 #endif
2516 #ifdef  ENCRYPTION
2517         encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)",
2518 #endif  /* ENCRYPTION */
2519         zhelp[] =       "suspend telnet",
2520 #ifdef OPIE
2521         opiehelp[] =    "compute response to OPIE challenge",
2522 #endif
2523         shellhelp[] =   "invoke a subshell",
2524         envhelp[] =     "change environment variables ('environ ?' for more)",
2525         modestring[] = "try to enter line or character mode ('mode ?' for more)";
2526
2527 static Command cmdtab[] = {
2528         { "close",      closehelp,      bye,            1 },
2529         { "logout",     logouthelp,     (int (*)(int, char **))logout,          1 },
2530         { "display",    displayhelp,    display,        0 },
2531         { "mode",       modestring,     modecmd,        0 },
2532         { "telnet",     openhelp,       tn,             0 },
2533         { "open",       openhelp,       tn,             0 },
2534         { "quit",       quithelp,       (int (*)(int, char **))quit,            0 },
2535         { "send",       sendhelp,       sendcmd,        0 },
2536         { "set",        sethelp,        setcmd,         0 },
2537         { "unset",      unsethelp,      unsetcmd,       0 },
2538         { "status",     statushelp,     status,         0 },
2539         { "toggle",     togglestring,   toggle,         0 },
2540         { "slc",        slchelp,        slccmd,         0 },
2541 #ifdef  AUTHENTICATION
2542         { "auth",       authhelp,       auth_cmd,       0 },
2543 #endif
2544 #ifdef  ENCRYPTION
2545         { "encrypt",    encrypthelp,    encrypt_cmd,    0 },
2546 #endif  /* ENCRYPTION */
2547         { "z",          zhelp,          (int (*)(int, char **))suspend, 0 },
2548         { "!",          shellhelp,      shell,          1 },
2549         { "environ",    envhelp,        env_cmd,        0 },
2550         { "?",          helphelp,       help,           0 },
2551 #ifdef OPIE
2552         { "opie",       opiehelp,       opie_calc,      0 },
2553 #endif          
2554         { NULL, NULL, NULL, 0 }
2555 };
2556
2557 static char     crmodhelp[] =   "deprecated command -- use 'toggle crmod' instead";
2558 static char     escapehelp[] =  "deprecated command -- use 'set escape' instead";
2559
2560 static Command cmdtab2[] = {
2561         { "help",       0,              help,           0 },
2562         { "escape",     escapehelp,     setescape,      0 },
2563         { "crmod",      crmodhelp,      (int (*)(int, char **))togcrmod,        0 },
2564         { NULL, NULL, NULL, 0 }
2565 };
2566
2567
2568 /*
2569  * Call routine with argc, argv set from args (terminated by 0).
2570  */
2571
2572 static int
2573 call(intrtn_t routine, ...)
2574 {
2575     va_list ap;
2576     char *args[100];
2577     int argno = 0;
2578
2579     va_start(ap, routine);
2580     while ((args[argno++] = va_arg(ap, char *)) != 0);
2581     va_end(ap);
2582     return (*routine)(argno-1, args);
2583 }
2584
2585
2586 static Command *
2587 getcmd(char *name)
2588 {
2589     Command *cm;
2590
2591     if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))))
2592         return cm;
2593     return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
2594 }
2595
2596 void
2597 command(int top, const char *tbuf, int cnt)
2598 {
2599     Command *c;
2600
2601     setcommandmode();
2602     if (!top) {
2603         putchar('\n');
2604     } else {
2605         (void) signal(SIGINT, SIG_DFL);
2606         (void) signal(SIGQUIT, SIG_DFL);
2607     }
2608     for (;;) {
2609         if (rlogin == _POSIX_VDISABLE)
2610                 printf("%s> ", prompt);
2611         if (tbuf) {
2612             char *cp;
2613             cp = line;
2614             while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
2615                 cnt--;
2616             tbuf = 0;
2617             if (cp == line || *--cp != '\n' || cp == line)
2618                 goto getline;
2619             *cp = '\0';
2620             if (rlogin == _POSIX_VDISABLE)
2621                 printf("%s\n", line);
2622         } else {
2623         getline:
2624             if (rlogin != _POSIX_VDISABLE)
2625                 printf("%s> ", prompt);
2626             if (fgets(line, sizeof(line), stdin) == NULL) {
2627                 if (feof(stdin) || ferror(stdin)) {
2628                     (void) quit();
2629                     /*NOTREACHED*/
2630                 }
2631                 break;
2632             }
2633         }
2634         if (line[0] == 0)
2635             break;
2636         makeargv();
2637         if (margv[0] == 0) {
2638             break;
2639         }
2640         c = getcmd(margv[0]);
2641         if (Ambiguous((void *)c)) {
2642             printf("?Ambiguous command\n");
2643             continue;
2644         }
2645         if (c == 0) {
2646             printf("?Invalid command\n");
2647             continue;
2648         }
2649         if (c->needconnect && !connected) {
2650             printf("?Need to be connected first.\n");
2651             continue;
2652         }
2653         if ((*c->handler)(margc, margv)) {
2654             break;
2655         }
2656     }
2657     if (!top) {
2658         if (!connected) {
2659             longjmp(toplevel, 1);
2660             /*NOTREACHED*/
2661         }
2662         setconnmode(0);
2663     }
2664 }
2665 \f
2666 /*
2667  * Help command.
2668  */
2669 static int
2670 help(int argc, char *argv[])
2671 {
2672         Command *c;
2673
2674         if (argc == 1) {
2675                 printf("Commands may be abbreviated.  Commands are:\n\n");
2676                 for (c = cmdtab; c->name; c++)
2677                         if (c->help) {
2678                                 printf("%-*s\t%s\n", (int)HELPINDENT, c->name,
2679                                                                     c->help);
2680                         }
2681                 return 0;
2682         }
2683         else while (--argc > 0) {
2684                 char *arg;
2685                 arg = *++argv;
2686                 c = getcmd(arg);
2687                 if (Ambiguous((void *)c))
2688                         printf("?Ambiguous help command %s\n", arg);
2689                 else if (c == NULL)
2690                         printf("?Invalid help command %s\n", arg);
2691                 else
2692                         printf("%s\n", c->help);
2693         }
2694         return 0;
2695 }
2696
2697 static char *rcname = 0;
2698 static char rcbuf[128];
2699
2700 void
2701 cmdrc(char *m1, char *m2)
2702 {
2703     Command *c;
2704     FILE *rcfile;
2705     int gotmachine = 0;
2706     int l1 = strlen(m1);
2707     int l2 = strlen(m2);
2708     char m1save[MAXHOSTNAMELEN];
2709
2710     if (skiprc)
2711         return;
2712
2713     strlcpy(m1save, m1, sizeof(m1save));
2714     m1 = m1save;
2715
2716     if (rcname == 0) {
2717         rcname = getenv("HOME");
2718         if (rcname && (strlen(rcname) + 10) < sizeof(rcbuf))
2719             strcpy(rcbuf, rcname);
2720         else
2721             rcbuf[0] = '\0';
2722         strcat(rcbuf, "/.telnetrc");
2723         rcname = rcbuf;
2724     }
2725
2726     if ((rcfile = fopen(rcname, "r")) == 0) {
2727         return;
2728     }
2729
2730     for (;;) {
2731         if (fgets(line, sizeof(line), rcfile) == NULL)
2732             break;
2733         if (line[0] == 0)
2734             break;
2735         if (line[0] == '#')
2736             continue;
2737         if (gotmachine) {
2738             if (!isspace(line[0]))
2739                 gotmachine = 0;
2740         }
2741         if (gotmachine == 0) {
2742             if (isspace(line[0]))
2743                 continue;
2744             if (strncasecmp(line, m1, l1) == 0)
2745                 strncpy(line, &line[l1], sizeof(line) - l1);
2746             else if (strncasecmp(line, m2, l2) == 0)
2747                 strncpy(line, &line[l2], sizeof(line) - l2);
2748             else if (strncasecmp(line, "DEFAULT", 7) == 0)
2749                 strncpy(line, &line[7], sizeof(line) - 7);
2750             else
2751                 continue;
2752             if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
2753                 continue;
2754             gotmachine = 1;
2755         }
2756         makeargv();
2757         if (margv[0] == 0)
2758             continue;
2759         c = getcmd(margv[0]);
2760         if (Ambiguous((void *)c)) {
2761             printf("?Ambiguous command: %s\n", margv[0]);
2762             continue;
2763         }
2764         if (c == 0) {
2765             printf("?Invalid command: %s\n", margv[0]);
2766             continue;
2767         }
2768         /*
2769          * This should never happen...
2770          */
2771         if (c->needconnect && !connected) {
2772             printf("?Need to be connected first for %s.\n", margv[0]);
2773             continue;
2774         }
2775         (*c->handler)(margc, margv);
2776     }
2777     fclose(rcfile);
2778 }
2779
2780 /*
2781  * Source route is handed in as
2782  *      [!]@hop1@hop2...[@|:]dst
2783  * If the leading ! is present, it is a
2784  * strict source route, otherwise it is
2785  * assmed to be a loose source route.
2786  *
2787  * We fill in the source route option as
2788  *      hop1,hop2,hop3...dest
2789  * and return a pointer to hop1, which will
2790  * be the address to connect() to.
2791  *
2792  * Arguments:
2793  *
2794  *      res:    ponter to addrinfo structure which contains sockaddr to
2795  *              the host to connect to.
2796  *
2797  *      arg:    pointer to route list to decipher
2798  *
2799  *      cpp:    If *cpp is not equal to NULL, this is a
2800  *              pointer to a pointer to a character array
2801  *              that should be filled in with the option.
2802  *
2803  *      lenp:   pointer to an integer that contains the
2804  *              length of *cpp if *cpp != NULL.
2805  *
2806  *      protop: pointer to an integer that should be filled in with
2807  *              appropriate protocol for setsockopt, as socket 
2808  *              protocol family.
2809  *
2810  *      optp:   pointer to an integer that should be filled in with
2811  *              appropriate option for setsockopt, as socket protocol
2812  *              family.
2813  *
2814  * Return values:
2815  *
2816  *      If the return value is 1, then all operations are
2817  *      successful. If the
2818  *      return value is -1, there was a syntax error in the
2819  *      option, either unknown characters, or too many hosts.
2820  *      If the return value is 0, one of the hostnames in the
2821  *      path is unknown, and *cpp is set to point to the bad
2822  *      hostname.
2823  *
2824  *      *cpp:   If *cpp was equal to NULL, it will be filled
2825  *              in with a pointer to our static area that has
2826  *              the option filled in.  This will be 32bit aligned.
2827  *
2828  *      *lenp:  This will be filled in with how long the option
2829  *              pointed to by *cpp is.
2830  *
2831  *      *protop: This will be filled in with appropriate protocol for
2832  *               setsockopt, as socket protocol family.
2833  *
2834  *      *optp:  This will be filled in with appropriate option for
2835  *              setsockopt, as socket protocol family.
2836  */
2837 static int
2838 sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop, int *optp)
2839 {
2840         static char buf[1024 + ALIGNBYTES];     /*XXX*/
2841         char *cp, *cp2, *lsrp, *ep;
2842         struct sockaddr_in *_sin;
2843 #ifdef INET6
2844         struct sockaddr_in6 *sin6;
2845         struct cmsghdr *cmsg;
2846         struct ip6_rthdr *rth;
2847 #endif
2848         struct addrinfo hints, *res;
2849         int error;
2850         char c;
2851
2852         /*
2853          * Verify the arguments, and make sure we have
2854          * at least 7 bytes for the option.
2855          */
2856         if (cpp == NULL || lenp == NULL)
2857                 return -1;
2858         if (*cpp != NULL) {
2859                 switch (res->ai_family) {
2860                 case AF_INET:
2861                         if (*lenp < 7)
2862                                 return -1;
2863                         break;
2864 #ifdef INET6
2865                 case AF_INET6:
2866                         if (*lenp < (int)CMSG_SPACE(sizeof(struct ip6_rthdr) +
2867                                                sizeof(struct in6_addr)))
2868                                 return -1;
2869                         break;
2870 #endif
2871                 }
2872         }
2873         /*
2874          * Decide whether we have a buffer passed to us,
2875          * or if we need to use our own static buffer.
2876          */
2877         if (*cpp) {
2878                 lsrp = *cpp;
2879                 ep = lsrp + *lenp;
2880         } else {
2881                 *cpp = lsrp = (char *)ALIGN(buf);
2882                 ep = lsrp + 1024;
2883         }
2884
2885         cp = arg;
2886
2887 #ifdef INET6
2888         if (ai->ai_family == AF_INET6) {
2889         /*
2890          * RFC3542 has obsoleted IPV6_PKTOPTIONS socket option.
2891          */
2892 #ifdef COMPAT_RFC1883           /* XXX */
2893                 cmsg = NULL;
2894                 if (*cp != '@')
2895                         return -1;
2896                 *protop = IPPROTO_IPV6;
2897                 *optp = IPV6_PKTOPTIONS;
2898 #else
2899                 return -1;
2900 #endif /* COMPAT_RFC1883 */
2901         } else
2902 #endif
2903       {
2904         /*
2905          * Next, decide whether we have a loose source
2906          * route or a strict source route, and fill in
2907          * the begining of the option.
2908          */
2909         if (*cp == '!') {
2910                 cp++;
2911                 *lsrp++ = IPOPT_SSRR;
2912         } else
2913                 *lsrp++ = IPOPT_LSRR;
2914
2915         if (*cp != '@')
2916                 return -1;
2917
2918         lsrp++;         /* skip over length, we'll fill it in later */
2919         *lsrp++ = 4;
2920         *protop = IPPROTO_IP;
2921         *optp = IP_OPTIONS;
2922       }
2923
2924         cp++;
2925         memset(&hints, 0, sizeof(hints));
2926         hints.ai_family = ai->ai_family;
2927         hints.ai_socktype = SOCK_STREAM;
2928         for (c = 0;;) {
2929                 if (
2930 #ifdef INET6
2931                     ai->ai_family != AF_INET6 &&
2932 #endif
2933                     c == ':')
2934                         cp2 = 0;
2935                 else for (cp2 = cp; (c = *cp2); cp2++) {
2936                         if (c == ',') {
2937                                 *cp2++ = '\0';
2938                                 if (*cp2 == '@')
2939                                         cp2++;
2940                         } else if (c == '@') {
2941                                 *cp2++ = '\0';
2942                         } else if (
2943 #ifdef INET6
2944                                    ai->ai_family != AF_INET6 &&
2945 #endif
2946                                    c == ':') {
2947                                 *cp2++ = '\0';
2948                         } else
2949                                 continue;
2950                         break;
2951                 }
2952                 if (!c)
2953                         cp2 = 0;
2954
2955                 hints.ai_flags = AI_NUMERICHOST;
2956                 error = getaddrinfo(cp, NULL, &hints, &res);
2957                 if (error == EAI_NODATA) {
2958                         hints.ai_flags = 0;
2959                         error = getaddrinfo(cp, NULL, &hints, &res);
2960                 }
2961                 if (error != 0) {
2962                         fprintf(stderr, "%s: %s\n", cp, gai_strerror(error));
2963                         if (error == EAI_SYSTEM)
2964                                 fprintf(stderr, "%s: %s\n", cp,
2965                                         strerror(errno));
2966                         *cpp = cp;
2967                         return(0);
2968                 }
2969 #ifdef INET6
2970                 if (res->ai_family == AF_INET6) {
2971                         return(0);
2972                 } else
2973 #endif
2974               {
2975                 _sin = (struct sockaddr_in *)res->ai_addr;
2976                 memcpy(lsrp, (char *)&_sin->sin_addr, 4);
2977                 lsrp += 4;
2978               }
2979                 if (cp2)
2980                         cp = cp2;
2981                 else
2982                         break;
2983                 /*
2984                  * Check to make sure there is space for next address
2985                  */
2986 #ifdef INET6
2987 #ifdef COMPAT_RFC1883           /* XXX */
2988                 if (res->ai_family == AF_INET6) {
2989                         if (((char *)CMSG_DATA(cmsg) +
2990                              sizeof(struct ip6_rthdr)) > ep)
2991                         return -1;
2992                 } else
2993 #endif /* COMPAT_RFC1883 */
2994 #endif
2995                 if (lsrp + 4 > ep)
2996                         return -1;
2997                 freeaddrinfo(res);
2998         }
2999 #ifdef INET6
3000         if (res->ai_family == AF_INET6) {
3001 #ifdef COMPAT_RFC1883           /* XXX */
3002                 *lenp = 0;
3003 #endif /* COMPAT_RFC1883 */
3004         } else
3005 #endif
3006       {
3007         if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) {
3008                 *cpp = 0;
3009                 *lenp = 0;
3010                 return -1;
3011         }
3012         *lsrp++ = IPOPT_NOP; /* 32 bit word align it */
3013         *lenp = lsrp - *cpp;
3014       }
3015         freeaddrinfo(res);
3016         return 1;
3017 }