merged RC_1_1 into master

This commit is contained in:
arvidn 2016-05-06 01:08:05 -04:00
commit 6cf5ac9dd1
26 changed files with 928 additions and 202 deletions

View File

@ -14,6 +14,8 @@
1.1.1 release
* added a new "preformatted" type to bencode entry variant type
* 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

@ -1322,7 +1322,7 @@ namespace libtorrent
// was opened for listening.
struct TORRENT_EXPORT listen_succeeded_alert 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
@ -1747,8 +1747,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 final : alert
{
// internal

View File

@ -256,6 +256,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

@ -210,6 +210,10 @@ namespace libtorrent
write_char(out, 'e');
ret += 2;
break;
case entry::preformatted_t:
std::copy(e.preformatted().begin(), e.preformatted().end(), out);
ret += e.preformatted().size();
break;
case entry::undefined_t:
// trying to encode a structure with uninitialized values!
// TORRENT_ASSERT_VAL(false, e.type());

View File

@ -50,11 +50,13 @@ 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_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

@ -108,6 +108,7 @@ namespace libtorrent
typedef std::string string_type;
typedef std::list<entry> list_type;
typedef boost::int64_t integer_type;
typedef std::vector<char> preformatted_type;
// the types an entry can have
enum data_type
@ -116,7 +117,8 @@ namespace libtorrent
string_t,
list_t,
dictionary_t,
undefined_t
undefined_t,
preformatted_t
};
// returns the concrete type of the entry
@ -129,6 +131,7 @@ namespace libtorrent
entry(string_type const&);
entry(list_type const&);
entry(integer_type const&);
entry(preformatted_type const&);
// construct an empty entry of the specified type.
// see data_type enum.
@ -160,6 +163,7 @@ namespace libtorrent
entry& operator=(string_type const&);
entry& operator=(list_type const&);
entry& operator=(integer_type const&);
entry& operator=(preformatted_type const&);
// The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions
// are accessors that return the respective type. If the ``entry`` object
@ -216,6 +220,8 @@ namespace libtorrent
const list_type& list() const;
dictionary_type& dict();
const dictionary_type& dict() const;
preformatted_type& preformatted();
const preformatted_type& preformatted() const;
// swaps the content of *this* with ``e``.
void swap(entry& e);
@ -268,17 +274,19 @@ namespace libtorrent
// assumes sizeof(map<string, char>) == sizeof(map<string, entry>)
// and sizeof(list<char>) == sizeof(list<entry>)
enum { union_size
= max4<sizeof(std::list<char>)
= max5<sizeof(std::list<char>)
, sizeof(std::map<std::string, char>)
, sizeof(string_type)
, sizeof(integer_type)>::value
, sizeof(integer_type)
, sizeof(preformatted_type)>::value
};
#else
enum { union_size
= max4<sizeof(list_type)
= max5<sizeof(list_type)
, sizeof(dictionary_type)
, sizeof(string_type)
, sizeof(integer_type)>::value
, sizeof(integer_type)
, sizeof(preformatted_type)>::value
};
#endif
integer_type data[(union_size + sizeof(integer_type) - 1)

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"
#include "libtorrent/debug.hpp"
@ -107,19 +109,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).data());
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);
@ -139,14 +165,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;
@ -190,6 +229,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

@ -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

@ -919,7 +919,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

@ -62,6 +62,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

