summaryrefslogtreecommitdiffstats
path: root/modules/proxy/mod_proxy_uwsgi.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/proxy/mod_proxy_uwsgi.c148
1 files changed, 99 insertions, 49 deletions
diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c
index c5d4f8e..4e57196 100644
--- a/modules/proxy/mod_proxy_uwsgi.c
+++ b/modules/proxy/mod_proxy_uwsgi.c
@@ -84,10 +84,29 @@ static int uwsgi_canon(request_rec *r, char *url)
host = apr_pstrcat(r->pool, "[", host, "]", NULL);
}
- path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
- r->proxyreq);
- if (!path) {
- return HTTP_BAD_REQUEST;
+ if (apr_table_get(r->notes, "proxy-nocanon")
+ || apr_table_get(r->notes, "proxy-noencode")) {
+ path = url; /* this is the raw/encoded path */
+ }
+ else {
+ core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
+ int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0;
+
+ path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags,
+ r->proxyreq);
+ if (!path) {
+ return HTTP_BAD_REQUEST;
+ }
+ }
+ /*
+ * If we have a raw control character or a ' ' in nocanon path,
+ * correct encoding was missed.
+ */
+ if (path == url && *ap_scan_vchar_obstext(path)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10417)
+ "To be forwarded path contains control "
+ "characters or spaces");
+ return HTTP_FORBIDDEN;
}
r->filename =
@@ -136,7 +155,7 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn)
int j;
apr_size_t headerlen = 4;
- apr_uint16_t pktsize, keylen, vallen;
+ apr_size_t pktsize, keylen, vallen;
const char *script_name;
const char *path_info;
const char *auth;
@@ -175,7 +194,16 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn)
env = (apr_table_entry_t *) env_table->elts;
for (j = 0; j < env_table->nelts; ++j) {
- headerlen += 2 + strlen(env[j].key) + 2 + strlen(env[j].val);
+ headerlen += 2 + strlen(env[j].key) + 2 + (env[j].val ? strlen(env[j].val) : 0);
+ }
+
+ pktsize = headerlen - 4;
+ if (pktsize > APR_UINT16_MAX) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10259)
+ "can't send headers to %s:%u: packet size too "
+ "large (%" APR_SIZE_T_FMT ")",
+ conn->hostname, conn->port, pktsize);
+ return HTTP_INTERNAL_SERVER_ERROR;
}
ptr = buf = apr_palloc(r->pool, headerlen);
@@ -189,15 +217,15 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn)
memcpy(ptr, env[j].key, keylen);
ptr += keylen;
- vallen = strlen(env[j].val);
+ vallen = env[j].val ? strlen(env[j].val) : 0;
*ptr++ = (apr_byte_t) (vallen & 0xff);
*ptr++ = (apr_byte_t) ((vallen >> 8) & 0xff);
- memcpy(ptr, env[j].val, vallen);
+ if (env[j].val) {
+ memcpy(ptr, env[j].val, vallen);
+ }
ptr += vallen;
}
- pktsize = headerlen - 4;
-
buf[0] = 0;
buf[1] = (apr_byte_t) (pktsize & 0xff);
buf[2] = (apr_byte_t) ((pktsize >> 8) & 0xff);
@@ -238,6 +266,7 @@ static request_rec *make_fake_req(conn_rec *c, request_rec *r)
request_rec *rp;
apr_pool_create(&pool, c->pool);
+ apr_pool_tag(pool, "proxy_uwsgi_rp");
rp = apr_pcalloc(pool, sizeof(*r));
@@ -297,18 +326,16 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend,
pass_bb = apr_brigade_create(r->pool, c->bucket_alloc);
len = ap_getline(buffer, sizeof(buffer), rp, 1);
-
if (len <= 0) {
- /* oops */
+ /* invalid or empty */
return HTTP_INTERNAL_SERVER_ERROR;
}
-
backend->worker->s->read += len;
-
- if (len >= sizeof(buffer) - 1) {
- /* oops */
+ if ((apr_size_t)len >= sizeof(buffer)) {
+ /* too long */
return HTTP_INTERNAL_SERVER_ERROR;
}
+
/* Position of http status code */
if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
status_start = 9;
@@ -317,8 +344,8 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend,
status_start = 7;
}
else {
- /* oops */
- return HTTP_INTERNAL_SERVER_ERROR;
+ /* not HTTP */
+ return HTTP_BAD_GATEWAY;
}
status_end = status_start + 3;
@@ -338,21 +365,50 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend,
}
r->status_line = apr_pstrdup(r->pool, &buffer[status_start]);
- /* start parsing headers */
+ /* parse headers */
while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) {
+ if ((apr_size_t)len >= sizeof(buffer)) {
+ /* too long */
+ len = -1;
+ break;
+ }
value = strchr(buffer, ':');
- /* invalid header skip */
- if (!value)
- continue;
- *value = '\0';
- ++value;
+ if (!value) {
+ /* invalid header */
+ len = -1;
+ break;
+ }
+ *value++ = '\0';
+ if (*ap_scan_http_token(buffer)) {
+ /* invalid name */
+ len = -1;
+ break;
+ }
while (apr_isspace(*value))
++value;
for (end = &value[strlen(value) - 1];
end > value && apr_isspace(*end); --end)
*end = '\0';
+ if (*ap_scan_http_field_content(value)) {
+ /* invalid value */
+ len = -1;
+ break;
+ }
apr_table_add(r->headers_out, buffer, value);
}
+ if (len < 0) {
+ /* Reset headers, but not to NULL because things below the chain expect
+ * this to be non NULL e.g. the ap_content_length_filter.
+ */
+ r->headers_out = apr_table_make(r->pool, 1);
+ return HTTP_BAD_GATEWAY;
+ }
+
+ /* T-E wins over C-L */
+ if (apr_table_get(r->headers_out, "Transfer-Encoding")) {
+ apr_table_unset(r->headers_out, "Content-Length");
+ backend->close = 1;
+ }
if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
ap_set_content_type(r, apr_pstrdup(r->pool, buf));
@@ -362,9 +418,9 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend,
#if AP_MODULE_MAGIC_AT_LEAST(20101106,0)
dconf =
ap_get_module_config(r->per_dir_config, &proxy_module);
- if (dconf->error_override && ap_is_HTTP_ERROR(r->status)) {
+ if (ap_proxy_should_override(dconf, r->status)) {
#else
- if (conf->error_override && ap_is_HTTP_ERROR(r->status)) {
+ if (ap_proxy_should_override(conf, r->status)) {
#endif
int status = r->status;
r->status = HTTP_OK;
@@ -446,11 +502,8 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker,
const char *proxyname, apr_port_t proxyport)
{
int status;
- int delta = 0;
- int decode_status;
proxy_conn_rec *backend = NULL;
apr_pool_t *p = r->pool;
- size_t w_len;
char server_portstr[32];
char *u_path_info;
apr_uri_t *uri;
@@ -462,24 +515,23 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker,
uri = apr_palloc(r->pool, sizeof(*uri));
- /* ADD PATH_INFO */
-#if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
- w_len = strlen(worker->s->name);
-#else
- w_len = strlen(worker->name);
-#endif
- u_path_info = r->filename + 6 + w_len;
- if (u_path_info[0] != '/') {
- delta = 1;
+ /* ADD PATH_INFO (unescaped) */
+ u_path_info = ap_strchr(url + sizeof(UWSGI_SCHEME) + 2, '/');
+ if (!u_path_info) {
+ u_path_info = apr_pstrdup(r->pool, "/");
}
- decode_status = ap_unescape_url(url + w_len - delta);
- if (decode_status) {
+ else if (ap_unescape_url(u_path_info) != OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10100)
- "unable to decode uri: %s", url + w_len - delta);
+ "unable to decode uwsgi uri: %s", url);
return HTTP_INTERNAL_SERVER_ERROR;
}
- apr_table_add(r->subprocess_env, "PATH_INFO", url + w_len - delta);
-
+ else {
+ /* Remove duplicate slashes at the beginning of PATH_INFO */
+ while (u_path_info[1] == '/') {
+ u_path_info++;
+ }
+ }
+ apr_table_add(r->subprocess_env, "PATH_INFO", u_path_info);
/* Create space for state information */
status = ap_proxy_acquire_connection(UWSGI_SCHEME, &backend, worker,
@@ -509,12 +561,10 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker,
}
/* Step Three: Create conn_rec */
- if (!backend->connection) {
- if ((status = ap_proxy_connection_create(UWSGI_SCHEME, backend,
- r->connection,
- r->server)) != OK)
- goto cleanup;
- }
+ if ((status = ap_proxy_connection_create(UWSGI_SCHEME, backend,
+ r->connection,
+ r->server)) != OK)
+ goto cleanup;
/* Step Four: Process the Request */
if (((status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK)