]> granicus.if.org Git - php/commitdiff
add IPv6 support to php-fpm
authorRobin Gloster <robin@loc-com.de>
Fri, 3 Oct 2014 09:35:27 +0000 (11:35 +0200)
committerFerenc Kovacs <tyrael@php.net>
Fri, 3 Oct 2014 09:35:27 +0000 (11:35 +0200)
sapi/fpm/fpm/fpm_sockets.c
sapi/fpm/fpm/fpm_sockets.h
sapi/fpm/php-fpm.conf.in
sapi/fpm/tests/003.phpt [new file with mode: 0644]

index e056565ea41199ae0bd54f30e79288e7961cc964..da14d63d8c367eeb2a35b354dca3f7f32bfadf85 100644 (file)
@@ -39,29 +39,6 @@ struct listening_socket_s {
 
 static struct fpm_array_s sockets_list;
 
-static int fpm_sockets_resolve_af_inet(char *node, char *service, struct sockaddr_in *addr) /* {{{ */
-{
-       struct addrinfo *res;
-       struct addrinfo hints;
-       int ret;
-
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_family = AF_INET;
-       ret = getaddrinfo(node, service, &hints, &res);
-
-       if (ret != 0) {
-               zlog(ZLOG_ERROR, "can't resolve hostname '%s%s%s': getaddrinfo said: %s%s%s\n",
-                                       node, service ? ":" : "", service ? service : "",
-                                       gai_strerror(ret), ret == EAI_SYSTEM ? ", system error: " : "", ret == EAI_SYSTEM ? strerror(errno) : "");
-               return -1;
-       }
-
-       *addr = *(struct sockaddr_in *) res->ai_addr;
-       freeaddrinfo(res);
-       return 0;
-}
-/* }}} */
-
 enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
 
 static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
@@ -98,14 +75,23 @@ static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
 }
 /* }}} */
 
+static void *fpm_get_in_addr(struct sockaddr *sa) /* {{{ */
+{
+    if (sa->sa_family == AF_INET) {
+        return &(((struct sockaddr_in*)sa)->sin_addr);
+    }
+
+    return &(((struct sockaddr_in6*)sa)->sin6_addr);
+}
+/* }}} */
+
 static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */
 {
        if (key == NULL) {
                switch (type) {
                        case FPM_AF_INET : {
-                               struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
-                               key = alloca(sizeof("xxx.xxx.xxx.xxx:ppppp"));
-                               sprintf(key, "%u.%u.%u.%u:%u", IPQUAD(&sa_in->sin_addr), (unsigned int) ntohs(sa_in->sin_port));
+                               key = alloca(INET6_ADDRSTRLEN);
+                               inet_ntop(sa->sa_family, fpm_get_in_addr(sa), key, sizeof key);
                                break;
                        }
 
@@ -254,11 +240,14 @@ 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) /* {{{ */
 {
-       struct sockaddr_in sa_in;
+       struct addrinfo hints, *servinfo, *p;
        char *dup_address = strdup(wp->config->listen_address);
-       char *port_str = strchr(dup_address, ':');
+       char *port_str = strrchr(dup_address, ':');
        char *addr = NULL;
+       int addr_len;
        int port = 0;
+       int sock;
+       int status;
 
        if (port_str) { /* this is host:port pair */
                *port_str++ = '\0';
@@ -274,23 +263,35 @@ static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /*
                return -1;
        }
 
-       memset(&sa_in, 0, sizeof(sa_in));
-
-       if (addr) {
-               sa_in.sin_addr.s_addr = inet_addr(addr);
-               if (sa_in.sin_addr.s_addr == INADDR_NONE) { /* do resolve */
-                       if (0 > fpm_sockets_resolve_af_inet(addr, NULL, &sa_in)) {
-                               return -1;
-                       }
-                       zlog(ZLOG_NOTICE, "address '%s' resolved as %u.%u.%u.%u", addr, IPQUAD(&sa_in.sin_addr));
+       // strip brackets from address for getaddrinfo
+       if (addr != NULL) {
+               addr_len = strlen(addr);
+               if (addr[0] == '[' && addr[addr_len - 1] == ']') {
+                       addr[addr_len - 1] = '\0';
+                       addr++;
                }
-       } else {
-               sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
        }
-       sa_in.sin_family = AF_INET;
-       sa_in.sin_port = htons(port);
+
+       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));
+               return -1;
+       }
+
        free(dup_address);
-       return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in));
+
+       for (p = servinfo; p != NULL; p = p->ai_next) {
+               if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) {
+                       break;
+               }
+       }
+
+       freeaddrinfo(servinfo);
+
+       return sock;
 }
 /* }}} */
 
index 121c016a7b89bce48bfa84bcecf89cfb253fadff..446c78e410a6535f908afdf74acea62b9f2b8cd7 100644 (file)
@@ -45,10 +45,4 @@ static inline int fd_set_blocked(int fd, int blocked) /* {{{ */
 }
 /* }}} */
 
-#define IPQUAD(sin_addr) \
-                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[0], \
-                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[1], \
-                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[2], \
-                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[3]
-
 #endif
index c5f4abc59ca2cc982992c14c7b47cf3257110fe1..631bc46f42acdbc198bb34d1de982fddaec94329 100644 (file)
@@ -152,6 +152,8 @@ group = @php_fpm_group@
 ; Valid syntaxes are:
 ;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
 ;                            a specific port;
+;   '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
+;                            a specific port;
 ;   'port'                 - to listen on a TCP socket to all addresses on a
 ;                            specific port;
 ;   '/path/to/unix/socket' - to listen on a unix socket.
diff --git a/sapi/fpm/tests/003.phpt b/sapi/fpm/tests/003.phpt
new file mode 100644 (file)
index 0000000..389cb24
--- /dev/null
@@ -0,0 +1,53 @@
+--TEST--
+FPM: Test IPv6 support
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+
+$cfg = <<<EOT
+[global]
+error_log = $logfile
+[unconfined]
+listen = [::1]:9000
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+EOT;
+
+$fpm = run_fpm($cfg, $tail);
+if (is_resource($fpm)) {
+    var_dump(fgets($tail));
+    var_dump(fgets($tail));
+    $i = 0;
+    while (($i++ < 30) && !($fp = fsockopen('[::1]', 9000))) {
+        usleep(10000);
+    }
+    if ($fp) {
+        echo "Done\n";
+        fclose($fp);
+    }
+    proc_terminate($fpm);
+    stream_get_contents($tail);
+    fclose($tail);
+    proc_close($fpm);
+}
+
+?>
+--EXPECTF--
+string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
+"
+string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
+"
+Done
+--CLEAN--
+<?php
+    $logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+    @unlink($logfile);
+?>