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