summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--docs/manual/mod/mod_proxy.xml5
-rw-r--r--docs/manual/mod/mod_proxy_connect.xml5
-rw-r--r--docs/manual/mod/mod_proxy_http.xml2
-rw-r--r--include/ap_mmn.h3
-rw-r--r--modules/proxy/mod_proxy.h1
-rw-r--r--modules/proxy/proxy_util.c146
7 files changed, 162 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index 542d2fc435..a2aa7bb790 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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;
}
-