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.
This commit is contained in:
Willy Tarreau 2013-02-20 15:55:15 +01:00
parent 7cf479cc09
commit d4448bc836
2 changed files with 63 additions and 77 deletions

View File

@ -228,16 +228,6 @@ struct sockaddr_un *str2sun(const char *str);
*/ */
struct sockaddr_storage *str2ip(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 * 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 * port range consisting in two integers. The low and high end are always set

View File

@ -610,89 +610,85 @@ struct sockaddr_storage *str2ip(const char *str)
} }
/* /*
* converts <str> to a locally allocated struct sockaddr_storage *. * Converts <str> to a locally allocated struct sockaddr_storage *, and a port
* The format is "addr[:[port]]", where "addr" can be a dotted IPv4 address, an * range or offset consisting in two integers that the caller will have to
* IPv6 address, a host name, or empty or "*" to indicate INADDR_ANY. If an IPv6 * check to find the relevant input format. The following format are supported :
* 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 * String format | address | port | low | high
* IPv6, use ":::port". NULL is returned if the host part cannot be resolved. * addr | <addr> | 0 | 0 | 0
*/ * addr: | <addr> | 0 | 0 | 0
struct sockaddr_storage *str2sa(const char *str) * addr:port | <addr> | <port> | <port> | <port>
{ * addr:pl-ph | <addr> | <pl> | <pl> | <ph>
struct sockaddr_storage *ret = NULL; * addr:+port | <addr> | <port> | 0 | <port>
char *str2; * addr:-port | <addr> |-<port> | <port> | 0
char *c; *
int port; * 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
str2 = strdup(str); * was specified. The caller may pass NULL for <low> and <high> if it is not
if (str2 == NULL) * interested in retrieving port ranges.
goto out; *
* Note that <addr> above may also be :
if ((c = strrchr(str2, ':')) != NULL) { /* Port */ * - empty ("") => family will be AF_INET and address will be INADDR_ANY
*c++ = '\0'; * - "*" => family will be AF_INET and address will be INADDR_ANY
port = atol(c); * - "::" => family will be AF_INET6 and address will be IN6ADDR_ANY
} * - a host name => family and address will depend on host name resolving.
else *
port = 0; * 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
ret = str2ip(str2); * returned if the address cannot be parsed. The <low> and <high> ports are
if (!ret) * always initialized if non-null.
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 *str2sa_range(const char *str, int *low, int *high)
{ {
struct sockaddr_storage *ret = NULL; struct sockaddr_storage *ret = NULL;
char *str2; char *str2;
char *c; char *port1, *port2;
int portl, porth; int portl, porth, porta;
portl = porth = porta = 0;
str2 = strdup(str); str2 = strdup(str);
if (str2 == NULL) if (str2 == NULL)
goto out; goto out;
if ((c = strrchr(str2,':')) != NULL) { /* Port */ port1 = strrchr(str2, ':');
char *sep; if (port1)
*c++ = '\0'; *port1++ = '\0';
sep = strchr(c, '-'); else
if (sep) port1 = "";
*sep++ = '\0';
else
sep = c;
portl = atol(c);
porth = atol(sep);
}
else {
portl = 0;
porth = 0;
}
ret = str2ip(str2); ret = str2ip(str2);
if (!ret) if (!ret)
goto out; goto out;
set_host_port(ret, portl); if (isdigit(*port1)) { /* single port or range */
port2 = strchr(port1, '-');
if (port2)
*port2++ = '\0';
else
port2 = port1;
portl = atoi(port1);
porth = atoi(port2);
porta = portl;
}
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;
set_host_port(ret, porta);
*low = portl;
*high = porth;
out: out:
if (low)
*low = portl;
if (high)
*high = porth;
free(str2); free(str2);
return ret; return ret;
} }
@ -889,7 +885,7 @@ int url2sa(const char *url, int ulen, struct sockaddr_storage *addr)
/* HTTP url matching */ /* HTTP url matching */
if (http_code == 0x68747470) { if (http_code == 0x68747470) {
/* We are looking for IP address. If you want to parse and /* 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 * be warned this can slow down global daemon performances
* while handling lagging dns responses. * while handling lagging dns responses.
*/ */