diff options
Diffstat (limited to 'src/option.c')
-rw-r--r-- | src/option.c | 212 |
1 files changed, 136 insertions, 76 deletions
diff --git a/src/option.c b/src/option.c index b8cb7d7..f2957ad 100644 --- a/src/option.c +++ b/src/option.c @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000 - 2006 Simon Kelley +/* dnsmasq is Copyright (c) 2000 - 2007 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,6 +43,9 @@ struct myoption { #define LOPT_REMOTE 269 #define LOPT_SUBSCR 270 #define LOPT_INTNAME 271 +#define LOPT_BANK 272 +#define LOPT_DHCP_HOST 273 +#define LOPT_APREF 274 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -119,6 +122,7 @@ static const struct myoption opts[] = {"dhcp-ignore-names", 2, 0, LOPT_NO_NAMES }, {"enable-tftp", 0, 0, LOPT_TFTP }, {"tftp-secure", 0, 0, LOPT_SECURE }, + {"tftp-unique-root", 0, 0, LOPT_APREF }, {"tftp-root", 1, 0, LOPT_PREFIX }, {"tftp-max", 1, 0, LOPT_TFTP_MAX }, {"ptr-record", 1, 0, LOPT_PTR }, @@ -133,6 +137,7 @@ static const struct myoption opts[] = {"dhcp-remoteid", 1, 0, LOPT_REMOTE }, {"dhcp-subscrid", 1, 0, LOPT_SUBSCR }, {"interface-name", 1, 0, LOPT_INTNAME }, + {"dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST }, { NULL, 0, 0, 0 } }; @@ -169,6 +174,7 @@ static const struct optflags optmap[] = { { LOPT_SECURE, OPT_TFTP_SECURE }, { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK }, { LOPT_LOG_OPTS, OPT_LOG_OPTS }, + { LOPT_APREF, OPT_TFTP_APREF }, { 'v', 0}, { 'w', 0}, { 0, 0 } @@ -192,7 +198,8 @@ static const struct { { "-f, --filterwin2k", gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL }, { "-F, --dhcp-range=ipaddr,ipaddr,time", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { "-g, --group=groupname", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, - { "-G, --dhcp-host=<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL }, + { "-G, --dhcp-host=<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL }, + { " --dhcp-hostsfile=<filename>", gettext_noop("Read DHCP host specs from file"), NULL }, { "-h, --no-hosts", gettext_noop("Do NOT load %s file."), HOSTSFILE }, { "-H, --addn-hosts=path", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE }, { "-i, --interface=interface", gettext_noop("Specify interface(s) to listen on."), NULL }, @@ -255,6 +262,7 @@ static const struct { { " --dhcp-ignore-names[=<id>]", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL }, { " --enable-tftp", gettext_noop("Enable integrated read-only TFTP server."), NULL }, { " --tftp-root=<directory>", gettext_noop("Export files by TFTP only from the specified subtree."), NULL }, + { " --tftp-unique-root", gettext_noop("Add client IP address to tftp-root."), NULL }, { " --tftp-secure", gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL }, { " --tftp-max=<connections>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" }, { " --tftp-no-blocksize", gettext_noop("Disable the TFTP blocksize extension."), NULL }, @@ -367,8 +375,6 @@ char *option_string(unsigned char opt) static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,."; -static void one_file(struct daemon *daemon, char *file, int nest); - static char hide_meta(char c) { unsigned int i; @@ -460,7 +466,7 @@ static int atoi_check(char *a, int *res) return 1; } -static void add_txt(struct daemon *daemon, char *name, char *txt) +static void add_txt(char *name, char *txt) { size_t len = strlen(txt); struct txt_record *r = safe_malloc(sizeof(struct txt_record)); @@ -525,7 +531,7 @@ static void display_opts(void) } /* This is too insanely large to keep in-line in the switch */ -static char *parse_dhcp_opt(struct daemon *daemon, char *arg, int forced) +static char *parse_dhcp_opt(char *arg, int forced) { struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt)); char lenchar = 0, *cp; @@ -749,7 +755,7 @@ static char *parse_dhcp_opt(struct daemon *daemon, char *arg, int forced) } if (!(m = realloc(m, len + strlen(arg) + 2 + header_size))) - die(_("could not get memory"), NULL); + die(_("could not get memory"), NULL, EC_NOMEM); p = m + header_size; q = p + len; @@ -809,7 +815,7 @@ static char *parse_dhcp_opt(struct daemon *daemon, char *arg, int forced) } -static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem, int nest) +static char *one_opt(int option, char *arg, char *problem, int nest) { int i; char *comma; @@ -830,7 +836,7 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem { char *file = safe_string_alloc(arg); if (file) - one_file(daemon, file, nest); + one_file(file, nest, 0); break; } @@ -844,7 +850,7 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem break; if (!(dir_stream = opendir(directory))) - die(_("cannot access directory %s: %s"), directory); + die(_("cannot access directory %s: %s"), directory, EC_FILE); while ((ent = readdir(dir_stream))) { @@ -863,13 +869,13 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem strcat(path, "/"); strcat(path, ent->d_name); if (stat(path, &buf) == -1) - die(_("cannot access %s: %s"), path); + die(_("cannot access %s: %s"), path, EC_FILE); /* only reg files allowed. */ if (!S_ISREG(buf.st_mode)) continue; /* dir is one level, so files must be readable */ - one_file(daemon, path, nest + 1); + one_file(path, nest + 1, 0); free(path); } @@ -900,6 +906,15 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem case 'x': /* --pid-file */ daemon->runfile = safe_string_alloc(arg); break; + + case LOPT_DHCP_HOST: /* --dhcp-hostfile */ + if (daemon->dhcp_hosts_file) + { + problem = _("only one dhcp-hostsfile allowed"); + option = '?'; + } + daemon->dhcp_hosts_file = safe_string_alloc(arg); + break; case 'r': /* --resolv-file */ { @@ -1210,7 +1225,7 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem } else newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY; - } + } #ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0) { @@ -1494,17 +1509,24 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem } break; } - + + /* Note, must not die() via safe_* if option is LOPT_BANK, since + when called with this we are re-loading the file. */ + case LOPT_BANK: case 'G': /* --dhcp-host */ { int j, k = 0; char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; - struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config)); + struct dhcp_config *new; struct in_addr in; + if (option != LOPT_BANK) + new = safe_malloc(sizeof(struct dhcp_config)); + else if (!(new = whine_malloc(sizeof(struct dhcp_config)))) + break; + new->next = daemon->dhcp_conf; - new->flags = 0; - + new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0; if ((a[0] = arg)) for (k = 1; k < 6; k++) @@ -1529,18 +1551,28 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem if (strchr(arg, ':')) len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL); else - len = (int) strlen(arg); - - new->flags |= CONFIG_CLID; - new->clid_len = len; - new->clid = safe_malloc(len); - memcpy(new->clid, arg, len); + { + unhide_metas(arg); + len = (int) strlen(arg); + } + + if ((new->clid = (option == LOPT_BANK) ? whine_malloc(len): safe_malloc(len))) + { + new->flags |= CONFIG_CLID; + new->clid_len = len; + memcpy(new->clid, arg, len); + } } } else if (strstr(arg, "net:") == arg) { - new->flags |= CONFIG_NETID; - new->netid.net = safe_string_alloc(arg+4); + int len = strlen(arg + 4) + 1; + if ((new->netid.net = (option == LOPT_BANK) ? whine_malloc(len): safe_malloc(len))) + { + new->flags |= CONFIG_NETID; + strcpy(new->netid.net, arg+4); + unhide_metas(new->netid.net); + } } else { @@ -1599,8 +1631,17 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem new->flags |= CONFIG_DISABLE; else { - new->hostname = safe_string_alloc(a[j]); - new->flags |= CONFIG_NAME; + int len = strlen(a[j]) + 1; + if (!canonicalise_opt(a[j])) + { + problem = _("bad DHCP host name"); + option = '?'; + } + else if ((new->hostname = (option == LOPT_BANK) ? whine_malloc(len): safe_malloc(len))) + { + new->flags |= CONFIG_NAME; + strcpy(new->hostname, a[j]); + } } } else @@ -1614,17 +1655,13 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem } } - if (option == '?') - problem = _("bad dhcp-host"); - else - daemon->dhcp_conf = new; - + daemon->dhcp_conf = new; break; } case 'O': case LOPT_FORCE: - if ((problem = parse_dhcp_opt(daemon, arg, option == LOPT_FORCE))) + if ((problem = parse_dhcp_opt(arg, option == LOPT_FORCE))) option = '?'; break; @@ -2007,21 +2044,30 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem return option == '?' ? problem : NULL; } -static void one_file(struct daemon *daemon, char *file, int nest) +void one_file(char *file, int nest, int hosts) { int i, option, lineno = 0; FILE *f; char *p, *arg, *start, *buff = daemon->namebuff; if (nest > 20) - die(_("files nested too deep in %s"), file); + die(_("files nested too deep in %s"), file, EC_BADCONF); if (!(f = fopen(file, "r"))) { if (errno == ENOENT && nest == 0) return; /* No conffile, all done. */ else - die(_("cannot read %s: %s"), file); + { + char *str = _("cannot read %s: %s"); + if (hosts) + { + my_syslog(LOG_ERR, str, file, strerror(errno)); + return; + } + else + die(str, file, EC_FILE); + } } while (fgets(buff, MAXDNAME, f)) @@ -2042,7 +2088,7 @@ static void one_file(struct daemon *daemon, char *file, int nest) memmove(p, p+1, strlen(p+1)+1); for(; *p && *p != '"'; p++) { - if (*p == '\\' && strchr("\"tn\033br\\", p[1])) + if (*p == '\\' && strchr("\"tnebr\\", p[1])) { if (p[1] == 't') p[1] = '\t'; @@ -2085,55 +2131,71 @@ static void one_file(struct daemon *daemon, char *file, int nest) if (*buff == 0) continue; - if ((p=strchr(buff, '='))) + if (hosts) + arg = buff; + else if ((p=strchr(buff, '='))) { /* allow spaces around "=" */ - for (arg = p+1; isspace(*arg); arg++); + arg = p+1; for (; p >= buff && (isspace(*p) || *p == '='); p--) *p = 0; } else arg = NULL; - /* skip leading space */ - for (start = buff; *start && isspace(*start); start++); - - for (option = 0, i = 0; opts[i].name; i++) - if (strcmp(opts[i].name, start) == 0) - { - option = opts[i].val; - break; - } - - if (!option) - errmess = _("bad option"); - else if (opts[i].has_arg == 0 && arg) - errmess = _("extraneous parameter"); - else if (opts[i].has_arg == 1 && !arg) - errmess = _("missing parameter"); + if (hosts) + option = LOPT_BANK; else - errmess = one_opt(daemon, option, arg, _("error"), nest + 1); + { + /* skip leading space */ + for (start = buff; *start && isspace(*start); start++); + + for (option = 0, i = 0; opts[i].name; i++) + if (strcmp(opts[i].name, start) == 0) + { + option = opts[i].val; + break; + } + + if (!option) + errmess = _("bad option"); + else if (opts[i].has_arg == 0 && arg) + errmess = _("extraneous parameter"); + else if (opts[i].has_arg == 1 && !arg) + errmess = _("missing parameter"); + } + + if (!errmess) + { + if (arg) + for (; isspace(*arg); arg++); + + errmess = one_opt(option, arg, _("error"), nest + 1); + } if (errmess) { oops: sprintf(buff, _("%s at line %d of %%s"), errmess, lineno); - die(buff, file); + if (hosts) + my_syslog(LOG_ERR, buff, file); + else + die(buff, file, EC_BADCONF); } } fclose(f); } -struct daemon *read_opts(int argc, char **argv, char *compile_opts) +void read_opts(int argc, char **argv, char *compile_opts) { - struct daemon *daemon = safe_malloc(sizeof(struct daemon)); char *buff = safe_malloc(MAXDNAME); int option, nest = 0; char *errmess, *arg, *conffile = CONFFILE; opterr = 0; - + + daemon = safe_malloc(sizeof(struct daemon)); memset(daemon, 0, sizeof(struct daemon)); daemon->namebuff = buff; @@ -2151,9 +2213,9 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) daemon->tftp_max = TFTP_MAX_CONNECTIONS; daemon->edns_pktsz = EDNS_PKTSZ; daemon->log_fac = -1; - add_txt(daemon, "version.bind", "dnsmasq-" VERSION ); - add_txt(daemon, "authors.bind", "Simon Kelley"); - add_txt(daemon, "copyright.bind", COPYRIGHT); + add_txt("version.bind", "dnsmasq-" VERSION ); + add_txt("authors.bind", "Simon Kelley"); + add_txt("copyright.bind", COPYRIGHT); while (1) { @@ -2202,17 +2264,17 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) else { #ifdef HAVE_GETOPT_LONG - errmess = one_opt(daemon, option, arg, _("try --help"), 0); + errmess = one_opt(option, arg, _("try --help"), 0); #else - errmess = one_opt(daemon, option, arg, _("try -w"), 0); + errmess = one_opt(option, arg, _("try -w"), 0); #endif if (errmess) - die(_("bad command line options: %s"), errmess); + die(_("bad command line options: %s"), errmess, EC_BADCONF); } } if (conffile) - one_file(daemon, conffile, nest); + one_file(conffile, nest, 0); /* port might no be known when the address is parsed - fill in here */ if (daemon->servers) @@ -2226,8 +2288,8 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) #ifdef HAVE_IPV6 else if (tmp->source_addr.sa.sa_family == AF_INET6) tmp->source_addr.in6.sin6_port = htons(daemon->query_port); -#endif - } +#endif + } } if (daemon->if_addrs) @@ -2248,7 +2310,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) struct mx_srv_record *mx; if (gethostname(buff, MAXDNAME) == -1) - die(_("cannot get host-name: %s"), NULL); + die(_("cannot get host-name: %s"), NULL, EC_MISC); for (mx = daemon->mxnames; mx; mx = mx->next) if (!mx->issrv && hostname_isequal(mx->name, buff)) @@ -2276,7 +2338,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) daemon->resolv_files && daemon->resolv_files->next && (daemon->options & OPT_NO_POLL)) - die(_("only one resolv.conf file allowed in no-poll mode."), NULL); + die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF); if (daemon->options & OPT_RESOLV_DOMAIN) { @@ -2286,10 +2348,10 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) if ((daemon->options & OPT_NO_RESOLV) || !daemon->resolv_files || (daemon->resolv_files)->next) - die(_("must have exactly one resolv.conf to read domain from."), NULL); + die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF); if (!(f = fopen((daemon->resolv_files)->name, "r"))) - die(_("failed to read %s: %s"), (daemon->resolv_files)->name); + die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE); while ((line = fgets(buff, MAXDNAME, f))) { @@ -2307,7 +2369,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) fclose(f); if (!daemon->domain_suffix) - die(_("no search directive found in %s"), (daemon->resolv_files)->name); + die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC); } if (daemon->domain_suffix) @@ -2327,8 +2389,6 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts) srv->name = safe_string_alloc(buff); } } - - return daemon; } |