forked from premiere/premiere-libtorrent
merged RC_1_1 into master
This commit is contained in:
commit
69dc73914a
|
@ -79,6 +79,8 @@
|
|||
|
||||
1.1.5 release
|
||||
|
||||
* fix leak of peer_class objects (when setting per-torrent rate limits)
|
||||
* expose peer_class API to python binding
|
||||
* fix integer overflow in whole_pieces_threshold logic
|
||||
* fix uTP path MTU discovery issue on windows (DF bit was not set correctly)
|
||||
* fix python binding for torrent_handle, to be hashable
|
||||
|
|
3
Jamfile
3
Jamfile
|
@ -393,9 +393,8 @@ rule tag ( name : type ? : property-set )
|
|||
feature ipv6 : on off : composite propagated link-incompatible ;
|
||||
feature.compose <ipv6>off : <define>TORRENT_USE_IPV6=0 ;
|
||||
|
||||
feature sanitize : off address memory bounds undefined thread rtc : composite propagated link-incompatible ;
|
||||
feature sanitize : off address memory undefined thread rtc : composite propagated link-incompatible ;
|
||||
# sanitize is a clang and GCC feature
|
||||
feature.compose <sanitize>bounds : <cflags>-fsanitize=bounds <cflags>-fsanitize-undefined-trap-on-error <linkflags>-fsanitize-undefined-trap-on-error ;
|
||||
feature.compose <sanitize>undefined : <cflags>-fsanitize=undefined <cflags>-fsanitize-undefined-trap-on-error <linkflags>-fsanitize=undefined <linkflags>-fsanitize-undefined-trap-on-error ;
|
||||
feature.compose <sanitize>thread : <cflags>-fsanitize=thread <linkflags>-fsanitize=thread ;
|
||||
feature.compose <sanitize>address : <cflags>-fsanitize=address <linkflags>-fsanitize=address ;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libtorrent/alert.hpp"
|
||||
#include "libtorrent/create_torrent.hpp" // for create_flags_t
|
||||
#include "libtorrent/portmap.hpp" // for port_mapping_t
|
||||
#include "libtorrent/peer_class.hpp"
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::python;
|
||||
|
@ -301,6 +302,7 @@ void bind_converters()
|
|||
to_python_converter<lt::piece_index_t, from_strong_typedef<lt::piece_index_t>>();
|
||||
to_python_converter<lt::file_index_t, from_strong_typedef<lt::file_index_t>>();
|
||||
to_python_converter<lt::port_mapping_t, from_strong_typedef<lt::port_mapping_t>>();
|
||||
to_python_converter<lt::peer_class_t, from_strong_typedef<lt::peer_class_t>>();
|
||||
to_python_converter<lt::torrent_flags_t, from_bitfield_flag<lt::torrent_flags_t>>();
|
||||
to_python_converter<lt::peer_flags_t, from_bitfield_flag<lt::peer_flags_t>>();
|
||||
to_python_converter<lt::peer_source_flags_t, from_bitfield_flag<lt::peer_source_flags_t>>();
|
||||
|
@ -361,6 +363,7 @@ void bind_converters()
|
|||
to_strong_typedef<lt::piece_index_t>();
|
||||
to_strong_typedef<lt::file_index_t>();
|
||||
to_strong_typedef<lt::port_mapping_t>();
|
||||
to_strong_typedef<lt::peer_class_t>();
|
||||
to_bitfield_flag<lt::torrent_flags_t>();
|
||||
to_bitfield_flag<lt::peer_flags_t>();
|
||||
to_bitfield_flag<lt::peer_source_flags_t>();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <libtorrent/time.hpp>
|
||||
#include <libtorrent/session_stats.hpp>
|
||||
#include <libtorrent/session_status.hpp>
|
||||
#include <libtorrent/peer_class_type_filter.hpp>
|
||||
|
||||
#include <libtorrent/extensions/smart_ban.hpp>
|
||||
#include <libtorrent/extensions/ut_metadata.hpp>
|
||||
|
@ -512,6 +513,72 @@ namespace
|
|||
ses.load_state(e, save_state_flags_t(flags));
|
||||
}
|
||||
|
||||
dict get_peer_class(lt::session& ses, lt::peer_class_t const pc)
|
||||
{
|
||||
lt::peer_class_info pci;
|
||||
{
|
||||
allow_threading_guard guard;
|
||||
pci = ses.get_peer_class(pc);
|
||||
}
|
||||
dict ret;
|
||||
ret["ignore_unchoke_slots"] = pci.ignore_unchoke_slots;
|
||||
ret["connection_limit_factor"] = pci.connection_limit_factor;
|
||||
ret["label"] = pci.label;
|
||||
ret["upload_limit"] = pci.upload_limit;
|
||||
ret["download_limit"] = pci.download_limit;
|
||||
ret["upload_priority"] = pci.upload_priority;
|
||||
ret["download_priority"] = pci.download_priority;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_peer_class(lt::session& ses, peer_class_t const pc, dict info)
|
||||
{
|
||||
lt::peer_class_info pci;
|
||||
stl_input_iterator<std::string> i(info.keys()), end;
|
||||
for (; i != end; ++i)
|
||||
{
|
||||
std::string const key = *i;
|
||||
|
||||
object const value = info[key];
|
||||
if (key == "ignore_unchoke_slots")
|
||||
{
|
||||
pci.ignore_unchoke_slots = extract<bool>(value);
|
||||
}
|
||||
else if (key == "connection_limit_factor")
|
||||
{
|
||||
pci.connection_limit_factor = extract<int>(value);
|
||||
}
|
||||
else if (key == "label")
|
||||
{
|
||||
pci.label = extract<std::string>(value);
|
||||
}
|
||||
else if (key == "upload_limit")
|
||||
{
|
||||
pci.upload_limit = extract<int>(value);
|
||||
}
|
||||
else if (key == "download_limit")
|
||||
{
|
||||
pci.download_limit = extract<int>(value);
|
||||
}
|
||||
else if (key == "upload_priority")
|
||||
{
|
||||
pci.upload_priority = extract<int>(value);
|
||||
}
|
||||
else if (key == "download_priority")
|
||||
{
|
||||
pci.download_priority = extract<int>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_KeyError, ("unknown name in peer_class_info: " + key).c_str());
|
||||
throw_error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
allow_threading_guard guard;
|
||||
ses.set_peer_class(pc, pci);
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
void dht_get_mutable_item(lt::session& ses, std::string key, std::string salt)
|
||||
{
|
||||
|
@ -567,7 +634,7 @@ namespace
|
|||
return lt::find_metric_idx(name);
|
||||
}
|
||||
|
||||
} // namespace unnamed
|
||||
} // anonymous namespace
|
||||
|
||||
struct dummy1 {};
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
|
@ -813,6 +880,23 @@ void bind_session()
|
|||
.value("upnp", lt::portmap_transport::upnp)
|
||||
;
|
||||
|
||||
class_<lt::peer_class_type_filter>("peer_class_type_filter")
|
||||
.def(init<>())
|
||||
.def("add", <::peer_class_type_filter::add)
|
||||
.def("remove", <::peer_class_type_filter::remove)
|
||||
.def("disallow", <::peer_class_type_filter::disallow)
|
||||
.def("allow", <::peer_class_type_filter::allow)
|
||||
.def("apply", <::peer_class_type_filter::apply)
|
||||
;
|
||||
|
||||
enum_<lt::peer_class_type_filter::socket_type_t>("socket_type_t")
|
||||
.value("tcp_socket", peer_class_type_filter::tcp_socket)
|
||||
.value("utp_socket", peer_class_type_filter::utp_socket)
|
||||
.value("ssl_tcp_socket", peer_class_type_filter::ssl_tcp_socket)
|
||||
.value("ssl_utp_socket", peer_class_type_filter::ssl_utp_socket)
|
||||
.value("i2p_socket", peer_class_type_filter::i2p_socket)
|
||||
;
|
||||
|
||||
{
|
||||
scope s = class_<lt::session, boost::noncopyable>("session", no_init)
|
||||
.def("__init__", boost::python::make_constructor(&make_session
|
||||
|
@ -902,6 +986,12 @@ void bind_session()
|
|||
.def("get_cache_info", &get_cache_info1, (arg("handle") = torrent_handle(), arg("flags") = 0))
|
||||
.def("add_port_mapping", allow_threads(<::session::add_port_mapping))
|
||||
.def("delete_port_mapping", allow_threads(<::session::delete_port_mapping))
|
||||
.def("set_peer_class_filter", <::session::set_peer_class_filter)
|
||||
.def("set_peer_class_type_filter", <::session::set_peer_class_type_filter)
|
||||
.def("create_peer_class", <::session::create_peer_class)
|
||||
.def("delete_peer_class", <::session::delete_peer_class)
|
||||
.def("get_peer_class", &get_peer_class)
|
||||
.def("set_peer_class", &set_peer_class)
|
||||
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
.def(
|
||||
|
@ -954,6 +1044,10 @@ void bind_session()
|
|||
|
||||
s.attr("tcp") = lt::portmap_protocol::tcp;
|
||||
s.attr("udp") = lt::portmap_protocol::udp;
|
||||
|
||||
s.attr("global_peer_class_id") = session::global_peer_class_id;
|
||||
s.attr("tcp_peer_class_id") = session::tcp_peer_class_id;
|
||||
s.attr("local_peer_class_id") = session::local_peer_class_id;
|
||||
}
|
||||
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
|
|
|
@ -417,6 +417,59 @@ class test_magnet_link(unittest.TestCase):
|
|||
h = ses.add_torrent(p)
|
||||
self.assertEqual(str(h.info_hash()), '178882f042c0c33426a6d81e0333ece346e68a68')
|
||||
|
||||
class test_peer_class(unittest.TestCase):
|
||||
|
||||
def test_peer_class_ids(self):
|
||||
s = lt.session({'enable_dht': False})
|
||||
|
||||
print('global_peer_class_id:', lt.session.global_peer_class_id)
|
||||
print('tcp_peer_class_id:', lt.session.tcp_peer_class_id)
|
||||
print('local_peer_class_id:', lt.session.local_peer_class_id)
|
||||
|
||||
print('global: ', s.get_peer_class(s.global_peer_class_id))
|
||||
print('tcp: ', s.get_peer_class(s.local_peer_class_id))
|
||||
print('local: ', s.get_peer_class(s.local_peer_class_id))
|
||||
|
||||
def test_peer_class(self):
|
||||
s = lt.session({'enable_dht': False})
|
||||
|
||||
c = s.create_peer_class('test class')
|
||||
print('new class: ', s.get_peer_class(c))
|
||||
|
||||
nfo = s.get_peer_class(c)
|
||||
self.assertEqual(nfo['download_limit'], 0)
|
||||
self.assertEqual(nfo['upload_limit'], 0)
|
||||
self.assertEqual(nfo['ignore_unchoke_slots'], False)
|
||||
self.assertEqual(nfo['connection_limit_factor'], 100)
|
||||
self.assertEqual(nfo['download_priority'], 1)
|
||||
self.assertEqual(nfo['upload_priority'], 1)
|
||||
self.assertEqual(nfo['label'], 'test class')
|
||||
|
||||
nfo['download_limit'] = 1337
|
||||
nfo['upload_limit'] = 1338
|
||||
nfo['ignore_unchoke_slots'] = True
|
||||
nfo['connection_limit_factor'] = 42
|
||||
nfo['download_priority'] = 2
|
||||
nfo['upload_priority'] = 3
|
||||
|
||||
s.set_peer_class(c, nfo)
|
||||
|
||||
nfo2 = s.get_peer_class(c)
|
||||
self.assertEqual(nfo, nfo2)
|
||||
|
||||
def test_peer_class_filter(self):
|
||||
filt = lt.peer_class_type_filter()
|
||||
filt.add(lt.socket_type_t.tcp_socket, lt.session.global_peer_class_id);
|
||||
filt.remove(lt.socket_type_t.utp_socket, lt.session.local_peer_class_id);
|
||||
|
||||
filt.disallow(lt.socket_type_t.tcp_socket, lt.session.global_peer_class_id);
|
||||
filt.allow(lt.socket_type_t.utp_socket, lt.session.local_peer_class_id);
|
||||
|
||||
def test_peer_class_ip_filter(self):
|
||||
s = lt.session({'enable_dht': False})
|
||||
s.set_peer_class_type_filter(lt.peer_class_type_filter())
|
||||
s.set_peer_class_filter(lt.ip_filter())
|
||||
|
||||
class test_session(unittest.TestCase):
|
||||
|
||||
def test_add_torrent(self):
|
||||
|
|
|
@ -61,7 +61,6 @@ namespace libtorrent {
|
|||
num_socket_types
|
||||
};
|
||||
|
||||
|
||||
// ``add()`` and ``remove()`` adds and removes a peer class to be added
|
||||
// to new peers based on socket type.
|
||||
void add(socket_type_t st, peer_class_t const peer_class)
|
||||
|
@ -125,9 +124,9 @@ namespace libtorrent {
|
|||
private:
|
||||
// maps socket type to a bitmask that's used to filter out
|
||||
// (mask) bits from the m_peer_class_filter.
|
||||
std::uint32_t m_peer_class_type_mask[5];
|
||||
std::uint32_t m_peer_class_type_mask[num_socket_types];
|
||||
// peer class bitfield added based on socket type
|
||||
std::uint32_t m_peer_class_type[5];
|
||||
std::uint32_t m_peer_class_type[num_socket_types];
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -877,7 +877,7 @@ namespace libtorrent {
|
|||
// the most recent pieces that are in the read cache.
|
||||
suggest_mode,
|
||||
|
||||
// ``max_queued_disk_bytes`` is the number maximum number of bytes, to
|
||||
// ``max_queued_disk_bytes`` is the maximum number of bytes, to
|
||||
// be written to disk, that can wait in the disk I/O thread queue.
|
||||
// This queue is only for waiting for the disk I/O thread to receive
|
||||
// the job and either write it to disk or insert it in the write
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace libtorrent {
|
|||
void peer_class_set::remove_class(peer_class_pool& pool, peer_class_t const c)
|
||||
{
|
||||
auto const i = std::find(m_class.begin(), m_class.begin() + m_size, c);
|
||||
int idx = int(i - m_class.begin());
|
||||
int const idx = int(i - m_class.begin());
|
||||
if (idx == m_size) return; // not found
|
||||
if (idx < m_size - 1)
|
||||
{
|
||||
|
|
|
@ -1086,7 +1086,7 @@ namespace {
|
|||
{
|
||||
TORRENT_ASSERT(is_single_thread());
|
||||
// if you hit this assert, you're deleting a non-existent peer class
|
||||
TORRENT_ASSERT(m_classes.at(cid));
|
||||
TORRENT_ASSERT_PRECOND(m_classes.at(cid));
|
||||
if (m_classes.at(cid) == nullptr) return;
|
||||
m_classes.decref(cid);
|
||||
}
|
||||
|
@ -1163,7 +1163,7 @@ namespace {
|
|||
{
|
||||
peer_class* pc = m_classes.at(cid);
|
||||
// if you hit this assert, you're passing in an invalid cid
|
||||
TORRENT_ASSERT(pc);
|
||||
TORRENT_ASSERT_PRECOND(pc);
|
||||
if (pc == nullptr) return;
|
||||
|
||||
pc->set_info(&pci);
|
||||
|
|
|
@ -4369,6 +4369,7 @@ namespace libtorrent {
|
|||
|
||||
if (m_peer_class > peer_class_t{0})
|
||||
{
|
||||
remove_class(m_ses.peer_classes(), m_peer_class);
|
||||
m_ses.peer_classes().decref(m_peer_class);
|
||||
m_peer_class = peer_class_t{0};
|
||||
}
|
||||
|
|
|
@ -1010,14 +1010,28 @@ namespace {
|
|||
m_files.set_piece_length(0);
|
||||
return false;
|
||||
}
|
||||
TORRENT_ASSERT(!files.name().empty());
|
||||
if (files.num_files() == 0)
|
||||
{
|
||||
ec = errors::no_files_in_torrent;
|
||||
// mark the torrent as invalid
|
||||
m_files.set_piece_length(0);
|
||||
return false;
|
||||
}
|
||||
if (files.name().empty())
|
||||
{
|
||||
ec = errors::torrent_missing_name;
|
||||
// mark the torrent as invalid
|
||||
m_files.set_piece_length(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract SHA-1 hashes for all pieces
|
||||
// we want this division to round upwards, that's why we have the
|
||||
// extra addition
|
||||
|
||||
if (files.total_size() >= static_cast<std::int64_t>(std::numeric_limits<int>::max() - files.piece_length())
|
||||
* files.piece_length())
|
||||
if (files.total_size() >=
|
||||
static_cast<boost::int64_t>(std::numeric_limits<int>::max()
|
||||
- files.piece_length()) * files.piece_length())
|
||||
{
|
||||
ec = errors::too_many_pieces_in_torrent;
|
||||
// mark the torrent as invalid
|
||||
|
|
|
@ -35,7 +35,6 @@ project tools
|
|||
<link>static
|
||||
;
|
||||
|
||||
exe fuzz_torrent : fuzz_torrent.cpp ;
|
||||
exe parse_access_log : parse_access_log.cpp ;
|
||||
exe dht : dht_put.cpp : <include>../ed25519/src ;
|
||||
exe session_log_alerts : session_log_alerts.cpp ;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
tool_programs = \
|
||||
fuzz_torrent \
|
||||
session_log_alerts
|
||||
|
||||
if ENABLE_EXAMPLES
|
||||
|
@ -20,7 +19,6 @@ EXTRA_DIST = Jamfile \
|
|||
parse_session_stats.py \
|
||||
parse_utp_log.py
|
||||
|
||||
fuzz_torrent_SOURCES = fuzz_torrent.cpp
|
||||
session_log_alerts_SOURCES = session_log_alerts.cpp
|
||||
|
||||
LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
|
||||
|
|
|
@ -1,416 +0,0 @@
|
|||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <cinttypes> // for PRId64 et.al.
|
||||
|
||||
#include "libtorrent/bdecode.hpp"
|
||||
#include "libtorrent/torrent_info.hpp"
|
||||
#include "libtorrent/error_code.hpp"
|
||||
|
||||
using lt::bdecode_node;
|
||||
using std::mt19937;
|
||||
using std::uniform_int_distribution;
|
||||
|
||||
char const* invalid_utf8_sequences[] =
|
||||
{
|
||||
"\x80",
|
||||
"\xbf",
|
||||
"\xff",
|
||||
"\xfe",
|
||||
"\xff\xff\xfe\xfe",
|
||||
"\xc0\xaf",
|
||||
"\xe0\x80\xaf",
|
||||
"\xf0\x80\x80\xaf",
|
||||
"\xf8\x80\x80\x80\xaf ",
|
||||
"\xfc\x80\x80\x80\x80\xaf",
|
||||
"\xc1\xbf",
|
||||
"\xe0\x9f\xbf",
|
||||
"\xf0\x8f\xbf\xbf",
|
||||
"\xf8\x87\xbf\xbf\xbf",
|
||||
"\xfc\x83\xbf\xbf\xbf\xbf",
|
||||
"\xc0\x80",
|
||||
"\xe0\x80\x80",
|
||||
"\xf0\x80\x80\x80",
|
||||
"\xf8\x80\x80\x80\x80",
|
||||
"\xfc\x80\x80\x80\x80\x80",
|
||||
"\xed\xa0\x80",
|
||||
"\xed\xad\xbf",
|
||||
"\xed\xae\x80",
|
||||
"\xed\xaf\xbf",
|
||||
"\xed\xb0\x80",
|
||||
"\xed\xbe\x80",
|
||||
"\xed\xbf\xbf",
|
||||
"\xed\xa0\x80\xed\xb0\x80",
|
||||
"\xed\xa0\x80\xed\xbf\xbf",
|
||||
"\xed\xad\xbf\xed\xb0\x80",
|
||||
"\xed\xad\xbf\xed\xbf\xbf",
|
||||
"\xed\xae\x80\xed\xb0\x80",
|
||||
"\xed\xae\x80\xed\xbf\xbf",
|
||||
"\xed\xaf\xbf\xed\xb0\x80",
|
||||
"\xed\xaf\xbf\xed\xbf\xbf",
|
||||
};
|
||||
|
||||
std::int64_t g_seed;
|
||||
|
||||
void print_ascii_number(std::string& output, std::int64_t val)
|
||||
{
|
||||
const bool overflow = g_seed == 1;
|
||||
const bool underflow = g_seed == 2;
|
||||
const bool negative = g_seed == 3;
|
||||
const bool double_negative = g_seed == 4;
|
||||
const bool zero = g_seed == 5;
|
||||
g_seed -= 5;
|
||||
|
||||
char const* numbers = "0123456789";
|
||||
if (zero)
|
||||
{
|
||||
output += '0';
|
||||
}
|
||||
else if (underflow)
|
||||
{
|
||||
output += '-';
|
||||
for (int i = 0; i < 100; ++i) output += numbers[rand() % 10];
|
||||
return;
|
||||
}
|
||||
else if (overflow)
|
||||
{
|
||||
for (int i = 0; i < 100; ++i) output += numbers[rand() % 10];
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (negative) output += '-';
|
||||
else if (double_negative) output += "--";
|
||||
char buf[50];
|
||||
std::snprintf(buf, sizeof(buf), "%" PRId64 "", val);
|
||||
output += buf;
|
||||
}
|
||||
}
|
||||
|
||||
void print_string(std::string& output, std::string str)
|
||||
{
|
||||
const bool empty_string = g_seed == 1;
|
||||
g_seed -= 1;
|
||||
if (empty_string)
|
||||
{
|
||||
print_ascii_number(output, 0);
|
||||
output += ':';
|
||||
return;
|
||||
}
|
||||
|
||||
const bool random_string = g_seed > 0 && g_seed <= 1000;
|
||||
const int str_seed = int(g_seed) - 1;
|
||||
g_seed -= 1000;
|
||||
if (random_string)
|
||||
{
|
||||
static mt19937 random_engine(str_seed);
|
||||
uniform_int_distribution<> d(0, 255);
|
||||
for (int i = 0; i < int(str.size()); ++i)
|
||||
str[i] = std::uint8_t(d(random_engine));
|
||||
|
||||
print_ascii_number(output, str.size());
|
||||
output += ':';
|
||||
output += str;
|
||||
return;
|
||||
}
|
||||
|
||||
const int num_sequences = (sizeof(invalid_utf8_sequences)/sizeof(char const*));
|
||||
const bool invalid_utf8 = g_seed <= num_sequences && g_seed > 0;
|
||||
|
||||
if (invalid_utf8)
|
||||
str += invalid_utf8_sequences[g_seed-1];
|
||||
|
||||
g_seed -= num_sequences;
|
||||
|
||||
print_ascii_number(output, str.size());
|
||||
output += ':';
|
||||
output += str;
|
||||
}
|
||||
|
||||
void print_terminate(std::string& output)
|
||||
{
|
||||
const bool unterminated = g_seed == 1;
|
||||
g_seed -= 1;
|
||||
if (!unterminated) output += 'e';
|
||||
}
|
||||
|
||||
void print_int(std::string& output, std::int64_t value)
|
||||
{
|
||||
const bool double_int = g_seed == 1;
|
||||
g_seed -= 1;
|
||||
if (double_int) output += 'i';
|
||||
output += 'i';
|
||||
print_ascii_number(output, value);
|
||||
print_terminate(output);
|
||||
}
|
||||
|
||||
void print_dict(std::string& output)
|
||||
{
|
||||
const bool double_dict = g_seed == 1;
|
||||
g_seed -= 1;
|
||||
if (double_dict) output += 'd';
|
||||
output += 'd';
|
||||
}
|
||||
|
||||
void print_list(std::string& output)
|
||||
{
|
||||
const bool double_list = g_seed == 1;
|
||||
g_seed -= 1;
|
||||
if (double_list) output += 'l';
|
||||
output += 'l';
|
||||
}
|
||||
|
||||
void render_arbitrary_item(std::string& out)
|
||||
{
|
||||
if (g_seed <= 0) return;
|
||||
|
||||
std::string option;
|
||||
print_int(option, 1337);
|
||||
if (g_seed <= 0)
|
||||
{
|
||||
out += option;
|
||||
return;
|
||||
}
|
||||
|
||||
option.clear();
|
||||
print_string(option, "abcdefgh");
|
||||
if (g_seed <= 0)
|
||||
{
|
||||
out += option;
|
||||
return;
|
||||
}
|
||||
|
||||
option.clear();
|
||||
print_dict(option);
|
||||
print_string(option, "abcdefgh");
|
||||
print_int(option, 1337);
|
||||
print_terminate(option);
|
||||
if (g_seed <= 0)
|
||||
{
|
||||
out += option;
|
||||
return;
|
||||
}
|
||||
|
||||
option.clear();
|
||||
print_list(option);
|
||||
print_string(option, "abcdefgh");
|
||||
print_terminate(option);
|
||||
if (g_seed <= 0)
|
||||
{
|
||||
out += option;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void render_variant(std::string& out, bdecode_node const& e)
|
||||
{
|
||||
switch (e.type())
|
||||
{
|
||||
case bdecode_node::dict_t:
|
||||
print_dict(out);
|
||||
for (int i = 0; i < e.dict_size(); ++i)
|
||||
{
|
||||
std::pair<lt::string_view, bdecode_node> item = e.dict_at(i);
|
||||
const bool duplicate = g_seed == 1;
|
||||
const bool skipped = g_seed == 2;
|
||||
g_seed -= 2;
|
||||
if (duplicate)
|
||||
{
|
||||
print_string(out, item.first.to_string());
|
||||
render_variant(out, item.second);
|
||||
}
|
||||
if (!skipped)
|
||||
{
|
||||
print_string(out, item.first.to_string());
|
||||
render_variant(out, item.second);
|
||||
}
|
||||
|
||||
render_arbitrary_item(out);
|
||||
}
|
||||
print_terminate(out);
|
||||
break;
|
||||
case bdecode_node::list_t:
|
||||
print_list(out);
|
||||
for (int i = 0; i < e.list_size(); ++i)
|
||||
{
|
||||
const bool duplicate = g_seed == 1;
|
||||
const bool skipped = g_seed == 2;
|
||||
g_seed -= 2;
|
||||
if (duplicate) render_variant(out, e.list_at(i));
|
||||
|
||||
render_arbitrary_item(out);
|
||||
|
||||
if (!skipped) render_variant(out, e.list_at(i));
|
||||
}
|
||||
print_terminate(out);
|
||||
break;
|
||||
case bdecode_node::int_t:
|
||||
print_int(out, e.int_value());
|
||||
break;
|
||||
case bdecode_node::string_t:
|
||||
print_string(out, e.string_value().to_string());
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int load_file(std::string const& filename, std::vector<char>& v
|
||||
, lt::error_code& ec, int limit = 8000000)
|
||||
{
|
||||
ec.clear();
|
||||
FILE* f = fopen(filename.c_str(), "rb");
|
||||
if (f == nullptr)
|
||||
{
|
||||
ec.assign(errno, boost::system::system_category());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int r = fseek(f, 0, SEEK_END);
|
||||
if (r != 0)
|
||||
{
|
||||
ec.assign(errno, boost::system::system_category());
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
long s = ftell(f);
|
||||
if (s < 0)
|
||||
{
|
||||
ec.assign(errno, boost::system::system_category());
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s > limit)
|
||||
{
|
||||
fclose(f);
|
||||
return -2;
|
||||
}
|
||||
|
||||
r = fseek(f, 0, SEEK_SET);
|
||||
if (r != 0)
|
||||
{
|
||||
ec.assign(errno, boost::system::system_category());
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
v.resize(s);
|
||||
if (s == 0)
|
||||
{
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = int(fread(&v[0], 1, v.size(), f));
|
||||
if (r < 0)
|
||||
{
|
||||
ec.assign(errno, boost::system::system_category());
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (r != s) return -3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char const* argv[])
|
||||
{
|
||||
std::vector<char> buf;
|
||||
lt::error_code ec;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
std::fprintf(stderr, "usage: fuzz_torrent torrent-file [torrent-file ...]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
--argc;
|
||||
++argv;
|
||||
for (;argc > 0; --argc, ++argv)
|
||||
{
|
||||
int ret = load_file(*argv, buf, ec);
|
||||
if (ret < 0)
|
||||
{
|
||||
std::fprintf(stderr, "ERROR loading file: %s\n%s\n"
|
||||
, *argv, ec.message().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
bdecode_node e;
|
||||
if (buf.empty() || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
|
||||
{
|
||||
std::fprintf(stderr, "ERROR parsing file: %s\n%s\n"
|
||||
, *argv, ec.message().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string test_buffer;
|
||||
int i = 0;
|
||||
for (i = 0; i < 10000000; ++i)
|
||||
{
|
||||
g_seed = i;
|
||||
test_buffer.clear();
|
||||
render_variant(test_buffer, e);
|
||||
|
||||
lt::error_code ec;
|
||||
lt::torrent_info t(test_buffer, ec, lt::from_span);
|
||||
|
||||
// TODO: add option to save to file unconditionally (to test other clients)
|
||||
/*
|
||||
{
|
||||
std::fprintf(stderr, "saving %d\n", i);
|
||||
char filename[100];
|
||||
std::snprintf(filename, sizeof(filename), "torrents/fuzz-%d.torrent", i);
|
||||
FILE* f = fopen(filename, "wb+");
|
||||
if (f == 0)
|
||||
{
|
||||
std::fprintf(stderr, "ERROR saving file: (%d) %s\n", errno, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
fwrite(test_buffer.c_str(), test_buffer.size(), 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
*/
|
||||
if (g_seed > 0) break;
|
||||
}
|
||||
std::fprintf(stderr, "tested %d variants of %s\n", i, *argv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue