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);
/*
* 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

View 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.
*/
struct sockaddr_storage *str2sa(const char *str)
{
struct sockaddr_storage *ret = NULL;
char *str2;
char *c;
int port;
str2 = strdup(str);
if (str2 == NULL)
goto out;
if ((c = strrchr(str2, ':')) != NULL) { /* Port */
*c++ = '\0';
port = atol(c);
}
else
port = 0;
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.
* 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_range(const char *str, int *low, int *high)
{
struct sockaddr_storage *ret = NULL;
char *str2;
char *c;
int portl, porth;
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 */
char *sep;
*c++ = '\0';
sep = strchr(c, '-');
if (sep)
*sep++ = '\0';
else
sep = c;
portl = atol(c);
porth = atol(sep);
}
else {
portl = 0;
porth = 0;
}
port1 = strrchr(str2, ':');
if (port1)
*port1++ = '\0';
else
port1 = "";
ret = str2ip(str2);
if (!ret)
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:
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.
*/