xref: /PHP-5.3/sapi/cgi/fastcgi.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #include "php.h"
22 #include "fastcgi.h"
23 
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <errno.h>
29 
30 #ifdef _WIN32
31 
32 #include <windows.h>
33 
34 	typedef unsigned int in_addr_t;
35 
36 	struct sockaddr_un {
37 		short   sun_family;
38 		char    sun_path[MAXPATHLEN];
39 	};
40 
41 	static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE;
42 	static int is_impersonate = 0;
43 
44 #define FCGI_LOCK(fd) \
45 	if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
46 		DWORD ret; \
47 		while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \
48 			if (in_shutdown) return -1; \
49 		} \
50 		if (ret == WAIT_FAILED) { \
51 			fprintf(stderr, "WaitForSingleObject() failed\n"); \
52 			return -1; \
53 		} \
54 	}
55 
56 #define FCGI_UNLOCK(fd) \
57 	if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
58 		ReleaseMutex(fcgi_accept_mutex); \
59 	}
60 
61 #else
62 
63 # include <sys/types.h>
64 # include <sys/stat.h>
65 # include <unistd.h>
66 # include <fcntl.h>
67 # include <sys/socket.h>
68 # include <sys/un.h>
69 # include <netinet/in.h>
70 # include <arpa/inet.h>
71 # include <netdb.h>
72 # include <signal.h>
73 
74 # define closesocket(s) close(s)
75 
76 # if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
77 #  include <sys/poll.h>
78 # endif
79 # if defined(HAVE_SYS_SELECT_H)
80 #  include <sys/select.h>
81 # endif
82 
83 #ifndef INADDR_NONE
84 #define INADDR_NONE ((unsigned long) -1)
85 #endif
86 
87 # ifndef HAVE_SOCKLEN_T
88 	typedef unsigned int socklen_t;
89 # endif
90 
91 # ifdef USE_LOCKING
92 #  define FCGI_LOCK(fd)								\
93 	do {											\
94 		struct flock lock;							\
95 		lock.l_type = F_WRLCK;						\
96 		lock.l_start = 0;							\
97 		lock.l_whence = SEEK_SET;					\
98 		lock.l_len = 0;								\
99 		if (fcntl(fd, F_SETLKW, &lock) != -1) {		\
100 			break;									\
101 		} else if (errno != EINTR || in_shutdown) {	\
102 			return -1;								\
103 		}											\
104 	} while (1)
105 
106 #  define FCGI_UNLOCK(fd)							\
107 	do {											\
108 		int orig_errno = errno;						\
109 		while (1) {									\
110 			struct flock lock;						\
111 			lock.l_type = F_UNLCK;					\
112 			lock.l_start = 0;						\
113 			lock.l_whence = SEEK_SET;				\
114 			lock.l_len = 0;							\
115 			if (fcntl(fd, F_SETLK, &lock) != -1) {	\
116 				break;								\
117 			} else if (errno != EINTR) {			\
118 				return -1;							\
119 			}										\
120 		}											\
121 		errno = orig_errno;							\
122 	} while (0)
123 # else
124 #  define FCGI_LOCK(fd)
125 #  define FCGI_UNLOCK(fd)
126 # endif
127 
128 #endif
129 
130 typedef union _sa_t {
131 	struct sockaddr     sa;
132 	struct sockaddr_un  sa_unix;
133 	struct sockaddr_in  sa_inet;
134 } sa_t;
135 
136 static HashTable fcgi_mgmt_vars;
137 
138 static int is_initialized = 0;
139 static int is_fastcgi = 0;
140 static int in_shutdown = 0;
141 static in_addr_t *allowed_clients = NULL;
142 
143 #ifdef _WIN32
144 
fcgi_shutdown_thread(LPVOID arg)145 static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg)
146 {
147 	HANDLE shutdown_event = (HANDLE) arg;
148 	WaitForSingleObject(shutdown_event, INFINITE);
149 	in_shutdown = 1;
150 	return 0;
151 }
152 
153 #else
154 
fcgi_signal_handler(int signo)155 static void fcgi_signal_handler(int signo)
156 {
157 	if (signo == SIGUSR1 || signo == SIGTERM) {
158 		in_shutdown = 1;
159 	}
160 }
161 
fcgi_setup_signals(void)162 static void fcgi_setup_signals(void)
163 {
164 	struct sigaction new_sa, old_sa;
165 
166 	sigemptyset(&new_sa.sa_mask);
167 	new_sa.sa_flags = 0;
168 	new_sa.sa_handler = fcgi_signal_handler;
169 	sigaction(SIGUSR1, &new_sa, NULL);
170 	sigaction(SIGTERM, &new_sa, NULL);
171 	sigaction(SIGPIPE, NULL, &old_sa);
172 	if (old_sa.sa_handler == SIG_DFL) {
173 		sigaction(SIGPIPE, &new_sa, NULL);
174 	}
175 }
176 #endif
177 
fcgi_in_shutdown(void)178 int fcgi_in_shutdown(void)
179 {
180 	return in_shutdown;
181 }
182 
fcgi_init(void)183 int fcgi_init(void)
184 {
185 	if (!is_initialized) {
186 #ifndef _WIN32
187 		sa_t sa;
188 		socklen_t len = sizeof(sa);
189 #endif
190 		zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1);
191 		fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS")-1, "0", sizeof("0")-1);
192 
193 		is_initialized = 1;
194 #ifdef _WIN32
195 # if 0
196 		/* TODO: Support for TCP sockets */
197 		WSADATA wsaData;
198 
199 		if (WSAStartup(MAKEWORD(2,0), &wsaData)) {
200 			fprintf(stderr, "Error starting Windows Sockets.  Error: %d", WSAGetLastError());
201 			return 0;
202 		}
203 # endif
204 		if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
205 		    (GetStdHandle(STD_ERROR_HANDLE)  == INVALID_HANDLE_VALUE) &&
206 		    (GetStdHandle(STD_INPUT_HANDLE)  != INVALID_HANDLE_VALUE)) {
207 			char *str;
208 			DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;
209 			HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE);
210 
211 			SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL);
212 
213 			str = getenv("_FCGI_SHUTDOWN_EVENT_");
214 			if (str != NULL) {
215 				HANDLE shutdown_event = (HANDLE) atoi(str);
216 				if (!CreateThread(NULL, 0, fcgi_shutdown_thread,
217 				                  shutdown_event, 0, NULL)) {
218 					return -1;
219 				}
220 			}
221 			str = getenv("_FCGI_MUTEX_");
222 			if (str != NULL) {
223 				fcgi_accept_mutex = (HANDLE) atoi(str);
224 			}
225 			return is_fastcgi = 1;
226 		} else {
227 			return is_fastcgi = 0;
228 		}
229 #else
230 		errno = 0;
231 		if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) {
232 			fcgi_setup_signals();
233 			return is_fastcgi = 1;
234 		} else {
235 			return is_fastcgi = 0;
236 		}
237 #endif
238 	}
239 	return is_fastcgi;
240 }
241 
242 
fcgi_is_fastcgi(void)243 int fcgi_is_fastcgi(void)
244 {
245 	if (!is_initialized) {
246 		return fcgi_init();
247 	} else {
248 		return is_fastcgi;
249 	}
250 }
251 
fcgi_shutdown(void)252 void fcgi_shutdown(void)
253 {
254 	if (is_initialized) {
255 		zend_hash_destroy(&fcgi_mgmt_vars);
256 	}
257 	is_fastcgi = 0;
258 	if (allowed_clients) {
259 		free(allowed_clients);
260 	}
261 }
262 
263 #ifdef _WIN32
264 /* Do some black magic with the NT security API.
265  * We prepare a DACL (Discretionary Access Control List) so that
266  * we, the creator, are allowed all access, while "Everyone Else"
267  * is only allowed to read and write to the pipe.
268  * This avoids security issues on shared hosts where a luser messes
269  * with the lower-level pipe settings and screws up the FastCGI service.
270  */
prepare_named_pipe_acl(PSECURITY_DESCRIPTOR sd,LPSECURITY_ATTRIBUTES sa)271 static PACL prepare_named_pipe_acl(PSECURITY_DESCRIPTOR sd, LPSECURITY_ATTRIBUTES sa)
272 {
273 	DWORD req_acl_size;
274 	char everyone_buf[32], owner_buf[32];
275 	PSID sid_everyone, sid_owner;
276 	SID_IDENTIFIER_AUTHORITY
277 		siaWorld = SECURITY_WORLD_SID_AUTHORITY,
278 		siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
279 	PACL acl;
280 
281 	sid_everyone = (PSID)&everyone_buf;
282 	sid_owner = (PSID)&owner_buf;
283 
284 	req_acl_size = sizeof(ACL) +
285 		(2 * ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetSidLengthRequired(1)));
286 
287 	acl = malloc(req_acl_size);
288 
289 	if (acl == NULL) {
290 		return NULL;
291 	}
292 
293 	if (!InitializeSid(sid_everyone, &siaWorld, 1)) {
294 		goto out_fail;
295 	}
296 	*GetSidSubAuthority(sid_everyone, 0) = SECURITY_WORLD_RID;
297 
298 	if (!InitializeSid(sid_owner, &siaCreator, 1)) {
299 		goto out_fail;
300 	}
301 	*GetSidSubAuthority(sid_owner, 0) = SECURITY_CREATOR_OWNER_RID;
302 
303 	if (!InitializeAcl(acl, req_acl_size, ACL_REVISION)) {
304 		goto out_fail;
305 	}
306 
307 	if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_GENERIC_READ | FILE_GENERIC_WRITE, sid_everyone)) {
308 		goto out_fail;
309 	}
310 
311 	if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, sid_owner)) {
312 		goto out_fail;
313 	}
314 
315 	if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
316 		goto out_fail;
317 	}
318 
319 	if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) {
320 		goto out_fail;
321 	}
322 
323 	sa->lpSecurityDescriptor = sd;
324 
325 	return acl;
326 
327 out_fail:
328 	free(acl);
329 	return NULL;
330 }
331 #endif
332 
is_port_number(const char * bindpath)333 static int is_port_number(const char *bindpath)
334 {
335 	while (*bindpath) {
336 		if (*bindpath < '0' || *bindpath > '9') {
337 			return 0;
338 		}
339 		bindpath++;
340 	}
341 	return 1;
342 }
343 
fcgi_listen(const char * path,int backlog)344 int fcgi_listen(const char *path, int backlog)
345 {
346 	char     *s;
347 	int       tcp = 0;
348 	char      host[MAXPATHLEN];
349 	short     port = 0;
350 	int       listen_socket;
351 	sa_t      sa;
352 	socklen_t sock_len;
353 #ifdef SO_REUSEADDR
354 # ifdef _WIN32
355 	BOOL reuse = 1;
356 # else
357 	int reuse = 1;
358 # endif
359 #endif
360 
361 	if ((s = strchr(path, ':'))) {
362 		port = atoi(s+1);
363 		if (port != 0 && (s-path) < MAXPATHLEN) {
364 			strncpy(host, path, s-path);
365 			host[s-path] = '\0';
366 			tcp = 1;
367 		}
368 	} else if (is_port_number(path)) {
369 		port = atoi(path);
370 		if (port != 0) {
371 			host[0] = '\0';
372 			tcp = 1;
373 		}
374 	}
375 
376 	/* Prepare socket address */
377 	if (tcp) {
378 		memset(&sa.sa_inet, 0, sizeof(sa.sa_inet));
379 		sa.sa_inet.sin_family = AF_INET;
380 		sa.sa_inet.sin_port = htons(port);
381 		sock_len = sizeof(sa.sa_inet);
382 
383 		if (!*host || !strncmp(host, "*", sizeof("*")-1)) {
384 			sa.sa_inet.sin_addr.s_addr = htonl(INADDR_ANY);
385 		} else {
386 			sa.sa_inet.sin_addr.s_addr = inet_addr(host);
387 			if (sa.sa_inet.sin_addr.s_addr == INADDR_NONE) {
388 				struct hostent *hep;
389 
390 				hep = gethostbyname(host);
391 				if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) {
392 					fprintf(stderr, "Cannot resolve host name '%s'!\n", host);
393 					return -1;
394 				} else if (hep->h_addr_list[1]) {
395 					fprintf(stderr, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host);
396 					return -1;
397 				}
398 				sa.sa_inet.sin_addr.s_addr = ((struct in_addr*)hep->h_addr_list[0])->s_addr;
399 			}
400 		}
401 	} else {
402 #ifdef _WIN32
403 		SECURITY_DESCRIPTOR  sd;
404 		SECURITY_ATTRIBUTES  saw;
405 		PACL                 acl;
406 		HANDLE namedPipe;
407 
408 		memset(&sa, 0, sizeof(saw));
409 		saw.nLength = sizeof(saw);
410 		saw.bInheritHandle = FALSE;
411 		acl = prepare_named_pipe_acl(&sd, &saw);
412 
413 		namedPipe = CreateNamedPipe(path,
414 			PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
415 			PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
416 			PIPE_UNLIMITED_INSTANCES,
417 			8192, 8192, 0, &saw);
418 		if (namedPipe == INVALID_HANDLE_VALUE) {
419 			return -1;
420 		}
421 		listen_socket = _open_osfhandle((long)namedPipe, 0);
422 		if (!is_initialized) {
423 			fcgi_init();
424 		}
425 		is_fastcgi = 1;
426 		return listen_socket;
427 
428 #else
429 		int path_len = strlen(path);
430 
431 		if (path_len >= sizeof(sa.sa_unix.sun_path)) {
432 			fprintf(stderr, "Listening socket's path name is too long.\n");
433 			return -1;
434 		}
435 
436 		memset(&sa.sa_unix, 0, sizeof(sa.sa_unix));
437 		sa.sa_unix.sun_family = AF_UNIX;
438 		memcpy(sa.sa_unix.sun_path, path, path_len + 1);
439 		sock_len = (size_t)(((struct sockaddr_un *)0)->sun_path)	+ path_len;
440 #ifdef HAVE_SOCKADDR_UN_SUN_LEN
441 		sa.sa_unix.sun_len = sock_len;
442 #endif
443 		unlink(path);
444 #endif
445 	}
446 
447 	/* Create, bind socket and start listen on it */
448 	if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||
449 #ifdef SO_REUSEADDR
450 	    setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0 ||
451 #endif
452 	    bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||
453 	    listen(listen_socket, backlog) < 0) {
454 
455 		fprintf(stderr, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno));
456 		return -1;
457 	}
458 
459 	if (!tcp) {
460 		chmod(path, 0777);
461 	} else {
462 			char *ip = getenv("FCGI_WEB_SERVER_ADDRS");
463 			char *cur, *end;
464 			int n;
465 
466 			if (ip) {
467 				ip = strdup(ip);
468 				cur = ip;
469 				n = 0;
470 				while (*cur) {
471 					if (*cur == ',') n++;
472 					cur++;
473 				}
474 				allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
475 				n = 0;
476 				cur = ip;
477 				while (cur) {
478 					end = strchr(cur, ',');
479 					if (end) {
480 						*end = 0;
481 						end++;
482 					}
483 					allowed_clients[n] = inet_addr(cur);
484 					if (allowed_clients[n] == INADDR_NONE) {
485 					fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur);
486 					}
487 					n++;
488 					cur = end;
489 				}
490 				allowed_clients[n] = INADDR_NONE;
491 			free(ip);
492 		}
493 	}
494 
495 	if (!is_initialized) {
496 		fcgi_init();
497 	}
498 	is_fastcgi = 1;
499 
500 #ifdef _WIN32
501 	if (tcp) {
502 		listen_socket = _open_osfhandle((long)listen_socket, 0);
503 	}
504 #else
505 	fcgi_setup_signals();
506 #endif
507 	return listen_socket;
508 }
509 
fcgi_init_request(fcgi_request * req,int listen_socket)510 void fcgi_init_request(fcgi_request *req, int listen_socket)
511 {
512 	memset(req, 0, sizeof(fcgi_request));
513 	req->listen_socket = listen_socket;
514 	req->fd = -1;
515 	req->id = -1;
516 
517 	req->in_len = 0;
518 	req->in_pad = 0;
519 
520 	req->out_hdr = NULL;
521 	req->out_pos = req->out_buf;
522 
523 #ifdef _WIN32
524 	req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
525 #endif
526 }
527 
safe_write(fcgi_request * req,const void * buf,size_t count)528 static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
529 {
530 	int    ret;
531 	size_t n = 0;
532 
533 	do {
534 		errno = 0;
535 #ifdef _WIN32
536 		if (!req->tcp) {
537 			ret = write(req->fd, ((char*)buf)+n, count-n);
538 		} else {
539 			ret = send(req->fd, ((char*)buf)+n, count-n, 0);
540 			if (ret <= 0) {
541 				errno = WSAGetLastError();
542 			}
543 		}
544 #else
545 		ret = write(req->fd, ((char*)buf)+n, count-n);
546 #endif
547 		if (ret > 0) {
548 			n += ret;
549 		} else if (ret <= 0 && errno != 0 && errno != EINTR) {
550 			return ret;
551 		}
552 	} while (n != count);
553 	return n;
554 }
555 
safe_read(fcgi_request * req,const void * buf,size_t count)556 static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count)
557 {
558 	int    ret;
559 	size_t n = 0;
560 
561 	do {
562 		errno = 0;
563 #ifdef _WIN32
564 		if (!req->tcp) {
565 			ret = read(req->fd, ((char*)buf)+n, count-n);
566 		} else {
567 			ret = recv(req->fd, ((char*)buf)+n, count-n, 0);
568 			if (ret <= 0) {
569 				errno = WSAGetLastError();
570 			}
571 		}
572 #else
573 		ret = read(req->fd, ((char*)buf)+n, count-n);
574 #endif
575 		if (ret > 0) {
576 			n += ret;
577 		} else if (ret == 0 && errno == 0) {
578 			return n;
579 		} else if (ret <= 0 && errno != 0 && errno != EINTR) {
580 			return ret;
581 		}
582 	} while (n != count);
583 	return n;
584 }
585 
fcgi_make_header(fcgi_header * hdr,fcgi_request_type type,int req_id,int len)586 static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len)
587 {
588 	int pad = ((len + 7) & ~7) - len;
589 
590 	hdr->contentLengthB0 = (unsigned char)(len & 0xff);
591 	hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff);
592 	hdr->paddingLength = (unsigned char)pad;
593 	hdr->requestIdB0 = (unsigned char)(req_id & 0xff);
594 	hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff);
595 	hdr->reserved = 0;
596 	hdr->type = type;
597 	hdr->version = FCGI_VERSION_1;
598 	if (pad) {
599 		memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad);
600 	}
601 	return pad;
602 }
603 
fcgi_get_params(fcgi_request * req,unsigned char * p,unsigned char * end)604 static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end)
605 {
606 	char buf[128];
607 	char *tmp = buf;
608 	size_t buf_size = sizeof(buf);
609 	unsigned int name_len, val_len;
610 	char *s;
611 	int ret = 1;
612 
613 	while (p < end) {
614 		name_len = *p++;
615 		if (name_len >= 128) {
616 			if (p + 3 >= end) {
617 				ret = 0;
618 				break;
619 			}
620 			name_len = ((name_len & 0x7f) << 24);
621 			name_len |= (*p++ << 16);
622 			name_len |= (*p++ << 8);
623 			name_len |= *p++;
624 		}
625 		if (p >= end) {
626 			ret = 0;
627 			break;
628 		}
629 		val_len = *p++;
630 		if (val_len >= 128) {
631 			if (p + 3 >= end) {
632 				ret = 0;
633 				break;
634 			}
635 			val_len = ((val_len & 0x7f) << 24);
636 			val_len |= (*p++ << 16);
637 			val_len |= (*p++ << 8);
638 			val_len |= *p++;
639 		}
640 		if (name_len + val_len > end - p) {
641 			/* Malformated request */
642 			ret = 0;
643 			break;
644 		}
645 		if (name_len+1 >= buf_size) {
646 			buf_size = name_len + 64;
647 			tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size));
648 		}
649 		memcpy(tmp, p, name_len);
650 		tmp[name_len] = 0;
651 		s = estrndup((char*)p + name_len, val_len);
652 		zend_hash_update(req->env, tmp, name_len+1, &s, sizeof(char*), NULL);
653 		p += name_len + val_len;
654 	}
655 	if (tmp != buf && tmp != NULL) {
656 		efree(tmp);
657 	}
658 	return ret;
659 }
660 
fcgi_free_var(char ** s)661 static void fcgi_free_var(char **s)
662 {
663 	efree(*s);
664 }
665 
fcgi_read_request(fcgi_request * req)666 static int fcgi_read_request(fcgi_request *req)
667 {
668 	fcgi_header hdr;
669 	int len, padding;
670 	unsigned char buf[FCGI_MAX_LENGTH+8];
671 
672 	req->keep = 0;
673 	req->closed = 0;
674 	req->in_len = 0;
675 	req->out_hdr = NULL;
676 	req->out_pos = req->out_buf;
677 	ALLOC_HASHTABLE(req->env);
678 	zend_hash_init(req->env, 0, NULL, (void (*)(void *)) fcgi_free_var, 0);
679 
680 	if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
681 	    hdr.version < FCGI_VERSION_1) {
682 		return 0;
683 	}
684 
685 	len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
686 	padding = hdr.paddingLength;
687 
688 	while (hdr.type == FCGI_STDIN && len == 0) {
689 		if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
690 		    hdr.version < FCGI_VERSION_1) {
691 			return 0;
692 		}
693 
694 		len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
695 		padding = hdr.paddingLength;
696 	}
697 
698 	if (len + padding > FCGI_MAX_LENGTH) {
699 		return 0;
700 	}
701 
702 	req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0;
703 
704 	if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) {
705 		char *val;
706 
707 		if (safe_read(req, buf, len+padding) != len+padding) {
708 			return 0;
709 		}
710 
711 		req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN);
712 		switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) {
713 			case FCGI_RESPONDER:
714 				val = estrdup("RESPONDER");
715 				zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
716 				break;
717 			case FCGI_AUTHORIZER:
718 				val = estrdup("AUTHORIZER");
719 				zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
720 				break;
721 			case FCGI_FILTER:
722 				val = estrdup("FILTER");
723 				zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
724 				break;
725 			default:
726 				return 0;
727 		}
728 
729 		if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
730 		    hdr.version < FCGI_VERSION_1) {
731 			return 0;
732 		}
733 
734 		len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
735 		padding = hdr.paddingLength;
736 
737 		while (hdr.type == FCGI_PARAMS && len > 0) {
738 			if (len + padding > FCGI_MAX_LENGTH) {
739 				return 0;
740 			}
741 
742 			if (safe_read(req, buf, len+padding) != len+padding) {
743 				req->keep = 0;
744 				return 0;
745 			}
746 
747 			if (!fcgi_get_params(req, buf, buf+len)) {
748 				req->keep = 0;
749 				return 0;
750 			}
751 
752 			if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
753 			    hdr.version < FCGI_VERSION_1) {
754 				req->keep = 0;
755 				return 0;
756 			}
757 			len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
758 			padding = hdr.paddingLength;
759 		}
760 	} else if (hdr.type == FCGI_GET_VALUES) {
761 		unsigned char *p = buf + sizeof(fcgi_header);
762 		HashPosition pos;
763 		char * str_index;
764 		uint str_length;
765 		ulong num_index;
766 		int key_type;
767 		zval ** value;
768 
769 		if (safe_read(req, buf, len+padding) != len+padding) {
770 			req->keep = 0;
771 			return 0;
772 		}
773 
774 		if (!fcgi_get_params(req, buf, buf+len)) {
775 			req->keep = 0;
776 			return 0;
777 		}
778 
779 		zend_hash_internal_pointer_reset_ex(req->env, &pos);
780 		while ((key_type = zend_hash_get_current_key_ex(req->env, &str_index, &str_length, &num_index, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
781 			int zlen;
782 			zend_hash_move_forward_ex(req->env, &pos);
783 			if (key_type != HASH_KEY_IS_STRING) {
784 				continue;
785 			}
786 			if (zend_hash_find(&fcgi_mgmt_vars, str_index, str_length, (void**) &value) != SUCCESS) {
787 				continue;
788 			}
789 			--str_length;
790 			zlen = Z_STRLEN_PP(value);
791 			if ((p + 4 + 4 + str_length + zlen) >= (buf + sizeof(buf))) {
792 				break;
793 			}
794 			if (str_length < 0x80) {
795 				*p++ = str_length;
796 			} else {
797 				*p++ = ((str_length >> 24) & 0xff) | 0x80;
798 				*p++ = (str_length >> 16) & 0xff;
799 				*p++ = (str_length >> 8) & 0xff;
800 				*p++ = str_length & 0xff;
801 			}
802 			if (zlen < 0x80) {
803 				*p++ = zlen;
804 			} else {
805 				*p++ = ((zlen >> 24) & 0xff) | 0x80;
806 				*p++ = (zlen >> 16) & 0xff;
807 				*p++ = (zlen >> 8) & 0xff;
808 				*p++ = zlen & 0xff;
809 			}
810 			memcpy(p, str_index, str_length);
811 			p += str_length;
812 			memcpy(p, Z_STRVAL_PP(value), zlen);
813 			p += zlen;
814 		}
815 		len = p - buf - sizeof(fcgi_header);
816 		len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len);
817 		if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) {
818 			req->keep = 0;
819 			return 0;
820 		}
821 		return 0;
822 	} else {
823 		return 0;
824 	}
825 
826 	return 1;
827 }
828 
fcgi_read(fcgi_request * req,char * str,int len)829 int fcgi_read(fcgi_request *req, char *str, int len)
830 {
831 	int ret, n, rest;
832 	fcgi_header hdr;
833 	unsigned char buf[255];
834 
835 	n = 0;
836 	rest = len;
837 	while (rest > 0) {
838 		if (req->in_len == 0) {
839 			if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
840 			    hdr.version < FCGI_VERSION_1 ||
841 			    hdr.type != FCGI_STDIN) {
842 				req->keep = 0;
843 				return 0;
844 			}
845 			req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
846 			req->in_pad = hdr.paddingLength;
847 			if (req->in_len == 0) {
848 				return n;
849 			}
850 		}
851 
852 		if (req->in_len >= rest) {
853 			ret = safe_read(req, str, rest);
854 		} else {
855 			ret = safe_read(req, str, req->in_len);
856 		}
857 		if (ret < 0) {
858 			req->keep = 0;
859 			return ret;
860 		} else if (ret > 0) {
861 			req->in_len -= ret;
862 			rest -= ret;
863 			n += ret;
864 			str += ret;
865 			if (req->in_len == 0) {
866 				if (req->in_pad) {
867 					if (safe_read(req, buf, req->in_pad) != req->in_pad) {
868 						req->keep = 0;
869 						return ret;
870 					}
871 				}
872 			} else {
873 				return n;
874 			}
875 		} else {
876 			return n;
877 		}
878 	}
879 	return n;
880 }
881 
fcgi_close(fcgi_request * req,int force,int destroy)882 static inline void fcgi_close(fcgi_request *req, int force, int destroy)
883 {
884 	if (destroy && req->env) {
885 		zend_hash_destroy(req->env);
886 		FREE_HASHTABLE(req->env);
887 		req->env = NULL;
888 	}
889 
890 #ifdef _WIN32
891 	if (is_impersonate && !req->tcp) {
892 		RevertToSelf();
893 	}
894 #endif
895 
896 	if ((force || !req->keep) && req->fd >= 0) {
897 #ifdef _WIN32
898 		if (!req->tcp) {
899 			HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
900 
901 			if (!force) {
902 				FlushFileBuffers(pipe);
903 			}
904 			DisconnectNamedPipe(pipe);
905 		} else {
906 			if (!force) {
907 				char buf[8];
908 
909 				shutdown(req->fd, 1);
910 				while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
911 			}
912 			closesocket(req->fd);
913 		}
914 #else
915 		if (!force) {
916 			char buf[8];
917 
918 			shutdown(req->fd, 1);
919 			while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
920 		}
921 		close(req->fd);
922 #endif
923 		req->fd = -1;
924 	}
925 }
926 
fcgi_accept_request(fcgi_request * req)927 int fcgi_accept_request(fcgi_request *req)
928 {
929 #ifdef _WIN32
930 	HANDLE pipe;
931 	OVERLAPPED ov;
932 #endif
933 
934 	while (1) {
935 		if (req->fd < 0) {
936 			while (1) {
937 				if (in_shutdown) {
938 					return -1;
939 				}
940 #ifdef _WIN32
941 				if (!req->tcp) {
942 					pipe = (HANDLE)_get_osfhandle(req->listen_socket);
943 					FCGI_LOCK(req->listen_socket);
944 					ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
945 					if (!ConnectNamedPipe(pipe, &ov)) {
946 						errno = GetLastError();
947 						if (errno == ERROR_IO_PENDING) {
948 							while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
949 								if (in_shutdown) {
950 									CloseHandle(ov.hEvent);
951 									FCGI_UNLOCK(req->listen_socket);
952 									return -1;
953 								}
954 							}
955 						} else if (errno != ERROR_PIPE_CONNECTED) {
956 						}
957 					}
958 					CloseHandle(ov.hEvent);
959 					req->fd = req->listen_socket;
960 					FCGI_UNLOCK(req->listen_socket);
961 				} else {
962 					SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
963 #else
964 				{
965 					int listen_socket = req->listen_socket;
966 #endif
967 					sa_t sa;
968 					socklen_t len = sizeof(sa);
969 
970 					FCGI_LOCK(req->listen_socket);
971 					req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
972 					FCGI_UNLOCK(req->listen_socket);
973 					if (req->fd >= 0 && allowed_clients) {
974 						int n = 0;
975 						int allowed = 0;
976 
977 							while (allowed_clients[n] != INADDR_NONE) {
978 								if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) {
979 									allowed = 1;
980 									break;
981 								}
982 								n++;
983 							}
984 						if (!allowed) {
985 							fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr));
986 							closesocket(req->fd);
987 							req->fd = -1;
988 							continue;
989 						}
990 					}
991 				}
992 
993 #ifdef _WIN32
994 				if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
995 #else
996 				if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) {
997 #endif
998 					return -1;
999 				}
1000 
1001 #ifdef _WIN32
1002 				break;
1003 #else
1004 				if (req->fd >= 0) {
1005 #if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
1006 					struct pollfd fds;
1007 					int ret;
1008 
1009 					fds.fd = req->fd;
1010 					fds.events = POLLIN;
1011 					fds.revents = 0;
1012 					do {
1013 						errno = 0;
1014 						ret = poll(&fds, 1, 5000);
1015 					} while (ret < 0 && errno == EINTR);
1016 					if (ret > 0 && (fds.revents & POLLIN)) {
1017 						break;
1018 					}
1019 					fcgi_close(req, 1, 0);
1020 #else
1021 					if (req->fd < FD_SETSIZE) {
1022 						struct timeval tv = {5,0};
1023 						fd_set set;
1024 						int ret;
1025 
1026 						FD_ZERO(&set);
1027 						FD_SET(req->fd, &set);
1028 						do {
1029 							errno = 0;
1030 							ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
1031 						} while (ret < 0 && errno == EINTR);
1032 						if (ret > 0 && FD_ISSET(req->fd, &set)) {
1033 							break;
1034 						}
1035 						fcgi_close(req, 1, 0);
1036 					} else {
1037 						fprintf(stderr, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
1038 						fcgi_close(req, 1, 0);
1039 					}
1040 #endif
1041 				}
1042 #endif
1043 			}
1044 		} else if (in_shutdown) {
1045 			return -1;
1046 		}
1047 		if (fcgi_read_request(req)) {
1048 #ifdef _WIN32
1049 			if (is_impersonate && !req->tcp) {
1050 				pipe = (HANDLE)_get_osfhandle(req->fd);
1051 				if (!ImpersonateNamedPipeClient(pipe)) {
1052 					fcgi_close(req, 1, 1);
1053 					continue;
1054 				}
1055 			}
1056 #endif
1057 			return req->fd;
1058 		} else {
1059 			fcgi_close(req, 1, 1);
1060 		}
1061 	}
1062 }
1063 
1064 static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type)
1065 {
1066 	req->out_hdr = (fcgi_header*) req->out_pos;
1067 	req->out_hdr->type = type;
1068 	req->out_pos += sizeof(fcgi_header);
1069 	return req->out_hdr;
1070 }
1071 
1072 static inline void close_packet(fcgi_request *req)
1073 {
1074 	if (req->out_hdr) {
1075 		int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header));
1076 
1077 		req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len);
1078 		req->out_hdr = NULL;
1079 	}
1080 }
1081 
1082 int fcgi_flush(fcgi_request *req, int close)
1083 {
1084 	int len;
1085 
1086 	close_packet(req);
1087 
1088 	len = req->out_pos - req->out_buf;
1089 
1090 	if (close) {
1091 		fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos);
1092 
1093 		fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request));
1094 		rec->body.appStatusB3 = 0;
1095 		rec->body.appStatusB2 = 0;
1096 		rec->body.appStatusB1 = 0;
1097 		rec->body.appStatusB0 = 0;
1098 		rec->body.protocolStatus = FCGI_REQUEST_COMPLETE;
1099 		len += sizeof(fcgi_end_request_rec);
1100 	}
1101 
1102 	if (safe_write(req, req->out_buf, len) != len) {
1103 		req->keep = 0;
1104 		return 0;
1105 	}
1106 
1107 	req->out_pos = req->out_buf;
1108 	return 1;
1109 }
1110 
1111 int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len)
1112 {
1113 	int limit, rest;
1114 
1115 	if (len <= 0) {
1116 		return 0;
1117 	}
1118 
1119 	if (req->out_hdr && req->out_hdr->type != type) {
1120 		close_packet(req);
1121 	}
1122 #if 0
1123 	/* Unoptimized, but clear version */
1124 	rest = len;
1125 	while (rest > 0) {
1126 		limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
1127 
1128 		if (!req->out_hdr) {
1129 			if (limit < sizeof(fcgi_header)) {
1130 				if (!fcgi_flush(req, 0)) {
1131 					return -1;
1132 				}
1133 			}
1134 			open_packet(req, type);
1135 		}
1136 		limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
1137 		if (rest < limit) {
1138 			memcpy(req->out_pos, str, rest);
1139 			req->out_pos += rest;
1140 			return len;
1141 		} else {
1142 			memcpy(req->out_pos, str, limit);
1143 			req->out_pos += limit;
1144 			rest -= limit;
1145 			str += limit;
1146 			if (!fcgi_flush(req, 0)) {
1147 				return -1;
1148 			}
1149 		}
1150 	}
1151 #else
1152 	/* Optimized version */
1153 	limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
1154 	if (!req->out_hdr) {
1155 		limit -= sizeof(fcgi_header);
1156 		if (limit < 0) limit = 0;
1157 	}
1158 
1159 	if (len < limit) {
1160 		if (!req->out_hdr) {
1161 			open_packet(req, type);
1162 		}
1163 		memcpy(req->out_pos, str, len);
1164 		req->out_pos += len;
1165 	} else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) {
1166 		if (!req->out_hdr) {
1167 			open_packet(req, type);
1168 		}
1169 		if (limit > 0) {
1170 			memcpy(req->out_pos, str, limit);
1171 			req->out_pos += limit;
1172 		}
1173 		if (!fcgi_flush(req, 0)) {
1174 			return -1;
1175 		}
1176 		if (len > limit) {
1177 			open_packet(req, type);
1178 			memcpy(req->out_pos, str + limit, len - limit);
1179 			req->out_pos += len - limit;
1180 		}
1181 	} else {
1182 		int pos = 0;
1183 		int pad;
1184 
1185 		close_packet(req);
1186 		while ((len - pos) > 0xffff) {
1187 			open_packet(req, type);
1188 			fcgi_make_header(req->out_hdr, type, req->id, 0xfff8);
1189 			req->out_hdr = NULL;
1190 			if (!fcgi_flush(req, 0)) {
1191 				return -1;
1192 			}
1193 			if (safe_write(req, str + pos, 0xfff8) != 0xfff8) {
1194 				req->keep = 0;
1195 				return -1;
1196 			}
1197 			pos += 0xfff8;
1198 		}
1199 
1200 		pad = (((len - pos) + 7) & ~7) - (len - pos);
1201 		rest = pad ? 8 - pad : 0;
1202 
1203 		open_packet(req, type);
1204 		fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest);
1205 		req->out_hdr = NULL;
1206 		if (!fcgi_flush(req, 0)) {
1207 			return -1;
1208 		}
1209 		if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) {
1210 			req->keep = 0;
1211 			return -1;
1212 		}
1213 		if (pad) {
1214 			open_packet(req, type);
1215 			memcpy(req->out_pos, str + len - rest,  rest);
1216 			req->out_pos += rest;
1217 		}
1218 	}
1219 #endif
1220 	return len;
1221 }
1222 
1223 int fcgi_finish_request(fcgi_request *req, int force_close)
1224 {
1225 	int ret = 1;
1226 
1227 	if (req->fd >= 0) {
1228 		if (!req->closed) {
1229 			ret = fcgi_flush(req, 1);
1230 			req->closed = 1;
1231 		}
1232 		fcgi_close(req, force_close, 1);
1233 	}
1234 	return ret;
1235 }
1236 
1237 char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
1238 {
1239 	char **val;
1240 
1241 	if (!req) return NULL;
1242 
1243 	if (zend_hash_find(req->env, (char*)var, var_len+1, (void**)&val) == SUCCESS) {
1244 		return *val;
1245 	}
1246 	return NULL;
1247 }
1248 
1249 char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
1250 {
1251 	if (var && req) {
1252 		if (val == NULL) {
1253 			zend_hash_del(req->env, var, var_len+1);
1254 		} else {
1255 			char **ret;
1256 
1257 			val = estrdup(val);
1258 			if (zend_hash_update(req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
1259 				return *ret;
1260 			}
1261 		}
1262 	}
1263 	return NULL;
1264 }
1265 
1266 #ifdef _WIN32
1267 void fcgi_impersonate(void)
1268 {
1269 	char *os_name;
1270 
1271 	os_name = getenv("OS");
1272 	if (os_name && stricmp(os_name, "Windows_NT") == 0) {
1273 		is_impersonate = 1;
1274 	}
1275 }
1276 #endif
1277 
1278 void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len)
1279 {
1280 	zval * zvalue;
1281 	zvalue = pemalloc(sizeof(*zvalue), 1);
1282 	Z_TYPE_P(zvalue) = IS_STRING;
1283 	Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1);
1284 	Z_STRLEN_P(zvalue) = value_len;
1285 	zend_hash_add(&fcgi_mgmt_vars, name, name_len + 1, &zvalue, sizeof(zvalue), NULL);
1286 }
1287 
1288 void fcgi_free_mgmt_var_cb(void * ptr)
1289 {
1290 	zval ** var = (zval **)ptr;
1291 	pefree(Z_STRVAL_PP(var), 1);
1292 	pefree(*var, 1);
1293 }
1294 
1295 /*
1296  * Local variables:
1297  * tab-width: 4
1298  * c-basic-offset: 4
1299  * End:
1300  * vim600: sw=4 ts=4 fdm=marker
1301  * vim<600: sw=4 ts=4
1302  */
1303