com php-src: Try fallback on IPv4 ANYADDR when IPv6 ANYADDR fails: sapi/fpm/fpm/fpm_sockets.c sapi/fpm/tests/023.php t

[email protected] (Sara Golemon)
Newsgroups php.cvs
Message-ID <[email protected]>
Commit:    b63c45c69e3a97b05314c6f3328a0ff695e28d36
Author:    Sara Golemon <[email protected]>         Wed, 8 Mar 2017 10:00:59 -0800
Parents:   2ba2b768cc4304048cc12250266edf44a3955d47
Branches:  master

Link:       http://git.php.net/?p=php-src.git;a=commitdiff;h=b63c45c69e3a97b05314c6f3328a0ff695e28d36

Log:
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.

Changed paths:
  M  sapi/fpm/fpm/fpm_sockets.c
  A  sapi/fpm/tests/023.phpt


Diff:
diff --git a/sapi/fpm/fpm/fpm_sockets.c b/sapi/fpm/fpm/fpm_sockets.c
index f27bb4a..d58a8f8 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 0000000..13d5cc0
--- /dev/null
+++ b/sapi/fpm/tests/023.phpt
@@ -0,0 +1,57 @@
+--TEST--
+FPM: Test already bound address
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+$port = 9000+PHP_INT_SIZE;
+
+$cfg = <<<EOT
+[global]
+log_level = debug
+error_log = $logfile
+[unconfined]
+listen = $port
+ping.path = /ping
+ping.response = pong
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+EOT;
+
+// Occupy our port and let things fail
+$busy = stream_socket_server("tcp://[::]:$port");
+
+$fpm = run_fpm($cfg, $tail);
+if (is_resource($fpm)) {
+	/* Expect two specific lines of log output and show them
+	 * If we get any different number of those patterns, display whole log
+	 */
+	$out = $all = '';
+	$count = 0;
+	while (!feof($tail)) {
+		$line = fgets($tail);
+		$all .= $line;
+		if ((false !== strpos($line, 'retrying with 0.0.0.0')) ||
+		    (false !== strpos($line, 'unable to bind'))) {
+			$out .= $line;
+			++$count;
+		}
+	}
+	echo ($count == 2) ? $out : $all;
+}
+?>
+--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--
+<?php
+    $logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+    @unlink($logfile);
+?>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.