Skip to content

Commit

Permalink
add option to send host header in HTTP proxy CONNECT command
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn committed Oct 21, 2024
1 parent 07b2574 commit 9acb255
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 12 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

2.0.11 not released

* add option to send host header in HTTP proxy CONNECT command
* don't hint FADV_RANDOM on posix systems. May improve seeding performance
* allow boost connect while checking resume data if no_verify_files flag is set
* fix BEP-40 peer priority for IPv6
Expand Down
4 changes: 4 additions & 0 deletions include/libtorrent/aux_/proxy_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ namespace aux {

// if true, tracker connections are subject to the proxy settings
bool proxy_tracker_connections = true;

// when proxying non plain HTTP over an HTTP proxy (SSL or peer
// connections), also tell the proxy which hostname we're connecting to
bool send_host_in_connect = false;
};

}}
Expand Down
14 changes: 9 additions & 5 deletions include/libtorrent/http_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,21 @@ class http_stream : public proxy_base
m_password = password;
}

void set_dst_name(std::string const& host)
void set_host(std::string const& host)
{
m_dst_name = host;
m_host = host;
}

void close(error_code& ec)
{
m_dst_name.clear();
m_host.clear();
proxy_base::close(ec);
}

#ifndef BOOST_NO_EXCEPTIONS
void close()
{
m_dst_name.clear();
m_host.clear();
proxy_base::close();
}
#endif
Expand Down Expand Up @@ -129,6 +129,10 @@ class http_stream : public proxy_base
std::back_insert_iterator<std::vector<char>> p(m_buffer);
std::string const endpoint = print_endpoint(m_remote_endpoint);
write_string("CONNECT " + endpoint + " HTTP/1.0\r\n", p);
if (!m_host.empty())
{
write_string("Host: " + m_host + "\r\n", p);
}
if (!m_user.empty())
{
write_string("Proxy-Authorization: Basic " + base64encode(
Expand Down Expand Up @@ -218,7 +222,7 @@ class http_stream : public proxy_base
// proxy authentication
std::string m_user;
std::string m_password;
std::string m_dst_name;
std::string m_host;

// this is true if the connection is HTTP based and
// want to talk directly to the proxy
Expand Down
8 changes: 8 additions & 0 deletions include/libtorrent/settings_pack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,14 @@ namespace aux {
// protocol may not be valid from the proxy's point of view.
socks5_udp_send_local_ep,

// When using HTTP proxy (in proxy_type), libtorrent will connect
// to peers and trackers using the `CONNECT` proxy command. In this
// command it's possible to reveal the hostname of the server we're
// connecting to. When this option is true, the hostname will be
// sent. This feature can be useful if the proxy is used to
// man-in-the-middle connections.
proxy_send_host_in_connect,

max_bool_setting_internal
};

Expand Down
30 changes: 25 additions & 5 deletions simulation/test_http_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ void run_suite(lt::aux::proxy_settings ps)
// the try-next test in his case would test the socks proxy itself, whether
// it has robust retry behavior (which the simple test proxy that comes with
// libsimulator doesn't).
if (ps.proxy_hostnames == false)
if (ps.type != settings_pack::socks5 && ps.proxy_hostnames == false)
{
// this hostname will resolve to multiple IPs, all but one that we cannot
// connect to and the second one where we'll get the test file response. Make
Expand Down Expand Up @@ -600,9 +600,7 @@ TORRENT_TEST(http_connection_http_error)
test_proxy_failure(settings_pack::http);
}

// Requests a proxied SSL connection. This test just ensures that the correct CONNECT request
// is sent to the proxy server.
TORRENT_TEST(http_connection_ssl_proxy)
void test_connection_ssl_proxy(bool const with_hostname)
{
using sim::asio::ip::address_v4;
sim_config network_cfg;
Expand All @@ -615,15 +613,24 @@ TORRENT_TEST(http_connection_ssl_proxy)
sim::http_server http_proxy(proxy_ios, 4445);

lt::aux::proxy_settings ps = make_proxy_settings(settings_pack::http);
ps.send_host_in_connect = with_hostname;

int client_counter = 0;
int proxy_counter = 0;

http_proxy.register_handler("10.0.0.2:8080"
, [&proxy_counter](std::string method, std::string req, std::map<std::string, std::string>&)
, [&proxy_counter, with_hostname](std::string method, std::string req, std::map<std::string, std::string>& headers)
{
proxy_counter++;
TEST_EQUAL(method, "CONNECT");
if (with_hostname)
{
TEST_EQUAL(headers["host"], "10.0.0.2");
}
else
{
TEST_CHECK(headers.empty());
}
return sim::send_response(403, "Not supported", 1337);
});

Expand Down Expand Up @@ -656,6 +663,19 @@ TORRENT_TEST(http_connection_ssl_proxy)
TEST_EQUAL(proxy_counter, 1);
}

// Requests a proxied SSL connection. This test just ensures that the correct CONNECT request
// is sent to the proxy server.
TORRENT_TEST(http_connection_ssl_proxy_no_hostname)
{
test_connection_ssl_proxy(false);
}

TORRENT_TEST(http_connection_ssl_proxy_hostname)
{
test_connection_ssl_proxy(true);
}


// TODO: test http proxy with password
// TODO: test socks5 with password
// TODO: test SSL
Expand Down
6 changes: 6 additions & 0 deletions src/http_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,12 @@ void http_connection::connect()
}
}

if (m_proxy.send_host_in_connect
&& boost::get<http_stream>(&*m_sock))
{
boost::get<http_stream>(*m_sock).set_host(m_hostname);
}

TORRENT_ASSERT(m_next_ep < int(m_endpoints.size()));
if (m_next_ep >= int(m_endpoints.size())) return;

Expand Down
2 changes: 2 additions & 0 deletions src/proxy_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ void init(proxy_settings& p, Settings const& sett)
settings_pack::proxy_peer_connections);
p.proxy_tracker_connections = sett.get_bool(
settings_pack::proxy_tracker_connections);
p.send_host_in_connect = sett.get_bool(
settings_pack::proxy_send_host_in_connect);
}

}
Expand Down
1 change: 1 addition & 0 deletions src/settings_pack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ constexpr int DISK_WRITE_MODE = settings_pack::enable_os_cache;
SET(allow_idna, false, nullptr),
SET(enable_set_file_valid_data, false, nullptr),
SET(socks5_udp_send_local_ep, false, nullptr),
SET(proxy_send_host_in_connect, false, nullptr),
}});

