"Fossies" - the Fresh Open Source Software Archive

Member "mod_fastcgi-2.4.7-0910052141/fcgi_protocol.c" (10 Apr 2012, 20180 Bytes) of package /linux/www/apache_httpd_modules/old/mod_fastcgi-2.4.7-0910052141.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "fcgi_protocol.c": 2.4.6_vs_2.4.7-0910052141.

    1 /*
    2  * $Id: fcgi_protocol.c,v 1.27 2008/09/23 14:48:13 robs Exp $
    3  */
    4 
    5 #include "fcgi.h"
    6 #include "fcgi_protocol.h"
    7 
    8 #ifdef APACHE2
    9 #include "apr_lib.h"
   10 #endif
   11 
   12 #ifdef WIN32
   13 #pragma warning( disable : 4706)
   14 #endif
   15 
   16  /*******************************************************************************
   17  * Build and queue a FastCGI message header.  It is the caller's
   18  * responsibility to make sure that there's enough space in the buffer, and
   19  * that the data bytes (specified by 'len') are queued immediately following
   20  * this header.
   21  */
   22 static void queue_header(fcgi_request *fr, unsigned char type, unsigned int len)
   23 {
   24     FCGI_Header header;
   25 
   26     ASSERT(type > 0);
   27     ASSERT(type <= FCGI_MAXTYPE);
   28     ASSERT(len <= 0xffff);
   29     ASSERT(BufferFree(fr->serverOutputBuffer) >= sizeof(FCGI_Header));
   30 
   31     /* Assemble and queue the packet header. */
   32     header.version = FCGI_VERSION;
   33     header.type = type;
   34     header.requestIdB1 = (unsigned char) (fr->requestId >> 8);
   35     header.requestIdB0 = (unsigned char) fr->requestId;
   36     header.contentLengthB1 = (unsigned char) (len / 256);  /* MSB */
   37     header.contentLengthB0 = (unsigned char) (len % 256);  /* LSB */
   38     header.paddingLength = 0;
   39     header.reserved = 0;
   40     fcgi_buf_add_block(fr->serverOutputBuffer, (char *) &header, sizeof(FCGI_Header));
   41 }
   42 
   43 /*******************************************************************************
   44  * Build a FCGI_BeginRequest message body.
   45  */
   46 static void build_begin_request(unsigned int role, unsigned char keepConnection,
   47         FCGI_BeginRequestBody *body)
   48 {
   49     ASSERT((role >> 16) == 0);
   50     body->roleB1 = (unsigned char) (role >>  8);
   51     body->roleB0 = (unsigned char) role;
   52     body->flags = (unsigned char) ((keepConnection) ? FCGI_KEEP_CONN : 0);
   53     memset(body->reserved, 0, sizeof(body->reserved));
   54 }
   55 
   56 /*******************************************************************************
   57  * Build and queue a FastCGI "Begin Request" message.
   58  */
   59 void fcgi_protocol_queue_begin_request(fcgi_request *fr)
   60 {
   61     FCGI_BeginRequestBody body;
   62     int bodySize = sizeof(FCGI_BeginRequestBody);
   63 
   64     /* We should be the first ones to use this buffer */
   65     ASSERT(BufferLength(fr->serverOutputBuffer) == 0);
   66 
   67     build_begin_request(fr->role, FALSE, &body);
   68     queue_header(fr, FCGI_BEGIN_REQUEST, bodySize);
   69     fcgi_buf_add_block(fr->serverOutputBuffer, (char *) &body, bodySize);
   70 }
   71 
   72 /*******************************************************************************
   73  * Build a FastCGI name-value pair (env) header.
   74  */
   75 static void build_env_header(int nameLen, int valueLen,
   76         unsigned char *headerBuffPtr, int *headerLenPtr)
   77 {
   78     unsigned char *startHeaderBuffPtr = headerBuffPtr;
   79 
   80     ASSERT(nameLen >= 0);
   81 
   82     if (nameLen < 0x80) {
   83         *headerBuffPtr++ = (unsigned char) nameLen;
   84     } else {
   85         *headerBuffPtr++ = (unsigned char) ((nameLen >> 24) | 0x80);
   86         *headerBuffPtr++ = (unsigned char) (nameLen >> 16);
   87         *headerBuffPtr++ = (unsigned char) (nameLen >> 8);
   88         *headerBuffPtr++ = (unsigned char) nameLen;
   89     }
   90 
   91     ASSERT(valueLen >= 0);
   92 
   93     if (valueLen < 0x80) {
   94         *headerBuffPtr++ = (unsigned char) valueLen;
   95     } else {
   96         *headerBuffPtr++ = (unsigned char) ((valueLen >> 24) | 0x80);
   97         *headerBuffPtr++ = (unsigned char) (valueLen >> 16);
   98         *headerBuffPtr++ = (unsigned char) (valueLen >> 8);
   99         *headerBuffPtr++ = (unsigned char) valueLen;
  100     }
  101     *headerLenPtr = headerBuffPtr - startHeaderBuffPtr;
  102 }
  103 
  104 /* A static fn stolen from Apache's util_script.c...
  105  * Obtain the Request-URI from the original request-line, returning
  106  * a new string from the request pool containing the URI or "".
  107  */
  108 static char *apache_original_uri(request_rec *r)
  109 {
  110     char *first, *last;
  111 
  112     if (r->the_request == NULL)
  113         return (char *) ap_pcalloc(r->pool, 1);
  114 
  115     first = r->the_request; /* use the request-line */
  116 
  117     while (*first && !ap_isspace(*first))
  118         ++first;            /* skip over the method */
  119 
  120     while (ap_isspace(*first))
  121         ++first;            /* and the space(s) */
  122 
  123     last = first;
  124     while (*last && !ap_isspace(*last))
  125         ++last;             /* end at next whitespace */
  126 
  127     return ap_pstrndup(r->pool, first, last - first);
  128 }
  129 
  130 /* Based on Apache's ap_add_cgi_vars() in util_script.c.
  131  * Apache's spins in sub_req_lookup_uri() trying to setup PATH_TRANSLATED,
  132  * so we just don't do that part.
  133  */
  134 static void add_auth_cgi_vars(request_rec *r, const int compat)
  135 {
  136     table *e = r->subprocess_env;
  137 
  138     ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
  139     ap_table_setn(e, "SERVER_PROTOCOL", r->protocol);
  140     ap_table_setn(e, "REQUEST_METHOD", r->method);
  141     ap_table_setn(e, "QUERY_STRING", r->args ? r->args : "");
  142     ap_table_setn(e, "REQUEST_URI", apache_original_uri(r));
  143 
  144     /* The FastCGI spec precludes sending of CONTENT_LENGTH, PATH_INFO,
  145      * PATH_TRANSLATED, and SCRIPT_NAME (for some reason?).  PATH_TRANSLATED we
  146      * don't have, its the variable that causes Apache to break trying to set
  147      * up (and thus the reason this fn exists vs. using ap_add_cgi_vars()). */
  148     if (compat) {
  149         ap_table_unset(e, "CONTENT_LENGTH");
  150         return;
  151     }
  152 
  153     /* Note that the code below special-cases scripts run from includes,
  154      * because it "knows" that the sub_request has been hacked to have the
  155      * args and path_info of the original request, and not any that may have
  156      * come with the script URI in the include command.  Ugh. */
  157     if (!strcmp(r->protocol, "INCLUDED")) {
  158         ap_table_setn(e, "SCRIPT_NAME", r->uri);
  159         if (r->path_info && *r->path_info)
  160             ap_table_setn(e, "PATH_INFO", r->path_info);
  161     }
  162     else if (!r->path_info || !*r->path_info)
  163         ap_table_setn(e, "SCRIPT_NAME", r->uri);
  164     else {
  165         int path_info_start = ap_find_path_info(r->uri, r->path_info);
  166 
  167         ap_table_setn(e, "SCRIPT_NAME", ap_pstrndup(r->pool, r->uri, path_info_start));
  168         ap_table_setn(e, "PATH_INFO", r->path_info);
  169     }
  170 }
  171 
  172 /* copied from util_script.c */
  173 static char *http2env(pool *a, const char *w)
  174 {
  175     char *res = (char *) ap_palloc(a, sizeof("HTTP_") + strlen(w));
  176     char *cp = res;
  177     char c;
  178 
  179     *cp++ = 'H';
  180     *cp++ = 'T';
  181     *cp++ = 'T';
  182     *cp++ = 'P';
  183     *cp++ = '_';
  184 
  185     while ((c = *w++) != 0) {
  186         if (!ap_isalnum(c)) {
  187             *cp++ = '_';
  188         }
  189         else {
  190             *cp++ = (char) ap_toupper(c);
  191         }
  192     }
  193     *cp = 0;
  194 
  195     return res;
  196 }
  197 
  198 static void add_pass_header_vars(fcgi_request *fr)
  199 {
  200     const array_header *ph = fr->dynamic ? dynamic_pass_headers : fr->fs->pass_headers;
  201 
  202     if (ph) {
  203         const char **elt = (const char **)ph->elts;
  204         int i = ph->nelts;
  205 
  206         for ( ; i; --i, ++elt) {
  207             const char *val = ap_table_get(fr->r->headers_in, *elt);
  208             if (val) {
  209                 const char *key = *elt;
  210 #ifndef USE_BROKEN_PASS_HEADER
  211                 key = http2env(fr->r->pool, key);
  212 #endif              
  213                 ap_table_setn(fr->r->subprocess_env, key, val);
  214             }
  215         }
  216     }
  217 }
  218 
  219 /*******************************************************************************
  220  * Build and queue the environment name-value pairs.  Returns TRUE if the
  221  * complete ENV was buffered, FALSE otherwise.  Note: envp is updated to
  222  * reflect the current position in the ENV.
  223  */
  224 int fcgi_protocol_queue_env(request_rec *r, fcgi_request *fr, env_status *env)
  225 {
  226     int charCount;
  227 
  228     if (env->envp == NULL) {
  229         ap_add_common_vars(r);
  230         add_pass_header_vars(fr);
  231 
  232         if (fr->role == FCGI_RESPONDER)
  233             ap_add_cgi_vars(r);
  234         else
  235             add_auth_cgi_vars(r, fr->auth_compat);
  236 
  237         env->envp = ap_create_environment(r->pool, r->subprocess_env);
  238         env->pass = PREP;
  239     }
  240 
  241     while (*env->envp) {
  242         switch (env->pass) 
  243         {
  244         case PREP:
  245             env->equalPtr = strchr(*env->envp, '=');
  246             ASSERT(env->equalPtr != NULL);
  247             env->nameLen = env->equalPtr - *env->envp;
  248             env->valueLen = strlen(++env->equalPtr);
  249             build_env_header(env->nameLen, env->valueLen, env->headerBuff, &env->headerLen);
  250             env->totalLen = env->headerLen + env->nameLen + env->valueLen;
  251             env->pass = HEADER;
  252             /* drop through */
  253 
  254         case HEADER:
  255             if (BufferFree(fr->serverOutputBuffer) < (int)(sizeof(FCGI_Header) + env->headerLen)) {
  256                 return (FALSE);
  257             }
  258             queue_header(fr, FCGI_PARAMS, env->totalLen);
  259             fcgi_buf_add_block(fr->serverOutputBuffer, (char *)env->headerBuff, env->headerLen);
  260             env->pass = NAME;
  261             /* drop through */
  262 
  263         case NAME:
  264             charCount = fcgi_buf_add_block(fr->serverOutputBuffer, *env->envp, env->nameLen);
  265             if (charCount != env->nameLen) {
  266                 *env->envp += charCount;
  267                 env->nameLen -= charCount;
  268                 return (FALSE);
  269             }
  270             env->pass = VALUE;
  271             /* drop through */
  272 
  273         case VALUE:
  274             charCount = fcgi_buf_add_block(fr->serverOutputBuffer, env->equalPtr, env->valueLen);
  275             if (charCount != env->valueLen) {
  276                 env->equalPtr += charCount;
  277                 env->valueLen -= charCount;
  278                 return (FALSE);
  279             }
  280             env->pass = PREP;
  281         }
  282         ++env->envp;
  283     }
  284 
  285     if (BufferFree(fr->serverOutputBuffer) < sizeof(FCGI_Header)) {
  286         return(FALSE);
  287     }
  288     queue_header(fr, FCGI_PARAMS, 0);
  289     return(TRUE);
  290 }
  291 
  292 /*******************************************************************************
  293  * Queue data from the client input buffer to the FastCGI server output
  294  * buffer (encapsulating the data in FastCGI protocol messages).
  295  */
  296 void fcgi_protocol_queue_client_buffer(fcgi_request *fr)
  297 {
  298     int movelen;
  299     int in_len, out_free;
  300 
  301     if (fr->eofSent)
  302         return;
  303 
  304     /*
  305      * If there's some client data and room for at least one byte
  306      * of data in the output buffer (after protocol overhead), then
  307      * move some data to the output buffer.
  308      */
  309     in_len = BufferLength(fr->clientInputBuffer);
  310     out_free = max(0, BufferFree(fr->serverOutputBuffer) - sizeof(FCGI_Header));
  311     movelen = min(in_len, out_free);
  312     if (movelen > 0) {
  313         queue_header(fr, FCGI_STDIN, movelen);
  314         fcgi_buf_get_to_buf(fr->serverOutputBuffer, fr->clientInputBuffer, movelen);
  315     }
  316 
  317     /*
  318      * If all the client data has been sent, and there's room
  319      * in the output buffer, indicate EOF.
  320      */
  321     if (movelen == in_len && fr->expectingClientContent <= 0
  322             && BufferFree(fr->serverOutputBuffer) >= sizeof(FCGI_Header))
  323     {
  324         queue_header(fr, FCGI_STDIN, 0);
  325         fr->eofSent = TRUE;
  326     }
  327 }
  328 
  329 /*******************************************************************************
  330  * Read FastCGI protocol messages from the FastCGI server input buffer into
  331  * fr->header when parsing headers, to fr->fs_stderr when reading stderr data,
  332  * or to the client output buffer otherwises.
  333  */
  334 int fcgi_protocol_dequeue(pool *p, fcgi_request *fr)
  335 {
  336     FCGI_Header header;
  337     int len;
  338 
  339     while (BufferLength(fr->serverInputBuffer) > 0) {
  340         /*
  341          * State #1:  looking for the next complete packet header.
  342          */
  343         if (fr->gotHeader == FALSE) {
  344             if (BufferLength(fr->serverInputBuffer) < sizeof(FCGI_Header)) {
  345                 return OK;
  346             }
  347             fcgi_buf_get_to_block(fr->serverInputBuffer, (char *) &header,
  348                     sizeof(FCGI_Header));
  349             /*
  350              * XXX: Better handling of packets with other version numbers
  351              * and other packet problems.
  352              */
  353             if (header.version != FCGI_VERSION) {
  354                 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
  355                     "FastCGI: comm with server \"%s\" aborted: protocol error: invalid version: %d != FCGI_VERSION(%d)",
  356                     fr->fs_path, header.version, FCGI_VERSION);
  357                 return HTTP_INTERNAL_SERVER_ERROR;
  358             }
  359             if (header.type > FCGI_MAXTYPE) {
  360                 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
  361                     "FastCGI: comm with server \"%s\" aborted: protocol error: invalid type: %d > FCGI_MAXTYPE(%d)",
  362                     fr->fs_path, header.type, FCGI_MAXTYPE);
  363                 return HTTP_INTERNAL_SERVER_ERROR;
  364             }
  365 
  366             fr->packetType = header.type;
  367             fr->dataLen = (header.contentLengthB1 << 8)
  368                     + header.contentLengthB0;
  369             fr->gotHeader = TRUE;
  370             fr->paddingLen = header.paddingLength;
  371         }
  372 
  373         /*
  374          * State #2:  got a header, and processing packet bytes.
  375          */
  376         len = min(fr->dataLen, BufferLength(fr->serverInputBuffer));
  377         ASSERT(len >= 0);
  378         switch (fr->packetType) {
  379             case FCGI_STDOUT:
  380                 if (len > 0) {
  381                     switch(fr->parseHeader) {
  382                         case SCAN_CGI_READING_HEADERS:
  383                             fcgi_buf_get_to_array(fr->serverInputBuffer, fr->header, len);
  384                             break;
  385                         case SCAN_CGI_FINISHED:
  386                             len = min(BufferFree(fr->clientOutputBuffer), len);
  387                             if (len > 0) {
  388                                 fcgi_buf_get_to_buf(fr->clientOutputBuffer, fr->serverInputBuffer, len);
  389                             } else {
  390                                 return OK;
  391                             }
  392                             break;
  393                         default:
  394                             /* Toss data on the floor */
  395                             fcgi_buf_removed(fr->serverInputBuffer, len);
  396                             break;
  397                     }
  398                     fr->dataLen -= len;
  399                 }
  400                 break;
  401 
  402             case FCGI_STDERR:
  403 
  404                 if (fr->fs_stderr == NULL)
  405                 {
  406                     fr->fs_stderr = ap_palloc(p, FCGI_SERVER_MAX_STDERR_LINE_LEN + 1);
  407                 }
  408 
  409                 /* We're gonna consume all thats here */
  410                 fr->dataLen -= len;
  411 
  412                 while (len > 0) 
  413                 {
  414                     char *null, *end, *start = fr->fs_stderr;
  415 
  416                     /* Get as much as will fit in the buffer */
  417                     int get_len = min(len, FCGI_SERVER_MAX_STDERR_LINE_LEN - fr->fs_stderr_len);
  418                     fcgi_buf_get_to_block(fr->serverInputBuffer, start + fr->fs_stderr_len, get_len);
  419                     len -= get_len;
  420                     fr->fs_stderr_len += get_len;
  421                     *(start + fr->fs_stderr_len) = '\0';
  422 
  423                     /* Disallow nulls, we could be nicer but this is the motivator */
  424                     while ((null = memchr(start, '\0', fr->fs_stderr_len)))
  425                     {
  426                         int discard = ++null - start;
  427                         ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
  428                             "FastCGI: server \"%s\" sent a null character in the stderr stream!?, "
  429                             "discarding %d characters of stderr", fr->fs_path, discard);
  430                         start = null;
  431                         fr->fs_stderr_len -= discard;
  432                     } 
  433 
  434                     /* Print as much as possible  */
  435                     while ((end = strpbrk(start, "\r\n"))) 
  436                     {
  437                         if (start != end)
  438                         {
  439                             *end = '\0';
  440                             ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, 
  441                                 "FastCGI: server \"%s\" stderr: %s", fr->fs_path, start);
  442                         }
  443                         end++;
  444                         end += strspn(end, "\r\n");
  445                         fr->fs_stderr_len -= (end - start);
  446                         start = end;
  447                     }
  448 
  449                     if (fr->fs_stderr_len) 
  450                     {
  451                         if (start != fr->fs_stderr)
  452                         {
  453                             /* Move leftovers down */
  454                             memmove(fr->fs_stderr, start, fr->fs_stderr_len);
  455                         }
  456                         else if (fr->fs_stderr_len == FCGI_SERVER_MAX_STDERR_LINE_LEN)
  457                         {
  458                             /* Full buffer, dump it and complain */
  459                             ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, 
  460                                "FastCGI: server \"%s\" stderr: %s", fr->fs_path, fr->fs_stderr);
  461                             ap_log_rerror(FCGI_LOG_WARN_NOERRNO, fr->r,
  462                                 "FastCGI: too much stderr received from server \"%s\", "
  463                                 "increase FCGI_SERVER_MAX_STDERR_LINE_LEN (%d) and rebuild "
  464                                 "or use \"\\n\" to terminate lines",
  465                                 fr->fs_path, FCGI_SERVER_MAX_STDERR_LINE_LEN);
  466                             fr->fs_stderr_len = 0;
  467                         }
  468                     }
  469                 }
  470                 break;
  471 
  472             case FCGI_END_REQUEST:
  473                 if (!fr->readingEndRequestBody) {
  474                     if (fr->dataLen != sizeof(FCGI_EndRequestBody)) {
  475                         ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
  476                             "FastCGI: comm with server \"%s\" aborted: protocol error: "
  477                             "invalid FCGI_END_REQUEST size: "
  478                             "%d != sizeof(FCGI_EndRequestBody)(%d)",
  479                             fr->fs_path, fr->dataLen, sizeof(FCGI_EndRequestBody));
  480                         return HTTP_INTERNAL_SERVER_ERROR;
  481                     }
  482                     fr->readingEndRequestBody = TRUE;
  483                 }
  484                 if (len>0) {
  485                     fcgi_buf_get_to_buf(fr->erBufPtr, fr->serverInputBuffer, len);
  486                     fr->dataLen -= len;
  487                 }
  488                 if (fr->dataLen == 0) {
  489                     FCGI_EndRequestBody *erBody = &fr->endRequestBody;
  490                     fcgi_buf_get_to_block(
  491                         fr->erBufPtr, (char *) &fr->endRequestBody,
  492                         sizeof(FCGI_EndRequestBody));
  493                     if (erBody->protocolStatus != FCGI_REQUEST_COMPLETE) {
  494                         /*
  495                          * XXX: What to do with FCGI_OVERLOADED?
  496                          */
  497                         ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
  498                             "FastCGI: comm with server \"%s\" aborted: protocol error: invalid FCGI_END_REQUEST status: "
  499                             "%d != FCGI_REQUEST_COMPLETE(%d)", fr->fs_path,
  500                             erBody->protocolStatus, FCGI_REQUEST_COMPLETE);
  501                         return HTTP_INTERNAL_SERVER_ERROR;
  502                     }
  503                     fr->exitStatus = (erBody->appStatusB3 << 24)
  504                         + (erBody->appStatusB2 << 16)
  505                         + (erBody->appStatusB1 <<  8)
  506                         + (erBody->appStatusB0 );
  507                     fr->exitStatusSet = TRUE;
  508                     fr->readingEndRequestBody = FALSE;
  509                 }
  510                 break;
  511             case FCGI_GET_VALUES_RESULT:
  512                 /* XXX coming soon */
  513             case FCGI_UNKNOWN_TYPE:
  514                 /* XXX coming soon */
  515 
  516             /*
  517              * XXX Ignore unknown packet types from the FastCGI server.
  518              */
  519             default:
  520                 fcgi_buf_toss(fr->serverInputBuffer, len);
  521                 fr->dataLen -= len;
  522                 break;
  523         } /* switch */
  524 
  525         /*
  526          * Discard padding, then start looking for
  527          * the next header.
  528          */
  529         if (fr->dataLen == 0) {
  530             if (fr->paddingLen > 0) {
  531                 len = min(fr->paddingLen,
  532                         BufferLength(fr->serverInputBuffer));
  533                 fcgi_buf_toss(fr->serverInputBuffer, len);
  534                 fr->paddingLen -= len;
  535             }
  536             if (fr->paddingLen == 0) {
  537                 fr->gotHeader = FALSE;
  538             }
  539         }
  540     } /* while */
  541     return OK;
  542 }