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.
This commit is contained in:
Simon Kelley
2021-06-24 23:28:47 +01:00
parent be291d979d
commit 8c9196bff8
2 changed files with 16 additions and 21 deletions

View File

@@ -16,7 +16,7 @@
#include "dnsmasq.h" #include "dnsmasq.h"
static int order(char *qdomain, int leading_dot, size_t qlen, struct server *serv); 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);
@@ -81,7 +81,7 @@ void build_server_array(void)
*/ */
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout) int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
{ {
int rc, crop_query, nodots, leading_dot = 1; int rc, crop_query, nodots;
ssize_t qlen; ssize_t qlen;
int try, high, low = 0; int try, high, low = 0;
int nlow = 0, nhigh = 0; int nlow = 0, nhigh = 0;
@@ -101,13 +101,10 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
if (qlen == 0 || flags & F_DNSSECOK) if (qlen == 0 || flags & F_DNSSECOK)
nodots = 0; nodots = 0;
/* account for leading dot */
qlen++;
/* Search shorter and shorter RHS substrings for a match */ /* Search shorter and shorter RHS substrings for a match */
while (qlen >= 0) while (qlen >= 0)
{ {
/* Note that when we chop off a character, all the possible matches /* Note that when we chop off a label, all the possible matches
MUST be at a larger index than the nearest failing match with one more MUST be at a larger index than the nearest failing match with one more
character, since the array is sorted longest to smallest. Hence character, since the array is sorted longest to smallest. Hence
we don't reset low to zero here, we can go further below and crop the we don't reset low to zero here, we can go further below and crop the
@@ -121,7 +118,7 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
{ {
try = (low + high)/2; try = (low + high)/2;
if ((rc = order(qdomain, leading_dot, qlen, daemon->serverarray[try])) == 0) if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
break; break;
if (rc < 0) if (rc < 0)
@@ -186,13 +183,12 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
if (crop_query == 0) if (crop_query == 0)
crop_query = 1; crop_query = 1;
/* strip chars off the query based on the largest possible remaining match,
then continue to the start of the next label. */
qlen -= crop_query; qlen -= crop_query;
if (leading_dot)
{
leading_dot = 0;
crop_query--;
}
qdomain += crop_query; qdomain += crop_query;
while (qlen > 0 && (*(qdomain-1) != '.'))
qlen--, qdomain++;
} }
/* domain has no dots, and we have at least one server configured to handle such, /* domain has no dots, and we have at least one server configured to handle such,
@@ -418,10 +414,9 @@ int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
#endif #endif
/* order by size, then by dictionary order */ /* order by size, then by dictionary order */
static int order(char *qdomain, int leading_dot, size_t qlen, struct server *serv) static int order(char *qdomain, size_t qlen, struct server *serv)
{ {
size_t dlen = 0; size_t dlen = 0;
int rc;
/* servers for dotless names always sort last /* servers for dotless names always sort last
searched for name is never dotless. */ searched for name is never dotless. */
@@ -436,10 +431,7 @@ static int order(char *qdomain, int leading_dot, size_t qlen, struct server *ser
if (qlen > dlen) if (qlen > dlen)
return -1; return -1;
if (leading_dot && (rc = '.' - serv->domain[0]) != 0) return strcmp(qdomain, serv->domain);
return rc;
return strcmp(qdomain, leading_dot ? &serv->domain[1] : serv->domain);
} }
static int order_servers(struct server *s1, struct server *s2) static int order_servers(struct server *s1, struct server *s2)
@@ -450,7 +442,7 @@ static int order_servers(struct server *s1, struct server *s2)
if (s1->flags & SERV_FOR_NODOTS) if (s1->flags & SERV_FOR_NODOTS)
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1; return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
return order(s1->domain, 0, s1->domain_len, s2); return order(s1->domain, s1->domain_len, s2);
} }
static int order_qsort(const void *a, const void *b) static int order_qsort(const void *a, const void *b)

View File

@@ -2656,6 +2656,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
char *last; char *last;
arg++; 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, '/'))) while ((last = split_chr(arg, '/')))