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