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