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