diff options
author | Dmitry Stogov <dmitry@php.net> | 2007-03-28 15:39:22 +0000 |
---|---|---|
committer | Dmitry Stogov <dmitry@php.net> | 2007-03-28 15:39:22 +0000 |
commit | 4a119f9a79b5db25ba41da8da8e4292c46aafa1f (patch) | |
tree | acb47a56b9d402f26d337b2aaaf72ad369e7d514 | |
parent | 1c36d38891253b556998db18cca6ac01781c9ba0 (diff) | |
download | php-git-4a119f9a79b5db25ba41da8da8e4292c46aafa1f.tar.gz |
Improved FastCGI SAPI to support external pipe and socket servers on win32
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | sapi/cgi/cgi_main.c | 47 | ||||
-rw-r--r-- | sapi/cgi/fastcgi.c | 223 | ||||
-rw-r--r-- | sapi/cgi/fastcgi.h | 3 |
4 files changed, 199 insertions, 77 deletions
@@ -9,6 +9,8 @@ PHP NEWS - Upgraded SQLite 3 to version 3.3.13 (Ilia) - Upgraded PCRE to version 7.0 (Nuno) - Updated timezone database to version 2007.3. (Derick) +- Improved FastCGI SAPI to support external pipe and socket servers on win32. + (Dmitry) - Improved Zend Memory Manager . guarantee of reasonable time for worst cases of best-fit free block searching algorithm. (Dmitry) @@ -58,6 +60,7 @@ PHP NEWS - Fixed bug #40770 (Apache child exits when PHP memory limit reached). (Dmitry) - Fixed bug #40764 (line thickness not respected for horizontal and vertical lines). (Pierre) +- Fixed bug #40758 (Test fcgi_is_fastcgi() is wrong on windows). (Dmitry) - Fixed bug #40754 (added substr() & substr_replace() overflow checks). (Ilia) - Fixed bug #40752 (parse_ini_file() segfaults when a scalar setting is redeclared as an array). (Tony) diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index 320b32d00a..3daa38d7f9 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -123,9 +123,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"}, @@ -630,7 +628,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 PHP_FASTCGI && !defined(PHP_WIN32) +#if PHP_FASTCGI " -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n" #endif " -C Do not chdir to the script's directory\n" @@ -997,21 +995,6 @@ void fastcgi_cleanup(int signal) } #endif -#if PHP_FASTCGI -#ifndef PHP_WIN32 -static int is_port_number(const char *bindpath) -{ - while (*bindpath) { - if (*bindpath < '0' || *bindpath > '9') { - return 0; - } - bindpath++; - } - return 1; -} -#endif -#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) @@ -1133,9 +1116,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 @@ -1233,8 +1214,6 @@ int main(int argc, char *argv[]) } break; } -#if PHP_FASTCGI -#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. */ @@ -1243,8 +1222,6 @@ int main(int argc, char *argv[]) bindpath = strdup(php_optarg); } break; -#endif -#endif } } @@ -1311,26 +1288,10 @@ consult the installation file that came with this distribution, or visit \n\ #endif /* FORCE_CGI_REDIRECT */ #if PHP_FASTCGI -#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 @@ -1340,7 +1301,7 @@ 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")) { @@ -1828,11 +1789,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 */ diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c index 6068887f56..ec63526a6a 100644 --- a/sapi/cgi/fastcgi.c +++ b/sapi/cgi/fastcgi.c @@ -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); diff --git a/sapi/cgi/fastcgi.h b/sapi/cgi/fastcgi.h index 940a9be55b..a2fede78c2 100644 --- a/sapi/cgi/fastcgi.h +++ b/sapi/cgi/fastcgi.h @@ -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; |