command.c revision 47689
16059Samurai/*
26059Samurai *		PPP User command processing module
36059Samurai *
46059Samurai *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
56059Samurai *
66059Samurai *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
76059Samurai *
86059Samurai * Redistribution and use in source and binary forms are permitted
96059Samurai * provided that the above copyright notice and this paragraph are
106059Samurai * duplicated in all such forms and that any documentation,
116059Samurai * advertising materials, and other materials related to such
126059Samurai * distribution and use acknowledge that the software was developed
136059Samurai * by the Internet Initiative Japan, Inc.  The name of the
146059Samurai * IIJ may not be used to endorse or promote products derived
156059Samurai * from this software without specific prior written permission.
166059Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
176059Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
186059Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
198857Srgrimes *
2047689Sbrian * $Id: command.c,v 1.195 1999/05/31 23:57:35 brian Exp $
218857Srgrimes *
226059Samurai */
2343313Sbrian#include <sys/param.h>
2430715Sbrian#include <netinet/in_systm.h>
2526031Sbrian#include <netinet/in.h>
2630715Sbrian#include <netinet/ip.h>
2726031Sbrian#include <arpa/inet.h>
2830715Sbrian#include <sys/socket.h>
2926031Sbrian#include <net/route.h>
3030715Sbrian#include <netdb.h>
3136285Sbrian#include <sys/un.h>
3230715Sbrian
3338628Sbrian#include <ctype.h>
3430715Sbrian#include <errno.h>
3526516Sbrian#include <fcntl.h>
3630715Sbrian#include <paths.h>
3730715Sbrian#include <stdio.h>
3830715Sbrian#include <stdlib.h>
3930715Sbrian#include <string.h>
4030715Sbrian#include <sys/wait.h>
4130715Sbrian#include <termios.h>
4230715Sbrian#include <unistd.h>
4330715Sbrian
4439395Sbrian#ifndef NOALIAS
4546086Sbrian#ifdef __FreeBSD__
4646086Sbrian#include <alias.h>
4746086Sbrian#else
4839395Sbrian#include "alias.h"
4939395Sbrian#endif
5039395Sbrian#endif
5146686Sbrian#include "layer.h"
5237009Sbrian#include "defs.h"
5331343Sbrian#include "command.h"
5430715Sbrian#include "mbuf.h"
5530715Sbrian#include "log.h"
5630715Sbrian#include "timer.h"
576059Samurai#include "fsm.h"
586059Samurai#include "lcp.h"
5931690Sbrian#include "iplist.h"
6036285Sbrian#include "throughput.h"
6136285Sbrian#include "slcompress.h"
6238557Sbrian#include "lqr.h"
6338557Sbrian#include "hdlc.h"
646059Samurai#include "ipcp.h"
6531343Sbrian#ifndef NOALIAS
6626031Sbrian#include "alias_cmd.h"
6731343Sbrian#endif
6825630Sbrian#include "systems.h"
6936285Sbrian#include "filter.h"
7036285Sbrian#include "descriptor.h"
7130715Sbrian#include "main.h"
7230715Sbrian#include "route.h"
7330715Sbrian#include "ccp.h"
7431080Sbrian#include "auth.h"
7536285Sbrian#include "async.h"
7636285Sbrian#include "link.h"
7736285Sbrian#include "physical.h"
7836285Sbrian#include "mp.h"
7943313Sbrian#ifndef NORADIUS
8043313Sbrian#include "radius.h"
8143313Sbrian#endif
8236285Sbrian#include "bundle.h"
8336285Sbrian#include "server.h"
8436285Sbrian#include "prompt.h"
8536285Sbrian#include "chat.h"
8636285Sbrian#include "chap.h"
8738174Sbrian#include "cbcp.h"
8836285Sbrian#include "datalink.h"
8940561Sbrian#include "iface.h"
906059Samurai
9136285Sbrian/* ``set'' values */
9236285Sbrian#define	VAR_AUTHKEY	0
9336285Sbrian#define	VAR_DIAL	1
9436285Sbrian#define	VAR_LOGIN	2
9536285Sbrian#define	VAR_AUTHNAME	3
9636285Sbrian#define	VAR_AUTOLOAD	4
9736285Sbrian#define	VAR_WINSIZE	5
9836285Sbrian#define	VAR_DEVICE	6
9936285Sbrian#define	VAR_ACCMAP	7
10036285Sbrian#define	VAR_MRRU	8
10136285Sbrian#define	VAR_MRU		9
10236285Sbrian#define	VAR_MTU		10
10336285Sbrian#define	VAR_OPENMODE	11
10436285Sbrian#define	VAR_PHONE	12
10536285Sbrian#define	VAR_HANGUP	13
10636285Sbrian#define	VAR_IDLETIMEOUT	14
10736285Sbrian#define	VAR_LQRPERIOD	15
10836285Sbrian#define	VAR_LCPRETRY	16
10936285Sbrian#define	VAR_CHAPRETRY	17
11036285Sbrian#define	VAR_PAPRETRY	18
11136285Sbrian#define	VAR_CCPRETRY	19
11236285Sbrian#define	VAR_IPCPRETRY	20
11336285Sbrian#define	VAR_DNS		21
11436285Sbrian#define	VAR_NBNS	22
11536285Sbrian#define	VAR_MODE	23
11638174Sbrian#define	VAR_CALLBACK	24
11738174Sbrian#define	VAR_CBCP	25
11838544Sbrian#define	VAR_CHOKED	26
11940665Sbrian#define	VAR_SENDPIPE	27
12040665Sbrian#define	VAR_RECVPIPE	28
12143313Sbrian#define	VAR_RADIUS	29
12244073Sbrian#define	VAR_CD		30
12346686Sbrian#define	VAR_PARITY	31
12446686Sbrian#define VAR_CRTSCTS	32
1256059Samurai
12636285Sbrian/* ``accept|deny|disable|enable'' masks */
12736285Sbrian#define NEG_HISMASK (1)
12836285Sbrian#define NEG_MYMASK (2)
12936285Sbrian
13036285Sbrian/* ``accept|deny|disable|enable'' values */
13136285Sbrian#define NEG_ACFCOMP	40
13244106Sbrian#define NEG_CHAP05	41
13344106Sbrian#define NEG_CHAP80	42
13444106Sbrian#define NEG_CHAP80LM	43
13544106Sbrian#define NEG_DEFLATE	44
13644106Sbrian#define NEG_LQR		45
13744106Sbrian#define NEG_PAP		46
13844106Sbrian#define NEG_PPPDDEFLATE	47
13944106Sbrian#define NEG_PRED1	48
14044106Sbrian#define NEG_PROTOCOMP	49
14144106Sbrian#define NEG_SHORTSEQ	50
14244106Sbrian#define NEG_VJCOMP	51
14344106Sbrian#define NEG_DNS		52
14436285Sbrian
14547061Sbrianconst char Version[] = "2.21";
14647689Sbrianconst char VersionDate[] = "$Date: 1999/05/31 23:57:35 $";
14736285Sbrian
14836285Sbrianstatic int ShowCommand(struct cmdargs const *);
14936285Sbrianstatic int TerminalCommand(struct cmdargs const *);
15036285Sbrianstatic int QuitCommand(struct cmdargs const *);
15136285Sbrianstatic int OpenCommand(struct cmdargs const *);
15236285Sbrianstatic int CloseCommand(struct cmdargs const *);
15336285Sbrianstatic int DownCommand(struct cmdargs const *);
15436285Sbrianstatic int SetCommand(struct cmdargs const *);
15536285Sbrianstatic int LinkCommand(struct cmdargs const *);
15636285Sbrianstatic int AddCommand(struct cmdargs const *);
15736285Sbrianstatic int DeleteCommand(struct cmdargs const *);
15836285Sbrianstatic int NegotiateCommand(struct cmdargs const *);
15936934Sbrianstatic int ClearCommand(struct cmdargs const *);
16040561Sbrianstatic int RunListCommand(struct cmdargs const *);
16140561Sbrianstatic int IfaceAddCommand(struct cmdargs const *);
16240561Sbrianstatic int IfaceDeleteCommand(struct cmdargs const *);
16340561Sbrianstatic int IfaceClearCommand(struct cmdargs const *);
16440679Sbrianstatic int SetProcTitle(struct cmdargs const *);
16531343Sbrian#ifndef NOALIAS
16636285Sbrianstatic int AliasEnable(struct cmdargs const *);
16736285Sbrianstatic int AliasOption(struct cmdargs const *);
16831343Sbrian#endif
1696059Samurai
17036285Sbrianstatic const char *
17136285Sbrianshowcx(struct cmdtab const *cmd)
17236285Sbrian{
17336285Sbrian  if (cmd->lauth & LOCAL_CX)
17436285Sbrian    return "(c)";
17536285Sbrian  else if (cmd->lauth & LOCAL_CX_OPT)
17636285Sbrian    return "(o)";
17736285Sbrian
17836285Sbrian  return "";
17936285Sbrian}
18036285Sbrian
1816059Samuraistatic int
18231343SbrianHelpCommand(struct cmdargs const *arg)
1836059Samurai{
18428679Sbrian  struct cmdtab const *cmd;
18536285Sbrian  int n, cmax, dmax, cols, cxlen;
18636285Sbrian  const char *cx;
1876059Samurai
18836285Sbrian  if (!arg->prompt) {
18936285Sbrian    log_Printf(LogWARN, "help: Cannot help without a prompt\n");
19026516Sbrian    return 0;
19136285Sbrian  }
19226516Sbrian
19336285Sbrian  if (arg->argc > arg->argn) {
19436285Sbrian    for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
19536285Sbrian      if ((cmd->lauth & arg->prompt->auth) &&
19636285Sbrian          ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) ||
19736285Sbrian           (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) {
19836285Sbrian	prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd));
19928679Sbrian	return 0;
2006059Samurai      }
20126516Sbrian    return -1;
2026059Samurai  }
20336285Sbrian
20431372Sbrian  cmax = dmax = 0;
20536285Sbrian  for (cmd = arg->cmdtab; cmd->func; cmd++)
20636285Sbrian    if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
20736285Sbrian      if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax)
20831372Sbrian        cmax = n;
20931372Sbrian      if ((n = strlen(cmd->helpmes)) > dmax)
21031372Sbrian        dmax = n;
21131372Sbrian    }
21231372Sbrian
21331372Sbrian  cols = 80 / (dmax + cmax + 3);
2146059Samurai  n = 0;
21536285Sbrian  prompt_Printf(arg->prompt, "(o) = Optional context,"
21636285Sbrian                " (c) = Context required\n");
21736285Sbrian  for (cmd = arg->cmdtab; cmd->func; cmd++)
21836285Sbrian    if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
21936285Sbrian      cx = showcx(cmd);
22036285Sbrian      cxlen = cmax - strlen(cmd->name);
22140482Sbrian      if (n % cols != 0)
22240482Sbrian        prompt_Printf(arg->prompt, " ");
22340482Sbrian      prompt_Printf(arg->prompt, "%s%-*.*s: %-*.*s",
22436285Sbrian              cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes);
22531372Sbrian      if (++n % cols == 0)
22636285Sbrian        prompt_Printf(arg->prompt, "\n");
2276059Samurai    }
22831372Sbrian  if (n % cols != 0)
22936285Sbrian    prompt_Printf(arg->prompt, "\n");
23026516Sbrian
23126516Sbrian  return 0;
2326059Samurai}
2336059Samurai
23436285Sbrianstatic int
23536285SbrianCloneCommand(struct cmdargs const *arg)
2366059Samurai{
23736285Sbrian  char namelist[LINE_LEN];
23836285Sbrian  char *name;
23936285Sbrian  int f;
2406059Samurai
24136285Sbrian  if (arg->argc == arg->argn)
24236285Sbrian    return -1;
24336285Sbrian
24436285Sbrian  namelist[sizeof namelist - 1] = '\0';
24536285Sbrian  for (f = arg->argn; f < arg->argc; f++) {
24636285Sbrian    strncpy(namelist, arg->argv[f], sizeof namelist - 1);
24736285Sbrian    for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
24836285Sbrian      bundle_DatalinkClone(arg->bundle, arg->cx, name);
2496059Samurai  }
25036285Sbrian
25136285Sbrian  return 0;
2526059Samurai}
2536059Samurai
2546059Samuraistatic int
25536285SbrianRemoveCommand(struct cmdargs const *arg)
2566059Samurai{
25736285Sbrian  if (arg->argc != arg->argn)
25836285Sbrian    return -1;
25911336Samurai
26036285Sbrian  if (arg->cx->state != DATALINK_CLOSED) {
26136285Sbrian    log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n");
26236285Sbrian    return 2;
2636059Samurai  }
26426516Sbrian
26536285Sbrian  bundle_DatalinkRemove(arg->bundle, arg->cx);
26636285Sbrian  return 0;
26736285Sbrian}
26832711Sbrian
26936285Sbrianstatic int
27036285SbrianRenameCommand(struct cmdargs const *arg)
27136285Sbrian{
27236285Sbrian  if (arg->argc != arg->argn + 1)
27336285Sbrian    return -1;
27431121Sbrian
27536285Sbrian  if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn]))
27636285Sbrian    return 0;
27736285Sbrian
27836285Sbrian  log_Printf(LogWARN, "%s -> %s: target name already exists\n",
27936285Sbrian             arg->cx->name, arg->argv[arg->argn]);
28036285Sbrian  return 1;
28136285Sbrian}
28236285Sbrian
28336285Sbrianint
28436285SbrianLoadCommand(struct cmdargs const *arg)
28536285Sbrian{
28640797Sbrian  const char *err;
28740797Sbrian  int n, mode;
28836285Sbrian
28940797Sbrian  mode = arg->bundle->phys_type.all;
29036285Sbrian
29140797Sbrian  if (arg->argn < arg->argc) {
29240797Sbrian    for (n = arg->argn; n < arg->argc; n++)
29340797Sbrian      if ((err = system_IsValid(arg->argv[n], arg->prompt, mode)) != NULL) {
29440797Sbrian        log_Printf(LogWARN, "%s: %s\n", arg->argv[n], err);
29540797Sbrian        return 1;
29640797Sbrian      }
29740797Sbrian
29840797Sbrian    for (n = arg->argn; n < arg->argc; n++) {
29940797Sbrian      bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
30040797Sbrian      system_Select(arg->bundle, arg->argv[n], CONFFILE, arg->prompt, arg->cx);
30140797Sbrian    }
30240797Sbrian    bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
30340797Sbrian  } else if ((err = system_IsValid("default", arg->prompt, mode)) != NULL) {
30440797Sbrian    log_Printf(LogWARN, "default: %s\n", err);
30536285Sbrian    return 1;
30636285Sbrian  } else {
30740797Sbrian    bundle_SetLabel(arg->bundle, "default");
30840797Sbrian    system_Select(arg->bundle, "default", CONFFILE, arg->prompt, arg->cx);
30940797Sbrian    bundle_SetLabel(arg->bundle, "default");
31036285Sbrian  }
31140797Sbrian
31226516Sbrian  return 0;
3136059Samurai}
3146059Samurai
31536285Sbrianint
31636285SbrianSaveCommand(struct cmdargs const *arg)
31736285Sbrian{
31836285Sbrian  log_Printf(LogWARN, "save command is not implemented (yet).\n");
31936285Sbrian  return 1;
32036285Sbrian}
32136285Sbrian
32210528Samuraistatic int
32336285SbrianDialCommand(struct cmdargs const *arg)
32428536Sbrian{
32536285Sbrian  int res;
32636285Sbrian
32736465Sbrian  if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO)))
32836465Sbrian      || (!arg->cx &&
32936928Sbrian          (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) {
33036285Sbrian    log_Printf(LogWARN, "Manual dial is only available for auto and"
33136285Sbrian              " interactive links\n");
33236285Sbrian    return 1;
33334536Sbrian  }
33436285Sbrian
33536285Sbrian  if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0)
33636285Sbrian    return res;
33736285Sbrian
33837993Sbrian  bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
33936285Sbrian
34036285Sbrian  return 0;
34128536Sbrian}
34228536Sbrian
34338628Sbrian#define isinword(ch) (isalnum(ch) || (ch) == '_')
34438628Sbrian
34538628Sbrianstatic char *
34638628Sbrianstrstrword(char *big, const char *little)
34738628Sbrian{
34838628Sbrian  /* Get the first occurance of the word ``little'' in ``big'' */
34938628Sbrian  char *pos;
35038628Sbrian  int len;
35138628Sbrian
35238628Sbrian  pos = big;
35338628Sbrian  len = strlen(little);
35438628Sbrian
35538628Sbrian  while ((pos = strstr(pos, little)) != NULL)
35638628Sbrian    if ((pos == big || !isinword(pos[-1])) && !isinword(pos[len]))
35738628Sbrian      break;
35838628Sbrian    else
35938628Sbrian      pos++;
36038628Sbrian
36138628Sbrian  return pos;
36238628Sbrian}
36338628Sbrian
36438628Sbrianstatic char *
36538628Sbriansubst(char *tgt, const char *oldstr, const char *newstr)
36638628Sbrian{
36738628Sbrian  /* tgt is a malloc()d area... realloc() as necessary */
36838628Sbrian  char *word, *ntgt;
36938628Sbrian  int ltgt, loldstr, lnewstr, pos;
37038628Sbrian
37138628Sbrian  if ((word = strstrword(tgt, oldstr)) == NULL)
37238628Sbrian    return tgt;
37338628Sbrian
37438628Sbrian  ltgt = strlen(tgt) + 1;
37538628Sbrian  loldstr = strlen(oldstr);
37638628Sbrian  lnewstr = strlen(newstr);
37738628Sbrian  do {
37838628Sbrian    pos = word - tgt;
37938628Sbrian    if (loldstr > lnewstr)
38038628Sbrian      bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
38138628Sbrian    if (loldstr != lnewstr) {
38238628Sbrian      ntgt = realloc(tgt, ltgt += lnewstr - loldstr);
38338628Sbrian      if (ntgt == NULL)
38438628Sbrian        break;			/* Oh wonderful ! */
38538628Sbrian      word = ntgt + pos;
38638628Sbrian      tgt = ntgt;
38738628Sbrian    }
38838628Sbrian    if (lnewstr > loldstr)
38938628Sbrian      bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
39038628Sbrian    bcopy(newstr, word, lnewstr);
39138628Sbrian  } while ((word = strstrword(word, oldstr)));
39238628Sbrian
39338628Sbrian  return tgt;
39438628Sbrian}
39538628Sbrian
39643888Sbrianvoid
39743888Sbriancommand_Expand(char **nargv, int argc, char const *const *oargv,
39843888Sbrian               struct bundle *bundle, int inc0)
39938628Sbrian{
40038628Sbrian  int arg;
40140678Sbrian  char pid[12];
40238628Sbrian
40341755Sbrian  if (inc0)
40441755Sbrian    arg = 0;		/* Start at arg 0 */
40541755Sbrian  else {
40641755Sbrian    nargv[0] = strdup(oargv[0]);
40741755Sbrian    arg = 1;
40841755Sbrian  }
40940678Sbrian  snprintf(pid, sizeof pid, "%d", getpid());
41041755Sbrian  for (; arg < argc; arg++) {
41138629Sbrian    nargv[arg] = strdup(oargv[arg]);
41238629Sbrian    nargv[arg] = subst(nargv[arg], "HISADDR",
41338628Sbrian                       inet_ntoa(bundle->ncp.ipcp.peer_ip));
41438629Sbrian    nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name);
41540561Sbrian    nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name);
41638628Sbrian    nargv[arg] = subst(nargv[arg], "MYADDR", inet_ntoa(bundle->ncp.ipcp.my_ip));
41738629Sbrian    nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname);
41838629Sbrian    nargv[arg] = subst(nargv[arg], "PEER_ENDDISC",
41938629Sbrian                       mp_Enddisc(bundle->ncp.mp.peer.enddisc.class,
42038629Sbrian                                  bundle->ncp.mp.peer.enddisc.address,
42138629Sbrian                                  bundle->ncp.mp.peer.enddisc.len));
42238629Sbrian    nargv[arg] = subst(nargv[arg], "ENDDISC",
42338629Sbrian                       mp_Enddisc(bundle->ncp.mp.cfg.enddisc.class,
42438629Sbrian                                  bundle->ncp.mp.cfg.enddisc.address,
42538629Sbrian                                  bundle->ncp.mp.cfg.enddisc.len));
42640678Sbrian    nargv[arg] = subst(nargv[arg], "PROCESSID", pid);
42738629Sbrian    nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle));
42838628Sbrian  }
42938628Sbrian  nargv[arg] = NULL;
43038628Sbrian}
43138628Sbrian
43228536Sbrianstatic int
43331343SbrianShellCommand(struct cmdargs const *arg, int bg)
43410528Samurai{
43510528Samurai  const char *shell;
43610528Samurai  pid_t shpid;
43720813Sjkh
43818856Ssos#ifdef SHELL_ONLY_INTERACTIVELY
43926911Sbrian  /* we're only allowed to shell when we run ppp interactively */
44036285Sbrian  if (arg->prompt && arg->prompt->owner) {
44136285Sbrian    log_Printf(LogWARN, "Can't start a shell from a network connection\n");
44226516Sbrian    return 1;
44310528Samurai  }
44426911Sbrian#endif
44528679Sbrian
44636285Sbrian  if (arg->argc == arg->argn) {
44736285Sbrian    if (!arg->prompt) {
44836285Sbrian      log_Printf(LogWARN, "Can't start an interactive shell from"
44936285Sbrian                " a config file\n");
45028381Sbrian      return 1;
45136285Sbrian    } else if (arg->prompt->owner) {
45236285Sbrian      log_Printf(LogWARN, "Can't start an interactive shell from"
45336285Sbrian                " a socket connection\n");
45436285Sbrian      return 1;
45528381Sbrian    } else if (bg) {
45636285Sbrian      log_Printf(LogWARN, "Can only start an interactive shell in"
45728679Sbrian		" the foreground mode\n");
45828381Sbrian      return 1;
45928381Sbrian    }
46034536Sbrian  }
46134536Sbrian
46228679Sbrian  if ((shpid = fork()) == 0) {
46336285Sbrian    int i, fd;
46418531Sbde
46536285Sbrian    if ((shell = getenv("SHELL")) == 0)
46636285Sbrian      shell = _PATH_BSHELL;
46732017Sbrian
46836285Sbrian    timer_TermService();
46936285Sbrian
47036285Sbrian    if (arg->prompt)
47136285Sbrian      fd = arg->prompt->fd_out;
47236285Sbrian    else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
47336285Sbrian      log_Printf(LogALERT, "Failed to open %s: %s\n",
47436285Sbrian                _PATH_DEVNULL, strerror(errno));
47528679Sbrian      exit(1);
47628679Sbrian    }
47728679Sbrian    for (i = 0; i < 3; i++)
47828679Sbrian      dup2(fd, i);
47926516Sbrian
48036285Sbrian    fcntl(3, F_SETFD, 1);	/* Set close-on-exec flag */
48126516Sbrian
48231061Sbrian    setuid(geteuid());
48336285Sbrian    if (arg->argc > arg->argn) {
48428679Sbrian      /* substitute pseudo args */
48538628Sbrian      char *argv[MAXARGS];
48638628Sbrian      int argc = arg->argc - arg->argn;
48738628Sbrian
48838628Sbrian      if (argc >= sizeof argv / sizeof argv[0]) {
48938628Sbrian        argc = sizeof argv / sizeof argv[0] - 1;
49038628Sbrian        log_Printf(LogWARN, "Truncating shell command to %d args\n", argc);
49131343Sbrian      }
49243888Sbrian      command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 0);
49328679Sbrian      if (bg) {
49428679Sbrian	pid_t p;
49510528Samurai
49628679Sbrian	p = getpid();
49728679Sbrian	if (daemon(1, 1) == -1) {
49836832Sbrian	  log_Printf(LogERROR, "%d: daemon: %s\n", (int)p, strerror(errno));
49928679Sbrian	  exit(1);
50028679Sbrian	}
50136285Sbrian      } else if (arg->prompt)
50236285Sbrian        printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]);
50331343Sbrian      execvp(argv[0], argv);
50430316Sbrian    } else {
50536285Sbrian      if (arg->prompt)
50632017Sbrian        printf("ppp: Pausing until %s finishes\n", shell);
50736285Sbrian      prompt_TtyOldMode(arg->prompt);
50831343Sbrian      execl(shell, shell, NULL);
50930316Sbrian    }
51020813Sjkh
51140665Sbrian    log_Printf(LogWARN, "exec() of %s failed: %s\n",
51240665Sbrian              arg->argc > arg->argn ? arg->argv[arg->argn] : shell,
51340665Sbrian              strerror(errno));
51428679Sbrian    exit(255);
51510528Samurai  }
51636285Sbrian
51736285Sbrian  if (shpid == (pid_t) - 1)
51836285Sbrian    log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno));
51936285Sbrian  else {
52010528Samurai    int status;
52131343Sbrian    waitpid(shpid, &status, 0);
52210528Samurai  }
52320813Sjkh
52436285Sbrian  if (arg->prompt && !arg->prompt->owner)
52536285Sbrian    prompt_TtyCommandMode(arg->prompt);
52620813Sjkh
52736285Sbrian  return 0;
52810528Samurai}
52910528Samurai
53031343Sbrianstatic int
53131343SbrianBgShellCommand(struct cmdargs const *arg)
53231343Sbrian{
53336285Sbrian  if (arg->argc == arg->argn)
53431343Sbrian    return -1;
53531343Sbrian  return ShellCommand(arg, 1);
53631343Sbrian}
53731343Sbrian
53831343Sbrianstatic int
53931343SbrianFgShellCommand(struct cmdargs const *arg)
54031343Sbrian{
54131343Sbrian  return ShellCommand(arg, 0);
54231343Sbrian}
54331343Sbrian
54440561Sbrian#ifndef NOALIAS
54540561Sbrianstatic struct cmdtab const AliasCommands[] =
54640561Sbrian{
54740561Sbrian  {"addr", NULL, alias_RedirectAddr, LOCAL_AUTH,
54840561Sbrian   "static address translation", "alias addr [addr_local addr_alias]"},
54940561Sbrian  {"deny_incoming", NULL, AliasOption, LOCAL_AUTH,
55040561Sbrian   "stop incoming connections", "alias deny_incoming [yes|no]",
55140561Sbrian   (const void *) PKT_ALIAS_DENY_INCOMING},
55240561Sbrian  {"enable", NULL, AliasEnable, LOCAL_AUTH,
55340561Sbrian   "enable IP aliasing", "alias enable [yes|no]"},
55440561Sbrian  {"log", NULL, AliasOption, LOCAL_AUTH,
55540561Sbrian   "log aliasing link creation", "alias log [yes|no]",
55640561Sbrian   (const void *) PKT_ALIAS_LOG},
55745042Sbrian  {"port", NULL, alias_RedirectPort, LOCAL_AUTH, "port redirection",
55845042Sbrian   "alias port proto localaddr:port[-port] aliasport[-aliasport]"},
55944557Sbrian  {"pptp", NULL, alias_Pptp, LOCAL_AUTH,
56044557Sbrian   "Set the PPTP address", "alias pptp IP"},
56144547Sbrian  {"proxy", NULL, alias_ProxyRule, LOCAL_AUTH,
56244547Sbrian   "proxy control", "alias proxy server host[:port] ..."},
56340561Sbrian  {"same_ports", NULL, AliasOption, LOCAL_AUTH,
56440561Sbrian   "try to leave port numbers unchanged", "alias same_ports [yes|no]",
56540561Sbrian   (const void *) PKT_ALIAS_SAME_PORTS},
56640561Sbrian  {"unregistered_only", NULL, AliasOption, LOCAL_AUTH,
56740561Sbrian   "alias unregistered (private) IP address space only",
56840561Sbrian   "alias unregistered_only [yes|no]",
56940561Sbrian   (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
57040561Sbrian  {"use_sockets", NULL, AliasOption, LOCAL_AUTH,
57140561Sbrian   "allocate host sockets", "alias use_sockets [yes|no]",
57240561Sbrian   (const void *) PKT_ALIAS_USE_SOCKETS},
57340561Sbrian  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
57440561Sbrian   "Display this message", "alias help|? [command]", AliasCommands},
57540561Sbrian  {NULL, NULL, NULL},
57640561Sbrian};
57740561Sbrian#endif
57840561Sbrian
57940561Sbrianstatic struct cmdtab const AllowCommands[] = {
58040561Sbrian  {"modes", "mode", AllowModes, LOCAL_AUTH,
58140561Sbrian  "Only allow certain ppp modes", "allow modes mode..."},
58240561Sbrian  {"users", "user", AllowUsers, LOCAL_AUTH,
58340561Sbrian  "Only allow ppp access to certain users", "allow users logname..."},
58440561Sbrian  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
58540561Sbrian  "Display this message", "allow help|? [command]", AllowCommands},
58640561Sbrian  {NULL, NULL, NULL},
58740561Sbrian};
58840561Sbrian
58940561Sbrianstatic struct cmdtab const IfaceCommands[] =
59040561Sbrian{
59140561Sbrian  {"add", NULL, IfaceAddCommand, LOCAL_AUTH,
59240561Sbrian   "Add iface address", "iface add addr[/bits| mask] peer", NULL},
59340561Sbrian  {NULL, "add!", IfaceAddCommand, LOCAL_AUTH,
59440561Sbrian   "Add or change an iface address", "iface add! addr[/bits| mask] peer",
59540561Sbrian   (void *)1},
59640561Sbrian  {"clear", NULL, IfaceClearCommand, LOCAL_AUTH,
59740561Sbrian   "Clear iface address(es)", "iface clear"},
59840561Sbrian  {"delete", "rm", IfaceDeleteCommand, LOCAL_AUTH,
59940561Sbrian   "Delete iface address", "iface delete addr", NULL},
60040561Sbrian  {NULL, "rm!", IfaceDeleteCommand, LOCAL_AUTH,
60140561Sbrian   "Delete iface address", "iface delete addr", (void *)1},
60240561Sbrian  {NULL, "delete!", IfaceDeleteCommand, LOCAL_AUTH,
60340561Sbrian   "Delete iface address", "iface delete addr", (void *)1},
60440561Sbrian  {"show", NULL, iface_Show, LOCAL_AUTH,
60540561Sbrian   "Show iface address(es)", "iface show"},
60640561Sbrian  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
60740561Sbrian   "Display this message", "alias help|? [command]", IfaceCommands},
60840561Sbrian  {NULL, NULL, NULL},
60940561Sbrian};
61040561Sbrian
61130715Sbrianstatic struct cmdtab const Commands[] = {
61236285Sbrian  {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
61328679Sbrian  "accept option request", "accept option .."},
61428679Sbrian  {"add", NULL, AddCommand, LOCAL_AUTH,
61532109Sbrian  "add route", "add dest mask gateway", NULL},
61636285Sbrian  {NULL, "add!", AddCommand, LOCAL_AUTH,
61732109Sbrian  "add or change route", "add! dest mask gateway", (void *)1},
61836285Sbrian#ifndef NOALIAS
61940561Sbrian  {"alias", NULL, RunListCommand, LOCAL_AUTH,
62040561Sbrian  "alias control", "alias option [yes|no]", AliasCommands},
62136285Sbrian#endif
62240561Sbrian  {"allow", "auth", RunListCommand, LOCAL_AUTH,
62340561Sbrian  "Allow ppp access", "allow users|modes ....", AllowCommands},
62428679Sbrian  {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
62531372Sbrian  "Run a background command", "[!]bg command"},
62636934Sbrian  {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
62746686Sbrian  "Clear throughput statistics",
62846686Sbrian  "clear ipcp|physical [current|overall|peak]..."},
62936285Sbrian  {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
63036285Sbrian  "Clone a link", "clone newname..."},
63136285Sbrian  {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
63236285Sbrian  "Close an FSM", "close [lcp|ccp]"},
63328679Sbrian  {"delete", NULL, DeleteCommand, LOCAL_AUTH,
63432109Sbrian  "delete route", "delete dest", NULL},
63536285Sbrian  {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
63632109Sbrian  "delete a route if it exists", "delete! dest", (void *)1},
63736285Sbrian  {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
63828679Sbrian  "Deny option request", "deny option .."},
63936285Sbrian  {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
64040797Sbrian  "Dial and login", "dial|call [system ...]", NULL},
64136285Sbrian  {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
64228679Sbrian  "Disable option", "disable option .."},
64336285Sbrian  {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
64446686Sbrian  "Generate a down event", "down [ccp|lcp]"},
64536285Sbrian  {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
64628679Sbrian  "Enable option", "enable option .."},
64740561Sbrian  {"iface", "interface", RunListCommand, LOCAL_AUTH,
64840561Sbrian  "interface control", "iface option ...", IfaceCommands},
64936285Sbrian  {"link", "datalink", LinkCommand, LOCAL_AUTH,
65036285Sbrian  "Link specific commands", "link name command ..."},
65137008Sbrian  {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
65240797Sbrian  "Load settings", "load [system ...]"},
65336285Sbrian  {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT,
65437955Sbrian  "Open an FSM", "open! [lcp|ccp|ipcp]", (void *)1},
65536285Sbrian  {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
65636285Sbrian  "Password for manipulation", "passwd LocalPassword"},
65736285Sbrian  {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
65836285Sbrian  "Quit PPP program", "quit|bye [all]"},
65936285Sbrian  {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
66036285Sbrian  "Remove a link", "remove"},
66136285Sbrian  {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
66236285Sbrian  "Rename a link", "rename name"},
66328679Sbrian  {"save", NULL, SaveCommand, LOCAL_AUTH,
66428679Sbrian  "Save settings", "save"},
66536285Sbrian  {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
66628679Sbrian  "Set parameters", "set[up] var value"},
66728679Sbrian  {"shell", "!", FgShellCommand, LOCAL_AUTH,
66828679Sbrian  "Run a subshell", "shell|! [sh command]"},
66936285Sbrian  {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
67031372Sbrian  "Show status and stats", "show var"},
67136285Sbrian  {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
67231372Sbrian  "Enter terminal mode", "term"},
67328679Sbrian  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
67431343Sbrian  "Display this message", "help|? [command]", Commands},
67528679Sbrian  {NULL, NULL, NULL},
6766059Samurai};
6776059Samurai
67828536Sbrianstatic int
67931343SbrianShowEscape(struct cmdargs const *arg)
6806059Samurai{
68136285Sbrian  if (arg->cx->physical->async.cfg.EscMap[32]) {
68236285Sbrian    int code, bit;
68336285Sbrian    const char *sep = "";
6846059Samurai
68526516Sbrian    for (code = 0; code < 32; code++)
68636285Sbrian      if (arg->cx->physical->async.cfg.EscMap[code])
68728679Sbrian	for (bit = 0; bit < 8; bit++)
68836285Sbrian	  if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
68936285Sbrian	    prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
69036285Sbrian            sep = ", ";
69136285Sbrian          }
69236285Sbrian    prompt_Printf(arg->prompt, "\n");
6936059Samurai  }
69431077Sbrian  return 0;
6956059Samurai}
6966059Samurai
69728679Sbrianstatic int
69836285SbrianShowTimerList(struct cmdargs const *arg)
6996059Samurai{
70036285Sbrian  timer_Show(0, arg->prompt);
70131077Sbrian  return 0;
7026059Samurai}
7036059Samurai
70428679Sbrianstatic int
70531343SbrianShowStopped(struct cmdargs const *arg)
70628327Sbrian{
70736285Sbrian  prompt_Printf(arg->prompt, " Stopped Timer:  LCP: ");
70836285Sbrian  if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
70936285Sbrian    prompt_Printf(arg->prompt, "Disabled");
71028327Sbrian  else
71136285Sbrian    prompt_Printf(arg->prompt, "%ld secs",
71236285Sbrian                  arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
71328461Sbrian
71436285Sbrian  prompt_Printf(arg->prompt, ", CCP: ");
71536285Sbrian  if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
71636285Sbrian    prompt_Printf(arg->prompt, "Disabled");
71728461Sbrian  else
71836285Sbrian    prompt_Printf(arg->prompt, "%ld secs",
71936285Sbrian                  arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
72028461Sbrian
72136285Sbrian  prompt_Printf(arg->prompt, "\n");
72228461Sbrian
72331077Sbrian  return 0;
72428327Sbrian}
72528327Sbrian
72628679Sbrianstatic int
72731343SbrianShowVersion(struct cmdargs const *arg)
7286059Samurai{
72936285Sbrian  prompt_Printf(arg->prompt, "PPP Version %s - %s\n", Version, VersionDate);
73031077Sbrian  return 0;
7316059Samurai}
7326059Samurai
73328679Sbrianstatic int
73436285SbrianShowProtocolStats(struct cmdargs const *arg)
73526326Sbrian{
73636285Sbrian  struct link *l = command_ChooseLink(arg);
73726326Sbrian
73836285Sbrian  prompt_Printf(arg->prompt, "%s:\n", l->name);
73936285Sbrian  link_ReportProtocolStatus(l, arg->prompt);
74031077Sbrian  return 0;
74126326Sbrian}
74226326Sbrian
74330715Sbrianstatic struct cmdtab const ShowCommands[] = {
74436285Sbrian  {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
74536285Sbrian  "bundle details", "show bundle"},
74636285Sbrian  {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
74736285Sbrian  "CCP status", "show cpp"},
74836285Sbrian  {"compress", NULL, sl_Show, LOCAL_AUTH,
74936285Sbrian  "VJ compression stats", "show compress"},
75036285Sbrian  {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
75136285Sbrian  "escape characters", "show escape"},
75236285Sbrian  {"filter", NULL, filter_Show, LOCAL_AUTH,
75336285Sbrian  "packet filters", "show filter [in|out|dial|alive]"},
75436285Sbrian  {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
75536285Sbrian  "HDLC errors", "show hdlc"},
75640561Sbrian  {"iface", "interface", iface_Show, LOCAL_AUTH,
75740561Sbrian  "Interface status", "show iface"},
75836285Sbrian  {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
75936285Sbrian  "IPCP status", "show ipcp"},
76047211Sbrian  {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT,
76147211Sbrian  "Protocol layers", "show layers"},
76236285Sbrian  {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
76336285Sbrian  "LCP status", "show lcp"},
76436285Sbrian  {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
76536285Sbrian  "(high-level) link info", "show link"},
76636285Sbrian  {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
76736285Sbrian  "available link names", "show links"},
76836285Sbrian  {"log", NULL, log_ShowLevel, LOCAL_AUTH,
76936285Sbrian  "log levels", "show log"},
77036285Sbrian  {"mem", NULL, mbuf_Show, LOCAL_AUTH,
77136285Sbrian  "mbuf allocations", "show mem"},
77246686Sbrian  {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX,
77346686Sbrian  "(low-level) link info", "show physical"},
77436285Sbrian  {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
77536285Sbrian  "multilink setup", "show mp"},
77636285Sbrian  {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
77736285Sbrian  "protocol summary", "show proto"},
77836285Sbrian  {"route", NULL, route_Show, LOCAL_AUTH,
77936285Sbrian  "routing table", "show route"},
78036285Sbrian  {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
78136285Sbrian  "STOPPED timeout", "show stopped"},
78236285Sbrian  {"timers", NULL, ShowTimerList, LOCAL_AUTH,
78336285Sbrian  "alarm timers", "show timers"},
78428679Sbrian  {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
78536285Sbrian  "version string", "show version"},
78636285Sbrian  {"who", NULL, log_ShowWho, LOCAL_AUTH,
78736285Sbrian  "client list", "show who"},
78828679Sbrian  {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
78931343Sbrian  "Display this message", "show help|? [command]", ShowCommands},
79028679Sbrian  {NULL, NULL, NULL},
7916059Samurai};
7926059Samurai
79330715Sbrianstatic struct cmdtab const *
79431343SbrianFindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
7956059Samurai{
79626516Sbrian  int nmatch;
79726516Sbrian  int len;
79828679Sbrian  struct cmdtab const *found;
7996059Samurai
80026516Sbrian  found = NULL;
80126516Sbrian  len = strlen(str);
80226516Sbrian  nmatch = 0;
8036059Samurai  while (cmds->func) {
80425566Sbrian    if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
80526516Sbrian      if (cmds->name[len] == '\0') {
80628679Sbrian	*pmatch = 1;
80728679Sbrian	return cmds;
80826516Sbrian      }
8096059Samurai      nmatch++;
8106059Samurai      found = cmds;
81128679Sbrian    } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
81226516Sbrian      if (cmds->alias[len] == '\0') {
81328679Sbrian	*pmatch = 1;
81428679Sbrian	return cmds;
81526516Sbrian      }
8166059Samurai      nmatch++;
8176059Samurai      found = cmds;
8186059Samurai    }
8196059Samurai    cmds++;
8206059Samurai  }
8216059Samurai  *pmatch = nmatch;
82226516Sbrian  return found;
8236059Samurai}
8246059Samurai
82536285Sbrianstatic const char *
82636285SbrianmkPrefix(int argc, char const *const *argv, char *tgt, int sz)
82736285Sbrian{
82836285Sbrian  int f, tlen, len;
82936285Sbrian
83036285Sbrian  tlen = 0;
83136285Sbrian  for (f = 0; f < argc && tlen < sz - 2; f++) {
83236285Sbrian    if (f)
83336285Sbrian      tgt[tlen++] = ' ';
83436285Sbrian    len = strlen(argv[f]);
83536285Sbrian    if (len > sz - tlen - 1)
83636285Sbrian      len = sz - tlen - 1;
83736285Sbrian    strncpy(tgt+tlen, argv[f], len);
83836285Sbrian    tlen += len;
83936285Sbrian  }
84036285Sbrian  tgt[tlen] = '\0';
84136285Sbrian  return tgt;
84236285Sbrian}
84336285Sbrian
84430715Sbrianstatic int
84536285SbrianFindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
84636285Sbrian         char const *const *argv, struct prompt *prompt, struct datalink *cx)
8476059Samurai{
84828679Sbrian  struct cmdtab const *cmd;
8496059Samurai  int val = 1;
8506059Samurai  int nmatch;
85131343Sbrian  struct cmdargs arg;
85236285Sbrian  char prefix[100];
8536059Samurai
85436285Sbrian  cmd = FindCommand(cmds, argv[argn], &nmatch);
8556059Samurai  if (nmatch > 1)
85636285Sbrian    log_Printf(LogWARN, "%s: Ambiguous command\n",
85736285Sbrian              mkPrefix(argn+1, argv, prefix, sizeof prefix));
85836285Sbrian  else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
85936285Sbrian    if ((cmd->lauth & LOCAL_CX) && !cx)
86036285Sbrian      /* We've got no context, but we require it */
86136285Sbrian      cx = bundle2datalink(bundle, NULL);
86236285Sbrian
86336285Sbrian    if ((cmd->lauth & LOCAL_CX) && !cx)
86436285Sbrian      log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
86536285Sbrian                mkPrefix(argn+1, argv, prefix, sizeof prefix));
86636285Sbrian    else {
86736285Sbrian      if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
86836285Sbrian        log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
86936285Sbrian                  mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
87036285Sbrian        cx = NULL;
87136285Sbrian      }
87236285Sbrian      arg.cmdtab = cmds;
87336285Sbrian      arg.cmd = cmd;
87436285Sbrian      arg.argc = argc;
87536285Sbrian      arg.argn = argn+1;
87636285Sbrian      arg.argv = argv;
87736285Sbrian      arg.bundle = bundle;
87836285Sbrian      arg.cx = cx;
87936285Sbrian      arg.prompt = prompt;
88036285Sbrian      val = (*cmd->func) (&arg);
88136285Sbrian    }
88231343Sbrian  } else
88336285Sbrian    log_Printf(LogWARN, "%s: Invalid command\n",
88436285Sbrian              mkPrefix(argn+1, argv, prefix, sizeof prefix));
88526516Sbrian
88626516Sbrian  if (val == -1)
88736285Sbrian    log_Printf(LogWARN, "Usage: %s\n", cmd->syntax);
88828679Sbrian  else if (val)
88936285Sbrian    log_Printf(LogWARN, "%s: Failed %d\n",
89036285Sbrian              mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
89126516Sbrian
89226516Sbrian  return val;
8936059Samurai}
8946059Samurai
89537009Sbrianint
89637009Sbriancommand_Interpret(char *buff, int nb, char *argv[MAXARGS])
8976059Samurai{
8986059Samurai  char *cp;
8996059Samurai
9006059Samurai  if (nb > 0) {
9016059Samurai    cp = buff + strcspn(buff, "\r\n");
9026059Samurai    if (cp)
9036059Samurai      *cp = '\0';
90437009Sbrian    return MakeArgs(buff, argv, MAXARGS);
90537009Sbrian  }
90637009Sbrian  return 0;
90731121Sbrian}
9086059Samurai
90931822Sbrianstatic int
91031822Sbrianarghidden(int argc, char const *const *argv, int n)
91131822Sbrian{
91231822Sbrian  /* Is arg n of the given command to be hidden from the log ? */
91331828Sbrian
91431828Sbrian  /* set authkey xxxxx */
91531828Sbrian  /* set key xxxxx */
91631822Sbrian  if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
91731822Sbrian      (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
91831822Sbrian    return 1;
91931822Sbrian
92031828Sbrian  /* passwd xxxxx */
92131828Sbrian  if (n == 1 && !strncasecmp(argv[0], "p", 1))
92231828Sbrian    return 1;
92331828Sbrian
92436285Sbrian  /* set server port xxxxx .... */
92536285Sbrian  if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
92636285Sbrian      !strncasecmp(argv[1], "se", 2))
92736285Sbrian    return 1;
92836285Sbrian
92931822Sbrian  return 0;
93031822Sbrian}
93131822Sbrian
93231121Sbrianvoid
93336285Sbriancommand_Run(struct bundle *bundle, int argc, char const *const *argv,
93437008Sbrian           struct prompt *prompt, const char *label, struct datalink *cx)
93531121Sbrian{
93631156Sbrian  if (argc > 0) {
93736285Sbrian    if (log_IsKept(LogCOMMAND)) {
93831156Sbrian      static char buf[LINE_LEN];
93931156Sbrian      int f, n;
94031156Sbrian
94131156Sbrian      *buf = '\0';
94231156Sbrian      if (label) {
94331962Sbrian        strncpy(buf, label, sizeof buf - 3);
94431962Sbrian        buf[sizeof buf - 3] = '\0';
94531156Sbrian        strcat(buf, ": ");
94631156Sbrian      }
94731156Sbrian      n = strlen(buf);
94831156Sbrian      for (f = 0; f < argc; f++) {
94931962Sbrian        if (n < sizeof buf - 1 && f)
95031156Sbrian          buf[n++] = ' ';
95131822Sbrian        if (arghidden(argc, argv, f))
95236285Sbrian          strncpy(buf+n, "********", sizeof buf - n - 1);
95331822Sbrian        else
95431962Sbrian          strncpy(buf+n, argv[f], sizeof buf - n - 1);
95531156Sbrian        n += strlen(buf+n);
95631156Sbrian      }
95736285Sbrian      log_Printf(LogCOMMAND, "%s\n", buf);
95831156Sbrian    }
95937008Sbrian    FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
96031156Sbrian  }
9616059Samurai}
9626059Samurai
96331121Sbrianvoid
96436285Sbriancommand_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
96536285Sbrian              const char *label)
96631121Sbrian{
96731121Sbrian  int argc;
96837009Sbrian  char *argv[MAXARGS];
96931121Sbrian
97037009Sbrian  argc = command_Interpret(buff, nb, argv);
97137008Sbrian  command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
97231121Sbrian}
97331121Sbrian
9746059Samuraistatic int
97531343SbrianShowCommand(struct cmdargs const *arg)
9766059Samurai{
97736285Sbrian  if (!arg->prompt)
97836285Sbrian    log_Printf(LogWARN, "show: Cannot show without a prompt\n");
97936285Sbrian  else if (arg->argc > arg->argn)
98036285Sbrian    FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
98136285Sbrian             arg->prompt, arg->cx);
9826059Samurai  else
98336285Sbrian    prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
98426516Sbrian
98526516Sbrian  return 0;
9866059Samurai}
9876059Samurai
9886059Samuraistatic int
98931343SbrianTerminalCommand(struct cmdargs const *arg)
9906059Samurai{
99136285Sbrian  if (!arg->prompt) {
99236285Sbrian    log_Printf(LogWARN, "term: Need a prompt\n");
99326516Sbrian    return 1;
9946059Samurai  }
99536285Sbrian
99636285Sbrian  if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
99736285Sbrian    prompt_Printf(arg->prompt, "LCP state is [%s]\n",
99836285Sbrian                  State2Nam(arg->cx->physical->link.lcp.fsm.state));
99936285Sbrian    return 1;
10006059Samurai  }
100136285Sbrian
100236285Sbrian  datalink_Up(arg->cx, 0, 0);
100336285Sbrian  prompt_TtyTermMode(arg->prompt, arg->cx);
100436285Sbrian  return 0;
10056059Samurai}
10066059Samurai
10076059Samuraistatic int
100831343SbrianQuitCommand(struct cmdargs const *arg)
10096059Samurai{
101036285Sbrian  if (!arg->prompt || prompt_IsController(arg->prompt) ||
101136285Sbrian      (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
101236285Sbrian       (arg->prompt->auth & LOCAL_AUTH)))
101336285Sbrian    Cleanup(EX_NORMAL);
101436285Sbrian  if (arg->prompt)
101536285Sbrian    prompt_Destroy(arg->prompt, 1);
101626516Sbrian
101726516Sbrian  return 0;
10186059Samurai}
10196059Samurai
10206059Samuraistatic int
102136285SbrianOpenCommand(struct cmdargs const *arg)
10226059Samurai{
102337160Sbrian  if (arg->argc == arg->argn)
102437993Sbrian    bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
102537160Sbrian  else if (arg->argc == arg->argn + 1) {
102637160Sbrian    if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
102737385Sbrian      struct datalink *cx = arg->cx ?
102837385Sbrian        arg->cx : bundle2datalink(arg->bundle, NULL);
102937385Sbrian      if (cx) {
103037385Sbrian        if (cx->physical->link.lcp.fsm.state == ST_OPENED)
103137385Sbrian          fsm_Reopen(&cx->physical->link.lcp.fsm);
103237160Sbrian        else
103337993Sbrian          bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1);
103437160Sbrian      } else
103537160Sbrian        log_Printf(LogWARN, "open lcp: You must specify a link\n");
103637160Sbrian    } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
103737160Sbrian      struct fsm *fp;
10386059Samurai
103937210Sbrian      fp = &command_ChooseLink(arg)->ccp.fsm;
104037160Sbrian      if (fp->link->lcp.fsm.state != ST_OPENED)
104137160Sbrian        log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
104237160Sbrian      else if (fp->state == ST_OPENED)
104337160Sbrian        fsm_Reopen(fp);
104437160Sbrian      else {
104537160Sbrian        fp->open_mode = 0;	/* Not passive any more */
104637160Sbrian        if (fp->state == ST_STOPPED) {
104737160Sbrian          fsm_Down(fp);
104837160Sbrian          fsm_Up(fp);
104937160Sbrian        } else {
105037160Sbrian          fsm_Up(fp);
105137160Sbrian          fsm_Open(fp);
105237160Sbrian        }
105336285Sbrian      }
105437160Sbrian    } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) {
105537160Sbrian      if (arg->cx)
105637160Sbrian        log_Printf(LogWARN, "open ipcp: You need not specify a link\n");
105737160Sbrian      if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
105837160Sbrian        fsm_Reopen(&arg->bundle->ncp.ipcp.fsm);
105937160Sbrian      else
106037993Sbrian        bundle_Open(arg->bundle, NULL, PHYS_ALL, 1);
106137160Sbrian    } else
106237160Sbrian      return -1;
106336285Sbrian  } else
106436285Sbrian    return -1;
106536285Sbrian
106626516Sbrian  return 0;
10676059Samurai}
10686059Samurai
106925067Sbrianstatic int
107036285SbrianCloseCommand(struct cmdargs const *arg)
10716059Samurai{
107237007Sbrian  if (arg->argc == arg->argn)
107337007Sbrian    bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
107437007Sbrian  else if (arg->argc == arg->argn + 1) {
107537007Sbrian    if (!strcasecmp(arg->argv[arg->argn], "lcp"))
107637007Sbrian      bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
107737007Sbrian    else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
107837007Sbrian             !strcasecmp(arg->argv[arg->argn], "ccp!")) {
107937007Sbrian      struct fsm *fp;
10806059Samurai
108137210Sbrian      fp = &command_ChooseLink(arg)->ccp.fsm;
108237007Sbrian      if (fp->state == ST_OPENED) {
108337007Sbrian        fsm_Close(fp);
108437007Sbrian        if (arg->argv[arg->argn][3] == '!')
108537007Sbrian          fp->open_mode = 0;		/* Stay ST_CLOSED */
108637007Sbrian        else
108737007Sbrian          fp->open_mode = OPEN_PASSIVE;	/* Wait for the peer to start */
108837007Sbrian      }
108937007Sbrian    } else
109036285Sbrian      return -1;
109136285Sbrian  } else
109236285Sbrian    return -1;
109336285Sbrian
109436285Sbrian  return 0;
10956059Samurai}
10966059Samurai
109725067Sbrianstatic int
109836285SbrianDownCommand(struct cmdargs const *arg)
109911336Samurai{
110037018Sbrian  if (arg->argc == arg->argn) {
110137018Sbrian      if (arg->cx)
110237018Sbrian        datalink_Down(arg->cx, CLOSE_STAYDOWN);
110337018Sbrian      else
110437018Sbrian        bundle_Down(arg->bundle, CLOSE_STAYDOWN);
110537018Sbrian  } else if (arg->argc == arg->argn + 1) {
110637018Sbrian    if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
110737018Sbrian      if (arg->cx)
110837018Sbrian        datalink_Down(arg->cx, CLOSE_LCP);
110937018Sbrian      else
111037018Sbrian        bundle_Down(arg->bundle, CLOSE_LCP);
111137018Sbrian    } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
111237018Sbrian      struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
111337018Sbrian                                 &arg->bundle->ncp.mp.link.ccp.fsm;
111437060Sbrian      fsm2initial(fp);
111537018Sbrian    } else
111637018Sbrian      return -1;
111736285Sbrian  } else
111836285Sbrian    return -1;
111936285Sbrian
112036285Sbrian  return 0;
112125067Sbrian}
112225067Sbrian
112325067Sbrianstatic int
112436285SbrianSetModemSpeed(struct cmdargs const *arg)
112525067Sbrian{
112636285Sbrian  long speed;
112736285Sbrian  char *end;
112811336Samurai
112936285Sbrian  if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
113036285Sbrian    if (arg->argc > arg->argn+1) {
113136285Sbrian      log_Printf(LogWARN, "SetModemSpeed: Too many arguments");
113236285Sbrian      return -1;
113311336Samurai    }
113436285Sbrian    if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
113536285Sbrian      physical_SetSync(arg->cx->physical);
113636285Sbrian      return 0;
113736285Sbrian    }
113836285Sbrian    end = NULL;
113936285Sbrian    speed = strtol(arg->argv[arg->argn], &end, 10);
114036285Sbrian    if (*end) {
114136285Sbrian      log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
114236285Sbrian                arg->argv[arg->argn]);
114336285Sbrian      return -1;
114436285Sbrian    }
114536285Sbrian    if (physical_SetSpeed(arg->cx->physical, speed))
114636285Sbrian      return 0;
114736285Sbrian    log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
114836285Sbrian  } else
114936285Sbrian    log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
115024939Sbrian
115126516Sbrian  return -1;
115211336Samurai}
115311336Samurai
115425067Sbrianstatic int
115531343SbrianSetStoppedTimeout(struct cmdargs const *arg)
115628327Sbrian{
115736285Sbrian  struct link *l = &arg->cx->physical->link;
115836285Sbrian
115936285Sbrian  l->lcp.fsm.StoppedTimer.load = 0;
116036285Sbrian  l->ccp.fsm.StoppedTimer.load = 0;
116136285Sbrian  if (arg->argc <= arg->argn+2) {
116236285Sbrian    if (arg->argc > arg->argn) {
116336285Sbrian      l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
116436285Sbrian      if (arg->argc > arg->argn+1)
116536285Sbrian        l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
116628461Sbrian    }
116728327Sbrian    return 0;
116828327Sbrian  }
116928327Sbrian  return -1;
117028327Sbrian}
117128327Sbrian
117231081Sbrian#define ismask(x) \
117331081Sbrian  (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3)
117431081Sbrian
117528327Sbrianstatic int
117631343SbrianSetServer(struct cmdargs const *arg)
117726940Sbrian{
117826940Sbrian  int res = -1;
117926940Sbrian
118036285Sbrian  if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
118131081Sbrian    const char *port, *passwd, *mask;
118231081Sbrian
118331081Sbrian    /* What's what ? */
118436285Sbrian    port = arg->argv[arg->argn];
118536285Sbrian    if (arg->argc == arg->argn + 2) {
118636285Sbrian      passwd = arg->argv[arg->argn+1];
118736285Sbrian      mask = NULL;
118836285Sbrian    } else if (arg->argc == arg->argn + 3) {
118936285Sbrian      passwd = arg->argv[arg->argn+1];
119036285Sbrian      mask = arg->argv[arg->argn+2];
119131081Sbrian      if (!ismask(mask))
119231081Sbrian        return -1;
119336285Sbrian    } else if (strcasecmp(port, "none") == 0) {
119436285Sbrian      if (server_Close(arg->bundle))
119536285Sbrian        log_Printf(LogPHASE, "Disabled server port.\n");
119636285Sbrian      return 0;
119731081Sbrian    } else
119836285Sbrian      return -1;
119931081Sbrian
120036285Sbrian    strncpy(server.passwd, passwd, sizeof server.passwd - 1);
120136285Sbrian    server.passwd[sizeof server.passwd - 1] = '\0';
120231081Sbrian
120336285Sbrian    if (*port == '/') {
120431081Sbrian      mode_t imask;
120536285Sbrian      char *ptr, name[LINE_LEN + 12];
120628679Sbrian
120731081Sbrian      if (mask != NULL) {
120828679Sbrian	unsigned m;
120928679Sbrian
121031081Sbrian	if (sscanf(mask, "%o", &m) == 1)
121131081Sbrian	  imask = m;
121231081Sbrian        else
121331081Sbrian          return -1;
121431081Sbrian      } else
121531081Sbrian        imask = (mode_t)-1;
121636285Sbrian
121736285Sbrian      ptr = strstr(port, "%d");
121836285Sbrian      if (ptr) {
121936285Sbrian        snprintf(name, sizeof name, "%.*s%d%s",
122037210Sbrian                 (int)(ptr - port), port, arg->bundle->unit, ptr + 2);
122136285Sbrian        port = name;
122236285Sbrian      }
122336285Sbrian      res = server_LocalOpen(arg->bundle, port, imask);
122427346Sbrian    } else {
122536285Sbrian      int iport, add = 0;
122628679Sbrian
122731081Sbrian      if (mask != NULL)
122831081Sbrian        return -1;
122928679Sbrian
123036285Sbrian      if (*port == '+') {
123136285Sbrian        port++;
123236285Sbrian        add = 1;
123336285Sbrian      }
123431081Sbrian      if (strspn(port, "0123456789") != strlen(port)) {
123531081Sbrian        struct servent *s;
123631081Sbrian
123731081Sbrian        if ((s = getservbyname(port, "tcp")) == NULL) {
123831081Sbrian	  iport = 0;
123936285Sbrian	  log_Printf(LogWARN, "%s: Invalid port or service\n", port);
124028679Sbrian	} else
124131081Sbrian	  iport = ntohs(s->s_port);
124227346Sbrian      } else
124331081Sbrian        iport = atoi(port);
124436285Sbrian
124536285Sbrian      if (iport) {
124636285Sbrian        if (add)
124736285Sbrian          iport += arg->bundle->unit;
124836285Sbrian        res = server_TcpOpen(arg->bundle, iport);
124936285Sbrian      } else
125036285Sbrian        res = -1;
125127346Sbrian    }
125231081Sbrian  }
125326940Sbrian
125426940Sbrian  return res;
125526940Sbrian}
125626940Sbrian
125726940Sbrianstatic int
125831343SbrianSetEscape(struct cmdargs const *arg)
12596059Samurai{
12606059Samurai  int code;
126136285Sbrian  int argc = arg->argc - arg->argn;
126236285Sbrian  char const *const *argv = arg->argv + arg->argn;
12636059Samurai
12646059Samurai  for (code = 0; code < 33; code++)
126536285Sbrian    arg->cx->physical->async.cfg.EscMap[code] = 0;
126631343Sbrian
12676059Samurai  while (argc-- > 0) {
12686059Samurai    sscanf(*argv++, "%x", &code);
12696059Samurai    code &= 0xff;
127036285Sbrian    arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
127136285Sbrian    arg->cx->physical->async.cfg.EscMap[32] = 1;
12726059Samurai  }
127326516Sbrian  return 0;
12746059Samurai}
12756059Samurai
12766059Samuraistatic int
127731343SbrianSetInterfaceAddr(struct cmdargs const *arg)
12786059Samurai{
127936285Sbrian  struct ipcp *ipcp = &arg->bundle->ncp.ipcp;
128032267Sbrian  const char *hisaddr;
128132267Sbrian
128240561Sbrian  if (arg->argc > arg->argn + 4)
128340561Sbrian    return -1;
128440561Sbrian
128532267Sbrian  hisaddr = NULL;
128644874Sbrian  memset(&ipcp->cfg.my_range, '\0', sizeof ipcp->cfg.my_range);
128744874Sbrian  memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
128836285Sbrian  ipcp->cfg.HaveTriggerAddress = 0;
128936285Sbrian  ipcp->cfg.netmask.s_addr = INADDR_ANY;
129036285Sbrian  iplist_reset(&ipcp->cfg.peer_list);
129128394Sbrian
129236285Sbrian  if (arg->argc > arg->argn) {
129343313Sbrian    if (!ParseAddr(ipcp, arg->argv[arg->argn],
129436285Sbrian                   &ipcp->cfg.my_range.ipaddr, &ipcp->cfg.my_range.mask,
129536285Sbrian                   &ipcp->cfg.my_range.width))
129628679Sbrian      return 1;
129736285Sbrian    if (arg->argc > arg->argn+1) {
129836285Sbrian      hisaddr = arg->argv[arg->argn+1];
129936285Sbrian      if (arg->argc > arg->argn+2) {
130044455Sbrian        ipcp->ifmask = ipcp->cfg.netmask = GetIpAddr(arg->argv[arg->argn+2]);
130136285Sbrian	if (arg->argc > arg->argn+3) {
130236285Sbrian	  ipcp->cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
130336285Sbrian	  ipcp->cfg.HaveTriggerAddress = 1;
13049440Samurai	}
13056059Samurai      }
13066059Samurai    }
13076059Samurai  }
130828394Sbrian
130940561Sbrian  /* 0.0.0.0 means any address (0 bits) */
131036285Sbrian  if (ipcp->cfg.my_range.ipaddr.s_addr == INADDR_ANY) {
131136285Sbrian    ipcp->cfg.my_range.mask.s_addr = INADDR_ANY;
131236285Sbrian    ipcp->cfg.my_range.width = 0;
13136059Samurai  }
131436285Sbrian  ipcp->my_ip.s_addr = ipcp->cfg.my_range.ipaddr.s_addr;
131547648Sbrian  bundle_AdjustFilters(arg->bundle, &ipcp->my_ip, NULL);
131636285Sbrian
131736285Sbrian  if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
131836928Sbrian                                  arg->bundle->phys_type.all & PHYS_AUTO))
131932267Sbrian    return 4;
132031121Sbrian
132126516Sbrian  return 0;
13226059Samurai}
13236059Samurai
132418752Sjkhstatic int
132544305SbrianSetRetry(int argc, char const *const *argv, u_int *timeout, u_int *maxreq,
132644305Sbrian          u_int *maxtrm, int def)
132744305Sbrian{
132844305Sbrian  if (argc == 0) {
132944305Sbrian    *timeout = DEF_FSMRETRY;
133044305Sbrian    *maxreq = def;
133144305Sbrian    if (maxtrm != NULL)
133244305Sbrian      *maxtrm = def;
133344305Sbrian  } else {
133444305Sbrian    long l = atol(argv[0]);
133544305Sbrian
133644305Sbrian    if (l < MIN_FSMRETRY) {
133744305Sbrian      log_Printf(LogWARN, "%ld: Invalid FSM retry period - min %d\n",
133844305Sbrian                 l, MIN_FSMRETRY);
133944305Sbrian      return 1;
134044305Sbrian    } else
134144305Sbrian      *timeout = l;
134244305Sbrian
134344305Sbrian    if (argc > 1) {
134444305Sbrian      l = atol(argv[1]);
134544305Sbrian      if (l < 1) {
134644305Sbrian        log_Printf(LogWARN, "%ld: Invalid FSM REQ tries - changed to 1\n", l);
134744305Sbrian        l = 1;
134844305Sbrian      }
134944305Sbrian      *maxreq = l;
135044305Sbrian
135144305Sbrian      if (argc > 2 && maxtrm != NULL) {
135244305Sbrian        l = atol(argv[2]);
135344305Sbrian        if (l < 1) {
135444305Sbrian          log_Printf(LogWARN, "%ld: Invalid FSM TRM tries - changed to 1\n", l);
135544305Sbrian          l = 1;
135644305Sbrian        }
135744305Sbrian        *maxtrm = l;
135844305Sbrian      }
135944305Sbrian    }
136044305Sbrian  }
136144305Sbrian
136244305Sbrian  return 0;
136344305Sbrian}
136444305Sbrian
136544305Sbrianstatic int
136631343SbrianSetVariable(struct cmdargs const *arg)
13676059Samurai{
136837210Sbrian  long long_val, param = (long)arg->cmd->args;
136937210Sbrian  int mode, dummyint;
137031343Sbrian  const char *argp;
137136285Sbrian  struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
137236285Sbrian  const char *err = NULL;
137336285Sbrian  struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
137436285Sbrian  struct in_addr dummyaddr, *addr;
13756059Samurai
137636285Sbrian  if (arg->argc > arg->argn)
137736285Sbrian    argp = arg->argv[arg->argn];
137826551Sbrian  else
137931343Sbrian    argp = "";
138026551Sbrian
138136285Sbrian  if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
138236285Sbrian    log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
138336285Sbrian              arg->cmd->name);
138436285Sbrian    return 1;
138536285Sbrian  } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
138636285Sbrian    log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
138736285Sbrian              arg->cmd->name, cx->name);
138836285Sbrian    cx = NULL;
138936285Sbrian  }
139036285Sbrian
139126551Sbrian  switch (param) {
139228679Sbrian  case VAR_AUTHKEY:
139340622Sbrian    switch (bundle_Phase(arg->bundle)) {
139440622Sbrian      case PHASE_DEAD:
139540622Sbrian      case PHASE_ESTABLISH:
139640622Sbrian        strncpy(arg->bundle->cfg.auth.key, argp,
139740622Sbrian                sizeof arg->bundle->cfg.auth.key - 1);
139840622Sbrian        arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
139940622Sbrian        break;
140040622Sbrian      default:
140140622Sbrian        err = "set authkey: Only available at phase DEAD/ESTABLISH\n";
140240622Sbrian        log_Printf(LogWARN, err);
140340622Sbrian        break;
140436285Sbrian    }
140528679Sbrian    break;
140637210Sbrian
140728679Sbrian  case VAR_AUTHNAME:
140840622Sbrian    switch (bundle_Phase(arg->bundle)) {
140940622Sbrian      case PHASE_DEAD:
141040622Sbrian      case PHASE_ESTABLISH:
141140622Sbrian        strncpy(arg->bundle->cfg.auth.name, argp,
141240622Sbrian                sizeof arg->bundle->cfg.auth.name - 1);
141340622Sbrian        arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0';
141440622Sbrian        break;
141540622Sbrian      default:
141640622Sbrian        err = "set authname: Only available at phase DEAD/ESTABLISH\n";
141740622Sbrian        log_Printf(LogWARN, err);
141840622Sbrian        break;
141936285Sbrian    }
142028679Sbrian    break;
142137210Sbrian
142236285Sbrian  case VAR_AUTOLOAD:
142336285Sbrian    if (arg->argc == arg->argn + 2 || arg->argc == arg->argn + 4) {
142436285Sbrian      arg->bundle->autoload.running = 1;
142536285Sbrian      arg->bundle->cfg.autoload.max.timeout = atoi(arg->argv[arg->argn]);
142636285Sbrian      arg->bundle->cfg.autoload.max.packets = atoi(arg->argv[arg->argn + 1]);
142736285Sbrian      if (arg->argc == arg->argn + 4) {
142836285Sbrian        arg->bundle->cfg.autoload.min.timeout = atoi(arg->argv[arg->argn + 2]);
142936285Sbrian        arg->bundle->cfg.autoload.min.packets = atoi(arg->argv[arg->argn + 3]);
143036285Sbrian      } else {
143136285Sbrian        arg->bundle->cfg.autoload.min.timeout = 0;
143236285Sbrian        arg->bundle->cfg.autoload.min.packets = 0;
143336285Sbrian      }
143436285Sbrian    } else {
143536285Sbrian      err = "Set autoload requires two or four arguments\n";
143636285Sbrian      log_Printf(LogWARN, err);
143736285Sbrian    }
143836285Sbrian    break;
143937210Sbrian
144028679Sbrian  case VAR_DIAL:
144136285Sbrian    strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
144236285Sbrian    cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
144328679Sbrian    break;
144437210Sbrian
144528679Sbrian  case VAR_LOGIN:
144636285Sbrian    strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
144736285Sbrian    cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
144828679Sbrian    break;
144937210Sbrian
145036285Sbrian  case VAR_WINSIZE:
145136285Sbrian    if (arg->argc > arg->argn) {
145236285Sbrian      l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
145336285Sbrian      if (l->ccp.cfg.deflate.out.winsize < 8 ||
145436285Sbrian          l->ccp.cfg.deflate.out.winsize > 15) {
145536285Sbrian          log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
145636285Sbrian                    l->ccp.cfg.deflate.out.winsize);
145736285Sbrian          l->ccp.cfg.deflate.out.winsize = 15;
145836285Sbrian      }
145936285Sbrian      if (arg->argc > arg->argn+1) {
146036285Sbrian        l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
146136285Sbrian        if (l->ccp.cfg.deflate.in.winsize < 8 ||
146236285Sbrian            l->ccp.cfg.deflate.in.winsize > 15) {
146336285Sbrian            log_Printf(LogWARN, "%d: Invalid incoming window size\n",
146436285Sbrian                      l->ccp.cfg.deflate.in.winsize);
146536285Sbrian            l->ccp.cfg.deflate.in.winsize = 15;
146636285Sbrian        }
146736285Sbrian      } else
146836285Sbrian        l->ccp.cfg.deflate.in.winsize = 0;
146936285Sbrian    } else {
147036285Sbrian      err = "No window size specified\n";
147136285Sbrian      log_Printf(LogWARN, err);
147236285Sbrian    }
147336285Sbrian    break;
147437210Sbrian
147528679Sbrian  case VAR_DEVICE:
147636285Sbrian    physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
147736285Sbrian                           arg->argv + arg->argn);
147836285Sbrian    break;
147937210Sbrian
148036285Sbrian  case VAR_ACCMAP:
148136285Sbrian    if (arg->argc > arg->argn) {
148237210Sbrian      u_long ulong_val;
148336285Sbrian      sscanf(argp, "%lx", &ulong_val);
148437210Sbrian      cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val;
148536285Sbrian    } else {
148636285Sbrian      err = "No accmap specified\n";
148736285Sbrian      log_Printf(LogWARN, err);
148836285Sbrian    }
148936285Sbrian    break;
149037210Sbrian
149136285Sbrian  case VAR_MODE:
149236285Sbrian    mode = Nam2mode(argp);
149336285Sbrian    if (mode == PHYS_NONE || mode == PHYS_ALL) {
149436285Sbrian      log_Printf(LogWARN, "%s: Invalid mode\n", argp);
149536285Sbrian      return -1;
149636285Sbrian    }
149736285Sbrian    bundle_SetMode(arg->bundle, cx, mode);
149836285Sbrian    break;
149937210Sbrian
150036285Sbrian  case VAR_MRRU:
150140622Sbrian    switch (bundle_Phase(arg->bundle)) {
150240622Sbrian      case PHASE_DEAD:
150340622Sbrian        break;
150440622Sbrian      case PHASE_ESTABLISH:
150540622Sbrian        /* Make sure none of our links are DATALINK_LCP or greater */
150640622Sbrian        if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
150740622Sbrian          log_Printf(LogWARN, "mrru: Only changable before LCP negotiations\n");
150840622Sbrian          return 1;
150940622Sbrian        }
151040622Sbrian        break;
151140622Sbrian      default:
151240622Sbrian        log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n");
151340622Sbrian        return 1;
151429696Sbrian    }
151537210Sbrian    long_val = atol(argp);
151637210Sbrian    if (long_val && long_val < MIN_MRU) {
151737210Sbrian      log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU);
151837210Sbrian      return 1;
151937210Sbrian    } else if (long_val > MAX_MRU) {
152037210Sbrian      log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
152137210Sbrian      return 1;
152237210Sbrian    } else
152337210Sbrian      arg->bundle->ncp.mp.cfg.mrru = long_val;
152428679Sbrian    break;
152537210Sbrian
152636285Sbrian  case VAR_MRU:
152737210Sbrian    long_val = atol(argp);
152837210Sbrian    if (long_val == 0)
152937210Sbrian      l->lcp.cfg.mru = DEF_MRU;
153037210Sbrian    else if (long_val < MIN_MRU) {
153137210Sbrian      log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
153237210Sbrian      return 1;
153337210Sbrian    } else if (long_val > MAX_MRU) {
153437210Sbrian      log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
153537210Sbrian      return 1;
153637210Sbrian    } else
153737210Sbrian      l->lcp.cfg.mru = long_val;
153828679Sbrian    break;
153937210Sbrian
154036285Sbrian  case VAR_MTU:
154137210Sbrian    long_val = atol(argp);
154237210Sbrian    if (long_val && long_val < MIN_MTU) {
154337210Sbrian      log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
154437210Sbrian      return 1;
154537210Sbrian    } else if (long_val > MAX_MTU) {
154637210Sbrian      log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
154737210Sbrian      return 1;
154837210Sbrian    } else
154937210Sbrian      arg->bundle->cfg.mtu = long_val;
155036285Sbrian    break;
155137210Sbrian
155236285Sbrian  case VAR_OPENMODE:
155336285Sbrian    if (strcasecmp(argp, "active") == 0)
155436285Sbrian      cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
155536285Sbrian        atoi(arg->argv[arg->argn+1]) : 1;
155636285Sbrian    else if (strcasecmp(argp, "passive") == 0)
155736285Sbrian      cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
155836285Sbrian    else {
155936285Sbrian      err = "%s: Invalid openmode\n";
156036285Sbrian      log_Printf(LogWARN, err, argp);
156136285Sbrian    }
156236285Sbrian    break;
156337210Sbrian
156428679Sbrian  case VAR_PHONE:
156536285Sbrian    strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
156636285Sbrian    cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
156738174Sbrian    cx->phone.alt = cx->phone.next = NULL;
156828679Sbrian    break;
156937210Sbrian
157028679Sbrian  case VAR_HANGUP:
157136285Sbrian    strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
157236285Sbrian    cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
157328679Sbrian    break;
157437210Sbrian
157536285Sbrian  case VAR_IDLETIMEOUT:
157636285Sbrian    if (arg->argc > arg->argn+1)
157736285Sbrian      err = "Too many idle timeout values\n";
157836285Sbrian    else if (arg->argc == arg->argn+1)
157936285Sbrian      bundle_SetIdleTimer(arg->bundle, atoi(argp));
158036285Sbrian    if (err)
158136285Sbrian      log_Printf(LogWARN, err);
158229549Sbrian    break;
158337210Sbrian
158436285Sbrian  case VAR_LQRPERIOD:
158537210Sbrian    long_val = atol(argp);
158637210Sbrian    if (long_val < MIN_LQRPERIOD) {
158737210Sbrian      log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n",
158837210Sbrian                 long_val, MIN_LQRPERIOD);
158937210Sbrian      return 1;
159036285Sbrian    } else
159137210Sbrian      l->lcp.cfg.lqrperiod = long_val;
159236285Sbrian    break;
159337210Sbrian
159436285Sbrian  case VAR_LCPRETRY:
159544305Sbrian    return SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
159644305Sbrian                    &cx->physical->link.lcp.cfg.fsm.timeout,
159744305Sbrian                    &cx->physical->link.lcp.cfg.fsm.maxreq,
159844305Sbrian                    &cx->physical->link.lcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
159936285Sbrian    break;
160037210Sbrian
160136285Sbrian  case VAR_CHAPRETRY:
160244305Sbrian    return SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
160344305Sbrian                    &cx->chap.auth.cfg.fsm.timeout,
160444305Sbrian                    &cx->chap.auth.cfg.fsm.maxreq, NULL, DEF_FSMAUTHTRIES);
160536285Sbrian    break;
160637210Sbrian
160736285Sbrian  case VAR_PAPRETRY:
160844305Sbrian    return SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
160944305Sbrian                    &cx->pap.cfg.fsm.timeout, &cx->pap.cfg.fsm.maxreq,
161044305Sbrian                    NULL, DEF_FSMAUTHTRIES);
161136285Sbrian    break;
161237210Sbrian
161336285Sbrian  case VAR_CCPRETRY:
161444305Sbrian    return SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
161544305Sbrian                    &l->ccp.cfg.fsm.timeout, &l->ccp.cfg.fsm.maxreq,
161644305Sbrian                    &l->ccp.cfg.fsm.maxtrm, DEF_FSMTRIES);
161736285Sbrian    break;
161837210Sbrian
161936285Sbrian  case VAR_IPCPRETRY:
162044305Sbrian    return SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
162144305Sbrian                    &arg->bundle->ncp.ipcp.cfg.fsm.timeout,
162244305Sbrian                    &arg->bundle->ncp.ipcp.cfg.fsm.maxreq,
162344305Sbrian                    &arg->bundle->ncp.ipcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
162436285Sbrian    break;
162537210Sbrian
162636285Sbrian  case VAR_NBNS:
162736285Sbrian  case VAR_DNS:
162836285Sbrian    if (param == VAR_DNS)
162936285Sbrian      addr = arg->bundle->ncp.ipcp.cfg.ns.dns;
163036285Sbrian    else
163136285Sbrian      addr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
163236285Sbrian
163336285Sbrian    addr[0].s_addr = addr[1].s_addr = INADDR_ANY;
163436285Sbrian
163536285Sbrian    if (arg->argc > arg->argn) {
163643313Sbrian      ParseAddr(&arg->bundle->ncp.ipcp, arg->argv[arg->argn],
163736285Sbrian                addr, &dummyaddr, &dummyint);
163836285Sbrian      if (arg->argc > arg->argn+1)
163943313Sbrian        ParseAddr(&arg->bundle->ncp.ipcp, arg->argv[arg->argn + 1],
164036285Sbrian                  addr + 1, &dummyaddr, &dummyint);
164136285Sbrian
164236285Sbrian      if (addr[1].s_addr == INADDR_ANY)
164336285Sbrian        addr[1].s_addr = addr[0].s_addr;
164436285Sbrian      if (addr[0].s_addr == INADDR_ANY)
164536285Sbrian        addr[0].s_addr = addr[1].s_addr;
164636285Sbrian    }
164736285Sbrian    break;
164838174Sbrian
164938174Sbrian  case VAR_CALLBACK:
165038174Sbrian    cx->cfg.callback.opmask = 0;
165138174Sbrian    for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) {
165238174Sbrian      if (!strcasecmp(arg->argv[dummyint], "auth"))
165338174Sbrian        cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH);
165438174Sbrian      else if (!strcasecmp(arg->argv[dummyint], "cbcp"))
165538174Sbrian        cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP);
165638174Sbrian      else if (!strcasecmp(arg->argv[dummyint], "e.164")) {
165738174Sbrian        if (dummyint == arg->argc - 1)
165838174Sbrian          log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n");
165938174Sbrian        else {
166038174Sbrian          cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164);
166138174Sbrian          strncpy(cx->cfg.callback.msg, arg->argv[++dummyint],
166238174Sbrian                  sizeof cx->cfg.callback.msg - 1);
166338174Sbrian          cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0';
166438174Sbrian        }
166538174Sbrian      } else if (!strcasecmp(arg->argv[dummyint], "none"))
166638174Sbrian        cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE);
166738174Sbrian      else
166838174Sbrian        return -1;
166938174Sbrian    }
167038174Sbrian    if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE))
167138174Sbrian      cx->cfg.callback.opmask = 0;
167238174Sbrian    break;
167338174Sbrian
167438174Sbrian  case VAR_CBCP:
167538174Sbrian    cx->cfg.cbcp.delay = 0;
167638174Sbrian    *cx->cfg.cbcp.phone = '\0';
167738174Sbrian    cx->cfg.cbcp.fsmretry = DEF_FSMRETRY;
167838174Sbrian    if (arg->argc > arg->argn) {
167938174Sbrian      strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn],
168038174Sbrian              sizeof cx->cfg.cbcp.phone - 1);
168138174Sbrian      cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0';
168238174Sbrian      if (arg->argc > arg->argn + 1) {
168338174Sbrian        cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]);
168438174Sbrian        if (arg->argc > arg->argn + 2) {
168538174Sbrian          long_val = atol(arg->argv[arg->argn + 2]);
168638174Sbrian          if (long_val < MIN_FSMRETRY)
168738174Sbrian            log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n",
168838174Sbrian                       long_val, MIN_FSMRETRY);
168938174Sbrian          else
169038174Sbrian            cx->cfg.cbcp.fsmretry = long_val;
169138174Sbrian        }
169238174Sbrian      }
169338174Sbrian    }
169438174Sbrian    break;
169538544Sbrian
169638544Sbrian  case VAR_CHOKED:
169738544Sbrian    arg->bundle->cfg.choked.timeout = atoi(argp);
169838544Sbrian    if (arg->bundle->cfg.choked.timeout <= 0)
169938544Sbrian      arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT;
170038544Sbrian    break;
170140665Sbrian
170240665Sbrian  case VAR_SENDPIPE:
170340665Sbrian    long_val = atol(argp);
170440665Sbrian    arg->bundle->ncp.ipcp.cfg.sendpipe = long_val;
170540665Sbrian    break;
170640665Sbrian
170740665Sbrian  case VAR_RECVPIPE:
170840665Sbrian    long_val = atol(argp);
170940665Sbrian    arg->bundle->ncp.ipcp.cfg.recvpipe = long_val;
171040665Sbrian    break;
171143313Sbrian
171243313Sbrian#ifndef NORADIUS
171343313Sbrian  case VAR_RADIUS:
171443313Sbrian    if (!*argp)
171543313Sbrian      *arg->bundle->radius.cfg.file = '\0';
171643313Sbrian    else if (access(argp, R_OK)) {
171743313Sbrian      log_Printf(LogWARN, "%s: %s\n", argp, strerror(errno));
171843313Sbrian      return 1;
171943313Sbrian    } else {
172043313Sbrian      strncpy(arg->bundle->radius.cfg.file, argp,
172143313Sbrian              sizeof arg->bundle->radius.cfg.file - 1);
172243313Sbrian      arg->bundle->radius.cfg.file
172343313Sbrian        [sizeof arg->bundle->radius.cfg.file - 1] = '\0';
172443313Sbrian    }
172543313Sbrian    break;
172643313Sbrian#endif
172744073Sbrian
172844073Sbrian  case VAR_CD:
172944073Sbrian    if (*argp) {
173044073Sbrian      long_val = atol(argp);
173144073Sbrian      if (long_val < 0)
173244073Sbrian        long_val = 0;
173344073Sbrian      cx->physical->cfg.cd.delay = long_val;
173444073Sbrian      cx->physical->cfg.cd.required = argp[strlen(argp)-1] == '!';
173544073Sbrian    } else {
173644073Sbrian      cx->physical->cfg.cd.delay = DEF_CDDELAY;
173744073Sbrian      cx->physical->cfg.cd.required = 0;
173844073Sbrian    }
173944073Sbrian    break;
174036285Sbrian
174146686Sbrian  case VAR_PARITY:
174246686Sbrian    if (arg->argc == arg->argn + 1)
174346686Sbrian      return physical_SetParity(arg->cx->physical, argp);
174446686Sbrian    else {
174546686Sbrian      err = "Parity value must be odd, even or none\n";
174646686Sbrian      log_Printf(LogWARN, err);
174746686Sbrian    }
174846686Sbrian    break;
17496059Samurai
175046686Sbrian  case VAR_CRTSCTS:
175146686Sbrian    if (strcasecmp(argp, "on") == 0)
175236285Sbrian      physical_SetRtsCts(arg->cx->physical, 1);
175346686Sbrian    else if (strcasecmp(argp, "off") == 0)
175436285Sbrian      physical_SetRtsCts(arg->cx->physical, 0);
175546686Sbrian    else {
175646686Sbrian      err = "RTS/CTS value must be on or off\n";
175746686Sbrian      log_Printf(LogWARN, err);
175846686Sbrian    }
175946686Sbrian    break;
176020812Sjkh  }
176146686Sbrian
176246686Sbrian  return err ? 1 : 0;
176320812Sjkh}
176420812Sjkh
176530715Sbrianstatic struct cmdtab const SetCommands[] = {
176636285Sbrian  {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
176736285Sbrian  "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
176828679Sbrian  {"authkey", "key", SetVariable, LOCAL_AUTH,
176936285Sbrian  "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
177028679Sbrian  {"authname", NULL, SetVariable, LOCAL_AUTH,
177136285Sbrian  "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
177236285Sbrian  {"autoload", NULL, SetVariable, LOCAL_AUTH,
177336285Sbrian  "auto link [de]activation", "set autoload maxtime maxload mintime minload",
177436285Sbrian  (const void *)VAR_AUTOLOAD},
177538174Sbrian  {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
177638174Sbrian  "callback control", "set callback [none|auth|cbcp|"
177738174Sbrian  "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK},
177838174Sbrian  {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
177938174Sbrian  "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]",
178038174Sbrian  (const void *)VAR_CBCP},
178144305Sbrian  {"ccpretry", "ccpretries", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
178244305Sbrian   "CCP retries", "set ccpretry value [attempts]", (const void *)VAR_CCPRETRY},
178344073Sbrian  {"cd", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Carrier delay requirement",
178444073Sbrian   "set cd value[!]", (const void *)VAR_CD},
178544305Sbrian  {"chapretry", "chapretries", SetVariable, LOCAL_AUTH | LOCAL_CX,
178644305Sbrian   "CHAP retries", "set chapretry value [attempts]",
178744305Sbrian   (const void *)VAR_CHAPRETRY},
178838544Sbrian  {"choked", NULL, SetVariable, LOCAL_AUTH,
178938544Sbrian  "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED},
179046686Sbrian  {"ctsrts", "crtscts", SetVariable, LOCAL_AUTH | LOCAL_CX,
179146686Sbrian   "Use hardware flow control", "set ctsrts [on|off]",
179246686Sbrian   (const char *)VAR_CRTSCTS},
179336285Sbrian  {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
179436285Sbrian  "deflate window sizes", "set deflate out-winsize in-winsize",
179536285Sbrian  (const void *) VAR_WINSIZE},
179636285Sbrian  {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
179746686Sbrian  "physical device name", "set device|line device-name[,device-name]",
179836285Sbrian  (const void *) VAR_DEVICE},
179936285Sbrian  {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
180036285Sbrian  "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
180136285Sbrian  {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
180236285Sbrian  "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
180336285Sbrian  {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
180436285Sbrian  "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]"},
180536285Sbrian  {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
180636285Sbrian  "escape characters", "set escape hex-digit ..."},
180736285Sbrian  {"filter", NULL, filter_Set, LOCAL_AUTH,
180836285Sbrian  "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
180936285Sbrian  "[src_addr[/width]] [dst_addr[/width]] [tcp|udp|icmp [src [lt|eq|gt port]] "
181036285Sbrian  "[dst [lt|eq|gt port]] [estab] [syn] [finrst]]"},
181136285Sbrian  {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
181236285Sbrian  "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
181336285Sbrian  {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
181431343Sbrian  "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
181544305Sbrian  {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries",
181644305Sbrian   "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY},
181744305Sbrian  {"lcpretry", "lcpretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "LCP retries",
181844305Sbrian   "set lcpretry value [attempts]", (const void *)VAR_LCPRETRY},
181936712Sbrian  {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
182038622Sbrian  "set log [local] [+|-]async|cbcp|ccp|chat|command|connect|debug|hdlc|id0|"
182138622Sbrian  "ipcp|lcp|lqm|phase|tcp/ip|timer|tun..."},
182236285Sbrian  {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
182336285Sbrian  "login script", "set login chat-script", (const void *) VAR_LOGIN},
182436285Sbrian  {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
182536285Sbrian  "LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
182636285Sbrian  {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
182736285Sbrian  "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
182836285Sbrian  {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
182936285Sbrian  "set mrru value", (const void *)VAR_MRRU},
183036285Sbrian  {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
183136285Sbrian  "MRU value", "set mru value", (const void *)VAR_MRU},
183236285Sbrian  {"mtu", NULL, SetVariable, LOCAL_AUTH,
183336285Sbrian  "interface MTU value", "set mtu value", (const void *)VAR_MTU},
183436285Sbrian  {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
183536285Sbrian  "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
183636285Sbrian  {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
183736285Sbrian  "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
183844305Sbrian  {"papretry", "papretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "PAP retries",
183944305Sbrian   "set papretry value [attempts]", (const void *)VAR_PAPRETRY},
184046686Sbrian  {"parity", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "serial parity",
184146686Sbrian   "set parity [odd|even|none]", (const void *)VAR_PARITY},
184236285Sbrian  {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
184336285Sbrian  "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
184440679Sbrian  {"proctitle", "title", SetProcTitle, LOCAL_AUTH,
184540679Sbrian  "Process title", "set proctitle [value]"},
184643313Sbrian#ifndef NORADIUS
184743313Sbrian  {"radius", NULL, SetVariable, LOCAL_AUTH,
184843313Sbrian  "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS},
184943313Sbrian#endif
185036285Sbrian  {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
185136285Sbrian  "Reconnect timeout", "set reconnect value ntries"},
185240665Sbrian  {"recvpipe", NULL, SetVariable, LOCAL_AUTH,
185340665Sbrian  "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE},
185436285Sbrian  {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
185544468Sbrian  "Redial timeout", "set redial secs[+inc[-incmax]][.next] [attempts]"},
185640665Sbrian  {"sendpipe", NULL, SetVariable, LOCAL_AUTH,
185740665Sbrian  "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE},
185828679Sbrian  {"server", "socket", SetServer, LOCAL_AUTH,
185936774Sbrian  "server port", "set server|socket TcpPort|LocalName|none password [mask]"},
186036285Sbrian  {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
186146686Sbrian  "physical speed", "set speed value|sync"},
186236285Sbrian  {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
186336285Sbrian  "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
186436285Sbrian  {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
186536285Sbrian  "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
186636285Sbrian  {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
186736285Sbrian  "vj values", "set vj slots|slotcomp [value]"},
186836285Sbrian  {"weight", NULL, mp_SetDatalinkWeight, LOCAL_AUTH | LOCAL_CX,
186936285Sbrian  "datalink weighting", "set weight n"},
187028679Sbrian  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
187131343Sbrian  "Display this message", "set help|? [command]", SetCommands},
187228679Sbrian  {NULL, NULL, NULL},
18736059Samurai};
18746059Samurai
18756059Samuraistatic int
187631343SbrianSetCommand(struct cmdargs const *arg)
18776059Samurai{
187836285Sbrian  if (arg->argc > arg->argn)
187936285Sbrian    FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
188036285Sbrian             arg->prompt, arg->cx);
188136285Sbrian  else if (arg->prompt)
188236285Sbrian    prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
188326516Sbrian	    " syntax help.\n");
18846059Samurai  else
188536285Sbrian    log_Printf(LogWARN, "set command must have arguments\n");
188626516Sbrian
188726516Sbrian  return 0;
18886059Samurai}
18896059Samurai
18906059Samuraistatic int
189131343SbrianAddCommand(struct cmdargs const *arg)
18926059Samurai{
18936059Samurai  struct in_addr dest, gateway, netmask;
189436285Sbrian  int gw, addrs;
18956059Samurai
189636285Sbrian  if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
189731598Sbrian    return -1;
189831598Sbrian
189936285Sbrian  addrs = 0;
190036285Sbrian  if (arg->argc == arg->argn+2) {
190136285Sbrian    if (!strcasecmp(arg->argv[arg->argn], "default"))
190236285Sbrian      dest.s_addr = netmask.s_addr = INADDR_ANY;
190331598Sbrian    else {
190436285Sbrian      int width;
190536285Sbrian
190643313Sbrian      if (!ParseAddr(&arg->bundle->ncp.ipcp, arg->argv[arg->argn],
190736285Sbrian	             &dest, &netmask, &width))
190836285Sbrian        return -1;
190936285Sbrian      if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
191036285Sbrian        addrs = ROUTE_DSTMYADDR;
191136285Sbrian      else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
191236285Sbrian        addrs = ROUTE_DSTHISADDR;
191331598Sbrian    }
191436285Sbrian    gw = 1;
191534536Sbrian  } else {
191636285Sbrian    if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
191736285Sbrian      addrs = ROUTE_DSTMYADDR;
191836285Sbrian      dest = arg->bundle->ncp.ipcp.my_ip;
191936285Sbrian    } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
192036285Sbrian      addrs = ROUTE_DSTHISADDR;
192136285Sbrian      dest = arg->bundle->ncp.ipcp.peer_ip;
192236285Sbrian    } else
192336285Sbrian      dest = GetIpAddr(arg->argv[arg->argn]);
192436285Sbrian    netmask = GetIpAddr(arg->argv[arg->argn+1]);
192531598Sbrian    gw = 2;
19266059Samurai  }
192736285Sbrian
192836285Sbrian  if (strcasecmp(arg->argv[arg->argn+gw], "HISADDR") == 0) {
192936285Sbrian    gateway = arg->bundle->ncp.ipcp.peer_ip;
193036285Sbrian    addrs |= ROUTE_GWHISADDR;
193140561Sbrian  } else
193236285Sbrian    gateway = GetIpAddr(arg->argv[arg->argn+gw]);
193336285Sbrian
193436285Sbrian  if (bundle_SetRoute(arg->bundle, RTM_ADD, dest, gateway, netmask,
193543313Sbrian                  arg->cmd->args ? 1 : 0, (addrs & ROUTE_GWHISADDR) ? 1 : 0)
193643313Sbrian      && addrs != ROUTE_STATIC)
193736285Sbrian    route_Add(&arg->bundle->ncp.ipcp.route, addrs, dest, netmask, gateway);
193836285Sbrian
193931598Sbrian  return 0;
19406059Samurai}
19416059Samurai
19426059Samuraistatic int
194331343SbrianDeleteCommand(struct cmdargs const *arg)
19446059Samurai{
194531598Sbrian  struct in_addr dest, none;
194636285Sbrian  int addrs;
19476059Samurai
194836285Sbrian  if (arg->argc == arg->argn+1) {
194936285Sbrian    if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
195036285Sbrian      route_IfDelete(arg->bundle, 0);
195136285Sbrian      route_DeleteAll(&arg->bundle->ncp.ipcp.route);
195236285Sbrian    } else {
195336285Sbrian      addrs = 0;
195436285Sbrian      if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
195536285Sbrian        dest = arg->bundle->ncp.ipcp.my_ip;
195636285Sbrian        addrs = ROUTE_DSTMYADDR;
195736285Sbrian      } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
195836285Sbrian        dest = arg->bundle->ncp.ipcp.peer_ip;
195936285Sbrian        addrs = ROUTE_DSTHISADDR;
196036285Sbrian      } else {
196144279Sbrian        dest = GetIpAddr(arg->argv[arg->argn]);
196244279Sbrian        if (dest.s_addr == INADDR_NONE) {
196344279Sbrian          log_Printf(LogWARN, "%s: Invalid IP address\n", arg->argv[arg->argn]);
196444279Sbrian          return -1;
196544279Sbrian        }
196636285Sbrian        addrs = ROUTE_STATIC;
196736285Sbrian      }
196831598Sbrian      none.s_addr = INADDR_ANY;
196936285Sbrian      bundle_SetRoute(arg->bundle, RTM_DELETE, dest, none, none,
197037927Sbrian                      arg->cmd->args ? 1 : 0, 0);
197136285Sbrian      route_Delete(&arg->bundle->ncp.ipcp.route, addrs, dest);
197231598Sbrian    }
197334536Sbrian  } else
197426516Sbrian    return -1;
197526516Sbrian
197626516Sbrian  return 0;
19776059Samurai}
19786059Samurai
197931343Sbrian#ifndef NOALIAS
198026031Sbrianstatic int
198131343SbrianAliasEnable(struct cmdargs const *arg)
198226031Sbrian{
198336285Sbrian  if (arg->argc == arg->argn+1) {
198436285Sbrian    if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
198546686Sbrian      if (!arg->bundle->AliasEnabled) {
198646686Sbrian        if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
198746686Sbrian          PacketAliasSetAddress(arg->bundle->ncp.ipcp.my_ip);
198846686Sbrian        arg->bundle->AliasEnabled = 1;
198946686Sbrian      }
199037191Sbrian      return 0;
199136285Sbrian    } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
199237191Sbrian      arg->bundle->AliasEnabled = 0;
199340561Sbrian      arg->bundle->cfg.opt &= ~OPT_IFACEALIAS;
199440561Sbrian      /* Don't iface_Clear() - there may be manually configured addresses */
199526516Sbrian      return 0;
199626142Sbrian    }
199735449Sbrian  }
199836285Sbrian
199926516Sbrian  return -1;
200026031Sbrian}
200126031Sbrian
200226031Sbrian
200326031Sbrianstatic int
200431343SbrianAliasOption(struct cmdargs const *arg)
200526031Sbrian{
200638559Sbrian  long param = (long)arg->cmd->args;
200738559Sbrian
200836285Sbrian  if (arg->argc == arg->argn+1) {
200936285Sbrian    if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
201037191Sbrian      if (arg->bundle->AliasEnabled) {
201137191Sbrian	PacketAliasSetMode(param, param);
201228679Sbrian	return 0;
201328679Sbrian      }
201436285Sbrian      log_Printf(LogWARN, "alias not enabled\n");
201536285Sbrian    } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
201637191Sbrian      if (arg->bundle->AliasEnabled) {
201737191Sbrian	PacketAliasSetMode(0, param);
201828679Sbrian	return 0;
201928679Sbrian      }
202036285Sbrian      log_Printf(LogWARN, "alias not enabled\n");
202128679Sbrian    }
202235449Sbrian  }
202328679Sbrian  return -1;
202426031Sbrian}
202531343Sbrian#endif /* #ifndef NOALIAS */
202631121Sbrian
202731121Sbrianstatic int
202836285SbrianLinkCommand(struct cmdargs const *arg)
202936285Sbrian{
203036285Sbrian  if (arg->argc > arg->argn+1) {
203136285Sbrian    char namelist[LINE_LEN];
203236285Sbrian    struct datalink *cx;
203336285Sbrian    char *name;
203436285Sbrian    int result = 0;
203536285Sbrian
203636285Sbrian    if (!strcmp(arg->argv[arg->argn], "*")) {
203736285Sbrian      struct datalink *dl;
203836285Sbrian
203936285Sbrian      cx = arg->bundle->links;
204036285Sbrian      while (cx) {
204136285Sbrian        /* Watch it, the command could be a ``remove'' */
204236285Sbrian        dl = cx->next;
204336285Sbrian        FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
204436285Sbrian                 arg->prompt, cx);
204536285Sbrian        for (cx = arg->bundle->links; cx; cx = cx->next)
204636285Sbrian          if (cx == dl)
204736285Sbrian            break;		/* Pointer's still valid ! */
204836285Sbrian      }
204936285Sbrian    } else {
205036285Sbrian      strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
205136285Sbrian      namelist[sizeof namelist - 1] = '\0';
205236285Sbrian      for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
205336285Sbrian        if (!bundle2datalink(arg->bundle, name)) {
205436285Sbrian          log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
205536285Sbrian          return 1;
205636285Sbrian        }
205736285Sbrian
205836285Sbrian      strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
205936285Sbrian      namelist[sizeof namelist - 1] = '\0';
206036285Sbrian      for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
206136285Sbrian        cx = bundle2datalink(arg->bundle, name);
206236285Sbrian        if (cx)
206336285Sbrian          FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
206436285Sbrian                   arg->prompt, cx);
206536285Sbrian        else {
206636285Sbrian          log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
206736285Sbrian          result++;
206836285Sbrian        }
206936285Sbrian      }
207036285Sbrian    }
207136285Sbrian    return result;
207236285Sbrian  }
207336285Sbrian
207436285Sbrian  log_Printf(LogWARN, "Usage: %s\n", arg->cmd->syntax);
207536285Sbrian  return 2;
207636285Sbrian}
207736285Sbrian
207836285Sbrianstruct link *
207936285Sbriancommand_ChooseLink(struct cmdargs const *arg)
208036285Sbrian{
208136285Sbrian  if (arg->cx)
208236285Sbrian    return &arg->cx->physical->link;
208337210Sbrian  else if (!arg->bundle->ncp.mp.cfg.mrru) {
208436285Sbrian    struct datalink *dl = bundle2datalink(arg->bundle, NULL);
208537210Sbrian    if (dl)
208637210Sbrian      return &dl->physical->link;
208736285Sbrian  }
208837210Sbrian  return &arg->bundle->ncp.mp.link;
208936285Sbrian}
209036285Sbrian
209136285Sbrianstatic const char *
209236285Sbrianident_cmd(const char *cmd, unsigned *keep, unsigned *add)
209336285Sbrian{
209436285Sbrian  const char *result;
209536285Sbrian
209636285Sbrian  switch (*cmd) {
209736285Sbrian    case 'A':
209836285Sbrian    case 'a':
209936285Sbrian      result = "accept";
210036285Sbrian      *keep = NEG_MYMASK;
210136285Sbrian      *add = NEG_ACCEPTED;
210236285Sbrian      break;
210336285Sbrian    case 'D':
210436285Sbrian    case 'd':
210536285Sbrian      switch (cmd[1]) {
210636285Sbrian        case 'E':
210736285Sbrian        case 'e':
210836285Sbrian          result = "deny";
210936285Sbrian          *keep = NEG_MYMASK;
211036285Sbrian          *add = 0;
211136285Sbrian          break;
211236285Sbrian        case 'I':
211336285Sbrian        case 'i':
211436285Sbrian          result = "disable";
211536285Sbrian          *keep = NEG_HISMASK;
211636285Sbrian          *add = 0;
211736285Sbrian          break;
211836285Sbrian        default:
211936285Sbrian          return NULL;
212036285Sbrian      }
212136285Sbrian      break;
212236285Sbrian    case 'E':
212336285Sbrian    case 'e':
212436285Sbrian      result = "enable";
212536285Sbrian      *keep = NEG_HISMASK;
212636285Sbrian      *add = NEG_ENABLED;
212736285Sbrian      break;
212836285Sbrian    default:
212936285Sbrian      return NULL;
213036285Sbrian  }
213136285Sbrian
213236285Sbrian  return result;
213336285Sbrian}
213436285Sbrian
213536285Sbrianstatic int
213636285SbrianOptSet(struct cmdargs const *arg)
213736285Sbrian{
213837574Sbrian  int bit = (int)(long)arg->cmd->args;
213936285Sbrian  const char *cmd;
214036285Sbrian  unsigned keep;			/* Keep these bits */
214136285Sbrian  unsigned add;				/* Add these bits */
214236285Sbrian
214336285Sbrian  if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
214436285Sbrian    return 1;
214536285Sbrian
214636285Sbrian  if (add)
214736285Sbrian    arg->bundle->cfg.opt |= bit;
214836285Sbrian  else
214936285Sbrian    arg->bundle->cfg.opt &= ~bit;
215036285Sbrian  return 0;
215136285Sbrian}
215236285Sbrian
215336285Sbrianstatic int
215440561SbrianIfaceAliasOptSet(struct cmdargs const *arg)
215540561Sbrian{
215640561Sbrian  unsigned save = arg->bundle->cfg.opt;
215740561Sbrian  int result = OptSet(arg);
215840561Sbrian
215940561Sbrian  if (result == 0)
216040561Sbrian    if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->AliasEnabled) {
216140561Sbrian      arg->bundle->cfg.opt = save;
216240561Sbrian      log_Printf(LogWARN, "Cannot enable iface-alias without IP aliasing\n");
216340561Sbrian      result = 2;
216440561Sbrian    }
216540561Sbrian
216640561Sbrian  return result;
216740561Sbrian}
216840561Sbrian
216940561Sbrianstatic int
217036285SbrianNegotiateSet(struct cmdargs const *arg)
217136285Sbrian{
217237210Sbrian  long param = (long)arg->cmd->args;
217336285Sbrian  struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
217436285Sbrian  struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
217536285Sbrian  const char *cmd;
217636285Sbrian  unsigned keep;			/* Keep these bits */
217736285Sbrian  unsigned add;				/* Add these bits */
217836285Sbrian
217936285Sbrian  if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
218036285Sbrian    return 1;
218136285Sbrian
218236285Sbrian  if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
218336285Sbrian    log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
218436285Sbrian              cmd, arg->cmd->name);
218536285Sbrian    return 2;
218636285Sbrian  } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
218736285Sbrian    log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
218836285Sbrian              cmd, arg->cmd->name, cx->name);
218936285Sbrian    cx = NULL;
219036285Sbrian  }
219136285Sbrian
219236285Sbrian  switch (param) {
219336285Sbrian    case NEG_ACFCOMP:
219436285Sbrian      cx->physical->link.lcp.cfg.acfcomp &= keep;
219536285Sbrian      cx->physical->link.lcp.cfg.acfcomp |= add;
219636285Sbrian      break;
219744106Sbrian    case NEG_CHAP05:
219844106Sbrian      cx->physical->link.lcp.cfg.chap05 &= keep;
219944106Sbrian      cx->physical->link.lcp.cfg.chap05 |= add;
220036285Sbrian      break;
220144106Sbrian#ifdef HAVE_DES
220244106Sbrian    case NEG_CHAP80:
220344106Sbrian      cx->physical->link.lcp.cfg.chap80nt &= keep;
220444106Sbrian      cx->physical->link.lcp.cfg.chap80nt |= add;
220544106Sbrian      break;
220644106Sbrian    case NEG_CHAP80LM:
220744106Sbrian      cx->physical->link.lcp.cfg.chap80lm &= keep;
220844106Sbrian      cx->physical->link.lcp.cfg.chap80lm |= add;
220944106Sbrian      break;
221044106Sbrian#endif
221136285Sbrian    case NEG_DEFLATE:
221236285Sbrian      l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
221336285Sbrian      l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
221436285Sbrian      break;
221536285Sbrian    case NEG_DNS:
221636285Sbrian      arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
221736285Sbrian      arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
221836285Sbrian      break;
221936285Sbrian    case NEG_LQR:
222036285Sbrian      cx->physical->link.lcp.cfg.lqr &= keep;
222136285Sbrian      cx->physical->link.lcp.cfg.lqr |= add;
222236285Sbrian      break;
222336285Sbrian    case NEG_PAP:
222436285Sbrian      cx->physical->link.lcp.cfg.pap &= keep;
222536285Sbrian      cx->physical->link.lcp.cfg.pap |= add;
222636285Sbrian      break;
222736285Sbrian    case NEG_PPPDDEFLATE:
222836285Sbrian      l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
222936285Sbrian      l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
223036285Sbrian      break;
223136285Sbrian    case NEG_PRED1:
223236285Sbrian      l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
223336285Sbrian      l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
223436285Sbrian      break;
223536285Sbrian    case NEG_PROTOCOMP:
223636285Sbrian      cx->physical->link.lcp.cfg.protocomp &= keep;
223736285Sbrian      cx->physical->link.lcp.cfg.protocomp |= add;
223836285Sbrian      break;
223936285Sbrian    case NEG_SHORTSEQ:
224040622Sbrian      switch (bundle_Phase(arg->bundle)) {
224140622Sbrian        case PHASE_DEAD:
224240622Sbrian          break;
224340622Sbrian        case PHASE_ESTABLISH:
224440622Sbrian          /* Make sure none of our links are DATALINK_LCP or greater */
224540622Sbrian          if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
224640622Sbrian            log_Printf(LogWARN, "shortseq: Only changable before"
224740622Sbrian                       " LCP negotiations\n");
224840622Sbrian            return 1;
224940622Sbrian          }
225040622Sbrian          break;
225140622Sbrian        default:
225240622Sbrian          log_Printf(LogWARN, "shortseq: Only changable at phase"
225340622Sbrian                     " DEAD/ESTABLISH\n");
225440622Sbrian          return 1;
225536285Sbrian      }
225640622Sbrian      arg->bundle->ncp.mp.cfg.shortseq &= keep;
225740622Sbrian      arg->bundle->ncp.mp.cfg.shortseq |= add;
225836285Sbrian      break;
225936285Sbrian    case NEG_VJCOMP:
226036285Sbrian      arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
226136285Sbrian      arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
226236285Sbrian      break;
226336285Sbrian  }
226436285Sbrian
226536285Sbrian  return 0;
226636285Sbrian}
226736285Sbrian
226836285Sbrianstatic struct cmdtab const NegotiateCommands[] = {
226936285Sbrian  {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
227036285Sbrian  "disable|enable", (const void *)OPT_IDCHECK},
227140666Sbrian  {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH,
227240666Sbrian   "retain interface addresses", "disable|enable",
227340666Sbrian   (const void *)OPT_IFACEALIAS},
227447689Sbrian  {"keep-session", NULL, OptSet, LOCAL_AUTH, "Retain device session leader",
227547689Sbrian  "disable|enable", (const void *)OPT_KEEPSESSION},
227636285Sbrian  {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
227736285Sbrian  "disable|enable", (const void *)OPT_LOOPBACK},
227836285Sbrian  {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
227936285Sbrian  "disable|enable", (const void *)OPT_PASSWDAUTH},
228040665Sbrian  {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry",
228136285Sbrian  "disable|enable", (const void *)OPT_PROXY},
228240665Sbrian  {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts",
228340665Sbrian  "disable|enable", (const void *)OPT_PROXYALL},
228436285Sbrian  {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
228536285Sbrian  "disable|enable", (const void *)OPT_SROUTES},
228636285Sbrian  {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
228736285Sbrian  "disable|enable", (const void *)OPT_THROUGHPUT},
228836285Sbrian  {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
228936285Sbrian  "disable|enable", (const void *)OPT_UTMP},
229036285Sbrian
229147689Sbrian#define OPT_MAX 10	/* accept/deny allowed below and not above */
229236285Sbrian
229336285Sbrian  {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
229436285Sbrian  "Address & Control field compression", "accept|deny|disable|enable",
229536285Sbrian  (const void *)NEG_ACFCOMP},
229644106Sbrian  {"chap", "chap05", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
229736285Sbrian  "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
229844106Sbrian  (const void *)NEG_CHAP05},
229944106Sbrian#ifdef HAVE_DES
230044106Sbrian  {"mschap", "chap80nt", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
230144106Sbrian  "Microsoft (NT) CHAP", "accept|deny|disable|enable",
230244106Sbrian  (const void *)NEG_CHAP80},
230344106Sbrian  {"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
230444106Sbrian  "Microsoft (NT) CHAP", "accept|deny|disable|enable",
230544106Sbrian  (const void *)NEG_CHAP80LM},
230644106Sbrian#endif
230736285Sbrian  {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
230836285Sbrian  "Deflate compression", "accept|deny|disable|enable",
230936285Sbrian  (const void *)NEG_DEFLATE},
231036285Sbrian  {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
231136285Sbrian  "Deflate (type 24) compression", "accept|deny|disable|enable",
231236285Sbrian  (const void *)NEG_PPPDDEFLATE},
231336285Sbrian  {"dns", NULL, NegotiateSet, LOCAL_AUTH,
231436285Sbrian  "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
231536285Sbrian  {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
231636285Sbrian  "Link Quality Reports", "accept|deny|disable|enable",
231736285Sbrian  (const void *)NEG_LQR},
231836285Sbrian  {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
231936285Sbrian  "Password Authentication protocol", "accept|deny|disable|enable",
232036285Sbrian  (const void *)NEG_PAP},
232136285Sbrian  {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
232236285Sbrian  "Predictor 1 compression", "accept|deny|disable|enable",
232336285Sbrian  (const void *)NEG_PRED1},
232436285Sbrian  {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
232536285Sbrian  "Protocol field compression", "accept|deny|disable|enable",
232636285Sbrian  (const void *)NEG_PROTOCOMP},
232736285Sbrian  {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
232836285Sbrian  "MP Short Sequence Numbers", "accept|deny|disable|enable",
232936285Sbrian  (const void *)NEG_SHORTSEQ},
233036285Sbrian  {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
233136285Sbrian  "Van Jacobson header compression", "accept|deny|disable|enable",
233236285Sbrian  (const void *)NEG_VJCOMP},
233336285Sbrian  {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
233436285Sbrian  "Display this message", "accept|deny|disable|enable help|? [value]",
233536285Sbrian  NegotiateCommands},
233636285Sbrian  {NULL, NULL, NULL},
233736285Sbrian};
233836285Sbrian
233936285Sbrianstatic int
234036285SbrianNegotiateCommand(struct cmdargs const *arg)
234136285Sbrian{
234236285Sbrian  if (arg->argc > arg->argn) {
234336285Sbrian    char const *argv[3];
234436285Sbrian    unsigned keep, add;
234536285Sbrian    int n;
234636285Sbrian
234736285Sbrian    if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
234836285Sbrian      return -1;
234936285Sbrian    argv[2] = NULL;
235036285Sbrian
235136285Sbrian    for (n = arg->argn; n < arg->argc; n++) {
235236285Sbrian      argv[1] = arg->argv[n];
235336285Sbrian      FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
235436285Sbrian               0 : OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
235536285Sbrian    }
235636285Sbrian  } else if (arg->prompt)
235736285Sbrian    prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
235836285Sbrian	    arg->argv[arg->argn-1]);
235936285Sbrian  else
236036285Sbrian    log_Printf(LogWARN, "%s command must have arguments\n",
236136285Sbrian              arg->argv[arg->argn] );
236236285Sbrian
236336285Sbrian  return 0;
236436285Sbrian}
236536285Sbrian
236636285Sbrianconst char *
236736285Sbriancommand_ShowNegval(unsigned val)
236836285Sbrian{
236936285Sbrian  switch (val&3) {
237036285Sbrian    case 1: return "disabled & accepted";
237136285Sbrian    case 2: return "enabled & denied";
237236285Sbrian    case 3: return "enabled & accepted";
237336285Sbrian  }
237436285Sbrian  return "disabled & denied";
237536285Sbrian}
237636934Sbrian
237736934Sbrianstatic int
237836934SbrianClearCommand(struct cmdargs const *arg)
237936934Sbrian{
238036934Sbrian  struct pppThroughput *t;
238136934Sbrian  struct datalink *cx;
238236934Sbrian  int i, clear_type;
238336934Sbrian
238436934Sbrian  if (arg->argc < arg->argn + 1)
238536934Sbrian    return -1;
238636934Sbrian
238746686Sbrian  if (strcasecmp(arg->argv[arg->argn], "physical") == 0) {
238836934Sbrian    cx = arg->cx;
238936934Sbrian    if (!cx)
239036934Sbrian      cx = bundle2datalink(arg->bundle, NULL);
239136934Sbrian    if (!cx) {
239246686Sbrian      log_Printf(LogWARN, "A link must be specified for ``clear physical''\n");
239336934Sbrian      return 1;
239436934Sbrian    }
239536934Sbrian    t = &cx->physical->link.throughput;
239636934Sbrian  } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
239736934Sbrian    t = &arg->bundle->ncp.ipcp.throughput;
239836934Sbrian  else
239936934Sbrian    return -1;
240036934Sbrian
240136934Sbrian  if (arg->argc > arg->argn + 1) {
240236934Sbrian    clear_type = 0;
240336934Sbrian    for (i = arg->argn + 1; i < arg->argc; i++)
240436934Sbrian      if (strcasecmp(arg->argv[i], "overall") == 0)
240536934Sbrian        clear_type |= THROUGHPUT_OVERALL;
240636934Sbrian      else if (strcasecmp(arg->argv[i], "current") == 0)
240736934Sbrian        clear_type |= THROUGHPUT_CURRENT;
240836934Sbrian      else if (strcasecmp(arg->argv[i], "peak") == 0)
240936934Sbrian        clear_type |= THROUGHPUT_PEAK;
241036934Sbrian      else
241136934Sbrian        return -1;
241236934Sbrian  } else
241336934Sbrian    clear_type = THROUGHPUT_ALL;
241436934Sbrian
241536934Sbrian  throughput_clear(t, clear_type, arg->prompt);
241636934Sbrian  return 0;
241736934Sbrian}
241840561Sbrian
241940561Sbrianstatic int
242040561SbrianRunListCommand(struct cmdargs const *arg)
242140561Sbrian{
242240561Sbrian  const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???";
242340561Sbrian
242440561Sbrian  if (arg->argc > arg->argn)
242540561Sbrian    FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv,
242640561Sbrian             arg->prompt, arg->cx);
242740561Sbrian  else if (arg->prompt)
242840561Sbrian    prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help"
242940561Sbrian                  " <option>' for syntax help.\n", cmd, cmd);
243040561Sbrian  else
243140561Sbrian    log_Printf(LogWARN, "%s command must have arguments\n", cmd);
243240561Sbrian
243340561Sbrian  return 0;
243440561Sbrian}
243540561Sbrian
243640561Sbrianstatic int
243740561SbrianIfaceAddCommand(struct cmdargs const *arg)
243840561Sbrian{
243940561Sbrian  int bits, n, how;
244040561Sbrian  struct in_addr ifa, mask, brd;
244140561Sbrian
244240664Sbrian  if (arg->argc == arg->argn + 1) {
244343313Sbrian    if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, NULL, NULL))
244440561Sbrian      return -1;
244540664Sbrian    mask.s_addr = brd.s_addr = INADDR_BROADCAST;
244640664Sbrian  } else {
244740664Sbrian    if (arg->argc == arg->argn + 2) {
244843313Sbrian      if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, &mask, &bits))
244940664Sbrian        return -1;
245040664Sbrian      n = 1;
245140664Sbrian    } else if (arg->argc == arg->argn + 3) {
245243313Sbrian      if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, NULL, NULL))
245340664Sbrian        return -1;
245443313Sbrian      if (!ParseAddr(NULL, arg->argv[arg->argn + 1], &mask, NULL, NULL))
245540664Sbrian        return -1;
245640664Sbrian      n = 2;
245740664Sbrian    } else
245840561Sbrian      return -1;
245940561Sbrian
246043313Sbrian    if (!ParseAddr(NULL, arg->argv[arg->argn + n], &brd, NULL, NULL))
246140664Sbrian      return -1;
246240664Sbrian  }
246340561Sbrian
246440561Sbrian  how = IFACE_ADD_LAST;
246540561Sbrian  if (arg->cmd->args)
246640561Sbrian    how |= IFACE_FORCE_ADD;
246740561Sbrian
246840561Sbrian  return !iface_inAdd(arg->bundle->iface, ifa, mask, brd, how);
246940561Sbrian}
247040561Sbrian
247140561Sbrianstatic int
247240561SbrianIfaceDeleteCommand(struct cmdargs const *arg)
247340561Sbrian{
247440561Sbrian  struct in_addr ifa;
247540561Sbrian  int ok;
247640561Sbrian
247740561Sbrian  if (arg->argc != arg->argn + 1)
247840561Sbrian    return -1;
247940561Sbrian
248043313Sbrian  if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, NULL, NULL))
248140561Sbrian    return -1;
248240561Sbrian
248340561Sbrian  if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
248440561Sbrian      arg->bundle->ncp.ipcp.my_ip.s_addr == ifa.s_addr) {
248540561Sbrian    log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
248640561Sbrian               inet_ntoa(ifa));
248740561Sbrian    return 1;
248840561Sbrian  }
248940561Sbrian
249040561Sbrian  ok = iface_inDelete(arg->bundle->iface, ifa);
249140561Sbrian  if (!ok) {
249240561Sbrian    if (arg->cmd->args)
249340561Sbrian      ok = 1;
249440561Sbrian    else if (arg->prompt)
249540561Sbrian      prompt_Printf(arg->prompt, "%s: No such address\n", inet_ntoa(ifa));
249640561Sbrian    else
249740561Sbrian      log_Printf(LogWARN, "%s: No such address\n", inet_ntoa(ifa));
249840561Sbrian  }
249940561Sbrian
250040561Sbrian  return !ok;
250140561Sbrian}
250240561Sbrian
250340561Sbrianstatic int
250440561SbrianIfaceClearCommand(struct cmdargs const *arg)
250540561Sbrian{
250640561Sbrian  int how;
250740561Sbrian
250840561Sbrian  if (arg->argc != arg->argn)
250940561Sbrian    return -1;
251040561Sbrian
251140941Sbrian  how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED ||
251240941Sbrian        arg->bundle->phys_type.all & PHYS_AUTO ?
251340561Sbrian        IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL;
251440561Sbrian  iface_Clear(arg->bundle->iface, how);
251540561Sbrian
251640561Sbrian  return 0;
251740561Sbrian}
251840679Sbrian
251940679Sbrianstatic int
252040679SbrianSetProcTitle(struct cmdargs const *arg)
252140679Sbrian{
252240679Sbrian  static char title[LINE_LEN];
252340679Sbrian  char *argv[MAXARGS], *ptr;
252440679Sbrian  int len, remaining, f, argc = arg->argc - arg->argn;
252540679Sbrian
252640679Sbrian  if (arg->argc == arg->argn) {
252740679Sbrian    arg->bundle->argv[0] = arg->bundle->argv0;
252840679Sbrian    arg->bundle->argv[1] = arg->bundle->argv1;
252940679Sbrian    return 0;
253040679Sbrian  }
253140679Sbrian
253240679Sbrian  if (argc >= sizeof argv / sizeof argv[0]) {
253340679Sbrian    argc = sizeof argv / sizeof argv[0] - 1;
253440679Sbrian    log_Printf(LogWARN, "Truncating proc title to %d args\n", argc);
253540679Sbrian  }
253643888Sbrian  command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1);
253740679Sbrian
253840679Sbrian  ptr = title;
253940679Sbrian  remaining = sizeof title - 1;
254040679Sbrian  for (f = 0; f < argc && remaining; f++) {
254140679Sbrian    if (f) {
254240679Sbrian      *ptr++ = ' ';
254340679Sbrian      remaining--;
254440679Sbrian    }
254540679Sbrian    len = strlen(argv[f]);
254640679Sbrian    if (len > remaining)
254740679Sbrian      len = remaining;
254840679Sbrian    memcpy(ptr, argv[f], len);
254940679Sbrian    remaining -= len;
255040679Sbrian    ptr += len;
255140679Sbrian  }
255240679Sbrian  *ptr = '\0';
255340679Sbrian
255440679Sbrian  arg->bundle->argv[0] = title;
255540679Sbrian  arg->bundle->argv[1] = NULL;
255640679Sbrian
255740679Sbrian  return 0;
255840679Sbrian}
2559