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