mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
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.
This commit is contained in:
@@ -460,13 +460,22 @@ repeated domain or ipaddr parts as required.
|
|||||||
More specific domains take precedence over less specific domains, so:
|
More specific domains take precedence over less specific domains, so:
|
||||||
.B --server=/google.com/1.2.3.4
|
.B --server=/google.com/1.2.3.4
|
||||||
.B --server=/www.google.com/2.3.4.5
|
.B --server=/www.google.com/2.3.4.5
|
||||||
will send queries for *.google.com to 1.2.3.4, except *www.google.com,
|
will send queries for google.com and gmail.google.com to 1.2.3.4, but www.google.com
|
||||||
which will go to 2.3.4.5
|
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
|
The special server address '#' means, "use the standard servers", so
|
||||||
.B --server=/google.com/1.2.3.4
|
.B --server=/google.com/1.2.3.4
|
||||||
.B --server=/www.google.com/#
|
.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.
|
be forwarded as usual.
|
||||||
|
|
||||||
Also permitted is a -S
|
Also permitted is a -S
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ union mysockaddr {
|
|||||||
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
|
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
|
||||||
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
|
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
|
||||||
#define SERV_MARK 256 /* for mark-and-delete and log code */
|
#define SERV_MARK 256 /* for mark-and-delete and log code */
|
||||||
/* #define SERV_COUNTED 512 /* workspace for log code */
|
#define SERV_WILDCARD 512 /* domain has leading '*' */
|
||||||
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
|
#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_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
|
||||||
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
||||||
@@ -1103,6 +1103,7 @@ extern struct daemon {
|
|||||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
||||||
struct bogus_addr *bogus_addr, *ignore_addr;
|
struct bogus_addr *bogus_addr, *ignore_addr;
|
||||||
struct server *servers, *local_domains, **serverarray, *no_rebind;
|
struct server *servers, *local_domains, **serverarray, *no_rebind;
|
||||||
|
int server_has_wildcard;
|
||||||
int serverarraysz, serverarrayhwm;
|
int serverarraysz, serverarrayhwm;
|
||||||
struct ipsets *ipsets;
|
struct ipsets *ipsets;
|
||||||
u32 allowlist_mask;
|
u32 allowlist_mask;
|
||||||
|
|||||||
@@ -32,10 +32,18 @@ void build_server_array(void)
|
|||||||
#ifdef HAVE_LOOP
|
#ifdef HAVE_LOOP
|
||||||
if (!(serv->flags & SERV_LOOP))
|
if (!(serv->flags & SERV_LOOP))
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
count++;
|
count++;
|
||||||
|
if (serv->flags & SERV_WILDCARD)
|
||||||
|
daemon->server_has_wildcard = 1;
|
||||||
|
}
|
||||||
|
|
||||||
for (serv = daemon->local_domains; serv; serv = serv->next)
|
for (serv = daemon->local_domains; serv; serv = serv->next)
|
||||||
|
{
|
||||||
count++;
|
count++;
|
||||||
|
if (serv->flags & SERV_WILDCARD)
|
||||||
|
daemon->server_has_wildcard = 1;
|
||||||
|
}
|
||||||
|
|
||||||
daemon->serverarraysz = count;
|
daemon->serverarraysz = count;
|
||||||
|
|
||||||
@@ -92,13 +100,13 @@ void build_server_array(void)
|
|||||||
reply of IPv4 or IPV6.
|
reply of IPv4 or IPV6.
|
||||||
return 0 if nothing found, 1 otherwise.
|
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;
|
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;
|
||||||
char *cp;
|
char *cp, *qdomain = domain;
|
||||||
|
|
||||||
/* may be no configured servers. */
|
/* may be no configured servers. */
|
||||||
if (daemon->serverarraysz == 0)
|
if (daemon->serverarraysz == 0)
|
||||||
@@ -177,17 +185,37 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
|
{
|
||||||
|
int found = 1;
|
||||||
|
|
||||||
|
if (daemon->server_has_wildcard)
|
||||||
|
{
|
||||||
|
/* 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.
|
/* We've matched a setting which says to use servers without a domain.
|
||||||
Continue the search with empty query */
|
Continue the search with empty query */
|
||||||
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
||||||
crop_query = qlen;
|
crop_query = qlen;
|
||||||
else
|
else if (filter_servers(try, flags, &nlow, &nhigh))
|
||||||
{
|
|
||||||
/* We have a match, but it may only be (say) an IPv6 address, and
|
/* 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
|
if the query wasn't for an AAAA record, it's no good, and we need
|
||||||
to continue generalising */
|
to continue generalising */
|
||||||
if (filter_servers(try, flags, &nlow, &nhigh))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,9 +225,11 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
|||||||
crop_query = 1;
|
crop_query = 1;
|
||||||
|
|
||||||
/* strip chars off the query based on the largest possible remaining match,
|
/* 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;
|
qlen -= crop_query;
|
||||||
qdomain += crop_query;
|
qdomain += crop_query;
|
||||||
|
if (!daemon->server_has_wildcard)
|
||||||
while (qlen > 0 && (*(qdomain-1) != '.'))
|
while (qlen > 0 && (*(qdomain-1) != '.'))
|
||||||
qlen--, qdomain++;
|
qlen--, qdomain++;
|
||||||
}
|
}
|
||||||
@@ -449,13 +479,22 @@ static int order(char *qdomain, size_t qlen, struct server *serv)
|
|||||||
|
|
||||||
static int order_servers(struct server *s1, struct server *s2)
|
static int order_servers(struct server *s1, struct server *s2)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* need full comparison of dotless servers in
|
/* need full comparison of dotless servers in
|
||||||
order_qsort() and filter_servers() */
|
order_qsort() and filter_servers() */
|
||||||
|
|
||||||
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, 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)
|
static int order_qsort(const void *a, const void *b)
|
||||||
@@ -540,8 +579,16 @@ int add_update_server(int flags,
|
|||||||
|
|
||||||
if (!domain || strlen(domain) == 0)
|
if (!domain || strlen(domain) == 0)
|
||||||
alloc_domain = whine_malloc(1);
|
alloc_domain = whine_malloc(1);
|
||||||
else if (!(alloc_domain = canonicalise((char *)domain, NULL)))
|
else
|
||||||
|
{
|
||||||
|
if (*domain == '*')
|
||||||
|
{
|
||||||
|
domain++;
|
||||||
|
flags |= SERV_WILDCARD;
|
||||||
|
}
|
||||||
|
if (!(alloc_domain = canonicalise((char *)domain, NULL)))
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* See if there is a suitable candidate, and unmark
|
/* See if there is a suitable candidate, and unmark
|
||||||
only do this for forwarding servers, not
|
only do this for forwarding servers, not
|
||||||
|
|||||||
@@ -1588,7 +1588,7 @@ void check_servers(int no_loop_check)
|
|||||||
|
|
||||||
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
|
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
|
||||||
{
|
{
|
||||||
char *s1, *s2, *s3 = "";
|
char *s1, *s2, *s3 = "", *s4 = "";
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
||||||
@@ -1599,9 +1599,9 @@ void check_servers(int no_loop_check)
|
|||||||
else if (strlen(serv->domain) == 0)
|
else if (strlen(serv->domain) == 0)
|
||||||
s1 = _("default"), s2 = "";
|
s1 = _("default"), s2 = "";
|
||||||
else
|
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
|
#ifdef HAVE_LOOP
|
||||||
else if (serv->flags & SERV_LOOP)
|
else if (serv->flags & SERV_LOOP)
|
||||||
|
|||||||
Reference in New Issue
Block a user