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