Skip to content

Commit

Permalink
GH-1101 Improve address parsing logic and add additional tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
heifner committed Jan 24, 2025
1 parent a0a621a commit 6c2442b
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 29 deletions.
21 changes: 17 additions & 4 deletions plugins/net_plugin/include/eosio/net_plugin/net_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,22 @@ namespace detail {
"Invalid address specification ${a}; IPv6 addresses must be enclosed in square brackets.", ("a", peer_add));
return {};

} else if (colon_count < 1 || colon_count > 3) {
EOS_ASSERT(!should_throw, chain::plugin_config_exception,
"Invalid address specification ${a}; unexpected number of colons.", ("a", peer_add));
return {};
}
string::size_type colon = peer_add.find(':', end_bracket+1);
if (colon == string::npos || colon == 0) {
return {};
}
if (end_bracket != 0 && end_bracket+1 != colon) {
EOS_ASSERT(!should_throw, chain::plugin_config_exception,
"Invalid address specification ${a}; unexpected character after ']'.", ("a", peer_add));
return {};
}
string::size_type colon2 = peer_add.find(':', colon + 1);
string host = (end_bracket > 0) ? peer_add.substr( 0, end_bracket+1 ) : peer_add.substr( 0, colon );
string host = peer_add.substr( 0, colon );
string port = peer_add.substr( colon + 1, colon2 == string::npos ? string::npos : colon2 - (colon + 1));
string remainder = colon2 == string::npos ? "" : peer_add.substr( colon2 + 1 );
return {std::move(host), std::move(port), std::move(remainder)};
Expand All @@ -85,17 +94,21 @@ namespace detail {
auto [host, port, remainder] = detail::split_host_port_remainder(peer_add, should_throw);
if (host.empty()) return {};

string::size_type end = remainder.find_first_of( " :+=.,<>!$%^&(*)|-#@\t" ); // future proof by including most symbols without using regex
std::string type = remainder.substr(0, end);
std::string type;
if (remainder.starts_with("blk") || remainder.starts_with("trx")) {
type = remainder.substr(0, 3);
}

return {std::move(host), std::move(port), std::move(type)};
}

/// @return listen address, type [trx|blk], and block sync rate limit (in bytes/sec) of address string
/// @return listen address and block sync rate limit (in bytes/sec) of address string
/// @throws chain::plugin_config_exception on invalid address
inline std::tuple<std::string, size_t> parse_listen_address( const std::string& address ) {
constexpr bool should_throw = true;
auto [host, port, remainder] = detail::split_host_port_remainder(address, should_throw);
EOS_ASSERT(!host.empty() && !port.empty(), chain::plugin_config_exception,
"Invalid address specification ${a}; host or port missing.", ("a", address));
auto listen_addr = host + ":" + port;
auto limit = remainder;
auto last_colon_location = remainder.rfind(':');
Expand Down
194 changes: 169 additions & 25 deletions plugins/net_plugin/tests/rate_limit_parse_unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,79 +26,223 @@ BOOST_AUTO_TEST_CASE(test_parse_rate_limit) {
, "0.0.0.0:9877:trx:640Kb/s"
, "0.0.0.0:9877:trx:999999999999999999999999999TiB/s"
, "0.0.0.0:9876:trx - 84c470d"
, "0.0.0.0:9877:trx:640KB/s - addition info"
, "0.0.0.0:9877:trx:640KB/s - additional info"
, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]additional info:trx:640KB/s"
, "0.0.0.0"
};
size_t which = 0;
auto [listen_addr, block_sync_rate_limit] = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
auto [listen_addr, block_sync_rate_limit] = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9876");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9776");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9877");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 640000u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "192.168.0.1:9878");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 20971520u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "localhost:9879");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 500u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "[::1]:9876");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u);
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "IPv6 addresses must be enclosed in square brackets");});
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "block sync rate limit must not be negative");});
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "invalid block sync rate limit specification");});
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "block sync rate limit specification overflowed");});
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9876");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9776");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9877");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 640000u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "192.168.0.1:9878");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 20971520u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "localhost:9879");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 500u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "[::1]:9876");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u);
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "IPv6 addresses must be enclosed in square brackets");});
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "block sync rate limit must not be negative");});
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "invalid block sync rate limit specification");});
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception,
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "block sync rate limit specification overflowed");});
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9876");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]);
std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9877");
BOOST_CHECK_EQUAL(block_sync_rate_limit, 640000u);
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "unexpected character after ']'");});
BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception,
[](const eosio::chain::plugin_config_exception& e)
{return std::strstr(e.top_message().c_str(), "unexpected number of colons");});
}

BOOST_AUTO_TEST_CASE(test_split_host_port_type) {
std::vector<std::string> p2p_addresses = {
"0.0.0.0:9876"
, "0.0.0.0:9776:0"
, "0.0.0.0:9877:640KB/s"
, "192.168.0.1:9878:20MiB/s"
, "localhost:9879:0.5KB/s"
, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876:250KB/s"
, "[::1]:9876:250KB/s"
, "2001:db8:85a3:8d3:1319:8a2e:370:7348:9876:250KB/s"
, "[::1]:9876:-250KB/s"
, "0.0.0.0:9877:640Kb/s"
, "0.0.0.0:9877:999999999999999999999999999TiB/s"
, "0.0.0.0:9876:trx"
, "0.0.0.0:9776:blk:0"
, "0.0.0.0:9877:trx:640KB/s"
, "192.168.0.1:9878:blk:20MiB/s"
, "localhost:9879:trx:0.5KB/s"
, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876:trx:250KB/s"
, "[::1]:9876:trx:250KB/s"
, "2001:db8:85a3:8d3:1319:8a2e:370:7348:9876:trx:250KB/s"
, "[::1]:9876:trx:-1KB/s"
, "0.0.0.0:9877:trx:640Kb/s"
, "0.0.0.0:9877:trx:999999999999999999999999999TiB/s"
, "0.0.0.0:9876:trx - 84c470d"
, "0.0.0.0:9877:trx:640KB/s - additional info"
, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]additional info:trx:640KB/s"
, "0.0.0.0"
};
size_t which = 0;
auto [host, port, type] = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9776");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9877");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "192.168.0.1");
BOOST_CHECK_EQUAL(port, "9878");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "localhost");
BOOST_CHECK_EQUAL(port, "9879");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "[::1]");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "");
BOOST_CHECK_EQUAL(port, "");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "[::1]");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9877");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9877");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9776");
BOOST_CHECK_EQUAL(type, "blk");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9877");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "192.168.0.1");
BOOST_CHECK_EQUAL(port, "9878");
BOOST_CHECK_EQUAL(type, "blk");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "localhost");
BOOST_CHECK_EQUAL(port, "9879");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "[::1]");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "");
BOOST_CHECK_EQUAL(port, "");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "[::1]");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9877");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9877");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9876");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "0.0.0.0");
BOOST_CHECK_EQUAL(port, "9877");
BOOST_CHECK_EQUAL(type, "trx");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "");
BOOST_CHECK_EQUAL(port, "");
BOOST_CHECK_EQUAL(type, "");
std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++));
BOOST_CHECK_EQUAL(host, "");
BOOST_CHECK_EQUAL(port, "");
BOOST_CHECK_EQUAL(type, "");
}

0 comments on commit 6c2442b

Please sign in to comment.