Back to index

php5  5.3.10
fastcgi.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
00016    +----------------------------------------------------------------------+
00017 */
00018 
00019 /* $Id: fastcgi.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #include "php.h"
00022 #include "fastcgi.h"
00023 
00024 #include <string.h>
00025 #include <stdlib.h>
00026 #include <stdio.h>
00027 #include <stdarg.h>
00028 #include <errno.h>
00029 
00030 #ifdef _WIN32
00031 
00032 #include <windows.h>
00033 
00034        typedef unsigned int in_addr_t;
00035 
00036        struct sockaddr_un {
00037               short   sun_family;
00038               char    sun_path[MAXPATHLEN];
00039        };
00040 
00041        static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE;
00042        static int is_impersonate = 0;
00043 
00044 #define FCGI_LOCK(fd) \
00045        if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
00046               DWORD ret; \
00047               while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \
00048                      if (in_shutdown) return -1; \
00049               } \
00050               if (ret == WAIT_FAILED) { \
00051                      fprintf(stderr, "WaitForSingleObject() failed\n"); \
00052                      return -1; \
00053               } \
00054        }
00055 
00056 #define FCGI_UNLOCK(fd) \
00057        if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
00058               ReleaseMutex(fcgi_accept_mutex); \
00059        }
00060 
00061 #else
00062 
00063 # include <sys/types.h>
00064 # include <sys/stat.h>
00065 # include <unistd.h>
00066 # include <fcntl.h>
00067 # include <sys/socket.h>
00068 # include <sys/un.h>
00069 # include <netinet/in.h>
00070 # include <arpa/inet.h>
00071 # include <netdb.h>
00072 # include <signal.h>
00073 
00074 # define closesocket(s) close(s)
00075 
00076 # if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
00077 #  include <sys/poll.h>
00078 # endif
00079 # if defined(HAVE_SYS_SELECT_H)
00080 #  include <sys/select.h>
00081 # endif
00082 
00083 #ifndef INADDR_NONE
00084 #define INADDR_NONE ((unsigned long) -1)
00085 #endif
00086 
00087 # ifndef HAVE_SOCKLEN_T
00088        typedef unsigned int socklen_t;
00089 # endif
00090 
00091 # ifdef USE_LOCKING
00092 #  define FCGI_LOCK(fd)                                                      \
00093        do {                                                                         \
00094               struct flock lock;                                             \
00095               lock.l_type = F_WRLCK;                                         \
00096               lock.l_start = 0;                                              \
00097               lock.l_whence = SEEK_SET;                               \
00098               lock.l_len = 0;                                                       \
00099               if (fcntl(fd, F_SETLKW, &lock) != -1) {          \
00100                      break;                                                         \
00101               } else if (errno != EINTR || in_shutdown) {      \
00102                      return -1;                                                     \
00103               }                                                                            \
00104        } while (1)
00105 
00106 #  define FCGI_UNLOCK(fd)                                             \
00107        do {                                                                         \
00108               int orig_errno = errno;                                        \
00109               while (1) {                                                           \
00110                      struct flock lock;                                      \
00111                      lock.l_type = F_UNLCK;                                  \
00112                      lock.l_start = 0;                                       \
00113                      lock.l_whence = SEEK_SET;                        \
00114                      lock.l_len = 0;                                                \
00115                      if (fcntl(fd, F_SETLK, &lock) != -1) {    \
00116                             break;                                                  \
00117                      } else if (errno != EINTR) {                     \
00118                             return -1;                                              \
00119                      }                                                                     \
00120               }                                                                            \
00121               errno = orig_errno;                                            \
00122        } while (0)
00123 # else
00124 #  define FCGI_LOCK(fd)
00125 #  define FCGI_UNLOCK(fd)
00126 # endif
00127 
00128 #endif
00129 
00130 typedef union _sa_t {
00131        struct sockaddr     sa;
00132        struct sockaddr_un  sa_unix;
00133        struct sockaddr_in  sa_inet;
00134 } sa_t;
00135 
00136 static HashTable fcgi_mgmt_vars;
00137 
00138 static int is_initialized = 0;
00139 static int is_fastcgi = 0;
00140 static int in_shutdown = 0;
00141 static in_addr_t *allowed_clients = NULL;
00142 
00143 #ifdef _WIN32
00144 
00145 static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg)
00146 {
00147        HANDLE shutdown_event = (HANDLE) arg;
00148        WaitForSingleObject(shutdown_event, INFINITE);
00149        in_shutdown = 1;
00150        return 0;
00151 }
00152 
00153 #else
00154 
00155 static void fcgi_signal_handler(int signo)
00156 {
00157        if (signo == SIGUSR1 || signo == SIGTERM) {
00158               in_shutdown = 1;
00159        }
00160 }
00161 
00162 static void fcgi_setup_signals(void)
00163 {
00164        struct sigaction new_sa, old_sa;
00165 
00166        sigemptyset(&new_sa.sa_mask);
00167        new_sa.sa_flags = 0;
00168        new_sa.sa_handler = fcgi_signal_handler;
00169        sigaction(SIGUSR1, &new_sa, NULL);
00170        sigaction(SIGTERM, &new_sa, NULL);
00171        sigaction(SIGPIPE, NULL, &old_sa);
00172        if (old_sa.sa_handler == SIG_DFL) {
00173               sigaction(SIGPIPE, &new_sa, NULL);
00174        }
00175 }
00176 #endif
00177 
00178 int fcgi_in_shutdown(void)
00179 {
00180        return in_shutdown;
00181 }
00182 
00183 int fcgi_init(void)
00184 {
00185        if (!is_initialized) {
00186 #ifndef _WIN32
00187               sa_t sa;
00188               socklen_t len = sizeof(sa);
00189 #endif
00190               zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1);
00191               fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS")-1, "0", sizeof("0")-1);
00192 
00193               is_initialized = 1;
00194 #ifdef _WIN32
00195 # if 0
00196               /* TODO: Support for TCP sockets */
00197               WSADATA wsaData;
00198 
00199               if (WSAStartup(MAKEWORD(2,0), &wsaData)) {
00200                      fprintf(stderr, "Error starting Windows Sockets.  Error: %d", WSAGetLastError());
00201                      return 0;
00202               }
00203 # endif
00204               if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
00205                   (GetStdHandle(STD_ERROR_HANDLE)  == INVALID_HANDLE_VALUE) &&
00206                   (GetStdHandle(STD_INPUT_HANDLE)  != INVALID_HANDLE_VALUE)) {
00207                      char *str;
00208                      DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;
00209                      HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE);
00210 
00211                      SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL);
00212 
00213                      str = getenv("_FCGI_SHUTDOWN_EVENT_");
00214                      if (str != NULL) {
00215                             HANDLE shutdown_event = (HANDLE) atoi(str);
00216                             if (!CreateThread(NULL, 0, fcgi_shutdown_thread,
00217                                               shutdown_event, 0, NULL)) {
00218                                    return -1;
00219                             }
00220                      }
00221                      str = getenv("_FCGI_MUTEX_");
00222                      if (str != NULL) {
00223                             fcgi_accept_mutex = (HANDLE) atoi(str);
00224                      }
00225                      return is_fastcgi = 1;
00226               } else {
00227                      return is_fastcgi = 0;
00228               }
00229 #else
00230               errno = 0;
00231               if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) {
00232                      fcgi_setup_signals();
00233                      return is_fastcgi = 1;
00234               } else {
00235                      return is_fastcgi = 0;
00236               }
00237 #endif
00238        }
00239        return is_fastcgi;
00240 }
00241 
00242 
00243 int fcgi_is_fastcgi(void)
00244 {
00245        if (!is_initialized) {
00246               return fcgi_init();
00247        } else {
00248               return is_fastcgi;
00249        }
00250 }
00251 
00252 void fcgi_shutdown(void)
00253 {
00254        if (is_initialized) {
00255               zend_hash_destroy(&fcgi_mgmt_vars);
00256        }
00257        is_fastcgi = 0;
00258        if (allowed_clients) {
00259               free(allowed_clients);
00260        }
00261 }
00262 
00263 #ifdef _WIN32
00264 /* Do some black magic with the NT security API.
00265  * We prepare a DACL (Discretionary Access Control List) so that
00266  * we, the creator, are allowed all access, while "Everyone Else"
00267  * is only allowed to read and write to the pipe.
00268  * This avoids security issues on shared hosts where a luser messes
00269  * with the lower-level pipe settings and screws up the FastCGI service.
00270  */
00271 static PACL prepare_named_pipe_acl(PSECURITY_DESCRIPTOR sd, LPSECURITY_ATTRIBUTES sa)
00272 {
00273        DWORD req_acl_size;
00274        char everyone_buf[32], owner_buf[32];
00275        PSID sid_everyone, sid_owner;
00276        SID_IDENTIFIER_AUTHORITY
00277               siaWorld = SECURITY_WORLD_SID_AUTHORITY,
00278               siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
00279        PACL acl;
00280 
00281        sid_everyone = (PSID)&everyone_buf;
00282        sid_owner = (PSID)&owner_buf;
00283 
00284        req_acl_size = sizeof(ACL) +
00285               (2 * ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetSidLengthRequired(1)));
00286 
00287        acl = malloc(req_acl_size);
00288 
00289        if (acl == NULL) {
00290               return NULL;
00291        }
00292 
00293        if (!InitializeSid(sid_everyone, &siaWorld, 1)) {
00294               goto out_fail;
00295        }
00296        *GetSidSubAuthority(sid_everyone, 0) = SECURITY_WORLD_RID;
00297 
00298        if (!InitializeSid(sid_owner, &siaCreator, 1)) {
00299               goto out_fail;
00300        }
00301        *GetSidSubAuthority(sid_owner, 0) = SECURITY_CREATOR_OWNER_RID;
00302 
00303        if (!InitializeAcl(acl, req_acl_size, ACL_REVISION)) {
00304               goto out_fail;
00305        }
00306 
00307        if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_GENERIC_READ | FILE_GENERIC_WRITE, sid_everyone)) {
00308               goto out_fail;
00309        }
00310 
00311        if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, sid_owner)) {
00312               goto out_fail;
00313        }
00314 
00315        if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
00316               goto out_fail;
00317        }
00318 
00319        if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) {
00320               goto out_fail;
00321        }
00322 
00323        sa->lpSecurityDescriptor = sd;
00324 
00325        return acl;
00326 
00327 out_fail:
00328        free(acl);
00329        return NULL;
00330 }
00331 #endif
00332 
00333 static int is_port_number(const char *bindpath)
00334 {
00335        while (*bindpath) {
00336               if (*bindpath < '0' || *bindpath > '9') {
00337                      return 0;
00338               }
00339               bindpath++;
00340        }
00341        return 1;
00342 }
00343 
00344 int fcgi_listen(const char *path, int backlog)
00345 {
00346        char     *s;
00347        int       tcp = 0;
00348        char      host[MAXPATHLEN];
00349        short     port = 0;
00350        int       listen_socket;
00351        sa_t      sa;
00352        socklen_t sock_len;
00353 #ifdef SO_REUSEADDR
00354 # ifdef _WIN32
00355        BOOL reuse = 1;
00356 # else
00357        int reuse = 1;
00358 # endif
00359 #endif
00360 
00361        if ((s = strchr(path, ':'))) {
00362               port = atoi(s+1);
00363               if (port != 0 && (s-path) < MAXPATHLEN) {
00364                      strncpy(host, path, s-path);
00365                      host[s-path] = '\0';
00366                      tcp = 1;
00367               }
00368        } else if (is_port_number(path)) {
00369               port = atoi(path);
00370               if (port != 0) {
00371                      host[0] = '\0';
00372                      tcp = 1;
00373               }
00374        }
00375 
00376        /* Prepare socket address */
00377        if (tcp) {
00378               memset(&sa.sa_inet, 0, sizeof(sa.sa_inet));
00379               sa.sa_inet.sin_family = AF_INET;
00380               sa.sa_inet.sin_port = htons(port);
00381               sock_len = sizeof(sa.sa_inet);
00382 
00383               if (!*host || !strncmp(host, "*", sizeof("*")-1)) {
00384                      sa.sa_inet.sin_addr.s_addr = htonl(INADDR_ANY);
00385               } else {
00386                      sa.sa_inet.sin_addr.s_addr = inet_addr(host);
00387                      if (sa.sa_inet.sin_addr.s_addr == INADDR_NONE) {
00388                             struct hostent *hep;
00389 
00390                             hep = gethostbyname(host);
00391                             if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) {
00392                                    fprintf(stderr, "Cannot resolve host name '%s'!\n", host);
00393                                    return -1;
00394                             } else if (hep->h_addr_list[1]) {
00395                                    fprintf(stderr, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host);
00396                                    return -1;
00397                             }
00398                             sa.sa_inet.sin_addr.s_addr = ((struct in_addr*)hep->h_addr_list[0])->s_addr;
00399                      }
00400               }
00401        } else {
00402 #ifdef _WIN32
00403               SECURITY_DESCRIPTOR  sd;
00404               SECURITY_ATTRIBUTES  saw;
00405               PACL                 acl;
00406               HANDLE namedPipe;
00407 
00408               memset(&sa, 0, sizeof(saw));
00409               saw.nLength = sizeof(saw);
00410               saw.bInheritHandle = FALSE;
00411               acl = prepare_named_pipe_acl(&sd, &saw);
00412 
00413               namedPipe = CreateNamedPipe(path,
00414                      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
00415                      PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
00416                      PIPE_UNLIMITED_INSTANCES,
00417                      8192, 8192, 0, &saw);
00418               if (namedPipe == INVALID_HANDLE_VALUE) {
00419                      return -1;
00420               }             
00421               listen_socket = _open_osfhandle((long)namedPipe, 0);
00422               if (!is_initialized) {
00423                      fcgi_init();
00424               }
00425               is_fastcgi = 1;
00426               return listen_socket;
00427 
00428 #else
00429               int path_len = strlen(path);
00430 
00431               if (path_len >= sizeof(sa.sa_unix.sun_path)) {
00432                      fprintf(stderr, "Listening socket's path name is too long.\n");
00433                      return -1;
00434               }
00435 
00436               memset(&sa.sa_unix, 0, sizeof(sa.sa_unix));
00437               sa.sa_unix.sun_family = AF_UNIX;
00438               memcpy(sa.sa_unix.sun_path, path, path_len + 1);
00439               sock_len = (size_t)(((struct sockaddr_un *)0)->sun_path)       + path_len;
00440 #ifdef HAVE_SOCKADDR_UN_SUN_LEN
00441               sa.sa_unix.sun_len = sock_len;
00442 #endif
00443               unlink(path);
00444 #endif
00445        }
00446 
00447        /* Create, bind socket and start listen on it */
00448        if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||
00449 #ifdef SO_REUSEADDR
00450            setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0 ||
00451 #endif
00452            bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||
00453            listen(listen_socket, backlog) < 0) {
00454 
00455               fprintf(stderr, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno));
00456               return -1;
00457        }
00458 
00459        if (!tcp) {
00460               chmod(path, 0777);
00461        } else {
00462                      char *ip = getenv("FCGI_WEB_SERVER_ADDRS");
00463                      char *cur, *end;
00464                      int n;
00465                      
00466                      if (ip) {
00467                             ip = strdup(ip);
00468                             cur = ip;
00469                             n = 0;
00470                             while (*cur) {
00471                                    if (*cur == ',') n++;
00472                                    cur++;
00473                             }
00474                             allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
00475                             n = 0;
00476                             cur = ip;
00477                             while (cur) {
00478                                    end = strchr(cur, ',');
00479                                    if (end) {
00480                                           *end = 0;
00481                                           end++;
00482                                    }
00483                                    allowed_clients[n] = inet_addr(cur);
00484                                    if (allowed_clients[n] == INADDR_NONE) {
00485                                    fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur);
00486                                    }
00487                                    n++;
00488                                    cur = end;
00489                             }
00490                             allowed_clients[n] = INADDR_NONE;
00491                      free(ip);
00492               }
00493        }
00494 
00495        if (!is_initialized) {
00496               fcgi_init();
00497        }
00498        is_fastcgi = 1;
00499 
00500 #ifdef _WIN32
00501        if (tcp) {
00502               listen_socket = _open_osfhandle((long)listen_socket, 0);
00503        }
00504 #else
00505        fcgi_setup_signals();
00506 #endif
00507        return listen_socket;
00508 }
00509 
00510 void fcgi_init_request(fcgi_request *req, int listen_socket)
00511 {
00512        memset(req, 0, sizeof(fcgi_request));
00513        req->listen_socket = listen_socket;
00514        req->fd = -1;
00515        req->id = -1;
00516 
00517        req->in_len = 0;
00518        req->in_pad = 0;
00519 
00520        req->out_hdr = NULL;
00521        req->out_pos = req->out_buf;
00522 
00523 #ifdef _WIN32
00524        req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
00525 #endif
00526 }
00527 
00528 static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
00529 {
00530        int    ret;
00531        size_t n = 0;
00532 
00533        do {
00534               errno = 0;
00535 #ifdef _WIN32
00536               if (!req->tcp) {
00537                      ret = write(req->fd, ((char*)buf)+n, count-n);
00538               } else {
00539                      ret = send(req->fd, ((char*)buf)+n, count-n, 0);
00540                      if (ret <= 0) {
00541                             errno = WSAGetLastError();
00542                      }
00543               }
00544 #else
00545               ret = write(req->fd, ((char*)buf)+n, count-n);
00546 #endif
00547               if (ret > 0) {
00548                      n += ret;
00549               } else if (ret <= 0 && errno != 0 && errno != EINTR) {
00550                      return ret;
00551               }
00552        } while (n != count);
00553        return n;
00554 }
00555 
00556 static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count)
00557 {
00558        int    ret;
00559        size_t n = 0;
00560 
00561        do {
00562               errno = 0;
00563 #ifdef _WIN32
00564               if (!req->tcp) {
00565                      ret = read(req->fd, ((char*)buf)+n, count-n);
00566               } else {
00567                      ret = recv(req->fd, ((char*)buf)+n, count-n, 0);
00568                      if (ret <= 0) {
00569                             errno = WSAGetLastError();
00570                      }
00571               }
00572 #else
00573               ret = read(req->fd, ((char*)buf)+n, count-n);
00574 #endif
00575               if (ret > 0) {
00576                      n += ret;
00577               } else if (ret == 0 && errno == 0) {
00578                      return n;
00579               } else if (ret <= 0 && errno != 0 && errno != EINTR) {
00580                      return ret;
00581               }
00582        } while (n != count);
00583        return n;
00584 }
00585 
00586 static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len)
00587 {
00588        int pad = ((len + 7) & ~7) - len;
00589 
00590        hdr->contentLengthB0 = (unsigned char)(len & 0xff);
00591        hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff);
00592        hdr->paddingLength = (unsigned char)pad;
00593        hdr->requestIdB0 = (unsigned char)(req_id & 0xff);
00594        hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff);
00595        hdr->reserved = 0;
00596        hdr->type = type;
00597        hdr->version = FCGI_VERSION_1;
00598        if (pad) {
00599               memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad);
00600        }
00601        return pad;
00602 }
00603 
00604 static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end)
00605 {
00606        char buf[128];
00607        char *tmp = buf;
00608        size_t buf_size = sizeof(buf);
00609        unsigned int name_len, val_len;
00610        char *s;
00611        int ret = 1;
00612 
00613        while (p < end) {
00614               name_len = *p++;
00615               if (name_len >= 128) {
00616                      if (p + 3 >= end) {
00617                             ret = 0;
00618                             break;
00619                      }
00620                      name_len = ((name_len & 0x7f) << 24);
00621                      name_len |= (*p++ << 16);
00622                      name_len |= (*p++ << 8);
00623                      name_len |= *p++;
00624               }
00625               if (p >= end) {
00626                      ret = 0;
00627                      break;
00628               }
00629               val_len = *p++;
00630               if (val_len >= 128) {
00631                      if (p + 3 >= end) {
00632                             ret = 0;
00633                             break;
00634                      }
00635                      val_len = ((val_len & 0x7f) << 24);
00636                      val_len |= (*p++ << 16);
00637                      val_len |= (*p++ << 8);
00638                      val_len |= *p++;
00639               }
00640               if (name_len + val_len > end - p) {
00641                      /* Malformated request */
00642                      ret = 0;
00643                      break;
00644               }
00645               if (name_len+1 >= buf_size) {
00646                      buf_size = name_len + 64;
00647                      tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size));
00648               }
00649               memcpy(tmp, p, name_len);
00650               tmp[name_len] = 0;
00651               s = estrndup((char*)p + name_len, val_len);
00652               zend_hash_update(req->env, tmp, name_len+1, &s, sizeof(char*), NULL);
00653               p += name_len + val_len;
00654        }
00655        if (tmp != buf && tmp != NULL) {
00656               efree(tmp);
00657        }
00658        return ret;
00659 }
00660 
00661 static void fcgi_free_var(char **s)
00662 {
00663        efree(*s);
00664 }
00665 
00666 static int fcgi_read_request(fcgi_request *req)
00667 {
00668        fcgi_header hdr;
00669        int len, padding;
00670        unsigned char buf[FCGI_MAX_LENGTH+8];
00671 
00672        req->keep = 0;
00673        req->closed = 0;
00674        req->in_len = 0;
00675        req->out_hdr = NULL;
00676        req->out_pos = req->out_buf;
00677        ALLOC_HASHTABLE(req->env);
00678        zend_hash_init(req->env, 0, NULL, (void (*)(void *)) fcgi_free_var, 0);
00679 
00680        if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
00681            hdr.version < FCGI_VERSION_1) {
00682               return 0;
00683        }
00684 
00685        len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
00686        padding = hdr.paddingLength;
00687 
00688        while (hdr.type == FCGI_STDIN && len == 0) {
00689               if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
00690                   hdr.version < FCGI_VERSION_1) {
00691                      return 0;
00692               }
00693 
00694               len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
00695               padding = hdr.paddingLength;
00696        }
00697 
00698        if (len + padding > FCGI_MAX_LENGTH) {
00699               return 0;
00700        }
00701 
00702        req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0;
00703 
00704        if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) {
00705               char *val;
00706 
00707               if (safe_read(req, buf, len+padding) != len+padding) {
00708                      return 0;
00709               }
00710 
00711               req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN);
00712               switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) {
00713                      case FCGI_RESPONDER:
00714                             val = estrdup("RESPONDER");
00715                             zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
00716                             break;
00717                      case FCGI_AUTHORIZER:
00718                             val = estrdup("AUTHORIZER");
00719                             zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
00720                             break;
00721                      case FCGI_FILTER:
00722                             val = estrdup("FILTER");
00723                             zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
00724                             break;
00725                      default:
00726                             return 0;
00727               }
00728 
00729               if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
00730                   hdr.version < FCGI_VERSION_1) {
00731                      return 0;
00732               }
00733 
00734               len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
00735               padding = hdr.paddingLength;
00736 
00737               while (hdr.type == FCGI_PARAMS && len > 0) {
00738                      if (len + padding > FCGI_MAX_LENGTH) {
00739                             return 0;
00740                      }
00741 
00742                      if (safe_read(req, buf, len+padding) != len+padding) {
00743                             req->keep = 0;
00744                             return 0;
00745                      }
00746 
00747                      if (!fcgi_get_params(req, buf, buf+len)) {
00748                             req->keep = 0;
00749                             return 0;
00750                      }
00751 
00752                      if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
00753                          hdr.version < FCGI_VERSION_1) {
00754                             req->keep = 0;
00755                             return 0;
00756                      }
00757                      len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
00758                      padding = hdr.paddingLength;
00759               }
00760        } else if (hdr.type == FCGI_GET_VALUES) {
00761               unsigned char *p = buf + sizeof(fcgi_header);
00762               HashPosition pos;
00763               char * str_index;
00764               uint str_length;
00765               ulong num_index;
00766               int key_type;
00767               zval ** value;
00768 
00769               if (safe_read(req, buf, len+padding) != len+padding) {
00770                      req->keep = 0;
00771                      return 0;
00772               }
00773 
00774               if (!fcgi_get_params(req, buf, buf+len)) {
00775                      req->keep = 0;
00776                      return 0;
00777               }
00778 
00779               zend_hash_internal_pointer_reset_ex(req->env, &pos);
00780               while ((key_type = zend_hash_get_current_key_ex(req->env, &str_index, &str_length, &num_index, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
00781                      int zlen;
00782                      zend_hash_move_forward_ex(req->env, &pos);
00783                      if (key_type != HASH_KEY_IS_STRING) {
00784                             continue;
00785                      }
00786                      if (zend_hash_find(&fcgi_mgmt_vars, str_index, str_length, (void**) &value) != SUCCESS) {
00787                             continue;
00788                      }
00789                      --str_length;
00790                      zlen = Z_STRLEN_PP(value);
00791                      if ((p + 4 + 4 + str_length + zlen) >= (buf + sizeof(buf))) {
00792                             break;
00793                      }
00794                      if (str_length < 0x80) {
00795                             *p++ = str_length;
00796                      } else {
00797                             *p++ = ((str_length >> 24) & 0xff) | 0x80;
00798                             *p++ = (str_length >> 16) & 0xff;
00799                             *p++ = (str_length >> 8) & 0xff;
00800                             *p++ = str_length & 0xff;
00801                      }
00802                      if (zlen < 0x80) {
00803                             *p++ = zlen;
00804                      } else {
00805                             *p++ = ((zlen >> 24) & 0xff) | 0x80;
00806                             *p++ = (zlen >> 16) & 0xff;
00807                             *p++ = (zlen >> 8) & 0xff;
00808                             *p++ = zlen & 0xff;
00809                      }
00810                      memcpy(p, str_index, str_length);
00811                      p += str_length;
00812                      memcpy(p, Z_STRVAL_PP(value), zlen);
00813                      p += zlen;
00814               }
00815               len = p - buf - sizeof(fcgi_header);
00816               len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len);
00817               if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) {
00818                      req->keep = 0;
00819                      return 0;
00820               }
00821               return 0;
00822        } else {
00823               return 0;
00824        }
00825 
00826        return 1;
00827 }
00828 
00829 int fcgi_read(fcgi_request *req, char *str, int len)
00830 {
00831        int ret, n, rest;
00832        fcgi_header hdr;
00833        unsigned char buf[255];
00834 
00835        n = 0;
00836        rest = len;
00837        while (rest > 0) {
00838               if (req->in_len == 0) {
00839                      if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
00840                          hdr.version < FCGI_VERSION_1 ||
00841                          hdr.type != FCGI_STDIN) {
00842                             req->keep = 0;
00843                             return 0;
00844                      }
00845                      req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
00846                      req->in_pad = hdr.paddingLength;
00847                      if (req->in_len == 0) {
00848                             return n;
00849                      }
00850               }
00851 
00852               if (req->in_len >= rest) {
00853                      ret = safe_read(req, str, rest);
00854               } else {
00855                      ret = safe_read(req, str, req->in_len);
00856               }
00857               if (ret < 0) {
00858                      req->keep = 0;
00859                      return ret;
00860               } else if (ret > 0) {
00861                      req->in_len -= ret;
00862                      rest -= ret;
00863                      n += ret;
00864                      str += ret;
00865                      if (req->in_len == 0) {
00866                             if (req->in_pad) {
00867                                    if (safe_read(req, buf, req->in_pad) != req->in_pad) {
00868                                           req->keep = 0;
00869                                           return ret;
00870                                    }
00871                             }
00872                      } else {
00873                             return n;
00874                      }
00875               } else {
00876                      return n;
00877               }
00878        }
00879        return n;
00880 }
00881 
00882 static inline void fcgi_close(fcgi_request *req, int force, int destroy)
00883 {
00884        if (destroy && req->env) {
00885               zend_hash_destroy(req->env);
00886               FREE_HASHTABLE(req->env);
00887               req->env = NULL;
00888        }
00889 
00890 #ifdef _WIN32
00891        if (is_impersonate && !req->tcp) {
00892               RevertToSelf();
00893        }
00894 #endif
00895 
00896        if ((force || !req->keep) && req->fd >= 0) {
00897 #ifdef _WIN32
00898               if (!req->tcp) {
00899                      HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
00900 
00901                      if (!force) {
00902                             FlushFileBuffers(pipe);
00903                      }
00904                      DisconnectNamedPipe(pipe);
00905               } else {
00906                      if (!force) {
00907                             char buf[8];
00908 
00909                             shutdown(req->fd, 1);
00910                             while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
00911                      }
00912                      closesocket(req->fd);
00913               }
00914 #else
00915               if (!force) {
00916                      char buf[8];
00917 
00918                      shutdown(req->fd, 1);
00919                      while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
00920               }
00921               close(req->fd);
00922 #endif
00923               req->fd = -1;
00924        }
00925 }
00926 
00927 int fcgi_accept_request(fcgi_request *req)
00928 {
00929 #ifdef _WIN32
00930        HANDLE pipe;
00931        OVERLAPPED ov;
00932 #endif
00933 
00934        while (1) {
00935               if (req->fd < 0) {
00936                      while (1) {
00937                             if (in_shutdown) {
00938                                    return -1;
00939                             }
00940 #ifdef _WIN32
00941                             if (!req->tcp) {
00942                                    pipe = (HANDLE)_get_osfhandle(req->listen_socket);
00943                                    FCGI_LOCK(req->listen_socket);
00944                                    ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
00945                                    if (!ConnectNamedPipe(pipe, &ov)) {
00946                                           errno = GetLastError();
00947                                           if (errno == ERROR_IO_PENDING) {
00948                                                  while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
00949                                                         if (in_shutdown) {
00950                                                                CloseHandle(ov.hEvent);
00951                                                                FCGI_UNLOCK(req->listen_socket);
00952                                                                return -1;
00953                                                         }
00954                                                  }
00955                                           } else if (errno != ERROR_PIPE_CONNECTED) {
00956                                           }
00957                                    }
00958                                    CloseHandle(ov.hEvent);
00959                                    req->fd = req->listen_socket;
00960                                    FCGI_UNLOCK(req->listen_socket);
00961                             } else {
00962                                    SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
00963 #else
00964                             {
00965                                    int listen_socket = req->listen_socket;
00966 #endif
00967                                    sa_t sa;
00968                                    socklen_t len = sizeof(sa);
00969 
00970                                    FCGI_LOCK(req->listen_socket);
00971                                    req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
00972                                    FCGI_UNLOCK(req->listen_socket);
00973                                    if (req->fd >= 0 && allowed_clients) {
00974                                           int n = 0;
00975                                           int allowed = 0;
00976 
00977                                                  while (allowed_clients[n] != INADDR_NONE) {
00978                                                         if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) {
00979                                                                allowed = 1;
00980                                                                break;
00981                                                         }
00982                                                         n++;
00983                                                  }
00984                                           if (!allowed) {
00985                                                  fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr));
00986                                                  closesocket(req->fd);
00987                                                  req->fd = -1;
00988                                                  continue;
00989                                           }
00990                                    }
00991                             }
00992 
00993 #ifdef _WIN32
00994                             if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
00995 #else
00996                             if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) {
00997 #endif
00998                                    return -1;
00999                             }
01000 
01001 #ifdef _WIN32
01002                             break;
01003 #else
01004                             if (req->fd >= 0) {
01005 #if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
01006                                    struct pollfd fds;
01007                                    int ret;
01008 
01009                                    fds.fd = req->fd;
01010                                    fds.events = POLLIN;
01011                                    fds.revents = 0;
01012                                    do {
01013                                           errno = 0;
01014                                           ret = poll(&fds, 1, 5000);
01015                                    } while (ret < 0 && errno == EINTR);
01016                                    if (ret > 0 && (fds.revents & POLLIN)) {
01017                                           break;
01018                                    }
01019                                    fcgi_close(req, 1, 0);
01020 #else
01021                                    if (req->fd < FD_SETSIZE) {
01022                                           struct timeval tv = {5,0};
01023                                           fd_set set;
01024                                           int ret;
01025 
01026                                           FD_ZERO(&set);
01027                                           FD_SET(req->fd, &set);
01028                                           do {
01029                                                  errno = 0;
01030                                                  ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
01031                                           } while (ret < 0 && errno == EINTR);
01032                                           if (ret > 0 && FD_ISSET(req->fd, &set)) {
01033                                                  break;
01034                                           }
01035                                           fcgi_close(req, 1, 0);
01036                                    } else {
01037                                           fprintf(stderr, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
01038                                           fcgi_close(req, 1, 0);
01039                                    }
01040 #endif
01041                             }
01042 #endif
01043                      }
01044               } else if (in_shutdown) {
01045                      return -1;
01046               }
01047               if (fcgi_read_request(req)) {
01048 #ifdef _WIN32
01049                      if (is_impersonate && !req->tcp) {
01050                             pipe = (HANDLE)_get_osfhandle(req->fd);
01051                             if (!ImpersonateNamedPipeClient(pipe)) {
01052                                    fcgi_close(req, 1, 1);
01053                                    continue;
01054                             }
01055                      }
01056 #endif
01057                      return req->fd;
01058               } else {
01059                      fcgi_close(req, 1, 1);
01060               }
01061        }
01062 }
01063 
01064 static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type)
01065 {
01066        req->out_hdr = (fcgi_header*) req->out_pos;
01067        req->out_hdr->type = type;
01068        req->out_pos += sizeof(fcgi_header);
01069        return req->out_hdr;
01070 }
01071 
01072 static inline void close_packet(fcgi_request *req)
01073 {
01074        if (req->out_hdr) {
01075               int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header));
01076 
01077               req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len);
01078               req->out_hdr = NULL;
01079        }
01080 }
01081 
01082 int fcgi_flush(fcgi_request *req, int close)
01083 {
01084        int len;
01085 
01086        close_packet(req);
01087 
01088        len = req->out_pos - req->out_buf;
01089 
01090        if (close) {
01091               fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos);
01092 
01093               fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request));
01094               rec->body.appStatusB3 = 0;
01095               rec->body.appStatusB2 = 0;
01096               rec->body.appStatusB1 = 0;
01097               rec->body.appStatusB0 = 0;
01098               rec->body.protocolStatus = FCGI_REQUEST_COMPLETE;
01099               len += sizeof(fcgi_end_request_rec);
01100        }
01101 
01102        if (safe_write(req, req->out_buf, len) != len) {
01103               req->keep = 0;
01104               return 0;
01105        }
01106 
01107        req->out_pos = req->out_buf;
01108        return 1;
01109 }
01110 
01111 int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len)
01112 {
01113        int limit, rest;
01114 
01115        if (len <= 0) {
01116               return 0;
01117        }
01118 
01119        if (req->out_hdr && req->out_hdr->type != type) {
01120               close_packet(req);
01121        }
01122 #if 0
01123        /* Unoptimized, but clear version */
01124        rest = len;
01125        while (rest > 0) {
01126               limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
01127 
01128               if (!req->out_hdr) {
01129                      if (limit < sizeof(fcgi_header)) {
01130                             if (!fcgi_flush(req, 0)) {
01131                                    return -1;
01132                             }
01133                      }
01134                      open_packet(req, type);
01135               }
01136               limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
01137               if (rest < limit) {
01138                      memcpy(req->out_pos, str, rest);
01139                      req->out_pos += rest;
01140                      return len;
01141               } else {
01142                      memcpy(req->out_pos, str, limit);
01143                      req->out_pos += limit;
01144                      rest -= limit;
01145                      str += limit;
01146                      if (!fcgi_flush(req, 0)) {
01147                             return -1;
01148                      }
01149               }
01150        }
01151 #else
01152        /* Optimized version */
01153        limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
01154        if (!req->out_hdr) {
01155               limit -= sizeof(fcgi_header);
01156               if (limit < 0) limit = 0;
01157        }
01158 
01159        if (len < limit) {
01160               if (!req->out_hdr) {
01161                      open_packet(req, type);
01162               }
01163               memcpy(req->out_pos, str, len);
01164               req->out_pos += len;
01165        } else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) {
01166               if (!req->out_hdr) {
01167                      open_packet(req, type);
01168               }
01169               if (limit > 0) {
01170                      memcpy(req->out_pos, str, limit);
01171                      req->out_pos += limit;
01172               }
01173               if (!fcgi_flush(req, 0)) {
01174                      return -1;
01175               }
01176               if (len > limit) {
01177                      open_packet(req, type);
01178                      memcpy(req->out_pos, str + limit, len - limit);
01179                      req->out_pos += len - limit;
01180               }
01181        } else {
01182               int pos = 0;
01183               int pad;
01184 
01185               close_packet(req);
01186               while ((len - pos) > 0xffff) {
01187                      open_packet(req, type);
01188                      fcgi_make_header(req->out_hdr, type, req->id, 0xfff8);
01189                      req->out_hdr = NULL;
01190                      if (!fcgi_flush(req, 0)) {
01191                             return -1;
01192                      }
01193                      if (safe_write(req, str + pos, 0xfff8) != 0xfff8) {
01194                             req->keep = 0;
01195                             return -1;
01196                      }
01197                      pos += 0xfff8;
01198               }             
01199               
01200               pad = (((len - pos) + 7) & ~7) - (len - pos);
01201               rest = pad ? 8 - pad : 0;
01202 
01203               open_packet(req, type);
01204               fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest);
01205               req->out_hdr = NULL;
01206               if (!fcgi_flush(req, 0)) {
01207                      return -1;
01208               }
01209               if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) {
01210                      req->keep = 0;
01211                      return -1;
01212               }
01213               if (pad) {
01214                      open_packet(req, type);
01215                      memcpy(req->out_pos, str + len - rest,  rest);
01216                      req->out_pos += rest;
01217               }
01218        }
01219 #endif
01220        return len;
01221 }
01222 
01223 int fcgi_finish_request(fcgi_request *req, int force_close)
01224 {
01225        int ret = 1;
01226 
01227        if (req->fd >= 0) {
01228               if (!req->closed) {
01229                      ret = fcgi_flush(req, 1);
01230                      req->closed = 1;
01231               }
01232               fcgi_close(req, force_close, 1);
01233        }
01234        return ret;
01235 }
01236 
01237 char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
01238 {
01239        char **val;
01240 
01241        if (!req) return NULL;
01242 
01243        if (zend_hash_find(req->env, (char*)var, var_len+1, (void**)&val) == SUCCESS) {
01244               return *val;
01245        }
01246        return NULL;
01247 }
01248 
01249 char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
01250 {
01251        if (var && req) {
01252               if (val == NULL) {
01253                      zend_hash_del(req->env, var, var_len+1);
01254               } else {
01255                      char **ret;
01256 
01257                      val = estrdup(val);
01258                      if (zend_hash_update(req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
01259                             return *ret;
01260                      }
01261               }
01262        }
01263        return NULL;
01264 }
01265 
01266 #ifdef _WIN32
01267 void fcgi_impersonate(void)
01268 {
01269        char *os_name;
01270 
01271        os_name = getenv("OS");
01272        if (os_name && stricmp(os_name, "Windows_NT") == 0) {
01273               is_impersonate = 1;
01274        }
01275 }
01276 #endif
01277 
01278 void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len)
01279 {
01280        zval * zvalue;
01281        zvalue = pemalloc(sizeof(*zvalue), 1);
01282        Z_TYPE_P(zvalue) = IS_STRING;
01283        Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1);
01284        Z_STRLEN_P(zvalue) = value_len;
01285        zend_hash_add(&fcgi_mgmt_vars, name, name_len + 1, &zvalue, sizeof(zvalue), NULL);
01286 }
01287 
01288 void fcgi_free_mgmt_var_cb(void * ptr)
01289 {
01290        zval ** var = (zval **)ptr;
01291        pefree(Z_STRVAL_PP(var), 1);
01292        pefree(*var, 1);
01293 }
01294 
01295 /*
01296  * Local variables:
01297  * tab-width: 4
01298  * c-basic-offset: 4
01299  * End:
01300  * vim600: sw=4 ts=4 fdm=marker
01301  * vim<600: sw=4 ts=4
01302  */