From cbc652423403e3cef00e00240f6beef713142246 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sun, 21 Dec 2014 21:21:53 +0000 Subject: Make caching work for CNAMEs pointing to A/AAAA records shadowed in /etc/hosts If the answer to an upstream query is a CNAME which points to an A/AAAA record which also exists in /etc/hosts and friends, then caching is suppressed, to avoid inconsistent answers. This is now modified to allow caching when the upstream and local A/AAAA records have the same value. --- src/cache.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/cache.c b/src/cache.c index f9e1d31..ff1ca6f 100644 --- a/src/cache.c +++ b/src/cache.c @@ -322,7 +322,7 @@ static int is_expired(time_t now, struct crec *crecp) return 1; } -static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags) +static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags) { /* Scan and remove old entries. If (flags & F_FORWARD) then remove any forward entries for name and any expired @@ -331,8 +331,8 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign entries in the whole cache. If (flags == 0) remove any expired entries in the whole cache. - In the flags & F_FORWARD case, the return code is valid, and returns zero if the - name exists in the cache as a HOSTS or DHCP entry (these are never deleted) + In the flags & F_FORWARD case, the return code is valid, and returns a non-NULL pointer + to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted) We take advantage of the fact that hash chains have stuff in the order ,, so that when we hit an entry which isn't reverse and is immortal, we're done. */ @@ -361,7 +361,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) - return 0; + return crecp; *up = crecp->hash_next; cache_unlink(crecp); cache_free(crecp); @@ -378,7 +378,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign crecp->addr.sig.type_covered == addr->addr.dnssec.type)) { if (crecp->flags & F_CONFIG) - return 0; + return crecp; *up = crecp->hash_next; cache_unlink(crecp); cache_free(crecp); @@ -423,7 +423,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign up = &crecp->hash_next; } - return 1; + return NULL; } /* Note: The normal calling sequence is @@ -471,10 +471,26 @@ struct crec *cache_insert(char *name, struct all_addr *addr, return NULL; /* First remove any expired entries and entries for the name/address we - are currently inserting. Fail if we attempt to delete a name from - /etc/hosts or DHCP. */ - if (!cache_scan_free(name, addr, now, flags)) + are currently inserting. */ + if ((new = cache_scan_free(name, addr, now, flags))) { + /* 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 + 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)) + { + if ((flags & F_IPV4) && (new->flags & F_IPV4) && + new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr) + return new; +#ifdef HAVE_IPV6 + else if ((flags & F_IPV6) && (new->flags & F_IPV6) && + IN6_ARE_ADDR_EQUAL(&new->addr.addr.addr.addr6, &addr->addr.addr6)) + return new; +#endif + } + insert_error = 1; return NULL; } -- cgit v1.2.1