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