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