mirror of
https://github.com/paulusmack/ppp.git
synced 2024-11-24 02:43:27 +08:00
a4efaa63e6
only disable holdoff if link terminated by idle timer open all network protos on callback add init and logfile options document various options
1484 lines
34 KiB
C
1484 lines
34 KiB
C
/*
|
|
* options.c - handles option processing for PPP.
|
|
*
|
|
* Copyright (c) 1989 Carnegie Mellon University.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that the above copyright notice and this paragraph are
|
|
* duplicated in all such forms and that any documentation,
|
|
* advertising materials, and other materials related to such
|
|
* distribution and use acknowledge that the software was developed
|
|
* by Carnegie Mellon University. The name of the
|
|
* University may not be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] = "$Id: options.c,v 1.60 1999/07/21 00:24:31 paulus Exp $";
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <termios.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <netdb.h>
|
|
#include <pwd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#ifdef PPP_FILTER
|
|
#include <pcap.h>
|
|
#include <pcap-int.h> /* XXX: To get struct pcap */
|
|
#endif
|
|
|
|
#include "pppd.h"
|
|
#include "pathnames.h"
|
|
#include "patchlevel.h"
|
|
#include "fsm.h"
|
|
#include "lcp.h"
|
|
#include "ipcp.h"
|
|
#include "upap.h"
|
|
#include "chap.h"
|
|
#include "ccp.h"
|
|
|
|
#include <net/ppp-comp.h>
|
|
|
|
#if defined(ultrix) || defined(NeXT)
|
|
char *strdup __P((char *));
|
|
#endif
|
|
|
|
/*
|
|
* Option variables and default values.
|
|
*/
|
|
#ifdef PPP_FILTER
|
|
int dflag = 0; /* Tell libpcap we want debugging */
|
|
#endif
|
|
int debug = 0; /* Debug flag */
|
|
int kdebugflag = 0; /* Tell kernel to print debug messages */
|
|
int default_device = 1; /* Using /dev/tty or equivalent */
|
|
char devnam[MAXPATHLEN]; /* Device name */
|
|
int crtscts = 0; /* Use hardware flow control */
|
|
bool modem = 1; /* Use modem control lines */
|
|
int inspeed = 0; /* Input/Output speed requested */
|
|
u_int32_t netmask = 0; /* IP netmask to set on interface */
|
|
bool lockflag = 0; /* Create lock file to lock the serial dev */
|
|
bool nodetach = 0; /* Don't detach from controlling tty */
|
|
bool updetach = 0; /* Detach once link is up */
|
|
char *initializer = NULL; /* Script to initialize physical link */
|
|
char *connector = NULL; /* Script to establish physical link */
|
|
char *disconnector = NULL; /* Script to disestablish physical link */
|
|
char *welcomer = NULL; /* Script to run after phys link estab. */
|
|
char *ptycommand = NULL; /* Command to run on other side of pty */
|
|
int maxconnect = 0; /* Maximum connect time */
|
|
char user[MAXNAMELEN]; /* Username for PAP */
|
|
char passwd[MAXSECRETLEN]; /* Password for PAP */
|
|
bool persist = 0; /* Reopen link after it goes down */
|
|
char our_name[MAXNAMELEN]; /* Our name for authentication purposes */
|
|
bool demand = 0; /* do dial-on-demand */
|
|
char *ipparam = NULL; /* Extra parameter for ip up/down scripts */
|
|
int idle_time_limit = 0; /* Disconnect if idle for this many seconds */
|
|
int holdoff = 30; /* # seconds to pause before reconnecting */
|
|
bool notty = 0; /* Stdin/out is not a tty */
|
|
char *record_file = NULL; /* File to record chars sent/received */
|
|
int using_pty = 0;
|
|
bool sync_serial = 0; /* Device is synchronous serial device */
|
|
int log_to_fd = 1; /* send log messages to this fd too */
|
|
|
|
extern option_t auth_options[];
|
|
extern struct stat devstat;
|
|
extern int prepass; /* Doing pre-pass to find device name */
|
|
|
|
struct option_info initializer_info;
|
|
struct option_info connector_info;
|
|
struct option_info disconnector_info;
|
|
struct option_info welcomer_info;
|
|
struct option_info devnam_info;
|
|
struct option_info ptycommand_info;
|
|
|
|
#ifdef PPP_FILTER
|
|
struct bpf_program pass_filter;/* Filter program for packets to pass */
|
|
struct bpf_program active_filter; /* Filter program for link-active pkts */
|
|
pcap_t pc; /* Fake struct pcap so we can compile expr */
|
|
#endif
|
|
|
|
char *current_option; /* the name of the option being parsed */
|
|
int privileged_option; /* set iff the current option came from root */
|
|
char *option_source; /* string saying where the option came from */
|
|
bool log_to_file; /* log_to_fd is a file opened by us */
|
|
|
|
/*
|
|
* Prototypes
|
|
*/
|
|
static int setdevname __P((char *));
|
|
static int setipaddr __P((char *));
|
|
static int setspeed __P((char *));
|
|
static int noopt __P((char **));
|
|
static int setdomain __P((char **));
|
|
static int setnetmask __P((char **));
|
|
static int setxonxoff __P((char **));
|
|
static int readfile __P((char **));
|
|
static int callfile __P((char **));
|
|
static int showversion __P((char **));
|
|
static int showhelp __P((char **));
|
|
static void usage __P((void));
|
|
static int setlogfile __P((char **));
|
|
|
|
#ifdef PPP_FILTER
|
|
static int setpassfilter __P((char **));
|
|
static int setactivefilter __P((char **));
|
|
#endif
|
|
|
|
|
|
static option_t *find_option __P((char *name));
|
|
static int process_option __P((option_t *, char **));
|
|
static int n_arguments __P((option_t *));
|
|
static int number_option __P((char *, u_int32_t *, int));
|
|
|
|
/*
|
|
* Valid arguments.
|
|
*/
|
|
option_t general_options[] = {
|
|
{ "debug", o_int, &debug,
|
|
"Increase debugging level", OPT_INC|OPT_NOARG|1 },
|
|
{ "-d", o_int, &debug,
|
|
"Increase debugging level", OPT_INC|OPT_NOARG|1 },
|
|
{ "kdebug", o_int, &kdebugflag,
|
|
"Set kernel driver debug level" },
|
|
{ "nodetach", o_bool, &nodetach,
|
|
"Don't detach from controlling tty", 1 },
|
|
{ "-detach", o_bool, &nodetach,
|
|
"Don't detach from controlling tty", 1 },
|
|
{ "updetach", o_bool, &updetach,
|
|
"Detach from controlling tty once link is up", 1 },
|
|
{ "holdoff", o_int, &holdoff,
|
|
"Set time in seconds before retrying connection" },
|
|
{ "idle", o_int, &idle_time_limit,
|
|
"Set time in seconds before disconnecting idle link" },
|
|
{ "lock", o_bool, &lockflag,
|
|
"Lock serial device with UUCP-style lock file", 1 },
|
|
{ "-all", o_special_noarg, noopt,
|
|
"Don't request/allow any LCP or IPCP options (useless)" },
|
|
{ "init", o_string, &initializer,
|
|
"A program to initialize the device",
|
|
OPT_A2INFO | OPT_PRIVFIX, &initializer_info },
|
|
{ "connect", o_string, &connector,
|
|
"A program to set up a connection",
|
|
OPT_A2INFO | OPT_PRIVFIX, &connector_info },
|
|
{ "disconnect", o_string, &disconnector,
|
|
"Program to disconnect serial device",
|
|
OPT_A2INFO | OPT_PRIVFIX, &disconnector_info },
|
|
{ "welcome", o_string, &welcomer,
|
|
"Script to welcome client",
|
|
OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
|
|
{ "pty", o_string, &ptycommand,
|
|
"Script to run on pseudo-tty master side",
|
|
OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
|
|
{ "notty", o_bool, ¬ty,
|
|
"Input/output is not a tty", OPT_DEVNAM | 1 },
|
|
{ "record", o_string, &record_file,
|
|
"Record characters sent/received to file" },
|
|
{ "maxconnect", o_int, &maxconnect,
|
|
"Set connection time limit", OPT_LLIMIT|OPT_NOINCR|OPT_ZEROINF },
|
|
{ "crtscts", o_int, &crtscts,
|
|
"Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) },
|
|
{ "nocrtscts", o_int, &crtscts,
|
|
"Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
|
|
{ "-crtscts", o_int, &crtscts,
|
|
"Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
|
|
{ "cdtrcts", o_int, &crtscts,
|
|
"Set alternate hardware (DTR/CTS) flow control", OPT_NOARG|OPT_VAL(2) },
|
|
{ "nocdtrcts", o_int, &crtscts,
|
|
"Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
|
|
{ "xonxoff", o_special_noarg, setxonxoff,
|
|
"Set software (XON/XOFF) flow control" },
|
|
{ "domain", o_special, setdomain,
|
|
"Add given domain name to hostname" },
|
|
{ "mtu", o_int, &lcp_allowoptions[0].mru,
|
|
"Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
|
|
{ "netmask", o_special, setnetmask,
|
|
"set netmask" },
|
|
{ "modem", o_bool, &modem,
|
|
"Use modem control lines", 1 },
|
|
{ "local", o_bool, &modem,
|
|
"Don't use modem control lines" },
|
|
{ "file", o_special, readfile,
|
|
"Take options from a file", OPT_PREPASS },
|
|
{ "call", o_special, callfile,
|
|
"Take options from a privileged file", OPT_PREPASS },
|
|
{ "persist", o_bool, &persist,
|
|
"Keep on reopening connection after close", 1 },
|
|
{ "nopersist", o_bool, &persist,
|
|
"Turn off persist option" },
|
|
{ "demand", o_bool, &demand,
|
|
"Dial on demand", OPT_INITONLY | 1, &persist },
|
|
{ "--version", o_special_noarg, showversion,
|
|
"Show version number" },
|
|
{ "--help", o_special_noarg, showhelp,
|
|
"Show brief listing of options" },
|
|
{ "-h", o_special_noarg, showhelp,
|
|
"Show brief listing of options" },
|
|
{ "sync", o_bool, &sync_serial,
|
|
"Use synchronous HDLC serial encoding", 1 },
|
|
{ "logfd", o_int, &log_to_fd,
|
|
"Send log messages to this file descriptor" },
|
|
{ "logfile", o_special, setlogfile,
|
|
"Append log messages to this file" },
|
|
{ "nolog", o_int, &log_to_fd,
|
|
"Don't send log messages to any file",
|
|
OPT_NOARG | OPT_VAL(-1) },
|
|
{ "nologfd", o_int, &log_to_fd,
|
|
"Don't send log messages to any file descriptor",
|
|
OPT_NOARG | OPT_VAL(-1) },
|
|
|
|
#ifdef PPP_FILTER
|
|
{ "pdebug", o_int, &dflag,
|
|
"libpcap debugging" },
|
|
{ "pass-filter", 1, setpassfilter,
|
|
"set filter for packets to pass" },
|
|
{ "active-filter", 1, setactivefilter,
|
|
"set filter for active pkts" },
|
|
#endif
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
#ifndef IMPLEMENTATION
|
|
#define IMPLEMENTATION ""
|
|
#endif
|
|
|
|
static char *usage_string = "\
|
|
pppd version %s.%d%s\n\
|
|
Usage: %s [ options ], where options are:\n\
|
|
<device> Communicate over the named device\n\
|
|
<speed> Set the baud rate to <speed>\n\
|
|
<loc>:<rem> Set the local and/or remote interface IP\n\
|
|
addresses. Either one may be omitted.\n\
|
|
asyncmap <n> Set the desired async map to hex <n>\n\
|
|
auth Require authentication from peer\n\
|
|
connect <p> Invoke shell command <p> to set up the serial line\n\
|
|
crtscts Use hardware RTS/CTS flow control\n\
|
|
defaultroute Add default route through interface\n\
|
|
file <f> Take options from file <f>\n\
|
|
modem Use modem control lines\n\
|
|
mru <n> Set MRU value to <n> for negotiation\n\
|
|
See pppd(8) for more options.\n\
|
|
";
|
|
|
|
/*
|
|
* parse_args - parse a string of arguments from the command line.
|
|
* If prepass is true, we are scanning for the device name and only
|
|
* processing a few options, so error messages are suppressed.
|
|
*/
|
|
int
|
|
parse_args(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
char *arg;
|
|
option_t *opt;
|
|
int ret;
|
|
|
|
privileged_option = privileged;
|
|
option_source = "command line";
|
|
while (argc > 0) {
|
|
arg = *argv++;
|
|
--argc;
|
|
|
|
/*
|
|
* First see if it's an option in the new option list.
|
|
*/
|
|
opt = find_option(arg);
|
|
if (opt != NULL) {
|
|
int n = n_arguments(opt);
|
|
if (argc < n) {
|
|
option_error("too few parameters for option %s", arg);
|
|
return 0;
|
|
}
|
|
current_option = arg;
|
|
if (!process_option(opt, argv))
|
|
return 0;
|
|
argc -= n;
|
|
argv += n;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Maybe a tty name, speed or IP address?
|
|
*/
|
|
if ((ret = setdevname(arg)) == 0
|
|
&& (ret = setspeed(arg)) == 0
|
|
&& (ret = setipaddr(arg)) == 0) {
|
|
option_error("unrecognized option '%s'", arg);
|
|
usage();
|
|
return 0;
|
|
}
|
|
if (ret < 0) /* error */
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* scan_args - scan the command line arguments to get the tty name,
|
|
* if specified. Also checks whether the notty or pty option was given.
|
|
*/
|
|
void
|
|
scan_args(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
char *arg;
|
|
option_t *opt;
|
|
|
|
privileged_option = privileged;
|
|
while (argc > 0) {
|
|
arg = *argv++;
|
|
--argc;
|
|
|
|
if (strcmp(arg, "notty") == 0 || strcmp(arg, "pty") == 0)
|
|
using_pty = 1;
|
|
|
|
/* Skip options and their arguments */
|
|
opt = find_option(arg);
|
|
if (opt != NULL) {
|
|
int n = n_arguments(opt);
|
|
argc -= n;
|
|
argv += n;
|
|
continue;
|
|
}
|
|
|
|
/* Check if it's a tty name and copy it if so */
|
|
(void) setdevname(arg, 1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* options_from_file - Read a string of options from a file,
|
|
* and interpret them.
|
|
*/
|
|
int
|
|
options_from_file(filename, must_exist, check_prot, priv)
|
|
char *filename;
|
|
int must_exist;
|
|
int check_prot;
|
|
int priv;
|
|
{
|
|
FILE *f;
|
|
int i, newline, ret, err;
|
|
option_t *opt;
|
|
int oldpriv;
|
|
char *oldsource;
|
|
char *argv[MAXARGS];
|
|
char args[MAXARGS][MAXWORDLEN];
|
|
char cmd[MAXWORDLEN];
|
|
|
|
if (check_prot)
|
|
seteuid(getuid());
|
|
f = fopen(filename, "r");
|
|
err = errno;
|
|
if (check_prot)
|
|
seteuid(0);
|
|
if (f == NULL) {
|
|
if (!must_exist && err == ENOENT)
|
|
return 1;
|
|
errno = err;
|
|
option_error("Can't open options file %s: %m", filename);
|
|
return 0;
|
|
}
|
|
|
|
oldpriv = privileged_option;
|
|
privileged_option = priv;
|
|
oldsource = option_source;
|
|
option_source = strdup(filename);
|
|
if (option_source == NULL)
|
|
option_source = "file";
|
|
ret = 0;
|
|
while (getword(f, cmd, &newline, filename)) {
|
|
/*
|
|
* First see if it's a command.
|
|
*/
|
|
opt = find_option(cmd);
|
|
if (opt != NULL) {
|
|
int n = n_arguments(opt);
|
|
for (i = 0; i < n; ++i) {
|
|
if (!getword(f, args[i], &newline, filename)) {
|
|
option_error(
|
|
"In file %s: too few parameters for option '%s'",
|
|
filename, cmd);
|
|
goto err;
|
|
}
|
|
argv[i] = args[i];
|
|
}
|
|
current_option = cmd;
|
|
if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
|
|
option_error("the %s option may not be used in the %s file",
|
|
cmd, filename);
|
|
goto err;
|
|
}
|
|
if (!process_option(opt, argv))
|
|
goto err;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Maybe a tty name, speed or IP address?
|
|
*/
|
|
if ((i = setdevname(cmd)) == 0
|
|
&& (i = setspeed(cmd)) == 0
|
|
&& (i = setipaddr(cmd)) == 0) {
|
|
option_error("In file %s: unrecognized option '%s'",
|
|
filename, cmd);
|
|
goto err;
|
|
}
|
|
if (i < 0) /* error */
|
|
goto err;
|
|
}
|
|
ret = 1;
|
|
|
|
err:
|
|
fclose(f);
|
|
privileged_option = oldpriv;
|
|
option_source = oldsource;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* options_from_user - See if the use has a ~/.ppprc file,
|
|
* and if so, interpret options from it.
|
|
*/
|
|
int
|
|
options_from_user()
|
|
{
|
|
char *user, *path, *file;
|
|
int ret;
|
|
struct passwd *pw;
|
|
size_t pl;
|
|
|
|
pw = getpwuid(getuid());
|
|
if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
|
|
return 1;
|
|
file = _PATH_USEROPT;
|
|
pl = strlen(user) + strlen(file) + 2;
|
|
path = malloc(pl);
|
|
if (path == NULL)
|
|
novm("init file name");
|
|
slprintf(path, pl, "%s/%s", user, file);
|
|
ret = options_from_file(path, 0, 1, privileged);
|
|
free(path);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* options_for_tty - See if an options file exists for the serial
|
|
* device, and if so, interpret options from it.
|
|
*/
|
|
int
|
|
options_for_tty()
|
|
{
|
|
char *dev, *path, *p;
|
|
int ret;
|
|
size_t pl;
|
|
|
|
dev = devnam;
|
|
if (strncmp(dev, "/dev/", 5) == 0)
|
|
dev += 5;
|
|
if (dev[0] == 0 || strcmp(dev, "tty") == 0)
|
|
return 1; /* don't look for /etc/ppp/options.tty */
|
|
pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1;
|
|
path = malloc(pl);
|
|
if (path == NULL)
|
|
novm("tty init file name");
|
|
slprintf(path, pl, "%s%s", _PATH_TTYOPT, dev);
|
|
/* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
|
|
for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p)
|
|
if (*p == '/')
|
|
*p = '.';
|
|
ret = options_from_file(path, 0, 0, 1);
|
|
free(path);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* options_from_list - process a string of options in a wordlist.
|
|
*/
|
|
int
|
|
options_from_list(w, priv)
|
|
struct wordlist *w;
|
|
int priv;
|
|
{
|
|
char *argv[MAXARGS];
|
|
option_t *opt;
|
|
int i, ret = 0;
|
|
|
|
privileged_option = priv;
|
|
option_source = "secrets file";
|
|
|
|
while (w != NULL) {
|
|
/*
|
|
* First see if it's a command.
|
|
*/
|
|
opt = find_option(w->word);
|
|
if (opt != NULL) {
|
|
int n = n_arguments(opt);
|
|
struct wordlist *w0 = w;
|
|
for (i = 0; i < n; ++i) {
|
|
w = w->next;
|
|
if (w == NULL) {
|
|
option_error(
|
|
"In secrets file: too few parameters for option '%s'",
|
|
w0->word);
|
|
goto err;
|
|
}
|
|
argv[i] = w->word;
|
|
}
|
|
current_option = w0->word;
|
|
if (!process_option(opt, argv))
|
|
goto err;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Maybe a tty name, speed or IP address?
|
|
*/
|
|
if ((i = setdevname(w->word)) == 0
|
|
&& (i = setspeed(w->word)) == 0
|
|
&& (i = setipaddr(w->word)) == 0) {
|
|
option_error("In secrets file: unrecognized option '%s'",
|
|
w->word);
|
|
goto err;
|
|
}
|
|
if (i < 0) /* error */
|
|
goto err;
|
|
}
|
|
ret = 1;
|
|
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* find_option - scan the option lists for the various protocols
|
|
* looking for an entry with the given name.
|
|
* This could be optimized by using a hash table.
|
|
*/
|
|
static option_t *
|
|
find_option(name)
|
|
char *name;
|
|
{
|
|
option_t *opt;
|
|
int i;
|
|
|
|
for (opt = general_options; opt->name != NULL; ++opt)
|
|
if (strcmp(name, opt->name) == 0)
|
|
return opt;
|
|
for (opt = auth_options; opt->name != NULL; ++opt)
|
|
if (strcmp(name, opt->name) == 0)
|
|
return opt;
|
|
for (i = 0; protocols[i] != NULL; ++i)
|
|
if ((opt = protocols[i]->options) != NULL)
|
|
for (; opt->name != NULL; ++opt)
|
|
if (strcmp(name, opt->name) == 0)
|
|
return opt;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* process_option - process one new-style option.
|
|
*/
|
|
static int
|
|
process_option(opt, argv)
|
|
option_t *opt;
|
|
char **argv;
|
|
{
|
|
u_int32_t v;
|
|
int iv, a;
|
|
char *sv;
|
|
int (*parser) __P((char **));
|
|
|
|
if ((opt->flags & OPT_PREPASS) == 0 && prepass)
|
|
return 1;
|
|
if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
|
|
option_error("it's too late to use the %s option", opt->name);
|
|
return 0;
|
|
}
|
|
if ((opt->flags & OPT_PRIV) && !privileged_option) {
|
|
option_error("using the %s option requires root privilege", opt->name);
|
|
return 0;
|
|
}
|
|
if ((opt->flags & OPT_ENABLE) && *(bool *)(opt->addr2) == 0) {
|
|
option_error("%s option is disabled", opt->name);
|
|
return 0;
|
|
}
|
|
if ((opt->flags & OPT_PRIVFIX) && !privileged_option) {
|
|
struct option_info *ip = (struct option_info *) opt->addr2;
|
|
if (ip && ip->priv) {
|
|
option_error("%s option cannot be overridden", opt->name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
switch (opt->type) {
|
|
case o_bool:
|
|
v = opt->flags & OPT_VALUE;
|
|
*(bool *)(opt->addr) = v;
|
|
if (opt->addr2 && (opt->flags & OPT_A2COPY))
|
|
*(bool *)(opt->addr2) = v;
|
|
break;
|
|
|
|
case o_int:
|
|
iv = 0;
|
|
if ((opt->flags & OPT_NOARG) == 0) {
|
|
if (!int_option(*argv, &iv))
|
|
return 0;
|
|
if ((((opt->flags & OPT_LLIMIT) && iv < opt->lower_limit)
|
|
|| ((opt->flags & OPT_ULIMIT) && iv > opt->upper_limit))
|
|
&& !((opt->flags & OPT_ZEROOK && iv == 0))) {
|
|
char *zok = (opt->flags & OPT_ZEROOK)? " zero or": "";
|
|
switch (opt->flags & OPT_LIMITS) {
|
|
case OPT_LLIMIT:
|
|
option_error("%s value must be%s >= %d",
|
|
opt->name, zok, opt->lower_limit);
|
|
break;
|
|
case OPT_ULIMIT:
|
|
option_error("%s value must be%s <= %d",
|
|
opt->name, zok, opt->upper_limit);
|
|
break;
|
|
case OPT_LIMITS:
|
|
option_error("%s value must be%s between %d and %d",
|
|
opt->name, opt->lower_limit, opt->upper_limit);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
a = opt->flags & OPT_VALUE;
|
|
if (a >= 128)
|
|
a -= 256; /* sign extend */
|
|
iv += a;
|
|
if (opt->flags & OPT_INC)
|
|
iv += *(int *)(opt->addr);
|
|
if ((opt->flags & OPT_NOINCR) && !privileged_option) {
|
|
int oldv = *(int *)(opt->addr);
|
|
if ((opt->flags & OPT_ZEROINF) ?
|
|
(oldv != 0 && (iv == 0 || iv > oldv)) : (iv > oldv)) {
|
|
option_error("%s value cannot be increased", opt->name);
|
|
return 0;
|
|
}
|
|
}
|
|
*(int *)(opt->addr) = iv;
|
|
if (opt->addr2 && (opt->flags & OPT_A2COPY))
|
|
*(int *)(opt->addr2) = iv;
|
|
break;
|
|
|
|
case o_uint32:
|
|
if (opt->flags & OPT_NOARG) {
|
|
v = opt->flags & OPT_VALUE;
|
|
} else if (!number_option(*argv, &v, 16))
|
|
return 0;
|
|
if (opt->flags & OPT_OR)
|
|
v |= *(u_int32_t *)(opt->addr);
|
|
*(u_int32_t *)(opt->addr) = v;
|
|
if (opt->addr2 && (opt->flags & OPT_A2COPY))
|
|
*(u_int32_t *)(opt->addr2) = v;
|
|
break;
|
|
|
|
case o_string:
|
|
if (opt->flags & OPT_STATIC) {
|
|
strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
|
|
} else {
|
|
sv = strdup(*argv);
|
|
if (sv == NULL)
|
|
novm("option argument");
|
|
*(char **)(opt->addr) = sv;
|
|
}
|
|
break;
|
|
|
|
case o_special_noarg:
|
|
case o_special:
|
|
parser = (int (*) __P((char **))) opt->addr;
|
|
if (!(*parser)(argv))
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
if (opt->addr2) {
|
|
if (opt->flags & OPT_A2INFO) {
|
|
struct option_info *ip = (struct option_info *) opt->addr2;
|
|
ip->priv = privileged_option;
|
|
ip->source = option_source;
|
|
} else if ((opt->flags & (OPT_A2COPY|OPT_ENABLE)) == 0)
|
|
*(bool *)(opt->addr2) = 1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* n_arguments - tell how many arguments an option takes
|
|
*/
|
|
static int
|
|
n_arguments(opt)
|
|
option_t *opt;
|
|
{
|
|
return (opt->type == o_bool || opt->type == o_special_noarg
|
|
|| (opt->flags & OPT_NOARG))? 0: 1;
|
|
}
|
|
|
|
/*
|
|
* usage - print out a message telling how to use the program.
|
|
*/
|
|
static void
|
|
usage()
|
|
{
|
|
if (phase == PHASE_INITIALIZE)
|
|
fprintf(stderr, usage_string, VERSION, PATCHLEVEL, IMPLEMENTATION,
|
|
progname);
|
|
}
|
|
|
|
/*
|
|
* showhelp - print out usage message and exit.
|
|
*/
|
|
static int
|
|
showhelp(argv)
|
|
char **argv;
|
|
{
|
|
if (phase == PHASE_INITIALIZE) {
|
|
usage();
|
|
exit(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* showversion - print out the version number and exit.
|
|
*/
|
|
static int
|
|
showversion(argv)
|
|
char **argv;
|
|
{
|
|
if (phase == PHASE_INITIALIZE) {
|
|
fprintf(stderr, "pppd version %s.%d%s\n",
|
|
VERSION, PATCHLEVEL, IMPLEMENTATION);
|
|
exit(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* option_error - print a message about an error in an option.
|
|
* The message is logged, and also sent to
|
|
* stderr if phase == PHASE_INITIALIZE.
|
|
*/
|
|
void
|
|
option_error __V((char *fmt, ...))
|
|
{
|
|
va_list args;
|
|
char buf[256];
|
|
|
|
#if __STDC__
|
|
va_start(args, fmt);
|
|
#else
|
|
char *fmt;
|
|
va_start(args);
|
|
fmt = va_arg(args, char *);
|
|
#endif
|
|
if (prepass) {
|
|
va_end(args);
|
|
return;
|
|
}
|
|
vslprintf(buf, sizeof(buf), fmt, args);
|
|
va_end(args);
|
|
if (phase == PHASE_INITIALIZE)
|
|
fprintf(stderr, "%s: %s\n", progname, buf);
|
|
syslog(LOG_ERR, "%s", buf);
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* readable - check if a file is readable by the real user.
|
|
*/
|
|
int
|
|
readable(fd)
|
|
int fd;
|
|
{
|
|
uid_t uid;
|
|
int i;
|
|
struct stat sbuf;
|
|
|
|
uid = getuid();
|
|
if (uid == 0)
|
|
return 1;
|
|
if (fstat(fd, &sbuf) != 0)
|
|
return 0;
|
|
if (sbuf.st_uid == uid)
|
|
return sbuf.st_mode & S_IRUSR;
|
|
if (sbuf.st_gid == getgid())
|
|
return sbuf.st_mode & S_IRGRP;
|
|
for (i = 0; i < ngroups; ++i)
|
|
if (sbuf.st_gid == groups[i])
|
|
return sbuf.st_mode & S_IRGRP;
|
|
return sbuf.st_mode & S_IROTH;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Read a word from a file.
|
|
* Words are delimited by white-space or by quotes (" or ').
|
|
* Quotes, white-space and \ may be escaped with \.
|
|
* \<newline> is ignored.
|
|
*/
|
|
int
|
|
getword(f, word, newlinep, filename)
|
|
FILE *f;
|
|
char *word;
|
|
int *newlinep;
|
|
char *filename;
|
|
{
|
|
int c, len, escape;
|
|
int quoted, comment;
|
|
int value, digit, got, n;
|
|
|
|
#define isoctal(c) ((c) >= '0' && (c) < '8')
|
|
|
|
*newlinep = 0;
|
|
len = 0;
|
|
escape = 0;
|
|
comment = 0;
|
|
|
|
/*
|
|
* First skip white-space and comments.
|
|
*/
|
|
for (;;) {
|
|
c = getc(f);
|
|
if (c == EOF)
|
|
break;
|
|
|
|
/*
|
|
* A newline means the end of a comment; backslash-newline
|
|
* is ignored. Note that we cannot have escape && comment.
|
|
*/
|
|
if (c == '\n') {
|
|
if (!escape) {
|
|
*newlinep = 1;
|
|
comment = 0;
|
|
} else
|
|
escape = 0;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Ignore characters other than newline in a comment.
|
|
*/
|
|
if (comment)
|
|
continue;
|
|
|
|
/*
|
|
* If this character is escaped, we have a word start.
|
|
*/
|
|
if (escape)
|
|
break;
|
|
|
|
/*
|
|
* If this is the escape character, look at the next character.
|
|
*/
|
|
if (c == '\\') {
|
|
escape = 1;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If this is the start of a comment, ignore the rest of the line.
|
|
*/
|
|
if (c == '#') {
|
|
comment = 1;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* A non-whitespace character is the start of a word.
|
|
*/
|
|
if (!isspace(c))
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Save the delimiter for quoted strings.
|
|
*/
|
|
if (!escape && (c == '"' || c == '\'')) {
|
|
quoted = c;
|
|
c = getc(f);
|
|
} else
|
|
quoted = 0;
|
|
|
|
/*
|
|
* Process characters until the end of the word.
|
|
*/
|
|
while (c != EOF) {
|
|
if (escape) {
|
|
/*
|
|
* This character is escaped: backslash-newline is ignored,
|
|
* various other characters indicate particular values
|
|
* as for C backslash-escapes.
|
|
*/
|
|
escape = 0;
|
|
if (c == '\n') {
|
|
c = getc(f);
|
|
continue;
|
|
}
|
|
|
|
got = 0;
|
|
switch (c) {
|
|
case 'a':
|
|
value = '\a';
|
|
break;
|
|
case 'b':
|
|
value = '\b';
|
|
break;
|
|
case 'f':
|
|
value = '\f';
|
|
break;
|
|
case 'n':
|
|
value = '\n';
|
|
break;
|
|
case 'r':
|
|
value = '\r';
|
|
break;
|
|
case 's':
|
|
value = ' ';
|
|
break;
|
|
case 't':
|
|
value = '\t';
|
|
break;
|
|
|
|
default:
|
|
if (isoctal(c)) {
|
|
/*
|
|
* \ddd octal sequence
|
|
*/
|
|
value = 0;
|
|
for (n = 0; n < 3 && isoctal(c); ++n) {
|
|
value = (value << 3) + (c & 07);
|
|
c = getc(f);
|
|
}
|
|
got = 1;
|
|
break;
|
|
}
|
|
|
|
if (c == 'x') {
|
|
/*
|
|
* \x<hex_string> sequence
|
|
*/
|
|
value = 0;
|
|
c = getc(f);
|
|
for (n = 0; n < 2 && isxdigit(c); ++n) {
|
|
digit = toupper(c) - '0';
|
|
if (digit > 10)
|
|
digit += '0' + 10 - 'A';
|
|
value = (value << 4) + digit;
|
|
c = getc (f);
|
|
}
|
|
got = 1;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Otherwise the character stands for itself.
|
|
*/
|
|
value = c;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Store the resulting character for the escape sequence.
|
|
*/
|
|
if (len < MAXWORDLEN-1)
|
|
word[len] = value;
|
|
++len;
|
|
|
|
if (!got)
|
|
c = getc(f);
|
|
continue;
|
|
|
|
}
|
|
|
|
/*
|
|
* Not escaped: see if we've reached the end of the word.
|
|
*/
|
|
if (quoted) {
|
|
if (c == quoted)
|
|
break;
|
|
} else {
|
|
if (isspace(c) || c == '#') {
|
|
ungetc (c, f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Backslash starts an escape sequence.
|
|
*/
|
|
if (c == '\\') {
|
|
escape = 1;
|
|
c = getc(f);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* An ordinary character: store it in the word and get another.
|
|
*/
|
|
if (len < MAXWORDLEN-1)
|
|
word[len] = c;
|
|
++len;
|
|
|
|
c = getc(f);
|
|
}
|
|
|
|
/*
|
|
* End of the word: check for errors.
|
|
*/
|
|
if (c == EOF) {
|
|
if (ferror(f)) {
|
|
if (errno == 0)
|
|
errno = EIO;
|
|
option_error("Error reading %s: %m", filename);
|
|
die(1);
|
|
}
|
|
/*
|
|
* If len is zero, then we didn't find a word before the
|
|
* end of the file.
|
|
*/
|
|
if (len == 0)
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Warn if the word was too long, and append a terminating null.
|
|
*/
|
|
if (len >= MAXWORDLEN) {
|
|
option_error("warning: word in file %s too long (%.20s...)",
|
|
filename, word);
|
|
len = MAXWORDLEN - 1;
|
|
}
|
|
word[len] = 0;
|
|
|
|
return 1;
|
|
|
|
#undef isoctal
|
|
|
|
}
|
|
|
|
/*
|
|
* number_option - parse an unsigned numeric parameter for an option.
|
|
*/
|
|
static int
|
|
number_option(str, valp, base)
|
|
char *str;
|
|
u_int32_t *valp;
|
|
int base;
|
|
{
|
|
char *ptr;
|
|
|
|
*valp = strtoul(str, &ptr, base);
|
|
if (ptr == str) {
|
|
option_error("invalid numeric parameter '%s' for %s option",
|
|
str, current_option);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* int_option - like number_option, but valp is int *,
|
|
* the base is assumed to be 0, and *valp is not changed
|
|
* if there is an error.
|
|
*/
|
|
int
|
|
int_option(str, valp)
|
|
char *str;
|
|
int *valp;
|
|
{
|
|
u_int32_t v;
|
|
|
|
if (!number_option(str, &v, 0))
|
|
return 0;
|
|
*valp = (int) v;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* The following procedures parse options.
|
|
*/
|
|
|
|
/*
|
|
* readfile - take commands from a file.
|
|
*/
|
|
static int
|
|
readfile(argv)
|
|
char **argv;
|
|
{
|
|
return options_from_file(*argv, 1, 1, privileged_option);
|
|
}
|
|
|
|
/*
|
|
* callfile - take commands from /etc/ppp/peers/<name>.
|
|
* Name may not contain /../, start with / or ../, or end in /..
|
|
*/
|
|
static int
|
|
callfile(argv)
|
|
char **argv;
|
|
{
|
|
char *fname, *arg, *p;
|
|
int l, ok;
|
|
|
|
arg = *argv;
|
|
ok = 1;
|
|
if (arg[0] == '/' || arg[0] == 0)
|
|
ok = 0;
|
|
else {
|
|
for (p = arg; *p != 0; ) {
|
|
if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
|
|
ok = 0;
|
|
break;
|
|
}
|
|
while (*p != '/' && *p != 0)
|
|
++p;
|
|
if (*p == '/')
|
|
++p;
|
|
}
|
|
}
|
|
if (!ok) {
|
|
option_error("call option value may not contain .. or start with /");
|
|
return 0;
|
|
}
|
|
|
|
l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
|
|
if ((fname = (char *) malloc(l)) == NULL)
|
|
novm("call file name");
|
|
slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg);
|
|
|
|
ok = options_from_file(fname, 1, 1, 1);
|
|
|
|
free(fname);
|
|
return ok;
|
|
}
|
|
|
|
#ifdef PPP_FILTER
|
|
/*
|
|
* setpdebug - Set libpcap debugging level.
|
|
*/
|
|
static int
|
|
setpdebug(argv)
|
|
char **argv;
|
|
{
|
|
return int_option(*argv, &dflag);
|
|
}
|
|
|
|
/*
|
|
* setpassfilter - Set the pass filter for packets
|
|
*/
|
|
static int
|
|
setpassfilter(argv)
|
|
char **argv;
|
|
{
|
|
pc.linktype = DLT_PPP;
|
|
pc.snapshot = PPP_HDRLEN;
|
|
|
|
if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
|
|
return 1;
|
|
option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* setactivefilter - Set the active filter for packets
|
|
*/
|
|
static int
|
|
setactivefilter(argv)
|
|
char **argv;
|
|
{
|
|
pc.linktype = DLT_PPP;
|
|
pc.snapshot = PPP_HDRLEN;
|
|
|
|
if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
|
|
return 1;
|
|
option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* noopt - Disable all options.
|
|
*/
|
|
static int
|
|
noopt(argv)
|
|
char **argv;
|
|
{
|
|
BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
|
|
BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
|
|
BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options));
|
|
BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options));
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* setdomain - Set domain name to append to hostname
|
|
*/
|
|
static int
|
|
setdomain(argv)
|
|
char **argv;
|
|
{
|
|
if (!privileged_option) {
|
|
option_error("using the domain option requires root privilege");
|
|
return 0;
|
|
}
|
|
gethostname(hostname, MAXNAMELEN);
|
|
if (**argv != 0) {
|
|
if (**argv != '.')
|
|
strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
|
|
strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
|
|
}
|
|
hostname[MAXNAMELEN-1] = 0;
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* setspeed - Set the speed.
|
|
*/
|
|
static int
|
|
setspeed(arg)
|
|
char *arg;
|
|
{
|
|
char *ptr;
|
|
int spd;
|
|
|
|
spd = strtol(arg, &ptr, 0);
|
|
if (ptr == arg || *ptr != 0 || spd == 0)
|
|
return 0;
|
|
inspeed = spd;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* setdevname - Set the device name.
|
|
*/
|
|
static int
|
|
setdevname(cp)
|
|
char *cp;
|
|
{
|
|
struct stat statbuf;
|
|
char dev[MAXPATHLEN];
|
|
|
|
if (*cp == 0)
|
|
return 0;
|
|
|
|
if (strncmp("/dev/", cp, 5) != 0) {
|
|
strlcpy(dev, "/dev/", sizeof(dev));
|
|
strlcat(dev, cp, sizeof(dev));
|
|
cp = dev;
|
|
}
|
|
|
|
/*
|
|
* Check if there is a character device by this name.
|
|
*/
|
|
if (stat(cp, &statbuf) < 0) {
|
|
if (errno == ENOENT)
|
|
return 0;
|
|
option_error("Couldn't stat %s: %m", cp);
|
|
return -1;
|
|
}
|
|
if (!S_ISCHR(statbuf.st_mode)) {
|
|
option_error("%s is not a character device", cp);
|
|
return -1;
|
|
}
|
|
|
|
if (phase != PHASE_INITIALIZE) {
|
|
option_error("device name cannot be changed after initialization");
|
|
return -1;
|
|
} else if (devnam_fixed) {
|
|
option_error("per-tty options file may not specify device name");
|
|
return -1;
|
|
}
|
|
|
|
if (devnam_info.priv && !privileged_option) {
|
|
option_error("device name cannot be overridden");
|
|
return -1;
|
|
}
|
|
|
|
strlcpy(devnam, cp, sizeof(devnam));
|
|
devstat = statbuf;
|
|
default_device = 0;
|
|
devnam_info.priv = privileged_option;
|
|
devnam_info.source = option_source;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* setipaddr - Set the IP address
|
|
*/
|
|
static int
|
|
setipaddr(arg)
|
|
char *arg;
|
|
{
|
|
struct hostent *hp;
|
|
char *colon;
|
|
u_int32_t local, remote;
|
|
ipcp_options *wo = &ipcp_wantoptions[0];
|
|
|
|
/*
|
|
* IP address pair separated by ":".
|
|
*/
|
|
if ((colon = strchr(arg, ':')) == NULL)
|
|
return 0;
|
|
if (prepass)
|
|
return 1;
|
|
|
|
/*
|
|
* If colon first character, then no local addr.
|
|
*/
|
|
if (colon != arg) {
|
|
*colon = '\0';
|
|
if ((local = inet_addr(arg)) == -1) {
|
|
if ((hp = gethostbyname(arg)) == NULL) {
|
|
option_error("unknown host: %s", arg);
|
|
return -1;
|
|
} else {
|
|
local = *(u_int32_t *)hp->h_addr;
|
|
}
|
|
}
|
|
if (bad_ip_adrs(local)) {
|
|
option_error("bad local IP address %s", ip_ntoa(local));
|
|
return -1;
|
|
}
|
|
if (local != 0)
|
|
wo->ouraddr = local;
|
|
*colon = ':';
|
|
}
|
|
|
|
/*
|
|
* If colon last character, then no remote addr.
|
|
*/
|
|
if (*++colon != '\0') {
|
|
if ((remote = inet_addr(colon)) == -1) {
|
|
if ((hp = gethostbyname(colon)) == NULL) {
|
|
option_error("unknown host: %s", colon);
|
|
return -1;
|
|
} else {
|
|
remote = *(u_int32_t *)hp->h_addr;
|
|
if (remote_name[0] == 0)
|
|
strlcpy(remote_name, colon, sizeof(remote_name));
|
|
}
|
|
}
|
|
if (bad_ip_adrs(remote)) {
|
|
option_error("bad remote IP address %s", ip_ntoa(remote));
|
|
return -1;
|
|
}
|
|
if (remote != 0)
|
|
wo->hisaddr = remote;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* setnetmask - set the netmask to be used on the interface.
|
|
*/
|
|
static int
|
|
setnetmask(argv)
|
|
char **argv;
|
|
{
|
|
u_int32_t mask, b;
|
|
int n, ok;
|
|
char *p, *endp;
|
|
|
|
/*
|
|
* Unfortunately, if we use inet_addr, we can't tell whether
|
|
* a result of all 1s is an error or a valid 255.255.255.255.
|
|
*/
|
|
p = *argv;
|
|
ok = 0;
|
|
mask = 0;
|
|
for (n = 3;; --n) {
|
|
b = strtoul(p, &endp, 0);
|
|
if (endp == p)
|
|
break;
|
|
if (b < 0 || b > 255) {
|
|
if (n == 3) {
|
|
/* accept e.g. 0xffffff00 */
|
|
p = endp;
|
|
mask = b;
|
|
}
|
|
break;
|
|
}
|
|
mask |= b << (n * 8);
|
|
p = endp;
|
|
if (*p != '.' || n == 0)
|
|
break;
|
|
++p;
|
|
}
|
|
|
|
mask = htonl(mask);
|
|
|
|
if (*p != 0 || (netmask & ~mask) != 0) {
|
|
option_error("invalid netmask value '%s'", *argv);
|
|
return 0;
|
|
}
|
|
|
|
netmask = mask;
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
setxonxoff(argv)
|
|
char **argv;
|
|
{
|
|
lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */
|
|
lcp_wantoptions[0].neg_asyncmap = 1;
|
|
|
|
crtscts = -2;
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
setlogfile(argv)
|
|
char **argv;
|
|
{
|
|
int fd, err;
|
|
|
|
if (!privileged_option)
|
|
seteuid(getuid());
|
|
fd = open(*argv, O_WRONLY | O_APPEND);
|
|
err = errno;
|
|
if (!privileged_option)
|
|
seteuid(0);
|
|
if (fd < 0) {
|
|
errno = err;
|
|
option_error("Can't open log file %s: %m", *argv);
|
|
return 0;
|
|
}
|
|
if (log_to_file && log_to_fd >= 0)
|
|
close(log_to_fd);
|
|
log_to_fd = fd;
|
|
log_to_file = 1;
|
|
return 1;
|
|
}
|