diff --git a/resolver.c b/resolver.c index 17aa9fe..aa47bdc 100644 --- a/resolver.c +++ b/resolver.c @@ -37,6 +37,22 @@ int tail; extern options_t options; +int guess_af(struct in6_addr *addr) +{ + /* 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? */ + uint32_t* probe; + int af; + + probe = (uint32_t *) addr; + return (probe[1] || probe[2] || probe[3]) ? AF_INET6 : AF_INET; +} + +socklen_t aflength(int af) +{ + return af == AF_INET6 ? sizeof(struct in6_addr) : sizeof(struct in_addr); +} /* * We have a choice of resolver methods. Real computers have getnameinfo or @@ -60,18 +76,11 @@ char *do_resolve(struct in6_addr *addr) { struct sockaddr_in6 sin6; char buf[NI_MAXHOST]; /* 1025 */ int res, af; - uint32_t* probe; memset(&sin, '\0', sizeof(sin)); 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) { + switch (guess_af(addr)) { case AF_INET: sin.sin_family = af; sin.sin_port = 0; @@ -106,26 +115,29 @@ char *do_resolve(struct in6_addr *addr) { * Some implementations of libc choose to implement gethostbyaddr_r as * a non thread-safe wrapper to gethostbyaddr. An interesting choice... */ -char* do_resolve(struct in_addr * addr) { +char* do_resolve(struct in6_addr * addr) { struct hostent hostbuf, *hp; size_t hstbuflen = 1024; char *tmphstbuf; int res; int herr; + int af; char * ret = NULL; /* Allocate buffer, remember to free it to avoid memory leakage. */ tmphstbuf = xmalloc (hstbuflen); + af = guess_af(addr); + /* Some machines have gethostbyaddr_r returning an integer error code; on * others, it returns a struct hostent*. */ #ifdef GETHOSTBYADDR_R_RETURNS_INT - while ((res = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET, + while ((res = gethostbyaddr_r((char*)addr, aflength(af), af, &hostbuf, tmphstbuf, hstbuflen, &hp, &herr)) == ERANGE) #else /* ... also assume one fewer argument.... */ - while ((hp = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET, + while ((hp = gethostbyaddr_r((char*)addr, aflength(af), af, &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL && errno == ERANGE) #endif @@ -152,22 +164,24 @@ char* do_resolve(struct in_addr * addr) { #elif defined(USE_GETHOSTBYADDR) /** - * Implementation using gethostbyname. Since this is nonreentrant, we have to + * Implementation using gethostbyaddr. Since this is nonreentrant, we have to * wrap it in a mutex, losing all benefit of multithreaded resolution. */ -char *do_resolve(struct in_addr *addr) { +char *do_resolve(struct in6_addr *addr) { static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER; char *s = NULL; struct hostent *he; + int af; + af = guess_af(addr); + pthread_mutex_lock(&ghba_mtx); - he = gethostbyaddr((char*)addr, sizeof *addr, AF_INET); + he = gethostbyaddr((char*)addr, aflength(af), af); if (he) s = xstrdup(he->h_name); pthread_mutex_unlock(&ghba_mtx); return s; } - #elif defined(USE_LIBRESOLV) #include @@ -299,7 +313,7 @@ char *do_resolve(struct in_addr * addr) { #elif defined(USE_FORKING_RESOLVER) /** - * Resolver which forks a process, then uses gethostbyname. + * Resolver which forks a process, then uses gethostbyaddr. */ #include @@ -308,13 +322,15 @@ char *do_resolve(struct in_addr * addr) { int forking_resolver_worker(int fd) { while (1) { - struct in_addr a; + struct in6_addr a; struct hostent *he; + int af; char buf[NAMESIZE] = {0}; - if (read(fd, &a, sizeof a) != sizeof a) + if (read(fd, &af, sizeof af) != sizeof af + || read(fd, &a, aflength(af)) != aflength(af)) return -1; - he = gethostbyaddr((char*)&a, sizeof a, AF_INET); + he = gethostbyaddr((char*)&a, aflength(af), af); if (he) strncpy(buf, he->h_name, NAMESIZE - 1); @@ -323,12 +339,13 @@ int forking_resolver_worker(int fd) { } } -char *do_resolve(struct in_addr *addr) { +char *do_resolve(struct in6_addr *addr) { struct { int fd; pid_t child; } *workerinfo; char name[NAMESIZE]; + int af; static pthread_mutex_t worker_init_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_key_t worker_key; static int gotkey; @@ -368,7 +385,9 @@ char *do_resolve(struct in_addr *addr) { } /* Now have a worker to which we can write requests. */ - if (write(workerinfo->fd, addr, sizeof *addr) != sizeof *addr + af = guess_af(addr); + if (write(workerinfo->fd, &af, sizeof af) != sizeof af + || write(workerinfo->fd, addr, aflength(af)) != aflength(af) || read(workerinfo->fd, name, NAMESIZE) != NAMESIZE) { /* Something went wrong. Just kill the child and get on with it. */ kill(workerinfo->child, SIGKILL);