Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 117 additions & 82 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2189,7 +2189,30 @@ void CConnman::ThreadDNSAddressSeed()
std::vector<std::string> seeds = m_params.DNSSeeds();
Shuffle(seeds.begin(), seeds.end(), rng);
int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections
int found = 0;
int target_outbound_connections = 2;
int outbound_connection_count = 0;

auto start = NodeClock::now();
if (gArgs.IsArgSet("-seednode")) {
LogPrintf("-seednode enabled. Trying the provided seeds before defaulting to the dnsseeds.\n");
while (!interruptNet) {
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
return;

// Abort if we have spent enough time without reaching our target.
// Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
if (NodeClock::now() > start + 30s) {
LogPrintf("Couldn't connect to enough peers via seed nodes. Handing fetch logic to the DNS seeds.\n");
break;
}

outbound_connection_count = GetFullOutboundConnCount();
if (outbound_connection_count >= target_outbound_connections) {
LogPrintf("P2P peers available. Finished fetching data from seed nodes.\n");
break;
}
}
}

if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) {
// When -forcednsseed is provided, query all.
Expand All @@ -2201,98 +2224,97 @@ void CConnman::ThreadDNSAddressSeed()
seeds_right_now = seeds.size();
}

// goal: only query DNS seed if address need is acute
// * If we have a reasonable number of peers in addrman, spend
// some time trying them first. This 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.
// * When querying DNS seeds query a few at once, this ensures
// that we don't give DNS seeds the ability to eclipse nodes
// that query them.
// * If we continue having problems, eventually query all the
// DNS seeds, and if that fails too, also try the fixed seeds.
// (done in ThreadOpenConnections)
const std::chrono::seconds seeds_wait_time = (addrman.Size() >= DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS);

