]> granicus.if.org Git - php/commitdiff
Improved FastCGI SAPI to support external pipe and socket servers on win32
authorDmitry Stogov <dmitry@php.net>
Wed, 28 Mar 2007 15:39:35 +0000 (15:39 +0000)
committerDmitry Stogov <dmitry@php.net>
Wed, 28 Mar 2007 15:39:35 +0000 (15:39 +0000)
sapi/cgi/cgi_main.c
sapi/cgi/fastcgi.c
sapi/cgi/fastcgi.h

index c56188be0a9ed501ff26736cadefdd2821c235d8..32d293cf8fa666f15f5f0c8eba19cc4e3ffdafda 100644 (file)
@@ -120,9 +120,7 @@ static zend_module_entry cgi_module_entry;
 
 static const opt_struct OPTIONS[] = {
        {'a', 0, "interactive"},
-#ifndef PHP_WIN32
        {'b', 1, "bindpath"},
-#endif
        {'C', 0, "no-chdir"},
        {'c', 1, "php-ini"},
        {'d', 1, "define"},
@@ -611,9 +609,7 @@ static void php_cgi_usage(char *argv0)
        php_printf("Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
                           "       %s <file> [args...]\n"
                           "  -a               Run interactively\n"
-#if !defined(PHP_WIN32)
                           "  -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
-#endif
                           "  -C               Do not chdir to the script's directory\n"
                           "  -c <path>|<file> Look for php.ini file in this directory\n"
                           "  -n               No php.ini file will be used\n"
@@ -970,19 +966,6 @@ void fastcgi_cleanup(int signal)
        exit(0);
 }
 
-#ifndef PHP_WIN32
-static int is_port_number(const char *bindpath)
-{
-       while (*bindpath) {
-               if (*bindpath < '0' || *bindpath > '9') {
-                       return 0;
-               }
-               bindpath++;
-       }
-       return 1;
-}
-#endif
-
 PHP_INI_BEGIN()
        STD_PHP_INI_ENTRY("cgi.rfc2616_headers",     "0",  PHP_INI_ALL,    OnUpdateBool,   rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
        STD_PHP_INI_ENTRY("cgi.nph",                 "0",  PHP_INI_ALL,    OnUpdateBool,   nph, php_cgi_globals_struct, php_cgi_globals)
@@ -1089,9 +1072,7 @@ int main(int argc, char *argv[])
        int max_requests = 500;
        int requests = 0;
        int fastcgi = fcgi_is_fastcgi();
-#ifndef PHP_WIN32
        char *bindpath = NULL;
-#endif
        int fcgi_fd = 0;
        fcgi_request request;
 #ifndef PHP_WIN32
@@ -1152,7 +1133,6 @@ int main(int argc, char *argv[])
                        case 'n':
                                cgi_sapi_module.php_ini_ignore = 1;
                                break;
-#ifndef PHP_WIN32
                        /* if we're started on command line, check to see if
                           we are being started as an 'external' fastcgi
                           server by accepting a bindpath parameter. */
@@ -1192,7 +1172,6 @@ int main(int argc, char *argv[])
                                        bindpath = strdup(php_optarg);
                                }
                                break;
-#endif
                }
 
        }
@@ -1259,26 +1238,10 @@ consult the installation file that came with this distribution, or visit \n\
                }
        }
 
-#ifndef PHP_WIN32
        /* for windows, socket listening is broken in the fastcgi library itself
           so dissabling this feature on windows till time is available to fix it */
        if (bindpath) {
-               /* Pass on the arg to the FastCGI library, with one exception.
-                * If just a port is specified, then we prepend a ':' onto the
-                * path (it's what the fastcgi library expects)
-                */             
-               if (strchr(bindpath, ':') == NULL && is_port_number(bindpath)) {
-                       char *tmp;
-
-                       tmp = malloc(strlen(bindpath) + 2);
-                       tmp[0] = ':';
-                       memcpy(tmp + 1, bindpath, strlen(bindpath) + 1);
-
-                       fcgi_fd = fcgi_listen(tmp, 128);
-                       free(tmp);
-               } else {
-                       fcgi_fd = fcgi_listen(bindpath, 128);
-               }
+               fcgi_fd = fcgi_listen(bindpath, 128);
                if (fcgi_fd < 0) {
                        fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
 #ifdef ZTS
@@ -1288,7 +1251,6 @@ consult the installation file that came with this distribution, or visit \n\
                }
                fastcgi = fcgi_is_fastcgi();
        }
