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