Author: pjd
Date: Fri May 20 11:14:05 2011
New Revision: 222118
URL: http://svn.freebsd.org/changeset/base/222118
Log:
  Now that hell is fully frozen it is good time to add IPv6 support to HAST.
  
  MFC after:    3 weeks

Modified:
  head/sbin/hastd/proto_tcp.c

Modified: head/sbin/hastd/proto_tcp.c
==============================================================================
--- head/sbin/hastd/proto_tcp.c Fri May 20 11:10:39 2011        (r222117)
+++ head/sbin/hastd/proto_tcp.c Fri May 20 11:14:05 2011        (r222118)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2009-2010 The FreeBSD Foundation
+ * Copyright (c) 2011 Pawel Jakub Dawidek <pa...@dawidek.net>
  * All rights reserved.
  *
  * This software was developed by Pawel Jakub Dawidek under sponsorship from
@@ -51,10 +52,10 @@ __FBSDID("$FreeBSD$");
 #include "proto_impl.h"
 #include "subr.h"
 
-#define        TCP_CTX_MAGIC   0x7c441c
+#define        TCP_CTX_MAGIC   0x7c41c
 struct tcp_ctx {
        int                     tc_magic;
-       struct sockaddr_in      tc_sin;
+       struct sockaddr_storage tc_sa;
        int                     tc_fd;
        int                     tc_side;
 #define        TCP_SIDE_CLIENT         0
@@ -65,24 +66,6 @@ struct tcp_ctx {
 static int tcp_connect_wait(void *ctx, int timeout);
 static void tcp_close(void *ctx);
 
-static in_addr_t
-str2ip(const char *str)
-{
-       struct hostent *hp;
-       in_addr_t ip;
-
-       ip = inet_addr(str);
-       if (ip != INADDR_NONE) {
-               /* It is a valid IP address. */
-               return (ip);
-       }
-       /* Check if it is a valid host name. */
-       hp = gethostbyname(str);
-       if (hp == NULL)
-               return (INADDR_NONE);
-       return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
-}
-
 /*
  * Function converts the given string to unsigned number.
  */
@@ -114,57 +97,93 @@ invalid:
 }
 
 static int
-tcp_addr(const char *addr, int defport, struct sockaddr_in *sinp)
+tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
 {
-       char iporhost[MAXHOSTNAMELEN];
+       char iporhost[MAXHOSTNAMELEN], portstr[6];
+       struct addrinfo hints;
+       struct addrinfo *res;
        const char *pp;
+       intmax_t port;
        size_t size;
-       in_addr_t ip;
+       int error;
 
        if (addr == NULL)
                return (-1);
 
-       if (strncasecmp(addr, "tcp://", 7) == 0)
+       bzero(&hints, sizeof(hints));
+       hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_protocol = IPPROTO_TCP;
+
+       if (strncasecmp(addr, "tcp4://", 7) == 0) {
+               addr += 7;
+               hints.ai_family = PF_INET;
+       } else if (strncasecmp(addr, "tcp6://", 7) == 0) {
                addr += 7;
-       else if (strncasecmp(addr, "tcp://", 6) == 0)
+               hints.ai_family = PF_INET6;
+       } else if (strncasecmp(addr, "tcp://", 6) == 0) {
                addr += 6;
-       else {
+       } else {
                /*
                 * Because TCP is the default assume IP or host is given without
                 * prefix.
                 */
        }
 
-       sinp->sin_family = AF_INET;
-       sinp->sin_len = sizeof(*sinp);
-       /* Extract optional port. */
-       pp = strrchr(addr, ':');
+       /*
+        * Extract optional port.
+        * There are three cases to consider.
+        * 1. hostname with port, eg. freefall.freebsd.org:8457
+        * 2. IPv4 address with port, eg. 192.168.0.101:8457
+        * 3. IPv6 address with port, eg. [fe80::1]:8457
+        * We discover IPv6 address by checking for two colons and if port is
+        * given, the address has to start with [.
+        */
+       pp = NULL;
+       if (strchr(addr, ':') != strrchr(addr, ':')) {
+               if (addr[0] == '[')
+                       pp = strrchr(addr, ':');
+       } else {
+               pp = strrchr(addr, ':');
+       }
        if (pp == NULL) {
                /* Port not given, use the default. */
-               sinp->sin_port = htons(defport);
+               port = defport;
        } else {
-               intmax_t port;
-
                if (numfromstr(pp + 1, 1, 65535, &port) < 0)
                        return (errno);
-               sinp->sin_port = htons(port);
        }
+       (void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
        /* Extract host name or IP address. */
        if (pp == NULL) {
                size = sizeof(iporhost);
                if (strlcpy(iporhost, addr, size) >= size)
                        return (ENAMETOOLONG);
+       } else if (addr[0] == '[' && pp[-1] == ']') {
+               size = (size_t)(pp - addr - 2 + 1);
+               if (size > sizeof(iporhost))
+                       return (ENAMETOOLONG);
+               (void)strlcpy(iporhost, addr + 1, size);
        } else {
                size = (size_t)(pp - addr + 1);
                if (size > sizeof(iporhost))
                        return (ENAMETOOLONG);
                (void)strlcpy(iporhost, addr, size);
        }
-       /* Convert string (IP address or host name) to in_addr_t. */
-       ip = str2ip(iporhost);
-       if (ip == INADDR_NONE)
+
+       error = getaddrinfo(iporhost, portstr, &hints, &res);
+       if (error != 0) {
+               pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
+                   portstr, gai_strerror(error));
                return (EINVAL);
-       sinp->sin_addr.s_addr = ip;
+       }
+       if (res == NULL)
+               return (ENOENT);
+
+       memcpy(sap, res->ai_addr, res->ai_addrlen);
+
+       freeaddrinfo(res);
 
        return (0);
 }
@@ -185,22 +204,21 @@ tcp_setup_new(const char *addr, int side
                return (errno);
 
        /* Parse given address. */
-       if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT,
-           &tctx->tc_sin)) != 0) {
+       if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) {
                free(tctx);
                return (ret);
        }
 
