Teach cache to store DS and DNSKEY records

This commit is contained in:
Simon Kelley
2012-04-04 14:05:35 +01:00
parent 19d69be220
commit 7b4ad2eb34
4 changed files with 105 additions and 16 deletions

View File

@@ -25,6 +25,9 @@ static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
static int uid = 0;
#ifdef HAVE_DNSSEC
static struct keydata *keyblock_free = NULL;
#endif
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
@@ -190,6 +193,10 @@ static void cache_free(struct crec *crecp)
big_free = crecp->name.bname;
crecp->flags &= ~F_BIGNAME;
}
#ifdef HAVE_DNSSEC
else if (crecp->flags & (F_DNSKEY | F_DS))
keydata_free(crecp->addr.key.keydata);
#endif
}
/* insert a new cache entry at the head of the list (youngest entry) */
@@ -280,7 +287,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
}
}
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name))
{
if (crecp->flags & (F_HOSTS | F_DHCP))
@@ -360,6 +367,8 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
int freed_all = flags & F_REVERSE;
int free_avail = 0;
/* Don't log keys */
if (flags & (F_IPV4 | F_IPV6))
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* if previous insertion failed give up now. */
@@ -452,8 +461,6 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
if (addr)
new->addr.addr = *addr;
else
new->addr.cname.cache = NULL;
new->ttd = now + (time_t)ttl;
new->next = new_chain;
@@ -1150,22 +1157,29 @@ void dump_cache(time_t now)
if (!is_outdated_cname_pointer(cache))
a = cache_get_name(cache->addr.cname.cache);
}
#ifdef HAVE_IPV6
#ifdef HAVE_DNSSEC
else if (cache->flags & (F_DNSKEY | F_DS))
{
a = daemon->addrbuff;
sprintf(a, "%u %u", cache->addr.key.algo, cache->addr.key.keylen);
}
#endif
else
{
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
#ifdef HAVE_IPV6
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
}
#else
else
a = inet_ntoa(cache->addr.addr.addr.addr4);
#endif
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
}
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
cache->flags & F_DNSKEY ? "K" : "",
cache->flags & F_DS ? "S" : "",
cache->flags & F_CNAME ? "C" : "",
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
@@ -1173,7 +1187,8 @@ void dump_cache(time_t now)
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ");
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
@@ -1287,3 +1302,50 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
}
#ifdef HAVE_DNSSEC
struct keydata *keydata_alloc(char *data, size_t len)
{
struct keydata *block, *ret = NULL;
struct keydata **prev = &ret;
while (len > 0)
{
if (keyblock_free)
{
block = keyblock_free;
keyblock_free = block->next;
}
else
block = whine_malloc(sizeof(struct keydata));
if (!block)
{
/* failed to alloc, free partial chain */
keydata_free(ret);
return NULL;
}
memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);
data += KEYBLOCK_LEN;
len -= KEYBLOCK_LEN;
*prev = block;
prev = &block->next;
block->next = NULL;
}
return ret;
}
void keydata_free(struct keydata *blocks)
{
struct keydata *tmp;
if (blocks)
{
for (tmp = blocks; tmp->next; tmp = tmp->next);
tmp->next = keyblock_free;
keyblock_free = blocks;
}
}
#endif

View File

@@ -18,6 +18,7 @@
#define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define KEYBLOCK_LEN 140 /* choose to mininise fragmentation when storing DNSSEC keys */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 20 seconds */

View File

@@ -298,6 +298,11 @@ union bigname {
union bigname *next; /* freelist */
};
struct keydata {
struct keydata *next;
unsigned char key[KEYBLOCK_LEN];
};
struct crec {
struct crec *next, *prev, *hash_next;
time_t ttd; /* time to die */
@@ -308,6 +313,12 @@ struct crec {
struct crec *cache;
int uid;
} cname;
struct {
struct keydata *keydata;
unsigned char algo;
unsigned char flags;
unsigned short keylen;
} key;
} addr;
unsigned short flags;
union {
@@ -329,14 +340,21 @@ struct crec {
#define F_BIGNAME (1u<<9)
#define F_NXDOMAIN (1u<<10)
#define F_CNAME (1u<<11)
#define F_NOERR (1u<<12)
#define F_DNSKEY (1u<<12)
#define F_CONFIG (1u<<13)
#define F_DS (1u<<14)
#define F_DNSSECOK (1u<<15)
/* below here are only valid as args to log_query: cache
entries are limited to 16 bits */
#define F_UPSTREAM (1u<<16)
#define F_RRNAME (1u<<17)
#define F_SERVER (1u<<18)
#define F_QUERY (1u<<19)
#define F_NOERR (1u<<20)
/* composites */
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
/* struct sockaddr is not large enough to hold any address,
@@ -839,6 +857,10 @@ char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr);
#endif
#ifdef HAVE_DNSSEC
struct keydata *keydata_alloc(char *data, size_t len);
void keydata_free(struct keydata *blocks);
#endif
/* rfc1035.c */
unsigned int extract_request(struct dns_header *header, size_t qlen,

View File

@@ -936,11 +936,15 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!cname_count--)
return 0; /* looped CNAMES */
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
if (newc && cpp)
if (newc)
{
newc->addr.cname.cache = NULL;
if (cpp)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
}
cpp = newc;
if (attl < cttl)