Merge #21328: net, refactor: pass uint16 CService::port as uint16

52dd40a9fe test: add missing netaddress include headers (Jon Atack)
6f09c0f6b5 util: add missing braces and apply clang format to SplitHostPort() (Jon Atack)
2875a764f7 util: add ParseUInt16(), use it in SplitHostPort() (Jon Atack)
6423c8175f p2p, refactor: pass and use uint16_t CService::port as uint16_t (Jon Atack)

Pull request description:

  As noticed during review today in https://github.com/bitcoin/bitcoin/pull/20685#discussion_r584873708 of the upcoming I2P network support, `CService::port` is `uint16_t` but is passed around the codebase and into the ctors as `int`, which causes uneeded conversions and casts. We can avoid these (including in the incoming I2P code without further changes to it) by using ports with the correct type. The remaining conversions are pushed out to the user input boundaries where they can be range-checked and raise with user feedback in the next patch.

ACKs for top commit:
  practicalswift:
    cr ACK 52dd40a9fe: patch looks correct
  MarcoFalke:
    cr ACK 52dd40a9fe
  vasild:
    ACK 52dd40a9fe

Tree-SHA512: 203c1cab3189a206c55ecada77b9548b810281cdc533252b8e3330ae0606b467731c75f730ce9deb07cbaab66facf97e1ffd2051084ff9077cba6750366b0432
This commit is contained in:
MarcoFalke 2021-03-19 20:47:04 +01:00
commit 18cd0888ef
No known key found for this signature in database
GPG Key ID: D2EA4850E7528B25
18 changed files with 82 additions and 56 deletions

View File

@ -618,9 +618,9 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
// 1. -rpcport // 1. -rpcport
// 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
// 3. default port for chain // 3. default port for chain
int port = BaseParams().RPCPort(); uint16_t port{BaseParams().RPCPort()};
SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
port = gArgs.GetArg("-rpcport", port); port = static_cast<uint16_t>(gArgs.GetArg("-rpcport", port));
// Obtain event base // Obtain event base
raii_event_base base = obtain_event_base(); raii_event_base base = obtain_event_base();

View File

@ -84,7 +84,7 @@ public:
const Consensus::Params& GetConsensus() const { return consensus; } const Consensus::Params& GetConsensus() const { return consensus; }
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
int GetDefaultPort() const { return nDefaultPort; } uint16_t GetDefaultPort() const { return nDefaultPort; }
const CBlock& GenesisBlock() const { return genesis; } const CBlock& GenesisBlock() const { return genesis; }
/** Default value for -checkmempool and -checkblockindex argument */ /** Default value for -checkmempool and -checkblockindex argument */
@ -121,7 +121,7 @@ protected:
Consensus::Params consensus; Consensus::Params consensus;
CMessageHeader::MessageStartChars pchMessageStart; CMessageHeader::MessageStartChars pchMessageStart;
int nDefaultPort; uint16_t nDefaultPort;
uint64_t nPruneAfterHeight; uint64_t nPruneAfterHeight;
uint64_t m_assumed_blockchain_size; uint64_t m_assumed_blockchain_size;
uint64_t m_assumed_chain_state_size; uint64_t m_assumed_chain_state_size;

View File

