Annotation of embedaddon/iftop/resolver.c, revision 1.1.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>