Generalise locally-configured CNAME handling.

It's now possible for the target of a CNAME to be any locally
configured RR or even point to a non-existent RR.
This commit is contained in:
Simon Kelley
2019-10-29 22:24:19 +00:00
parent 456a319775
commit 84449bf41c
4 changed files with 34 additions and 108 deletions

View File

@@ -603,12 +603,9 @@ Return a CAA DNS record, as specified in RFC6844.
.TP
.B --cname=<cname>,[<cname>,]<target>[,<TTL>]
Return a CNAME record which indicates that <cname> is really
<target>. There are significant limitations on the target; it must be a
DNS name which is known to dnsmasq from /etc/hosts (or additional
hosts files), from DHCP, from \fB--interface-name\fP or from another
.B --cname.
If the target does not satisfy this
criteria, the whole cname is ignored. The cname must be unique, but it
<target>. There is a significant limitation on the target; it must be a
DNS record which is known to dnsmasq and NOT a DNS record which comes from
an upstream server. The cname must be unique, but it
is permissible to have more than one cname pointing to the same target. Indeed
it's possible to declare multiple cnames to a target in a single line, like so:
.B --cname=cname1,cname2,target

View File

@@ -276,10 +276,10 @@ char *cache_get_name(struct crec *crecp)
char *cache_get_cname_target(struct crec *crecp)
{
if (crecp->addr.cname.uid != SRC_INTERFACE)
if (crecp->addr.cname.uid != SRC_PTR)
return cache_get_name(crecp->addr.cname.target.cache);
return crecp->addr.cname.target.int_name->name;
return crecp->addr.cname.target.name;
}
@@ -309,7 +309,7 @@ struct crec *cache_enumerate(int init)
static int is_outdated_cname_pointer(struct crec *crecp)
{
if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == SRC_INTERFACE)
if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == SRC_PTR)
return 0;
/* NB. record may be reused as DS or DNSKEY, where uid is
@@ -513,7 +513,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
{
/* We're trying to insert a record over one from
/etc/hosts or DHCP, or other config. If the
existing record is for an A or AAAA and
existing record is for an A or AAAA or CNAME and
the record we're trying to insert is the same,
just drop the insert, but don't error the whole process. */
if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD) && addr)
@@ -956,47 +956,19 @@ struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr,
return NULL;
}
static void add_hosts_cname(struct crec *target)
{
struct crec *crec;
struct cname *a;
for (a = daemon->cnames; a; a = a->next)
if (a->alias[1] != '*' &&
hostname_isequal(cache_get_name(target), a->target) &&
(crec = whine_malloc(SIZEOF_POINTER_CREC)))
{
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
crec->ttd = a->ttl;
crec->name.namep = a->alias;
crec->addr.cname.target.cache = target;
next_uid(target);
crec->addr.cname.uid = target->uid;
crec->uid = UID_NONE;
cache_hash(crec);
make_non_terminals(crec);
add_hosts_cname(crec); /* handle chains */
}
}
static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen,
unsigned int index, struct crec **rhash, int hashsz)
{
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
int i, nameexists = 0;
int i;
unsigned int j;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS))
{
nameexists = 1;
if (memcmp(&lookup->addr, addr, addrlen) == 0)
if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
{
free(cache);
return;
}
}
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, The entries are kept in hash chains, linked
@@ -1048,10 +1020,6 @@ static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrle
memcpy(&cache->addr, addr, addrlen);
cache_hash(cache);
make_non_terminals(cache);
/* don't need to do alias stuff for second and subsequent addresses. */
if (!nameexists)
add_hosts_cname(cache);
}
static int eatspace(FILE *f)
@@ -1211,7 +1179,6 @@ void cache_reload(void)
struct host_record *hr;
struct name_list *nl;
struct cname *a;
struct interface_name *intr;
#ifdef HAVE_DNSSEC
struct ds_config *ds;
#endif
@@ -1244,22 +1211,19 @@ void cache_reload(void)
up = &cache->hash_next;
}
/* Add CNAMEs to interface_names to the cache */
/* Add locally-configured CNAMEs to the cache */
for (a = daemon->cnames; a; a = a->next)
for (intr = daemon->int_names; intr; intr = intr->next)
if (a->alias[1] != '*' &&
hostname_isequal(a->target, intr->name) &&
((cache = whine_malloc(SIZEOF_POINTER_CREC))))
{
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
cache->ttd = a->ttl;
cache->name.namep = a->alias;
cache->addr.cname.target.int_name = intr;
cache->addr.cname.uid = SRC_INTERFACE;
cache->addr.cname.target.name = a->target;
cache->addr.cname.uid = SRC_PTR;
cache->uid = UID_NONE;
cache_hash(cache);
make_non_terminals(cache);
add_hosts_cname(cache); /* handle chains */
}
#ifdef HAVE_DNSSEC
@@ -1364,39 +1328,6 @@ void cache_unhash_dhcp(void)
up = &cache->hash_next;
}
static void add_dhcp_cname(struct crec *target, time_t ttd)
{
struct crec *aliasc;
struct cname *a;
for (a = daemon->cnames; a; a = a->next)
if (a->alias[1] != '*' &&
hostname_isequal(cache_get_name(target), a->target))
{
if ((aliasc = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
aliasc = whine_malloc(SIZEOF_POINTER_CREC);
if (aliasc)
{
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;
if (ttd == 0)
aliasc->flags |= F_IMMORTAL;
else
aliasc->ttd = ttd;
aliasc->name.namep = a->alias;
aliasc->addr.cname.target.cache = target;
next_uid(target);
aliasc->addr.cname.uid = target->uid;
aliasc->uid = UID_NONE;
cache_hash(aliasc);
make_non_terminals(aliasc);
add_dhcp_cname(aliasc, ttd);
}
}
}
void cache_add_dhcp_entry(char *host_name, int prot,
union all_addr *host_address, time_t ttd)
{
@@ -1479,8 +1410,6 @@ void cache_add_dhcp_entry(char *host_name, int prot,
crec->uid = UID_NONE;
cache_hash(crec);
make_non_terminals(crec);
add_dhcp_cname(crec, ttd);
}
}
#endif

View File

@@ -287,9 +287,9 @@ union all_addr {
struct {
union {
struct crec *cache;
struct interface_name *int_name;
char *name;
} target;
unsigned int uid; /* 0 if union is interface-name */
unsigned int uid; /* 0 if union is char * */
} cname;
struct {
struct blockdata *keydata;
@@ -483,10 +483,10 @@ struct crec {
#define UID_NONE 0
/* Values of uid in crecs with F_CONFIG bit set. */
/* cname to uid SRC_INTERFACE are to interface names,
so use UID_NONE for that to eliminate clashes with
any other uid */
#define SRC_INTERFACE UID_NONE
/* cname to uid SRC_PTR are to locally-configured CNAME
so use UID_NONE for that to
eliminate clashes with any other uid */
#define SRC_PTR UID_NONE
#define SRC_CONFIG 1
#define SRC_HOSTS 2
#define SRC_AH 3

View File

@@ -788,7 +788,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (newc)
{
newc->addr.cname.target.cache = NULL;
/* anything other than zero, to avoid being mistaken for CNAME to interface-name */
/* anything other than zero, to avoid being mistaken for a local CNAME */
newc->addr.cname.uid = 1;
if (cpp)
{