lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | /* |
| 2 | * options.c - handles option processing for PPP. |
| 3 | * |
| 4 | * Copyright (c) 1989 Carnegie Mellon University. |
| 5 | * All rights reserved. |
| 6 | * |
| 7 | * Redistribution and use in source and binary forms are permitted |
| 8 | * provided that the above copyright notice and this paragraph are |
| 9 | * duplicated in all such forms and that any documentation, |
| 10 | * advertising materials, and other materials related to such |
| 11 | * distribution and use acknowledge that the software was developed |
| 12 | * by Carnegie Mellon University. The name of the |
| 13 | * University may not be used to endorse or promote products derived |
| 14 | * from this software without specific prior written permission. |
| 15 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| 16 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| 17 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| 18 | */ |
| 19 | |
| 20 | #define RCSID "$Id: options.c,v 1.1 2008-08-04 06:11:51 winfred Exp $" |
| 21 | |
| 22 | #include <ctype.h> |
| 23 | #include <stdio.h> |
| 24 | #include <errno.h> |
| 25 | #include <unistd.h> |
| 26 | #include <fcntl.h> |
| 27 | #include <stdlib.h> |
| 28 | #include <syslog.h> |
| 29 | #include <string.h> |
| 30 | #include <pwd.h> |
| 31 | #ifdef PLUGIN |
| 32 | #include <dlfcn.h> |
| 33 | #endif |
| 34 | #ifdef PPP_FILTER |
| 35 | #include <pcap.h> |
| 36 | #include <pcap-int.h> |
| 37 | #endif |
| 38 | |
| 39 | #include "pppd.h" |
| 40 | #include "pathnames.h" |
| 41 | |
| 42 | #if defined(ultrix) || defined(NeXT) |
| 43 | char *strdup __P((char *)); |
| 44 | #endif |
| 45 | |
| 46 | static const char rcsid[] = RCSID; |
| 47 | |
| 48 | struct option_value { |
| 49 | struct option_value *next; |
| 50 | const char *source; |
| 51 | char value[1]; |
| 52 | }; |
| 53 | |
| 54 | /* |
| 55 | * Option variables and default values. |
| 56 | */ |
| 57 | #ifdef PPP_FILTER |
| 58 | int dflag = 0; /* Tell libpcap we want debugging */ |
| 59 | #endif |
| 60 | int debug = 0; /* Debug flag */ |
| 61 | int kdebugflag = 0; /* Tell kernel to print debug messages */ |
| 62 | int default_device = 1; /* Using /dev/tty or equivalent */ |
| 63 | char devnam[MAXPATHLEN]; /* Device name */ |
| 64 | bool nodetach = 0; /* Don't detach from controlling tty */ |
| 65 | bool updetach = 0; /* Detach once link is up */ |
| 66 | int maxconnect = 0; /* Maximum connect time */ |
| 67 | char user[MAXNAMELEN]; /* Username for PAP */ |
| 68 | char passwd[MAXSECRETLEN]; /* Password for PAP */ |
| 69 | bool persist = 0; /* Reopen link after it goes down */ |
| 70 | char our_name[MAXNAMELEN]; /* Our name for authentication purposes */ |
| 71 | bool demand = 0; /* do dial-on-demand */ |
| 72 | char *ipparam = NULL; /* Extra parameter for ip up/down scripts */ |
| 73 | int idle_time_limit = 0; /* Disconnect if idle for this many seconds */ |
| 74 | int holdoff = 30; /* # seconds to pause before reconnecting */ |
| 75 | bool holdoff_specified; /* true if a holdoff value has been given */ |
| 76 | int log_to_fd = 1; /* send log messages to this fd too */ |
| 77 | bool log_default = 1; /* log_to_fd is default (stdout) */ |
| 78 | int maxfail = 10; /* max # of unsuccessful connection attempts */ |
| 79 | char linkname[MAXPATHLEN]; /* logical name for link */ |
| 80 | bool tune_kernel; /* may alter kernel settings */ |
| 81 | int connect_delay = 1000; /* wait this many ms after connect script */ |
| 82 | int req_unit = -1; /* requested interface unit */ |
| 83 | bool multilink = 0; /* Enable multilink operation */ |
| 84 | char *bundle_name = NULL; /* bundle name for multilink */ |
| 85 | bool dump_options; /* print out option values */ |
| 86 | bool dryrun; /* print out option values and exit */ |
| 87 | char *domain; /* domain name set by domain option */ |
| 88 | |
| 89 | extern option_t auth_options[]; |
| 90 | extern struct stat devstat; |
| 91 | |
| 92 | #ifdef PPP_FILTER |
| 93 | struct bpf_program pass_filter;/* Filter program for packets to pass */ |
| 94 | struct bpf_program active_filter; /* Filter program for link-active pkts */ |
| 95 | pcap_t pc; /* Fake struct pcap so we can compile expr */ |
| 96 | #endif |
| 97 | |
| 98 | char *current_option; /* the name of the option being parsed */ |
| 99 | int privileged_option; /* set iff the current option came from root */ |
| 100 | char *option_source; /* string saying where the option came from */ |
| 101 | int option_priority = OPRIO_CFGFILE; /* priority of the current options */ |
| 102 | bool devnam_fixed; /* can no longer change device name */ |
| 103 | |
| 104 | static int logfile_fd = -1; /* fd opened for log file */ |
| 105 | static char logfile_name[MAXPATHLEN]; /* name of log file */ |
| 106 | |
| 107 | /* |
| 108 | * Prototypes |
| 109 | */ |
| 110 | static int setdomain __P((char **)); |
| 111 | static int readfile __P((char **)); |
| 112 | static int callfile __P((char **)); |
| 113 | static int showversion __P((char **)); |
| 114 | static int showhelp __P((char **)); |
| 115 | static void usage __P((void)); |
| 116 | static int setlogfile __P((char **)); |
| 117 | #ifdef PLUGIN |
| 118 | static int loadplugin __P((char **)); |
| 119 | #endif |
| 120 | |
| 121 | #ifdef PPP_FILTER |
| 122 | static int setpassfilter __P((char **)); |
| 123 | static int setactivefilter __P((char **)); |
| 124 | #endif |
| 125 | |
| 126 | static option_t *find_option __P((const char *name)); |
| 127 | static int process_option __P((option_t *, char *, char **)); |
| 128 | static int n_arguments __P((option_t *)); |
| 129 | static int number_option __P((char *, u_int32_t *, int)); |
| 130 | |
| 131 | /* |
| 132 | * Structure to store extra lists of options. |
| 133 | */ |
| 134 | struct option_list { |
| 135 | option_t *options; |
| 136 | struct option_list *next; |
| 137 | }; |
| 138 | |
| 139 | static struct option_list *extra_options = NULL; |
| 140 | |
| 141 | /* |
| 142 | * Valid arguments. |
| 143 | */ |
| 144 | option_t general_options[] = { |
| 145 | { "debug", o_int, &debug, |
| 146 | "Increase debugging level", OPT_INC | OPT_NOARG | 1 }, |
| 147 | { "-d", o_int, &debug, |
| 148 | "Increase debugging level", |
| 149 | OPT_ALIAS | OPT_INC | OPT_NOARG | 1 }, |
| 150 | |
| 151 | { "kdebug", o_int, &kdebugflag, |
| 152 | "Set kernel driver debug level", OPT_PRIO }, |
| 153 | |
| 154 | { "nodetach", o_bool, &nodetach, |
| 155 | "Don't detach from controlling tty", OPT_PRIO | 1 }, |
| 156 | { "-detach", o_bool, &nodetach, |
| 157 | "Don't detach from controlling tty", OPT_ALIAS | OPT_PRIOSUB | 1 }, |
| 158 | { "updetach", o_bool, &updetach, |
| 159 | "Detach from controlling tty once link is up", |
| 160 | OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach }, |
| 161 | |
| 162 | { "holdoff", o_int, &holdoff, |
| 163 | "Set time in seconds before retrying connection", OPT_PRIO }, |
| 164 | |
| 165 | { "idle", o_int, &idle_time_limit, |
| 166 | "Set time in seconds before disconnecting idle link", OPT_PRIO }, |
| 167 | |
| 168 | { "maxconnect", o_int, &maxconnect, |
| 169 | "Set connection time limit", |
| 170 | OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF }, |
| 171 | |
| 172 | { "domain", o_special, (void *)setdomain, |
| 173 | "Add given domain name to hostname", |
| 174 | OPT_PRIO | OPT_PRIV | OPT_A2STRVAL, &domain }, |
| 175 | |
| 176 | { "file", o_special, (void *)readfile, |
| 177 | "Take options from a file", OPT_NOPRINT }, |
| 178 | { "call", o_special, (void *)callfile, |
| 179 | "Take options from a privileged file", OPT_NOPRINT }, |
| 180 | |
| 181 | { "persist", o_bool, &persist, |
| 182 | "Keep on reopening connection after close", OPT_PRIO | 1 }, |
| 183 | { "nopersist", o_bool, &persist, |
| 184 | "Turn off persist option", OPT_PRIOSUB }, |
| 185 | |
| 186 | { "demand", o_bool, &demand, |
| 187 | "Dial on demand", OPT_INITONLY | 1, &persist }, |
| 188 | |
| 189 | { "--version", o_special_noarg, (void *)showversion, |
| 190 | "Show version number" }, |
| 191 | { "--help", o_special_noarg, (void *)showhelp, |
| 192 | "Show brief listing of options" }, |
| 193 | { "-h", o_special_noarg, (void *)showhelp, |
| 194 | "Show brief listing of options", OPT_ALIAS }, |
| 195 | |
| 196 | { "logfile", o_special, (void *)setlogfile, |
| 197 | "Append log messages to this file", |
| 198 | OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, &logfile_name }, |
| 199 | { "logfd", o_int, &log_to_fd, |
| 200 | "Send log messages to this file descriptor", |
| 201 | OPT_PRIOSUB | OPT_A2CLR, &log_default }, |
| 202 | { "nolog", o_int, &log_to_fd, |
| 203 | "Don't send log messages to any file", |
| 204 | OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) }, |
| 205 | { "nologfd", o_int, &log_to_fd, |
| 206 | "Don't send log messages to any file descriptor", |
| 207 | OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, |
| 208 | |
| 209 | { "linkname", o_string, linkname, |
| 210 | "Set logical name for link", |
| 211 | OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXPATHLEN }, |
| 212 | |
| 213 | { "maxfail", o_int, &maxfail, |
| 214 | "Maximum number of unsuccessful connection attempts to allow", |
| 215 | OPT_PRIO }, |
| 216 | |
| 217 | { "ktune", o_bool, &tune_kernel, |
| 218 | "Alter kernel settings as necessary", OPT_PRIO | 1 }, |
| 219 | { "noktune", o_bool, &tune_kernel, |
| 220 | "Don't alter kernel settings", OPT_PRIOSUB }, |
| 221 | |
| 222 | { "connect-delay", o_int, &connect_delay, |
| 223 | "Maximum time (in ms) to wait after connect script finishes", |
| 224 | OPT_PRIO }, |
| 225 | |
| 226 | { "unit", o_int, &req_unit, |
| 227 | "PPP interface unit number to use if possible", |
| 228 | OPT_PRIO | OPT_LLIMIT, 0, 0 }, |
| 229 | |
| 230 | { "dump", o_bool, &dump_options, |
| 231 | "Print out option values after parsing all options", 1 }, |
| 232 | { "dryrun", o_bool, &dryrun, |
| 233 | "Stop after parsing, printing, and checking options", 1 }, |
| 234 | |
| 235 | #ifdef HAVE_MULTILINK |
| 236 | { "multilink", o_bool, &multilink, |
| 237 | "Enable multilink operation", OPT_PRIO | 1 }, |
| 238 | { "mp", o_bool, &multilink, |
| 239 | "Enable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 1 }, |
| 240 | { "nomultilink", o_bool, &multilink, |
| 241 | "Disable multilink operation", OPT_PRIOSUB | 0 }, |
| 242 | { "nomp", o_bool, &multilink, |
| 243 | "Disable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 0 }, |
| 244 | |
| 245 | { "bundle", o_string, &bundle_name, |
| 246 | "Bundle name for multilink", OPT_PRIO }, |
| 247 | #endif /* HAVE_MULTILINK */ |
| 248 | |
| 249 | #ifdef PLUGIN |
| 250 | { "plugin", o_special, (void *)loadplugin, |
| 251 | "Load a plug-in module into pppd", OPT_PRIV | OPT_A2LIST }, |
| 252 | #endif |
| 253 | |
| 254 | #ifdef PPP_FILTER |
| 255 | { "pdebug", o_int, &dflag, |
| 256 | "libpcap debugging", OPT_PRIO }, |
| 257 | |
| 258 | { "pass-filter", 1, setpassfilter, |
| 259 | "set filter for packets to pass", OPT_PRIO }, |
| 260 | |
| 261 | { "active-filter", 1, setactivefilter, |
| 262 | "set filter for active pkts", OPT_PRIO }, |
| 263 | #endif |
| 264 | |
| 265 | { NULL } |
| 266 | }; |
| 267 | |
| 268 | #ifndef IMPLEMENTATION |
| 269 | #define IMPLEMENTATION "" |
| 270 | #endif |
| 271 | |
| 272 | static char *usage_string = "\ |
| 273 | pppd version %s\n\ |
| 274 | Usage: %s [ options ], where options are:\n\ |
| 275 | <device> Communicate over the named device\n\ |
| 276 | <speed> Set the baud rate to <speed>\n\ |
| 277 | <loc>:<rem> Set the local and/or remote interface IP\n\ |
| 278 | addresses. Either one may be omitted.\n\ |
| 279 | asyncmap <n> Set the desired async map to hex <n>\n\ |
| 280 | auth Require authentication from peer\n\ |
| 281 | connect <p> Invoke shell command <p> to set up the serial line\n\ |
| 282 | crtscts Use hardware RTS/CTS flow control\n\ |
| 283 | defaultroute Add default route through interface\n\ |
| 284 | file <f> Take options from file <f>\n\ |
| 285 | modem Use modem control lines\n\ |
| 286 | mru <n> Set MRU value to <n> for negotiation\n\ |
| 287 | See pppd(8) for more options.\n\ |
| 288 | "; |
| 289 | |
| 290 | /* |
| 291 | * parse_args - parse a string of arguments from the command line. |
| 292 | */ |
| 293 | int |
| 294 | parse_args(argc, argv) |
| 295 | int argc; |
| 296 | char **argv; |
| 297 | { |
| 298 | char *arg; |
| 299 | option_t *opt; |
| 300 | int n; |
| 301 | |
| 302 | privileged_option = privileged; |
| 303 | option_source = "command line"; |
| 304 | option_priority = OPRIO_CMDLINE; |
| 305 | while (argc > 0) { |
| 306 | arg = *argv++; |
| 307 | --argc; |
| 308 | opt = find_option(arg); |
| 309 | if (opt == NULL) { |
| 310 | option_error("unrecognized option '%s'", arg); |
| 311 | usage(); |
| 312 | return 0; |
| 313 | } |
| 314 | n = n_arguments(opt); |
| 315 | if (argc < n) { |
| 316 | option_error("too few parameters for option %s", arg); |
| 317 | return 0; |
| 318 | } |
| 319 | if (!process_option(opt, arg, argv)) |
| 320 | return 0; |
| 321 | argc -= n; |
| 322 | argv += n; |
| 323 | } |
| 324 | return 1; |
| 325 | } |
| 326 | |
| 327 | /* |
| 328 | * options_from_file - Read a string of options from a file, |
| 329 | * and interpret them. |
| 330 | */ |
| 331 | int |
| 332 | options_from_file(filename, must_exist, check_prot, priv) |
| 333 | char *filename; |
| 334 | int must_exist; |
| 335 | int check_prot; |
| 336 | int priv; |
| 337 | { |
| 338 | FILE *f; |
| 339 | int i, newline, ret, err; |
| 340 | option_t *opt; |
| 341 | int oldpriv, n; |
| 342 | char *oldsource; |
| 343 | char *argv[MAXARGS]; |
| 344 | char args[MAXARGS][MAXWORDLEN]; |
| 345 | char cmd[MAXWORDLEN]; |
| 346 | |
| 347 | if (check_prot) |
| 348 | seteuid(getuid()); |
| 349 | f = fopen(filename, "r"); |
| 350 | err = errno; |
| 351 | if (check_prot) |
| 352 | seteuid(0); |
| 353 | if (f == NULL) { |
| 354 | errno = err; |
| 355 | if (!must_exist) { |
| 356 | if (err != ENOENT && err != ENOTDIR) |
| 357 | warn("Warning: can't open options file %s: %m", filename); |
| 358 | return 1; |
| 359 | } |
| 360 | option_error("Can't open options file %s: %m", filename); |
| 361 | return 0; |
| 362 | } |
| 363 | |
| 364 | oldpriv = privileged_option; |
| 365 | privileged_option = priv; |
| 366 | oldsource = option_source; |
| 367 | option_source = strdup(filename); |
| 368 | if (option_source == NULL) |
| 369 | option_source = "file"; |
| 370 | ret = 0; |
| 371 | while (getword(f, cmd, &newline, filename)) { |
| 372 | opt = find_option(cmd); |
| 373 | if (opt == NULL) { |
| 374 | option_error("In file %s: unrecognized option '%s'", |
| 375 | filename, cmd); |
| 376 | goto err; |
| 377 | } |
| 378 | n = n_arguments(opt); |
| 379 | for (i = 0; i < n; ++i) { |
| 380 | if (!getword(f, args[i], &newline, filename)) { |
| 381 | option_error( |
| 382 | "In file %s: too few parameters for option '%s'", |
| 383 | filename, cmd); |
| 384 | goto err; |
| 385 | } |
| 386 | argv[i] = args[i]; |
| 387 | } |
| 388 | if (!process_option(opt, cmd, argv)) |
| 389 | goto err; |
| 390 | } |
| 391 | ret = 1; |
| 392 | |
| 393 | err: |
| 394 | fclose(f); |
| 395 | privileged_option = oldpriv; |
| 396 | option_source = oldsource; |
| 397 | return ret; |
| 398 | } |
| 399 | |
| 400 | /* |
| 401 | * options_from_user - See if the use has a ~/.ppprc file, |
| 402 | * and if so, interpret options from it. |
| 403 | */ |
| 404 | int |
| 405 | options_from_user() |
| 406 | { |
| 407 | char *user, *path, *file; |
| 408 | int ret; |
| 409 | struct passwd *pw; |
| 410 | size_t pl; |
| 411 | |
| 412 | pw = getpwuid(getuid()); |
| 413 | if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) |
| 414 | return 1; |
| 415 | file = _PATH_USEROPT; |
| 416 | pl = strlen(user) + strlen(file) + 2; |
| 417 | path = malloc(pl); |
| 418 | if (path == NULL) |
| 419 | novm("init file name"); |
| 420 | slprintf(path, pl, "%s/%s", user, file); |
| 421 | option_priority = OPRIO_CFGFILE; |
| 422 | ret = options_from_file(path, 0, 1, privileged); |
| 423 | free(path); |
| 424 | return ret; |
| 425 | } |
| 426 | |
| 427 | /* |
| 428 | * options_for_tty - See if an options file exists for the serial |
| 429 | * device, and if so, interpret options from it. |
| 430 | * We only allow the per-tty options file to override anything from |
| 431 | * the command line if it is something that the user can't override |
| 432 | * once it has been set by root; this is done by giving configuration |
| 433 | * files a lower priority than the command line. |
| 434 | */ |
| 435 | int |
| 436 | options_for_tty() |
| 437 | { |
| 438 | char *dev, *path, *p; |
| 439 | int ret; |
| 440 | size_t pl; |
| 441 | |
| 442 | dev = devnam; |
| 443 | if (strncmp(dev, "/dev/", 5) == 0) |
| 444 | dev += 5; |
| 445 | if (dev[0] == 0 || strcmp(dev, "tty") == 0) |
| 446 | return 1; /* don't look for /etc/ppp/options.tty */ |
| 447 | pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1; |
| 448 | path = malloc(pl); |
| 449 | if (path == NULL) |
| 450 | novm("tty init file name"); |
| 451 | slprintf(path, pl, "%s%s", _PATH_TTYOPT, dev); |
| 452 | /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */ |
| 453 | for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p) |
| 454 | if (*p == '/') |
| 455 | *p = '.'; |
| 456 | option_priority = OPRIO_CFGFILE; |
| 457 | ret = options_from_file(path, 0, 0, 1); |
| 458 | free(path); |
| 459 | return ret; |
| 460 | } |
| 461 | |
| 462 | /* |
| 463 | * options_from_list - process a string of options in a wordlist. |
| 464 | */ |
| 465 | int |
| 466 | options_from_list(w, priv) |
| 467 | struct wordlist *w; |
| 468 | int priv; |
| 469 | { |
| 470 | char *argv[MAXARGS]; |
| 471 | option_t *opt; |
| 472 | int i, n, ret = 0; |
| 473 | struct wordlist *w0; |
| 474 | |
| 475 | privileged_option = priv; |
| 476 | option_source = "secrets file"; |
| 477 | option_priority = OPRIO_SECFILE; |
| 478 | |
| 479 | while (w != NULL) { |
| 480 | opt = find_option(w->word); |
| 481 | if (opt == NULL) { |
| 482 | option_error("In secrets file: unrecognized option '%s'", |
| 483 | w->word); |
| 484 | goto err; |
| 485 | } |
| 486 | n = n_arguments(opt); |
| 487 | w0 = w; |
| 488 | for (i = 0; i < n; ++i) { |
| 489 | w = w->next; |
| 490 | if (w == NULL) { |
| 491 | option_error( |
| 492 | "In secrets file: too few parameters for option '%s'", |
| 493 | w0->word); |
| 494 | goto err; |
| 495 | } |
| 496 | argv[i] = w->word; |
| 497 | } |
| 498 | if (!process_option(opt, w0->word, argv)) |
| 499 | goto err; |
| 500 | w = w->next; |
| 501 | } |
| 502 | ret = 1; |
| 503 | |
| 504 | err: |
| 505 | return ret; |
| 506 | } |
| 507 | |
| 508 | /* |
| 509 | * match_option - see if this option matches an option_t structure. |
| 510 | */ |
| 511 | static int |
| 512 | match_option(name, opt, dowild) |
| 513 | char *name; |
| 514 | option_t *opt; |
| 515 | int dowild; |
| 516 | { |
| 517 | int (*match) __P((char *, char **, int)); |
| 518 | |
| 519 | if (dowild != (opt->type == o_wild)) |
| 520 | return 0; |
| 521 | if (!dowild) |
| 522 | return strcmp(name, opt->name) == 0; |
| 523 | match = (int (*) __P((char *, char **, int))) opt->addr; |
| 524 | return (*match)(name, NULL, 0); |
| 525 | } |
| 526 | |
| 527 | /* |
| 528 | * find_option - scan the option lists for the various protocols |
| 529 | * looking for an entry with the given name. |
| 530 | * This could be optimized by using a hash table. |
| 531 | */ |
| 532 | static option_t * |
| 533 | find_option(name) |
| 534 | const char *name; |
| 535 | { |
| 536 | option_t *opt; |
| 537 | struct option_list *list; |
| 538 | int i, dowild; |
| 539 | |
| 540 | for (dowild = 0; dowild <= 1; ++dowild) { |
| 541 | for (opt = general_options; opt->name != NULL; ++opt) |
| 542 | if (match_option(name, opt, dowild)) |
| 543 | return opt; |
| 544 | for (opt = auth_options; opt->name != NULL; ++opt) |
| 545 | if (match_option(name, opt, dowild)) |
| 546 | return opt; |
| 547 | for (list = extra_options; list != NULL; list = list->next) |
| 548 | for (opt = list->options; opt->name != NULL; ++opt) |
| 549 | if (match_option(name, opt, dowild)) |
| 550 | return opt; |
| 551 | for (opt = the_channel->options; opt->name != NULL; ++opt) |
| 552 | if (match_option(name, opt, dowild)) |
| 553 | return opt; |
| 554 | for (i = 0; protocols[i] != NULL; ++i) |
| 555 | if ((opt = protocols[i]->options) != NULL) |
| 556 | for (; opt->name != NULL; ++opt) |
| 557 | if (match_option(name, opt, dowild)) |
| 558 | return opt; |
| 559 | } |
| 560 | return NULL; |
| 561 | } |
| 562 | |
| 563 | /* |
| 564 | * process_option - process one new-style option. |
| 565 | */ |
| 566 | static int |
| 567 | process_option(opt, cmd, argv) |
| 568 | option_t *opt; |
| 569 | char *cmd; |
| 570 | char **argv; |
| 571 | { |
| 572 | u_int32_t v; |
| 573 | int iv, a; |
| 574 | char *sv; |
| 575 | int (*parser) __P((char **)); |
| 576 | int (*wildp) __P((char *, char **, int)); |
| 577 | char *optopt = (opt->type == o_wild)? "": " option"; |
| 578 | int prio = option_priority; |
| 579 | option_t *mainopt = opt; |
| 580 | |
| 581 | if ((opt->flags & OPT_PRIVFIX) && privileged_option) |
| 582 | prio += OPRIO_ROOT; |
| 583 | while (mainopt->flags & OPT_PRIOSUB) |
| 584 | --mainopt; |
| 585 | if (mainopt->flags & OPT_PRIO) { |
| 586 | if (prio < mainopt->priority) { |
| 587 | /* new value doesn't override old */ |
| 588 | if (prio == OPRIO_CMDLINE && mainopt->priority > OPRIO_ROOT) { |
| 589 | option_error("%s%s set in %s cannot be overridden\n", |
| 590 | opt->name, optopt, mainopt->source); |
| 591 | return 0; |
| 592 | } |
| 593 | return 1; |
| 594 | } |
| 595 | if (prio > OPRIO_ROOT && mainopt->priority == OPRIO_CMDLINE) |
| 596 | warn("%s%s from %s overrides command line", |
| 597 | opt->name, optopt, option_source); |
| 598 | } |
| 599 | |
| 600 | if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) { |
| 601 | option_error("%s%s cannot be changed after initialization", |
| 602 | opt->name, optopt); |
| 603 | return 0; |
| 604 | } |
| 605 | if ((opt->flags & OPT_PRIV) && !privileged_option) { |
| 606 | option_error("using the %s%s requires root privilege", |
| 607 | opt->name, optopt); |
| 608 | return 0; |
| 609 | } |
| 610 | if ((opt->flags & OPT_ENABLE) && *(bool *)(opt->addr2) == 0) { |
| 611 | option_error("%s%s is disabled", opt->name, optopt); |
| 612 | return 0; |
| 613 | } |
| 614 | if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) { |
| 615 | option_error("the %s%s may not be changed in %s", |
| 616 | opt->name, optopt, option_source); |
| 617 | return 0; |
| 618 | } |
| 619 | |
| 620 | switch (opt->type) { |
| 621 | case o_bool: |
| 622 | v = opt->flags & OPT_VALUE; |
| 623 | *(bool *)(opt->addr) = v; |
| 624 | if (opt->addr2 && (opt->flags & OPT_A2COPY)) |
| 625 | *(bool *)(opt->addr2) = v; |
| 626 | break; |
| 627 | |
| 628 | case o_int: |
| 629 | iv = 0; |
| 630 | if ((opt->flags & OPT_NOARG) == 0) { |
| 631 | if (!int_option(*argv, &iv)) |
| 632 | return 0; |
| 633 | if ((((opt->flags & OPT_LLIMIT) && iv < opt->lower_limit) |
| 634 | || ((opt->flags & OPT_ULIMIT) && iv > opt->upper_limit)) |
| 635 | && !((opt->flags & OPT_ZEROOK && iv == 0))) { |
| 636 | char *zok = (opt->flags & OPT_ZEROOK)? " zero or": ""; |
| 637 | switch (opt->flags & OPT_LIMITS) { |
| 638 | case OPT_LLIMIT: |
| 639 | option_error("%s value must be%s >= %d", |
| 640 | opt->name, zok, opt->lower_limit); |
| 641 | break; |
| 642 | case OPT_ULIMIT: |
| 643 | option_error("%s value must be%s <= %d", |
| 644 | opt->name, zok, opt->upper_limit); |
| 645 | break; |
| 646 | case OPT_LIMITS: |
| 647 | option_error("%s value must be%s between %d and %d", |
| 648 | opt->name, opt->lower_limit, opt->upper_limit); |
| 649 | break; |
| 650 | } |
| 651 | return 0; |
| 652 | } |
| 653 | } |
| 654 | a = opt->flags & OPT_VALUE; |
| 655 | if (a >= 128) |
| 656 | a -= 256; /* sign extend */ |
| 657 | iv += a; |
| 658 | if (opt->flags & OPT_INC) |
| 659 | iv += *(int *)(opt->addr); |
| 660 | if ((opt->flags & OPT_NOINCR) && !privileged_option) { |
| 661 | int oldv = *(int *)(opt->addr); |
| 662 | if ((opt->flags & OPT_ZEROINF) ? |
| 663 | (oldv != 0 && (iv == 0 || iv > oldv)) : (iv > oldv)) { |
| 664 | option_error("%s value cannot be increased", opt->name); |
| 665 | return 0; |
| 666 | } |
| 667 | } |
| 668 | *(int *)(opt->addr) = iv; |
| 669 | if (opt->addr2 && (opt->flags & OPT_A2COPY)) |
| 670 | *(int *)(opt->addr2) = iv; |
| 671 | break; |
| 672 | |
| 673 | case o_uint32: |
| 674 | if (opt->flags & OPT_NOARG) { |
| 675 | v = opt->flags & OPT_VALUE; |
| 676 | if (v & 0x80) |
| 677 | v |= 0xffffff00U; |
| 678 | } else if (!number_option(*argv, &v, 16)) |
| 679 | return 0; |
| 680 | if (opt->flags & OPT_OR) |
| 681 | v |= *(u_int32_t *)(opt->addr); |
| 682 | *(u_int32_t *)(opt->addr) = v; |
| 683 | if (opt->addr2 && (opt->flags & OPT_A2COPY)) |
| 684 | *(u_int32_t *)(opt->addr2) = v; |
| 685 | break; |
| 686 | |
| 687 | case o_string: |
| 688 | if (opt->flags & OPT_STATIC) { |
| 689 | strlcpy((char *)(opt->addr), *argv, opt->upper_limit); |
| 690 | } else { |
| 691 | sv = strdup(*argv); |
| 692 | if (sv == NULL) |
| 693 | novm("option argument"); |
| 694 | *(char **)(opt->addr) = sv; |
| 695 | } |
| 696 | break; |
| 697 | |
| 698 | case o_special_noarg: |
| 699 | case o_special: |
| 700 | parser = (int (*) __P((char **))) opt->addr; |
| 701 | if (!(*parser)(argv)) |
| 702 | return 0; |
| 703 | if (opt->flags & OPT_A2LIST) { |
| 704 | struct option_value *ovp, **pp; |
| 705 | |
| 706 | ovp = malloc(sizeof(*ovp) + strlen(*argv)); |
| 707 | if (ovp != 0) { |
| 708 | strcpy(ovp->value, *argv); |
| 709 | ovp->source = option_source; |
| 710 | ovp->next = NULL; |
| 711 | pp = (struct option_value **) &opt->addr2; |
| 712 | while (*pp != 0) |
| 713 | pp = &(*pp)->next; |
| 714 | *pp = ovp; |
| 715 | } |
| 716 | } |
| 717 | break; |
| 718 | |
| 719 | case o_wild: |
| 720 | wildp = (int (*) __P((char *, char **, int))) opt->addr; |
| 721 | if (!(*wildp)(cmd, argv, 1)) |
| 722 | return 0; |
| 723 | break; |
| 724 | } |
| 725 | |
| 726 | if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE |
| 727 | |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST)) == 0) |
| 728 | *(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR); |
| 729 | |
| 730 | mainopt->source = option_source; |
| 731 | mainopt->priority = prio; |
| 732 | mainopt->winner = opt - mainopt; |
| 733 | |
| 734 | return 1; |
| 735 | } |
| 736 | |
| 737 | /* |
| 738 | * override_value - if the option priorities would permit us to |
| 739 | * override the value of option, return 1 and update the priority |
| 740 | * and source of the option value. Otherwise returns 0. |
| 741 | */ |
| 742 | int |
| 743 | override_value(option, priority, source) |
| 744 | const char *option; |
| 745 | int priority; |
| 746 | const char *source; |
| 747 | { |
| 748 | option_t *opt; |
| 749 | |
| 750 | opt = find_option(option); |
| 751 | if (opt == NULL) |
| 752 | return 0; |
| 753 | while (opt->flags & OPT_PRIOSUB) |
| 754 | --opt; |
| 755 | if ((opt->flags & OPT_PRIO) && priority < opt->priority) |
| 756 | return 0; |
| 757 | opt->priority = priority; |
| 758 | opt->source = source; |
| 759 | opt->winner = -1; |
| 760 | return 1; |
| 761 | } |
| 762 | |
| 763 | /* |
| 764 | * n_arguments - tell how many arguments an option takes |
| 765 | */ |
| 766 | static int |
| 767 | n_arguments(opt) |
| 768 | option_t *opt; |
| 769 | { |
| 770 | return (opt->type == o_bool || opt->type == o_special_noarg |
| 771 | || (opt->flags & OPT_NOARG))? 0: 1; |
| 772 | } |
| 773 | |
| 774 | /* |
| 775 | * add_options - add a list of options to the set we grok. |
| 776 | */ |
| 777 | void |
| 778 | add_options(opt) |
| 779 | option_t *opt; |
| 780 | { |
| 781 | struct option_list *list; |
| 782 | |
| 783 | list = malloc(sizeof(*list)); |
| 784 | if (list == 0) |
| 785 | novm("option list entry"); |
| 786 | list->options = opt; |
| 787 | list->next = extra_options; |
| 788 | extra_options = list; |
| 789 | } |
| 790 | |
| 791 | /* |
| 792 | * remove_option - permanently remove an option from consideration... |
| 793 | * for use by modules to remove choices which no longer make sense. |
| 794 | * returns true if found an option |
| 795 | */ |
| 796 | int |
| 797 | remove_option(name) |
| 798 | char *name; |
| 799 | { |
| 800 | option_t *o; |
| 801 | o = find_option(name); |
| 802 | if (o == NULL) |
| 803 | return 0; |
| 804 | o->name = ""; |
| 805 | return 1; |
| 806 | } |
| 807 | |
| 808 | /* |
| 809 | * check_options - check that options are valid and consistent. |
| 810 | */ |
| 811 | void |
| 812 | check_options() |
| 813 | { |
| 814 | if (logfile_fd >= 0 && logfile_fd != log_to_fd) |
| 815 | close(logfile_fd); |
| 816 | } |
| 817 | |
| 818 | /* |
| 819 | * print_option - print out an option and its value |
| 820 | */ |
| 821 | static void |
| 822 | print_option(opt, mainopt, printer, arg) |
| 823 | option_t *opt, *mainopt; |
| 824 | void (*printer) __P((void *, char *, ...)); |
| 825 | void *arg; |
| 826 | { |
| 827 | int i, v; |
| 828 | char *p; |
| 829 | |
| 830 | if (opt->flags & OPT_NOPRINT) |
| 831 | return; |
| 832 | switch (opt->type) { |
| 833 | case o_bool: |
| 834 | v = opt->flags & OPT_VALUE; |
| 835 | if (*(bool *)opt->addr != v) |
| 836 | /* this can happen legitimately, e.g. lock |
| 837 | option turned off for default device */ |
| 838 | break; |
| 839 | printer(arg, "%s", opt->name); |
| 840 | break; |
| 841 | case o_int: |
| 842 | v = opt->flags & OPT_VALUE; |
| 843 | if (v >= 128) |
| 844 | v -= 256; |
| 845 | i = *(int *)opt->addr; |
| 846 | if (opt->flags & OPT_NOARG) { |
| 847 | printer(arg, "%s", opt->name); |
| 848 | if (i != v) { |
| 849 | if (opt->flags & OPT_INC) { |
| 850 | for (; i > v; i -= v) |
| 851 | printer(arg, " %s", opt->name); |
| 852 | } else |
| 853 | printer(arg, " # oops: %d not %d\n", |
| 854 | i, v); |
| 855 | } |
| 856 | } else { |
| 857 | printer(arg, "%s %d", opt->name, i); |
| 858 | } |
| 859 | break; |
| 860 | case o_uint32: |
| 861 | printer(arg, "%s", opt->name); |
| 862 | if ((opt->flags & OPT_NOARG) == 0) |
| 863 | printer(arg, " %x", *(u_int32_t *)opt->addr); |
| 864 | break; |
| 865 | |
| 866 | case o_string: |
| 867 | if (opt->flags & OPT_HIDE) { |
| 868 | p = "??????"; |
| 869 | } else { |
| 870 | p = (char *) opt->addr; |
| 871 | if ((opt->flags & OPT_STATIC) == 0) |
| 872 | p = *(char **)p; |
| 873 | } |
| 874 | printer(arg, "%s %q", opt->name, p); |
| 875 | break; |
| 876 | |
| 877 | case o_special: |
| 878 | case o_special_noarg: |
| 879 | case o_wild: |
| 880 | if (opt->type != o_wild) { |
| 881 | printer(arg, "%s", opt->name); |
| 882 | if (n_arguments(opt) == 0) |
| 883 | break; |
| 884 | printer(arg, " "); |
| 885 | } |
| 886 | if (opt->flags & OPT_A2PRINTER) { |
| 887 | void (*oprt) __P((option_t *, |
| 888 | void ((*)__P((void *, char *, ...))), |
| 889 | void *)); |
| 890 | oprt = opt->addr2; |
| 891 | (*oprt)(opt, printer, arg); |
| 892 | } else if (opt->flags & OPT_A2STRVAL) { |
| 893 | p = (char *) opt->addr2; |
| 894 | if ((opt->flags & OPT_STATIC) == 0) |
| 895 | p = *(char **)p; |
| 896 | printer("%q", p); |
| 897 | } else if (opt->flags & OPT_A2LIST) { |
| 898 | struct option_value *ovp; |
| 899 | |
| 900 | ovp = (struct option_value *) opt->addr2; |
| 901 | for (;;) { |
| 902 | printer(arg, "%q", ovp->value); |
| 903 | if ((ovp = ovp->next) == NULL) |
| 904 | break; |
| 905 | printer(arg, "\t\t# (from %s)\n%s ", |
| 906 | ovp->source, opt->name); |
| 907 | } |
| 908 | } else { |
| 909 | printer(arg, "xxx # [don't know how to print value]"); |
| 910 | } |
| 911 | break; |
| 912 | |
| 913 | default: |
| 914 | printer(arg, "# %s value (type %d??)", opt->name, opt->type); |
| 915 | break; |
| 916 | } |
| 917 | printer(arg, "\t\t# (from %s)\n", mainopt->source); |
| 918 | } |
| 919 | |
| 920 | /* |
| 921 | * print_option_list - print out options in effect from an |
| 922 | * array of options. |
| 923 | */ |
| 924 | static void |
| 925 | print_option_list(opt, printer, arg) |
| 926 | option_t *opt; |
| 927 | void (*printer) __P((void *, char *, ...)); |
| 928 | void *arg; |
| 929 | { |
| 930 | while (opt->name != NULL) { |
| 931 | if (opt->priority != OPRIO_DEFAULT |
| 932 | && opt->winner != (short int) -1) |
| 933 | print_option(opt + opt->winner, opt, printer, arg); |
| 934 | do { |
| 935 | ++opt; |
| 936 | } while (opt->flags & OPT_PRIOSUB); |
| 937 | } |
| 938 | } |
| 939 | |
| 940 | /* |
| 941 | * print_options - print out what options are in effect. |
| 942 | */ |
| 943 | void |
| 944 | print_options(printer, arg) |
| 945 | void (*printer) __P((void *, char *, ...)); |
| 946 | void *arg; |
| 947 | { |
| 948 | struct option_list *list; |
| 949 | int i; |
| 950 | |
| 951 | printer(arg, "pppd options in effect:\n"); |
| 952 | print_option_list(general_options, printer, arg); |
| 953 | print_option_list(auth_options, printer, arg); |
| 954 | for (list = extra_options; list != NULL; list = list->next) |
| 955 | print_option_list(list->options, printer, arg); |
| 956 | print_option_list(the_channel->options, printer, arg); |
| 957 | for (i = 0; protocols[i] != NULL; ++i) |
| 958 | print_option_list(protocols[i]->options, printer, arg); |
| 959 | } |
| 960 | |
| 961 | /* |
| 962 | * usage - print out a message telling how to use the program. |
| 963 | */ |
| 964 | static void |
| 965 | usage() |
| 966 | { |
| 967 | if (phase == PHASE_INITIALIZE) |
| 968 | fprintf(stderr, usage_string, VERSION, progname); |
| 969 | } |
| 970 | |
| 971 | /* |
| 972 | * showhelp - print out usage message and exit. |
| 973 | */ |
| 974 | static int |
| 975 | showhelp(argv) |
| 976 | char **argv; |
| 977 | { |
| 978 | if (phase == PHASE_INITIALIZE) { |
| 979 | usage(); |
| 980 | exit(0); |
| 981 | } |
| 982 | return 0; |
| 983 | } |
| 984 | |
| 985 | /* |
| 986 | * showversion - print out the version number and exit. |
| 987 | */ |
| 988 | static int |
| 989 | showversion(argv) |
| 990 | char **argv; |
| 991 | { |
| 992 | if (phase == PHASE_INITIALIZE) { |
| 993 | fprintf(stderr, "pppd version %s\n", VERSION); |
| 994 | exit(0); |
| 995 | } |
| 996 | return 0; |
| 997 | } |
| 998 | |
| 999 | /* |
| 1000 | * option_error - print a message about an error in an option. |
| 1001 | * The message is logged, and also sent to |
| 1002 | * stderr if phase == PHASE_INITIALIZE. |
| 1003 | */ |
| 1004 | void |
| 1005 | option_error __V((char *fmt, ...)) |
| 1006 | { |
| 1007 | va_list args; |
| 1008 | char buf[1024]; |
| 1009 | |
| 1010 | #if defined(__STDC__) |
| 1011 | va_start(args, fmt); |
| 1012 | #else |
| 1013 | char *fmt; |
| 1014 | va_start(args); |
| 1015 | fmt = va_arg(args, char *); |
| 1016 | #endif |
| 1017 | vslprintf(buf, sizeof(buf), fmt, args); |
| 1018 | va_end(args); |
| 1019 | if (phase == PHASE_INITIALIZE) |
| 1020 | fprintf(stderr, "%s: %s\n", progname, buf); |
| 1021 | syslog(LOG_ERR, "%s", buf); |
| 1022 | } |
| 1023 | |
| 1024 | |
| 1025 | /* |
| 1026 | * Read a word from a file. |
| 1027 | * Words are delimited by white-space or by quotes (" or '). |
| 1028 | * Quotes, white-space and \ may be escaped with \. |
| 1029 | * \<newline> is ignored. |
| 1030 | */ |
| 1031 | int |
| 1032 | getword(f, word, newlinep, filename) |
| 1033 | FILE *f; |
| 1034 | char *word; |
| 1035 | int *newlinep; |
| 1036 | char *filename; |
| 1037 | { |
| 1038 | int c, len, escape; |
| 1039 | int quoted, comment; |
| 1040 | int value, digit, got, n; |
| 1041 | |
| 1042 | #define isoctal(c) ((c) >= '0' && (c) < '8') |
| 1043 | |
| 1044 | *newlinep = 0; |
| 1045 | len = 0; |
| 1046 | escape = 0; |
| 1047 | comment = 0; |
| 1048 | |
| 1049 | /* |
| 1050 | * First skip white-space and comments. |
| 1051 | */ |
| 1052 | for (;;) { |
| 1053 | c = getc(f); |
| 1054 | if (c == EOF) |
| 1055 | break; |
| 1056 | |
| 1057 | /* |
| 1058 | * A newline means the end of a comment; backslash-newline |
| 1059 | * is ignored. Note that we cannot have escape && comment. |
| 1060 | */ |
| 1061 | if (c == '\n') { |
| 1062 | if (!escape) { |
| 1063 | *newlinep = 1; |
| 1064 | comment = 0; |
| 1065 | } else |
| 1066 | escape = 0; |
| 1067 | continue; |
| 1068 | } |
| 1069 | |
| 1070 | /* |
| 1071 | * Ignore characters other than newline in a comment. |
| 1072 | */ |
| 1073 | if (comment) |
| 1074 | continue; |
| 1075 | |
| 1076 | /* |
| 1077 | * If this character is escaped, we have a word start. |
| 1078 | */ |
| 1079 | if (escape) |
| 1080 | break; |
| 1081 | |
| 1082 | /* |
| 1083 | * If this is the escape character, look at the next character. |
| 1084 | */ |
| 1085 | if (c == '\\') { |
| 1086 | escape = 1; |
| 1087 | continue; |
| 1088 | } |
| 1089 | |
| 1090 | /* |
| 1091 | * If this is the start of a comment, ignore the rest of the line. |
| 1092 | */ |
| 1093 | if (c == '#') { |
| 1094 | comment = 1; |
| 1095 | continue; |
| 1096 | } |
| 1097 | |
| 1098 | /* |
| 1099 | * A non-whitespace character is the start of a word. |
| 1100 | */ |
| 1101 | if (!isspace(c)) |
| 1102 | break; |
| 1103 | } |
| 1104 | |
| 1105 | /* |
| 1106 | * Save the delimiter for quoted strings. |
| 1107 | */ |
| 1108 | if (!escape && (c == '"' || c == '\'')) { |
| 1109 | quoted = c; |
| 1110 | c = getc(f); |
| 1111 | } else |
| 1112 | quoted = 0; |
| 1113 | |
| 1114 | /* |
| 1115 | * Process characters until the end of the word. |
| 1116 | */ |
| 1117 | while (c != EOF) { |
| 1118 | if (escape) { |
| 1119 | /* |
| 1120 | * This character is escaped: backslash-newline is ignored, |
| 1121 | * various other characters indicate particular values |
| 1122 | * as for C backslash-escapes. |
| 1123 | */ |
| 1124 | escape = 0; |
| 1125 | if (c == '\n') { |
| 1126 | c = getc(f); |
| 1127 | continue; |
| 1128 | } |
| 1129 | |
| 1130 | got = 0; |
| 1131 | switch (c) { |
| 1132 | case 'a': |
| 1133 | value = '\a'; |
| 1134 | break; |
| 1135 | case 'b': |
| 1136 | value = '\b'; |
| 1137 | break; |
| 1138 | case 'f': |
| 1139 | value = '\f'; |
| 1140 | break; |
| 1141 | case 'n': |
| 1142 | value = '\n'; |
| 1143 | break; |
| 1144 | case 'r': |
| 1145 | value = '\r'; |
| 1146 | break; |
| 1147 | case 's': |
| 1148 | value = ' '; |
| 1149 | break; |
| 1150 | case 't': |
| 1151 | value = '\t'; |
| 1152 | break; |
| 1153 | |
| 1154 | default: |
| 1155 | if (isoctal(c)) { |
| 1156 | /* |
| 1157 | * \ddd octal sequence |
| 1158 | */ |
| 1159 | value = 0; |
| 1160 | for (n = 0; n < 3 && isoctal(c); ++n) { |
| 1161 | value = (value << 3) + (c & 07); |
| 1162 | c = getc(f); |
| 1163 | } |
| 1164 | got = 1; |
| 1165 | break; |
| 1166 | } |
| 1167 | |
| 1168 | if (c == 'x') { |
| 1169 | /* |
| 1170 | * \x<hex_string> sequence |
| 1171 | */ |
| 1172 | value = 0; |
| 1173 | c = getc(f); |
| 1174 | for (n = 0; n < 2 && isxdigit(c); ++n) { |
| 1175 | digit = toupper(c) - '0'; |
| 1176 | if (digit > 10) |
| 1177 | digit += '0' + 10 - 'A'; |
| 1178 | value = (value << 4) + digit; |
| 1179 | c = getc (f); |
| 1180 | } |
| 1181 | got = 1; |
| 1182 | break; |
| 1183 | } |
| 1184 | |
| 1185 | /* |
| 1186 | * Otherwise the character stands for itself. |
| 1187 | */ |
| 1188 | value = c; |
| 1189 | break; |
| 1190 | } |
| 1191 | |
| 1192 | /* |
| 1193 | * Store the resulting character for the escape sequence. |
| 1194 | */ |
| 1195 | if (len < MAXWORDLEN-1) |
| 1196 | word[len] = value; |
| 1197 | ++len; |
| 1198 | |
| 1199 | if (!got) |
| 1200 | c = getc(f); |
| 1201 | continue; |
| 1202 | |
| 1203 | } |
| 1204 | |
| 1205 | /* |
| 1206 | * Not escaped: see if we've reached the end of the word. |
| 1207 | */ |
| 1208 | if (quoted) { |
| 1209 | if (c == quoted) |
| 1210 | break; |
| 1211 | } else { |
| 1212 | if (isspace(c) || c == '#') { |
| 1213 | ungetc (c, f); |
| 1214 | break; |
| 1215 | } |
| 1216 | } |
| 1217 | |
| 1218 | /* |
| 1219 | * Backslash starts an escape sequence. |
| 1220 | */ |
| 1221 | if (c == '\\') { |
| 1222 | escape = 1; |
| 1223 | c = getc(f); |
| 1224 | continue; |
| 1225 | } |
| 1226 | |
| 1227 | /* |
| 1228 | * An ordinary character: store it in the word and get another. |
| 1229 | */ |
| 1230 | if (len < MAXWORDLEN-1) |
| 1231 | word[len] = c; |
| 1232 | ++len; |
| 1233 | |
| 1234 | c = getc(f); |
| 1235 | } |
| 1236 | |
| 1237 | /* |
| 1238 | * End of the word: check for errors. |
| 1239 | */ |
| 1240 | if (c == EOF) { |
| 1241 | if (ferror(f)) { |
| 1242 | if (errno == 0) |
| 1243 | errno = EIO; |
| 1244 | option_error("Error reading %s: %m", filename); |
| 1245 | die(1); |
| 1246 | } |
| 1247 | /* |
| 1248 | * If len is zero, then we didn't find a word before the |
| 1249 | * end of the file. |
| 1250 | */ |
| 1251 | if (len == 0) |
| 1252 | return 0; |
| 1253 | } |
| 1254 | |
| 1255 | /* |
| 1256 | * Warn if the word was too long, and append a terminating null. |
| 1257 | */ |
| 1258 | if (len >= MAXWORDLEN) { |
| 1259 | option_error("warning: word in file %s too long (%.20s...)", |
| 1260 | filename, word); |
| 1261 | len = MAXWORDLEN - 1; |
| 1262 | } |
| 1263 | word[len] = 0; |
| 1264 | |
| 1265 | return 1; |
| 1266 | |
| 1267 | #undef isoctal |
| 1268 | |
| 1269 | } |
| 1270 | |
| 1271 | /* |
| 1272 | * number_option - parse an unsigned numeric parameter for an option. |
| 1273 | */ |
| 1274 | static int |
| 1275 | number_option(str, valp, base) |
| 1276 | char *str; |
| 1277 | u_int32_t *valp; |
| 1278 | int base; |
| 1279 | { |
| 1280 | char *ptr; |
| 1281 | |
| 1282 | *valp = strtoul(str, &ptr, base); |
| 1283 | if (ptr == str) { |
| 1284 | option_error("invalid numeric parameter '%s' for %s option", |
| 1285 | str, current_option); |
| 1286 | return 0; |
| 1287 | } |
| 1288 | return 1; |
| 1289 | } |
| 1290 | |
| 1291 | |
| 1292 | /* |
| 1293 | * int_option - like number_option, but valp is int *, |
| 1294 | * the base is assumed to be 0, and *valp is not changed |
| 1295 | * if there is an error. |
| 1296 | */ |
| 1297 | int |
| 1298 | int_option(str, valp) |
| 1299 | char *str; |
| 1300 | int *valp; |
| 1301 | { |
| 1302 | u_int32_t v; |
| 1303 | |
| 1304 | if (!number_option(str, &v, 0)) |
| 1305 | return 0; |
| 1306 | *valp = (int) v; |
| 1307 | return 1; |
| 1308 | } |
| 1309 | |
| 1310 | |
| 1311 | /* |
| 1312 | * The following procedures parse options. |
| 1313 | */ |
| 1314 | |
| 1315 | /* |
| 1316 | * readfile - take commands from a file. |
| 1317 | */ |
| 1318 | static int |
| 1319 | readfile(argv) |
| 1320 | char **argv; |
| 1321 | { |
| 1322 | return options_from_file(*argv, 1, 1, privileged_option); |
| 1323 | } |
| 1324 | |
| 1325 | /* |
| 1326 | * callfile - take commands from /etc/ppp/peers/<name>. |
| 1327 | * Name may not contain /../, start with / or ../, or end in /.. |
| 1328 | */ |
| 1329 | static int |
| 1330 | callfile(argv) |
| 1331 | char **argv; |
| 1332 | { |
| 1333 | char *fname, *arg, *p; |
| 1334 | int l, ok; |
| 1335 | |
| 1336 | arg = *argv; |
| 1337 | ok = 1; |
| 1338 | if (arg[0] == '/' || arg[0] == 0) |
| 1339 | ok = 0; |
| 1340 | else { |
| 1341 | for (p = arg; *p != 0; ) { |
| 1342 | if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) { |
| 1343 | ok = 0; |
| 1344 | break; |
| 1345 | } |
| 1346 | while (*p != '/' && *p != 0) |
| 1347 | ++p; |
| 1348 | if (*p == '/') |
| 1349 | ++p; |
| 1350 | } |
| 1351 | } |
| 1352 | if (!ok) { |
| 1353 | option_error("call option value may not contain .. or start with /"); |
| 1354 | return 0; |
| 1355 | } |
| 1356 | |
| 1357 | l = strlen(arg) + strlen(_PATH_PEERFILES) + 1; |
| 1358 | if ((fname = (char *) malloc(l)) == NULL) |
| 1359 | novm("call file name"); |
| 1360 | slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg); |
| 1361 | |
| 1362 | ok = options_from_file(fname, 1, 1, 1); |
| 1363 | |
| 1364 | free(fname); |
| 1365 | return ok; |
| 1366 | } |
| 1367 | |
| 1368 | #ifdef PPP_FILTER |
| 1369 | /* |
| 1370 | * setpassfilter - Set the pass filter for packets |
| 1371 | */ |
| 1372 | static int |
| 1373 | setpassfilter(argv) |
| 1374 | char **argv; |
| 1375 | { |
| 1376 | pc.linktype = DLT_PPP; |
| 1377 | pc.snapshot = PPP_HDRLEN; |
| 1378 | |
| 1379 | if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0) |
| 1380 | return 1; |
| 1381 | option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc)); |
| 1382 | return 0; |
| 1383 | } |
| 1384 | |
| 1385 | /* |
| 1386 | * setactivefilter - Set the active filter for packets |
| 1387 | */ |
| 1388 | static int |
| 1389 | setactivefilter(argv) |
| 1390 | char **argv; |
| 1391 | { |
| 1392 | pc.linktype = DLT_PPP; |
| 1393 | pc.snapshot = PPP_HDRLEN; |
| 1394 | |
| 1395 | if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0) |
| 1396 | return 1; |
| 1397 | option_error("error in active-filter expression: %s\n", pcap_geterr(&pc)); |
| 1398 | return 0; |
| 1399 | } |
| 1400 | #endif |
| 1401 | |
| 1402 | /* |
| 1403 | * setdomain - Set domain name to append to hostname |
| 1404 | */ |
| 1405 | static int |
| 1406 | setdomain(argv) |
| 1407 | char **argv; |
| 1408 | { |
| 1409 | gethostname(hostname, MAXNAMELEN); |
| 1410 | if (**argv != 0) { |
| 1411 | if (**argv != '.') |
| 1412 | strncat(hostname, ".", MAXNAMELEN - strlen(hostname)); |
| 1413 | domain = hostname + strlen(hostname); |
| 1414 | strncat(hostname, *argv, MAXNAMELEN - strlen(hostname)); |
| 1415 | } |
| 1416 | hostname[MAXNAMELEN-1] = 0; |
| 1417 | return (1); |
| 1418 | } |
| 1419 | |
| 1420 | |
| 1421 | static int |
| 1422 | setlogfile(argv) |
| 1423 | char **argv; |
| 1424 | { |
| 1425 | int fd, err; |
| 1426 | |
| 1427 | if (!privileged_option) |
| 1428 | seteuid(getuid()); |
| 1429 | fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644); |
| 1430 | if (fd < 0 && errno == EEXIST) |
| 1431 | fd = open(*argv, O_WRONLY | O_APPEND); |
| 1432 | err = errno; |
| 1433 | if (!privileged_option) |
| 1434 | seteuid(0); |
| 1435 | if (fd < 0) { |
| 1436 | errno = err; |
| 1437 | option_error("Can't open log file %s: %m", *argv); |
| 1438 | return 0; |
| 1439 | } |
| 1440 | strlcpy(logfile_name, *argv, sizeof(logfile_name)); |
| 1441 | if (logfile_fd >= 0) |
| 1442 | close(logfile_fd); |
| 1443 | logfile_fd = fd; |
| 1444 | log_to_fd = fd; |
| 1445 | log_default = 0; |
| 1446 | return 1; |
| 1447 | } |
| 1448 | |
| 1449 | #ifdef PLUGIN |
| 1450 | static int |
| 1451 | loadplugin(argv) |
| 1452 | char **argv; |
| 1453 | { |
| 1454 | char *arg = *argv; |
| 1455 | void *handle; |
| 1456 | const char *err; |
| 1457 | void (*init) __P((void)); |
| 1458 | char *path = arg; |
| 1459 | const char *vers; |
| 1460 | |
| 1461 | if (strchr(arg, '/') == 0) { |
| 1462 | const char *base = _PATH_PLUGIN; |
| 1463 | int l = strlen(base) + strlen(arg) + 2; |
| 1464 | path = malloc(l); |
| 1465 | if (path == 0) |
| 1466 | novm("plugin file path"); |
| 1467 | strlcpy(path, base, l); |
| 1468 | strlcat(path, "/", l); |
| 1469 | strlcat(path, arg, l); |
| 1470 | } |
| 1471 | handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); |
| 1472 | if (handle == 0) { |
| 1473 | err = dlerror(); |
| 1474 | if (err != 0) |
| 1475 | option_error("%s", err); |
| 1476 | option_error("Couldn't load plugin %s", arg); |
| 1477 | goto err; |
| 1478 | } |
| 1479 | init = (void (*)(void))dlsym(handle, "plugin_init"); |
| 1480 | if (init == 0) { |
| 1481 | option_error("%s has no initialization entry point", arg); |
| 1482 | goto errclose; |
| 1483 | } |
| 1484 | vers = (const char *) dlsym(handle, "pppd_version"); |
| 1485 | if (vers == 0) { |
| 1486 | warn("Warning: plugin %s has no version information", arg); |
| 1487 | } else if (strcmp(vers, VERSION) != 0) { |
| 1488 | option_error("Plugin %s is for pppd version %s, this is %s", |
| 1489 | vers, VERSION); |
| 1490 | goto errclose; |
| 1491 | } |
| 1492 | info("Plugin %s loaded.", arg); |
| 1493 | (*init)(); |
| 1494 | return 1; |
| 1495 | |
| 1496 | errclose: |
| 1497 | dlclose(handle); |
| 1498 | err: |
| 1499 | if (path != arg) |
| 1500 | free(path); |
| 1501 | return 0; |
| 1502 | } |
| 1503 | #endif /* PLUGIN */ |