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