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 mod_proxy and
+ mod_proxy_connect is not needed in this case.
+
Warning
Do not enable proxying until you have secured your server. 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 @@
This module requires the service of mod_proxy. It provides the features used for
- proxying HTTP requests. mod_proxy_http
+ proxying HTTP and HTTPS requests. mod_proxy_http
supports HTTP/0.9, HTTP/1.0 and HTTP/1.1. It does not
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;
}
-
--
2.40.0