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