mirror of https://github.com/bitcoin/bitcoin.git
config: allow setting -proxy per network
`-proxy=addr:port` specifies the proxy for all networks (except I2P). Previously only the Tor proxy could have been specified separately via `-onion=addr:port`. Make it possible to specify separately the proxy for IPv4, IPv6, Tor and CJDNS by e.g. `-proxy=addr:port=ipv6`. Or remove the proxy for a given network, e.g. `-proxy=0=cjdns`. Resolves: https://github.com/bitcoin/bitcoin/issues/24450
This commit is contained in:
parent
baa848b8d3
commit
ca5781e23a
137
src/init.cpp
137
src/init.cpp
|
@ -552,11 +552,26 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
|
|||
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet3: %u, testnet4: %u, signet: %u, regtest: %u). Not relevant for I2P (see doc/i2p.md). If set to a value x, the default onion listening port will be set to x+1.", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), testnet4ChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
|
||||
const std::string proxy_doc_for_value =
|
||||
#ifdef HAVE_SOCKADDR_UN
|
||||
argsman.AddArg("-proxy=<ip:port|path>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled). May be a local file path prefixed with 'unix:' if the proxy supports it.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION);
|
||||
"<ip>[:<port>]|unix:<path>";
|
||||
#else
|
||||
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION);
|
||||
"<ip>[:<port>]";
|
||||
#endif
|
||||
const std::string proxy_doc_for_unix_socket =
|
||||
#ifdef HAVE_SOCKADDR_UN
|
||||
"May be a local file path prefixed with 'unix:' if the proxy supports it. ";
|
||||
#else
|
||||
"";
|
||||
#endif
|
||||
argsman.AddArg("-proxy=" + proxy_doc_for_value + "[=<network>]",
|
||||
"Connect through SOCKS5 proxy, set -noproxy to disable. " +
|
||||
proxy_doc_for_unix_socket +
|
||||
"Could end in =network to set the proxy only for that network. " +
|
||||
"The network can be any of ipv4, ipv6, tor or cjdns. " +
|
||||
"(default: disabled)",
|
||||
ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION,
|
||||
OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes. During startup, seednodes will be tried before dnsseeds.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
|
@ -1189,31 +1204,34 @@ bool CheckHostPortOptions(const ArgsManager& args) {
|
|||
}
|
||||
}
|
||||
|
||||
for ([[maybe_unused]] const auto& [arg, unix] : std::vector<std::pair<std::string, bool>>{
|
||||
// arg name UNIX socket support
|
||||
{"-i2psam", false},
|
||||
{"-onion", true},
|
||||
{"-proxy", true},
|
||||
{"-rpcbind", false},
|
||||
{"-torcontrol", false},
|
||||
{"-whitebind", false},
|
||||
{"-zmqpubhashblock", true},
|
||||
{"-zmqpubhashtx", true},
|
||||
{"-zmqpubrawblock", true},
|
||||
{"-zmqpubrawtx", true},
|
||||
{"-zmqpubsequence", true},
|
||||
for ([[maybe_unused]] const auto& [param_name, unix, suffix_allowed] : std::vector<std::tuple<std::string, bool, bool>>{
|
||||
// arg name UNIX socket support =suffix allowed
|
||||
{"-i2psam", false, false},
|
||||
{"-onion", true, false},
|
||||
{"-proxy", true, true},
|
||||
{"-bind", false, true},
|
||||
{"-rpcbind", false, false},
|
||||
{"-torcontrol", false, false},
|
||||
{"-whitebind", false, false},
|
||||
{"-zmqpubhashblock", true, false},
|
||||
{"-zmqpubhashtx", true, false},
|
||||
{"-zmqpubrawblock", true, false},
|
||||
{"-zmqpubrawtx", true, false},
|
||||
{"-zmqpubsequence", true, false},
|
||||
}) {
|
||||
for (const std::string& socket_addr : args.GetArgs(arg)) {
|
||||
for (const std::string& param_value : args.GetArgs(param_name)) {
|
||||
const std::string param_value_hostport{
|
||||
suffix_allowed ? param_value.substr(0, param_value.rfind('=')) : param_value};
|
||||
std::string host_out;
|
||||
uint16_t port_out{0};
|
||||
if (!SplitHostPort(socket_addr, port_out, host_out)) {
|
||||
if (!SplitHostPort(param_value_hostport, port_out, host_out)) {
|
||||
#ifdef HAVE_SOCKADDR_UN
|
||||
// Allow unix domain sockets for some options e.g. unix:/some/file/path
|
||||
if (!unix || !socket_addr.starts_with(ADDR_PREFIX_UNIX)) {
|
||||
return InitError(InvalidPortErrMsg(arg, socket_addr));
|
||||
if (!unix || !param_value.starts_with(ADDR_PREFIX_UNIX)) {
|
||||
return InitError(InvalidPortErrMsg(param_name, param_value));
|
||||
}
|
||||
#else
|
||||
return InitError(InvalidPortErrMsg(arg, socket_addr));
|
||||
return InitError(InvalidPortErrMsg(param_name, param_value));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1569,33 +1587,66 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
// Check for host lookup allowed before parsing any network related parameters
|
||||
fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
|
||||
|
||||
Proxy onion_proxy;
|
||||
|
||||
bool proxyRandomize = args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
|
||||
// -proxy sets a proxy for all outgoing network traffic
|
||||
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
|
||||
std::string proxyArg = args.GetArg("-proxy", "");
|
||||
if (proxyArg != "" && proxyArg != "0") {
|
||||
Proxy addrProxy;
|
||||
if (IsUnixSocketPath(proxyArg)) {
|
||||
addrProxy = Proxy(proxyArg, /*tor_stream_isolation=*/proxyRandomize);
|
||||
} else {
|
||||
const std::optional<CService> proxyAddr{Lookup(proxyArg, 9050, fNameLookup)};
|
||||
if (!proxyAddr.has_value()) {
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
|
||||
// -proxy sets a proxy for outgoing network traffic, possibly per network.
|
||||
// -noproxy, -proxy=0 or -proxy="" can be used to remove the proxy setting, this is the default
|
||||
Proxy ipv4_proxy;
|
||||
Proxy ipv6_proxy;
|
||||
Proxy onion_proxy;
|
||||
Proxy name_proxy;
|
||||
Proxy cjdns_proxy;
|
||||
for (const std::string& param_value : args.GetArgs("-proxy")) {
|
||||
const auto eq_pos{param_value.rfind('=')};
|
||||
const std::string proxy_str{param_value.substr(0, eq_pos)}; // e.g. 127.0.0.1:9050=ipv4 -> 127.0.0.1:9050
|
||||
std::string net_str;
|
||||
if (eq_pos != std::string::npos) {
|
||||
if (eq_pos + 1 == param_value.length()) {
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname, ends with '=': '%s'"), param_value));
|
||||
}
|
||||
|
||||
addrProxy = Proxy(proxyAddr.value(), /*tor_stream_isolation=*/proxyRandomize);
|
||||
net_str = ToLower(param_value.substr(eq_pos + 1)); // e.g. 127.0.0.1:9050=ipv4 -> ipv4
|
||||
}
|
||||
|
||||
if (!addrProxy.IsValid())
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
|
||||
Proxy proxy;
|
||||
if (!proxy_str.empty() && proxy_str != "0") {
|
||||
if (IsUnixSocketPath(proxy_str)) {
|
||||
proxy = Proxy{proxy_str, /*tor_stream_isolation=*/proxyRandomize};
|
||||
} else {
|
||||
const std::optional<CService> addr{Lookup(proxy_str, DEFAULT_TOR_SOCKS_PORT, fNameLookup)};
|
||||
if (!addr.has_value()) {
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxy_str));
|
||||
}
|
||||
proxy = Proxy{addr.value(), /*tor_stream_isolation=*/proxyRandomize};
|
||||
}
|
||||
if (!proxy.IsValid()) {
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxy_str));
|
||||
}
|
||||
}
|
||||
|
||||
SetProxy(NET_IPV4, addrProxy);
|
||||
SetProxy(NET_IPV6, addrProxy);
|
||||
SetProxy(NET_CJDNS, addrProxy);
|
||||
SetNameProxy(addrProxy);
|
||||
onion_proxy = addrProxy;
|
||||
if (net_str.empty()) { // For all networks.
|
||||
ipv4_proxy = ipv6_proxy = name_proxy = cjdns_proxy = onion_proxy = proxy;
|
||||
} else if (net_str == "ipv4") {
|
||||
ipv4_proxy = name_proxy = proxy;
|
||||
} else if (net_str == "ipv6") {
|
||||
ipv6_proxy = name_proxy = proxy;
|
||||
} else if (net_str == "tor" || net_str == "onion") {
|
||||
onion_proxy = proxy;
|
||||
} else if (net_str == "cjdns") {
|
||||
cjdns_proxy = proxy;
|
||||
} else {
|
||||
return InitError(strprintf(_("Unrecognized network in -proxy='%s': '%s'"), param_value, net_str));
|
||||
}
|
||||
}
|
||||
if (ipv4_proxy.IsValid()) {
|
||||
SetProxy(NET_IPV4, ipv4_proxy);
|
||||
}
|
||||
if (ipv6_proxy.IsValid()) {
|
||||
SetProxy(NET_IPV6, ipv6_proxy);
|
||||
}
|
||||
if (name_proxy.IsValid()) {
|
||||
SetNameProxy(name_proxy);
|
||||
}
|
||||
if (cjdns_proxy.IsValid()) {
|
||||
SetProxy(NET_CJDNS, cjdns_proxy);
|
||||
}
|
||||
|
||||
const bool onlynet_used_with_onion{!onlynets.empty() && g_reachable_nets.Contains(NET_ONION)};
|
||||
|
@ -1616,7 +1667,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
if (IsUnixSocketPath(onionArg)) {
|
||||
onion_proxy = Proxy(onionArg, /*tor_stream_isolation=*/proxyRandomize);
|
||||
} else {
|
||||
const std::optional<CService> addr{Lookup(onionArg, 9050, fNameLookup)};
|
||||
const std::optional<CService> addr{Lookup(onionArg, DEFAULT_TOR_SOCKS_PORT, fNameLookup)};
|
||||
if (!addr.has_value() || !addr->IsValid()) {
|
||||
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
|
||||
}
|
||||
|
|
|
@ -71,7 +71,6 @@ static const float RECONNECT_TIMEOUT_MAX = 600.0;
|
|||
* this is belt-and-suspenders sanity limit to prevent memory exhaustion.
|
||||
*/
|
||||
static const int MAX_LINE_LENGTH = 100000;
|
||||
static const uint16_t DEFAULT_TOR_SOCKS_PORT = 9050;
|
||||
|
||||
/****** Low-level TorControlConnection ********/
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
constexpr uint16_t DEFAULT_TOR_SOCKS_PORT{9050};
|
||||
constexpr int DEFAULT_TOR_CONTROL_PORT = 9051;
|
||||
extern const std::string DEFAULT_TOR_CONTROL;
|
||||
static const bool DEFAULT_LISTEN_ONION = true;
|
||||
|
|
|
@ -444,6 +444,47 @@ class ProxyTest(BitcoinTestFramework):
|
|||
msg = "Error: Unknown network specified in -onlynet: 'abc'"
|
||||
self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
|
||||
|
||||
self.log.info("Test passing trailing '=' raises expected init error")
|
||||
self.nodes[1].extra_args = ["-proxy=127.0.0.1:9050="]
|
||||
msg = "Error: Invalid -proxy address or hostname, ends with '=': '127.0.0.1:9050='"
|
||||
self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
|
||||
|
||||
self.log.info("Test passing unrecognized network raises expected init error")
|
||||
self.nodes[1].extra_args = ["-proxy=127.0.0.1:9050=foo"]
|
||||
msg = "Error: Unrecognized network in -proxy='127.0.0.1:9050=foo': 'foo'"
|
||||
self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
|
||||
|
||||
self.log.info("Test passing proxy only for IPv6")
|
||||
self.start_node(1, extra_args=["-proxy=127.6.6.6:6666=ipv6"])
|
||||
nets = networks_dict(self.nodes[1].getnetworkinfo())
|
||||
assert_equal(nets["ipv4"]["proxy"], "")
|
||||
assert_equal(nets["ipv6"]["proxy"], "127.6.6.6:6666")
|
||||
self.stop_node(1)
|
||||
|
||||
self.log.info("Test passing separate proxy for IPv4 and IPv6")
|
||||
self.start_node(1, extra_args=["-proxy=127.4.4.4:4444=ipv4", "-proxy=127.6.6.6:6666=ipv6"])
|
||||
nets = networks_dict(self.nodes[1].getnetworkinfo())
|
||||
assert_equal(nets["ipv4"]["proxy"], "127.4.4.4:4444")
|
||||
assert_equal(nets["ipv6"]["proxy"], "127.6.6.6:6666")
|
||||
self.stop_node(1)
|
||||
|
||||
self.log.info("Test overriding the Tor proxy")
|
||||
self.start_node(1, extra_args=["-proxy=127.1.1.1:1111", "-proxy=127.2.2.2:2222=tor"])
|
||||
nets = networks_dict(self.nodes[1].getnetworkinfo())
|
||||
assert_equal(nets["ipv4"]["proxy"], "127.1.1.1:1111")
|
||||
assert_equal(nets["ipv6"]["proxy"], "127.1.1.1:1111")
|
||||
assert_equal(nets["onion"]["proxy"], "127.2.2.2:2222")
|
||||
self.stop_node(1)
|
||||
|
||||
self.log.info("Test removing CJDNS proxy")
|
||||
self.start_node(1, extra_args=["-proxy=127.1.1.1:1111", "-proxy=0=cjdns"])
|
||||
nets = networks_dict(self.nodes[1].getnetworkinfo())
|
||||
assert_equal(nets["ipv4"]["proxy"], "127.1.1.1:1111")
|
||||
assert_equal(nets["ipv6"]["proxy"], "127.1.1.1:1111")
|
||||
assert_equal(nets["onion"]["proxy"], "127.1.1.1:1111")
|
||||
assert_equal(nets["cjdns"]["proxy"], "")
|
||||
self.stop_node(1)
|
||||
|
||||
self.log.info("Test passing too-long unix path to -proxy raises init error")
|
||||
self.nodes[1].extra_args = [f"-proxy=unix:{'x' * 1000}"]
|
||||
if self.have_unix_sockets:
|
||||
|
|
Loading…
Reference in New Issue