File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / iftop / resolver.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:57:34 2012 UTC (12 years, 4 months ago) by misho
Branches: iftop, MAIN
CVS tags: v0_17p0, v0_17, HEAD
iftop

    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>