Mercurial > dovecot > core-2.2
changeset 16856:b9498573f0d0
lib-http: http-client: Added support for tunneling SSL conntections through proxy.
author | Stephan Bosch <stephan@rename-it.nl> |
---|---|
date | Sat, 12 Oct 2013 11:11:04 +0300 |
parents | c3884a6acde6 |
children | 73216464c8e1 |
files | src/lib-http/http-client-connection.c src/lib-http/http-client-host.c src/lib-http/http-client-peer.c src/lib-http/http-client-private.h src/lib-http/http-client-request.c src/lib-http/http-client.c src/lib-http/http-client.h |
diffstat | 7 files changed, 172 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- 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,
--- 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;
--- 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)); }
--- 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);
--- 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 */ + req->ssl_tunnel = TRUE; + } else { + req->host_url = proxy_url; /* proxy server */ + } } else { - req->host_url = &req->origin_url; /* origin server */ + req->host_url = &req->origin_url; /* origin server */ } /* use submission date if no date is set explicitly */ @@ -327,7 +350,7 @@ req->urgent = TRUE; } - host = http_client_host_get(req->client, req->host_url->host_name); + host = http_client_host_get(req->client, req->host_url); req->state = HTTP_REQUEST_STATE_QUEUED; http_client_host_submit_request(host, req);
--- a/src/lib-http/http-client.c Sat Oct 12 11:05:08 2013 +0300 +++ b/src/lib-http/http-client.c Sat Oct 12 11:11:04 2013 +0300 @@ -103,6 +103,7 @@ (set->max_pipelined_requests > 0 ? set->max_pipelined_requests : 1); client->set.max_attempts = set->max_attempts; client->set.no_auto_redirect = set->no_auto_redirect; + client->set.no_ssl_tunnel = set->no_ssl_tunnel; client->set.max_redirects = set->max_redirects; client->set.response_hdr_limits = set->response_hdr_limits; client->set.request_timeout_msecs = set->request_timeout_msecs;
--- a/src/lib-http/http-client.h Sat Oct 12 11:05:08 2013 +0300 +++ b/src/lib-http/http-client.h Sat Oct 12 11:11:04 2013 +0300 @@ -64,6 +64,10 @@ /* don't automatically act upon redirect responses */ bool no_auto_redirect; + /* if we use a proxy, delegate SSL negotiation to proxy, rather than + creating a CONNECT tunnel through the proxy for the SSL link */ + bool no_ssl_tunnel; + /* maximum number of redirects for a request (default = 0; redirects refused) */ @@ -138,6 +142,16 @@ CALLBACK_TYPECHECK(callback, void (*)( \ const struct http_response *response, typeof(context))), \ (http_client_request_callback_t *)callback, context) +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); +#define http_client_request_connect_ip(client, ip, port, callback, context) \ + http_client_request_connect_ip(client, ip, port + \ + CALLBACK_TYPECHECK(callback, void (*)( \ + const struct http_response *response, typeof(context))), \ + (http_client_request_callback_t *)callback, context) void http_client_request_set_port(struct http_client_request *req, in_port_t port);