Compare commits

...

29 Commits

Author SHA1 Message Date
Dominik DL6ER
e7ccd95c04 Add EDE return when no matching key found. 2021-07-09 22:12:42 +01:00
Simon Kelley
719f79a8fd Subtle change to priority of --server types.
Make --server=/example.com/1.2.3.4 take priority over
--server=/example.com/ (AKA --address=/example.com/ or --local=/example.com/)

This corrects a regression in the domain-match rewrite, and appears
to be the more useful order. It got swapped because I didn't consider
that both could usefully co-exist.
2021-07-06 21:02:35 +01:00
Kevin Darbyshire-Bryant
96f6444958 Fix thinko in a92c6d77dc 2021-07-05 21:00:47 +01:00
Simon Kelley
df25f204ba Fix logical error in d0ae3f5a4d
The code which checked for a possible local answer to a domain,
like --address=/example.com/1.2.3.4 could return false positives,
causing upstream NXDOMAIN replies to be rewritten as NOERROR.

Thanks to Dominik DL6ER for the bug report and analysis.
2021-07-05 20:56:11 +01:00
Simon Kelley
8acdc3ede7 Add calls to dump internally generated answers for dumpmask=0x0002 2021-07-04 23:12:14 +01:00
Simon Kelley
857b445522 Fix order of calls to resize-packet() and add_pseudoheader().
Avoids malformed replies with EDE in certain circumstances.
2021-07-04 22:38:26 +01:00
Simon Kelley
5bcca1219a Support IPv6 in --bogus-nxdomian and --ignore-address 2021-07-04 22:27:00 +01:00
Simon Kelley
4558c26fcd Make --rebind-localhost-ok apply to :: and 0.0.0.0
Also make the definition of local IPv6 addresses
the same for --bogus-priv and rebind protection.
2021-07-04 21:09:10 +01:00
Simon Kelley
a92c6d77dc Tidy domain parsing, make --server=/*/1.2.3.4 equivalent to --server=1.2.3.4 2021-07-03 12:56:50 +01:00
Petr Menšík
0c95a5ff53 Modify and propagate changed lease.
If hostname is reset on existing lease, propagate such change to leases
file and script.
2021-07-02 16:58:48 +01:00
Simon Kelley
cb6d06bb54 Rationalise SERV_MARK use. 2021-07-01 23:00:22 +01:00
Simon Kelley
3ef955c85a Fix oversight in build_server_array().
The index computation went awry when servers are disabled
by the loop-detection system.

Thanks to Xingcong Li for spotting this.
2021-07-01 22:40:31 +01:00
Simon Kelley
5e95c16c32 Allow wildcards in domain patterns.
Domain patterns in --address, --server and --local have, for many years,
matched complete labels only, so
--server=/google.com/1.2.3.4
will apply to google.com and www.google.com but NOT supergoogle.com

This commit introduces an optional '*' at the LHS of the domain string which
changes this behaviour so as to include substring matches _within_ labels. So,
--server=/*google.com/1.2.3.4
applies to google.com, www.google.com AND supergoogle.com.
2021-07-01 22:28:24 +01:00
Simon Kelley
4205e2ebcf Reuse workspace bit in struct server ->flags. 2021-07-01 13:22:10 +01:00
Etan Kissling
9d806c51c2 Fix ipset support.
This fixes a problem with ipset processing that got recently introduced
when `extract_request` filtering was tightened. During the recent change
an incorrect assumption was made that `extract_request` was only called
for requests but with ipset it is also called when processing responses.

The fix ensures that the new filters only apply to requests (QR=0 @ hdr)

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-06-30 12:31:51 +01:00
Simon Kelley
a38bb31727 Revert "Treat failure of ubus_add_object() in ubus_init() as retry-able."
This reverts commit 8a1ef367e2.
2021-06-30 12:30:15 +01:00
Simon Kelley
8a1ef367e2 Treat failure of ubus_add_object() in ubus_init() as retry-able.
3c93e8eb41 regularised ubus_init()
by avoiding logging calls (it can be called before logging is up)
but it instead returned any error from ubus_add_object() which
made such an error fatal. It turns out this is awkward, so this
patch returns NULL always, so that the event-loop will continue
attemping to connect to ubus forever.

This is not necessarily optimal either, and should be looked at
by a UBUS grown-up, but it does solve the immediate problem.
2021-06-27 21:32:10 +01:00
Simon Kelley
1291865c92 Fix trivial breakage of DBUS done by 85bc7534da 2021-06-27 21:16:30 +01:00
Simon Kelley
a9ebbee7b6 Compiler warnings. 2021-06-27 21:03:52 +01:00
Simon Kelley
06df5ad7d0 Tidy up interface to dbus and ubus modules.
Consistently treat a non-NULL return from [ud]bus-init() as a fatal error:
either die() if still starting, or log an error and disable
the relevant module if dnsmasq has already started.

Also rationalise calls to set and check listeners depending on
configuration.
2021-06-27 20:56:58 +01:00
Simon Kelley
66b863c989 Fix problem with re-allocation of serverarray. 2021-06-26 21:13:41 +01:00
Simon Kelley
c9efe8e5e1 Rationalise domain parsing for --rev-server and --domain. 2021-06-26 18:51:05 +01:00
Simon Kelley
d515223bb5 Don't re-use datastructures for --address and --local.
Doing so makes the loading process quadratic, which is a problem
when there are a large number.
2021-06-26 01:00:37 +01:00
Simon Kelley
b908f4334b Merge branch 'extended-error' 2021-06-26 00:38:55 +01:00
Simon Kelley
6261aba026 Initial implementation of RFC-8914 extended DNS errors. 2021-06-26 00:38:01 +01:00
Simon Kelley
85bc7534da Rationalise --server parsing and datastructure building.
Use add_update_server for everything.
2021-06-25 22:09:08 +01:00
Simon Kelley
1b30fd1732 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2021-06-25 10:46:53 +01:00
Simon Kelley
8c9196bff8 Correct domain search algorithm.
For reasons unknown, I (srk) assumed that the orginal
substring domain matching algorithm was still in use,
where example.com would match eg. sexample.com

In fact the far more sensible label-based match, where
example.com (or .example.com) matches example.com and
www.example.com, but not sexample.com, has been in use
since release 2.22. This commit implements the 2.22 to 2.85
behaviour in the new domain-search code.

Thanks to Kevin Darbyshire-Bryant for spotting my mistake.
2021-06-25 10:46:06 +01:00
Simon Kelley
11c52d032b Initial changes for extended DNS error codes. 2021-06-21 17:37:46 +01:00
16 changed files with 917 additions and 632 deletions

View File

@@ -327,8 +327,8 @@ are re-written. So
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
.TP
.B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
Transform replies which contain the IP specified address or subnet into "No such
domain" replies. This is intended to counteract a devious move made by
Transform replies which contain the specified address or subnet into "No such
domain" replies. IPv4 and IPv6 are supported. This is intended to counteract a devious move made by
Verisign in September 2003 when they started returning the address of
an advertising web page in response to queries for unregistered names,
instead of the correct NXDOMAIN response. This option tells dnsmasq to
@@ -336,7 +336,7 @@ fake the correct response when it sees this behaviour. As at Sept 2003
the IP address being returned by Verisign is 64.94.110.11
.TP
.B --ignore-address=<ipaddr>[/prefix]
Ignore replies to A-record queries which include the specified address or subnet.
Ignore replies to A or AAAA queries which include the specified address or subnet.
No error is generated, dnsmasq simply continues to listen for another reply.
This is useful to defeat blocking strategies which rely on quickly supplying a
forged answer to a DNS request for certain domain, before the correct answer can arrive.
@@ -460,13 +460,22 @@ repeated domain or ipaddr parts as required.
More specific domains take precedence over less specific domains, so:
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/2.3.4.5
will send queries for *.google.com to 1.2.3.4, except *www.google.com,
which will go to 2.3.4.5
will send queries for google.com and gmail.google.com to 1.2.3.4, but www.google.com
will go to 2.3.4.5
Matching of domains is normally done on complete labels, so /google.com/ matches google.com and www.google.com
but NOT supergoogle.com. This can be overridden with a * at the start of a pattern only: /*google.com/
will match google.com and www.google.com AND supergoogle.com. The non-wildcard form has priority, so
if /google.com/ and /*google.com/ are both specified then google.com and www.google.com will match /google.com/
and /*google.com/ will only match supergoogle.com.
For historical reasons, the pattern /.google.com/ is equivalent to /google.com/ if you wish to match any subdomain
of google.com but NOT google.com itself, use /*.google.com/
The special server address '#' means, "use the standard servers", so
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/#
will send queries for *.google.com to 1.2.3.4, except *www.google.com which will
will send queries for google.com and its subdomains to 1.2.3.4, except www.google.com (and its subdomains) which will
be forwarded as usual.
Also permitted is a -S

View File

@@ -1602,18 +1602,18 @@ int cache_make_stat(struct txt_record *t)
case TXT_STAT_SERVERS:
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED;
serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & SERV_COUNTED))
if (!(serv->flags & SERV_MARK))
{
char *new, *lenp;
int port, newlen, bytes_avail, bytes_needed;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_COUNTED) && sockaddr_isequal(&serv->addr, &serv1->addr))
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_COUNTED;
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
}
@@ -1641,6 +1641,7 @@ int cache_make_stat(struct txt_record *t)
}
t->txt = (unsigned char *)buff;
t->len = p - buff;
return 1;
}
@@ -1683,24 +1684,24 @@ void dump_cache(time_t now)
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED;
serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & SERV_COUNTED))
if (!(serv->flags & SERV_MARK))
{
int port;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_COUNTED) && sockaddr_isequal(&serv->addr, &serv1->addr))
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_COUNTED;
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
}
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
}
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
{
struct crec *cache ;
@@ -1861,10 +1862,44 @@ char *querystr(char *desc, unsigned short type)
return buff ? buff : "";
}
static char *edestr(int ede)
{
switch (ede)
{
case EDE_OTHER: return "other";
case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm";
case EDE_USUPDS: return "unsupported DS digest";
case EDE_STALE: return "stale answer";
case EDE_FORGED: return "forged";
case EDE_DNSSEC_IND: return "DNSSEC indeterminate";
case EDE_DNSSEC_BOGUS: return "DNSSEC bogus";
case EDE_SIG_EXP: return "DNSSEC signature expired";
case EDE_SIG_NYV: return "DNSSEC sig not yet valid";
case EDE_NO_DNSKEY: return "DNSKEY missing";
case EDE_NO_RRSIG: return "RRSIG missing";
case EDE_NO_ZONEKEY: return "no zone key bit set";
case EDE_NO_NSEC: return "NSEC(3) missing";
case EDE_CACHED_ERR: return "cached error";
case EDE_NOT_READY: return "not ready";
case EDE_BLOCKED: return "blocked";
case EDE_CENSORED: return "censored";
case EDE_FILTERED: return "filtered";
case EDE_PROHIBITED: return "prohibited";
case EDE_STALE_NXD: return "stale NXDOMAIN";
case EDE_NOT_AUTH: return "not authoritative";
case EDE_NOT_SUP: return "not supported";
case EDE_NO_AUTH: return "no reachable authority";
case EDE_NETERR: return "network error";
case EDE_INVALID_DATA: return "invalid data";
default: return "unknown";
}
}
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
{
char *source, *dest = daemon->addrbuff;
char *verb = "is";
char *extra = "";
if (!option_bool(OPT_LOG))
return;
@@ -1887,6 +1922,12 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
dest = "not implemented";
else
sprintf(daemon->addrbuff, "%u", rcode);
if (addr->log.ede != EDE_UNSET)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
}
else
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
@@ -1932,7 +1973,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_SECSTAT)
source = "validation";
{
if (addr && addr->log.ede != EDE_UNSET)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
source = "validation";
dest = arg;
}
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_SERVER)
@@ -1966,11 +2015,11 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
if (option_bool(OPT_EXTRALOG))
{
if (flags & F_NOEXTRA)
my_syslog(LOG_INFO, "%u %s %s %s %s", daemon->log_display_id, source, name, verb, dest);
my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
else
{
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
}
}
else

View File

@@ -215,7 +215,7 @@ static void dbus_read_servers(DBusMessage *message)
domain = NULL;
if (!skip)
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain, NULL);
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
}
@@ -276,7 +276,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
{
const char *str = NULL;
union mysockaddr addr, source_addr;
int flags = 0;
u16 flags = 0;
char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL;
@@ -361,10 +361,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strcpy(str_addr, str);
}
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
memset(&interface, 0, sizeof(interface));
/* parse the IP address */
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
{
@@ -392,7 +388,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else
p = NULL;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
} while ((str_domain = p));
}
else
@@ -407,7 +403,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
@@ -416,7 +412,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
}
cleanup_servers();
if (dup)
free(dup);
@@ -715,7 +711,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
if (new_servers)
{
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
check_servers();
check_servers(0);
if (option_bool(OPT_RELOAD))
clear_cache = 1;
}

