From 23d3e6b21b4b6debff87da8c6558495e564cc38e Mon Sep 17 00:00:00 2001 From: Mattias Falk Date: Mon, 4 Apr 2011 16:12:35 +0200 Subject: [PATCH] Add support for a dns cache per interface Initial commit for dns cache per interface. Added a type that holds a reference to a cache and name of associated interface, address of interface, name server(s) associated with an interface etc. New functions to set default interface, address of name servers etc. Change-Id: Ie991bc5592fd998409853d8bf77d7fe69035dac5 --- libc/include/resolv.h | 16 ++ libc/netbsd/resolv/res_cache.c | 447 +++++++++++++++++++++++++++++++-- libc/private/resolv_cache.h | 38 ++- 3 files changed, 480 insertions(+), 21 deletions(-) diff --git a/libc/include/resolv.h b/libc/include/resolv.h index 4247d68b7..bb21c2380 100644 --- a/libc/include/resolv.h +++ b/libc/include/resolv.h @@ -34,6 +34,7 @@ #include #include #include +#include __BEGIN_DECLS @@ -49,6 +50,21 @@ extern struct __res_state *__res_state(void); extern int b64_ntop(u_char const *, size_t, char *, size_t); extern int b64_pton(char const *, u_char *, size_t); +/* Set name of default interface */ +extern void _resolv_set_default_iface(const char* ifname); + +/* set name servers for an interface */ +extern void _resolv_set_nameservers_for_iface(const char* ifname, char** servers, int numservers); + +/* tell resolver of the address of an interface */ +extern void _resolv_set_addr_of_iface(const char* ifname, struct in_addr* addr); + +/* flush the cache associated with the default interface */ +extern void _resolv_flush_cache_for_default_iface(); + +/* flush the cache associated with a certain interface */ +extern void _resolv_flush_cache_for_iface(const char* ifname); + __END_DECLS #endif /* _RESOLV_H_ */ diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c index e6302edb7..efce7dc98 100644 --- a/libc/netbsd/resolv/res_cache.c +++ b/libc/netbsd/resolv/res_cache.c @@ -27,6 +27,7 @@ */ #include "resolv_cache.h" +#include #include #include #include @@ -35,6 +36,12 @@ #include #include "arpa_nameser.h" #include +#include +#include +#include + +#include +#include "resolv_private.h" /* This code implements a small and *simple* DNS resolver cache. * @@ -159,9 +166,6 @@ #include #include -#include -#include "resolv_private.h" - /** BOUNDED BUFFER FORMATTING **/ @@ -1165,6 +1169,14 @@ typedef struct resolv_cache { Entry* entries; } Cache; +typedef struct resolv_cache_info { + char ifname[IF_NAMESIZE + 1]; + struct in_addr ifaddr; + Cache* cache; + struct resolv_cache_info* next; + char* nameservers[MAXNS +1]; + struct addrinfo* nsaddrinfo[MAXNS + 1]; +} CacheInfo; #define HTABLE_VALID(x) ((x) != NULL && (x) != HTABLE_DELETED) @@ -1543,11 +1555,47 @@ Exit: /****************************************************************************/ /****************************************************************************/ -static struct resolv_cache* _res_cache; static pthread_once_t _res_cache_once; +// Head of the list of caches. Protected by _res_cache_list_lock. +static struct resolv_cache_info _res_cache_list; + +// name of the current default inteface +static char _res_default_ifname[IF_NAMESIZE + 1]; + +// lock protecting everything in the _resolve_cache_info structs (next ptr, etc) +static pthread_mutex_t _res_cache_list_lock; + + +/* lookup the default interface name */ +static char *_get_default_iface_locked(); +/* insert resolv_cache_info into the list of resolv_cache_infos */ +static void _insert_cache_info_locked(struct resolv_cache_info* cache_info); +/* creates a resolv_cache_info */ +static struct resolv_cache_info* _create_cache_info( void ); +/* gets cache associated with an interface name, or NULL if none exists */ +static struct resolv_cache* _find_named_cache_locked(const char* ifname); +/* gets a resolv_cache_info associated with an interface name, or NULL if not found */ +static struct resolv_cache_info* _find_cache_info_locked(const char* ifname); +/* free dns name server list of a resolv_cache_info structure */ +static void _free_nameservers(struct resolv_cache_info* cache_info); +/* look up the named cache, and creates one if needed */ +static struct resolv_cache* _get_res_cache_for_iface_locked(const char* ifname); +/* empty the named cache */ +static void _flush_cache_for_iface_locked(const char* ifname); +/* empty the nameservers set for the named cache */ +static void _free_nameservers_locked(struct resolv_cache_info* cache_info); +/* lookup the namserver for the name interface */ +static int _get_nameserver_locked(const char* ifname, int n, char* addr, int addrLen); +/* lookup the addr of the nameserver for the named interface */ +static struct addrinfo* _get_nameserver_addr_locked(const char* ifname, int n); +/* lookup the inteface's address */ +static struct in_addr* _get_addr_locked(const char * ifname); + + + static void -_res_cache_init( void ) +_res_cache_init(void) { const char* env = getenv(CONFIG_ENV); @@ -1556,29 +1604,392 @@ _res_cache_init( void ) return; } - _res_cache = _resolv_cache_create(); + memset(&_res_default_ifname, 0, sizeof(_res_default_ifname)); + memset(&_res_cache_list, 0, sizeof(_res_cache_list)); + pthread_mutex_init(&_res_cache_list_lock, NULL); } - struct resolv_cache* -__get_res_cache( void ) +__get_res_cache(void) { - pthread_once( &_res_cache_once, _res_cache_init ); - return _res_cache; + struct resolv_cache *cache; + + pthread_once(&_res_cache_once, _res_cache_init); + + pthread_mutex_lock(&_res_cache_list_lock); + + char* ifname = _get_default_iface_locked(); + + // if default interface not set then use the first cache + // associated with an interface as the default one. + if (ifname[0] == '\0') { + struct resolv_cache_info* cache_info = _res_cache_list.next; + while (cache_info) { + if (cache_info->ifname[0] != '\0') { + ifname = cache_info->ifname; + } + + cache_info = cache_info->next; + } + } + cache = _get_res_cache_for_iface_locked(ifname); + + pthread_mutex_unlock(&_res_cache_list_lock); + XLOG("_get_res_cache. default_ifname = %s\n", ifname); + return cache; +} + +static struct resolv_cache* +_get_res_cache_for_iface_locked(const char* ifname) +{ + if (ifname == NULL) + return NULL; + + struct resolv_cache* cache = _find_named_cache_locked(ifname); + if (!cache) { + struct resolv_cache_info* cache_info = _create_cache_info(); + if (cache_info) { + cache = _resolv_cache_create(); + if (cache) { + int len = sizeof(cache_info->ifname); + cache_info->cache = cache; + strncpy(cache_info->ifname, ifname, len - 1); + cache_info->ifname[len - 1] = '\0'; + + _insert_cache_info_locked(cache_info); + } else { + free(cache_info); + } + } + } + return cache; } void -_resolv_cache_reset( unsigned generation ) +_resolv_cache_reset(unsigned generation) { XLOG("%s: generation=%d", __FUNCTION__, generation); - if (_res_cache == NULL) - return; + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); - pthread_mutex_lock( &_res_cache->lock ); - if (_res_cache->generation != generation) { - _cache_flush_locked(_res_cache); - _res_cache->generation = generation; + char* ifname = _get_default_iface_locked(); + // if default interface not set then use the first cache + // associated with an interface as the default one. + // Note: Copied the code from __get_res_cache since this + // method will be deleted/obsolete when cache per interface + // implemented all over + if (ifname[0] == '\0') { + struct resolv_cache_info* cache_info = _res_cache_list.next; + while (cache_info) { + if (cache_info->ifname[0] != '\0') { + ifname = cache_info->ifname; + } + + cache_info = cache_info->next; + } } - pthread_mutex_unlock( &_res_cache->lock ); + struct resolv_cache* cache = _get_res_cache_for_iface_locked(ifname); + + if (cache == NULL) { + pthread_mutex_unlock(&_res_cache_list_lock); + return; + } + + pthread_mutex_lock( &cache->lock ); + if (cache->generation != generation) { + _cache_flush_locked(cache); + cache->generation = generation; + } + pthread_mutex_unlock( &cache->lock ); + + pthread_mutex_unlock(&_res_cache_list_lock); +} + +void +_resolv_flush_cache_for_default_iface(void) +{ + char* ifname; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + ifname = _get_default_iface_locked(); + _flush_cache_for_iface_locked(ifname); + + pthread_mutex_unlock(&_res_cache_list_lock); +} + +void +_resolv_flush_cache_for_iface(const char* ifname) +{ + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + _flush_cache_for_iface_locked(ifname); + + pthread_mutex_unlock(&_res_cache_list_lock); +} + +static void +_flush_cache_for_iface_locked(const char* ifname) +{ + struct resolv_cache* cache = _find_named_cache_locked(ifname); + if (cache) { + pthread_mutex_lock(&cache->lock); + _cache_flush_locked(cache); + pthread_mutex_unlock(&cache->lock); + } +} + +static struct resolv_cache_info* +_create_cache_info(void) +{ + struct resolv_cache_info* cache_info; + + cache_info = calloc(sizeof(*cache_info), 1); + return cache_info; +} + +static void +_insert_cache_info_locked(struct resolv_cache_info* cache_info) +{ + struct resolv_cache_info* last; + + for (last = &_res_cache_list; last->next; last = last->next); + + last->next = cache_info; + +} + +static struct resolv_cache* +_find_named_cache_locked(const char* ifname) { + + struct resolv_cache_info* info = _find_cache_info_locked(ifname); + + if (info != NULL) return info->cache; + + return NULL; +} + +static struct resolv_cache_info* +_find_cache_info_locked(const char* ifname) +{ + if (ifname == NULL) + return NULL; + + struct resolv_cache_info* cache_info = _res_cache_list.next; + + while (cache_info) { + if (strcmp(cache_info->ifname, ifname) == 0) { + break; + } + + cache_info = cache_info->next; + } + return cache_info; +} + +static char* +_get_default_iface_locked(void) +{ + char* iface = _res_default_ifname; + + return iface; +} + +void +_resolv_set_default_iface(const char* ifname) +{ + XLOG("_resolv_set_default_if ifname %s\n",ifname); + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + int size = sizeof(_res_default_ifname); + memset(_res_default_ifname, 0, size); + strncpy(_res_default_ifname, ifname, size - 1); + _res_default_ifname[size - 1] = '\0'; + + pthread_mutex_unlock(&_res_cache_list_lock); +} + +void +_resolv_set_nameservers_for_iface(const char* ifname, char** servers, int numservers) +{ + int i, rt, index; + struct addrinfo hints; + char sbuf[NI_MAXSERV]; + + pthread_once(&_res_cache_once, _res_cache_init); + + pthread_mutex_lock(&_res_cache_list_lock); + // creates the cache if not created + _get_res_cache_for_iface_locked(ifname); + + struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname); + + if (cache_info != NULL) { + // free current before adding new + _free_nameservers_locked(cache_info); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + sprintf(sbuf, "%u", NAMESERVER_PORT); + + index = 0; + for (i = 0; i < numservers && i < MAXNS; i++) { + rt = getaddrinfo(servers[i], sbuf, &hints, &cache_info->nsaddrinfo[index]); + if (rt == 0) { + cache_info->nameservers[index] = strdup(servers[i]); + index++; + } else { + cache_info->nsaddrinfo[index] = NULL; + } + } + } + pthread_mutex_unlock(&_res_cache_list_lock); +} + +static void +_free_nameservers_locked(struct resolv_cache_info* cache_info) +{ + int i; + for (i = 0; i <= MAXNS; i++) { + free(cache_info->nameservers[i]); + cache_info->nameservers[i] = NULL; + freeaddrinfo(cache_info->nsaddrinfo[i]); + cache_info->nsaddrinfo[i] = NULL; + } +} + +int +_resolv_cache_get_nameserver(int n, char* addr, int addrLen) +{ + char *ifname; + int result = 0; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + ifname = _get_default_iface_locked(); + result = _get_nameserver_locked(ifname, n, addr, addrLen); + + pthread_mutex_unlock(&_res_cache_list_lock); + return result; +} + +static int +_get_nameserver_locked(const char* ifname, int n, char* addr, int addrLen) +{ + int len = 0; + char* ns; + struct resolv_cache_info* cache_info; + + if (n < 1 || n > MAXNS || !addr) + return 0; + + cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + ns = cache_info->nameservers[n - 1]; + if (ns) { + len = strlen(ns); + if (len < addrLen) { + strncpy(addr, ns, len); + addr[len] = '\0'; + } else { + len = 0; + } + } + } + + return len; +} + +struct addrinfo* +_cache_get_nameserver_addr(int n) +{ + struct addrinfo *result; + char* ifname; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + ifname = _get_default_iface_locked(); + + result = _get_nameserver_addr_locked(ifname, n); + pthread_mutex_unlock(&_res_cache_list_lock); + return result; +} + +static struct addrinfo* +_get_nameserver_addr_locked(const char* ifname, int n) +{ + struct addrinfo* ai = NULL; + struct resolv_cache_info* cache_info; + + if (n < 1 || n > MAXNS) + return NULL; + + cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + ai = cache_info->nsaddrinfo[n - 1]; + } + return ai; +} + +void +_resolv_set_addr_of_iface(const char* ifname, struct in_addr* addr) +{ + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + memcpy(&cache_info->ifaddr, addr, sizeof(*addr)); + + if (DEBUG) { + char* addr_s = inet_ntoa(cache_info->ifaddr); + XLOG("address of interface %s is %s\n", ifname, addr_s); + } + } + pthread_mutex_unlock(&_res_cache_list_lock); +} + +struct in_addr* +_resolv_get_addr_of_default_iface(void) +{ + struct in_addr* ai = NULL; + char* ifname; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + ifname = _get_default_iface_locked(); + ai = _get_addr_locked(ifname); + pthread_mutex_unlock(&_res_cache_list_lock); + + return ai; +} + +struct in_addr* +_resolv_get_addr_of_iface(const char* ifname) +{ + struct in_addr* ai = NULL; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + ai =_get_addr_locked(ifname); + pthread_mutex_unlock(&_res_cache_list_lock); + return ai; +} + +static struct in_addr* +_get_addr_locked(const char * ifname) +{ + struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + return &cache_info->ifaddr; + } + return NULL; } diff --git a/libc/private/resolv_cache.h b/libc/private/resolv_cache.h index cd876fb28..2a5445355 100644 --- a/libc/private/resolv_cache.h +++ b/libc/private/resolv_cache.h @@ -30,13 +30,45 @@ struct resolv_cache; /* forward */ -/* get cache instance, can be NULL if cache is disabled - * (e.g. through an environment variable) */ +/* gets the cache for the default interface. Might be NULL*/ extern struct resolv_cache* __get_res_cache(void); +/* get the cache for a specified interface. Can be NULL*/ +extern struct resolv_cache* __get_res_cache_for_iface(const char* ifname); + /* this gets called everytime we detect some changes in the DNS configuration * and will flush the cache */ -extern void _resolv_cache_reset( unsigned generation ); +extern void _resolv_cache_reset( unsigned generation ); + +/* Gets the address of the n:th name server for the default interface + * Return length of address on success else 0. + * Note: The first name server is at n = 1 */ +extern int _resolv_cache_get_nameserver(int n, char* addr, int addrLen); + +/* Gets the address of the n:th name server for a certain interface + * Return length of address on success else 0. + * Note: The first name server is at n = 1 */ +extern int _resolv_cache_get_nameserver_for_iface(const char* ifname, int n, + char* addr, int addrLen); + +/* Gets addrinfo of the n:th name server associated with an interface. + * NULL is returned if no address if found. + * Note: The first name server is at n = 1. */ +extern struct addrinfo* _resolv_cache_get_nameserver_addr_for_iface(const char* ifname, int n); + +/* Gets addrinfo of the n:th name server associated with the default interface + * NULL is returned if no address if found. + * Note: The first name server is at n = 1. */ +extern struct addrinfo* _resolv_cache_get_nameserver_addr(int n); + +/* gets the address associated with the default interface */ +extern struct in_addr* _resolv_get_addr_of_default_iface(); + +/* gets the address associated with the specified interface */ +extern struct in_addr* _resolv_get_addr_of_iface(const char* ifname); + +/* Get name of default interface */ +extern char* _resolv_get_default_iface(); typedef enum { RESOLV_CACHE_UNSUPPORTED, /* the cache can't handle that kind of queries */