dovecot-2.2: lib-http: http-client: Added support for tunneling ...

dovecot at dovecot.org dovecot at dovecot.org
Sat Oct 12 11:14:58 EEST 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/b9498573f0d0
changeset: 16856:b9498573f0d0
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sat Oct 12 11:11:04 2013 +0300
description:
lib-http: http-client: Added support for tunneling SSL conntections through proxy.

diffstat:

 src/lib-http/http-client-connection.c |  108 ++++++++++++++++++++++++++++++++-
 src/lib-http/http-client-host.c       |    9 ++-
 src/lib-http/http-client-peer.c       |    1 +
 src/lib-http/http-client-private.h    |   21 +++++-
 src/lib-http/http-client-request.c    |   31 ++++++++-
 src/lib-http/http-client.c            |    1 +
 src/lib-http/http-client.h            |   14 ++++
 7 files changed, 172 insertions(+), 13 deletions(-)

diffs (truncated from 361 to 300 lines):

diff -r c3884a6acde6 -r b9498573f0d0 src/lib-http/http-client-connection.c
--- a/src/lib-http/http-client-connection.c	Sat Oct 12 11:05:08 2013 +0300
+++ b/src/lib-http/http-client-connection.c	Sat Oct 12 11:11:04 2013 +0300
@@ -4,6 +4,7 @@
 #include "net.h"
 #include "str.h"
 #include "hash.h"
+#include "llist.h"
 #include "array.h"
 #include "ioloop.h"
 #include "istream.h"
@@ -912,7 +913,8 @@
 	http_client_connection_destroy(&conn->conn);
 }
 
-static void http_client_connection_connect(struct http_client_connection *conn)
+static void
+http_client_connection_connect(struct http_client_connection *conn)
 {
 	unsigned int msecs;
 
@@ -936,6 +938,94 @@
 	}
 }
 