View File

@@ -80,10 +80,42 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
#define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
/* RFC-8914 extended errors, negative values are our definitions */
#define EDE_UNSET -1 /* No extended DNS error available */
#define EDE_OTHER 0 /* Other */
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
#define EDE_USUPDS 2 /* Unsupported DS Digest */
#define EDE_STALE 3 /* Stale answer */
#define EDE_FORGED 4 /* Forged answer */
#define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */
#define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */
#define EDE_SIG_EXP 7 /* Signature Expired */
#define EDE_SIG_NYV 8 /* Signature Not Yet Valid */
#define EDE_NO_DNSKEY 9 /* DNSKEY missing */
#define EDE_NO_RRSIG 10 /* RRSIGs missing */
#define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */
#define EDE_NO_NSEC 12 /* NSEC Missing */
#define EDE_CACHED_ERR 13 /* Cached Error */
#define EDE_NOT_READY 14 /* Not Ready */
#define EDE_BLOCKED 15 /* Blocked */
#define EDE_CENSORED 16 /* Censored */
#define EDE_FILTERED 17 /* Filtered */
#define EDE_PROHIBITED 18 /* Prohibited */
#define EDE_STALE_NXD 19 /* Stale NXDOMAIN */
#define EDE_NOT_AUTH 20 /* Not Authoritative */
#define EDE_NOT_SUP 21 /* Not Supported */
#define EDE_NO_AUTH 22 /* No Reachable Authority */
#define EDE_NETERR 23 /* Network error */
#define EDE_INVALID_DATA 24 /* Invalid Data */
struct dns_header {
u16 id;
u8 hb3,hb4;

View File

@@ -437,8 +437,6 @@ int main (int argc, char **argv)
#ifdef HAVE_DBUS
{
char *err;
daemon->dbus = NULL;
daemon->watches = NULL;
if ((err = dbus_init()))
die(_("DBus error: %s"), err, EC_MISC);
}
@@ -450,7 +448,6 @@ int main (int argc, char **argv)
#ifdef HAVE_UBUS
{
char *err;
daemon->ubus = NULL;
if ((err = ubus_init()))
die(_("UBus error: %s"), err, EC_MISC);
}
@@ -1034,7 +1031,7 @@ int main (int argc, char **argv)
close(err_pipe[1]);
if (daemon->port != 0)
check_servers();
check_servers(0);
pid = getpid();
@@ -1065,14 +1062,15 @@ int main (int argc, char **argv)
set_dns_listeners();
#ifdef HAVE_DBUS
set_dbus_listeners();
if (option_bool(OPT_DBUS))
set_dbus_listeners();
#endif
#ifdef HAVE_UBUS
if (option_bool(OPT_UBUS))
set_ubus_listeners();
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4)
{
@@ -1192,28 +1190,44 @@ int main (int argc, char **argv)
#ifdef HAVE_DBUS
/* if we didn't create a DBus connection, retry now. */
if (option_bool(OPT_DBUS) && !daemon->dbus)
if (option_bool(OPT_DBUS))
{
char *err;
if ((err = dbus_init()))
my_syslog(LOG_WARNING, _("DBus error: %s"), err);
if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus"));
if (!daemon->dbus)
{
char *err = dbus_init();
if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus"));
else if (err)
{
my_syslog(LOG_ERR, _("DBus error: %s"), err);
reset_option_bool(OPT_DBUS); /* fatal error, stop trying. */
}
}
check_dbus_listeners();
}
check_dbus_listeners();
#endif
#ifdef HAVE_UBUS
/* if we didn't create a UBus connection, retry now. */
if (option_bool(OPT_UBUS) && !daemon->ubus)
{
char *err;
if ((err = ubus_init()))
my_syslog(LOG_WARNING, _("UBus error: %s"), err);
if (daemon->ubus)
my_syslog(LOG_INFO, _("connected to system UBus"));
if (option_bool(OPT_UBUS))
{
if (!daemon->ubus)
{
char *err = ubus_init();
if (daemon->ubus)
my_syslog(LOG_INFO, _("connected to system UBus"));
else if (err)
{
my_syslog(LOG_ERR, _("UBus error: %s"), err);
reset_option_bool(OPT_UBUS); /* fatal error, stop trying. */
}
}
check_ubus_listeners();
}
check_ubus_listeners();
#endif
check_dns_listeners(now);
@@ -1446,7 +1460,7 @@ static void async_event(int pipe, time_t now)
}
if (check)
check_servers();
check_servers(0);
}
#ifdef HAVE_DHCP
@@ -1645,7 +1659,7 @@ static void poll_resolv(int force, int do_reload, time_t now)
{
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers();
check_servers(0);
if (option_bool(OPT_RELOAD) && do_reload)
clear_cache_and_reload(now);
}

