xref: /PHP-5.4/sapi/cgi/fastcgi.c (revision b30a6d60)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2014 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 				continue;
1002 			}
1003 			zlen = Z_STRLEN_PP(value);
1004 			if ((p + 4 + 4 + q->var_len + zlen) >= (buf + sizeof(buf))) {
1005 				break;
1006 			}
1007 			if (q->var_len < 0x80) {
1008 				*p++ = q->var_len;
1009 			} else {
1010 				*p++ = ((q->var_len >> 24) & 0xff) | 0x80;
1011 				*p++ = (q->var_len >> 16) & 0xff;
1012 				*p++ = (q->var_len >> 8) & 0xff;
1013 				*p++ = q->var_len & 0xff;
1014 			}
1015 			if (zlen < 0x80) {
1016 				*p++ = zlen;
1017 			} else {
1018 				*p++ = ((zlen >> 24) & 0xff) | 0x80;
1019 				*p++ = (zlen >> 16) & 0xff;
1020 				*p++ = (zlen >> 8) & 0xff;
1021 				*p++ = zlen & 0xff;
1022 			}
1023 			memcpy(p, q->var, q->var_len);
1024 			p += q->var_len;
1025 			memcpy(p, Z_STRVAL_PP(value), zlen);
1026 			p += zlen;
1027 		}
1028 		len = p - buf - sizeof(fcgi_header);
1029 		len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len);
1030 		if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) {
1031 			req->keep = 0;
1032 			return 0;
1033 		}
1034 		return 0;
1035 	} else {
1036 		return 0;
1037 	}
1038 
1039 	return 1;
1040 }
1041 
fcgi_read(fcgi_request * req,char * str,int len)1042 int fcgi_read(fcgi_request *req, char *str, int len)
1043 {
1044 	int ret, n, rest;
1045 	fcgi_header hdr;
1046 	unsigned char buf[255];
1047 
1048 	n = 0;
1049 	rest = len;
1050 	while (rest > 0) {
1051 		if (req->in_len == 0) {
1052 			if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
1053 			    hdr.version < FCGI_VERSION_1 ||
1054 			    hdr.type != FCGI_STDIN) {
1055 				req->keep = 0;
1056 				return 0;
1057 			}
1058 			req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
1059 			req->in_pad = hdr.paddingLength;
1060 			if (req->in_len == 0) {
1061 				return n;
1062 			}
1063 		}
1064 
1065 		if (req->in_len >= rest) {
1066 			ret = safe_read(req, str, rest);
1067 		} else {
1068 			ret = safe_read(req, str, req->in_len);
1069 		}
1070 		if (ret < 0) {
1071 			req->keep = 0;
1072 			return ret;
1073 		} else if (ret > 0) {
1074 			req->in_len -= ret;
1075 			rest -= ret;
1076 			n += ret;
1077 			str += ret;
1078 			if (req->in_len == 0) {
1079 				if (req->in_pad) {
1080 					if (safe_read(req, buf, req->in_pad) != req->in_pad) {
1081 						req->keep = 0;
1082 						return ret;
1083 					}
1084 				}
1085 			} else {
1086 				return n;
1087 			}
1088 		} else {
1089 			return n;
1090 		}
1091 	}
1092 	return n;
1093 }
1094 
fcgi_close(fcgi_request * req,int force,int destroy)1095 static inline void fcgi_close(fcgi_request *req, int force, int destroy)
1096 {
1097 	if (destroy && req->has_env) {
1098 		fcgi_hash_clean(&req->env);
1099 		req->has_env = 0;
1100 	}
1101 
1102 #ifdef _WIN32
1103 	if (is_impersonate && !req->tcp) {
1104 		RevertToSelf();
1105 	}
1106 #endif
1107 
1108 	if ((force || !req->keep) && req->fd >= 0) {
1109 #ifdef _WIN32
1110 		if (!req->tcp) {
1111 			HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
1112 
1113 			if (!force) {
1114 				FlushFileBuffers(pipe);
1115 			}
1116 			DisconnectNamedPipe(pipe);
1117 		} else {
1118 			if (!force) {
1119 				fcgi_header buf;
1120 
1121 				shutdown(req->fd, 1);
1122 				/* read the last FCGI_STDIN header (it may be omitted) */
1123 				recv(req->fd, &buf, sizeof(buf), 0);
1124 			}
1125 			closesocket(req->fd);
1126 		}
1127 #else
1128 		if (!force) {
1129 			fcgi_header buf;
1130 
1131 			shutdown(req->fd, 1);
1132 			/* read the last FCGI_STDIN header (it may be omitted) */
1133 			recv(req->fd, &buf, sizeof(buf), 0);
1134 		}
1135 		close(req->fd);
1136 #endif
1137 #ifdef TCP_NODELAY
1138 		req->nodelay = 0;
1139 #endif
1140 		req->fd = -1;
1141 	}
1142 }
1143 
fcgi_accept_request(fcgi_request * req)1144 int fcgi_accept_request(fcgi_request *req)
1145 {
1146 #ifdef _WIN32
1147 	HANDLE pipe;
1148 	OVERLAPPED ov;
1149 #endif
1150 
1151 	while (1) {
1152 		if (req->fd < 0) {
1153 			while (1) {
1154 				if (in_shutdown) {
1155 					return -1;
1156 				}
1157 #ifdef _WIN32
1158 				if (!req->tcp) {
1159 					pipe = (HANDLE)_get_osfhandle(req->listen_socket);
1160 					FCGI_LOCK(req->listen_socket);
1161 					ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1162 					if (!ConnectNamedPipe(pipe, &ov)) {
1163 						errno = GetLastError();
1164 						if (errno == ERROR_IO_PENDING) {
1165 							while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
1166 								if (in_shutdown) {
1167 									CloseHandle(ov.hEvent);
1168 									FCGI_UNLOCK(req->listen_socket);
1169 									return -1;
1170 								}
1171 							}
1172 						} else if (errno != ERROR_PIPE_CONNECTED) {
1173 						}
1174 					}
1175 					CloseHandle(ov.hEvent);
1176 					req->fd = req->listen_socket;
1177 					FCGI_UNLOCK(req->listen_socket);
1178 				} else {
1179 					SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
1180 #else
1181 				{
1182 					int listen_socket = req->listen_socket;
1183 #endif
1184 					sa_t sa;
1185 					socklen_t len = sizeof(sa);
1186 
1187 					FCGI_LOCK(req->listen_socket);
1188 					req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
1189 					FCGI_UNLOCK(req->listen_socket);
1190 					if (req->fd >= 0) {
1191 						if (((struct sockaddr *)&sa)->sa_family == AF_INET) {
1192 #ifndef _WIN32
1193 							req->tcp = 1;
1194 #endif
1195 							if (allowed_clients) {
1196 								int n = 0;
1197 								int allowed = 0;
1198 
1199 								while (allowed_clients[n] != INADDR_NONE) {
1200 									if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) {
1201 										allowed = 1;
1202 										break;
1203 									}
1204 									n++;
1205 								}
1206 								if (!allowed) {
1207 									fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr));
1208 									closesocket(req->fd);
1209 									req->fd = -1;
1210 									continue;
1211 								}
1212 							}
1213 #ifndef _WIN32
1214 						} else {
1215 							req->tcp = 0;
1216 #endif
1217 						}
1218 					}
1219 				}
1220 
1221 #ifdef _WIN32
1222 				if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
1223 #else
1224 				if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) {
1225 #endif
1226 					return -1;
1227 				}
1228 
1229 #ifdef _WIN32
1230 				break;
1231 #else
1232 				if (req->fd >= 0) {
1233 #if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
1234 					struct pollfd fds;
1235 					int ret;
1236 
1237 					fds.fd = req->fd;
1238 					fds.events = POLLIN;
1239 					fds.revents = 0;
1240 					do {
1241 						errno = 0;
1242 						ret = poll(&fds, 1, 5000);
1243 					} while (ret < 0 && errno == EINTR);
1244 					if (ret > 0 && (fds.revents & POLLIN)) {
1245 						break;
1246 					}
1247 					fcgi_close(req, 1, 0);
1248 #else
1249 					if (req->fd < FD_SETSIZE) {
1250 						struct timeval tv = {5,0};
1251 						fd_set set;
1252 						int ret;
1253 
1254 						FD_ZERO(&set);
1255 						FD_SET(req->fd, &set);
1256 						do {
1257 							errno = 0;
1258 							ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
1259 						} while (ret < 0 && errno == EINTR);
1260 						if (ret > 0 && FD_ISSET(req->fd, &set)) {
1261 							break;
1262 						}
1263 						fcgi_close(req, 1, 0);
1264 					} else {
1265 						fprintf(stderr, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
1266 						fcgi_close(req, 1, 0);
1267 					}
1268 #endif
1269 				}
1270 #endif
1271 			}
1272 		} else if (in_shutdown) {
1273 			return -1;
1274 		}
1275 		if (fcgi_read_request(req)) {
1276 #ifdef _WIN32
1277 			if (is_impersonate && !req->tcp) {
1278 				pipe = (HANDLE)_get_osfhandle(req->fd);
1279 				if (!ImpersonateNamedPipeClient(pipe)) {
1280 					fcgi_close(req, 1, 1);
1281 					continue;
1282 				}
1283 			}
1284 #endif
1285 			return req->fd;
1286 		} else {
1287 			fcgi_close(req, 1, 1);
1288 		}
1289 	}
1290 }
1291 
1292 static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type)
1293 {
1294 	req->out_hdr = (fcgi_header*) req->out_pos;
1295 	req->out_hdr->type = type;
1296 	req->out_pos += sizeof(fcgi_header);
1297 	return req->out_hdr;
1298 }
1299 
1300 static inline void close_packet(fcgi_request *req)
1301 {
1302 	if (req->out_hdr) {
1303 		int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header));
1304 
1305 		req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len);
1306 		req->out_hdr = NULL;
1307 	}
1308 }
1309 
1310 int fcgi_flush(fcgi_request *req, int close)
1311 {
1312 	int len;
1313 
1314 	close_packet(req);
1315 
1316 	len = req->out_pos - req->out_buf;
1317 
1318 	if (close) {
1319 		fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos);
1320 
1321 		fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request));
1322 		rec->body.appStatusB3 = 0;
1323 		rec->body.appStatusB2 = 0;
1324 		rec->body.appStatusB1 = 0;
1325 		rec->body.appStatusB0 = 0;
1326 		rec->body.protocolStatus = FCGI_REQUEST_COMPLETE;
1327 		len += sizeof(fcgi_end_request_rec);
1328 	}
1329 
1330 	if (safe_write(req, req->out_buf, len) != len) {
1331 		req->keep = 0;
1332 		return 0;
1333 	}
1334 
1335 	req->out_pos = req->out_buf;
1336 	return 1;
1337 }
1338 
1339 int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len)
1340 {
1341 	int limit, rest;
1342 
1343 	if (len <= 0) {
1344 		return 0;
1345 	}
1346 
1347 	if (req->out_hdr && req->out_hdr->type != type) {
1348 		close_packet(req);
1349 	}
1350 #if 0
1351 	/* Unoptimized, but clear version */
1352 	rest = len;
1353 	while (rest > 0) {
1354 		limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
1355 
1356 		if (!req->out_hdr) {
1357 			if (limit < sizeof(fcgi_header)) {
1358 				if (!fcgi_flush(req, 0)) {
1359 					return -1;
1360 				}
1361 			}
1362 			open_packet(req, type);
1363 		}
1364 		limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
1365 		if (rest < limit) {
1366 			memcpy(req->out_pos, str, rest);
1367 			req->out_pos += rest;
1368 			return len;
1369 		} else {
1370 			memcpy(req->out_pos, str, limit);
1371 			req->out_pos += limit;
1372 			rest -= limit;
1373 			str += limit;
1374 			if (!fcgi_flush(req, 0)) {
1375 				return -1;
1376 			}
1377 		}
1378 	}
1379 #else
1380 	/* Optimized version */
1381 	limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
1382 	if (!req->out_hdr) {
1383 		limit -= sizeof(fcgi_header);
1384 		if (limit < 0) limit = 0;
1385 	}
1386 
1387 	if (len < limit) {
1388 		if (!req->out_hdr) {
1389 			open_packet(req, type);
1390 		}
1391 		memcpy(req->out_pos, str, len);
1392 		req->out_pos += len;
1393 	} else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) {
1394 		if (!req->out_hdr) {
1395 			open_packet(req, type);
1396 		}
1397 		if (limit > 0) {
1398 			memcpy(req->out_pos, str, limit);
1399 			req->out_pos += limit;
1400 		}
1401 		if (!fcgi_flush(req, 0)) {
1402 			return -1;
1403 		}
1404 		if (len > limit) {
1405 			open_packet(req, type);
1406 			memcpy(req->out_pos, str + limit, len - limit);
1407 			req->out_pos += len - limit;
1408 		}
1409 	} else {
1410 		int pos = 0;
1411 		int pad;
1412 
1413 		close_packet(req);
1414 		while ((len - pos) > 0xffff) {
1415 			open_packet(req, type);
1416 			fcgi_make_header(req->out_hdr, type, req->id, 0xfff8);
1417 			req->out_hdr = NULL;
1418 			if (!fcgi_flush(req, 0)) {
1419 				return -1;
1420 			}
1421 			if (safe_write(req, str + pos, 0xfff8) != 0xfff8) {
1422 				req->keep = 0;
1423 				return -1;
1424 			}
1425 			pos += 0xfff8;
1426 		}
1427 
1428 		pad = (((len - pos) + 7) & ~7) - (len - pos);
1429 		rest = pad ? 8 - pad : 0;
1430 
1431 		open_packet(req, type);
1432 		fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest);
1433 		req->out_hdr = NULL;
1434 		if (!fcgi_flush(req, 0)) {
1435 			return -1;
1436 		}
1437 		if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) {
1438 			req->keep = 0;
1439 			return -1;
1440 		}
1441 		if (pad) {
1442 			open_packet(req, type);
1443 			memcpy(req->out_pos, str + len - rest,  rest);
1444 			req->out_pos += rest;
1445 		}
1446 	}
1447 #endif
1448 	return len;
1449 }
1450 
1451 int fcgi_finish_request(fcgi_request *req, int force_close)
1452 {
1453 	int ret = 1;
1454 
1455 	if (req->fd >= 0) {
1456 		if (!req->closed) {
1457 			ret = fcgi_flush(req, 1);
1458 			req->closed = 1;
1459 		}
1460 		fcgi_close(req, force_close, 1);
1461 	}
1462 	return ret;
1463 }
1464 
1465 char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
1466 {
1467 	unsigned int val_len;
1468 
1469 	if (!req) return NULL;
1470 
1471 	return fcgi_hash_get(&req->env, FCGI_HASH_FUNC(var, var_len), (char*)var, var_len, &val_len);
1472 }
1473 
1474 char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value)
1475 {
1476 	unsigned int val_len;
1477 
1478 	return fcgi_hash_get(&req->env, hash_value, (char*)var, var_len, &val_len);
1479 }
1480 
1481 char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
1482 {
1483 	if (!req) return NULL;
1484 	if (val == NULL) {
1485 		fcgi_hash_del(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len);
1486 		return NULL;
1487 	} else {
1488 		return fcgi_hash_set(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len, val, strlen(val));
1489 	}
1490 }
1491 
1492 char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val)
1493 {
1494 	if (val == NULL) {
1495 		fcgi_hash_del(&req->env, hash_value, var, var_len);
1496 		return NULL;
1497 	} else {
1498 		return fcgi_hash_set(&req->env, hash_value, var, var_len, val, strlen(val));
1499 	}
1500 }
1501 
1502 void fcgi_loadenv(fcgi_request *req, fcgi_apply_func func, zval *array TSRMLS_DC)
1503 {
1504 	fcgi_hash_apply(&req->env, func, array TSRMLS_CC);
1505 }
1506 
1507 #ifdef _WIN32
1508 void fcgi_impersonate(void)
1509 {
1510 	char *os_name;
1511 
1512 	os_name = getenv("OS");
1513 	if (os_name && stricmp(os_name, "Windows_NT") == 0) {
1514 		is_impersonate = 1;
1515 	}
1516 }
1517 #endif
1518 
1519 void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len)
1520 {
1521 	zval * zvalue;
1522 	zvalue = pemalloc(sizeof(*zvalue), 1);
1523 	Z_TYPE_P(zvalue) = IS_STRING;
1524 	Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1);
1525 	Z_STRLEN_P(zvalue) = value_len;
1526 	zend_hash_add(&fcgi_mgmt_vars, name, name_len, &zvalue, sizeof(zvalue), NULL);
1527 }
1528 
1529 void fcgi_free_mgmt_var_cb(void * ptr)
1530 {
1531 	zval ** var = (zval **)ptr;
1532 	pefree(Z_STRVAL_PP(var), 1);
1533 	pefree(*var, 1);
1534 }
1535 
1536 /*
1537  * Local variables:
1538  * tab-width: 4
1539  * c-basic-offset: 4
1540  * End:
1541  * vim600: sw=4 ts=4 fdm=marker
1542  * vim<600: sw=4 ts=4
1543  */
1544