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