View File

@@ -323,12 +323,14 @@ union all_addr {
/* for log_query */
struct {
unsigned short keytag, algo, digest, rcode;
int ede;
} log;
};
struct bogus_addr {
struct in_addr addr, mask;
int is6, prefix;
union all_addr addr;
struct bogus_addr *next;
};
@@ -535,8 +537,8 @@ union mysockaddr {
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
#define SERV_MARK 256 /* for mark-and-delete */
#define SERV_COUNTED 512 /* workspace for log code */
#define SERV_MARK 256 /* for mark-and-delete and log code */
#define SERV_WILDCARD 512 /* domain has leading '*' */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
#define SERV_FROM_FILE 4096 /* read from --servers-file */
@@ -686,17 +688,28 @@ struct hostsfile {
#define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080
/* DNSSEC status values. */
#define STAT_SECURE 1
#define STAT_INSECURE 2
#define STAT_BOGUS 3
#define STAT_NEED_DS 4
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_OK 8
#define STAT_ABANDONED 9
#define STAT_SECURE 0x10000
#define STAT_INSECURE 0x20000
#define STAT_BOGUS 0x30000
#define STAT_NEED_DS 0x40000
#define STAT_NEED_KEY 0x50000
#define STAT_TRUNCATED 0x60000
#define STAT_SECURE_WILDCARD 0x70000
#define STAT_OK 0x80000
#define STAT_ABANDONED 0x90000
#define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */
#define DNSSEC_FAIL_EXP 0x0002 /* key expired */
#define DNSSEC_FAIL_INDET 0x0004 /* indetermined */
#define DNSSEC_FAIL_NOKEYSUP 0x0008 /* no supported key algo. */
#define DNSSEC_FAIL_NOSIG 0x0010 /* No RRsigs */
#define DNSSEC_FAIL_NOZONE 0x0020 /* No Zone bit set */
#define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */
#define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */
#define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
@@ -1091,7 +1104,8 @@ extern struct daemon {
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
struct bogus_addr *bogus_addr, *ignore_addr;
struct server *servers, *local_domains, **serverarray, *no_rebind;
int serverarraysz;
int server_has_wildcard;
int serverarraysz, serverarrayhwm;
struct ipsets *ipsets;
u32 allowlist_mask;
struct allowlist *allowlists;
@@ -1280,7 +1294,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen);
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep);
void setup_reply(struct dns_header *header, unsigned int flags);
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, char **ipsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored);
@@ -1311,6 +1325,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif
/* dnssec.c */
#ifdef HAVE_DNSSEC
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
@@ -1319,6 +1334,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
int setup_timestamp(void);
int errflags_to_ede(int status);
#endif
/* hash_questions.c */
void hash_questions_init(void);
@@ -1352,6 +1369,7 @@ int hostname_issubdomain(char *a, char *b);
time_t dnsmasq_time(void);
int netmask_length(struct in_addr mask);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
u64 addr6part(struct in6_addr *addr);
void setaddr6part(struct in6_addr *addr, u64 host);
@@ -1414,14 +1432,7 @@ int indextoname(int fd, int index, char *name);
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
void mark_servers(int flag);
void cleanup_servers(void);
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain);
void check_servers(void);
void check_servers(int no_loop_call);
int enumerate_interfaces(int reset);
void create_wildcard_listeners(void);
void create_bound_listeners(int dienow);
@@ -1749,9 +1760,16 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
int filter_servers(int seed, int flags, int *lowout, int *highout);
int is_local_answer(time_t now, int first, char *name);
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
char *name, char *limit, int first, int last);
char *name, char *limit, int first, int last, int ede);
int server_samegroup(struct server *a, struct server *b);
#ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
#endif
void mark_servers(int flag);
void cleanup_servers(void);
int add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain,
union all_addr *local_addr);

View File

@@ -215,14 +215,6 @@ static int is_check_date(unsigned long curtime)
return !daemon->dnssec_no_time_check;
}
/* Check whether today/now is between date_start and date_end */
static int check_date_range(unsigned long curtime, u32 date_start, u32 date_end)
{
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
return serial_compare_32(curtime, date_start) == SERIAL_GT
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
}
/* Return bytes of canonicalised rrdata one by one.
Init state->ip with the RR, and state->end with the end of same.
Init state->op to NULL.
@@ -534,7 +526,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
struct crec *crecp = NULL;
u16 *rr_desc = rrfilter_desc(type);
u32 sig_expiration, sig_inception;
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
unsigned long curtime = time(0);
int time_check = is_check_date(curtime);
@@ -557,6 +550,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
void *ctx;
char *name_start;
u32 nsigttl, ttl, orig_ttl;
failflags &= ~DNSSEC_FAIL_NOSIG;
p = sigs[j];
GETLONG(ttl, p);
@@ -574,12 +569,31 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (!extract_name(header, plen, &p, keyname, 1, 0))
return STAT_BOGUS;
if ((time_check && !check_date_range(curtime, sig_inception, sig_expiration)) ||
labels > name_labels ||
!(hash = hash_find(algo_digest_name(algo))) ||
if (!time_check)
failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
else
{
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
if (serial_compare_32(curtime, sig_inception) == SERIAL_LT)
continue;
else
failflags &= ~DNSSEC_FAIL_NYV;
if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT)
continue;
else
failflags &= ~DNSSEC_FAIL_EXP;
}
if (!(hash = hash_find(algo_digest_name(algo))))
continue;
else
failflags &= ~DNSSEC_FAIL_NOKEYSUP;
if (labels > name_labels ||
!hash_init(hash, &ctx, &digest))
continue;
/* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
return STAT_NEED_KEY;
@@ -730,7 +744,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
}
}
return STAT_BOGUS;
/* If we reach this point, no verifying key was found */
return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
}
@@ -751,17 +766,18 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
unsigned long ttl, sig_ttl;
struct blockdata *key;
union all_addr a;
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY;
if (ntohs(header->qdcount) != 1 ||
RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
!extract_name(header, plen, &p, name, 1, 4))
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
/* See if we have cached a DS record which validates this key */
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
@@ -795,14 +811,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(flags, p);
if (*p++ != 3)
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
algo = *p++;
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
key = NULL;
/* key must have zone key flag set */
if (flags & 0x100)
key = blockdata_alloc((char*)p, rdlen - 4);
{
key = blockdata_alloc((char*)p, rdlen - 4);
failflags &= ~DNSSEC_FAIL_NOZONE;
}
p = psave;
@@ -823,15 +842,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
unsigned char *digest, *ds_digest;
const struct nettle_hash *hash;
int sigcnt, rrcnt;
int wire_len;
if (recp1->addr.ds.algo == algo &&
recp1->addr.ds.keytag == keytag &&
recp1->uid == (unsigned int)class &&
(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
hash_init(hash, &ctx, &digest))
recp1->uid == (unsigned int)class)
{
int wire_len = to_wire(name);
failflags &= ~DNSSEC_FAIL_NOKEY;
if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))))
continue;
else
failflags &= ~DNSSEC_FAIL_NODSSUP;
if (!hash_init(hash, &ctx, &digest))
continue;
wire_len = to_wire(name);
/* Note that digest may be different between DSs, so
we can't move this outside the loop. */
@@ -846,12 +873,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
sigcnt != 0 && rrcnt != 0 &&
validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
NULL, key, rdlen - 4, algo, keytag, &sig_ttl) == STAT_SECURE)
rrcnt != 0)
{
valid = 1;
break;
if (sigcnt == 0)
continue;
else
failflags &= ~DNSSEC_FAIL_NOSIG;
rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
NULL, key, rdlen - 4, algo, keytag, &sig_ttl);
failflags &= rc;
if (STAT_ISEQUAL(rc, STAT_SECURE))
{
valid = 1;
break;
}
}
}
}
@@ -936,7 +974,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
return STAT_BOGUS;
return STAT_BOGUS | failflags;
}
/* The DNS packet is expected to contain the answer to a DS query
@@ -971,10 +1009,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
else
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);
if (rc == STAT_INSECURE)
if (STAT_ISEQUAL(rc, STAT_INSECURE))
{
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
rc = STAT_BOGUS;
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure");
return STAT_BOGUS | DNSSEC_FAIL_INDET;
}
p = (unsigned char *)(header+1);
@@ -984,13 +1023,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
/* If the key needed to validate the DS is on the same domain as the DS, we'll
loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */
if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
return STAT_BOGUS;
}
if (rc != STAT_SECURE)
if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc;
if (!neganswer)
@@ -1456,7 +1495,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if (!(p = skip_name(nsecs[i], header, plen, 15)))
return 0; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
p += 10; /* type, class, TTL, rdlen */
algo = *p++;
if ((hash = hash_find(nsec3_digest_name(algo))))
@@ -1959,15 +1998,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (check_unsigned && i < ntohs(header->ancount))
{
rc = zone_status(name, class1, keyname, now);
if (rc == STAT_SECURE)
rc = STAT_BOGUS;
if (STAT_ISEQUAL(rc, STAT_SECURE))
rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
}
else
rc = STAT_INSECURE;
if (rc != STAT_INSECURE)
if (!STAT_ISEQUAL(rc, STAT_INSECURE))
return rc;
}
}
@@ -1978,7 +2017,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
strcpy(daemon->workspacename, keyname);
rc = zone_status(daemon->workspacename, class1, keyname, now);
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
{
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
@@ -1986,13 +2025,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
/* Zone is insecure, don't need to validate RRset */
if (rc == STAT_SECURE)
if (STAT_ISEQUAL(rc, STAT_SECURE))
{
unsigned long sig_ttl;
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
{
if (class)
*class = class1; /* Class for DS or DNSKEY */
@@ -2025,21 +2064,21 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
That's not a problem since if the RRsets later fail
we'll return BOGUS then. */
if (rc == STAT_SECURE_WILDCARD &&
if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
!prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
rc = STAT_SECURE;
}
}
}
if (rc == STAT_INSECURE)
if (STAT_ISEQUAL(rc, STAT_INSECURE))
secure = STAT_INSECURE;
}
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
if (secure == STAT_SECURE)
if (STAT_ISEQUAL(secure, STAT_SECURE))
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
@@ -2057,16 +2096,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc;
}
return STAT_BOGUS; /* signed zone, no NSECs */
return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
}
}
@@ -2130,4 +2169,31 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
return ret;
}
int errflags_to_ede(int status)
{
/* We can end up with more than one flag set for some errors,
so this encodes a rough priority so the (eg) No sig is reported
before no-unexpired-sig. */
if (status & DNSSEC_FAIL_NYV)
return EDE_SIG_NYV;
else if (status & DNSSEC_FAIL_EXP)
return EDE_SIG_EXP;
else if (status & DNSSEC_FAIL_NOKEYSUP)
return EDE_USUPDNSKEY;
else if (status & DNSSEC_FAIL_NOZONE)
return EDE_NO_ZONEKEY;
else if (status & DNSSEC_FAIL_NOKEY)
return EDE_NO_DNSKEY;
else if (status & DNSSEC_FAIL_NODSSUP)
return EDE_USUPDS;
else if (status & DNSSEC_FAIL_NONSEC)
return EDE_NO_NSEC;
else if (status & DNSSEC_FAIL_INDET)
return EDE_DNSSEC_IND;
else if (status & DNSSEC_FAIL_NOSIG)
return EDE_NO_RRSIG;
else
return EDE_UNSET;
}
#endif /* HAVE_DNSSEC */

