[v2,1/9] lib: Add support and helpers for IPv6 host addresses

Message ID 20180524044742.25889-2-sam@mendozajonas.com
State New
Headers show
Series
  • IPv6 Support
Related show

Commit Message

Samuel Mendoza-Jonas May 24, 2018, 4:47 a.m.
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      |  6 +++-
 lib/types/types.h                  |  1 +
 lib/url/url.c                      | 45 ++++++++++++++++++++++++++----
 lib/url/url.h                      |  2 ++
 test/urls/Makefile.am              |  3 ++
 test/urls/data/ipv6-full.test      |  7 +++++
 test/urls/data/ipv6-multidirs.test |  7 +++++
 test/urls/data/ipv6-noport.test    |  7 +++++
 8 files changed, 72 insertions(+), 6 deletions(-)
 create mode 100644 test/urls/data/ipv6-full.test
 create mode 100644 test/urls/data/ipv6-multidirs.test
 create mode 100644 test/urls/data/ipv6-noport.test

Patch

diff --git a/lib/pb-protocol/pb-protocol.c b/lib/pb-protocol/pb-protocol.c
index dbbda40..87505fc 100644
--- a/lib/pb-protocol/pb-protocol.c
+++ b/lib/pb-protocol/pb-protocol.c
@@ -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++) {
@@ -494,6 +495,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);
@@ -1011,6 +1013,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;
 	}
diff --git a/lib/types/types.h b/lib/types/types.h
index 9ab2a43..5f99b58 100644
--- a/lib/types/types.h
+++ b/lib/types/types.h
@@ -110,6 +110,7 @@  struct interface_info {
 	char		*name;
 	bool		link;
 	char		*address;
+	char		*address_v6;
 };
 
 struct blockdev_info {
diff --git a/lib/url/url.c b/lib/url/url.c
index 6eeced3..f0e8b0e 100644
--- a/lib/url/url.c
+++ b/lib/url/url.c
@@ -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)
diff --git a/lib/url/url.h b/lib/url/url.h
index 9043615..49dff4a 100644
--- a/lib/url/url.h
+++ b/lib/url/url.h
@@ -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);
diff --git a/test/urls/Makefile.am b/test/urls/Makefile.am
index ad670b8..aab0f2b 100644
--- a/test/urls/Makefile.am
+++ b/test/urls/Makefile.am
@@ -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
index 0000000..b4943eb
--- /dev/null
+++ b/test/urls/data/ipv6-full.test
@@ -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
index 0000000..68b852a
--- /dev/null
+++ b/test/urls/data/ipv6-multidirs.test
@@ -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
index 0000000..bd3b008
--- /dev/null
+++ b/test/urls/data/ipv6-noport.test
@@ -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