Merge #15558: Don't query all DNS seeds at once

6170ec5d3a Do not query all DNS seed at once (Pieter Wuille)

Pull request description:

  Before this PR, when we don't have enough connections after 11 seconds, we proceed to query all DNS seeds in a fixed order, loading responses from all of them.

  Change this to to only query three randomly-selected DNS seed. If 11 seconds later we still don't have enough connections, try again with another one, and so on.

  This reduces the amount of information DNS seeds can observe about the requesters by spreading the load over all of them.

ACKs for top commit:
  Sjors:
    ACK 6170ec5d3
  sdaftuar:
    ACK 6170ec5d3a
  jonasschnelli:
    utACK 6170ec5d3a - I think the risk of a single seeder codebase is orthogonal to this PR. Such risks could also be interpreted differently (diversity could also increase the risk based on the threat model).
  fanquake:
    ACK 6170ec5d3a - Agree with the reasoning behind the change. Did some testing with and without `-forcednsseed` and/or a `peers.dat` and monitored the DNS activity.

Tree-SHA512: 33f6be5f924a85d312303ce272aa8f8d5e04cb616b4b492be98832e3ff37558d13d2b16ede68644ad399aff2bf5ff0ad33844e55eb40b7f8e3fddf9ae43add57
pull/16940/head
fanquake 5 years ago
commit 3ce8298888
No known key found for this signature in database
GPG Key ID: 2EEB9F5CC09526C1

@ -50,6 +50,9 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"
// Dump addresses to peers.dat every 15 minutes (900s) // Dump addresses to peers.dat every 15 minutes (900s)
static constexpr int DUMP_PEERS_INTERVAL = 15 * 60; static constexpr int DUMP_PEERS_INTERVAL = 15 * 60;
/** Number of DNS seeds to query when the number of connections is low. */
static constexpr int DNSSEEDS_TO_QUERY_AT_ONCE = 3;
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization. // We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1 #define FEELER_SLEEP_WINDOW 1
@ -1535,35 +1538,41 @@ void StopMapPort()
void CConnman::ThreadDNSAddressSeed() void CConnman::ThreadDNSAddressSeed()
{ {
// goal: only query DNS seeds if address need is acute FastRandomContext rng;
// Avoiding DNS seeds when we don't need them improves user privacy by std::vector<std::string> seeds = Params().DNSSeeds();
// creating fewer identifying DNS requests, reduces trust by giving seeds Shuffle(seeds.begin(), seeds.end(), rng);
// less influence on the network topology, and reduces traffic to the seeds. int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections
if ((addrman.size() > 0) && int found = 0;
(!gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) {
if (!interruptNet.sleep_for(std::chrono::seconds(11)))
return;
LOCK(cs_vNodes); if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) {
int nRelevant = 0; // When -forcednsseed is provided, query all.
for (const CNode* pnode : vNodes) { seeds_right_now = seeds.size();
nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
}
if (nRelevant >= 2) {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
return;
}
} }
const std::vector<std::string> &vSeeds = Params().DNSSeeds(); for (const std::string& seed : seeds) {
int found = 0; // goal: only query DNS seed if address need is acute
// Avoiding DNS seeds when we don't need them improves user privacy by
// creating fewer identifying DNS requests, reduces trust by giving seeds
// less influence on the network topology, and reduces traffic to the seeds.
if (addrman.size() > 0 && seeds_right_now == 0) {
if (!interruptNet.sleep_for(std::chrono::seconds(11))) return;
LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); LOCK(cs_vNodes);
int nRelevant = 0;
for (const CNode* pnode : vNodes) {
nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
}
if (nRelevant >= 2) {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
return;
}
seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE;
}
for (const std::string &seed : vSeeds) {
if (interruptNet) { if (interruptNet) {
return; return;
} }
LogPrintf("Loading addresses from DNS seed %s\n", seed);
if (HaveNameProxy()) { if (HaveNameProxy()) {
AddOneShot(seed); AddOneShot(seed);
} else { } else {
@ -1576,13 +1585,11 @@ void CConnman::ThreadDNSAddressSeed()
continue; continue;
} }
unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed
if (LookupHost(host.c_str(), vIPs, nMaxIPs, true)) if (LookupHost(host.c_str(), vIPs, nMaxIPs, true)) {
{ for (const CNetAddr& ip : vIPs) {
for (const CNetAddr& ip : vIPs)
{
int nOneDay = 24*3600; int nOneDay = 24*3600;
CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits);
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old addr.nTime = GetTime() - 3*nOneDay - rng.randrange(4*nOneDay); // use a random age between 3 and 7 days old
vAdd.push_back(addr); vAdd.push_back(addr);
found++; found++;
} }
@ -1593,8 +1600,8 @@ void CConnman::ThreadDNSAddressSeed()
AddOneShot(seed); AddOneShot(seed);
} }
} }
--seeds_right_now;
} }
LogPrintf("%d addresses found from DNS seeds\n", found); LogPrintf("%d addresses found from DNS seeds\n", found);
} }

Loading…
Cancel
Save