xref: /freebsd/usr.sbin/ppp/command.c (revision 7562eaabc01a48e6b11d5b558c41e3b92dae5c2d)
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.4";
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 >= (int)(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 __unused)
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 >= (int)(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]", NULL},
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", NULL},
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]", NULL},
770   {"proto", NULL, nat_RedirectProto, LOCAL_AUTH, "protocol redirection",
771    "nat proto proto localIP [publicIP [remoteIP]]", NULL},
772   {"proxy", NULL, nat_ProxyRule, LOCAL_AUTH,
773    "proxy control", "nat proxy server host[:port] ...", NULL},
774 #ifndef NO_FW_PUNCH
775   {"punch_fw", NULL, nat_PunchFW, LOCAL_AUTH,
776    "firewall control", "nat punch_fw [base count]", NULL},
777 #endif
778   {"skinny_port", NULL, nat_SkinnyPort, LOCAL_AUTH,
779    "TCP port used by Skinny Station protocol", "nat skinny_port [port]", NULL},
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", NULL},
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, 0, 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...", NULL},
801   {"users", "user", AllowUsers, LOCAL_AUTH,
802   "Only allow ppp access to certain users", "allow users logname...", NULL},
803   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
804   "Display this message", "allow help|? [command]", AllowCommands},
805   {NULL, NULL, NULL, 0, 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]", NULL},
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", NULL},
825   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
826    "Display this message", "nat help|? [command]", IfaceCommands},
827   {NULL, NULL, NULL, 0, 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 ..", NULL},
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", NULL},
841   {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
842   "Clear throughput statistics",
843   "clear ipcp|ipv6cp|physical [current|overall|peak]...", NULL},
844   {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
845   "Clone a link", "clone newname...", NULL},
846   {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
847   "Close an FSM", "close [lcp|ccp]", NULL},
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 ..", NULL},
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 ..", NULL},
858   {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
859   "Generate a down event", "down [ccp|lcp]", NULL},
860   {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
861   "Enable option", "enable option ..", NULL},
862   {"ident", NULL, IdentCommand, LOCAL_AUTH | LOCAL_CX,
863   "Set the link identity", "ident text...", NULL},
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 ...", NULL},
868   {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
869   "Load settings", "load [system ...]", NULL},
870   {"log", NULL, LogCommand, LOCAL_AUTH | LOCAL_CX_OPT,
871   "log information", "log word ...", NULL},
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", NULL},
880   {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
881   "Quit PPP program", "quit|bye [all]", NULL},
882   {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
883   "Remove a link", "remove", NULL},
884   {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
885   "Rename a link", "rename name", NULL},
886   {"resolv", NULL, ResolvCommand, LOCAL_AUTH,
887   "Manipulate resolv.conf", "resolv readonly|reload|restore|rewrite|writable",
888   NULL},
889   {"save", NULL, SaveCommand, LOCAL_AUTH,
890   "Save settings", "save", NULL},
891   {"sendident", NULL, SendIdentification, LOCAL_AUTH | LOCAL_CX,
892   "Transmit the link identity", "sendident", NULL},
893   {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
894   "Set parameters", "set[up] var value", NULL},
895   {"shell", "!", FgShellCommand, LOCAL_AUTH,
896   "Run a subshell", "shell|! [sh command]", NULL},
897   {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
898   "Show status and stats", "show var", NULL},
899   {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
900   "Enter terminal mode", "term", NULL},
901   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
902   "Display this message", "help|? [command]", Commands},
903   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
904 };
905 
906 static int
907 ShowEscape(struct cmdargs const *arg)
908 {
909   if (arg->cx->physical->async.cfg.EscMap[32]) {
910     int code, bit;
911     const char *sep = "";
912 
913     for (code = 0; code < 32; code++)
914       if (arg->cx->physical->async.cfg.EscMap[code])
915 	for (bit = 0; bit < 8; bit++)
916 	  if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
917 	    prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
918             sep = ", ";
919           }
920     prompt_Printf(arg->prompt, "\n");
921   }
922   return 0;
923 }
924 
925 static int
926 ShowTimerList(struct cmdargs const *arg)
927 {
928   timer_Show(0, arg->prompt);
929   return 0;
930 }
931 
932 static int
933 ShowStopped(struct cmdargs const *arg)
934 {
935   prompt_Printf(arg->prompt, " Stopped Timer:  LCP: ");
936   if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
937     prompt_Printf(arg->prompt, "Disabled");
938   else
939     prompt_Printf(arg->prompt, "%ld secs",
940                   arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
941 
942   prompt_Printf(arg->prompt, ", CCP: ");
943   if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
944     prompt_Printf(arg->prompt, "Disabled");
945   else
946     prompt_Printf(arg->prompt, "%ld secs",
947                   arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
948 
949   prompt_Printf(arg->prompt, "\n");
950 
951   return 0;
952 }
953 
954 static int
955 ShowVersion(struct cmdargs const *arg)
956 {
957   prompt_Printf(arg->prompt, "PPP Version %s - %s\n", Version, __DATE__);
958   return 0;
959 }
960 
961 static int
962 ShowProtocolStats(struct cmdargs const *arg)
963 {
964   struct link *l = command_ChooseLink(arg);
965 
966   prompt_Printf(arg->prompt, "%s:\n", l->name);
967   link_ReportProtocolStatus(l, arg->prompt);
968   return 0;
969 }
970 
971 static struct cmdtab const ShowCommands[] = {
972   {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
973   "bundle details", "show bundle", NULL},
974   {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
975   "CCP status", "show cpp", NULL},
976   {"compress", NULL, sl_Show, LOCAL_AUTH,
977   "VJ compression stats", "show compress", NULL},
978   {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
979   "escape characters", "show escape", NULL},
980   {"filter", NULL, filter_Show, LOCAL_AUTH,
981   "packet filters", "show filter [in|out|dial|alive]", NULL},
982   {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
983   "HDLC errors", "show hdlc", NULL},
984   {"iface", "interface", iface_Show, LOCAL_AUTH,
985   "Interface status", "show iface", NULL},
986   {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
987   "IPCP status", "show ipcp", NULL},
988 #ifndef NOINET6
989   {"ipv6cp", NULL, ipv6cp_Show, LOCAL_AUTH,
990   "IPV6CP status", "show ipv6cp", NULL},
991 #endif
992   {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT,
993   "Protocol layers", "show layers", NULL},
994   {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
995   "LCP status", "show lcp", NULL},
996   {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
997   "(high-level) link info", "show link", NULL},
998   {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
999   "available link names", "show links", NULL},
1000   {"log", NULL, log_ShowLevel, LOCAL_AUTH,
1001   "log levels", "show log", NULL},
1002   {"mem", NULL, mbuf_Show, LOCAL_AUTH,
1003   "mbuf allocations", "show mem", NULL},
1004   {"ncp", NULL, ncp_Show, LOCAL_AUTH,
1005   "NCP status", "show ncp", NULL},
1006   {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX,
1007   "(low-level) link info", "show physical", NULL},
1008   {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
1009   "multilink setup", "show mp", NULL},
1010   {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
1011   "protocol summary", "show proto", NULL},
1012   {"route", NULL, route_Show, LOCAL_AUTH,
1013   "routing table", "show route", NULL},
1014   {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
1015   "STOPPED timeout", "show stopped", NULL},
1016   {"timers", NULL, ShowTimerList, LOCAL_AUTH,
1017   "alarm timers", "show timers", NULL},
1018   {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
1019   "version string", "show version", NULL},
1020   {"who", NULL, log_ShowWho, LOCAL_AUTH,
1021   "client list", "show who", NULL},
1022   {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
1023   "Display this message", "show help|? [command]", ShowCommands},
1024   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
1025 };
1026 
1027 static struct cmdtab const *
1028 FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
1029 {
1030   int nmatch;
1031   int len;
1032   struct cmdtab const *found;
1033 
1034   found = NULL;
1035   len = strlen(str);
1036   nmatch = 0;
1037   while (cmds->func) {
1038     if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
1039       if (cmds->name[len] == '\0') {
1040 	*pmatch = 1;
1041 	return cmds;
1042       }
1043       nmatch++;
1044       found = cmds;
1045     } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
1046       if (cmds->alias[len] == '\0') {
1047 	*pmatch = 1;
1048 	return cmds;
1049       }
1050       nmatch++;
1051       found = cmds;
1052     }
1053     cmds++;
1054   }
1055   *pmatch = nmatch;
1056   return found;
1057 }
1058 
1059 static const char *
1060 mkPrefix(int argc, char const *const *argv, char *tgt, int sz)
1061 {
1062   int f, tlen, len;
1063 
1064   tlen = 0;
1065   for (f = 0; f < argc && tlen < sz - 2; f++) {
1066     if (f)
1067       tgt[tlen++] = ' ';
1068     len = strlen(argv[f]);
1069     if (len > sz - tlen - 1)
1070       len = sz - tlen - 1;
1071     strncpy(tgt+tlen, argv[f], len);
1072     tlen += len;
1073   }
1074   tgt[tlen] = '\0';
1075   return tgt;
1076 }
1077 
1078 static int
1079 FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
1080          char const *const *argv, struct prompt *prompt, struct datalink *cx)
1081 {
1082   struct cmdtab const *cmd;
1083   int val = 1;
1084   int nmatch;
1085   struct cmdargs arg;
1086   char prefix[100];
1087 
1088   cmd = FindCommand(cmds, argv[argn], &nmatch);
1089   if (nmatch > 1)
1090     log_Printf(LogWARN, "%s: Ambiguous command\n",
1091               mkPrefix(argn+1, argv, prefix, sizeof prefix));
1092   else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
1093     if ((cmd->lauth & LOCAL_CX) && !cx)
1094       /* We've got no context, but we require it */
1095       cx = bundle2datalink(bundle, NULL);
1096 
1097     if ((cmd->lauth & LOCAL_CX) && !cx)
1098       log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
1099                 mkPrefix(argn+1, argv, prefix, sizeof prefix));
1100     else {
1101       if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1102         log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
1103                   mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
1104         cx = NULL;
1105       }
1106       arg.cmdtab = cmds;
1107       arg.cmd = cmd;
1108       arg.argc = argc;
1109       arg.argn = argn+1;
1110       arg.argv = argv;
1111       arg.bundle = bundle;
1112       arg.cx = cx;
1113       arg.prompt = prompt;
1114       val = (*cmd->func) (&arg);
1115     }
1116   } else
1117     log_Printf(LogWARN, "%s: Invalid command\n",
1118               mkPrefix(argn+1, argv, prefix, sizeof prefix));
1119 
1120   if (val == -1)
1121     log_Printf(LogWARN, "usage: %s\n", cmd->syntax);
1122   else if (val)
1123     log_Printf(LogWARN, "%s: Failed %d\n",
1124               mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
1125 
1126   return val;
1127 }
1128 
1129 int
1130 command_Expand_Interpret(char *buff, int nb, char *argv[MAXARGS], int offset)
1131 {
1132   char buff2[LINE_LEN-offset];
1133 
1134   InterpretArg(buff, buff2);
1135   strncpy(buff, buff2, LINE_LEN - offset - 1);
1136   buff[LINE_LEN - offset - 1] = '\0';
1137 
1138   return command_Interpret(buff, nb, argv);
1139 }
1140 
1141 int
1142 command_Interpret(char *buff, int nb, char *argv[MAXARGS])
1143 {
1144   char *cp;
1145 
1146   if (nb > 0) {
1147     cp = buff + strcspn(buff, "\r\n");
1148     if (cp)
1149       *cp = '\0';
1150     return MakeArgs(buff, argv, MAXARGS, PARSE_REDUCE);
1151   }
1152   return 0;
1153 }
1154 
1155 static int
1156 arghidden(char const *const *argv, int n)
1157 {
1158   /* Is arg n of the given command to be hidden from the log ? */
1159 
1160   /* set authkey xxxxx */
1161   /* set key xxxxx */
1162   if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
1163       (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
1164     return 1;
1165 
1166   /* passwd xxxxx */
1167   if (n == 1 && !strncasecmp(argv[0], "p", 1))
1168     return 1;
1169 
1170   /* set server port xxxxx .... */
1171   if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
1172       !strncasecmp(argv[1], "se", 2))
1173     return 1;
1174 
1175   return 0;
1176 }
1177 
1178 void
1179 command_Run(struct bundle *bundle, int argc, char const *const *argv,
1180            struct prompt *prompt, const char *label, struct datalink *cx)
1181 {
1182   if (argc > 0) {
1183     if (log_IsKept(LogCOMMAND)) {
1184       char buf[LINE_LEN];
1185       int f;
1186       size_t n;
1187 
1188       if (label) {
1189         strncpy(buf, label, sizeof buf - 3);
1190         buf[sizeof buf - 3] = '\0';
1191         strcat(buf, ": ");
1192         n = strlen(buf);
1193       } else {
1194         *buf = '\0';
1195         n = 0;
1196       }
1197       buf[sizeof buf - 1] = '\0';	/* In case we run out of room in buf */
1198 
1199       for (f = 0; f < argc; f++) {
1200         if (n < sizeof buf - 1 && f)
1201           buf[n++] = ' ';
1202         if (arghidden(argv, f))
1203           strncpy(buf+n, "********", sizeof buf - n - 1);
1204         else
1205           strncpy(buf+n, argv[f], sizeof buf - n - 1);
1206         n += strlen(buf+n);
1207       }
1208       log_Printf(LogCOMMAND, "%s\n", buf);
1209     }
1210     FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
1211   }
1212 }
1213 
1214 int
1215 command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
1216               const char *label)
1217 {
1218   int argc;
1219   char *argv[MAXARGS];
1220 
1221   if ((argc = command_Expand_Interpret(buff, nb, argv, 0)) < 0)
1222     return 0;
1223 
1224   command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
1225   return 1;
1226 }
1227 
1228 static int
1229 ShowCommand(struct cmdargs const *arg)
1230 {
1231   if (!arg->prompt)
1232     log_Printf(LogWARN, "show: Cannot show without a prompt\n");
1233   else if (arg->argc > arg->argn)
1234     FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
1235              arg->prompt, arg->cx);
1236   else
1237     prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
1238 
1239   return 0;
1240 }
1241 
1242 static int
1243 TerminalCommand(struct cmdargs const *arg)
1244 {
1245   if (!arg->prompt) {
1246     log_Printf(LogWARN, "term: Need a prompt\n");
1247     return 1;
1248   }
1249 
1250   if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
1251     prompt_Printf(arg->prompt, "LCP state is [%s]\n",
1252                   State2Nam(arg->cx->physical->link.lcp.fsm.state));
1253     return 1;
1254   }
1255 
1256   datalink_Up(arg->cx, 0, 0);
1257   prompt_TtyTermMode(arg->prompt, arg->cx);
1258   return 0;
1259 }
1260 
1261 static int
1262 QuitCommand(struct cmdargs const *arg)
1263 {
1264   if (!arg->prompt || prompt_IsController(arg->prompt) ||
1265       (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
1266        (arg->prompt->auth & LOCAL_AUTH)))
1267     Cleanup();
1268   if (arg->prompt)
1269     prompt_Destroy(arg->prompt, 1);
1270 
1271   return 0;
1272 }
1273 
1274 static int
1275 OpenCommand(struct cmdargs const *arg)
1276 {
1277   if (arg->argc == arg->argn)
1278     bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
1279   else if (arg->argc == arg->argn + 1) {
1280     if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1281       struct datalink *cx = arg->cx ?
1282         arg->cx : bundle2datalink(arg->bundle, NULL);
1283       if (cx) {
1284         if (cx->physical->link.lcp.fsm.state == ST_OPENED)
1285           fsm_Reopen(&cx->physical->link.lcp.fsm);
1286         else
1287           bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1);
1288       } else
1289         log_Printf(LogWARN, "open lcp: You must specify a link\n");
1290     } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1291       struct fsm *fp;
1292 
1293       fp = &command_ChooseLink(arg)->ccp.fsm;
1294       if (fp->link->lcp.fsm.state != ST_OPENED)
1295         log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
1296       else if (fp->state == ST_OPENED)
1297         fsm_Reopen(fp);
1298       else {
1299         fp->open_mode = 0;	/* Not passive any more */
1300         if (fp->state == ST_STOPPED) {
1301           fsm_Down(fp);
1302           fsm_Up(fp);
1303         } else {
1304           fsm_Up(fp);
1305           fsm_Open(fp);
1306         }
1307       }
1308     } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) {
1309       if (arg->cx)
1310         log_Printf(LogWARN, "open ipcp: You need not specify a link\n");
1311       if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
1312         fsm_Reopen(&arg->bundle->ncp.ipcp.fsm);
1313       else
1314         bundle_Open(arg->bundle, NULL, PHYS_ALL, 1);
1315     } else
1316       return -1;
1317   } else
1318     return -1;
1319 
1320   return 0;
1321 }
1322 
1323 static int
1324 CloseCommand(struct cmdargs const *arg)
1325 {
1326   if (arg->argc == arg->argn)
1327     bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
1328   else if (arg->argc == arg->argn + 1) {
1329     if (!strcasecmp(arg->argv[arg->argn], "lcp"))
1330       bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
1331     else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
1332              !strcasecmp(arg->argv[arg->argn], "ccp!")) {
1333       struct fsm *fp;
1334 
1335       fp = &command_ChooseLink(arg)->ccp.fsm;
1336       if (fp->state == ST_OPENED) {
1337         fsm_Close(fp);
1338         if (arg->argv[arg->argn][3] == '!')
1339           fp->open_mode = 0;		/* Stay ST_CLOSED */
1340         else
1341           fp->open_mode = OPEN_PASSIVE;	/* Wait for the peer to start */
1342       }
1343     } else
1344       return -1;
1345   } else
1346     return -1;
1347 
1348   return 0;
1349 }
1350 
1351 static int
1352 DownCommand(struct cmdargs const *arg)
1353 {
1354   if (arg->argc == arg->argn) {
1355       if (arg->cx)
1356         datalink_Down(arg->cx, CLOSE_STAYDOWN);
1357       else
1358         bundle_Down(arg->bundle, CLOSE_STAYDOWN);
1359   } else if (arg->argc == arg->argn + 1) {
1360     if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1361       if (arg->cx)
1362         datalink_Down(arg->cx, CLOSE_LCP);
1363       else
1364         bundle_Down(arg->bundle, CLOSE_LCP);
1365     } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1366       struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
1367                                  &arg->bundle->ncp.mp.link.ccp.fsm;
1368       fsm2initial(fp);
1369     } else
1370       return -1;
1371   } else
1372     return -1;
1373 
1374   return 0;
1375 }
1376 
1377 static int
1378 SetModemSpeed(struct cmdargs const *arg)
1379 {
1380   long speed;
1381   char *end;
1382 
1383   if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
1384     if (arg->argc > arg->argn+1) {
1385       log_Printf(LogWARN, "SetModemSpeed: Too many arguments\n");
1386       return -1;
1387     }
1388     if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
1389       physical_SetSync(arg->cx->physical);
1390       return 0;
1391     }
1392     end = NULL;
1393     speed = strtol(arg->argv[arg->argn], &end, 10);
1394     if (*end || speed < 0) {
1395       log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
1396                 arg->argv[arg->argn]);
1397       return -1;
1398     }
1399     if (physical_SetSpeed(arg->cx->physical, speed))
1400       return 0;
1401     log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
1402   } else
1403     log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
1404 
1405   return -1;
1406 }
1407 
1408 static int
1409 SetStoppedTimeout(struct cmdargs const *arg)
1410 {
1411   struct link *l = &arg->cx->physical->link;
1412 
1413   l->lcp.fsm.StoppedTimer.load = 0;
1414   l->ccp.fsm.StoppedTimer.load = 0;
1415   if (arg->argc <= arg->argn+2) {
1416     if (arg->argc > arg->argn) {
1417       l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
1418       if (arg->argc > arg->argn+1)
1419         l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
1420     }
1421     return 0;
1422   }
1423   return -1;
1424 }
1425 
1426 static int
1427 SetServer(struct cmdargs const *arg)
1428 {
1429   int res = -1;
1430 
1431   if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
1432     const char *port, *passwd, *mask;
1433     size_t mlen;
1434 
1435     /* What's what ? */
1436     port = arg->argv[arg->argn];
1437     if (arg->argc == arg->argn + 2) {
1438       passwd = arg->argv[arg->argn+1];
1439       mask = NULL;
1440     } else if (arg->argc == arg->argn + 3) {
1441       passwd = arg->argv[arg->argn+1];
1442       mask = arg->argv[arg->argn+2];
1443       mlen = strlen(mask);
1444       if (mlen == 0 || mlen > 4 || strspn(mask, "01234567") != mlen ||
1445           (mlen == 4 && *mask != '0')) {
1446         log_Printf(LogWARN, "%s %s: %s: Invalid mask\n",
1447                    arg->argv[arg->argn - 2], arg->argv[arg->argn - 1], mask);
1448         return -1;
1449       }
1450     } else if (arg->argc != arg->argn + 1)
1451       return -1;
1452     else if (strcasecmp(port, "none") == 0) {
1453       if (server_Clear(arg->bundle))
1454         log_Printf(LogPHASE, "Disabled server socket\n");
1455       return 0;
1456     } else if (strcasecmp(port, "open") == 0) {
1457       switch (server_Reopen(arg->bundle)) {
1458         case SERVER_OK:
1459           return 0;
1460         case SERVER_FAILED:
1461           log_Printf(LogWARN, "Failed to reopen server port\n");
1462           return 1;
1463         case SERVER_UNSET:
1464           log_Printf(LogWARN, "Cannot reopen unset server socket\n");
1465           return 1;
1466         default:
1467           break;
1468       }
1469       return -1;
1470     } else if (strcasecmp(port, "closed") == 0) {
1471       if (server_Close(arg->bundle))
1472         log_Printf(LogPHASE, "Closed server socket\n");
1473       else
1474         log_Printf(LogWARN, "Server socket not open\n");
1475 
1476       return 0;
1477     } else
1478       return -1;
1479 
1480     strncpy(server.cfg.passwd, passwd, sizeof server.cfg.passwd - 1);
1481     server.cfg.passwd[sizeof server.cfg.passwd - 1] = '\0';
1482 
1483     if (*port == '/') {
1484       mode_t imask;
1485       char *ptr, name[LINE_LEN + 12];
1486 
1487       if (mask == NULL)
1488         imask = (mode_t)-1;
1489       else for (imask = mlen = 0; mask[mlen]; mlen++)
1490         imask = (imask * 8) + mask[mlen] - '0';
1491 
1492       ptr = strstr(port, "%d");
1493       if (ptr) {
1494         snprintf(name, sizeof name, "%.*s%d%s",
1495                  (int)(ptr - port), port, arg->bundle->unit, ptr + 2);
1496         port = name;
1497       }
1498       res = server_LocalOpen(arg->bundle, port, imask);
1499     } else {
1500       int iport, add = 0;
1501 
1502       if (mask != NULL)
1503         return -1;
1504 
1505       if (*port == '+') {
1506         port++;
1507         add = 1;
1508       }
1509       if (strspn(port, "0123456789") != strlen(port)) {
1510         struct servent *s;
1511 
1512         if ((s = getservbyname(port, "tcp")) == NULL) {
1513 	  iport = 0;
1514 	  log_Printf(LogWARN, "%s: Invalid port or service\n", port);
1515 	} else
1516 	  iport = ntohs(s->s_port);
1517       } else
1518         iport = atoi(port);
1519 
1520       if (iport) {
1521         if (add)
1522           iport += arg->bundle->unit;
1523         res = server_TcpOpen(arg->bundle, iport);
1524       } else
1525         res = -1;
1526     }
1527   }
1528 
1529   return res;
1530 }
1531 
1532 static int
1533 SetEscape(struct cmdargs const *arg)
1534 {
1535   int code;
1536   int argc = arg->argc - arg->argn;
1537   char const *const *argv = arg->argv + arg->argn;
1538 
1539   for (code = 0; code < 33; code++)
1540     arg->cx->physical->async.cfg.EscMap[code] = 0;
1541 
1542   while (argc-- > 0) {
1543     sscanf(*argv++, "%x", &code);
1544     code &= 0xff;
1545     arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
1546     arg->cx->physical->async.cfg.EscMap[32] = 1;
1547   }
1548   return 0;
1549 }
1550 
1551 static int
1552 SetInterfaceAddr(struct cmdargs const *arg)
1553 {
1554   struct ncp *ncp = &arg->bundle->ncp;
1555   struct ncpaddr ncpaddr;
1556   const char *hisaddr;
1557 
1558   if (arg->argc > arg->argn + 4)
1559     return -1;
1560 
1561   hisaddr = NULL;
1562   memset(&ncp->ipcp.cfg.my_range, '\0', sizeof ncp->ipcp.cfg.my_range);
1563   memset(&ncp->ipcp.cfg.peer_range, '\0', sizeof ncp->ipcp.cfg.peer_range);
1564   ncp->ipcp.cfg.HaveTriggerAddress = 0;
1565   ncp->ipcp.cfg.netmask.s_addr = INADDR_ANY;
1566   iplist_reset(&ncp->ipcp.cfg.peer_list);
1567 
1568   if (arg->argc > arg->argn) {
1569     if (!ncprange_aton(&ncp->ipcp.cfg.my_range, ncp, arg->argv[arg->argn]))
1570       return 1;
1571     if (arg->argc > arg->argn+1) {
1572       hisaddr = arg->argv[arg->argn+1];
1573       if (arg->argc > arg->argn+2) {
1574         ncp->ipcp.ifmask = ncp->ipcp.cfg.netmask =
1575           GetIpAddr(arg->argv[arg->argn+2]);
1576 	if (arg->argc > arg->argn+3) {
1577 	  ncp->ipcp.cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
1578 	  ncp->ipcp.cfg.HaveTriggerAddress = 1;
1579 	}
1580       }
1581     }
1582   }
1583 
1584   /* 0.0.0.0 means any address (0 bits) */
1585   ncprange_getaddr(&ncp->ipcp.cfg.my_range, &ncpaddr);
1586   ncpaddr_getip4(&ncpaddr, &ncp->ipcp.my_ip);
1587   if (ncp->ipcp.my_ip.s_addr == INADDR_ANY)
1588     ncprange_setwidth(&ncp->ipcp.cfg.my_range, 0);
1589   bundle_AdjustFilters(arg->bundle, &ncpaddr, NULL);
1590 
1591   if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
1592                                   arg->bundle->phys_type.all & PHYS_AUTO))
1593     return 4;
1594 
1595   return 0;
1596 }
1597 
1598 static int
1599 SetRetry(int argc, char const *const *argv, u_int *timeout, u_int *maxreq,
1600           u_int *maxtrm, int def)
1601 {
1602   if (argc == 0) {
1603     *timeout = DEF_FSMRETRY;
1604     *maxreq = def;
1605     if (maxtrm != NULL)
1606       *maxtrm = def;
1607   } else {
1608     long l = atol(argv[0]);
1609 
1610     if (l < MIN_FSMRETRY) {
1611       log_Printf(LogWARN, "%ld: Invalid FSM retry period - min %d\n",
1612                  l, MIN_FSMRETRY);
1613       return 1;
1614     } else
1615       *timeout = l;
1616 
1617     if (argc > 1) {
1618       l = atol(argv[1]);
1619       if (l < 1) {
1620         log_Printf(LogWARN, "%ld: Invalid FSM REQ tries - changed to 1\n", l);
1621         l = 1;
1622       }
1623       *maxreq = l;
1624 
1625       if (argc > 2 && maxtrm != NULL) {
1626         l = atol(argv[2]);
1627         if (l < 1) {
1628           log_Printf(LogWARN, "%ld: Invalid FSM TRM tries - changed to 1\n", l);
1629           l = 1;
1630         }
1631         *maxtrm = l;
1632       }
1633     }
1634   }
1635 
1636   return 0;
1637 }
1638 
1639 static int
1640 SetVariable(struct cmdargs const *arg)
1641 {
1642   long long_val, param = (long)arg->cmd->args;
1643   int mode, dummyint, f, first, res;
1644   u_short *change;
1645   const char *argp;
1646   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1647   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1648   struct in_addr *ipaddr;
1649   struct ncpaddr ncpaddr[2];
1650 
1651   if (arg->argc > arg->argn)
1652     argp = arg->argv[arg->argn];
1653   else
1654     argp = "";
1655 
1656   res = 0;
1657 
1658   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1659     log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
1660               arg->cmd->name);
1661     return 1;
1662   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1663     log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
1664               arg->cmd->name, cx->name);
1665     cx = NULL;
1666   }
1667 
1668   switch (param) {
1669   case VAR_AUTHKEY:
1670     strncpy(arg->bundle->cfg.auth.key, argp,
1671             sizeof arg->bundle->cfg.auth.key - 1);
1672     arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
1673     break;
1674 
1675   case VAR_AUTHNAME:
1676     switch (bundle_Phase(arg->bundle)) {
1677       default:
1678         log_Printf(LogWARN, "Altering authname while at phase %s\n",
1679                    bundle_PhaseName(arg->bundle));
1680         /* drop through */
1681       case PHASE_DEAD:
1682       case PHASE_ESTABLISH:
1683         strncpy(arg->bundle->cfg.auth.name, argp,
1684                 sizeof arg->bundle->cfg.auth.name - 1);
1685         arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0';
1686         break;
1687     }
1688     break;
1689 
1690   case VAR_AUTOLOAD:
1691     if (arg->argc == arg->argn + 3) {
1692       int v1, v2, v3;
1693       char *end;
1694 
1695       v1 = strtol(arg->argv[arg->argn], &end, 0);
1696       if (v1 < 0 || *end) {
1697         log_Printf(LogWARN, "autoload: %s: Invalid min percentage\n",
1698                    arg->argv[arg->argn]);
1699         res = 1;
1700         break;
1701       }
1702 
1703       v2 = strtol(arg->argv[arg->argn + 1], &end, 0);
1704       if (v2 < 0 || *end) {
1705         log_Printf(LogWARN, "autoload: %s: Invalid max percentage\n",
1706                    arg->argv[arg->argn + 1]);
1707         res = 1;
1708         break;
1709       }
1710       if (v2 < v1) {
1711         v3 = v1;
1712         v1 = v2;
1713         v2 = v3;
1714       }
1715 
1716       v3 = strtol(arg->argv[arg->argn + 2], &end, 0);
1717       if (v3 <= 0 || *end) {
1718         log_Printf(LogWARN, "autoload: %s: Invalid throughput period\n",
1719                    arg->argv[arg->argn + 2]);
1720         res = 1;
1721         break;
1722       }
1723 
1724       arg->bundle->ncp.mp.cfg.autoload.min = v1;
1725       arg->bundle->ncp.mp.cfg.autoload.max = v2;
1726       arg->bundle->ncp.mp.cfg.autoload.period = v3;
1727       mp_RestartAutoloadTimer(&arg->bundle->ncp.mp);
1728     } else {
1729       log_Printf(LogWARN, "Set autoload requires three arguments\n");
1730       res = 1;
1731     }
1732     break;
1733 
1734   case VAR_DIAL:
1735     strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
1736     cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
1737     break;
1738 
1739   case VAR_LOGIN:
1740     strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
1741     cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
1742     break;
1743 
1744   case VAR_WINSIZE:
1745     if (arg->argc > arg->argn) {
1746       l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
1747       if (l->ccp.cfg.deflate.out.winsize < 8 ||
1748           l->ccp.cfg.deflate.out.winsize > 15) {
1749           log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
1750                     l->ccp.cfg.deflate.out.winsize);
1751           l->ccp.cfg.deflate.out.winsize = 15;
1752       }
1753       if (arg->argc > arg->argn+1) {
1754         l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
1755         if (l->ccp.cfg.deflate.in.winsize < 8 ||
1756             l->ccp.cfg.deflate.in.winsize > 15) {
1757             log_Printf(LogWARN, "%d: Invalid incoming window size\n",
1758                       l->ccp.cfg.deflate.in.winsize);
1759             l->ccp.cfg.deflate.in.winsize = 15;
1760         }
1761       } else
1762         l->ccp.cfg.deflate.in.winsize = 0;
1763     } else {
1764       log_Printf(LogWARN, "No window size specified\n");
1765       res = 1;
1766     }
1767     break;
1768 
1769 #ifndef NODES
1770   case VAR_MPPE:
1771     if (arg->argc > arg->argn + 2) {
1772       res = -1;
1773       break;
1774     }
1775 
1776     if (arg->argc == arg->argn) {
1777       l->ccp.cfg.mppe.keybits = 0;
1778       l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1779       l->ccp.cfg.mppe.required = 0;
1780       break;
1781     }
1782 
1783     if (!strcmp(argp, "*"))
1784       long_val = 0;
1785     else {
1786       long_val = atol(argp);
1787       if (long_val != 40 && long_val != 56 && long_val != 128) {
1788         log_Printf(LogWARN, "%s: Invalid bits value\n", argp);
1789         res = -1;
1790         break;
1791       }
1792     }
1793 
1794     if (arg->argc == arg->argn + 2) {
1795       if (!strcmp(arg->argv[arg->argn + 1], "*"))
1796         l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1797       else if (!strcasecmp(arg->argv[arg->argn + 1], "stateless"))
1798         l->ccp.cfg.mppe.state = MPPE_STATELESS;
1799       else if (!strcasecmp(arg->argv[arg->argn + 1], "stateful"))
1800         l->ccp.cfg.mppe.state = MPPE_STATEFUL;
1801       else {
1802         log_Printf(LogWARN, "%s: Invalid state value\n",
1803                    arg->argv[arg->argn + 1]);
1804         res = -1;
1805         break;
1806       }
1807     } else
1808       l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1809     l->ccp.cfg.mppe.keybits = long_val;
1810     l->ccp.cfg.mppe.required = 1;
1811     break;
1812 #endif
1813 
1814   case VAR_DEVICE:
1815     physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
1816                            arg->argv + arg->argn);
1817     break;
1818 
1819   case VAR_ACCMAP:
1820     if (arg->argc > arg->argn) {
1821       u_long ulong_val;
1822       sscanf(argp, "%lx", &ulong_val);
1823       cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val;
1824     } else {
1825       log_Printf(LogWARN, "No accmap specified\n");
1826       res = 1;
1827     }
1828     break;
1829 
1830   case VAR_MODE:
1831     mode = Nam2mode(argp);
1832     if (mode == PHYS_NONE || mode == PHYS_ALL) {
1833       log_Printf(LogWARN, "%s: Invalid mode\n", argp);
1834       res = -1;
1835       break;
1836     }
1837     bundle_SetMode(arg->bundle, cx, mode);
1838     break;
1839 
1840   case VAR_MRRU:
1841     switch (bundle_Phase(arg->bundle)) {
1842       case PHASE_DEAD:
1843         break;
1844       case PHASE_ESTABLISH:
1845         /* Make sure none of our links are DATALINK_LCP or greater */
1846         if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
1847           log_Printf(LogWARN, "mrru: Only changable before LCP negotiations\n");
1848           res = 1;
1849           break;
1850         }
1851         break;
1852       default:
1853         log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n");
1854         res = 1;
1855         break;
1856     }
1857     if (res != 0)
1858       break;
1859     long_val = atol(argp);
1860     if (long_val && long_val < MIN_MRU) {
1861       log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU);
1862       res = 1;
1863       break;
1864     } else if (long_val > MAX_MRU) {
1865       log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
1866       res = 1;
1867       break;
1868     } else
1869       arg->bundle->ncp.mp.cfg.mrru = long_val;
1870     break;
1871 
1872   case VAR_MRU:
1873     long_val = 0;	/* silence gcc */
1874     change = NULL;	/* silence gcc */
1875     switch(arg->argc - arg->argn) {
1876     case 1:
1877       if (argp[strspn(argp, "0123456789")] != '\0') {
1878         res = -1;
1879         break;
1880       }
1881       /*FALLTHRU*/
1882     case 0:
1883       long_val = atol(argp);
1884       change = &l->lcp.cfg.mru;
1885       if (long_val > l->lcp.cfg.max_mru) {
1886         log_Printf(LogWARN, "MRU %ld: too large - max set to %d\n", long_val,
1887                    l->lcp.cfg.max_mru);
1888         res = 1;
1889         break;
1890       }
1891       break;
1892     case 2:
1893       if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
1894         res = -1;
1895         break;
1896       }
1897       long_val = atol(arg->argv[arg->argn + 1]);
1898       change = &l->lcp.cfg.max_mru;
1899       if (long_val > MAX_MRU) {
1900         log_Printf(LogWARN, "MRU %ld: too large - maximum is %d\n", long_val,
1901                    MAX_MRU);
1902         res = 1;
1903         break;
1904       }
1905       break;
1906     default:
1907       res = -1;
1908       break;
1909     }
1910     if (res != 0)
1911       break;
1912 
1913     if (long_val == 0)
1914       *change = 0;
1915     else if (long_val < MIN_MRU) {
1916       log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
1917       res = 1;
1918       break;
1919     } else if (long_val > MAX_MRU) {
1920       log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
1921       res = 1;
1922       break;
1923     } else
1924       *change = long_val;
1925     if (l->lcp.cfg.mru > *change)
1926       l->lcp.cfg.mru = *change;
1927     break;
1928 
1929   case VAR_MTU:
1930     long_val = 0;	/* silence gcc */
1931     change = NULL;	/* silence gcc */
1932     switch(arg->argc - arg->argn) {
1933     case 1:
1934       if (argp[strspn(argp, "0123456789")] != '\0') {
1935         res = -1;
1936         break;
1937       }
1938       /*FALLTHRU*/
1939     case 0:
1940       long_val = atol(argp);
1941       change = &l->lcp.cfg.mtu;
1942       if (long_val > l->lcp.cfg.max_mtu) {
1943         log_Printf(LogWARN, "MTU %ld: too large - max set to %d\n", long_val,
1944                    l->lcp.cfg.max_mtu);
1945         res = 1;
1946         break;
1947       }
1948       break;
1949     case 2:
1950       if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
1951         res = -1;
1952         break;
1953       }
1954       long_val = atol(arg->argv[arg->argn + 1]);
1955       change = &l->lcp.cfg.max_mtu;
1956       if (long_val > MAX_MTU) {
1957         log_Printf(LogWARN, "MTU %ld: too large - maximum is %d\n", long_val,
1958                    MAX_MTU);
1959         res = 1;
1960         break;
1961       }
1962       break;
1963     default:
1964       res = -1;
1965       break;
1966     }
1967 
1968     if (res != 0)
1969       break;
1970 
1971     if (long_val && long_val < MIN_MTU) {
1972       log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
1973       res = 1;
1974       break;
1975     } else if (long_val > MAX_MTU) {
1976       log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
1977       res = 1;
1978       break;
1979     } else
1980       *change = long_val;
1981     if (l->lcp.cfg.mtu > *change)
1982       l->lcp.cfg.mtu = *change;
1983     break;
1984 
1985   case VAR_OPENMODE:
1986     if (strcasecmp(argp, "active") == 0)
1987       cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
1988         atoi(arg->argv[arg->argn+1]) : 1;
1989     else if (strcasecmp(argp, "passive") == 0)
1990       cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
1991     else {
1992       log_Printf(LogWARN, "%s: Invalid openmode\n", argp);
1993       res = 1;
1994     }
1995     break;
1996 
1997   case VAR_PHONE:
1998     strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
1999     cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
2000     cx->phone.alt = cx->phone.next = NULL;
2001     break;
2002 
2003   case VAR_HANGUP:
2004     strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
2005     cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
2006     break;
2007 
2008   case VAR_IFQUEUE:
2009     long_val = atol(argp);
2010     arg->bundle->cfg.ifqueue = long_val < 0 ? 0 : long_val;
2011     break;
2012 
2013   case VAR_LOGOUT:
2014     strncpy(cx->cfg.script.logout, argp, sizeof cx->cfg.script.logout - 1);
2015     cx->cfg.script.logout[sizeof cx->cfg.script.logout - 1] = '\0';
2016     break;
2017 
2018   case VAR_IDLETIMEOUT:
2019     if (arg->argc > arg->argn+2) {
2020       log_Printf(LogWARN, "Too many idle timeout values\n");
2021       res = 1;
2022     } else if (arg->argc == arg->argn) {
2023       log_Printf(LogWARN, "Too few idle timeout values\n");
2024       res = 1;
2025     } else {
2026       unsigned long timeout, min;
2027 
2028       timeout = strtoul(argp, NULL, 10);
2029       min = arg->bundle->cfg.idle.min_timeout;
2030       if (arg->argc == arg->argn + 2)
2031 	min = strtoul(arg->argv[arg->argn + 1], NULL, 10);
2032       bundle_SetIdleTimer(arg->bundle, timeout, min);
2033     }
2034     break;
2035 
2036 #ifndef NORADIUS
2037   case VAR_RAD_ALIVE:
2038     if (arg->argc > arg->argn + 2) {
2039       log_Printf(LogWARN, "Too many RADIUS alive interval values\n");
2040       res = 1;
2041     } else if (arg->argc == arg->argn) {
2042       log_Printf(LogWARN, "Too few RADIUS alive interval values\n");
2043       res = 1;
2044     } else {
2045       arg->bundle->radius.alive.interval = atoi(argp);
2046       if (arg->bundle->radius.alive.interval && !arg->bundle->radius.cfg.file) {
2047         log_Printf(LogWARN, "rad_alive requires radius to be configured\n");
2048 	res = 1;
2049       } else if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED) {
2050 	if (arg->bundle->radius.alive.interval)
2051 	  radius_StartTimer(arg->bundle);
2052 	else
2053 	  radius_StopTimer(&arg->bundle->radius);
2054       }
2055     }
2056     break;
2057 #endif
2058 
2059   case VAR_LQRPERIOD:
2060     long_val = atol(argp);
2061     if (long_val < MIN_LQRPERIOD) {
2062       log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n",
2063                  long_val, MIN_LQRPERIOD);
2064       res = 1;
2065     } else
2066       l->lcp.cfg.lqrperiod = long_val;
2067     break;
2068 
2069   case VAR_LCPRETRY:
2070     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2071                    &cx->physical->link.lcp.cfg.fsm.timeout,
2072                    &cx->physical->link.lcp.cfg.fsm.maxreq,
2073                    &cx->physical->link.lcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2074     break;
2075 
2076   case VAR_CHAPRETRY:
2077     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2078                    &cx->chap.auth.cfg.fsm.timeout,
2079                    &cx->chap.auth.cfg.fsm.maxreq, NULL, DEF_FSMAUTHTRIES);
2080     break;
2081 
2082   case VAR_PAPRETRY:
2083     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2084                    &cx->pap.cfg.fsm.timeout, &cx->pap.cfg.fsm.maxreq,
2085                    NULL, DEF_FSMAUTHTRIES);
2086     break;
2087 
2088   case VAR_CCPRETRY:
2089     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2090                    &l->ccp.cfg.fsm.timeout, &l->ccp.cfg.fsm.maxreq,
2091                    &l->ccp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2092     break;
2093 
2094   case VAR_IPCPRETRY:
2095     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2096                    &arg->bundle->ncp.ipcp.cfg.fsm.timeout,
2097                    &arg->bundle->ncp.ipcp.cfg.fsm.maxreq,
2098                    &arg->bundle->ncp.ipcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2099     break;
2100 
2101 #ifndef NOINET6
2102   case VAR_IPV6CPRETRY:
2103     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2104                    &arg->bundle->ncp.ipv6cp.cfg.fsm.timeout,
2105                    &arg->bundle->ncp.ipv6cp.cfg.fsm.maxreq,
2106                    &arg->bundle->ncp.ipv6cp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2107     break;
2108 #endif
2109 
2110   case VAR_NBNS:
2111   case VAR_DNS:
2112     if (param == VAR_DNS) {
2113       ipaddr = arg->bundle->ncp.ipcp.cfg.ns.dns;
2114       ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_NONE;
2115     } else {
2116       ipaddr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
2117       ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_ANY;
2118     }
2119 
2120     if (arg->argc > arg->argn) {
2121       ncpaddr_aton(ncpaddr, &arg->bundle->ncp, arg->argv[arg->argn]);
2122       if (!ncpaddr_getip4(ncpaddr, ipaddr))
2123         return -1;
2124       if (arg->argc > arg->argn+1) {
2125         ncpaddr_aton(ncpaddr + 1, &arg->bundle->ncp, arg->argv[arg->argn + 1]);
2126         if (!ncpaddr_getip4(ncpaddr + 1, ipaddr + 1))
2127           return -1;
2128       }
2129 
2130       if (ipaddr[0].s_addr == INADDR_ANY) {
2131         ipaddr[0] = ipaddr[1];
2132         ipaddr[1].s_addr = INADDR_ANY;
2133       }
2134       if (ipaddr[0].s_addr == INADDR_NONE) {
2135         ipaddr[0] = ipaddr[1];
2136         ipaddr[1].s_addr = INADDR_NONE;
2137       }
2138     }
2139     break;
2140 
2141   case VAR_CALLBACK:
2142     cx->cfg.callback.opmask = 0;
2143     for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) {
2144       if (!strcasecmp(arg->argv[dummyint], "auth"))
2145         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH);
2146       else if (!strcasecmp(arg->argv[dummyint], "cbcp"))
2147         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP);
2148       else if (!strcasecmp(arg->argv[dummyint], "e.164")) {
2149         if (dummyint == arg->argc - 1)
2150           log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n");
2151         else {
2152           cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164);
2153           strncpy(cx->cfg.callback.msg, arg->argv[++dummyint],
2154                   sizeof cx->cfg.callback.msg - 1);
2155           cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0';
2156         }
2157       } else if (!strcasecmp(arg->argv[dummyint], "none"))
2158         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE);
2159       else {
2160         res = -1;
2161         break;
2162       }
2163     }
2164     if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE))
2165       cx->cfg.callback.opmask = 0;
2166     break;
2167 
2168   case VAR_CBCP:
2169     cx->cfg.cbcp.delay = 0;
2170     *cx->cfg.cbcp.phone = '\0';
2171     cx->cfg.cbcp.fsmretry = DEF_FSMRETRY;
2172     if (arg->argc > arg->argn) {
2173       strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn],
2174               sizeof cx->cfg.cbcp.phone - 1);
2175       cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0';
2176       if (arg->argc > arg->argn + 1) {
2177         cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]);
2178         if (arg->argc > arg->argn + 2) {
2179           long_val = atol(arg->argv[arg->argn + 2]);
2180           if (long_val < MIN_FSMRETRY)
2181             log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n",
2182                        long_val, MIN_FSMRETRY);
2183           else
2184             cx->cfg.cbcp.fsmretry = long_val;
2185         }
2186       }
2187     }
2188     break;
2189 
2190   case VAR_CHOKED:
2191     arg->bundle->cfg.choked.timeout = atoi(argp);
2192     if (arg->bundle->cfg.choked.timeout <= 0)
2193       arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT;
2194     break;
2195 
2196   case VAR_SENDPIPE:
2197     long_val = atol(argp);
2198     arg->bundle->ncp.cfg.sendpipe = long_val;
2199     break;
2200 
2201   case VAR_RECVPIPE:
2202     long_val = atol(argp);
2203     arg->bundle->ncp.cfg.recvpipe = long_val;
2204     break;
2205 
2206 #ifndef NORADIUS
2207   case VAR_RADIUS:
2208     if (!*argp)
2209       *arg->bundle->radius.cfg.file = '\0';
2210     else if (access(argp, R_OK)) {
2211       log_Printf(LogWARN, "%s: %s\n", argp, strerror(errno));
2212       res = 1;
2213       break;
2214     } else {
2215       strncpy(arg->bundle->radius.cfg.file, argp,
2216               sizeof arg->bundle->radius.cfg.file - 1);
2217       arg->bundle->radius.cfg.file
2218         [sizeof arg->bundle->radius.cfg.file - 1] = '\0';
2219     }
2220     break;
2221 #endif
2222 
2223   case VAR_CD:
2224     if (*argp) {
2225       if (strcasecmp(argp, "off")) {
2226         long_val = atol(argp);
2227         if (long_val < 0)
2228           long_val = 0;
2229         cx->physical->cfg.cd.delay = long_val;
2230         cx->physical->cfg.cd.necessity = argp[strlen(argp)-1] == '!' ?
2231           CD_REQUIRED : CD_VARIABLE;
2232       } else
2233         cx->physical->cfg.cd.necessity = CD_NOTREQUIRED;
2234     } else {
2235       cx->physical->cfg.cd.delay = 0;
2236       cx->physical->cfg.cd.necessity = CD_DEFAULT;
2237     }
2238     break;
2239 
2240   case VAR_PARITY:
2241     if (arg->argc == arg->argn + 1)
2242       res = physical_SetParity(arg->cx->physical, argp);
2243     else {
2244       log_Printf(LogWARN, "Parity value must be odd, even or none\n");
2245       res = 1;
2246     }
2247     break;
2248 
2249   case VAR_CRTSCTS:
2250     if (strcasecmp(argp, "on") == 0)
2251       physical_SetRtsCts(arg->cx->physical, 1);
2252     else if (strcasecmp(argp, "off") == 0)
2253       physical_SetRtsCts(arg->cx->physical, 0);
2254     else {
2255       log_Printf(LogWARN, "RTS/CTS value must be on or off\n");
2256       res = 1;
2257     }
2258     break;
2259 
2260   case VAR_URGENTPORTS:
2261     if (arg->argn == arg->argc) {
2262       ncp_SetUrgentTOS(&arg->bundle->ncp);
2263       ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2264       ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2265     } else if (!strcasecmp(arg->argv[arg->argn], "udp")) {
2266       ncp_SetUrgentTOS(&arg->bundle->ncp);
2267       if (arg->argn == arg->argc - 1)
2268         ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2269       else for (f = arg->argn + 1; f < arg->argc; f++)
2270         if (*arg->argv[f] == '+')
2271           ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2272         else if (*arg->argv[f] == '-')
2273           ncp_RemoveUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2274         else {
2275           if (f == arg->argn)
2276             ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2277           ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
2278         }
2279     } else if (arg->argn == arg->argc - 1 &&
2280                !strcasecmp(arg->argv[arg->argn], "none")) {
2281       ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2282       ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2283       ncp_ClearUrgentTOS(&arg->bundle->ncp);
2284     } else {
2285       ncp_SetUrgentTOS(&arg->bundle->ncp);
2286       first = arg->argn;
2287       if (!strcasecmp(arg->argv[first], "tcp") && ++first == arg->argc)
2288         ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2289 
2290       for (f = first; f < arg->argc; f++)
2291         if (*arg->argv[f] == '+')
2292           ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2293         else if (*arg->argv[f] == '-')
2294           ncp_RemoveUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2295         else {
2296           if (f == first)
2297             ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2298           ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
2299         }
2300     }
2301     break;
2302 
2303   case VAR_PPPOE:
2304     if (strcasecmp(argp, "3Com") == 0)
2305       physical_SetPPPoEnonstandard(arg->cx->physical, 1);
2306     else if (strcasecmp(argp, "standard") == 0)
2307       physical_SetPPPoEnonstandard(arg->cx->physical, 0);
2308     else {
2309       log_Printf(LogWARN, "PPPoE standard value must be \"standard\" or \"3Com\"\n");
2310       res = 1;
2311     }
2312     break;
2313 
2314   }
2315 
2316   return res;
2317 }
2318 
2319 static struct cmdtab const SetCommands[] = {
2320   {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2321   "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
2322   {"authkey", "key", SetVariable, LOCAL_AUTH,
2323   "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
2324   {"authname", NULL, SetVariable, LOCAL_AUTH,
2325   "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
2326   {"autoload", NULL, SetVariable, LOCAL_AUTH,
2327   "auto link [de]activation", "set autoload maxtime maxload mintime minload",
2328   (const void *)VAR_AUTOLOAD},
2329   {"bandwidth", NULL, mp_SetDatalinkBandwidth, LOCAL_AUTH | LOCAL_CX,
2330   "datalink bandwidth", "set bandwidth value", NULL},
2331   {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2332   "callback control", "set callback [none|auth|cbcp|"
2333   "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK},
2334   {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2335   "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]",
2336   (const void *)VAR_CBCP},
2337   {"ccpretry", "ccpretries", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2338    "CCP retries", "set ccpretry value [attempts]", (const void *)VAR_CCPRETRY},
2339   {"cd", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Carrier delay requirement",
2340    "set cd value[!]", (const void *)VAR_CD},
2341   {"chapretry", "chapretries", SetVariable, LOCAL_AUTH | LOCAL_CX,
2342    "CHAP retries", "set chapretry value [attempts]",
2343    (const void *)VAR_CHAPRETRY},
2344   {"choked", NULL, SetVariable, LOCAL_AUTH,
2345   "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED},
2346   {"ctsrts", "crtscts", SetVariable, LOCAL_AUTH | LOCAL_CX,
2347    "Use hardware flow control", "set ctsrts [on|off]",
2348    (const char *)VAR_CRTSCTS},
2349   {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2350   "deflate window sizes", "set deflate out-winsize in-winsize",
2351   (const void *) VAR_WINSIZE},
2352 #ifndef NODES
2353   {"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2354   "MPPE key size and state", "set mppe [40|56|128|* [stateful|stateless|*]]",
2355   (const void *) VAR_MPPE},
2356 #endif
2357   {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
2358   "physical device name", "set device|line device-name[,device-name]",
2359   (const void *) VAR_DEVICE},
2360   {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2361   "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
2362   {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
2363   "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
2364   {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
2365   "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]", NULL},
2366   {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
2367   "escape characters", "set escape hex-digit ...", NULL},
2368   {"filter", NULL, filter_Set, LOCAL_AUTH,
2369   "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
2370   "[src_addr[/width]] [dst_addr[/width]] [proto "
2371   "[src [lt|eq|gt port]] [dst [lt|eq|gt port]] [estab] [syn] [finrst]]", NULL},
2372   {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2373   "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
2374   {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
2375   "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]", NULL},
2376   {"ifqueue", NULL, SetVariable, LOCAL_AUTH, "interface queue",
2377   "set ifqueue packets", (const void *)VAR_IFQUEUE},
2378   {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries",
2379    "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY},
2380   {"ipv6cpretry", "ipv6cpretries", SetVariable, LOCAL_AUTH, "IPV6CP retries",
2381    "set ipv6cpretry value [attempts]", (const void *)VAR_IPV6CPRETRY},
2382   {"lcpretry", "lcpretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "LCP retries",
2383    "set lcpretry value [attempts]", (const void *)VAR_LCPRETRY},
2384   {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
2385   "set log [local] [+|-]all|async|cbcp|ccp|chat|command|connect|debug|dns|hdlc|"
2386   "id0|ipcp|lcp|lqm|phase|physical|radius|sync|tcp/ip|timer|tun...", NULL},
2387   {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2388   "login script", "set login chat-script", (const void *) VAR_LOGIN},
2389   {"logout", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2390   "logout script", "set logout chat-script", (const void *) VAR_LOGOUT},
2391   {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2392   "LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
2393   {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
2394   "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
2395   {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
2396   "set mrru value", (const void *)VAR_MRRU},
2397   {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2398   "MRU value", "set mru [max[imum]] [value]", (const void *)VAR_MRU},
2399   {"mtu", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2400   "interface MTU value", "set mtu [max[imum]] [value]", (const void *)VAR_MTU},
2401   {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
2402   "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
2403   {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
2404   "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
2405   {"papretry", "papretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "PAP retries",
2406    "set papretry value [attempts]", (const void *)VAR_PAPRETRY},
2407   {"parity", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "serial parity",
2408    "set parity [odd|even|none]", (const void *)VAR_PARITY},
2409   {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
2410   "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
2411   {"proctitle", "title", SetProcTitle, LOCAL_AUTH,
2412   "Process title", "set proctitle [value]", NULL},
2413 #ifndef NORADIUS
2414   {"radius", NULL, SetVariable, LOCAL_AUTH,
2415   "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS},
2416   {"rad_alive", NULL, SetVariable, LOCAL_AUTH,
2417   "Raduis alive interval", "set rad_alive value",
2418   (const void *)VAR_RAD_ALIVE},
2419 #endif
2420   {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
2421   "Reconnect timeout", "set reconnect value ntries", NULL},
2422   {"recvpipe", NULL, SetVariable, LOCAL_AUTH,
2423   "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE},
2424   {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
2425   "Redial timeout", "set redial secs[+inc[-incmax]][.next] [attempts]", NULL},
2426   {"sendpipe", NULL, SetVariable, LOCAL_AUTH,
2427   "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE},
2428   {"server", "socket", SetServer, LOCAL_AUTH, "diagnostic port",
2429   "set server|socket TcpPort|LocalName|none|open|closed [password [mask]]",
2430   NULL},
2431   {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
2432   "physical speed", "set speed value|sync", NULL},
2433   {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
2434   "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]", NULL},
2435   {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
2436   "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
2437   {"urgent", NULL, SetVariable, LOCAL_AUTH, "urgent ports",
2438   "set urgent [tcp|udp] [+|-]port...", (const void *)VAR_URGENTPORTS},
2439   {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
2440   "vj values", "set vj slots|slotcomp [value]", NULL},
2441   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
2442   "Display this message", "set help|? [command]", SetCommands},
2443   {"pppoe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2444    "Connect using standard/3Com mode", "set pppoe [standard|3Com]",
2445    (const char *)VAR_PPPOE},
2446   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
2447 };
2448 
2449 static int
2450 SetCommand(struct cmdargs const *arg)
2451 {
2452   if (arg->argc > arg->argn)
2453     FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
2454              arg->prompt, arg->cx);
2455   else if (arg->prompt)
2456     prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
2457 	          " syntax help.\n");
2458   else
2459     log_Printf(LogWARN, "set command must have arguments\n");
2460 
2461   return 0;
2462 }
2463 
2464 static int
2465 AddCommand(struct cmdargs const *arg)
2466 {
2467   struct ncpaddr gw;
2468   struct ncprange dest;
2469   struct in_addr host;
2470 #ifndef NOINET6
2471   struct in6_addr host6;
2472 #endif
2473   int dest_default, gw_arg, addrs;
2474 
2475   if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
2476     return -1;
2477 
2478   addrs = 0;
2479   dest_default = 0;
2480   if (arg->argc == arg->argn + 2) {
2481     if (!strcasecmp(arg->argv[arg->argn], "default"))
2482       dest_default = 1;
2483     else {
2484       if (!ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]))
2485         return -1;
2486       if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
2487         addrs = ROUTE_DSTMYADDR;
2488       else if (!strncasecmp(arg->argv[arg->argn], "MYADDR6", 7))
2489         addrs = ROUTE_DSTMYADDR6;
2490       else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
2491         addrs = ROUTE_DSTHISADDR;
2492       else if (!strncasecmp(arg->argv[arg->argn], "HISADDR6", 8))
2493         addrs = ROUTE_DSTHISADDR6;
2494       else if (!strncasecmp(arg->argv[arg->argn], "DNS0", 4))
2495         addrs = ROUTE_DSTDNS0;
2496       else if (!strncasecmp(arg->argv[arg->argn], "DNS1", 4))
2497         addrs = ROUTE_DSTDNS1;
2498     }
2499     gw_arg = 1;
2500   } else {
2501     if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
2502       addrs = ROUTE_DSTMYADDR;
2503       host = arg->bundle->ncp.ipcp.my_ip;
2504     } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
2505       addrs = ROUTE_DSTHISADDR;
2506       host = arg->bundle->ncp.ipcp.peer_ip;
2507     } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
2508       addrs = ROUTE_DSTDNS0;
2509       host = arg->bundle->ncp.ipcp.ns.dns[0];
2510     } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
2511       addrs = ROUTE_DSTDNS1;
2512       host = arg->bundle->ncp.ipcp.ns.dns[1];
2513     } else {
2514       host = GetIpAddr(arg->argv[arg->argn]);
2515       if (host.s_addr == INADDR_NONE) {
2516         log_Printf(LogWARN, "%s: Invalid destination address\n",
2517                    arg->argv[arg->argn]);
2518         return -1;
2519       }
2520     }
2521     ncprange_setip4(&dest, host, GetIpAddr(arg->argv[arg->argn + 1]));
2522     gw_arg = 2;
2523   }
2524 
2525   if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR") == 0) {
2526     ncpaddr_setip4(&gw, arg->bundle->ncp.ipcp.peer_ip);
2527     addrs |= ROUTE_GWHISADDR;
2528 #ifndef NOINET6
2529   } else if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR6") == 0) {
2530     if (!ncpaddr_getip6(&arg->bundle->ncp.ipv6cp.hisaddr, &host6))
2531       memset(&host6, '\0', sizeof host6);
2532     ncpaddr_setip6(&gw, &host6);
2533     addrs |= ROUTE_GWHISADDR6;
2534 #endif
2535   } else {
2536     if (!ncpaddr_aton(&gw, &arg->bundle->ncp, arg->argv[arg->argn + gw_arg])) {
2537       log_Printf(LogWARN, "%s: Invalid gateway address\n",
2538                  arg->argv[arg->argn + gw_arg]);
2539       return -1;
2540     }
2541   }
2542 
2543   if (dest_default)
2544     ncprange_setdefault(&dest, ncpaddr_family(&gw));
2545 
2546   if (rt_Set(arg->bundle, RTM_ADD, &dest, &gw, arg->cmd->args ? 1 : 0,
2547              ((addrs & ROUTE_GWHISADDR) || (addrs & ROUTE_GWHISADDR6)) ? 1 : 0)
2548       && addrs != ROUTE_STATIC)
2549     route_Add(&arg->bundle->ncp.route, addrs, &dest, &gw);
2550 
2551   return 0;
2552 }
2553 
2554 static int
2555 DeleteCommand(struct cmdargs const *arg)
2556 {
2557   struct ncprange dest;
2558   int addrs;
2559 
2560   if (arg->argc == arg->argn+1) {
2561     if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
2562       route_IfDelete(arg->bundle, 0);
2563       route_DeleteAll(&arg->bundle->ncp.route);
2564     } else {
2565       addrs = 0;
2566       if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
2567         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.my_ip);
2568         addrs = ROUTE_DSTMYADDR;
2569 #ifndef NOINET6
2570       } else if (strcasecmp(arg->argv[arg->argn], "MYADDR6") == 0) {
2571         ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.myaddr);
2572         addrs = ROUTE_DSTMYADDR6;
2573 #endif
2574       } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
2575         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.peer_ip);
2576         addrs = ROUTE_DSTHISADDR;
2577 #ifndef NOINET6
2578       } else if (strcasecmp(arg->argv[arg->argn], "HISADDR6") == 0) {
2579         ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.hisaddr);
2580         addrs = ROUTE_DSTHISADDR6;
2581 #endif
2582       } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
2583         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[0]);
2584         addrs = ROUTE_DSTDNS0;
2585       } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
2586         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[1]);
2587         addrs = ROUTE_DSTDNS1;
2588       } else {
2589         ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]);
2590         addrs = ROUTE_STATIC;
2591       }
2592       rt_Set(arg->bundle, RTM_DELETE, &dest, NULL, arg->cmd->args ? 1 : 0, 0);
2593       route_Delete(&arg->bundle->ncp.route, addrs, &dest);
2594     }
2595   } else
2596     return -1;
2597 
2598   return 0;
2599 }
2600 
2601 #ifndef NONAT
2602 static int
2603 NatEnable(struct cmdargs const *arg)
2604 {
2605   if (arg->argc == arg->argn+1) {
2606     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
2607       if (!arg->bundle->NatEnabled) {
2608         if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
2609           PacketAliasSetAddress(arg->bundle->ncp.ipcp.my_ip);
2610         arg->bundle->NatEnabled = 1;
2611       }
2612       return 0;
2613     } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
2614       arg->bundle->NatEnabled = 0;
2615       arg->bundle->cfg.opt &= ~OPT_IFACEALIAS;
2616       /* Don't iface_Clear() - there may be manually configured addresses */
2617       return 0;
2618     }
2619   }
2620 
2621   return -1;
2622 }
2623 
2624 
2625 static int
2626 NatOption(struct cmdargs const *arg)
2627 {
2628   long param = (long)arg->cmd->args;
2629 
2630   if (arg->argc == arg->argn+1) {
2631     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
2632       if (arg->bundle->NatEnabled) {
2633 	PacketAliasSetMode(param, param);
2634 	return 0;
2635       }
2636       log_Printf(LogWARN, "nat not enabled\n");
2637     } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
2638       if (arg->bundle->NatEnabled) {
2639 	PacketAliasSetMode(0, param);
2640 	return 0;
2641       }
2642       log_Printf(LogWARN, "nat not enabled\n");
2643     }
2644   }
2645   return -1;
2646 }
2647 #endif /* #ifndef NONAT */
2648 
2649 static int
2650 LinkCommand(struct cmdargs const *arg)
2651 {
2652   if (arg->argc > arg->argn+1) {
2653     char namelist[LINE_LEN];
2654     struct datalink *cx;
2655     char *name;
2656     int result = 0;
2657 
2658     if (!strcmp(arg->argv[arg->argn], "*")) {
2659       struct datalink *dl;
2660 
2661       cx = arg->bundle->links;
2662       while (cx) {
2663         /* Watch it, the command could be a ``remove'' */
2664         dl = cx->next;
2665         FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
2666                  arg->prompt, cx);
2667         for (cx = arg->bundle->links; cx; cx = cx->next)
2668           if (cx == dl)
2669             break;		/* Pointer's still valid ! */
2670       }
2671     } else {
2672       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
2673       namelist[sizeof namelist - 1] = '\0';
2674       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
2675         if (!bundle2datalink(arg->bundle, name)) {
2676           log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
2677           return 1;
2678         }
2679 
2680       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
2681       namelist[sizeof namelist - 1] = '\0';
2682       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
2683         cx = bundle2datalink(arg->bundle, name);
2684         if (cx)
2685           FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
2686                    arg->prompt, cx);
2687         else {
2688           log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
2689           result++;
2690         }
2691       }
2692     }
2693     return result;
2694   }
2695 
2696   log_Printf(LogWARN, "usage: %s\n", arg->cmd->syntax);
2697   return 2;
2698 }
2699 
2700 struct link *
2701 command_ChooseLink(struct cmdargs const *arg)
2702 {
2703   if (arg->cx)
2704     return &arg->cx->physical->link;
2705   else if (!arg->bundle->ncp.mp.cfg.mrru) {
2706     struct datalink *dl = bundle2datalink(arg->bundle, NULL);
2707     if (dl)
2708       return &dl->physical->link;
2709   }
2710   return &arg->bundle->ncp.mp.link;
2711 }
2712 
2713 static const char *
2714 ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
2715 {
2716   const char *result;
2717 
2718   switch (*cmd) {
2719     case 'A':
2720     case 'a':
2721       result = "accept";
2722       *keep = NEG_MYMASK;
2723       *add = NEG_ACCEPTED;
2724       break;
2725     case 'D':
2726     case 'd':
2727       switch (cmd[1]) {
2728         case 'E':
2729         case 'e':
2730           result = "deny";
2731           *keep = NEG_MYMASK;
2732           *add = 0;
2733           break;
2734         case 'I':
2735         case 'i':
2736           result = "disable";
2737           *keep = NEG_HISMASK;
2738           *add = 0;
2739           break;
2740         default:
2741           return NULL;
2742       }
2743       break;
2744     case 'E':
2745     case 'e':
2746       result = "enable";
2747       *keep = NEG_HISMASK;
2748       *add = NEG_ENABLED;
2749       break;
2750     default:
2751       return NULL;
2752   }
2753 
2754   return result;
2755 }
2756 
2757 static int
2758 OptSet(struct cmdargs const *arg)
2759 {
2760   int bit = (int)(long)arg->cmd->args;
2761   unsigned keep;			/* Keep these bits */
2762   unsigned add;				/* Add these bits */
2763 
2764   if (ident_cmd(arg->argv[arg->argn - 2], &keep, &add) == NULL)
2765     return 1;
2766 
2767 #ifndef NOINET6
2768   if (add == NEG_ENABLED && bit == OPT_IPV6CP && !probe.ipv6_available) {
2769     log_Printf(LogWARN, "IPv6 is not available on this machine\n");
2770     return 1;
2771   }
2772 #endif
2773 
2774   if (add)
2775     arg->bundle->cfg.opt |= bit;
2776   else
2777     arg->bundle->cfg.opt &= ~bit;
2778 
2779   return 0;
2780 }
2781 
2782 static int
2783 IfaceAliasOptSet(struct cmdargs const *arg)
2784 {
2785   unsigned save = arg->bundle->cfg.opt;
2786   int result = OptSet(arg);
2787 
2788   if (result == 0)
2789     if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->NatEnabled) {
2790       arg->bundle->cfg.opt = save;
2791       log_Printf(LogWARN, "Cannot enable iface-alias without NAT\n");
2792       result = 2;
2793     }
2794 
2795   return result;
2796 }
2797 
2798 static int
2799 NegotiateSet(struct cmdargs const *arg)
2800 {
2801   long param = (long)arg->cmd->args;
2802   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
2803   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
2804   const char *cmd;
2805   unsigned keep;			/* Keep these bits */
2806   unsigned add;				/* Add these bits */
2807 
2808   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
2809     return 1;
2810 
2811   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
2812     log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
2813               cmd, arg->cmd->name);
2814     return 2;
2815   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
2816     log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
2817               cmd, arg->cmd->name, cx->name);
2818     cx = NULL;
2819   }
2820 
2821   switch (param) {
2822     case NEG_ACFCOMP:
2823       cx->physical->link.lcp.cfg.acfcomp &= keep;
2824       cx->physical->link.lcp.cfg.acfcomp |= add;
2825       break;
2826     case NEG_CHAP05:
2827       cx->physical->link.lcp.cfg.chap05 &= keep;
2828       cx->physical->link.lcp.cfg.chap05 |= add;
2829       break;
2830 #ifndef NODES
2831     case NEG_CHAP80:
2832       cx->physical->link.lcp.cfg.chap80nt &= keep;
2833       cx->physical->link.lcp.cfg.chap80nt |= add;
2834       break;
2835     case NEG_CHAP80LM:
2836       cx->physical->link.lcp.cfg.chap80lm &= keep;
2837       cx->physical->link.lcp.cfg.chap80lm |= add;
2838       break;
2839     case NEG_CHAP81:
2840       cx->physical->link.lcp.cfg.chap81 &= keep;
2841       cx->physical->link.lcp.cfg.chap81 |= add;
2842       break;
2843     case NEG_MPPE:
2844       l->ccp.cfg.neg[CCP_NEG_MPPE] &= keep;
2845       l->ccp.cfg.neg[CCP_NEG_MPPE] |= add;
2846       break;
2847 #endif
2848     case NEG_DEFLATE:
2849       l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
2850       l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
2851       break;
2852     case NEG_DNS:
2853       arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
2854       arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
2855       break;
2856     case NEG_ENDDISC:
2857       arg->bundle->ncp.mp.cfg.negenddisc &= keep;
2858       arg->bundle->ncp.mp.cfg.negenddisc |= add;
2859       break;
2860     case NEG_LQR:
2861       cx->physical->link.lcp.cfg.lqr &= keep;
2862       cx->physical->link.lcp.cfg.lqr |= add;
2863       break;
2864     case NEG_PAP:
2865       cx->physical->link.lcp.cfg.pap &= keep;
2866       cx->physical->link.lcp.cfg.pap |= add;
2867       break;
2868     case NEG_PPPDDEFLATE:
2869       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
2870       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
2871       break;
2872     case NEG_PRED1:
2873       l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
2874       l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
2875       break;
2876     case NEG_PROTOCOMP:
2877       cx->physical->link.lcp.cfg.protocomp &= keep;
2878       cx->physical->link.lcp.cfg.protocomp |= add;
2879       break;
2880     case NEG_SHORTSEQ:
2881       switch (bundle_Phase(arg->bundle)) {
2882         case PHASE_DEAD:
2883           break;
2884         case PHASE_ESTABLISH:
2885           /* Make sure none of our links are DATALINK_LCP or greater */
2886           if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
2887             log_Printf(LogWARN, "shortseq: Only changable before"
2888                        " LCP negotiations\n");
2889             return 1;
2890           }
2891           break;
2892         default:
2893           log_Printf(LogWARN, "shortseq: Only changable at phase"
2894                      " DEAD/ESTABLISH\n");
2895           return 1;
2896       }
2897       arg->bundle->ncp.mp.cfg.shortseq &= keep;
2898       arg->bundle->ncp.mp.cfg.shortseq |= add;
2899       break;
2900     case NEG_VJCOMP:
2901       arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
2902       arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
2903       break;
2904   }
2905 
2906   return 0;
2907 }
2908 
2909 static struct cmdtab const NegotiateCommands[] = {
2910   {"filter-decapsulation", NULL, OptSet, LOCAL_AUTH,
2911   "filter on PPPoUDP payloads", "disable|enable",
2912   (const void *)OPT_FILTERDECAP},
2913   {"force-scripts", NULL, OptSet, LOCAL_AUTH,
2914    "Force execution of the configured chat scripts", "disable|enable",
2915    (const void *)OPT_FORCE_SCRIPTS},
2916   {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
2917   "disable|enable", (const void *)OPT_IDCHECK},
2918   {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH,
2919   "retain interface addresses", "disable|enable",
2920   (const void *)OPT_IFACEALIAS},
2921 #ifndef NOINET6
2922   {"ipcp", NULL, OptSet, LOCAL_AUTH, "IP Network Control Protocol",
2923   "disable|enable", (const void *)OPT_IPCP},
2924   {"ipv6cp", NULL, OptSet, LOCAL_AUTH, "IPv6 Network Control Protocol",
2925   "disable|enable", (const void *)OPT_IPV6CP},
2926 #endif
2927   {"keep-session", NULL, OptSet, LOCAL_AUTH, "Retain device session leader",
2928   "disable|enable", (const void *)OPT_KEEPSESSION},
2929   {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
2930   "disable|enable", (const void *)OPT_LOOPBACK},
2931   {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
2932   "disable|enable", (const void *)OPT_PASSWDAUTH},
2933   {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry",
2934   "disable|enable", (const void *)OPT_PROXY},
2935   {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts",
2936   "disable|enable", (const void *)OPT_PROXYALL},
2937   {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
2938   "disable|enable", (const void *)OPT_SROUTES},
2939   {"tcpmssfixup", "mssfixup", OptSet, LOCAL_AUTH, "Modify MSS options",
2940   "disable|enable", (const void *)OPT_TCPMSSFIXUP},
2941   {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
2942   "disable|enable", (const void *)OPT_THROUGHPUT},
2943   {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
2944   "disable|enable", (const void *)OPT_UTMP},
2945 
2946 #ifndef NOINET6
2947 #define OPT_MAX 14	/* accept/deny allowed below and not above */
2948 #else
2949 #define OPT_MAX 12
2950 #endif
2951 
2952   {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2953   "Address & Control field compression", "accept|deny|disable|enable",
2954   (const void *)NEG_ACFCOMP},
2955   {"chap", "chap05", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2956   "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
2957   (const void *)NEG_CHAP05},
2958 #ifndef NODES
2959   {"mschap", "chap80nt", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2960   "Microsoft (NT) CHAP", "accept|deny|disable|enable",
2961   (const void *)NEG_CHAP80},
2962   {"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2963   "Microsoft (NT) CHAP", "accept|deny|disable|enable",
2964   (const void *)NEG_CHAP80LM},
2965   {"mschapv2", "chap81", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2966   "Microsoft CHAP v2", "accept|deny|disable|enable",
2967   (const void *)NEG_CHAP81},
2968   {"mppe", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2969   "MPPE encryption", "accept|deny|disable|enable",
2970   (const void *)NEG_MPPE},
2971 #endif
2972   {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2973   "Deflate compression", "accept|deny|disable|enable",
2974   (const void *)NEG_DEFLATE},
2975   {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2976   "Deflate (type 24) compression", "accept|deny|disable|enable",
2977   (const void *)NEG_PPPDDEFLATE},
2978   {"dns", NULL, NegotiateSet, LOCAL_AUTH,
2979   "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
2980   {"enddisc", NULL, NegotiateSet, LOCAL_AUTH, "ENDDISC negotiation",
2981   "accept|deny|disable|enable", (const void *)NEG_ENDDISC},
2982   {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2983   "Link Quality Reports", "accept|deny|disable|enable",
2984   (const void *)NEG_LQR},
2985   {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2986   "Password Authentication protocol", "accept|deny|disable|enable",
2987   (const void *)NEG_PAP},
2988   {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2989   "Predictor 1 compression", "accept|deny|disable|enable",
2990   (const void *)NEG_PRED1},
2991   {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2992   "Protocol field compression", "accept|deny|disable|enable",
2993   (const void *)NEG_PROTOCOMP},
2994   {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
2995   "MP Short Sequence Numbers", "accept|deny|disable|enable",
2996   (const void *)NEG_SHORTSEQ},
2997   {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
2998   "Van Jacobson header compression", "accept|deny|disable|enable",
2999   (const void *)NEG_VJCOMP},
3000   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
3001   "Display this message", "accept|deny|disable|enable help|? [value]",
3002   NegotiateCommands},
3003   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
3004 };
3005 
3006 static int
3007 NegotiateCommand(struct cmdargs const *arg)
3008 {
3009   if (arg->argc > arg->argn) {
3010     char const *argv[3];
3011     unsigned keep, add;
3012     int n;
3013 
3014     if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
3015       return -1;
3016     argv[2] = NULL;
3017 
3018     for (n = arg->argn; n < arg->argc; n++) {
3019       argv[1] = arg->argv[n];
3020       FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
3021                0 : OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
3022     }
3023   } else if (arg->prompt)
3024     prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
3025 	    arg->argv[arg->argn-1]);
3026   else
3027     log_Printf(LogWARN, "%s command must have arguments\n",
3028               arg->argv[arg->argn] );
3029 
3030   return 0;
3031 }
3032 
3033 const char *
3034 command_ShowNegval(unsigned val)
3035 {
3036   switch (val&3) {
3037     case 1: return "disabled & accepted";
3038     case 2: return "enabled & denied";
3039     case 3: return "enabled & accepted";
3040   }
3041   return "disabled & denied";
3042 }
3043 
3044 static int
3045 ClearCommand(struct cmdargs const *arg)
3046 {
3047   struct pppThroughput *t;
3048   struct datalink *cx;
3049   int i, clear_type;
3050 
3051   if (arg->argc < arg->argn + 1)
3052     return -1;
3053 
3054   if (strcasecmp(arg->argv[arg->argn], "physical") == 0) {
3055     cx = arg->cx;
3056     if (!cx)
3057       cx = bundle2datalink(arg->bundle, NULL);
3058     if (!cx) {
3059       log_Printf(LogWARN, "A link must be specified for ``clear physical''\n");
3060       return 1;
3061     }
3062     t = &cx->physical->link.stats.total;
3063   } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
3064     t = &arg->bundle->ncp.ipcp.throughput;
3065 #ifndef NOINET6
3066   else if (strcasecmp(arg->argv[arg->argn], "ipv6cp") == 0)
3067     t = &arg->bundle->ncp.ipv6cp.throughput;
3068 #endif
3069   else
3070     return -1;
3071 
3072   if (arg->argc > arg->argn + 1) {
3073     clear_type = 0;
3074     for (i = arg->argn + 1; i < arg->argc; i++)
3075       if (strcasecmp(arg->argv[i], "overall") == 0)
3076         clear_type |= THROUGHPUT_OVERALL;
3077       else if (strcasecmp(arg->argv[i], "current") == 0)
3078         clear_type |= THROUGHPUT_CURRENT;
3079       else if (strcasecmp(arg->argv[i], "peak") == 0)
3080         clear_type |= THROUGHPUT_PEAK;
3081       else
3082         return -1;
3083   } else
3084     clear_type = THROUGHPUT_ALL;
3085 
3086   throughput_clear(t, clear_type, arg->prompt);
3087   return 0;
3088 }
3089 
3090 static int
3091 RunListCommand(struct cmdargs const *arg)
3092 {
3093   const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???";
3094 
3095 #ifndef NONAT
3096   if (arg->cmd->args == NatCommands &&
3097       tolower(*arg->argv[arg->argn - 1]) == 'a') {
3098     if (arg->prompt)
3099       prompt_Printf(arg->prompt, "The alias command is deprecated\n");
3100     else
3101       log_Printf(LogWARN, "The alias command is deprecated\n");
3102   }
3103 #endif
3104 
3105   if (arg->argc > arg->argn)
3106     FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv,
3107              arg->prompt, arg->cx);
3108   else if (arg->prompt)
3109     prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help"
3110                   " <option>' for syntax help.\n", cmd, cmd);
3111   else
3112     log_Printf(LogWARN, "%s command must have arguments\n", cmd);
3113 
3114   return 0;
3115 }
3116 
3117 static int
3118 IfaceAddCommand(struct cmdargs const *arg)
3119 {
3120   struct ncpaddr peer, addr;
3121   struct ncprange ifa;
3122   struct in_addr mask;
3123   int n, how;
3124 
3125   if (arg->argc == arg->argn + 1) {
3126     if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
3127       return -1;
3128     ncpaddr_init(&peer);
3129   } else {
3130     if (arg->argc == arg->argn + 2) {
3131       if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
3132         return -1;
3133       n = 1;
3134     } else if (arg->argc == arg->argn + 3) {
3135       if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn]))
3136         return -1;
3137       if (ncpaddr_family(&addr) != AF_INET)
3138         return -1;
3139       ncprange_sethost(&ifa, &addr);
3140       if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn + 1]))
3141         return -1;
3142       if (!ncpaddr_getip4(&addr, &mask))
3143         return -1;
3144       if (!ncprange_setip4mask(&ifa, mask))
3145         return -1;
3146       n = 2;
3147     } else
3148       return -1;
3149 
3150     if (!ncpaddr_aton(&peer, NULL, arg->argv[arg->argn + n]))
3151       return -1;
3152 
3153     if (ncprange_family(&ifa) != ncpaddr_family(&peer)) {
3154       log_Printf(LogWARN, "IfaceAddCommand: src and dst address families"
3155                  " differ\n");
3156       return -1;
3157     }
3158   }
3159 
3160   how = IFACE_ADD_LAST;
3161   if (arg->cmd->args)
3162     how |= IFACE_FORCE_ADD;
3163 
3164   return !iface_Add(arg->bundle->iface, &arg->bundle->ncp, &ifa, &peer, how);
3165 }
3166 
3167 static int
3168 IfaceDeleteCommand(struct cmdargs const *arg)
3169 {
3170   struct ncpaddr ifa;
3171   struct in_addr ifa4;
3172   int ok;
3173 
3174   if (arg->argc != arg->argn + 1)
3175     return -1;
3176 
3177   if (!ncpaddr_aton(&ifa, NULL, arg->argv[arg->argn]))
3178     return -1;
3179 
3180   if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
3181       ncpaddr_getip4(&ifa, &ifa4) &&
3182       arg->bundle->ncp.ipcp.my_ip.s_addr == ifa4.s_addr) {
3183     log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
3184                ncpaddr_ntoa(&ifa));
3185     return 1;
3186   }
3187 
3188   ok = iface_Delete(arg->bundle->iface, &arg->bundle->ncp, &ifa);
3189   if (!ok) {
3190     if (arg->cmd->args)
3191       ok = 1;
3192     else if (arg->prompt)
3193       prompt_Printf(arg->prompt, "%s: No such interface address\n",
3194                     ncpaddr_ntoa(&ifa));
3195     else
3196       log_Printf(LogWARN, "%s: No such interface address\n",
3197                  ncpaddr_ntoa(&ifa));
3198   }
3199 
3200   return !ok;
3201 }
3202 
3203 static int
3204 IfaceClearCommand(struct cmdargs const *arg)
3205 {
3206   int family, how;
3207 
3208   family = 0;
3209   if (arg->argc == arg->argn + 1) {
3210     if (strcasecmp(arg->argv[arg->argn], "inet") == 0)
3211       family = AF_INET;
3212 #ifndef NOINET6
3213     else if (strcasecmp(arg->argv[arg->argn], "inet6") == 0)
3214       family = AF_INET6;
3215 #endif
3216     else
3217       return -1;
3218   } else if (arg->argc != arg->argn)
3219     return -1;
3220 
3221   how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED ||
3222         arg->bundle->phys_type.all & PHYS_AUTO ?
3223         IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL;
3224   iface_Clear(arg->bundle->iface, &arg->bundle->ncp, family, how);
3225 
3226   return 0;
3227 }
3228 
3229 static int
3230 SetProcTitle(struct cmdargs const *arg)
3231 {
3232   static char title[LINE_LEN];
3233   char *argv[MAXARGS];
3234   int argc = arg->argc - arg->argn;
3235 
3236   if (arg->argc <= arg->argn) {
3237     SetTitle(NULL);
3238     return 0;
3239   }
3240 
3241   if ((unsigned)argc >= sizeof argv / sizeof argv[0]) {
3242     argc = sizeof argv / sizeof argv[0] - 1;
3243     log_Printf(LogWARN, "Truncating proc title to %d args\n", argc);
3244   }
3245   command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
3246   Concatinate(title, sizeof title, argc, (const char *const *)argv);
3247   SetTitle(title);
3248   command_Free(argc, argv);
3249 
3250   return 0;
3251 }
3252