xref: /freebsd/usr.sbin/ppp/command.c (revision a33b2ef7)
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.145 1998/06/16 19:40:26 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 "defs.h"
48 #include "command.h"
49 #include "mbuf.h"
50 #include "log.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/16 19:40:26 $";
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(LogWARN, "%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 int
713 command_Interpret(char *buff, int nb, char *argv[MAXARGS])
714 {
715   char *cp;
716 
717   if (nb > 0) {
718     cp = buff + strcspn(buff, "\r\n");
719     if (cp)
720       *cp = '\0';
721     return MakeArgs(buff, argv, MAXARGS);
722   }
723   return 0;
724 }
725 
726 static int
727 arghidden(int argc, char const *const *argv, int n)
728 {
729   /* Is arg n of the given command to be hidden from the log ? */
730 
731   /* set authkey xxxxx */
732   /* set key xxxxx */
733   if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
734       (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
735     return 1;
736 
737   /* passwd xxxxx */
738   if (n == 1 && !strncasecmp(argv[0], "p", 1))
739     return 1;
740 
741   /* set server port xxxxx .... */
742   if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
743       !strncasecmp(argv[1], "se", 2))
744     return 1;
745 
746   return 0;
747 }
748 
749 void
750 command_Run(struct bundle *bundle, int argc, char const *const *argv,
751            struct prompt *prompt, const char *label, struct datalink *cx)
752 {
753   if (argc > 0) {
754     if (log_IsKept(LogCOMMAND)) {
755       static char buf[LINE_LEN];
756       int f, n;
757 
758       *buf = '\0';
759       if (label) {
760         strncpy(buf, label, sizeof buf - 3);
761         buf[sizeof buf - 3] = '\0';
762         strcat(buf, ": ");
763       }
764       n = strlen(buf);
765       for (f = 0; f < argc; f++) {
766         if (n < sizeof buf - 1 && f)
767           buf[n++] = ' ';
768         if (arghidden(argc, argv, f))
769           strncpy(buf+n, "********", sizeof buf - n - 1);
770         else
771           strncpy(buf+n, argv[f], sizeof buf - n - 1);
772         n += strlen(buf+n);
773       }
774       log_Printf(LogCOMMAND, "%s\n", buf);
775     }
776     FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
777   }
778 }
779 
780 void
781 command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
782               const char *label)
783 {
784   int argc;
785   char *argv[MAXARGS];
786 
787   argc = command_Interpret(buff, nb, argv);
788   command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
789 }
790 
791 static int
792 ShowCommand(struct cmdargs const *arg)
793 {
794   if (!arg->prompt)
795     log_Printf(LogWARN, "show: Cannot show without a prompt\n");
796   else if (arg->argc > arg->argn)
797     FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
798              arg->prompt, arg->cx);
799   else
800     prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
801 
802   return 0;
803 }
804 
805 static int
806 TerminalCommand(struct cmdargs const *arg)
807 {
808   if (!arg->prompt) {
809     log_Printf(LogWARN, "term: Need a prompt\n");
810     return 1;
811   }
812 
813   if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
814     prompt_Printf(arg->prompt, "LCP state is [%s]\n",
815                   State2Nam(arg->cx->physical->link.lcp.fsm.state));
816     return 1;
817   }
818 
819   datalink_Up(arg->cx, 0, 0);
820   prompt_TtyTermMode(arg->prompt, arg->cx);
821   return 0;
822 }
823 
824 static int
825 QuitCommand(struct cmdargs const *arg)
826 {
827   if (!arg->prompt || prompt_IsController(arg->prompt) ||
828       (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
829        (arg->prompt->auth & LOCAL_AUTH)))
830     Cleanup(EX_NORMAL);
831   if (arg->prompt)
832     prompt_Destroy(arg->prompt, 1);
833 
834   return 0;
835 }
836 
837 static int
838 OpenCommand(struct cmdargs const *arg)
839 {
840   if (arg->argc == arg->argn ||
841       (arg->argc == arg->argn+1 && !strcasecmp(arg->argv[arg->argn], "lcp")))
842     bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL);
843   else if (arg->argc == arg->argn+1 &&
844            !strcasecmp(arg->argv[arg->argn], "ccp")) {
845     struct link *l;
846     struct fsm *fp;
847 
848     if (!(l = command_ChooseLink(arg)))
849       return -1;
850     fp = &l->ccp.fsm;
851 
852     if (fp->link->lcp.fsm.state != ST_OPENED)
853       log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
854     else if (fp->state != ST_OPENED) {
855       fp->open_mode = 0;	/* Not passive any more */
856       if (fp->state == ST_STOPPED) {
857         fsm_Down(fp);
858         fsm_Up(fp);
859       } else {
860         fsm_Up(fp);
861         fsm_Open(fp);
862       }
863     }
864   } else
865     return -1;
866 
867   return 0;
868 }
869 
870 static int
871 CloseCommand(struct cmdargs const *arg)
872 {
873   if (arg->argc == arg->argn)
874     bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
875   else if (arg->argc == arg->argn + 1) {
876     if (!strcasecmp(arg->argv[arg->argn], "lcp"))
877       bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
878     else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
879              !strcasecmp(arg->argv[arg->argn], "ccp!")) {
880       struct link *l;
881       struct fsm *fp;
882 
883       if (!(l = command_ChooseLink(arg)))
884         return -1;
885       fp = &l->ccp.fsm;
886 
887       if (fp->state == ST_OPENED) {
888         fsm_Close(fp);
889         if (arg->argv[arg->argn][3] == '!')
890           fp->open_mode = 0;		/* Stay ST_CLOSED */
891         else
892           fp->open_mode = OPEN_PASSIVE;	/* Wait for the peer to start */
893       }
894     } else
895       return -1;
896   } else
897     return -1;
898 
899   return 0;
900 }
901 
902 static int
903 DownCommand(struct cmdargs const *arg)
904 {
905   if (arg->argc == arg->argn) {
906       if (arg->cx)
907         datalink_Down(arg->cx, CLOSE_STAYDOWN);
908       else
909         bundle_Down(arg->bundle, CLOSE_STAYDOWN);
910   } else if (arg->argc == arg->argn + 1) {
911     if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
912       if (arg->cx)
913         datalink_Down(arg->cx, CLOSE_LCP);
914       else
915         bundle_Down(arg->bundle, CLOSE_LCP);
916     } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
917       struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
918                                  &arg->bundle->ncp.mp.link.ccp.fsm;
919       fsm_Down(fp);
920       fsm_Close(fp);
921     } else
922       return -1;
923   } else
924     return -1;
925 
926   return 0;
927 }
928 
929 static int
930 SetModemSpeed(struct cmdargs const *arg)
931 {
932   long speed;
933   char *end;
934 
935   if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
936     if (arg->argc > arg->argn+1) {
937       log_Printf(LogWARN, "SetModemSpeed: Too many arguments");
938       return -1;
939     }
940     if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
941       physical_SetSync(arg->cx->physical);
942       return 0;
943     }
944     end = NULL;
945     speed = strtol(arg->argv[arg->argn], &end, 10);
946     if (*end) {
947       log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
948                 arg->argv[arg->argn]);
949       return -1;
950     }
951     if (physical_SetSpeed(arg->cx->physical, speed))
952       return 0;
953     log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
954   } else
955     log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
956 
957   return -1;
958 }
959 
960 static int
961 SetStoppedTimeout(struct cmdargs const *arg)
962 {
963   struct link *l = &arg->cx->physical->link;
964 
965   l->lcp.fsm.StoppedTimer.load = 0;
966   l->ccp.fsm.StoppedTimer.load = 0;
967   if (arg->argc <= arg->argn+2) {
968     if (arg->argc > arg->argn) {
969       l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
970       if (arg->argc > arg->argn+1)
971         l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
972     }
973     return 0;
974   }
975   return -1;
976 }
977 
978 #define ismask(x) \
979   (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3)
980 
981 static int
982 SetServer(struct cmdargs const *arg)
983 {
984   int res = -1;
985 
986   if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
987     const char *port, *passwd, *mask;
988 
989     /* What's what ? */
990     port = arg->argv[arg->argn];
991     if (arg->argc == arg->argn + 2) {
992       passwd = arg->argv[arg->argn+1];
993       mask = NULL;
994     } else if (arg->argc == arg->argn + 3) {
995       passwd = arg->argv[arg->argn+1];
996       mask = arg->argv[arg->argn+2];
997       if (!ismask(mask))
998         return -1;
999     } else if (strcasecmp(port, "none") == 0) {
1000       if (server_Close(arg->bundle))
1001         log_Printf(LogPHASE, "Disabled server port.\n");
1002       return 0;
1003     } else
1004       return -1;
1005 
1006     strncpy(server.passwd, passwd, sizeof server.passwd - 1);
1007     server.passwd[sizeof server.passwd - 1] = '\0';
1008 
1009     if (*port == '/') {
1010       mode_t imask;
1011       char *ptr, name[LINE_LEN + 12];
1012 
1013       if (mask != NULL) {
1014 	unsigned m;
1015 
1016 	if (sscanf(mask, "%o", &m) == 1)
1017 	  imask = m;
1018         else
1019           return -1;
1020       } else
1021         imask = (mode_t)-1;
1022 
1023       ptr = strstr(port, "%d");
1024       if (ptr) {
1025         snprintf(name, sizeof name, "%.*s%d%s",
1026                  ptr - port, port, arg->bundle->unit, ptr + 2);
1027         port = name;
1028       }
1029       res = server_LocalOpen(arg->bundle, port, imask);
1030     } else {
1031       int iport, add = 0;
1032 
1033       if (mask != NULL)
1034         return -1;
1035 
1036       if (*port == '+') {
1037         port++;
1038         add = 1;
1039       }
1040       if (strspn(port, "0123456789") != strlen(port)) {
1041         struct servent *s;
1042 
1043         if ((s = getservbyname(port, "tcp")) == NULL) {
1044 	  iport = 0;
1045 	  log_Printf(LogWARN, "%s: Invalid port or service\n", port);
1046 	} else
1047 	  iport = ntohs(s->s_port);
1048       } else
1049         iport = atoi(port);
1050 
1051       if (iport) {
1052         if (add)
1053           iport += arg->bundle->unit;
1054         res = server_TcpOpen(arg->bundle, iport);
1055       } else
1056         res = -1;
1057     }
1058   }
1059 
1060   return res;
1061 }
1062 
1063 static int
1064 SetModemParity(struct cmdargs const *arg)
1065 {
1066   return arg->argc > arg->argn ? modem_SetParity(arg->cx->physical,
1067                                                  arg->argv[arg->argn]) : -1;
1068 }
1069 
1070 static int
1071 SetEscape(struct cmdargs const *arg)
1072 {
1073   int code;
1074   int argc = arg->argc - arg->argn;
1075   char const *const *argv = arg->argv + arg->argn;
1076 
1077   for (code = 0; code < 33; code++)
1078     arg->cx->physical->async.cfg.EscMap[code] = 0;
1079 
1080   while (argc-- > 0) {
1081     sscanf(*argv++, "%x", &code);
1082     code &= 0xff;
1083     arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
1084     arg->cx->physical->async.cfg.EscMap[32] = 1;
1085   }
1086   return 0;
1087 }
1088 
1089 static struct in_addr
1090 GetIpAddr(const char *cp)
1091 {
1092   struct hostent *hp;
1093   struct in_addr ipaddr;
1094 
1095   if (inet_aton(cp, &ipaddr) == 0) {
1096     hp = gethostbyname(cp);
1097     if (hp && hp->h_addrtype == AF_INET)
1098       memcpy(&ipaddr, hp->h_addr, hp->h_length);
1099     else
1100       ipaddr.s_addr = 0;
1101   }
1102   return (ipaddr);
1103 }
1104 
1105 static int
1106 SetInterfaceAddr(struct cmdargs const *arg)
1107 {
1108   struct ipcp *ipcp = &arg->bundle->ncp.ipcp;
1109   const char *hisaddr;
1110 
1111   hisaddr = NULL;
1112   ipcp->cfg.my_range.ipaddr.s_addr = INADDR_ANY;
1113   ipcp->cfg.peer_range.ipaddr.s_addr = INADDR_ANY;
1114 
1115   if (arg->argc > arg->argn + 4)
1116     return -1;
1117 
1118   ipcp->cfg.HaveTriggerAddress = 0;
1119   ipcp->cfg.netmask.s_addr = INADDR_ANY;
1120   iplist_reset(&ipcp->cfg.peer_list);
1121 
1122   if (arg->argc > arg->argn) {
1123     if (!ParseAddr(ipcp, arg->argc - arg->argn, arg->argv + arg->argn,
1124                    &ipcp->cfg.my_range.ipaddr, &ipcp->cfg.my_range.mask,
1125                    &ipcp->cfg.my_range.width))
1126       return 1;
1127     if (arg->argc > arg->argn+1) {
1128       hisaddr = arg->argv[arg->argn+1];
1129       if (arg->argc > arg->argn+2) {
1130         ipcp->cfg.netmask = GetIpAddr(arg->argv[arg->argn+2]);
1131 	if (arg->argc > arg->argn+3) {
1132 	  ipcp->cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
1133 	  ipcp->cfg.HaveTriggerAddress = 1;
1134 	}
1135       }
1136     }
1137   }
1138 
1139   /*
1140    * For backwards compatibility, 0.0.0.0 means any address.
1141    */
1142   if (ipcp->cfg.my_range.ipaddr.s_addr == INADDR_ANY) {
1143     ipcp->cfg.my_range.mask.s_addr = INADDR_ANY;
1144     ipcp->cfg.my_range.width = 0;
1145   }
1146   ipcp->my_ip.s_addr = ipcp->cfg.my_range.ipaddr.s_addr;
1147 
1148   if (ipcp->cfg.peer_range.ipaddr.s_addr == INADDR_ANY) {
1149     ipcp->cfg.peer_range.mask.s_addr = INADDR_ANY;
1150     ipcp->cfg.peer_range.width = 0;
1151   }
1152 
1153   if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
1154                                   arg->bundle->phys_type.all & PHYS_AUTO))
1155     return 4;
1156 
1157   return 0;
1158 }
1159 
1160 static int
1161 SetVariable(struct cmdargs const *arg)
1162 {
1163   u_long ulong_val;
1164   const char *argp;
1165   int param = (int)arg->cmd->args, mode;
1166   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1167   const char *err = NULL;
1168   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1169   int dummyint;
1170   struct in_addr dummyaddr, *addr;
1171 
1172   if (!l)
1173     return -1;
1174 
1175   if (arg->argc > arg->argn)
1176     argp = arg->argv[arg->argn];
1177   else
1178     argp = "";
1179 
1180   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1181     log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
1182               arg->cmd->name);
1183     return 1;
1184   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1185     log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
1186               arg->cmd->name, cx->name);
1187     cx = NULL;
1188   }
1189 
1190   switch (param) {
1191   case VAR_AUTHKEY:
1192     if (bundle_Phase(arg->bundle) == PHASE_DEAD) {
1193       strncpy(arg->bundle->cfg.auth.key, argp,
1194               sizeof arg->bundle->cfg.auth.key - 1);
1195       arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
1196     } else {
1197       err = "set authkey: Only available at phase DEAD\n";
1198       log_Printf(LogWARN, err);
1199     }
1200     break;
1201   case VAR_AUTHNAME:
1202     if (bundle_Phase(arg->bundle) == PHASE_DEAD) {
1203       strncpy(arg->bundle->cfg.auth.name, argp,
1204               sizeof arg->bundle->cfg.auth.name - 1);
1205       arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name - 1] = '\0';
1206     } else {
1207       err = "set authname: Only available at phase DEAD\n";
1208       log_Printf(LogWARN, err);
1209     }
1210     break;
1211   case VAR_AUTOLOAD:
1212     if (arg->argc == arg->argn + 2 || arg->argc == arg->argn + 4) {
1213       arg->bundle->autoload.running = 1;
1214       arg->bundle->cfg.autoload.max.timeout = atoi(arg->argv[arg->argn]);
1215       arg->bundle->cfg.autoload.max.packets = atoi(arg->argv[arg->argn + 1]);
1216       if (arg->argc == arg->argn + 4) {
1217         arg->bundle->cfg.autoload.min.timeout = atoi(arg->argv[arg->argn + 2]);
1218         arg->bundle->cfg.autoload.min.packets = atoi(arg->argv[arg->argn + 3]);
1219       } else {
1220         arg->bundle->cfg.autoload.min.timeout = 0;
1221         arg->bundle->cfg.autoload.min.packets = 0;
1222       }
1223     } else {
1224       err = "Set autoload requires two or four arguments\n";
1225       log_Printf(LogWARN, err);
1226     }
1227     break;
1228   case VAR_DIAL:
1229     strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
1230     cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
1231     break;
1232   case VAR_LOGIN:
1233     strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
1234     cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
1235     break;
1236   case VAR_WINSIZE:
1237     if (arg->argc > arg->argn) {
1238       l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
1239       if (l->ccp.cfg.deflate.out.winsize < 8 ||
1240           l->ccp.cfg.deflate.out.winsize > 15) {
1241           log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
1242                     l->ccp.cfg.deflate.out.winsize);
1243           l->ccp.cfg.deflate.out.winsize = 15;
1244       }
1245       if (arg->argc > arg->argn+1) {
1246         l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
1247         if (l->ccp.cfg.deflate.in.winsize < 8 ||
1248             l->ccp.cfg.deflate.in.winsize > 15) {
1249             log_Printf(LogWARN, "%d: Invalid incoming window size\n",
1250                       l->ccp.cfg.deflate.in.winsize);
1251             l->ccp.cfg.deflate.in.winsize = 15;
1252         }
1253       } else
1254         l->ccp.cfg.deflate.in.winsize = 0;
1255     } else {
1256       err = "No window size specified\n";
1257       log_Printf(LogWARN, err);
1258     }
1259     break;
1260   case VAR_DEVICE:
1261     physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
1262                            arg->argv + arg->argn);
1263     break;
1264   case VAR_ACCMAP:
1265     if (arg->argc > arg->argn) {
1266       sscanf(argp, "%lx", &ulong_val);
1267       cx->physical->link.lcp.cfg.accmap = ulong_val;
1268     } else {
1269       err = "No accmap specified\n";
1270       log_Printf(LogWARN, err);
1271     }
1272     break;
1273   case VAR_MODE:
1274     mode = Nam2mode(argp);
1275     if (mode == PHYS_NONE || mode == PHYS_ALL) {
1276       log_Printf(LogWARN, "%s: Invalid mode\n", argp);
1277       return -1;
1278     }
1279     bundle_SetMode(arg->bundle, cx, mode);
1280     break;
1281   case VAR_MRRU:
1282     if (bundle_Phase(arg->bundle) != PHASE_DEAD)
1283       log_Printf(LogWARN, "mrru: Only changable at phase DEAD\n");
1284     else {
1285       ulong_val = atol(argp);
1286       if (ulong_val < MIN_MRU)
1287         err = "Given MRRU value (%lu) is too small.\n";
1288       else if (ulong_val > MAX_MRU)
1289         err = "Given MRRU value (%lu) is too big.\n";
1290       else
1291         arg->bundle->ncp.mp.cfg.mrru = ulong_val;
1292       if (err)
1293         log_Printf(LogWARN, err, ulong_val);
1294     }
1295     break;
1296   case VAR_MRU:
1297     ulong_val = atol(argp);
1298     if (ulong_val < MIN_MRU)
1299       err = "Given MRU value (%lu) is too small.\n";
1300     else if (ulong_val > MAX_MRU)
1301       err = "Given MRU value (%lu) is too big.\n";
1302     else
1303       l->lcp.cfg.mru = ulong_val;
1304     if (err)
1305       log_Printf(LogWARN, err, ulong_val);
1306     break;
1307   case VAR_MTU:
1308     ulong_val = atol(argp);
1309     if (ulong_val == 0)
1310       arg->bundle->cfg.mtu = 0;
1311     else if (ulong_val < MIN_MTU)
1312       err = "Given MTU value (%lu) is too small.\n";
1313     else if (ulong_val > MAX_MTU)
1314       err = "Given MTU value (%lu) is too big.\n";
1315     else
1316       arg->bundle->cfg.mtu = ulong_val;
1317     if (err)
1318       log_Printf(LogWARN, err, ulong_val);
1319     break;
1320   case VAR_OPENMODE:
1321     if (strcasecmp(argp, "active") == 0)
1322       cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
1323         atoi(arg->argv[arg->argn+1]) : 1;
1324     else if (strcasecmp(argp, "passive") == 0)
1325       cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
1326     else {
1327       err = "%s: Invalid openmode\n";
1328       log_Printf(LogWARN, err, argp);
1329     }
1330     break;
1331   case VAR_PHONE:
1332     strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
1333     cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
1334     break;
1335   case VAR_HANGUP:
1336     strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
1337     cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
1338     break;
1339   case VAR_IDLETIMEOUT:
1340     if (arg->argc > arg->argn+1)
1341       err = "Too many idle timeout values\n";
1342     else if (arg->argc == arg->argn+1)
1343       bundle_SetIdleTimer(arg->bundle, atoi(argp));
1344     if (err)
1345       log_Printf(LogWARN, err);
1346     break;
1347   case VAR_LQRPERIOD:
1348     ulong_val = atol(argp);
1349     if (ulong_val <= 0) {
1350       err = "%s: Invalid lqr period\n";
1351       log_Printf(LogWARN, err, argp);
1352     } else
1353       l->lcp.cfg.lqrperiod = ulong_val;
1354     break;
1355   case VAR_LCPRETRY:
1356     ulong_val = atol(argp);
1357     if (ulong_val <= 0) {
1358       err = "%s: Invalid LCP FSM retry period\n";
1359       log_Printf(LogWARN, err, argp);
1360     } else
1361       cx->physical->link.lcp.cfg.fsmretry = ulong_val;
1362     break;
1363   case VAR_CHAPRETRY:
1364     ulong_val = atol(argp);
1365     if (ulong_val <= 0) {
1366       err = "%s: Invalid CHAP retry period\n";
1367       log_Printf(LogWARN, err, argp);
1368     } else
1369       cx->chap.auth.cfg.fsmretry = ulong_val;
1370     break;
1371   case VAR_PAPRETRY:
1372     ulong_val = atol(argp);
1373     if (ulong_val <= 0) {
1374       err = "%s: Invalid PAP retry period\n";
1375       log_Printf(LogWARN, err, argp);
1376     } else
1377       cx->pap.cfg.fsmretry = ulong_val;
1378     break;
1379   case VAR_CCPRETRY:
1380     ulong_val = atol(argp);
1381     if (ulong_val <= 0) {
1382       err = "%s: Invalid CCP FSM retry period\n";
1383       log_Printf(LogWARN, err, argp);
1384     } else
1385       l->ccp.cfg.fsmretry = ulong_val;
1386     break;
1387   case VAR_IPCPRETRY:
1388     ulong_val = atol(argp);
1389     if (ulong_val <= 0) {
1390       err = "%s: Invalid IPCP FSM retry period\n";
1391       log_Printf(LogWARN, err, argp);
1392     } else
1393       arg->bundle->ncp.ipcp.cfg.fsmretry = ulong_val;
1394     break;
1395   case VAR_NBNS:
1396   case VAR_DNS:
1397     if (param == VAR_DNS)
1398       addr = arg->bundle->ncp.ipcp.cfg.ns.dns;
1399     else
1400       addr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
1401 
1402     addr[0].s_addr = addr[1].s_addr = INADDR_ANY;
1403 
1404     if (arg->argc > arg->argn) {
1405       ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn,
1406                 addr, &dummyaddr, &dummyint);
1407       if (arg->argc > arg->argn+1)
1408         ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn + 1,
1409                   addr + 1, &dummyaddr, &dummyint);
1410 
1411       if (addr[1].s_addr == INADDR_ANY)
1412         addr[1].s_addr = addr[0].s_addr;
1413       if (addr[0].s_addr == INADDR_ANY)
1414         addr[0].s_addr = addr[1].s_addr;
1415     }
1416     break;
1417   }
1418 
1419   return err ? 1 : 0;
1420 }
1421 
1422 static int
1423 SetCtsRts(struct cmdargs const *arg)
1424 {
1425   if (arg->argc == arg->argn+1) {
1426     if (strcmp(arg->argv[arg->argn], "on") == 0)
1427       physical_SetRtsCts(arg->cx->physical, 1);
1428     else if (strcmp(arg->argv[arg->argn], "off") == 0)
1429       physical_SetRtsCts(arg->cx->physical, 0);
1430     else
1431       return -1;
1432     return 0;
1433   }
1434   return -1;
1435 }
1436 
1437 static struct cmdtab const SetCommands[] = {
1438   {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1439   "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
1440   {"authkey", "key", SetVariable, LOCAL_AUTH,
1441   "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
1442   {"authname", NULL, SetVariable, LOCAL_AUTH,
1443   "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
1444   {"autoload", NULL, SetVariable, LOCAL_AUTH,
1445   "auto link [de]activation", "set autoload maxtime maxload mintime minload",
1446   (const void *)VAR_AUTOLOAD},
1447   {"ccpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1448   "FSM retry period", "set ccpretry value", (const void *)VAR_CCPRETRY},
1449   {"chapretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1450   "CHAP retry period", "set chapretry value", (const void *)VAR_CHAPRETRY},
1451   {"ctsrts", "crtscts", SetCtsRts, LOCAL_AUTH | LOCAL_CX,
1452   "Use hardware flow control", "set ctsrts [on|off]"},
1453   {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1454   "deflate window sizes", "set deflate out-winsize in-winsize",
1455   (const void *) VAR_WINSIZE},
1456   {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
1457   "modem device name", "set device|line device-name[,device-name]",
1458   (const void *) VAR_DEVICE},
1459   {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1460   "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
1461   {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
1462   "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
1463   {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
1464   "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]"},
1465   {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
1466   "escape characters", "set escape hex-digit ..."},
1467   {"filter", NULL, filter_Set, LOCAL_AUTH,
1468   "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
1469   "[src_addr[/width]] [dst_addr[/width]] [tcp|udp|icmp [src [lt|eq|gt port]] "
1470   "[dst [lt|eq|gt port]] [estab] [syn] [finrst]]"},
1471   {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1472   "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
1473   {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
1474   "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
1475   {"ipcpretry", NULL, SetVariable, LOCAL_AUTH,
1476   "FSM retry period", "set ipcpretry value", (const void *)VAR_IPCPRETRY},
1477   {"lcpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1478   "FSM retry period", "set lcpretry value", (const void *)VAR_LCPRETRY},
1479   {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
1480   "set log [local] [+|-]async|ccp|chat|command|connect|debug|hdlc|id0|ipcp|"
1481   "lcp|lqm|phase|tcp/ip|timer|tun..."},
1482   {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1483   "login script", "set login chat-script", (const void *) VAR_LOGIN},
1484   {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1485   "LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
1486   {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
1487   "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
1488   {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
1489   "set mrru value", (const void *)VAR_MRRU},
1490   {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1491   "MRU value", "set mru value", (const void *)VAR_MRU},
1492   {"mtu", NULL, SetVariable, LOCAL_AUTH,
1493   "interface MTU value", "set mtu value", (const void *)VAR_MTU},
1494   {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
1495   "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
1496   {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
1497   "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
1498   {"papretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1499   "PAP retry period", "set papretry value", (const void *)VAR_PAPRETRY},
1500   {"parity", NULL, SetModemParity, LOCAL_AUTH | LOCAL_CX,
1501   "modem parity", "set parity [odd|even|none]"},
1502   {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
1503   "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
1504   {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
1505   "Reconnect timeout", "set reconnect value ntries"},
1506   {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
1507   "Redial timeout", "set redial value|random[.value|random] [attempts]"},
1508   {"server", "socket", SetServer, LOCAL_AUTH,
1509   "server port", "set server|socket TcpPort|LocalName|none password [mask]"},
1510   {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
1511   "modem speed", "set speed value"},
1512   {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
1513   "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
1514   {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
1515   "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
1516   {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
1517   "vj values", "set vj slots|slotcomp [value]"},
1518   {"weight", NULL, mp_SetDatalinkWeight, LOCAL_AUTH | LOCAL_CX,
1519   "datalink weighting", "set weight n"},
1520   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1521   "Display this message", "set help|? [command]", SetCommands},
1522   {NULL, NULL, NULL},
1523 };
1524 
1525 static int
1526 SetCommand(struct cmdargs const *arg)
1527 {
1528   if (arg->argc > arg->argn)
1529     FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
1530              arg->prompt, arg->cx);
1531   else if (arg->prompt)
1532     prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
1533 	    " syntax help.\n");
1534   else
1535     log_Printf(LogWARN, "set command must have arguments\n");
1536 
1537   return 0;
1538 }
1539 
1540 
1541 static int
1542 AddCommand(struct cmdargs const *arg)
1543 {
1544   struct in_addr dest, gateway, netmask;
1545   int gw, addrs;
1546 
1547   if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
1548     return -1;
1549 
1550   addrs = 0;
1551   if (arg->argc == arg->argn+2) {
1552     if (!strcasecmp(arg->argv[arg->argn], "default"))
1553       dest.s_addr = netmask.s_addr = INADDR_ANY;
1554     else {
1555       int width;
1556 
1557       if (!ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn,
1558 	             &dest, &netmask, &width))
1559         return -1;
1560       if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
1561         addrs = ROUTE_DSTMYADDR;
1562       else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
1563         addrs = ROUTE_DSTHISADDR;
1564     }
1565     gw = 1;
1566   } else {
1567     if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
1568       addrs = ROUTE_DSTMYADDR;
1569       dest = arg->bundle->ncp.ipcp.my_ip;
1570     } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
1571       addrs = ROUTE_DSTHISADDR;
1572       dest = arg->bundle->ncp.ipcp.peer_ip;
1573     } else
1574       dest = GetIpAddr(arg->argv[arg->argn]);
1575     netmask = GetIpAddr(arg->argv[arg->argn+1]);
1576     gw = 2;
1577   }
1578 
1579   if (strcasecmp(arg->argv[arg->argn+gw], "HISADDR") == 0) {
1580     gateway = arg->bundle->ncp.ipcp.peer_ip;
1581     addrs |= ROUTE_GWHISADDR;
1582   } else if (strcasecmp(arg->argv[arg->argn+gw], "INTERFACE") == 0)
1583     gateway.s_addr = INADDR_ANY;
1584   else
1585     gateway = GetIpAddr(arg->argv[arg->argn+gw]);
1586 
1587   if (bundle_SetRoute(arg->bundle, RTM_ADD, dest, gateway, netmask,
1588                   arg->cmd->args ? 1 : 0))
1589     route_Add(&arg->bundle->ncp.ipcp.route, addrs, dest, netmask, gateway);
1590 
1591   return 0;
1592 }
1593 
1594 static int
1595 DeleteCommand(struct cmdargs const *arg)
1596 {
1597   struct in_addr dest, none;
1598   int addrs;
1599 
1600   if (arg->argc == arg->argn+1) {
1601     if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
1602       route_IfDelete(arg->bundle, 0);
1603       route_DeleteAll(&arg->bundle->ncp.ipcp.route);
1604     } else {
1605       addrs = 0;
1606       if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
1607         dest = arg->bundle->ncp.ipcp.my_ip;
1608         addrs = ROUTE_DSTMYADDR;
1609       } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
1610         dest = arg->bundle->ncp.ipcp.peer_ip;
1611         addrs = ROUTE_DSTHISADDR;
1612       } else {
1613         if (strcasecmp(arg->argv[arg->argn], "default") == 0)
1614           dest.s_addr = INADDR_ANY;
1615         else
1616           dest = GetIpAddr(arg->argv[arg->argn]);
1617         addrs = ROUTE_STATIC;
1618       }
1619       none.s_addr = INADDR_ANY;
1620       bundle_SetRoute(arg->bundle, RTM_DELETE, dest, none, none,
1621                       arg->cmd->args ? 1 : 0);
1622       route_Delete(&arg->bundle->ncp.ipcp.route, addrs, dest);
1623     }
1624   } else
1625     return -1;
1626 
1627   return 0;
1628 }
1629 
1630 #ifndef NOALIAS
1631 static struct cmdtab const AliasCommands[] =
1632 {
1633   {"addr", NULL, alias_RedirectAddr, LOCAL_AUTH,
1634    "static address translation", "alias addr [addr_local addr_alias]"},
1635   {"deny_incoming", NULL, AliasOption, LOCAL_AUTH,
1636    "stop incoming connections", "alias deny_incoming [yes|no]",
1637    (const void *) PKT_ALIAS_DENY_INCOMING},
1638   {"enable", NULL, AliasEnable, LOCAL_AUTH,
1639    "enable IP aliasing", "alias enable [yes|no]"},
1640   {"log", NULL, AliasOption, LOCAL_AUTH,
1641    "log aliasing link creation", "alias log [yes|no]",
1642    (const void *) PKT_ALIAS_LOG},
1643   {"port", NULL, alias_RedirectPort, LOCAL_AUTH,
1644    "port redirection", "alias port [proto addr_local:port_local  port_alias]"},
1645   {"same_ports", NULL, AliasOption, LOCAL_AUTH,
1646    "try to leave port numbers unchanged", "alias same_ports [yes|no]",
1647    (const void *) PKT_ALIAS_SAME_PORTS},
1648   {"unregistered_only", NULL, AliasOption, LOCAL_AUTH,
1649    "alias unregistered (private) IP address space only",
1650    "alias unregistered_only [yes|no]",
1651    (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
1652   {"use_sockets", NULL, AliasOption, LOCAL_AUTH,
1653    "allocate host sockets", "alias use_sockets [yes|no]",
1654    (const void *) PKT_ALIAS_USE_SOCKETS},
1655   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1656    "Display this message", "alias help|? [command]", AliasCommands},
1657   {NULL, NULL, NULL},
1658 };
1659 
1660 
1661 static int
1662 AliasCommand(struct cmdargs const *arg)
1663 {
1664   if (arg->argc > arg->argn)
1665     FindExec(arg->bundle, AliasCommands, arg->argc, arg->argn, arg->argv,
1666              arg->prompt, arg->cx);
1667   else if (arg->prompt)
1668     prompt_Printf(arg->prompt, "Use `alias help' to get a list or `alias help"
1669             " <option>' for syntax help.\n");
1670   else
1671     log_Printf(LogWARN, "alias command must have arguments\n");
1672 
1673   return 0;
1674 }
1675 
1676 static int
1677 AliasEnable(struct cmdargs const *arg)
1678 {
1679   if (arg->argc == arg->argn+1) {
1680     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
1681       if (alias_Load() == 0)
1682 	return 0;
1683       log_Printf(LogWARN, "Cannot load alias library\n");
1684       return 1;
1685     } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
1686       alias_Unload();
1687       return 0;
1688     }
1689   }
1690 
1691   return -1;
1692 }
1693 
1694 
1695 static int
1696 AliasOption(struct cmdargs const *arg)
1697 {
1698   unsigned param = (unsigned)arg->cmd->args;
1699   if (arg->argc == arg->argn+1) {
1700     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
1701       if (alias_IsEnabled()) {
1702 	(*PacketAlias.SetMode)(param, param);
1703 	return 0;
1704       }
1705       log_Printf(LogWARN, "alias not enabled\n");
1706     } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
1707       if (alias_IsEnabled()) {
1708 	(*PacketAlias.SetMode)(0, param);
1709 	return 0;
1710       }
1711       log_Printf(LogWARN, "alias not enabled\n");
1712     }
1713   }
1714   return -1;
1715 }
1716 #endif /* #ifndef NOALIAS */
1717 
1718 static struct cmdtab const AllowCommands[] = {
1719   {"modes", "mode", AllowModes, LOCAL_AUTH,
1720   "Only allow certain ppp modes", "allow modes mode..."},
1721   {"users", "user", AllowUsers, LOCAL_AUTH,
1722   "Allow users access to ppp", "allow users logname..."},
1723   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1724   "Display this message", "allow help|? [command]", AllowCommands},
1725   {NULL, NULL, NULL},
1726 };
1727 
1728 static int
1729 AllowCommand(struct cmdargs const *arg)
1730 {
1731   /* arg->bundle may be NULL (see system_IsValid()) ! */
1732   if (arg->argc > arg->argn)
1733     FindExec(arg->bundle, AllowCommands, arg->argc, arg->argn, arg->argv,
1734              arg->prompt, arg->cx);
1735   else if (arg->prompt)
1736     prompt_Printf(arg->prompt, "Use `allow ?' to get a list or `allow ? <cmd>'"
1737                   " for syntax help.\n");
1738   else
1739     log_Printf(LogWARN, "allow command must have arguments\n");
1740 
1741   return 0;
1742 }
1743 
1744 static int
1745 LinkCommand(struct cmdargs const *arg)
1746 {
1747   if (arg->argc > arg->argn+1) {
1748     char namelist[LINE_LEN];
1749     struct datalink *cx;
1750     char *name;
1751     int result = 0;
1752 
1753     if (!strcmp(arg->argv[arg->argn], "*")) {
1754       struct datalink *dl;
1755 
1756       cx = arg->bundle->links;
1757       while (cx) {
1758         /* Watch it, the command could be a ``remove'' */
1759         dl = cx->next;
1760         FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
1761                  arg->prompt, cx);
1762         for (cx = arg->bundle->links; cx; cx = cx->next)
1763           if (cx == dl)
1764             break;		/* Pointer's still valid ! */
1765       }
1766     } else {
1767       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
1768       namelist[sizeof namelist - 1] = '\0';
1769       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
1770         if (!bundle2datalink(arg->bundle, name)) {
1771           log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
1772           return 1;
1773         }
1774 
1775       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
1776       namelist[sizeof namelist - 1] = '\0';
1777       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
1778         cx = bundle2datalink(arg->bundle, name);
1779         if (cx)
1780           FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
1781                    arg->prompt, cx);
1782         else {
1783           log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
1784           result++;
1785         }
1786       }
1787     }
1788     return result;
1789   }
1790 
1791   log_Printf(LogWARN, "Usage: %s\n", arg->cmd->syntax);
1792   return 2;
1793 }
1794 
1795 struct link *
1796 command_ChooseLink(struct cmdargs const *arg)
1797 {
1798   if (arg->cx)
1799     return &arg->cx->physical->link;
1800   else if (arg->bundle->ncp.mp.cfg.mrru)
1801     return &arg->bundle->ncp.mp.link;
1802   else {
1803     struct datalink *dl = bundle2datalink(arg->bundle, NULL);
1804     return dl ? &dl->physical->link : NULL;
1805   }
1806 }
1807 
1808 static const char *
1809 ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
1810 {
1811   const char *result;
1812 
1813   switch (*cmd) {
1814     case 'A':
1815     case 'a':
1816       result = "accept";
1817       *keep = NEG_MYMASK;
1818       *add = NEG_ACCEPTED;
1819       break;
1820     case 'D':
1821     case 'd':
1822       switch (cmd[1]) {
1823         case 'E':
1824         case 'e':
1825           result = "deny";
1826           *keep = NEG_MYMASK;
1827           *add = 0;
1828           break;
1829         case 'I':
1830         case 'i':
1831           result = "disable";
1832           *keep = NEG_HISMASK;
1833           *add = 0;
1834           break;
1835         default:
1836           return NULL;
1837       }
1838       break;
1839     case 'E':
1840     case 'e':
1841       result = "enable";
1842       *keep = NEG_HISMASK;
1843       *add = NEG_ENABLED;
1844       break;
1845     default:
1846       return NULL;
1847   }
1848 
1849   return result;
1850 }
1851 
1852 static int
1853 OptSet(struct cmdargs const *arg)
1854 {
1855   int bit = (int)arg->cmd->args;
1856   const char *cmd;
1857   unsigned keep;			/* Keep these bits */
1858   unsigned add;				/* Add these bits */
1859 
1860   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
1861     return 1;
1862 
1863   if (add)
1864     arg->bundle->cfg.opt |= bit;
1865   else
1866     arg->bundle->cfg.opt &= ~bit;
1867   return 0;
1868 }
1869 
1870 static int
1871 NegotiateSet(struct cmdargs const *arg)
1872 {
1873   int param = (int)arg->cmd->args;
1874   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1875   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1876   const char *cmd;
1877   unsigned keep;			/* Keep these bits */
1878   unsigned add;				/* Add these bits */
1879 
1880   if (!l)
1881     return -1;
1882 
1883   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
1884     return 1;
1885 
1886   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1887     log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
1888               cmd, arg->cmd->name);
1889     return 2;
1890   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1891     log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
1892               cmd, arg->cmd->name, cx->name);
1893     cx = NULL;
1894   }
1895 
1896   switch (param) {
1897     case NEG_ACFCOMP:
1898       cx->physical->link.lcp.cfg.acfcomp &= keep;
1899       cx->physical->link.lcp.cfg.acfcomp |= add;
1900       break;
1901     case NEG_CHAP:
1902       cx->physical->link.lcp.cfg.chap &= keep;
1903       cx->physical->link.lcp.cfg.chap |= add;
1904       break;
1905     case NEG_DEFLATE:
1906       l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
1907       l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
1908       break;
1909     case NEG_DNS:
1910       arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
1911       arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
1912       break;
1913     case NEG_LQR:
1914       cx->physical->link.lcp.cfg.lqr &= keep;
1915       cx->physical->link.lcp.cfg.lqr |= add;
1916       break;
1917     case NEG_PAP:
1918       cx->physical->link.lcp.cfg.pap &= keep;
1919       cx->physical->link.lcp.cfg.pap |= add;
1920       break;
1921     case NEG_PPPDDEFLATE:
1922       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
1923       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
1924       break;
1925     case NEG_PRED1:
1926       l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
1927       l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
1928       break;
1929     case NEG_PROTOCOMP:
1930       cx->physical->link.lcp.cfg.protocomp &= keep;
1931       cx->physical->link.lcp.cfg.protocomp |= add;
1932       break;
1933     case NEG_SHORTSEQ:
1934       if (bundle_Phase(arg->bundle) != PHASE_DEAD)
1935         log_Printf(LogWARN, "shortseq: Only changable at phase DEAD\n");
1936       else {
1937         arg->bundle->ncp.mp.cfg.shortseq &= keep;
1938         arg->bundle->ncp.mp.cfg.shortseq |= add;
1939       }
1940       break;
1941     case NEG_VJCOMP:
1942       arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
1943       arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
1944       break;
1945   }
1946 
1947   return 0;
1948 }
1949 
1950 static struct cmdtab const NegotiateCommands[] = {
1951   {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
1952   "disable|enable", (const void *)OPT_IDCHECK},
1953   {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
1954   "disable|enable", (const void *)OPT_LOOPBACK},
1955   {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
1956   "disable|enable", (const void *)OPT_PASSWDAUTH},
1957   {"proxy", NULL, OptSet, LOCAL_AUTH, "Create proxy ARP entry",
1958   "disable|enable", (const void *)OPT_PROXY},
1959   {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
1960   "disable|enable", (const void *)OPT_SROUTES},
1961   {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
1962   "disable|enable", (const void *)OPT_THROUGHPUT},
1963   {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
1964   "disable|enable", (const void *)OPT_UTMP},
1965 
1966 #define OPT_MAX 7	/* accept/deny allowed below and not above */
1967 
1968   {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1969   "Address & Control field compression", "accept|deny|disable|enable",
1970   (const void *)NEG_ACFCOMP},
1971   {"chap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1972   "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
1973   (const void *)NEG_CHAP},
1974   {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
1975   "Deflate compression", "accept|deny|disable|enable",
1976   (const void *)NEG_DEFLATE},
1977   {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
1978   "Deflate (type 24) compression", "accept|deny|disable|enable",
1979   (const void *)NEG_PPPDDEFLATE},
1980   {"dns", NULL, NegotiateSet, LOCAL_AUTH,
1981   "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
1982   {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1983   "Link Quality Reports", "accept|deny|disable|enable",
1984   (const void *)NEG_LQR},
1985   {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1986   "Password Authentication protocol", "accept|deny|disable|enable",
1987   (const void *)NEG_PAP},
1988   {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
1989   "Predictor 1 compression", "accept|deny|disable|enable",
1990   (const void *)NEG_PRED1},
1991   {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
1992   "Protocol field compression", "accept|deny|disable|enable",
1993   (const void *)NEG_PROTOCOMP},
1994   {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
1995   "MP Short Sequence Numbers", "accept|deny|disable|enable",
1996   (const void *)NEG_SHORTSEQ},
1997   {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
1998   "Van Jacobson header compression", "accept|deny|disable|enable",
1999   (const void *)NEG_VJCOMP},
2000   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
2001   "Display this message", "accept|deny|disable|enable help|? [value]",
2002   NegotiateCommands},
2003   {NULL, NULL, NULL},
2004 };
2005 
2006 static int
2007 NegotiateCommand(struct cmdargs const *arg)
2008 {
2009   if (arg->argc > arg->argn) {
2010     char const *argv[3];
2011     unsigned keep, add;
2012     int n;
2013 
2014     if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
2015       return -1;
2016     argv[2] = NULL;
2017 
2018     for (n = arg->argn; n < arg->argc; n++) {
2019       argv[1] = arg->argv[n];
2020       FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
2021                0 : OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
2022     }
2023   } else if (arg->prompt)
2024     prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
2025 	    arg->argv[arg->argn-1]);
2026   else
2027     log_Printf(LogWARN, "%s command must have arguments\n",
2028               arg->argv[arg->argn] );
2029 
2030   return 0;
2031 }
2032 
2033 const char *
2034 command_ShowNegval(unsigned val)
2035 {
2036   switch (val&3) {
2037     case 1: return "disabled & accepted";
2038     case 2: return "enabled & denied";
2039     case 3: return "enabled & accepted";
2040   }
2041   return "disabled & denied";
2042 }
2043 
2044 static int
2045 ClearCommand(struct cmdargs const *arg)
2046 {
2047   struct pppThroughput *t;
2048   struct datalink *cx;
2049   int i, clear_type;
2050 
2051   if (arg->argc < arg->argn + 1)
2052     return -1;
2053 
2054   if (strcasecmp(arg->argv[arg->argn], "modem") == 0) {
2055     cx = arg->cx;
2056     if (!cx)
2057       cx = bundle2datalink(arg->bundle, NULL);
2058     if (!cx) {
2059       log_Printf(LogWARN, "A link must be specified for ``clear modem''\n");
2060       return 1;
2061     }
2062     t = &cx->physical->link.throughput;
2063   } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
2064     t = &arg->bundle->ncp.ipcp.throughput;
2065   else
2066     return -1;
2067 
2068   if (arg->argc > arg->argn + 1) {
2069     clear_type = 0;
2070     for (i = arg->argn + 1; i < arg->argc; i++)
2071       if (strcasecmp(arg->argv[i], "overall") == 0)
2072         clear_type |= THROUGHPUT_OVERALL;
2073       else if (strcasecmp(arg->argv[i], "current") == 0)
2074         clear_type |= THROUGHPUT_CURRENT;
2075       else if (strcasecmp(arg->argv[i], "peak") == 0)
2076         clear_type |= THROUGHPUT_PEAK;
2077       else
2078         return -1;
2079   } else
2080     clear_type = THROUGHPUT_ALL;
2081 
2082   throughput_clear(t, clear_type, arg->prompt);
2083   return 0;
2084 }
2085