View File

@@ -20,49 +20,71 @@ static int order(char *qdomain, size_t qlen, struct server *serv);
static int order_qsort(const void *a, const void *b);
static int order_servers(struct server *s, struct server *s2);
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
void build_server_array(void)
{
struct server *serv;
int count = 0;
for (serv = daemon->servers; serv; serv = serv->next)
count++;
for (serv = daemon->local_domains; serv; serv = serv->next)
count++;
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
if (count > daemon->serverarraysz)
for (serv = daemon->local_domains; serv; serv = serv->next)
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
daemon->serverarraysz = count;
if (count > daemon->serverarrayhwm)
{
struct server **new;
count += 10; /* A few extra without re-allocating. */
if ((new = whine_malloc(count * sizeof(struct server *))))
{
if (daemon->serverarray)
free(daemon->serverarray);
daemon->serverarray = new;
daemon->serverarraysz = count;
daemon->serverarrayhwm = count;
}
}
count = 0;
for (serv = daemon->servers; serv; serv = serv->next, count++)
{
daemon->serverarray[count] = serv;
serv->serial = count;
serv->last_server = -1;
}
for (serv = daemon->servers; serv; serv = serv->next)
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
{
daemon->serverarray[count] = serv;
serv->serial = count;
serv->last_server = -1;
count++;
}
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
daemon->serverarray[count] = serv;
qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort);
/* servers need the location in the array to find all the whole
set of equivalent servers from a pointer to a single one. */
for (count = 0; count < daemon->serverarraysz; count++)
if (!(daemon->serverarray[count]->flags & (SERV_LITERAL_ADDRESS | SERV_USE_RESOLV)))
if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL))
daemon->serverarray[count]->arrayposn = count;
}
@@ -79,13 +101,13 @@ void build_server_array(void)
reply of IPv4 or IPV6.
return 0 if nothing found, 1 otherwise.
*/
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
int lookup_domain(char *domain, int flags, int *lowout, int *highout)
{
int rc, crop_query, nodots;
ssize_t qlen;
int try, high, low = 0;
int nlow = 0, nhigh = 0;
char *cp;
char *cp, *qdomain = domain;
/* may be no configured servers. */
if (daemon->serverarraysz == 0)
@@ -165,16 +187,36 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
if (rc == 0)
{
/* We've matched a setting which says to use servers without a domain.
Continue the search with empty query */
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
crop_query = qlen;
else
int found = 1;
if (daemon->server_has_wildcard)
{
/* We have a match, but it may only be (say) an IPv6 address, and
if the query wasn't for an AAAA record, it's no good, and we need
to continue generalising */
if (filter_servers(try, flags, &nlow, &nhigh))
/* if we have example.com and *example.com we need to check against *example.com,
but the binary search may have found either. Use the fact that example.com is sorted before *example.com
We favour example.com in the case that both match (ie www.example.com) */
while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0)
try--;
if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.'))
{
while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0)
try++;
if (!(daemon->serverarray[try]->flags & SERV_WILDCARD))
found = 0;
}
}
if (found)
{
/* We've matched a setting which says to use servers without a domain.
Continue the search with empty query */
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
crop_query = qlen;
else if (filter_servers(try, flags, &nlow, &nhigh))
/* We have a match, but it may only be (say) an IPv6 address, and
if the query wasn't for an AAAA record, it's no good, and we need
to continue generalising */
break;
}
}
@@ -184,11 +226,13 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
crop_query = 1;
/* strip chars off the query based on the largest possible remaining match,
then continue to the start of the next label. */
then continue to the start of the next label unless we have a wildcard
domain somewhere, in which case we have to go one at a time. */
qlen -= crop_query;
qdomain += crop_query;
while (qlen > 0 && (*(qdomain-1) != '.'))
qlen--, qdomain++;
if (!daemon->server_has_wildcard)
while (qlen > 0 && (*(qdomain-1) != '.'))
qlen--, qdomain++;
}
/* domain has no dots, and we have at least one server configured to handle such,
@@ -199,11 +243,6 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
/* F_DOMAINSRV returns only domain-specific servers, so if we got to a
general server, return empty set. */
if (nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh;
if (lowout)
*lowout = nlow;
@@ -238,19 +277,26 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
nhigh++;
/* Now the servers are on order between low and high, in the order
return zero for both, IPv6 addr, IPv4 addr, no-data return, send upstream.
See which of those match our query in that priority order and narrow (low, high) */
#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
for (i = nlow; (flags & F_CONFIG) && i < nhigh && (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS); i++);
if (i != nlow)
nhigh = i;
if (flags & F_CONFIG)
{
/* We're just lookin for any matches that return an RR. */
for (i = nlow; i < nhigh; i++)
if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS)
break;
/* failed, return failure. */
if (i == nhigh)
nhigh = nlow;
}
else
{
/* Now the servers are on order between low and high, in the order
IPv6 addr, IPv4 addr, return zero for both, send upstream, no-data return.
See which of those match our query in that priority order and narrow (low, high) */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
if (i != nlow && (flags & F_IPV6))
@@ -275,18 +321,26 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
{
nlow = i;
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
/* --local=/domain/, only return if we don't need a server. */
if (i != nlow && !(flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER)))
nhigh = i;
else
/* now look for a server */
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
if (i != nlow)
{
nlow = i;
/* If we want a server that can do DNSSEC, and this one can't,
return nothing. */
return nothing, similarly if were looking only for a server
for a particular domain. */
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
nlow = nhigh;
else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh;
else
nhigh = i;
}
else
{
/* --local=/domain/, only return if we don't need a server. */
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
nhigh = i;
}
}
}
@@ -331,7 +385,7 @@ int is_local_answer(time_t now, int first, char *name)
return rc;
}
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last)
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
{
int trunc = 0;
unsigned char *p;
@@ -341,7 +395,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
if (flags & (F_NXDOMAIN | F_NOERR))
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
setup_reply(header, flags);
setup_reply(header, flags, ede);
if (!(p = skip_questions(header, size)))
return 0;
@@ -436,13 +490,22 @@ static int order(char *qdomain, size_t qlen, struct server *serv)
static int order_servers(struct server *s1, struct server *s2)
{
int rc;
/* need full comparison of dotless servers in
order_qsort() and filter_servers() */
if (s1->flags & SERV_FOR_NODOTS)
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
return order(s1->domain, s1->domain_len, s2);
if ((rc = order(s1->domain, s1->domain_len, s2)) != 0)
return rc;
/* For identical domains, sort wildcard ones first */
if (s1->flags & SERV_WILDCARD)
return (s2->flags & SERV_WILDCARD) ? 0 : 1;
return (s2->flags & SERV_WILDCARD) ? -1 : 0;
}
static int order_qsort(const void *a, const void *b)
@@ -455,10 +518,12 @@ static int order_qsort(const void *a, const void *b)
rc = order_servers(s1, s2);
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
in a very specific order. */
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
so the order is IPv6 literal, IPv4 literal, all-zero literal,
upstream server, NXDOMAIN literal. */
if (rc == 0)
rc = (s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) -
(s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS));
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
if (rc == 0)
@@ -467,3 +532,167 @@ static int order_qsort(const void *a, const void *b)
return rc;
}
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
for (serv = daemon->local_domains; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
int add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain,
union all_addr *local_addr)
{
struct server *serv = NULL;
char *alloc_domain;
if (!domain)
domain = "";
/* .domain == domain, for historical reasons. */
if (*domain == '.')
while (*domain == '.') domain++;
else if (*domain == '*')
{
domain++;
if (*domain != 0)
flags |= SERV_WILDCARD;
}
if (*domain == 0)
alloc_domain = whine_malloc(1);
else if (!(alloc_domain = canonicalise((char *)domain, NULL)))
return 0;
/* See if there is a suitable candidate, and unmark
only do this for forwarding servers, not
address or local, to avoid delays on large numbers. */
if (flags & SERV_IS_LOCAL)
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
break;
if (serv)
{
free(alloc_domain);
alloc_domain = serv->domain;
}
else
{
size_t size;
if (flags & SERV_LITERAL_ADDRESS)
{
if (flags & SERV_6ADDR)
size = sizeof(struct serv_addr6);
else if (flags & SERV_4ADDR)
size = sizeof(struct serv_addr4);
else
size = sizeof(struct serv_local);
}
else
size = sizeof(struct server);
if (!(serv = whine_malloc(size)))
return 0;
if (flags & SERV_IS_LOCAL)
{
serv->next = daemon->local_domains;
daemon->local_domains = serv;
}
else
{
struct server *s;
/* Add to the end of the chain, for order */
if (!daemon->servers)
daemon->servers = serv;
else
{
for (s = daemon->servers; s->next; s = s->next);
s->next = serv;
}
serv->next = NULL;
}
}
if (!(flags & SERV_IS_LOCAL))
memset(serv, 0, sizeof(struct server));
serv->flags = flags;
serv->domain = alloc_domain;
serv->domain_len = strlen(alloc_domain);
if (flags & SERV_4ADDR)
((struct serv_addr4*)serv)->addr = local_addr->addr4;
if (flags & SERV_6ADDR)
((struct serv_addr6*)serv)->addr = local_addr->addr6;
if (!(flags & SERV_IS_LOCAL))
{
#ifdef HAVE_LOOP
serv->uid = rand32();
#endif
if (interface)
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
return 1;
}

View File

@@ -177,6 +177,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
int subnet, cacheable, forwarded = 0;
size_t edns0_len;
unsigned char *pheader;
int ede = EDE_UNSET;
(void)do_bit;
if (header->hb4 & HB4_CD)
@@ -270,7 +271,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* no available server. */
if (!lookup_domain(daemon->namebuff, gotname, &first, &last))
goto reply;
{
ede = EDE_NOT_READY;
goto reply;
}
/* Configured answer. */
if ((flags = is_local_answer(now, first, daemon->namebuff)))
@@ -521,15 +525,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* could not send on, prepare to return */
header->id = htons(forward->frec_src.orig_id);
free_frec(forward); /* cancel */
ede = EDE_NETERR;
reply:
if (udpfd != -1)
{
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, limit, first, last)))
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, limit, first, last, ede)))
return 0;
if (oph)
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
{
u16 swap = htons((u16)ede);
if (ede != -EDE_UNSET)
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
}
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
if (option_bool(OPT_CMARK_ALST_EN))
@@ -549,14 +561,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
int check_subnet, union mysockaddr *query_source)
int check_subnet, union mysockaddr *query_source, unsigned char *limit, int ede)
{
unsigned char *pheader, *sizep;
char **sets = 0;
int munged = 0, is_sign;
unsigned int rcode = RCODE(header);
size_t plen;
(void)ad_reqd;
(void)do_bit;
(void)bogusanswer;
@@ -604,11 +616,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
else
{
unsigned short udpsz;
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. We can't do this for signed packets. */
unsigned short udpsz;
GETSHORT(udpsz, sizep);
if (udpsz > daemon->edns_pktsz)
{
@@ -645,6 +656,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
{
union all_addr a;
a.log.rcode = rcode;
a.log.ede = ede;
log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL);
return resize_packet(header, n, pheader, plen);
@@ -667,6 +679,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA;
cache_secure = 0;
ede = EDE_BLOCKED;
}
else
{
@@ -693,6 +706,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
cache_secure = 0;
ede = EDE_BLOCKED;
}
if (doctored)
@@ -722,7 +736,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
if (munged)
{
header->ancount = htons(0);
@@ -734,7 +747,15 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
sections of the packet. Find the new length here and put back pseudoheader
if it was removed. */
return resize_packet(header, n, pheader, plen);
n = resize_packet(header, n, pheader, plen);
if (pheader && ede != EDE_UNSET)
{
u16 swap = htons((u16)ede);
n = add_pseudoheader(header, n, limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
}
return n;
}
#ifdef HAVE_DNSSEC
@@ -762,7 +783,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
would invite infinite loops, since the answers to DNSKEY and DS queries
will not be cached, so they'll be repeated. */
if (status != STAT_BOGUS && status != STAT_TRUNCATED && status != STAT_ABANDONED)
if (!STAT_ISEQUAL(status, STAT_BOGUS) && !STAT_ISEQUAL(status, STAT_TRUNCATED) && !STAT_ISEQUAL(status, STAT_ABANDONED))
{
if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class);
@@ -773,7 +794,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL);
#ifdef HAVE_DUMPFILE
if (status == STAT_BOGUS)
if (STAT_ISEQUAL(status, STAT_BOGUS))
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
header, (size_t)plen, &forward->sentto->addr, NULL);
#endif
@@ -781,7 +802,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Can't validate, as we're missing key data. Put this
answer aside, whilst we get that. */
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
if (STAT_ISEQUAL(status, STAT_NEED_DS) || STAT_ISEQUAL(status, STAT_NEED_KEY))
{
struct frec *new = NULL;
int serverind;
@@ -800,9 +821,9 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* validate routines leave name of required record in daemon->keyname */
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class,
status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz);
flags = (status == STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
hash = hash_questions(header, nn, daemon->namebuff);
if ((new = lookup_frec_by_query(hash, flags, FREC_DNSKEY_QUERY | FREC_DS_QUERY)))
@@ -872,7 +893,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
#endif
server_send_log(server, fd, header, nn, DUMP_SEC_QUERY,
F_NOEXTRA | F_DNSSEC, daemon->keyname,
querystr("dnssec-query", status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
querystr("dnssec-query", STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS));
server->queries++;
}
@@ -1073,6 +1094,7 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
{
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
size_t nn;
int ede = EDE_UNSET;
(void)status;
@@ -1085,36 +1107,40 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
no_cache_dnssec = 1;
#ifdef HAVE_DNSSEC
if (status != STAT_OK)
if (!STAT_ISEQUAL(status, STAT_OK))
{
/* status is STAT_OK when validation not turned on. */
no_cache_dnssec = 0;
if (status == STAT_TRUNCATED)
if (STAT_ISEQUAL(status, STAT_TRUNCATED))
header->hb3 |= HB3_TC;
else
{
char *result, *domain = "result";
if (status == STAT_ABANDONED)
union all_addr a;
a.log.ede = ede = errflags_to_ede(status);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{
result = "ABANDONED";
status = STAT_BOGUS;
}
else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
domain = daemon->namebuff;
if (STAT_ISEQUAL(status, STAT_SECURE))
cache_secure = 1;
else if (STAT_ISEQUAL(status, STAT_BOGUS))
{
no_cache_dnssec = 1;
bogusanswer = 1;
if (extract_request(header, n, daemon->namebuff, NULL))
domain = daemon->namebuff;
}
log_query(F_SECSTAT, domain, NULL, result);
}
if (status == STAT_SECURE)
cache_secure = 1;
else if (status == STAT_BOGUS)
{
no_cache_dnssec = 1;
bogusanswer = 1;
log_query(F_SECSTAT, domain, &a, result);
}
}
#endif
@@ -1135,7 +1161,8 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source)))
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source,
((unsigned char *)header) + daemon->edns_pktsz, ede)))
{
struct frec_src *src;
@@ -1215,13 +1242,15 @@ static int is_query_allowed_for_mark(u32 mark, const char *name)
static size_t answer_disallowed(struct dns_header *header, size_t qlen, u32 mark, const char *name)
{
unsigned char *p;
(void)name;
(void)mark;
#ifdef HAVE_UBUS
if (name)
ubus_event_bcast_connmark_allowlist_refused(mark, name);
#endif
setup_reply(header, /* flags: */ 0);
setup_reply(header, /* flags: */ 0, EDE_BLOCKED);
if (!(p = skip_questions(header, qlen)))
return 0;
@@ -1545,13 +1574,19 @@ void receive_query(struct listener *listen, time_t now)
#ifdef HAVE_CONNTRACK
else if (!allowed)
{
u16 swap = htons(EDE_BLOCKED);
m = answer_disallowed(header, (size_t)n, (u32)mark, is_single_query ? daemon->namebuff : NULL);
if (have_pseudoheader && m != 0)
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz,
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
if (m >= 1)
{
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr);
#endif
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
@@ -1565,6 +1600,9 @@ void receive_query(struct listener *listen, time_t now)
local_auth, do_bit, have_pseudoheader);
if (m >= 1)
{
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr);
#endif
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
if (local_auth)
if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask))
@@ -1579,15 +1617,18 @@ void receive_query(struct listener *listen, time_t now)
else
{
int ad_reqd = do_bit;
/* RFC 6840 5.7 */
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
ad_reqd = 1;
m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
if (m >= 1)
{
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr);
#endif
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask))
report_addresses(header, m, mark);
@@ -1731,16 +1772,16 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
if (--(*keycount) == 0)
new_status = STAT_ABANDONED;
else if (status == STAT_NEED_KEY)
else if (STAT_ISEQUAL(status, STAT_NEED_KEY))
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
else if (status == STAT_NEED_DS)
else if (STAT_ISEQUAL(status, STAT_NEED_DS))
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL);
if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY))
break;
/* Can't validate because we need a key/DS whose name now in keyname.
@@ -1758,7 +1799,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
}
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz);
if ((start = dnssec_server(server, daemon->keyname, &first, &last)) == -1 ||
(m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0)
@@ -1771,13 +1812,13 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
daemon->log_display_id = ++daemon->log_id;
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, keyname, &server->addr,
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
querystr("dnssec-query", STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS));
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
daemon->log_display_id = log_save;
if (new_status != STAT_OK)
if (!STAT_ISEQUAL(new_status, STAT_OK))
break;
}
@@ -1826,7 +1867,7 @@ unsigned char *tcp_request(int confd, time_t now,
int have_mark = 0;
int first, last;
unsigned int flags = 0;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
@@ -1877,6 +1918,8 @@ unsigned char *tcp_request(int confd, time_t now,
while (1)
{
int ede = EDE_UNSET;
if (query_count == TCP_MAX_QUERIES ||
!packet ||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
@@ -1962,10 +2005,13 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_CONNTRACK
else if (!allowed)
{
u16 swap = htons(EDE_BLOCKED);
m = answer_disallowed(header, size, (u32)mark, is_single_query ? daemon->namebuff : NULL);
if (have_pseudoheader && m != 0)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz,
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
}
#endif
#ifdef HAVE_AUTH
@@ -1998,7 +2044,9 @@ unsigned char *tcp_request(int confd, time_t now,
!strchr(daemon->namebuff, '.') &&
strlen(daemon->namebuff) != 0)
flags = F_NOERR;
else if (lookup_domain(daemon->namebuff, gotname, &first, &last) && !(flags = is_local_answer(now, first, daemon->namebuff)))
else if (!lookup_domain(daemon->namebuff, gotname, &first, &last))
ede = EDE_NOT_READY; /* No configured servers */
else if (!(flags = is_local_answer(now, first, daemon->namebuff)))
{
master = daemon->serverarray[first];
@@ -2028,7 +2076,10 @@ unsigned char *tcp_request(int confd, time_t now,
/* Loop round available servers until we succeed in connecting to one. */
if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0)
break;
{
ede = EDE_NETERR;
break;
}
/* get query name again for logging - may have been overwritten */
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
@@ -2042,28 +2093,30 @@ unsigned char *tcp_request(int confd, time_t now,
int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname,
serv, have_mark, mark, &keycount);
char *result, *domain = "result";
if (status == STAT_ABANDONED)
union all_addr a;
a.log.ede = ede = errflags_to_ede(status);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{
result = "ABANDONED";
status = STAT_BOGUS;
}
else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
domain = daemon->namebuff;
log_query(F_SECSTAT, domain, NULL, result);
if (status == STAT_BOGUS)
if (STAT_ISEQUAL(status, STAT_SECURE))
cache_secure = 1;
else if (STAT_ISEQUAL(status, STAT_BOGUS))
{
no_cache_dnssec = 1;
bogusanswer = 1;
if (extract_request(header, m, daemon->namebuff, NULL))
domain = daemon->namebuff;
}
if (status == STAT_SECURE)
cache_secure = 1;
log_query(F_SECSTAT, domain, &a, result);
}
#endif
@@ -2080,7 +2133,7 @@ unsigned char *tcp_request(int confd, time_t now,
m = process_reply(header, now, serv, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr);
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr, ((unsigned char *)header) + 65536, ede);
}
}
}
@@ -2089,11 +2142,18 @@ unsigned char *tcp_request(int confd, time_t now,
if (m == 0)
{
if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff,
((char *) header) + 65536, first, last)))
((char *) header) + 65536, first, last, ede)))
break;
if (have_pseudoheader)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
{
u16 swap = htons((u16)ede);
if (ede != EDE_UNSET)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
}
}
check_log_writer(1);

