promote errors in parsing listen_interfaces to post listen_failed_alert
This commit is contained in:
parent
41dab18644
commit
59a29a44e2
@ -1,3 +1,4 @@
|
||||
* promote errors in parsing listen_interfaces to post listen_failed_alert
|
||||
* fix bug in protocol encryption/obfuscation
|
||||
* fix buffer overflow in SOCKS5 UDP logic
|
||||
* fix issue of rapid calls to file_priority() clobbering each other
|
||||
|
@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/string_view.hpp"
|
||||
#include "libtorrent/span.hpp"
|
||||
#include "libtorrent/error_code.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@ -60,6 +61,9 @@ namespace libtorrent {
|
||||
url += '/';
|
||||
}
|
||||
|
||||
// internal
|
||||
TORRENT_EXTRA_EXPORT string_view strip_string(string_view in);
|
||||
|
||||
TORRENT_EXTRA_EXPORT bool is_print(char c);
|
||||
TORRENT_EXTRA_EXPORT bool is_space(char c);
|
||||
TORRENT_EXTRA_EXPORT char to_lower(char c);
|
||||
@ -80,16 +84,25 @@ namespace libtorrent {
|
||||
std::string device;
|
||||
int port;
|
||||
bool ssl;
|
||||
friend bool operator==(listen_interface_t const& lhs, listen_interface_t const& rhs)
|
||||
{
|
||||
return lhs.device == rhs.device
|
||||
&& lhs.port == rhs.port
|
||||
&& lhs.ssl == rhs.ssl;
|
||||
}
|
||||
};
|
||||
|
||||
// this parses the string that's used as the listen_interfaces setting.
|
||||
// it is a comma-separated list of IP or device names with ports. For
|
||||
// example: "eth0:6881,eth1:6881" or "127.0.0.1:6881"
|
||||
TORRENT_EXTRA_EXPORT std::vector<listen_interface_t> parse_listen_interfaces(
|
||||
std::string const& in);
|
||||
std::string const& in, std::vector<std::string>& errors);
|
||||
|
||||
#if TORRENT_ABI_VERSION == 1 \
|
||||
|| !defined TORRENT_DISABLE_LOGGING
|
||||
TORRENT_EXTRA_EXPORT std::string print_listen_interfaces(
|
||||
std::vector<listen_interface_t> const& in);
|
||||
#endif
|
||||
|
||||
// this parses the string that's used as the listen_interfaces setting.
|
||||
// it is a comma-separated list of IP or device names with ports. For
|
||||
|
@ -5192,8 +5192,9 @@ namespace aux {
|
||||
// this function maps the previous functionality of just setting the ssl
|
||||
// listen port in order to enable the ssl listen sockets, to the new
|
||||
// mechanism where SSL sockets are specified in listen_interfaces.
|
||||
std::vector<std::string> ignore;
|
||||
auto current_ifaces = parse_listen_interfaces(
|
||||
m_settings.get_str(settings_pack::listen_interfaces));
|
||||
m_settings.get_str(settings_pack::listen_interfaces), ignore);
|
||||
// these are the current interfaces we have, first remove all the SSL
|
||||
// interfaces
|
||||
current_ifaces.erase(std::remove_if(current_ifaces.begin(), current_ifaces.end()
|
||||
@ -5227,16 +5228,18 @@ namespace aux {
|
||||
INVARIANT_CHECK;
|
||||
|
||||
std::string const net_interfaces = m_settings.get_str(settings_pack::listen_interfaces);
|
||||
m_listen_interfaces = parse_listen_interfaces(net_interfaces);
|
||||
std::vector<std::string> err;
|
||||
m_listen_interfaces = parse_listen_interfaces(net_interfaces, err);
|
||||
|
||||
for (auto const& e : err)
|
||||
{
|
||||
m_alerts.emplace_alert<listen_failed_alert>(e, lt::address{}, 0
|
||||
, operation_t::parse_address, errors::invalid_port, lt::socket_type_t::tcp);
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
if (should_log())
|
||||
{
|
||||
if (!net_interfaces.empty() && m_listen_interfaces.empty())
|
||||
{
|
||||
session_log("ERROR: failed to parse listen_interfaces setting: %s"
|
||||
, net_interfaces.c_str());
|
||||
}
|
||||
session_log("update listen interfaces: %s", net_interfaces.c_str());
|
||||
session_log("parsed listen interfaces count: %d, ifaces: %s"
|
||||
, int(m_listen_interfaces.size())
|
||||
|
@ -149,6 +149,8 @@ namespace libtorrent {
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#if TORRENT_ABI_VERSION == 1 \
|
||||
|| !defined TORRENT_DISABLE_LOGGING
|
||||
std::string print_listen_interfaces(std::vector<listen_interface_t> const& in)
|
||||
{
|
||||
std::string ret;
|
||||
@ -176,92 +178,101 @@ namespace libtorrent {
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
string_view strip_string(string_view in)
|
||||
{
|
||||
while (!in.empty() && is_space(in.front()))
|
||||
in.remove_prefix(1);
|
||||
|
||||
while (!in.empty() && is_space(in.back()))
|
||||
in.remove_suffix(1);
|
||||
return in;
|
||||
}
|
||||
|
||||
// this parses the string that's used as the listen_interfaces setting.
|
||||
// it is a comma-separated list of IP or device names with ports. For
|
||||
// example: "eth0:6881,eth1:6881" or "127.0.0.1:6881"
|
||||
std::vector<listen_interface_t> parse_listen_interfaces(std::string const& in)
|
||||
std::vector<listen_interface_t> parse_listen_interfaces(std::string const& in
|
||||
, std::vector<std::string>& err)
|
||||
{
|
||||
std::vector<listen_interface_t> out;
|
||||
|
||||
std::string::size_type start = 0;
|
||||
|
||||
while (start < in.size())
|
||||
string_view rest = in;
|
||||
while (!rest.empty())
|
||||
{
|
||||
// skip leading spaces
|
||||
while (start < in.size() && is_space(in[start]))
|
||||
++start;
|
||||
string_view element;
|
||||
std::tie(element, rest) = split_string(rest, ',');
|
||||
|
||||
if (start == in.size()) return out;
|
||||
element = strip_string(element);
|
||||
if (element.size() > 1 && element.front() == '"' && element.back() == '"')
|
||||
element = element.substr(1, element.size() - 2);
|
||||
if (element.empty()) continue;
|
||||
|
||||
listen_interface_t iface;
|
||||
iface.ssl = false;
|
||||
|
||||
if (in[start] == '[')
|
||||
string_view port;
|
||||
if (element.front() == '[')
|
||||
{
|
||||
++start;
|
||||
// IPv6 address
|
||||
while (start < in.size() && in[start] != ']')
|
||||
iface.device += in[start++];
|
||||
auto const pos = find_first_of(element, ']', 0);
|
||||
if (pos == string_view::npos
|
||||
|| pos+1 >= element.size()
|
||||
|| element[pos+1] != ':')
|
||||
{
|
||||
err.emplace_back(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip to the colon
|
||||
while (start < in.size() && in[start] != ':')
|
||||
++start;
|
||||
iface.device = strip_string(element.substr(1, pos - 1)).to_string();
|
||||
|
||||
port = strip_string(element.substr(pos + 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// consume device name
|
||||
while (start < in.size() && !is_space(in[start]) && in[start] != ':')
|
||||
iface.device += in[start++];
|
||||
auto const pos = find_first_of(element, ':', 0);
|
||||
iface.device = strip_string(element.substr(0, pos)).to_string();
|
||||
if (pos == string_view::npos)
|
||||
{
|
||||
err.emplace_back(element);
|
||||
continue;
|
||||
}
|
||||
port = strip_string(element.substr(pos + 1));
|
||||
}
|
||||
|
||||
// skip spaces
|
||||
while (start < in.size() && is_space(in[start]))
|
||||
++start;
|
||||
|
||||
if (start == in.size() || in[start] != ':') return out;
|
||||
++start; // skip colon
|
||||
|
||||
// skip spaces
|
||||
while (start < in.size() && is_space(in[start]))
|
||||
++start;
|
||||
|
||||
// consume port
|
||||
std::string port;
|
||||
while (start < in.size() && is_digit(in[start]) && in[start] != ',')
|
||||
port += in[start++];
|
||||
std::string port_str;
|
||||
for (std::size_t i = 0; i < port.size() && is_digit(port[i]); ++i)
|
||||
port_str += port[i];
|
||||
|
||||
if (port.empty() || port.size() > 5)
|
||||
if (port_str.empty() || port_str.size() > 5)
|
||||
{
|
||||
iface.port = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iface.port = std::atoi(port.c_str());
|
||||
if (iface.port < 0 || iface.port > 65535) iface.port = -1;
|
||||
err.emplace_back(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip spaces
|
||||
while (start < in.size() && is_space(in[start]))
|
||||
++start;
|
||||
iface.port = std::atoi(port_str.c_str());
|
||||
if (iface.port < 0 || iface.port > 65535)
|
||||
{
|
||||
err.emplace_back(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
port.remove_prefix(port_str.size());
|
||||
port = strip_string(port);
|
||||
|
||||
// consume potential SSL 's'
|
||||
if (start < in.size() && in[start] == 's')
|
||||
for (auto const c : port)
|
||||
{
|
||||
iface.ssl = true;
|
||||
++start;
|
||||
switch (c)
|
||||
{
|
||||
case 's': iface.ssl = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
// skip until end or comma
|
||||
while (start < in.size() && in[start] != ',')
|
||||
++start;
|
||||
|
||||
if (iface.port >= 0) out.push_back(iface);
|
||||
|
||||
// skip the comma
|
||||
if (start < in.size() && in[start] == ',')
|
||||
++start;
|
||||
|
||||
TORRENT_ASSERT(iface.port >= 0);
|
||||
out.emplace_back(iface);
|
||||
}
|
||||
|
||||
return out;
|
||||
|
@ -346,19 +346,23 @@ TORRENT_TEST(path)
|
||||
namespace {
|
||||
|
||||
void test_parse_interface(char const* input
|
||||
, std::vector<listen_interface_t> expected
|
||||
, std::string output)
|
||||
, std::vector<listen_interface_t> const expected
|
||||
, std::vector<std::string> const expected_e
|
||||
, string_view const output)
|
||||
{
|
||||
std::printf("parse interface: %s\n", input);
|
||||
auto const list = parse_listen_interfaces(input);
|
||||
std::vector<std::string> err;
|
||||
auto const list = parse_listen_interfaces(input, err);
|
||||
TEST_EQUAL(list.size(), expected.size());
|
||||
if (list.size() == expected.size())
|
||||
{
|
||||
TEST_CHECK(std::equal(list.begin(), list.end(), expected.begin()
|
||||
, [&](listen_interface_t const& lhs, listen_interface_t const& rhs)
|
||||
{ return lhs.device == rhs.device && lhs.port == rhs.port && lhs.ssl == rhs.ssl; }));
|
||||
}
|
||||
TEST_CHECK(list == expected);
|
||||
TEST_CHECK(err == expected_e);
|
||||
#if TORRENT_ABI_VERSION == 1 \
|
||||
|| !defined TORRENT_DISABLE_LOGGING
|
||||
TEST_EQUAL(print_listen_interfaces(list), output);
|
||||
std::cout << "RESULT: " << print_listen_interfaces(list) << '\n';
|
||||
#endif
|
||||
for (auto const& e : err)
|
||||
std::cout << "ERR: \"" << e << "\"\n";
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@ -367,46 +371,68 @@ TORRENT_TEST(parse_list)
|
||||
{
|
||||
std::vector<std::string> list;
|
||||
parse_comma_separated_string(" a,b, c, d ,e \t,foobar\n\r,[::1]", list);
|
||||
TEST_EQUAL(list.size(), 7);
|
||||
TEST_EQUAL(list[0], "a");
|
||||
TEST_EQUAL(list[1], "b");
|
||||
TEST_EQUAL(list[2], "c");
|
||||
TEST_EQUAL(list[3], "d");
|
||||
TEST_EQUAL(list[4], "e");
|
||||
TEST_EQUAL(list[5], "foobar");
|
||||
TEST_EQUAL(list[6], "[::1]");
|
||||
TEST_CHECK((list == std::vector<std::string>{"a", "b", "c", "d", "e", "foobar", "[::1]"}));
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_interface)
|
||||
{
|
||||
test_parse_interface(" a:4,b:35, c : 1000s, d: 351 ,e \t:42,foobar:1337s\n\r,[2001::1]:6881"
|
||||
, {{"a", 4, false}, {"b", 35, false}, {"c", 1000, true}, {"d", 351, false}
|
||||
, {"e", 42, false}, {"foobar", 1337, true}, {"2001::1", 6881, false}}
|
||||
, {}
|
||||
, "a:4,b:35,c:1000s,d:351,e:42,foobar:1337s,[2001::1]:6881");
|
||||
|
||||
// IPv6 address
|
||||
test_parse_interface("[2001:ffff::1]:6882s"
|
||||
, {{"2001:ffff::1", 6882, true}}
|
||||
, {}
|
||||
, "[2001:ffff::1]:6882s");
|
||||
|
||||
// IPv4 address
|
||||
test_parse_interface("127.0.0.1:6882"
|
||||
, {{"127.0.0.1", 6882, false}}
|
||||
, {}
|
||||
, "127.0.0.1:6882");
|
||||
|
||||
// maximum padding
|
||||
test_parse_interface(" nic\r\n:\t 12\r s "
|
||||
, {{"nic", 12, true}}
|
||||
, {}
|
||||
, "nic:12s");
|
||||
|
||||
// negative tests
|
||||
test_parse_interface("nic:99999999999999999999999", {}, "");
|
||||
test_parse_interface("nic: -3", {}, "");
|
||||
test_parse_interface("nic: ", {}, "");
|
||||
test_parse_interface("nic :", {}, "");
|
||||
test_parse_interface("nic ", {}, "");
|
||||
test_parse_interface("nic s", {}, "");
|
||||
test_parse_interface("nic:99999999999999999999999", {}, {"nic:99999999999999999999999"}, "");
|
||||
test_parse_interface("nic: -3", {}, {"nic: -3"}, "");
|
||||
test_parse_interface("nic: ", {}, {"nic:"}, "");
|
||||
test_parse_interface("nic :", {}, {"nic :"}, "");
|
||||
test_parse_interface("nic ", {}, {"nic"}, "");
|
||||
test_parse_interface("nic s", {}, {"nic s"}, "");
|
||||
|
||||
// parse interface with port 0
|
||||
test_parse_interface("127.0.0.1:0"
|
||||
, {{"127.0.0.1", 0, false}}, "127.0.0.1:0");
|
||||
test_parse_interface("127.0.0.1:0", {{"127.0.0.1", 0, false}}, {}, "127.0.0.1:0");
|
||||
|
||||
// IPv6 error
|
||||
test_parse_interface("[aaaa::1", {}, {"[aaaa::1"}, "");
|
||||
test_parse_interface("[aaaa::1]", {}, {"[aaaa::1]"}, "");
|
||||
test_parse_interface("[aaaa::1]:", {}, {"[aaaa::1]:"}, "");
|
||||
test_parse_interface("[aaaa::1]:s", {}, {"[aaaa::1]:s"}, "");
|
||||
test_parse_interface("[aaaa::1] :6881", {}, {"[aaaa::1] :6881"}, "");
|
||||
test_parse_interface("[aaaa::1]:6881", {{"aaaa::1", 6881, false}}, {}, "[aaaa::1]:6881");
|
||||
|
||||
// unterminated [
|
||||
test_parse_interface("[aaaa::1,foobar:0", {{"foobar", 0, false}}, {"[aaaa::1"}, "foobar:0");
|
||||
|
||||
// multiple errors
|
||||
test_parse_interface("foo:,bar", {}, {"foo:", "bar"}, "");
|
||||
|
||||
// quoted elements
|
||||
test_parse_interface("\"abc,.\",bar", {}, {"abc,.", "bar"}, "");
|
||||
|
||||
// silent error
|
||||
test_parse_interface("\"", {}, {"\""}, "");
|
||||
|
||||
// multiple errors and one correct
|
||||
test_parse_interface("foo,bar,0.0.0.0:6881", {{"0.0.0.0", 6881, false}}, {"foo", "bar"}, "0.0.0.0:6881");
|
||||
}
|
||||
|
||||
TORRENT_TEST(split_string)
|
||||
@ -508,3 +534,15 @@ TORRENT_TEST(find_first_of)
|
||||
TEST_EQUAL(find_first_of(test, "61", 3), 6);
|
||||
TEST_EQUAL(find_first_of(test, "61", 4), 6);
|
||||
}
|
||||
|
||||
TORRENT_TEST(strip_string)
|
||||
{
|
||||
TEST_EQUAL(strip_string(" ab"), "ab");
|
||||
TEST_EQUAL(strip_string(" ab "), "ab");
|
||||
TEST_EQUAL(strip_string(" "), "");
|
||||
TEST_EQUAL(strip_string(""), "");
|
||||
TEST_EQUAL(strip_string("a b"), "a b");
|
||||
TEST_EQUAL(strip_string(" a b "), "a b");
|
||||
TEST_EQUAL(strip_string(" \t \t ab\t\t\t"), "ab");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user