From 84449bf41c78dcf7be3d0500cf6cca1111c15eda Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 29 Oct 2019 22:24:19 +0000 Subject: [PATCH] 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. --- man/dnsmasq.8 | 9 ++-- src/cache.c | 119 ++++++++++---------------------------------------- src/dnsmasq.h | 12 ++--- src/rfc1035.c | 2 +- 4 files changed, 34 insertions(+), 108 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 85c04a1..addc29c 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -603,12 +603,9 @@ Return a CAA DNS record, as specified in RFC6844. .TP .B --cname=,[,][,] Return a CNAME record which indicates that is really -. 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 +. 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 diff --git a/src/cache.c b/src/cache.c index ec193b9..aa2011c 100644 --- a/src/cache.c +++ b/src/cache.c @@ -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) @@ -525,7 +525,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho IN6_ARE_ADDR_EQUAL(&new->addr.addr6, &addr->addr6)) return new; } - + insert_error = 1; return NULL; } @@ -956,48 +956,20 @@ 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)) + if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0) { - nameexists = 1; - if (memcmp(&lookup->addr, addr, addrlen) == 0) - { - free(cache); - return; - } + 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 by ->next (which is unused at this point) held in hash buckets in @@ -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,24 +1211,21 @@ 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->uid = UID_NONE; - cache_hash(cache); - make_non_terminals(cache); - add_hosts_cname(cache); /* handle chains */ - } - + if (a->alias[1] != '*' && + ((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.name = a->target; + cache->addr.cname.uid = SRC_PTR; + cache->uid = UID_NONE; + cache_hash(cache); + make_non_terminals(cache); + } + #ifdef HAVE_DNSSEC for (ds = daemon->ds; ds; ds = ds->next) if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) && @@ -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 diff --git a/src/dnsmasq.h b/src/dnsmasq.h index b7c9490..c81e00a 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -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 diff --git a/src/rfc1035.c b/src/rfc1035.c index 6a9af24..6c6f66d 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -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) {