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