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