7 #include <sys/socket.h>
8 #include <netinet/in.h>
21 #include "threadprof.h"
26 #define RESOLVE_QUEUE_LENGTH 20
29 int af; /* AF_INET or AF_INET6 */
30 int len; /* sizeof(struct in_addr or in6_addr) */
33 struct in6_addr addr6;
35 #define as_addr4 addr.addr4
36 #define as_addr6 addr.addr6
39 struct addr_storage resolve_queue[RESOLVE_QUEUE_LENGTH];
41 pthread_cond_t resolver_queue_cond;
42 pthread_mutex_t resolver_queue_mutex;
49 extern options_t options;
53 * We have a choice of resolver methods. Real computers have getnameinfo or
54 * gethostbyaddr_r, which are reentrant and therefore thread safe. Other
55 * machines don't, and so we can use non-reentrant gethostbyaddr and have only
56 * one resolver thread. Alternatively, we can use the MIT ares asynchronous
57 * DNS library to do this.
60 #if defined(USE_GETNAMEINFO)
62 * Implementation of do_resolve for platforms with getaddrinfo.
64 * This is a fairly sane function with a uniform interface which is even --
65 * shock! -- standardised by POSIX and in RFC 2553. Unfortunately systems such
66 * as NetBSD break the RFC and implement it in a non-thread-safe fashion, so
67 * for the moment, the configure script won't try to use it.
69 char *do_resolve(struct addr_storage *addr) {
70 struct sockaddr_in sin;
71 struct sockaddr_in6 sin6;
72 char buf[NI_MAXHOST]; /* 1025 */
77 sin.sin_family = addr->af;
79 memcpy(&sin.sin_addr, &addr->as_addr4, addr->len);
81 ret = getnameinfo((struct sockaddr*)&sin, sizeof sin,
82 buf, sizeof buf, NULL, 0, NI_NAMEREQD);
85 sin6.sin6_family = addr->af;
87 memcpy(&sin6.sin6_addr, &addr->as_addr6, addr->len);
89 ret = getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
90 buf, sizeof buf, NULL, 0, NI_NAMEREQD);
102 #elif defined(USE_GETHOSTBYADDR_R)
104 * Implementation of do_resolve for platforms with working gethostbyaddr_r
106 * Some implementations of libc choose to implement gethostbyaddr_r as
107 * a non thread-safe wrapper to gethostbyaddr. An interesting choice...
109 char* do_resolve(struct addr_storage *addr) {
110 struct hostent hostbuf, *hp = NULL;
111 size_t hstbuflen = 1024;
117 /* Allocate buffer, remember to free it to avoid memory leakage. */
118 tmphstbuf = xmalloc (hstbuflen);
120 /* nss-myhostname's gethostbyaddr_r() causes an assertion failure if an
121 * "invalid" (as in outside of IPv4 or IPv6) address family is passed */
122 if (addr->af == AF_INET || addr->af == AF_INET6) {
124 /* Some machines have gethostbyaddr_r returning an integer error code; on
125 * others, it returns a struct hostent*. */
126 #ifdef GETHOSTBYADDR_R_RETURNS_INT
127 while ((res = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
128 &hostbuf, tmphstbuf, hstbuflen,
129 &hp, &herr)) == ERANGE)
131 /* ... also assume one fewer argument.... */
132 while ((hp = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
133 &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
138 /* Enlarge the buffer. */
140 tmphstbuf = realloc (tmphstbuf, hstbuflen);
144 /* Check for errors. */
145 if (res || hp == NULL) {
147 /* Leave the unresolved IP in the hash */
150 ret = xstrdup(hp->h_name);
157 #elif defined(USE_GETHOSTBYADDR)
160 * Implementation using gethostbyname. Since this is nonreentrant, we have to
161 * wrap it in a mutex, losing all benefit of multithreaded resolution.
163 char *do_resolve(struct addr_storage *addr) {
164 static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
167 pthread_mutex_lock(&ghba_mtx);
168 he = gethostbyaddr((char*)&addr->addr, addr->len, addr->af);
170 s = xstrdup(he->h_name);
171 pthread_mutex_unlock(&ghba_mtx);
176 #elif defined(USE_LIBRESOLV)
178 #include <arpa/nameser.h>
182 * libresolv implementation
183 * resolver functions may not be thread safe
185 char* do_resolve(struct addr_storage *addr) {
192 if (addr->af != AF_INET)
195 a = (unsigned char*)&addr->addr;
197 snprintf(s, 35, "%d.%d.%d.%d.in-addr.arpa.",a[3], a[2], a[1], a[0]);
199 l = res_search(s, C_IN, T_PTR, msg, PACKETSZ);
203 if(ns_initparse(msg, l, &nsmsg) != -1) {
206 c = ns_msg_count(nsmsg, ns_s_an);
207 for(i = 0; i < c; i++) {
208 if(ns_parserr(&nsmsg, ns_s_an, i, &rr) == 0){
209 if(ns_rr_type(rr) == T_PTR) {
211 ns_name_uncompress(msg, msg + l, ns_rr_rdata(rr), buf, 256);
221 #elif defined(USE_ARES)
224 * ares implementation
227 #include <sys/time.h>
229 #include <arpa/nameser.h>
231 /* callback function for ares */
232 struct ares_callback_comm {
233 struct in_addr *addr;
238 static void do_resolve_ares_callback(void *arg, int status, unsigned char *abuf, int alen) {
240 struct ares_callback_comm *C;
241 C = (struct ares_callback_comm*)arg;
243 if (status == ARES_SUCCESS) {
245 ares_parse_ptr_reply(abuf, alen, C->addr, sizeof *C->addr, AF_INET, &he);
246 C->name = xstrdup(he->h_name);;
247 ares_free_hostent(he);
253 char *do_resolve(struct addr_storage * addr) {
254 struct ares_callback_comm C;
258 static pthread_mutex_t ares_init_mtx = PTHREAD_MUTEX_INITIALIZER;
259 static pthread_key_t ares_key;
262 if (addr->af != AF_INET)
265 /* Make sure we have an ARES channel for this thread. */
266 pthread_mutex_lock(&ares_init_mtx);
268 pthread_key_create(&ares_key, NULL);
272 pthread_mutex_unlock(&ares_init_mtx);
274 chan = pthread_getspecific(ares_key);
276 chan = xmalloc(sizeof *chan);
277 pthread_setspecific(ares_key, chan);
278 if (ares_init(chan) != ARES_SUCCESS) return NULL;
281 a = (unsigned char*)&addr->as_addr4;
282 sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]);
285 C.addr = &addr->as_addr4;
286 ares_query(*chan, s, C_IN, T_PTR, do_resolve_ares_callback, &C);
287 while (C.result == 0) {
289 fd_set readfds, writefds;
293 n = ares_fds(*chan, &readfds, &writefds);
294 ares_timeout(*chan, NULL, &tv);
295 select(n, &readfds, &writefds, NULL, &tv);
296 ares_process(*chan, &readfds, &writefds);
299 /* At this stage, the query should be complete. */
302 case 0: /* shouldn't happen */
310 #elif defined(USE_FORKING_RESOLVER)
313 * Resolver which forks a process, then uses gethostbyname.
320 int forking_resolver_worker(int fd) {
322 struct addr_storage a;
324 char buf[NAMESIZE] = {0};
325 if (read(fd, &a, sizeof a) != sizeof a)
328 he = gethostbyaddr((char*)&a.addr, a.len, a.af);
330 strncpy(buf, he->h_name, NAMESIZE - 1);
332 if (write(fd, buf, NAMESIZE) != NAMESIZE)
337 char *do_resolve(struct in6_addr *addr) {
343 static pthread_mutex_t worker_init_mtx = PTHREAD_MUTEX_INITIALIZER;
344 static pthread_key_t worker_key;
347 /* If no process exists, we need to spawn one. */
348 pthread_mutex_lock(&worker_init_mtx);
350 pthread_key_create(&worker_key, NULL);
353 pthread_mutex_unlock(&worker_init_mtx);
355 workerinfo = pthread_getspecific(worker_key);
359 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
362 workerinfo = xmalloc(sizeof *workerinfo);
363 pthread_setspecific(worker_key, workerinfo);
364 workerinfo->fd = p[0];
366 switch (workerinfo->child = fork()) {
369 _exit(forking_resolver_worker(p[1]));
381 /* Now have a worker to which we can write requests. */
382 if (write(workerinfo->fd, addr, sizeof *addr) != sizeof *addr
383 || read(workerinfo->fd, name, NAMESIZE) != NAMESIZE) {
384 /* Something went wrong. Just kill the child and get on with it. */
385 kill(workerinfo->child, SIGKILL);
387 close(workerinfo->fd);
389 pthread_setspecific(worker_key, NULL);
395 return xstrdup(name);
400 # warning No name resolution method specified; name resolution will not work
402 char *do_resolve(struct addr_storage *addr) {
408 void resolver_worker(void* ptr) {
409 /* int thread_number = *(int*)ptr;*/
410 pthread_mutex_lock(&resolver_queue_mutex);
413 /* Wait until we are told that an address has been added to the
415 pthread_cond_wait(&resolver_queue_cond, &resolver_queue_mutex);
417 /* Keep resolving until the queue is empty */
418 while(head != tail) {
420 struct addr_storage addr = resolve_queue[tail];
422 /* mutex always locked at this point */
424 tail = (tail + 1) % RESOLVE_QUEUE_LENGTH;
426 pthread_mutex_unlock(&resolver_queue_mutex);
428 hostname = do_resolve(&addr);
431 * Store the result in ns_hash
433 pthread_mutex_lock(&resolver_queue_mutex);
435 if(hostname != NULL) {
441 if(hash_find(ns_hash, &addr, u_old.void_pp) == HASH_STATUS_OK) {
442 hash_delete(ns_hash, &addr);
445 hash_insert(ns_hash, &addr, (void*)hostname);
452 void resolver_initialise() {
458 ns_hash = ns_hash_create();
460 pthread_mutex_init(&resolver_queue_mutex, NULL);
461 pthread_cond_init(&resolver_queue_cond, NULL);
463 for(i = 0; i < 2; i++) {
464 n = (int*)xmalloc(sizeof *n);
466 pthread_create(&thread, NULL, (void*)&resolver_worker, (void*)n);
471 void resolve(int af, void* addr, char* result, int buflen) {
476 } u_hostname = { &hostname };
478 struct addr_storage *raddr;
480 if(options.dnsresolution == 1) {
482 raddr = malloc(sizeof *raddr);
483 memset(raddr, 0, sizeof *raddr);
485 raddr->len = (af == AF_INET ? sizeof(struct in_addr)
486 : sizeof(struct in6_addr));
487 memcpy(&raddr->addr, addr, raddr->len);
489 pthread_mutex_lock(&resolver_queue_mutex);
491 if(hash_find(ns_hash, raddr, u_hostname.void_pp) == HASH_STATUS_OK) {
492 /* Found => already resolved, or on the queue, no need to keep
497 hostname = xmalloc(INET6_ADDRSTRLEN);
498 inet_ntop(af, &raddr->addr, hostname, INET6_ADDRSTRLEN);
500 hash_insert(ns_hash, raddr, hostname);
502 if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
505 else if ((af == AF_INET6)
506 && (IN6_IS_ADDR_LINKLOCAL(&raddr->as_addr6)
507 || IN6_IS_ADDR_SITELOCAL(&raddr->as_addr6))) {
508 /* Link-local and site-local stay numerical. */
511 resolve_queue[head] = *raddr;
512 head = (head + 1) % RESOLVE_QUEUE_LENGTH;
516 pthread_mutex_unlock(&resolver_queue_mutex);
519 pthread_cond_signal(&resolver_queue_cond);
522 if(result != NULL && buflen > 1) {
523 strncpy(result, hostname, buflen - 1);
524 result[buflen - 1] = '\0';