@ -290,8 +290,8 @@ static bool ThreadHTTP(struct event_base* base)
/** Bind HTTP server to specified addresses */ /** Bind HTTP server to specified addresses */
static bool HTTPBindAddresses(struct evhttp* http) static bool HTTPBindAddresses(struct evhttp* http)
{ {
int http_port = gArgs.GetArg("-rpcport", BaseParams().RPCPort()); uint16_t http_port{static_cast<uint16_t>(gArgs.GetArg("-rpcport", BaseParams().RPCPort()))};
std::vector<std::pair<std::string, uint16_t> > endpoints; std::vector<std::pair<std::string, uint16_t>> endpoints;
// Determine what addresses to bind to // Determine what addresses to bind to
if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs
@ -305,7 +305,7 @@ static bool HTTPBindAddresses(struct evhttp* http)
} }
} else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address
for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) { for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
int port = http_port; uint16_t port{http_port};
std::string host; std::string host;
SplitHostPort(strRPCBind, port, host); SplitHostPort(strRPCBind, port, host);
endpoints.push_back(std::make_pair(host, port)); endpoints.push_back(std::make_pair(host, port));

View File

@ -113,7 +113,7 @@ void CConnman::AddAddrFetch(const std::string& strDest)
uint16_t GetListenPort() uint16_t GetListenPort()
{ {
return (uint16_t)(gArgs.GetArg("-port", Params().GetDefaultPort())); return static_cast<uint16_t>(gArgs.GetArg("-port", Params().GetDefaultPort()));
} }
// find 'best' local address for a particular peer // find 'best' local address for a particular peer
@ -394,7 +394,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
// Resolve // Resolve
const int default_port = Params().GetDefaultPort(); const uint16_t default_port{Params().GetDefaultPort()};
if (pszDest) { if (pszDest) {
std::vector<CService> resolved; std::vector<CService> resolved;
if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
@ -462,7 +462,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
return nullptr; return nullptr;
} }
std::string host; std::string host;
int port = default_port; uint16_t port{default_port};
SplitHostPort(std::string(pszDest), port, host); SplitHostPort(std::string(pszDest), port, host);
bool proxyConnectionFailed; bool proxyConnectionFailed;
connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout, connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout,

View File

@ -229,7 +229,7 @@ extern std::string strSubVersion;
struct LocalServiceInfo { struct LocalServiceInfo {
int nScore; int nScore;
int nPort; uint16_t nPort;
}; };
extern RecursiveMutex cs_mapLocalHost; extern RecursiveMutex cs_mapLocalHost;

View File

@ -194,12 +194,12 @@ bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSL
return true; return true;
} }
bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function) bool Lookup(const std::string& name, std::vector<CService>& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function)
{ {
if (name.empty() || !ValidAsCString(name)) { if (name.empty() || !ValidAsCString(name)) {
return false; return false;
} }
int port = portDefault; uint16_t port{portDefault};
std::string hostname; std::string hostname;
SplitHostPort(name, port, hostname); SplitHostPort(name, port, hostname);
@ -213,7 +213,7 @@ bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefau
return true; return true;
} }
bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function) bool Lookup(const std::string& name, CService& addr, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function)
{ {
if (!ValidAsCString(name)) { if (!ValidAsCString(name)) {
return false; return false;
@ -226,7 +226,7 @@ bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllo
return true; return true;
} }
CService LookupNumeric(const std::string& name, int portDefault, DNSLookupFn dns_lookup_function) CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupFn dns_lookup_function)
{ {
if (!ValidAsCString(name)) { if (!ValidAsCString(name)) {
return {}; return {};
@ -363,7 +363,7 @@ static std::string Socks5ErrorString(uint8_t err)
} }
} }
bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& sock) bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& sock)
{ {
IntrRecvError recvr; IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest); LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
@ -665,7 +665,7 @@ bool IsProxy(const CNetAddr &addr) {
return false; return false;
} }
bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed) bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
{ {
// first connect to proxy server // first connect to proxy server
if (!ConnectSocketDirectly(proxy.proxy, sock.Get(), nTimeout, true)) { if (!ConnectSocketDirectly(proxy.proxy, sock.Get(), nTimeout, true)) {
@ -677,11 +677,11 @@ bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int
ProxyCredentials random_auth; ProxyCredentials random_auth;
static std::atomic_int counter(0); static std::atomic_int counter(0);
random_auth.username = random_auth.password = strprintf("%i", counter++); random_auth.username = random_auth.password = strprintf("%i", counter++);
if (!Socks5(strDest, (uint16_t)port, &random_auth, sock)) { if (!Socks5(strDest, port, &random_auth, sock)) {
return false; return false;
} }
} else { } else {
if (!Socks5(strDest, (uint16_t)port, 0, sock)) { if (!Socks5(strDest, port, 0, sock)) {
return false; return false;
} }
} }

View File

@ -111,7 +111,7 @@ extern DNSLookupFn g_dns_lookup;
* @returns Whether or not the specified host string successfully resolved to * @returns Whether or not the specified host string successfully resolved to
* any resulting network addresses. * any resulting network addresses.
* *
* @see Lookup(const std::string&, std::vector<CService>&, int, bool, unsigned int, DNSLookupFn) * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions. * for additional parameter descriptions.
*/ */
bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
@ -119,7 +119,7 @@ bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned in
/** /**
* Resolve a host string to its first corresponding network address. * Resolve a host string to its first corresponding network address.
* *
* @see LookupHost(const std::string&, std::vector<CNetAddr>&, unsigned int, bool, DNSLookupFn) * @see LookupHost(const std::string&, std::vector<CNetAddr>&, uint16_t, bool, DNSLookupFn)
* for additional parameter descriptions. * for additional parameter descriptions.
*/ */
bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
@ -129,7 +129,7 @@ bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSL
* *
* @param name The string representing a service. Could be a name or a * @param name The string representing a service. Could be a name or a
* numerical IP address (IPv6 addresses should be in their * numerical IP address (IPv6 addresses should be in their
* disambiguated bracketed form), optionally followed by a port * disambiguated bracketed form), optionally followed by a uint16_t port
* number. (e.g. example.com:8333 or * number. (e.g. example.com:8333 or
* [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420) * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420)
* @param[out] vAddr The resulting services to which the specified service string * @param[out] vAddr The resulting services to which the specified service string
@ -144,15 +144,15 @@ bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSL
* @returns Whether or not the service string successfully resolved to any * @returns Whether or not the service string successfully resolved to any
* resulting services. * resulting services.
*/ */
bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup); bool Lookup(const std::string& name, std::vector<CService>& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup);
/** /**
* Resolve a service string to its first corresponding service. * Resolve a service string to its first corresponding service.
* *
* @see Lookup(const std::string&, std::vector<CService>&, int, bool, unsigned int, DNSLookupFn) * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions. * for additional parameter descriptions.
*/ */
bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); bool Lookup(const std::string& name, CService& addr, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
/** /**
* Resolve a service string with a numeric IP to its first corresponding * Resolve a service string with a numeric IP to its first corresponding
@ -160,10 +160,10 @@ bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllo
* *
* @returns The resulting CService if the resolution was successful, [::]:0 otherwise. * @returns The resulting CService if the resolution was successful, [::]:0 otherwise.
* *
* @see Lookup(const std::string&, std::vector<CService>&, int, bool, unsigned int, DNSLookupFn) * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions. * for additional parameter descriptions.
*/ */
CService LookupNumeric(const std::string& name, int portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup); CService LookupNumeric(const std::string& name, uint16_t portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup);
/** /**
* Parse and resolve a specified subnet string into the appropriate internal * Parse and resolve a specified subnet string into the appropriate internal
@ -219,7 +219,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
* *
* @returns Whether or not the operation succeeded. * @returns Whether or not the operation succeeded.
*/ */
bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed); bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);
/** Disable or enable blocking-mode for a socket */ /** Disable or enable blocking-mode for a socket */
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking); bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking);
@ -245,6 +245,6 @@ void InterruptSocks5(bool interrupt);
* @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
* Version 5</a> * Version 5</a>
*/ */
bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& socket); bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& socket);
#endif // BITCOIN_NETBASE_H #endif // BITCOIN_NETBASE_H

