433 lines
13 KiB
Diff
433 lines
13 KiB
Diff
commit 244dba98c6d93c84a362177bb4ef629b6b3345d0
|
|
Author: Tomas Korbar <tkorbar@redhat.com>
|
|
Date: Tue Aug 19 11:03:38 2025 +0200
|
|
|
|
Backport of upstream commits
|
|
|
|
45d8a2435e8200e892b82b6a04c7ddfb07a4165a
|
|
eb1fe15ca80b6bc43cd6bfdf309ec6c590aff811
|
|
|
|
diff --git a/src/cache.c b/src/cache.c
|
|
index 8b1b560..612ad22 100644
|
|
--- a/src/cache.c
|
|
+++ b/src/cache.c
|
|
@@ -77,17 +77,20 @@ static void cache_link(struct crec *crecp);
|
|
static void rehash(int size);
|
|
static void cache_hash(struct crec *crecp);
|
|
|
|
-static unsigned int next_uid(void)
|
|
+void next_uid(struct crec *crecp)
|
|
{
|
|
static unsigned int uid = 0;
|
|
|
|
- uid++;
|
|
-
|
|
- /* uid == 0 used to indicate CNAME to interface name. */
|
|
- if (uid == SRC_INTERFACE)
|
|
- uid++;
|
|
+ if (crecp->uid == UID_NONE)
|
|
+ {
|
|
+ uid++;
|
|
|
|
- return uid;
|
|
+ /* uid == 0 used to indicate CNAME to interface name. */
|
|
+ if (uid == UID_NONE)
|
|
+ uid++;
|
|
+
|
|
+ crecp->uid = uid;
|
|
+ }
|
|
}
|
|
|
|
void cache_init(void)
|
|
@@ -105,7 +108,7 @@ void cache_init(void)
|
|
{
|
|
cache_link(crecp);
|
|
crecp->flags = 0;
|
|
- crecp->uid = next_uid();
|
|
+ crecp->uid = UID_NONE;
|
|
}
|
|
}
|
|
|
|
@@ -204,7 +207,7 @@ static void cache_free(struct crec *crecp)
|
|
{
|
|
crecp->flags &= ~F_FORWARD;
|
|
crecp->flags &= ~F_REVERSE;
|
|
- crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */
|
|
+ crecp->uid = UID_NONE; /* invalidate CNAMES pointing to this. */
|
|
|
|
if (cache_tail)
|
|
cache_tail->next = crecp;
|
|
@@ -322,7 +325,8 @@ static int is_expired(time_t now, struct crec *crecp)
|
|
return 1;
|
|
}
|
|
|
|
-static struct crec *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,
|
|
+ struct crec **target_crec, unsigned int *target_uid)
|
|
{
|
|
/* Scan and remove old entries.
|
|
If (flags & F_FORWARD) then remove any forward entries for name and any expired
|
|
@@ -335,7 +339,10 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
|
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 <reverse>,<other>,<immortal>
|
|
- so that when we hit an entry which isn't reverse and is immortal, we're done. */
|
|
+ so that when we hit an entry which isn't reverse and is immortal, we're done.
|
|
+
|
|
+ If we free a crec which is a CNAME target, return the entry and uid in target_crec and target_uid.
|
|
+ This entry will get re-used with the same name, to preserve CNAMEs. */
|
|
|
|
struct crec *crecp, **up;
|
|
|
|
@@ -343,17 +350,6 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
|
{
|
|
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
|
|
{
|
|
- if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
|
- {
|
|
- *up = crecp->hash_next;
|
|
- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
|
- {
|
|
- cache_unlink(crecp);
|
|
- cache_free(crecp);
|
|
- }
|
|
- continue;
|
|
- }
|
|
-
|
|
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
|
|
{
|
|
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
|
|
@@ -363,6 +359,16 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
|
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
|
return crecp;
|
|
*up = crecp->hash_next;
|
|
+ /* If this record is for the name we're inserting and is the target
|
|
+ of a CNAME record. Make the new record for the same name, in the same
|
|
+ crec, with the same uid to avoid breaking the existing CNAME. */
|
|
+ if (crecp->uid != UID_NONE)
|
|
+ {
|
|
+ if (target_crec)
|
|
+ *target_crec = crecp;
|
|
+ if (target_uid)
|
|
+ *target_uid = crecp->uid;
|
|
+ }
|
|
cache_unlink(crecp);
|
|
cache_free(crecp);
|
|
continue;
|
|
@@ -381,6 +387,18 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
|
}
|
|
#endif
|
|
}
|
|
+
|
|
+ if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
|
+ {
|
|
+ *up = crecp->hash_next;
|
|
+ if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
|
+ {
|
|
+ cache_unlink(crecp);
|
|
+ cache_free(crecp);
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
up = &crecp->hash_next;
|
|
}
|
|
}
|
|
@@ -447,11 +465,12 @@ void cache_start_insert(void)
|
|
struct crec *cache_insert(char *name, struct all_addr *addr,
|
|
time_t now, unsigned long ttl, unsigned short flags)
|
|
{
|
|
- struct crec *new;
|
|
+ struct crec *new, *target_crec = NULL;
|
|
union bigname *big_name = NULL;
|
|
int freed_all = flags & F_REVERSE;
|
|
int free_avail = 0;
|
|
-
|
|
+ unsigned int target_uid;
|
|
+
|
|
/* Don't log DNSSEC records here, done elsewhere */
|
|
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
|
|
{
|
|
@@ -469,7 +488,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
|
|
|
/* First remove any expired entries and entries for the name/address we
|
|
are currently inserting. */
|
|
- if ((new = cache_scan_free(name, addr, now, flags)))
|
|
+ if ((new = cache_scan_free(name, addr, now, flags, &target_crec, &target_uid)))
|
|
{
|
|
/* We're trying to insert a record over one from
|
|
/etc/hosts or DHCP, or other config. If the
|
|
@@ -493,81 +512,89 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
|
}
|
|
|
|
/* Now get a cache entry from the end of the LRU list */
|
|
- while (1) {
|
|
- if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
|
|
- {
|
|
- insert_error = 1;
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- /* End of LRU list is still in use: if we didn't scan all the hash
|
|
- chains for expired entries do that now. If we already tried that
|
|
- then it's time to start spilling things. */
|
|
-
|
|
- if (new->flags & (F_FORWARD | F_REVERSE))
|
|
- {
|
|
- /* If free_avail set, we believe that an entry has been freed.
|
|
- Bugs have been known to make this not true, resulting in
|
|
- a tight loop here. If that happens, abandon the
|
|
- insert. Once in this state, all inserts will probably fail. */
|
|
- if (free_avail)
|
|
- {
|
|
- static int warned = 0;
|
|
- if (!warned)
|
|
- {
|
|
- my_syslog(LOG_ERR, _("Internal error in cache."));
|
|
- warned = 1;
|
|
- }
|
|
- insert_error = 1;
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- if (freed_all)
|
|
- {
|
|
- struct all_addr free_addr = new->addr.addr;;
|
|
+ if (!target_crec)
|
|
+ while (1) {
|
|
+ if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
|
|
+ {
|
|
+ insert_error = 1;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* Free entry at end of LRU list, use it. */
|
|
+ if (!(new->flags & (F_FORWARD | F_REVERSE)))
|
|
+ break;
|
|
|
|
+ /* End of LRU list is still in use: if we didn't scan all the hash
|
|
+ chains for expired entries do that now. If we already tried that
|
|
+ then it's time to start spilling things. */
|
|
+
|
|
+ /* If free_avail set, we believe that an entry has been freed.
|
|
+ Bugs have been known to make this not true, resulting in
|
|
+ a tight loop here. If that happens, abandon the
|
|
+ insert. Once in this state, all inserts will probably fail. */
|
|
+ if (free_avail)
|
|
+ {
|
|
+ static int warned = 0;
|
|
+ if (!warned)
|
|
+ {
|
|
+ my_syslog(LOG_ERR, _("Internal error in cache."));
|
|
+ warned = 1;
|
|
+ }
|
|
+ insert_error = 1;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (freed_all)
|
|
+ {
|
|
+ struct all_addr free_addr = new->addr.addr;;
|
|
+
|
|
#ifdef HAVE_DNSSEC
|
|
- /* For DNSSEC records, addr holds class. */
|
|
- if (new->flags & (F_DS | F_DNSKEY))
|
|
- free_addr.addr.dnssec.class = new->uid;
|
|
+ /* For DNSSEC records, addr holds class. */
|
|
+ if (new->flags & (F_DS | F_DNSKEY))
|
|
+ free_addr.addr.dnssec.class = new->uid;
|
|
#endif
|
|
-
|
|
- free_avail = 1; /* Must be free space now. */
|
|
- cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
|
|
- cache_live_freed++;
|
|
- }
|
|
- else
|
|
- {
|
|
- cache_scan_free(NULL, NULL, now, 0);
|
|
- freed_all = 1;
|
|
- }
|
|
- continue;
|
|
- }
|
|
-
|
|
- /* Check if we need to and can allocate extra memory for a long name.
|
|
- If that fails, give up now, always succeed for DNSSEC records. */
|
|
- if (name && (strlen(name) > SMALLDNAME-1))
|
|
- {
|
|
- if (big_free)
|
|
- {
|
|
- big_name = big_free;
|
|
- big_free = big_free->next;
|
|
- }
|
|
- else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
|
|
- !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
|
|
- {
|
|
- insert_error = 1;
|
|
- return NULL;
|
|
- }
|
|
- else if (bignames_left != 0)
|
|
- bignames_left--;
|
|
-
|
|
- }
|
|
+
|
|
+ free_avail = 1; /* Must be free space now. */
|
|
+ cache_scan_free(cache_get_name(new), &free_addr, now, new->flags, NULL, NULL);
|
|
+ cache_live_freed++;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ cache_scan_free(NULL, NULL, now, 0, NULL, NULL);
|
|
+ freed_all = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Check if we need to and can allocate extra memory for a long name.
|
|
+ If that fails, give up now, always succeed for DNSSEC records. */
|
|
+ if (name && (strlen(name) > SMALLDNAME-1))
|
|
+ {
|
|
+ if (big_free)
|
|
+ {
|
|
+ big_name = big_free;
|
|
+ big_free = big_free->next;
|
|
+ }
|
|
+ else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
|
|
+ !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
|
|
+ {
|
|
+ insert_error = 1;
|
|
+ return NULL;
|
|
+ }
|
|
+ else if (bignames_left != 0)
|
|
+ bignames_left--;
|
|
+
|
|
+ }
|
|
|
|
- /* Got the rest: finally grab entry. */
|
|
- cache_unlink(new);
|
|
- break;
|
|
- }
|
|
+ /* If we freed a cache entry for our name which was a CNAME target, use that.
|
|
+ and preserve the uid, so that existing CNAMES are not broken. */
|
|
+ if (target_crec)
|
|
+ {
|
|
+ new = target_crec;
|
|
+ new->uid = target_uid;
|
|
+ }
|
|
+
|
|
+ /* Got the rest: finally grab entry. */
|
|
+ cache_unlink(new);
|
|
|
|
new->flags = flags;
|
|
if (big_name)
|
|
@@ -787,8 +814,9 @@ static void add_hosts_cname(struct crec *target)
|
|
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 = next_uid();
|
|
+ crec->uid = UID_NONE;
|
|
cache_hash(crec);
|
|
add_hosts_cname(crec); /* handle chains */
|
|
}
|
|
@@ -1071,7 +1099,7 @@ void cache_reload(void)
|
|
cache->name.namep = a->alias;
|
|
cache->addr.cname.target.int_name = intr;
|
|
cache->addr.cname.uid = SRC_INTERFACE;
|
|
- cache->uid = next_uid();
|
|
+ cache->uid = UID_NONE;
|
|
cache_hash(cache);
|
|
add_hosts_cname(cache); /* handle chains */
|
|
}
|
|
@@ -1201,8 +1229,9 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
|
|
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 = next_uid();
|
|
+ aliasc->uid = UID_NONE;
|
|
cache_hash(aliasc);
|
|
add_dhcp_cname(aliasc, ttd);
|
|
}
|
|
@@ -1243,7 +1272,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
|
}
|
|
else if (!(crec->flags & F_DHCP))
|
|
{
|
|
- cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
|
|
+ cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD), NULL, NULL);
|
|
/* scan_free deletes all addresses associated with name */
|
|
break;
|
|
}
|
|
@@ -1270,7 +1299,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
|
if (crec->flags & F_NEG)
|
|
{
|
|
flags |= F_REVERSE;
|
|
- cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
|
|
+ cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags, NULL, NULL);
|
|
}
|
|
}
|
|
else
|
|
@@ -1290,7 +1319,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
|
crec->ttd = ttd;
|
|
crec->addr.addr = *host_address;
|
|
crec->name.namep = host_name;
|
|
- crec->uid = next_uid();
|
|
+ crec->uid = UID_NONE;
|
|
cache_hash(crec);
|
|
|
|
add_dhcp_cname(crec, ttd);
|
|
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
|
|
index cf1c9c4..2d83a48 100644
|
|
--- a/src/dnsmasq.h
|
|
+++ b/src/dnsmasq.h
|
|
@@ -471,8 +471,12 @@ struct crec {
|
|
#define F_NOEXTRA (1u<<27)
|
|
#define F_SERVFAIL (1u<<28)
|
|
|
|
+#define UID_NONE 0
|
|
/* Values of uid in crecs with F_CONFIG bit set. */
|
|
-#define SRC_INTERFACE 0
|
|
+/* 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
|
|
#define SRC_CONFIG 1
|
|
#define SRC_HOSTS 2
|
|
#define SRC_AH 3
|
|
@@ -1147,6 +1151,7 @@ extern struct daemon {
|
|
|
|
/* cache.c */
|
|
void cache_init(void);
|
|
+void next_uid(struct crec *crecp);
|
|
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
|
|
char *record_source(unsigned int index);
|
|
char *querystr(char *desc, unsigned short type);
|
|
diff --git a/src/rfc1035.c b/src/rfc1035.c
|
|
index 48804b8..021661e 100644
|
|
--- a/src/rfc1035.c
|
|
+++ b/src/rfc1035.c
|
|
@@ -739,6 +739,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|
newc->addr.cname.uid = 1;
|
|
if (cpp)
|
|
{
|
|
+ next_uid(newc);
|
|
cpp->addr.cname.target.cache = newc;
|
|
cpp->addr.cname.uid = newc->uid;
|
|
}
|
|
@@ -795,6 +796,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
|
|
if (newc && cpp)
|
|
{
|
|
+ next_uid(newc);
|
|
cpp->addr.cname.target.cache = newc;
|
|
cpp->addr.cname.uid = newc->uid;
|
|
}
|
|
@@ -821,6 +823,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
|
if (newc && cpp)
|
|
{
|
|
+ next_uid(newc);
|
|
cpp->addr.cname.target.cache = newc;
|
|
cpp->addr.cname.uid = newc->uid;
|
|
}
|