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