View File

@ -67,7 +67,7 @@ private Q_SLOTS:
void updateDefaultProxyNets(); void updateDefaultProxyNets();
Q_SIGNALS: Q_SIGNALS:
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort); void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort);
private: private:
Ui::OptionsDialog *ui; Ui::OptionsDialog *ui;

View File

@ -914,7 +914,7 @@ static RPCHelpMan addpeeraddress()
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
std::string addr_string = request.params[0].get_str(); std::string addr_string = request.params[0].get_str();
uint16_t port = request.params[1].get_int(); uint16_t port{static_cast<uint16_t>(request.params[1].get_int())};
CNetAddr net_addr; CNetAddr net_addr;
if (!LookupHost(addr_string, net_addr, false)) { if (!LookupHost(addr_string, net_addr, false)) {

View File

@ -100,7 +100,7 @@ static CNetAddr ResolveIP(const std::string& ip)
return addr; return addr;
} }
static CService ResolveService(const std::string& ip, const int port = 0) static CService ResolveService(const std::string& ip, uint16_t port = 0)
{ {
CService serv; CService serv;
BOOST_CHECK_MESSAGE(Lookup(ip, serv, port, false), strprintf("failed to resolve: %s:%i", ip, port)); BOOST_CHECK_MESSAGE(Lookup(ip, serv, port, false), strprintf("failed to resolve: %s:%i", ip, port));

View File

@ -2,8 +2,9 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/fuzz/fuzz.h> #include <netaddress.h>
#include <util/asmap.h> #include <util/asmap.h>
#include <test/fuzz/fuzz.h>
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>

View File

@ -18,7 +18,7 @@ FUZZ_TARGET(netbase_dns_lookup)
const std::string name = fuzzed_data_provider.ConsumeRandomLengthString(512); const std::string name = fuzzed_data_provider.ConsumeRandomLengthString(512);
const unsigned int max_results = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); const unsigned int max_results = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
const bool allow_lookup = fuzzed_data_provider.ConsumeBool(); const bool allow_lookup = fuzzed_data_provider.ConsumeBool();
const int default_port = fuzzed_data_provider.ConsumeIntegral<int>(); const uint16_t default_port = fuzzed_data_provider.ConsumeIntegral<uint16_t>();
auto fuzzed_dns_lookup_function = [&](const std::string&, bool) { auto fuzzed_dns_lookup_function = [&](const std::string&, bool) {
std::vector<CNetAddr> resolved_addresses; std::vector<CNetAddr> resolved_addresses;

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h> #include <test/fuzz/fuzz.h>
@ -38,7 +39,7 @@ FUZZ_TARGET_INIT(socks5, initialize_socks5)
// This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within // This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within
// a few seconds of fuzzing. // a few seconds of fuzzing.
(void)Socks5(fuzzed_data_provider.ConsumeRandomLengthString(512), (void)Socks5(fuzzed_data_provider.ConsumeRandomLengthString(512),
fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeIntegral<uint16_t>(),
fuzzed_data_provider.ConsumeBool() ? &proxy_credentials : nullptr, fuzzed_data_provider.ConsumeBool() ? &proxy_credentials : nullptr,
fuzzed_sock); fuzzed_sock);
} }

View File

@ -5,6 +5,7 @@
#include <blockfilter.h> #include <blockfilter.h>
#include <clientversion.h> #include <clientversion.h>
#include <logging.h> #include <logging.h>
#include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <outputtype.h> #include <outputtype.h>
#include <rpc/client.h> #include <rpc/client.h>
@ -82,7 +83,7 @@ FUZZ_TARGET(string)
#ifndef WIN32 #ifndef WIN32
(void)ShellEscape(random_string_1); (void)ShellEscape(random_string_1);
#endif // WIN32 #endif // WIN32
int port_out; uint16_t port_out;
std::string host_out; std::string host_out;
SplitHostPort(random_string_1, port_out, host_out); SplitHostPort(random_string_1, port_out, host_out);
(void)TimingResistantEqual(random_string_1, random_string_2); (void)TimingResistantEqual(random_string_1, random_string_2);

View File

@ -8,6 +8,7 @@
#include <clientversion.h> #include <clientversion.h>
#include <cstdint> #include <cstdint>
#include <net.h> #include <net.h>
#include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <serialize.h> #include <serialize.h>
#include <span.h> #include <span.h>
@ -91,7 +92,7 @@ BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(cnode_listen_port) BOOST_AUTO_TEST_CASE(cnode_listen_port)
{ {
// test default // test default
uint16_t port = GetListenPort(); uint16_t port{GetListenPort()};
BOOST_CHECK(port == Params().GetDefaultPort()); BOOST_CHECK(port == Params().GetDefaultPort());
// test set port // test set port
uint16_t altPort = 12345; uint16_t altPort = 12345;

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <net_permissions.h> #include <net_permissions.h>
#include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <protocol.h> #include <protocol.h>
#include <serialize.h> #include <serialize.h>
@ -83,31 +84,31 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
} }
bool static TestSplitHost(std::string test, std::string host, int port) bool static TestSplitHost(const std::string& test, const std::string& host, uint16_t port)
{ {
std::string hostOut; std::string hostOut;
int portOut = -1; uint16_t portOut{0};
SplitHostPort(test, portOut, hostOut); SplitHostPort(test, portOut, hostOut);
return hostOut == host && port == portOut; return hostOut == host && port == portOut;
} }
BOOST_AUTO_TEST_CASE(netbase_splithost) BOOST_AUTO_TEST_CASE(netbase_splithost)
{ {
BOOST_CHECK(TestSplitHost("www.bitcoincore.org", "www.bitcoincore.org", -1)); BOOST_CHECK(TestSplitHost("www.bitcoincore.org", "www.bitcoincore.org", 0));
BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]", "www.bitcoincore.org", -1)); BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]", "www.bitcoincore.org", 0));
BOOST_CHECK(TestSplitHost("www.bitcoincore.org:80", "www.bitcoincore.org", 80)); BOOST_CHECK(TestSplitHost("www.bitcoincore.org:80", "www.bitcoincore.org", 80));
BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]:80", "www.bitcoincore.org", 80)); BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]:80", "www.bitcoincore.org", 80));
BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1)); BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", 0));
BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333)); BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1)); BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", 0));
BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333)); BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333));
BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1)); BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", 0));
BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333)); BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333));
BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333)); BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333));
BOOST_CHECK(TestSplitHost("::8333", "::8333", -1)); BOOST_CHECK(TestSplitHost("::8333", "::8333", 0));
BOOST_CHECK(TestSplitHost(":8333", "", 8333)); BOOST_CHECK(TestSplitHost(":8333", "", 8333));
BOOST_CHECK(TestSplitHost("[]:8333", "", 8333)); BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
BOOST_CHECK(TestSplitHost("", "", -1)); BOOST_CHECK(TestSplitHost("", "", 0));
} }
bool static TestParse(std::string src, std::string canon) bool static TestParse(std::string src, std::string canon)

