From b63c45c69e3a97b05314c6f3328a0ff695e28d36 Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Wed, 8 Mar 2017 10:00:59 -0800 Subject: [PATCH] Try fallback on IPv4 ANYADDR when IPv6 ANYADDR fails https://bugs.php.net/bug.php?id=74166 A host system with no/limited IPv6 support will fail at binding the IPv6 ANYADDR address (::) as the address family is unsupported. Deal with this by handling failure to implicitly bind to :: as a soft failure, falling back to 0.0.0.0. If binding to :: failed for some other reason (e.g. port in use) then binding to 0.0.0.0 will likely fail as well, but we'll get appropriate warnings for that. --- sapi/fpm/fpm/fpm_sockets.c | 86 +++++++++++++++++++++++++------------- sapi/fpm/tests/023.phpt | 57 +++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 sapi/fpm/tests/023.phpt diff --git a/sapi/fpm/fpm/fpm_sockets.c b/sapi/fpm/fpm/fpm_sockets.c index f27bb4afe3..d58a8f82b6 100644 --- a/sapi/fpm/fpm/fpm_sockets.c +++ b/sapi/fpm/fpm/fpm_sockets.c @@ -246,27 +246,63 @@ enum fpm_address_domain fpm_sockets_domain_from_address(char *address) /* {{{ */ } /* }}} */ -static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */ +static int fpm_socket_af_inet_socket_by_addr(struct fpm_worker_pool_s *wp, const char *addr, const char *port) /* {{{ */ { struct addrinfo hints, *servinfo, *p; + char tmpbuf[INET6_ADDRSTRLEN]; + int status; + int sock = -1; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((status = getaddrinfo(addr, port, &hints, &servinfo)) != 0) { + zlog(ZLOG_ERROR, "getaddrinfo: %s\n", gai_strerror(status)); + return -1; + } + + for (p = servinfo; p != NULL; p = p->ai_next) { + inet_ntop(p->ai_family, fpm_get_in_addr(p->ai_addr), tmpbuf, INET6_ADDRSTRLEN); + if (sock < 0) { + if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) { + zlog(ZLOG_DEBUG, "Found address for %s, socket opened on %s", addr, tmpbuf); + } + } else { + zlog(ZLOG_WARNING, "Found multiple addresses for %s, %s ignored", addr, tmpbuf); + } + } + + freeaddrinfo(servinfo); + + return sock; +} +/* }}} */ + +static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */ +{ char *dup_address = strdup(wp->config->listen_address); char *port_str = strrchr(dup_address, ':'); char *addr = NULL; - char tmpbuf[INET6_ADDRSTRLEN]; int addr_len; int port = 0; int sock = -1; - int status; if (port_str) { /* this is host:port pair */ *port_str++ = '\0'; port = atoi(port_str); addr = dup_address; + + /* strip brackets from address for getaddrinfo */ + addr_len = strlen(addr); + if (addr[0] == '[' && addr[addr_len - 1] == ']') { + addr[addr_len - 1] = '\0'; + addr++; + } + } else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */ port = atoi(dup_address); port_str = dup_address; - /* IPv6 catch-all + IPv4-mapped */ - addr = "::"; } if (port == 0) { @@ -274,36 +310,28 @@ static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* return -1; } - /* strip brackets from address for getaddrinfo */ - addr_len = strlen(addr); - if (addr[0] == '[' && addr[addr_len - 1] == ']') { - addr[addr_len - 1] = '\0'; - addr++; - } + if (addr) { + /* Bind a specific address */ + sock = fpm_socket_af_inet_socket_by_addr(wp, addr, port_str); + } else { + /* Bind ANYADDR + * + * Try "::" first as that covers IPv6 ANYADDR and mapped IPv4 ANYADDR + * silencing warnings since failure is an option + * + * If that fails (because AF_INET6 is unsupported) retry with 0.0.0.0 + */ + int old_level = zlog_set_level(ZLOG_ALERT); + sock = fpm_socket_af_inet_socket_by_addr(wp, "::", port_str); + zlog_set_level(old_level); - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if ((status = getaddrinfo(addr, port_str, &hints, &servinfo)) != 0) { - zlog(ZLOG_ERROR, "getaddrinfo: %s\n", gai_strerror(status)); - free(dup_address); - return -1; - } - - for (p = servinfo; p != NULL; p = p->ai_next) { - inet_ntop(p->ai_family, fpm_get_in_addr(p->ai_addr), tmpbuf, INET6_ADDRSTRLEN); if (sock < 0) { - if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) { - zlog(ZLOG_DEBUG, "Found address for %s, socket opened on %s", addr, tmpbuf); - } - } else { - zlog(ZLOG_WARNING, "Found multiple addresses for %s, %s ignored", addr, tmpbuf); + zlog(ZLOG_NOTICE, "Failed implicitly binding to ::, retrying with 0.0.0.0"); + sock = fpm_socket_af_inet_socket_by_addr(wp, "0.0.0.0", port_str); } } free(dup_address); - freeaddrinfo(servinfo); return sock; } diff --git a/sapi/fpm/tests/023.phpt b/sapi/fpm/tests/023.phpt new file mode 100644 index 0000000000..13d5cc0a78 --- /dev/null +++ b/sapi/fpm/tests/023.phpt @@ -0,0 +1,57 @@ +--TEST-- +FPM: Test already bound address +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +[%d-%s-%d %d:%d:%f] NOTICE: pid %d, fpm_socket_af_inet_listening_socket(), line %d: Failed implicitly binding to ::, retrying with 0.0.0.0 +[%d-%s-%d %d:%d:%f] ERROR: pid %d, fpm_sockets_new_listening_socket(), line %d: unable to bind listening socket for address '%d': %s +--CLEAN-- + -- 2.40.0