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