xref: /freebsd/usr.sbin/ppp/command.c (revision 565e35e50e2cdac423588a3d18742544bde128b0)
1 /*
2  *		PPP User command processing module
3  *
4  *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5  *
6  *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the Internet Initiative Japan, Inc.  The name of the
14  * IIJ may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  * $Id: command.c,v 1.131.2.56 1998/04/07 23:45:45 brian Exp $
21  *
22  */
23 #include <sys/types.h>
24 #include <netinet/in_systm.h>
25 #include <netinet/in.h>
26 #include <netinet/ip.h>
27 #include <arpa/inet.h>
28 #include <sys/socket.h>
29 #include <net/route.h>
30 #include <netdb.h>
31 #include <sys/un.h>
32 
33 #ifndef NOALIAS
34 #include <alias.h>
35 #endif
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <paths.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/wait.h>
44 #include <termios.h>
45 #include <unistd.h>
46 
47 #include "command.h"
48 #include "mbuf.h"
49 #include "log.h"
50 #include "defs.h"
51 #include "timer.h"
52 #include "fsm.h"
53 #include "lcp.h"
54 #include "iplist.h"
55 #include "throughput.h"
56 #include "slcompress.h"
57 #include "ipcp.h"
58 #include "modem.h"
59 #ifndef NOALIAS
60 #include "alias_cmd.h"
61 #endif
62 #include "lqr.h"
63 #include "hdlc.h"
64 #include "loadalias.h"
65 #include "vars.h"
66 #include "systems.h"
67 #include "filter.h"
68 #include "descriptor.h"
69 #include "main.h"
70 #include "route.h"
71 #include "ccp.h"
72 #include "auth.h"
73 #include "async.h"
74 #include "link.h"
75 #include "physical.h"
76 #include "mp.h"
77 #include "bundle.h"
78 #include "server.h"
79 #include "prompt.h"
80 #include "chat.h"
81 #include "chap.h"
82 #include "datalink.h"
83 
84 static const char *HIDDEN = "********";
85 
86 static int ShowCommand(struct cmdargs const *);
87 static int TerminalCommand(struct cmdargs const *);
88 static int QuitCommand(struct cmdargs const *);
89 static int CloseCommand(struct cmdargs const *);
90 static int DownCommand(struct cmdargs const *);
91 static int AllowCommand(struct cmdargs const *);
92 static int SetCommand(struct cmdargs const *);
93 static int LinkCommand(struct cmdargs const *);
94 static int AddCommand(struct cmdargs const *);
95 static int DeleteCommand(struct cmdargs const *);
96 #ifndef NOALIAS
97 static int AliasCommand(struct cmdargs const *);
98 static int AliasEnable(struct cmdargs const *);
99 static int AliasOption(struct cmdargs const *);
100 #endif
101 
102 static int
103 HelpCommand(struct cmdargs const *arg)
104 {
105   struct cmdtab const *cmd;
106   int n, cmax, dmax, cols;
107 
108   if (!arg->prompt) {
109     LogPrintf(LogWARN, "help: Cannot help without a prompt\n");
110     return 0;
111   }
112 
113   if (arg->argc > 0) {
114     for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
115       if ((cmd->lauth & arg->prompt->auth) &&
116           ((cmd->name && !strcasecmp(cmd->name, *arg->argv)) ||
117            (cmd->alias && !strcasecmp(cmd->alias, *arg->argv)))) {
118 	prompt_Printf(arg->prompt, "%s\n", cmd->syntax);
119 	return 0;
120       }
121     return -1;
122   }
123 
124   cmax = dmax = 0;
125   for (cmd = arg->cmdtab; cmd->func; cmd++)
126     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
127       if ((n = strlen(cmd->name)) > cmax)
128         cmax = n;
129       if ((n = strlen(cmd->helpmes)) > dmax)
130         dmax = n;
131     }
132 
133   cols = 80 / (dmax + cmax + 3);
134   n = 0;
135   for (cmd = arg->cmdtab; cmd->func; cmd++)
136     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
137       prompt_Printf(arg->prompt, " %-*.*s: %-*.*s",
138               cmax, cmax, cmd->name, dmax, dmax, cmd->helpmes);
139       if (++n % cols == 0)
140         prompt_Printf(arg->prompt, "\n");
141     }
142   if (n % cols != 0)
143     prompt_Printf(arg->prompt, "\n");
144 
145   return 0;
146 }
147 
148 static int
149 CloneCommand(struct cmdargs const *arg)
150 {
151   int f;
152 
153   if (arg->argc == 0)
154     return -1;
155 
156   if (!arg->bundle->ncp.mp.active) {
157     LogPrintf(LogWARN, "clone: Only available in multilink mode\n");
158     return 1;
159   }
160 
161   for (f = 0; f < arg->argc; f++)
162     bundle_DatalinkClone(arg->bundle, arg->cx, arg->argv[f]);
163   return 0;
164 }
165 
166 static int
167 RemoveCommand(struct cmdargs const *arg)
168 {
169   if (arg->argc != 0)
170     return -1;
171 
172   if (!arg->bundle->ncp.mp.active) {
173     LogPrintf(LogWARN, "remove: Only available in multilink mode\n");
174     return 1;
175   }
176 
177   if (arg->cx->state != DATALINK_CLOSED) {
178     LogPrintf(LogWARN, "remove: Cannot delete links that aren't closed\n");
179     return 2;
180   }
181 
182   bundle_DatalinkRemove(arg->bundle, arg->cx);
183   return 0;
184 }
185 
186 int
187 LoadCommand(struct cmdargs const *arg)
188 {
189   const char *name;
190 
191   if (arg->argc > 0)
192     name = *arg->argv;
193   else
194     name = "default";
195 
196   if (!ValidSystem(name, arg->prompt, arg->bundle->phys_type)) {
197     LogPrintf(LogERROR, "%s: Label not allowed\n", name);
198     return 1;
199   } else if (SelectSystem(arg->bundle, name, CONFFILE, arg->prompt) < 0) {
200     LogPrintf(LogWARN, "%s: label not found.\n", name);
201     return -1;
202   } else
203     SetLabel(arg->argc ? name : NULL);
204   return 0;
205 }
206 
207 int
208 SaveCommand(struct cmdargs const *arg)
209 {
210   LogPrintf(LogWARN, "save command is not implemented (yet).\n");
211   return 1;
212 }
213 
214 static int
215 DialCommand(struct cmdargs const *arg)
216 {
217   int res;
218 
219   if ((arg->cx && !(arg->cx->physical->type & (PHYS_MANUAL|PHYS_DEMAND)))
220       || (!arg->cx && (arg->bundle->phys_type & ~(PHYS_MANUAL|PHYS_DEMAND)))) {
221     LogPrintf(LogWARN,
222               "Manual dial is only available in auto and interactive mode\n");
223     return 1;
224   }
225 
226   if (arg->argc > 0 && (res = LoadCommand(arg)) != 0)
227     return res;
228 
229   bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL);
230 
231   return 0;
232 }
233 
234 static int
235 ShellCommand(struct cmdargs const *arg, int bg)
236 {
237   const char *shell;
238   pid_t shpid;
239   int argc;
240   char *argv[MAXARGS];
241 
242 #ifdef SHELL_ONLY_INTERACTIVELY
243   /* we're only allowed to shell when we run ppp interactively */
244   if (arg->prompt && arg->prompt->owner) {
245     LogPrintf(LogWARN, "Can't start a shell from a network connection\n");
246     return 1;
247   }
248 #endif
249 
250   if (arg->argc == 0)
251     if (!arg->prompt) {
252       LogPrintf(LogWARN, "Can't start an interactive shell from"
253                 " a config file\n");
254       return 1;
255     } else if (arg->prompt->owner) {
256       LogPrintf(LogWARN, "Can't start an interactive shell from"
257                 " a socket connection\n");
258       return 1;
259     } else if (bg) {
260       LogPrintf(LogWARN, "Can only start an interactive shell in"
261 		" the foreground mode\n");
262       return 1;
263     }
264 
265   if ((shpid = fork()) == 0) {
266     int dtablesize, i, fd;
267 
268     if ((shell = getenv("SHELL")) == 0)
269       shell = _PATH_BSHELL;
270 
271     TermTimerService();
272     signal(SIGINT, SIG_DFL);
273     signal(SIGQUIT, SIG_DFL);
274     signal(SIGTERM, SIG_DFL);
275     signal(SIGHUP, SIG_DFL);
276     signal(SIGALRM, SIG_DFL);
277 
278     if (arg->prompt)
279       fd = arg->prompt->fd_out;
280     else if ((fd = open("/dev/null", O_RDWR)) == -1) {
281       LogPrintf(LogALERT, "Failed to open /dev/null: %s\n", strerror(errno));
282       exit(1);
283     }
284     for (i = 0; i < 3; i++)
285       dup2(fd, i);
286 
287     for (dtablesize = getdtablesize(), i = 3; i < dtablesize; i++)
288       close(i);
289 
290     setuid(geteuid());
291     if (arg->argc > 0) {
292       /* substitute pseudo args */
293       argv[0] = strdup(arg->argv[0]);
294       for (argc = 1; argc < arg->argc; argc++) {
295 	if (strcasecmp(arg->argv[argc], "HISADDR") == 0)
296 	  argv[argc] = strdup(inet_ntoa(arg->bundle->ncp.ipcp.peer_ip));
297 	else if (strcasecmp(arg->argv[argc], "INTERFACE") == 0)
298 	  argv[argc] = strdup(arg->bundle->ifname);
299 	else if (strcasecmp(arg->argv[argc], "MYADDR") == 0)
300 	  argv[argc] = strdup(inet_ntoa(arg->bundle->ncp.ipcp.my_ip));
301         else
302           argv[argc] = strdup(arg->argv[argc]);
303       }
304       argv[argc] = NULL;
305       if (bg) {
306 	pid_t p;
307 
308 	p = getpid();
309 	if (daemon(1, 1) == -1) {
310 	  LogPrintf(LogERROR, "%d: daemon: %s\n", p, strerror(errno));
311 	  exit(1);
312 	}
313       } else if (arg->prompt)
314         printf("ppp: Pausing until %s finishes\n", arg->argv[0]);
315       execvp(argv[0], argv);
316     } else {
317       if (arg->prompt)
318         printf("ppp: Pausing until %s finishes\n", shell);
319       prompt_TtyOldMode(arg->prompt);
320       execl(shell, shell, NULL);
321     }
322 
323     LogPrintf(LogWARN, "exec() of %s failed\n",
324               arg->argc > 0 ? arg->argv[0] : shell);
325     exit(255);
326   }
327 
328   if (shpid == (pid_t) - 1)
329     LogPrintf(LogERROR, "Fork failed: %s\n", strerror(errno));
330   else {
331     int status;
332     waitpid(shpid, &status, 0);
333   }
334 
335   if (arg->prompt && !arg->prompt->owner)
336     prompt_TtyCommandMode(arg->prompt);
337 
338   return 0;
339 }
340 
341 static int
342 BgShellCommand(struct cmdargs const *arg)
343 {
344   if (arg->argc == 0)
345     return -1;
346   return ShellCommand(arg, 1);
347 }
348 
349 static int
350 FgShellCommand(struct cmdargs const *arg)
351 {
352   return ShellCommand(arg, 0);
353 }
354 
355 static struct cmdtab const Commands[] = {
356   {"accept", NULL, AcceptCommand, LOCAL_AUTH,
357   "accept option request", "accept option .."},
358   {"add", NULL, AddCommand, LOCAL_AUTH,
359   "add route", "add dest mask gateway", NULL},
360   {NULL, "add!", AddCommand, LOCAL_AUTH,
361   "add or change route", "add! dest mask gateway", (void *)1},
362 #ifndef NOALIAS
363   {"alias", NULL, AliasCommand, LOCAL_AUTH,
364   "alias control", "alias option [yes|no]"},
365 #endif
366   {"allow", "auth", AllowCommand, LOCAL_AUTH,
367   "Allow ppp access", "allow users|modes ...."},
368   {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
369   "Run a background command", "[!]bg command"},
370   {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
371   "Clone a link", "clone newname..."},
372   {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
373   "Close connection", "close"},
374   {"delete", NULL, DeleteCommand, LOCAL_AUTH,
375   "delete route", "delete dest", NULL},
376   {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
377   "delete a route if it exists", "delete! dest", (void *)1},
378   {"deny", NULL, DenyCommand, LOCAL_AUTH,
379   "Deny option request", "deny option .."},
380   {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
381   "Dial and login", "dial|call [remote]"},
382   {"disable", NULL, DisableCommand, LOCAL_AUTH,
383   "Disable option", "disable option .."},
384   {"display", NULL, DisplayCommand, LOCAL_AUTH,
385   "Display option configs", "display"},
386   {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX,
387   "Generate a down event", "down"},
388   {"enable", NULL, EnableCommand, LOCAL_AUTH,
389   "Enable option", "enable option .."},
390   {"link", "datalink", LinkCommand, LOCAL_AUTH,
391   "Link specific commands", "link name command ..."},
392   {"load", NULL, LoadCommand, LOCAL_AUTH,
393   "Load settings", "load [remote]"},
394   {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
395   "Password for manipulation", "passwd LocalPassword"},
396   {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
397   "Quit PPP program", "quit|bye [all]"},
398   {"remove", NULL, RemoveCommand, LOCAL_AUTH | LOCAL_CX,
399   "Remove a link", "remove"},
400   {"save", NULL, SaveCommand, LOCAL_AUTH,
401   "Save settings", "save"},
402   {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
403   "Set parameters", "set[up] var value"},
404   {"shell", "!", FgShellCommand, LOCAL_AUTH,
405   "Run a subshell", "shell|! [sh command]"},
406   {"show", NULL, ShowCommand, LOCAL_AUTH,
407   "Show status and stats", "show var"},
408   {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
409   "Enter terminal mode", "term"},
410   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
411   "Display this message", "help|? [command]", Commands},
412   {NULL, NULL, NULL},
413 };
414 
415 static int
416 ShowEscape(struct cmdargs const *arg)
417 {
418   if (arg->cx->physical->async.cfg.EscMap[32]) {
419     int code, bit;
420     char *sep = "";
421 
422     for (code = 0; code < 32; code++)
423       if (arg->cx->physical->async.cfg.EscMap[code])
424 	for (bit = 0; bit < 8; bit++)
425 	  if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
426 	    prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
427             sep = ", ";
428           }
429     prompt_Printf(arg->prompt, "\n");
430   }
431   return 0;
432 }
433 
434 static int
435 ShowTimeout(struct cmdargs const *arg)
436 {
437   int remaining;
438 
439   prompt_Printf(arg->prompt, "Idle Timer: %ds\n",
440                 arg->bundle->cfg.idle_timeout);
441   remaining = bundle_RemainingIdleTime(arg->bundle);
442   if (remaining != -1)
443     prompt_Printf(arg->prompt, "Remaining:  %ds\n", remaining);
444 
445   return 0;
446 }
447 
448 static int
449 ShowTimerList(struct cmdargs const *arg)
450 {
451   ShowTimers(0, arg->prompt);
452   return 0;
453 }
454 
455 static int
456 ShowStopped(struct cmdargs const *arg)
457 {
458   prompt_Printf(arg->prompt, " Stopped Timer:  LCP: ");
459   if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
460     prompt_Printf(arg->prompt, "Disabled");
461   else
462     prompt_Printf(arg->prompt, "%ld secs",
463                   arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
464 
465   prompt_Printf(arg->prompt, ", CCP: ");
466   if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
467     prompt_Printf(arg->prompt, "Disabled");
468   else
469     prompt_Printf(arg->prompt, "%ld secs",
470                   arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
471 
472   prompt_Printf(arg->prompt, "\n");
473 
474   return 0;
475 }
476 
477 static int
478 ShowAuthKey(struct cmdargs const *arg)
479 {
480   prompt_Printf(arg->prompt, "AuthName = %s\n", arg->bundle->cfg.auth.name);
481   prompt_Printf(arg->prompt, "AuthKey  = %s\n", HIDDEN);
482   return 0;
483 }
484 
485 static int
486 ShowVersion(struct cmdargs const *arg)
487 {
488   static char VarVersion[] = "PPP Version 2.0-beta";
489   static char VarLocalVersion[] = "$Date: 1998/04/07 23:45:45 $";
490 
491   prompt_Printf(arg->prompt, "%s - %s \n", VarVersion, VarLocalVersion);
492   return 0;
493 }
494 
495 int
496 ShowProtocolStats(struct cmdargs const *arg)
497 {
498   struct link *l = ChooseLink(arg);
499 
500   prompt_Printf(arg->prompt, "%s:\n", l->name);
501   link_ReportProtocolStatus(l, arg->prompt);
502   return 0;
503 }
504 
505 
506 #ifndef NOMSEXT
507 static int
508 ShowMSExt(struct cmdargs const *arg)
509 {
510   prompt_Printf(arg->prompt, " MS PPP extention values \n");
511   prompt_Printf(arg->prompt, "   Primary NS     : %s\n",
512                 inet_ntoa(arg->bundle->ncp.ipcp.cfg.ns_entries[0]));
513   prompt_Printf(arg->prompt, "   Secondary NS   : %s\n",
514                 inet_ntoa(arg->bundle->ncp.ipcp.cfg.ns_entries[1]));
515   prompt_Printf(arg->prompt, "   Primary NBNS   : %s\n",
516                 inet_ntoa(arg->bundle->ncp.ipcp.cfg.nbns_entries[0]));
517   prompt_Printf(arg->prompt, "   Secondary NBNS : %s\n",
518                 inet_ntoa(arg->bundle->ncp.ipcp.cfg.nbns_entries[1]));
519 
520   return 0;
521 }
522 
523 #endif
524 
525 static struct cmdtab const ShowCommands[] = {
526   {"auth", NULL, ShowAuthKey, LOCAL_AUTH,
527   "Show auth details", "show auth"},
528   {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
529   "Show CCP status", "show cpp"},
530   {"compress", NULL, ReportCompress, LOCAL_AUTH,
531   "Show compression stats", "show compress"},
532   {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
533   "Show escape characters", "show escape"},
534   {"filter", NULL, ShowFilter, LOCAL_AUTH,
535   "Show packet filters", "show filter [in|out|dial|alive]"},
536   {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
537   "Show HDLC errors", "show hdlc"},
538   {"ipcp", NULL, ReportIpcpStatus, LOCAL_AUTH,
539   "Show IPCP status", "show ipcp"},
540   {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
541   "Show LCP status", "show lcp"},
542   {"links", "link", bundle_ShowLinks, LOCAL_AUTH,
543   "Show available link names", "show links"},
544   {"log", NULL, log_ShowLevel, LOCAL_AUTH,
545   "Show log levels", "show log"},
546   {"mem", NULL, ShowMemMap, LOCAL_AUTH,
547   "Show memory map", "show mem"},
548   {"modem", NULL, modem_ShowStatus, LOCAL_AUTH | LOCAL_CX,
549   "Show modem setups", "show modem"},
550 #ifndef NOMSEXT
551   {"msext", NULL, ShowMSExt, LOCAL_AUTH,
552   "Show MS PPP extentions", "show msext"},
553 #endif
554   {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
555   "Show protocol summary", "show proto"},
556   {"route", NULL, ShowRoute, LOCAL_AUTH,
557   "Show routing table", "show route"},
558   {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
559   "Show STOPPED timeout", "show stopped"},
560   {"timeout", NULL, ShowTimeout, LOCAL_AUTH,
561   "Show Idle timeout", "show timeout"},
562   {"timers", NULL, ShowTimerList, LOCAL_AUTH,
563   "Show alarm timers", "show timers"},
564   {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
565   "Show version string", "show version"},
566   {"who", NULL, log_ShowWho, LOCAL_AUTH,
567   "Show client list", "show who"},
568   {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
569   "Display this message", "show help|? [command]", ShowCommands},
570   {NULL, NULL, NULL},
571 };
572 
573 static struct cmdtab const *
574 FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
575 {
576   int nmatch;
577   int len;
578   struct cmdtab const *found;
579 
580   found = NULL;
581   len = strlen(str);
582   nmatch = 0;
583   while (cmds->func) {
584     if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
585       if (cmds->name[len] == '\0') {
586 	*pmatch = 1;
587 	return cmds;
588       }
589       nmatch++;
590       found = cmds;
591     } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
592       if (cmds->alias[len] == '\0') {
593 	*pmatch = 1;
594 	return cmds;
595       }
596       nmatch++;
597       found = cmds;
598     }
599     cmds++;
600   }
601   *pmatch = nmatch;
602   return found;
603 }
604 
605 static int
606 FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc,
607          char const *const *argv, const char *prefix, struct prompt *prompt,
608          struct datalink *cx)
609 {
610   struct cmdtab const *cmd;
611   int val = 1;
612   int nmatch;
613   struct cmdargs arg;
614 
615   cmd = FindCommand(cmds, *argv, &nmatch);
616   if (nmatch > 1)
617     LogPrintf(LogWARN, "%s%s: Ambiguous command\n", prefix, *argv);
618   else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
619     if ((cmd->lauth & LOCAL_CX) && !cx)
620       /* We've got no context, but we require it */
621       cx = bundle2datalink(bundle, NULL);
622 
623     if ((cmd->lauth & LOCAL_CX) && !cx)
624       LogPrintf(LogWARN, "%s%s: No context (use the `link' command)\n",
625                 prefix, *argv);
626     else {
627       if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
628         LogPrintf(LogWARN, "%s%s: Redundant context (%s) ignored\n",
629                   prefix, *argv, cx->name);
630         cx = NULL;
631       }
632       arg.cmdtab = cmds;
633       arg.cmd = cmd;
634       arg.argc = argc-1;
635       arg.argv = argv+1;
636       arg.bundle = bundle;
637       arg.cx = cx;
638       arg.prompt = prompt;
639       val = (cmd->func) (&arg);
640     }
641   } else
642     LogPrintf(LogWARN, "%s%s: Invalid command\n", prefix, *argv);
643 
644   if (val == -1)
645     LogPrintf(LogWARN, "Usage: %s\n", cmd->syntax);
646   else if (val)
647     LogPrintf(LogWARN, "%s%s: Failed %d\n", prefix, *argv, val);
648 
649   return val;
650 }
651 
652 void
653 InterpretCommand(char *buff, int nb, int *argc, char ***argv)
654 {
655   static char *vector[MAXARGS];
656   char *cp;
657 
658   if (nb > 0) {
659     cp = buff + strcspn(buff, "\r\n");
660     if (cp)
661       *cp = '\0';
662     *argc = MakeArgs(buff, vector, VECSIZE(vector));
663     *argv = vector;
664   } else
665     *argc = 0;
666 }
667 
668 static int
669 arghidden(int argc, char const *const *argv, int n)
670 {
671   /* Is arg n of the given command to be hidden from the log ? */
672 
673   /* set authkey xxxxx */
674   /* set key xxxxx */
675   if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
676       (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
677     return 1;
678 
679   /* passwd xxxxx */
680   if (n == 1 && !strncasecmp(argv[0], "p", 1))
681     return 1;
682 
683   return 0;
684 }
685 
686 void
687 RunCommand(struct bundle *bundle, int argc, char const *const *argv,
688            struct prompt *prompt, const char *label)
689 {
690   if (argc > 0) {
691     if (LogIsKept(LogCOMMAND)) {
692       static char buf[LINE_LEN];
693       int f, n;
694 
695       *buf = '\0';
696       if (label) {
697         strncpy(buf, label, sizeof buf - 3);
698         buf[sizeof buf - 3] = '\0';
699         strcat(buf, ": ");
700       }
701       n = strlen(buf);
702       for (f = 0; f < argc; f++) {
703         if (n < sizeof buf - 1 && f)
704           buf[n++] = ' ';
705         if (arghidden(argc, argv, f))
706           strncpy(buf+n, HIDDEN, sizeof buf - n - 1);
707         else
708           strncpy(buf+n, argv[f], sizeof buf - n - 1);
709         n += strlen(buf+n);
710       }
711       LogPrintf(LogCOMMAND, "%s\n", buf);
712     }
713     FindExec(bundle, Commands, argc, argv, "", prompt, NULL);
714   }
715 }
716 
717 void
718 DecodeCommand(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
719               const char *label)
720 {
721   int argc;
722   char **argv;
723 
724   InterpretCommand(buff, nb, &argc, &argv);
725   RunCommand(bundle, argc, (char const *const *)argv, prompt, label);
726 }
727 
728 static int
729 ShowCommand(struct cmdargs const *arg)
730 {
731   if (!arg->prompt)
732     LogPrintf(LogWARN, "show: Cannot show without a prompt\n");
733   else if (arg->argc > 0)
734     FindExec(arg->bundle, ShowCommands, arg->argc, arg->argv, "show ",
735              arg->prompt, arg->cx);
736   else
737     prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
738 
739   return 0;
740 }
741 
742 static int
743 TerminalCommand(struct cmdargs const *arg)
744 {
745   if (!arg->prompt) {
746     LogPrintf(LogWARN, "term: Need a prompt\n");
747     return 1;
748   }
749 
750   if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
751     prompt_Printf(arg->prompt, "LCP state is [%s]\n",
752                   State2Nam(arg->cx->physical->link.lcp.fsm.state));
753     return 1;
754   }
755 
756   datalink_Up(arg->cx, 0, 0);
757   prompt_TtyTermMode(arg->prompt, arg->cx);
758   return 0;
759 }
760 
761 static int
762 QuitCommand(struct cmdargs const *arg)
763 {
764   if (!arg->prompt || prompt_IsController(arg->prompt) ||
765       (arg->argc > 0 && !strcasecmp(*arg->argv, "all") &&
766        (arg->prompt->auth & LOCAL_AUTH)))
767     Cleanup(EX_NORMAL);
768   if (arg->prompt)
769     prompt_Destroy(arg->prompt, 1);
770 
771   return 0;
772 }
773 
774 static int
775 CloseCommand(struct cmdargs const *arg)
776 {
777   bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, 1);
778   return 0;
779 }
780 
781 static int
782 DownCommand(struct cmdargs const *arg)
783 {
784   datalink_Down(arg->cx, 1);
785   return 0;
786 }
787 
788 static int
789 SetModemSpeed(struct cmdargs const *arg)
790 {
791   long speed;
792   char *end;
793 
794   if (arg->argc > 0 && **arg->argv) {
795     if (arg->argc > 1) {
796       LogPrintf(LogWARN, "SetModemSpeed: Too many arguments");
797       return -1;
798     }
799     if (strcasecmp(*arg->argv, "sync") == 0) {
800       Physical_SetSync(arg->cx->physical);
801       return 0;
802     }
803     end = NULL;
804     speed = strtol(*arg->argv, &end, 10);
805     if (*end) {
806       LogPrintf(LogWARN, "SetModemSpeed: Bad argument \"%s\"", *arg->argv);
807       return -1;
808     }
809     if (Physical_SetSpeed(arg->cx->physical, speed))
810       return 0;
811     LogPrintf(LogWARN, "%s: Invalid speed\n", *arg->argv);
812   } else
813     LogPrintf(LogWARN, "SetModemSpeed: No speed specified\n");
814 
815   return -1;
816 }
817 
818 static int
819 SetStoppedTimeout(struct cmdargs const *arg)
820 {
821   struct link *l = &arg->cx->physical->link;
822 
823   l->lcp.fsm.StoppedTimer.load = 0;
824   l->ccp.fsm.StoppedTimer.load = 0;
825   if (arg->argc <= 2) {
826     if (arg->argc > 0) {
827       l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[0]) * SECTICKS;
828       if (arg->argc > 1)
829         l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[1]) * SECTICKS;
830     }
831     return 0;
832   }
833   return -1;
834 }
835 
836 #define ismask(x) \
837   (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3)
838 
839 static int
840 SetServer(struct cmdargs const *arg)
841 {
842   int res = -1;
843 
844   if (arg->argc > 0 && arg->argc < 4) {
845     const char *port, *passwd, *mask;
846 
847     /* What's what ? */
848     port = arg->argv[0];
849     if (arg->argc == 2) {
850       passwd = arg->argv[1];
851       mask = NULL;
852     } else if (arg->argc == 3) {
853       passwd = arg->argv[1];
854       mask = arg->argv[2];
855       if (!ismask(mask))
856         return -1;
857     } else if (strcasecmp(port, "none") == 0) {
858       if (ServerClose(arg->bundle))
859         LogPrintf(LogPHASE, "Disabled server port.\n");
860       return 0;
861     } else
862       return -1;
863 
864     strncpy(server.passwd, passwd, sizeof server.passwd - 1);
865     server.passwd[sizeof server.passwd - 1] = '\0';
866 
867     if (*port == '/') {
868       mode_t imask;
869 
870       if (mask != NULL) {
871 	unsigned m;
872 
873 	if (sscanf(mask, "%o", &m) == 1)
874 	  imask = m;
875         else
876           return -1;
877       } else
878         imask = (mode_t)-1;
879       res = ServerLocalOpen(arg->bundle, port, imask);
880     } else {
881       int iport;
882 
883       if (mask != NULL)
884         return -1;
885 
886       if (strspn(port, "0123456789") != strlen(port)) {
887         struct servent *s;
888 
889         if ((s = getservbyname(port, "tcp")) == NULL) {
890 	  iport = 0;
891 	  LogPrintf(LogWARN, "%s: Invalid port or service\n", port);
892 	} else
893 	  iport = ntohs(s->s_port);
894       } else
895         iport = atoi(port);
896       res = iport ? ServerTcpOpen(arg->bundle, iport) : -1;
897     }
898   }
899 
900   return res;
901 }
902 
903 static int
904 SetModemParity(struct cmdargs const *arg)
905 {
906   return arg->argc > 0 ? modem_SetParity(arg->cx->physical, *arg->argv) : -1;
907 }
908 
909 static int
910 SetEscape(struct cmdargs const *arg)
911 {
912   int code;
913   int argc = arg->argc;
914   char const *const *argv = arg->argv;
915 
916   for (code = 0; code < 33; code++)
917     arg->cx->physical->async.cfg.EscMap[code] = 0;
918 
919   while (argc-- > 0) {
920     sscanf(*argv++, "%x", &code);
921     code &= 0xff;
922     arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
923     arg->cx->physical->async.cfg.EscMap[32] = 1;
924   }
925   return 0;
926 }
927 
928 static struct in_addr
929 GetIpAddr(const char *cp)
930 {
931   struct hostent *hp;
932   struct in_addr ipaddr;
933 
934   if (inet_aton(cp, &ipaddr) == 0) {
935     hp = gethostbyname(cp);
936     if (hp && hp->h_addrtype == AF_INET)
937       memcpy(&ipaddr, hp->h_addr, hp->h_length);
938     else
939       ipaddr.s_addr = 0;
940   }
941   return (ipaddr);
942 }
943 
944 static int
945 SetInterfaceAddr(struct cmdargs const *arg)
946 {
947   struct ipcp *ipcp = &arg->bundle->ncp.ipcp;
948   const char *hisaddr;
949 
950   hisaddr = NULL;
951   ipcp->cfg.my_range.ipaddr.s_addr = INADDR_ANY;
952   ipcp->cfg.peer_range.ipaddr.s_addr = INADDR_ANY;
953 
954   if (arg->argc > 4)
955     return -1;
956 
957   ipcp->cfg.HaveTriggerAddress = 0;
958   ipcp->cfg.netmask.s_addr = INADDR_ANY;
959   iplist_reset(&ipcp->cfg.peer_list);
960 
961   if (arg->argc > 0) {
962     if (!ParseAddr(ipcp, arg->argc, arg->argv, &ipcp->cfg.my_range.ipaddr,
963 		   &ipcp->cfg.my_range.mask, &ipcp->cfg.my_range.width))
964       return 1;
965     if (arg->argc > 1) {
966       hisaddr = arg->argv[1];
967       if (arg->argc > 2) {
968         ipcp->cfg.netmask = GetIpAddr(arg->argv[2]);
969 	if (arg->argc > 3) {
970 	  ipcp->cfg.TriggerAddress = GetIpAddr(arg->argv[3]);
971 	  ipcp->cfg.HaveTriggerAddress = 1;
972 	}
973       }
974     }
975   }
976 
977   /*
978    * For backwards compatibility, 0.0.0.0 means any address.
979    */
980   if (ipcp->cfg.my_range.ipaddr.s_addr == INADDR_ANY) {
981     ipcp->cfg.my_range.mask.s_addr = INADDR_ANY;
982     ipcp->cfg.my_range.width = 0;
983   }
984   ipcp->my_ip.s_addr = ipcp->cfg.my_range.ipaddr.s_addr;
985 
986   if (ipcp->cfg.peer_range.ipaddr.s_addr == INADDR_ANY) {
987     ipcp->cfg.peer_range.mask.s_addr = INADDR_ANY;
988     ipcp->cfg.peer_range.width = 0;
989   }
990 
991   if (hisaddr &&
992       !UseHisaddr(arg->bundle, hisaddr, arg->bundle->phys_type & PHYS_DEMAND))
993     return 4;
994 
995   return 0;
996 }
997 
998 #ifndef NOMSEXT
999 
1000 static void
1001 SetMSEXT(struct ipcp *ipcp, struct in_addr * pri_addr,
1002 	 struct in_addr * sec_addr, int argc, char const *const *argv)
1003 {
1004   int dummyint;
1005   struct in_addr dummyaddr;
1006 
1007   pri_addr->s_addr = sec_addr->s_addr = 0L;
1008 
1009   if (argc > 0) {
1010     ParseAddr(ipcp, argc, argv++, pri_addr, &dummyaddr, &dummyint);
1011     if (--argc > 0)
1012       ParseAddr(ipcp, argc, argv++, sec_addr, &dummyaddr, &dummyint);
1013     else
1014       sec_addr->s_addr = pri_addr->s_addr;
1015   }
1016 
1017   /*
1018    * if the primary/secondary ns entries are 0.0.0.0 we should set them to
1019    * either the localhost's ip, or the values in /etc/resolv.conf ??
1020    *
1021    * up to you if you want to implement this...
1022    */
1023 
1024 }
1025 
1026 static int
1027 SetNS(struct cmdargs const *arg)
1028 {
1029   SetMSEXT(&arg->bundle->ncp.ipcp, &arg->bundle->ncp.ipcp.cfg.ns_entries[0],
1030            &arg->bundle->ncp.ipcp.cfg.ns_entries[1], arg->argc, arg->argv);
1031   return 0;
1032 }
1033 
1034 static int
1035 SetNBNS(struct cmdargs const *arg)
1036 {
1037   SetMSEXT(&arg->bundle->ncp.ipcp, &arg->bundle->ncp.ipcp.cfg.nbns_entries[0],
1038            &arg->bundle->ncp.ipcp.cfg.nbns_entries[1], arg->argc, arg->argv);
1039   return 0;
1040 }
1041 
1042 #endif				/* MS_EXT */
1043 
1044 static int
1045 SetVariable(struct cmdargs const *arg)
1046 {
1047   u_long ulong_val;
1048   const char *argp;
1049   int param = (int)arg->cmd->args;
1050   struct datalink *cx = arg->cx;	/* AUTH_CX uses this */
1051   const char *err = NULL;
1052   struct link *l = ChooseLink(arg);	/* AUTH_CX_OPT uses this */
1053 
1054   if (arg->argc > 0)
1055     argp = *arg->argv;
1056   else
1057     argp = "";
1058 
1059   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1060     LogPrintf(LogWARN, "set %s: No context (use the `link' command)\n",
1061               arg->cmd->name);
1062     return 1;
1063   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1064     LogPrintf(LogWARN, "set %s: Redundant context (%s) ignored\n",
1065               arg->cmd->name, cx->name);
1066     cx = NULL;
1067   }
1068 
1069   switch (param) {
1070   case VAR_AUTHKEY:
1071     if (bundle_Phase(arg->bundle) == PHASE_DEAD) {
1072       strncpy(arg->bundle->cfg.auth.key, argp,
1073               sizeof arg->bundle->cfg.auth.key - 1);
1074       arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
1075     } else {
1076       err = "set authkey: Only available at phase DEAD\n";
1077       LogPrintf(LogWARN, err);
1078     }
1079     break;
1080   case VAR_AUTHNAME:
1081     if (bundle_Phase(arg->bundle) == PHASE_DEAD) {
1082       strncpy(arg->bundle->cfg.auth.name, argp,
1083               sizeof arg->bundle->cfg.auth.name - 1);
1084       arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name - 1] = '\0';
1085     } else {
1086       err = "set authname: Only available at phase DEAD\n";
1087       LogPrintf(LogWARN, err);
1088     }
1089     break;
1090   case VAR_DIAL:
1091     strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
1092     cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
1093     break;
1094   case VAR_LOGIN:
1095     strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
1096     cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
1097     break;
1098   case VAR_WINSIZE:
1099     if (arg->argc > 0) {
1100       l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[0]);
1101       if (l->ccp.cfg.deflate.out.winsize < 8 ||
1102           l->ccp.cfg.deflate.out.winsize > 15) {
1103           LogPrintf(LogWARN, "%d: Invalid outgoing window size\n",
1104                     l->ccp.cfg.deflate.out.winsize);
1105           l->ccp.cfg.deflate.out.winsize = 15;
1106       }
1107       if (arg->argc > 1) {
1108         l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[1]);
1109         if (l->ccp.cfg.deflate.in.winsize < 8 ||
1110             l->ccp.cfg.deflate.in.winsize > 15) {
1111             LogPrintf(LogWARN, "%d: Invalid incoming window size\n",
1112                       l->ccp.cfg.deflate.in.winsize);
1113             l->ccp.cfg.deflate.in.winsize = 15;
1114         }
1115       } else
1116         l->ccp.cfg.deflate.in.winsize = 0;
1117     } else {
1118       err = "No window size specified\n";
1119       LogPrintf(LogWARN, err);
1120     }
1121     break;
1122   case VAR_DEVICE:
1123     Physical_SetDeviceList(cx->physical, argp);
1124     break;
1125   case VAR_ACCMAP:
1126     if (arg->argc > 0) {
1127       sscanf(argp, "%lx", &ulong_val);
1128       cx->physical->link.lcp.cfg.accmap = ulong_val;
1129     } else {
1130       err = "No accmap specified\n";
1131       LogPrintf(LogWARN, err);
1132     }
1133     break;
1134   case VAR_MRU:
1135     ulong_val = atol(argp);
1136     if (ulong_val < MIN_MRU)
1137       err = "Given MRU value (%lu) is too small.\n";
1138     else if (ulong_val > MAX_MRU)
1139       err = "Given MRU value (%lu) is too big.\n";
1140     else
1141       l->lcp.cfg.mru = ulong_val;
1142     if (err)
1143       LogPrintf(LogWARN, err, ulong_val);
1144     break;
1145   case VAR_MTU:
1146     ulong_val = atol(argp);
1147     if (ulong_val == 0)
1148       l->lcp.cfg.mtu = 0;
1149     else if (ulong_val < MIN_MTU)
1150       err = "Given MTU value (%lu) is too small.\n";
1151     else if (ulong_val > MAX_MTU)
1152       err = "Given MTU value (%lu) is too big.\n";
1153     else
1154       l->lcp.cfg.mtu = ulong_val;
1155     if (err)
1156       LogPrintf(LogWARN, err, ulong_val);
1157     break;
1158   case VAR_OPENMODE:
1159     if (strcasecmp(argp, "active") == 0)
1160       cx->physical->link.lcp.cfg.openmode = arg->argc > 1 ?
1161         atoi(arg->argv[1]) : 1;
1162     else if (strcasecmp(argp, "passive") == 0)
1163       cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
1164     else {
1165       err = "%s: Invalid openmode\n";
1166       LogPrintf(LogWARN, err, argp);
1167     }
1168     break;
1169   case VAR_PHONE:
1170     strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
1171     cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
1172     break;
1173   case VAR_HANGUP:
1174     strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
1175     cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
1176     break;
1177   case VAR_IDLETIMEOUT:
1178     if (arg->argc > 1)
1179       err = "Too many idle timeout values\n";
1180     else if (arg->argc == 1)
1181       bundle_SetIdleTimer(arg->bundle, atoi(argp));
1182     if (err)
1183       LogPrintf(LogWARN, err);
1184     break;
1185   case VAR_LQRPERIOD:
1186     ulong_val = atol(argp);
1187     if (ulong_val <= 0) {
1188       err = "%s: Invalid lqr period\n";
1189       LogPrintf(LogWARN, err, argp);
1190     } else
1191       l->lcp.cfg.lqrperiod = ulong_val;
1192     break;
1193   case VAR_LCPRETRY:
1194     ulong_val = atol(argp);
1195     if (ulong_val <= 0) {
1196       err = "%s: Invalid LCP FSM retry period\n";
1197       LogPrintf(LogWARN, err, argp);
1198     } else
1199       cx->physical->link.lcp.cfg.fsmretry = ulong_val;
1200     break;
1201   case VAR_CHAPRETRY:
1202     ulong_val = atol(argp);
1203     if (ulong_val <= 0) {
1204       err = "%s: Invalid CHAP retry period\n";
1205       LogPrintf(LogWARN, err, argp);
1206     } else
1207       cx->chap.auth.cfg.fsmretry = ulong_val;
1208     break;
1209   case VAR_PAPRETRY:
1210     ulong_val = atol(argp);
1211     if (ulong_val <= 0) {
1212       err = "%s: Invalid PAP retry period\n";
1213       LogPrintf(LogWARN, err, argp);
1214     } else
1215       cx->pap.cfg.fsmretry = ulong_val;
1216     break;
1217   case VAR_CCPRETRY:
1218     ulong_val = atol(argp);
1219     if (ulong_val <= 0) {
1220       err = "%s: Invalid CCP FSM retry period\n";
1221       LogPrintf(LogWARN, err, argp);
1222     } else
1223       l->ccp.cfg.fsmretry = ulong_val;
1224     break;
1225   case VAR_IPCPRETRY:
1226     ulong_val = atol(argp);
1227     if (ulong_val <= 0) {
1228       err = "%s: Invalid IPCP FSM retry period\n";
1229       LogPrintf(LogWARN, err, argp);
1230     } else
1231       arg->bundle->ncp.ipcp.cfg.fsmretry = ulong_val;
1232     break;
1233   }
1234 
1235   return err ? 1 : 0;
1236 }
1237 
1238 static int
1239 SetCtsRts(struct cmdargs const *arg)
1240 {
1241   if (arg->argc == 1) {
1242     if (strcmp(*arg->argv, "on") == 0)
1243       Physical_SetRtsCts(arg->cx->physical, 1);
1244     else if (strcmp(*arg->argv, "off") == 0)
1245       Physical_SetRtsCts(arg->cx->physical, 0);
1246     else
1247       return -1;
1248     return 0;
1249   }
1250   return -1;
1251 }
1252 
1253 static struct cmdtab const SetCommands[] = {
1254   {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1255   "Set accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
1256   {"authkey", "key", SetVariable, LOCAL_AUTH,
1257   "Set authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
1258   {"authname", NULL, SetVariable, LOCAL_AUTH,
1259   "Set authentication name", "set authname name", (const void *)VAR_AUTHNAME},
1260   {"ccpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1261   "Set FSM retry period", "set ccpretry value", (const void *)VAR_CCPRETRY},
1262   {"chapretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1263   "Set CHAP retry period", "set chapretry value", (const void *)VAR_CHAPRETRY},
1264   {"ctsrts", "crtscts", SetCtsRts, LOCAL_AUTH | LOCAL_CX,
1265   "Use hardware flow control", "set ctsrts [on|off]"},
1266   {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1267   "Set deflate window sizes", "set deflate out-winsize in-winsize",
1268   (const void *) VAR_WINSIZE},
1269   {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
1270   "Set modem device name", "set device|line device-name[,device-name]",
1271   (const void *) VAR_DEVICE},
1272   {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1273   "Set dialing script", "set dial chat-script", (const void *) VAR_DIAL},
1274   {"encrypt", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1275   "Select CHAP encryption type", "set encrypt MSChap|MD5",
1276   (const void *)VAR_ENC},
1277   {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
1278   "Set escape characters", "set escape hex-digit ..."},
1279   {"filter", NULL, SetFilter, LOCAL_AUTH,
1280   "Set packet filters", "set filter in|out|dial|alive ..."},
1281   {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1282   "Set hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
1283   {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "Set destination address",
1284   "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
1285   {"ipcpretry", NULL, SetVariable, LOCAL_AUTH,
1286   "Set FSM retry period", "set ipcpretry value", (const void *)VAR_IPCPRETRY},
1287   {"lcpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1288   "Set FSM retry period", "set lcpretry value", (const void *)VAR_LCPRETRY},
1289   {"log", NULL, log_SetLevel, LOCAL_AUTH,
1290   "Set log level", "set log [local] [+|-]value..."},
1291   {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1292   "Set login script", "set login chat-script", (const void *) VAR_LOGIN},
1293   {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1294   "Set LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
1295   {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1296   "Set MRU value", "set mru value", (const void *)VAR_MRU},
1297   {"mtu", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1298   "Set MTU value", "set mtu value", (const void *)VAR_MTU},
1299 #ifndef NOMSEXT
1300   {"nbns", NULL, SetNBNS, LOCAL_AUTH,
1301   "Set NetBIOS NameServer", "set nbns pri-addr [sec-addr]"},
1302   {"ns", NULL, SetNS, LOCAL_AUTH,
1303   "Set NameServer", "set ns pri-addr [sec-addr]"},
1304 #endif
1305   {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set open mode",
1306   "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
1307   {"papretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1308   "Set PAP retry period", "set papretry value", (const void *)VAR_PAPRETRY},
1309   {"parity", NULL, SetModemParity, LOCAL_AUTH | LOCAL_CX,
1310   "Set modem parity", "set parity [odd|even|none]"},
1311   {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set telephone number(s)",
1312   "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
1313   {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
1314   "Set Reconnect timeout", "set reconnect value ntries"},
1315   {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
1316   "Set Redial timeout", "set redial value|random[.value|random] [attempts]"},
1317   {"server", "socket", SetServer, LOCAL_AUTH,
1318   "Set server port", "set server|socket TcpPort|LocalName|none [mask]"},
1319   {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
1320   "Set modem speed", "set speed value"},
1321   {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
1322   "Set STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
1323   {"timeout", NULL, SetVariable, LOCAL_AUTH, "Set Idle timeout",
1324   "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
1325   {"vj", NULL, SetInitVJ, LOCAL_AUTH,
1326   "Set vj values", "set vj slots|slotcomp [value]"},
1327   {"weight", NULL, mp_SetDatalinkWeight, LOCAL_AUTH | LOCAL_CX,
1328   "Set datalink weighting", "set weight n"},
1329   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1330   "Display this message", "set help|? [command]", SetCommands},
1331   {NULL, NULL, NULL},
1332 };
1333 
1334 static int
1335 SetCommand(struct cmdargs const *arg)
1336 {
1337   if (arg->argc > 0)
1338     FindExec(arg->bundle, SetCommands, arg->argc, arg->argv, "set ",
1339              arg->prompt, arg->cx);
1340   else if (arg->prompt)
1341     prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
1342 	    " syntax help.\n");
1343   else
1344     LogPrintf(LogWARN, "set command must have arguments\n");
1345 
1346   return 0;
1347 }
1348 
1349 
1350 static int
1351 AddCommand(struct cmdargs const *arg)
1352 {
1353   struct in_addr dest, gateway, netmask;
1354   int gw;
1355 
1356   if (arg->argc != 3 && arg->argc != 2)
1357     return -1;
1358 
1359   if (arg->argc == 2)
1360     if (strcasecmp(arg->argv[0], "default"))
1361       return -1;
1362     else {
1363       dest.s_addr = netmask.s_addr = INADDR_ANY;
1364       gw = 1;
1365     }
1366   else {
1367     if (strcasecmp(arg->argv[0], "MYADDR") == 0)
1368       dest = arg->bundle->ncp.ipcp.my_ip;
1369     else if (strcasecmp(arg->argv[0], "HISADDR") == 0)
1370       dest = arg->bundle->ncp.ipcp.peer_ip;
1371     else
1372       dest = GetIpAddr(arg->argv[0]);
1373     netmask = GetIpAddr(arg->argv[1]);
1374     gw = 2;
1375   }
1376   if (strcasecmp(arg->argv[gw], "HISADDR") == 0)
1377     gateway = arg->bundle->ncp.ipcp.peer_ip;
1378   else if (strcasecmp(arg->argv[gw], "INTERFACE") == 0)
1379     gateway.s_addr = INADDR_ANY;
1380   else
1381     gateway = GetIpAddr(arg->argv[gw]);
1382   bundle_SetRoute(arg->bundle, RTM_ADD, dest, gateway, netmask,
1383                   arg->cmd->args ? 1 : 0);
1384   return 0;
1385 }
1386 
1387 static int
1388 DeleteCommand(struct cmdargs const *arg)
1389 {
1390   struct in_addr dest, none;
1391 
1392   if (arg->argc == 1)
1393     if(strcasecmp(arg->argv[0], "all") == 0)
1394       DeleteIfRoutes(arg->bundle, 0);
1395     else {
1396       if (strcasecmp(arg->argv[0], "MYADDR") == 0)
1397         dest = arg->bundle->ncp.ipcp.my_ip;
1398       else if (strcasecmp(arg->argv[0], "default") == 0)
1399         dest.s_addr = INADDR_ANY;
1400       else
1401         dest = GetIpAddr(arg->argv[0]);
1402       none.s_addr = INADDR_ANY;
1403       bundle_SetRoute(arg->bundle, RTM_DELETE, dest, none, none,
1404                       arg->cmd->args ? 1 : 0);
1405     }
1406   else
1407     return -1;
1408 
1409   return 0;
1410 }
1411 
1412 #ifndef NOALIAS
1413 static struct cmdtab const AliasCommands[] =
1414 {
1415   {"addr", NULL, AliasRedirectAddr, LOCAL_AUTH,
1416    "static address translation", "alias addr [addr_local addr_alias]"},
1417   {"deny_incoming", NULL, AliasOption, LOCAL_AUTH,
1418    "stop incoming connections", "alias deny_incoming [yes|no]",
1419    (const void *) PKT_ALIAS_DENY_INCOMING},
1420   {"enable", NULL, AliasEnable, LOCAL_AUTH,
1421    "enable IP aliasing", "alias enable [yes|no]"},
1422   {"log", NULL, AliasOption, LOCAL_AUTH,
1423    "log aliasing link creation", "alias log [yes|no]",
1424    (const void *) PKT_ALIAS_LOG},
1425   {"port", NULL, AliasRedirectPort, LOCAL_AUTH,
1426    "port redirection", "alias port [proto addr_local:port_local  port_alias]"},
1427   {"same_ports", NULL, AliasOption, LOCAL_AUTH,
1428    "try to leave port numbers unchanged", "alias same_ports [yes|no]",
1429    (const void *) PKT_ALIAS_SAME_PORTS},
1430   {"unregistered_only", NULL, AliasOption, LOCAL_AUTH,
1431    "alias unregistered (private) IP address space only",
1432    "alias unregistered_only [yes|no]",
1433    (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
1434   {"use_sockets", NULL, AliasOption, LOCAL_AUTH,
1435    "allocate host sockets", "alias use_sockets [yes|no]",
1436    (const void *) PKT_ALIAS_USE_SOCKETS},
1437   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1438    "Display this message", "alias help|? [command]", AliasCommands},
1439   {NULL, NULL, NULL},
1440 };
1441 
1442 
1443 static int
1444 AliasCommand(struct cmdargs const *arg)
1445 {
1446   if (arg->argc > 0)
1447     FindExec(arg->bundle, AliasCommands, arg->argc, arg->argv, "alias ",
1448              arg->prompt, arg->cx);
1449   else if (arg->prompt)
1450     prompt_Printf(arg->prompt, "Use `alias help' to get a list or `alias help"
1451             " <option>' for syntax help.\n");
1452   else
1453     LogPrintf(LogWARN, "alias command must have arguments\n");
1454 
1455   return 0;
1456 }
1457 
1458 static int
1459 AliasEnable(struct cmdargs const *arg)
1460 {
1461   if (arg->argc == 1)
1462     if (strcasecmp(arg->argv[0], "yes") == 0) {
1463       if (loadAliasHandlers() == 0)
1464 	return 0;
1465       LogPrintf(LogWARN, "Cannot load alias library\n");
1466       return 1;
1467     } else if (strcasecmp(arg->argv[0], "no") == 0) {
1468       unloadAliasHandlers();
1469       return 0;
1470     }
1471   return -1;
1472 }
1473 
1474 
1475 static int
1476 AliasOption(struct cmdargs const *arg)
1477 {
1478   unsigned param = (unsigned)arg->cmd->args;
1479   if (arg->argc == 1)
1480     if (strcasecmp(arg->argv[0], "yes") == 0) {
1481       if (AliasEnabled()) {
1482 	(*PacketAlias.SetMode)(param, param);
1483 	return 0;
1484       }
1485       LogPrintf(LogWARN, "alias not enabled\n");
1486     } else if (strcmp(arg->argv[0], "no") == 0) {
1487       if (AliasEnabled()) {
1488 	(*PacketAlias.SetMode)(0, param);
1489 	return 0;
1490       }
1491       LogPrintf(LogWARN, "alias not enabled\n");
1492     }
1493   return -1;
1494 }
1495 #endif /* #ifndef NOALIAS */
1496 
1497 static struct cmdtab const AllowCommands[] = {
1498   {"modes", "mode", AllowModes, LOCAL_AUTH,
1499   "Only allow certain ppp modes", "allow modes mode..."},
1500   {"users", "user", AllowUsers, LOCAL_AUTH,
1501   "Allow users access to ppp", "allow users logname..."},
1502   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1503   "Display this message", "allow help|? [command]", AllowCommands},
1504   {NULL, NULL, NULL},
1505 };
1506 
1507 static int
1508 AllowCommand(struct cmdargs const *arg)
1509 {
1510   /* arg->bundle may be NULL (see ValidSystem()) ! */
1511   if (arg->argc > 0)
1512     FindExec(arg->bundle, AllowCommands, arg->argc, arg->argv, "allow ",
1513              arg->prompt, arg->cx);
1514   else if (arg->prompt)
1515     prompt_Printf(arg->prompt, "Use `allow ?' to get a list or `allow ? <cmd>'"
1516                   " for syntax help.\n");
1517   else
1518     LogPrintf(LogWARN, "allow command must have arguments\n");
1519 
1520   return 0;
1521 }
1522 
1523 static int
1524 LinkCommand(struct cmdargs const *arg)
1525 {
1526   if (arg->argc > 1) {
1527     struct datalink *cx = bundle2datalink(arg->bundle, arg->argv[0]);
1528     if (cx)
1529       FindExec(arg->bundle, Commands, arg->argc - 1, arg->argv + 1, "",
1530                arg->prompt, cx);
1531     else {
1532       LogPrintf(LogWARN, "link: %s: Invalid link name\n", arg->argv[0]);
1533       return 1;
1534     }
1535   } else {
1536     LogPrintf(LogWARN, "Usage: %s\n", arg->cmd->syntax);
1537     return 2;
1538   }
1539 
1540   return 0;
1541 }
1542 
1543 struct link *
1544 ChooseLink(struct cmdargs const *arg)
1545 {
1546   if (arg->cx)
1547     return &arg->cx->physical->link;
1548   else if (arg->bundle->ncp.mp.active)
1549     return &arg->bundle->ncp.mp.link;
1550   else {
1551     struct datalink *dl = bundle2datalink(arg->bundle, NULL);
1552     return dl ? &dl->physical->link : NULL;
1553   }
1554 }
1555