Annotation of embedaddon/php/sapi/cgi/fastcgi.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 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: fastcgi.c 321634 2012-01-01 13:15:04Z felipe $ */
! 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:
! 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:
! 155: static void fcgi_signal_handler(int signo)
! 156: {
! 157: if (signo == SIGUSR1 || signo == SIGTERM) {
! 158: in_shutdown = 1;
! 159: }
! 160: }
! 161:
! 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:
! 178: int fcgi_in_shutdown(void)
! 179: {
! 180: return in_shutdown;
! 181: }
! 182:
! 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:
! 243: int fcgi_is_fastcgi(void)
! 244: {
! 245: if (!is_initialized) {
! 246: return fcgi_init();
! 247: } else {
! 248: return is_fastcgi;
! 249: }
! 250: }
! 251:
! 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: */
! 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:
! 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:
! 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:
! 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:
! 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:
! 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:
! 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:
! 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:
! 661: static void fcgi_free_var(char **s)
! 662: {
! 663: efree(*s);
! 664: }
! 665:
! 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:
! 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:
! 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:
! 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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>