123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /*
- * resolver.c:
- *
- */
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <netdb.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include "ns_hash.h"
- #include "iftop.h"
- #include "threadprof.h"
- #include "options.h"
- #define RESOLVE_QUEUE_LENGTH 20
- 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_mutex_t resolver_queue_mutex;
- hash_type* ns_hash;
- int head;
- int tail;
- extern options_t options;
- /*
- * We have a choice of resolver methods. Real computers have getnameinfo or
- * gethostbyaddr_r, which are reentrant and therefore thread safe. Other
- * machines don't, and so we can use non-reentrant gethostbyaddr and have only
- * one resolver thread. Alternatively, we can use the MIT ares asynchronous
- * DNS library to do this.
- */
- #if defined(USE_GETNAMEINFO)
- /**
- * Implementation of do_resolve for platforms with getaddrinfo.
- *
- * This is a fairly sane function with a uniform interface which is even --
- * shock! -- standardised by POSIX and in RFC 2553. Unfortunately systems such
- * 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.
- */
- char *do_resolve(struct addr_storage *addr) {
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
- char buf[NI_MAXHOST]; /* 1025 */
- int ret;
- switch (addr->af) {
- case AF_INET:
- sin.sin_family = addr->af;
- sin.sin_port = 0;
- memcpy(&sin.sin_addr, &addr->as_addr4, addr->len);
- ret = getnameinfo((struct sockaddr*)&sin, sizeof sin,
- buf, sizeof buf, NULL, 0, NI_NAMEREQD);
- break;
- case AF_INET6:
- sin6.sin6_family = addr->af;
- sin6.sin6_port = 0;
- memcpy(&sin6.sin6_addr, &addr->as_addr6, addr->len);
- ret = getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
- buf, sizeof buf, NULL, 0, NI_NAMEREQD);
- break;
- default:
- return NULL;
- }
- if (ret == 0)
- return xstrdup(buf);
- else
- return NULL;
- }
- #elif defined(USE_GETHOSTBYADDR_R)
- /**
- * Implementation of do_resolve for platforms with working gethostbyaddr_r
- *
- * Some implementations of libc choose to implement gethostbyaddr_r as
- * a non thread-safe wrapper to gethostbyaddr. An interesting choice...
- */
- char* do_resolve(struct addr_storage *addr) {
- struct hostent hostbuf, *hp = NULL;
- size_t hstbuflen = 1024;
- char *tmphstbuf;
- int res = 0;
- int herr;
- char * ret = NULL;
- /* Allocate buffer, remember to free it to avoid memory leakage. */
- tmphstbuf = xmalloc (hstbuflen);
- /* nss-myhostname's gethostbyaddr_r() causes an assertion failure if an
- * "invalid" (as in outside of IPv4 or IPv6) address family is passed */
- if (addr->af == AF_INET || addr->af == AF_INET6) {
- /* 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->addr, addr->len, addr->af,
- &hostbuf, tmphstbuf, hstbuflen,
- &hp, &herr)) == ERANGE)
- #else
- /* ... also assume one fewer argument.... */
- while ((hp = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
- &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
- && errno == ERANGE)
- #endif
- {
-
- /* Enlarge the buffer. */
- hstbuflen *= 2;
- tmphstbuf = realloc (tmphstbuf, hstbuflen);
- }
- }
- /* Check for errors. */
- if (res || hp == NULL) {
- /* failed */
- /* Leave the unresolved IP in the hash */
- }
- else {
- ret = xstrdup(hp->h_name);
- }
- xfree(tmphstbuf);
- return ret;
- }
- #elif defined(USE_GETHOSTBYADDR)
- /**
- * Implementation using gethostbyname. Since this is nonreentrant, we have to
- * wrap it in a mutex, losing all benefit of multithreaded resolution.
- */
- char *do_resolve(struct addr_storage *addr) {
- static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
- char *s = NULL;
- struct hostent *he;
- pthread_mutex_lock(&ghba_mtx);
- he = gethostbyaddr((char*)&addr->addr, addr->len, addr->af);
- if (he)
- s = xstrdup(he->h_name);
- pthread_mutex_unlock(&ghba_mtx);
- return s;
- }
- #elif defined(USE_LIBRESOLV)
- #include <arpa/nameser.h>
- #include <resolv.h>
- /**
- * libresolv implementation
- * resolver functions may not be thread safe
- */
- char* do_resolve(struct addr_storage *addr) {
- char msg[PACKETSZ];
- char s[35];
- int l;
- unsigned char* a;
- char * ret = NULL;
- 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]);
- l = res_search(s, C_IN, T_PTR, msg, PACKETSZ);
- if(l != -1) {
- ns_msg nsmsg;
- ns_rr rr;
- if(ns_initparse(msg, l, &nsmsg) != -1) {
- int c;
- int i;
- c = ns_msg_count(nsmsg, ns_s_an);
- for(i = 0; i < c; i++) {
- if(ns_parserr(&nsmsg, ns_s_an, i, &rr) == 0){
- if(ns_rr_type(rr) == T_PTR) {
- char buf[256];
- ns_name_uncompress(msg, msg + l, ns_rr_rdata(rr), buf, 256);
- ret = xstrdup(buf);
- }
- }
- }
- }
- }
- return ret;
- }
- #elif defined(USE_ARES)
- /**
- * ares implementation
- */
- #include <sys/time.h>
- #include <ares.h>
- #include <arpa/nameser.h>
- /* callback function for ares */
- struct ares_callback_comm {
- struct in_addr *addr;
- int result;
- char *name;
- };
- static void do_resolve_ares_callback(void *arg, int status, unsigned char *abuf, int alen) {
- struct hostent *he;
- struct ares_callback_comm *C;
- C = (struct ares_callback_comm*)arg;
- if (status == ARES_SUCCESS) {
- C->result = 1;
- ares_parse_ptr_reply(abuf, alen, C->addr, sizeof *C->addr, AF_INET, &he);
- C->name = xstrdup(he->h_name);;
- ares_free_hostent(he);
- } else {
- C->result = -1;
- }
- }
- char *do_resolve(struct addr_storage * addr) {
- struct ares_callback_comm C;
- char s[35];
- unsigned char *a;
- ares_channel *chan;
- static pthread_mutex_t ares_init_mtx = PTHREAD_MUTEX_INITIALIZER;
- static pthread_key_t ares_key;
- static int gotkey;
- if (addr->af != AF_INET)
- return NULL;
- /* Make sure we have an ARES channel for this thread. */
- pthread_mutex_lock(&ares_init_mtx);
- if (!gotkey) {
- pthread_key_create(&ares_key, NULL);
- gotkey = 1;
-
- }
- pthread_mutex_unlock(&ares_init_mtx);
-
- chan = pthread_getspecific(ares_key);
- if (!chan) {
- chan = xmalloc(sizeof *chan);
- pthread_setspecific(ares_key, chan);
- if (ares_init(chan) != ARES_SUCCESS) return NULL;
- }
-
- a = (unsigned char*)&addr->as_addr4;
- sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]);
-
- C.result = 0;
- C.addr = &addr->as_addr4;
- ares_query(*chan, s, C_IN, T_PTR, do_resolve_ares_callback, &C);
- while (C.result == 0) {
- int n;
- fd_set readfds, writefds;
- struct timeval tv;
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
- n = ares_fds(*chan, &readfds, &writefds);
- ares_timeout(*chan, NULL, &tv);
- select(n, &readfds, &writefds, NULL, &tv);
- ares_process(*chan, &readfds, &writefds);
- }
- /* At this stage, the query should be complete. */
- switch (C.result) {
- case -1:
- case 0: /* shouldn't happen */
- return NULL;
- default:
- return C.name;
- }
- }
- #elif defined(USE_FORKING_RESOLVER)
- /**
- * Resolver which forks a process, then uses gethostbyname.
- */
- #include <signal.h>
- #define NAMESIZE 64
- int forking_resolver_worker(int fd) {
- while (1) {
- struct addr_storage a;
- struct hostent *he;
- char buf[NAMESIZE] = {0};
- if (read(fd, &a, sizeof a) != sizeof a)
- return -1;
- he = gethostbyaddr((char*)&a.addr, a.len, a.af);
- if (he)
- strncpy(buf, he->h_name, NAMESIZE - 1);
- if (write(fd, buf, NAMESIZE) != NAMESIZE)
- return -1;
- }
- }
- char *do_resolve(struct in6_addr *addr) {
- struct {
- int fd;
- pid_t child;
- } *workerinfo;
- char name[NAMESIZE];
- static pthread_mutex_t worker_init_mtx = PTHREAD_MUTEX_INITIALIZER;
- static pthread_key_t worker_key;
- static int gotkey;
- /* If no process exists, we need to spawn one. */
- pthread_mutex_lock(&worker_init_mtx);
- if (!gotkey) {
- pthread_key_create(&worker_key, NULL);
- gotkey = 1;
- }
- pthread_mutex_unlock(&worker_init_mtx);
-
- workerinfo = pthread_getspecific(worker_key);
- if (!workerinfo) {
- int p[2];
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
- return NULL;
- workerinfo = xmalloc(sizeof *workerinfo);
- pthread_setspecific(worker_key, workerinfo);
- workerinfo->fd = p[0];
-
- switch (workerinfo->child = fork()) {
- case 0:
- close(p[0]);
- _exit(forking_resolver_worker(p[1]));
- case -1:
- close(p[0]);
- close(p[1]);
- return NULL;
- default:
- close(p[1]);
- }
- }
- /* Now have a worker to which we can write requests. */
- if (write(workerinfo->fd, addr, sizeof *addr) != sizeof *addr
- || read(workerinfo->fd, name, NAMESIZE) != NAMESIZE) {
- /* Something went wrong. Just kill the child and get on with it. */
- kill(workerinfo->child, SIGKILL);
- wait(NULL);
- close(workerinfo->fd);
- xfree(workerinfo);
- pthread_setspecific(worker_key, NULL);
- *name = 0;
- }
- if (!*name)
- return NULL;
- else
- return xstrdup(name);
- }
- #else
- # warning No name resolution method specified; name resolution will not work
- char *do_resolve(struct addr_storage *addr) {
- return NULL;
- }
- #endif
- void resolver_worker(void* ptr) {
- /* int thread_number = *(int*)ptr;*/
- pthread_mutex_lock(&resolver_queue_mutex);
- sethostent(1);
- while(1) {
- /* Wait until we are told that an address has been added to the
- * queue. */
- pthread_cond_wait(&resolver_queue_cond, &resolver_queue_mutex);
- /* Keep resolving until the queue is empty */
- while(head != tail) {
- char * hostname;
- struct addr_storage addr = resolve_queue[tail];
- /* mutex always locked at this point */
- tail = (tail + 1) % RESOLVE_QUEUE_LENGTH;
- pthread_mutex_unlock(&resolver_queue_mutex);
- hostname = do_resolve(&addr);
- /*
- * Store the result in ns_hash
- */
- pthread_mutex_lock(&resolver_queue_mutex);
- if(hostname != NULL) {
- char* old;
- union {
- char **ch_pp;
- void **void_pp;
- } u_old = { &old };
- if(hash_find(ns_hash, &addr, u_old.void_pp) == HASH_STATUS_OK) {
- hash_delete(ns_hash, &addr);
- xfree(old);
- }
- hash_insert(ns_hash, &addr, (void*)hostname);
- }
- }
- }
- }
- void resolver_initialise() {
- int* n;
- int i;
- pthread_t thread;
- head = tail = 0;
- ns_hash = ns_hash_create();
-
- pthread_mutex_init(&resolver_queue_mutex, NULL);
- pthread_cond_init(&resolver_queue_cond, NULL);
- for(i = 0; i < 2; i++) {
- n = (int*)xmalloc(sizeof *n);
- *n = i;
- pthread_create(&thread, NULL, (void*)&resolver_worker, (void*)n);
- }
- }
- void resolve(int af, void* addr, char* result, int buflen) {
- char* hostname;
- union {
- char **ch_pp;
- void **void_pp;
- } u_hostname = { &hostname };
- int added = 0;
- struct addr_storage *raddr;
- if(options.dnsresolution == 1) {
- 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);
- pthread_mutex_lock(&resolver_queue_mutex);
- if(hash_find(ns_hash, raddr, u_hostname.void_pp) == HASH_STATUS_OK) {
- /* Found => already resolved, or on the queue, no need to keep
- * it around */
- free(raddr);
- }
- else {
- hostname = xmalloc(INET6_ADDRSTRLEN);
- inet_ntop(af, &raddr->addr, hostname, INET6_ADDRSTRLEN);
- hash_insert(ns_hash, raddr, hostname);
- if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
- /* queue full */
- }
- else if ((af == AF_INET6)
- && (IN6_IS_ADDR_LINKLOCAL(&raddr->as_addr6)
- || IN6_IS_ADDR_SITELOCAL(&raddr->as_addr6))) {
- /* Link-local and site-local stay numerical. */
- }
- else {
- resolve_queue[head] = *raddr;
- head = (head + 1) % RESOLVE_QUEUE_LENGTH;
- added = 1;
- }
- }
- pthread_mutex_unlock(&resolver_queue_mutex);
- if(added == 1) {
- pthread_cond_signal(&resolver_queue_cond);
- }
- if(result != NULL && buflen > 1) {
- strncpy(result, hostname, buflen - 1);
- result[buflen - 1] = '\0';
- }
- }
- }
|