Compare commits

..
20 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
15 changed files with 319 additions and 177 deletions

No files matched your search

+15 -6
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
+14 -13
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 ;
@@ -1922,10 +1923,10 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
else
sprintf(daemon->addrbuff, "%u", rcode);
if (addr->log.ede != -1)
if (addr->log.ede != EDE_UNSET)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE:%s)", edestr(addr->log.ede));
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
}
else
@@ -1973,10 +1974,10 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
source = "reply";
else if (flags & F_SECSTAT)
{
if (addr && addr->log.ede != -1)
if (addr && addr->log.ede != EDE_UNSET)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE:%s)", edestr(addr->log.ede));
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
source = "validation";
dest = arg;
+3 -5
View File
@@ -222,7 +222,6 @@ static void dbus_read_servers(DBusMessage *message)
/* unlink and free anything still marked. */
cleanup_servers();
check_servers(0);
}
#ifdef HAVE_LOOP
@@ -277,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;
@@ -413,8 +412,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
}
cleanup_servers();
check_servers(0);
if (dup)
free(dup);
@@ -713,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;
}
+2 -1
View File
@@ -85,7 +85,8 @@
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
/* RFC-8914 extended errors */
/* 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 */
+35 -21
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);
}
@@ -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);
+6 -3
View File
@@ -329,7 +329,8 @@ union all_addr {
struct bogus_addr {
struct in_addr addr, mask;
int is6, prefix;
union all_addr addr;
struct bogus_addr *next;
};
@@ -536,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 */
@@ -1103,6 +1104,7 @@ 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 server_has_wildcard;
int serverarraysz, serverarrayhwm;
struct ipsets *ipsets;
u32 allowlist_mask;
@@ -1367,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);
+3 -2
View File
@@ -744,7 +744,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
}
}
return STAT_BOGUS | failflags;
/* If we reach this point, no verifying key was found */
return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
}
@@ -2193,6 +2194,6 @@ int errflags_to_ede(int status)
else if (status & DNSSEC_FAIL_NOSIG)
return EDE_NO_RRSIG;
else
return -1;
return EDE_UNSET;
}
#endif /* HAVE_DNSSEC */
+114 -45
View File
@@ -32,11 +32,19 @@ void build_server_array(void)
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
count++;
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
for (serv = daemon->local_domains; serv; serv = serv->next)
count++;
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
daemon->serverarraysz = count;
if (count > daemon->serverarrayhwm)
@@ -57,7 +65,7 @@ void build_server_array(void)
count = 0;
for (serv = daemon->servers; serv; serv = serv->next, count++)
for (serv = daemon->servers; serv; serv = serv->next)
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
@@ -65,6 +73,7 @@ void build_server_array(void)
daemon->serverarray[count] = serv;
serv->serial = count;
serv->last_server = -1;
count++;
}
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
@@ -92,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)
@@ -178,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;
}
}
@@ -197,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,
@@ -212,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;
@@ -251,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))
@@ -288,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;
}
}
}
@@ -449,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)
@@ -468,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)
@@ -489,10 +541,14 @@ void mark_servers(int 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)
@@ -538,7 +594,20 @@ int add_update_server(int flags,
struct server *serv = NULL;
char *alloc_domain;
if (!domain || strlen(domain) == 0)
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;
+27 -14
View File
@@ -177,7 +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 = -1;
int ede = EDE_UNSET;
(void)do_bit;
if (header->hb4 & HB4_CD)
@@ -537,7 +537,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
{
u16 swap = htons((u16)ede);
if (ede != -1)
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);
@@ -734,12 +734,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
#endif
if (pheader && ede != -1)
{
u16 swap = htons((u16)ede);
n = add_pseudoheader(header, n, limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
}
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
if (munged)
@@ -753,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
@@ -1092,7 +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 = -1;
int ede = EDE_UNSET;
(void)status;
@@ -1240,6 +1242,8 @@ 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)
@@ -1580,6 +1584,9 @@ void receive_query(struct listener *listen, time_t now)
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]++;
@@ -1593,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))
@@ -1607,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);
@@ -1905,7 +1918,7 @@ unsigned char *tcp_request(int confd, time_t now,
while (1)
{
int ede = -1;
int ede = EDE_UNSET;
if (query_count == TCP_MAX_QUERIES ||
!packet ||
@@ -2136,7 +2149,7 @@ unsigned char *tcp_request(int confd, time_t now,
{
u16 swap = htons((u16)ede);
if (ede != -1)
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);
-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;
+1
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;
}
}
+6 -3
View File
@@ -1506,6 +1506,9 @@ void check_servers(int no_loop_check)
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))
@@ -1588,7 +1591,7 @@ void check_servers(int no_loop_check)
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))
@@ -1599,9 +1602,9 @@ void check_servers(int no_loop_check)
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)
+36 -23
View File
@@ -2527,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;
}
@@ -2636,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, '/')))
{
+46 -39
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 */
@@ -1087,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)))
@@ -1109,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;
}
}
@@ -1618,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 */
+11 -1
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)
{