@ -416,7 +416,9 @@ namespace libtorrent
m_piece_hash.resize(m_files.num_pieces());
for (int i = 0; i < num_pieces(); ++i) set_hash(i, ti.hash_for_piece(i));
m_info_dict = bdecode(&ti.metadata()[0], &ti.metadata()[0] + ti.metadata_size());
boost::shared_array<char> const info = ti.metadata();
int const size = ti.metadata_size();
m_info_dict.preformatted().assign(&info[0], &info[0] + size);
m_info_hash = ti.info_hash();
}
@ -508,7 +510,8 @@ namespace libtorrent
}
entry& info = dict["info"];
if (m_info_dict.type() == entry::dictionary_t)
if (m_info_dict.type() == entry::dictionary_t
|| m_info_dict.type() == entry::preformatted_t)
{
info = m_info_dict;
return dict;

View File

@ -158,6 +158,7 @@ namespace libtorrent
entry& entry::operator=(const entry& e)
{
if (&e == this) return *this;
destruct();
copy(e);
return *this;
@ -261,6 +262,29 @@ namespace libtorrent
return *reinterpret_cast<const dictionary_type*>(data);
}
entry::preformatted_type& entry::preformatted()
{
if (m_type == undefined_t) construct(preformatted_t);
#ifndef BOOST_NO_EXCEPTIONS
if (m_type != preformatted_t) throw_type_error();
#elif defined TORRENT_DEBUG
TORRENT_ASSERT(m_type_queried);
#endif
TORRENT_ASSERT(m_type == preformatted_t);
return *reinterpret_cast<preformatted_type*>(data);
}
entry::preformatted_type const& entry::preformatted() const
{
#ifndef BOOST_NO_EXCEPTIONS
if (m_type != preformatted_t) throw_type_error();
#elif defined TORRENT_DEBUG
TORRENT_ASSERT(m_type_queried);
#endif
TORRENT_ASSERT(m_type == preformatted_t);
return *reinterpret_cast<const preformatted_type*>(data);
}
entry::entry()
: m_type(undefined_t)
{
@ -339,6 +363,16 @@ namespace libtorrent
m_type = int_t;
}
entry::entry(preformatted_type const& v)
: m_type(undefined_t)
{
#ifdef TORRENT_DEBUG
m_type_queried = true;
#endif
new(data) preformatted_type(v);
m_type = preformatted_t;
}
// convert a bdecode_node into an old skool entry
entry& entry::operator=(bdecode_node const& e)
{
@ -417,6 +451,17 @@ namespace libtorrent
}
#endif
entry& entry::operator=(preformatted_type const& v)
{
destruct();
new(data) preformatted_type(v);
m_type = preformatted_t;
#ifdef TORRENT_DEBUG
m_type_queried = true;
#endif
return *this;
}
entry& entry::operator=(dictionary_type const& v)
{
destruct();
@ -475,6 +520,8 @@ namespace libtorrent
return list() == e.list();
case dictionary_t:
return dict() == e.dict();
case preformatted_t:
return preformatted() == e.preformatted();
default:
TORRENT_ASSERT(m_type == undefined_t);
return true;
@ -499,6 +546,9 @@ namespace libtorrent
break;
case undefined_t:
break;
case preformatted_t:
new (data) preformatted_type;
break;
}
m_type = t;
#ifdef TORRENT_DEBUG
@ -524,6 +574,10 @@ namespace libtorrent
break;
case undefined_t:
TORRENT_ASSERT(e.type() == undefined_t);
break;
case preformatted_t:
new (data) preformatted_type(e.preformatted());
break;
}
m_type = e.type();
#ifdef TORRENT_DEBUG
@ -547,6 +601,9 @@ namespace libtorrent
case dictionary_t:
call_destructor(reinterpret_cast<dictionary_type*>(data));
break;
case preformatted_t:
call_destructor(reinterpret_cast<preformatted_type*>(data));
break;
default:
TORRENT_ASSERT(m_type == undefined_t);
break;
@ -597,6 +654,10 @@ namespace libtorrent
std::swap(*reinterpret_cast<dictionary_type*>(data)
, *reinterpret_cast<dictionary_type*>(e.data));
break;
case preformatted_t:
std::swap(*reinterpret_cast<preformatted_type*>(data)
, *reinterpret_cast<preformatted_type*>(e.data));
break;
default:
break;
}
@ -689,6 +750,9 @@ namespace libtorrent
i->second.to_string_impl(out, indent+2);
}
} break;
case preformatted_t:
out += "<preformatted>\n";
break;
case undefined_t:
default:
out += "<uninitialized>\n";

View File

