[Iftop-users] Reverse ip6 resolution

Frédéric Perrin frederic.perrin at resel.fr
Sun, 27 Feb 2011 14:10:44 +0100


--=-=-=
Content-Type: multipart/signed; boundary="==-=-=";
	micalg=pgp-sha1; protocol="application/pgp-signature"

--==-=-=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hello,

I recently had to revisit and slightly improve the patches I sent to the
list in January. They are attached in this mail, and also available at
<http://gitweb.fperrin.net/?p=3Diftop.git;a=3Dshortlog;h=3Drefs/heads/ip6re=
verse>.
Paul, can you apply them if they DTRT ?

The first patch makes iftop compile on FreeBSD.

The second and the third make reverse ip6 resolution available to
methods beyond getnameinfo(3).

The methods that correctly resolve reverse ip6 addresses are:
=2D getnameinfo;
=2D gethostbyaddr_r;
=2D gethostbyaddr;
=2D gethostbyaddr with fork.

This leaves out the ares and libresolv methods, I suppose that can be
fixed quickly.

In patch n=C2=B03, this is done by saving the addresses in a structure that
remembers the AF, which is given by the caller to resolve(). I believe
this in the correct thing to do.

In patch n=C2=B02, this is done by importing the getnameinfo's AF guessing =
to
these methods. This is less intrusive.

Regards,
=2D-=20
Fr=C3=A9d=C3=A9ric Perrin -- http://tar-jx.bz


--==-=-=
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)

iEYEARECAAYFAk1qTVQACgkQlSqR5GqTBEN6lgCfQbOyFCjCNWsiacjpNTFEuntK
uWkAoMb7ibsTbNaC+hor35/HD9R0OWrQ
=DDq6
-----END PGP SIGNATURE-----
--==-=-=--

--=-=-=
Content-Disposition: attachment; filename=freebsd-compile.diff
Content-Description: Make iftop compile on FreeBSD

diff --git a/config.h.in b/config.h.in
index 0139996..59fb109 100644
--- a/config.h.in
+++ b/config.h.in
@@ -156,5 +156,8 @@
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const
 
+/* Define to a way to access the inner bytes of an ip6 address */
+#undef s6_addr32
+
 /* Define to `unsigned int' if <sys/types.h> does not define. */
 #undef size_t
diff --git a/config/int_ghba_r.c b/config/int_ghba_r.c
index b6caac5..f80b6ff 100644
--- a/config/int_ghba_r.c
+++ b/config/int_ghba_r.c
@@ -6,6 +6,7 @@
 
 static const char rcsid[] = "$Id: int_ghba_r.c,v 1.1 2002/11/04 12:27:35 chris Exp $";
 
+#include <sys/socket.h>
 #include <sys/types.h>
 
 #include <errno.h>
diff --git a/configure.in b/configure.in
index 8f2e779..2780fa4 100644
--- a/configure.in
+++ b/configure.in
@@ -81,6 +81,14 @@ AC_C_CONST
 AC_TYPE_SIZE_T
 AC_HEADER_TIME
 
