]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tools: support specifying explicit address families in str2sa_range()
authorWilly Tarreau <w@1wt.eu>
Sun, 10 Mar 2013 20:32:12 +0000 (21:32 +0100)
committerWilly Tarreau <w@1wt.eu>
Sun, 10 Mar 2013 21:46:55 +0000 (22:46 +0100)
This change allows one to force the address family in any address parsed
by str2sa_range() by specifying it as a prefix followed by '@' then the
address. Currently supported address prefixes are 'ipv4@', 'ipv6@', 'unix@'.
This also helps forcing resolving for host names (when getaddrinfo is used),
and force the family of the empty address (eg: 'ipv4@' = 0.0.0.0 while
'ipv6@' = ::).

The main benefits is that unix sockets can now get a local name without
being forced to begin with a slash. This is useful during development as
it is no longer necessary to have stats socket sent to /tmp.

doc/configuration.txt
include/common/standard.h
src/cfgparse.c
src/standard.c

index 195f330ec4c70efdff6af25321430f599e0515c4..4d575856e035eb53a8b0dd6174381df815509b60 100644 (file)
@@ -1609,6 +1609,13 @@ bind /<path> [, ...] [param*]
                   listen on. If unset, all IPv4 addresses of the system will be
                   listened on. The same will apply for '*' or the system's
                   special address "0.0.0.0". The IPv6 equivalent is '::'.
+                  Optionally, an address family prefix may be used before the
+                  address to force the family regardless of the address format,
+                  which can be useful to specify a path to a unix socket with
+                  no slash ('/'). Currently supported prefixes are :
+                    - 'ipv4@'  -> address is always IPv4
+                    - 'ipv6@'  -> address is always IPv6
+                    - 'unix@'  -> address is a path to a local unix socket
 
     <port_range>  is either a unique TCP port, or a port range for which the
                   proxy will accept connections for the IP address specified
@@ -1660,6 +1667,11 @@ bind /<path> [, ...] [param*]
             bind :80
             bind :443 ssl crt /etc/haproxy/site.pem
 
+        listen http_https_proxy_explicit
+            bind ipv6@:80
+            bind ipv4@public_ssl:443 ssl crt /etc/haproxy/site.pem
+            bind unix@ssl-frontend.sock user root mode 600 accept-proxy
+
   See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol
              documentation, and section 5 about bind options.
 
@@ -5072,7 +5084,13 @@ server <name> <address>[:[port]] [param*]
               intercepted and haproxy must forward to the original destination
               address. This is more or less what the "transparent" keyword does
               except that with a server it's possible to limit concurrency and
-              to report statistics.
+              to report statistics. Optionally, an address family prefix may be
+              used before the address to force the family regardless of the
+              address format, which can be useful to specify a path to a unix
+              socket with no slash ('/'). Currently supported prefixes are :
+                    - 'ipv4@'  -> address is always IPv4
+                    - 'ipv6@'  -> address is always IPv6
+                    - 'unix@'  -> address is a path to a local unix socket
 
     <port>    is an optional port specification. If set, all connections will
               be sent to this port. If unset, the same port the client
@@ -5087,6 +5105,7 @@ server <name> <address>[:[port]] [param*]
   Examples :
         server first  10.1.1.1:1080 cookie first  check inter 1000
         server second 10.1.1.2:1080 cookie second check inter 1000
+        server transp ipv4@
 
   See also: "default-server", "http-send-name-header" and section 5 about
              server options
@@ -5101,8 +5120,16 @@ source <addr>[:<port>] [interface <name>]
   Arguments :
     <addr>    is the IPv4 address HAProxy will bind to before connecting to a
               server. This address is also used as a source for health checks.
+
               The default value of 0.0.0.0 means that the system will select
-              the most appropriate address to reach its destination.
+              the most appropriate address to reach its destination. Optionally
+              an address family prefix may be used before the address to force
+              the family regardless of the address format, which can be useful
+              to specify a path to a unix socket with no slash ('/'). Currently
+              supported prefixes are :
+                - 'ipv4@' -> address is always IPv4
+                - 'ipv6@' -> address is always IPv6
+                - 'unix@' -> address is a path to a local unix socket
 
     <port>    is an optional port. It is normally not needed but may be useful
               in some very specific contexts. The default value of zero means
