diff options
author | Rainer Jung <rjung@apache.org> | 2010-02-12 10:58:48 +0100 |
---|---|---|
committer | Rainer Jung <rjung@apache.org> | 2010-02-12 10:58:48 +0100 |
commit | 8eb4de81e977e6a2646c077d03a89acd38d42459 (patch) | |
tree | 202525fb8fd6d02310f6d93d69fe64c6f31ed69f | |
parent | Fix typo in comment. (diff) | |
download | apache2-8eb4de81e977e6a2646c077d03a89acd38d42459.tar.xz apache2-8eb4de81e977e6a2646c077d03a89acd38d42459.zip |
Support remote https proxies by using HTTP CONNECT.
PR: 19188
Submitted by: Philippe Dutrueux <lilas evidian.com>
Reviewed by: rjung
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@909323 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | docs/manual/mod/mod_proxy.xml | 5 | ||||
-rw-r--r-- | docs/manual/mod/mod_proxy_connect.xml | 5 | ||||
-rw-r--r-- | docs/manual/mod/mod_proxy_http.xml | 2 | ||||
-rw-r--r-- | include/ap_mmn.h | 3 | ||||
-rw-r--r-- | modules/proxy/mod_proxy.h | 1 | ||||
-rw-r--r-- | modules/proxy/proxy_util.c | 146 |
7 files changed, 162 insertions, 6 deletions
@@ -1,5 +1,11 @@ -*- coding: utf-8 -*- +Changes with Apache 2.3.7 + + *) mod_proxy, mod_proxy_http: Support remote https proxies + by using HTTP CONNECT. + PR 19188. [Philippe Dutrueux <lilas evidian.com>, Rainer Jung] + Changes with Apache 2.3.6 *) worker: Don't report server has reached MaxClients until it has. diff --git a/docs/manual/mod/mod_proxy.xml b/docs/manual/mod/mod_proxy.xml index 16c539cbc4..f3e0c3d39c 100644 --- a/docs/manual/mod/mod_proxy.xml +++ b/docs/manual/mod/mod_proxy.xml @@ -465,8 +465,9 @@ request</description> </example> <p><var>scheme</var> is effectively the protocol that should be used to - communicate with the remote server; only <code>http</code> is supported by - this module.</p> + communicate with the remote server; only <code>http</code> and <code>https</code> + are supported by this module. When using <code>https</code>, the requests + are forwarded through the remote proxy using the HTTP CONNECT method.</p> <example><title>Example</title> ProxyRemote http://goodguys.example.com/ http://mirrorguys.example.com:8000<br /> diff --git a/docs/manual/mod/mod_proxy_connect.xml b/docs/manual/mod/mod_proxy_connect.xml index d021f1913d..c683230dd9 100644 --- a/docs/manual/mod/mod_proxy_connect.xml +++ b/docs/manual/mod/mod_proxy_connect.xml @@ -39,6 +39,11 @@ requests, <module>mod_proxy</module> and <module>mod_proxy_connect</module> have to be present in the server.</p> + <p>CONNECT is also used, when the server needs to send an HTTPS request + through a forward proxy. In this case the server acts as a CONNECT client. + This functionality is part of <module>mod_proxy</module> and + <module>mod_proxy_connect</module> is not needed in this case.</p> + <note type="warning"><title>Warning</title> <p>Do not enable proxying until you have <a href="mod_proxy.html#access">secured your server</a>. Open proxy diff --git a/docs/manual/mod/mod_proxy_http.xml b/docs/manual/mod/mod_proxy_http.xml index cbd77651ea..92e1625160 100644 --- a/docs/manual/mod/mod_proxy_http.xml +++ b/docs/manual/mod/mod_proxy_http.xml @@ -32,7 +32,7 @@ <summary> <p>This module <em>requires</em> the service of <module >mod_proxy</module>. It provides the features used for - proxying HTTP requests. <module>mod_proxy_http</module> + proxying HTTP and HTTPS requests. <module>mod_proxy_http</module> supports HTTP/0.9, HTTP/1.0 and HTTP/1.1. It does <em>not</em> provide any caching abilities. If you want to set up a caching proxy, you might want to use the additional service of the diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 327a7f6641..b47ed321cb 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -215,6 +215,7 @@ * 20091230.3 (2.3.6-dev) add ap_parse_log_level() * 20091230.4 (2.3.6-dev) export ap_process_request_after_handler() for mod_serf * 20100208.0 (2.3.6-dev) ap_socache_provider_t API changes to store and iterate + * 20100208.1 (2.3.6-dev) Added forward member to proxy_conn_rec * */ @@ -223,7 +224,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20100208 #endif -#define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index a2da941979..adf1376367 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -232,6 +232,7 @@ typedef struct { * which the backend currently answers. */ int need_flush;/* Flag to decide whether we need to flush the * filter chain or not */ + void *forward; /* opaque forward proxy data */ } proxy_conn_rec; typedef struct { diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 85ab9d0139..91fdbc9a6e 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -29,6 +29,18 @@ #define apr_socket_create apr_socket_create_ex #endif +/* + * Opaque structure containing target server info when + * using a forward proxy. + * Up to now only used in combination with HTTP CONNECT. + */ +typedef struct { + int use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */ + const char *target_host; /* Target hostname */ + apr_port_t target_port; /* Target port */ + const char *proxy_auth; /* Proxy authorization */ +} forward_info; + /* Global balancer counter */ int PROXY_DECLARE_DATA proxy_lb_workers = 0; static int lb_workers_limit = 0; @@ -2109,6 +2121,34 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, if (proxyname) { conn->hostname = apr_pstrdup(conn->pool, proxyname); conn->port = proxyport; + /* + * If we have a forward proxy and the protocol is HTTPS, + * then we need to prepend a HTTP CONNECT request before + * sending our actual HTTPS requests. + * Save our real backend data for using it later during HTTP CONNECT. + */ + if (conn->is_ssl) { + const char *proxy_auth; + + forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info)); + conn->forward = forward; + forward->use_http_connect = 1; + forward->target_host = uri->hostname; + forward->target_port = uri->port; + /* Do we want to pass Proxy-Authorization along? + * If we haven't used it, then YES + * If we have used it then MAYBE: RFC2616 says we MAY propagate it. + * So let's make it configurable by env. + * The logic here is the same used in mod_proxy_http. + */ + proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization"); + if (proxy_auth != NULL && + proxy_auth[0] != '\0' && + r->user == NULL && /* we haven't yet authenticated */ + apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { + forward->proxy_auth = proxy_auth; + } + } } else { conn->hostname = apr_pstrdup(conn->pool, uri->hostname); @@ -2250,6 +2290,83 @@ static int is_socket_connected(apr_socket_t *sock) } #endif /* USE_ALTERNATE_IS_CONNECTED */ + +/* + * Send a HTTP CONNECT request to a forward proxy. + * The proxy is given by "backend", the target server + * is contained in the "forward" member of "backend". + */ +static apr_status_t send_http_connect(proxy_conn_rec *backend, + server_rec *s) +{ + int status; + apr_size_t nbytes; + char buffer[HUGE_STRING_LEN]; + forward_info *forward = (forward_info *)backend->forward; + int len = 0; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "proxy: CONNECT: sending the CONNECT request for %s:%d " + "to the remote proxy %pI (%s)", + forward->target_host, forward->target_port, + backend->addr, backend->hostname); + /* Create the CONNECT request */ + nbytes = apr_snprintf(buffer, sizeof(buffer), + "CONNECT %s:%d HTTP/1.0" CRLF, + forward->target_host, forward->target_port); + /* Add proxy authorization from the initial request if necessary */ + if (forward->proxy_auth != NULL) { + nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, + "Proxy-Authorization: %s" CRLF, + forward->proxy_auth); + } + /* Set a reasonable agent and send everything */ + nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, + "Proxy-agent: %s" CRLF CRLF, + ap_get_server_banner()); + apr_socket_send(backend->sock, buffer, &nbytes); + + /* Receive the whole CONNECT response */ + nbytes = sizeof(buffer) - 1; + status = apr_socket_recv(backend->sock, buffer, &nbytes); + while (status == APR_SUCCESS) { + len += nbytes; + buffer[len] = '\0'; + if (strstr(buffer, "\r\n\r\n") != NULL) { + break; + } + nbytes = sizeof(buffer) - 1 - len; + status = apr_socket_recv(backend->sock, buffer + len, &nbytes); + } + + /* Check for HTTP_OK response status */ + if (status == APR_SUCCESS) { + int major, minor; + char code_str[10]; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "send_http_connect: response from the forward proxy: %s", + buffer); + + /* Extract the returned code */ + if (sscanf(buffer, "HTTP/%u.%u %s", &major, &minor, code_str) == 3) { + status = atoi(code_str); + if (status == HTTP_OK) { + status = APR_SUCCESS; + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "send_http_connect: the forward proxy returned code is %s", + code_str); + status = APR_INCOMPLETE; + } + } + } + + return(status); +} + + PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, @@ -2360,7 +2477,33 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, apr_socket_timeout_set(newsock, s->timeout); } - conn->sock = newsock; + conn->sock = newsock; + + if (conn->forward) { + forward_info *forward = (forward_info *)conn->forward; + /* + * For HTTP CONNECT we need to prepend CONNECT request before + * sending our actual HTTPS requests. + */ + if (forward->use_http_connect) { + rv = send_http_connect(conn, s); + /* If an error occurred, loop round and try again */ + if (rv != APR_SUCCESS) { + conn->sock = NULL; + apr_socket_close(newsock); + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, + "proxy: %s: attempt to connect to %s:%d " + "via http CONNECT through %pI (%s) failed", + proxy_function, + forward->target_host, forward->target_port, + backend_addr, worker->hostname); + backend_addr = backend_addr->next; + continue; + } + } + } + connected = 1; } /* @@ -2547,4 +2690,3 @@ ap_proxy_buckets_lifetime_transform(request_rec *r, apr_bucket_brigade *from, } return rv; } - |