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