index 318e10f9e2c72e1d080a248899f828b79849c67f..f9f21b06a967ccdad092be9f8452f4b4abdb2ae7 100644 (file)
@@ -211,17 +211,6 @@ extern const char *invalid_char(const char *name);
  */
 extern const char *invalid_domainchar(const char *name);
 
-/*
- * converts <str> to a struct sockaddr_storage* provided by the caller. The
- * string is assumed to contain only an address, no port. The address can be a
- * dotted IPv4 address, an IPv6 address, a host name, or empty or "*" to
- * indicate INADDR_ANY. NULL is returned if the host part cannot be resolved.
- * The return address will only have the address family and the address set,
- * all other fields remain zero. The string is not supposed to be modified.
- * The IPv6 '::' address is IN6ADDR_ANY.
- */
-struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa);
-
 /*
  * 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 2c8faadb3280427f156fcb324799f22bd2651625..3821edb77dafc2f6a48686d76f783f1f2998ca93 100644 (file)
@@ -1928,7 +1928,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
                        err_code |= ERR_WARN;
 
-               if ( *(args[1]) != '/' && strchr(args[1], ':') == NULL) {
+               if (!*(args[1])) {
                        Alert("parsing [%s:%d] : '%s' expects {<path>|[addr1]:port1[-end1]}{,[addr]:port[-end]}... as arguments.\n",
                              file, linenum, args[0]);
                        err_code |= ERR_ALERT | ERR_FATAL;
index c491534eb69c1d0d268ff62dbab179ac938ae8b6..c670be0ac0124a48a4e620c5112b3dc491cbe531 100644 (file)
@@ -505,6 +505,10 @@ const char *invalid_domainchar(const char *name) {
 
 /*
  * converts <str> to a struct sockaddr_storage* provided by the caller. The
+ * caller must have zeroed <sa> first, and may have set sa->ss_family to force
+ * parse a specific address format. If the ss_family is 0 or AF_UNSPEC, then
+ * the function tries to guess the address family from the syntax. If the
+ * family is forced and the format doesn't match, an error is returned. The
  * string is assumed to contain only an address, no port. The address can be a
  * dotted IPv4 address, an IPv6 address, a host name, or empty or "*" to
  * indicate INADDR_ANY. NULL is returned if the host part cannot be resolved.
@@ -512,32 +516,36 @@ const char *invalid_domainchar(const char *name) {
  * all other fields remain zero. The string is not supposed to be modified.
  * The IPv6 '::' address is IN6ADDR_ANY.
  */
-struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa)
+static struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa)
 {
        struct hostent *he;
 
-       memset(sa, 0, sizeof(sa));
-
        /* Any IPv6 address */
        if (str[0] == ':' && str[1] == ':' && !str[2]) {
-               sa->ss_family = AF_INET6;
+               if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
+                       sa->ss_family = AF_INET6;
+               else if (sa->ss_family != AF_INET6)
+                       goto fail;
                return sa;
        }
 
-       /* Any IPv4 address */
+       /* Any address for the family, defaults to IPv4 */
        if (!str[0] || (str[0] == '*' && !str[1])) {
-               sa->ss_family = AF_INET;
+               if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
+                       sa->ss_family = AF_INET;
                return sa;
        }
 
        /* check for IPv6 first */
-       if (inet_pton(AF_INET6, str, &((struct sockaddr_in6 *)sa)->sin6_addr)) {
+       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;
        }
 
        /* then check for IPv4 */
-       if (inet_pton(AF_INET, str, &((struct sockaddr_in *)sa)->sin_addr)) {
+       if ((!sa->ss_family || sa->ss_family == AF_UNSPEC || sa->ss_family == AF_INET) &&
+           inet_pton(AF_INET, str, &((struct sockaddr_in *)sa)->sin_addr)) {
                sa->ss_family = AF_INET;
                return sa;
        }
@@ -545,7 +553,11 @@ struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa)
        /* try to resolve an IPv4/IPv6 hostname */
        he = gethostbyname(str);
        if (he) {
-               sa->ss_family = he->h_addrtype;
+               if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
+                       sa->ss_family = he->h_addrtype;
+               else if (sa->ss_family != he->h_addrtype)
+                       goto fail;
+
                switch (sa->ss_family) {
                case AF_INET:
                        ((struct sockaddr_in *)sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list);
@@ -561,13 +573,17 @@ struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa)
 
                memset(&result, 0, sizeof(result));
                memset(&hints, 0, sizeof(hints));
-               hints.ai_family = AF_UNSPEC;
+               hints.ai_family = sa->ss_family ? sa->ss_family : AF_UNSPEC;
                hints.ai_socktype = SOCK_DGRAM;
                hints.ai_flags = AI_PASSIVE;
                hints.ai_protocol = 0;
 
                if (getaddrinfo(str, NULL, &hints, &result) == 0) {
-                       sa->ss_family = result->ai_family;
+                       if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
+                               sa->ss_family = result->ai_family;
+                       else if (sa->ss_family != result->ai_family)
+                               goto fail;
+
                        switch (result->ai_family) {
                        case AF_INET:
                                memcpy((struct sockaddr_in *)sa, result->ai_addr, result->ai_addrlen);
@@ -583,7 +599,7 @@ struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa)
        }
 #endif
        /* unsupported address family */
-
+ fail:
        return NULL;
 }
 
@@ -611,10 +627,16 @@ struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa)
  *    - "::"        => family will be AF_INET6 and address will be IN6ADDR_ANY
  *    - a host name => family and address will depend on host name resolving.
  *
+ * A prefix may be passed in before the address above to force the family :
+ *    - "ipv4@"  => force address to resolve as IPv4 and fail if not possible.
+ *    - "ipv6@"  => force address to resolve as IPv6 and fail if not possible.
+ *    - "unix@"  => force address to be a path to a UNIX socket even if the
+ *                  path does not start with a '/'
+ *
  * 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.
+ * 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).
@@ -623,20 +645,39 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char
 {
        static struct sockaddr_storage ss;
        struct sockaddr_storage *ret = NULL;
-       char *str2;
+       char *back, *str2;
        char *port1, *port2;
        int portl, porth, porta;
 
        portl = porth = porta = 0;
 
-       str2 = strdup(str);
+       str2 = back = strdup(str);
        if (str2 == NULL) {
                memprintf(err, "out of memory in '%s'\n", __FUNCTION__);
                goto out;
        }
 
-       if (*str2 == '/') {
-               /* unix socket */
+       memset(&ss, 0, sizeof(ss));
+
+       if (strncmp(str2, "unix@", 5) == 0) {
+               str2 += 5;
+               ss.ss_family = AF_UNIX;
+       }
+       else if (strncmp(str2, "ipv4@", 5) == 0) {
+               str2 += 5;
+               ss.ss_family = AF_INET;
+       }
+       else if (strncmp(str2, "ipv6@", 5) == 0) {
+               str2 += 5;
+               ss.ss_family = AF_INET6;
+       }
+       else if (*str2 == '/') {
+               ss.ss_family = AF_UNIX;
+       }
+       else
+               ss.ss_family = AF_UNSPEC;
+
+       if (ss.ss_family == AF_UNIX) {
                int prefix_path_len;
                int max_path_len;
 
@@ -652,8 +693,6 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char
                        goto out;
                }
 
-               memset(&ss, 0, sizeof(ss));
-               ss.ss_family = AF_UNIX;
                if (pfx) {
                        memcpy(((struct sockaddr_un *)&ss)->sun_path, pfx, prefix_path_len);
                        strcpy(((struct sockaddr_un *)&ss)->sun_path + prefix_path_len, str2);
@@ -662,7 +701,7 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char
                        strcpy(((struct sockaddr_un *)&ss)->sun_path, str2);
                }
        }
-       else {
+       else { /* IPv4 and IPv6 */
                port1 = strrchr(str2, ':');
                if (port1)
                        *port1++ = '\0';
@@ -705,7 +744,7 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char
                *low = portl;
        if (high)
                *high = porth;
-       free(str2);
+       free(back);
        return ret;
 }