@ -2038,19 +2038,76 @@ namespace aux {
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);
ADD_OUTSTANDING_ASYNC("session_impl::on_socks_accept");
ADD_OUTSTANDING_ASYNC("session_impl::on_socks_listen");
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 = 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));
m_socks_listen_port = listen_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"
, tcp::endpoint(), 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.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)
{
COMPLETE_ASYNC("session_impl::on_socks_accept");
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"
, tcp::endpoint(), listen_failed_alert::accept, e
, listen_failed_alert::socks5);
return;
}
open_new_incoming_socks_connection();
incoming_connection(s);
}
void session_impl::update_i2p_bridge()
@ -2805,24 +2862,6 @@ namespace aux {
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)
{
COMPLETE_ASYNC("session_impl::on_socks_accept");
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"
, tcp::endpoint(), 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
@ -5245,7 +5284,7 @@ namespace aux {
// 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)
{
@ -322,10 +372,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)
{
@ -351,21 +400,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)
{
ADD_OUTSTANDING_ASYNC("socks5_stream::connect1");
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);
}
@ -379,10 +429,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
@ -412,13 +464,13 @@ namespace libtorrent
{
if (m_listen == 0)
{
ADD_OUTSTANDING_ASYNC("socks5_stream::connect1");
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);
}
@ -452,27 +504,12 @@ namespace libtorrent
{
if (m_listen == 0)
{
ADD_OUTSTANDING_ASYNC("socks5_stream::connect1");
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
@ -4716,10 +4717,6 @@ namespace libtorrent
update_want_tick();
update_want_scrape();
update_gauge();
// if the torrent is paused, it doesn't need
// to announce with even=stopped again.
if (!is_paused())
stop_announcing();
error_code ec;
@ -6326,7 +6323,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>()
@ -6494,8 +6494,11 @@ namespace libtorrent
if (valid_metadata())
{
if (m_magnet_link || (m_save_resume_flags & torrent_handle::save_info_dict))
ret["info"] = bdecode(&torrent_file().metadata()[0]
, &torrent_file().metadata()[0] + torrent_file().metadata_size());
{
boost::shared_array<char> const info = torrent_file().metadata();
int const size = torrent_file().metadata_size();
ret["info"].preformatted().assign(&info[0], &info[0] + size);
}
}
// blocks per piece
@ -8284,7 +8287,7 @@ namespace libtorrent
TORRENT_ASSERT(want_peers_download() == m_links[aux::session_interface::torrent_want_peers_download].in_list());
TORRENT_ASSERT(want_peers_finished() == m_links[aux::session_interface::torrent_want_peers_finished].in_list());
TORRENT_ASSERT(want_tick() == m_links[aux::session_interface::torrent_want_tick].in_list());
TORRENT_ASSERT((!m_allow_peers && m_auto_managed) == m_links[aux::session_interface::torrent_want_scrape].in_list());
TORRENT_ASSERT((!m_allow_peers && m_auto_managed && !m_abort) == m_links[aux::session_interface::torrent_want_scrape].in_list());
bool is_checking = false;
bool is_downloading = false;

View File

@ -1749,7 +1749,7 @@ private:
// send_pkt() again)
// returns true if there is more space for payload in our
// congestion window, false if there is no more space.
bool utp_socket_impl::send_pkt(int flags)
bool utp_socket_impl::send_pkt(int const flags)
{
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
INVARIANT_CHECK;
@ -2028,7 +2028,7 @@ bool utp_socket_impl::send_pkt(int flags)
// for ST_DATA packets, payload size is 0. Such packets do not have unique
// sequence numbers and should never be used as mtu probes
if ((mtu_probe || p->mtu_probe) && payload_size > 0)
if ((mtu_probe || p->mtu_probe) && payload_size > m_mtu_floor)
{
p->mtu_probe = true;
m_mtu_seq = m_seq_nr;

View File

@ -112,6 +112,7 @@ feature.compose <launcher>valgrind : <testing.launcher>"valgrind --tool=memcheck
test-suite libtorrent :
[ run
test_primitives.cpp
test_create_torrent.cpp
test_packet_buffer.cpp
test_timestamp_history.cpp
test_sha1_hash.cpp

View File

@ -27,6 +27,7 @@ test_programs = \
test_torrent \
test_tracker \
test_transfer \
test_create_torrent \
enum_if \
test_utp \
test_session \
@ -207,6 +208,7 @@ test_ssl_SOURCES = test_ssl.cpp
test_torrent_SOURCES = test_torrent.cpp
test_tracker_SOURCES = test_tracker.cpp
test_transfer_SOURCES = test_transfer.cpp
test_create_torrent_SOURCES = test_create_torrent.cpp
enum_if_SOURCES = enum_if.cpp
test_utp_SOURCES = test_utp.cpp
test_session_SOURCES = test_session.cpp

View File

@ -58,35 +58,35 @@ entry decode(std::string const& str)
return bdecode(str.begin(), str.end());
}
TORRENT_TEST(bencoding)
{
// ** strings **
TORRENT_TEST(strings)
{
entry e("spam");
TEST_CHECK(encode(e) == "4:spam");
TEST_CHECK(decode(encode(e)) == e);
}
// ** integers **
TORRENT_TEST(integers)
{
entry e(3);
TEST_CHECK(encode(e) == "i3e");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(integers2)
{
entry e(-3);
TEST_CHECK(encode(e) == "i-3e");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(integers3)
{
entry e(int(0));
TEST_CHECK(encode(e) == "i0e");
TEST_CHECK(decode(encode(e)) == e);
}
// ** lists **
TORRENT_TEST(lists)
{
entry::list_type l;
l.push_back(entry("spam"));
@ -96,7 +96,7 @@ TORRENT_TEST(bencoding)
TEST_CHECK(decode(encode(e)) == e);
}
// ** dictionaries **
TORRENT_TEST(dictionaries)
{
entry e(entry::dictionary_t);
e["spam"] = entry("eggs");
@ -105,7 +105,25 @@ TORRENT_TEST(bencoding)
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(preformatted)
{
entry e(entry::preformatted_t);
char const str[] = "foobar";
e.preformatted().assign(str, str + sizeof(str)-1);
TEST_EQUAL(encode(e), "foobar");
}
TORRENT_TEST(preformatted_node)
{
entry e(entry::dictionary_t);
char const str[] = "foobar";
e["info"] = entry::preformatted_type(str, str + sizeof(str)-1);
TEST_EQUAL(encode(e), "d4:infofoobare");
}
#ifndef TORRENT_NO_DEPRECATE
TORRENT_TEST(lazy_entry)
{
{
char b[] = "i12453e";
lazy_entry e;
@ -609,8 +627,6 @@ TORRENT_TEST(bencoding)
printf("%s\n", print_entry(e).c_str());
}
}
#endif // TORRENT_NO_DEPRECATE
}
#endif // TORRENT_NO_DEPRECATE

View File

@ -0,0 +1,67 @@
/*
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 "test.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/create_torrent.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/aux_/escape_string.hpp" // for convert_path_to_posix
#include <boost/make_shared.hpp>
#include <cstring>
namespace lt = libtorrent;
// make sure creating a torrent from an existing handle preserves the
// info-dictionary verbatim, so as to not alter the info-hash
TORRENT_TEST(create_verbatim_torrent)
{
char const test_torrent[] = "d4:infod4:name6:foobar6:lengthi12345e12:piece lengthi65536e6:pieces20:ababababababababababee";
lt::torrent_info info(test_torrent, sizeof(test_torrent) - 1);
lt::create_torrent t(info);
std::vector<char> buffer;
lt::bencode(std::back_inserter(buffer), t.generate());
// now, make sure the info dictionary was unchanged
buffer.push_back('\0');
char const* dest_info = std::strstr(&buffer[0], "4:info");
TEST_CHECK(dest_info != NULL);
// +1 and -2 here is to strip the outermost dictionary from the source
// torrent, since create_torrent may have added items next to the info dict
TEST_CHECK(memcmp(dest_info, test_torrent + 1, sizeof(test_torrent)-3) == 0);
}