Commit 8d718cbb authored by Simon Kelley's avatar Simon Kelley

Nasty cache failure and memory leak with DNSSEC.

parent f6a2b793
...@@ -21,20 +21,33 @@ ...@@ -21,20 +21,33 @@
static struct blockdata *keyblock_free; static struct blockdata *keyblock_free;
static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced; static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
static void blockdata_expand(int n)
{
struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
if (new)
{
int i;
new[n-1].next = keyblock_free;
keyblock_free = new;
for (i = 0; i < n - 1; i++)
new[i].next = &new[i+1];
blockdata_alloced += n;
}
}
/* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */ /* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */
void blockdata_init(void) void blockdata_init(void)
{ {
int i; keyblock_free = NULL;
blockdata_alloced = 0;
blockdata_alloced = (daemon->cachesize * 100) / sizeof(struct blockdata);
keyblock_free = safe_malloc(blockdata_alloced * sizeof(struct blockdata));
keyblock_free[blockdata_alloced-1].next = NULL;
for (i = 0; i < blockdata_alloced - 1; i++)
keyblock_free[i].next = &keyblock_free[i+1];
blockdata_count = 0; blockdata_count = 0;
blockdata_hwm = 0; blockdata_hwm = 0;
blockdata_expand((daemon->cachesize * 100) / sizeof(struct blockdata));
} }
void blockdata_report(void) void blockdata_report(void)
...@@ -51,18 +64,15 @@ struct blockdata *blockdata_alloc(char *data, size_t len) ...@@ -51,18 +64,15 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
while (len > 0) while (len > 0)
{ {
if (!keyblock_free)
blockdata_expand(50);
if (keyblock_free) if (keyblock_free)
{ {
block = keyblock_free; block = keyblock_free;
keyblock_free = block->next; keyblock_free = block->next;
blockdata_count++; blockdata_count++;
} }
else if ((block = whine_malloc(sizeof(struct blockdata))))
{
blockdata_count++;
if (blockdata_alloced < blockdata_count)
blockdata_alloced = blockdata_count;
}
if (!block) if (!block)
{ {
......
...@@ -456,7 +456,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr, ...@@ -456,7 +456,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
/* if previous insertion failed give up now. */ /* if previous insertion failed give up now. */
if (insert_error) if (insert_error)
return NULL; return NULL;
/* First remove any expired entries and entries for the name/address we /* First remove any expired entries and entries for the name/address we
are currently inserting. Fail is we attempt to delete a name from are currently inserting. Fail is we attempt to delete a name from
/etc/hosts or DHCP. */ /etc/hosts or DHCP. */
...@@ -486,14 +486,32 @@ struct crec *cache_insert(char *name, struct all_addr *addr, ...@@ -486,14 +486,32 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
insert. Once in this state, all inserts will probably fail. */ insert. Once in this state, all inserts will probably fail. */
if (free_avail) if (free_avail)
{ {
static warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("Internal error in cache."));
warned = 1;
}
insert_error = 1; insert_error = 1;
return NULL; return NULL;
} }
if (freed_all) if (freed_all)
{ {
struct all_addr free_addr = new->addr.addr;;
#ifdef HAVE_DNSSEC
/* For DNSSEC records, addr holds class and type_covered for RRSIG */
if (new->flags & (F_DS | F_DNSKEY))
{
free_addr.addr.dnssec.class = new->uid;
if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
free_addr.addr.dnssec.type = new->addr.sig.type_covered;
}
#endif
free_avail = 1; /* Must be free space now. */ free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags); cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
cache_live_freed++; cache_live_freed++;
} }
else else
...@@ -505,7 +523,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr, ...@@ -505,7 +523,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
} }
/* Check if we need to and can allocate extra memory for a long name. /* Check if we need to and can allocate extra memory for a long name.
If that fails, give up now. */ If that fails, give up now, always succeed for DNSSEC records. */
if (name && (strlen(name) > SMALLDNAME-1)) if (name && (strlen(name) > SMALLDNAME-1))
{ {
if (big_free) if (big_free)
...@@ -513,13 +531,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr, ...@@ -513,13 +531,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
big_name = big_free; big_name = big_free;
big_free = big_free->next; big_free = big_free->next;
} }
else if (!bignames_left || else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
!(big_name = (union bigname *)whine_malloc(sizeof(union bigname)))) !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
{ {
insert_error = 1; insert_error = 1;
return NULL; return NULL;
} }
else else if (bignames_left != 0)
bignames_left--; bignames_left--;
} }
......
This diff is collapsed.
...@@ -770,12 +770,12 @@ void reply_query(int fd, int family, time_t now) ...@@ -770,12 +770,12 @@ void reply_query(int fd, int family, time_t now)
status = STAT_TRUNCATED; status = STAT_TRUNCATED;
} }
else if (forward->flags & FREC_DNSKEY_QUERY) else if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else if (forward->flags & FREC_DS_QUERY) else if (forward->flags & FREC_DS_QUERY)
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else else
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class); status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
/* Can't validate, as we're missing key data. Put this /* Can't validate, as we're missing key data. Put this
answer aside, whilst we get that. */ answer aside, whilst we get that. */
if (status == STAT_NEED_DS || status == STAT_NEED_KEY) if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment