mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Optimise lokkup_domain()
This commit is contained in:
@@ -564,7 +564,7 @@ struct randfd_list {
|
||||
|
||||
|
||||
struct server {
|
||||
int flags;
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
int serial, arrayposn;
|
||||
@@ -583,23 +583,23 @@ struct server {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* First three fields must match struct server in next three definitions.. */
|
||||
/* First four fields must match struct server in next three definitions.. */
|
||||
struct serv_addr4 {
|
||||
int flags;
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
struct in_addr addr;
|
||||
};
|
||||
|
||||
struct serv_addr6 {
|
||||
int flags;
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
struct in6_addr addr;
|
||||
};
|
||||
|
||||
struct serv_local {
|
||||
int flags;
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
};
|
||||
@@ -1381,7 +1381,7 @@ void set_option_bool(unsigned int opt);
|
||||
void reset_option_bool(unsigned int opt);
|
||||
struct hostsfile *expand_filelist(struct hostsfile *list);
|
||||
char *parse_server(char *arg, union mysockaddr *addr,
|
||||
union mysockaddr *source_addr, char *interface, int *flags);
|
||||
union mysockaddr *source_addr, char *interface, u16 *flags);
|
||||
int option_read_dynfile(char *file, int flags);
|
||||
|
||||
/* forward.c */
|
||||
|
||||
@@ -79,18 +79,18 @@ void build_server_array(void)
|
||||
*/
|
||||
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
||||
{
|
||||
int rc, nodots, leading_dot = 1;
|
||||
ssize_t qlen, maxlen;
|
||||
int rc, crop_query, nodots, leading_dot = 1;
|
||||
ssize_t qlen;
|
||||
int try, high, low = 0;
|
||||
int nlow = 0, nhigh = 0;
|
||||
char *cp;
|
||||
|
||||
int compares = 0;
|
||||
|
||||
/* may be no configured servers. */
|
||||
if (daemon->serverarraysz == 0)
|
||||
return 0;
|
||||
|
||||
maxlen = strlen(daemon->serverarray[0]->domain);
|
||||
|
||||
/* find query length and presence of '.' */
|
||||
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
|
||||
if (*cp == '.')
|
||||
@@ -101,13 +101,8 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
||||
if (qlen == 0 || flags & F_DNSSECOK)
|
||||
nodots = 0;
|
||||
|
||||
/* No point trying to match more than the largest server domain */
|
||||
if (qlen > maxlen)
|
||||
{
|
||||
qdomain += qlen - maxlen;
|
||||
qlen = maxlen;
|
||||
leading_dot = 0;
|
||||
}
|
||||
/* account for leading dot */
|
||||
qlen++;
|
||||
|
||||
/* Search shorter and shorter RHS substrings for a match */
|
||||
while (qlen >= 0)
|
||||
@@ -115,14 +110,19 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
||||
/* Note that when we chop off a character, all the possible matches
|
||||
MUST be at a larger index than the nearest failing match with one more
|
||||
character, since the array is sorted longest to smallest. Hence
|
||||
we don't reset low here. */
|
||||
we don't reset low to zero here, we can go further below and crop the
|
||||
search string to the size of the largest remaining server
|
||||
when this match fails. */
|
||||
high = daemon->serverarraysz;
|
||||
crop_query = 1;
|
||||
|
||||
/* binary search */
|
||||
do
|
||||
while (1)
|
||||
{
|
||||
try = (low + high)/2;
|
||||
|
||||
compares++;
|
||||
|
||||
if ((rc = order(qdomain, leading_dot, qlen, daemon->serverarray[try])) == 0)
|
||||
break;
|
||||
|
||||
@@ -138,19 +138,14 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
||||
break;
|
||||
low = try;
|
||||
}
|
||||
}
|
||||
while (low != high);
|
||||
};
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
/* We've matched a setting which says to use servers without a domain.
|
||||
Continue the search with empty query (the last character gets stripped
|
||||
by the loop. */
|
||||
Continue the search with empty query */
|
||||
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
||||
{
|
||||
qdomain += qlen - 1;
|
||||
qlen = 1;
|
||||
}
|
||||
crop_query = qlen;
|
||||
else
|
||||
{
|
||||
/* We have a match, but it may only be (say) an IPv6 address, and
|
||||
@@ -160,27 +155,50 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leading_dot)
|
||||
leading_dot = 0;
|
||||
else
|
||||
{
|
||||
qlen--;
|
||||
qdomain++;
|
||||
/* try now points to the last domain that sorts before the query, so
|
||||
we know that a substring of the query shorter than it is required to match, so
|
||||
find the largest domain that's shorter than try. Note that just going to
|
||||
try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
|
||||
to aaa, since ccc sorts after bbb, but the first domain that has a chance to
|
||||
match is dd. So find the length of the first domain later than try which is
|
||||
is shorter than it. */
|
||||
ssize_t len, old = daemon->serverarray[try]->domain_len;
|
||||
while (++try != daemon->serverarraysz)
|
||||
{
|
||||
if (old != (len = daemon->serverarray[try]->domain_len))
|
||||
{
|
||||
/* crop_query must be at least one always. */
|
||||
if (qlen != len)
|
||||
crop_query = qlen - len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qlen -= crop_query;
|
||||
if (leading_dot)
|
||||
{
|
||||
leading_dot = 0;
|
||||
crop_query--;
|
||||
}
|
||||
qdomain += crop_query;
|
||||
}
|
||||
|
||||
printf("compares: %d\n", compares);
|
||||
|
||||
/* domain has no dots, and we have at least one server configured to handle such,
|
||||
These servers always sort to the very end of the array.
|
||||
A configured server eg server=/lan/ will take precdence. */
|
||||
if (nodots &&
|
||||
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
|
||||
(nlow == nhigh || strlen(daemon->serverarray[nlow]->domain) == 0))
|
||||
(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) && strlen(daemon->serverarray[nlow]->domain) == 0)
|
||||
if (nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
nlow = nhigh;
|
||||
|
||||
if (lowout)
|
||||
@@ -382,10 +400,7 @@ static int order(char *qdomain, int leading_dot, size_t qlen, struct server *ser
|
||||
if (serv->flags & SERV_FOR_NODOTS)
|
||||
return -1;
|
||||
|
||||
if (leading_dot)
|
||||
qlen++;
|
||||
|
||||
dlen = strlen(serv->domain);
|
||||
dlen = serv->domain_len;
|
||||
|
||||
if (qlen < dlen)
|
||||
return 1;
|
||||
@@ -401,14 +416,13 @@ static int order(char *qdomain, int leading_dot, size_t qlen, struct server *ser
|
||||
|
||||
static int order_servers(struct server *s1, struct server *s2)
|
||||
{
|
||||
size_t dlen = strlen(s1->domain);
|
||||
|
||||
/* 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, 0, dlen, s2);
|
||||
return order(s1->domain, 0, s1->domain_len, s2);
|
||||
}
|
||||
|
||||
static int order_qsort(const void *a, const void *b)
|
||||
|
||||
@@ -153,9 +153,8 @@ static int domain_no_rebind(char *domain)
|
||||
struct server *serv;
|
||||
int dlen = (int)strlen(domain);
|
||||
|
||||
/* flags is misused to hold length of domain. */
|
||||
for (serv = daemon->no_rebind; serv; serv = serv->next)
|
||||
if (dlen >= serv->flags && strcmp(serv->domain, &domain[dlen - serv->flags]) == 0)
|
||||
if (dlen >= serv->domain_len && strcmp(serv->domain, &domain[dlen - serv->flags]) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1617,7 +1617,7 @@ void add_update_server(int flags,
|
||||
|
||||
serv->flags = flags;
|
||||
serv->domain = domain_str;
|
||||
|
||||
serv->domain_len = strlen(domain_str);
|
||||
|
||||
if (!(flags & SERV_IS_LOCAL))
|
||||
{
|
||||
|
||||
@@ -812,7 +812,7 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
|
||||
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
|
||||
{
|
||||
int source_port = 0, serv_port = NAMESERVER_PORT;
|
||||
char *portno, *source;
|
||||
@@ -2617,7 +2617,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
comma = split_chr(arg, '/');
|
||||
new = opt_malloc(sizeof(struct serv_local));
|
||||
new->domain = opt_string_alloc(arg);
|
||||
new->flags = strlen(arg);
|
||||
new->domain_len = strlen(arg);
|
||||
new->next = daemon->no_rebind;
|
||||
daemon->no_rebind = new;
|
||||
arg = comma;
|
||||
@@ -2634,7 +2634,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
size_t size;
|
||||
char *lastdomain = NULL, *domain = "";
|
||||
char *alloc_domain;
|
||||
int flags = 0;
|
||||
u16 flags = 0;
|
||||
char *err;
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
@@ -2730,6 +2730,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
|
||||
new->domain = alloc_domain;
|
||||
new->domain_len = strlen(alloc_domain);
|
||||
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (strlen(domain) == 0 && lastdomain)
|
||||
@@ -2751,6 +2752,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user