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>