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