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