+dnl At least FeeBSD doesn't define s6_addr32 in user-land code, with a
+dnl note saying this is not standard
+AC_CHECK_DECL([s6_addr32],
+	[],
+	[AC_DEFINE([s6_addr32], [__u6_addr.__u6_addr32],
+	 [Define to a way to access the inner bytes of an ip6 address.])],
+	[#include <netinet/in.h>])
+
 dnl
 dnl Are we on a system that uses the STREAMS low-level DLPI interface?
 dnl

--=-=-=
Content-Disposition: attachment; filename=ip6reverse-guessaf.diff
Content-Description: ip6 reverse resolution, guessing the AF from bit
 patterns

diff --git a/resolver.c b/resolver.c
index 17aa9fe..aa47bdc 100644
--- a/resolver.c
+++ b/resolver.c
@@ -37,6 +37,22 @@ int tail;
 
 extern options_t options;
 
+int guess_af(struct in6_addr *addr)
+{
+    /* 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? */
+    uint32_t* probe;
+    int af;
+
+    probe = (uint32_t *) addr;
+    return (probe[1] || probe[2] || probe[3]) ? AF_INET6 : AF_INET;
+}
+
+socklen_t aflength(int af)
+{
+    return af == AF_INET6 ? sizeof(struct in6_addr) : sizeof(struct in_addr);
+}
 
 /* 
  * We have a choice of resolver methods. Real computers have getnameinfo or
@@ -60,18 +76,11 @@ char *do_resolve(struct in6_addr *addr) {
     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;
-
-    switch (af) {
+    switch (guess_af(addr)) {
         case AF_INET:
             sin.sin_family = af;
             sin.sin_port = 0;
@@ -106,26 +115,29 @@ 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) {
+char* do_resolve(struct in6_addr * addr) {
     struct hostent hostbuf, *hp;
     size_t hstbuflen = 1024;
     char *tmphstbuf;
     int res;
     int herr;
+    int af;
     char * ret = NULL;
 
     /* Allocate buffer, remember to free it to avoid memory leakage.  */            
     tmphstbuf = xmalloc (hstbuflen);
 
+    af = guess_af(addr);
+
     /* 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, aflength(af), 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,
+    while ((hp = gethostbyaddr_r((char*)addr, aflength(af), af,
                            &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
             && errno == ERANGE)
 #endif
@@ -152,22 +164,24 @@ char* do_resolve(struct in_addr * addr) {
 #elif defined(USE_GETHOSTBYADDR)
 
 /**
- * Implementation using gethostbyname. Since this is nonreentrant, we have to
+ * Implementation using gethostbyaddr. 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 in6_addr *addr) {
     static pthread_mutex_t ghba_mtx = PTHREAD_MUTEX_INITIALIZER;
     char *s = NULL;
     struct hostent *he;
+    int af;
+    af = guess_af(addr);
+
     pthread_mutex_lock(&ghba_mtx);
-    he = gethostbyaddr((char*)addr, sizeof *addr, AF_INET);
+    he = gethostbyaddr((char*)addr, aflength(af), af);
     if (he)
         s = xstrdup(he->h_name);
     pthread_mutex_unlock(&ghba_mtx);
     return s;
 }
 
-
 #elif defined(USE_LIBRESOLV)
 
 #include <arpa/nameser.h>
@@ -299,7 +313,7 @@ char *do_resolve(struct in_addr * addr) {
 #elif defined(USE_FORKING_RESOLVER)
 
 /**
- * Resolver which forks a process, then uses gethostbyname.
+ * Resolver which forks a process, then uses gethostbyaddr.
  */
 
 #include <signal.h>
@@ -308,13 +322,15 @@ char *do_resolve(struct in_addr * addr) {
 
 int forking_resolver_worker(int fd) {
     while (1) {
-        struct in_addr a;
+        struct in6_addr a;
         struct hostent *he;
+	int af;
         char buf[NAMESIZE] = {0};
-        if (read(fd, &a, sizeof a) != sizeof a)
+        if (read(fd, &af, sizeof af) != sizeof af
+	    || read(fd, &a, aflength(af)) != aflength(af))
             return -1;
 
-        he = gethostbyaddr((char*)&a, sizeof a, AF_INET);
+        he = gethostbyaddr((char*)&a, aflength(af), af);
         if (he)
             strncpy(buf, he->h_name, NAMESIZE - 1);
 
@@ -323,12 +339,13 @@ 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;
     } *workerinfo;
     char name[NAMESIZE];
+    int af;
     static pthread_mutex_t worker_init_mtx = PTHREAD_MUTEX_INITIALIZER;
     static pthread_key_t worker_key;
     static int gotkey;
@@ -368,7 +385,9 @@ char *do_resolve(struct in_addr *addr) {
     }
 
     /* Now have a worker to which we can write requests. */
-    if (write(workerinfo->fd, addr, sizeof *addr) != sizeof *addr
+    af = guess_af(addr);
+    if (write(workerinfo->fd, &af, sizeof af) != sizeof af
+	|| write(workerinfo->fd, addr, aflength(af)) != aflength(af)
         || read(workerinfo->fd, name, NAMESIZE) != NAMESIZE) {
         /* Something went wrong. Just kill the child and get on with it. */
         kill(workerinfo->child, SIGKILL);

--=-=-=
Content-Disposition: attachment; filename=ip6reverse-addr_storage.diff
Content-Description: ip6 reverse resolution, putting addresses in a
 structure remembering the AF

diff --git a/resolver.c b/resolver.c
index 17aa9fe..b21e33c 100644
--- a/resolver.c
+++ b/resolver.c
@@ -25,7 +25,18 @@
 
 #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,7 +106,7 @@ 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) {
+char* do_resolve(struct addr_storage *addr) {
     struct hostent hostbuf, *hp;
     size_t hstbuflen = 1024;
     char *tmphstbuf;
@@ -114,19 +114,19 @@ char* do_resolve(struct in_addr * addr) {
     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);
 
     /* 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
             {
@@ -155,12 +155,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 +177,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 +245,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 +254,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 +273,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 +314,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 +329,7 @@ int forking_resolver_worker(int fd) {
     }
 }
 
-char *do_resolve(struct in_addr *addr) {
+char *do_resolve(struct addr_storage *addr) {
     struct {
         int fd;
         pid_t child;
@@ -388,7 +394,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 +412,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 +463,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;
+
+    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);
 
     if(options.dnsresolution == 1) {
 
         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;
             }

--=-=-=--