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"},
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"
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)
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
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. */
bindpath = strdup(php_optarg);
}
break;
-#endif
}
}
}
}
-#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
}
fastcgi = fcgi_is_fastcgi();
}
-#endif
if (fastcgi) {
/* How many times to run PHP scripts before dying */
if (getenv("PHP_FCGI_MAX_REQUESTS")) {
requests++;
if (max_requests && (requests == max_requests)) {
fcgi_finish_request(&request);
-#ifndef PHP_WIN32
if (bindpath) {
free(bindpath);
}
-#endif
break;
}
/* end of fastcgi loop */
#include <windows.h>
typedef unsigned int size_t;
+ typedef unsigned int in_addr_t;
struct sockaddr_un {
short sun_family;
# 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
static int is_initialized = 0;
static int is_fastcgi = 0;
static int in_shutdown = 0;
+static in_addr_t *allowed_clients = NULL;
#ifdef _WIN32
#else
-static in_addr_t *allowed_clients = NULL;
-
static void fcgi_signal_handler(int signo)
{
if (signo == SIGUSR1 || signo == SIGTERM) {
}
}
+#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];
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 */
}
}
} 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)) {
sa.sa_unix.sun_len = sock_len;
#endif
unlink(path);
+#endif
}
/* Create, bind socket and start listen on it */
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)
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)
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) {
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) {
}
#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];
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;
}
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;
}
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);