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>