Here is a working patch for IPv6 literal with square brackets.
Tested with :
"2001:db8::1234:5678",
"2001:db8::1234:5678:",
"2001:db8::1234:5678:80",
"2001:db8::1234:5678:80:",
"::",
":::",
":::80",
"[2001:db8::1234:5678]",
"[2001:db8::1234:5678]:",
"[2001:db8::1234:5678]:80",
"[::]",
"[::]:",
"[::]:80"

--
Mildis

Le 2015-10-06 17:47, Willy Tarreau a écrit :
Hi,

On Tue, Oct 06, 2015 at 05:34:07PM +0200, Mildis wrote:
Hi Willy,

My bad : in the doc, I didn???t get the « add a colon without a port to
end an address » trick.
That???s why I was lost at first.
The doc obviously says ???<address>[:[port]]??? making the port optional and an ending colon valid but it looks like a typo to me as ending an IPv6
address with a colon is not one of my habit.

The provided patch sent initially should handle square brackets but
still makes the colon mandatory even with no port.
I???ll rework it so it accepts :
- 2001:db8::1234: as IPv6 no port (for backward compatibility)
- [2001:db8::1234] as IPv6 no port
- [2001:db8::1234]:80 as IPv6 with port
but forbid ???[2001:db8::1234]:??? and ???2001:db8::1234???

You must not forbid "[2001:db8::1234]:" otherwise people will not adopt
the square brackets notation. Some of them probably already do things like
this :

      server srv1 $IP_SRV1:$PORT_SRV1

And fill the respective environment variables with either IPv4 or IPv6.
By preventing a clearly non-ambigous config from being parsed, you'll
simply make them stay away from this new syntax, which is the opposite
to what you're seeking.

And regarding "2001:db8::1234", you can't forbit it simply because you
don't know if 1234 is a port or not in this context, as you have reported.

Otherwise it's OK for me. Don't forget to update the doc accordingly!

Willy
From 44390f0ab9295450635021e59eb9ef04b050b82e Mon Sep 17 00:00:00 2001
From: mildis <[email protected]>
Date: Sat, 10 Oct 2015 12:51:07 +0200
Subject: [PATCH] OPTIM: check IPv6 literal with square brackets

---
 src/standard.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 57 insertions(+), 13 deletions(-)

diff --git a/src/standard.c b/src/standard.c
index b5f4bf4..7b49332 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -622,7 +622,7 @@ struct sockaddr_storage *str2ip2(const char *str, struct sockaddr_storage *sa, i
 	struct hostent *he;
 
 	/* Any IPv6 address */
-	if (str[0] == ':' && str[1] == ':' && !str[2]) {
+	if (str[0] == ':' && str[1] == ':' && (str[2]=='\0' || !str[2])) {
 		if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
 			sa->ss_family = AF_INET6;
 		else if (sa->ss_family != AF_INET6)
@@ -637,11 +637,38 @@ struct sockaddr_storage *str2ip2(const char *str, struct sockaddr_storage *sa, i
 		return sa;
 	}
 
+	/* check IPv6 literal */
+	if (str[0] == '[') {
+		if (strrchr(str, ']') != NULL)
+			/* has opening AND closing bracket */
+			sa->ss_family = AF_INET6;
+		else
+			/* no closing bracket */
+			goto fail;
+	}
+
 	/* check for IPv6 first */
-	if ((!sa->ss_family || sa->ss_family == AF_UNSPEC || sa->ss_family == AF_INET6) &&
-	    inet_pton(AF_INET6, str, &((struct sockaddr_in6 *)sa)->sin6_addr)) {
-		sa->ss_family = AF_INET6;
-		return sa;
+	if (!sa->ss_family || sa->ss_family == AF_UNSPEC || sa->ss_family == AF_INET6) {
+		/* IPv6 literal with opening and closing bracket ? */
+		if (str[0] == '[' && strchr(str, ']') != NULL) {
+			/* strip the closing bracket */
+			char *tmpip6 = strdup(str);
+			if (tmpip6) {
+				*(strchr(tmpip6, ']')) = '\0';
+				/* if inet_pton is OK, it is a valid IPv6 address */
+				if (inet_pton(AF_INET6, tmpip6+1, &((struct sockaddr_in6 *)sa)->sin6_addr)) {
+					sa->ss_family = AF_INET6;
+					return sa;
+				}
+			}
+			else
+				goto fail;
+		}
+		/* IPv6 without brackets */
+		else if (inet_pton(AF_INET6, str, &((struct sockaddr_in6 *)sa)->sin6_addr)) {
+			sa->ss_family = AF_INET6;
+			return sa;
+		}
 	}
 
 	/* then check for IPv4 */
@@ -747,10 +774,10 @@ struct sockaddr_storage *str2ip2(const char *str, struct sockaddr_storage *sa, i
  *                  the first byte of the address.
  *    - "fd@"    => an integer must follow, and is a file descriptor number.
  *
- * Also note that in order to avoid any ambiguity with IPv6 addresses, the ':'
- * is mandatory after the IP address even when no port is specified. NULL is
- * returned if the address cannot be parsed. The <low> and <high> ports are
- * always initialized if non-null, even for non-IP families.
+ * IPv6 addresses can be declared with or with square brackets. If not using
+ * square brackets, the last colon ':' is mandatory even when no port is
+ * specified. NULL is returned if the address cannot be parsed. The <low> and
+ * <high> ports are always initialized if non-null, even for non-IP families.
  *
  * If <pfx> is non-null, it is used as a string prefix before any path-based
  * address (typically the path to a unix socket).
@@ -856,11 +883,28 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char
 	else { /* IPv4 and IPv6 */
 		int use_fqdn = 0;
 
-		port1 = strrchr(str2, ':');
-		if (port1)
-			*port1++ = '\0';
-		else
+		/* IPv6 wildcard */
+		if (!strcmp(str2, "::")) {
+			port1 = "";
+		}
+		/* IPv6 literal with a port */
+		else if (strstr(str2, "]:")) {
+			port1 = strrchr(str2, ':');
+			if (port1)
+				*port1++ = '\0';
+		}
+		/* IPv6 literal without a port */
+		else if (strstr(str2, "]\0")) {
 			port1 = "";
+		}
+		/* not an IPv6 in square bracket */
+		else if (!strchr(str2, '[') && !strrchr(str2, ']')) {
+			port1 = strrchr(str2, ':');
+			if (port1)
+				*port1++ = '\0';
+			else
+				port1 = "";
+		}
 
 		if (str2ip2(str2, &ss, 0) == NULL) {
 			use_fqdn = 1;
-- 
2.3.8 (Apple Git-58)

Reply via email to