-#endif
        if (fastcgi) {
                /* How many times to run PHP scripts before dying */
                if (getenv("PHP_FCGI_MAX_REQUESTS")) {
@@ -1749,11 +1711,9 @@ fastcgi_request_done:
                        requests++;
                        if (max_requests && (requests == max_requests)) {
                                fcgi_finish_request(&request);
-#ifndef PHP_WIN32
                                if (bindpath) {
                                        free(bindpath);
                                }
-#endif
                                break;
                        }
                        /* end of fastcgi loop */
index 7d340c4ccccdffff992498699dcae4112fe4f12f..11ef73cf787df868bb57e56cf23516901b3c04b2 100644 (file)
@@ -32,6 +32,7 @@
 #include <windows.h>
 
        typedef unsigned int size_t;
+       typedef unsigned int in_addr_t;
 
        struct sockaddr_un {
                short   sun_family;
@@ -71,6 +72,8 @@
 # include <netdb.h>
 # include <signal.h>
 
+# define closesocket(s) close(s)
+
 # if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
 #  include <sys/poll.h>
 # endif
@@ -147,6 +150,7 @@ static const fcgi_mgmt_rec fcgi_mgmt_vars[] = {
 static int is_initialized = 0;
 static int is_fastcgi = 0;
 static int in_shutdown = 0;
+static in_addr_t *allowed_clients = NULL;
 
 #ifdef _WIN32
 
@@ -160,8 +164,6 @@ static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg)
 
 #else
 
-static in_addr_t *allowed_clients = NULL;
-
 static void fcgi_signal_handler(int signo)
 {
        if (signo == SIGUSR1 || signo == SIGTERM) {
@@ -254,12 +256,89 @@ int fcgi_is_fastcgi(void)
        }
 }
 
+#ifdef _WIN32
+/* Do some black magic with the NT security API.
+ * We prepare a DACL (Discretionary Access Control List) so that
+ * we, the creator, are allowed all access, while "Everyone Else"
+ * is only allowed to read and write to the pipe.
+ * This avoids security issues on shared hosts where a luser messes
+ * with the lower-level pipe settings and screws up the FastCGI service.
+ */
+static PACL prepare_named_pipe_acl(PSECURITY_DESCRIPTOR sd, LPSECURITY_ATTRIBUTES sa)
+{
+  DWORD req_acl_size;
+  char everyone_buf[32], owner_buf[32];
+  PSID sid_everyone, sid_owner;
+  SID_IDENTIFIER_AUTHORITY
+    siaWorld = SECURITY_WORLD_SID_AUTHORITY,
+    siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
+  PACL acl;
+
+  sid_everyone = (PSID)&everyone_buf;
+  sid_owner = (PSID)&owner_buf;
+
+  req_acl_size = sizeof(ACL) +
+    (2 * ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetSidLengthRequired(1)));
+
+  acl = malloc(req_acl_size);
+
+  if (acl == NULL) {
+    return NULL;
+  }
+
+  if (!InitializeSid(sid_everyone, &siaWorld, 1)) {
+    goto out_fail;
+  }
+  *GetSidSubAuthority(sid_everyone, 0) = SECURITY_WORLD_RID;
+
+  if (!InitializeSid(sid_owner, &siaCreator, 1)) {
+    goto out_fail;
+  }
+  *GetSidSubAuthority(sid_owner, 0) = SECURITY_CREATOR_OWNER_RID;
+
+  if (!InitializeAcl(acl, req_acl_size, ACL_REVISION)) {
+    goto out_fail;
+  }
+
+  if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_GENERIC_READ | FILE_GENERIC_WRITE, sid_everyone)) {
+    goto out_fail;
+  }
+
+  if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, sid_owner)) {
+    goto out_fail;
+  }
+
+  if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
+    goto out_fail;
+  }
+
+  if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) {
+    goto out_fail;
+  }
+
+  sa->lpSecurityDescriptor = sd;
+
+  return acl;
+
+out_fail:
+  free(acl);
+  return NULL;
+}
+#endif
+
+static int is_port_number(const char *bindpath)
+{
+       while (*bindpath) {
+               if (*bindpath < '0' || *bindpath > '9') {
+                       return 0;
+               }
+               bindpath++;
+       }
+       return 1;
+}
+
 int fcgi_listen(const char *path, int backlog)
 {
-#ifdef _WIN32
-       /* TODO: Support for manual binding on TCP sockets (php -b <port>) */
-       return -1;
-#else
        char     *s;
        int       tcp = 0;
        char      host[MAXPATHLEN];
@@ -275,6 +354,12 @@ int fcgi_listen(const char *path, int backlog)
                        host[s-path] = '\0';
                        tcp = 1;
                }
