From 8f6431df56c16a763a2a4dc10e9525c51d6faf4f Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Sun, 25 Mar 2001 20:23:25 +0000 Subject: [PATCH] Header Bugfix / General Cleanups in ap_proxy_http_handler / X-Forwarded-* headers PR: Obtained from: Reviewed by: Chuck Murcko git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88579 13f79535-47bb-0310-9956-ffa450edef68 --- modules/proxy/mod_proxy.c | 29 ++-- modules/proxy/proxy_http.c | 287 +++++++++++++++++++++---------------- 2 files changed, 187 insertions(+), 129 deletions(-) diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 26542772e6..93640d3fd8 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -298,17 +298,30 @@ static int proxy_handler(request_rec *r) if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; - if (r->method_number == M_TRACE && + if ((r->method_number == M_TRACE || r->method_number == M_OPTIONS) && (maxfwd_str = apr_table_get(r->headers_in, "Max-Forwards")) != NULL) { long maxfwd = strtol(maxfwd_str, NULL, 10); if (maxfwd < 1) { - int access_status; - r->proxyreq = PROXYREQ_NONE; - if ((access_status = ap_send_http_trace(r))) - ap_die(access_status, r); - else - ap_finalize_request_protocol(r); - return OK; + switch (r->method_number) { + case M_TRACE: { + int access_status; + r->proxyreq = PROXYREQ_NONE; + if ((access_status = ap_send_http_trace(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return OK; + } + case M_OPTIONS: { + int access_status; + r->proxyreq = PROXYREQ_NONE; + if ((access_status = ap_send_http_options(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return OK; + } + } } apr_table_setn(r->headers_in, "Max-Forwards", apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd-1 : 0)); diff --git a/modules/proxy/proxy_http.c b/modules/proxy/proxy_http.c index 772dbde83c..67a7fdade2 100644 --- a/modules/proxy/proxy_http.c +++ b/modules/proxy/proxy_http.c @@ -179,67 +179,77 @@ static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers) int ap_proxy_http_handler(request_rec *r, char *url, const char *proxyhost, int proxyport) { - const char *strp; - char *strp2; + apr_pool_t *p = r->pool; char *desthost; + int destport = 0; + char *destportstr = NULL; + char server_portstr[32]; + const char *uri = NULL; apr_socket_t *sock; - int i, len, backasswards, content_length = -1; + int i, len, backasswards; apr_status_t err; - apr_array_header_t *reqhdrs_arr; - apr_table_t *resp_hdrs = NULL; - apr_table_entry_t *reqhdrs; - const char *table_buf; + apr_array_header_t *headers_in_array; + apr_table_entry_t *headers_in; struct sockaddr_in server; struct in_addr destaddr; char buffer[HUGE_STRING_LEN]; - char *buffer2; - char portstr[32]; - apr_pool_t *p = r->pool; - int destport = 0; - char *destportstr = NULL; - const char *urlptr = NULL; - apr_ssize_t cntr; - apr_file_t *cachefp = NULL; + char *response; char *buf; conn_rec *origin; apr_bucket *e; - apr_bucket_brigade *bb = apr_brigade_create(r->pool); + apr_bucket_brigade *bb = apr_brigade_create(p); void *sconf = r->server->module_config; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; - int nocache = 0; memset(&server, '\0', sizeof(server)); server.sin_family = AF_INET; - /* We break the URL into host, port, path-search */ - - urlptr = strstr(url, "://"); - if (urlptr == NULL) - return HTTP_BAD_REQUEST; - urlptr += 3; - destport = DEFAULT_HTTP_PORT; - strp = ap_strchr_c(urlptr, '/'); - if (strp == NULL) { - desthost = apr_pstrdup(p, urlptr); - urlptr = "/"; + /* We break the URL into host, port, uri */ + { + const char *buf; + + uri = strstr(url, "://"); + if (uri == NULL) + return HTTP_BAD_REQUEST; + uri += 3; + destport = DEFAULT_HTTP_PORT; + buf = ap_strchr_c(uri, '/'); + if (buf == NULL) { + desthost = apr_pstrdup(p, uri); + uri = "/"; + } + else { + char *q = apr_palloc(p, buf - uri + 1); + memcpy(q, uri, buf - uri); + q[buf - uri] = '\0'; + uri = buf; + desthost = q; + } } - else { - char *q = apr_palloc(p, strp - urlptr + 1); - memcpy(q, urlptr, strp - urlptr); - q[strp - urlptr] = '\0'; - urlptr = strp; - desthost = q; + + /* Get the port number - put it in destport and destportstr */ + { + char *buf; + buf = ap_strchr(desthost, ':'); + if (buf != NULL) { + *(buf++) = '\0'; + if (apr_isdigit(*buf)) { + destport = atoi(buf); + destportstr = buf; + } + } } - strp2 = ap_strchr(desthost, ':'); - if (strp2 != NULL) { - *(strp2++) = '\0'; - if (apr_isdigit(*strp2)) { - destport = atoi(strp2); - destportstr = strp2; + /* Get the server port for the Via headers */ + { + i = ap_get_server_port(r); + if (ap_is_default_port(i,r)) { + strcpy(server_portstr,""); + } else { + apr_snprintf(server_portstr, sizeof server_portstr, ":%d", i); } } @@ -254,7 +264,7 @@ int ap_proxy_http_handler(request_rec *r, char *url, "Connect to remote machine blocked"); } - if ((apr_socket_create(&sock, APR_INET, SOCK_STREAM, r->pool)) != APR_SUCCESS) { + if ((apr_socket_create(&sock, APR_INET, SOCK_STREAM, p)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy: error creating socket"); return HTTP_INTERNAL_SERVER_ERROR; @@ -279,18 +289,18 @@ int ap_proxy_http_handler(request_rec *r, char *url, if (proxyhost != NULL) return DECLINED; /* try again another way */ else - return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(r->pool, + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, "Could not connect to remote machine: ", desthost, NULL)); } - origin = ap_new_connection(r->pool, r->server, sock, 0); + origin = ap_new_connection(p, r->server, sock, 0); if (!origin) { /* the peer reset the connection already; ap_new_connection() * closed the socket */ /* XXX somebody that knows what they're doing add an error path */ /* XXX how's this? */ - return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(r->pool, + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, "Connection reset by peer: ", desthost, NULL)); } @@ -298,20 +308,20 @@ int ap_proxy_http_handler(request_rec *r, char *url, ap_add_output_filter("CORE", NULL, NULL, origin); /* strip connection listed hop-by-hop headers from the request */ - ap_proxy_clear_connection(r->pool, r->headers_in); + ap_proxy_clear_connection(p, r->headers_in); - buf = apr_pstrcat(r->pool, r->method, " ", proxyhost ? url : urlptr, + buf = apr_pstrcat(p, r->method, " ", proxyhost ? url : uri, " HTTP/1.1" CRLF, NULL); - e = apr_bucket_pool_create(buf, strlen(buf), r->pool); + e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); if (destportstr != NULL && destport != DEFAULT_HTTP_PORT) { - buf = apr_pstrcat(r->pool, "Host: ", desthost, ":", destportstr, CRLF, NULL); - e = apr_bucket_pool_create(buf, strlen(buf), r->pool); + buf = apr_pstrcat(p, "Host: ", desthost, ":", destportstr, CRLF, NULL); + e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); } else { - buf = apr_pstrcat(r->pool, "Host: ", desthost, CRLF, NULL); - e = apr_bucket_pool_create(buf, strlen(buf), r->pool); + buf = apr_pstrcat(p, "Host: ", desthost, CRLF, NULL); + e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); } @@ -321,42 +331,80 @@ int ap_proxy_http_handler(request_rec *r, char *url, apr_table_unset(r->headers_in, "Via"); } else if (conf->viaopt != via_off) { /* Create a "Via:" request header entry and merge it */ - i = ap_get_server_port(r); - if (ap_is_default_port(i,r)) { - strcpy(portstr,""); - } else { - apr_snprintf(portstr, sizeof portstr, ":%d", i); - } /* Generate outgoing Via: header with/without server comment: */ apr_table_mergen(r->headers_in, "Via", (conf->viaopt == via_full) ? apr_psprintf(p, "%d.%d %s%s (%s)", HTTP_VERSION_MAJOR(r->proto_num), HTTP_VERSION_MINOR(r->proto_num), - ap_get_server_name(r), portstr, + ap_get_server_name(r), server_portstr, AP_SERVER_BASEVERSION) : apr_psprintf(p, "%d.%d %s%s", HTTP_VERSION_MAJOR(r->proto_num), HTTP_VERSION_MINOR(r->proto_num), - ap_get_server_name(r), portstr) + ap_get_server_name(r), server_portstr) ); } + /* X-Forwarded-*: handling + * + * XXX Privacy Note: + * ----------------- + * + * These request headers are only really useful when the mod_proxy + * is used in a reverse proxy configuration, so that useful info + * about the client can be passed through the reverse proxy and on + * to the backend server, which may require the information to + * function properly. + * + * In a forward proxy situation, these options are a potential + * privacy violation, as information about clients behind the proxy + * are revealed to arbitrary servers out there on the internet. + * + * The HTTP/1.1 Via: header is designed for passing client + * information through proxies to a server, and should be used in + * a forward proxy configuation instead of X-Forwarded-*. See the + * ProxyVia option for details. + */ + + if (PROXYREQ_REVERSE == r->proxyreq) { + const char *buf; + + /* Add X-Forwarded-For: so that the upstream has a chance to + * determine, where the original request came from. + */ + apr_table_mergen(r->headers_in, "X-Forwarded-For", r->connection->remote_ip); + + /* Add X-Forwarded-Host: so that upstream knows what the + * original request hostname was. + */ + if ((buf = apr_table_get(r->headers_in, "Host"))) { + apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf); + } + + /* Add X-Forwarded-Server: so that upstream knows what the + * name of this proxy server is (if there are more than one) + * XXX: This duplicates Via: - do we strictly need it? + */ + apr_table_mergen(r->headers_in, "X-Forwarded-Server", + r->server->server_hostname); + } + /* send request headers */ - reqhdrs_arr = apr_table_elts(r->headers_in); - reqhdrs = (apr_table_entry_t *) reqhdrs_arr->elts; - for (i = 0; i < reqhdrs_arr->nelts; i++) { - if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL + headers_in_array = apr_table_elts(r->headers_in); + headers_in = (apr_table_entry_t *) headers_in_array->elts; + for (i = 0; i < headers_in_array->nelts; i++) { + if (headers_in[i].key == NULL || headers_in[i].val == NULL /* Clear out hop-by-hop request headers not to send * RFC2616 13.5.1 says we should strip these headers */ - || !strcasecmp(reqhdrs[i].key, "Host") /* Already sent */ - || !strcasecmp(reqhdrs[i].key, "Keep-Alive") - || !strcasecmp(reqhdrs[i].key, "TE") - || !strcasecmp(reqhdrs[i].key, "Trailer") - || !strcasecmp(reqhdrs[i].key, "Transfer-Encoding") - || !strcasecmp(reqhdrs[i].key, "Upgrade") + || !strcasecmp(headers_in[i].key, "Host") /* Already sent */ + || !strcasecmp(headers_in[i].key, "Keep-Alive") + || !strcasecmp(headers_in[i].key, "TE") + || !strcasecmp(headers_in[i].key, "Trailer") + || !strcasecmp(headers_in[i].key, "Transfer-Encoding") + || !strcasecmp(headers_in[i].key, "Upgrade") /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be * suppressed if THIS server requested the authentication, @@ -367,23 +415,23 @@ int ap_proxy_http_handler(request_rec *r, char *url, * code itself, not here. This saves us having to signal * somehow whether this request was authenticated or not. */ - || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization") - || !strcasecmp(reqhdrs[i].key, "Proxy-Authenticate")) + || !strcasecmp(headers_in[i].key, "Proxy-Authorization") + || !strcasecmp(headers_in[i].key, "Proxy-Authenticate")) continue; - buf = apr_pstrcat(r->pool, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL); - e = apr_bucket_pool_create(buf, strlen(buf), r->pool); + buf = apr_pstrcat(p, headers_in[i].key, ": ", headers_in[i].val, CRLF, NULL); + e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); } /* we don't yet support keepalives - but we will soon, I promise! */ - buf = apr_pstrcat(r->pool, "Connection: close", CRLF, NULL); - e = apr_bucket_pool_create(buf, strlen(buf), r->pool); + buf = apr_pstrcat(p, "Connection: close", CRLF, NULL); + e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); /* add empty line at the end of the headers */ - e = apr_bucket_pool_create(CRLF, strlen(CRLF), r->pool); + e = apr_bucket_pool_create(CRLF, strlen(CRLF), p); APR_BRIGADE_INSERT_TAIL(bb, e); e = apr_bucket_flush_create(); APR_BRIGADE_INSERT_TAIL(bb, e); @@ -393,7 +441,7 @@ int ap_proxy_http_handler(request_rec *r, char *url, /* send the request data, if any. */ if (ap_should_client_block(r)) { while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) { - e = apr_bucket_pool_create(buffer, i, r->pool); + e = apr_bucket_pool_create(buffer, i, p); APR_BRIGADE_INSERT_TAIL(bb, e); } } @@ -406,14 +454,14 @@ int ap_proxy_http_handler(request_rec *r, char *url, ap_add_input_filter("CORE_IN", NULL, NULL, origin); apr_brigade_destroy(bb); - bb = apr_brigade_create(r->pool); + bb = apr_brigade_create(p); /* Tell http_filter to grab the data one line at a time. */ origin->remain = 0; ap_get_brigade(origin->input_filters, bb, AP_MODE_BLOCKING); e = APR_BRIGADE_FIRST(bb); - apr_bucket_read(e, (const char **)&buffer2, &len, APR_BLOCK_READ); + apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ); if (len == -1) { apr_socket_close(sock); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, @@ -430,56 +478,62 @@ int ap_proxy_http_handler(request_rec *r, char *url, apr_bucket_destroy(e); /* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */ - if (ap_checkmask(buffer2, "HTTP/#.# ###*")) { + if (ap_checkmask(response, "HTTP/#.# ###*")) { int major, minor; - if (2 != sscanf(buffer2, "HTTP/%u.%u", &major, &minor)) { + if (2 != sscanf(response, "HTTP/%u.%u", &major, &minor)) { major = 1; minor = 1; } /* If not an HTTP/1 message or if the status line was > 8192 bytes */ - if (buffer2[5] != '1' || buffer2[len - 1] != '\n') { + if (response[5] != '1' || response[len - 1] != '\n') { apr_socket_close(sock); return HTTP_BAD_GATEWAY; } backasswards = 0; - buffer2[--len] = '\0'; + response[--len] = '\0'; - buffer2[12] = '\0'; - r->status = atoi(&buffer2[9]); + response[12] = '\0'; + r->status = atoi(&response[9]); - buffer2[12] = ' '; - r->status_line = apr_pstrdup(p, &buffer2[9]); + response[12] = ' '; + r->status_line = apr_pstrdup(p, &response[9]); /* read the headers. */ /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ /* Also, take care with headers with multiple occurences. */ - resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, origin); - if (resp_hdrs == NULL) { + r->headers_out = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, origin); + if (r->headers_out == NULL) { ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server, "proxy: Bad HTTP/%d.%d header returned by %s (%s)", major, minor, r->uri, r->method); - nocache = 1; /* do not cache this broken file */ } else { /* strip connection listed hop-by-hop headers from response */ - ap_proxy_clear_connection(p, resp_hdrs); - if (apr_table_get(resp_hdrs, "Content-type")) { - r->content_type = apr_pstrdup(r->pool, apr_table_get(resp_hdrs, "Content-type")); + const char *buf; + ap_proxy_clear_connection(p, r->headers_out); + if ((buf = apr_table_get(r->headers_out, "Content-type"))) { + r->content_type = apr_pstrdup(p, buf); } } /* handle Via header in response */ if (conf->viaopt != via_off && conf->viaopt != via_block) { /* Create a "Via:" response header entry and merge it */ - i = ap_get_server_port(r); - if (ap_is_default_port(i,r)) { - strcpy(portstr,""); - } else { - apr_snprintf(portstr, sizeof portstr, ":%d", i); - } + ap_table_mergen(r->headers_out, "Via", + (conf->viaopt == via_full) + ? apr_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), server_portstr) + ); } } else { @@ -490,38 +544,29 @@ int ap_proxy_http_handler(request_rec *r, char *url, } /* munge the Location and URI response headers according to ProxyPassReverse */ - if ((table_buf = apr_table_get(resp_hdrs, "Location")) != NULL) - apr_table_set(resp_hdrs, "Location", ap_proxy_location_reverse_map(r, buf)); - if ((table_buf = apr_table_get(resp_hdrs, "Content-Location")) != NULL) - apr_table_set(resp_hdrs, "Content-Location", ap_proxy_location_reverse_map(r, buf)); - if ((table_buf = apr_table_get(resp_hdrs, "URI")) != NULL) - apr_table_set(resp_hdrs, "URI", ap_proxy_location_reverse_map(r, buf)); + { + 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, 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, buf)); + if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) + apr_table_set(r->headers_out, "URI", ap_proxy_location_reverse_map(r, buf)); + } -/* - if (!r->assbackwards) - ap_rputs(CRLF, r); -*/ r->sent_bodyct = 1; /* Is it an HTTP/0.9 response? If so, send the extra data */ if (backasswards) { - cntr = len; + apr_ssize_t cntr = len; + /* FIXME: what is buffer used for here? It is of limited size */ e = apr_bucket_heap_create(buffer, cntr, 0, NULL); APR_BRIGADE_INSERT_TAIL(bb, e); - if (cachefp && apr_file_write(cachefp, buffer, &cntr) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "proxy: error writing extra data to cache"); - } } /* send body */ - /* if header only, then cache will be NULL */ /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */ if (!r->header_only) { - proxy_completion pc; - pc.content_length = content_length; - pc.cache_completion = conf->cache_completion; - - origin->remain = content_length; + origin->remain = -1; while (ap_get_brigade(origin->input_filters, bb, AP_MODE_BLOCKING) == APR_SUCCESS) { if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { ap_pass_brigade(r->output_filters, bb); @@ -529,7 +574,7 @@ int ap_proxy_http_handler(request_rec *r, char *url, } ap_pass_brigade(r->output_filters, bb); apr_brigade_destroy(bb); - bb = apr_brigade_create(r->pool); + bb = apr_brigade_create(p); } } -- 2.40.0