CONSTEXPR_SETTINGS
Expand Down
19 changes: 17 additions & 2 deletions src/torrent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6658,11 +6658,11 @@ namespace {
aux::socket_type s = instantiate_connection(m_ses.get_context()
, m_ses.proxy(), userdata, nullptr, true, false);

if (boost::get<http_stream>(&s))
if (auto* inner = boost::get<http_stream>(&s))
{
// the web seed connection will talk immediately to
// the proxy, without requiring CONNECT support
boost::get<http_stream>(s).set_no_connect(true);
inner->set_no_connect(true);
}

std::string hostname;
Expand Down Expand Up @@ -6723,6 +6723,21 @@ namespace {
bool const proxy_hostnames = settings().get_bool(settings_pack::proxy_hostnames)
&& !is_ip;

if (!is_ip
&& settings().get_bool(settings_pack::proxy_send_host_in_connect))
{
if (auto* inner1 = boost::get<http_stream>(&s))
{
inner1->set_host(hostname);
}
#if TORRENT_USE_SSL
else if (auto* inner2 = boost::get<ssl_stream<http_stream>>(&s))
{
inner2->next_layer().set_host(hostname);
}
#endif
}

if (proxy_hostnames
&& (boost::get<socks5_stream>(&s)
#if TORRENT_USE_SSL
Expand Down

0 comments on commit 9acb255

Please sign in to comment.