+static void
+http_client_connect_tunnel_timeout(struct http_client_connection *conn)
+{
+	http_client_connection_unref(&conn);
+}
+
+// FIXME: put something like this in lib/connection.c
+static void
+_connection_init_from_streams(struct connection_list *list,
+			    struct connection *conn, const char *name,
+			    struct istream *input, struct ostream *output)
+{
+	i_assert(name != NULL);
+
+	conn->list = list;
+	conn->name = i_strdup(name);
+	conn->fd_in = i_stream_get_fd(input);
+	conn->fd_out = o_stream_get_fd(output);
+
+	i_assert(conn->fd_in >= 0);
+	i_assert(conn->fd_out >= 0);
+	i_assert(conn->io == NULL);
+	i_assert(conn->input == NULL);
+	i_assert(conn->output == NULL);
+	i_assert(conn->to == NULL);
+
+	conn->input = input;
+	i_stream_set_name(conn->input, conn->name);
+
+	conn->output = output;
+	o_stream_set_no_error_handling(conn->output, TRUE);
+	o_stream_set_name(conn->output, conn->name);
+
+	conn->io = io_add(conn->fd_in, IO_READ, *list->v.input, conn);
+	
+	DLLIST_PREPEND(&list->connections, conn);
+	list->connections_count++;
+
+	if (list->v.client_connected != NULL)
+		list->v.client_connected(conn, TRUE);
+}
+
+static void
+http_client_connection_tunnel_response(const struct http_response *response,
+			       struct http_client_connection *conn)
+{
+	struct http_client_tunnel tunnel;
+	const char *name = http_client_peer_addr2str(&conn->peer->addr);
+
+	if (response->status != 200) {
+		http_client_peer_connection_failure(conn->peer, t_strdup_printf(
+			"tunnel connect(%s) failed: %d %s", name,
+				response->status, response->reason));
+		conn->connect_request = NULL;
+		return;
+	}
+
+	http_client_request_start_tunnel(conn->connect_request, &tunnel);
+	conn->connect_request = NULL;
+
+	_connection_init_from_streams
+		(conn->client->conn_list, &conn->conn, name, tunnel.input, tunnel.output);
+}
+
+static void
+http_client_connection_connect_tunnel(struct http_client_connection *conn,
+	const struct ip_addr *ip, unsigned int port)
+{
+	unsigned int msecs;
+
+	conn->connect_start_timestamp = ioloop_timeval;
+
+	conn->connect_request = http_client_request_connect_ip
+		(conn->client, ip, port, http_client_connection_tunnel_response, conn);
+	http_client_request_set_urgent(conn->connect_request);
+	http_client_request_submit(conn->connect_request);
+
+	/* don't use connection.h timeout because we want this timeout
+	   to include also the SSL handshake */
+	msecs = conn->client->set.connect_timeout_msecs;
+	if (msecs == 0)
+		msecs = conn->client->set.request_timeout_msecs;
+	if (msecs > 0) {
+		conn->to_connect =
+			timeout_add(msecs, http_client_connect_tunnel_timeout, conn);
+	}
+}
+
 struct http_client_connection *
 http_client_connection_create(struct http_client_peer *peer)
 {
@@ -951,6 +1041,9 @@
 	case HTTP_CLIENT_PEER_ADDR_HTTPS:
 		conn_type = "HTTPS";
 		break;
+	case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+		conn_type = "Tunneled HTTPS";
+		break;
 	case HTTP_CLIENT_PEER_ADDR_RAW:
 		conn_type = "Raw";
 		break;
@@ -964,9 +1057,13 @@
 	if (peer->addr.type != HTTP_CLIENT_PEER_ADDR_RAW)
 		i_array_init(&conn->request_wait_list, 16);
 
-	connection_init_client_ip
-		(peer->client->conn_list, &conn->conn, &addr->ip, addr->port);
-	http_client_connection_connect(conn);
+	if (peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL) {
+		http_client_connection_connect_tunnel(conn, &addr->ip, addr->port);
+	} else {
+		connection_init_client_ip
+			(peer->client->conn_list, &conn->conn, &addr->ip, addr->port);
+		http_client_connection_connect(conn);
+	}
 
 	array_append(&peer->conns, &conn, 1);
 
@@ -1000,6 +1097,9 @@
 	conn->closing = TRUE;
 	conn->connected = FALSE;
 
+	if (conn->connect_request != NULL)
+		http_client_request_abort(&conn->connect_request);
+
 	if (conn->incoming_payload != NULL) {
 		/* the stream is still accessed by lib-http caller. */
 		i_stream_remove_destroy_callback(conn->incoming_payload,
diff -r c3884a6acde6 -r b9498573f0d0 src/lib-http/http-client-host.c
--- a/src/lib-http/http-client-host.c	Sat Oct 12 11:05:08 2013 +0300
+++ b/src/lib-http/http-client-host.c	Sat Oct 12 11:11:04 2013 +0300
@@ -428,9 +428,10 @@
 }
 
 struct http_client_host *http_client_host_get
-(struct http_client *client, const char *hostname)
+(struct http_client *client, const struct http_url *host_url)
 {
 	struct http_client_host *host;
+	const char *hostname = host_url->host_name;
 
 	host = hash_table_lookup(client->hosts, hostname);
 	if (host == NULL) {
@@ -445,6 +446,12 @@
 		hash_table_insert(client->hosts, hostname, host);
 		DLLIST_PREPEND(&client->hosts_list, host);
 
+		if (host_url->have_host_ip) {
+			host->ips_count = 1;
+			host->ips = i_new(struct ip_addr, host->ips_count);
+			host->ips[0] = host_url->host_ip;
+		}
+
 		http_client_host_debug(host, "Host created");
 	}
 	return host;
diff -r c3884a6acde6 -r b9498573f0d0 src/lib-http/http-client-peer.c
--- a/src/lib-http/http-client-peer.c	Sat Oct 12 11:05:08 2013 +0300
+++ b/src/lib-http/http-client-peer.c	Sat Oct 12 11:11:04 2013 +0300
@@ -48,6 +48,7 @@
 	case HTTP_CLIENT_PEER_ADDR_HTTP:
 		return net_ip_hash(&peer->ip) + peer->port;
 	case HTTP_CLIENT_PEER_ADDR_HTTPS:
+	case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
 		return net_ip_hash(&peer->ip) + peer->port +
 			(peer->https_name == NULL ? 0 : str_hash(peer->https_name));
 	}
diff -r c3884a6acde6 -r b9498573f0d0 src/lib-http/http-client-private.h
--- a/src/lib-http/http-client-private.h	Sat Oct 12 11:05:08 2013 +0300
+++ b/src/lib-http/http-client-private.h	Sat Oct 12 11:11:04 2013 +0300
@@ -35,6 +35,7 @@
 enum http_client_peer_addr_type {
 	HTTP_CLIENT_PEER_ADDR_HTTP = 0,
 	HTTP_CLIENT_PEER_ADDR_HTTPS,
+	HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL,
 	HTTP_CLIENT_PEER_ADDR_RAW
 };
 
@@ -97,6 +98,7 @@
 	unsigned int submitted:1;
 	unsigned int connect_tunnel:1;
 	unsigned int connect_direct:1;
+	unsigned int ssl_tunnel:1;
 };
 
 struct http_client_host_port {
@@ -178,6 +180,7 @@
 	int connect_errno;
 	struct timeval connect_start_timestamp;
 	struct timeval connected_timestamp;
+	struct http_client_request *connect_request;
 
 	struct ssl_iostream *ssl_iostream;
 	struct http_response_parser *http_parser;
@@ -248,7 +251,10 @@
 		addr->type = HTTP_CLIENT_PEER_ADDR_RAW;
 		addr->port = (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT);
 	} else if (host_url->have_ssl) {
-		addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS;
+		if (req->ssl_tunnel)
+			addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL;
+		else
+			addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS;
 		addr->https_name = host_url->host_name;
  		addr->port = (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT);
 	} else {
@@ -260,13 +266,19 @@
 static inline const char *
 http_client_connection_label(struct http_client_connection *conn)
 {
-	return t_strdup_printf("%s [%d]",
-		 http_client_peer_addr2str(&conn->peer->addr), conn->id);
+	return t_strdup_printf("%s%s [%d]",
+		http_client_peer_addr2str(&conn->peer->addr),
+		(conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL ?
+			" (tunnel)" : ""), conn->id);
 }
 
 static inline const char *
 http_client_peer_label(struct http_client_peer *peer)
 {
+	if (peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL) {
+		return t_strconcat
+			(http_client_peer_addr2str(&peer->addr), " (tunnel)", NULL);
+	}
 	return http_client_peer_addr2str(&peer->addr);
 }
 
@@ -344,7 +356,8 @@
 void http_client_peer_switch_ioloop(struct http_client_peer *peer);
 
 struct http_client_host *
-	http_client_host_get(struct http_client *client, const char *hostname);
+http_client_host_get(struct http_client *client,
+	const struct http_url *host_url);
 void http_client_host_free(struct http_client_host **_host);
 void http_client_host_submit_request(struct http_client_host *host,
 	struct http_client_request *req);
diff -r c3884a6acde6 -r b9498573f0d0 src/lib-http/http-client-request.c
--- a/src/lib-http/http-client-request.c	Sat Oct 12 11:05:08 2013 +0300
+++ b/src/lib-http/http-client-request.c	Sat Oct 12 11:11:04 2013 +0300
@@ -115,7 +115,24 @@
 	req->origin_url.port = port;
 	req->origin_url.have_port = TRUE;
 	req->connect_tunnel = TRUE;
-	req->target = "";
+	req->target = req->origin_url.host_name;
+	return req;
+}
+
+#undef http_client_request_connect_ip
+struct http_client_request *
+http_client_request_connect_ip(struct http_client *client,
+		    const struct ip_addr *ip, in_port_t port,
+		    http_client_request_callback_t *callback,
+				void *context)
+{
+	struct http_client_request *req;
+	const char *hostname = net_ip2addr(ip);
+
+	req = http_client_request_connect
+		(client, hostname, port, callback, context);
+	req->origin_url.host_ip = *ip;
+	req->origin_url.have_host_ip = TRUE;
 	return req;
 }
 
@@ -300,9 +317,15 @@
 
 	/* determine what host to contact to submit this request */
 	if (proxy_url != NULL) {
-		req->host_url = proxy_url;         /* proxy server */
+		if (req->origin_url.have_ssl && !client->set.no_ssl_tunnel &&
+			!req->connect_tunnel) {
+			req->host_url = &req->origin_url;  /* tunnel to origin server */


More information about the dovecot-cvs mailing list