]> gitweb.fperrin.net Git - iftop.git/blobdiff - resolver.c
Import iftop-1.0pre4
[iftop.git] / resolver.c
index 17aa9fea6b56ea261662519f3db5f0ba100135d4..adddbc141a91b2132d1ecd3256b0a5e9fe66df61 100644 (file)
 
 #define RESOLVE_QUEUE_LENGTH 20
 
-struct in6_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
+struct addr_storage {
+    int af;                     /* AF_INET or AF_INET6 */
+    int len;                    /* sizeof(struct in_addr or in6_addr) */
+    union {
+        struct in_addr  addr4;
+        struct in6_addr addr6;
+    } addr;
+#define as_addr4 addr.addr4
+#define as_addr6 addr.addr6
+};
+
+struct addr_storage resolve_queue[RESOLVE_QUEUE_LENGTH];
 
 pthread_cond_t resolver_queue_cond;
 pthread_mutex_t resolver_queue_mutex;
@@ -55,48 +66,37 @@ extern options_t options;
  * as NetBSD break the RFC and implement it in a non-thread-safe fashion, so
  * for the moment, the configure script won't try to use it.
  */
-char *do_resolve(struct in6_addr *addr) {
+char *do_resolve(struct addr_storage *addr) {
     struct sockaddr_in sin;
     struct sockaddr_in6 sin6;
     char buf[NI_MAXHOST]; /* 1025 */
-    int res, af;
-    uint32_t* probe;
-
-    memset(&sin, '\0', sizeof(sin));
-    memset(&sin6, '\0', sizeof(sin6));
-
-    /* If the upper three (network byte order) uint32-parts
-     * are null, then there ought to be an IPv4 address here.
-     * Any such IPv6 would have to be 'xxxx::'. Neglectable? */
-    probe = (uint32_t *) addr;
-    af = (probe[1] || probe[2] || probe[3]) ? AF_INET6 : AF_INET;
+    int ret;
 
-    switch (af) {
+    switch (addr->af) {
         case AF_INET:
-            sin.sin_family = af;
+            sin.sin_family = addr->af;
             sin.sin_port = 0;
-            memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));
+            memcpy(&sin.sin_addr, &addr->as_addr4, addr->len);
 
-            if (getnameinfo((struct sockaddr*)&sin, sizeof sin,
-                            buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
-                return xstrdup(buf);
-            else
-                return NULL;
+            ret = getnameinfo((struct sockaddr*)&sin, sizeof sin,
+                              buf, sizeof buf, NULL, 0, NI_NAMEREQD);
             break;
         case AF_INET6:
-            sin6.sin6_family = af;
+            sin6.sin6_family = addr->af;
             sin6.sin6_port = 0;
-            memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr));
+            memcpy(&sin6.sin6_addr, &addr->as_addr6, addr->len);
 
-            if (getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
-                            buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
-                return xstrdup(buf);
-            else
-                return NULL;
-            break;
+            ret = getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
+                              buf, sizeof buf, NULL, 0, NI_NAMEREQD);
+           break;
         default:
             return NULL;
     }
+
+    if (ret == 0)
+        return xstrdup(buf);
+    else
+        return NULL;
 }
 
 #elif defined(USE_GETHOSTBYADDR_R)
