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