Fix some edge cases wth domains and --address and --server.

Consider what happens when the same domain appears in
--address and --server.

This commit fixes the order, I think correctly like this:
highest to lowest priority.

--address with a IPv4 or IPv6 address (as long as the query matches the type)
--address with # for all-zeros, as long as the query is A or AAAA)
--address with no address, which returns NXDOMAIN or NOERROR for all types.
--server with address set to # to use the unqualified servers.
--server with matching domain.
--server without domain or from /etc/resolv.conf.

Note that the above is only valid when same domain appears.
The domain being matched is determined first, and has a higher
priority, so you can send google.com to a server and force com
to return NXDOMAIN and for google.com the server config will
override the address config, because there's a longer match.
This commit is contained in:
Simon Kelley
2025-04-29 16:33:22 +01:00
parent e127a972d1
commit e86d53c438
3 changed files with 27 additions and 26 deletions

View File

@@ -558,9 +558,9 @@ union mysockaddr {
/* The actual values here matter, since we sort on them to get records in the order /* The actual values here matter, since we sort on them to get records in the order
IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return */ IPv6 addr, IPv4 addr, all zero return, no-data return, resolvconf servers, upstream server */
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */ #define SERV_USE_RESOLV 1 /* forward this domain in the normal way */
#define SERV_USE_RESOLV 2 /* forward this domain in the normal way */ #define SERV_LITERAL_ADDRESS 2 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
#define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */ #define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */
#define SERV_4ADDR 8 /* addr is IPv4 */ #define SERV_4ADDR 8 /* addr is IPv4 */
#define SERV_6ADDR 16 /* addr is IPv6 */ #define SERV_6ADDR 16 /* addr is IPv6 */

View File

@@ -20,7 +20,7 @@ static int order(char *qdomain, size_t qlen, struct server *serv);
static int order_qsort(const void *a, const void *b); static int order_qsort(const void *a, const void *b);
static int order_servers(struct server *s, struct server *s2); 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. */ /* If the server is USE_RESOLV or LITERAL_ADDRESS, it lives on the local_domains chain. */
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS) #define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
void build_server_array(void) void build_server_array(void)
@@ -259,7 +259,6 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
return 1; return 1;
} }
/* Return first server in group of equivalent servers; this is the "master" record. */
int server_samegroup(struct server *a, struct server *b) int server_samegroup(struct server *a, struct server *b)
{ {
return order_servers(a, b) == 0; return order_servers(a, b) == 0;
@@ -296,8 +295,10 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
} }
else else
{ {
/* Now the servers are on order between low and high, in the order /* Now the matching server records are all between low and high.
IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return. order_qsort() ensures that they are in the order
IPv6 addr, IPv4 addr, return zero for both, no-data return,
"use resolvconf" servers, domain-specific upstream servers.
See which of those match our query in that priority order and narrow (low, high) */ See which of those match our query in that priority order and narrow (low, high) */
@@ -325,29 +326,27 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
{ {
nlow = i; nlow = i;
/* Short to resolv.conf servers */ /* now look for a NXDOMAIN answer --local=/domain/ */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
if (!(flags & (F_DOMAINSRV | F_SERVER)) && i != nlow)
nhigh = i;
else
{
nlow = i;
/* return "use resolv.conf servers" if they exist */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++); for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
if (i != nlow) if (i != nlow)
nhigh = i; nhigh = i;
else else
{
/* 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 for a particular domain, and this one isn't, return nothing. */ /* If we want a server for a particular domain, and this one isn't, return nothing. */
if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0) if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh; nlow = nhigh;
else else
nhigh = i; nlow = i;
}
else
{
/* --local=/domain/, only return if we don't need a server. */
if (flags & (F_DOMAINSRV | F_SERVER))
nhigh = i;
} }
} }
} }
@@ -543,12 +542,14 @@ static int order_qsort(const void *a, const void *b)
rc = order_servers(s1, s2); rc = order_servers(s1, s2);
/* Sort all literal NODATA and local IPV4 or IPV6 responses together, /* Sort all literal NODATA and local IPV4 or IPV6 responses together,
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit in a very specific order IPv6 literal, IPv4 literal, all-zero literal,
so the order is IPv6 literal, IPv4 literal, all-zero literal, NXDOMAIN literal. We also include SERV_USE_RESOLV in this, so that
unqualified servers, upstream server, NXDOMAIN literal. */ use-standard servers sort before ordinary servers. (SERV_USR_RESOLV set
implies that none of SERV_LITERAL_ADDRESS,SERV_4ADDR,SERV_6ADDR,SERV_ALL_ZEROS
are set) */
if (rc == 0) if (rc == 0)
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) - rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV))) -
((s1->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_ALL_ZEROS | SERV_USE_RESOLV)));
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */ /* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
if (rc == 0) if (rc == 0)

View File

@@ -1665,7 +1665,7 @@ void check_servers(int no_loop_check)
if (++locals <= LOCALS_LOGGED) if (++locals <= LOCALS_LOGGED)
my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain); my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
} }
else if (serv->flags & SERV_USE_RESOLV) else if (serv->flags & SERV_USE_RESOLV && serv->domain_len != 0)
my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain); my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
} }