From 7b4ad2eb3481586b620b8c640df63a7917a74c92 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 4 Apr 2012 14:05:35 +0100 Subject: [PATCH] Teach cache to store DS and DNSKEY records --- src/cache.c | 86 ++++++++++++++++++++++++++++++++++++++++++++------- src/config.h | 1 + src/dnsmasq.h | 24 +++++++++++++- src/rfc1035.c | 10 ++++-- 4 files changed, 105 insertions(+), 16 deletions(-) diff --git a/src/cache.c b/src/cache.c index 6940204..cf6cdb9 100644 --- a/src/cache.c +++ b/src/cache.c @@ -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,7 +367,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr, int freed_all = flags & F_REVERSE; int free_avail = 0; - log_query(flags | F_UPSTREAM, name, addr, NULL); + /* 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. */ if (insert_error) @@ -452,9 +461,7 @@ 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; new_chain = new; @@ -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 + + diff --git a/src/config.h b/src/config.h index 661bf80..84025f7 100644 --- a/src/config.h +++ b/src/config.h @@ -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 */ diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 3b4eac2..bfac3da 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -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, diff --git a/src/rfc1035.c b/src/rfc1035.c index 3d7f3e5..5b2d661 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -936,10 +936,14 @@ 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) { - cpp->addr.cname.cache = newc; - cpp->addr.cname.uid = newc->uid; + newc->addr.cname.cache = NULL; + if (cpp) + { + cpp->addr.cname.cache = newc; + cpp->addr.cname.uid = newc->uid; + } } cpp = newc;