summaryrefslogtreecommitdiff
path: root/src/option.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/option.c')
-rw-r--r--src/option.c979
1 files changed, 728 insertions, 251 deletions
diff --git a/src/option.c b/src/option.c
index ffce9fc..8e61a6b 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2022 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
@@ -174,7 +174,18 @@ struct myoption {
#define LOPT_CMARK_ALST_EN 365
#define LOPT_CMARK_ALST 366
#define LOPT_QUIET_TFTP 367
-
+#define LOPT_NFTSET 368
+#define LOPT_FILTER_A 369
+#define LOPT_FILTER_AAAA 370
+#define LOPT_STRIP_SBNET 371
+#define LOPT_STRIP_MAC 372
+#define LOPT_CONF_OPT 373
+#define LOPT_CONF_SCRIPT 374
+#define LOPT_RANDPORT_LIM 375
+#define LOPT_FAST_RETRY 376
+#define LOPT_STALE_CACHE 377
+#define LOPT_NORR 378
+
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
#else
@@ -211,6 +222,8 @@ static const struct myoption opts[] =
{ "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
{ "selfmx", 0, 0, 'e' },
{ "filterwin2k", 0, 0, 'f' },
+ { "filter-A", 0, 0, LOPT_FILTER_A },
+ { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA },
{ "pid-file", 2, 0, 'x' },
{ "strict-order", 0, 0, 'o' },
{ "server", 1, 0, 'S' },
@@ -218,11 +231,13 @@ static const struct myoption opts[] =
{ "local", 1, 0, LOPT_LOCAL },
{ "address", 1, 0, 'A' },
{ "conf-file", 2, 0, 'C' },
+ { "conf-script", 1, 0, LOPT_CONF_SCRIPT },
{ "no-resolv", 0, 0, 'R' },
{ "expand-hosts", 0, 0, 'E' },
{ "localmx", 0, 0, 'L' },
{ "local-ttl", 1, 0, 'T' },
{ "no-negcache", 0, 0, 'N' },
+ { "no-round-robin", 0, 0, LOPT_NORR },
{ "addn-hosts", 1, 0, 'H' },
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
{ "query-port", 1, 0, 'Q' },
@@ -309,7 +324,9 @@ static const struct myoption opts[] =
{ "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
{ "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
{ "add-mac", 2, 0, LOPT_ADD_MAC },
+ { "strip-mac", 0, 0, LOPT_STRIP_MAC },
{ "add-subnet", 2, 0, LOPT_ADD_SBNET },
+ { "strip-subnet", 0, 0, LOPT_STRIP_SBNET },
{ "add-cpe-id", 1, 0 , LOPT_CPE_ID },
{ "proxy-dnssec", 0, 0, LOPT_DNSSEC },
{ "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
@@ -327,6 +344,7 @@ static const struct myoption opts[] =
{ "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
{ "ipset", 1, 0, LOPT_IPSET },
+ { "nftset", 1, 0, LOPT_NFTSET },
{ "connmark-allowlist-enable", 2, 0, LOPT_CMARK_ALST_EN },
{ "connmark-allowlist", 1, 0, LOPT_CMARK_ALST },
{ "synth-domain", 1, 0, LOPT_SYNTH },
@@ -351,8 +369,11 @@ static const struct myoption opts[] =
{ "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
{ "dynamic-host", 1, 0, LOPT_DYNHOST },
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
- { "umbrella", 2, 0, LOPT_UMBRELLA },
+ { "umbrella", 2, 0, LOPT_UMBRELLA },
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
+ { "port-limit", 1, 0, LOPT_RANDPORT_LIM },
+ { "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
+ { "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
{ NULL, 0, 0, 0 }
};
@@ -380,6 +401,8 @@ static struct {
{ 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
{ 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
{ 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
+ { LOPT_FILTER_A, OPT_FILTER_A, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL },
+ { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL },
{ 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
{ 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
{ 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
@@ -408,6 +431,7 @@ static struct {
{ 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
{ 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
{ 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
+ { LOPT_STALE_CACHE, ARG_ONE, "[=<max_expired>]", gettext_noop("Use expired cache data for faster reply."), NULL },
{ 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
{ 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
{ LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
@@ -415,6 +439,7 @@ static struct {
{ 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
{ 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
+ { LOPT_RANDPORT_LIM, ARG_ONE, "#ports", gettext_noop("Set maximum number of random originating ports for a query."), NULL },
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
@@ -428,6 +453,7 @@ static struct {
{ LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
{ LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
{ LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
+ { LOPT_FAST_RETRY, ARG_ONE, "<milliseconds>", gettext_noop("Retry DNS queries after this many milliseconds."), NULL},
{ 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
{ 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
{ 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
@@ -455,6 +481,7 @@ static struct {
{ LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
{ LOPT_SCRIPT_ARP, OPT_SCRIPT_ARP, NULL, gettext_noop("Call dhcp-script with changes to local ARP table."), NULL },
{ '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
+ { LOPT_CONF_SCRIPT, ARG_DUP, "<path>", gettext_noop("Execute file and read configuration from stdin."), NULL },
{ '8', ARG_ONE, "<facility>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
{ '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
{ '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
@@ -493,7 +520,9 @@ static struct {
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
{ LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
+ { LOPT_STRIP_MAC, OPT_STRIP_MAC, NULL, gettext_noop("Strip MAC information from queries."), NULL },
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
+ { LOPT_STRIP_SBNET, OPT_STRIP_ECS, NULL, gettext_noop("Strip ECS information from queries."), NULL },
{ LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
@@ -514,6 +543,7 @@ static struct {
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
+ { LOPT_NFTSET, ARG_DUP, "/<domain>[/<domain>...]/<nftset>...", gettext_noop("Specify nftables sets to which matching domains should be added"), NULL },
{ LOPT_CMARK_ALST_EN, ARG_ONE, "[=<mask>]", gettext_noop("Enable filtering of DNS queries with connection-track marks."), NULL },
{ LOPT_CMARK_ALST, ARG_DUP, "<connmark>[/<mask>][,<pattern>[/<pattern>...]]", gettext_noop("Set allowed DNS patterns for a connection-track mark."), NULL },
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
@@ -539,6 +569,7 @@ static struct {
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
+ { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -654,7 +685,7 @@ static char *canonicalise_opt(char *s)
return 0;
if (strlen(s) == 0)
- return opt_string_alloc("");
+ return opt_malloc(1); /* Heap-allocated empty string */
unhide_metas(s);
if (!(ret = canonicalise(s, &nomem)) && nomem)
@@ -798,7 +829,7 @@ static void do_usage(void)
if (usage[i].arg)
{
- strcpy(buff, usage[i].arg);
+ safe_strncpy(buff, usage[i].arg, sizeof(buff));
for (j = 0; tab[j].handle; j++)
if (tab[j].handle == *(usage[i].arg))
sprintf(buff, "%d", tab[j].val);
@@ -824,163 +855,407 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
return NULL;
}
-char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
+char *parse_server(char *arg, struct server_details *sdetails)
{
- int source_port = 0, serv_port = NAMESERVER_PORT;
- char *portno, *source;
- char *interface_opt = NULL;
- int scope_index = 0;
- char *scope_id;
-
- *interface = 0;
+ sdetails->serv_port = NAMESERVER_PORT;
+ char *portno;
+ int ecode = 0;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(struct addrinfo));
+
+ *sdetails->interface = 0;
+ sdetails->addr_type = AF_UNSPEC;
+
if (strcmp(arg, "#") == 0)
{
- if (flags)
- *flags |= SERV_USE_RESOLV;
+ if (sdetails->flags)
+ *sdetails->flags |= SERV_USE_RESOLV;
+ sdetails->addr_type = AF_LOCAL;
+ sdetails->valid = 1;
return NULL;
}
- if ((source = split_chr(arg, '@')) && /* is there a source. */
- (portno = split_chr(source, '#')) &&
- !atoi_check16(portno, &source_port))
+ if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */
+ (portno = split_chr(sdetails->source, '#')) &&
+ !atoi_check16(portno, &sdetails->source_port))
return _("bad port");
if ((portno = split_chr(arg, '#')) && /* is there a port no. */
- !atoi_check16(portno, &serv_port))
+ !atoi_check16(portno, &sdetails->serv_port))
return _("bad port");
- scope_id = split_chr(arg, '%');
+ sdetails->scope_id = split_chr(arg, '%');
- if (source) {
- interface_opt = split_chr(source, '@');
+ if (sdetails->source) {
+ sdetails->interface_opt = split_chr(sdetails->source, '@');
- if (interface_opt)
+ if (sdetails->interface_opt)
{
#if defined(SO_BINDTODEVICE)
- safe_strncpy(interface, source, IF_NAMESIZE);
- source = interface_opt;
+ safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
+ sdetails->source = sdetails->interface_opt;
#else
return _("interface binding not supported");
#endif
}
}
- if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
+ if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0)
+ sdetails->addr_type = AF_INET;
+ else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0)
+ sdetails->addr_type = AF_INET6;
+ else
+ {
+ /* if the argument is neither an IPv4 not an IPv6 address, it might be a
+ hostname and we should try to resolve it to a suitable address. */
+ memset(&hints, 0, sizeof(hints));
+ /* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
+ the result only if the local system has at least one IPv4 address
+ configured, and IPv6 addresses are returned only if the local system
+ has at least one IPv6 address configured. The loopback address is not
+ considered for this case as valid as a configured address. This flag is
+ useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
+ does not return IPv6 socket addresses that would always fail in
+ subsequent connect() or bind() attempts. */
+ hints.ai_flags = AI_ADDRCONFIG;
+#if defined(HAVE_IDN) && defined(AI_IDN)
+ /* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
+ the node name given in node is converted to IDN format if necessary.
+ The source encoding is that of the current locale. */
+ hints.ai_flags |= AI_IDN;
+#endif
+ /* The value AF_UNSPEC indicates that getaddrinfo() should return socket
+ addresses for any address family (either IPv4 or IPv6, for example)
+ that can be used with node <arg> and service "domain". */
+ hints.ai_family = AF_UNSPEC;
+
+ /* Get addresses suitable for sending datagrams. We assume that we can use the
+ same addresses for TCP connections. Settting this to zero gets each address
+ threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
+ hints.ai_socktype = SOCK_DGRAM;
+
+ /* Get address associated with this hostname */
+ ecode = getaddrinfo(arg, NULL, &hints, &sdetails->hostinfo);
+ if (ecode == 0)
+ {
+ /* The getaddrinfo() function allocated and initialized a linked list of
+ addrinfo structures, one for each network address that matches node
+ and service, subject to the restrictions imposed by our <hints>
+ above, and returns a pointer to the start of the list in <hostinfo>.
+ The items in the linked list are linked by the <ai_next> field. */
+ sdetails->valid = 1;
+ sdetails->orig_hostinfo = sdetails->hostinfo;
+ return NULL;
+ }
+ else
+ {
+ /* Lookup failed, return human readable error string */
+ if (ecode == EAI_AGAIN)
+ return _("Cannot resolve server name");
+ else
+ return _((char*)gai_strerror(ecode));
+ }
+ }
+
+ sdetails->valid = 1;
+ return NULL;
+}
+
+char *parse_server_addr(struct server_details *sdetails)
+{
+ if (sdetails->addr_type == AF_INET)
{
- addr->in.sin_port = htons(serv_port);
- addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
+ sdetails->addr->in.sin_port = htons(sdetails->serv_port);
+ sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
- source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
+ sdetails->source_addr->in.sin_len = sdetails->addr->in.sin_len = sizeof(struct sockaddr_in);
#endif
- source_addr->in.sin_addr.s_addr = INADDR_ANY;
- source_addr->in.sin_port = htons(daemon->query_port);
+ sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
+ sdetails->source_addr->in.sin_port = htons(daemon->query_port);
- if (source)
+ if (sdetails->source)
{
- if (flags)
- *flags |= SERV_HAS_SOURCE;
- source_addr->in.sin_port = htons(source_port);
- if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
+ if (sdetails->flags)
+ *sdetails->flags |= SERV_HAS_SOURCE;
+ sdetails->source_addr->in.sin_port = htons(sdetails->source_port);
+ if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 0)
{
+ if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 1)
+ {
+ sdetails->source_addr->sa.sa_family = AF_INET6;
+ /* When resolving a server IP by hostname, we can simply skip mismatching
+ server / source IP pairs. Otherwise, when an IP address is given directly,
+ this is a fatal error. */
+ if (!sdetails->orig_hostinfo)
+ return _("cannot use IPv4 server address with IPv6 source address");
+ }
+ else
+ {
#if defined(SO_BINDTODEVICE)
- if (interface_opt)
- return _("interface can only be specified once");
-
- source_addr->in.sin_addr.s_addr = INADDR_ANY;
- safe_strncpy(interface, source, IF_NAMESIZE);
+ if (sdetails->interface_opt)
+ return _("interface can only be specified once");
+
+ sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
+ safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
#else
- return _("interface binding not supported");
+ return _("interface binding not supported");
#endif
+ }
}
}
}
- else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
+ else if (sdetails->addr_type == AF_INET6)
{
- if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
+ if (sdetails->scope_id && (sdetails->scope_index = if_nametoindex(sdetails->scope_id)) == 0)
return _("bad interface name");
-
- addr->in6.sin6_port = htons(serv_port);
- addr->in6.sin6_scope_id = scope_index;
- source_addr->in6.sin6_addr = in6addr_any;
- source_addr->in6.sin6_port = htons(daemon->query_port);
- source_addr->in6.sin6_scope_id = 0;
- addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
- addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
+
+ sdetails->addr->in6.sin6_port = htons(sdetails->serv_port);
+ sdetails->addr->in6.sin6_scope_id = sdetails->scope_index;
+ sdetails->source_addr->in6.sin6_addr = in6addr_any;
+ sdetails->source_addr->in6.sin6_port = htons(daemon->query_port);
+ sdetails->source_addr->in6.sin6_scope_id = 0;
+ sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6;
+ sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
- addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
+ sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(sdetails->addr->in6);
#endif
- if (source)
+ if (sdetails->source)
{
- if (flags)
- *flags |= SERV_HAS_SOURCE;
- source_addr->in6.sin6_port = htons(source_port);
- if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
+ if (sdetails->flags)
+ *sdetails->flags |= SERV_HAS_SOURCE;
+ sdetails->source_addr->in6.sin6_port = htons(sdetails->source_port);
+ if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 0)
{
+ if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 1)
+ {
+ sdetails->source_addr->sa.sa_family = AF_INET;
+ /* When resolving a server IP by hostname, we can simply skip mismatching
+ server / source IP pairs. Otherwise, when an IP address is given directly,
+ this is a fatal error. */
+ if(!sdetails->orig_hostinfo)
+ return _("cannot use IPv6 server address with IPv4 source address");
+ }
+ else
+ {
#if defined(SO_BINDTODEVICE)
- if (interface_opt)
- return _("interface can only be specified once");
-
- source_addr->in6.sin6_addr = in6addr_any;
- safe_strncpy(interface, source, IF_NAMESIZE);
+ if (sdetails->interface_opt)
+ return _("interface can only be specified once");
+
+ sdetails->source_addr->in6.sin6_addr = in6addr_any;
+ safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
#else
- return _("interface binding not supported");
+ return _("interface binding not supported");
#endif
+ }
}
}
}
- else
+ else if (sdetails->addr_type != AF_LOCAL)
return _("bad address");
-
+
return NULL;
}
-static int domain_rev4(char *domain, struct in_addr addr, int msize)
+int parse_server_next(struct server_details *sdetails)
+{
+ /* Looping over resolved addresses? */
+ if (sdetails->hostinfo)
+ {
+ /* Get address type */
+ sdetails->addr_type = sdetails->hostinfo->ai_family;
+
+ /* Get address */
+ if (sdetails->addr_type == AF_INET)
+ memcpy(&sdetails->addr->in.sin_addr,
+ &((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr,
+ sizeof(sdetails->addr->in.sin_addr));
+ else if (sdetails->addr_type == AF_INET6)
+ memcpy(&sdetails->addr->in6.sin6_addr,
+ &((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr,
+ sizeof(sdetails->addr->in6.sin6_addr));
+
+ /* Iterate to the next available address */
+ sdetails->valid = sdetails->hostinfo->ai_next != NULL;
+ sdetails->hostinfo = sdetails->hostinfo->ai_next;
+ return 1;
+ }
+ else if (sdetails->valid)
+ {
+ /* When using an IP address, we return the address only once */
+ sdetails->valid = 0;
+ return 1;
+ }
+ /* Stop iterating here, we used all available addresses */
+ return 0;
+}
+
+static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
{
- in_addr_t a = ntohl(addr.s_addr);
+ int i, j;
+ char *string;
+ int msize;
+ u16 flags = 0;
+ char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
+ union mysockaddr serv_addr, source_addr;
+ char interface[IF_NAMESIZE+1];
+ int count = 1, rem, addrbytes, addrbits;
+ struct server_details sdetails;
+
+ memset(&sdetails, 0, sizeof(struct server_details));
+ sdetails.addr = &serv_addr;
+ sdetails.source_addr = &source_addr;
+ sdetails.interface = interface;
+ sdetails.flags = &flags;
+
+ if (!server)
+ flags = SERV_LITERAL_ADDRESS;
+ else if ((string = parse_server(server, &sdetails)))
+ return string;
+
+ if (from_file)
+ flags |= SERV_FROM_FILE;
- *domain = 0;
+ rem = size & 0x7;
+ addrbytes = (32 - size) >> 3;
+ addrbits = (32 - size) & 7;
+
+ if (size > 32 || size < 1)
+ return _("bad IPv4 prefix length");
+
+ /* Zero out last address bits according to CIDR mask */
+ ((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1);
+
+ size = size & ~0x7;
- switch (msize)
+ if (rem != 0)
+ count = 1 << (8 - rem);
+
+ for (i = 0; i < count; i++)
{
- case 32:
- domain += sprintf(domain, "%u.", a & 0xff);
- /* fall through */
- case 24:
- domain += sprintf(domain, "%d.", (a >> 8) & 0xff);
- /* fall through */
- case 16:
- domain += sprintf(domain, "%d.", (a >> 16) & 0xff);
- /* fall through */
- case 8:
- domain += sprintf(domain, "%d.", (a >> 24) & 0xff);
- break;
- default:
- return 0;
+ *domain = 0;
+ string = domain;
+ msize = size/8;
+
+ for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
+ {
+ int dig = ((unsigned char *)addr4)[j];
+
+ if (j == msize)
+ dig += i;
+
+ string += sprintf(string, "%d.", dig);
+ }
+
+ sprintf(string, "in-addr.arpa");
+
+ if (flags & SERV_LITERAL_ADDRESS)
+ {
+ if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
+ return _("error");
+ }
+ else
+ {
+ while (parse_server_next(&sdetails))
+ {
+ if ((string = parse_server_addr(&sdetails)))
+ return string;
+
+ if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
+ return _("error");
+ }
+
+ if (sdetails.orig_hostinfo)
+ freeaddrinfo(sdetails.orig_hostinfo);
+ }
}
- domain += sprintf(domain, "in-addr.arpa");
-
- return 1;
+ return NULL;
}
-static int domain_rev6(char *domain, struct in6_addr *addr, int msize)
+static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, int size)
{
- int i;
+ int i, j;
+ char *string;
+ int msize;
+ u16 flags = 0;
+ char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
+ union mysockaddr serv_addr, source_addr;
+ char interface[IF_NAMESIZE+1];
+ int count = 1, rem, addrbytes, addrbits;
+ struct server_details sdetails;
+
+ memset(&sdetails, 0, sizeof(struct server_details));
+ sdetails.addr = &serv_addr;
+ sdetails.source_addr = &source_addr;
+ sdetails.interface = interface;
+ sdetails.flags = &flags;
+
+ if (!server)
+ flags = SERV_LITERAL_ADDRESS;
+ else if ((string = parse_server(server, &sdetails)))
+ return string;
- if (msize > 128 || msize%4)
- return 0;
+ if (from_file)
+ flags |= SERV_FROM_FILE;
+
+ rem = size & 0x3;
+ addrbytes = (128 - size) >> 3;
+ addrbits = (128 - size) & 7;
+
+ if (size > 128 || size < 1)
+ return _("bad IPv6 prefix length");
+
+ /* Zero out last address bits according to CIDR mask */
+ addr6->s6_addr[15-addrbytes] &= ~((1 << addrbits) - 1);
+
+ size = size & ~0x3;
+
+ if (rem != 0)
+ count = 1 << (4 - rem);
+
+ for (i = 0; i < count; i++)
+ {
+ *domain = 0;
+ string = domain;
+ msize = size/4;
- *domain = 0;
+ for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
+ {
+ int dig = ((unsigned char *)addr6)[j>>1];
+
+ dig = j & 1 ? dig & 15 : dig >> 4;
+
+ if (j == msize)
+ dig += i;
+
+ string += sprintf(string, "%.1x.", dig);
+ }
+
+ sprintf(string, "ip6.arpa");
- for (i = msize-1; i >= 0; i -= 4)
- {
- int dig = ((unsigned char *)addr)[i>>3];
- domain += sprintf(domain, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
+ if (flags & SERV_LITERAL_ADDRESS)
+ {
+ if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
+ return _("error");
+ }
+ else
+ {
+ while (parse_server_next(&sdetails))
+ {
+ if ((string = parse_server_addr(&sdetails)))
+ return string;
+
+ if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
+ return _("error");
+ }
+
+ if (sdetails.orig_hostinfo)
+ freeaddrinfo(sdetails.orig_hostinfo);
+ }
}
- domain += sprintf(domain, "ip6.arpa");
- return 1;
+ return NULL;
}
#ifdef HAVE_DHCP
@@ -1723,6 +1998,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
}
+ case LOPT_CONF_SCRIPT: /* --conf-script */
+ {
+ char *file = opt_string_alloc(arg);
+ if (file)
+ {
+ one_file(file, LOPT_CONF_SCRIPT);
+ free(file);
+ }
+ break;
+ }
+
case '7': /* --conf-dir */
{
DIR *dir_stream;
@@ -1829,6 +2115,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->next = li;
*up = new;
}
+ else
+ free(path);
}
@@ -1995,7 +2283,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (!(name = canonicalise_opt(arg)) ||
(comma && !(target = canonicalise_opt(comma))))
- ret_err(_("bad MX name"));
+ {
+ free(name);
+ free(target);
+ ret_err(_("bad MX name"));
+ }
new = opt_malloc(sizeof(struct mx_srv_record));
new->next = daemon->mxnames;
@@ -2045,15 +2337,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
- case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
- case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
- case LOPT_HOST_INOTIFY: /* --hostsdir */
case 'H': /* --addn-hosts */
{
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
- static unsigned int hosts_index = SRC_AH;
new->fname = opt_string_alloc(arg);
- new->index = hosts_index++;
+ new->index = daemon->host_index++;
new->flags = 0;
if (option == 'H')
{
@@ -2069,21 +2357,29 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
new->next = daemon->dhcp_opts_file;
daemon->dhcp_opts_file = new;
- }
- else
- {
- new->next = daemon->dynamic_dirs;
- daemon->dynamic_dirs = new;
- if (option == LOPT_DHCP_INOTIFY)
- new->flags |= AH_DHCP_HST;
- else if (option == LOPT_DHOPT_INOTIFY)
- new->flags |= AH_DHCP_OPT;
- else if (option == LOPT_HOST_INOTIFY)
- new->flags |= AH_HOSTS;
}
break;
}
+
+ case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
+ case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
+ case LOPT_HOST_INOTIFY: /* --hostsdir */
+ {
+ struct dyndir *new = opt_malloc(sizeof(struct dyndir));
+ new->dname = opt_string_alloc(arg);
+ new->flags = 0;
+ new->next = daemon->dynamic_dirs;
+ daemon->dynamic_dirs = new;
+ if (option == LOPT_DHCP_INOTIFY)
+ new->flags |= AH_DHCP_HST;
+ else if (option == LOPT_DHOPT_INOTIFY)
+ new->flags |= AH_DHCP_OPT;
+ else if (option == LOPT_HOST_INOTIFY)
+ new->flags |= AH_HOSTS;
+
+ break;
+ }
case LOPT_AUTHSERV: /* --auth-server */
comma = split(arg);
@@ -2146,8 +2442,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
comma = split(arg);
new = opt_malloc(sizeof(struct auth_zone));
- new->domain = opt_string_alloc(arg);
- new->subnet = NULL;
+ new->domain = canonicalise_opt(arg);
+ if (!new->domain)
+ ret_err_free(_("invalid auth-zone"), new);
+ new->subnet = NULL;
new->exclude = NULL;
new->interface_names = NULL;
new->next = daemon->auth_zones;
@@ -2302,17 +2600,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
ret_err_free(_("bad prefix"), new);
}
- else if (strcmp(arg, "local") != 0 ||
- (msize != 8 && msize != 16 && msize != 24))
+ else if (strcmp(arg, "local") != 0)
ret_err_free(gen_err, new);
else
{
- char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
/* local=/xxx.yyy.zzz.in-addr.arpa/ */
- /* domain_rev4 can't fail here, msize checked above. */
- domain_rev4(domain, new->start, msize);
- add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
-
+ domain_rev4(0, NULL, &new->start, msize);
+
/* local=/<domain>/ */
/* d_raw can't failed to canonicalise here, checked above. */
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
@@ -2347,16 +2641,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
ret_err_free(_("bad prefix"), new);
}
- else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
+ else if (strcmp(arg, "local") != 0)
ret_err_free(gen_err, new);
else
{
- char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
/* generate the equivalent of
local=/xxx.yyy.zzz.ip6.arpa/ */
- domain_rev6(domain, &new->start6, msize);
- add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
-
+ domain_rev6(0, NULL, &new->start6, msize);
+
/* local=/<domain>/ */
/* d_raw can't failed to canonicalise here, checked above. */
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
@@ -2388,9 +2680,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
else if (!inet_pton(AF_INET6, arg, &new->end6))
ret_err_free(gen_err, new);
}
- else
+ else if (option == 's')
+ {
+ /* subnet from interface. */
+ new->interface = opt_string_alloc(comma);
+ new->al = NULL;
+ }
+ else
ret_err_free(gen_err, new);
-
+
if (option != 's' && prefstr)
{
if (!(new->prefix = canonicalise_opt(prefstr)) ||
@@ -2436,39 +2734,48 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_UMBRELLA: /* --umbrella */
set_option_bool(OPT_UMBRELLA);
- while (arg) {
- comma = split(arg);
- if (strstr(arg, "deviceid:")) {
- arg += 9;
- if (strlen(arg) != 16)
- ret_err(gen_err);
- for (char *p = arg; *p; p++) {
- if (!isxdigit((int)*p))
- ret_err(gen_err);
- }
- set_option_bool(OPT_UMBRELLA_DEVID);
-
- u8 *u = daemon->umbrella_device;
- char word[3];
- for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
- memcpy(word, &(arg[0]), 2);
- *u++ = strtoul(word, NULL, 16);
- }
- }
- else if (strstr(arg, "orgid:")) {
- if (!strtoul_check(arg+6, &daemon->umbrella_org)) {
- ret_err(gen_err);
- }
- }
- else if (strstr(arg, "assetid:")) {
- if (!strtoul_check(arg+8, &daemon->umbrella_asset)) {
- ret_err(gen_err);
- }
- }
- arg = comma;
- }
+ while (arg)
+ {
+ comma = split(arg);
+ if (strstr(arg, "deviceid:"))
+ {
+ char *p;
+ u8 *u = daemon->umbrella_device;
+ char word[3];
+
+ arg += 9;
+ if (strlen(arg) != 16)
+ ret_err(gen_err);
+
+ for (p = arg; *p; p++)
+ if (!isxdigit((int)*p))
+ ret_err(gen_err);
+
+ set_option_bool(OPT_UMBRELLA_DEVID);
+
+ for (i = 0; i < (int)sizeof(daemon->umbrella_device); i++, arg+=2)
+ {
+ memcpy(word, &(arg[0]), 2);
+ *u++ = strtoul(word, NULL, 16);
+ }
+ }
+ else if (strstr(arg, "orgid:"))
+ {
+ if (!strtoul_check(arg+6, &daemon->umbrella_org))
+ ret_err(gen_err);
+ }
+ else if (strstr(arg, "assetid:"))
+ {
+ if (!strtoul_check(arg+8, &daemon->umbrella_asset))
+ ret_err(gen_err);
+ }
+ else
+ ret_err(gen_err);
+
+ arg = comma;
+ }
break;
-
+
case LOPT_ADD_MAC: /* --add-mac */
if (!arg)
set_option_bool(OPT_ADD_MAC);
@@ -2635,7 +2942,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_NO_REBIND: /* --rebind-domain-ok */
{
- struct server *new;
+ struct rebind_domain *new;
unhide_metas(arg);
@@ -2644,9 +2951,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
do {
comma = split_chr(arg, '/');
- new = opt_malloc(sizeof(struct serv_local));
- new->domain = opt_string_alloc(arg);
- new->domain_len = strlen(arg);
+ new = opt_malloc(sizeof(struct rebind_domain));
+ new->domain = canonicalise_opt(arg);
new->next = daemon->no_rebind;
daemon->no_rebind = new;
arg = comma;
@@ -2659,13 +2965,20 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
{
- char *lastdomain = NULL, *domain = "";
+ char *lastdomain = NULL, *domain = "", *cur_domain;
u16 flags = 0;
char *err;
union all_addr addr;
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
+ struct server_details sdetails;
+ memset(&sdetails, 0, sizeof(struct server_details));
+ sdetails.addr = &serv_addr;
+ sdetails.source_addr = &source_addr;
+ sdetails.interface = interface;
+ sdetails.flags = &flags;
+
unhide_metas(arg);
/* split the domain args, if any and skip to the end of them. */
@@ -2698,29 +3011,55 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
else
{
- if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
+ if ((err = parse_server(arg, &sdetails)))
ret_err(err);
}
if (servers_only && option == 'S')
flags |= SERV_FROM_FILE;
-
- while (1)
+
+ cur_domain = domain;
+ while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
{
- /* server=//1.2.3.4 is special. */
- if (strlen(domain) == 0 && lastdomain)
- flags |= SERV_FOR_NODOTS;
- else
- flags &= ~SERV_FOR_NODOTS;
+ cur_domain = domain;
- if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
- ret_err(gen_err);
-
- if (!lastdomain || domain == lastdomain)
+ if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
+ ret_err(err);
+
+ /* When source is set only use DNS records of the same type and skip all others */
+ if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family)
+ continue;
+
+ while (1)
+ {
+ /* server=//1.2.3.4 is special. */
+ if (lastdomain)
+ {
+ if (strlen(cur_domain) == 0)
+ flags |= SERV_FOR_NODOTS;
+ else
+ flags &= ~SERV_FOR_NODOTS;
+
+ /* address=/#/ matches the same as without domain */
+ if (option == 'A' && cur_domain[0] == '#' && cur_domain[1] == 0)
+ cur_domain[0] = 0;
+ }
+
+ if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr))
+ ret_err(gen_err);
+
+ if (!lastdomain || cur_domain == lastdomain)
+ break;
+
+ cur_domain += strlen(cur_domain) + 1;
+ }
+
+ if (flags & SERV_LITERAL_ADDRESS)
break;
-
- domain += strlen(domain) + 1;
}
+
+ if (sdetails.orig_hostinfo)
+ freeaddrinfo(sdetails.orig_hostinfo);
break;
}
@@ -2729,57 +3068,62 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
char *string;
int size;
- u16 flags = 0;
- char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
struct in_addr addr4;
struct in6_addr addr6;
- union mysockaddr serv_addr, source_addr;
- char interface[IF_NAMESIZE+1];
-
+
unhide_metas(arg);
if (!arg)
ret_err(gen_err);
comma=split(arg);
-
- if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
- ret_err(gen_err);
+ if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
+ size = -1;
+
if (inet_pton(AF_INET, arg, &addr4))
{
- if (!domain_rev4(domain, addr4, size))
- ret_err(_("bad IPv4 prefix"));
+ if (size == -1)
+ size = 32;
+
+ if ((string = domain_rev4(servers_only, comma, &addr4, size)))
+ ret_err(string);
}
else if (inet_pton(AF_INET6, arg, &addr6))
{
- if (!domain_rev6(domain, &addr6, size))
- ret_err(_("bad IPv6 prefix"));
+ if (size == -1)
+ size = 128;
+
+ if ((string = domain_rev6(servers_only, comma, &addr6, size)))
+ ret_err(string);
}
else
ret_err(gen_err);
- if (!comma)
- flags |= SERV_LITERAL_ADDRESS;
- else if ((string = parse_server(comma, &serv_addr, &source_addr, interface, &flags)))
- ret_err(string);
-
- if (servers_only)
- flags |= SERV_FROM_FILE;
-
- if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
- ret_err(gen_err);
-
break;
}
case LOPT_IPSET: /* --ipset */
+ case LOPT_NFTSET: /* --nftset */
#ifndef HAVE_IPSET
- ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
- break;
-#else
+ if (option == LOPT_IPSET)
+ {
+ ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
+ break;
+ }
+#endif
+#ifndef HAVE_NFTSET
+ if (option == LOPT_NFTSET)
+ {
+ ret_err(_("recompile with HAVE_NFTSET defined to enable nftset directives"));
+ break;
+ }
+#endif
+
{
struct ipsets ipsets_head;
struct ipsets *ipsets = &ipsets_head;
+ struct ipsets **daemon_sets =
+ (option == LOPT_IPSET) ? &daemon->ipsets : &daemon->nftsets;
int size;
char *end;
char **sets, **sets_pos;
@@ -2824,19 +3168,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
sets = sets_pos = opt_malloc(sizeof(char *) * size);
do {
+ char *p;
end = split(arg);
- *sets_pos++ = opt_string_alloc(arg);
+ *sets_pos = opt_string_alloc(arg);
+ /* Use '#' to delimit table and set */
+ if (option == LOPT_NFTSET)
+ while ((p = strchr(*sets_pos, '#')))
+ *p = ' ';
+ sets_pos++;
arg = end;
} while (end);
*sets_pos = 0;
for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
ipsets->next->sets = sets;
- ipsets->next = daemon->ipsets;
- daemon->ipsets = ipsets_head.next;
+ ipsets->next = *daemon_sets;
+ *daemon_sets = ipsets_head.next;
break;
}
-#endif
case LOPT_CMARK_ALST_EN: /* --connmark-allowlist-enable */
#ifndef HAVE_CONNTRACK
@@ -3044,6 +3393,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (daemon->query_port == 0)
daemon->osport = 1;
break;
+
+ case LOPT_RANDPORT_LIM: /* --port-limit */
+ if (!atoi_check(arg, &daemon->randport_limit) || (daemon->randport_limit < 1))
+ ret_err(gen_err);
+ break;
case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */
@@ -3079,7 +3433,30 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->local_ttl = (unsigned long)ttl;
break;
}
+
+ case LOPT_FAST_RETRY:
+ daemon->fast_retry_timeout = TIMEOUT;
+ if (!arg)
+ daemon->fast_retry_time = DEFAULT_FAST_RETRY;
+ else
+ {
+ int retry;
+
+ comma = split(arg);
+ if (!atoi_check(arg, &retry) || retry < 50)
+ ret_err(gen_err);
+ daemon->fast_retry_time = retry;
+
+ if (comma)
+ {
+ if (!atoi_check(comma, &retry))
+ ret_err(gen_err);
+ daemon->fast_retry_timeout = retry/1000;
+ }
+ }
+ break;
+
#ifdef HAVE_DHCP
case 'X': /* --dhcp-lease-max */
if (!atoi_check(arg, &daemon->dhcp_max))
@@ -3616,6 +3993,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
inet_ntop(AF_INET, &in, daemon->addrbuff, ADDRSTRLEN);
sprintf(errstr, _("duplicate dhcp-host IP address %s"),
daemon->addrbuff);
+ dhcp_config_free(new);
return 0;
}
}
@@ -3779,16 +4157,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_NAME_MATCH: /* --dhcp-name-match */
{
- struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
- struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
+ struct dhcp_match_name *new;
ssize_t len;
if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
ret_err(gen_err);
+ new = opt_malloc(sizeof(struct dhcp_match_name));
new->wildcard = 0;
- new->netid = id;
- id->net = opt_string_alloc(set_prefix(arg));
+ new->netid = opt_malloc(sizeof(struct dhcp_netid));
+ new->netid->net = opt_string_alloc(set_prefix(arg));
if (comma[len-1] == '*')
{
@@ -3992,6 +4370,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
}
+ dhcp_netid_free(new->netid);
+ free(new);
ret_err(gen_err);
}
@@ -4026,7 +4406,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_SUBSCR: /* --dhcp-subscrid */
{
unsigned char *p;
- int dig = 0;
+ int dig, colon;
struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
if (!(comma = split(arg)))
@@ -4050,13 +4430,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
else
comma = arg;
- for (p = (unsigned char *)comma; *p; p++)
+ for (dig = 0, colon = 0, p = (unsigned char *)comma; *p; p++)
if (isxdigit(*p))
dig = 1;
- else if (*p != ':')
+ else if (*p == ':')
+ colon = 1;
+ else
break;
+
unhide_metas(comma);
- if (option == 'U' || option == 'j' || *p || !dig)
+ if (option == 'U' || option == 'j' || *p || !dig || !colon)
{
new->len = strlen(comma);
new->data = opt_malloc(new->len);
@@ -4179,26 +4562,66 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
}
break;
-
+
case LOPT_RELAY: /* --dhcp-relay */
{
struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
- comma = split(arg);
- new->interface = opt_string_alloc(split(comma));
+ char *two = split(arg);
+ char *three = split(two);
+
new->iface_index = 0;
- if (comma && inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
+
+ if (two)
{
- new->next = daemon->relay4;
- daemon->relay4 = new;
- }
+ if (inet_pton(AF_INET, arg, &new->local))
+ {
+ char *hash = split_chr(two, '#');
+
+ if (!hash || !atoi_check16(hash, &new->port))
+ new->port = DHCP_SERVER_PORT;
+
+ if (!inet_pton(AF_INET, two, &new->server))
+ {
+ new->server.addr4.s_addr = 0;
+
+ /* Fail for three arg version where there are not two addresses.
+ Also fail when broadcasting to wildcard address. */
+ if (three || strchr(two, '*'))
+ two = NULL;
+ else
+ three = two;
+ }
+
+ new->next = daemon->relay4;
+ daemon->relay4 = new;
+ }
#ifdef HAVE_DHCP6
- else if (comma && inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
- {
- new->next = daemon->relay6;
- daemon->relay6 = new;
- }
+ else if (inet_pton(AF_INET6, arg, &new->local))
+ {
+ char *hash = split_chr(two, '#');
+
+ if (!hash || !atoi_check16(hash, &new->port))
+ new->port = DHCPV6_SERVER_PORT;
+
+ if (!inet_pton(AF_INET6, two, &new->server))
+ {
+ inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6);
+ /* Fail for three arg version where there are not two addresses.
+ Also fail when multicasting to wildcard address. */
+ if (three || strchr(two, '*'))
+ two = NULL;
+ else
+ three = two;
+ }
+ new->next = daemon->relay6;
+ daemon->relay6 = new;
+ }
#endif
- else
+
+ new->interface = opt_string_alloc(three);
+ }
+
+ if (!two)
{
free(new->interface);
ret_err_free(_("Bad dhcp-relay"), new);
@@ -4367,7 +4790,7 @@ err:
case LOPT_CNAME: /* --cname */
{
struct cname *new;
- char *alias, *target, *last, *pen;
+ char *alias, *target=NULL, *last, *pen;
int ttl = -1;
for (last = pen = NULL, comma = arg; comma; comma = split(comma))
@@ -4382,13 +4805,13 @@ err:
if (pen != arg && atoi_check(last, &ttl))
last = pen;
- target = canonicalise_opt(last);
-
while (arg != last)
{
int arglen = strlen(arg);
alias = canonicalise_opt(arg);
+ if (!target)
+ target = canonicalise_opt(last);
if (!alias || !target)
{
free(target);
@@ -4691,7 +5114,7 @@ err:
struct name_list *nl;
if (!canon)
{
- struct name_list *tmp = new->names, *next;
+ struct name_list *tmp, *next;
for (tmp = new->names; tmp; tmp = next)
{
next = tmp->next;
@@ -4728,6 +5151,24 @@ err:
break;
}
+ case LOPT_STALE_CACHE:
+ {
+ int max_expiry = STALE_CACHE_EXPIRY;
+ if (arg)
+ {
+ /* Don't accept negative TTLs here, they'd have the counter-intuitive
+ side-effect of evicting cache records before they expire */
+ if (!atoi_check(arg, &max_expiry) || max_expiry < 0)
+ ret_err(gen_err);
+ /* Store "serve expired forever" as -1 internally, the option isn't
+ active for daemon->cache_max_expiry == 0 */
+ if (max_expiry == 0)
+ max_expiry = -1;
+ }
+ daemon->cache_max_expiry = max_expiry;
+ break;
+ }
+
#ifdef HAVE_DNSSEC
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
daemon->timestamp_file = opt_string_alloc(arg);
@@ -4809,7 +5250,7 @@ err:
return 1;
}
-static void read_file(char *file, FILE *f, int hard_opt)
+static void read_file(char *file, FILE *f, int hard_opt, int from_script)
{
volatile int lineno = 0;
char *buff = daemon->namebuff;
@@ -4817,10 +5258,12 @@ static void read_file(char *file, FILE *f, int hard_opt)
while (fgets(buff, MAXDNAME, f))
{
int white, i;
- volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
+ volatile int option;
char *errmess, *p, *arg, *start;
size_t len;
+ option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
+
/* Memory allocation failure longjmps here if mem_recover == 1 */
if (option != 0 || hard_opt == LOPT_REV_SERV)
{
@@ -4828,7 +5271,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
continue;
mem_recover = 1;
}
-
+
arg = NULL;
lineno++;
errmess = NULL;
@@ -4934,7 +5377,11 @@ static void read_file(char *file, FILE *f, int hard_opt)
if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
{
- sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
+ if (from_script)
+ sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" in output from %s"), file);
+ else
+ sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
+
if (hard_opt != 0)
my_syslog(LOG_ERR, "%s", daemon->namebuff);
else
@@ -4943,7 +5390,6 @@ static void read_file(char *file, FILE *f, int hard_opt)
}
mem_recover = 0;
- fclose(f);
}
#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
@@ -4963,7 +5409,7 @@ int option_read_dynfile(char *file, int flags)
static int one_file(char *file, int hard_opt)
{
FILE *f;
- int nofile_ok = 0;
+ int nofile_ok = 0, do_popen = 0;
static int read_stdin = 0;
static struct fileread {
dev_t dev;
@@ -4971,14 +5417,20 @@ static int one_file(char *file, int hard_opt)
struct fileread *next;
} *filesread = NULL;
- if (hard_opt == '7')
+ if (hard_opt == LOPT_CONF_OPT)
{
/* default conf-file reading */
hard_opt = 0;
nofile_ok = 1;
}
- if (hard_opt == 0 && strcmp(file, "-") == 0)
+ if (hard_opt == LOPT_CONF_SCRIPT)
+ {
+ hard_opt = 0;
+ do_popen = 1;
+ }
+
+ if (hard_opt == 0 && !do_popen && strcmp(file, "-") == 0)
{
if (read_stdin == 1)
return 1;
@@ -5005,8 +5457,13 @@ static int one_file(char *file, int hard_opt)
r->dev = statbuf.st_dev;
r->ino = statbuf.st_ino;
}
-
- if (!(f = fopen(file, "r")))
+
+ if (do_popen)
+ {
+ if (!(f = popen(file, "r")))
+ die(_("cannot execute %s: %s"), file, EC_FILE);
+ }
+ else if (!(f = fopen(file, "r")))
{
if (errno == ENOENT && nofile_ok)
return 1; /* No conffile, all done. */
@@ -5024,7 +5481,21 @@ static int one_file(char *file, int hard_opt)
}
}
- read_file(file, f, hard_opt);
+ read_file(file, f, hard_opt, do_popen);
+
+ if (do_popen)
+ {
+ int rc;
+
+ if ((rc = pclose(f)) == -1)
+ die(_("error executing %s: %s"), file, EC_MISC);
+
+ if (rc != 0)
+ die(_("%s returns non-zero error code"), file, rc+10);
+ }
+ else
+ fclose(f);
+
return 1;
}
@@ -5164,7 +5635,8 @@ void read_servers_file(void)
}
mark_servers(SERV_FROM_FILE);
- read_file(daemon->servers_file, f, LOPT_REV_SERV);
+ read_file(daemon->servers_file, f, LOPT_REV_SERV, 0);
+ fclose(f);
cleanup_servers();
check_servers(0);
}
@@ -5282,6 +5754,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_refresh = SOA_REFRESH;
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
+ daemon->randport_limit = 1;
+ daemon->host_index = SRC_AH;
#ifndef NO_ID
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
@@ -5297,7 +5771,10 @@ void read_opts(int argc, char **argv, char *compile_opts)
#endif
add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
#endif
-
+
+ /* See comment above make_servers(). Optimises server-read code. */
+ mark_servers(0);
+
while (1)
{
#ifdef HAVE_GETOPT_LONG
@@ -5390,7 +5867,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
free(conffile);
}
else
- one_file(CONFFILE, '7');
+ one_file(CONFFILE, LOPT_CONF_OPT);
/* port might not be known when the address is parsed - fill in here */
if (daemon->servers)