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