diff options
Diffstat (limited to 'modules/proxy/mod_proxy_fcgi.c')
-rw-r--r-- | modules/proxy/mod_proxy_fcgi.c | 446 |
1 files changed, 133 insertions, 313 deletions
diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c index 0f844163..ee702900 100644 --- a/modules/proxy/mod_proxy_fcgi.c +++ b/modules/proxy/mod_proxy_fcgi.c @@ -15,59 +15,12 @@ */ #include "mod_proxy.h" -#include "fcgi_protocol.h" +#include "util_fcgi.h" #include "util_script.h" module AP_MODULE_DECLARE_DATA proxy_fcgi_module; /* - * The below 3 functions serve to map the FCGI structs - * back and forth between an 8 byte array. We do this to avoid - * any potential padding issues when we send or read these - * structures. - * - * NOTE: These have specific internal knowledge of the - * layout of the fcgi_header and fcgi_begin_request_body - * structs! - */ -static void fcgi_header_to_array(fcgi_header *h, unsigned char a[]) -{ - a[FCGI_HDR_VERSION_OFFSET] = h->version; - a[FCGI_HDR_TYPE_OFFSET] = h->type; - a[FCGI_HDR_REQUEST_ID_B1_OFFSET] = h->requestIdB1; - a[FCGI_HDR_REQUEST_ID_B0_OFFSET] = h->requestIdB0; - a[FCGI_HDR_CONTENT_LEN_B1_OFFSET] = h->contentLengthB1; - a[FCGI_HDR_CONTENT_LEN_B0_OFFSET] = h->contentLengthB0; - a[FCGI_HDR_PADDING_LEN_OFFSET] = h->paddingLength; - a[FCGI_HDR_RESERVED_OFFSET] = h->reserved; -} - -static void fcgi_header_from_array(fcgi_header *h, unsigned char a[]) -{ - h->version = a[FCGI_HDR_VERSION_OFFSET]; - h->type = a[FCGI_HDR_TYPE_OFFSET]; - h->requestIdB1 = a[FCGI_HDR_REQUEST_ID_B1_OFFSET]; - h->requestIdB0 = a[FCGI_HDR_REQUEST_ID_B0_OFFSET]; - h->contentLengthB1 = a[FCGI_HDR_CONTENT_LEN_B1_OFFSET]; - h->contentLengthB0 = a[FCGI_HDR_CONTENT_LEN_B0_OFFSET]; - h->paddingLength = a[FCGI_HDR_PADDING_LEN_OFFSET]; - h->reserved = a[FCGI_HDR_RESERVED_OFFSET]; -} - -static void fcgi_begin_request_body_to_array(fcgi_begin_request_body *h, - unsigned char a[]) -{ - a[FCGI_BRB_ROLEB1_OFFSET] = h->roleB1; - a[FCGI_BRB_ROLEB0_OFFSET] = h->roleB0; - a[FCGI_BRB_FLAGS_OFFSET] = h->flags; - a[FCGI_BRB_RESERVED0_OFFSET] = h->reserved[0]; - a[FCGI_BRB_RESERVED1_OFFSET] = h->reserved[1]; - a[FCGI_BRB_RESERVED2_OFFSET] = h->reserved[2]; - a[FCGI_BRB_RESERVED3_OFFSET] = h->reserved[3]; - a[FCGI_BRB_RESERVED4_OFFSET] = h->reserved[4]; -} - -/* * Canonicalise http-like URLs. * scheme is the scheme for the URL * url is the URL starting with the first '/' @@ -129,33 +82,6 @@ static int proxy_fcgi_canon(request_rec *r, char *url) return OK; } -/* - * Fill in a fastcgi request header with the following type, request id, - * content length, and padding length. - * - * The header array must be at least FCGI_HEADER_LEN bytes long. - */ -static void fill_in_header(fcgi_header *header, - unsigned char type, - apr_uint16_t request_id, - apr_uint16_t content_len, - unsigned char padding_len) -{ - header->version = FCGI_VERSION; - - header->type = type; - - header->requestIdB1 = ((request_id >> 8) & 0xff); - header->requestIdB0 = ((request_id) & 0xff); - - header->contentLengthB1 = ((content_len >> 8) & 0xff); - header->contentLengthB0 = ((content_len) & 0xff); - - header->paddingLength = padding_len; - - header->reserved = 0; -} - /* Wrapper for apr_socket_sendv that handles updating the worker stats. */ static apr_status_t send_data(proxy_conn_rec *conn, struct iovec *vec, @@ -234,28 +160,43 @@ static apr_status_t get_data(proxy_conn_rec *conn, return rv; } -static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id) +static apr_status_t get_data_full(proxy_conn_rec *conn, + char *buffer, + apr_size_t buflen) +{ + apr_size_t readlen; + apr_size_t cumulative_len = 0; + apr_status_t rv; + + do { + readlen = buflen - cumulative_len; + rv = get_data(conn, buffer + cumulative_len, &readlen); + if (rv != APR_SUCCESS) { + return rv; + } + cumulative_len += readlen; + } while (cumulative_len < buflen); + + return APR_SUCCESS; +} + +static apr_status_t send_begin_request(proxy_conn_rec *conn, + apr_uint16_t request_id) { struct iovec vec[2]; - fcgi_header header; - unsigned char farray[FCGI_HEADER_LEN]; - fcgi_begin_request_body brb; - unsigned char abrb[FCGI_HEADER_LEN]; + ap_fcgi_header header; + unsigned char farray[AP_FCGI_HEADER_LEN]; + ap_fcgi_begin_request_body brb; + unsigned char abrb[AP_FCGI_HEADER_LEN]; apr_size_t len; - fill_in_header(&header, FCGI_BEGIN_REQUEST, request_id, sizeof(abrb), 0); + ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id, + sizeof(abrb), 0); - brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff); - brb.roleB0 = ((FCGI_RESPONDER) & 0xff); - brb.flags = FCGI_KEEP_CONN; - brb.reserved[0] = 0; - brb.reserved[1] = 0; - brb.reserved[2] = 0; - brb.reserved[3] = 0; - brb.reserved[4] = 0; + ap_fcgi_fill_in_request_body(&brb, AP_FCGI_RESPONDER, AP_FCGI_KEEP_CONN); - fcgi_header_to_array(&header, farray); - fcgi_begin_request_body_to_array(&brb, abrb); + ap_fcgi_header_to_array(&header, farray); + ap_fcgi_begin_request_body_to_array(&brb, abrb); vec[0].iov_base = (void *)farray; vec[0].iov_len = sizeof(farray); @@ -266,26 +207,24 @@ static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id) } static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r, - int request_id) + apr_pool_t *temp_pool, + apr_uint16_t request_id) { const apr_array_header_t *envarr; const apr_table_entry_t *elts; struct iovec vec[2]; - fcgi_header header; - unsigned char farray[FCGI_HEADER_LEN]; - apr_size_t bodylen, envlen; - char *body, *itr; + ap_fcgi_header header; + unsigned char farray[AP_FCGI_HEADER_LEN]; + char *body; apr_status_t rv; - apr_size_t len; - int i, numenv; + apr_size_t avail_len, len, required_len; + int next_elem, starting_elem; ap_add_common_vars(r); ap_add_cgi_vars(r); /* XXX are there any FastCGI specific env vars we need to send? */ - bodylen = envlen = 0; - /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in * the TZ value specially. We could use that, but it would mean * parsing the key/value pairs back OUT of the allocated env array, @@ -293,118 +232,75 @@ static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r, * place, which would suck. */ envarr = apr_table_elts(r->subprocess_env); - elts = (const apr_table_entry_t *) envarr->elts; - for (i = 0; i < envarr->nelts; ++i) { - apr_size_t keylen, vallen; - - if (! elts[i].key) { - continue; - } - - keylen = strlen(elts[i].key); - - if (keylen >> 7 == 0) { - envlen += 1; - } - else { - envlen += 4; - } - - envlen += keylen; - - vallen = strlen(elts[i].val); - #ifdef FCGI_DUMP_ENV_VARS - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01062) - "sending env var '%s' value '%s'", - elts[i].key, elts[i].val); -#endif - - if (vallen >> 7 == 0) { - envlen += 1; + { + int i; + + for (i = 0; i < envarr->nelts; ++i) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01062) + "sending env var '%s' value '%s'", + elts[i].key, elts[i].val); } - else { - envlen += 4; - } - - envlen += vallen; + } +#endif - /* The cast of bodylen is safe since FCGI_MAX_ENV_SIZE is for sure an int */ - if (envlen > FCGI_MAX_ENV_SIZE) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01063) - "truncating environment to %d bytes and %d elements", - (int)bodylen, i); + /* Send envvars over in as many FastCGI records as it takes, */ + next_elem = 0; /* starting with the first one */ + + avail_len = 16 * 1024; /* our limit per record, which could have been up + * to AP_FCGI_MAX_CONTENT_LEN + */ + + while (next_elem < envarr->nelts) { + starting_elem = next_elem; + required_len = ap_fcgi_encoded_env_len(r->subprocess_env, + avail_len, + &next_elem); + + if (!required_len) { + if (next_elem < envarr->nelts) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + APLOGNO(02536) "couldn't encode envvar '%s' in %" + APR_SIZE_T_FMT " bytes", + elts[next_elem].key, avail_len); + /* skip this envvar and continue */ + ++next_elem; + continue; + } + /* only an unused element at the end of the array */ break; } - bodylen = envlen; - } + body = apr_palloc(temp_pool, required_len); + rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len, + &starting_elem); + /* we pre-compute, so we can't run out of space */ + ap_assert(rv == APR_SUCCESS); + /* compute and encode must be in sync */ + ap_assert(starting_elem == next_elem); - numenv = i; + ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, + (apr_uint16_t)required_len, 0); + ap_fcgi_header_to_array(&header, farray); - body = apr_pcalloc(r->pool, bodylen); + vec[0].iov_base = (void *)farray; + vec[0].iov_len = sizeof(farray); + vec[1].iov_base = body; + vec[1].iov_len = required_len; - itr = body; + rv = send_data(conn, vec, 2, &len, 1); + apr_pool_clear(temp_pool); - for (i = 0; i < numenv; ++i) { - apr_size_t keylen, vallen; - - if (! elts[i].key) { - continue; - } - - keylen = strlen(elts[i].key); - - if (keylen >> 7 == 0) { - itr[0] = keylen & 0xff; - itr += 1; + if (rv) { + return rv; } - else { - itr[0] = ((keylen >> 24) & 0xff) | 0x80; - itr[1] = ((keylen >> 16) & 0xff); - itr[2] = ((keylen >> 8) & 0xff); - itr[3] = ((keylen) & 0xff); - itr += 4; - } - - vallen = strlen(elts[i].val); - - if (vallen >> 7 == 0) { - itr[0] = vallen & 0xff; - itr += 1; - } - else { - itr[0] = ((vallen >> 24) & 0xff) | 0x80; - itr[1] = ((vallen >> 16) & 0xff); - itr[2] = ((vallen >> 8) & 0xff); - itr[3] = ((vallen) & 0xff); - itr += 4; - } - - memcpy(itr, elts[i].key, keylen); - itr += keylen; - - memcpy(itr, elts[i].val, vallen); - itr += vallen; } - fill_in_header(&header, FCGI_PARAMS, request_id, bodylen, 0); - fcgi_header_to_array(&header, farray); - - vec[0].iov_base = (void *)farray; - vec[0].iov_len = sizeof(farray); - vec[1].iov_base = body; - vec[1].iov_len = bodylen; - - rv = send_data(conn, vec, 2, &len, 1); - if (rv) { - return rv; - } - - fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0); - fcgi_header_to_array(&header, farray); + /* Envvars sent, so say we're done */ + ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 0, 0); + ap_fcgi_header_to_array(&header, farray); vec[0].iov_base = (void *)farray; vec[0].iov_len = sizeof(farray); @@ -481,69 +377,9 @@ static int handle_headers(request_rec *r, return 0; } -static void dump_header_to_log(request_rec *r, unsigned char fheader[], - apr_size_t length) -{ -#ifdef FCGI_DUMP_HEADERS - apr_size_t posn = 0; - char asc_line[20]; - char hex_line[60]; - int i = 0; - - memset(asc_line, 0, sizeof(asc_line)); - memset(hex_line, 0, sizeof(hex_line)); - - while (posn < length) { - unsigned char c = fheader[posn]; - - if (i >= 20) { - i = 0; - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01064) - "HEADER: %s %s", asc_line, hex_line); - - memset(asc_line, 0, sizeof(asc_line)); - memset(hex_line, 0, sizeof(hex_line)); - } - - if (isprint(c)) { - asc_line[i] = c; - } - else { - asc_line[i] = '.'; - } - - if ((c >> 4) >= 10) { - hex_line[i * 3] = 'a' + ((c >> 4) - 10); - } - else { - hex_line[i * 3] = '0' + (c >> 4); - } - - if ((c & 0x0F) >= 10) { - hex_line[i * 3 + 1] = 'a' + ((c & 0x0F) - 10); - } - else { - hex_line[i * 3 + 1] = '0' + (c & 0xF); - } - - hex_line[i * 3 + 2] = ' '; - - i++; - posn++; - } - - if (i != 1) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01065) "HEADER: %s %s", - asc_line, hex_line); - } - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01066) "HEADER: -EOH-"); -#endif -} - static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, - request_rec *r, int request_id) + request_rec *r, apr_pool_t *setaside_pool, + apr_uint16_t request_id) { apr_bucket_brigade *ib, *ob; int seen_end_of_headers = 0, done = 0; @@ -551,14 +387,11 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, int script_error_status = HTTP_OK; conn_rec *c = r->connection; struct iovec vec[2]; - fcgi_header header; - unsigned char farray[FCGI_HEADER_LEN]; + ap_fcgi_header header; + unsigned char farray[AP_FCGI_HEADER_LEN]; apr_pollfd_t pfd; int header_state = HDR_STATE_READING_HEADERS; - apr_pool_t *setaside_pool; - - apr_pool_create(&setaside_pool, r->pool); - + pfd.desc_type = APR_POLL_SOCKET; pfd.desc.s = conn->sock; pfd.p = r->pool; @@ -568,15 +401,13 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, ob = apr_brigade_create(r->pool, c->bucket_alloc); while (! done) { - apr_interval_time_t timeout = conn->worker->s->timeout; + apr_interval_time_t timeout; apr_size_t len; int n; /* We need SOME kind of timeout here, or virtually anything will * cause timeout errors. */ - if (! conn->worker->s->timeout_set) { - timeout = apr_time_from_sec(30); - } + apr_socket_timeout_get(conn->sock, &timeout); rv = apr_poll(&pfd, 1, &n, timeout); if (rv != APR_SUCCESS) { @@ -613,9 +444,9 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, break; } - fill_in_header(&header, FCGI_STDIN, request_id, - (apr_uint16_t) writebuflen, 0); - fcgi_header_to_array(&header, farray); + ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, + (apr_uint16_t) writebuflen, 0); + ap_fcgi_header_to_array(&header, farray); vec[nvec].iov_base = (void *)farray; vec[nvec].iov_len = sizeof(farray); @@ -634,9 +465,10 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, if (last_stdin) { pfd.reqevents = APR_POLLIN; /* Done with input data */ - if (writebuflen) { /* empty FCGI_STDIN not already sent? */ - fill_in_header(&header, FCGI_STDIN, request_id, 0, 0); - fcgi_header_to_array(&header, farray); + if (writebuflen) { /* empty AP_FCGI_STDIN not already sent? */ + ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, + 0, 0); + ap_fcgi_header_to_array(&header, farray); vec[0].iov_base = (void *)farray; vec[0].iov_len = sizeof(farray); @@ -652,47 +484,37 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, * the headers, even if we fill the entire length in the recv. */ char readbuf[AP_IOBUFSIZE + 1]; apr_size_t readbuflen; - apr_size_t clen; - int rid, type; + apr_uint16_t clen, rid; apr_bucket *b; - char plen; + unsigned char plen; + unsigned char type, version; memset(readbuf, 0, sizeof(readbuf)); memset(farray, 0, sizeof(farray)); /* First, we grab the header... */ - readbuflen = FCGI_HEADER_LEN; - - rv = get_data(conn, (char *) farray, &readbuflen); + rv = get_data_full(conn, (char *) farray, AP_FCGI_HEADER_LEN); if (rv != APR_SUCCESS) { - break; - } - - dump_header_to_log(r, farray, readbuflen); - - if (readbuflen != FCGI_HEADER_LEN) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067) - "Failed to read entire header " - "got %" APR_SIZE_T_FMT " wanted %d", - readbuflen, FCGI_HEADER_LEN); - rv = APR_EINVAL; + "Failed to read FastCGI header"); break; } - fcgi_header_from_array(&header, farray); +#ifdef FCGI_DUMP_HEADERS + ap_log_rdata(APLOG_MARK, APLOG_DEBUG, r, "FastCGI header", + farray, AP_FCGI_HEADER_LEN, 0); +#endif + + ap_fcgi_header_fields_from_array(&version, &type, &rid, + &clen, &plen, farray); - if (header.version != FCGI_VERSION) { + if (version != AP_FCGI_VERSION_1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068) "Got bogus version %d", (int) header.version); rv = APR_EINVAL; break; } - type = header.type; - - rid = header.requestIdB1 << 8; - rid |= header.requestIdB0; - if (rid != request_id) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069) "Got bogus rid %d, expected %d", @@ -701,11 +523,6 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, break; } - clen = header.contentLengthB1 << 8; - clen |= header.contentLengthB0; - - plen = header.paddingLength; - recv_again: if (clen > sizeof(readbuf) - 1) { readbuflen = sizeof(readbuf) - 1; @@ -725,7 +542,7 @@ recv_again: } switch (type) { - case FCGI_STDOUT: + case AP_FCGI_STDOUT: if (clen != 0) { b = apr_bucket_transient_create(readbuf, readbuflen, @@ -825,7 +642,7 @@ recv_again: } break; - case FCGI_STDERR: + case AP_FCGI_STDERR: /* TODO: Should probably clean up this logging a bit... */ if (clen) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071) @@ -838,7 +655,7 @@ recv_again: } break; - case FCGI_END_REQUEST: + case AP_FCGI_END_REQUEST: done = 1; break; @@ -849,10 +666,10 @@ recv_again: } if (plen) { - readbuflen = plen; - - rv = get_data(conn, readbuf, &readbuflen); + rv = get_data_full(conn, readbuf, plen); if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + APLOGNO(02537) "Error occurred reading padding"); break; } } @@ -884,10 +701,11 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r, * multiple requests to the same FastCGI connection, but * we don't support that, and always use a value of '1' to * keep things simple. */ - int request_id = 1; + apr_uint16_t request_id = 1; apr_status_t rv; + apr_pool_t *temp_pool; - /* Step 1: Send FCGI_BEGIN_REQUEST */ + /* Step 1: Send AP_FCGI_BEGIN_REQUEST */ rv = send_begin_request(conn, request_id); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01073) @@ -896,8 +714,10 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r, return HTTP_SERVICE_UNAVAILABLE; } + apr_pool_create(&temp_pool, r->pool); + /* Step 2: Send Environment via FCGI_PARAMS */ - rv = send_environment(conn, r, request_id); + rv = send_environment(conn, r, temp_pool, request_id); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01074) "Failed writing Environment to %s:", server_portstr); @@ -906,7 +726,7 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r, } /* Step 3: Read records from the back end server and handle them. */ - rv = dispatch(conn, conf, r, request_id); + rv = dispatch(conn, conf, r, temp_pool, request_id); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01075) "Error dispatching request to %s:", server_portstr); |