File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / iftop / resolver.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 18 14:04:50 2016 UTC (7 years, 7 months ago) by misho
Branches: iftop, MAIN
CVS tags: v1_0rc4, HEAD
iftop 1.0pre4

    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 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];
   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:  */
   69: char *do_resolve(struct addr_storage *addr) {
   70:     struct sockaddr_in sin;
   71:     struct sockaddr_in6 sin6;
   72:     char buf[NI_MAXHOST]; /* 1025 */
   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:     }
   95: 
   96:     if (ret == 0)
   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:  */
  109: char* do_resolve(struct addr_storage *addr) {
  110:     struct hostent hostbuf, *hp = NULL;
  111:     size_t hstbuflen = 1024;
  112:     char *tmphstbuf;
  113:     int res = 0;
  114:     int herr;
  115:     char * ret = NULL;
  116: 
  117:     /* Allocate buffer, remember to free it to avoid memory leakage. */
  118:     tmphstbuf = xmalloc (hstbuflen);
  119: 
  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: 
  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
  127:     while ((res = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
  128:                                   &hostbuf, tmphstbuf, hstbuflen,
  129:                                   &hp, &herr)) == ERANGE)
  130: #else
  131:     /* ... also assume one fewer argument.... */
  132:     while ((hp = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
  133:                                  &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
  134:             && errno == ERANGE)
  135: #endif
  136:             {
  137:         
  138:         /* Enlarge the buffer.  */
  139:         hstbuflen *= 2;
  140:         tmphstbuf = realloc (tmphstbuf, hstbuflen);
  141:       }
  142:     }
  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:  */
  163: char *do_resolve(struct addr_storage *addr) {
  164:     static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
  165:     char *s = NULL;
  166:     struct hostent *he;
  167:     pthread_mutex_lock(&ghba_mtx);
  168:     he = gethostbyaddr((char*)&addr->addr, addr->len, addr->af);
  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:  */
  185: char* do_resolve(struct addr_storage *addr) {
  186:   char msg[PACKETSZ];
  187:   char s[35];
  188:   int l;
  189:   unsigned char* a;
  190:   char * ret = NULL;
  191: 
  192:   if (addr->af != AF_INET)
  193:     return NULL;
  194: 
  195:   a = (unsigned char*)&addr->addr;
  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: 
  253: char *do_resolve(struct addr_storage * addr) {
  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: 
  262:     if (addr->af != AF_INET)
  263:         return NULL;
  264: 
  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:     
  281:     a = (unsigned char*)&addr->as_addr4;
  282:     sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]);
  283:     
  284:     C.result = 0;
  285:     C.addr = &addr->as_addr4;
  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) {
  322:         struct addr_storage a;
  323:         struct hostent *he;
  324:         char buf[NAMESIZE] = {0};
  325:         if (read(fd, &a, sizeof a) != sizeof a)
  326:             return -1;
  327: 
  328:         he = gethostbyaddr((char*)&a.addr, a.len, a.af);
  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: 
  337: char *do_resolve(struct in6_addr *addr) {
  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: 
  402: char *do_resolve(struct addr_storage *addr) {
  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;
  420:             struct addr_storage addr = resolve_queue[tail];
  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: 
  471: void resolve(int af, void* addr, char* result, int buflen) {
  472:     char* hostname;
  473:     union {
  474: 	char **ch_pp;
  475: 	void **void_pp;
  476:     } u_hostname = { &hostname };
  477:     int added = 0;
  478:     struct addr_storage *raddr;
  479: 
  480:     if(options.dnsresolution == 1) {
  481: 
  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: 
  489:         pthread_mutex_lock(&resolver_queue_mutex);
  490: 
  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);
  495:         }
  496:         else {
  497:             hostname = xmalloc(INET6_ADDRSTRLEN);
  498:             inet_ntop(af, &raddr->addr, hostname, INET6_ADDRSTRLEN);
  499: 
  500:             hash_insert(ns_hash, raddr, hostname);
  501: 
  502:             if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
  503:                 /* queue full */
  504:             }
  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:             }
  510:             else {
  511:                 resolve_queue[head] = *raddr;
  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>