01_convert-v6-mapped-v4-addresses.diff
authorGerrit Renker <grenker@cscs.ch>
Thu, 25 Feb 2010 16:05:44 +0000 (17:05 +0100)
committerAndre Noll <maan@systemlinux.org>
Thu, 25 Feb 2010 16:05:44 +0000 (17:05 +0100)
This updates the resolution of v4/v6 addresses to present v6-mapped-v4
addresses in dot-quad format rather than as subtype of v6 address.

The getnameinfo(3) is in use for both address classes, where potential
DNS lookups (requiring Internet connectivity) are avoided by using the
NI_NUMERICHOST flag.
The use of this flag reduces getnameinfo() to inet_ntop(3). In the 'Bugs'
section of the inet_ntop() manpage it is stated that v6-mapped-v4 addresses
are converted into an IPv6 format, which is what had been observed here.

The patch works around the issue by casting the v6-mapped-v4 address into
a genuine AF_INET IPv4 socket address.

net.c

diff --git a/net.c b/net.c
index 00844d7..126b514 100644 (file)
--- a/net.c
+++ b/net.c
@@ -368,28 +368,70 @@ int para_listen(unsigned l3type, unsigned l4type, unsigned short port)
        return fd;
 }
 
+/**
+ * Determine IPv4/v6 socket address length.
+ * \param sa Container of IPv4 or IPv6 address.
+ * \return Address-family dependent address length.
+ */
+static socklen_t salen(const struct sockaddr *sa)
+{
+       assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+       return sa->sa_family == AF_INET6
+               ? sizeof(struct sockaddr_in6)
+               : sizeof(struct sockaddr_in);
+}
+
+/**
+ * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address.
+ * \param ss Container of IPv4/6 address.
+ * \return Pointer to normalized address (may be static storage).
+ *
+ * \sa RFC 3493
+ */
+static const struct sockaddr *
+normalize_ip_address(const struct sockaddr_storage *ss)
+{
+       const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss;
+
+       assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
+
+       if (ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr)) {
+               static struct sockaddr_in ia;
+
+               ia.sin_family = AF_INET;
+               ia.sin_port   = ia6->sin6_port;
+               memcpy(&ia.sin_addr.s_addr, &(ia6->sin6_addr.s6_addr[12]), 4);
+               return (const struct sockaddr *)&ia;
+       }
+       return (const struct sockaddr *)ss;
+}
+
 /**
  * Print numeric host and port number (beware - uses static char).
  *
  * \param sa The IPv4/IPv6 socket address to use.
- * \param len The length of \p sa.
  *
  * \sa getnameinfo(3).
  */
-static char *host_and_port(struct sockaddr *sa, socklen_t len)
+static char *host_and_port(const struct sockaddr_storage *ss)
 {
-       static char output[NI_MAXHOST + NI_MAXSERV + 2];
+       const struct sockaddr *sa = normalize_ip_address(ss);
        char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+       static char output[sizeof(hbuf) + sizeof(sbuf) + 2];
        int ret;
 
-       ret = getnameinfo(sa, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
-               NI_NUMERICHOST | NI_NUMERICSERV);
-       if (ret) {
+       ret = getnameinfo(sa, salen(sa),
+                         hbuf, sizeof(hbuf),
+                         sbuf, sizeof(sbuf),
+                         NI_NUMERICHOST | NI_NUMERICSERV);
+       if (ret == 0) {
+               snprintf(output, sizeof(output), "%s#%s", hbuf, sbuf);
+       } else {
+               snprintf(output, sizeof(output), "(unknown)");
                PARA_WARNING_LOG("hostname lookup error (%s).\n",
-                       gai_strerror(ret));
-               sprintf(output, "(unknown)");
-       } else
-               sprintf(output, "%s#%s", hbuf, sbuf);
+                                gai_strerror(ret));
+       }
        return output;
 }
 
@@ -417,7 +459,7 @@ static char *__get_sock_name(int fd, int (*getname)(int, struct sockaddr*,
                        fd, strerror(errno));
                return dont_know;
        }
-       return host_and_port((struct sockaddr *)&ss, sslen);
+       return host_and_port(&ss);
 }
 
 /**
@@ -460,15 +502,10 @@ char *remote_name(int sockfd)
 struct in_addr extract_v4_addr(const struct sockaddr_storage *ss)
 {
        struct in_addr ia = {.s_addr = 0};
+       const struct sockaddr *sa = normalize_ip_address(ss);
 
-       if (ss->ss_family == AF_INET)
-               ia.s_addr = ((struct sockaddr_in *)ss)->sin_addr.s_addr;
-       if (ss->ss_family == AF_INET6) {
-               const struct in6_addr v6_addr = ((struct sockaddr_in6 *)ss)->sin6_addr;
-
-               if (IN6_IS_ADDR_V4MAPPED(&v6_addr))
-                       memcpy(&ia.s_addr, &(v6_addr.s6_addr[12]), 4);
-       }
+       if (sa->sa_family == AF_INET)
+               ia = ((struct sockaddr_in *)sa)->sin_addr;
        return ia;
 }