View File

@@ -235,7 +235,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
}
else
continue;
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;

View File

@@ -1021,6 +1021,7 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
}
kill_name(lease_tmp);
lease_tmp->flags |= LEASE_CHANGED; /* run script on change */
break;
}
}

View File

@@ -31,11 +31,13 @@ void loop_send_probes()
identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
for (serv = daemon->servers; serv; serv = serv->next)
if (strlen(serv->domain) == 0 &&
!(serv->flags & (SERV_FOR_NODOTS | SERV_LOOP)))
!(serv->flags & (SERV_FOR_NODOTS)))
{
ssize_t len = loop_make_probe(serv->uid);
int fd;
serv->flags &= ~SERV_LOOP;
if ((fd = allocate_rfd(&rfds, serv)) == -1)
continue;
@@ -101,7 +103,7 @@ int detect_loop(char *query, int type)
uid == serv->uid)
{
serv->flags |= SERV_LOOP;
check_servers(); /* log new state */
check_servers(1); /* log new state - don't send more probes. */
return 1;
}

View File

@@ -1494,149 +1494,7 @@ void pre_allocate_sfds(void)
}
}
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
{
if (serv->flags & flag)
serv->flags |= SERV_MARK;
#ifdef HAVE_LOOP
/* Give looped servers another chance */
serv->flags &= ~SERV_LOOP;
#endif
}
for (serv = daemon->local_domains; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
#ifdef HAVE_LOOP
/* Now we have a new set of servers, test for loops. */
loop_send_probes();
#endif
}
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv;
char *domain_str;
if (!domain)
domain = "";
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain.
NOTE that we can get local=/domain/ here, but NOT address=/domain/1.2.3.4 */
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
/* See if there is a suitable candidate, and unmark */
for (serv = (flags & SERV_IS_LOCAL) ? daemon->local_domains : daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_MARK) && hostname_isequal(domain, serv->domain))
break;
if (serv)
domain_str = serv->domain;
else if ((serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
if (!(domain_str = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
strcpy(domain_str, domain);
if (flags & SERV_IS_LOCAL)
{
serv->next = daemon->local_domains;
daemon->local_domains = serv;
}
else
{
struct server *s;
/* Add to the end of the chain, for order */
if (!daemon->servers)
daemon->servers = serv;
else
{
for (s = daemon->servers; s->next; s = s->next);
s->next = serv;
}
serv->next = NULL;
}
}
}
if (serv)
{
if (!(flags & SERV_IS_LOCAL))
memset(serv, 0, sizeof(struct server));
serv->flags = flags;
serv->domain = domain_str;
serv->domain_len = strlen(domain_str);
if (!(flags & SERV_IS_LOCAL))
{
serv->queries = serv->failed_queries = 0;
#ifdef HAVE_LOOP
serv->uid = rand32();
#endif
if (interface)
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
}
}
void check_servers(void)
void check_servers(int no_loop_check)
{
struct irec *iface;
struct server *serv;
@@ -1644,7 +1502,15 @@ void check_servers(void)
int port = 0, count;
int locals = 0;
/* interface may be new since startup */
#ifdef HAVE_LOOP
if (!no_loop_check)
loop_send_probes();
#endif
/* clear all marks. */
mark_servers(0);
/* interface may be new since startup */
if (!option_bool(OPT_NOWILD))
enumerate_interfaces(0);
@@ -1725,7 +1591,7 @@ void check_servers(void)
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
{
char *s1, *s2, *s3 = "";
char *s1, *s2, *s3 = "", *s4 = "";
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
@@ -1736,9 +1602,9 @@ void check_servers(void)
else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = "";
else
s1 = _("domain"), s2 = serv->domain;
s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : "";
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3);
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3);
}
#ifdef HAVE_LOOP
else if (serv->flags & SERV_LOOP)
@@ -1786,8 +1652,8 @@ void check_servers(void)
up = &sfd->next;
}
cleanup_servers();
build_server_array();
cleanup_servers(); /* remove servers we just deleted. */
build_server_array();
}
/* Return zero if no servers found, in that case we keep polling.
@@ -1860,7 +1726,7 @@ int reload_servers(char *fname)
continue;
}
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL);
gotone = 1;
}

View File

@@ -829,6 +829,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
int scope_index = 0;
char *scope_id;
*interface = 0;
if (strcmp(arg, "#") == 0)
{
if (flags)
@@ -930,58 +932,52 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return NULL;
}
static struct server *add_rev4(struct in_addr addr, int msize)
static int domain_rev4(char *domain, struct in_addr addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
in_addr_t a = ntohl(addr.s_addr);
char *p;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
*domain = 0;
switch (msize)
{
case 32:
p += sprintf(p, "%u.", a & 0xff);
domain += sprintf(domain, "%u.", a & 0xff);
/* fall through */
case 24:
p += sprintf(p, "%d.", (a >> 8) & 0xff);
domain += sprintf(domain, "%d.", (a >> 8) & 0xff);
/* fall through */
case 16:
p += sprintf(p, "%d.", (a >> 16) & 0xff);
domain += sprintf(domain, "%d.", (a >> 16) & 0xff);
/* fall through */
case 8:
p += sprintf(p, "%d.", (a >> 24) & 0xff);
domain += sprintf(domain, "%d.", (a >> 24) & 0xff);
break;
default:
free(serv->domain);
free(serv);
return NULL;
return 0;
}
p += sprintf(p, "in-addr.arpa");
return serv;
domain += sprintf(domain, "in-addr.arpa");
return 1;
}
static struct server *add_rev6(struct in6_addr *addr, int msize)
static int domain_rev6(char *domain, struct in6_addr *addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
char *p;
int i;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
if (msize%4)
return 0;
*domain = 0;
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)addr)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
domain += sprintf(domain, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
domain += sprintf(domain, "ip6.arpa");
return serv;
return 1;
}
#ifdef HAVE_DHCP
@@ -2256,7 +2252,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
set_option_bool(OPT_RESOLV_DOMAIN);
else
{
char *d;
char *d, *d_raw = arg;
comma = split(arg);
if (!(d = canonicalise_opt(arg)))
ret_err(gen_err);
@@ -2297,23 +2293,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err_free(gen_err, new);
else
{
/* generate the equivalent of
local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = add_rev4(new->start, msize);
if (!serv)
ret_err_free(_("bad prefix"), new);
serv->flags |= SERV_LITERAL_ADDRESS;
serv->next = daemon->local_domains;
daemon->local_domains = serv;
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);
/* local=/<domain>/ */
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_LITERAL_ADDRESS;
serv->next = daemon->local_domains;
daemon->local_domains = serv;
/* d_raw can't failed to canonicalise here, checked above. */
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
}
}
}
@@ -2345,20 +2333,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
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/ */
struct server *serv = add_rev6(&new->start6, msize);
serv->flags |= SERV_LITERAL_ADDRESS;
serv->next = daemon->local_domains;
daemon->local_domains = serv;
domain_rev6(domain, &new->start6, msize);
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
/* local=/<domain>/ */
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_LITERAL_ADDRESS;
serv->next = daemon->local_domains;
daemon->local_domains = serv;
/* d_raw can't failed to canonicalise here, checked above. */
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
}
}
}
@@ -2544,30 +2527,47 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'B': /* --bogus-nxdomain */
case LOPT_IGNORE_ADDR: /* --ignore-address */
{
struct in_addr addr;
int prefix = 32;
union all_addr addr;
int prefix, is6 = 0;
struct bogus_addr *baddr;
unhide_metas(arg);
if (!arg ||
((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
(inet_pton(AF_INET, arg, &addr) != 1))
ret_err(gen_err); /* error */
((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)))
ret_err(gen_err);
if (inet_pton(AF_INET6, arg, &addr.addr6) == 1)
is6 = 1;
else if (inet_pton(AF_INET, arg, &addr.addr4) != 1)
ret_err(gen_err);
if (!comma)
{
if (is6)
prefix = 128;
else
prefix = 32;
}
if (prefix > 128 || (!is6 && prefix > 32))
ret_err(gen_err);
baddr = opt_malloc(sizeof(struct bogus_addr));
if (option == 'B')
{
baddr->next = daemon->bogus_addr;
daemon->bogus_addr = baddr;
}
else
{
struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
if (option == 'B')
{
baddr->next = daemon->bogus_addr;
daemon->bogus_addr = baddr;
}
else
{
baddr->next = daemon->ignore_addr;
daemon->ignore_addr = baddr;
}
baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
baddr->next = daemon->ignore_addr;
daemon->ignore_addr = baddr;
}
baddr->prefix = prefix;
baddr->is6 = is6;
baddr->addr = addr;
break;
}
@@ -2639,14 +2639,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
{
struct server *new;
size_t size;
char *lastdomain = NULL, *domain = "";
char *alloc_domain;
u16 flags = 0;
char *err;
struct in_addr addr4;
struct in6_addr addr6;
union all_addr addr;
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
unhide_metas(arg);
@@ -2655,11 +2653,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
char *last;
arg++;
/* elide leading dots - they are implied in the search algorithm */
while (*arg == '.') arg++;
domain = lastdomain = arg;
domain = lastdomain = ++arg;
while ((last = split_chr(arg, '/')))
{
@@ -2668,116 +2662,45 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
}
if (servers_only && option == 'S')
flags |= SERV_FROM_FILE;
if (!arg || !*arg)
flags = SERV_LITERAL_ADDRESS;
else if (option == 'A')
{
/* # as literal address means return zero address for 4 and 6 */
if (strcmp(arg, "#") == 0)
flags |= SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS;
else if (inet_pton(AF_INET, arg, &addr4) > 0)
flags |= SERV_4ADDR | SERV_LITERAL_ADDRESS;
else if (inet_pton(AF_INET6, arg, &addr6) > 0)
flags |= SERV_6ADDR | SERV_LITERAL_ADDRESS;
flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS;
else if (inet_pton(AF_INET, arg, &addr.addr4) > 0)
flags = SERV_4ADDR | SERV_LITERAL_ADDRESS;
else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0)
flags = SERV_6ADDR | SERV_LITERAL_ADDRESS;
else
ret_err(_("Bad address in --address"));
}
if (!(alloc_domain = canonicalise_opt(domain)))
ret_err(gen_err);
if (flags & SERV_LITERAL_ADDRESS)
{
if (flags & SERV_6ADDR)
{
size = sizeof(struct serv_addr6);
new = opt_malloc(sizeof(struct serv_addr6));
((struct serv_addr6*)new)->addr = addr6;
}
else if (flags & SERV_4ADDR)
{
size = sizeof(struct serv_addr4);
new = opt_malloc(sizeof(struct serv_addr4));
((struct serv_addr4*)new)->addr = addr4;
}
else
{
size = sizeof(struct serv_local);
new = opt_malloc(sizeof(struct serv_local));
}
new->next = daemon->local_domains;
daemon->local_domains = new;
}
else
{
size = sizeof(struct server);
new = opt_malloc(sizeof(struct server));
if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
ret_err(err);
#ifdef HAVE_LOOP
new->uid = rand32();
#endif
if ((err = parse_server(arg, &new->addr, &new->source_addr, new->interface, &flags)))
{
free(new);
ret_err(err);
}
/* server=//1.2.3.4 is special. */
if (strlen(domain) == 0 && lastdomain)
flags |= SERV_FOR_NODOTS;
}
if (servers_only && option == 'S')
flags |= SERV_FROM_FILE;
while (1)
{
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
ret_err(gen_err);
/* Since domains that use standard servers don't have the
network stuff, it's easier to treat them as local. */
if (flags & SERV_USE_RESOLV)
{
new->next = daemon->local_domains;
daemon->local_domains = new;
}
else
{
new->next = daemon->servers;
daemon->servers = new;
}
if (!lastdomain || domain == lastdomain)
break;
domain += strlen(domain) + 1;
}
new->domain = alloc_domain;
new->domain_len = strlen(alloc_domain);
/* server=//1.2.3.4 is special. */
if (strlen(domain) == 0 && lastdomain)
flags |= SERV_FOR_NODOTS;
new->flags = flags;
/* If we have more than one domain, copy and iterate */
if (lastdomain)
while (domain != lastdomain)
{
struct server *last = new;
domain += strlen(domain) + 1;
if (!(alloc_domain = canonicalise_opt(domain)))
ret_err(gen_err);
new = opt_malloc(size);
memcpy(new, last, size);
new->domain = alloc_domain;
new->domain_len = strlen(alloc_domain);
if (flags & (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS))
{
new->next = daemon->local_domains;
daemon->local_domains = new;
}
else
{
new->next = daemon->servers;
daemon->servers = new;
}
}
break;
}
@@ -2785,10 +2708,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
char *string;
int size;
struct server *serv;
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);
@@ -2800,28 +2726,27 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (inet_pton(AF_INET, arg, &addr4))
{
serv = add_rev4(addr4, size);
if (!serv)
ret_err(_("bad prefix"));
serv->next = daemon->servers;
daemon->servers = serv;
if (!domain_rev4(domain, addr4, size))
ret_err(_("bad IPv4 prefix"));
}
else if (inet_pton(AF_INET6, arg, &addr6))
{
serv = add_rev6(&addr6, size);
serv->next = daemon->servers;
daemon->servers = serv;
if (!domain_rev6(domain, &addr6, size))
ret_err(_("bad IPv6 prefix"));
}
else
ret_err(gen_err);
string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
if (string)
if (!comma)
flags |= SERV_LITERAL_ADDRESS;
else if ((string = parse_server(comma, &serv_addr, &source_addr, interface, &flags)))
ret_err(string);
if (servers_only)
serv->flags |= SERV_FROM_FILE;
flags |= SERV_FROM_FILE;
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
ret_err(gen_err);
break;
}
@@ -4739,8 +4664,7 @@ err:
}
else
{
int nomem;
char *canon = canonicalise(arg, &nomem);
char *canon = canonicalise_opt(arg);
struct name_list *nl;
if (!canon)
{
@@ -5193,9 +5117,9 @@ void read_servers_file(void)
}
mark_servers(SERV_FROM_FILE);
cleanup_servers();
read_file(daemon->servers_file, f, LOPT_REV_SERV);
cleanup_servers();
check_servers(0);
}

