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