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