Frédéric Perrin patch for ipv6 reverse resolution via address structure that

retains IP family.
This commit is contained in:
pdw
2011-10-03 20:00:58 +00:00
parent 7f9d185d83
commit 970508628b

View File

@@ -25,7 +25,18 @@
#define RESOLVE_QUEUE_LENGTH 20 #define RESOLVE_QUEUE_LENGTH 20
struct in6_addr resolve_queue[RESOLVE_QUEUE_LENGTH]; struct addr_storage {
int af; /* AF_INET or AF_INET6 */
int len; /* sizeof(struct in_addr or in6_addr) */
union {
struct in_addr addr4;
struct in6_addr addr6;
} addr;
#define as_addr4 addr.addr4
#define as_addr6 addr.addr6
};
struct addr_storage resolve_queue[RESOLVE_QUEUE_LENGTH];
pthread_cond_t resolver_queue_cond; pthread_cond_t resolver_queue_cond;
pthread_mutex_t resolver_queue_mutex; pthread_mutex_t resolver_queue_mutex;
@@ -55,48 +66,37 @@ extern options_t options;
* as NetBSD break the RFC and implement it in a non-thread-safe fashion, so * as NetBSD break the RFC and implement it in a non-thread-safe fashion, so
* for the moment, the configure script won't try to use it. * for the moment, the configure script won't try to use it.
*/ */
char *do_resolve(struct in6_addr *addr) { char *do_resolve(struct addr_storage *addr) {
struct sockaddr_in sin; struct sockaddr_in sin;
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
char buf[NI_MAXHOST]; /* 1025 */ char buf[NI_MAXHOST]; /* 1025 */
int res, af; int ret;
uint32_t* probe;
memset(&sin, '\0', sizeof(sin)); switch (addr->af) {
memset(&sin6, '\0', sizeof(sin6));
/* If the upper three (network byte order) uint32-parts
* are null, then there ought to be an IPv4 address here.
* Any such IPv6 would have to be 'xxxx::'. Neglectable? */
probe = (uint32_t *) addr;
af = (probe[1] || probe[2] || probe[3]) ? AF_INET6 : AF_INET;
switch (af) {
case AF_INET: case AF_INET:
sin.sin_family = af; sin.sin_family = addr->af;
sin.sin_port = 0; sin.sin_port = 0;
memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr)); memcpy(&sin.sin_addr, &addr->as_addr4, addr->len);
if (getnameinfo((struct sockaddr*)&sin, sizeof sin, ret = getnameinfo((struct sockaddr*)&sin, sizeof sin,
buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0) buf, sizeof buf, NULL, 0, NI_NAMEREQD);
return xstrdup(buf);
else
return NULL;
break; break;
case AF_INET6: case AF_INET6:
sin6.sin6_family = af; sin6.sin6_family = addr->af;
sin6.sin6_port = 0; sin6.sin6_port = 0;
memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr)); memcpy(&sin6.sin6_addr, &addr->as_addr6, addr->len);
if (getnameinfo((struct sockaddr*)&sin6, sizeof sin6, ret = getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0) buf, sizeof buf, NULL, 0, NI_NAMEREQD);
return xstrdup(buf); break;
else
return NULL;
break;
default: default:
return NULL; return NULL;
} }
if (ret == 0)
return xstrdup(buf);
else
return NULL;
} }
#elif defined(USE_GETHOSTBYADDR_R) #elif defined(USE_GETHOSTBYADDR_R)
@@ -106,7 +106,7 @@ char *do_resolve(struct in6_addr *addr) {
* Some implementations of libc choose to implement gethostbyaddr_r as * Some implementations of libc choose to implement gethostbyaddr_r as
* a non thread-safe wrapper to gethostbyaddr. An interesting choice... * a non thread-safe wrapper to gethostbyaddr. An interesting choice...
*/ */
char* do_resolve(struct in_addr * addr) { char* do_resolve(struct addr_storage *addr) {
struct hostent hostbuf, *hp; struct hostent hostbuf, *hp;
size_t hstbuflen = 1024; size_t hstbuflen = 1024;
char *tmphstbuf; char *tmphstbuf;
@@ -114,19 +114,19 @@ char* do_resolve(struct in_addr * addr) {
int herr; int herr;
char * ret = NULL; char * ret = NULL;
/* Allocate buffer, remember to free it to avoid memory leakage. */ /* Allocate buffer, remember to free it to avoid memory leakage. */
tmphstbuf = xmalloc (hstbuflen); tmphstbuf = xmalloc (hstbuflen);
/* Some machines have gethostbyaddr_r returning an integer error code; on /* Some machines have gethostbyaddr_r returning an integer error code; on
* others, it returns a struct hostent*. */ * others, it returns a struct hostent*. */
#ifdef GETHOSTBYADDR_R_RETURNS_INT #ifdef GETHOSTBYADDR_R_RETURNS_INT
while ((res = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET, while ((res = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
&hostbuf, tmphstbuf, hstbuflen, &hostbuf, tmphstbuf, hstbuflen,
&hp, &herr)) == ERANGE) &hp, &herr)) == ERANGE)
#else #else
/* ... also assume one fewer argument.... */ /* ... also assume one fewer argument.... */
while ((hp = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET, while ((hp = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
&hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
&& errno == ERANGE) && errno == ERANGE)
#endif #endif
{ {
@@ -152,22 +152,22 @@ char* do_resolve(struct in_addr * addr) {
#elif defined(USE_GETHOSTBYADDR) #elif defined(USE_GETHOSTBYADDR)
/** /**
* Implementation using gethostbyaddr. Since this is nonreentrant, we have to * Implementation using gethostbyname. Since this is nonreentrant, we have to
* wrap it in a mutex, losing all benefit of multithreaded resolution. * wrap it in a mutex, losing all benefit of multithreaded resolution.
*/ */
char *do_resolve(struct in6_addr *addr) { char *do_resolve(struct addr_storage *addr) {
static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
char *s = NULL; char *s = NULL;
struct hostent *he; struct hostent *he;
pthread_mutex_lock(&ghba_mtx); pthread_mutex_lock(&ghba_mtx);
he = gethostbyaddr((char*)addr, sizeof *addr, AF_INET); he = gethostbyaddr((char*)&addr->addr, addr->len, addr->af);
if (he) if (he)
s = xstrdup(he->h_name); s = xstrdup(he->h_name);
pthread_mutex_unlock(&ghba_mtx); pthread_mutex_unlock(&ghba_mtx);
return s; return s;
} }
#elif defined(USE_LIBRESOLV) #elif defined(USE_LIBRESOLV)
#include <arpa/nameser.h> #include <arpa/nameser.h>
@@ -177,14 +177,17 @@ char *do_resolve(struct in6_addr *addr) {
* libresolv implementation * libresolv implementation
* resolver functions may not be thread safe * resolver functions may not be thread safe
*/ */
char* do_resolve(struct in6_addr * addr) { char* do_resolve(struct addr_storage *addr) {
char msg[PACKETSZ]; char msg[PACKETSZ];
char s[35]; char s[35];
int l; int l;
unsigned char* a; unsigned char* a;
char * ret = NULL; char * ret = NULL;
a = (unsigned char*)addr; if (addr->af != AF_INET)
return NULL;
a = (unsigned char*)&addr->addr;
snprintf(s, 35, "%d.%d.%d.%d.in-addr.arpa.",a[3], a[2], a[1], a[0]); snprintf(s, 35, "%d.%d.%d.%d.in-addr.arpa.",a[3], a[2], a[1], a[0]);
@@ -242,7 +245,7 @@ static void do_resolve_ares_callback(void *arg, int status, unsigned char *abuf,
} }
} }
char *do_resolve(struct in6_addr * addr) { char *do_resolve(struct addr_storage * addr) {
struct ares_callback_comm C; struct ares_callback_comm C;
char s[35]; char s[35];
unsigned char *a; unsigned char *a;
@@ -251,6 +254,9 @@ char *do_resolve(struct in6_addr * addr) {
static pthread_key_t ares_key; static pthread_key_t ares_key;
static int gotkey; static int gotkey;
if (addr->af != AF_INET)
return NULL;
/* Make sure we have an ARES channel for this thread. */ /* Make sure we have an ARES channel for this thread. */
pthread_mutex_lock(&ares_init_mtx); pthread_mutex_lock(&ares_init_mtx);
if (!gotkey) { if (!gotkey) {
@@ -267,11 +273,11 @@ char *do_resolve(struct in6_addr * addr) {
if (ares_init(chan) != ARES_SUCCESS) return NULL; if (ares_init(chan) != ARES_SUCCESS) return NULL;
} }
a = (unsigned char*)addr; a = (unsigned char*)&addr->as_addr4;
sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]); sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]);
C.result = 0; C.result = 0;
C.addr = addr; C.addr = &addr->as_addr4;
ares_query(*chan, s, C_IN, T_PTR, do_resolve_ares_callback, &C); ares_query(*chan, s, C_IN, T_PTR, do_resolve_ares_callback, &C);
while (C.result == 0) { while (C.result == 0) {
int n; int n;
@@ -308,13 +314,13 @@ char *do_resolve(struct in6_addr * addr) {
int forking_resolver_worker(int fd) { int forking_resolver_worker(int fd) {
while (1) { while (1) {
struct in_addr a; struct addr_storage a;
struct hostent *he; struct hostent *he;
char buf[NAMESIZE] = {0}; char buf[NAMESIZE] = {0};
if (read(fd, &a, sizeof a) != sizeof a) if (read(fd, &a, sizeof a) != sizeof a)
return -1; return -1;
he = gethostbyaddr((char*)&a, sizeof a, AF_INET); he = gethostbyaddr((char*)&a.addr, a.len, a.af);
if (he) if (he)
strncpy(buf, he->h_name, NAMESIZE - 1); strncpy(buf, he->h_name, NAMESIZE - 1);
@@ -388,7 +394,7 @@ char *do_resolve(struct in6_addr *addr) {
# warning No name resolution method specified; name resolution will not work # warning No name resolution method specified; name resolution will not work
char *do_resolve(struct in6_addr *addr) { char *do_resolve(struct addr_storage *addr) {
return NULL; return NULL;
} }
@@ -406,7 +412,7 @@ void resolver_worker(void* ptr) {
/* Keep resolving until the queue is empty */ /* Keep resolving until the queue is empty */
while(head != tail) { while(head != tail) {
char * hostname; char * hostname;
struct in6_addr addr = resolve_queue[tail]; struct addr_storage addr = resolve_queue[tail];
/* mutex always locked at this point */ /* mutex always locked at this point */
@@ -457,36 +463,47 @@ void resolver_initialise() {
} }
void resolve(int af, struct in6_addr* addr, char* result, int buflen) { void resolve(int af, void* addr, char* result, int buflen) {
char* hostname; char* hostname;
union { union {
char **ch_pp; char **ch_pp;
void **void_pp; void **void_pp;
} u_hostname = { &hostname }; } u_hostname = { &hostname };
int added = 0; int added = 0;
struct addr_storage *raddr;
raddr = malloc(sizeof *raddr);
memset(raddr, 0, sizeof *raddr);
raddr->af = af;
raddr->len = (af == AF_INET ? sizeof(struct in_addr)
: sizeof(struct in6_addr));
memcpy(&raddr->addr, addr, raddr->len);
if(options.dnsresolution == 1) { if(options.dnsresolution == 1) {
pthread_mutex_lock(&resolver_queue_mutex); pthread_mutex_lock(&resolver_queue_mutex);
if(hash_find(ns_hash, addr, u_hostname.void_pp) == HASH_STATUS_OK) { if(hash_find(ns_hash, raddr, u_hostname.void_pp) == HASH_STATUS_OK) {
/* Found => already resolved, or on the queue */ /* Found => already resolved, or on the queue, no need to keep
* it around */
free(raddr);
} }
else { else {
hostname = xmalloc(INET6_ADDRSTRLEN); hostname = xmalloc(INET6_ADDRSTRLEN);
inet_ntop(af, addr, hostname, INET6_ADDRSTRLEN); inet_ntop(af, &raddr->addr, hostname, INET6_ADDRSTRLEN);
hash_insert(ns_hash, addr, hostname);
hash_insert(ns_hash, raddr, hostname);
if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) { if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
/* queue full */ /* queue full */
} }
else if((af == AF_INET6) else if ((af == AF_INET6)
&& (IN6_IS_ADDR_LINKLOCAL(addr) && (IN6_IS_ADDR_LINKLOCAL(&raddr->as_addr6)
|| IN6_IS_ADDR_SITELOCAL(addr))) { || IN6_IS_ADDR_SITELOCAL(&raddr->as_addr6))) {
/* Link-local and site-local stay numerical. */ /* Link-local and site-local stay numerical. */
} }
else { else {
resolve_queue[head] = *addr; resolve_queue[head] = *raddr;
head = (head + 1) % RESOLVE_QUEUE_LENGTH; head = (head + 1) % RESOLVE_QUEUE_LENGTH;
added = 1; added = 1;
} }