MEDIUM: tools: make str2sa_range support all address syntaxes

Right now we have multiple methods for parsing IP addresses in the
configuration. This is quite painful. This patch aims at adapting
str2sa_range() to make it support all formats, so that the callers
perform the appropriate tests on the return values. str2sa() was
changed to simply return str2sa_range().

The output values are now the following ones (taken from the comment
on top of the function).

  Converts <str> to a locally allocated struct sockaddr_storage *, and a port
  range or offset consisting in two integers that the caller will have to
  check to find the relevant input format. The following format are supported :

    String format           | address |  port  |  low   |  high
     addr                   | <addr>  |   0    |   0    |   0
     addr:                  | <addr>  |   0    |   0    |   0
     addr:port              | <addr>  | <port> | <port> | <port>
     addr:pl-ph             | <addr>  |  <pl>  |  <pl>  |  <ph>
     addr:+port             | <addr>  | <port> |   0    | <port>
     addr:-port             | <addr>  |-<port> | <port> |   0

  The detection of a port range or increment by the caller is made by
  comparing <low> and <high>. If both are equal, then port 0 means no port
  was specified. The caller may pass NULL for <low> and <high> if it is not
  interested in retrieving port ranges.

  Note that <addr> above may also be :
    - empty ("")  => family will be AF_INET and address will be INADDR_ANY
    - "*"         => family will be AF_INET and address will be INADDR_ANY
    - "::"        => family will be AF_INET6 and address will be IN6ADDR_ANY
    - a host name => family and address will depend on host name resolving.
diff --git a/src/standard.c b/src/standard.c
index 380f7be..db0e0e4 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -610,89 +610,85 @@
 }
 
 /*
- * converts <str> to a locally allocated struct sockaddr_storage *.
- * The format is "addr[:[port]]", where "addr" can be a dotted IPv4 address, an
- * IPv6 address, a host name, or empty or "*" to indicate INADDR_ANY. If an IPv6
- * address wants to ignore port, it must be terminated by a trailing colon (':').
- * The IPv6 '::' address is IN6ADDR_ANY, so in order to bind to a given port on
- * IPv6, use ":::port". NULL is returned if the host part cannot be resolved.
+ * Converts <str> to a locally allocated struct sockaddr_storage *, and a port
+ * range or offset consisting in two integers that the caller will have to
+ * check to find the relevant input format. The following format are supported :
+ *
+ *   String format           | address |  port  |  low   |  high
+ *    addr                   | <addr>  |   0    |   0    |   0
+ *    addr:                  | <addr>  |   0    |   0    |   0
+ *    addr:port              | <addr>  | <port> | <port> | <port>
+ *    addr:pl-ph             | <addr>  |  <pl>  |  <pl>  |  <ph>
+ *    addr:+port             | <addr>  | <port> |   0    | <port>
+ *    addr:-port             | <addr>  |-<port> | <port> |   0
+ *
+ * The detection of a port range or increment by the caller is made by
+ * comparing <low> and <high>. If both are equal, then port 0 means no port
+ * was specified. The caller may pass NULL for <low> and <high> if it is not
+ * interested in retrieving port ranges.
+ *
+ * Note that <addr> above may also be :
+ *    - empty ("")  => family will be AF_INET and address will be INADDR_ANY
+ *    - "*"         => family will be AF_INET and address will be INADDR_ANY
+ *    - "::"        => family will be AF_INET6 and address will be IN6ADDR_ANY
+ *    - a host name => family and address will depend on host name resolving.
+ *
+ * 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.
  */
-struct sockaddr_storage *str2sa(const char *str)
+struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high)
 {
 	struct sockaddr_storage *ret = NULL;
 	char *str2;
-	char *c;
-	int port;
+	char *port1, *port2;
+	int portl, porth, porta;
+
+	portl = porth = porta = 0;
 
 	str2 = strdup(str);
 	if (str2 == NULL)
 		goto out;
 
-	if ((c = strrchr(str2, ':')) != NULL) { /* Port */
-		*c++ = '\0';
-		port = atol(c);
-	}
+	port1 = strrchr(str2, ':');
+	if (port1)
+		*port1++ = '\0';
 	else
-		port = 0;
+		port1 = "";
 
 	ret = str2ip(str2);
 	if (!ret)
 		goto out;
 
-	set_host_port(ret, port);
- out:
-	free(str2);
-	return ret;
-}
-
-/*
- * converts <str> to a locally allocated struct sockaddr_storage *, and a
- * port range consisting in two integers. The low and high end are always set
- * even if the port is unspecified, in which case (0,0) is returned. The low
- * port is set in the sockaddr. Thus, it is enough to check the size of the
- * returned range to know if an array must be allocated or not. The format is
- * "addr[:[port[-port]]]", where "addr" can be a dotted IPv4 address, an IPv6
- * address, a host name, or empty or "*" to indicate INADDR_ANY. If an IPv6
- * address wants to ignore port, it must be terminated by a trailing colon (':').
- * The IPv6 '::' address is IN6ADDR_ANY, so in order to bind to a given port on
- * IPv6, use ":::port". NULL is returned if the host part cannot be resolved.
- */
-struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high)
-{
-	struct sockaddr_storage *ret = NULL;
-	char *str2;
-	char *c;
-	int portl, porth;
-
-	str2 = strdup(str);
-	if (str2 == NULL)
-		goto out;
-
-	if ((c = strrchr(str2,':')) != NULL) { /* Port */
-		char *sep;
-		*c++ = '\0';
-		sep = strchr(c, '-');
-		if (sep)
-			*sep++ = '\0';
+	if (isdigit(*port1)) {	/* single port or range */
+		port2 = strchr(port1, '-');
+		if (port2)
+			*port2++ = '\0';
 		else
-			sep = c;
-		portl = atol(c);
-		porth = atol(sep);
+			port2 = port1;
+		portl = atoi(port1);
+		porth = atoi(port2);
+		porta = portl;
 	}
-	else {
-		portl = 0;
-		porth = 0;
+	else if (*port1 == '-') { /* negative offset */
+		portl = atoi(port1 + 1);
+		porta = -portl;
 	}
-
-	ret = str2ip(str2);
-	if (!ret)
-		goto out;
+	else if (*port1 == '+') { /* positive offset */
+		porth = atoi(port1 + 1);
+		porta = porth;
+	}
+	else if (*port1) /* other any unexpected char */
+		ret = NULL;
 
-	set_host_port(ret, portl);
+	set_host_port(ret, porta);
 
-	*low = portl;
-	*high = porth;
  out:
+	if (low)
+		*low = portl;
+	if (high)
+		*high = porth;
 	free(str2);
 	return ret;
 }
@@ -889,7 +885,7 @@
 		/* HTTP url matching */
 		if (http_code == 0x68747470) {
 			/* We are looking for IP address. If you want to parse and
-			 * resolve hostname found in url, you can use str2sa(), but
+			 * resolve hostname found in url, you can use str2sa_range(), but
 			 * be warned this can slow down global daemon performances
 			 * while handling lagging dns responses.
 			 */