View File

@@ -364,7 +364,7 @@ int private_net(struct in_addr addr, int ban_localhost)
return
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
(((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
@@ -375,12 +375,21 @@ int private_net(struct in_addr addr, int ban_localhost)
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
}
static int private_net6(struct in6_addr *a)
static int private_net6(struct in6_addr *a, int ban_localhost)
{
return
IN6_IS_ADDR_UNSPECIFIED(a) || /* RFC 6303 4.3 */
IN6_IS_ADDR_LOOPBACK(a) || /* RFC 6303 4.3 */
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
if (IN6_IS_ADDR_V4MAPPED(a))
{
struct in_addr v4;
v4.s_addr = ((const uint32_t *) (a))[3];
return private_net(v4, ban_localhost);
}
return
(IN6_IS_ADDR_UNSPECIFIED(a) && ban_localhost) || /* RFC 6303 4.3 */
(IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
IN6_IS_ADDR_SITELOCAL(a) ||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
}
@@ -795,28 +804,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
if (flags & F_IPV6)
{
if (IN6_IS_ADDR_V4MAPPED(&addr.addr6))
{
struct in_addr v4;
v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
}
/* Check for link-local (LL) and site-local (ULA) IPv6 addresses */
if (IN6_IS_ADDR_LINKLOCAL(&addr.addr6) ||
IN6_IS_ADDR_SITELOCAL(&addr.addr6))
return 1;
/* Check for the IPv6 loopback address (::1) when
option rebind-localhost-ok is NOT set */
if (!option_bool(OPT_LOCAL_REBIND) &&
IN6_IS_ADDR_LOOPBACK(&addr.addr6))
return 1;
}
if ((flags & F_IPV6) &&
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
return 1;
}
#ifdef HAVE_IPSET
@@ -982,10 +972,12 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
*name = 0; /* return empty name if no query found. */
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY ||
ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0)
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
return 0; /* must be exactly one query. */
if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
return 0; /* non-standard query. */
if (!extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
@@ -1005,17 +997,19 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
return F_IPV4 | F_IPV6;
}
#ifdef HAVE_DNSSEC
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
to servers for domains without a trust anchor. This make the
behaviour for DS and DNSKEY queries we forward the same
as for DS and DNSKEY queries we originate. */
if (qtype == T_DS || qtype == T_DNSKEY)
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
return F_DNSSECOK;
#endif
return F_QUERY;
}
void setup_reply(struct dns_header *header, unsigned int flags)
void setup_reply(struct dns_header *header, unsigned int flags, int ede)
{
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
@@ -1038,6 +1032,7 @@ void setup_reply(struct dns_header *header, unsigned int flags)
{
union all_addr a;
a.log.rcode = REFUSED;
a.log.ede = ede;
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
SET_RCODE(header, REFUSED);
}
@@ -1084,7 +1079,6 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
int i, qtype, qclass, rdlen;
unsigned long ttl;
struct bogus_addr *baddrp;
struct in_addr addr;
/* skip over questions */
if (!(p = skip_questions(header, qlen)))
@@ -1106,17 +1100,33 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
if (ttlp)
*ttlp = ttl;
if (qclass == C_IN && qtype == T_A)
if (qclass == C_IN)
{
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (qtype == T_A)
{
memcpy(&addr, p, INADDRSZ);
struct in_addr addr;
if ((addr.s_addr & baddrp->mask.s_addr) == baddrp->addr.s_addr)
return 1;
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
memcpy(&addr, p, INADDRSZ);
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (!baddrp->is6 && is_same_net_prefix(addr, baddrp->addr.addr4, baddrp->prefix))
return 1;
}
else if (qtype == T_AAAA)
{
struct in6_addr addr;
if (!CHECK_LEN(header, p, qlen, IN6ADDRSZ))
return 0;
memcpy(&addr, p, IN6ADDRSZ);
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (baddrp->is6 && is_same_net6(&addr, &baddrp->addr.addr6, baddrp->prefix))
return 1;
}
}
@@ -1615,7 +1625,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
else if (option_bool(OPT_BOGUSPRIV) &&
((is_arpa == F_IPV6 && private_net6(&addr.addr6)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) &&
((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) &&
!lookup_domain(name, F_DOMAINSRV, NULL, NULL))
{
/* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */

View File

@@ -436,7 +436,17 @@ int netmask_length(struct in_addr mask)
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
{
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}
}
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix)
{
struct in_addr mask;
mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
return is_same_net(a, b, mask);
}
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
{