7 #include <sys/socket.h>
8 #include <netinet/in.h>
21 #include "threadprof.h"
26 #define RESOLVE_QUEUE_LENGTH 20
28 struct in6_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
30 pthread_cond_t resolver_queue_cond;
31 pthread_mutex_t resolver_queue_mutex;
38 extern options_t options;
42 * We have a choice of resolver methods. Real computers have getnameinfo or
43 * gethostbyaddr_r, which are reentrant and therefore thread safe. Other
44 * machines don't, and so we can use non-reentrant gethostbyaddr and have only
45 * one resolver thread. Alternatively, we can use the MIT ares asynchronous
46 * DNS library to do this.
49 #if defined(USE_GETNAMEINFO)
51 * Implementation of do_resolve for platforms with getaddrinfo.
53 * This is a fairly sane function with a uniform interface which is even --
54 * shock! -- standardised by POSIX and in RFC 2553. Unfortunately systems such
55 * as NetBSD break the RFC and implement it in a non-thread-safe fashion, so
56 * for the moment, the configure script won't try to use it.
58 char *do_resolve(struct in6_addr *addr) {
59 struct sockaddr_in sin;
60 struct sockaddr_in6 sin6;
61 char buf[NI_MAXHOST]; /* 1025 */
65 memset(&sin, '\0', sizeof(sin));
66 memset(&sin6, '\0', sizeof(sin6));
68 /* If the upper three (network byte order) uint32-parts
69 * are null, then there ought to be an IPv4 address here.
70 * Any such IPv6 would have to be 'xxxx::'. Neglectable? */
71 probe = (uint32_t *) addr;
72 af = (probe[1] || probe[2] || probe[3]) ? AF_INET6 : AF_INET;
78 memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));
80 if (getnameinfo((struct sockaddr*)&sin, sizeof sin,
81 buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
87 sin6.sin6_family = af;
89 memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr));
91 if (getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
92 buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
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 in_addr * addr) {
110 struct hostent hostbuf, *hp;
111 size_t hstbuflen = 1024;
117 /* Allocate buffer, remember to free it to avoid memory leakage. */
118 tmphstbuf = xmalloc (hstbuflen);
120 /* Some machines have gethostbyaddr_r returning an integer error code; on
121 * others, it returns a struct hostent*. */
122 #ifdef GETHOSTBYADDR_R_RETURNS_INT
123 while ((res = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET,
124 &hostbuf, tmphstbuf, hstbuflen,
125 &hp, &herr)) == ERANGE)
127 /* ... also assume one fewer argument.... */
128 while ((hp = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET,
129 &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
134 /* Enlarge the buffer. */
136 tmphstbuf = realloc (tmphstbuf, hstbuflen);
139 /* Check for errors. */
140 if (res || hp == NULL) {
142 /* Leave the unresolved IP in the hash */
145 ret = xstrdup(hp->h_name);
152 #elif defined(USE_GETHOSTBYADDR)
155 * Implementation using gethostbyname. Since this is nonreentrant, we have to
156 * wrap it in a mutex, losing all benefit of multithreaded resolution.
158 char *do_resolve(struct in_addr *addr) {
159 static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
162 pthread_mutex_lock(&ghba_mtx);
163 he = gethostbyaddr((char*)addr, sizeof *addr, AF_INET);
165 s = xstrdup(he->h_name);
166 pthread_mutex_unlock(&ghba_mtx);
171 #elif defined(USE_LIBRESOLV)
173 #include <arpa/nameser.h>
177 * libresolv implementation
178 * resolver functions may not be thread safe
180 char* do_resolve(struct in_addr * addr) {
187 a = (unsigned char*)addr;
189 snprintf(s, 35, "%d.%d.%d.%d.in-addr.arpa.",a[3], a[2], a[1], a[0]);
191 l = res_search(s, C_IN, T_PTR, msg, PACKETSZ);
195 if(ns_initparse(msg, l, &nsmsg) != -1) {
198 c = ns_msg_count(nsmsg, ns_s_an);
199 for(i = 0; i < c; i++) {
200 if(ns_parserr(&nsmsg, ns_s_an, i, &rr) == 0){
201 if(ns_rr_type(rr) == T_PTR) {
203 ns_name_uncompress(msg, msg + l, ns_rr_rdata(rr), buf, 256);
213 #elif defined(USE_ARES)
216 * ares implementation
219 #include <sys/time.h>
221 #include <arpa/nameser.h>
223 /* callback function for ares */
224 struct ares_callback_comm {
225 struct in_addr *addr;
230 static void do_resolve_ares_callback(void *arg, int status, unsigned char *abuf, int alen) {
232 struct ares_callback_comm *C;
233 C = (struct ares_callback_comm*)arg;
235 if (status == ARES_SUCCESS) {
237 ares_parse_ptr_reply(abuf, alen, C->addr, sizeof *C->addr, AF_INET, &he);
238 C->name = xstrdup(he->h_name);;
239 ares_free_hostent(he);
245 char *do_resolve(struct in_addr * addr) {
246 struct ares_callback_comm C;
250 static pthread_mutex_t ares_init_mtx = PTHREAD_MUTEX_INITIALIZER;
251 static pthread_key_t ares_key;
254 /* Make sure we have an ARES channel for this thread. */
255 pthread_mutex_lock(&ares_init_mtx);
257 pthread_key_create(&ares_key, NULL);
261 pthread_mutex_unlock(&ares_init_mtx);
263 chan = pthread_getspecific(ares_key);
265 chan = xmalloc(sizeof *chan);
266 pthread_setspecific(ares_key, chan);
267 if (ares_init(chan) != ARES_SUCCESS) return NULL;
270 a = (unsigned char*)addr;
271 sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]);
275 ares_query(*chan, s, C_IN, T_PTR, do_resolve_ares_callback, &C);
276 while (C.result == 0) {
278 fd_set readfds, writefds;
282 n = ares_fds(*chan, &readfds, &writefds);
283 ares_timeout(*chan, NULL, &tv);
284 select(n, &readfds, &writefds, NULL, &tv);
285 ares_process(*chan, &readfds, &writefds);
288 /* At this stage, the query should be complete. */
291 case 0: /* shouldn't happen */
299 #elif defined(USE_FORKING_RESOLVER)
302 * Resolver which forks a process, then uses gethostbyname.
309 int forking_resolver_worker(int fd) {
313 char buf[NAMESIZE] = {0};
314 if (read(fd, &a, sizeof a) != sizeof a)
317 he = gethostbyaddr((char*)&a, sizeof a, AF_INET);
319 strncpy(buf, he->h_name, NAMESIZE - 1);
321 if (write(fd, buf, NAMESIZE) != NAMESIZE)
326 char *do_resolve(struct in_addr *addr) {
332 static pthread_mutex_t worker_init_mtx = PTHREAD_MUTEX_INITIALIZER;
333 static pthread_key_t worker_key;
336 /* If no process exists, we need to spawn one. */
337 pthread_mutex_lock(&worker_init_mtx);
339 pthread_key_create(&worker_key, NULL);
342 pthread_mutex_unlock(&worker_init_mtx);
344 workerinfo = pthread_getspecific(worker_key);
348 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
351 workerinfo = xmalloc(sizeof *workerinfo);
352 pthread_setspecific(worker_key, workerinfo);
353 workerinfo->fd = p[0];
355 switch (workerinfo->child = fork()) {
358 _exit(forking_resolver_worker(p[1]));
370 /* Now have a worker to which we can write requests. */
371 if (write(workerinfo->fd, addr, sizeof *addr) != sizeof *addr
372 || read(workerinfo->fd, name, NAMESIZE) != NAMESIZE) {
373 /* Something went wrong. Just kill the child and get on with it. */
374 kill(workerinfo->child, SIGKILL);
376 close(workerinfo->fd);
378 pthread_setspecific(worker_key, NULL);
384 return xstrdup(name);
389 # warning No name resolution method specified; name resolution will not work
391 char *do_resolve(struct in_addr *addr) {
397 void resolver_worker(void* ptr) {
398 /* int thread_number = *(int*)ptr;*/
399 pthread_mutex_lock(&resolver_queue_mutex);
402 /* Wait until we are told that an address has been added to the
404 pthread_cond_wait(&resolver_queue_cond, &resolver_queue_mutex);
406 /* Keep resolving until the queue is empty */
407 while(head != tail) {
409 struct in6_addr addr = resolve_queue[tail];
411 /* mutex always locked at this point */
413 tail = (tail + 1) % RESOLVE_QUEUE_LENGTH;
415 pthread_mutex_unlock(&resolver_queue_mutex);
417 hostname = do_resolve(&addr);
420 * Store the result in ns_hash
422 pthread_mutex_lock(&resolver_queue_mutex);
424 if(hostname != NULL) {
430 if(hash_find(ns_hash, &addr, u_old.void_pp) == HASH_STATUS_OK) {
431 hash_delete(ns_hash, &addr);
434 hash_insert(ns_hash, &addr, (void*)hostname);
441 void resolver_initialise() {
447 ns_hash = ns_hash_create();
449 pthread_mutex_init(&resolver_queue_mutex, NULL);
450 pthread_cond_init(&resolver_queue_cond, NULL);
452 for(i = 0; i < 2; i++) {
453 n = (int*)xmalloc(sizeof *n);
455 pthread_create(&thread, NULL, (void*)&resolver_worker, (void*)n);
460 void resolve(int af, struct in6_addr* addr, char* result, int buflen) {
465 } u_hostname = { &hostname };
468 if(options.dnsresolution == 1) {
470 pthread_mutex_lock(&resolver_queue_mutex);
472 if(hash_find(ns_hash, addr, u_hostname.void_pp) == HASH_STATUS_OK) {
473 /* Found => already resolved, or on the queue */
476 hostname = xmalloc(INET6_ADDRSTRLEN);
477 inet_ntop(af, addr, hostname, INET6_ADDRSTRLEN);
478 hash_insert(ns_hash, addr, hostname);
480 if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
483 else if((af == AF_INET6)
484 && (IN6_IS_ADDR_LINKLOCAL(addr)
485 || IN6_IS_ADDR_SITELOCAL(addr))) {
486 /* Link-local and site-local stay numerical. */
489 resolve_queue[head] = *addr;
490 head = (head + 1) % RESOLVE_QUEUE_LENGTH;
494 pthread_mutex_unlock(&resolver_queue_mutex);
497 pthread_cond_signal(&resolver_queue_cond);
500 if(result != NULL && buflen > 1) {
501 strncpy(result, hostname, buflen - 1);
502 result[buflen - 1] = '\0';