@@ -106,27 +106,31 @@ char *do_resolve(struct in6_addr *addr) {
  * Some implementations of libc choose to implement gethostbyaddr_r as
  * a non thread-safe wrapper to gethostbyaddr.  An interesting choice...
  */
-char* do_resolve(struct in_addr * addr) {
-    struct hostent hostbuf, *hp;
+char* do_resolve(struct addr_storage *addr) {
+    struct hostent hostbuf, *hp = NULL;
     size_t hstbuflen = 1024;
     char *tmphstbuf;
-    int res;
+    int res = 0;
     int herr;
     char * ret = NULL;
 
-    /* Allocate buffer, remember to free it to avoid memory leakage.  */            
+    /* Allocate buffer, remember to free it to avoid memory leakage. */
     tmphstbuf = xmalloc (hstbuflen);
 
+    /* nss-myhostname's gethostbyaddr_r() causes an assertion failure if an
+     * "invalid" (as in outside of IPv4 or IPv6) address family is passed */
+    if (addr->af == AF_INET || addr->af == AF_INET6) {
+
     /* Some machines have gethostbyaddr_r returning an integer error code; on
      * others, it returns a struct hostent*. */
 #ifdef GETHOSTBYADDR_R_RETURNS_INT
-    while ((res = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET,
+    while ((res = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
                                   &hostbuf, tmphstbuf, hstbuflen,
                                   &hp, &herr)) == ERANGE)
 #else
     /* ... also assume one fewer argument.... */
-    while ((hp = gethostbyaddr_r((char*)addr, sizeof(struct in_addr), AF_INET,
-                           &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
+    while ((hp = gethostbyaddr_r((char*)&addr->addr, addr->len, addr->af,
+                                 &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
             && errno == ERANGE)
 #endif
             {
@@ -135,6 +139,7 @@ char* do_resolve(struct in_addr * addr) {
         hstbuflen *= 2;
         tmphstbuf = realloc (tmphstbuf, hstbuflen);
       }
+    }
 
     /*  Check for errors.  */
     if (res || hp == NULL) {
@@ -155,12 +160,12 @@ char* do_resolve(struct in_addr * addr) {
  * Implementation using gethostbyname. Since this is nonreentrant, we have to
  * wrap it in a mutex, losing all benefit of multithreaded resolution.
  */
-char *do_resolve(struct in_addr *addr) {
+char *do_resolve(struct addr_storage *addr) {
     static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
     char *s = NULL;
     struct hostent *he;
     pthread_mutex_lock(&ghba_mtx);
-    he = gethostbyaddr((char*)addr, sizeof *addr, AF_INET);
+    he = gethostbyaddr((char*)&addr->addr, addr->len, addr->af);
     if (he)
         s = xstrdup(he->h_name);
     pthread_mutex_unlock(&ghba_mtx);
@@ -177,14 +182,17 @@ char *do_resolve(struct in_addr *addr) {
  * libresolv implementation 
  * resolver functions may not be thread safe
  */
-char* do_resolve(struct in_addr * addr) {
+char* do_resolve(struct addr_storage *addr) {
   char msg[PACKETSZ];
   char s[35];
   int l;
   unsigned char* a;
   char * ret = NULL;
 
-  a = (unsigned char*)addr;
+  if (addr->af != AF_INET)
+    return NULL;
+
+  a = (unsigned char*)&addr->addr;
 
   snprintf(s, 35, "%d.%d.%d.%d.in-addr.arpa.",a[3], a[2], a[1], a[0]);
 
@@ -242,7 +250,7 @@ static void do_resolve_ares_callback(void *arg, int status, unsigned char *abuf,
     }
 }
 
-char *do_resolve(struct in_addr * addr) {
+char *do_resolve(struct addr_storage * addr) {
     struct ares_callback_comm C;
     char s[35];
     unsigned char *a;
@@ -251,6 +259,9 @@ char *do_resolve(struct in_addr * addr) {
     static pthread_key_t ares_key;
     static int gotkey;
 
+    if (addr->af != AF_INET)
+        return NULL;
+
     /* Make sure we have an ARES channel for this thread. */
     pthread_mutex_lock(&ares_init_mtx);
     if (!gotkey) {
@@ -267,11 +278,11 @@ char *do_resolve(struct in_addr * addr) {
         if (ares_init(chan) != ARES_SUCCESS) return NULL;
     }
     
-    a = (unsigned char*)addr;
+    a = (unsigned char*)&addr->as_addr4;
     sprintf(s, "%d.%d.%d.%d.in-addr.arpa.", a[3], a[2], a[1], a[0]);
     
     C.result = 0;
-    C.addr = addr;
+    C.addr = &addr->as_addr4;
     ares_query(*chan, s, C_IN, T_PTR, do_resolve_ares_callback, &C);
     while (C.result == 0) {
         int n;
@@ -308,13 +319,13 @@ char *do_resolve(struct in_addr * addr) {
 
 int forking_resolver_worker(int fd) {
     while (1) {
-        struct in_addr a;
+        struct addr_storage a;
         struct hostent *he;
         char buf[NAMESIZE] = {0};
         if (read(fd, &a, sizeof a) != sizeof a)
             return -1;
 
-        he = gethostbyaddr((char*)&a, sizeof a, AF_INET);
+        he = gethostbyaddr((char*)&a.addr, a.len, a.af);
         if (he)
             strncpy(buf, he->h_name, NAMESIZE - 1);
 
@@ -323,7 +334,7 @@ int forking_resolver_worker(int fd) {
     }
 }
 
-char *do_resolve(struct in_addr *addr) {
+char *do_resolve(struct in6_addr *addr) {
     struct {
         int fd;
         pid_t child;
@@ -388,7 +399,7 @@ char *do_resolve(struct in_addr *addr) {
 
 #   warning No name resolution method specified; name resolution will not work
 
-char *do_resolve(struct in_addr *addr) {
+char *do_resolve(struct addr_storage *addr) {
     return NULL;
 }
 
@@ -406,7 +417,7 @@ void resolver_worker(void* ptr) {
         /* Keep resolving until the queue is empty */
         while(head != tail) {
             char * hostname;
-            struct in6_addr addr = resolve_queue[tail];
+            struct addr_storage addr = resolve_queue[tail];
 
             /* mutex always locked at this point */
 
@@ -457,36 +468,47 @@ void resolver_initialise() {
 
 }
 
-void resolve(int af, struct in6_addr* addr, char* result, int buflen) {
+void resolve(int af, void* addr, char* result, int buflen) {
     char* hostname;
     union {
        char **ch_pp;
        void **void_pp;
     } u_hostname = { &hostname };
     int added = 0;
+    struct addr_storage *raddr;
 
     if(options.dnsresolution == 1) {
 
+        raddr = malloc(sizeof *raddr);
+        memset(raddr, 0, sizeof *raddr);
+        raddr->af = af;
+        raddr->len = (af == AF_INET ? sizeof(struct in_addr)
+                      : sizeof(struct in6_addr));
+        memcpy(&raddr->addr, addr, raddr->len);
+
         pthread_mutex_lock(&resolver_queue_mutex);
 
-        if(hash_find(ns_hash, addr, u_hostname.void_pp) == HASH_STATUS_OK) {
-            /* Found => already resolved, or on the queue */
+        if(hash_find(ns_hash, raddr, u_hostname.void_pp) == HASH_STATUS_OK) {
+            /* Found => already resolved, or on the queue, no need to keep
+            * it around */
+            free(raddr);
         }
         else {
             hostname = xmalloc(INET6_ADDRSTRLEN);
-            inet_ntop(af, addr, hostname, INET6_ADDRSTRLEN);
-            hash_insert(ns_hash, addr, hostname);
+            inet_ntop(af, &raddr->addr, hostname, INET6_ADDRSTRLEN);
+
+            hash_insert(ns_hash, raddr, hostname);
 
             if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
                 /* queue full */
             }
-            else if((af == AF_INET6)
-                        && (IN6_IS_ADDR_LINKLOCAL(addr)
-                            || IN6_IS_ADDR_SITELOCAL(addr))) {
+            else if ((af == AF_INET6)
+                     && (IN6_IS_ADDR_LINKLOCAL(&raddr->as_addr6)
+                         || IN6_IS_ADDR_SITELOCAL(&raddr->as_addr6))) {
                 /* Link-local and site-local stay numerical. */
             }
             else {
-                resolve_queue[head] = *addr;
+                resolve_queue[head] = *raddr;
                 head = (head + 1) % RESOLVE_QUEUE_LENGTH;
                 added = 1;
             }