+       } else if (is_port_number(path)) {
+               port = atoi(path);
+               if (port != 0) {
+                       host[0] = '\0';
+                       tcp = 1;
+               }
        }
 
        /* Prepare socket address */
@@ -303,6 +388,33 @@ int fcgi_listen(const char *path, int backlog)
                        }
                }
        } else {
+#ifdef _WIN32
+           SECURITY_DESCRIPTOR  sd;
+       SECURITY_ATTRIBUTES  sa;
+           PACL                 acl;
+               HANDLE namedPipe;
+
+               memset(&sa, 0, sizeof(sa));
+               sa.nLength = sizeof(sa);
+               sa.bInheritHandle = FALSE;
+               acl = prepare_named_pipe_acl(&sd, &sa);
+
+               namedPipe = CreateNamedPipe(path,
+                       PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                       PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
+                       PIPE_UNLIMITED_INSTANCES,
+                       8192, 8192, 0, &sa);
+               if (namedPipe == INVALID_HANDLE_VALUE) {
+                       return -1;
+               }               
+               listen_socket = _open_osfhandle((long)namedPipe, 0);
+               if (!is_initialized) {
+                       fcgi_init();
+               }
+               is_fastcgi = 1;
+               return listen_socket;
+
+#else
                int path_len = strlen(path);
 
                if (path_len >= sizeof(sa.sa_unix.sun_path)) {
@@ -318,6 +430,7 @@ int fcgi_listen(const char *path, int backlog)
                sa.sa_unix.sun_len = sock_len;
 #endif
                unlink(path);
+#endif
        }
 
        /* Create, bind socket and start listen on it */
@@ -369,8 +482,13 @@ int fcgi_listen(const char *path, int backlog)
                fcgi_init();
        }
        is_fastcgi = 1;
-       return listen_socket;
+
+#ifdef _WIN32
+       if (tcp) {
+               listen_socket = _open_osfhandle((long)listen_socket, 0);
+       }
 #endif
+       return listen_socket;
 }
 
 void fcgi_init_request(fcgi_request *req, int listen_socket)
@@ -385,6 +503,10 @@ void fcgi_init_request(fcgi_request *req, int listen_socket)
 
        req->out_hdr  = NULL;
        req->out_pos  = req->out_buf;
+
+#ifdef _WIN32
+       req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
+#endif
 }
 
 static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
