summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@php.net>2007-03-28 15:39:22 +0000
committerDmitry Stogov <dmitry@php.net>2007-03-28 15:39:22 +0000
commit4a119f9a79b5db25ba41da8da8e4292c46aafa1f (patch)
treeacb47a56b9d402f26d337b2aaaf72ad369e7d514
parent1c36d38891253b556998db18cca6ac01781c9ba0 (diff)
downloadphp-git-4a119f9a79b5db25ba41da8da8e4292c46aafa1f.tar.gz
Improved FastCGI SAPI to support external pipe and socket servers on win32
-rw-r--r--NEWS3
-rw-r--r--sapi/cgi/cgi_main.c47
-rw-r--r--sapi/cgi/fastcgi.c223
-rw-r--r--sapi/cgi/fastcgi.h3
4 files changed, 199 insertions, 77 deletions
diff --git a/NEWS b/NEWS
index a4448f4200..e2a9036efa 100644
--- a/NEWS
+++ b/NEWS
@@ -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;