Annotation of embedaddon/iftop/resolver.c, revision 1.1.1.2

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