merged RC_1_2 into RC_2_0
This commit is contained in:
33
.github/workflows/linux.yml
vendored
33
.github/workflows/linux.yml
vendored
@ -106,7 +106,7 @@ jobs:
|
||||
- name: install boost
|
||||
run: |
|
||||
sudo apt install libboost-tools-dev libboost-dev
|
||||
echo "using clang_tidy : : clang-tidy \"-checks=-clang-analyzer-core.*,-clang-analyzer-unix.*\" : <cxxflags>-std=c++11 <cxxflags>-I/usr/local/clang-7.0.0/include/c++/v1 <cxxflags>-stdlib=libc++ <linkflags>-stdlib=libc++ ;" >> ~/user-config.jam;
|
||||
echo "using clang_tidy : : clang-tidy \"-checks=-clang-analyzer-core.*,-clang-analyzer-unix.*\" : <cxxflags>-std=c++14 <cxxflags>-I/usr/local/clang-7.0.0/include/c++/v1 <cxxflags>-stdlib=libc++ <linkflags>-stdlib=libc++ ;" >> ~/user-config.jam;
|
||||
|
||||
- name: analyze
|
||||
run: |
|
||||
@ -196,3 +196,34 @@ jobs:
|
||||
run: |
|
||||
cd bindings/python
|
||||
LD_LIBRARY_PATH=./dependencies python3 test.py
|
||||
|
||||
dist:
|
||||
name: build dist
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: install boost
|
||||
run: |
|
||||
sudo apt install libboost-tools-dev libboost-python-dev libboost-dev libboost-system-dev
|
||||
sudo apt install python-docutils python-pygments python-pil gsfonts inkscape icoutils graphviz hunspell imagemagick
|
||||
python -m pip install aafigure
|
||||
echo "using gcc ;" >>~/user-config.jam
|
||||
|
||||
- name: build tarball
|
||||
run: AAFIGURE=~/.local/bin/aafigure RST2HTML=rst2html make dist
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: tarball
|
||||
path: libtorrent-rasterbar-*.tar.gz
|
||||
|
||||
- name: test-tarball
|
||||
run: |
|
||||
tar xvzf libtorrent-rasterbar-*.tar.gz
|
||||
cd libtorrent-rasterbar-*/test
|
||||
b2 link=static -j2 cxxstd=14 testing.execute=off
|
||||
|
@ -43,8 +43,9 @@
|
||||
* libtorrent now requires C++14 to build
|
||||
* added support for GnuTLS for HTTPS and torrents over SSL
|
||||
|
||||
* validate HTTPS certificates by default (trackers and web seeds)
|
||||
* load SSL certificates from windows system certificate store, to authenticate trackers
|
||||
* introduce mitigation for Server Side Request Forgery in tracker announces
|
||||
* introduce mitigation for Server Side Request Forgery in tracker and web seed URLs
|
||||
* fix error handling for pool allocation failure
|
||||
|
||||
1.2.11 released
|
||||
|
1
Makefile
1
Makefile
@ -622,6 +622,7 @@ HEADERS = \
|
||||
aux_/packet_pool.hpp \
|
||||
aux_/path.hpp \
|
||||
aux_/polymorphic_socket.hpp \
|
||||
aux_/pool.hpp \
|
||||
aux_/portmap.hpp \
|
||||
aux_/posix_part_file.hpp \
|
||||
aux_/posix_storage.hpp \
|
||||
|
@ -72,6 +72,8 @@ fuzzer dht_node ;
|
||||
fuzzer utp ;
|
||||
fuzzer resume_data ;
|
||||
fuzzer peer_conn ;
|
||||
fuzzer idna ;
|
||||
fuzzer parse_url ;
|
||||
fuzzer session_params ;
|
||||
fuzzer add_torrent ;
|
||||
|
||||
|
42
fuzzers/src/idna.cpp
Normal file
42
fuzzers/src/idna.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2020, 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/parse_url.hpp"
|
||||
#include "libtorrent/string_view.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(std::uint8_t const* data, size_t size)
|
||||
{
|
||||
lt::is_idna(lt::string_view(reinterpret_cast<char const*>(data), size));
|
||||
return 0;
|
||||
}
|
43
fuzzers/src/parse_url.cpp
Normal file
43
fuzzers/src/parse_url.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2020, 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/parse_url.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(std::uint8_t const* data, size_t size)
|
||||
{
|
||||
lt::error_code ec;
|
||||
lt::parse_url_components(std::string(reinterpret_cast<char const*>(data), size), ec);
|
||||
return 0;
|
||||
}
|
@ -10,7 +10,7 @@ corpus_dirs = [
|
||||
'dht_node', 'escape_path', 'escape_string', 'file_storage_add_file',
|
||||
'http_parser', 'lazy_bdecode', 'parse_int', 'parse_magnet_uri', 'resume_data',
|
||||
'sanitize_path', 'utf8_codepoint', 'utp',
|
||||
'verify_encoding', 'peer_conn', 'add_torrent']
|
||||
'verify_encoding', 'peer_conn', 'add_torrent', 'idna', 'parse_url']
|
||||
|
||||
for p in corpus_dirs:
|
||||
try:
|
||||
@ -31,7 +31,7 @@ xml_tests = [
|
||||
'<selfclosing />']
|
||||
|
||||
for x in xml_tests:
|
||||
name = hashlib.sha1(x).hexdigest()
|
||||
name = hashlib.sha1(x.encode('ascii')).hexdigest()
|
||||
with open(os.path.join('corpus', 'upnp', name), 'w+') as f:
|
||||
f.write(x)
|
||||
|
||||
@ -39,6 +39,20 @@ gzip_dir = '../test'
|
||||
for f in ['zeroes.gz', 'corrupt.gz', 'invalid1.gz']:
|
||||
shutil.copy(os.path.join(gzip_dir, f), os.path.join('corpus', 'gzip'))
|
||||
|
||||
idna = ['....', 'xn--foo-.bar', 'foo.xn--bar-.com', 'Xn--foobar-', 'XN--foobar-', '..xnxn--foobar-']
|
||||
|
||||
counter = 0
|
||||
for i in idna:
|
||||
open(os.path.join('corpus', 'idna', '%d' % counter), 'w+').write(i)
|
||||
counter += 1
|
||||
|
||||
urls = ['https://user:password@example.com:8080/path?query']
|
||||
|
||||
counter = 0
|
||||
for i in urls:
|
||||
open(os.path.join('corpus', 'parse_url', '%d' % counter), 'w+').write(i)
|
||||
counter += 1
|
||||
|
||||
# generate peer protocol messages
|
||||
messages = []
|
||||
|
||||
@ -48,13 +62,13 @@ def add_length(msg):
|
||||
|
||||
|
||||
def add_reserved(msg):
|
||||
return '\0\0\0\0\0\x18\0\x05' + msg
|
||||
return b'\0\0\0\0\0\x18\0\x05' + msg
|
||||
|
||||
|
||||
# extended handshake
|
||||
def add_extended_handshake(msg):
|
||||
ext_handshake = 'd1:md11:ut_metadatai1e11:lt_donthavei2e12:ut_holepunch' + \
|
||||
'i3e11:upload_onlyi4ee11:upload_onlyi1e10:share_modei1e4:reqqi1234e6:yourip4:0000e'
|
||||
ext_handshake = b'd1:md11:ut_metadatai1e11:lt_donthavei2e12:ut_holepunch' + \
|
||||
b'i3e11:upload_onlyi4ee11:upload_onlyi1e10:share_modei1e4:reqqi1234e6:yourip4:0000e'
|
||||
return add_length(struct.pack('BB', 20, 0) + ext_handshake) + msg
|
||||
|
||||
|
||||
@ -70,7 +84,7 @@ for i in range(101):
|
||||
|
||||
# piece
|
||||
for i in range(101):
|
||||
messages.append(add_length(struct.pack('>Bii', 7, i, 0) + ('a' * 0x4000)))
|
||||
messages.append(add_length(struct.pack('>Bii', 7, i, 0) + (b'a' * 0x4000)))
|
||||
|
||||
# single-byte
|
||||
for i in range(256):
|
||||
@ -115,7 +129,7 @@ for i in range(-10, 200, 20):
|
||||
# hash
|
||||
for i in range(-10, 200, 20):
|
||||
for j in range(-1, 1):
|
||||
messages.append(add_length(struct.pack('>Biiiii', 22, i, j, 0, 2, 0) + ('0' * 32 * 5)))
|
||||
messages.append(add_length(struct.pack('>Biiiii', 22, i, j, 0, 2, 0) + (b'0' * 32 * 5)))
|
||||
|
||||
# lt_dont_have
|
||||
messages.append(add_extended_handshake(add_length(struct.pack('>BBi', 20, 7, -1))))
|
||||
@ -141,17 +155,17 @@ for i in range(0, 1):
|
||||
bitfield_len = (100 + 7) // 8
|
||||
|
||||
for i in range(256):
|
||||
messages.append(add_length(struct.pack('B', 5) + (chr(i) * bitfield_len)))
|
||||
messages.append(add_length(struct.pack('B', 5) + (struct.pack('B', i) * bitfield_len)))
|
||||
|
||||
mixes = []
|
||||
|
||||
for i in range(200):
|
||||
shuffle(messages)
|
||||
mixes.append(''.join(messages[1:20]))
|
||||
mixes.append(b''.join(messages[1:20]))
|
||||
|
||||
messages += mixes
|
||||
|
||||
for m in messages:
|
||||
f = open('corpus/peer_conn/%s' % hashlib.sha1(m).hexdigest(), 'w+')
|
||||
f = open('corpus/peer_conn/%s' % hashlib.sha1(m).hexdigest(), 'wb+')
|
||||
f.write(add_reserved(m))
|
||||
f.close()
|
||||
|
@ -1763,7 +1763,8 @@ TORRENT_VERSION_NAMESPACE_3
|
||||
privileged_ports,
|
||||
utp_disabled,
|
||||
tcp_disabled,
|
||||
invalid_local_interface
|
||||
invalid_local_interface,
|
||||
ssrf_mitigation
|
||||
};
|
||||
|
||||
// the reason for the peer being blocked. Is one of the values from the
|
||||
|
@ -69,6 +69,7 @@ using http_handler = std::function<void(error_code const&
|
||||
using http_connect_handler = std::function<void(http_connection&)>;
|
||||
|
||||
using http_filter_handler = std::function<void(http_connection&, std::vector<tcp::endpoint>&)>;
|
||||
using hostname_filter_handler = std::function<bool(http_connection&, string_view)>;
|
||||
|
||||
// when bottled, the last two arguments to the handler
|
||||
// will always be 0
|
||||
@ -84,6 +85,7 @@ struct TORRENT_EXTRA_EXPORT http_connection
|
||||
, int max_bottled_buffer_size
|
||||
, http_connect_handler ch
|
||||
, http_filter_handler fh
|
||||
, hostname_filter_handler hfh
|
||||
#if TORRENT_USE_SSL
|
||||
, ssl::context* ssl_ctx
|
||||
#endif
|
||||
@ -177,6 +179,7 @@ private:
|
||||
http_handler m_handler;
|
||||
http_connect_handler m_connect_handler;
|
||||
http_filter_handler m_filter_handler;
|
||||
hostname_filter_handler m_hostname_filter_handler;
|
||||
deadline_timer m_timer;
|
||||
|
||||
time_duration m_completion_timeout;
|
||||
|
@ -74,6 +74,7 @@ namespace libtorrent {
|
||||
}
|
||||
|
||||
void on_filter(http_connection& c, std::vector<tcp::endpoint>& endpoints);
|
||||
bool on_filter_hostname(http_connection& c, string_view hostname);
|
||||
void on_connect(http_connection& c);
|
||||
void on_response(error_code const& ec, http_parser const& parser
|
||||
, span<char const> data);
|
||||
|
@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <string>
|
||||
|
||||
#include "libtorrent/error_code.hpp"
|
||||
#include "libtorrent/string_view.hpp"
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
@ -50,6 +51,10 @@ namespace libtorrent {
|
||||
// split a URL in its base and path parts
|
||||
TORRENT_EXTRA_EXPORT std::tuple<std::string, std::string>
|
||||
split_url(std::string url, error_code& ec);
|
||||
|
||||
// returns true if the hostname contains any IDNA (internationalized domain
|
||||
// name) labels.
|
||||
TORRENT_EXTRA_EXPORT bool is_idna(string_view hostname);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -938,17 +938,33 @@ namespace aux {
|
||||
// small piece sizes
|
||||
piece_extent_affinity,
|
||||
|
||||
// when set to true, the certificate of HTTPS trackers will be
|
||||
// validated against the system's certificate store (as defined by
|
||||
// OpenSSL). If the system does not have one, enabling this may cause
|
||||
// HTTPS trackers to fail.
|
||||
// when set to true, the certificate of HTTPS trackers and HTTPS web
|
||||
// seeds will be validated against the system's certificate store
|
||||
// (as defined by OpenSSL). If the system does not have a
|
||||
// certificate store, this option may have to be disabled in order
|
||||
// to get trackers and web seeds to work).
|
||||
validate_https_trackers,
|
||||
|
||||
// when enabled, any HTTP(S) tracker requests to localhost (loopback)
|
||||
// when enabled, tracker and web seed requests are subject to
|
||||
// certain restrictions.
|
||||
//
|
||||
// An HTTP(s) tracker requests to localhost (loopback)
|
||||
// must have the request path start with "/announce". This is the
|
||||
// conventional bittorrent tracker request. Any other HTTP(S)
|
||||
// tracker request to loopback will be ignored.
|
||||
tracker_ssrf_mitigation,
|
||||
// tracker request to loopback will be rejected. This applies to
|
||||
// trackers that redirect to loopback as well.
|
||||
//
|
||||
// Web seeds that end up on the client's local network (i.e. in a
|
||||
// private IP address range) may not include query string arguments.
|
||||
// This applies to web seeds redirecting to the local network as
|
||||
// well.
|
||||
ssrf_mitigation,
|
||||
|
||||
// when disabled, any tracker or web seed with an IDNA hostname
|
||||
// (internationalized domain name) is ignored. This is a security
|
||||
// precaution to avoid various unicode encoding attacks that might
|
||||
// happen at the application level.
|
||||
allow_idna,
|
||||
|
||||
// when set to true, enables the attempt to use SetFileValidData()
|
||||
// to pre-allocate disk space. This system call will only work when
|
||||
|
@ -182,6 +182,7 @@ std::shared_ptr<http_connection> test_request(io_context& ios
|
||||
std::printf("CONNECTED: %s\n", url.c_str());
|
||||
}
|
||||
, lt::http_filter_handler()
|
||||
, lt::hostname_filter_handler()
|
||||
#if TORRENT_USE_SSL
|
||||
, &ssl_ctx
|
||||
#endif
|
||||
@ -641,6 +642,7 @@ TORRENT_TEST(http_connection_ssl_proxy)
|
||||
}
|
||||
, true, 1024*1024, lt::http_connect_handler()
|
||||
, http_filter_handler()
|
||||
, hostname_filter_handler()
|
||||
#if TORRENT_USE_SSL
|
||||
, &ssl_ctx
|
||||
#endif
|
||||
|
@ -326,6 +326,16 @@ struct sim_config : sim::default_config
|
||||
result.push_back(make_address_v6("::1"));
|
||||
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(1));
|
||||
}
|
||||
if (hostname == "xn--tracker-.com")
|
||||
{
|
||||
result.push_back(make_address_v4("123.0.0.2"));
|
||||
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
|
||||
}
|
||||
if (hostname == "redirector.com")
|
||||
{
|
||||
result.push_back(make_address_v4("123.0.0.4"));
|
||||
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
return default_config::hostname_lookup(requestor, hostname, result, ec);
|
||||
}
|
||||
@ -622,7 +632,8 @@ TORRENT_TEST(ipv6_support_bind_v6_v4)
|
||||
// port 8080.
|
||||
template <typename Setup, typename Announce, typename Test1, typename Test2>
|
||||
void tracker_test(Setup setup, Announce a, Test1 test1, Test2 test2
|
||||
, char const* url_path = "/announce")
|
||||
, char const* url_path = "/announce"
|
||||
, char const* redirect = "http://123.0.0.2/announce")
|
||||
{
|
||||
using sim::asio::ip::address_v4;
|
||||
sim_config network_cfg;
|
||||
@ -630,6 +641,7 @@ void tracker_test(Setup setup, Announce a, Test1 test1, Test2 test2
|
||||
|
||||
sim::asio::io_context tracker_ios(sim, make_address_v4("123.0.0.2"));
|
||||
sim::asio::io_context tracker_ios6(sim, make_address_v6("ff::dead:beef"));
|
||||
sim::asio::io_context redirector_ios(sim, make_address_v4("123.0.0.4"));
|
||||
|
||||
sim::asio::io_context tracker_lo_ios(sim, make_address_v4("127.0.0.1"));
|
||||
sim::asio::io_context tracker_lo_ios6(sim, make_address_v6("::1"));
|
||||
@ -639,11 +651,13 @@ void tracker_test(Setup setup, Announce a, Test1 test1, Test2 test2
|
||||
sim::http_server http6(tracker_ios6, 8080);
|
||||
sim::http_server http_lo(tracker_lo_ios, 8080);
|
||||
sim::http_server http6_lo(tracker_lo_ios6, 8080);
|
||||
sim::http_server http_redirect(redirector_ios, 8080);
|
||||
|
||||
http.register_handler(url_path, a);
|
||||
http6.register_handler(url_path, a);
|
||||
http_lo.register_handler(url_path, a);
|
||||
http6_lo.register_handler(url_path, a);
|
||||
http_redirect.register_redirect(url_path, redirect);
|
||||
|
||||
lt::session_proxy zombie;
|
||||
|
||||
@ -1345,15 +1359,15 @@ TORRENT_TEST(tracker_user_agent_privacy_mode_private_torrent)
|
||||
TEST_EQUAL(got_announce, true);
|
||||
}
|
||||
|
||||
void test_ssrf(char const* announce_path, bool const feature_on
|
||||
, char const* tracker_url, bool const expect_announce)
|
||||
bool test_ssrf(char const* announce_path, bool const feature_on
|
||||
, char const* tracker_url)
|
||||
{
|
||||
bool got_announce = false;
|
||||
tracker_test(
|
||||
[&](lt::add_torrent_params& p, lt::session& ses)
|
||||
{
|
||||
settings_pack pack;
|
||||
pack.set_bool(settings_pack::tracker_ssrf_mitigation, feature_on);
|
||||
pack.set_bool(settings_pack::ssrf_mitigation, feature_on);
|
||||
ses.apply_settings(pack);
|
||||
p.trackers.emplace_back(tracker_url);
|
||||
return 60;
|
||||
@ -1367,28 +1381,73 @@ void test_ssrf(char const* announce_path, bool const feature_on
|
||||
, [](torrent_handle h) {}
|
||||
, [](torrent_handle h) {}
|
||||
, announce_path);
|
||||
TEST_EQUAL(got_announce, expect_announce);
|
||||
return got_announce;
|
||||
}
|
||||
|
||||
TORRENT_TEST(tracker_ssrf_localhost)
|
||||
TORRENT_TEST(ssrf_localhost)
|
||||
{
|
||||
test_ssrf("/announce", true, "http://localhost:8080/announce", true);
|
||||
test_ssrf("/unusual-announce-path", true, "http://localhost:8080/unusual-announce-path", false);
|
||||
test_ssrf("/unusual-announce-path", false, "http://localhost:8080/unusual-announce-path", true);
|
||||
TEST_CHECK(test_ssrf("/announce", true, "http://localhost:8080/announce"));
|
||||
TEST_CHECK(!test_ssrf("/unusual-announce-path", true, "http://localhost:8080/unusual-announce-path"));
|
||||
TEST_CHECK(test_ssrf("/unusual-announce-path", false, "http://localhost:8080/unusual-announce-path"));
|
||||
|
||||
TEST_CHECK(!test_ssrf("/short", true, "http://localhost:8080/short"));
|
||||
TEST_CHECK(test_ssrf("/short", false, "http://localhost:8080/short"));
|
||||
}
|
||||
|
||||
TORRENT_TEST(tracker_ssrf_IPv4)
|
||||
TORRENT_TEST(ssrf_IPv4)
|
||||
{
|
||||
test_ssrf("/announce", true, "http://127.0.0.1:8080/announce", true);
|
||||
test_ssrf("/unusual-announce-path", true, "http://127.0.0.1:8080/unusual-announce-path", false);
|
||||
test_ssrf("/unusual-announce-path", false, "http://127.0.0.1:8080/unusual-announce-path", true);
|
||||
TEST_CHECK(test_ssrf("/announce", true, "http://127.0.0.1:8080/announce"));
|
||||
TEST_CHECK(!test_ssrf("/unusual-announce-path", true, "http://127.0.0.1:8080/unusual-announce-path"));
|
||||
TEST_CHECK(test_ssrf("/unusual-announce-path", false, "http://127.0.0.1:8080/unusual-announce-path"));
|
||||
}
|
||||
|
||||
TORRENT_TEST(tracker_ssrf_IPv6)
|
||||
TORRENT_TEST(ssrf_IPv6)
|
||||
{
|
||||
test_ssrf("/announce", true, "http://[::1]:8080/announce", true);
|
||||
test_ssrf("/unusual-announce-path", true, "http://[::1]:8080/unusual-announce-path", false);
|
||||
test_ssrf("/unusual-announce-path", false, "http://[::1]:8080/unusual-announce-path", true);
|
||||
TEST_CHECK(test_ssrf("/announce", true, "http://[::1]:8080/announce"));
|
||||
TEST_CHECK(!test_ssrf("/unusual-announce-path", true, "http://[::1]:8080/unusual-announce-path"));
|
||||
TEST_CHECK(test_ssrf("/unusual-announce-path", false, "http://[::1]:8080/unusual-announce-path"));
|
||||
}
|
||||
|
||||
bool test_idna(char const* tracker_url, char const* redirect
|
||||
, bool const feature_on)
|
||||
{
|
||||
bool got_announce = false;
|
||||
tracker_test(
|
||||
[&](lt::add_torrent_params& p, lt::session& ses)
|
||||
{
|
||||
settings_pack pack;
|
||||
pack.set_bool(settings_pack::allow_idna, feature_on);
|
||||
ses.apply_settings(pack);
|
||||
p.trackers.emplace_back(tracker_url);
|
||||
return 60;
|
||||
},
|
||||
[&](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
got_announce = true;
|
||||
return sim::send_response(200, "OK", 11) + "d5:peers0:e";
|
||||
}
|
||||
, [](torrent_handle h) {}
|
||||
, [](torrent_handle h) {}
|
||||
, "/announce"
|
||||
, redirect ? redirect : ""
|
||||
);
|
||||
return got_announce;
|
||||
}
|
||||
|
||||
TORRENT_TEST(tracker_idna)
|
||||
{
|
||||
TEST_EQUAL(test_idna("http://tracker.com:8080/announce", nullptr, true), true);
|
||||
TEST_EQUAL(test_idna("http://tracker.com:8080/announce", nullptr, false), true);
|
||||
|
||||
TEST_EQUAL(test_idna("http://xn--tracker-.com:8080/announce", nullptr, true), true);
|
||||
TEST_EQUAL(test_idna("http://xn--tracker-.com:8080/announce", nullptr, false), false);
|
||||
}
|
||||
|
||||
TORRENT_TEST(tracker_idna_redirect)
|
||||
{
|
||||
TEST_EQUAL(test_idna("http://redirector.com:8080/announce", "http://xn--tracker-.com:8080/announce", true), true);
|
||||
TEST_EQUAL(test_idna("http://redirector.com:8080/announce", "http://xn--tracker-.com:8080/announce", false), false);
|
||||
}
|
||||
|
||||
// This test sets up two peers, one seed an one downloader. The downloader has
|
||||
|
@ -103,6 +103,48 @@ add_torrent_params create_torrent(file_storage& fs, bool const v1_only = false)
|
||||
ret.save_path = ".";
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sim_config : sim::default_config
|
||||
{
|
||||
explicit sim_config() {}
|
||||
|
||||
chrono::high_resolution_clock::duration hostname_lookup(
|
||||
asio::ip::address const& requestor
|
||||
, std::string hostname
|
||||
, std::vector<asio::ip::address>& result
|
||||
, boost::system::error_code& ec) override
|
||||
{
|
||||
auto const ret = duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
|
||||
if (hostname == "2.server.com")
|
||||
{
|
||||
result.push_back(make_address_v4("2.2.2.2"));
|
||||
return ret;
|
||||
}
|
||||
if (hostname == "2.xn--server-.com")
|
||||
{
|
||||
result.push_back(make_address_v4("2.2.2.2"));
|
||||
return ret;
|
||||
}
|
||||
if (hostname == "3.server.com")
|
||||
{
|
||||
result.push_back(make_address_v4("3.3.3.3"));
|
||||
return ret;
|
||||
}
|
||||
if (hostname == "3.xn--server-.com")
|
||||
{
|
||||
result.push_back(make_address_v4("3.3.3.3"));
|
||||
return ret;
|
||||
}
|
||||
if (hostname == "local-network.com")
|
||||
{
|
||||
result.push_back(make_address_v4("192.168.1.13"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return default_config::hostname_lookup(requestor, hostname, result, ec);
|
||||
}
|
||||
};
|
||||
|
||||
// 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)
|
||||
@ -113,7 +155,7 @@ void run_test(Setup const& setup
|
||||
, lt::seconds const timeout = lt::seconds{100})
|
||||
{
|
||||
// setup the simulation
|
||||
sim::default_config network_cfg;
|
||||
sim_config network_cfg;
|
||||
sim::simulation sim{network_cfg};
|
||||
std::unique_ptr<sim::asio::io_context> ios = make_io_context(sim, 0);
|
||||
lt::session_proxy zombie;
|
||||
@ -639,3 +681,151 @@ TORRENT_TEST(web_seed_connection_limit)
|
||||
TEST_CHECK(std::accumulate(expected.begin(), expected.end(), 0) == 2);
|
||||
}
|
||||
|
||||
bool test_idna(char const* url, char const* redirect, bool allow_idna)
|
||||
{
|
||||
using namespace lt;
|
||||
file_storage fs;
|
||||
fs.add_file("1", 0xc030);
|
||||
lt::add_torrent_params params = ::create_torrent(fs);
|
||||
params.url_seeds.emplace_back(url);
|
||||
|
||||
bool seeding = false;
|
||||
|
||||
error_code ignore;
|
||||
remove("1", ignore);
|
||||
|
||||
run_test(
|
||||
[&](lt::session& ses)
|
||||
{
|
||||
settings_pack pack;
|
||||
pack.set_bool(settings_pack::allow_idna, allow_idna);
|
||||
ses.apply_settings(pack);
|
||||
ses.async_add_torrent(params);
|
||||
},
|
||||
[&](lt::session&, lt::alert const* alert) {
|
||||
if (lt::alert_cast<lt::torrent_finished_alert>(alert))
|
||||
seeding = true;
|
||||
},
|
||||
[&](sim::simulation& sim, lt::session&)
|
||||
{
|
||||
// http1 is the root web server that will just redirect requests to
|
||||
// other servers
|
||||
sim::asio::io_context web_server1(sim, make_address_v4("2.2.2.2"));
|
||||
sim::http_server http1(web_server1, 8080);
|
||||
// redirect file 1 and file 2 to the same servers
|
||||
if (redirect)
|
||||
http1.register_redirect("/1", redirect);
|
||||
|
||||
// server for serving the content
|
||||
sim::asio::io_context web_server2(sim, make_address_v4("3.3.3.3"));
|
||||
sim::http_server http2(web_server2, 8080);
|
||||
serve_content_for(http2, "/1", fs, file_index_t(0));
|
||||
|
||||
sim.run();
|
||||
}
|
||||
);
|
||||
|
||||
return seeding;
|
||||
}
|
||||
|
||||
TORRENT_TEST(idna)
|
||||
{
|
||||
// disallow IDNA hostnames
|
||||
TEST_EQUAL(test_idna("http://3.server.com:8080", nullptr, false), true);
|
||||
TEST_EQUAL(test_idna("http://3.xn--server-.com:8080", nullptr, false), false);
|
||||
|
||||
// allow IDNA hostnames
|
||||
TEST_EQUAL(test_idna("http://3.server.com:8080", nullptr, true), true);
|
||||
TEST_EQUAL(test_idna("http://3.xn--server-.com:8080", nullptr, true), true);
|
||||
}
|
||||
|
||||
TORRENT_TEST(idna_redirect)
|
||||
{
|
||||
// disallow IDNA hostnames
|
||||
TEST_EQUAL(test_idna("http://2.server.com:8080", "http://3.server.com:8080/1", false), true);
|
||||
TEST_EQUAL(test_idna("http://2.server.com:8080", "http://3.xn--server-.com:8080/1", false), false);
|
||||
|
||||
TEST_EQUAL(test_idna("http://2.xn--server-.com:8080", "http://3.server.com:8080/1", false), false);
|
||||
TEST_EQUAL(test_idna("http://2.xn--server-.com:8080", "http://3.xn--server-.com:8080/1", false), false);
|
||||
|
||||
// allow IDNA hostnames
|
||||
TEST_EQUAL(test_idna("http://2.server.com:8080", "http://3.server.com:8080/1", true), true);
|
||||
TEST_EQUAL(test_idna("http://2.server.com:8080", "http://3.xn--server-.com:8080/1", true), true);
|
||||
|
||||
TEST_EQUAL(test_idna("http://2.xn--server-.com:8080", "http://3.server.com:8080/1", true), true);
|
||||
TEST_EQUAL(test_idna("http://2.xn--server-.com:8080", "http://3.xn--server-.com:8080/1", true), true);
|
||||
}
|
||||
|
||||
bool test_ssrf(char const* url, char const* redirect, bool enable_feature)
|
||||
{
|
||||
using namespace lt;
|
||||
file_storage fs;
|
||||
fs.add_file("1", 0xc030);
|
||||
lt::add_torrent_params params = ::create_torrent(fs);
|
||||
params.url_seeds.emplace_back(url);
|
||||
|
||||
bool seeding = false;
|
||||
|
||||
error_code ignore;
|
||||
remove("1", ignore);
|
||||
|
||||
run_test(
|
||||
[&](lt::session& ses)
|
||||
{
|
||||
settings_pack pack;
|
||||
pack.set_bool(settings_pack::ssrf_mitigation, enable_feature);
|
||||
ses.apply_settings(pack);
|
||||
ses.async_add_torrent(params);
|
||||
},
|
||||
[&](lt::session&, lt::alert const* alert) {
|
||||
if (lt::alert_cast<lt::torrent_finished_alert>(alert))
|
||||
seeding = true;
|
||||
},
|
||||
[&](sim::simulation& sim, lt::session&)
|
||||
{
|
||||
// http1 is the root web server that will just redirect requests to
|
||||
// other servers
|
||||
sim::asio::io_context web_server1(sim, make_address_v4("2.2.2.2"));
|
||||
sim::http_server http1(web_server1, 8080);
|
||||
// redirect file 1 and file 2 to the same servers
|
||||
if (redirect)
|
||||
http1.register_redirect("/1", redirect);
|
||||
|
||||
// server for serving the content. This is on the local network
|
||||
sim::asio::io_context web_server2(sim, make_address_v4("192.168.1.13"));
|
||||
sim::http_server http2(web_server2, 8080);
|
||||
serve_content_for(http2, "/1", fs, file_index_t(0));
|
||||
serve_content_for(http2, "/1?query_string=1", fs, file_index_t(0));
|
||||
|
||||
sim.run();
|
||||
}
|
||||
);
|
||||
|
||||
return seeding;
|
||||
}
|
||||
|
||||
TORRENT_TEST(ssrf_mitigation)
|
||||
{
|
||||
TEST_CHECK(test_ssrf("http://192.168.1.13:8080/1", nullptr, true));
|
||||
TEST_CHECK(test_ssrf("http://192.168.1.13:8080/1", nullptr, false));
|
||||
TEST_CHECK(test_ssrf("http://local-network.com:8080/1", nullptr, true));
|
||||
TEST_CHECK(test_ssrf("http://local-network.com:8080/1", nullptr, false));
|
||||
|
||||
TEST_CHECK(!test_ssrf("http://192.168.1.13:8080/1?query_string=1", nullptr, true));
|
||||
TEST_CHECK(test_ssrf("http://192.168.1.13:8080/1?query_string=1", nullptr, false));
|
||||
TEST_CHECK(!test_ssrf("http://local-network.com:8080/1?query_string=1", nullptr, true));
|
||||
TEST_CHECK(test_ssrf("http://local-network.com:8080/1?query_string=1", nullptr, false));
|
||||
}
|
||||
|
||||
TORRENT_TEST(ssrf_mitigation_redirect)
|
||||
{
|
||||
TEST_CHECK(test_ssrf("http://2.2.2.2:8080/1", "http://192.168.1.13:8080/1", true));
|
||||
TEST_CHECK(test_ssrf("http://2.2.2.2:8080/1", "http://192.168.1.13:8080/1", false));
|
||||
TEST_CHECK(test_ssrf("http://2.2.2.2:8080/1", "http://local-network.com:8080/1", true));
|
||||
TEST_CHECK(test_ssrf("http://2.2.2.2:8080/1", "http://local-network.com:8080/1", false));
|
||||
|
||||
TEST_CHECK(!test_ssrf("http://2.2.2.2:8080/1", "http://192.168.1.13:8080/1?query_string=1", true));
|
||||
TEST_CHECK(test_ssrf("http://2.2.2.2:8080/1", "http://192.168.1.13:8080/1?query_string=1", false));
|
||||
TEST_CHECK(!test_ssrf("http://2.2.2.2:8080/1", "http://local-network.com:8080/1?query_string=1", true));
|
||||
TEST_CHECK(test_ssrf("http://2.2.2.2:8080/1", "http://local-network.com:8080/1?query_string=1", false));
|
||||
}
|
||||
|
@ -1424,7 +1424,8 @@ namespace {
|
||||
"privileged_ports",
|
||||
"utp_disabled",
|
||||
"tcp_disabled",
|
||||
"invalid_local_interface"
|
||||
"invalid_local_interface",
|
||||
"ssrf_mitigation"
|
||||
};
|
||||
|
||||
std::snprintf(ret, sizeof(ret), "%s: blocked peer [%s]"
|
||||
|
@ -71,6 +71,7 @@ http_connection::http_connection(io_context& ios
|
||||
, int max_bottled_buffer_size
|
||||
, http_connect_handler ch
|
||||
, http_filter_handler fh
|
||||
, hostname_filter_handler hfh
|
||||
#if TORRENT_USE_SSL
|
||||
, ssl::context* ssl_ctx
|
||||
#endif
|
||||
@ -87,6 +88,7 @@ http_connection::http_connection(io_context& ios
|
||||
, m_handler(std::move(handler))
|
||||
, m_connect_handler(std::move(ch))
|
||||
, m_filter_handler(std::move(fh))
|
||||
, m_hostname_filter_handler(std::move(hfh))
|
||||
, m_timer(ios)
|
||||
, m_completion_timeout(seconds(5))
|
||||
, m_limiter_timer(ios)
|
||||
@ -146,6 +148,14 @@ void http_connection::get(std::string const& url, time_duration timeout, int pri
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_hostname_filter_handler && !m_hostname_filter_handler(*this, hostname))
|
||||
{
|
||||
error_code err(errors::banned_by_ip_filter);
|
||||
post(m_ios, std::bind(&http_connection::callback
|
||||
, me, err, span<char>{}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocol != "http"
|
||||
#if TORRENT_USE_SSL
|
||||
&& protocol != "https"
|
||||
|
@ -215,6 +215,7 @@ namespace libtorrent {
|
||||
, true, settings.get_int(settings_pack::max_http_recv_buffer_size)
|
||||
, std::bind(&http_tracker_connection::on_connect, shared_from_this(), _1)
|
||||
, std::bind(&http_tracker_connection::on_filter, shared_from_this(), _1, _2)
|
||||
, std::bind(&http_tracker_connection::on_filter_hostname, shared_from_this(), _1, _2)
|
||||
#if TORRENT_USE_SSL
|
||||
, tracker_req().ssl_ctx
|
||||
#endif
|
||||
@ -295,7 +296,7 @@ namespace libtorrent {
|
||||
}
|
||||
|
||||
aux::session_settings const& settings = m_man.settings();
|
||||
bool const ssrf_mitigation = settings.get_bool(settings_pack::tracker_ssrf_mitigation);
|
||||
bool const ssrf_mitigation = settings.get_bool(settings_pack::ssrf_mitigation);
|
||||
if (ssrf_mitigation && std::find_if(endpoints.begin(), endpoints.end()
|
||||
, [](tcp::endpoint const& ep) { return ep.address().is_loopback(); }) != endpoints.end())
|
||||
{
|
||||
@ -357,6 +358,15 @@ namespace libtorrent {
|
||||
fail(errors::banned_by_ip_filter, operation_t::bittorrent);
|
||||
}
|
||||
|
||||
// returns true if the hostname is allowed
|
||||
bool http_tracker_connection::on_filter_hostname(http_connection&
|
||||
, string_view hostname)
|
||||
{
|
||||
aux::session_settings const& settings = m_man.settings();
|
||||
if (settings.get_bool(settings_pack::allow_idna)) return true;
|
||||
return !is_idna(hostname);
|
||||
}
|
||||
|
||||
void http_tracker_connection::on_connect(http_connection& c)
|
||||
{
|
||||
error_code ec;
|
||||
|
@ -175,4 +175,20 @@ exit:
|
||||
return std::make_tuple(std::move(base), std::move(path));
|
||||
}
|
||||
|
||||
TORRENT_EXTRA_EXPORT bool is_idna(string_view hostname)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto dot = hostname.find('.');
|
||||
string_view const label = (dot == string_view::npos) ? hostname : hostname.substr(0, dot);
|
||||
if (label.size() >= 4
|
||||
&& (label[0] == 'x' || label[0] == 'X')
|
||||
&& (label[1] == 'n' || label[1] == 'N')
|
||||
&& label.substr(2, 2) == "--"_sv)
|
||||
return true;
|
||||
if (dot == string_view::npos) return false;
|
||||
hostname = hostname.substr(dot + 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -216,8 +216,9 @@ namespace libtorrent {
|
||||
SET(dht_ignore_dark_internet, true, nullptr),
|
||||
SET(dht_read_only, false, nullptr),
|
||||
SET(piece_extent_affinity, false, nullptr),
|
||||
SET(validate_https_trackers, false, &session_impl::update_validate_https),
|
||||
SET(tracker_ssrf_mitigation, true, nullptr),
|
||||
SET(validate_https_trackers, true, &session_impl::update_validate_https),
|
||||
SET(ssrf_mitigation, true, nullptr),
|
||||
SET(allow_idna, false, nullptr),
|
||||
SET(enable_set_file_valid_data, false, nullptr),
|
||||
}});
|
||||
|
||||
|
@ -6066,6 +6066,23 @@ namespace {
|
||||
error_code ec;
|
||||
std::tie(protocol, auth, hostname, port, path)
|
||||
= parse_url_components(web->url, ec);
|
||||
|
||||
if (!settings().get_bool(settings_pack::allow_idna) && is_idna(hostname))
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
if (should_log())
|
||||
debug_log("IDNA disallowed in web seeds: %s", web->url.c_str());
|
||||
#endif
|
||||
if (m_ses.alerts().should_post<url_seed_alert>())
|
||||
{
|
||||
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
||||
, web->url, error_code(errors::banned_by_ip_filter));
|
||||
}
|
||||
// never try it again
|
||||
remove_web_seed_iter(web);
|
||||
return;
|
||||
}
|
||||
|
||||
if (port == -1)
|
||||
{
|
||||
port = protocol == "http" ? 80 : 443;
|
||||
@ -6403,9 +6420,10 @@ namespace {
|
||||
}
|
||||
|
||||
std::string hostname;
|
||||
std::string path;
|
||||
error_code ec;
|
||||
using std::ignore;
|
||||
std::tie(ignore, ignore, hostname, ignore, ignore)
|
||||
std::tie(ignore, ignore, hostname, ignore, path)
|
||||
= parse_url_components(web->url, ec);
|
||||
if (ec)
|
||||
{
|
||||
@ -6414,6 +6432,46 @@ namespace {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settings().get_bool(settings_pack::allow_idna) && is_idna(hostname))
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
if (should_log())
|
||||
debug_log("IDNA disallowed in web seeds: %s", web->url.c_str());
|
||||
#endif
|
||||
if (m_ses.alerts().should_post<url_seed_alert>())
|
||||
{
|
||||
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
||||
, web->url, error_code(errors::banned_by_ip_filter));
|
||||
}
|
||||
// never try it again
|
||||
remove_web_seed_iter(web);
|
||||
return;
|
||||
}
|
||||
|
||||
// The SSRF mitigation for web seeds is that any HTTP server on the
|
||||
// local network may not use any query string parameters
|
||||
if (settings().get_bool(settings_pack::ssrf_mitigation)
|
||||
&& aux::is_local(web->peer_info.addr)
|
||||
&& path.find('?') != std::string::npos)
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
if (should_log())
|
||||
{
|
||||
debug_log("*** SSRF MITIGATION BLOCKED WEB SEED: %s"
|
||||
, web->url.c_str());
|
||||
}
|
||||
#endif
|
||||
if (m_ses.alerts().should_post<url_seed_alert>())
|
||||
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
||||
, web->url, errors::banned_by_ip_filter);
|
||||
if (m_ses.alerts().should_post<peer_blocked_alert>())
|
||||
m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
||||
, a, peer_blocked_alert::ssrf_mitigation);
|
||||
// never try it again
|
||||
remove_web_seed_iter(web);
|
||||
return;
|
||||
}
|
||||
|
||||
bool const is_ip = aux::is_ip_address(hostname);
|
||||
if (is_ip) a.address(make_address(hostname, ec));
|
||||
bool const proxy_hostnames = settings().get_bool(settings_pack::proxy_hostnames)
|
||||
|
@ -440,6 +440,7 @@ void upnp::connect(rootdevice& d)
|
||||
, std::ref(d), _4), true, default_max_bottled_buffer_size
|
||||
, http_connect_handler()
|
||||
, http_filter_handler()
|
||||
, hostname_filter_handler()
|
||||
#if TORRENT_USE_SSL
|
||||
, &m_ssl_ctx
|
||||
#endif
|
||||
@ -852,6 +853,7 @@ void upnp::update_map(rootdevice& d, port_mapping_t const i)
|
||||
, std::ref(d), i, _4), true, default_max_bottled_buffer_size
|
||||
, std::bind(&upnp::create_port_mapping, self(), _1, std::ref(d), i)
|
||||
, http_filter_handler()
|
||||
, hostname_filter_handler()
|
||||
#if TORRENT_USE_SSL
|
||||
, &m_ssl_ctx
|
||||
#endif
|
||||
@ -869,6 +871,7 @@ void upnp::update_map(rootdevice& d, port_mapping_t const i)
|
||||
, std::ref(d), i, _4), true, default_max_bottled_buffer_size
|
||||
, std::bind(&upnp::delete_port_mapping, self(), std::ref(d), i)
|
||||
, http_filter_handler()
|
||||
, hostname_filter_handler()
|
||||
#if TORRENT_USE_SSL
|
||||
, &m_ssl_ctx
|
||||
#endif
|
||||
@ -1087,6 +1090,7 @@ void upnp::on_upnp_xml(error_code const& e
|
||||
, std::ref(d), _4), true, default_max_bottled_buffer_size
|
||||
, std::bind(&upnp::get_ip_address, self(), std::ref(d))
|
||||
, http_filter_handler()
|
||||
, hostname_filter_handler()
|
||||
#if TORRENT_USE_SSL
|
||||
, &m_ssl_ctx
|
||||
#endif
|
||||
|
@ -133,6 +133,7 @@ void run_test(std::string const& url, int size, int status, int connected
|
||||
std::shared_ptr<http_connection> h = std::make_shared<http_connection>(ios
|
||||
, res, &::http_handler_test, true, 1024*1024, &::http_connect_handler_test
|
||||
, http_filter_handler()
|
||||
, hostname_filter_handler()
|
||||
#if TORRENT_USE_SSL
|
||||
, &ssl_ctx
|
||||
#endif
|
||||
|
@ -779,3 +779,56 @@ TORRENT_TEST(invalid_chunk_3)
|
||||
http_parser parser;
|
||||
feed_bytes(parser, {reinterpret_cast<char const*>(invalid_chunked_input), sizeof(invalid_chunked_input)});
|
||||
}
|
||||
|
||||
TORRENT_TEST(idna)
|
||||
{
|
||||
TEST_CHECK(!is_idna("a.b.com"));
|
||||
TEST_CHECK(!is_idna("example.com"));
|
||||
TEST_CHECK(!is_idna("exn--ample.com"));
|
||||
|
||||
// xn-- is the ACE introducer for a punycoded label. It can appear at the
|
||||
// start of any label, but not in the middle (if it does, it's not
|
||||
// interpreted as an ACE). Since hostnames are case insensitive, the ACE
|
||||
// introducer has to be as well
|
||||
TEST_CHECK(is_idna("xn--example.com"));
|
||||
TEST_CHECK(is_idna("xN--example.com"));
|
||||
TEST_CHECK(is_idna("xn--example-.com"));
|
||||
TEST_CHECK(is_idna("subdomain.xn--example-.com"));
|
||||
TEST_CHECK(is_idna("subdomain.example.xn--com-"));
|
||||
|
||||
// this isn't valid IDNA, but it's suspicious
|
||||
TEST_CHECK(is_idna("xn--.com"));
|
||||
|
||||
// some weird edge-cases
|
||||
TEST_CHECK(!is_idna(".............."));
|
||||
TEST_CHECK(is_idna(".....xn--........."));
|
||||
TEST_CHECK(is_idna(".....Xn--........."));
|
||||
TEST_CHECK(is_idna(".....xN--........."));
|
||||
TEST_CHECK(is_idna(".....XN--........."));
|
||||
TEST_CHECK(!is_idna(".....-xn--........."));
|
||||
TEST_CHECK(is_idna("xn--"));
|
||||
TEST_CHECK(is_idna("Xn--"));
|
||||
TEST_CHECK(is_idna("XN--"));
|
||||
TEST_CHECK(is_idna("xN--"));
|
||||
TEST_CHECK(is_idna("xn--.xn--"));
|
||||
TEST_CHECK(is_idna(".....xn--"));
|
||||
TEST_CHECK(is_idna("xn--..."));
|
||||
TEST_CHECK(is_idna("xN--..."));
|
||||
TEST_CHECK(!is_idna(""));
|
||||
TEST_CHECK(!is_idna("."));
|
||||
|
||||
TEST_CHECK(!is_idna("x"));
|
||||
TEST_CHECK(!is_idna("xn"));
|
||||
TEST_CHECK(!is_idna("xn-"));
|
||||
TEST_CHECK(!is_idna("-xn--"));
|
||||
|
||||
TEST_CHECK(!is_idna(".x"));
|
||||
TEST_CHECK(!is_idna(".xn"));
|
||||
TEST_CHECK(!is_idna(".xn-"));
|
||||
TEST_CHECK(!is_idna(".-xn--"));
|
||||
|
||||
TEST_CHECK(!is_idna("x."));
|
||||
TEST_CHECK(!is_idna("xn."));
|
||||
TEST_CHECK(!is_idna("xn-."));
|
||||
TEST_CHECK(!is_idna("-xn--."));
|
||||
}
|
||||
|
@ -100,6 +100,11 @@ void test_transfer(lt::session& ses, std::shared_ptr<torrent_info> torrent_file
|
||||
, keepalive ? "yes" : "no");
|
||||
|
||||
int proxy_port = 0;
|
||||
settings_pack pack;
|
||||
// we use a self-signed cert for HTTPS trackers, the test would fail if we
|
||||
// tried to validate it.
|
||||
if (protocol == "https"_sv)
|
||||
pack.set_bool(settings_pack::validate_https_trackers, false);
|
||||
if (proxy)
|
||||
{
|
||||
proxy_port = start_proxy(proxy);
|
||||
@ -108,26 +113,23 @@ void test_transfer(lt::session& ses, std::shared_ptr<torrent_info> torrent_file
|
||||
std::printf("failed to start proxy");
|
||||
return;
|
||||
}
|
||||
settings_pack pack;
|
||||
pack.set_str(settings_pack::proxy_hostname, "127.0.0.1");
|
||||
pack.set_str(settings_pack::proxy_username, "testuser");
|
||||
pack.set_str(settings_pack::proxy_password, "testpass");
|
||||
pack.set_int(settings_pack::proxy_type, proxy);
|
||||
pack.set_int(settings_pack::proxy_port, proxy_port);
|
||||
pack.set_bool(settings_pack::proxy_peer_connections, proxy_peers);
|
||||
ses.apply_settings(pack);
|
||||
}
|
||||
else
|
||||
{
|
||||
settings_pack pack;
|
||||
pack.set_str(settings_pack::proxy_hostname, "");
|
||||
pack.set_str(settings_pack::proxy_username, "");
|
||||
pack.set_str(settings_pack::proxy_password, "");
|
||||
pack.set_int(settings_pack::proxy_type, settings_pack::none);
|
||||
pack.set_int(settings_pack::proxy_port, 0);
|
||||
pack.set_bool(settings_pack::proxy_peer_connections, proxy_peers);
|
||||
ses.apply_settings(pack);
|
||||
}
|
||||
ses.apply_settings(pack);
|
||||
|
||||
add_torrent_params p;
|
||||
p.flags &= ~torrent_flags::paused;
|
||||
|
Reference in New Issue
Block a user