summaryrefslogtreecommitdiff
path: root/src/domain-match.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/domain-match.c')
-rw-r--r--src/domain-match.c309
1 files changed, 180 insertions, 129 deletions
diff --git a/src/domain-match.c b/src/domain-match.c
index f8e4796..fe8e25a 100644
--- a/src/domain-match.c
+++ b/src/domain-match.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -207,16 +207,20 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
}
}
- if (found)
+ if (found && 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 */
{
/* 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 */
+ Continue the search with empty query. We set the F_SERVER flag
+ so that --address=/#/... doesn't match. */
+ if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV)
+ {
+ crop_query = qlen;
+ flags |= F_SERVER;
+ }
+ else
break;
}
}
@@ -273,7 +277,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
nlow--;
while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0)
- nhigh++;
+ nhigh++;
nhigh++;
@@ -293,13 +297,13 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
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.
+ IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, 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))
+ if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
nhigh = i;
else
{
@@ -307,7 +311,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
- if (i != nlow && (flags & F_IPV4))
+ if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV4))
nhigh = i;
else
{
@@ -315,38 +319,46 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
- if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
+ if (!(flags & F_SERVER) && i != nlow && (flags & (F_IPV4 | F_IPV6)))
nhigh = i;
else
{
nlow = i;
- /* now look for a server */
- for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
-
+ /* Short to resolv.conf servers */
+ for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
+
if (i != nlow)
- {
- /* If we want a server that can do DNSSEC, and this one can't,
- 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;
- }
+ nhigh = i;
else
{
- /* --local=/domain/, only return if we don't need a server. */
- if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
- nhigh = i;
+ /* now look for a server */
+ for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
+
+ if (i != nlow)
+ {
+ /* If we want a server that can do DNSSEC, and this one can't,
+ 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;
+ }
}
}
}
}
}
-
+
*lowout = nlow;
*highout = nhigh;
@@ -387,13 +399,13 @@ 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, int ede)
{
- int trunc = 0;
+ int trunc = 0, anscount = 0;
unsigned char *p;
int start;
union all_addr addr;
if (flags & (F_NXDOMAIN | F_NOERR))
- log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
+ log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
setup_reply(header, flags, ede);
@@ -410,9 +422,9 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
else
addr.addr4 = srv->addr;
- header->ancount = htons(ntohs(header->ancount) + 1);
- add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
- log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
+ if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
+ anscount++;
+ log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
}
if (flags & gotname & F_IPV6)
@@ -425,14 +437,15 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
else
addr.addr6 = srv->addr;
- header->ancount = htons(ntohs(header->ancount) + 1);
- add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
- log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
+ if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
+ anscount++;
+ log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
}
if (trunc)
header->hb3 |= HB3_TC;
-
+ header->ancount = htons(anscount);
+
return p - (unsigned char *)header;
}
@@ -485,7 +498,7 @@ static int order(char *qdomain, size_t qlen, struct server *serv)
if (qlen > dlen)
return -1;
- return strcmp(qdomain, serv->domain);
+ return hostname_order(qdomain, serv->domain);
}
static int order_servers(struct server *s1, struct server *s2)
@@ -520,10 +533,10 @@ static int order_qsort(const void *a, const void *b)
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
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. */
+ unqualified servers, upstream server, NXDOMAIN literal. */
if (rc == 0)
- 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);
+ rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
+ ((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
if (rc == 0)
@@ -533,22 +546,53 @@ static int order_qsort(const void *a, const void *b)
return rc;
}
+
+/* When loading large numbers of server=.... lines during startup,
+ there's no possibility that there will be server records that can be reused, but
+ searching a long list for each server added grows as O(n^2) and slows things down.
+ This flag is set only if is known there may be free server records that can be reused.
+ There's a call to mark_servers(0) in read_opts() to reset the flag before
+ main config read. */
+
+static int maybe_free_servers = 0;
+
+/* Must be called before add_update_server() to set daemon->servers_tail */
void mark_servers(int flag)
{
- struct server *serv;
+ struct server *serv, *next, **up;
+ maybe_free_servers = !!flag;
+
+ daemon->servers_tail = NULL;
+
/* 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;
+ {
+ 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;
+ daemon->servers_tail = serv;
+ }
+
+ /* --address etc is different: since they are expected to be
+ 1) numerous and 2) not reloaded often. We just delete
+ and recreate. */
+ if (flag)
+ for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = next)
+ {
+ next = serv->next;
+
+ if (serv->flags & flag)
+ {
+ *up = next;
+ free(serv->domain);
+ free(serv);
+ }
+ else
+ up = &serv->next;
+ }
}
void cleanup_servers(void)
@@ -556,7 +600,7 @@ 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)
+ for (serv = daemon->servers, up = &daemon->servers, daemon->servers_tail = NULL; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
@@ -567,21 +611,11 @@ void cleanup_servers(void)
free(serv);
}
else
- up = &serv->next;
+ {
+ up = &serv->next;
+ daemon->servers_tail = serv;
+ }
}
-
- 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,
@@ -609,82 +643,95 @@ int add_update_server(int flags,
if (*domain == 0)
alloc_domain = whine_malloc(1);
- else if (!(alloc_domain = canonicalise((char *)domain, NULL)))
+ else
+ alloc_domain = canonicalise((char *)domain, NULL);
+
+ if (!alloc_domain)
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);
- }
+
+ if (flags & SERV_6ADDR)
+ size = sizeof(struct serv_addr6);
+ else if (flags & SERV_4ADDR)
+ size = sizeof(struct serv_addr4);
else
- size = sizeof(struct server);
+ size = sizeof(struct serv_local);
if (!(serv = whine_malloc(size)))
- return 0;
+ {
+ free(alloc_domain);
+ return 0;
+ }
+
+ serv->next = daemon->local_domains;
+ daemon->local_domains = serv;
- if (flags & SERV_IS_LOCAL)
+ if (flags & SERV_4ADDR)
+ ((struct serv_addr4*)serv)->addr = local_addr->addr4;
+
+ if (flags & SERV_6ADDR)
+ ((struct serv_addr6*)serv)->addr = local_addr->addr6;
+ }
+ else
+ {
+ /* Upstream servers. See if there is a suitable candidate, if so unmark
+ and move to the end of the list, for order. The entry found may already
+ be at the end. */
+ struct server **up, *tmp;
+
+ serv = NULL;
+
+ if (maybe_free_servers)
+ for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
+ {
+ tmp = serv->next;
+ if ((serv->flags & SERV_MARK) &&
+ hostname_isequal(alloc_domain, serv->domain))
+ {
+ /* Need to move down? */
+ if (serv->next)
+ {
+ *up = serv->next;
+ daemon->servers_tail->next = serv;
+ daemon->servers_tail = serv;
+ serv->next = NULL;
+ }
+ break;
+ }
+ else
+ up = &serv->next;
+ }
+
+ if (serv)
{
- serv->next = daemon->local_domains;
- daemon->local_domains = serv;
+ free(alloc_domain);
+ alloc_domain = serv->domain;
}
else
{
- struct server *s;
- /* Add to the end of the chain, for order */
- if (!daemon->servers)
- daemon->servers = serv;
- else
+ if (!(serv = whine_malloc(sizeof(struct server))))
{
- for (s = daemon->servers; s->next; s = s->next);
- s->next = serv;
+ free(alloc_domain);
+ return 0;
}
- serv->next = NULL;
+ memset(serv, 0, sizeof(struct server));
+
+ /* Add to the end of the chain, for order */
+ if (daemon->servers_tail)
+ daemon->servers_tail->next = serv;
+ else
+ daemon->servers = serv;
+ daemon->servers_tail = serv;
}
- }
-
- 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)
@@ -692,7 +739,11 @@ int add_update_server(int flags,
if (source_addr)
serv->source_addr = *source_addr;
}
-
+
+ serv->flags = flags;
+ serv->domain = alloc_domain;
+ serv->domain_len = strlen(alloc_domain);
+
return 1;
}