]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tools: make str2sa_range support all address syntaxes
authorWilly Tarreau <w@1wt.eu>
Wed, 20 Feb 2013 14:55:15 +0000 (15:55 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 20 Feb 2013 16:29:30 +0000 (17:29 +0100)
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.

include/common/standard.h
src/standard.c

index 6946ded371ababe96d281337cba27a7341b16007..0178757e9c99a4dcab08256a8c520c46d0da1b7a 100644 (file)
@@ -228,16 +228,6 @@ struct sockaddr_un *str2sun(const char *str);
  */
 struct sockaddr_storage *str2ip(const char *str);
 
-/*
- * 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.
- */
-struct sockaddr_storage *str2sa(const char *str);
-
 /*
  * 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
index 380f7bedcca8e6db4c2ffc15843dd1a877ae28d9..db0e0e409085f5e450cf07c68f18ceac21274769 100644 (file)
@@ -610,89 +610,85 @@ struct sockaddr_storage *str2ip(const char *str)
 }
 
 /*
- * 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;
        }
+       else if (*port1 == '+') { /* positive offset */
+               porth = atoi(port1 + 1);
+               porta = porth;
+       }
+       else if (*port1) /* other any unexpected char */
+               ret = NULL;
 
-       ret = str2ip(str2);
-       if (!ret)
-               goto out;
-
-       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 @@ int url2sa(const char *url, int ulen, struct sockaddr_storage *addr)
                /* 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.
                         */