diff options
author | Gleb Naumenko <naumenko.gs@gmail.com> | 2020-05-16 21:05:44 -0400 |
---|---|---|
committer | Gleb Naumenko <naumenko.gs@gmail.com> | 2020-07-30 14:38:48 +0300 |
commit | acd6135b43941fa51d52f5fcdb2ce944280ad01e (patch) | |
tree | 4d49cad92bd7a620e2b48fee2ad5b29201aade63 | |
parent | 7cc0e8101f01891aa8be093a00d993bb7579c385 (diff) | |
download | bitcoin-acd6135b43941fa51d52f5fcdb2ce944280ad01e.tar.xz |
Cache responses to addr requests
Prevents a spy from scraping victim's AddrMan by
reconnecting and re-requesting addrs.
-rw-r--r-- | src/net.cpp | 11 | ||||
-rw-r--r-- | src/net.h | 31 | ||||
-rw-r--r-- | src/net_processing.cpp | 2 |
3 files changed, 43 insertions, 1 deletions
diff --git a/src/net.cpp b/src/net.cpp index 171358bb5f..bf29d928a1 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2539,6 +2539,17 @@ std::vector<CAddress> CConnman::GetAddresses() return addresses; } +std::vector<CAddress> CConnman::GetAddresses(Network requestor_network) +{ + const auto current_time = GetTime<std::chrono::microseconds>(); + if (m_addr_response_caches.find(requestor_network) == m_addr_response_caches.end() || + m_addr_response_caches[requestor_network].m_update_addr_response < current_time) { + m_addr_response_caches[requestor_network].m_addrs_response_cache = GetAddresses(); + m_addr_response_caches[requestor_network].m_update_addr_response = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6)); + } + return m_addr_response_caches[requestor_network].m_addrs_response_cache; +} + bool CConnman::AddNode(const std::string& strNode) { LOCK(cs_vAddedNodes); @@ -27,6 +27,7 @@ #include <atomic> #include <cstdint> #include <deque> +#include <map> #include <thread> #include <memory> #include <condition_variable> @@ -254,6 +255,13 @@ public: void MarkAddressGood(const CAddress& addr); void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0); std::vector<CAddress> GetAddresses(); + /** + * Cache is used to minimize topology leaks, so it should + * be used for all non-trusted calls, for example, p2p. + * A non-malicious call (from RPC) should + * call the function without a parameter to avoid using the cache. + */ + std::vector<CAddress> GetAddresses(Network requestor_network); // This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding // a peer that is better than all our current peers. @@ -419,6 +427,29 @@ private: unsigned int nPrevNodeCount{0}; /** + * Cache responses to addr requests to minimize privacy leak. + * Attack example: scraping addrs in real-time may allow an attacker + * to infer new connections of the victim by detecting new records + * with fresh timestamps (per self-announcement). + */ + struct CachedAddrResponse { + std::vector<CAddress> m_addrs_response_cache; + std::chrono::microseconds m_update_addr_response{0}; + }; + + /** + * Addr responses stored in different caches + * per network prevent cross-network node identification. + * If a node for example is multi-homed under Tor and IPv6, + * a single cache (or no cache at all) would let an attacker + * to easily detect that it is the same node by comparing responses. + * The used memory equals to 1000 CAddress records (or around 32 bytes) per + * distinct Network (up to 5) we have/had an inbound peer from, + * resulting in at most ~160 KB. + */ + std::map<Network, CachedAddrResponse> m_addr_response_caches; + + /** * Services this instance offers. * * This data is replicated in each CNode instance we create during peer diff --git a/src/net_processing.cpp b/src/net_processing.cpp index a591836f2e..a9f6ed960f 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3477,7 +3477,7 @@ void ProcessMessage( pfrom.fSentAddr = true; pfrom.vAddrToSend.clear(); - std::vector<CAddress> vAddr = connman.GetAddresses(); + std::vector<CAddress> vAddr = connman.GetAddresses(pfrom.addr.GetNetwork()); FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) { pfrom.PushAddress(addr, insecure_rand); |