summaryrefslogtreecommitdiff
path: root/sapi/cgi/fastcgi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sapi/cgi/fastcgi.c')
-rw-r--r--sapi/cgi/fastcgi.c369
1 files changed, 312 insertions, 57 deletions
diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c
index 6f528109f..7fd1fa86b 100644
--- a/sapi/cgi/fastcgi.c
+++ b/sapi/cgi/fastcgi.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2006 The PHP Group |
+ | Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -16,7 +16,7 @@
+----------------------------------------------------------------------+
*/
-/* $Id: fastcgi.c,v 1.4.2.13.2.11 2006/10/16 10:46:59 dmitry Exp $ */
+/* $Id: fastcgi.c,v 1.4.2.13.2.24 2007/04/09 15:39:59 dmitry Exp $ */
#include "php.h"
#include "fastcgi.h"
@@ -31,7 +31,7 @@
#include <windows.h>
- typedef unsigned int size_t;
+ typedef unsigned int in_addr_t;
struct sockaddr_un {
short sun_family;
@@ -71,6 +71,15 @@
# 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
+# if defined(HAVE_SYS_SELECT_H)
+# include <sys/select.h>
+# endif
+
#ifndef INADDR_NONE
#define INADDR_NONE ((unsigned long) -1)
#endif
@@ -126,7 +135,7 @@ typedef union _sa_t {
typedef struct _fcgi_mgmt_rec {
char* name;
- size_t name_len;
+ char name_len;
char val;
} fcgi_mgmt_rec;
@@ -140,6 +149,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
@@ -162,6 +172,11 @@ static void fcgi_signal_handler(int signo)
#endif
+int fcgi_in_shutdown(void)
+{
+ return in_shutdown;
+}
+
int fcgi_init(void)
{
if (!is_initialized) {
@@ -240,12 +255,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];
@@ -261,6 +353,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 */
@@ -289,6 +387,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)) {
@@ -304,6 +429,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 */
@@ -317,14 +443,51 @@ int fcgi_listen(const char *path, int backlog)
if (!tcp) {
chmod(path, 0777);
+ } else {
+ char *ip = getenv("FCGI_WEB_SERVER_ADDRS");
+ char *cur, *end;
+ int n;
+
+ if (ip) {
+ ip = strdup(ip);
+ cur = ip;
+ n = 0;
+ while (*cur) {
+ if (*cur == ',') n++;
+ cur++;
+ }
+ allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
+ n = 0;
+ cur = ip;
+ while (cur) {
+ end = strchr(cur, ',');
+ if (end) {
+ *end = 0;
+ end++;
+ }
+ allowed_clients[n] = inet_addr(cur);
+ if (allowed_clients[n] == INADDR_NONE) {
+ fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur);
+ }
+ n++;
+ cur = end;
+ }
+ allowed_clients[n] = INADDR_NONE;
+ free(ip);
+ }
}
if (!is_initialized) {
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)
@@ -339,6 +502,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)
@@ -347,7 +514,19 @@ static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t coun
size_t n = 0;
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) {
@@ -363,7 +542,19 @@ static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count
size_t n = 0;
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) {
@@ -540,8 +731,8 @@ static int fcgi_read_request(fcgi_request *req)
for (j = 0; j < sizeof(fcgi_mgmt_vars)/sizeof(fcgi_mgmt_vars[0]); j++) {
if (zend_hash_exists(&req->env, fcgi_mgmt_vars[j].name, fcgi_mgmt_vars[j].name_len+1) == 0) {
- sprintf((char*)p, "%c%c%s%c", fcgi_mgmt_vars[j].name_len, 1, fcgi_mgmt_vars[j].name, fcgi_mgmt_vars[j].val);
- p += fcgi_mgmt_vars[j].name_len + 3;
+ sprintf((char*)p, "%c%c%s%c", fcgi_mgmt_vars[j].name_len, 1, fcgi_mgmt_vars[j].name, fcgi_mgmt_vars[j].val);
+ p += fcgi_mgmt_vars[j].name_len + 3;
}
}
len = p - buf - sizeof(fcgi_header);
@@ -616,16 +807,30 @@ static inline void fcgi_close(fcgi_request *req, int force, int destroy)
if (destroy) {
zend_hash_destroy(&req->env);
}
+
+#ifdef _WIN32
+ 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);
- }
- DisconnectNamedPipe(pipe);
- if (is_impersonate) {
- RevertToSelf();
+ 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);
}
#else
if (!force) {
@@ -655,41 +860,57 @@ 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);
- if (is_impersonate && !ImpersonateNamedPipeClient(pipe)) {
- DisconnectNamedPipe(pipe);
- req->fd = -1;
- } else {
+ CloseHandle(ov.hEvent);
req->fd = req->listen_socket;
- }
- FCGI_UNLOCK(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;
+ int allowed = 0;
+
+ while (allowed_clients[n] != INADDR_NONE) {
+ if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) {
+ allowed = 1;
+ break;
+ }
+ n++;
+ }
+ if (!allowed) {
+ fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr));
+ closesocket(req->fd);
+ req->fd = -1;
+ continue;
+ }
+ }
}
-#endif
if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
return -1;
@@ -699,18 +920,42 @@ int fcgi_accept_request(fcgi_request *req)
break;
#else
if (req->fd >= 0) {
- struct timeval tv = {5,0};
- fd_set set;
-
- FD_ZERO(&set);
- FD_SET(req->fd, &set);
-try_again:
- errno = 0;
- if (select(req->fd + 1, &set, NULL, NULL, &tv) >= 0 && FD_ISSET(req->fd, &set)) {
+#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+ struct pollfd fds;
+ int ret;
+
+ fds.fd = req->fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ do {
+ errno = 0;
+ ret = poll(&fds, 1, 5000);
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && (fds.revents & POLLIN)) {
break;
}
- if (errno == EINTR) goto try_again;
fcgi_close(req, 1, 0);
+#else
+ if (req->fd < FD_SETSIZE) {
+ struct timeval tv = {5,0};
+ fd_set set;
+ int ret;
+
+ FD_ZERO(&set);
+ FD_SET(req->fd, &set);
+ do {
+ errno = 0;
+ ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && FD_ISSET(req->fd, &set)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+ } else {
+ fprintf(stderr, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
+ fcgi_close(req, 1, 0);
+ }
+#endif
}
#endif
}
@@ -718,6 +963,15 @@ try_again:
return -1;
}
if (fcgi_read_request(req)) {
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->fd);
+ if (!ImpersonateNamedPipeClient(pipe)) {
+ fcgi_close(req, 1, 1);
+ continue;
+ }
+ }
+#endif
return req->fd;
} else {
fcgi_close(req, 1, 1);
@@ -908,14 +1162,15 @@ char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
{
if (var && req) {
- char **ret;
-
if (val == NULL) {
- val = "";
- }
- val = strdup(val);
- if (zend_hash_update(&req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
- return *ret;
+ zend_hash_del(&req->env, var, var_len+1);
+ } else {
+ char **ret;
+
+ val = strdup(val);
+ if (zend_hash_update(&req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
+ return *ret;
+ }
}
}
return NULL;