improvements to socks5 support (for 1.1.1 release) (#567)

capture listen IP and port from socks5 BIND response. add tests for socks5 and improve support for capturing the local endpoint (i.e. bind port) for BIND command socket connections. post listen_succeeded_alert when successfully bound to listen socket on SOCKS5 proxy. make sure to announce the socks5 listen port
This commit is contained in:
Arvid Norberg 2016-05-05 17:09:11 -04:00
parent f065cacebf
commit f9bc6dbc54
17 changed files with 705 additions and 150 deletions

View File

@ -1,5 +1,6 @@
1.1.1 release
* improved Socks5 support and test coverage
* fix set_settings in python binding
* Added missing alert categories in python binding
* Added dht_get_peers_reply_alert alert in python binding

View File

@ -1326,7 +1326,7 @@ namespace libtorrent
// was opened for listening.
struct TORRENT_EXPORT listen_succeeded_alert TORRENT_FINAL : alert
{
enum socket_type_t { tcp, tcp_ssl, udp, utp_ssl };
enum socket_type_t { tcp, tcp_ssl, udp, i2p, socks5, utp_ssl };
// internal
listen_succeeded_alert(aux::stack_allocator& alloc, tcp::endpoint const& ep
@ -1799,8 +1799,8 @@ namespace libtorrent
// an incoming connection, through any mean. The most straight-forward ways
// of accepting incoming connections are through the TCP listen socket and
// the UDP listen socket for uTP sockets. However, connections may also be
// accepted offer a Socks5 or i2p listen socket, or via a torrent specific
// listen socket for SSL torrents.
// accepted through a Socks5 or i2p listen socket, or via an SSL listen
// socket.
struct TORRENT_EXPORT incoming_connection_alert TORRENT_FINAL : alert
{
// internal

View File

@ -242,6 +242,8 @@ namespace libtorrent
void async_accept(boost::shared_ptr<tcp::acceptor> const& listener, bool ssl);
void on_accept_connection(boost::shared_ptr<socket_type> const& s
, boost::weak_ptr<tcp::acceptor> listener, error_code const& e, bool ssl);
void on_socks_listen(boost::shared_ptr<socket_type> const& s
, error_code const& e);
void on_socks_accept(boost::shared_ptr<socket_type> const& s
, error_code const& e);

View File

@ -50,12 +50,14 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
// TODO: 2 facto these functions out
TORRENT_EXTRA_EXPORT bool is_local(address const& a);
TORRENT_EXTRA_EXPORT bool is_loopback(address const& addr);
TORRENT_EXTRA_EXPORT bool is_multicast(address const& addr);
TORRENT_EXTRA_EXPORT bool is_any(address const& addr);
TORRENT_EXTRA_EXPORT bool is_teredo(address const& addr);
TORRENT_EXTRA_EXPORT int cidr_distance(address const& a1, address const& a2);
bool is_ip_address(char const* host);
// determines if the operating system supports IPv6
TORRENT_EXTRA_EXPORT bool supports_ipv6();

View File

@ -37,10 +37,12 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#include "libtorrent/proxy_base.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_ip_address
#include "libtorrent/assert.hpp"
#if defined TORRENT_ASIO_DEBUGGING
#include "libtorrent/debug.hpp"
@ -98,7 +100,7 @@ public:
void set_command(int c)
{
TORRENT_ASSERT(c >= socks5_connect && c <=socks5_udp_associate);
TORRENT_ASSERT(c >= socks5_connect && c <= socks5_udp_associate);
m_command = c;
}
@ -109,19 +111,43 @@ public:
m_password = password;
}
template <typename Handler>
void async_accept(Handler const& handler)
{
TORRENT_ASSERT(m_listen == 1);
TORRENT_ASSERT(m_command == socks5_bind);
// to avoid unnecessary copying of the handler,
// store it in a shaed_ptr
error_code e;
connect1(e, boost::make_shared<handler_type>(handler));
}
template <typename Handler>
void async_listen(tcp::endpoint const& ep, Handler const& handler)
{
m_command = socks5_bind;
m_remote_endpoint = ep;
// to avoid unnecessary copying of the handler,
// store it in a shaed_ptr
boost::shared_ptr<handler_type> h(new handler_type(handler));
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("socks5_stream::name_lookup");
#endif
tcp::resolver::query q(m_hostname, to_string(m_port).elems);
m_resolver.async_resolve(q, boost::bind(
&socks5_stream::name_lookup, this, _1, _2, h));
}
void set_dst_name(std::string const& host)
{
// TODO: 3 enable this assert and fix remaining causes of it triggering
/*
#if TORRENT_USE_ASSERTS
error_code ec;
address::from_string(host, ec);
// if this assert trips, set_dst_name() is called wth an IP address rather
// than a hostname. Instead, resolve the IP into an address and pass it to
// async_connect instead
TORRENT_ASSERT(ec);
#endif
*/
TORRENT_ASSERT(!is_ip_address(host.c_str()));
m_dst_name = host;
if (m_dst_name.size() > 255)
m_dst_name.resize(255);
@ -141,14 +167,27 @@ public:
}
#endif
#ifndef BOOST_NO_EXCEPTIONS
endpoint_type local_endpoint() const
{
return m_local_endpoint;
}
#endif
endpoint_type local_endpoint(error_code&) const
{
return m_local_endpoint;
}
// TODO: 2 add async_connect() that takes a hostname and port as well
template <class Handler>
void async_connect(endpoint_type const& endpoint, Handler const& handler)
{
// make sure we don't try to connect to INADDR_ANY. binding is fine,
// and using a hostname is fine on SOCKS version 5.
TORRENT_ASSERT(m_command == socks5_bind
|| endpoint.address() != address()
TORRENT_ASSERT(m_command != socks5_bind);
TORRENT_ASSERT(endpoint.address() != address()
|| (!m_dst_name.empty() && m_version == 5));
m_remote_endpoint = endpoint;
@ -194,6 +233,11 @@ private:
std::string m_user;
std::string m_password;
std::string m_dst_name;
// when listening via a socks proxy, this is the IP and port our listen
// socket bound to
endpoint_type m_local_endpoint;
int m_version;
// the socks command to send for this connection (connect, bind,

View File

@ -24,6 +24,7 @@ project
;
alias libtorrent-sims :
[ run test_socks5.cpp ]
[ run test_checking.cpp ]
[ run test_optimistic_unchoke.cpp ]
[ run test_transfer.cpp ]

151
simulation/fake_peer.hpp Normal file
View File

@ -0,0 +1,151 @@
/*
Copyright (c) 2015, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SIMULATION_FAKE_PEER_HPP
#define SIMULATION_FAKE_PEER_HPP
#include <array>
#include "test.hpp"
#include "simulator/simulator.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/socket_io.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/sha1_hash.hpp"
#include "libtorrent/torrent_info.hpp"
using namespace sim;
namespace lt = libtorrent;
struct fake_peer
{
fake_peer(simulation& sim, char const* ip)
: m_ios(sim, asio::ip::address::from_string(ip))
, m_acceptor(m_ios)
, m_in_socket(m_ios)
, m_out_socket(m_ios)
, m_tripped(false)
{
boost::system::error_code ec;
m_acceptor.open(asio::ip::tcp::v4(), ec);
TEST_CHECK(!ec);
m_acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v4::any(), 6881), ec);
TEST_CHECK(!ec);
m_acceptor.listen(10, ec);
TEST_CHECK(!ec);
m_acceptor.async_accept(m_in_socket, [&] (boost::system::error_code const& ec)
{
// TODO: ideally we would kick off a read on the socket to verify that
// we received a bittorrent handshake
if (!ec) m_tripped = true;
});
}
void close()
{
m_acceptor.close();
m_in_socket.close();
m_out_socket.close();
}
void connect_to(asio::ip::tcp::endpoint ep, lt::sha1_hash const& ih)
{
using namespace std::placeholders;
boost::system::error_code ec;
m_out_socket.async_connect(ep, std::bind(&fake_peer::write_handshake
, this, _1, ih));
}
bool tripped() const { return m_tripped; }
private:
void write_handshake(boost::system::error_code const& ec, lt::sha1_hash ih)
{
asio::ip::tcp::endpoint ep = m_out_socket.remote_endpoint();
printf("fake_peer::connect (%s) -> (%d) %s\n"
, lt::print_endpoint(ep).c_str(), ec.value()
, ec.message().c_str());
static char const handshake[]
= "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04"
" " // space for info-hash
"aaaaaaaaaaaaaaaaaaaa" // peer-id
"\0\0\0\x01\x02"; // interested
int const len = sizeof(handshake) - 1;
memcpy(m_out_buffer, handshake, len);
memcpy(&m_out_buffer[28], ih.data(), 20);
asio::async_write(m_out_socket, asio::const_buffers_1(&m_out_buffer[0]
, len), [=](boost::system::error_code const& ec, size_t bytes_transferred)
{
printf("fake_peer::write_handshake(%s) -> (%d) %s\n"
, lt::print_endpoint(ep).c_str(), ec.value()
, ec.message().c_str());
this->m_out_socket.close();
});
}
char m_out_buffer[300];
asio::io_service m_ios;
asio::ip::tcp::acceptor m_acceptor;
asio::ip::tcp::socket m_in_socket;
asio::ip::tcp::socket m_out_socket;
bool m_tripped;
};
inline void add_fake_peers(lt::torrent_handle h)
{
// add the fake peers
for (int i = 0; i < 5; ++i)
{
char ep[30];
snprintf(ep, sizeof(ep), "60.0.0.%d", i);
h.connect_peer(lt::tcp::endpoint(
lt::address_v4::from_string(ep), 6881));
}
}
inline void check_tripped(std::array<fake_peer*, 5>& test_peers, std::array<bool, 5> expected)
{
int idx = 0;
for (auto p : test_peers)
{
TEST_EQUAL(p->tripped(), expected[idx]);
++idx;
}
}
#endif

View File

@ -41,47 +41,13 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/alert_types.hpp"
#include "simulator/simulator.hpp"
#include "simulator/utils.hpp"
#include "fake_peer.hpp"
#include "utils.hpp" // for print_alerts
using namespace sim;
namespace lt = libtorrent;
struct fake_peer
{
fake_peer(simulation& sim, char const* ip)
: m_ios(sim, asio::ip::address::from_string(ip))
, m_acceptor(m_ios)
, m_socket(m_ios)
, m_tripped(false)
{
boost::system::error_code ec;
m_acceptor.open(asio::ip::tcp::v4(), ec);
TEST_CHECK(!ec);
m_acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v4::any(), 6881), ec);
TEST_CHECK(!ec);
m_acceptor.listen(10, ec);
TEST_CHECK(!ec);
m_acceptor.async_accept(m_socket, [&] (boost::system::error_code const& ec)
{ if (!ec) m_tripped = true; });
}
void close()
{
m_acceptor.close();
m_socket.close();
}
bool tripped() const { return m_tripped; }
private:
asio::io_service m_ios;
asio::ip::tcp::acceptor m_acceptor;
asio::ip::tcp::socket m_socket;
bool m_tripped;
};
template <typename Setup, typename HandleAlerts, typename Test>
void run_test(Setup const& setup
, HandleAlerts const& on_alert
@ -134,28 +100,6 @@ void run_test(Setup const& setup
sim.run();
}
void add_fake_peers(lt::torrent_handle h)
{
// add the fake peers
for (int i = 0; i < 5; ++i)
{
char ep[30];
snprintf(ep, sizeof(ep), "60.0.0.%d", i);
h.connect_peer(lt::tcp::endpoint(
lt::address_v4::from_string(ep), 6881));
}
}
void check_tripped(std::array<fake_peer*, 5>& test_peers, std::array<bool, 5> expected)
{
int idx = 0;
for (auto p : test_peers)
{
TEST_EQUAL(p->tripped(), expected[idx]);
++idx;
}
}
void add_ip_filter(lt::session& ses)
{
lt::ip_filter filter;

306
simulation/test_socks5.cpp Normal file
View File

@ -0,0 +1,306 @@
/*
Copyright (c) 2016, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/session.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/settings_pack.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/deadline_timer.hpp"
#include "simulator/http_server.hpp"
#include "settings.hpp"
#include "create_torrent.hpp"
#include "simulator/simulator.hpp"
#include "setup_swarm.hpp"
#include "utils.hpp"
#include "simulator/socks_server.hpp"
#include "simulator/utils.hpp"
#include "fake_peer.hpp"
#include <iostream>
using namespace sim;
using namespace libtorrent;
namespace lt = libtorrent;
std::unique_ptr<sim::asio::io_service> make_io_service(sim::simulation& sim, int i)
{
char ep[30];
snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff);
return std::unique_ptr<sim::asio::io_service>(new sim::asio::io_service(
sim, address_v4::from_string(ep)));
}
// this is the general template for these tests. create the session with custom
// settings (Settings), set up the test, by adding torrents with certain
// arguments (Setup), run the test and verify the end state (Test)
template <typename Setup, typename HandleAlerts, typename Test>
void run_test(Setup const& setup
, HandleAlerts const& on_alert
, Test const& test)
{
// setup the simulation
sim::default_config network_cfg;
sim::simulation sim{network_cfg};
std::unique_ptr<sim::asio::io_service> ios = make_io_service(sim, 0);
lt::session_proxy zombie;
sim::asio::io_service proxy_ios{sim, addr("50.50.50.50") };
sim::socks_server socks4(proxy_ios, 4444, 4);
sim::socks_server socks5(proxy_ios, 5555, 5);
lt::settings_pack pack = settings();
// create session
std::shared_ptr<lt::session> ses = std::make_shared<lt::session>(pack, *ios);
// set up test, like adding torrents (customization point)
setup(*ses);
// only monitor alerts for session 0 (the downloader)
print_alerts(*ses, [=](lt::session& ses, lt::alert const* a) {
on_alert(ses, a);
});
lt::add_torrent_params params = create_torrent(1);
params.flags &= ~lt::add_torrent_params::flag_auto_managed;
params.flags &= ~lt::add_torrent_params::flag_paused;
params.save_path = save_path(0);
ses->async_add_torrent(params);
// set up a timer to fire later, to verify everything we expected to happen
// happened
sim::timer t(sim, lt::seconds(100), [&](boost::system::error_code const& ec)
{
fprintf(stderr, "shutting down\n");
// shut down
ses->set_alert_notify([] {});
zombie = ses->abort();
ses.reset();
});
test(sim, *ses, params.ti);
}
TORRENT_TEST(socks5_tcp_accept)
{
using namespace libtorrent;
bool incoming_connection = false;
run_test(
[](lt::session& ses)
{
set_proxy(ses, settings_pack::socks5);
},
[&](lt::session& ses, lt::alert const* alert) {
if (auto* a = lt::alert_cast<lt::incoming_connection_alert>(alert))
{
TEST_EQUAL(a->socket_type, 2);
incoming_connection = true;
}
},
[](sim::simulation& sim, lt::session& ses
, boost::shared_ptr<lt::torrent_info> ti)
{
// test connecting to the client via its socks5 listen port
// TODO: maybe we could use peer_conn here instead
fake_peer peer1(sim, "60.0.0.0");
fake_peer peer2(sim, "60.0.0.1");
sim::timer t1(sim, lt::seconds(2), [&](boost::system::error_code const& ec)
{
peer1.connect_to(tcp::endpoint(addr("50.50.50.50"), 6881), ti->info_hash());
});
sim::timer t2(sim, lt::seconds(3), [&](boost::system::error_code const& ec)
{
peer2.connect_to(tcp::endpoint(addr("50.50.50.50"), 6881), ti->info_hash());
});
sim.run();
}
);
TEST_EQUAL(incoming_connection, true);
}
TORRENT_TEST(socks4_tcp_accept)
{
using namespace libtorrent;
bool incoming_connection = false;
run_test(
[](lt::session& ses)
{
set_proxy(ses, settings_pack::socks4);
},
[&](lt::session& ses, lt::alert const* alert) {
if (auto* a = lt::alert_cast<lt::incoming_connection_alert>(alert))
{
TEST_EQUAL(a->socket_type, 2);
TEST_EQUAL(a->ip.address(), addr("60.0.0.0"))
incoming_connection = true;
}
},
[](sim::simulation& sim, lt::session& ses
, boost::shared_ptr<lt::torrent_info> ti)
{
fake_peer peer1(sim, "60.0.0.0");
sim::timer t1(sim, lt::seconds(2), [&](boost::system::error_code const& ec)
{
peer1.connect_to(tcp::endpoint(addr("50.50.50.50"), 6881), ti->info_hash());
});
sim.run();
}
);
TEST_EQUAL(incoming_connection, true);
}
// make sure a listen_succeeded_alert is issued when successfully listening on
// incoming connections via a socks5 proxy
TORRENT_TEST(socks4_tcp_listen_alert)
{
using namespace libtorrent;
bool listen_alert = false;
run_test(
[](lt::session& ses)
{
set_proxy(ses, settings_pack::socks4);
},
[&](lt::session& ses, lt::alert const* alert) {
if (auto* a = lt::alert_cast<lt::listen_succeeded_alert>(alert))
{
if (a->sock_type == listen_succeeded_alert::socks5)
{
TEST_EQUAL(a->endpoint.address(), addr("50.50.50.50"));
TEST_EQUAL(a->endpoint.port(), 6881);
listen_alert = true;
}
}
},
[](sim::simulation& sim, lt::session& ses
, boost::shared_ptr<lt::torrent_info> ti)
{
sim.run();
}
);
TEST_EQUAL(listen_alert, true);
}
TORRENT_TEST(socks5_tcp_listen_alert)
{
using namespace libtorrent;
bool listen_alert = false;
run_test(
[](lt::session& ses)
{
set_proxy(ses, settings_pack::socks5);
},
[&](lt::session& ses, lt::alert const* alert) {
if (auto* a = lt::alert_cast<lt::listen_succeeded_alert>(alert))
{
if (a->sock_type == listen_succeeded_alert::socks5)
{
TEST_EQUAL(a->endpoint.address(), addr("50.50.50.50"));
TEST_EQUAL(a->endpoint.port(), 6881);
listen_alert = true;
}
}
},
[](sim::simulation& sim, lt::session& ses
, boost::shared_ptr<lt::torrent_info> ti)
{
sim.run();
}
);
TEST_EQUAL(listen_alert, true);
}
TORRENT_TEST(socks5_tcp_announce)
{
using namespace libtorrent;
int tracker_port = -1;
int alert_port = -1;
run_test(
[](lt::session& ses)
{
set_proxy(ses, settings_pack::socks5);
lt::add_torrent_params params;
params.info_hash = sha1_hash("abababababababababab");
params.trackers.push_back("http://2.2.2.2:8080/announce");
params.save_path = ".";
ses.async_add_torrent(params);
},
[&alert_port](lt::session& ses, lt::alert const* alert) {
if (auto* a = lt::alert_cast<lt::listen_succeeded_alert>(alert))
{
if (a->sock_type == listen_succeeded_alert::socks5)
{
alert_port = a->endpoint.port();
}
}
},
[&tracker_port](sim::simulation& sim, lt::session& ses
, boost::shared_ptr<lt::torrent_info> ti)
{
sim::asio::io_service web_server(sim, address_v4::from_string("2.2.2.2"));
// listen on port 8080
sim::http_server http(web_server, 8080);
http.register_handler("/announce"
, [&tracker_port](std::string method, std::string req
, std::map<std::string, std::string>&)
{
if (req.find("&event=started") != std::string::npos)
{
std::string::size_type port_pos = req.find("&port=");
TEST_CHECK(port_pos != std::string::npos);
if (port_pos != std::string::npos)
{
tracker_port = atoi(req.c_str() + port_pos + 6);
}
}
return sim::send_response(200, "OK", 27) + "d8:intervali1800e5:peers0:e";
});
sim.run();
}
);
TEST_EQUAL(alert_port, tracker_port);
TEST_CHECK(alert_port != -1);
TEST_CHECK(tracker_port != -1);
}

View File

@ -68,7 +68,7 @@ void test_interval(int interval)
std::vector<int> announces;
http.register_handler("/announce"
, [&announces,interval,start](std::string method, std::string req
, [&announces,interval,start](std::string method, std::string req
, std::map<std::string, std::string>&)
{
boost::uint32_t seconds = chrono::duration_cast<lt::seconds>(
@ -414,7 +414,7 @@ void tracker_test(Setup setup, Announce a, Test1 test1, Test2 test2
});
sim::timer t2(sim, lt::seconds(5 + delay)
, [&ses,&test2](boost::system::error_code const& ec)
, [&ses,&test2](boost::system::error_code const& ec)
{
std::vector<lt::torrent_handle> torrents = ses->get_torrents();
TEST_EQUAL(torrents.size(), 1);

View File

@ -36,7 +36,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "settings.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/session_stats.hpp"
#include "libtorrent/deadline_timer.hpp"
#include "libtorrent/settings_pack.hpp"
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/alert_types.hpp"
@ -52,6 +51,8 @@ using namespace sim;
namespace lt = libtorrent;
const int connect_socks = 2;
template <typename Setup, typename HandleAlerts, typename Test>
void run_test(
Setup const& setup
@ -111,7 +112,8 @@ void run_test(
print_alerts(*ses[0], [=](lt::session& ses, lt::alert const* a) {
if (auto ta = alert_cast<lt::torrent_added_alert>(a))
{
ta->handle.connect_peer(lt::tcp::endpoint(peer1, 6881));
ta->handle.connect_peer(lt::tcp::endpoint(
(flags & connect_socks) ? proxy : peer1, 6881));
}
on_alert(ses, a);
});
@ -162,7 +164,7 @@ TORRENT_TEST(socks4_tcp)
);
}
TORRENT_TEST(socks5_tcp)
TORRENT_TEST(socks5_tcp_connect)
{
using namespace libtorrent;
run_test(
@ -178,6 +180,26 @@ TORRENT_TEST(socks5_tcp)
);
}
TORRENT_TEST(socks5_tcp_accept)
{
using namespace libtorrent;
run_test(
[](lt::session& ses0, lt::session& ses1)
{
// this time, the session accepting the connection is listening on a
// socks5 BIND session
set_proxy(ses1, settings_pack::socks5);
filter_ips(ses0);
},
[](lt::session& ses, lt::alert const* alert) {},
[](std::shared_ptr<lt::session> ses[2]) {
TEST_EQUAL(is_seed(*ses[0]), true);
},
connect_socks
);
}
TORRENT_TEST(encryption_tcp)
{
using namespace libtorrent;
@ -260,10 +282,8 @@ TORRENT_TEST(no_proxy_utp)
}
);
}
// TODO: the socks server does not support UDP yet
/*
// TOD: 3 figure out why this test is failing
TORRENT_TEST(encryption_utp)
{
using namespace libtorrent;
@ -276,7 +296,10 @@ TORRENT_TEST(encryption_utp)
}
);
}
*/
// TODO: the socks server does not support UDP yet
/*
TORRENT_TEST(socks5_utp)
{
using namespace libtorrent;

View File

@ -924,7 +924,7 @@ namespace libtorrent {
std::string listen_succeeded_alert::message() const
{
char const* type_str[] = { "TCP", "SSL/TCP", "UDP", "SSL/uTP" };
char const* type_str[] = { "TCP", "SSL/TCP", "UDP", "i2p", "socks5", "SSL/uTP" };
char ret[200];
snprintf(ret, sizeof(ret), "successfully listening on [%s] %s"
, type_str[sock_type], print_endpoint(endpoint).c_str());

View File

@ -65,6 +65,13 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
bool is_ip_address(char const* host)
{
error_code ec;
address::from_string(host, ec);
return !ec;
}
bool is_local(address const& a)
{
TORRENT_TRY {

View File

@ -2276,21 +2276,80 @@ retry:
if (m_socks_listen_socket) return;
m_socks_listen_socket = boost::shared_ptr<socket_type>(new socket_type(m_io_service));
m_socks_listen_socket = boost::make_shared<socket_type>(boost::ref(m_io_service));
bool const ret = instantiate_connection(m_io_service, proxy()
, *m_socks_listen_socket, NULL, NULL, false, false);
TORRENT_ASSERT_VAL(ret, ret);
TORRENT_UNUSED(ret);
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("session_impl::on_socks_listen");
#endif
socks5_stream& s = *m_socks_listen_socket->get<socks5_stream>();
m_socks_listen_port = m_listen_interface.port();
if (m_socks_listen_port == 0) m_socks_listen_port = 2000 + random() % 60000;
s.async_listen(tcp::endpoint(address_v4::any(), m_socks_listen_port)
, boost::bind(&session_impl::on_socks_listen, this
, m_socks_listen_socket, _1));
}
void session_impl::on_socks_listen(boost::shared_ptr<socket_type> const& sock
, error_code const& e)
{
#if defined TORRENT_ASIO_DEBUGGING
complete_async("session_impl::on_socks_listen");
#endif
TORRENT_ASSERT(sock == m_socks_listen_socket || !m_socks_listen_socket);
if (e)
{
m_socks_listen_socket.reset();
if (e == boost::asio::error::operation_aborted) return;
if (m_alerts.should_post<listen_failed_alert>())
m_alerts.emplace_alert<listen_failed_alert>("socks5"
, -1, listen_failed_alert::accept, e
, listen_failed_alert::socks5);
return;
}
error_code ec;
tcp::endpoint ep = sock->local_endpoint(ec);
TORRENT_ASSERT(!ec);
TORRENT_UNUSED(ec);
if (m_alerts.should_post<listen_succeeded_alert>())
m_alerts.emplace_alert<listen_succeeded_alert>(
ep, listen_succeeded_alert::socks5);
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("session_impl::on_socks_accept");
#endif
socks5_stream& s = *m_socks_listen_socket->get<socks5_stream>();
s.set_command(2); // 2 means BIND (as opposed to CONNECT)
m_socks_listen_port = m_listen_interface.port();
if (m_socks_listen_port == 0) m_socks_listen_port = 2000 + random() % 60000;
s.async_connect(tcp::endpoint(address_v4::any(), m_socks_listen_port)
, boost::bind(&session_impl::on_socks_accept, this, m_socks_listen_socket, _1));
s.async_accept(boost::bind(&session_impl::on_socks_accept, this
, m_socks_listen_socket, _1));
}
void session_impl::on_socks_accept(boost::shared_ptr<socket_type> const& s
, error_code const& e)
{
#if defined TORRENT_ASIO_DEBUGGING
complete_async("session_impl::on_socks_accept");
#endif
TORRENT_ASSERT(s == m_socks_listen_socket || !m_socks_listen_socket);
m_socks_listen_socket.reset();
if (e == boost::asio::error::operation_aborted) return;
if (e)
{
if (m_alerts.should_post<listen_failed_alert>())
m_alerts.emplace_alert<listen_failed_alert>("socks5"
, -1, listen_failed_alert::accept, e
, listen_failed_alert::socks5);
return;
}
open_new_incoming_socks_connection();
incoming_connection(s);
}
void session_impl::update_i2p_bridge()
@ -2842,26 +2901,6 @@ retry:
set_socket_buffer_size(s, m_settings, ec);
}
void session_impl::on_socks_accept(boost::shared_ptr<socket_type> const& s
, error_code const& e)
{
#if defined TORRENT_ASIO_DEBUGGING
complete_async("session_impl::on_socks_accept");
#endif
m_socks_listen_socket.reset();
if (e == boost::asio::error::operation_aborted) return;
if (e)
{
if (m_alerts.should_post<listen_failed_alert>())
m_alerts.emplace_alert<listen_failed_alert>("socks5"
, -1, listen_failed_alert::accept, e
, listen_failed_alert::socks5);
return;
}
open_new_incoming_socks_connection();
incoming_connection(s);
}
// if cancel_with_cq is set, the peer connection is
// currently expected to be scheduled for a connection
// with the connection queue, and should be cancelled
@ -5426,7 +5465,7 @@ retry:
// proxy, and it's the same one as we're using for the tracker
// just tell the tracker the socks5 port we're listening on
if (m_socks_listen_socket && m_socks_listen_socket->is_open())
return m_socks_listen_port;
return m_socks_listen_socket->local_endpoint().port();
// if not, don't tell the tracker anything if we're in force_proxy
// mode. We don't want to leak our listen port since it can

View File

@ -79,6 +79,56 @@ namespace libtorrent
return socks_category;
}
namespace
{
// parse out the endpoint from a SOCKS response
tcp::endpoint parse_endpoint(std::vector<char> const& buffer
, int const version)
{
using namespace libtorrent::detail;
char const* p = &buffer[0];
p += 2; // version & response code
if (version == 5)
{
++p; // reserved byte
int const atyp = read_uint8(p);
if (atyp == 1)
{
tcp::endpoint ret;
ret.address(read_v4_address(p));
ret.port(read_uint16(p));
return ret;
}
else if (atyp == 3)
{
// we don't support resolving the endpoint address
// if we receive a domain name, just set the remote
// endpoint to INADDR_ANY
return tcp::endpoint();
}
else if (atyp == 4)
{
tcp::endpoint ret;
#if TORRENT_USE_IPV6
ret.address(read_v6_address(p));
ret.port(read_uint16(p));
#endif
return ret;
}
}
else if (version == 4)
{
tcp::endpoint ret;
ret.port(read_uint16(p));
ret.address(read_v4_address(p));
return ret;
}
TORRENT_ASSERT(false);
return tcp::endpoint();
}
}
void socks5_stream::name_lookup(error_code const& e, tcp::resolver::iterator i
, boost::shared_ptr<handler_type> h)
{
@ -350,10 +400,9 @@ namespace libtorrent
using namespace libtorrent::detail;
// send SOCKS5 connect command
char* p = &m_buffer[0];
int version = read_uint8(p);
int response = read_uint8(p);
char const* p = &m_buffer[0];
int const version = read_uint8(p);
int const response = read_uint8(p);
if (m_version == 5)
{
@ -379,23 +428,22 @@ namespace libtorrent
return;
}
p += 1; // reserved
int atyp = read_uint8(p);
// we ignore the proxy IP it was bound to
int const atyp = read_uint8(p);
// read the proxy IP it was bound to (this is variable length depending
// on address type)
if (atyp == 1)
{
if (m_command == socks5_bind)
{
if (m_listen == 0)
{
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("socks5_stream::connect1");
#endif
m_local_endpoint = parse_endpoint(m_buffer, m_version);
m_listen = 1;
connect1(e, h);
return;
}
m_remote_endpoint.address(read_v4_address(p));
m_remote_endpoint.port(read_uint16(p));
else
{
m_remote_endpoint = parse_endpoint(m_buffer, m_version);
}
std::vector<char>().swap(m_buffer);
(*h)(e);
}
@ -409,10 +457,12 @@ namespace libtorrent
int extra_bytes = 0;
if (atyp == 4)
{
// IPv6
extra_bytes = 12;
}
else if (atyp == 3)
{
// hostname with length prefix
extra_bytes = read_uint8(p) - 3;
}
else
@ -444,15 +494,13 @@ namespace libtorrent
{
if (m_listen == 0)
{
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("socks5_stream::connect1");
#endif
m_local_endpoint = parse_endpoint(m_buffer, m_version);
m_listen = 1;
connect1(e, h);
return;
}
m_remote_endpoint.address(read_v4_address(p));
m_remote_endpoint.port(read_uint16(p));
else
{
m_remote_endpoint = parse_endpoint(m_buffer, m_version);
}
std::vector<char>().swap(m_buffer);
(*h)(e);
}
@ -488,29 +536,12 @@ namespace libtorrent
{
if (m_listen == 0)
{
#if defined TORRENT_ASIO_DEBUGGING
add_outstanding_async("socks5_stream::connect1");
#endif
m_local_endpoint = parse_endpoint(m_buffer, m_version);
m_listen = 1;
connect1(e, h);
return;
}
char* p = &m_buffer[0];
p += 2; // version and response code
int atyp = read_uint8(p);
TORRENT_ASSERT(atyp == 3 || atyp == 4);
if (atyp == 4)
else
{
// we don't support resolving the endpoint address
// if we receive a domain name, just set the remote
// endpoint to INADDR_ANY
m_remote_endpoint = tcp::endpoint();
}
else if (atyp == 3)
{
m_remote_endpoint.address(read_v4_address(p));
m_remote_endpoint.port(read_uint16(p));
m_remote_endpoint = parse_endpoint(m_buffer, m_version);
}
}
std::vector<char>().swap(m_buffer);

View File

@ -98,6 +98,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/file_progress.hpp"
#include "libtorrent/alert_manager.hpp"
#include "libtorrent/disk_interface.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_ip_address
// TODO: factor out cache_status to its own header
#include "libtorrent/disk_io_thread.hpp" // for cache_status
@ -3153,7 +3154,7 @@ namespace libtorrent
// if we are aborting. we don't want any new peers
req.num_want = (req.event == tracker_request::stopped)
?0:settings().get_int(settings_pack::num_want);
? 0 : settings().get_int(settings_pack::num_want);
time_point now = clock_type::now();
@ -6572,7 +6573,10 @@ namespace libtorrent
return;
}
bool proxy_hostnames = settings().get_bool(settings_pack::proxy_hostnames);
bool const is_ip = is_ip_address(hostname.c_str());
if (is_ip) a.address(address::from_string(hostname.c_str(), ec));
bool const proxy_hostnames = settings().get_bool(settings_pack::proxy_hostnames)
&& !is_ip;
if (proxy_hostnames
&& (s->get<socks5_stream>()

View File

@ -80,7 +80,7 @@ private:
char write_buf_proto[100];
boost::uint32_t write_buffer[17*1024/4];
boost::uint32_t buffer[17*1024/4];
peer_mode_t m_mode;
torrent_info const& m_ti;