Skip to content

Commit

Permalink
cli: Sanitize ports in rpcconnect and rpcport
Browse files Browse the repository at this point in the history
Adds error handling of invalid ports to rpcconnect and rpcport,
with associated functional tests.
  • Loading branch information
tdb3 committed Mar 30, 2024
1 parent 61de64d commit caa5242
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 2 deletions.
25 changes: 23 additions & 2 deletions src/bitcoin-cli.cpp
Expand Up @@ -747,8 +747,29 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
// 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
// 3. default port for chain
uint16_t port{BaseParams().RPCPort()};
SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
port = static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", port));
{
uint16_t rpcconnect_port{0};
const std::string rpcconnect_str = gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
if (!SplitHostPort(rpcconnect_str, rpcconnect_port, host)) {
throw std::runtime_error(strprintf("Invalid port provided in -rpcconnect: %s", rpcconnect_str));
} else {
if (rpcconnect_port != 0) {
// Use the valid port provided in rpcconnect
port = rpcconnect_port;
} // else, no port was provided in rpcconnect (continue using default one)
}

if (std::optional<std::string> rpcport_arg = gArgs.GetArg("-rpcport")) {
// -rpcport was specified
std::optional<uint16_t> rpcport_int{ToIntegral<uint16_t>(rpcport_arg.value())};
if (!rpcport_int.has_value() || rpcport_int.value() == 0) {
throw std::runtime_error(strprintf("Invalid port provided in -rpcport: %s", rpcport_arg.value()));
}

// Use the valid port provided
port = rpcport_int.value();
}
}

// Obtain event base
raii_event_base base = obtain_event_base();
Expand Down
30 changes: 30 additions & 0 deletions test/functional/interface_bitcoin_cli.py
Expand Up @@ -15,6 +15,7 @@
assert_raises_process_error,
assert_raises_rpc_error,
get_auth_cookie,
rpc_port,
)
import time

Expand Down Expand Up @@ -107,6 +108,35 @@ def run_test(self):
self.log.info("Test connecting to a non-existing server")
assert_raises_process_error(1, "Could not connect to the server", self.nodes[0].cli('-rpcport=1').echo)

self.log.info("Test handling of invalid ports in rpcconnect")
assert_raises_process_error(1, "Invalid port provided in -rpcconnect: 127.0.0.1:notaport", self.nodes[0].cli("-rpcconnect=127.0.0.1:notaport").echo)
assert_raises_process_error(1, "Invalid port provided in -rpcconnect: 127.0.0.1:-1", self.nodes[0].cli("-rpcconnect=127.0.0.1:-1").echo)
assert_raises_process_error(1, "Invalid port provided in -rpcconnect: 127.0.0.1:0", self.nodes[0].cli("-rpcconnect=127.0.0.1:0").echo)
assert_raises_process_error(1, "Invalid port provided in -rpcconnect: 127.0.0.1:65536", self.nodes[0].cli("-rpcconnect=127.0.0.1:65536").echo)

self.log.info("Test handling of invalid ports in rpcport")
assert_raises_process_error(1, "Invalid port provided in -rpcport: notaport", self.nodes[0].cli("-rpcport=notaport").echo)
assert_raises_process_error(1, "Invalid port provided in -rpcport: -1", self.nodes[0].cli("-rpcport=-1").echo)
assert_raises_process_error(1, "Invalid port provided in -rpcport: 0", self.nodes[0].cli("-rpcport=0").echo)
assert_raises_process_error(1, "Invalid port provided in -rpcport: 65536", self.nodes[0].cli("-rpcport=65536").echo)

self.log.info("Test port usage preferences")
node_rpc_port = rpc_port(self.nodes[0].index)
# Prevent bitcoin-cli from using existing rpcport in conf
conf_rpcport = "rpcport=" + str(node_rpc_port)
self.nodes[0].replace_in_config([(conf_rpcport, "#" + conf_rpcport)])
# prefer rpcport over rpcconnect
assert_raises_process_error(1, "Could not connect to the server 127.0.0.1:1", self.nodes[0].cli(f"-rpcconnect=127.0.0.1:{node_rpc_port}", "-rpcport=1").echo)
assert_equal(BLOCKS, self.nodes[0].cli("-rpcconnect=127.0.0.1:18999", f'-rpcport={node_rpc_port}').getblockcount())
# prefer rpcconnect port over default
assert_equal(BLOCKS, self.nodes[0].cli(f"-rpcconnect=127.0.0.1:{node_rpc_port}").getblockcount())
# prefer rpcport over default
assert_equal(BLOCKS, self.nodes[0].cli(f'-rpcport={node_rpc_port}').getblockcount())
# use default if nothing else available
assert_raises_process_error(1, "Could not connect to the server 127.0.0.1:18443", self.nodes[0].cli("-rpcconnect=127.0.0.1").echo)
# Re-enable rpcport in conf if present
self.nodes[0].replace_in_config([("#" + conf_rpcport, conf_rpcport)])

self.log.info("Test connecting with non-existing RPC cookie file")
assert_raises_process_error(1, "Could not locate RPC credentials", self.nodes[0].cli('-rpccookiefile=does-not-exist', '-rpcpassword=').echo)

Expand Down

0 comments on commit caa5242

Please sign in to comment.