xref: /freebsd/usr.sbin/ppp/command.c (revision 30291ffb)
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.142 1998/06/15 19:05:12 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 "systems.h"
66 #include "filter.h"
67 #include "descriptor.h"
68 #include "main.h"
69 #include "route.h"
70 #include "ccp.h"
71 #include "auth.h"
72 #include "async.h"
73 #include "link.h"
74 #include "physical.h"
75 #include "mp.h"
76 #include "bundle.h"
77 #include "server.h"
78 #include "prompt.h"
79 #include "chat.h"
80 #include "chap.h"
81 #include "datalink.h"
82 
83 /* ``set'' values */
84 #define	VAR_AUTHKEY	0
85 #define	VAR_DIAL	1
86 #define	VAR_LOGIN	2
87 #define	VAR_AUTHNAME	3
88 #define	VAR_AUTOLOAD	4
89 #define	VAR_WINSIZE	5
90 #define	VAR_DEVICE	6
91 #define	VAR_ACCMAP	7
92 #define	VAR_MRRU	8
93 #define	VAR_MRU		9
94 #define	VAR_MTU		10
95 #define	VAR_OPENMODE	11
96 #define	VAR_PHONE	12
97 #define	VAR_HANGUP	13
98 #define	VAR_IDLETIMEOUT	14
99 #define	VAR_LQRPERIOD	15
100 #define	VAR_LCPRETRY	16
101 #define	VAR_CHAPRETRY	17
102 #define	VAR_PAPRETRY	18
103 #define	VAR_CCPRETRY	19
104 #define	VAR_IPCPRETRY	20
105 #define	VAR_DNS		21
106 #define	VAR_NBNS	22
107 #define	VAR_MODE	23
108 
109 /* ``accept|deny|disable|enable'' masks */
110 #define NEG_HISMASK (1)
111 #define NEG_MYMASK (2)
112 
113 /* ``accept|deny|disable|enable'' values */
114 #define NEG_ACFCOMP	40
115 #define NEG_CHAP	41
116 #define NEG_DEFLATE	42
117 #define NEG_LQR		43
118 #define NEG_PAP		44
119 #define NEG_PPPDDEFLATE	45
120 #define NEG_PRED1	46
121 #define NEG_PROTOCOMP	47
122 #define NEG_SHORTSEQ	48
123 #define NEG_VJCOMP	49
124 #define NEG_DNS		50
125 
126 const char Version[] = "2.0-beta";
127 const char VersionDate[] = "$Date: 1998/06/15 19:05:12 $";
128 
129 static int ShowCommand(struct cmdargs const *);
130 static int TerminalCommand(struct cmdargs const *);
131 static int QuitCommand(struct cmdargs const *);
132 static int OpenCommand(struct cmdargs const *);
133 static int CloseCommand(struct cmdargs const *);
134 static int DownCommand(struct cmdargs const *);
135 static int AllowCommand(struct cmdargs const *);
136 static int SetCommand(struct cmdargs const *);
137 static int LinkCommand(struct cmdargs const *);
138 static int AddCommand(struct cmdargs const *);
139 static int DeleteCommand(struct cmdargs const *);
140 static int NegotiateCommand(struct cmdargs const *);
141 static int ClearCommand(struct cmdargs const *);
142 #ifndef NOALIAS
143 static int AliasCommand(struct cmdargs const *);
144 static int AliasEnable(struct cmdargs const *);
145 static int AliasOption(struct cmdargs const *);
146 #endif
147 
148 static const char *
149 showcx(struct cmdtab const *cmd)
150 {
151   if (cmd->lauth & LOCAL_CX)
152     return "(c)";
153   else if (cmd->lauth & LOCAL_CX_OPT)
154     return "(o)";
155 
156   return "";
157 }
158 
159 static int
160 HelpCommand(struct cmdargs const *arg)
161 {
162   struct cmdtab const *cmd;
163   int n, cmax, dmax, cols, cxlen;
164   const char *cx;
165 
166   if (!arg->prompt) {
167     log_Printf(LogWARN, "help: Cannot help without a prompt\n");
168     return 0;
169   }
170 
171   if (arg->argc > arg->argn) {
172     for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
173       if ((cmd->lauth & arg->prompt->auth) &&
174           ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) ||
175            (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) {
176 	prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd));
177 	return 0;
178       }
179     return -1;
180   }
181 
182   cmax = dmax = 0;
183   for (cmd = arg->cmdtab; cmd->func; cmd++)
184     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
185       if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax)
186         cmax = n;
187       if ((n = strlen(cmd->helpmes)) > dmax)
188         dmax = n;
189     }
190 
191   cols = 80 / (dmax + cmax + 3);
192   n = 0;
193   prompt_Printf(arg->prompt, "(o) = Optional context,"
194                 " (c) = Context required\n");
195   for (cmd = arg->cmdtab; cmd->func; cmd++)
196     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
197       cx = showcx(cmd);
198       cxlen = cmax - strlen(cmd->name);
199       prompt_Printf(arg->prompt, " %s%-*.*s: %-*.*s",
200               cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes);
201       if (++n % cols == 0)
202         prompt_Printf(arg->prompt, "\n");
203     }
204   if (n % cols != 0)
205     prompt_Printf(arg->prompt, "\n");
206 
207   return 0;
208 }
209 
210 static int
211 CloneCommand(struct cmdargs const *arg)
212 {
213   char namelist[LINE_LEN];
214   char *name;
215   int f;
216 
217   if (arg->argc == arg->argn)
218     return -1;
219 
220   namelist[sizeof namelist - 1] = '\0';
221   for (f = arg->argn; f < arg->argc; f++) {
222     strncpy(namelist, arg->argv[f], sizeof namelist - 1);
223     for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
224       bundle_DatalinkClone(arg->bundle, arg->cx, name);
225   }
226 
227   return 0;
228 }
229 
230 static int
231 RemoveCommand(struct cmdargs const *arg)
232 {
233   if (arg->argc != arg->argn)
234     return -1;
235 
236   if (arg->cx->state != DATALINK_CLOSED) {
237     log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n");
238     return 2;
239   }
240 
241   bundle_DatalinkRemove(arg->bundle, arg->cx);
242   return 0;
243 }
244 
245 static int
246 RenameCommand(struct cmdargs const *arg)
247 {
248   if (arg->argc != arg->argn + 1)
249     return -1;
250 
251   if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn]))
252     return 0;
253 
254   log_Printf(LogWARN, "%s -> %s: target name already exists\n",
255              arg->cx->name, arg->argv[arg->argn]);
256   return 1;
257 }
258 
259 int
260 LoadCommand(struct cmdargs const *arg)
261 {
262   const char *name;
263 
264   if (arg->argc > arg->argn)
265     name = arg->argv[arg->argn];
266   else
267     name = "default";
268 
269   if (!system_IsValid(name, arg->prompt, arg->bundle->phys_type.all)) {
270     log_Printf(LogERROR, "%s: Label not allowed\n", name);
271     return 1;
272   } else {
273     /*
274      * Set the label before & after so that `set enddisc' works and
275      * we handle nested `load' commands.
276      */
277     bundle_SetLabel(arg->bundle, arg->argc > arg->argn ? name : NULL);
278     if (system_Select(arg->bundle, name, CONFFILE, arg->prompt, arg->cx) < 0) {
279       bundle_SetLabel(arg->bundle, NULL);
280       log_Printf(LogWARN, "%s: label not found.\n", name);
281       return -1;
282     }
283     bundle_SetLabel(arg->bundle, arg->argc > arg->argn ? name : NULL);
284   }
285   return 0;
286 }
287 
288 int
289 SaveCommand(struct cmdargs const *arg)
290 {
291   log_Printf(LogWARN, "save command is not implemented (yet).\n");
292   return 1;
293 }
294 
295 static int
296 DialCommand(struct cmdargs const *arg)
297 {
298   int res;
299 
300   if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO)))
301       || (!arg->cx &&
302           (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) {
303     log_Printf(LogWARN, "Manual dial is only available for auto and"
304               " interactive links\n");
305     return 1;
306   }
307 
308   if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0)
309     return res;
310 
311   bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL);
312 
313   return 0;
314 }
315 
316 static int
317 ShellCommand(struct cmdargs const *arg, int bg)
318 {
319   const char *shell;
320   pid_t shpid;
321   int argc;
322   char *argv[MAXARGS];
323 
324 #ifdef SHELL_ONLY_INTERACTIVELY
325   /* we're only allowed to shell when we run ppp interactively */
326   if (arg->prompt && arg->prompt->owner) {
327     log_Printf(LogWARN, "Can't start a shell from a network connection\n");
328     return 1;
329   }
330 #endif
331 
332   if (arg->argc == arg->argn) {
333     if (!arg->prompt) {
334       log_Printf(LogWARN, "Can't start an interactive shell from"
335                 " a config file\n");
336       return 1;
337     } else if (arg->prompt->owner) {
338       log_Printf(LogWARN, "Can't start an interactive shell from"
339                 " a socket connection\n");
340       return 1;
341     } else if (bg) {
342       log_Printf(LogWARN, "Can only start an interactive shell in"
343 		" the foreground mode\n");
344       return 1;
345     }
346   }
347 
348   if ((shpid = fork()) == 0) {
349     int i, fd;
350 
351     if ((shell = getenv("SHELL")) == 0)
352       shell = _PATH_BSHELL;
353 
354     timer_TermService();
355 
356     if (arg->prompt)
357       fd = arg->prompt->fd_out;
358     else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
359       log_Printf(LogALERT, "Failed to open %s: %s\n",
360                 _PATH_DEVNULL, strerror(errno));
361       exit(1);
362     }
363     for (i = 0; i < 3; i++)
364       dup2(fd, i);
365 
366     fcntl(3, F_SETFD, 1);	/* Set close-on-exec flag */
367 
368     setuid(geteuid());
369     if (arg->argc > arg->argn) {
370       /* substitute pseudo args */
371       argv[0] = strdup(arg->argv[arg->argn]);
372       for (argc = 1; argc < arg->argc - arg->argn; argc++) {
373 	if (strcasecmp(arg->argv[argc + arg->argn], "HISADDR") == 0)
374 	  argv[argc] = strdup(inet_ntoa(arg->bundle->ncp.ipcp.peer_ip));
375 	else if (strcasecmp(arg->argv[argc + arg->argn], "INTERFACE") == 0)
376 	  argv[argc] = strdup(arg->bundle->ifp.Name);
377 	else if (strcasecmp(arg->argv[argc + arg->argn], "MYADDR") == 0)
378 	  argv[argc] = strdup(inet_ntoa(arg->bundle->ncp.ipcp.my_ip));
379         else
380           argv[argc] = strdup(arg->argv[argc + arg->argn]);
381       }
382       argv[argc] = NULL;
383       if (bg) {
384 	pid_t p;
385 
386 	p = getpid();
387 	if (daemon(1, 1) == -1) {
388 	  log_Printf(LogERROR, "%d: daemon: %s\n", (int)p, strerror(errno));
389 	  exit(1);
390 	}
391       } else if (arg->prompt)
392         printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]);
393       execvp(argv[0], argv);
394     } else {
395       if (arg->prompt)
396         printf("ppp: Pausing until %s finishes\n", shell);
397       prompt_TtyOldMode(arg->prompt);
398       execl(shell, shell, NULL);
399     }
400 
401     log_Printf(LogWARN, "exec() of %s failed\n",
402               arg->argc > arg->argn ? arg->argv[arg->argn] : shell);
403     exit(255);
404   }
405 
406   if (shpid == (pid_t) - 1)
407     log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno));
408   else {
409     int status;
410     waitpid(shpid, &status, 0);
411   }
412 
413   if (arg->prompt && !arg->prompt->owner)
414     prompt_TtyCommandMode(arg->prompt);
415 
416   return 0;
417 }
418 
419 static int
420 BgShellCommand(struct cmdargs const *arg)
421 {
422   if (arg->argc == arg->argn)
423     return -1;
424   return ShellCommand(arg, 1);
425 }
426 
427 static int
428 FgShellCommand(struct cmdargs const *arg)
429 {
430   return ShellCommand(arg, 0);
431 }
432 
433 static struct cmdtab const Commands[] = {
434   {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
435   "accept option request", "accept option .."},
436   {"add", NULL, AddCommand, LOCAL_AUTH,
437   "add route", "add dest mask gateway", NULL},
438   {NULL, "add!", AddCommand, LOCAL_AUTH,
439   "add or change route", "add! dest mask gateway", (void *)1},
440 #ifndef NOALIAS
441   {"alias", NULL, AliasCommand, LOCAL_AUTH,
442   "alias control", "alias option [yes|no]"},
443 #endif
444   {"allow", "auth", AllowCommand, LOCAL_AUTH,
445   "Allow ppp access", "allow users|modes ...."},
446   {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
447   "Run a background command", "[!]bg command"},
448   {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
449   "Clear throughput statistics", "clear ipcp|modem [current|overall|peak]..."},
450   {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
451   "Clone a link", "clone newname..."},
452   {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
453   "Close an FSM", "close [lcp|ccp]"},
454   {"delete", NULL, DeleteCommand, LOCAL_AUTH,
455   "delete route", "delete dest", NULL},
456   {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
457   "delete a route if it exists", "delete! dest", (void *)1},
458   {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
459   "Deny option request", "deny option .."},
460   {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
461   "Dial and login", "dial|call [remote]"},
462   {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
463   "Disable option", "disable option .."},
464   {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
465   "Generate a down event", "down"},
466   {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
467   "Enable option", "enable option .."},
468   {"link", "datalink", LinkCommand, LOCAL_AUTH,
469   "Link specific commands", "link name command ..."},
470   {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
471   "Load settings", "load [remote]"},
472   {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT,
473   "Open an FSM", "open [lcp|ccp]"},
474   {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
475   "Password for manipulation", "passwd LocalPassword"},
476   {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
477   "Quit PPP program", "quit|bye [all]"},
478   {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
479   "Remove a link", "remove"},
480   {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
481   "Rename a link", "rename name"},
482   {"save", NULL, SaveCommand, LOCAL_AUTH,
483   "Save settings", "save"},
484   {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
485   "Set parameters", "set[up] var value"},
486   {"shell", "!", FgShellCommand, LOCAL_AUTH,
487   "Run a subshell", "shell|! [sh command]"},
488   {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
489   "Show status and stats", "show var"},
490   {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
491   "Enter terminal mode", "term"},
492   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
493   "Display this message", "help|? [command]", Commands},
494   {NULL, NULL, NULL},
495 };
496 
497 static int
498 ShowEscape(struct cmdargs const *arg)
499 {
500   if (arg->cx->physical->async.cfg.EscMap[32]) {
501     int code, bit;
502     const char *sep = "";
503 
504     for (code = 0; code < 32; code++)
505       if (arg->cx->physical->async.cfg.EscMap[code])
506 	for (bit = 0; bit < 8; bit++)
507 	  if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
508 	    prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
509             sep = ", ";
510           }
511     prompt_Printf(arg->prompt, "\n");
512   }
513   return 0;
514 }
515 
516 static int
517 ShowTimerList(struct cmdargs const *arg)
518 {
519   timer_Show(0, arg->prompt);
520   return 0;
521 }
522 
523 static int
524 ShowStopped(struct cmdargs const *arg)
525 {
526   prompt_Printf(arg->prompt, " Stopped Timer:  LCP: ");
527   if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
528     prompt_Printf(arg->prompt, "Disabled");
529   else
530     prompt_Printf(arg->prompt, "%ld secs",
531                   arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
532 
533   prompt_Printf(arg->prompt, ", CCP: ");
534   if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
535     prompt_Printf(arg->prompt, "Disabled");
536   else
537     prompt_Printf(arg->prompt, "%ld secs",
538                   arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
539 
540   prompt_Printf(arg->prompt, "\n");
541 
542   return 0;
543 }
544 
545 static int
546 ShowVersion(struct cmdargs const *arg)
547 {
548   prompt_Printf(arg->prompt, "PPP Version %s - %s\n", Version, VersionDate);
549   return 0;
550 }
551 
552 static int
553 ShowProtocolStats(struct cmdargs const *arg)
554 {
555   struct link *l = command_ChooseLink(arg);
556 
557   if (!l)
558     return -1;
559   prompt_Printf(arg->prompt, "%s:\n", l->name);
560   link_ReportProtocolStatus(l, arg->prompt);
561   return 0;
562 }
563 
564 static struct cmdtab const ShowCommands[] = {
565   {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
566   "bundle details", "show bundle"},
567   {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
568   "CCP status", "show cpp"},
569   {"compress", NULL, sl_Show, LOCAL_AUTH,
570   "VJ compression stats", "show compress"},
571   {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
572   "escape characters", "show escape"},
573   {"filter", NULL, filter_Show, LOCAL_AUTH,
574   "packet filters", "show filter [in|out|dial|alive]"},
575   {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
576   "HDLC errors", "show hdlc"},
577   {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
578   "IPCP status", "show ipcp"},
579   {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
580   "LCP status", "show lcp"},
581   {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
582   "(high-level) link info", "show link"},
583   {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
584   "available link names", "show links"},
585   {"log", NULL, log_ShowLevel, LOCAL_AUTH,
586   "log levels", "show log"},
587   {"mem", NULL, mbuf_Show, LOCAL_AUTH,
588   "mbuf allocations", "show mem"},
589   {"modem", NULL, modem_ShowStatus, LOCAL_AUTH | LOCAL_CX,
590   "(low-level) link info", "show modem"},
591   {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
592   "multilink setup", "show mp"},
593   {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
594   "protocol summary", "show proto"},
595   {"route", NULL, route_Show, LOCAL_AUTH,
596   "routing table", "show route"},
597   {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
598   "STOPPED timeout", "show stopped"},
599   {"timers", NULL, ShowTimerList, LOCAL_AUTH,
600   "alarm timers", "show timers"},
601   {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
602   "version string", "show version"},
603   {"who", NULL, log_ShowWho, LOCAL_AUTH,
604   "client list", "show who"},
605   {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
606   "Display this message", "show help|? [command]", ShowCommands},
607   {NULL, NULL, NULL},
608 };
609 
610 static struct cmdtab const *
611 FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
612 {
613   int nmatch;
614   int len;
615   struct cmdtab const *found;
616 
617   found = NULL;
618   len = strlen(str);
619   nmatch = 0;
620   while (cmds->func) {
621     if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
622       if (cmds->name[len] == '\0') {
623 	*pmatch = 1;
624 	return cmds;
625       }
626       nmatch++;
627       found = cmds;
628     } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
629       if (cmds->alias[len] == '\0') {
630 	*pmatch = 1;
631 	return cmds;
632       }
633       nmatch++;
634       found = cmds;
635     }
636     cmds++;
637   }
638   *pmatch = nmatch;
639   return found;
640 }
641 
642 static const char *
643 mkPrefix(int argc, char const *const *argv, char *tgt, int sz)
644 {
645   int f, tlen, len;
646 
647   tlen = 0;
648   for (f = 0; f < argc && tlen < sz - 2; f++) {
649     if (f)
650       tgt[tlen++] = ' ';
651     len = strlen(argv[f]);
652     if (len > sz - tlen - 1)
653       len = sz - tlen - 1;
654     strncpy(tgt+tlen, argv[f], len);
655     tlen += len;
656   }
657   tgt[tlen] = '\0';
658   return tgt;
659 }
660 
661 static int
662 FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
663          char const *const *argv, struct prompt *prompt, struct datalink *cx)
664 {
665   struct cmdtab const *cmd;
666   int val = 1;
667   int nmatch;
668   struct cmdargs arg;
669   char prefix[100];
670 
671   cmd = FindCommand(cmds, argv[argn], &nmatch);
672   if (nmatch > 1)
673     log_Printf(LogWARN, "%s: Ambiguous command\n",
674               mkPrefix(argn+1, argv, prefix, sizeof prefix));
675   else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
676     if ((cmd->lauth & LOCAL_CX) && !cx)
677       /* We've got no context, but we require it */
678       cx = bundle2datalink(bundle, NULL);
679 
680     if ((cmd->lauth & LOCAL_CX) && !cx)
681       log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
682                 mkPrefix(argn+1, argv, prefix, sizeof prefix));
683     else {
684       if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
685         log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
686                   mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
687         cx = NULL;
688       }
689       arg.cmdtab = cmds;
690       arg.cmd = cmd;
691       arg.argc = argc;
692       arg.argn = argn+1;
693       arg.argv = argv;
694       arg.bundle = bundle;
695       arg.cx = cx;
696       arg.prompt = prompt;
697       val = (*cmd->func) (&arg);
698     }
699   } else
700     log_Printf(LogWARN, "%s: Invalid command\n",
701               mkPrefix(argn+1, argv, prefix, sizeof prefix));
702 
703   if (val == -1)
704     log_Printf(LogWARN, "Usage: %s\n", cmd->syntax);
705   else if (val)
706     log_Printf(LogWARN, "%s: Failed %d\n",
707               mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
708 
709   return val;
710 }
711 
712 void
713 command_Interpret(char *buff, int nb, int *argc, char ***argv)
714 {
715   static char *vector[MAXARGS];
716   char *cp;
717 
718   if (nb > 0) {
719     cp = buff + strcspn(buff, "\r\n");
720     if (cp)
721       *cp = '\0';
722     *argc = MakeArgs(buff, vector, VECSIZE(vector));
723     *argv = vector;
724   } else
725     *argc = 0;
726 }
727 
728 static int
729 arghidden(int argc, char const *const *argv, int n)
730 {
731   /* Is arg n of the given command to be hidden from the log ? */
732 
733   /* set authkey xxxxx */
734   /* set key xxxxx */
735   if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
736       (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
737     return 1;
738 
739   /* passwd xxxxx */
740   if (n == 1 && !strncasecmp(argv[0], "p", 1))
741     return 1;
742 
743   /* set server port xxxxx .... */
744   if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
745       !strncasecmp(argv[1], "se", 2))
746     return 1;
747 
748   return 0;
749 }
750 
751 void
752 command_Run(struct bundle *bundle, int argc, char const *const *argv,
753            struct prompt *prompt, const char *label, struct datalink *cx)
754 {
755   if (argc > 0) {
756     if (log_IsKept(LogCOMMAND)) {
757       static char buf[LINE_LEN];
758       int f, n;
759 
760       *buf = '\0';
761       if (label) {
762         strncpy(buf, label, sizeof buf - 3);
763         buf[sizeof buf - 3] = '\0';
764         strcat(buf, ": ");
765       }
766       n = strlen(buf);
767       for (f = 0; f < argc; f++) {
768         if (n < sizeof buf - 1 && f)
769           buf[n++] = ' ';
770         if (arghidden(argc, argv, f))
771           strncpy(buf+n, "********", sizeof buf - n - 1);
772         else
773           strncpy(buf+n, argv[f], sizeof buf - n - 1);
774         n += strlen(buf+n);
775       }
776       log_Printf(LogCOMMAND, "%s\n", buf);
777     }
778     FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
779   }
780 }
781 
782 void
783 command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
784               const char *label)
785 {
786   int argc;
787   char **argv;
788 
789   command_Interpret(buff, nb, &argc, &argv);
790   command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
791 }
792 
793 static int
794 ShowCommand(struct cmdargs const *arg)
795 {
796   if (!arg->prompt)
797     log_Printf(LogWARN, "show: Cannot show without a prompt\n");
798   else if (arg->argc > arg->argn)
799     FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
800              arg->prompt, arg->cx);
801   else
802     prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
803 
804   return 0;
805 }
806 
807 static int
808 TerminalCommand(struct cmdargs const *arg)
809 {
810   if (!arg->prompt) {
811     log_Printf(LogWARN, "term: Need a prompt\n");
812     return 1;
813   }
814 
815   if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
816     prompt_Printf(arg->prompt, "LCP state is [%s]\n",
817                   State2Nam(arg->cx->physical->link.lcp.fsm.state));
818     return 1;
819   }
820 
821   datalink_Up(arg->cx, 0, 0);
822   prompt_TtyTermMode(arg->prompt, arg->cx);
823   return 0;
824 }
825 
826 static int
827 QuitCommand(struct cmdargs const *arg)
828 {
829   if (!arg->prompt || prompt_IsController(arg->prompt) ||
830       (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
831        (arg->prompt->auth & LOCAL_AUTH)))
832     Cleanup(EX_NORMAL);
833   if (arg->prompt)
834     prompt_Destroy(arg->prompt, 1);
835 
836   return 0;
837 }
838 
839 static int
840 OpenCommand(struct cmdargs const *arg)
841 {
842   if (arg->argc == arg->argn ||
843       (arg->argc == arg->argn+1 && !strcasecmp(arg->argv[arg->argn], "lcp")))
844     bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL);
845   else if (arg->argc == arg->argn+1 &&
846            !strcasecmp(arg->argv[arg->argn], "ccp")) {
847     struct link *l;
848     struct fsm *fp;
849 
850     if (!(l = command_ChooseLink(arg)))
851       return -1;
852     fp = &l->ccp.fsm;
853 
854     if (fp->link->lcp.fsm.state != ST_OPENED)
855       log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
856     else if (fp->state != ST_OPENED) {
857       fp->open_mode = 0;	/* Not passive any more */
858       if (fp->state == ST_STOPPED) {
859         fsm_Down(fp);
860         fsm_Up(fp);
861       } else {
862         fsm_Up(fp);
863         fsm_Open(fp);
864       }
865     }
866   } else
867     return -1;
868 
869   return 0;
870 }
871 
872 static int
873 CloseCommand(struct cmdargs const *arg)
874 {
875   if (arg->argc == arg->argn)
876     bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
877   else if (arg->argc == arg->argn + 1) {
878     if (!strcasecmp(arg->argv[arg->argn], "lcp"))
879       bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
880     else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
881              !strcasecmp(arg->argv[arg->argn], "ccp!")) {
882       struct link *l;
883       struct fsm *fp;
884 
885       if (!(l = command_ChooseLink(arg)))
886         return -1;
887       fp = &l->ccp.fsm;
888 
889       if (fp->state == ST_OPENED) {
890         fsm_Close(fp);
891         if (arg->argv[arg->argn][3] == '!')
892           fp->open_mode = 0;		/* Stay ST_CLOSED */
893         else
894           fp->open_mode = OPEN_PASSIVE;	/* Wait for the peer to start */
895       }
896     } else
897       return -1;
898   } else
899     return -1;
900 
901   return 0;
902 }
903 
904 static int
905 DownCommand(struct cmdargs const *arg)
906 {
907   if (arg->argc == arg->argn ||
908       (arg->argc == arg->argn+1 && !strcasecmp(arg->argv[arg->argn], "lcp"))) {
909     if (arg->cx)
910       datalink_Down(arg->cx, CLOSE_STAYDOWN);
911     else
912       bundle_Down(arg->bundle);
913   } else if (arg->argc == arg->argn+1 &&
914            !strcasecmp(arg->argv[arg->argn], "ccp")) {
915     struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
916                                &arg->bundle->ncp.mp.link.ccp.fsm;
917     fsm_Down(fp);
918     fsm_Close(fp);
919   } else
920     return -1;
921 
922   return 0;
923 }
924 
925 static int
926 SetModemSpeed(struct cmdargs const *arg)
927 {
928   long speed;
929   char *end;
930 
931   if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
932     if (arg->argc > arg->argn+1) {
933       log_Printf(LogWARN, "SetModemSpeed: Too many arguments");
934       return -1;
935     }
936     if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
937       physical_SetSync(arg->cx->physical);
938       return 0;
939     }
940     end = NULL;
941     speed = strtol(arg->argv[arg->argn], &end, 10);
942     if (*end) {
943       log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
944                 arg->argv[arg->argn]);
945       return -1;
946     }
947     if (physical_SetSpeed(arg->cx->physical, speed))
948       return 0;
949     log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
950   } else
951     log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
952 
953   return -1;
954 }
955 
956 static int
957 SetStoppedTimeout(struct cmdargs const *arg)
958 {
959   struct link *l = &arg->cx->physical->link;
960 
961   l->lcp.fsm.StoppedTimer.load = 0;
962   l->ccp.fsm.StoppedTimer.load = 0;
963   if (arg->argc <= arg->argn+2) {
964     if (arg->argc > arg->argn) {
965       l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
966       if (arg->argc > arg->argn+1)
967         l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
968     }
969     return 0;
970   }
971   return -1;
972 }
973 
974 #define ismask(x) \
975   (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3)
976 
977 static int
978 SetServer(struct cmdargs const *arg)
979 {
980   int res = -1;
981 
982   if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
983     const char *port, *passwd, *mask;
984 
985     /* What's what ? */
986     port = arg->argv[arg->argn];
987     if (arg->argc == arg->argn + 2) {
988       passwd = arg->argv[arg->argn+1];
989       mask = NULL;
990     } else if (arg->argc == arg->argn + 3) {
991       passwd = arg->argv[arg->argn+1];
992       mask = arg->argv[arg->argn+2];
993       if (!ismask(mask))
994         return -1;
995     } else if (strcasecmp(port, "none") == 0) {
996       if (server_Close(arg->bundle))
997         log_Printf(LogPHASE, "Disabled server port.\n");
998       return 0;
999     } else
1000       return -1;
1001 
1002     strncpy(server.passwd, passwd, sizeof server.passwd - 1);
1003     server.passwd[sizeof server.passwd - 1] = '\0';
1004 
1005     if (*port == '/') {
1006       mode_t imask;
1007       char *ptr, name[LINE_LEN + 12];
1008 
1009       if (mask != NULL) {
1010 	unsigned m;
1011 
1012 	if (sscanf(mask, "%o", &m) == 1)
1013 	  imask = m;
1014         else
1015           return -1;
1016       } else
1017         imask = (mode_t)-1;
1018 
1019       ptr = strstr(port, "%d");
1020       if (ptr) {
1021         snprintf(name, sizeof name, "%.*s%d%s",
1022                  ptr - port, port, arg->bundle->unit, ptr + 2);
1023         port = name;
1024       }
1025       res = server_LocalOpen(arg->bundle, port, imask);
1026     } else {
1027       int iport, add = 0;
1028 
1029       if (mask != NULL)
1030         return -1;
1031 
1032       if (*port == '+') {
1033         port++;
1034         add = 1;
1035       }
1036       if (strspn(port, "0123456789") != strlen(port)) {
1037         struct servent *s;
1038 
1039         if ((s = getservbyname(port, "tcp")) == NULL) {
1040 	  iport = 0;
1041 	  log_Printf(LogWARN, "%s: Invalid port or service\n", port);
1042 	} else
1043 	  iport = ntohs(s->s_port);
1044       } else
1045         iport = atoi(port);
1046 
1047       if (iport) {
1048         if (add)
1049           iport += arg->bundle->unit;
1050         res = server_TcpOpen(arg->bundle, iport);
1051       } else
1052         res = -1;
1053     }
1054   }
1055 
1056   return res;
1057 }
1058 
1059 static int
1060 SetModemParity(struct cmdargs const *arg)
1061 {
1062   return arg->argc > arg->argn ? modem_SetParity(arg->cx->physical,
1063                                                  arg->argv[arg->argn]) : -1;
1064 }
1065 
1066 static int
1067 SetEscape(struct cmdargs const *arg)
1068 {
1069   int code;
1070   int argc = arg->argc - arg->argn;
1071   char const *const *argv = arg->argv + arg->argn;
1072 
1073   for (code = 0; code < 33; code++)
1074     arg->cx->physical->async.cfg.EscMap[code] = 0;
1075 
1076   while (argc-- > 0) {
1077     sscanf(*argv++, "%x", &code);
1078     code &= 0xff;
1079     arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
1080     arg->cx->physical->async.cfg.EscMap[32] = 1;
1081   }
1082   return 0;
1083 }
1084 
1085 static struct in_addr
1086 GetIpAddr(const char *cp)
1087 {
1088   struct hostent *hp;
1089   struct in_addr ipaddr;
1090 
1091   if (inet_aton(cp, &ipaddr) == 0) {
1092     hp = gethostbyname(cp);
1093     if (hp && hp->h_addrtype == AF_INET)
1094       memcpy(&ipaddr, hp->h_addr, hp->h_length);
1095     else
1096       ipaddr.s_addr = 0;
1097   }
1098   return (ipaddr);
1099 }
1100 
1101 static int
1102 SetInterfaceAddr(struct cmdargs const *arg)
1103 {
1104   struct ipcp *ipcp = &arg->bundle->ncp.ipcp;
1105   const char *hisaddr;
1106 
1107   hisaddr = NULL;
1108   ipcp->cfg.my_range.ipaddr.s_addr = INADDR_ANY;
1109   ipcp->cfg.peer_range.ipaddr.s_addr = INADDR_ANY;
1110 
1111   if (arg->argc > arg->argn + 4)
1112     return -1;
1113 
1114   ipcp->cfg.HaveTriggerAddress = 0;
1115   ipcp->cfg.netmask.s_addr = INADDR_ANY;
1116   iplist_reset(&ipcp->cfg.peer_list);
1117 
1118   if (arg->argc > arg->argn) {
1119     if (!ParseAddr(ipcp, arg->argc - arg->argn, arg->argv + arg->argn,
1120                    &ipcp->cfg.my_range.ipaddr, &ipcp->cfg.my_range.mask,
1121                    &ipcp->cfg.my_range.width))
1122       return 1;
1123     if (arg->argc > arg->argn+1) {
1124       hisaddr = arg->argv[arg->argn+1];
1125       if (arg->argc > arg->argn+2) {
1126         ipcp->cfg.netmask = GetIpAddr(arg->argv[arg->argn+2]);
1127 	if (arg->argc > arg->argn+3) {
1128 	  ipcp->cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
1129 	  ipcp->cfg.HaveTriggerAddress = 1;
1130 	}
1131       }
1132     }
1133   }
1134 
1135   /*
1136    * For backwards compatibility, 0.0.0.0 means any address.
1137    */
1138   if (ipcp->cfg.my_range.ipaddr.s_addr == INADDR_ANY) {
1139     ipcp->cfg.my_range.mask.s_addr = INADDR_ANY;
1140     ipcp->cfg.my_range.width = 0;
1141   }
1142   ipcp->my_ip.s_addr = ipcp->cfg.my_range.ipaddr.s_addr;
1143 
1144   if (ipcp->cfg.peer_range.ipaddr.s_addr == INADDR_ANY) {
1145     ipcp->cfg.peer_range.mask.s_addr = INADDR_ANY;
1146     ipcp->cfg.peer_range.width = 0;
1147   }
1148 
1149   if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
1150                                   arg->bundle->phys_type.all & PHYS_AUTO))
1151     return 4;
1152 
1153   return 0;
1154 }
1155 
1156 static int
1157 SetVariable(struct cmdargs const *arg)
1158 {
1159   u_long ulong_val;
1160   const char *argp;
1161   int param = (int)arg->cmd->args, mode;
1162   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1163   const char *err = NULL;
1164   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1165   int dummyint;
1166   struct in_addr dummyaddr, *addr;
1167 
1168   if (!l)
1169     return -1;
1170 
1171   if (arg->argc > arg->argn)
1172     argp = arg->argv[arg->argn];
1173   else
1174     argp = "";
1175 
1176   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1177     log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
1178               arg->cmd->name);
1179     return 1;
1180   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1181     log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
1182               arg->cmd->name, cx->name);
1183     cx = NULL;
1184   }
1185 
1186   switch (param) {
1187   case VAR_AUTHKEY:
1188     if (bundle_Phase(arg->bundle) == PHASE_DEAD) {
1189       strncpy(arg->bundle->cfg.auth.key, argp,
1190               sizeof arg->bundle->cfg.auth.key - 1);
1191       arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
1192     } else {
1193       err = "set authkey: Only available at phase DEAD\n";
1194       log_Printf(LogWARN, err);
1195     }
1196     break;
1197   case VAR_AUTHNAME:
1198     if (bundle_Phase(arg->bundle) == PHASE_DEAD) {
1199       strncpy(arg->bundle->cfg.auth.name, argp,
1200               sizeof arg->bundle->cfg.auth.name - 1);
1201       arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name - 1] = '\0';
1202     } else {
1203       err = "set authname: Only available at phase DEAD\n";
1204       log_Printf(LogWARN, err);
1205     }
1206     break;
1207   case VAR_AUTOLOAD:
1208     if (arg->argc == arg->argn + 2 || arg->argc == arg->argn + 4) {
1209       arg->bundle->autoload.running = 1;
1210       arg->bundle->cfg.autoload.max.timeout = atoi(arg->argv[arg->argn]);
1211       arg->bundle->cfg.autoload.max.packets = atoi(arg->argv[arg->argn + 1]);
1212       if (arg->argc == arg->argn + 4) {
1213         arg->bundle->cfg.autoload.min.timeout = atoi(arg->argv[arg->argn + 2]);
1214         arg->bundle->cfg.autoload.min.packets = atoi(arg->argv[arg->argn + 3]);
1215       } else {
1216         arg->bundle->cfg.autoload.min.timeout = 0;
1217         arg->bundle->cfg.autoload.min.packets = 0;
1218       }
1219     } else {
1220       err = "Set autoload requires two or four arguments\n";
1221       log_Printf(LogWARN, err);
1222     }
1223     break;
1224   case VAR_DIAL:
1225     strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
1226     cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
1227     break;
1228   case VAR_LOGIN:
1229     strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
1230     cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
1231     break;
1232   case VAR_WINSIZE:
1233     if (arg->argc > arg->argn) {
1234       l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
1235       if (l->ccp.cfg.deflate.out.winsize < 8 ||
1236           l->ccp.cfg.deflate.out.winsize > 15) {
1237           log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
1238                     l->ccp.cfg.deflate.out.winsize);
1239           l->ccp.cfg.deflate.out.winsize = 15;
1240       }
1241       if (arg->argc > arg->argn+1) {
1242         l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
1243         if (l->ccp.cfg.deflate.in.winsize < 8 ||
1244             l->ccp.cfg.deflate.in.winsize > 15) {
1245             log_Printf(LogWARN, "%d: Invalid incoming window size\n",
1246                       l->ccp.cfg.deflate.in.winsize);
1247             l->ccp.cfg.deflate.in.winsize = 15;
1248         }
1249       } else
1250         l->ccp.cfg.deflate.in.winsize = 0;
1251     } else {
1252       err = "No window size specified\n";
1253       log_Printf(LogWARN, err);
1254     }
1255     break;
1256   case VAR_DEVICE:
1257     physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
1258                            arg->argv + arg->argn);
1259     break;
1260   case VAR_ACCMAP:
1261     if (arg->argc > arg->argn) {
1262       sscanf(argp, "%lx", &ulong_val);
1263       cx->physical->link.lcp.cfg.accmap = ulong_val;
1264     } else {
1265       err = "No accmap specified\n";
1266       log_Printf(LogWARN, err);
1267     }
1268     break;
1269   case VAR_MODE:
1270     mode = Nam2mode(argp);
1271     if (mode == PHYS_NONE || mode == PHYS_ALL) {
1272       log_Printf(LogWARN, "%s: Invalid mode\n", argp);
1273       return -1;
1274     }
1275     bundle_SetMode(arg->bundle, cx, mode);
1276     break;
1277   case VAR_MRRU:
1278     if (bundle_Phase(arg->bundle) != PHASE_DEAD)
1279       log_Printf(LogWARN, "mrru: Only changable at phase DEAD\n");
1280     else {
1281       ulong_val = atol(argp);
1282       if (ulong_val < MIN_MRU)
1283         err = "Given MRRU value (%lu) is too small.\n";
1284       else if (ulong_val > MAX_MRU)
1285         err = "Given MRRU value (%lu) is too big.\n";
1286       else
1287         arg->bundle->ncp.mp.cfg.mrru = ulong_val;
1288       if (err)
1289         log_Printf(LogWARN, err, ulong_val);
1290     }
1291     break;
1292   case VAR_MRU:
1293     ulong_val = atol(argp);
1294     if (ulong_val < MIN_MRU)
1295       err = "Given MRU value (%lu) is too small.\n";
1296     else if (ulong_val > MAX_MRU)
1297       err = "Given MRU value (%lu) is too big.\n";
1298     else
1299       l->lcp.cfg.mru = ulong_val;
1300     if (err)
1301       log_Printf(LogWARN, err, ulong_val);
1302     break;
1303   case VAR_MTU:
1304     ulong_val = atol(argp);
1305     if (ulong_val == 0)
1306       arg->bundle->cfg.mtu = 0;
1307     else if (ulong_val < MIN_MTU)
1308       err = "Given MTU value (%lu) is too small.\n";
1309     else if (ulong_val > MAX_MTU)
1310       err = "Given MTU value (%lu) is too big.\n";
1311     else
1312       arg->bundle->cfg.mtu = ulong_val;
1313     if (err)
1314       log_Printf(LogWARN, err, ulong_val);
1315     break;
1316   case VAR_OPENMODE:
1317     if (strcasecmp(argp, "active") == 0)
1318       cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
1319         atoi(arg->argv[arg->argn+1]) : 1;
1320     else if (strcasecmp(argp, "passive") == 0)
1321       cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
1322     else {
1323       err = "%s: Invalid openmode\n";
1324       log_Printf(LogWARN, err, argp);
1325     }
1326     break;
1327   case VAR_PHONE:
1328     strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
1329     cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
1330     break;
1331   case VAR_HANGUP:
1332     strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
1333     cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
1334     break;
1335   case VAR_IDLETIMEOUT:
1336     if (arg->argc > arg->argn+1)
1337       err = "Too many idle timeout values\n";
1338     else if (arg->argc == arg->argn+1)
1339       bundle_SetIdleTimer(arg->bundle, atoi(argp));
1340     if (err)
1341       log_Printf(LogWARN, err);
1342     break;
1343   case VAR_LQRPERIOD:
1344     ulong_val = atol(argp);
1345     if (ulong_val <= 0) {
1346       err = "%s: Invalid lqr period\n";
1347       log_Printf(LogWARN, err, argp);
1348     } else
1349       l->lcp.cfg.lqrperiod = ulong_val;
1350     break;
1351   case VAR_LCPRETRY:
1352     ulong_val = atol(argp);
1353     if (ulong_val <= 0) {
1354       err = "%s: Invalid LCP FSM retry period\n";
1355       log_Printf(LogWARN, err, argp);
1356     } else
1357       cx->physical->link.lcp.cfg.fsmretry = ulong_val;
1358     break;
1359   case VAR_CHAPRETRY:
1360     ulong_val = atol(argp);
1361     if (ulong_val <= 0) {
1362       err = "%s: Invalid CHAP retry period\n";
1363       log_Printf(LogWARN, err, argp);
1364     } else
1365       cx->chap.auth.cfg.fsmretry = ulong_val;
1366     break;
1367   case VAR_PAPRETRY:
1368     ulong_val = atol(argp);
1369     if (ulong_val <= 0) {
1370       err = "%s: Invalid PAP retry period\n";
1371       log_Printf(LogWARN, err, argp);
1372     } else
1373       cx->pap.cfg.fsmretry = ulong_val;
1374     break;
1375   case VAR_CCPRETRY:
1376     ulong_val = atol(argp);
1377     if (ulong_val <= 0) {
1378       err = "%s: Invalid CCP FSM retry period\n";
1379       log_Printf(LogWARN, err, argp);
1380     } else
1381       l->ccp.cfg.fsmretry = ulong_val;
1382     break;
1383   case VAR_IPCPRETRY:
1384     ulong_val = atol(argp);
1385     if (ulong_val <= 0) {
1386       err = "%s: Invalid IPCP FSM retry period\n";
1387       log_Printf(LogWARN, err, argp);
1388     } else
1389       arg->bundle->ncp.ipcp.cfg.fsmretry = ulong_val;
1390     break;
1391   case VAR_NBNS:
1392   case VAR_DNS:
1393     if (param == VAR_DNS)
1394       addr = arg->bundle->ncp.ipcp.cfg.ns.dns;
1395     else
1396       addr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
1397 
1398     addr[0].s_addr = addr[1].s_addr = INADDR_ANY;
1399 
1400     if (arg->argc > arg->argn) {
1401       ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn,
1402                 addr, &dummyaddr, &dummyint);
1403       if (arg->argc > arg->argn+1)
1404         ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn + 1,
1405                   addr + 1, &dummyaddr, &dummyint);
1406 
1407       if (addr[1].s_addr == INADDR_ANY)
1408         addr[1].s_addr = addr[0].s_addr;
1409       if (addr[0].s_addr == INADDR_ANY)
1410         addr[0].s_addr = addr[1].s_addr;
1411     }
1412     break;
1413   }
1414 
1415   return err ? 1 : 0;
1416 }
1417 
1418 static int
1419 SetCtsRts(struct cmdargs const *arg)
1420 {
1421   if (arg->argc == arg->argn+1) {
1422     if (strcmp(arg->argv[arg->argn], "on") == 0)
1423       physical_SetRtsCts(arg->cx->physical, 1);
1424     else if (strcmp(arg->argv[arg->argn], "off") == 0)
1425       physical_SetRtsCts(arg->cx->physical, 0);
1426     else
1427       return -1;
1428     return 0;
1429   }
1430   return -1;
1431 }
1432 
1433 static struct cmdtab const SetCommands[] = {
1434   {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1435   "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
1436   {"authkey", "key", SetVariable, LOCAL_AUTH,
1437   "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
1438   {"authname", NULL, SetVariable, LOCAL_AUTH,
1439   "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
1440   {"autoload", NULL, SetVariable, LOCAL_AUTH,
1441   "auto link [de]activation", "set autoload maxtime maxload mintime minload",
1442   (const void *)VAR_AUTOLOAD},
1443   {"ccpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1444   "FSM retry period", "set ccpretry value", (const void *)VAR_CCPRETRY},
1445   {"chapretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1446   "CHAP retry period", "set chapretry value", (const void *)VAR_CHAPRETRY},
1447   {"ctsrts", "crtscts", SetCtsRts, LOCAL_AUTH | LOCAL_CX,
1448   "Use hardware flow control", "set ctsrts [on|off]"},
1449   {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1450   "deflate window sizes", "set deflate out-winsize in-winsize",
1451   (const void *) VAR_WINSIZE},
1452   {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
1453   "modem device name", "set device|line device-name[,device-name]",
1454   (const void *) VAR_DEVICE},
1455   {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1456   "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
1457   {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
1458   "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
1459   {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
1460   "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]"},
1461   {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
1462   "escape characters", "set escape hex-digit ..."},
1463   {"filter", NULL, filter_Set, LOCAL_AUTH,
1464   "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
1465   "[src_addr[/width]] [dst_addr[/width]] [tcp|udp|icmp [src [lt|eq|gt port]] "
1466   "[dst [lt|eq|gt port]] [estab] [syn] [finrst]]"},
1467   {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1468   "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
1469   {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
1470   "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
1471   {"ipcpretry", NULL, SetVariable, LOCAL_AUTH,
1472   "FSM retry period", "set ipcpretry value", (const void *)VAR_IPCPRETRY},
1473   {"lcpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1474   "FSM retry period", "set lcpretry value", (const void *)VAR_LCPRETRY},
1475   {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
1476   "set log [local] [+|-]async|ccp|chat|command|connect|debug|hdlc|id0|ipcp|"
1477   "lcp|lqm|phase|tcp/ip|timer|tun..."},
1478   {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1479   "login script", "set login chat-script", (const void *) VAR_LOGIN},
1480   {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1481   "LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
1482   {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
1483   "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
1484   {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
1485   "set mrru value", (const void *)VAR_MRRU},
1486   {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1487   "MRU value", "set mru value", (const void *)VAR_MRU},
1488   {"mtu", NULL, SetVariable, LOCAL_AUTH,
1489   "interface MTU value", "set mtu value", (const void *)VAR_MTU},
1490   {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
1491   "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
1492   {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
1493   "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
1494   {"papretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1495   "PAP retry period", "set papretry value", (const void *)VAR_PAPRETRY},
1496   {"parity", NULL, SetModemParity, LOCAL_AUTH | LOCAL_CX,
1497   "modem parity", "set parity [odd|even|none]"},
1498   {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
1499   "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
1500   {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
1501   "Reconnect timeout", "set reconnect value ntries"},
1502   {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
1503   "Redial timeout", "set redial value|random[.value|random] [attempts]"},
1504   {"server", "socket", SetServer, LOCAL_AUTH,
1505   "server port", "set server|socket TcpPort|LocalName|none password [mask]"},
1506   {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
1507   "modem speed", "set speed value"},
1508   {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
1509   "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
1510   {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
1511   "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
1512   {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
1513   "vj values", "set vj slots|slotcomp [value]"},
1514   {"weight", NULL, mp_SetDatalinkWeight, LOCAL_AUTH | LOCAL_CX,
1515   "datalink weighting", "set weight n"},
1516   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1517   "Display this message", "set help|? [command]", SetCommands},
1518   {NULL, NULL, NULL},
1519 };
1520 
1521 static int
1522 SetCommand(struct cmdargs const *arg)
1523 {
1524   if (arg->argc > arg->argn)
1525     FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
1526              arg->prompt, arg->cx);
1527   else if (arg->prompt)
1528     prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
1529 	    " syntax help.\n");
1530   else
1531     log_Printf(LogWARN, "set command must have arguments\n");
1532 
1533   return 0;
1534 }
1535 
1536 
1537 static int
1538 AddCommand(struct cmdargs const *arg)
1539 {
1540   struct in_addr dest, gateway, netmask;
1541   int gw, addrs;
1542 
1543   if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
1544     return -1;
1545 
1546   addrs = 0;
1547   if (arg->argc == arg->argn+2) {
1548     if (!strcasecmp(arg->argv[arg->argn], "default"))
1549       dest.s_addr = netmask.s_addr = INADDR_ANY;
1550     else {
1551       int width;
1552 
1553       if (!ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn,
1554 	             &dest, &netmask, &width))
1555         return -1;
1556       if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
1557         addrs = ROUTE_DSTMYADDR;
1558       else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
1559         addrs = ROUTE_DSTHISADDR;
1560     }
1561     gw = 1;
1562   } else {
1563     if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
1564       addrs = ROUTE_DSTMYADDR;
1565       dest = arg->bundle->ncp.ipcp.my_ip;
1566     } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
1567       addrs = ROUTE_DSTHISADDR;
1568       dest = arg->bundle->ncp.ipcp.peer_ip;
1569     } else
1570       dest = GetIpAddr(arg->argv[arg->argn]);
1571     netmask = GetIpAddr(arg->argv[arg->argn+1]);
1572     gw = 2;
1573   }
1574 
1575   if (strcasecmp(arg->argv[arg->argn+gw], "HISADDR") == 0) {
1576     gateway = arg->bundle->ncp.ipcp.peer_ip;
1577     addrs |= ROUTE_GWHISADDR;
1578   } else if (strcasecmp(arg->argv[arg->argn+gw], "INTERFACE") == 0)
1579     gateway.s_addr = INADDR_ANY;
1580   else
1581     gateway = GetIpAddr(arg->argv[arg->argn+gw]);
1582 
1583   if (bundle_SetRoute(arg->bundle, RTM_ADD, dest, gateway, netmask,
1584                   arg->cmd->args ? 1 : 0))
1585     route_Add(&arg->bundle->ncp.ipcp.route, addrs, dest, netmask, gateway);
1586 
1587   return 0;
1588 }
1589 
1590 static int
1591 DeleteCommand(struct cmdargs const *arg)
1592 {
1593   struct in_addr dest, none;
1594   int addrs;
1595 
1596   if (arg->argc == arg->argn+1) {
1597     if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
1598       route_IfDelete(arg->bundle, 0);
1599       route_DeleteAll(&arg->bundle->ncp.ipcp.route);
1600     } else {
1601       addrs = 0;
1602       if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
1603         dest = arg->bundle->ncp.ipcp.my_ip;
1604         addrs = ROUTE_DSTMYADDR;
1605       } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
1606         dest = arg->bundle->ncp.ipcp.peer_ip;
1607         addrs = ROUTE_DSTHISADDR;
1608       } else {
1609         if (strcasecmp(arg->argv[arg->argn], "default") == 0)
1610           dest.s_addr = INADDR_ANY;
1611         else
1612           dest = GetIpAddr(arg->argv[arg->argn]);
1613         addrs = ROUTE_STATIC;
1614       }
1615       none.s_addr = INADDR_ANY;
1616       bundle_SetRoute(arg->bundle, RTM_DELETE, dest, none, none,
1617                       arg->cmd->args ? 1 : 0);
1618       route_Delete(&arg->bundle->ncp.ipcp.route, addrs, dest);
1619     }
1620   } else
1621     return -1;
1622 
1623   return 0;
1624 }
1625 
1626 #ifndef NOALIAS
1627 static struct cmdtab const AliasCommands[] =
1628 {
1629   {"addr", NULL, alias_RedirectAddr, LOCAL_AUTH,
1630    "static address translation", "alias addr [addr_local addr_alias]"},
1631   {"deny_incoming", NULL, AliasOption, LOCAL_AUTH,
1632    "stop incoming connections", "alias deny_incoming [yes|no]",
1633    (const void *) PKT_ALIAS_DENY_INCOMING},
1634   {"enable", NULL, AliasEnable, LOCAL_AUTH,
1635    "enable IP aliasing", "alias enable [yes|no]"},
1636   {"log", NULL, AliasOption, LOCAL_AUTH,
1637    "log aliasing link creation", "alias log [yes|no]",
1638    (const void *) PKT_ALIAS_LOG},
1639   {"port", NULL, alias_RedirectPort, LOCAL_AUTH,
1640    "port redirection", "alias port [proto addr_local:port_local  port_alias]"},
1641   {"same_ports", NULL, AliasOption, LOCAL_AUTH,
1642    "try to leave port numbers unchanged", "alias same_ports [yes|no]",
1643    (const void *) PKT_ALIAS_SAME_PORTS},
1644   {"unregistered_only", NULL, AliasOption, LOCAL_AUTH,
1645    "alias unregistered (private) IP address space only",
1646    "alias unregistered_only [yes|no]",
1647    (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
1648   {"use_sockets", NULL, AliasOption, LOCAL_AUTH,
1649    "allocate host sockets", "alias use_sockets [yes|no]",
1650    (const void *) PKT_ALIAS_USE_SOCKETS},
1651   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1652    "Display this message", "alias help|? [command]", AliasCommands},
1653   {NULL, NULL, NULL},
1654 };
1655 
1656 
1657 static int
1658 AliasCommand(struct cmdargs const *arg)
1659 {
1660   if (arg->argc > arg->argn)
1661     FindExec(arg->bundle, AliasCommands, arg->argc, arg->argn, arg->argv,
1662              arg->prompt, arg->cx);
1663   else if (arg->prompt)
1664     prompt_Printf(arg->prompt, "Use `alias help' to get a list or `alias help"
1665             " <option>' for syntax help.\n");
1666   else
1667     log_Printf(LogWARN, "alias command must have arguments\n");
1668 
1669   return 0;
1670 }
1671 
1672 static int
1673 AliasEnable(struct cmdargs const *arg)
1674 {
1675   if (arg->argc == arg->argn+1) {
1676     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
1677       if (alias_Load() == 0)
1678 	return 0;
1679       log_Printf(LogWARN, "Cannot load alias library\n");
1680       return 1;
1681     } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
1682       alias_Unload();
1683       return 0;
1684     }
1685   }
1686 
1687   return -1;
1688 }
1689 
1690 
1691 static int
1692 AliasOption(struct cmdargs const *arg)
1693 {
1694   unsigned param = (unsigned)arg->cmd->args;
1695   if (arg->argc == arg->argn+1) {
1696     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
1697       if (alias_IsEnabled()) {
1698 	(*PacketAlias.SetMode)(param, param);
1699 	return 0;
1700       }
1701       log_Printf(LogWARN, "alias not enabled\n");
1702     } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
1703       if (alias_IsEnabled()) {
1704 	(*PacketAlias.SetMode)(0, param);
1705 	return 0;
1706       }
1707       log_Printf(LogWARN, "alias not enabled\n");
1708     }
1709   }
1710   return -1;
1711 }
1712 #endif /* #ifndef NOALIAS */
1713 
1714 static struct cmdtab const AllowCommands[] = {
1715   {"modes", "mode", AllowModes, LOCAL_AUTH,
1716   "Only allow certain ppp modes", "allow modes mode..."},
1717   {"users", "user", AllowUsers, LOCAL_AUTH,
1718   "Allow users access to ppp", "allow users logname..."},
1719   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1720   "Display this message", "allow help|? [command]", AllowCommands},
1721   {NULL, NULL, NULL},
1722 };
1723 
1724 static int
1725 AllowCommand(struct cmdargs const *arg)
1726 {
1727   /* arg->bundle may be NULL (see system_IsValid()) ! */
1728   if (arg->argc > arg->argn)
1729     FindExec(arg->bundle, AllowCommands, arg->argc, arg->argn, arg->argv,
1730              arg->prompt, arg->cx);
1731   else if (arg->prompt)
1732     prompt_Printf(arg->prompt, "Use `allow ?' to get a list or `allow ? <cmd>'"
1733                   " for syntax help.\n");
1734   else
1735     log_Printf(LogWARN, "allow command must have arguments\n");
1736 
1737   return 0;
1738 }
1739 
1740 static int
1741 LinkCommand(struct cmdargs const *arg)
1742 {
1743   if (arg->argc > arg->argn+1) {
1744     char namelist[LINE_LEN];
1745     struct datalink *cx;
1746     char *name;
1747     int result = 0;
1748 
1749     if (!strcmp(arg->argv[arg->argn], "*")) {
1750       struct datalink *dl;
1751 
1752       cx = arg->bundle->links;
1753       while (cx) {
1754         /* Watch it, the command could be a ``remove'' */
1755         dl = cx->next;
1756         FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
1757                  arg->prompt, cx);
1758         for (cx = arg->bundle->links; cx; cx = cx->next)
1759           if (cx == dl)
1760             break;		/* Pointer's still valid ! */
1761       }
1762     } else {
1763       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
1764       namelist[sizeof namelist - 1] = '\0';
1765       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
1766         if (!bundle2datalink(arg->bundle, name)) {
1767           log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
1768           return 1;
1769         }
1770 
1771       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
1772       namelist[sizeof namelist - 1] = '\0';
1773       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
1774         cx = bundle2datalink(arg->bundle, name);
1775         if (cx)
1776           FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
1777                    arg->prompt, cx);
1778         else {
1779           log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
1780           result++;
1781         }
1782       }
1783     }
1784     return result;
1785   }
1786 
1787   log_Printf(LogWARN, "Usage: %s\n", arg->cmd->syntax);
1788   return 2;
1789 }
1790 
1791 struct link *
1792 command_ChooseLink(struct cmdargs const *arg)
1793 {
1794   if (arg->cx)
1795     return &arg->cx->physical->link;
1796   else if (arg->bundle->ncp.mp.cfg.mrru)
1797     return &arg->bundle->ncp.mp.link;
1798   else {
1799     struct datalink *dl = bundle2datalink(arg->bundle, NULL);
1800     return dl ? &dl->physical->link : NULL;
1801   }
1802 }
1803 
1804 static const char *
1805 ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
1806 {
1807   const char *result;
1808 
1809   switch (*cmd) {
1810     case 'A':
1811     case 'a':
1812       result = "accept";
1813       *keep = NEG_MYMASK;
1814       *add = NEG_ACCEPTED;
1815       break;
1816     case 'D':
1817     case 'd':
1818       switch (cmd[1]) {
1819         case 'E':
1820         case 'e':
1821           result = "deny";
1822           *keep = NEG_MYMASK;
1823           *add = 0;
1824           break;
1825         case 'I':
1826         case 'i':
1827           result = "disable";
1828           *keep = NEG_HISMASK;
1829           *add = 0;
1830           break;
1831         default:
1832           return NULL;
1833       }
1834       break;
1835     case 'E':
1836     case 'e':
1837       result = "enable";
1838       *keep = NEG_HISMASK;
1839       *add = NEG_ENABLED;
1840       break;
1841     default:
1842       return NULL;
1843   }
1844 
1845   return result;
1846 }
1847 
1848 static int
1849 OptSet(struct cmdargs const *arg)
1850 {
1851   int bit = (int)arg->cmd->args;
1852   const char *cmd;
1853   unsigned keep;			/* Keep these bits */
1854   unsigned add;				/* Add these bits */
1855 
1856   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
1857     return 1;
1858 
1859   if (add)
1860     arg->bundle->cfg.opt |= bit;
1861   else
1862     arg->bundle->cfg.opt &= ~bit;
1863   return 0;
1864 }
1865 
1866 static int
1867 NegotiateSet(struct cmdargs const *arg)
1868 {
1869   int param = (int)arg->cmd->args;
1870   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1871   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1872   const char *cmd;
1873   unsigned keep;			/* Keep these bits */
1874   unsigned add;				/* Add these bits */
1875 
1876   if (!l)
1877     return -1;
1878 
1879   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
1880     return 1;
1881 
1882   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1883     log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
1884               cmd, arg->cmd->name);
1885     return 2;
1886   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1887     log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
1888               cmd, arg->cmd->name, cx->name);
1889     cx = NULL;
1890   }
1891 
1892   switch (param) {
1893     case NEG_ACFCOMP:
1894       cx->physical->link.lcp.cfg.acfcomp &= keep;
1895       cx->physical->link.lcp.cfg.acfcomp |= add;
1896       break;
1897     case NEG_CHAP:
1898       cx->physical->link.lcp.cfg.chap &= keep;
1899       cx->physical->link.lcp.cfg.chap |= add;
1900       break;
1901     case NEG_DEFLATE:
1902       l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
1903       l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
1904       break;
1905     case NEG_DNS:
1906       arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
1907       arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
1908       break;
1909     case NEG_LQR:
1910       cx->physical->link.lcp.cfg.lqr &= keep;
1911       cx->physical->link.lcp.cfg.lqr |= add;
1912       break;
1913     case NEG_PAP:
1914       cx->physical->link.lcp.cfg.pap &= keep;
1915       cx->physical->link.lcp.cfg.pap |= add;
1916       break;
1917     case NEG_PPPDDEFLATE:
1918       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
1919       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
1920       break;
1921     case NEG_PRED1:
1922       l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
1923       l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
1924       break;
1925     case NEG_PROTOCOMP:
1926       cx->physical->link.lcp.cfg.protocomp &= keep;
1927       cx->physical->link.lcp.cfg.protocomp |= add;
1928       break;
1929     case NEG_SHORTSEQ:
1930       if (bundle_Phase(arg->bundle) != PHASE_DEAD)
1931         log_Printf(LogWARN, "shortseq: Only changable at phase DEAD\n");
1932       else {
1933         arg->bundle->ncp.mp.cfg.shortseq &= keep;
1934         arg->bundle->ncp.mp.cfg.shortseq |= add;
1935       }
1936       break;
1937     case NEG_VJCOMP:
1938       arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
1939       arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
1940       break;
1941   }
1942 
1943   return 0;
1944 }
1945 
1946 static struct cmdtab const NegotiateCommands[] = {
1947   {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
1948   "disable|enable", (const void *)OPT_IDCHECK},
1949   {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
1950   "disable|enable", (const void *)OPT_LOOPBACK},
1951   {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
1952   "disable|enable", (const void *)OPT_PASSWDAUTH},
1953   {"proxy", NULL, OptSet, LOCAL_AUTH, "Create proxy ARP entry",
1954   "disable|enable", (const void *)OPT_PROXY},
1955   {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
1956   "disable|enable", (const void *)OPT_SROUTES},
1957   {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
1958   "disable|enable", (const void *)OPT_THROUGHPUT},
1959   {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
1960   "disable|enable", (const void *)OPT_UTMP},
1961 
1962 #define OPT_MAX 7	/* accept/deny allowed below and not above */
1963 
1964   {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1965   "Address & Control field compression", "accept|deny|disable|enable",
1966   (const void *)NEG_ACFCOMP},
1967   {"chap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1968   "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
1969   (const void *)NEG_CHAP},
1970   {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
1971   "Deflate compression", "accept|deny|disable|enable",
1972   (const void *)NEG_DEFLATE},
1973   {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
1974   "Deflate (type 24) compression", "accept|deny|disable|enable",
1975   (const void *)NEG_PPPDDEFLATE},
1976   {"dns", NULL, NegotiateSet, LOCAL_AUTH,
1977   "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
1978   {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1979   "Link Quality Reports", "accept|deny|disable|enable",
1980   (const void *)NEG_LQR},
1981   {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1982   "Password Authentication protocol", "accept|deny|disable|enable",
1983   (const void *)NEG_PAP},
1984   {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
1985   "Predictor 1 compression", "accept|deny|disable|enable",
1986   (const void *)NEG_PRED1},
1987   {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1988   "Protocol field compression", "accept|deny|disable|enable",
1989   (const void *)NEG_PROTOCOMP},
1990   {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
1991   "MP Short Sequence Numbers", "accept|deny|disable|enable",
1992   (const void *)NEG_SHORTSEQ},
1993   {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
1994   "Van Jacobson header compression", "accept|deny|disable|enable",
1995   (const void *)NEG_VJCOMP},
1996   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1997   "Display this message", "accept|deny|disable|enable help|? [value]",
1998   NegotiateCommands},
1999   {NULL, NULL, NULL},
2000 };
2001 
2002 static int
2003 NegotiateCommand(struct cmdargs const *arg)
2004 {
2005   if (arg->argc > arg->argn) {
2006     char const *argv[3];
2007     unsigned keep, add;
2008     int n;
2009 
2010     if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
2011       return -1;
2012     argv[2] = NULL;
2013 
2014     for (n = arg->argn; n < arg->argc; n++) {
2015       argv[1] = arg->argv[n];
2016       FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
2017                0 : OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
2018     }
2019   } else if (arg->prompt)
2020     prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
2021 	    arg->argv[arg->argn-1]);
2022   else
2023     log_Printf(LogWARN, "%s command must have arguments\n",
2024               arg->argv[arg->argn] );
2025 
2026   return 0;
2027 }
2028 
2029 const char *
2030 command_ShowNegval(unsigned val)
2031 {
2032   switch (val&3) {
2033     case 1: return "disabled & accepted";
2034     case 2: return "enabled & denied";
2035     case 3: return "enabled & accepted";
2036   }
2037   return "disabled & denied";
2038 }
2039 
2040 static int
2041 ClearCommand(struct cmdargs const *arg)
2042 {
2043   struct pppThroughput *t;
2044   struct datalink *cx;
2045   int i, clear_type;
2046 
2047   if (arg->argc < arg->argn + 1)
2048     return -1;
2049 
2050   if (strcasecmp(arg->argv[arg->argn], "modem") == 0) {
2051     cx = arg->cx;
2052     if (!cx)
2053       cx = bundle2datalink(arg->bundle, NULL);
2054     if (!cx) {
2055       log_Printf(LogWARN, "A link must be specified for ``clear modem''\n");
2056       return 1;
2057     }
2058     t = &cx->physical->link.throughput;
2059   } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
2060     t = &arg->bundle->ncp.ipcp.throughput;
2061   else
2062     return -1;
2063 
2064   if (arg->argc > arg->argn + 1) {
2065     clear_type = 0;
2066     for (i = arg->argn + 1; i < arg->argc; i++)
2067       if (strcasecmp(arg->argv[i], "overall") == 0)
2068         clear_type |= THROUGHPUT_OVERALL;
2069       else if (strcasecmp(arg->argv[i], "current") == 0)
2070         clear_type |= THROUGHPUT_CURRENT;
2071       else if (strcasecmp(arg->argv[i], "peak") == 0)
2072         clear_type |= THROUGHPUT_PEAK;
2073       else
2074         return -1;
2075   } else
2076     clear_type = THROUGHPUT_ALL;
2077 
2078   throughput_clear(t, clear_type, arg->prompt);
2079   return 0;
2080 }
2081