From: Nick Kew Date: Tue, 29 Jun 2004 06:37:21 +0000 (+0000) Subject: Fix for multiple proxy bugs - review please: X-Git-Tag: pre_ajp_proxy~130 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b6e89d86f7e766ee9d08751642a03b31f1553e5a;p=apache Fix for multiple proxy bugs - review please: #10722 Reverse proxying cookies #15207 Proxy passing canonicalised URIs to backend #16812 Case-insensitivity of proxypassreverse #19317 Canonicalised URI causing infinite loop #20372 AllowEncodedSlashes May also fix 13577 (untested) This is really two fixes: 10722 and 15207; the others are trivial consequences. To make review easier, the simpler fix (#15207) is entirely contained in #ifdef FIX_15207 (new code) and #ifndef FIX_15207 (removed code) git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@104070 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 78c59083f3..c0bb0a7992 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -1,3 +1,4 @@ +#define FIX_15207 /* Copyright 1999-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,10 +43,22 @@ /* -------------------------------------------------------------- */ /* Translate the URL into a 'filename' */ +#ifdef FIX_15207 +#define x2c(x) ((x>='0')&&(x<='9'))?(x-'0'):(((x>='a')&&(x<='f'))?(10+x-'a'):((x>='A')&&(x<='F'))?(10+x-'A'):0) +static unsigned char hex2c(const char* p) { + char c1 = p[1] ; + char c2 = p[2] ; + int i1 = x2c(c1) ; + int i2 = x2c(c2) ; + unsigned char ret = (i1<<4) | i2 ; + return ret ; +} +#endif static int alias_match(const char *uri, const char *alias_fakename) { const char *end_fakename = alias_fakename + strlen(alias_fakename); const char *aliasp = alias_fakename, *urip = uri; + unsigned char uric, aliasc ; while (aliasp < end_fakename) { if (*aliasp == '/') { @@ -61,9 +74,28 @@ static int alias_match(const char *uri, const char *alias_fakename) ++urip; } else { +#ifndef FIX_15207 /* Other characters are compared literally */ if (*urip++ != *aliasp++) return 0; +#else + /* Other characters are canonicalised and compared literally */ + if ( *urip == '%' ) { + uric = hex2c(urip) ; + urip += 3 ; + } else { + uric = (unsigned char)*urip++ ; + } + if ( *aliasp == '%' ) { + aliasc = hex2c(aliasp) ; + aliasp += 3 ; + } else { + aliasc = (unsigned char)*aliasp++ ; + } + if ( uric != aliasc ) { + return 0; + } +#endif } } @@ -94,9 +126,12 @@ static int alias_match(const char *uri, const char *alias_fakename) static int proxy_detect(request_rec *r) { void *sconf = r->server->module_config; - proxy_server_conf *conf; - - conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); +#ifdef FIX_15207 + int i, len; + struct proxy_alias *ent = (struct proxy_alias *)conf->aliases->elts; +#endif /* Ick... msvc (perhaps others) promotes ternary short results to int */ @@ -121,6 +156,21 @@ static int proxy_detect(request_rec *r) r->uri = r->unparsed_uri; r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); r->handler = "proxy-server"; +#ifdef FIX_15207 + } else { + /* test for a ProxyPass */ + for (i = 0; i < conf->aliases->nelts; i++) { + len = alias_match(r->unparsed_uri, ent[i].fake); + if (len > 0) { + r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real, + r->unparsed_uri + len, NULL); + r->handler = "proxy-server"; + r->proxyreq = PROXYREQ_REVERSE; + r->uri = r->unparsed_uri; + break; + } + } +#endif } return DECLINED; } @@ -139,7 +189,7 @@ static int proxy_trans(request_rec *r) */ return OK; } - +#ifndef FIX_15207 /* XXX: since r->uri has been manipulated already we're not really * compliant with RFC1945 at this point. But this probably isn't * an issue because this is a hybrid proxy/origin server. @@ -160,6 +210,7 @@ static int proxy_trans(request_rec *r) return OK; } } +#endif return DECLINED; } @@ -221,7 +272,7 @@ static int proxy_map_location(request_rec *r) return OK; } - +#ifndef FIX_15207 /* -------------------------------------------------------------- */ /* Fixup the filename */ @@ -236,6 +287,15 @@ static int proxy_fixup(request_rec *r) if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; +#ifdef FIX_15207 +/* We definitely shouldn't canonicalize a proxy_pass. + * But should we really canonicalize a STD_PROXY??? -- Fahree + */ + if (r->proxyreq == PROXYREQ_REVERSE) { + return OK; + } +#endif + /* XXX: Shouldn't we try this before we run the proxy_walk? */ url = &r->filename[6]; @@ -250,7 +310,7 @@ static int proxy_fixup(request_rec *r) return OK; /* otherwise; we've done the best we can */ } - +#endif /* Send a redirection if the request contains a hostname which is not */ /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */ /* servers like Netscape's allow this and access hosts from the local */ @@ -439,6 +499,10 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s) ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote)); ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->cookie_path_str = apr_strmatch_precompile(p, "path=", 0) ; + ps->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0) ; ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry)); ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry)); ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int)); @@ -474,6 +538,12 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv) ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy); ps->aliases = apr_array_append(p, base->aliases, overrides->aliases); ps->raliases = apr_array_append(p, base->raliases, overrides->raliases); + ps->cookie_paths + = apr_array_append(p, base->cookie_paths, overrides->cookie_paths); + ps->cookie_domains + = apr_array_append(p, base->cookie_domains, overrides->cookie_domains) ; + ps->cookie_path_str = base->cookie_path_str; + ps->cookie_domain_str = base->cookie_domain_str; ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies); ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn); ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports); @@ -639,6 +709,34 @@ static const char * return NULL; } +static const char* + cookie_path(cmd_parms *cmd, void *dummy, const char *f, const char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf; + struct proxy_alias *new; + + conf = (proxy_server_conf *)ap_get_module_config(s->module_config, + &proxy_module); + new = apr_array_push(conf->cookie_paths) ; + new->fake = f; + new->real = r; + return NULL ; +} +static const char* + cookie_domain(cmd_parms *cmd, void *dummy, const char *f, const char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf; + struct proxy_alias *new; + + conf = (proxy_server_conf *)ap_get_module_config(s->module_config, + &proxy_module); + new = apr_array_push(conf->cookie_domains) ; + new->fake = f; + new->real = r; + return NULL ; +} static const char * set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg) @@ -1005,6 +1103,10 @@ static const command_rec proxy_cmds[] = "a virtual path and a URL"), AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL for reverse proxy behaviour"), + AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL, + RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies") , + AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL, + RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies") , AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, "A list of names, hosts or domains to which the proxy will not connect"), AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, @@ -1081,8 +1183,10 @@ static void register_hooks(apr_pool_t *p) ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST); /* walk entries and suppress default TRACE behavior */ ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST); +#ifndef FIX_15207 /* fixups */ ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST); +#endif /* post read_request handling */ ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST); /* post config handling */ diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index ec7e1759e3..68027c2000 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -49,6 +49,7 @@ #include "apr_strings.h" #include "apr_uri.h" #include "apr_date.h" +#include "apr_strmatch.h" #include "apr_fnmatch.h" #define APR_WANT_STRFUNC #include "apr_want.h" @@ -158,6 +159,14 @@ typedef struct { bad_body } badopt; /* how to deal with bad headers */ char badopt_set; +/* putting new stuff on the end maximises binary back-compatibility. + * the strmatch_patterns are really a const just to have a + * case-independent strstr. + */ + apr_array_header_t* cookie_paths ; + apr_array_header_t* cookie_domains ; + const apr_strmatch_pattern* cookie_path_str ; + const apr_strmatch_pattern* cookie_domain_str ; } proxy_server_conf; @@ -229,7 +238,6 @@ PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, en PROXY_DECLARE(char *)ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, char **passwordp, char **hostp, apr_port_t *port); PROXY_DECLARE(const char *)ap_proxy_date_canon(apr_pool_t *p, const char *x); -PROXY_DECLARE(apr_table_t *)ap_proxy_read_headers(request_rec *r, request_rec *rp, char *buffer, int size, conn_rec *c); PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val); PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val); PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x); diff --git a/modules/proxy/proxy_http.c b/modules/proxy/proxy_http.c index a0296d90e2..471a7b8dc3 100644 --- a/modules/proxy/proxy_http.c +++ b/modules/proxy/proxy_http.c @@ -122,13 +122,106 @@ static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_co ent = (struct proxy_alias *)conf->raliases->elts; for (i = 0; i < conf->raliases->nelts; i++) { l2 = strlen(ent[i].real); - if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) { + if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) { u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL); return ap_construct_url(r->pool, u, r); } } return url; } +/* cookies are a bit trickier to match: we've got two substrings to worry + * about, and we can't just find them with strstr 'cos of case. Regexp + * matching would be an easy fix, but for better consistency with all the + * other matches we'll refrain and use apr_strmatch to find path=/domain= + * and stick to plain strings for the config values. + */ +static const char *proxy_cookie_reverse_map(request_rec *r, + proxy_server_conf *conf, const char *str) +{ + struct proxy_alias *ent; + size_t len = strlen(str); + const char* newpath = NULL ; + const char* newdomain = NULL ; + const char* pathp ; + const char* domainp ; + const char* pathe ; + const char* domaine ; + size_t l1, l2, i, poffs, doffs ; + int ddiff = 0 ; + int pdiff = 0 ; + char* ret ; + +/* find the match and replacement, but save replacing until we've done + both path and domain so we know the new strlen +*/ + if ( pathp = apr_strmatch(conf->cookie_path_str, str, len) , pathp ) { + pathp += 5 ; + poffs = pathp - str ; + pathe = strchr(pathp, ';') ; + l1 = pathe ? (pathe-pathp) : strlen(pathp) ; + pathe = pathp + l1 ; + ent = (struct proxy_alias *)conf->cookie_paths->elts; + for (i = 0; i < conf->cookie_paths->nelts; i++) { + l2 = strlen(ent[i].fake); + if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) { + newpath = ent[i].real ; + pdiff = strlen(newpath) - l1 ; + break ; + } + } + } + if ( domainp = apr_strmatch(conf->cookie_domain_str, str, len) , domainp ) { + domainp += 7 ; + doffs = domainp - str ; + domaine = strchr(domainp, ';') ; + l1 = domaine ? (domaine-domainp) : strlen(domainp) ; + domaine = domainp + l1 ; + ent = (struct proxy_alias *)conf->cookie_domains->elts; + for (i = 0; i < conf->cookie_domains->nelts; i++) { + l2 = strlen(ent[i].fake); + if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) { + newdomain = ent[i].real ; + ddiff = strlen(newdomain) - l1 ; + break ; + } + } + } + if ( newpath ) { + ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ; + l1 = strlen(newpath) ; + if ( newdomain ) { + l2 = strlen(newdomain) ; + if ( doffs > poffs ) { + memcpy(ret, str, poffs) ; + memcpy(ret+poffs, newpath, l1) ; + memcpy(ret+poffs+l1, pathe, domainp-pathe) ; + memcpy(ret+doffs+pdiff, newdomain, l2) ; + strcpy(ret+doffs+pdiff+l2, domaine) ; + } else { + memcpy(ret, str, doffs) ; + memcpy(ret+doffs, newdomain, l2) ; + memcpy(ret+doffs+l2, domaine, pathp-domaine) ; + memcpy(ret+poffs+ddiff, newpath, l1) ; + strcpy(ret+poffs+ddiff+l1, pathe) ; + } + } else { + memcpy(ret, str, poffs) ; + memcpy(ret+poffs, newpath, l1) ; + strcpy(ret+poffs+l1, pathe) ; + } + } else { + if ( newdomain ) { + ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ; + l2 = strlen(newdomain) ; + memcpy(ret, str, doffs) ; + memcpy(ret+doffs, newdomain, l2) ; + strcpy(ret+doffs+l2, domaine) ; + } else { + ret = (char*) str ; /* no change */ + } + } + return ret ; +} /* Clear all connection-based headers from the incoming headers table */ static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers) @@ -375,6 +468,7 @@ apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r, return OK; } + static apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r, proxy_http_conn_t *p_conn, conn_rec *origin, @@ -751,6 +845,139 @@ apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r, return APR_SUCCESS; } +static void process_proxy_header(request_rec* r, proxy_server_conf* c, + const char* key, const char* value) +{ + static const char* date_hdrs[] + = { "Date", "Expires", "Last-Modified", NULL } ; + static const struct { + const char* name ; + const char* (*func)(request_rec*, proxy_server_conf*, const char*) ; + } transform_hdrs[] = { + { "Location", ap_proxy_location_reverse_map } , + { "Content-Location", ap_proxy_location_reverse_map } , + { "URI", ap_proxy_location_reverse_map } , + { "Set-Cookie", proxy_cookie_reverse_map } , + { NULL, NULL } + } ; + int i ; + for ( i = 0 ; date_hdrs[i] ; ++i ) { + if ( !strcasecmp(date_hdrs[i], key) ) { + apr_table_add(r->headers_out, key, + ap_proxy_date_canon(r->pool, value)) ; + return ; + } + } + for ( i = 0 ; transform_hdrs[i].name ; ++i ) { + if ( !strcasecmp(transform_hdrs[i].name, key) ) { + apr_table_add(r->headers_out, key, + (*transform_hdrs[i].func)(r, c, value)) ; + return ; + } + } + apr_table_add(r->headers_out, key, value) ; + return ; +} + +static void ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c) +{ + int len; + char *value, *end; + char field[MAX_STRING_LEN]; + int saw_headers = 0; + void *sconf = r->server->module_config; + proxy_server_conf *psc; + + psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + r->headers_out = apr_table_make(r->pool, 20); + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), or we timeout. + */ + while ((len = ap_getline(buffer, size, rr, 1)) > 0) { + + if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ + + /* We may encounter invalid headers, usually from buggy + * MS IIS servers, so we need to determine just how to handle + * them. We can either ignore them, assume that they mark the + * start-of-body (eg: a missing CRLF) or (the default) mark + * the headers as totally bogus and return a 500. The sole + * exception is an extra "HTTP/1.0 200, OK" line sprinkled + * in between the usual MIME headers, which is a favorite + * IIS bug. + */ + /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */ + + if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + if (psc->badopt == bad_error) { + /* Nope, it wasn't even an extra HTTP header. Give up. */ + return ; + } + else if (psc->badopt == bad_body) { + /* if we've already started loading headers_out, then + * return what we've accumulated so far, in the hopes + * that they are useful. Otherwise, we completely bail. + */ + /* FIXME: We've already scarfed the supposed 1st line of + * the body, so the actual content may end up being bogus + * as well. If the content is HTML, we may be lucky. + */ + if (saw_headers) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: Starting body due to bogus non-header in headers " + "returned by %s (%s)", r->uri, r->method); + return ; + } else { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: No HTTP headers " + "returned by %s (%s)", r->uri, r->method); + return ; + } + } + } + /* this is the psc->badopt == bad_ignore case */ + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: Ignoring bogus HTTP header " + "returned by %s (%s)", r->uri, r->method); + continue; + } + + *value = '\0'; + ++value; + /* XXX: RFC2068 defines only SP and HT as whitespace, this test is + * wrong... and so are many others probably. + */ + while (apr_isspace(*value)) + ++value; /* Skip to start of value */ + + /* should strip trailing whitespace as well */ + for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); -- +end) + *end = '\0'; + + /* make sure we add so as not to destroy duplicated headers + * Modify headers requiring canonicalisation and/or affected + * by ProxyPassReverse and family with process_proxy_header + */ + process_proxy_header(r, psc, buffer, value) ; + saw_headers = 1; + + /* the header was too long; at the least we should skip extra data */ + if (len >= size - 1) { + while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1)) + >= MAX_STRING_LEN - 1) { + /* soak up the extra data */ + } + if (len == 0) /* time to exit the larger loop as well */ + break; + } + } +} + + static int addit_dammit(void *v, const char *key, const char *val) { @@ -850,13 +1077,11 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, /* First, tuck away all already existing cookies */ save_table = apr_table_make(r->pool, 2); - apr_table_do(addit_dammit, save_table, r->err_headers_out, - "Set-Cookie", NULL); apr_table_do(addit_dammit, save_table, r->headers_out, "Set-Cookie", NULL); - r->headers_out = ap_proxy_read_headers(r, rp, buffer, - sizeof(buffer), origin); + /* shove the headers direct into r->headers_out */ + ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin); if (r->headers_out == NULL) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, @@ -946,42 +1171,9 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, "proxy: HTTP: received interim %d response", r->status); } - - /* we must accept 3 kinds of date, but generate only 1 kind of date */ - { - const char *buf; - if ((buf = apr_table_get(r->headers_out, "Date")) != NULL) { - apr_table_set(r->headers_out, "Date", - ap_proxy_date_canon(p, buf)); - } - if ((buf = apr_table_get(r->headers_out, "Expires")) != NULL) { - apr_table_set(r->headers_out, "Expires", - ap_proxy_date_canon(p, buf)); - } - if ((buf = apr_table_get(r->headers_out, "Last-Modified")) != NULL) { - apr_table_set(r->headers_out, "Last-Modified", - ap_proxy_date_canon(p, buf)); - } - } - - /* munge the Location and URI response headers according to - * ProxyPassReverse + /* Moved the fixups of Date headers and those affected by + * ProxyPassReverse/etc from here to ap_proxy_read_headers */ - { - const char *buf; - if ((buf = apr_table_get(r->headers_out, "Location")) != NULL) { - apr_table_set(r->headers_out, "Location", - ap_proxy_location_reverse_map(r, conf, buf)); - } - if ((buf = apr_table_get(r->headers_out, "Content-Location")) != NULL) { - apr_table_set(r->headers_out, "Content-Location", - ap_proxy_location_reverse_map(r, conf, buf)); - } - if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) { - apr_table_set(r->headers_out, "URI", - ap_proxy_location_reverse_map(r, conf, buf)); - } - } if ((r->status == 401) && (conf->error_override != 0)) { const char *buf; diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 8134371371..2925268806 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -352,112 +352,6 @@ PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r) return rp; } -/* - * Reads headers from a buffer and returns an array of headers. - * Returns NULL on file error - * This routine tries to deal with too long lines and continuation lines. - * - * Note: Currently the headers are passed through unmerged. This has to be - * done so that headers which react badly to merging (such as Set-Cookie - * headers, which contain commas within the date field) do not get stuffed - * up. - */ -PROXY_DECLARE(apr_table_t *)ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c) -{ - apr_table_t *headers_out; - int len; - char *value, *end; - char field[MAX_STRING_LEN]; - int saw_headers = 0; - void *sconf = r->server->module_config; - proxy_server_conf *psc; - - psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); - - headers_out = apr_table_make(r->pool, 20); - - /* - * Read header lines until we get the empty separator line, a read error, - * the connection closes (EOF), or we timeout. - */ - while ((len = ap_getline(buffer, size, rr, 1)) > 0) { - - if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ - - /* We may encounter invalid headers, usually from buggy - * MS IIS servers, so we need to determine just how to handle - * them. We can either ignore them, assume that they mark the - * start-of-body (eg: a missing CRLF) or (the default) mark - * the headers as totally bogus and return a 500. The sole - * exception is an extra "HTTP/1.0 200, OK" line sprinkled - * in between the usual MIME headers, which is a favorite - * IIS bug. - */ - /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */ - - if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) { - if (psc->badopt == bad_error) { - /* Nope, it wasn't even an extra HTTP header. Give up. */ - return NULL; - } - else if (psc->badopt == bad_body) { - /* if we've already started loading headers_out, then - * return what we've accumulated so far, in the hopes - * that they are useful. Otherwise, we completely bail. - */ - /* FIXME: We've already scarfed the supposed 1st line of - * the body, so the actual content may end up being bogus - * as well. If the content is HTML, we may be lucky. - */ - if (saw_headers) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, - "proxy: Starting body due to bogus non-header in headers " - "returned by %s (%s)", r->uri, r->method); - return headers_out; - } else { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, - "proxy: No HTTP headers " - "returned by %s (%s)", r->uri, r->method); - return NULL; - } - } - } - /* this is the psc->badopt == bad_ignore case */ - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, - "proxy: Ignoring bogus HTTP header " - "returned by %s (%s)", r->uri, r->method); - continue; - } - - *value = '\0'; - ++value; - /* XXX: RFC2068 defines only SP and HT as whitespace, this test is - * wrong... and so are many others probably. - */ - while (apr_isspace(*value)) - ++value; /* Skip to start of value */ - - /* should strip trailing whitespace as well */ - for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end) - *end = '\0'; - - /* make sure we add so as not to destroy duplicated headers */ - apr_table_add(headers_out, buffer, value); - saw_headers = 1; - - /* the header was too long; at the least we should skip extra data */ - if (len >= size - 1) { - while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1)) - >= MAX_STRING_LEN - 1) { - /* soak up the extra data */ - } - if (len == 0) /* time to exit the larger loop as well */ - break; - } - } - return headers_out; -} - /* * list is a comma-separated list of case-insensitive tokens, with