lib: Add support and helpers for IPv6 host addresses
authorSamuel Mendoza-Jonas <sam@mendozajonas.com>
Wed, 9 May 2018 01:13:54 +0000 (11:13 +1000)
committerSamuel Mendoza-Jonas <sam@mendozajonas.com>
Tue, 10 Jul 2018 04:00:08 +0000 (14:00 +1000)
Recognise IPv6 addresses and URLs, and allow an interface_info struct to
have both an IPv4 and IPv6 address.
The addr_scheme() helper returns the address family of a given address.

Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
lib/pb-protocol/pb-protocol.c
lib/types/types.h
lib/url/url.c
lib/url/url.h
test/urls/Makefile.am
test/urls/data/ipv6-full.test [new file with mode: 0644]
test/urls/data/ipv6-multidirs.test [new file with mode: 0644]
test/urls/data/ipv6-noport.test [new file with mode: 0644]

index 8e37348c07ccd9787ee7fe0531eb2ac45b3d9649..41707da09ab0d7345c8d0a0a2d2f9d1b48f43517 100644 (file)
@@ -253,7 +253,8 @@ int pb_protocol_system_info_len(const struct system_info *sysinfo)
                len +=  4 + if_info->hwaddr_size +
                        4 + optional_strlen(if_info->name) +
                        sizeof(if_info->link) +
-                       4 + optional_strlen(if_info->address);
+                       4 + optional_strlen(if_info->address) +
+                       4 + optional_strlen(if_info->address_v6);
        }
 
        for (i = 0; i < sysinfo->n_blockdevs; i++) {
@@ -509,6 +510,7 @@ int pb_protocol_serialise_system_info(const struct system_info *sysinfo,
                pos += sizeof(bool);
 
                pos += pb_protocol_serialise_string(pos, if_info->address);
+               pos += pb_protocol_serialise_string(pos, if_info->address_v6);
        }
 
        *(uint32_t *)pos = __cpu_to_be32(sysinfo->n_blockdevs);
@@ -1046,6 +1048,8 @@ int pb_protocol_deserialise_system_info(struct system_info *sysinfo,
 
                if (read_string(if_info, &pos, &len, &if_info->address))
                        goto out;
+               if (read_string(if_info, &pos, &len, &if_info->address_v6))
+                       goto out;
 
                sysinfo->interfaces[i] = if_info;
        }
index 9ab2a43497602d22507e65602132930a20d52f0f..5f99b58d52b33d11dc5dc8369bbdc5569489e8e4 100644 (file)
@@ -110,6 +110,7 @@ struct interface_info {
        char            *name;
        bool            link;
        char            *address;
+       char            *address_v6;
 };
 
 struct blockdev_info {
index 6eeced32538350cb0aea0fa3edbe1a9c73aab411..f0e8b0eef610ae7021c6b749fd396ddfa6039be3 100644 (file)
@@ -197,19 +197,35 @@ struct pb_url *pb_url_parse(void *ctx, const char *url_str)
                        goto fail;
                }
 
-               col = strchr(p, ':');
+               col = strrchr(p, ':');
+               if (col <= p)
+                       col = NULL;
+               if (col && strchr(col , ']')) {
+                       /*
+                        * This is likely an IPv6 address with no port.
+                        * See https://www.ietf.org/rfc/rfc2732.txt
+                        */
+                       col = NULL;
+               }
 
                if (col) {
                        len = path - col - 1;
                        url->port = len ? talloc_strndup(url, col + 1, len)
                                : NULL;
                        len = col - p;
-                       url->host = len ? talloc_strndup(url, p, len) : NULL;
                } else {
                        url->port = NULL;
-                       url->host = talloc_strndup(url, p, path - p);
+                       len = path - p;
                }
 
+               if (p[0] == '[' && p[len - 1] == ']')
+                       /* IPv6 */
+                       url->host = talloc_strndup(url, p + 1, len - 2);
+               else
+                       /* IPv4 */
+                       url->host = talloc_strndup(url, p, len);
+
+
                /* remove multiple leading slashes */
                for (; *path && *(path+1) == '/'; path++)
                        ;
@@ -231,13 +247,32 @@ bool is_url(const char *str)
        return strstr(str, "://") != NULL;
 }
 
