mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-21 22:01:31 +02:00
MEDIUM: tools: support specifying explicit address families in str2sa_range()
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.
This commit is contained in:
parent
902636fd73
commit
24709286fe
@ -1609,6 +1609,13 @@ bind /<path> [, ...] [param*]
|
|||||||
listen on. If unset, all IPv4 addresses of the system will be
|
listen on. If unset, all IPv4 addresses of the system will be
|
||||||
listened on. The same will apply for '*' or the system's
|
listened on. The same will apply for '*' or the system's
|
||||||
special address "0.0.0.0". The IPv6 equivalent is '::'.
|
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
|
<port_range> is either a unique TCP port, or a port range for which the
|
||||||
proxy will accept connections for the IP address specified
|
proxy will accept connections for the IP address specified
|
||||||
@ -1660,6 +1667,11 @@ bind /<path> [, ...] [param*]
|
|||||||
bind :80
|
bind :80
|
||||||
bind :443 ssl crt /etc/haproxy/site.pem
|
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
|
See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol
|
||||||
documentation, and section 5 about bind options.
|
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
|
intercepted and haproxy must forward to the original destination
|
||||||
address. This is more or less what the "transparent" keyword does
|
address. This is more or less what the "transparent" keyword does
|
||||||
except that with a server it's possible to limit concurrency and
|
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
|
<port> is an optional port specification. If set, all connections will
|
||||||
be sent to this port. If unset, the same port the client
|
be sent to this port. If unset, the same port the client
|
||||||
@ -5087,6 +5105,7 @@ server <name> <address>[:[port]] [param*]
|
|||||||
Examples :
|
Examples :
|
||||||
server first 10.1.1.1:1080 cookie first check inter 1000
|
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 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
|
See also: "default-server", "http-send-name-header" and section 5 about
|
||||||
server options
|
server options
|
||||||
@ -5101,8 +5120,16 @@ source <addr>[:<port>] [interface <name>]
|
|||||||
Arguments :
|
Arguments :
|
||||||
<addr> is the IPv4 address HAProxy will bind to before connecting to a
|
<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.
|
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 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
|
<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
|
in some very specific contexts. The default value of zero means
|
||||||
|
@ -211,17 +211,6 @@ extern const char *invalid_char(const char *name);
|
|||||||
*/
|
*/
|
||||||
extern const char *invalid_domainchar(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
|
* 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
|
||||||
|
@ -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))
|
if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
|
||||||
err_code |= ERR_WARN;
|
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",
|
Alert("parsing [%s:%d] : '%s' expects {<path>|[addr1]:port1[-end1]}{,[addr]:port[-end]}... as arguments.\n",
|
||||||
file, linenum, args[0]);
|
file, linenum, args[0]);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
@ -505,6 +505,10 @@ const char *invalid_domainchar(const char *name) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* converts <str> to a struct sockaddr_storage* provided by the caller. The
|
* 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
|
* 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
|
* 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.
|
* 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.
|
* all other fields remain zero. The string is not supposed to be modified.
|
||||||
* The IPv6 '::' address is IN6ADDR_ANY.
|
* 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;
|
struct hostent *he;
|
||||||
|
|
||||||
memset(sa, 0, sizeof(sa));
|
|
||||||
|
|
||||||
/* Any IPv6 address */
|
/* Any IPv6 address */
|
||||||
if (str[0] == ':' && str[1] == ':' && !str[2]) {
|
if (str[0] == ':' && str[1] == ':' && !str[2]) {
|
||||||
|
if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
|
||||||
sa->ss_family = AF_INET6;
|
sa->ss_family = AF_INET6;
|
||||||
|
else if (sa->ss_family != AF_INET6)
|
||||||
|
goto fail;
|
||||||
return sa;
|
return sa;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Any IPv4 address */
|
/* Any address for the family, defaults to IPv4 */
|
||||||
if (!str[0] || (str[0] == '*' && !str[1])) {
|
if (!str[0] || (str[0] == '*' && !str[1])) {
|
||||||
|
if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
|
||||||
sa->ss_family = AF_INET;
|
sa->ss_family = AF_INET;
|
||||||
return sa;
|
return sa;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for IPv6 first */
|
/* 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;
|
sa->ss_family = AF_INET6;
|
||||||
return sa;
|
return sa;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* then check for IPv4 */
|
/* 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;
|
sa->ss_family = AF_INET;
|
||||||
return sa;
|
return sa;
|
||||||
}
|
}
|
||||||
@ -545,7 +553,11 @@ struct sockaddr_storage *str2ip(const char *str, struct sockaddr_storage *sa)
|
|||||||
/* try to resolve an IPv4/IPv6 hostname */
|
/* try to resolve an IPv4/IPv6 hostname */
|
||||||
he = gethostbyname(str);
|
he = gethostbyname(str);
|
||||||
if (he) {
|
if (he) {
|
||||||
|
if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
|
||||||
sa->ss_family = he->h_addrtype;
|
sa->ss_family = he->h_addrtype;
|
||||||
|
else if (sa->ss_family != he->h_addrtype)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
switch (sa->ss_family) {
|
switch (sa->ss_family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
((struct sockaddr_in *)sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list);
|
((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(&result, 0, sizeof(result));
|
||||||
memset(&hints, 0, sizeof(hints));
|
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_socktype = SOCK_DGRAM;
|
||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
hints.ai_protocol = 0;
|
hints.ai_protocol = 0;
|
||||||
|
|
||||||
if (getaddrinfo(str, NULL, &hints, &result) == 0) {
|
if (getaddrinfo(str, NULL, &hints, &result) == 0) {
|
||||||
|
if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
|
||||||
sa->ss_family = result->ai_family;
|
sa->ss_family = result->ai_family;
|
||||||
|
else if (sa->ss_family != result->ai_family)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
switch (result->ai_family) {
|
switch (result->ai_family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
memcpy((struct sockaddr_in *)sa, result->ai_addr, result->ai_addrlen);
|
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
|
#endif
|
||||||
/* unsupported address family */
|
/* unsupported address family */
|
||||||
|
fail:
|
||||||
return NULL;
|
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
|
* - "::" => family will be AF_INET6 and address will be IN6ADDR_ANY
|
||||||
* - a host name => family and address will depend on host name resolving.
|
* - 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 ':'
|
* 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
|
* 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
|
* 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
|
* If <pfx> is non-null, it is used as a string prefix before any path-based
|
||||||
* address (typically the path to a unix socket).
|
* 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;
|
static struct sockaddr_storage ss;
|
||||||
struct sockaddr_storage *ret = NULL;
|
struct sockaddr_storage *ret = NULL;
|
||||||
char *str2;
|
char *back, *str2;
|
||||||
char *port1, *port2;
|
char *port1, *port2;
|
||||||
int portl, porth, porta;
|
int portl, porth, porta;
|
||||||
|
|
||||||
portl = porth = porta = 0;
|
portl = porth = porta = 0;
|
||||||
|
|
||||||
str2 = strdup(str);
|
str2 = back = strdup(str);
|
||||||
if (str2 == NULL) {
|
if (str2 == NULL) {
|
||||||
memprintf(err, "out of memory in '%s'\n", __FUNCTION__);
|
memprintf(err, "out of memory in '%s'\n", __FUNCTION__);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*str2 == '/') {
|
memset(&ss, 0, sizeof(ss));
|
||||||
/* unix socket */
|
|
||||||
|
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 prefix_path_len;
|
||||||
int max_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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&ss, 0, sizeof(ss));
|
|
||||||
ss.ss_family = AF_UNIX;
|
|
||||||
if (pfx) {
|
if (pfx) {
|
||||||
memcpy(((struct sockaddr_un *)&ss)->sun_path, pfx, prefix_path_len);
|
memcpy(((struct sockaddr_un *)&ss)->sun_path, pfx, prefix_path_len);
|
||||||
strcpy(((struct sockaddr_un *)&ss)->sun_path + prefix_path_len, str2);
|
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);
|
strcpy(((struct sockaddr_un *)&ss)->sun_path, str2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else { /* IPv4 and IPv6 */
|
||||||
port1 = strrchr(str2, ':');
|
port1 = strrchr(str2, ':');
|
||||||
if (port1)
|
if (port1)
|
||||||
*port1++ = '\0';
|
*port1++ = '\0';
|
||||||
@ -705,7 +744,7 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char
|
|||||||
*low = portl;
|
*low = portl;
|
||||||
if (high)
|
if (high)
|
||||||
*high = porth;
|
*high = porth;
|
||||||
free(str2);
|
free(back);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user