View File

@ -107,23 +107,25 @@ std::vector<unsigned char> ParseHex(const std::string& str)
return ParseHex(str.c_str()); return ParseHex(str.c_str());
} }
void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut)
{
size_t colon = in.find_last_of(':'); size_t colon = in.find_last_of(':');
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
bool fHaveColon = colon != in.npos; bool fHaveColon = colon != in.npos;
bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); bool fMultiColon = fHaveColon && (in.find_last_of(':', colon - 1) != in.npos);
if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
int32_t n; uint16_t n;
if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { if (ParseUInt16(in.substr(colon + 1), &n)) {
in = in.substr(0, colon); in = in.substr(0, colon);
portOut = n; portOut = n;
} }
} }
if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
hostOut = in.substr(1, in.size()-2); hostOut = in.substr(1, in.size() - 2);
else } else {
hostOut = in; hostOut = in;
}
} }
std::string EncodeBase64(Span<const unsigned char> input) std::string EncodeBase64(Span<const unsigned char> input)
@ -334,6 +336,18 @@ bool ParseUInt8(const std::string& str, uint8_t *out)
return true; return true;
} }
bool ParseUInt16(const std::string& str, uint16_t* out)
{
uint32_t u32;
if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint16_t>::max()) {
return false;
}
if (out != nullptr) {
*out = static_cast<uint16_t>(u32);
}
return true;
}
bool ParseUInt32(const std::string& str, uint32_t *out) bool ParseUInt32(const std::string& str, uint32_t *out)
{ {
if (!ParsePrechecks(str)) if (!ParsePrechecks(str))

View File

@ -65,7 +65,7 @@ std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
*/ */
std::string EncodeBase32(const std::string& str, bool pad = true); std::string EncodeBase32(const std::string& str, bool pad = true);
void SplitHostPort(std::string in, int& portOut, std::string& hostOut); void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut);
int64_t atoi64(const std::string& str); int64_t atoi64(const std::string& str);
int atoi(const std::string& str); int atoi(const std::string& str);
@ -115,6 +115,13 @@ constexpr inline bool IsSpace(char c) noexcept {
*/ */
[[nodiscard]] bool ParseUInt8(const std::string& str, uint8_t *out); [[nodiscard]] bool ParseUInt8(const std::string& str, uint8_t *out);
/**
* Convert decimal string to unsigned 16-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if the entire string could not be parsed or if overflow or underflow occurred.
*/
[[nodiscard]] bool ParseUInt16(const std::string& str, uint16_t* out);
/** /**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback. * Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer, * @returns true if the entire string could be parsed as valid integer,