From e1000940beaa3fa9730ac49fc3d9a05f39dfef03 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 20 Nov 2025 10:00:46 +0100 Subject: [PATCH 1/3] CNetAddr: Add IsBindAny method to check for INADDR_ANY Port of: https://github.com/bitcoin/bitcoin/commit/d6a1287481428d982dc03be3a6d9aeef8398f468 --- src/netbase.cpp | 10 ++++++++++ src/netbase.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/netbase.cpp b/src/netbase.cpp index 3a3faad1c26..b1fd32fdcaa 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -734,6 +734,16 @@ unsigned int CNetAddr::GetByte(int n) const return ip[15-n]; } +bool CNetAddr::IsBindAny() const +{ + const int cmplen = IsIPv4() ? 4 : 16; + for (int i = 0; i < cmplen; ++i) { + if (GetByte(i)) return false; + } + + return true; +} + bool CNetAddr::IsIPv4() const { return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); diff --git a/src/netbase.h b/src/netbase.h index 89ad56f1bd3..2b52b4048e0 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -75,6 +75,7 @@ class CNetAddr void SetRaw(Network network, const uint8_t *data); bool SetSpecial(const std::string &strName); // for Tor addresses + bool IsBindAny() const; // INADDR_ANY equivalent bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) From 921c2f1c7cd06ca48dbd0ecf94fca08b7abb6b8e Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 20 Nov 2025 10:02:09 +0100 Subject: [PATCH 2/3] net: Always default rpcbind to localhost, never "all interfaces" https://github.com/bitcoin/bitcoin/pull/14532 --- src/httpserver.cpp | 16 ++++++++++------ src/init.cpp | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index f0b98dae3c6..866ddec4ad7 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -336,23 +336,23 @@ static bool HTTPBindAddresses(struct evhttp* http) std::vector > endpoints; // Determine what addresses to bind to - if (!mapArgs.count("-rpcallowip")) { // Default to loopback if not allowing external IPs + if (!(mapArgs.count("-rpcallowip") && mapArgs.count("-rpcbind"))) { // Default to loopback if not allowing external IPs endpoints.push_back(std::make_pair("::1", defaultPort)); endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); + if (mapArgs.count("-rpcallowip")) { + LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n"); + } if (mapArgs.count("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); } } else if (mapArgs.count("-rpcbind")) { // Specific bind address const std::vector& vbind = mapMultiArgs["-rpcbind"]; - for (std::vector::const_iterator i = vbind.begin(); i != vbind.end(); ++i) { + for (const std::string& strRPCBind : vbind) { int port = defaultPort; std::string host; - SplitHostPort(*i, port, host); + SplitHostPort(strRPCBind, port, host); endpoints.push_back(std::make_pair(host, port)); } - } else { // No specific bind address specified, bind to any - endpoints.push_back(std::make_pair("::", defaultPort)); - endpoints.push_back(std::make_pair("0.0.0.0", defaultPort)); } // Bind addresses @@ -360,6 +360,10 @@ static bool HTTPBindAddresses(struct evhttp* http) LogPrint("http", "Binding RPC on address %s port %i\n", i->first, i->second); evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? NULL : i->first.c_str(), i->second); if (bind_handle) { + CNetAddr addr; + if (i->first.empty() || (LookupHost(i->first.c_str(), addr, false) && addr.IsBindAny())) { + LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n"); + } boundSockets.push_back(bind_handle); } else { LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second); diff --git a/src/init.cpp b/src/init.cpp index 941c59496ae..fccadbe051a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -564,7 +564,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("RPC server options:")); strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), 0)); - strUsage += HelpMessageOpt("-rpcbind=", _("Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)")); + strUsage += HelpMessageOpt("-rpcbind=[:port]", _("Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)")); strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcport=", strprintf(_("Listen for JSON-RPC connections on (default: %u or testnet: %u)"), 7771, 17771)); From 75ca0881d33329c682057dac3b2a797b29dcda2f Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 20 Nov 2025 10:17:24 +0100 Subject: [PATCH 3/3] netbase: Add overloaded LookupHost function to resolve a host string to a single network address --- src/netbase.cpp | 13 +++++++++++++ src/netbase.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/netbase.cpp b/src/netbase.cpp index b1fd32fdcaa..63bb6ea2c81 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -219,6 +219,19 @@ bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nM return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup); } + /** + * Resolve a host string to its first corresponding network address. + */ + bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup) + { + std::vector vIP; + LookupHost(pszName, vIP, 1, fAllowLookup); + if(vIP.empty()) + return false; + addr = vIP.front(); + return true; + } + bool Lookup(const char *pszName, std::vector& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions) { if (pszName[0] == 0) diff --git a/src/netbase.h b/src/netbase.h index 2b52b4048e0..72cafbb6c18 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -223,6 +223,7 @@ bool IsProxy(const CNetAddr &addr); bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); +bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup); bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true); bool Lookup(const char *pszName, std::vector& vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0); bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0);