Annotation of embedaddon/iftop/resolver.c, revision 1.1
1.1 ! misho 1: /*
! 2: * resolver.c:
! 3: *
! 4: */
! 5:
! 6: #include <sys/types.h>
! 7: #include <sys/socket.h>
! 8: #include <netinet/in.h>
! 9: #include <arpa/inet.h>
! 10: #include <pthread.h>
! 11: #include <stdio.h>
! 12: #include <stdlib.h>
! 13: #include <netdb.h>
! 14: #include <errno.h>
! 15: #include <string.h>
! 16: #include <unistd.h>
! 17:
! 18: #include "ns_hash.h"
! 19: #include "iftop.h"
! 20:
! 21: #include "threadprof.h"
! 22:
! 23: #include "options.h"
! 24:
! 25:
! 26: #define RESOLVE_QUEUE_LENGTH 20
! 27:
! 28: struct in_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
! 29:
! 30: pthread_cond_t resolver_queue_cond;
! 31: pthread_mutex_t resolver_queue_mutex;
! 32:
! 33: hash_type* ns_hash;
! 34:
! 35: int head;
! 36: int tail;
! 37:
! 38: extern options_t options;
! 39:
! 40:
! 41: /*
! 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.
! 47: */
! 48:
! 49: #if defined(USE_GETNAMEINFO)
! 50: /**
! 51: * Implementation of do_resolve for platforms with getaddrinfo.
! 52: *
! 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.
! 57: */
! 58: char *do_resolve(struct in_addr *addr) {
! 59: struct sockaddr_in sin = {0};
! 60: char buf[NI_MAXHOST]; /* 1025 */
! 61: int res;
! 62: sin.sin_family = AF_INET;
! 63: sin.sin_addr = *addr;
! 64: sin.sin_port = 0;
! 65:
! 66: if (getnameinfo((struct sockaddr*)&sin, sizeof sin, buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
! 67: return xstrdup(buf);
! 68: else
! 69: return NULL;
! 70: }
! 71:
! 72: #elif defined(USE_GETHOSTBYADDR_R)
! 73: /**
! 74: * Implementation of do_resolve for platforms with working gethostbyaddr_r
! 75: *
! 76: * Some implementations of libc choose to implement gethostbyaddr_r as
! 77: * a non thread-safe wrapper to gethostbyaddr. An interesting choice...
! 78: */
! 79: char* do_resolve(struct in_addr * addr) {
! 80: struct hostent hostbuf, *hp;
! 81: size_t hstbuflen = 1024;
! 82: char *tmphstbuf;
! 83: int res;
! 84: int herr;
! 85: char * ret = NULL;
! 86:
! 87: /* Allocate buffer, remember to free it to avoid memory leakage. */
! 88: tmphstbuf = xmalloc (hstbuflen);
! 89:
! 90: /* Some machines have gethostbyaddr_r returning an integer error code; on
! 91: * others, it returns a struct hostent*. */
! 92: #ifdef GETHOSTBYADDR_R_RETURNS_INT
! 93: while ((res = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET,
! 94: &hostbuf, tmphstbuf, hstbuflen,
! 95: &hp, &herr)) == ERANGE)
! 96: #else
! 97: /* ... also assume one fewer argument.... */
! 98: while ((hp = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET,
! 99: &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
! 100: && errno == ERANGE)
! 101: #endif
! 102: {
! 103:
! 104: /* Enlarge the buffer. */
! 105: hstbuflen *= 2;
! 106: tmphstbuf = realloc (tmphstbuf, hstbuflen);
! 107: }
! 108:
! 109: /* Check for errors. */
! 110: if (res || hp == NULL) {
! 111: /* failed */
! 112: /* Leave the unresolved IP in the hash */
! 113: }
! 114: else {
! 115: ret = xstrdup(hp->h_name);
! 116:
! 117: }
! 118: xfree(tmphstbuf);
! 119: return ret;
! 120: }
! 121:
! 122: #elif defined(USE_GETHOSTBYADDR)
! 123:
! 124: /**
! 125: * Implementation using gethostbyname. Since this is nonreentrant, we have to
! 126: * wrap it in a mutex, losing all benefit of multithreaded resolution.
! 127: */
! 128: char *do_resolve(struct in_addr *addr) {
! 129: static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
! 130: char *s = NULL;
! 131: struct hostent *he;
! 132: pthread_mutex_lock(&ghba_mtx);
! 133: he = gethostbyaddr((char*)addr, sizeof *addr, AF_INET);
! 134: if (he)
! 135: s = xstrdup(he->h_name);
! 136: pthread_mutex_unlock(&ghba_mtx);
! 137: return s;
! 138: }
! 139:
! 140:
! 141: #elif defined(USE_LIBRESOLV)
! 142:
! 143: #include <arpa/nameser.h>
! 144: #include <resolv.h>
! 145:
! 146: /**
! 147: * libresolv implementation
! 148: * resolver functions may not be thread safe
! 149: */
! 150: char* do_resolve(struct in_addr * addr) {
! 151: char msg[PACKETSZ];
! 152: char s[35];
! 153: int l;
! 154: unsigned char* a;
! 155: char * ret = NULL;
! 156:
! 157: a = (unsigned char*)addr;
! 158:
! 159: snprintf(s, 35, "%d.%d.%d.%d.in-addr.arpa.",a[3], a[2], a[1], a[0]);
! 160:
! 161: l = res_search(s, C_IN, T_PTR, msg, PACKETSZ);
! 162: if(l != -1) {
! 163: ns_msg nsmsg;
! 164: ns_rr rr;
! 165: if(ns_initparse(msg, l, &nsmsg) != -1) {
! 166: int c;
! 167: int i;
! 168: c = ns_msg_count(nsmsg, ns_s_an);
! 169: for(i = 0; i < c; i++) {
! 170: if(ns_parserr(&nsmsg, ns_s_an, i, &rr) == 0){
! 171: if(ns_rr_type(rr) == T_PTR) {
! 172: char buf[256];
! 173: ns_name_uncompress(msg, msg + l, ns_rr_rdata(rr), buf, 256);
! 174: ret = xstrdup(buf);
! 175: }
! 176: }
! 177: }
! 178: }
! 179: }
! 180: return ret;
! 181: }
! 182:
! 183: #elif defined(USE_ARES)
! 184:
! 185: /**
! 186: * ares implementation
! 187: */
! 188:
! 189: #include <sys/time.h>
! 190: #include <ares.h>
! 191: #include <arpa/nameser.h>
! 192:
! 193: /* callback function for ares */
! 194: struct ares_callback_comm {
! 195: struct in_addr *addr;
! 196: int result;
! 197: char *name;
! 198: };
! 199:
! 200: static void do_resolve_ares_callback(void *arg, int status, unsigned char *abuf, int alen) {
! 201: struct hostent *he;
! 202: struct ares_callback_comm *C;
! 203: C = (struct ares_callback_comm*)arg;
! 204:
! 205: if (status == ARES_SUCCESS) {
! 206: C->result = 1;
! 207: ares_parse_ptr_reply(abuf, alen, C->addr, sizeof *C->addr, AF_INET, &he);
! 208: C->name = xstrdup(he->h_name);;
! 209: ares_free_hostent(he);
! 210: } else {
! 211: C->result = -1;
! 212: }
! 213: }
! 214:
! 215: char *do_resolve(struct in_addr * addr) {
! 216: struct ares_callback_comm C;
! 217: char s[35];
! 218: unsigned char *a;
! 219: ares_channel *chan;
! 220: static pthread_mutex_t ares_init_mtx = PTHREAD_MUTEX_INITIALIZER;
! 221: static pthread_key_t ares_key;
! 222: static int gotkey;
! 223:
! 224: /* Make sure we have an ARES channel for this thread. */
! 225: pthread_mutex_lock(&ares_init_mtx);
! 226: if (!gotkey) {
! 227: pthread_key_create(&ares_key, NULL);
! 228: gotkey = 1;
! 229:
! 230: }
! 231: pthread_mutex_unlock(&ares_init_mtx);
! 232:
! 233: chan = pthread_getspecific(ares_key);
! 234: if (!chan) {
! 235: chan = xmalloc(sizeof *chan);
! 236: pthread_setspecific(ares_key, chan);
! 237: if (ares_init(chan) != ARES_SUCCESS) return NULL;
! 238: }
! 239:
! 240: a = (unsigned char*)addr;
! 241: sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]);
! 242:
! 243: C.result = 0;
! 244: C.addr = addr;
! 245: ares_query(*chan, s, C_IN, T_PTR, do_resolve_ares_callback, &C);
! 246: while (C.result == 0) {
! 247: int n;
! 248: fd_set readfds, writefds;
! 249: struct timeval tv;
! 250: FD_ZERO(&readfds);
! 251: FD_ZERO(&writefds);
! 252: n = ares_fds(*chan, &readfds, &writefds);
! 253: ares_timeout(*chan, NULL, &tv);
! 254: select(n, &readfds, &writefds, NULL, &tv);
! 255: ares_process(*chan, &readfds, &writefds);
! 256: }
! 257:
! 258: /* At this stage, the query should be complete. */
! 259: switch (C.result) {
! 260: case -1:
! 261: case 0: /* shouldn't happen */
! 262: return NULL;
! 263:
! 264: default:
! 265: return C.name;
! 266: }
! 267: }
! 268:
! 269: #elif defined(USE_FORKING_RESOLVER)
! 270:
! 271: /**
! 272: * Resolver which forks a process, then uses gethostbyname.
! 273: */
! 274:
! 275: #include <signal.h>
! 276:
! 277: #define NAMESIZE 64
! 278:
! 279: int forking_resolver_worker(int fd) {
! 280: while (1) {
! 281: struct in_addr a;
! 282: struct hostent *he;
! 283: char buf[NAMESIZE] = {0};
! 284: if (read(fd, &a, sizeof a) != sizeof a)
! 285: return -1;
! 286:
! 287: he = gethostbyaddr((char*)&a, sizeof a, AF_INET);
! 288: if (he)
! 289: strncpy(buf, he->h_name, NAMESIZE - 1);
! 290:
! 291: if (write(fd, buf, NAMESIZE) != NAMESIZE)
! 292: return -1;
! 293: }
! 294: }
! 295:
! 296: char *do_resolve(struct in_addr *addr) {
! 297: struct {
! 298: int fd;
! 299: pid_t child;
! 300: } *workerinfo;
! 301: char name[NAMESIZE];
! 302: static pthread_mutex_t worker_init_mtx = PTHREAD_MUTEX_INITIALIZER;
! 303: static pthread_key_t worker_key;
! 304: static int gotkey;
! 305:
! 306: /* If no process exists, we need to spawn one. */
! 307: pthread_mutex_lock(&worker_init_mtx);
! 308: if (!gotkey) {
! 309: pthread_key_create(&worker_key, NULL);
! 310: gotkey = 1;
! 311: }
! 312: pthread_mutex_unlock(&worker_init_mtx);
! 313:
! 314: workerinfo = pthread_getspecific(worker_key);
! 315: if (!workerinfo) {
! 316: int p[2];
! 317:
! 318: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
! 319: return NULL;
! 320:
! 321: workerinfo = xmalloc(sizeof *workerinfo);
! 322: pthread_setspecific(worker_key, workerinfo);
! 323: workerinfo->fd = p[0];
! 324:
! 325: switch (workerinfo->child = fork()) {
! 326: case 0:
! 327: close(p[0]);
! 328: _exit(forking_resolver_worker(p[1]));
! 329:
! 330: case -1:
! 331: close(p[0]);
! 332: close(p[1]);
! 333: return NULL;
! 334:
! 335: default:
! 336: close(p[1]);
! 337: }
! 338: }
! 339:
! 340: /* Now have a worker to which we can write requests. */
! 341: if (write(workerinfo->fd, addr, sizeof *addr) != sizeof *addr
! 342: || read(workerinfo->fd, name, NAMESIZE) != NAMESIZE) {
! 343: /* Something went wrong. Just kill the child and get on with it. */
! 344: kill(workerinfo->child, SIGKILL);
! 345: wait(NULL);
! 346: close(workerinfo->fd);
! 347: xfree(workerinfo);
! 348: pthread_setspecific(worker_key, NULL);
! 349: *name = 0;
! 350: }
! 351: if (!*name)
! 352: return NULL;
! 353: else
! 354: return xstrdup(name);
! 355: }
! 356:
! 357: #else
! 358:
! 359: # warning No name resolution method specified; name resolution will not work
! 360:
! 361: char *do_resolve(struct in_addr *addr) {
! 362: return NULL;
! 363: }
! 364:
! 365: #endif
! 366:
! 367: void resolver_worker(void* ptr) {
! 368: /* int thread_number = *(int*)ptr;*/
! 369: pthread_mutex_lock(&resolver_queue_mutex);
! 370: sethostent(1);
! 371: while(1) {
! 372: /* Wait until we are told that an address has been added to the
! 373: * queue. */
! 374: pthread_cond_wait(&resolver_queue_cond, &resolver_queue_mutex);
! 375:
! 376: /* Keep resolving until the queue is empty */
! 377: while(head != tail) {
! 378: char * hostname;
! 379: struct in_addr addr = resolve_queue[tail];
! 380:
! 381: /* mutex always locked at this point */
! 382:
! 383: tail = (tail + 1) % RESOLVE_QUEUE_LENGTH;
! 384:
! 385: pthread_mutex_unlock(&resolver_queue_mutex);
! 386:
! 387: hostname = do_resolve(&addr);
! 388:
! 389: /*
! 390: * Store the result in ns_hash
! 391: */
! 392: pthread_mutex_lock(&resolver_queue_mutex);
! 393:
! 394: if(hostname != NULL) {
! 395: char* old;
! 396: union {
! 397: char **ch_pp;
! 398: void **void_pp;
! 399: } u_old = { &old };
! 400: if(hash_find(ns_hash, &addr, u_old.void_pp) == HASH_STATUS_OK) {
! 401: hash_delete(ns_hash, &addr);
! 402: xfree(old);
! 403: }
! 404: hash_insert(ns_hash, &addr, (void*)hostname);
! 405: }
! 406:
! 407: }
! 408: }
! 409: }
! 410:
! 411: void resolver_initialise() {
! 412: int* n;
! 413: int i;
! 414: pthread_t thread;
! 415: head = tail = 0;
! 416:
! 417: ns_hash = ns_hash_create();
! 418:
! 419: pthread_mutex_init(&resolver_queue_mutex, NULL);
! 420: pthread_cond_init(&resolver_queue_cond, NULL);
! 421:
! 422: for(i = 0; i < 2; i++) {
! 423: n = (int*)xmalloc(sizeof *n);
! 424: *n = i;
! 425: pthread_create(&thread, NULL, (void*)&resolver_worker, (void*)n);
! 426: }
! 427:
! 428: }
! 429:
! 430: void resolve(struct in_addr* addr, char* result, int buflen) {
! 431: char* hostname;
! 432: union {
! 433: char **ch_pp;
! 434: void **void_pp;
! 435: } u_hostname = { &hostname };
! 436: int added = 0;
! 437:
! 438: if(options.dnsresolution == 1) {
! 439:
! 440: pthread_mutex_lock(&resolver_queue_mutex);
! 441:
! 442: if(hash_find(ns_hash, addr, u_hostname.void_pp) == HASH_STATUS_OK) {
! 443: /* Found => already resolved, or on the queue */
! 444: }
! 445: else {
! 446: hostname = strdup(inet_ntoa(*addr));
! 447: hash_insert(ns_hash, addr, hostname);
! 448:
! 449: if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
! 450: /* queue full */
! 451: }
! 452: else {
! 453: resolve_queue[head] = *addr;
! 454: head = (head + 1) % RESOLVE_QUEUE_LENGTH;
! 455: added = 1;
! 456: }
! 457: }
! 458: pthread_mutex_unlock(&resolver_queue_mutex);
! 459:
! 460: if(added == 1) {
! 461: pthread_cond_signal(&resolver_queue_cond);
! 462: }
! 463:
! 464: if(result != NULL && buflen > 1) {
! 465: strncpy(result, hostname, buflen - 1);
! 466: result[buflen - 1] = '\0';
! 467: }
! 468: }
! 469: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>