@@ -394,7 +516,18 @@ static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t coun
 
        do {
                errno = 0;
+#ifdef _WIN32
+               if (!req->tcp) {
+                       ret = write(req->fd, ((char*)buf)+n, count-n);
+               } else {
+                       ret = send(req->fd, ((char*)buf)+n, count-n, 0);
+                       if (ret <= 0) {
+                               errno = WSAGetLastError();
+                       }
+               }
+#else
                ret = write(req->fd, ((char*)buf)+n, count-n);
+#endif
                if (ret > 0) {
                        n += ret;
                } else if (ret <= 0 && errno != 0 && errno != EINTR) {
@@ -411,7 +544,18 @@ static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count
 
        do {
                errno = 0;
+#ifdef _WIN32
+               if (!req->tcp) {
+                       ret = read(req->fd, ((char*)buf)+n, count-n);
+               } else {
+                       ret = recv(req->fd, ((char*)buf)+n, count-n, 0);
+                       if (ret <= 0) {
+                               errno = WSAGetLastError();
+                       }
+               }
+#else
                ret = read(req->fd, ((char*)buf)+n, count-n);
+#endif
                if (ret > 0) {
                        n += ret;
                } else if (ret == 0 && errno == 0) {
@@ -666,19 +810,29 @@ static inline void fcgi_close(fcgi_request *req, int force, int destroy)
        }
 
 #ifdef _WIN32
-       if (is_impersonate) {
+       if (is_impersonate && !req->tcp) {
                RevertToSelf();
        }
 #endif
 
        if ((force || !req->keep) && req->fd >= 0) {
 #ifdef _WIN32
-               HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
+               if (!req->tcp) {
+                       HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
 
-               if (!force) {
-                       FlushFileBuffers(pipe);
+                       if (!force) {
+                               FlushFileBuffers(pipe);
+                       }
+                       DisconnectNamedPipe(pipe);
+               } else {
+                       if (!force) {
+                               char buf[8];
+
+                               shutdown(req->fd, 1);
+                               while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
+                       }
+                       closesocket(req->fd);
                }
-               DisconnectNamedPipe(pipe);
 #else
                if (!force) {
                        char buf[8];
@@ -707,33 +861,37 @@ int fcgi_accept_request(fcgi_request *req)
                                        return -1;
                                }
 #ifdef _WIN32
-                               pipe = (HANDLE)_get_osfhandle(req->listen_socket);
-
-                               FCGI_LOCK(req->listen_socket);
-                               ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-                               if (!ConnectNamedPipe(pipe, &ov)) {
-                                       errno = GetLastError();
-                                       if (errno == ERROR_IO_PENDING) {
-                                               while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
-                                                       if (in_shutdown) {
-                                                               CloseHandle(ov.hEvent);
-                                                               FCGI_UNLOCK(req->listen_socket);
-                                                               return -1;
+                               if (!req->tcp) {
+                                       pipe = (HANDLE)_get_osfhandle(req->listen_socket);
+                                       FCGI_LOCK(req->listen_socket);
+                                       ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+                                       if (!ConnectNamedPipe(pipe, &ov)) {
+                                               errno = GetLastError();
+                                               if (errno == ERROR_IO_PENDING) {
+                                                       while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
+                                                               if (in_shutdown) {
+                                                                       CloseHandle(ov.hEvent);
+                                                                       FCGI_UNLOCK(req->listen_socket);
+                                                                       return -1;
+                                                               }
                                                        }
+                                               } else if (errno != ERROR_PIPE_CONNECTED) {
                                                }
-                                       } else if (errno != ERROR_PIPE_CONNECTED) {
                                        }
-                               }
-                               CloseHandle(ov.hEvent);
-                               req->fd = req->listen_socket;
-                               FCGI_UNLOCK(req->listen_socket);
+                                       CloseHandle(ov.hEvent);
+                                       req->fd = req->listen_socket;
+                                       FCGI_UNLOCK(req->listen_socket);
+                               } else {
+                                       SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
 #else
                                {
+                                       int listen_socket = req->listen_socket;
+#endif
                                        sa_t sa;
                                        socklen_t len = sizeof(sa);
 
                                        FCGI_LOCK(req->listen_socket);
-                                       req->fd = accept(req->listen_socket, (struct sockaddr *)&sa, &len);
+                                       req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
                                        FCGI_UNLOCK(req->listen_socket);
                                        if (req->fd >= 0 && allowed_clients) {
                                                int n = 0;
@@ -748,13 +906,12 @@ int fcgi_accept_request(fcgi_request *req)
                                        }
                                                if (!allowed) {
                                                        fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr));
-                                                       close(req->fd);
+                                                       closesocket(req->fd);
                                                        req->fd = -1;
                                                        continue;
                                                }
                                        }
                                }
-#endif
 
                                if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
                                        return -1;
@@ -808,7 +965,7 @@ int fcgi_accept_request(fcgi_request *req)
                }
                if (fcgi_read_request(req)) {
 #ifdef _WIN32
-                       if (is_impersonate) {
+                       if (is_impersonate && !req->tcp) {
                                pipe = (HANDLE)_get_osfhandle(req->fd);
                                if (!ImpersonateNamedPipeClient(pipe)) {
                                        fcgi_close(req, 1, 1);
index 940a9be55b95720a6fc8cbdbe0c83fc145452f5d..a2fede78c24bfafa34c9ed699371ae30d9d3c939 100644 (file)
@@ -93,6 +93,9 @@ typedef struct _fcgi_end_request_rec {
 
 typedef struct _fcgi_request {
        int            listen_socket;
+#ifdef _WIN32
+       int            tcp;
+#endif
        int            fd;
        int            id;
        int            keep;