-       PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
+       PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
 
-       tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
+       tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
        if (tctx->tc_fd == -1) {
                ret = errno;
                free(tctx);
                return (ret);
        }
 
-       PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
+       PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
 
        /* Socket settings. */
        nodelay = 1;
@@ -231,7 +249,7 @@ tcp_setup_wrap(int fd, int side, void **
                return (errno);
 
        tctx->tc_fd = fd;
-       tctx->tc_sin.sin_family = AF_UNSPEC;
+       tctx->tc_sa.ss_family = AF_UNSPEC;
        tctx->tc_side = side;
        tctx->tc_magic = TCP_CTX_MAGIC;
        *ctxp = tctx;
@@ -243,7 +261,7 @@ static int
 tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
 {
        struct tcp_ctx *tctx;
-       struct sockaddr_in sin;
+       struct sockaddr_storage sa;
        int ret;
 
        ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
@@ -252,12 +270,12 @@ tcp_client(const char *srcaddr, const ch
        tctx = *ctxp;
        if (srcaddr == NULL)
                return (0);
-       ret = tcp_addr(srcaddr, 0, &sin);
+       ret = tcp_addr(srcaddr, 0, &sa);
        if (ret != 0) {
                tcp_close(tctx);
                return (ret);
        }
-       if (bind(tctx->tc_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+       if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) < 0) {
                ret = errno;
                tcp_close(tctx);
                return (ret);
@@ -275,7 +293,7 @@ tcp_connect(void *ctx, int timeout)
        PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
        PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
        PJDLOG_ASSERT(tctx->tc_fd >= 0);
-       PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
+       PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
        PJDLOG_ASSERT(timeout >= -1);
 
        flags = fcntl(tctx->tc_fd, F_GETFL);
@@ -295,8 +313,8 @@ tcp_connect(void *ctx, int timeout)
                return (errno);
        }
 
-       if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
-           sizeof(tctx->tc_sin)) == 0) {
+       if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
+           tctx->tc_sa.ss_len) == 0) {
                if (timeout == -1)
                        return (0);
                error = 0;
@@ -403,10 +421,10 @@ tcp_server(const char *addr, void **ctxp
        (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
           sizeof(val));
 
-       PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
+       PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
 
-       if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
-           sizeof(tctx->tc_sin)) < 0) {
+       if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
+           tctx->tc_sa.ss_len) < 0) {
                ret = errno;
                tcp_close(tctx);
                return (ret);
@@ -432,14 +450,14 @@ tcp_accept(void *ctx, void **newctxp)
        PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
        PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
        PJDLOG_ASSERT(tctx->tc_fd >= 0);
-       PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
+       PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
 
        newtctx = malloc(sizeof(*newtctx));
        if (newtctx == NULL)
                return (errno);
 
-       fromlen = sizeof(tctx->tc_sin);
-       newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
+       fromlen = tctx->tc_sa.ss_len;
+       newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
            &fromlen);
        if (newtctx->tc_fd < 0) {
                ret = errno;
@@ -503,59 +521,82 @@ static bool
 tcp_address_match(const void *ctx, const char *addr)
 {
        const struct tcp_ctx *tctx = ctx;
-       struct sockaddr_in sin;
-       socklen_t sinlen;
-       in_addr_t ip1, ip2;
+       struct sockaddr_storage sa1, sa2;
+       socklen_t salen;
 
        PJDLOG_ASSERT(tctx != NULL);
        PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
 
-       if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sin) != 0)
+       if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
+               return (false);
+
+       salen = sizeof(sa2);
+       if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0)
                return (false);
-       ip1 = sin.sin_addr.s_addr;
 
-       sinlen = sizeof(sin);
-       if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
+       if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
                return (false);
-       ip2 = sin.sin_addr.s_addr;
 
-       return (ip1 == ip2);
+       switch (sa1.ss_family) {
+       case AF_INET:
+           {
+               struct sockaddr_in *sin1, *sin2;
+
+               sin1 = (struct sockaddr_in *)&sa1;
+               sin2 = (struct sockaddr_in *)&sa2;
+
+               return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
+                   sizeof(sin1->sin_addr)) == 0);
+           }
+       case AF_INET6:
+           {
+               struct sockaddr_in6 *sin1, *sin2;
+
+               sin1 = (struct sockaddr_in6 *)&sa1;
+               sin2 = (struct sockaddr_in6 *)&sa2;
+
+               return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
+                   sizeof(sin1->sin6_addr)) == 0);
+           }
+       default:
+               return (false);
+       }
 }
 
 static void
 tcp_local_address(const void *ctx, char *addr, size_t size)
 {
        const struct tcp_ctx *tctx = ctx;
-       struct sockaddr_in sin;
-       socklen_t sinlen;
+       struct sockaddr_storage sa;
+       socklen_t salen;
 
        PJDLOG_ASSERT(tctx != NULL);
        PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
 
-       sinlen = sizeof(sin);
-       if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
+       salen = sizeof(sa);
+       if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
                PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
                return;
        }
-       PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size);
+       PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
 }
 
 static void
 tcp_remote_address(const void *ctx, char *addr, size_t size)
 {
        const struct tcp_ctx *tctx = ctx;
-       struct sockaddr_in sin;
-       socklen_t sinlen;
+       struct sockaddr_storage sa;
+       socklen_t salen;
 
        PJDLOG_ASSERT(tctx != NULL);
        PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
 
-       sinlen = sizeof(sin);
-       if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
+       salen = sizeof(sa);
+       if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
                PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
                return;
        }
-       PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size);
+       PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
 }
 
 static void
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to