for (const std::string& seed : seeds) {
if (seeds_right_now == 0) {
seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE;

if (addrman.Size() > 0) {
LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count());
std::chrono::seconds to_wait = seeds_wait_time;
while (to_wait.count() > 0) {
// if sleeping for the MANY_PEERS interval, wake up
// early to see if we have enough peers and can stop
// this thread entirely freeing up its resources
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
if (!interruptNet.sleep_for(w)) return;
to_wait -= w;

int nRelevant = 0;
{
LOCK(m_nodes_mutex);
for (const CNode* pnode : m_nodes) {
if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn()) ++nRelevant;
// Proceed with dnsseeds if seednodes hasn't reached the target or if forcednsseed is set
if (outbound_connection_count < target_outbound_connections || seeds_right_now) {
// goal: only query DNS seed if address need is acute
// * If we have a reasonable number of peers in addrman, spend
// some time trying them first. This 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.
// * When querying DNS seeds query a few at once, this ensures
// that we don't give DNS seeds the ability to eclipse nodes
// that query them.
// * If we continue having problems, eventually query all the
// DNS seeds, and if that fails too, also try the fixed seeds.
// (done in ThreadOpenConnections)
int found = 0;
const std::chrono::seconds seeds_wait_time = (addrman.Size() >= DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS);

for (const std::string& seed : seeds) {
if (seeds_right_now == 0) {
seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE;

if (addrman.Size() > 0) {
LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count());
std::chrono::seconds to_wait = seeds_wait_time;
while (to_wait.count() > 0) {
// if sleeping for the MANY_PEERS interval, wake up
// early to see if we have enough peers and can stop
// this thread entirely freeing up its resources
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
if (!interruptNet.sleep_for(w)) return;
to_wait -= w;

if (GetFullOutboundConnCount() >= target_outbound_connections) {
if (found > 0) {
LogPrintf("%d addresses found from DNS seeds\n", found);
LogPrintf("P2P peers available. Finished DNS seeding.\n");
} else {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
}
return;
}
}
if (nRelevant >= 2) {
if (found > 0) {
LogPrintf("%d addresses found from DNS seeds\n", found);
LogPrintf("P2P peers available. Finished DNS seeding.\n");
} else {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
}
return;
}
}
}
}

if (interruptNet) return;

// hold off on querying seeds if P2P network deactivated
if (!fNetworkActive) {
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
do {
if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
} while (!fNetworkActive);
}
if (interruptNet) return;

LogPrintf("Loading addresses from DNS seed %s\n", seed);
// If -proxy is in use, we make an ADDR_FETCH connection to the DNS resolved peer address
// for the base dns seed domain in chainparams
if (HaveNameProxy()) {
AddAddrFetch(seed);
} else {
std::vector<CAddress> vAdd;
constexpr ServiceFlags requiredServiceBits{SeedsServiceFlags()};
std::string host = strprintf("x%x.%s", requiredServiceBits, seed);
CNetAddr resolveSource;
if (!resolveSource.SetInternal(host)) {
continue;
// hold off on querying seeds if P2P network deactivated
if (!fNetworkActive) {
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
do {
if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
} while (!fNetworkActive);
}
unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed
const auto addresses{LookupHost(host, nMaxIPs, true)};
if (!addresses.empty()) {
for (const CNetAddr& ip : addresses) {
CAddress addr = CAddress(CService(ip, m_params.GetDefaultPort()), requiredServiceBits);
addr.nTime = rng.rand_uniform_delay(Now<NodeSeconds>() - 3 * 24h, -4 * 24h); // use a random age between 3 and 7 days old
vAdd.push_back(addr);
found++;
}
addrman.Add(vAdd, resolveSource);
} else {
// If the seed does not support a subdomain with our desired service bits,
// we make an ADDR_FETCH connection to the DNS resolved peer address for the
// base dns seed domain in chainparams

LogPrintf("Loading addresses from DNS seed %s\n", seed);
// If -proxy is in use, we make an ADDR_FETCH connection to the DNS resolved peer address
// for the base dns seed domain in chainparams
if (HaveNameProxy()) {
AddAddrFetch(seed);
} else {
std::vector<CAddress> vAdd;
constexpr ServiceFlags requiredServiceBits{SeedsServiceFlags()};
std::string host = strprintf("x%x.%s", requiredServiceBits, seed);
CNetAddr resolveSource;
if (!resolveSource.SetInternal(host)) {
continue;
}
unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed
const auto addresses{LookupHost(host, nMaxIPs, true)};
if (!addresses.empty()) {
for (const CNetAddr& ip : addresses) {
CAddress addr = CAddress(CService(ip, m_params.GetDefaultPort()), requiredServiceBits);
addr.nTime = rng.rand_uniform_delay(Now<NodeSeconds>() - 3 * 24h, -4 * 24h); // use a random age between 3 and 7 days old
vAdd.push_back(addr);
found++;
}
addrman.Add(vAdd, resolveSource);
} else {
// If the seed does not support a subdomain with our desired service bits,
// we make an ADDR_FETCH connection to the DNS resolved peer address for the
// base dns seed domain in chainparams
AddAddrFetch(seed);
}
}
--seeds_right_now;
}
--seeds_right_now;
LogPrintf("%d addresses found from DNS seeds\n", found);
} else {
LogPrintf("Skipping DNS seeds. Enough peers have been found\n");
}
LogPrintf("%d addresses found from DNS seeds\n", found);
}

void CConnman::DumpAddresses()
Expand Down Expand Up @@ -2343,6 +2365,19 @@ void CConnman::StartExtraBlockRelayPeers()
m_start_extra_block_relay_peers = true;
}

// Return the number of outbound connections that are full relay (not blocks only)
int CConnman::GetFullOutboundConnCount() const
{
int nRelevant = 0;
{
LOCK(m_nodes_mutex);
for (const CNode* pnode : m_nodes) {
if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn()) ++nRelevant;
}
}
return nRelevant;
}

// Return the number of peers we have over our outbound connection limit
// Exclude peers that are marked for disconnect, or are going to be
// disconnected soon (eg ADDR_FETCH and FEELER)
Expand Down
2 changes: 2 additions & 0 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,8 @@ class CConnman

void StartExtraBlockRelayPeers();

// Count the number of full-relay peer we have.
int GetFullOutboundConnCount() const;
// Return the number of outbound peers we have in excess of our target (eg,
// if we previously called SetTryNewOutboundPeer(true), and have since set
// to false, we may have extra peers that we wish to disconnect). This may
Expand Down