+int addr_scheme(const char *address)
+{
+       uint8_t buf[sizeof(struct in6_addr)];
+       int rc;
+
+       rc = inet_pton(AF_INET, address , buf);
+       if (rc == 1)
+               return AF_INET;
+       rc = inet_pton(AF_INET6, address , buf);
+       if (rc == 1)
+               return AF_INET6;
+
+       return 0;
+}
+
 char *pb_url_to_string(struct pb_url *url)
 {
        const struct pb_scheme_info *scheme = pb_url_scheme_info(url->scheme);
        assert(scheme);
 
-       return talloc_asprintf(url, "%s://%s%s", scheme->str,
-                       scheme->has_host ? url->host : "", url->path);
+       if (scheme->has_host && addr_scheme(url->host) == AF_INET6)
+               return talloc_asprintf(url, "%s://[%s]%s", scheme->str,
+                               url->host, url->path);
+       else
+               return talloc_asprintf(url, "%s://%s%s", scheme->str,
+                               scheme->has_host ? url->host : "", url->path);
 }
 
 static void pb_url_update_full(struct pb_url *url)
index 9043615590c1a7e95e5d938ccc2d0565af994456..49dff4ac9388827d4fcdd4f33acd91e3fa0bd301 100644 (file)
@@ -19,6 +19,7 @@
 #if !defined(_PB_URL_PARSER_H)
 #define _PB_URL_PARSER_H
 
+#include <arpa/inet.h>
 #include <stdbool.h>
 
 /**
@@ -61,6 +62,7 @@ struct pb_url {
 };
 
 bool is_url(const char *str);
+int addr_scheme(const char *address);
 struct pb_url *pb_url_parse(void *ctx, const char *url_str);
 struct pb_url *pb_url_copy(void *ctx, const struct pb_url *url);
 struct pb_url *pb_url_join(void *ctx, const struct pb_url *url, const char *s);
index ad670b87cb6f2ff992bdee22b04e2032e3e264d8..aab0f2b5b97f64e0805c138b38ec1cb4959fe126 100644 (file)
@@ -20,6 +20,9 @@ test_urls_parse_url_LDADD = $(core_lib)
 url_TESTS = \
        test/urls/data/double-slash.test \
        test/urls/data/http-simple.test \
+       test/urls/data/ipv6-full.test \
+       test/urls/data/ipv6-multidirs.test \
+       test/urls/data/ipv6-noport.test \
        test/urls/data/join-full.test \
        test/urls/data/join-absolute.test \
        test/urls/data/join-relative.test \
diff --git a/test/urls/data/ipv6-full.test b/test/urls/data/ipv6-full.test
new file mode 100644 (file)
index 0000000..b4943eb
--- /dev/null
@@ -0,0 +1,7 @@
+http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html
+scheme http
+host   FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
+port   80
+path   /index.html
+dir    /
+file   index.html
diff --git a/test/urls/data/ipv6-multidirs.test b/test/urls/data/ipv6-multidirs.test
new file mode 100644 (file)
index 0000000..68b852a
--- /dev/null
@@ -0,0 +1,7 @@
+tftp://[fd69:d65f:b8b5:61::1]/installers/ubuntu-18.04/vmlinux
+scheme tftp
+host   fd69:d65f:b8b5:61::1
+port   (null)
+path   /installers/ubuntu-18.04/vmlinux
+dir    /installers/ubuntu-18.04/
+file   vmlinux
diff --git a/test/urls/data/ipv6-noport.test b/test/urls/data/ipv6-noport.test
new file mode 100644 (file)
index 0000000..bd3b008
--- /dev/null
@@ -0,0 +1,7 @@
+http://[1080:0:0:0:8:800:200C:417A]/index.html
+scheme http
+host   1080:0:0:0:8:800:200C:417A
+port   (null)
+path   /index.html
+dir    /
+file   index.html