command.c revision 95258
1/*-
2 * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
3 *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
4 *                           Internet Initiative Japan, Inc (IIJ)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/usr.sbin/ppp/command.c 95258 2002-04-22 13:44:47Z des $
29 */
30
31#include <sys/param.h>
32#include <netinet/in_systm.h>
33#include <netinet/in.h>
34#include <netinet/ip.h>
35#include <arpa/inet.h>
36#include <sys/socket.h>
37#include <net/route.h>
38#include <netdb.h>
39#include <sys/un.h>
40
41#include <ctype.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <paths.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <sys/wait.h>
49#include <termios.h>
50#include <unistd.h>
51
52#ifndef NONAT
53#ifdef LOCALNAT
54#include "alias.h"
55#else
56#include <alias.h>
57#endif
58#endif
59
60#include "layer.h"
61#include "defs.h"
62#include "command.h"
63#include "mbuf.h"
64#include "log.h"
65#include "timer.h"
66#include "fsm.h"
67#include "iplist.h"
68#include "throughput.h"
69#include "slcompress.h"
70#include "lqr.h"
71#include "hdlc.h"
72#include "lcp.h"
73#include "ncpaddr.h"
74#include "ip.h"
75#include "ipcp.h"
76#ifndef NONAT
77#include "nat_cmd.h"
78#endif
79#include "systems.h"
80#include "filter.h"
81#include "descriptor.h"
82#include "main.h"
83#include "route.h"
84#include "ccp.h"
85#include "auth.h"
86#include "async.h"
87#include "link.h"
88#include "physical.h"
89#include "mp.h"
90#ifndef NORADIUS
91#include "radius.h"
92#endif
93#include "ipv6cp.h"
94#include "ncp.h"
95#include "bundle.h"
96#include "server.h"
97#include "prompt.h"
98#include "chat.h"
99#include "chap.h"
100#include "cbcp.h"
101#include "datalink.h"
102#include "iface.h"
103#include "id.h"
104#include "probe.h"
105
106/* ``set'' values */
107#define	VAR_AUTHKEY	0
108#define	VAR_DIAL	1
109#define	VAR_LOGIN	2
110#define	VAR_AUTHNAME	3
111#define	VAR_AUTOLOAD	4
112#define	VAR_WINSIZE	5
113#define	VAR_DEVICE	6
114#define	VAR_ACCMAP	7
115#define	VAR_MRRU	8
116#define	VAR_MRU		9
117#define	VAR_MTU		10
118#define	VAR_OPENMODE	11
119#define	VAR_PHONE	12
120#define	VAR_HANGUP	13
121#define	VAR_IDLETIMEOUT	14
122#define	VAR_LQRPERIOD	15
123#define	VAR_LCPRETRY	16
124#define	VAR_CHAPRETRY	17
125#define	VAR_PAPRETRY	18
126#define	VAR_CCPRETRY	19
127#define	VAR_IPCPRETRY	20
128#define	VAR_DNS		21
129#define	VAR_NBNS	22
130#define	VAR_MODE	23
131#define	VAR_CALLBACK	24
132#define	VAR_CBCP	25
133#define	VAR_CHOKED	26
134#define	VAR_SENDPIPE	27
135#define	VAR_RECVPIPE	28
136#define	VAR_RADIUS	29
137#define	VAR_CD		30
138#define	VAR_PARITY	31
139#define VAR_CRTSCTS	32
140#define VAR_URGENTPORTS	33
141#define	VAR_LOGOUT	34
142#define	VAR_IFQUEUE	35
143#define	VAR_MPPE	36
144
145/* ``accept|deny|disable|enable'' masks */
146#define NEG_HISMASK (1)
147#define NEG_MYMASK (2)
148
149/* ``accept|deny|disable|enable'' values */
150#define NEG_ACFCOMP	40
151#define NEG_CHAP05	41
152#define NEG_CHAP80	42
153#define NEG_CHAP80LM	43
154#define NEG_DEFLATE	44
155#define NEG_DNS		45
156#define NEG_ENDDISC	46
157#define NEG_LQR		47
158#define NEG_PAP		48
159#define NEG_PPPDDEFLATE	49
160#define NEG_PRED1	50
161#define NEG_PROTOCOMP	51
162#define NEG_SHORTSEQ	52
163#define NEG_VJCOMP	53
164#define NEG_MPPE	54
165#define NEG_CHAP81	55
166
167const char Version[] = "3.0.2";
168
169static int ShowCommand(struct cmdargs const *);
170static int TerminalCommand(struct cmdargs const *);
171static int QuitCommand(struct cmdargs const *);
172static int OpenCommand(struct cmdargs const *);
173static int CloseCommand(struct cmdargs const *);
174static int DownCommand(struct cmdargs const *);
175static int SetCommand(struct cmdargs const *);
176static int LinkCommand(struct cmdargs const *);
177static int AddCommand(struct cmdargs const *);
178static int DeleteCommand(struct cmdargs const *);
179static int NegotiateCommand(struct cmdargs const *);
180static int ClearCommand(struct cmdargs const *);
181static int RunListCommand(struct cmdargs const *);
182static int IfaceAddCommand(struct cmdargs const *);
183static int IfaceDeleteCommand(struct cmdargs const *);
184static int IfaceClearCommand(struct cmdargs const *);
185static int SetProcTitle(struct cmdargs const *);
186#ifndef NONAT
187static int NatEnable(struct cmdargs const *);
188static int NatOption(struct cmdargs const *);
189#endif
190
191static const char *
192showcx(struct cmdtab const *cmd)
193{
194  if (cmd->lauth & LOCAL_CX)
195    return "(c)";
196  else if (cmd->lauth & LOCAL_CX_OPT)
197    return "(o)";
198
199  return "";
200}
201
202static int
203HelpCommand(struct cmdargs const *arg)
204{
205  struct cmdtab const *cmd;
206  int n, cmax, dmax, cols, cxlen;
207  const char *cx;
208
209  if (!arg->prompt) {
210    log_Printf(LogWARN, "help: Cannot help without a prompt\n");
211    return 0;
212  }
213
214  if (arg->argc > arg->argn) {
215    for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
216      if ((cmd->lauth & arg->prompt->auth) &&
217          ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) ||
218           (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) {
219	prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd));
220	return 0;
221      }
222    return -1;
223  }
224
225  cmax = dmax = 0;
226  for (cmd = arg->cmdtab; cmd->func; cmd++)
227    if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
228      if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax)
229        cmax = n;
230      if ((n = strlen(cmd->helpmes)) > dmax)
231        dmax = n;
232    }
233
234  cols = 80 / (dmax + cmax + 3);
235  n = 0;
236  prompt_Printf(arg->prompt, "(o) = Optional context,"
237                " (c) = Context required\n");
238  for (cmd = arg->cmdtab; cmd->func; cmd++)
239    if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
240      cx = showcx(cmd);
241      cxlen = cmax - strlen(cmd->name);
242      if (n % cols != 0)
243        prompt_Printf(arg->prompt, " ");
244      prompt_Printf(arg->prompt, "%s%-*.*s: %-*.*s",
245              cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes);
246      if (++n % cols == 0)
247        prompt_Printf(arg->prompt, "\n");
248    }
249  if (n % cols != 0)
250    prompt_Printf(arg->prompt, "\n");
251
252  return 0;
253}
254
255static int
256IdentCommand(struct cmdargs const *arg)
257{
258  Concatinate(arg->cx->physical->link.lcp.cfg.ident,
259              sizeof arg->cx->physical->link.lcp.cfg.ident,
260              arg->argc - arg->argn, arg->argv + arg->argn);
261  return 0;
262}
263
264static int
265SendIdentification(struct cmdargs const *arg)
266{
267  if (arg->cx->state < DATALINK_LCP) {
268    log_Printf(LogWARN, "sendident: link has not reached LCP\n");
269    return 2;
270  }
271  return lcp_SendIdentification(&arg->cx->physical->link.lcp) ? 0 : 1;
272}
273
274static int
275CloneCommand(struct cmdargs const *arg)
276{
277  char namelist[LINE_LEN];
278  char *name;
279  int f;
280
281  if (arg->argc == arg->argn)
282    return -1;
283
284  namelist[sizeof namelist - 1] = '\0';
285  for (f = arg->argn; f < arg->argc; f++) {
286    strncpy(namelist, arg->argv[f], sizeof namelist - 1);
287    for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
288      bundle_DatalinkClone(arg->bundle, arg->cx, name);
289  }
290
291  return 0;
292}
293
294static int
295RemoveCommand(struct cmdargs const *arg)
296{
297  if (arg->argc != arg->argn)
298    return -1;
299
300  if (arg->cx->state != DATALINK_CLOSED) {
301    log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n");
302    return 2;
303  }
304
305  bundle_DatalinkRemove(arg->bundle, arg->cx);
306  return 0;
307}
308
309static int
310RenameCommand(struct cmdargs const *arg)
311{
312  if (arg->argc != arg->argn + 1)
313    return -1;
314
315  if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn]))
316    return 0;
317
318  log_Printf(LogWARN, "%s -> %s: target name already exists\n",
319             arg->cx->name, arg->argv[arg->argn]);
320  return 1;
321}
322
323static int
324LoadCommand(struct cmdargs const *arg)
325{
326  const char *err;
327  int n, mode;
328
329  mode = arg->bundle->phys_type.all;
330
331  if (arg->argn < arg->argc) {
332    for (n = arg->argn; n < arg->argc; n++)
333      if ((err = system_IsValid(arg->argv[n], arg->prompt, mode)) != NULL) {
334        log_Printf(LogWARN, "%s: %s\n", arg->argv[n], err);
335        return 1;
336      }
337
338    for (n = arg->argn; n < arg->argc; n++) {
339      bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
340      system_Select(arg->bundle, arg->argv[n], CONFFILE, arg->prompt, arg->cx);
341    }
342    bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
343  } else if ((err = system_IsValid("default", arg->prompt, mode)) != NULL) {
344    log_Printf(LogWARN, "default: %s\n", err);
345    return 1;
346  } else {
347    bundle_SetLabel(arg->bundle, "default");
348    system_Select(arg->bundle, "default", CONFFILE, arg->prompt, arg->cx);
349    bundle_SetLabel(arg->bundle, "default");
350  }
351
352  return 0;
353}
354
355static int
356LogCommand(struct cmdargs const *arg)
357{
358  char buf[LINE_LEN];
359
360  if (arg->argn < arg->argc) {
361    char *argv[MAXARGS];
362    int argc = arg->argc - arg->argn;
363
364    if (argc >= sizeof argv / sizeof argv[0]) {
365      argc = sizeof argv / sizeof argv[0] - 1;
366      log_Printf(LogWARN, "Truncating log command to %d args\n", argc);
367    }
368    command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
369    Concatinate(buf, sizeof buf, argc, (const char *const *)argv);
370    log_Printf(LogLOG, "%s\n", buf);
371    command_Free(argc, argv);
372    return 0;
373  }
374
375  return -1;
376}
377
378static int
379SaveCommand(struct cmdargs const *arg)
380{
381  log_Printf(LogWARN, "save command is not yet implemented.\n");
382  return 1;
383}
384
385static int
386DialCommand(struct cmdargs const *arg)
387{
388  int res;
389
390  if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO)))
391      || (!arg->cx &&
392          (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) {
393    log_Printf(LogWARN, "Manual dial is only available for auto and"
394              " interactive links\n");
395    return 1;
396  }
397
398  if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0)
399    return res;
400
401  bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
402
403  return 0;
404}
405
406#define isinword(ch) (isalnum(ch) || (ch) == '_')
407
408static char *
409strstrword(char *big, const char *little)
410{
411  /* Get the first occurance of the word ``little'' in ``big'' */
412  char *pos;
413  int len;
414
415  pos = big;
416  len = strlen(little);
417
418  while ((pos = strstr(pos, little)) != NULL)
419    if ((pos != big && isinword(pos[-1])) || isinword(pos[len]))
420      pos++;
421    else if (pos != big && pos[-1] == '\\')
422      memmove(pos - 1, pos, strlen(pos) + 1);
423    else
424      break;
425
426  return pos;
427}
428
429static char *
430subst(char *tgt, const char *oldstr, const char *newstr)
431{
432  /* tgt is a malloc()d area... realloc() as necessary */
433  char *word, *ntgt;
434  int ltgt, loldstr, lnewstr, pos;
435
436  if ((word = strstrword(tgt, oldstr)) == NULL)
437    return tgt;
438
439  ltgt = strlen(tgt) + 1;
440  loldstr = strlen(oldstr);
441  lnewstr = strlen(newstr);
442  do {
443    pos = word - tgt;
444    if (loldstr > lnewstr)
445      bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
446    if (loldstr != lnewstr) {
447      ntgt = realloc(tgt, ltgt += lnewstr - loldstr);
448      if (ntgt == NULL)
449        break;			/* Oh wonderful ! */
450      word = ntgt + pos;
451      tgt = ntgt;
452    }
453    if (lnewstr > loldstr)
454      bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
455    bcopy(newstr, word, lnewstr);
456  } while ((word = strstrword(word, oldstr)));
457
458  return tgt;
459}
460
461static char *
462substip(char *tgt, const char *oldstr, struct in_addr ip)
463{
464  return subst(tgt, oldstr, inet_ntoa(ip));
465}
466
467static char *
468substint(char *tgt, const char *oldstr, int i)
469{
470  char buf[12];
471
472  snprintf(buf, sizeof buf, "%d", i);
473
474  return subst(tgt, oldstr, buf);
475}
476
477static char *
478substull(char *tgt, const char *oldstr, unsigned long long ull)
479{
480  char buf[21];
481
482  snprintf(buf, sizeof buf, "%llu", ull);
483
484  return subst(tgt, oldstr, buf);
485}
486
487
488#ifndef NOINET6
489static char *
490substipv6(char *tgt, const char *oldstr, const struct ncpaddr *ip)
491{
492    return subst(tgt, oldstr, ncpaddr_ntoa(ip));
493}
494#endif
495
496void
497command_Expand(char **nargv, int argc, char const *const *oargv,
498               struct bundle *bundle, int inc0, pid_t pid)
499{
500  int arg, secs;
501  char uptime[20];
502  unsigned long long oin, oout, pin, pout;
503
504  if (inc0)
505    arg = 0;		/* Start at arg 0 */
506  else {
507    nargv[0] = strdup(oargv[0]);
508    arg = 1;
509  }
510
511  secs = bundle_Uptime(bundle);
512  snprintf(uptime, sizeof uptime, "%d:%02d:%02d",
513           secs / 3600, (secs / 60) % 60, secs % 60);
514  oin = bundle->ncp.ipcp.throughput.OctetsIn;
515  oout = bundle->ncp.ipcp.throughput.OctetsOut;
516  pin = bundle->ncp.ipcp.throughput.PacketsIn;
517  pout = bundle->ncp.ipcp.throughput.PacketsOut;
518#ifndef NOINET6
519  oin += bundle->ncp.ipv6cp.throughput.OctetsIn;
520  oout += bundle->ncp.ipv6cp.throughput.OctetsOut;
521  pin += bundle->ncp.ipv6cp.throughput.PacketsIn;
522  pout += bundle->ncp.ipv6cp.throughput.PacketsOut;
523#endif
524
525  for (; arg < argc; arg++) {
526    nargv[arg] = strdup(oargv[arg]);
527    nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name);
528    nargv[arg] = subst(nargv[arg], "COMPILATIONDATE", __DATE__);
529    nargv[arg] = substip(nargv[arg], "DNS0", bundle->ncp.ipcp.ns.dns[0]);
530    nargv[arg] = substip(nargv[arg], "DNS1", bundle->ncp.ipcp.ns.dns[1]);
531    nargv[arg] = subst(nargv[arg], "ENDDISC",
532                       mp_Enddisc(bundle->ncp.mp.cfg.enddisc.class,
533                                  bundle->ncp.mp.cfg.enddisc.address,
534                                  bundle->ncp.mp.cfg.enddisc.len));
535    nargv[arg] = substip(nargv[arg], "HISADDR", bundle->ncp.ipcp.peer_ip);
536#ifndef NOINET6
537    nargv[arg] = substipv6(nargv[arg], "HISADDR6", &bundle->ncp.ipv6cp.hisaddr);
538#endif
539    nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name);
540    nargv[arg] = substull(nargv[arg], "IPOCTETSIN",
541                          bundle->ncp.ipcp.throughput.OctetsIn);
542    nargv[arg] = substull(nargv[arg], "IPOCTETSOUT",
543                          bundle->ncp.ipcp.throughput.OctetsOut);
544    nargv[arg] = substull(nargv[arg], "IPPACKETSIN",
545                          bundle->ncp.ipcp.throughput.PacketsIn);
546    nargv[arg] = substull(nargv[arg], "IPPACKETSOUT",
547                          bundle->ncp.ipcp.throughput.PacketsOut);
548#ifndef NOINET6
549    nargv[arg] = substull(nargv[arg], "IPV6OCTETSIN",
550                          bundle->ncp.ipv6cp.throughput.OctetsIn);
551    nargv[arg] = substull(nargv[arg], "IPV6OCTETSOUT",
552                          bundle->ncp.ipv6cp.throughput.OctetsOut);
553    nargv[arg] = substull(nargv[arg], "IPV6PACKETSIN",
554                          bundle->ncp.ipv6cp.throughput.PacketsIn);
555    nargv[arg] = substull(nargv[arg], "IPV6PACKETSOUT",
556                          bundle->ncp.ipv6cp.throughput.PacketsOut);
557#endif
558    nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle));
559    nargv[arg] = substip(nargv[arg], "MYADDR", bundle->ncp.ipcp.my_ip);
560#ifndef NOINET6
561    nargv[arg] = substipv6(nargv[arg], "MYADDR6", &bundle->ncp.ipv6cp.myaddr);
562#endif
563    nargv[arg] = substull(nargv[arg], "OCTETSIN", oin);
564    nargv[arg] = substull(nargv[arg], "OCTETSOUT", oout);
565    nargv[arg] = substull(nargv[arg], "PACKETSIN", pin);
566    nargv[arg] = substull(nargv[arg], "PACKETSOUT", pout);
567    nargv[arg] = subst(nargv[arg], "PEER_ENDDISC",
568                       mp_Enddisc(bundle->ncp.mp.peer.enddisc.class,
569                                  bundle->ncp.mp.peer.enddisc.address,
570                                  bundle->ncp.mp.peer.enddisc.len));
571    nargv[arg] = substint(nargv[arg], "PROCESSID", pid);
572    if (server.cfg.port)
573      nargv[arg] = substint(nargv[arg], "SOCKNAME", server.cfg.port);
574    else
575      nargv[arg] = subst(nargv[arg], "SOCKNAME", server.cfg.sockname);
576    nargv[arg] = subst(nargv[arg], "UPTIME", uptime);
577    nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname);
578    nargv[arg] = subst(nargv[arg], "VERSION", Version);
579  }
580  nargv[arg] = NULL;
581}
582
583void
584command_Free(int argc, char **argv)
585{
586  while (argc) {
587    free(*argv);
588    argc--;
589    argv++;
590  }
591}
592
593static int
594ShellCommand(struct cmdargs const *arg, int bg)
595{
596  const char *shell;
597  pid_t shpid, pid;
598
599#ifdef SHELL_ONLY_INTERACTIVELY
600  /* we're only allowed to shell when we run ppp interactively */
601  if (arg->prompt && arg->prompt->owner) {
602    log_Printf(LogWARN, "Can't start a shell from a network connection\n");
603    return 1;
604  }
605#endif
606
607  if (arg->argc == arg->argn) {
608    if (!arg->prompt) {
609      log_Printf(LogWARN, "Can't start an interactive shell from"
610                " a config file\n");
611      return 1;
612    } else if (arg->prompt->owner) {
613      log_Printf(LogWARN, "Can't start an interactive shell from"
614                " a socket connection\n");
615      return 1;
616    } else if (bg) {
617      log_Printf(LogWARN, "Can only start an interactive shell in"
618		" the foreground mode\n");
619      return 1;
620    }
621  }
622
623  pid = getpid();
624  if ((shpid = fork()) == 0) {
625    int i, fd;
626
627    if ((shell = getenv("SHELL")) == 0)
628      shell = _PATH_BSHELL;
629
630    timer_TermService();
631
632    if (arg->prompt)
633      fd = arg->prompt->fd_out;
634    else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
635      log_Printf(LogALERT, "Failed to open %s: %s\n",
636                _PATH_DEVNULL, strerror(errno));
637      exit(1);
638    }
639    dup2(fd, STDIN_FILENO);
640    dup2(fd, STDOUT_FILENO);
641    dup2(fd, STDERR_FILENO);
642    for (i = getdtablesize(); i > STDERR_FILENO; i--)
643      fcntl(i, F_SETFD, 1);
644
645#ifndef NOSUID
646    setuid(ID0realuid());
647#endif
648    if (arg->argc > arg->argn) {
649      /* substitute pseudo args */
650      char *argv[MAXARGS];
651      int argc = arg->argc - arg->argn;
652
653      if (argc >= sizeof argv / sizeof argv[0]) {
654        argc = sizeof argv / sizeof argv[0] - 1;
655        log_Printf(LogWARN, "Truncating shell command to %d args\n", argc);
656      }
657      command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 0, pid);
658      if (bg) {
659	pid_t p;
660
661	p = getpid();
662	if (daemon(1, 1) == -1) {
663	  log_Printf(LogERROR, "%d: daemon: %s\n", (int)p, strerror(errno));
664	  exit(1);
665	}
666      } else if (arg->prompt)
667        printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]);
668      execvp(argv[0], argv);
669    } else {
670      if (arg->prompt)
671        printf("ppp: Pausing until %s finishes\n", shell);
672      prompt_TtyOldMode(arg->prompt);
673      execl(shell, shell, (char *)NULL);
674    }
675
676    log_Printf(LogWARN, "exec() of %s failed: %s\n",
677              arg->argc > arg->argn ? arg->argv[arg->argn] : shell,
678              strerror(errno));
679    _exit(255);
680  }
681
682  if (shpid == (pid_t) - 1)
683    log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno));
684  else {
685    int status;
686    waitpid(shpid, &status, 0);
687  }
688
689  if (arg->prompt && !arg->prompt->owner)
690    prompt_TtyCommandMode(arg->prompt);
691
692  return 0;
693}
694
695static int
696BgShellCommand(struct cmdargs const *arg)
697{
698  if (arg->argc == arg->argn)
699    return -1;
700  return ShellCommand(arg, 1);
701}
702
703static int
704FgShellCommand(struct cmdargs const *arg)
705{
706  return ShellCommand(arg, 0);
707}
708
709static int
710ResolvCommand(struct cmdargs const *arg)
711{
712  if (arg->argc == arg->argn + 1) {
713    if (!strcasecmp(arg->argv[arg->argn], "reload"))
714      ipcp_LoadDNS(&arg->bundle->ncp.ipcp);
715    else if (!strcasecmp(arg->argv[arg->argn], "restore"))
716      ipcp_RestoreDNS(&arg->bundle->ncp.ipcp);
717    else if (!strcasecmp(arg->argv[arg->argn], "rewrite"))
718      ipcp_WriteDNS(&arg->bundle->ncp.ipcp);
719    else if (!strcasecmp(arg->argv[arg->argn], "readonly"))
720      arg->bundle->ncp.ipcp.ns.writable = 0;
721    else if (!strcasecmp(arg->argv[arg->argn], "writable"))
722      arg->bundle->ncp.ipcp.ns.writable = 1;
723    else
724      return -1;
725
726    return 0;
727  }
728
729  return -1;
730}
731
732#ifndef NONAT
733static struct cmdtab const NatCommands[] =
734{
735  {"addr", NULL, nat_RedirectAddr, LOCAL_AUTH,
736   "static address translation", "nat addr [addr_local addr_alias]"},
737  {"deny_incoming", NULL, NatOption, LOCAL_AUTH,
738   "stop incoming connections", "nat deny_incoming yes|no",
739   (const void *) PKT_ALIAS_DENY_INCOMING},
740  {"enable", NULL, NatEnable, LOCAL_AUTH,
741   "enable NAT", "nat enable yes|no"},
742  {"log", NULL, NatOption, LOCAL_AUTH,
743   "log NAT link creation", "nat log yes|no",
744   (const void *) PKT_ALIAS_LOG},
745  {"port", NULL, nat_RedirectPort, LOCAL_AUTH, "port redirection",
746   "nat port proto localaddr:port[-port] aliasport[-aliasport]"},
747  {"proto", NULL, nat_RedirectProto, LOCAL_AUTH, "protocol redirection",
748   "nat proto proto localIP [publicIP [remoteIP]]"},
749  {"proxy", NULL, nat_ProxyRule, LOCAL_AUTH,
750   "proxy control", "nat proxy server host[:port] ..."},
751#ifndef NO_FW_PUNCH
752  {"punch_fw", NULL, nat_PunchFW, LOCAL_AUTH,
753   "firewall control", "nat punch_fw [base count]"},
754#endif
755  {"same_ports", NULL, NatOption, LOCAL_AUTH,
756   "try to leave port numbers unchanged", "nat same_ports yes|no",
757   (const void *) PKT_ALIAS_SAME_PORTS},
758  {"target", NULL, nat_SetTarget, LOCAL_AUTH,
759   "Default address for incoming connections", "nat target addr" },
760  {"unregistered_only", NULL, NatOption, LOCAL_AUTH,
761   "translate unregistered (private) IP address space only",
762   "nat unregistered_only yes|no",
763   (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
764  {"use_sockets", NULL, NatOption, LOCAL_AUTH,
765   "allocate host sockets", "nat use_sockets yes|no",
766   (const void *) PKT_ALIAS_USE_SOCKETS},
767  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
768   "Display this message", "nat help|? [command]", NatCommands},
769  {NULL, NULL, NULL},
770};
771#endif
772
773static struct cmdtab const AllowCommands[] = {
774  {"modes", "mode", AllowModes, LOCAL_AUTH,
775  "Only allow certain ppp modes", "allow modes mode..."},
776  {"users", "user", AllowUsers, LOCAL_AUTH,
777  "Only allow ppp access to certain users", "allow users logname..."},
778  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
779  "Display this message", "allow help|? [command]", AllowCommands},
780  {NULL, NULL, NULL},
781};
782
783static struct cmdtab const IfaceCommands[] =
784{
785  {"add", NULL, IfaceAddCommand, LOCAL_AUTH,
786   "Add iface address", "iface add addr[/bits| mask] peer", NULL},
787  {NULL, "add!", IfaceAddCommand, LOCAL_AUTH,
788   "Add or change an iface address", "iface add! addr[/bits| mask] peer",
789   (void *)1},
790  {"clear", NULL, IfaceClearCommand, LOCAL_AUTH,
791   "Clear iface address(es)", "iface clear [INET | INET6]"},
792  {"delete", "rm", IfaceDeleteCommand, LOCAL_AUTH,
793   "Delete iface address", "iface delete addr", NULL},
794  {NULL, "rm!", IfaceDeleteCommand, LOCAL_AUTH,
795   "Delete iface address", "iface delete addr", (void *)1},
796  {NULL, "delete!", IfaceDeleteCommand, LOCAL_AUTH,
797   "Delete iface address", "iface delete addr", (void *)1},
798  {"show", NULL, iface_Show, LOCAL_AUTH,
799   "Show iface address(es)", "iface show"},
800  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
801   "Display this message", "nat help|? [command]", IfaceCommands},
802  {NULL, NULL, NULL},
803};
804
805static struct cmdtab const Commands[] = {
806  {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
807  "accept option request", "accept option .."},
808  {"add", NULL, AddCommand, LOCAL_AUTH,
809  "add route", "add dest mask gateway", NULL},
810  {NULL, "add!", AddCommand, LOCAL_AUTH,
811  "add or change route", "add! dest mask gateway", (void *)1},
812  {"allow", "auth", RunListCommand, LOCAL_AUTH,
813  "Allow ppp access", "allow users|modes ....", AllowCommands},
814  {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
815  "Run a background command", "[!]bg command"},
816  {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
817  "Clear throughput statistics",
818  "clear ipcp|ipv6cp|physical [current|overall|peak]..."},
819  {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
820  "Clone a link", "clone newname..."},
821  {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
822  "Close an FSM", "close [lcp|ccp]"},
823  {"delete", NULL, DeleteCommand, LOCAL_AUTH,
824  "delete route", "delete dest", NULL},
825  {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
826  "delete a route if it exists", "delete! dest", (void *)1},
827  {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
828  "Deny option request", "deny option .."},
829  {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
830  "Dial and login", "dial|call [system ...]", NULL},
831  {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
832  "Disable option", "disable option .."},
833  {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
834  "Generate a down event", "down [ccp|lcp]"},
835  {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
836  "Enable option", "enable option .."},
837  {"ident", NULL, IdentCommand, LOCAL_AUTH | LOCAL_CX,
838  "Set the link identity", "ident text..."},
839  {"iface", "interface", RunListCommand, LOCAL_AUTH,
840  "interface control", "iface option ...", IfaceCommands},
841  {"link", "datalink", LinkCommand, LOCAL_AUTH,
842  "Link specific commands", "link name command ..."},
843  {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
844  "Load settings", "load [system ...]"},
845  {"log", NULL, LogCommand, LOCAL_AUTH | LOCAL_CX_OPT,
846  "log information", "log word ..."},
847#ifndef NONAT
848  {"nat", "alias", RunListCommand, LOCAL_AUTH,
849  "NAT control", "nat option yes|no", NatCommands},
850#endif
851  {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT,
852  "Open an FSM", "open! [lcp|ccp|ipcp]", (void *)1},
853  {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
854  "Password for manipulation", "passwd LocalPassword"},
855  {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
856  "Quit PPP program", "quit|bye [all]"},
857  {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
858  "Remove a link", "remove"},
859  {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
860  "Rename a link", "rename name"},
861  {"resolv", NULL, ResolvCommand, LOCAL_AUTH,
862  "Manipulate resolv.conf", "resolv readonly|reload|restore|rewrite|writable"},
863  {"save", NULL, SaveCommand, LOCAL_AUTH,
864  "Save settings", "save"},
865  {"sendident", NULL, SendIdentification, LOCAL_AUTH | LOCAL_CX,
866  "Transmit the link identity", "sendident"},
867  {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
868  "Set parameters", "set[up] var value"},
869  {"shell", "!", FgShellCommand, LOCAL_AUTH,
870  "Run a subshell", "shell|! [sh command]"},
871  {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
872  "Show status and stats", "show var"},
873  {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
874  "Enter terminal mode", "term"},
875  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
876  "Display this message", "help|? [command]", Commands},
877  {NULL, NULL, NULL},
878};
879
880static int
881ShowEscape(struct cmdargs const *arg)
882{
883  if (arg->cx->physical->async.cfg.EscMap[32]) {
884    int code, bit;
885    const char *sep = "";
886
887    for (code = 0; code < 32; code++)
888      if (arg->cx->physical->async.cfg.EscMap[code])
889	for (bit = 0; bit < 8; bit++)
890	  if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
891	    prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
892            sep = ", ";
893          }
894    prompt_Printf(arg->prompt, "\n");
895  }
896  return 0;
897}
898
899static int
900ShowTimerList(struct cmdargs const *arg)
901{
902  timer_Show(0, arg->prompt);
903  return 0;
904}
905
906static int
907ShowStopped(struct cmdargs const *arg)
908{
909  prompt_Printf(arg->prompt, " Stopped Timer:  LCP: ");
910  if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
911    prompt_Printf(arg->prompt, "Disabled");
912  else
913    prompt_Printf(arg->prompt, "%ld secs",
914                  arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
915
916  prompt_Printf(arg->prompt, ", CCP: ");
917  if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
918    prompt_Printf(arg->prompt, "Disabled");
919  else
920    prompt_Printf(arg->prompt, "%ld secs",
921                  arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
922
923  prompt_Printf(arg->prompt, "\n");
924
925  return 0;
926}
927
928static int
929ShowVersion(struct cmdargs const *arg)
930{
931  prompt_Printf(arg->prompt, "PPP Version %s - %s\n", Version, __DATE__);
932  return 0;
933}
934
935static int
936ShowProtocolStats(struct cmdargs const *arg)
937{
938  struct link *l = command_ChooseLink(arg);
939
940  prompt_Printf(arg->prompt, "%s:\n", l->name);
941  link_ReportProtocolStatus(l, arg->prompt);
942  return 0;
943}
944
945static struct cmdtab const ShowCommands[] = {
946  {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
947  "bundle details", "show bundle"},
948  {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
949  "CCP status", "show cpp"},
950  {"compress", NULL, sl_Show, LOCAL_AUTH,
951  "VJ compression stats", "show compress"},
952  {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
953  "escape characters", "show escape"},
954  {"filter", NULL, filter_Show, LOCAL_AUTH,
955  "packet filters", "show filter [in|out|dial|alive]"},
956  {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
957  "HDLC errors", "show hdlc"},
958  {"iface", "interface", iface_Show, LOCAL_AUTH,
959  "Interface status", "show iface"},
960  {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
961  "IPCP status", "show ipcp"},
962#ifndef NOINET6
963  {"ipv6cp", NULL, ipv6cp_Show, LOCAL_AUTH,
964  "IPV6CP status", "show ipv6cp"},
965#endif
966  {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT,
967  "Protocol layers", "show layers"},
968  {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
969  "LCP status", "show lcp"},
970  {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
971  "(high-level) link info", "show link"},
972  {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
973  "available link names", "show links"},
974  {"log", NULL, log_ShowLevel, LOCAL_AUTH,
975  "log levels", "show log"},
976  {"mem", NULL, mbuf_Show, LOCAL_AUTH,
977  "mbuf allocations", "show mem"},
978  {"ncp", NULL, ncp_Show, LOCAL_AUTH,
979  "NCP status", "show ncp"},
980  {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX,
981  "(low-level) link info", "show physical"},
982  {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
983  "multilink setup", "show mp"},
984  {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
985  "protocol summary", "show proto"},
986  {"route", NULL, route_Show, LOCAL_AUTH,
987  "routing table", "show route"},
988  {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
989  "STOPPED timeout", "show stopped"},
990  {"timers", NULL, ShowTimerList, LOCAL_AUTH,
991  "alarm timers", "show timers"},
992  {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
993  "version string", "show version"},
994  {"who", NULL, log_ShowWho, LOCAL_AUTH,
995  "client list", "show who"},
996  {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
997  "Display this message", "show help|? [command]", ShowCommands},
998  {NULL, NULL, NULL},
999};
1000
1001static struct cmdtab const *
1002FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
1003{
1004  int nmatch;
1005  int len;
1006  struct cmdtab const *found;
1007
1008  found = NULL;
1009  len = strlen(str);
1010  nmatch = 0;
1011  while (cmds->func) {
1012    if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
1013      if (cmds->name[len] == '\0') {
1014	*pmatch = 1;
1015	return cmds;
1016      }
1017      nmatch++;
1018      found = cmds;
1019    } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
1020      if (cmds->alias[len] == '\0') {
1021	*pmatch = 1;
1022	return cmds;
1023      }
1024      nmatch++;
1025      found = cmds;
1026    }
1027    cmds++;
1028  }
1029  *pmatch = nmatch;
1030  return found;
1031}
1032
1033static const char *
1034mkPrefix(int argc, char const *const *argv, char *tgt, int sz)
1035{
1036  int f, tlen, len;
1037
1038  tlen = 0;
1039  for (f = 0; f < argc && tlen < sz - 2; f++) {
1040    if (f)
1041      tgt[tlen++] = ' ';
1042    len = strlen(argv[f]);
1043    if (len > sz - tlen - 1)
1044      len = sz - tlen - 1;
1045    strncpy(tgt+tlen, argv[f], len);
1046    tlen += len;
1047  }
1048  tgt[tlen] = '\0';
1049  return tgt;
1050}
1051
1052static int
1053FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
1054         char const *const *argv, struct prompt *prompt, struct datalink *cx)
1055{
1056  struct cmdtab const *cmd;
1057  int val = 1;
1058  int nmatch;
1059  struct cmdargs arg;
1060  char prefix[100];
1061
1062  cmd = FindCommand(cmds, argv[argn], &nmatch);
1063  if (nmatch > 1)
1064    log_Printf(LogWARN, "%s: Ambiguous command\n",
1065              mkPrefix(argn+1, argv, prefix, sizeof prefix));
1066  else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
1067    if ((cmd->lauth & LOCAL_CX) && !cx)
1068      /* We've got no context, but we require it */
1069      cx = bundle2datalink(bundle, NULL);
1070
1071    if ((cmd->lauth & LOCAL_CX) && !cx)
1072      log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
1073                mkPrefix(argn+1, argv, prefix, sizeof prefix));
1074    else {
1075      if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1076        log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
1077                  mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
1078        cx = NULL;
1079      }
1080      arg.cmdtab = cmds;
1081      arg.cmd = cmd;
1082      arg.argc = argc;
1083      arg.argn = argn+1;
1084      arg.argv = argv;
1085      arg.bundle = bundle;
1086      arg.cx = cx;
1087      arg.prompt = prompt;
1088      val = (*cmd->func) (&arg);
1089    }
1090  } else
1091    log_Printf(LogWARN, "%s: Invalid command\n",
1092              mkPrefix(argn+1, argv, prefix, sizeof prefix));
1093
1094  if (val == -1)
1095    log_Printf(LogWARN, "usage: %s\n", cmd->syntax);
1096  else if (val)
1097    log_Printf(LogWARN, "%s: Failed %d\n",
1098              mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
1099
1100  return val;
1101}
1102
1103int
1104command_Expand_Interpret(char *buff, int nb, char *argv[MAXARGS], int offset)
1105{
1106  char buff2[LINE_LEN-offset];
1107
1108  InterpretArg(buff, buff2);
1109  strncpy(buff, buff2, LINE_LEN - offset - 1);
1110  buff[LINE_LEN - offset - 1] = '\0';
1111
1112  return command_Interpret(buff, nb, argv);
1113}
1114
1115int
1116command_Interpret(char *buff, int nb, char *argv[MAXARGS])
1117{
1118  char *cp;
1119
1120  if (nb > 0) {
1121    cp = buff + strcspn(buff, "\r\n");
1122    if (cp)
1123      *cp = '\0';
1124    return MakeArgs(buff, argv, MAXARGS, PARSE_REDUCE);
1125  }
1126  return 0;
1127}
1128
1129static int
1130arghidden(int argc, char const *const *argv, int n)
1131{
1132  /* Is arg n of the given command to be hidden from the log ? */
1133
1134  /* set authkey xxxxx */
1135  /* set key xxxxx */
1136  if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
1137      (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
1138    return 1;
1139
1140  /* passwd xxxxx */
1141  if (n == 1 && !strncasecmp(argv[0], "p", 1))
1142    return 1;
1143
1144  /* set server port xxxxx .... */
1145  if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
1146      !strncasecmp(argv[1], "se", 2))
1147    return 1;
1148
1149  return 0;
1150}
1151
1152void
1153command_Run(struct bundle *bundle, int argc, char const *const *argv,
1154           struct prompt *prompt, const char *label, struct datalink *cx)
1155{
1156  if (argc > 0) {
1157    if (log_IsKept(LogCOMMAND)) {
1158      char buf[LINE_LEN];
1159      int f, n;
1160
1161      if (label) {
1162        strncpy(buf, label, sizeof buf - 3);
1163        buf[sizeof buf - 3] = '\0';
1164        strcat(buf, ": ");
1165        n = strlen(buf);
1166      } else {
1167        *buf = '\0';
1168        n = 0;
1169      }
1170      buf[sizeof buf - 1] = '\0';	/* In case we run out of room in buf */
1171
1172      for (f = 0; f < argc; f++) {
1173        if (n < sizeof buf - 1 && f)
1174          buf[n++] = ' ';
1175        if (arghidden(argc, argv, f))
1176          strncpy(buf+n, "********", sizeof buf - n - 1);
1177        else
1178          strncpy(buf+n, argv[f], sizeof buf - n - 1);
1179        n += strlen(buf+n);
1180      }
1181      log_Printf(LogCOMMAND, "%s\n", buf);
1182    }
1183    FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
1184  }
1185}
1186
1187int
1188command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
1189              const char *label)
1190{
1191  int argc;
1192  char *argv[MAXARGS];
1193
1194  if ((argc = command_Expand_Interpret(buff, nb, argv, 0)) < 0)
1195    return 0;
1196
1197  command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
1198  return 1;
1199}
1200
1201static int
1202ShowCommand(struct cmdargs const *arg)
1203{
1204  if (!arg->prompt)
1205    log_Printf(LogWARN, "show: Cannot show without a prompt\n");
1206  else if (arg->argc > arg->argn)
1207    FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
1208             arg->prompt, arg->cx);
1209  else
1210    prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
1211
1212  return 0;
1213}
1214
1215static int
1216TerminalCommand(struct cmdargs const *arg)
1217{
1218  if (!arg->prompt) {
1219    log_Printf(LogWARN, "term: Need a prompt\n");
1220    return 1;
1221  }
1222
1223  if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
1224    prompt_Printf(arg->prompt, "LCP state is [%s]\n",
1225                  State2Nam(arg->cx->physical->link.lcp.fsm.state));
1226    return 1;
1227  }
1228
1229  datalink_Up(arg->cx, 0, 0);
1230  prompt_TtyTermMode(arg->prompt, arg->cx);
1231  return 0;
1232}
1233
1234static int
1235QuitCommand(struct cmdargs const *arg)
1236{
1237  if (!arg->prompt || prompt_IsController(arg->prompt) ||
1238      (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
1239       (arg->prompt->auth & LOCAL_AUTH)))
1240    Cleanup(EX_NORMAL);
1241  if (arg->prompt)
1242    prompt_Destroy(arg->prompt, 1);
1243
1244  return 0;
1245}
1246
1247static int
1248OpenCommand(struct cmdargs const *arg)
1249{
1250  if (arg->argc == arg->argn)
1251    bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
1252  else if (arg->argc == arg->argn + 1) {
1253    if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1254      struct datalink *cx = arg->cx ?
1255        arg->cx : bundle2datalink(arg->bundle, NULL);
1256      if (cx) {
1257        if (cx->physical->link.lcp.fsm.state == ST_OPENED)
1258          fsm_Reopen(&cx->physical->link.lcp.fsm);
1259        else
1260          bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1);
1261      } else
1262        log_Printf(LogWARN, "open lcp: You must specify a link\n");
1263    } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1264      struct fsm *fp;
1265
1266      fp = &command_ChooseLink(arg)->ccp.fsm;
1267      if (fp->link->lcp.fsm.state != ST_OPENED)
1268        log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
1269      else if (fp->state == ST_OPENED)
1270        fsm_Reopen(fp);
1271      else {
1272        fp->open_mode = 0;	/* Not passive any more */
1273        if (fp->state == ST_STOPPED) {
1274          fsm_Down(fp);
1275          fsm_Up(fp);
1276        } else {
1277          fsm_Up(fp);
1278          fsm_Open(fp);
1279        }
1280      }
1281    } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) {
1282      if (arg->cx)
1283        log_Printf(LogWARN, "open ipcp: You need not specify a link\n");
1284      if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
1285        fsm_Reopen(&arg->bundle->ncp.ipcp.fsm);
1286      else
1287        bundle_Open(arg->bundle, NULL, PHYS_ALL, 1);
1288    } else
1289      return -1;
1290  } else
1291    return -1;
1292
1293  return 0;
1294}
1295
1296static int
1297CloseCommand(struct cmdargs const *arg)
1298{
1299  if (arg->argc == arg->argn)
1300    bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
1301  else if (arg->argc == arg->argn + 1) {
1302    if (!strcasecmp(arg->argv[arg->argn], "lcp"))
1303      bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
1304    else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
1305             !strcasecmp(arg->argv[arg->argn], "ccp!")) {
1306      struct fsm *fp;
1307
1308      fp = &command_ChooseLink(arg)->ccp.fsm;
1309      if (fp->state == ST_OPENED) {
1310        fsm_Close(fp);
1311        if (arg->argv[arg->argn][3] == '!')
1312          fp->open_mode = 0;		/* Stay ST_CLOSED */
1313        else
1314          fp->open_mode = OPEN_PASSIVE;	/* Wait for the peer to start */
1315      }
1316    } else
1317      return -1;
1318  } else
1319    return -1;
1320
1321  return 0;
1322}
1323
1324static int
1325DownCommand(struct cmdargs const *arg)
1326{
1327  if (arg->argc == arg->argn) {
1328      if (arg->cx)
1329        datalink_Down(arg->cx, CLOSE_STAYDOWN);
1330      else
1331        bundle_Down(arg->bundle, CLOSE_STAYDOWN);
1332  } else if (arg->argc == arg->argn + 1) {
1333    if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1334      if (arg->cx)
1335        datalink_Down(arg->cx, CLOSE_LCP);
1336      else
1337        bundle_Down(arg->bundle, CLOSE_LCP);
1338    } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1339      struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
1340                                 &arg->bundle->ncp.mp.link.ccp.fsm;
1341      fsm2initial(fp);
1342    } else
1343      return -1;
1344  } else
1345    return -1;
1346
1347  return 0;
1348}
1349
1350static int
1351SetModemSpeed(struct cmdargs const *arg)
1352{
1353  long speed;
1354  char *end;
1355
1356  if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
1357    if (arg->argc > arg->argn+1) {
1358      log_Printf(LogWARN, "SetModemSpeed: Too many arguments\n");
1359      return -1;
1360    }
1361    if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
1362      physical_SetSync(arg->cx->physical);
1363      return 0;
1364    }
1365    end = NULL;
1366    speed = strtol(arg->argv[arg->argn], &end, 10);
1367    if (*end) {
1368      log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
1369                arg->argv[arg->argn]);
1370      return -1;
1371    }
1372    if (physical_SetSpeed(arg->cx->physical, speed))
1373      return 0;
1374    log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
1375  } else
1376    log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
1377
1378  return -1;
1379}
1380
1381static int
1382SetStoppedTimeout(struct cmdargs const *arg)
1383{
1384  struct link *l = &arg->cx->physical->link;
1385
1386  l->lcp.fsm.StoppedTimer.load = 0;
1387  l->ccp.fsm.StoppedTimer.load = 0;
1388  if (arg->argc <= arg->argn+2) {
1389    if (arg->argc > arg->argn) {
1390      l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
1391      if (arg->argc > arg->argn+1)
1392        l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
1393    }
1394    return 0;
1395  }
1396  return -1;
1397}
1398
1399static int
1400SetServer(struct cmdargs const *arg)
1401{
1402  int res = -1;
1403
1404  if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
1405    const char *port, *passwd, *mask;
1406    int mlen;
1407
1408    /* What's what ? */
1409    port = arg->argv[arg->argn];
1410    if (arg->argc == arg->argn + 2) {
1411      passwd = arg->argv[arg->argn+1];
1412      mask = NULL;
1413    } else if (arg->argc == arg->argn + 3) {
1414      passwd = arg->argv[arg->argn+1];
1415      mask = arg->argv[arg->argn+2];
1416      mlen = strlen(mask);
1417      if (mlen == 0 || mlen > 4 || strspn(mask, "01234567") != mlen ||
1418          (mlen == 4 && *mask != '0')) {
1419        log_Printf(LogWARN, "%s %s: %s: Invalid mask\n",
1420                   arg->argv[arg->argn - 2], arg->argv[arg->argn - 1], mask);
1421        return -1;
1422      }
1423    } else if (arg->argc != arg->argn + 1)
1424      return -1;
1425    else if (strcasecmp(port, "none") == 0) {
1426      if (server_Clear(arg->bundle))
1427        log_Printf(LogPHASE, "Disabled server socket\n");
1428      return 0;
1429    } else if (strcasecmp(port, "open") == 0) {
1430      switch (server_Reopen(arg->bundle)) {
1431        case SERVER_OK:
1432          return 0;
1433        case SERVER_FAILED:
1434          log_Printf(LogWARN, "Failed to reopen server port\n");
1435          return 1;
1436        case SERVER_UNSET:
1437          log_Printf(LogWARN, "Cannot reopen unset server socket\n");
1438          return 1;
1439        default:
1440          break;
1441      }
1442      return -1;
1443    } else if (strcasecmp(port, "closed") == 0) {
1444      if (server_Close(arg->bundle))
1445        log_Printf(LogPHASE, "Closed server socket\n");
1446      else
1447        log_Printf(LogWARN, "Server socket not open\n");
1448
1449      return 0;
1450    } else
1451      return -1;
1452
1453    strncpy(server.cfg.passwd, passwd, sizeof server.cfg.passwd - 1);
1454    server.cfg.passwd[sizeof server.cfg.passwd - 1] = '\0';
1455
1456    if (*port == '/') {
1457      mode_t imask;
1458      char *ptr, name[LINE_LEN + 12];
1459
1460      if (mask == NULL)
1461        imask = (mode_t)-1;
1462      else for (imask = mlen = 0; mask[mlen]; mlen++)
1463        imask = (imask * 8) + mask[mlen] - '0';
1464
1465      ptr = strstr(port, "%d");
1466      if (ptr) {
1467        snprintf(name, sizeof name, "%.*s%d%s",
1468                 (int)(ptr - port), port, arg->bundle->unit, ptr + 2);
1469        port = name;
1470      }
1471      res = server_LocalOpen(arg->bundle, port, imask);
1472    } else {
1473      int iport, add = 0;
1474
1475      if (mask != NULL)
1476        return -1;
1477
1478      if (*port == '+') {
1479        port++;
1480        add = 1;
1481      }
1482      if (strspn(port, "0123456789") != strlen(port)) {
1483        struct servent *s;
1484
1485        if ((s = getservbyname(port, "tcp")) == NULL) {
1486	  iport = 0;
1487	  log_Printf(LogWARN, "%s: Invalid port or service\n", port);
1488	} else
1489	  iport = ntohs(s->s_port);
1490      } else
1491        iport = atoi(port);
1492
1493      if (iport) {
1494        if (add)
1495          iport += arg->bundle->unit;
1496        res = server_TcpOpen(arg->bundle, iport);
1497      } else
1498        res = -1;
1499    }
1500  }
1501
1502  return res;
1503}
1504
1505static int
1506SetEscape(struct cmdargs const *arg)
1507{
1508  int code;
1509  int argc = arg->argc - arg->argn;
1510  char const *const *argv = arg->argv + arg->argn;
1511
1512  for (code = 0; code < 33; code++)
1513    arg->cx->physical->async.cfg.EscMap[code] = 0;
1514
1515  while (argc-- > 0) {
1516    sscanf(*argv++, "%x", &code);
1517    code &= 0xff;
1518    arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
1519    arg->cx->physical->async.cfg.EscMap[32] = 1;
1520  }
1521  return 0;
1522}
1523
1524static int
1525SetInterfaceAddr(struct cmdargs const *arg)
1526{
1527  struct ncp *ncp = &arg->bundle->ncp;
1528  struct ncpaddr ncpaddr;
1529  const char *hisaddr;
1530
1531  if (arg->argc > arg->argn + 4)
1532    return -1;
1533
1534  hisaddr = NULL;
1535  memset(&ncp->ipcp.cfg.my_range, '\0', sizeof ncp->ipcp.cfg.my_range);
1536  memset(&ncp->ipcp.cfg.peer_range, '\0', sizeof ncp->ipcp.cfg.peer_range);
1537  ncp->ipcp.cfg.HaveTriggerAddress = 0;
1538  ncp->ipcp.cfg.netmask.s_addr = INADDR_ANY;
1539  iplist_reset(&ncp->ipcp.cfg.peer_list);
1540
1541  if (arg->argc > arg->argn) {
1542    if (!ncprange_aton(&ncp->ipcp.cfg.my_range, ncp, arg->argv[arg->argn]))
1543      return 1;
1544    if (arg->argc > arg->argn+1) {
1545      hisaddr = arg->argv[arg->argn+1];
1546      if (arg->argc > arg->argn+2) {
1547        ncp->ipcp.ifmask = ncp->ipcp.cfg.netmask =
1548          GetIpAddr(arg->argv[arg->argn+2]);
1549	if (arg->argc > arg->argn+3) {
1550	  ncp->ipcp.cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
1551	  ncp->ipcp.cfg.HaveTriggerAddress = 1;
1552	}
1553      }
1554    }
1555  }
1556
1557  /* 0.0.0.0 means any address (0 bits) */
1558  ncpaddr_getip4(&ncpaddr, &ncp->ipcp.my_ip);
1559  ncprange_getaddr(&ncp->ipcp.cfg.my_range, &ncpaddr);
1560  if (ncp->ipcp.my_ip.s_addr == INADDR_ANY)
1561    ncprange_setwidth(&ncp->ipcp.cfg.my_range, 0);
1562  bundle_AdjustFilters(arg->bundle, &ncpaddr, NULL);
1563
1564  if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
1565                                  arg->bundle->phys_type.all & PHYS_AUTO))
1566    return 4;
1567
1568  return 0;
1569}
1570
1571static int
1572SetRetry(int argc, char const *const *argv, u_int *timeout, u_int *maxreq,
1573          u_int *maxtrm, int def)
1574{
1575  if (argc == 0) {
1576    *timeout = DEF_FSMRETRY;
1577    *maxreq = def;
1578    if (maxtrm != NULL)
1579      *maxtrm = def;
1580  } else {
1581    long l = atol(argv[0]);
1582
1583    if (l < MIN_FSMRETRY) {
1584      log_Printf(LogWARN, "%ld: Invalid FSM retry period - min %d\n",
1585                 l, MIN_FSMRETRY);
1586      return 1;
1587    } else
1588      *timeout = l;
1589
1590    if (argc > 1) {
1591      l = atol(argv[1]);
1592      if (l < 1) {
1593        log_Printf(LogWARN, "%ld: Invalid FSM REQ tries - changed to 1\n", l);
1594        l = 1;
1595      }
1596      *maxreq = l;
1597
1598      if (argc > 2 && maxtrm != NULL) {
1599        l = atol(argv[2]);
1600        if (l < 1) {
1601          log_Printf(LogWARN, "%ld: Invalid FSM TRM tries - changed to 1\n", l);
1602          l = 1;
1603        }
1604        *maxtrm = l;
1605      }
1606    }
1607  }
1608
1609  return 0;
1610}
1611
1612static int
1613SetVariable(struct cmdargs const *arg)
1614{
1615  long long_val, param = (long)arg->cmd->args;
1616  int mode, dummyint, f, first, res;
1617  u_short *change;
1618  const char *argp;
1619  struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1620  struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1621  struct in_addr *ipaddr;
1622  struct ncpaddr ncpaddr[2];
1623
1624  if (arg->argc > arg->argn)
1625    argp = arg->argv[arg->argn];
1626  else
1627    argp = "";
1628
1629  res = 0;
1630
1631  if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1632    log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
1633              arg->cmd->name);
1634    return 1;
1635  } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1636    log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
1637              arg->cmd->name, cx->name);
1638    cx = NULL;
1639  }
1640
1641  switch (param) {
1642  case VAR_AUTHKEY:
1643    strncpy(arg->bundle->cfg.auth.key, argp,
1644            sizeof arg->bundle->cfg.auth.key - 1);
1645    arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
1646    break;
1647
1648  case VAR_AUTHNAME:
1649    switch (bundle_Phase(arg->bundle)) {
1650      default:
1651        log_Printf(LogWARN, "Altering authname while at phase %s\n",
1652                   bundle_PhaseName(arg->bundle));
1653        /* drop through */
1654      case PHASE_DEAD:
1655      case PHASE_ESTABLISH:
1656        strncpy(arg->bundle->cfg.auth.name, argp,
1657                sizeof arg->bundle->cfg.auth.name - 1);
1658        arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0';
1659        break;
1660    }
1661    break;
1662
1663  case VAR_AUTOLOAD:
1664    if (arg->argc == arg->argn + 3) {
1665      int v1, v2, v3;
1666      char *end;
1667
1668      v1 = strtol(arg->argv[arg->argn], &end, 0);
1669      if (v1 < 0 || *end) {
1670        log_Printf(LogWARN, "autoload: %s: Invalid min percentage\n",
1671                   arg->argv[arg->argn]);
1672        res = 1;
1673        break;
1674      }
1675
1676      v2 = strtol(arg->argv[arg->argn + 1], &end, 0);
1677      if (v2 < 0 || *end) {
1678        log_Printf(LogWARN, "autoload: %s: Invalid max percentage\n",
1679                   arg->argv[arg->argn + 1]);
1680        res = 1;
1681        break;
1682      }
1683      if (v2 < v1) {
1684        v3 = v1;
1685        v1 = v2;
1686        v2 = v3;
1687      }
1688
1689      v3 = strtol(arg->argv[arg->argn + 2], &end, 0);
1690      if (v3 <= 0 || *end) {
1691        log_Printf(LogWARN, "autoload: %s: Invalid throughput period\n",
1692                   arg->argv[arg->argn + 2]);
1693        res = 1;
1694        break;
1695      }
1696
1697      arg->bundle->ncp.mp.cfg.autoload.min = v1;
1698      arg->bundle->ncp.mp.cfg.autoload.max = v2;
1699      arg->bundle->ncp.mp.cfg.autoload.period = v3;
1700      mp_RestartAutoloadTimer(&arg->bundle->ncp.mp);
1701    } else {
1702      log_Printf(LogWARN, "Set autoload requires three arguments\n");
1703      res = 1;
1704    }
1705    break;
1706
1707  case VAR_DIAL:
1708    strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
1709    cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
1710    break;
1711
1712  case VAR_LOGIN:
1713    strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
1714    cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
1715    break;
1716
1717  case VAR_WINSIZE:
1718    if (arg->argc > arg->argn) {
1719      l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
1720      if (l->ccp.cfg.deflate.out.winsize < 8 ||
1721          l->ccp.cfg.deflate.out.winsize > 15) {
1722          log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
1723                    l->ccp.cfg.deflate.out.winsize);
1724          l->ccp.cfg.deflate.out.winsize = 15;
1725      }
1726      if (arg->argc > arg->argn+1) {
1727        l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
1728        if (l->ccp.cfg.deflate.in.winsize < 8 ||
1729            l->ccp.cfg.deflate.in.winsize > 15) {
1730            log_Printf(LogWARN, "%d: Invalid incoming window size\n",
1731                      l->ccp.cfg.deflate.in.winsize);
1732            l->ccp.cfg.deflate.in.winsize = 15;
1733        }
1734      } else
1735        l->ccp.cfg.deflate.in.winsize = 0;
1736    } else {
1737      log_Printf(LogWARN, "No window size specified\n");
1738      res = 1;
1739    }
1740    break;
1741
1742#ifndef NODES
1743  case VAR_MPPE:
1744    if (arg->argc > arg->argn + 2) {
1745      res = -1;
1746      break;
1747    }
1748
1749    if (arg->argc == arg->argn) {
1750      l->ccp.cfg.mppe.keybits = 0;
1751      l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1752      l->ccp.cfg.mppe.required = 0;
1753      break;
1754    }
1755
1756    if (!strcmp(argp, "*"))
1757      long_val = 0;
1758    else {
1759      long_val = atol(argp);
1760      if (long_val != 40 && long_val != 56 && long_val != 128) {
1761        log_Printf(LogWARN, "%s: Invalid bits value\n", argp);
1762        res = -1;
1763        break;
1764      }
1765    }
1766
1767    if (arg->argc == arg->argn + 2) {
1768      if (!strcmp(arg->argv[arg->argn + 1], "*"))
1769        l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1770      else if (!strcasecmp(arg->argv[arg->argn + 1], "stateless"))
1771        l->ccp.cfg.mppe.state = MPPE_STATELESS;
1772      else if (!strcasecmp(arg->argv[arg->argn + 1], "stateful"))
1773        l->ccp.cfg.mppe.state = MPPE_STATEFUL;
1774      else {
1775        log_Printf(LogWARN, "%s: Invalid state value\n",
1776                   arg->argv[arg->argn + 1]);
1777        res = -1;
1778        break;
1779      }
1780    } else
1781      l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1782    l->ccp.cfg.mppe.keybits = long_val;
1783    l->ccp.cfg.mppe.required = 1;
1784    break;
1785#endif
1786
1787  case VAR_DEVICE:
1788    physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
1789                           arg->argv + arg->argn);
1790    break;
1791
1792  case VAR_ACCMAP:
1793    if (arg->argc > arg->argn) {
1794      u_long ulong_val;
1795      sscanf(argp, "%lx", &ulong_val);
1796      cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val;
1797    } else {
1798      log_Printf(LogWARN, "No accmap specified\n");
1799      res = 1;
1800    }
1801    break;
1802
1803  case VAR_MODE:
1804    mode = Nam2mode(argp);
1805    if (mode == PHYS_NONE || mode == PHYS_ALL) {
1806      log_Printf(LogWARN, "%s: Invalid mode\n", argp);
1807      res = -1;
1808      break;
1809    }
1810    bundle_SetMode(arg->bundle, cx, mode);
1811    break;
1812
1813  case VAR_MRRU:
1814    switch (bundle_Phase(arg->bundle)) {
1815      case PHASE_DEAD:
1816        break;
1817      case PHASE_ESTABLISH:
1818        /* Make sure none of our links are DATALINK_LCP or greater */
1819        if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
1820          log_Printf(LogWARN, "mrru: Only changable before LCP negotiations\n");
1821          res = 1;
1822          break;
1823        }
1824        break;
1825      default:
1826        log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n");
1827        res = 1;
1828        break;
1829    }
1830    if (res != 0)
1831      break;
1832    long_val = atol(argp);
1833    if (long_val && long_val < MIN_MRU) {
1834      log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU);
1835      res = 1;
1836      break;
1837    } else if (long_val > MAX_MRU) {
1838      log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
1839      res = 1;
1840      break;
1841    } else
1842      arg->bundle->ncp.mp.cfg.mrru = long_val;
1843    break;
1844
1845  case VAR_MRU:
1846    long_val = 0;	/* silence gcc */
1847    change = NULL;	/* silence gcc */
1848    switch(arg->argc - arg->argn) {
1849    case 1:
1850      if (argp[strspn(argp, "0123456789")] != '\0') {
1851        res = -1;
1852        break;
1853      }
1854      /*FALLTHRU*/
1855    case 0:
1856      long_val = atol(argp);
1857      change = &l->lcp.cfg.mru;
1858      if (long_val > l->lcp.cfg.max_mru) {
1859        log_Printf(LogWARN, "MRU %ld: too large - max set to %d\n", long_val,
1860                   l->lcp.cfg.max_mru);
1861        res = 1;
1862        break;
1863      }
1864      break;
1865    case 2:
1866      if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
1867        res = -1;
1868        break;
1869      }
1870      long_val = atol(arg->argv[arg->argn + 1]);
1871      change = &l->lcp.cfg.max_mru;
1872      if (long_val > MAX_MRU) {
1873        log_Printf(LogWARN, "MRU %ld: too large - maximum is %d\n", long_val,
1874                   MAX_MRU);
1875        res = 1;
1876        break;
1877      }
1878      break;
1879    default:
1880      res = -1;
1881      break;
1882    }
1883    if (res != 0)
1884      break;
1885
1886    if (long_val == 0)
1887      *change = 0;
1888    else if (long_val < MIN_MRU) {
1889      log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
1890      res = 1;
1891      break;
1892    } else if (long_val > MAX_MRU) {
1893      log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
1894      res = 1;
1895      break;
1896    } else
1897      *change = long_val;
1898    if (l->lcp.cfg.mru > *change)
1899      l->lcp.cfg.mru = *change;
1900    break;
1901
1902  case VAR_MTU:
1903    long_val = 0;	/* silence gcc */
1904    change = NULL;	/* silence gcc */
1905    switch(arg->argc - arg->argn) {
1906    case 1:
1907      if (argp[strspn(argp, "0123456789")] != '\0') {
1908        res = -1;
1909        break;
1910      }
1911      /*FALLTHRU*/
1912    case 0:
1913      long_val = atol(argp);
1914      change = &l->lcp.cfg.mtu;
1915      if (long_val > l->lcp.cfg.max_mtu) {
1916        log_Printf(LogWARN, "MTU %ld: too large - max set to %d\n", long_val,
1917                   l->lcp.cfg.max_mtu);
1918        res = 1;
1919        break;
1920      }
1921      break;
1922    case 2:
1923      if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
1924        res = -1;
1925        break;
1926      }
1927      long_val = atol(arg->argv[arg->argn + 1]);
1928      change = &l->lcp.cfg.max_mtu;
1929      if (long_val > MAX_MTU) {
1930        log_Printf(LogWARN, "MTU %ld: too large - maximum is %d\n", long_val,
1931                   MAX_MTU);
1932        res = 1;
1933        break;
1934      }
1935      break;
1936    default:
1937      res = -1;
1938      break;
1939    }
1940
1941    if (res != 0)
1942      break;
1943
1944    if (long_val && long_val < MIN_MTU) {
1945      log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
1946      res = 1;
1947      break;
1948    } else if (long_val > MAX_MTU) {
1949      log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
1950      res = 1;
1951      break;
1952    } else
1953      *change = long_val;
1954    if (l->lcp.cfg.mtu > *change)
1955      l->lcp.cfg.mtu = *change;
1956    break;
1957
1958  case VAR_OPENMODE:
1959    if (strcasecmp(argp, "active") == 0)
1960      cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
1961        atoi(arg->argv[arg->argn+1]) : 1;
1962    else if (strcasecmp(argp, "passive") == 0)
1963      cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
1964    else {
1965      log_Printf(LogWARN, "%s: Invalid openmode\n", argp);
1966      res = 1;
1967    }
1968    break;
1969
1970  case VAR_PHONE:
1971    strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
1972    cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
1973    cx->phone.alt = cx->phone.next = NULL;
1974    break;
1975
1976  case VAR_HANGUP:
1977    strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
1978    cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
1979    break;
1980
1981  case VAR_IFQUEUE:
1982    long_val = atol(argp);
1983    arg->bundle->cfg.ifqueue = long_val < 0 ? 0 : long_val;
1984    break;
1985
1986  case VAR_LOGOUT:
1987    strncpy(cx->cfg.script.logout, argp, sizeof cx->cfg.script.logout - 1);
1988    cx->cfg.script.logout[sizeof cx->cfg.script.logout - 1] = '\0';
1989    break;
1990
1991  case VAR_IDLETIMEOUT:
1992    if (arg->argc > arg->argn+2) {
1993      log_Printf(LogWARN, "Too many idle timeout values\n");
1994      res = 1;
1995    } else if (arg->argc == arg->argn) {
1996      log_Printf(LogWARN, "Too few idle timeout values\n");
1997      res = 1;
1998    } else {
1999      int timeout, min;
2000
2001      timeout = atoi(argp);
2002      min = arg->argc == arg->argn + 2 ? atoi(arg->argv[arg->argn + 1]) : -1;
2003      bundle_SetIdleTimer(arg->bundle, timeout, min);
2004    }
2005    break;
2006
2007  case VAR_LQRPERIOD:
2008    long_val = atol(argp);
2009    if (long_val < MIN_LQRPERIOD) {
2010      log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n",
2011                 long_val, MIN_LQRPERIOD);
2012      res = 1;
2013    } else
2014      l->lcp.cfg.lqrperiod = long_val;
2015    break;
2016
2017  case VAR_LCPRETRY:
2018    res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2019                   &cx->physical->link.lcp.cfg.fsm.timeout,
2020                   &cx->physical->link.lcp.cfg.fsm.maxreq,
2021                   &cx->physical->link.lcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2022    break;
2023
2024  case VAR_CHAPRETRY:
2025    res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2026                   &cx->chap.auth.cfg.fsm.timeout,
2027                   &cx->chap.auth.cfg.fsm.maxreq, NULL, DEF_FSMAUTHTRIES);
2028    break;
2029
2030  case VAR_PAPRETRY:
2031    res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2032                   &cx->pap.cfg.fsm.timeout, &cx->pap.cfg.fsm.maxreq,
2033                   NULL, DEF_FSMAUTHTRIES);
2034    break;
2035
2036  case VAR_CCPRETRY:
2037    res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2038                   &l->ccp.cfg.fsm.timeout, &l->ccp.cfg.fsm.maxreq,
2039                   &l->ccp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2040    break;
2041
2042  case VAR_IPCPRETRY:
2043    res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2044                   &arg->bundle->ncp.ipcp.cfg.fsm.timeout,
2045                   &arg->bundle->ncp.ipcp.cfg.fsm.maxreq,
2046                   &arg->bundle->ncp.ipcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2047    break;
2048
2049  case VAR_NBNS:
2050  case VAR_DNS:
2051    if (param == VAR_DNS) {
2052      ipaddr = arg->bundle->ncp.ipcp.cfg.ns.dns;
2053      ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_NONE;
2054    } else {
2055      ipaddr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
2056      ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_ANY;
2057    }
2058
2059    if (arg->argc > arg->argn) {
2060      ncpaddr_aton(ncpaddr, &arg->bundle->ncp, arg->argv[arg->argn]);
2061      if (!ncpaddr_getip4(ncpaddr, ipaddr))
2062        return -1;
2063      if (arg->argc > arg->argn+1) {
2064        ncpaddr_aton(ncpaddr + 1, &arg->bundle->ncp, arg->argv[arg->argn + 1]);
2065        if (!ncpaddr_getip4(ncpaddr + 1, ipaddr + 1))
2066          return -1;
2067      }
2068
2069      if (ipaddr[0].s_addr == INADDR_ANY) {
2070        ipaddr[0] = ipaddr[1];
2071        ipaddr[1].s_addr = INADDR_ANY;
2072      }
2073      if (ipaddr[0].s_addr == INADDR_NONE) {
2074        ipaddr[0] = ipaddr[1];
2075        ipaddr[1].s_addr = INADDR_NONE;
2076      }
2077    }
2078    break;
2079
2080  case VAR_CALLBACK:
2081    cx->cfg.callback.opmask = 0;
2082    for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) {
2083      if (!strcasecmp(arg->argv[dummyint], "auth"))
2084        cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH);
2085      else if (!strcasecmp(arg->argv[dummyint], "cbcp"))
2086        cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP);
2087      else if (!strcasecmp(arg->argv[dummyint], "e.164")) {
2088        if (dummyint == arg->argc - 1)
2089          log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n");
2090        else {
2091          cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164);
2092          strncpy(cx->cfg.callback.msg, arg->argv[++dummyint],
2093                  sizeof cx->cfg.callback.msg - 1);
2094          cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0';
2095        }
2096      } else if (!strcasecmp(arg->argv[dummyint], "none"))
2097        cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE);
2098      else {
2099        res = -1;
2100        break;
2101      }
2102    }
2103    if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE))
2104      cx->cfg.callback.opmask = 0;
2105    break;
2106
2107  case VAR_CBCP:
2108    cx->cfg.cbcp.delay = 0;
2109    *cx->cfg.cbcp.phone = '\0';
2110    cx->cfg.cbcp.fsmretry = DEF_FSMRETRY;
2111    if (arg->argc > arg->argn) {
2112      strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn],
2113              sizeof cx->cfg.cbcp.phone - 1);
2114      cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0';
2115      if (arg->argc > arg->argn + 1) {
2116        cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]);
2117        if (arg->argc > arg->argn + 2) {
2118          long_val = atol(arg->argv[arg->argn + 2]);
2119          if (long_val < MIN_FSMRETRY)
2120            log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n",
2121                       long_val, MIN_FSMRETRY);
2122          else
2123            cx->cfg.cbcp.fsmretry = long_val;
2124        }
2125      }
2126    }
2127    break;
2128
2129  case VAR_CHOKED:
2130    arg->bundle->cfg.choked.timeout = atoi(argp);
2131    if (arg->bundle->cfg.choked.timeout <= 0)
2132      arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT;
2133    break;
2134
2135  case VAR_SENDPIPE:
2136    long_val = atol(argp);
2137    arg->bundle->ncp.cfg.sendpipe = long_val;
2138    break;
2139
2140  case VAR_RECVPIPE:
2141    long_val = atol(argp);
2142    arg->bundle->ncp.cfg.recvpipe = long_val;
2143    break;
2144
2145#ifndef NORADIUS
2146  case VAR_RADIUS:
2147    if (!*argp)
2148      *arg->bundle->radius.cfg.file = '\0';
2149    else if (access(argp, R_OK)) {
2150      log_Printf(LogWARN, "%s: %s\n", argp, strerror(errno));
2151      res = 1;
2152      break;
2153    } else {
2154      strncpy(arg->bundle->radius.cfg.file, argp,
2155              sizeof arg->bundle->radius.cfg.file - 1);
2156      arg->bundle->radius.cfg.file
2157        [sizeof arg->bundle->radius.cfg.file - 1] = '\0';
2158    }
2159    break;
2160#endif
2161
2162  case VAR_CD:
2163    if (*argp) {
2164      if (strcasecmp(argp, "off")) {
2165        long_val = atol(argp);
2166        if (long_val < 0)
2167          long_val = 0;
2168        cx->physical->cfg.cd.delay = long_val;
2169        cx->physical->cfg.cd.necessity = argp[strlen(argp)-1] == '!' ?
2170          CD_REQUIRED : CD_VARIABLE;
2171      } else
2172        cx->physical->cfg.cd.necessity = CD_NOTREQUIRED;
2173    } else {
2174      cx->physical->cfg.cd.delay = 0;
2175      cx->physical->cfg.cd.necessity = CD_DEFAULT;
2176    }
2177    break;
2178
2179  case VAR_PARITY:
2180    if (arg->argc == arg->argn + 1)
2181      res = physical_SetParity(arg->cx->physical, argp);
2182    else {
2183      log_Printf(LogWARN, "Parity value must be odd, even or none\n");
2184      res = 1;
2185    }
2186    break;
2187
2188  case VAR_CRTSCTS:
2189    if (strcasecmp(argp, "on") == 0)
2190      physical_SetRtsCts(arg->cx->physical, 1);
2191    else if (strcasecmp(argp, "off") == 0)
2192      physical_SetRtsCts(arg->cx->physical, 0);
2193    else {
2194      log_Printf(LogWARN, "RTS/CTS value must be on or off\n");
2195      res = 1;
2196    }
2197    break;
2198
2199  case VAR_URGENTPORTS:
2200    if (arg->argn == arg->argc) {
2201      ncp_SetUrgentTOS(&arg->bundle->ncp);
2202      ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2203      ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2204    } else if (!strcasecmp(arg->argv[arg->argn], "udp")) {
2205      ncp_SetUrgentTOS(&arg->bundle->ncp);
2206      if (arg->argn == arg->argc - 1)
2207        ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2208      else for (f = arg->argn + 1; f < arg->argc; f++)
2209        if (*arg->argv[f] == '+')
2210          ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2211        else if (*arg->argv[f] == '-')
2212          ncp_RemoveUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2213        else {
2214          if (f == arg->argn)
2215            ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2216          ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
2217        }
2218    } else if (arg->argn == arg->argc - 1 &&
2219               !strcasecmp(arg->argv[arg->argn], "none")) {
2220      ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2221      ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2222      ncp_ClearUrgentTOS(&arg->bundle->ncp);
2223    } else {
2224      ncp_SetUrgentTOS(&arg->bundle->ncp);
2225      first = arg->argn;
2226      if (!strcasecmp(arg->argv[first], "tcp") && ++first == arg->argc)
2227        ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2228
2229      for (f = first; f < arg->argc; f++)
2230        if (*arg->argv[f] == '+')
2231          ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2232        else if (*arg->argv[f] == '-')
2233          ncp_RemoveUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2234        else {
2235          if (f == first)
2236            ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2237          ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
2238        }
2239    }
2240    break;
2241  }
2242
2243  return res;
2244}
2245
2246static struct cmdtab const SetCommands[] = {
2247  {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2248  "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
2249  {"authkey", "key", SetVariable, LOCAL_AUTH,
2250  "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
2251  {"authname", NULL, SetVariable, LOCAL_AUTH,
2252  "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
2253  {"autoload", NULL, SetVariable, LOCAL_AUTH,
2254  "auto link [de]activation", "set autoload maxtime maxload mintime minload",
2255  (const void *)VAR_AUTOLOAD},
2256  {"bandwidth", NULL, mp_SetDatalinkBandwidth, LOCAL_AUTH | LOCAL_CX,
2257  "datalink bandwidth", "set bandwidth value"},
2258  {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2259  "callback control", "set callback [none|auth|cbcp|"
2260  "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK},
2261  {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2262  "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]",
2263  (const void *)VAR_CBCP},
2264  {"ccpretry", "ccpretries", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2265   "CCP retries", "set ccpretry value [attempts]", (const void *)VAR_CCPRETRY},
2266  {"cd", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Carrier delay requirement",
2267   "set cd value[!]", (const void *)VAR_CD},
2268  {"chapretry", "chapretries", SetVariable, LOCAL_AUTH | LOCAL_CX,
2269   "CHAP retries", "set chapretry value [attempts]",
2270   (const void *)VAR_CHAPRETRY},
2271  {"choked", NULL, SetVariable, LOCAL_AUTH,
2272  "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED},
2273  {"ctsrts", "crtscts", SetVariable, LOCAL_AUTH | LOCAL_CX,
2274   "Use hardware flow control", "set ctsrts [on|off]",
2275   (const char *)VAR_CRTSCTS},
2276  {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2277  "deflate window sizes", "set deflate out-winsize in-winsize",
2278  (const void *) VAR_WINSIZE},
2279#ifndef NODES
2280  {"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2281  "MPPE key size and state", "set mppe [40|56|128|* [stateful|stateless|*]]",
2282  (const void *) VAR_MPPE},
2283#endif
2284  {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
2285  "physical device name", "set device|line device-name[,device-name]",
2286  (const void *) VAR_DEVICE},
2287  {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2288  "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
2289  {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
2290  "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
2291  {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
2292  "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]"},
2293  {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
2294  "escape characters", "set escape hex-digit ..."},
2295  {"filter", NULL, filter_Set, LOCAL_AUTH,
2296  "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
2297  "[src_addr[/width]] [dst_addr[/width]] [proto "
2298  "[src [lt|eq|gt port]] [dst [lt|eq|gt port]] [estab] [syn] [finrst]]"},
2299  {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2300  "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
2301  {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
2302  "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
2303  {"ifqueue", NULL, SetVariable, LOCAL_AUTH, "interface queue",
2304  "set ifqueue packets", (const void *)VAR_IFQUEUE},
2305  {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries",
2306   "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY},
2307  {"lcpretry", "lcpretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "LCP retries",
2308   "set lcpretry value [attempts]", (const void *)VAR_LCPRETRY},
2309  {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
2310  "set log [local] [+|-]all|async|cbcp|ccp|chat|command|connect|debug|dns|hdlc|"
2311  "id0|ipcp|lcp|lqm|phase|physical|sync|tcp/ip|timer|tun..."},
2312  {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2313  "login script", "set login chat-script", (const void *) VAR_LOGIN},
2314  {"logout", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2315  "logout script", "set logout chat-script", (const void *) VAR_LOGOUT},
2316  {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2317  "LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
2318  {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
2319  "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
2320  {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
2321  "set mrru value", (const void *)VAR_MRRU},
2322  {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2323  "MRU value", "set mru [max[imum]] [value]", (const void *)VAR_MRU},
2324  {"mtu", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2325  "interface MTU value", "set mtu [max[imum]] [value]", (const void *)VAR_MTU},
2326  {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
2327  "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
2328  {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
2329  "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
2330  {"papretry", "papretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "PAP retries",
2331   "set papretry value [attempts]", (const void *)VAR_PAPRETRY},
2332  {"parity", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "serial parity",
2333   "set parity [odd|even|none]", (const void *)VAR_PARITY},
2334  {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
2335  "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
2336  {"proctitle", "title", SetProcTitle, LOCAL_AUTH,
2337  "Process title", "set proctitle [value]"},
2338#ifndef NORADIUS
2339  {"radius", NULL, SetVariable, LOCAL_AUTH,
2340  "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS},
2341#endif
2342  {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
2343  "Reconnect timeout", "set reconnect value ntries"},
2344  {"recvpipe", NULL, SetVariable, LOCAL_AUTH,
2345  "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE},
2346  {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
2347  "Redial timeout", "set redial secs[+inc[-incmax]][.next] [attempts]"},
2348  {"sendpipe", NULL, SetVariable, LOCAL_AUTH,
2349  "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE},
2350  {"server", "socket", SetServer, LOCAL_AUTH, "diagnostic port",
2351  "set server|socket TcpPort|LocalName|none|open|closed [password [mask]]"},
2352  {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
2353  "physical speed", "set speed value|sync"},
2354  {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
2355  "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
2356  {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
2357  "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
2358  {"urgent", NULL, SetVariable, LOCAL_AUTH, "urgent ports",
2359  "set urgent [tcp|udp] [+|-]port...", (const void *)VAR_URGENTPORTS},
2360  {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
2361  "vj values", "set vj slots|slotcomp [value]"},
2362  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
2363  "Display this message", "set help|? [command]", SetCommands},
2364  {NULL, NULL, NULL},
2365};
2366
2367static int
2368SetCommand(struct cmdargs const *arg)
2369{
2370  if (arg->argc > arg->argn)
2371    FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
2372             arg->prompt, arg->cx);
2373  else if (arg->prompt)
2374    prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
2375	          " syntax help.\n");
2376  else
2377    log_Printf(LogWARN, "set command must have arguments\n");
2378
2379  return 0;
2380}
2381
2382static int
2383AddCommand(struct cmdargs const *arg)
2384{
2385  struct ncpaddr gw;
2386  struct ncprange dest;
2387  struct in_addr host;
2388  int dest_default, gw_arg, addrs;
2389
2390  if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
2391    return -1;
2392
2393  addrs = 0;
2394  dest_default = 0;
2395  if (arg->argc == arg->argn + 2) {
2396    if (!strcasecmp(arg->argv[arg->argn], "default"))
2397      dest_default = 1;
2398    else {
2399      if (!ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]))
2400        return -1;
2401      if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
2402        addrs = ROUTE_DSTMYADDR;
2403      else if (!strncasecmp(arg->argv[arg->argn], "MYADDR6", 7))
2404        addrs = ROUTE_DSTMYADDR6;
2405      else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
2406        addrs = ROUTE_DSTHISADDR;
2407      else if (!strncasecmp(arg->argv[arg->argn], "HISADDR6", 8))
2408        addrs = ROUTE_DSTHISADDR6;
2409      else if (!strncasecmp(arg->argv[arg->argn], "DNS0", 4))
2410        addrs = ROUTE_DSTDNS0;
2411      else if (!strncasecmp(arg->argv[arg->argn], "DNS1", 4))
2412        addrs = ROUTE_DSTDNS1;
2413    }
2414    gw_arg = 1;
2415  } else {
2416    if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
2417      addrs = ROUTE_DSTMYADDR;
2418      host = arg->bundle->ncp.ipcp.my_ip;
2419    } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
2420      addrs = ROUTE_DSTHISADDR;
2421      host = arg->bundle->ncp.ipcp.peer_ip;
2422    } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
2423      addrs = ROUTE_DSTDNS0;
2424      host = arg->bundle->ncp.ipcp.ns.dns[0];
2425    } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
2426      addrs = ROUTE_DSTDNS1;
2427      host = arg->bundle->ncp.ipcp.ns.dns[1];
2428    } else {
2429      host = GetIpAddr(arg->argv[arg->argn]);
2430      if (host.s_addr == INADDR_NONE) {
2431        log_Printf(LogWARN, "%s: Invalid destination address\n",
2432                   arg->argv[arg->argn]);
2433        return -1;
2434      }
2435    }
2436    ncprange_setip4(&dest, host, GetIpAddr(arg->argv[arg->argn + 1]));
2437    gw_arg = 2;
2438  }
2439
2440  if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR") == 0) {
2441    ncpaddr_setip4(&gw, arg->bundle->ncp.ipcp.peer_ip);
2442    addrs |= ROUTE_GWHISADDR;
2443#ifndef NOINET6
2444  } else if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR6") == 0) {
2445    ncpaddr_copy(&gw, &arg->bundle->ncp.ipv6cp.hisaddr);
2446    addrs |= ROUTE_GWHISADDR6;
2447#endif
2448  } else {
2449    if (!ncpaddr_aton(&gw, &arg->bundle->ncp, arg->argv[arg->argn + gw_arg])) {
2450      log_Printf(LogWARN, "%s: Invalid gateway address\n",
2451                 arg->argv[arg->argn + gw_arg]);
2452      return -1;
2453    }
2454  }
2455
2456  if (dest_default)
2457    ncprange_setdefault(&dest, ncpaddr_family(&gw));
2458
2459  if (rt_Set(arg->bundle, RTM_ADD, &dest, &gw, arg->cmd->args ? 1 : 0,
2460             ((addrs & ROUTE_GWHISADDR) || (addrs & ROUTE_GWHISADDR6)) ? 1 : 0)
2461      && addrs != ROUTE_STATIC)
2462    route_Add(&arg->bundle->ncp.route, addrs, &dest, &gw);
2463
2464  return 0;
2465}
2466
2467static int
2468DeleteCommand(struct cmdargs const *arg)
2469{
2470  struct ncprange dest;
2471  int addrs;
2472
2473  if (arg->argc == arg->argn+1) {
2474    if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
2475      route_IfDelete(arg->bundle, 0);
2476      route_DeleteAll(&arg->bundle->ncp.route);
2477    } else {
2478      addrs = 0;
2479      if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
2480        ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.my_ip);
2481        addrs = ROUTE_DSTMYADDR;
2482#ifndef NOINET6
2483      } else if (strcasecmp(arg->argv[arg->argn], "MYADDR6") == 0) {
2484        ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.myaddr);
2485        addrs = ROUTE_DSTMYADDR6;
2486#endif
2487      } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
2488        ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.peer_ip);
2489        addrs = ROUTE_DSTHISADDR;
2490#ifndef NOINET6
2491      } else if (strcasecmp(arg->argv[arg->argn], "HISADDR6") == 0) {
2492        ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.hisaddr);
2493        addrs = ROUTE_DSTHISADDR6;
2494#endif
2495      } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
2496        ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[0]);
2497        addrs = ROUTE_DSTDNS0;
2498      } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
2499        ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[1]);
2500        addrs = ROUTE_DSTDNS1;
2501      } else {
2502        ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]);
2503        addrs = ROUTE_STATIC;
2504      }
2505      rt_Set(arg->bundle, RTM_DELETE, &dest, NULL, arg->cmd->args ? 1 : 0, 0);
2506      route_Delete(&arg->bundle->ncp.route, addrs, &dest);
2507    }
2508  } else
2509    return -1;
2510
2511  return 0;
2512}
2513
2514#ifndef NONAT
2515static int
2516NatEnable(struct cmdargs const *arg)
2517{
2518  if (arg->argc == arg->argn+1) {
2519    if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
2520      if (!arg->bundle->NatEnabled) {
2521        if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
2522          PacketAliasSetAddress(arg->bundle->ncp.ipcp.my_ip);
2523        arg->bundle->NatEnabled = 1;
2524      }
2525      return 0;
2526    } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
2527      arg->bundle->NatEnabled = 0;
2528      arg->bundle->cfg.opt &= ~OPT_IFACEALIAS;
2529      /* Don't iface_Clear() - there may be manually configured addresses */
2530      return 0;
2531    }
2532  }
2533
2534  return -1;
2535}
2536
2537
2538static int
2539NatOption(struct cmdargs const *arg)
2540{
2541  long param = (long)arg->cmd->args;
2542
2543  if (arg->argc == arg->argn+1) {
2544    if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
2545      if (arg->bundle->NatEnabled) {
2546	PacketAliasSetMode(param, param);
2547	return 0;
2548      }
2549      log_Printf(LogWARN, "nat not enabled\n");
2550    } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
2551      if (arg->bundle->NatEnabled) {
2552	PacketAliasSetMode(0, param);
2553	return 0;
2554      }
2555      log_Printf(LogWARN, "nat not enabled\n");
2556    }
2557  }
2558  return -1;
2559}
2560#endif /* #ifndef NONAT */
2561
2562static int
2563LinkCommand(struct cmdargs const *arg)
2564{
2565  if (arg->argc > arg->argn+1) {
2566    char namelist[LINE_LEN];
2567    struct datalink *cx;
2568    char *name;
2569    int result = 0;
2570
2571    if (!strcmp(arg->argv[arg->argn], "*")) {
2572      struct datalink *dl;
2573
2574      cx = arg->bundle->links;
2575      while (cx) {
2576        /* Watch it, the command could be a ``remove'' */
2577        dl = cx->next;
2578        FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
2579                 arg->prompt, cx);
2580        for (cx = arg->bundle->links; cx; cx = cx->next)
2581          if (cx == dl)
2582            break;		/* Pointer's still valid ! */
2583      }
2584    } else {
2585      strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
2586      namelist[sizeof namelist - 1] = '\0';
2587      for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
2588        if (!bundle2datalink(arg->bundle, name)) {
2589          log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
2590          return 1;
2591        }
2592
2593      strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
2594      namelist[sizeof namelist - 1] = '\0';
2595      for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
2596        cx = bundle2datalink(arg->bundle, name);
2597        if (cx)
2598          FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
2599                   arg->prompt, cx);
2600        else {
2601          log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
2602          result++;
2603        }
2604      }
2605    }
2606    return result;
2607  }
2608
2609  log_Printf(LogWARN, "usage: %s\n", arg->cmd->syntax);
2610  return 2;
2611}
2612
2613struct link *
2614command_ChooseLink(struct cmdargs const *arg)
2615{
2616  if (arg->cx)
2617    return &arg->cx->physical->link;
2618  else if (!arg->bundle->ncp.mp.cfg.mrru) {
2619    struct datalink *dl = bundle2datalink(arg->bundle, NULL);
2620    if (dl)
2621      return &dl->physical->link;
2622  }
2623  return &arg->bundle->ncp.mp.link;
2624}
2625
2626static const char *
2627ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
2628{
2629  const char *result;
2630
2631  switch (*cmd) {
2632    case 'A':
2633    case 'a':
2634      result = "accept";
2635      *keep = NEG_MYMASK;
2636      *add = NEG_ACCEPTED;
2637      break;
2638    case 'D':
2639    case 'd':
2640      switch (cmd[1]) {
2641        case 'E':
2642        case 'e':
2643          result = "deny";
2644          *keep = NEG_MYMASK;
2645          *add = 0;
2646          break;
2647        case 'I':
2648        case 'i':
2649          result = "disable";
2650          *keep = NEG_HISMASK;
2651          *add = 0;
2652          break;
2653        default:
2654          return NULL;
2655      }
2656      break;
2657    case 'E':
2658    case 'e':
2659      result = "enable";
2660      *keep = NEG_HISMASK;
2661      *add = NEG_ENABLED;
2662      break;
2663    default:
2664      return NULL;
2665  }
2666
2667  return result;
2668}
2669
2670static int
2671OptSet(struct cmdargs const *arg)
2672{
2673  int bit = (int)(long)arg->cmd->args;
2674  unsigned keep;			/* Keep these bits */
2675  unsigned add;				/* Add these bits */
2676
2677  if (ident_cmd(arg->argv[arg->argn - 2], &keep, &add) == NULL)
2678    return 1;
2679
2680#ifndef NOINET6
2681  if (add == NEG_ENABLED && bit == OPT_IPV6CP && !probe.ipv6_available) {
2682    log_Printf(LogWARN, "IPv6 is not available on this machine\n");
2683    return 1;
2684  }
2685#endif
2686
2687  if (add)
2688    arg->bundle->cfg.opt |= bit;
2689  else
2690    arg->bundle->cfg.opt &= ~bit;
2691
2692  return 0;
2693}
2694
2695static int
2696IfaceAliasOptSet(struct cmdargs const *arg)
2697{
2698  unsigned save = arg->bundle->cfg.opt;
2699  int result = OptSet(arg);
2700
2701  if (result == 0)
2702    if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->NatEnabled) {
2703      arg->bundle->cfg.opt = save;
2704      log_Printf(LogWARN, "Cannot enable iface-alias without NAT\n");
2705      result = 2;
2706    }
2707
2708  return result;
2709}
2710
2711static int
2712NegotiateSet(struct cmdargs const *arg)
2713{
2714  long param = (long)arg->cmd->args;
2715  struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
2716  struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
2717  const char *cmd;
2718  unsigned keep;			/* Keep these bits */
2719  unsigned add;				/* Add these bits */
2720
2721  if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
2722    return 1;
2723
2724  if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
2725    log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
2726              cmd, arg->cmd->name);
2727    return 2;
2728  } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
2729    log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
2730              cmd, arg->cmd->name, cx->name);
2731    cx = NULL;
2732  }
2733
2734  switch (param) {
2735    case NEG_ACFCOMP:
2736      cx->physical->link.lcp.cfg.acfcomp &= keep;
2737      cx->physical->link.lcp.cfg.acfcomp |= add;
2738      break;
2739    case NEG_CHAP05:
2740      cx->physical->link.lcp.cfg.chap05 &= keep;
2741      cx->physical->link.lcp.cfg.chap05 |= add;
2742      break;
2743#ifndef NODES
2744    case NEG_CHAP80:
2745      cx->physical->link.lcp.cfg.chap80nt &= keep;
2746      cx->physical->link.lcp.cfg.chap80nt |= add;
2747      break;
2748    case NEG_CHAP80LM:
2749      cx->physical->link.lcp.cfg.chap80lm &= keep;
2750      cx->physical->link.lcp.cfg.chap80lm |= add;
2751      break;
2752    case NEG_CHAP81:
2753      cx->physical->link.lcp.cfg.chap81 &= keep;
2754      cx->physical->link.lcp.cfg.chap81 |= add;
2755      break;
2756    case NEG_MPPE:
2757      l->ccp.cfg.neg[CCP_NEG_MPPE] &= keep;
2758      l->ccp.cfg.neg[CCP_NEG_MPPE] |= add;
2759      break;
2760#endif
2761    case NEG_DEFLATE:
2762      l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
2763      l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
2764      break;
2765    case NEG_DNS:
2766      arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
2767      arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
2768      break;
2769    case NEG_ENDDISC:
2770      arg->bundle->ncp.mp.cfg.negenddisc &= keep;
2771      arg->bundle->ncp.mp.cfg.negenddisc |= add;
2772      break;
2773    case NEG_LQR:
2774      cx->physical->link.lcp.cfg.lqr &= keep;
2775      cx->physical->link.lcp.cfg.lqr |= add;
2776      break;
2777    case NEG_PAP:
2778      cx->physical->link.lcp.cfg.pap &= keep;
2779      cx->physical->link.lcp.cfg.pap |= add;
2780      break;
2781    case NEG_PPPDDEFLATE:
2782      l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
2783      l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
2784      break;
2785    case NEG_PRED1:
2786      l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
2787      l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
2788      break;
2789    case NEG_PROTOCOMP:
2790      cx->physical->link.lcp.cfg.protocomp &= keep;
2791      cx->physical->link.lcp.cfg.protocomp |= add;
2792      break;
2793    case NEG_SHORTSEQ:
2794      switch (bundle_Phase(arg->bundle)) {
2795        case PHASE_DEAD:
2796          break;
2797        case PHASE_ESTABLISH:
2798          /* Make sure none of our links are DATALINK_LCP or greater */
2799          if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
2800            log_Printf(LogWARN, "shortseq: Only changable before"
2801                       " LCP negotiations\n");
2802            return 1;
2803          }
2804          break;
2805        default:
2806          log_Printf(LogWARN, "shortseq: Only changable at phase"
2807                     " DEAD/ESTABLISH\n");
2808          return 1;
2809      }
2810      arg->bundle->ncp.mp.cfg.shortseq &= keep;
2811      arg->bundle->ncp.mp.cfg.shortseq |= add;
2812      break;
2813    case NEG_VJCOMP:
2814      arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
2815      arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
2816      break;
2817  }
2818
2819  return 0;
2820}
2821
2822static struct cmdtab const NegotiateCommands[] = {
2823  {"filter-decapsulation", NULL, OptSet, LOCAL_AUTH,
2824  "filter on PPPoUDP payloads", "disable|enable",
2825  (const void *)OPT_FILTERDECAP},
2826  {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
2827  "disable|enable", (const void *)OPT_IDCHECK},
2828  {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH,
2829  "retain interface addresses", "disable|enable",
2830  (const void *)OPT_IFACEALIAS},
2831#ifndef NOINET6
2832  {"ipcp", NULL, OptSet, LOCAL_AUTH, "IP Network Control Protocol",
2833  "disable|enable", (const void *)OPT_IPCP},
2834  {"ipv6cp", NULL, OptSet, LOCAL_AUTH, "IPv6 Network Control Protocol",
2835  "disable|enable", (const void *)OPT_IPV6CP},
2836#endif
2837  {"keep-session", NULL, OptSet, LOCAL_AUTH, "Retain device session leader",
2838  "disable|enable", (const void *)OPT_KEEPSESSION},
2839  {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
2840  "disable|enable", (const void *)OPT_LOOPBACK},
2841  {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
2842  "disable|enable", (const void *)OPT_PASSWDAUTH},
2843  {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry",
2844  "disable|enable", (const void *)OPT_PROXY},
2845  {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts",
2846  "disable|enable", (const void *)OPT_PROXYALL},
2847  {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
2848  "disable|enable", (const void *)OPT_SROUTES},
2849  {"tcpmssfixup", "mssfixup", OptSet, LOCAL_AUTH, "Modify MSS options",
2850  "disable|enable", (const void *)OPT_TCPMSSFIXUP},
2851  {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
2852  "disable|enable", (const void *)OPT_THROUGHPUT},
2853  {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
2854  "disable|enable", (const void *)OPT_UTMP},
2855
2856#ifndef NOINET6
2857#define OPT_MAX 13	/* accept/deny allowed below and not above */
2858#else
2859#define OPT_MAX 11
2860#endif
2861
2862  {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2863  "Address & Control field compression", "accept|deny|disable|enable",
2864  (const void *)NEG_ACFCOMP},
2865  {"chap", "chap05", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2866  "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
2867  (const void *)NEG_CHAP05},
2868#ifndef NODES
2869  {"mschap", "chap80nt", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2870  "Microsoft (NT) CHAP", "accept|deny|disable|enable",
2871  (const void *)NEG_CHAP80},
2872  {"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2873  "Microsoft (NT) CHAP", "accept|deny|disable|enable",
2874  (const void *)NEG_CHAP80LM},
2875  {"mschapv2", "chap81", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2876  "Microsoft CHAP v2", "accept|deny|disable|enable",
2877  (const void *)NEG_CHAP81},
2878  {"mppe", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2879  "MPPE encryption", "accept|deny|disable|enable",
2880  (const void *)NEG_MPPE},
2881#endif
2882  {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2883  "Deflate compression", "accept|deny|disable|enable",
2884  (const void *)NEG_DEFLATE},
2885  {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2886  "Deflate (type 24) compression", "accept|deny|disable|enable",
2887  (const void *)NEG_PPPDDEFLATE},
2888  {"dns", NULL, NegotiateSet, LOCAL_AUTH,
2889  "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
2890  {"enddisc", NULL, NegotiateSet, LOCAL_AUTH, "ENDDISC negotiation",
2891  "accept|deny|disable|enable", (const void *)NEG_ENDDISC},
2892  {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2893  "Link Quality Reports", "accept|deny|disable|enable",
2894  (const void *)NEG_LQR},
2895  {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2896  "Password Authentication protocol", "accept|deny|disable|enable",
2897  (const void *)NEG_PAP},
2898  {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2899  "Predictor 1 compression", "accept|deny|disable|enable",
2900  (const void *)NEG_PRED1},
2901  {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2902  "Protocol field compression", "accept|deny|disable|enable",
2903  (const void *)NEG_PROTOCOMP},
2904  {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
2905  "MP Short Sequence Numbers", "accept|deny|disable|enable",
2906  (const void *)NEG_SHORTSEQ},
2907  {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
2908  "Van Jacobson header compression", "accept|deny|disable|enable",
2909  (const void *)NEG_VJCOMP},
2910  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
2911  "Display this message", "accept|deny|disable|enable help|? [value]",
2912  NegotiateCommands},
2913  {NULL, NULL, NULL},
2914};
2915
2916static int
2917NegotiateCommand(struct cmdargs const *arg)
2918{
2919  if (arg->argc > arg->argn) {
2920    char const *argv[3];
2921    unsigned keep, add;
2922    int n;
2923
2924    if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
2925      return -1;
2926    argv[2] = NULL;
2927
2928    for (n = arg->argn; n < arg->argc; n++) {
2929      argv[1] = arg->argv[n];
2930      FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
2931               0 : OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
2932    }
2933  } else if (arg->prompt)
2934    prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
2935	    arg->argv[arg->argn-1]);
2936  else
2937    log_Printf(LogWARN, "%s command must have arguments\n",
2938              arg->argv[arg->argn] );
2939
2940  return 0;
2941}
2942
2943const char *
2944command_ShowNegval(unsigned val)
2945{
2946  switch (val&3) {
2947    case 1: return "disabled & accepted";
2948    case 2: return "enabled & denied";
2949    case 3: return "enabled & accepted";
2950  }
2951  return "disabled & denied";
2952}
2953
2954static int
2955ClearCommand(struct cmdargs const *arg)
2956{
2957  struct pppThroughput *t;
2958  struct datalink *cx;
2959  int i, clear_type;
2960
2961  if (arg->argc < arg->argn + 1)
2962    return -1;
2963
2964  if (strcasecmp(arg->argv[arg->argn], "physical") == 0) {
2965    cx = arg->cx;
2966    if (!cx)
2967      cx = bundle2datalink(arg->bundle, NULL);
2968    if (!cx) {
2969      log_Printf(LogWARN, "A link must be specified for ``clear physical''\n");
2970      return 1;
2971    }
2972    t = &cx->physical->link.stats.total;
2973  } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
2974    t = &arg->bundle->ncp.ipcp.throughput;
2975#ifndef NOINET6
2976  else if (strcasecmp(arg->argv[arg->argn], "ipv6cp") == 0)
2977    t = &arg->bundle->ncp.ipv6cp.throughput;
2978#endif
2979  else
2980    return -1;
2981
2982  if (arg->argc > arg->argn + 1) {
2983    clear_type = 0;
2984    for (i = arg->argn + 1; i < arg->argc; i++)
2985      if (strcasecmp(arg->argv[i], "overall") == 0)
2986        clear_type |= THROUGHPUT_OVERALL;
2987      else if (strcasecmp(arg->argv[i], "current") == 0)
2988        clear_type |= THROUGHPUT_CURRENT;
2989      else if (strcasecmp(arg->argv[i], "peak") == 0)
2990        clear_type |= THROUGHPUT_PEAK;
2991      else
2992        return -1;
2993  } else
2994    clear_type = THROUGHPUT_ALL;
2995
2996  throughput_clear(t, clear_type, arg->prompt);
2997  return 0;
2998}
2999
3000static int
3001RunListCommand(struct cmdargs const *arg)
3002{
3003  const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???";
3004
3005#ifndef NONAT
3006  if (arg->cmd->args == NatCommands &&
3007      tolower(*arg->argv[arg->argn - 1]) == 'a') {
3008    if (arg->prompt)
3009      prompt_Printf(arg->prompt, "The alias command is deprecated\n");
3010    else
3011      log_Printf(LogWARN, "The alias command is deprecated\n");
3012  }
3013#endif
3014
3015  if (arg->argc > arg->argn)
3016    FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv,
3017             arg->prompt, arg->cx);
3018  else if (arg->prompt)
3019    prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help"
3020                  " <option>' for syntax help.\n", cmd, cmd);
3021  else
3022    log_Printf(LogWARN, "%s command must have arguments\n", cmd);
3023
3024  return 0;
3025}
3026
3027static int
3028IfaceAddCommand(struct cmdargs const *arg)
3029{
3030  struct ncpaddr peer, addr;
3031  struct ncprange ifa;
3032  struct in_addr mask;
3033  int n, how;
3034
3035  if (arg->argc == arg->argn + 1) {
3036    if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
3037      return -1;
3038    ncpaddr_init(&peer);
3039  } else {
3040    if (arg->argc == arg->argn + 2) {
3041      if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
3042        return -1;
3043      n = 1;
3044    } else if (arg->argc == arg->argn + 3) {
3045      if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn]))
3046        return -1;
3047      if (ncpaddr_family(&addr) != AF_INET)
3048        return -1;
3049      ncprange_sethost(&ifa, &addr);
3050      if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn + 1]))
3051        return -1;
3052      if (!ncpaddr_getip4(&addr, &mask))
3053        return -1;
3054      if (!ncprange_setip4mask(&ifa, mask))
3055        return -1;
3056      n = 2;
3057    } else
3058      return -1;
3059
3060    if (!ncpaddr_aton(&peer, NULL, arg->argv[arg->argn + n]))
3061      return -1;
3062
3063    if (ncprange_family(&ifa) != ncpaddr_family(&peer)) {
3064      log_Printf(LogWARN, "IfaceAddCommand: src and dst address families"
3065                 " differ\n");
3066      return -1;
3067    }
3068  }
3069
3070  how = IFACE_ADD_LAST;
3071  if (arg->cmd->args)
3072    how |= IFACE_FORCE_ADD;
3073
3074  return !iface_Add(arg->bundle->iface, &arg->bundle->ncp, &ifa, &peer, how);
3075}
3076
3077static int
3078IfaceDeleteCommand(struct cmdargs const *arg)
3079{
3080  struct ncpaddr ifa;
3081  struct in_addr ifa4;
3082  int ok;
3083
3084  if (arg->argc != arg->argn + 1)
3085    return -1;
3086
3087  if (!ncpaddr_aton(&ifa, NULL, arg->argv[arg->argn]))
3088    return -1;
3089
3090  if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
3091      ncpaddr_getip4(&ifa, &ifa4) &&
3092      arg->bundle->ncp.ipcp.my_ip.s_addr == ifa4.s_addr) {
3093    log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
3094               ncpaddr_ntoa(&ifa));
3095    return 1;
3096  }
3097
3098  ok = iface_Delete(arg->bundle->iface, &arg->bundle->ncp, &ifa);
3099  if (!ok) {
3100    if (arg->cmd->args)
3101      ok = 1;
3102    else if (arg->prompt)
3103      prompt_Printf(arg->prompt, "%s: No such interface address\n",
3104                    ncpaddr_ntoa(&ifa));
3105    else
3106      log_Printf(LogWARN, "%s: No such interface address\n",
3107                 ncpaddr_ntoa(&ifa));
3108  }
3109
3110  return !ok;
3111}
3112
3113static int
3114IfaceClearCommand(struct cmdargs const *arg)
3115{
3116  int family, how;
3117
3118  family = 0;
3119  if (arg->argc == arg->argn + 1) {
3120    if (strcasecmp(arg->argv[arg->argn], "inet") == 0)
3121      family = AF_INET;
3122#ifndef NOINET6
3123    else if (strcasecmp(arg->argv[arg->argn], "inet6") == 0)
3124      family = AF_INET6;
3125#endif
3126    else
3127      return -1;
3128  } else if (arg->argc != arg->argn)
3129    return -1;
3130
3131  how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED ||
3132        arg->bundle->phys_type.all & PHYS_AUTO ?
3133        IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL;
3134  iface_Clear(arg->bundle->iface, &arg->bundle->ncp, family, how);
3135
3136  return 0;
3137}
3138
3139static int
3140SetProcTitle(struct cmdargs const *arg)
3141{
3142  static char title[LINE_LEN];
3143  char *argv[MAXARGS];
3144  int argc = arg->argc - arg->argn;
3145
3146  if (arg->argc == arg->argn) {
3147    SetTitle(NULL);
3148    return 0;
3149  }
3150
3151  if (argc >= sizeof argv / sizeof argv[0]) {
3152    argc = sizeof argv / sizeof argv[0] - 1;
3153    log_Printf(LogWARN, "Truncating proc title to %d args\n", argc);
3154  }
3155  command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
3156  Concatinate(title, sizeof title, argc, (const char *const *)argv);
3157  SetTitle(title);
3158  command_Free(argc, argv);
3159
3160  return 0;
3161}
3162