Merge #10446: net: avoid extra dns query per seed

c1be285 chainparams: make supported service bits option explicit (Cory Fields)
d5c7c1c net: use an internal address for fixed seeds (Cory Fields)
6cdc488 net: switch to dummy internal ip for dns seed source (Cory Fields)
6d0bd5b net: do not allow resolving to an internal address (Cory Fields)
7f31762 net: add an internal subnet for representing unresolved hostnames (Cory Fields)

Tree-SHA512: 9bf1042bef546ac3ef0e0d3a9a5555eb21628ff2674a0cf8c6367194b22bfdab477adf452c0e7c56f44e0fb37debc5e14bdb623452e076fb9c492c7702601d7a
This commit is contained in:
Wladimir J. van der Laan 2017-06-24 12:16:41 +02:00 committed by Pasta
parent f68c7bc5c8
commit 64ef42c797
No known key found for this signature in database
GPG Key ID: D362C9F7142766AE
8 changed files with 91 additions and 33 deletions

View File

@ -313,10 +313,9 @@ public:
assert(consensus.hashGenesisBlock == uint256S("0x00000ffd590b1485b3caadc19b22e6379c733355108f107a430458cdf3407ab6")); assert(consensus.hashGenesisBlock == uint256S("0x00000ffd590b1485b3caadc19b22e6379c733355108f107a430458cdf3407ab6"));
assert(genesis.hashMerkleRoot == uint256S("0xe0028eb9648db56b1ac77cf090b99048a8007e2bb64b68f092c03c7f56a662c7")); assert(genesis.hashMerkleRoot == uint256S("0xe0028eb9648db56b1ac77cf090b99048a8007e2bb64b68f092c03c7f56a662c7"));
vSeeds.emplace_back("dash.org", "dnsseed.dash.org");
vSeeds.push_back(CDNSSeedData("dash.org", "dnsseed.dash.org")); vSeeds.emplace_back("dashdot.io", "dnsseed.dashdot.io");
vSeeds.push_back(CDNSSeedData("dashdot.io", "dnsseed.dashdot.io")); vSeeds.emplace_back("masternode.io", "dnsseed.masternode.io");
vSeeds.push_back(CDNSSeedData("masternode.io", "dnsseed.masternode.io"));
// Dash addresses start with 'X' // Dash addresses start with 'X'
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,76); base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,76);
@ -493,8 +492,8 @@ public:
vSeeds.clear(); vSeeds.clear();
// nodes with support for servicebits filtering should be at the top // nodes with support for servicebits filtering should be at the top
vSeeds.push_back(CDNSSeedData("dashdot.io", "testnet-seed.dashdot.io")); vSeeds.emplace_back("dashdot.io", "testnet-seed.dashdot.io");
vSeeds.push_back(CDNSSeedData("masternode.io", "test.dnsseed.masternode.io")); vSeeds.emplace_back("masternode.io", "test.dnsseed.masternode.io");
// Testnet Dash addresses start with 'y' // Testnet Dash addresses start with 'y'
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,140); base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,140);

View File

@ -15,9 +15,9 @@
#include <vector> #include <vector>
struct CDNSSeedData { struct CDNSSeedData {
std::string name, host; std::string host;
bool supportsServiceBitsFiltering; bool supportsServiceBitsFiltering;
CDNSSeedData(const std::string &strName, const std::string &strHost, bool supportsServiceBitsFilteringIn = false) : name(strName), host(strHost), supportsServiceBitsFiltering(supportsServiceBitsFilteringIn) {} CDNSSeedData(const std::string &strHost, bool supportsServiceBitsFilteringIn) : host(strHost), supportsServiceBitsFiltering(supportsServiceBitsFilteringIn) {}
}; };
struct SeedSpec6 { struct SeedSpec6 {

View File

@ -250,7 +250,7 @@ bool RemoveLocal(const CService& addr)
/** Make a particular network entirely off-limits (no automatic connects to it) */ /** Make a particular network entirely off-limits (no automatic connects to it) */
void SetLimited(enum Network net, bool fLimited) void SetLimited(enum Network net, bool fLimited)
{ {
if (net == NET_UNROUTABLE) if (net == NET_UNROUTABLE || net == NET_INTERNAL)
return; return;
LOCK(cs_mapLocalHost); LOCK(cs_mapLocalHost);
vfLimited[net] = fLimited; vfLimited[net] = fLimited;
@ -1713,7 +1713,12 @@ void CConnman::ThreadDNSAddressSeed()
std::vector<CNetAddr> vIPs; std::vector<CNetAddr> vIPs;
std::vector<CAddress> vAdd; std::vector<CAddress> vAdd;
ServiceFlags requiredServiceBits = nRelevantServices; ServiceFlags requiredServiceBits = nRelevantServices;
if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true)) std::string host = GetDNSHost(seed, &requiredServiceBits);
CNetAddr resolveSource;
if (!resolveSource.SetInternal(host)) {
continue;
}
if (LookupHost(host.c_str(), vIPs, 0, true))
{ {
for (const CNetAddr& ip : vIPs) for (const CNetAddr& ip : vIPs)
{ {
@ -1723,18 +1728,7 @@ void CConnman::ThreadDNSAddressSeed()
vAdd.push_back(addr); vAdd.push_back(addr);
found++; found++;
} }
} addrman.Add(vAdd, resolveSource);
if (interruptNet) {
return;
}
// TODO: The seed name resolve may fail, yielding an IP of [::], which results in
// addrman assigning the same source to results from different seeds.
// This should switch to a hard-coded stable dummy IP for each seed name, so that the
// resolve is not required at all.
if (!vIPs.empty()) {
CService seedSource;
Lookup(seed.name.c_str(), seedSource, 0, true);
addrman.Add(vAdd, seedSource);
} }
} }
} }
@ -1833,7 +1827,7 @@ void CConnman::ThreadOpenConnections()
if (!done) { if (!done) {
LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n");
CNetAddr local; CNetAddr local;
LookupHost("127.0.0.1", local, false); local.SetInternal("fixedseeds");
addrman.Add(convertSeed6(Params().FixedSeeds()), local); addrman.Add(convertSeed6(Params().FixedSeeds()), local);
done = true; done = true;
} }

View File

@ -16,6 +16,9 @@
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
// 0xFD + sha256("bitcoin")[0:5]
static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 };
bool fAllowPrivateNet = DEFAULT_ALLOWPRIVATENET; bool fAllowPrivateNet = DEFAULT_ALLOWPRIVATENET;
void CNetAddr::Init() void CNetAddr::Init()
@ -45,6 +48,18 @@ void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
} }
} }
bool CNetAddr::SetInternal(const std::string &name)
{
if (name.empty()) {
return false;
}
unsigned char hash[32] = {};
CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash);
memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix));
memcpy(ip + sizeof(g_internal_prefix), hash, sizeof(ip) - sizeof(g_internal_prefix));
return true;
}
bool CNetAddr::SetSpecial(const std::string &strName) bool CNetAddr::SetSpecial(const std::string &strName)
{ {
if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") { if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
@ -87,7 +102,7 @@ bool CNetAddr::IsIPv4() const
bool CNetAddr::IsIPv6() const bool CNetAddr::IsIPv6() const
{ {
return (!IsIPv4() && !IsTor()); return (!IsIPv4() && !IsTor() && !IsInternal());
} }
bool CNetAddr::IsRFC1918() const bool CNetAddr::IsRFC1918() const
@ -202,6 +217,9 @@ bool CNetAddr::IsValid() const
if (IsRFC3849()) if (IsRFC3849())
return false; return false;
if (IsInternal())
return false;
if (IsIPv4()) if (IsIPv4())
{ {
// INADDR_NONE // INADDR_NONE
@ -224,11 +242,19 @@ bool CNetAddr::IsRoutable() const
return false; return false;
if (!fAllowPrivateNet && IsRFC1918()) if (!fAllowPrivateNet && IsRFC1918())
return false; return false;
return !(IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal()); return !(IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal() || IsInternal());
}
bool CNetAddr::IsInternal() const
{
return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0;
} }
enum Network CNetAddr::GetNetwork() const enum Network CNetAddr::GetNetwork() const
{ {
if (IsInternal())
return NET_INTERNAL;
if (!IsRoutable()) if (!IsRoutable())
return NET_UNROUTABLE; return NET_UNROUTABLE;
@ -245,6 +271,8 @@ std::string CNetAddr::ToStringIP(bool fUseGetnameinfo) const
{ {
if (IsTor()) if (IsTor())
return EncodeBase32(&ip[6], 10) + ".onion"; return EncodeBase32(&ip[6], 10) + ".onion";
if (IsInternal())
return EncodeBase32(ip + sizeof(g_internal_prefix), sizeof(ip) - sizeof(g_internal_prefix)) + ".internal";
if (fUseGetnameinfo) if (fUseGetnameinfo)
{ {
CService serv(*this, 0); CService serv(*this, 0);
@ -315,9 +343,15 @@ std::vector<unsigned char> CNetAddr::GetGroup() const
nClass = 255; nClass = 255;
nBits = 0; nBits = 0;
} }
// all internal-usage addresses get their own group
// all unroutable addresses belong to the same group if (IsInternal())
if (!IsRoutable()) {
nClass = NET_INTERNAL;
nStartByte = sizeof(g_internal_prefix);
nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8;
}
// all other unroutable addresses belong to the same group
else if (!IsRoutable())
{ {
nClass = NET_UNROUTABLE; nClass = NET_UNROUTABLE;
nBits = 0; nBits = 0;
@ -403,7 +437,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
REACH_PRIVATE REACH_PRIVATE
}; };
if (!IsRoutable()) if (!IsRoutable() || IsInternal())
return REACH_UNREACHABLE; return REACH_UNREACHABLE;
int ourNet = GetExtNetwork(this); int ourNet = GetExtNetwork(this);
@ -562,7 +596,7 @@ std::string CService::ToStringPort() const
std::string CService::ToStringIPPort(bool fUseGetnameinfo) const std::string CService::ToStringIPPort(bool fUseGetnameinfo) const
{ {
if (IsIPv4() || IsTor()) { if (IsIPv4() || IsTor() || IsInternal()) {
return ToStringIP(fUseGetnameinfo) + ":" + ToStringPort(); return ToStringIP(fUseGetnameinfo) + ":" + ToStringPort();
} else { } else {
return "[" + ToStringIP(fUseGetnameinfo) + "]:" + ToStringPort(); return "[" + ToStringIP(fUseGetnameinfo) + "]:" + ToStringPort();

View File

@ -24,6 +24,7 @@ enum Network
NET_IPV4, NET_IPV4,
NET_IPV6, NET_IPV6,
NET_TOR, NET_TOR,
NET_INTERNAL,
NET_MAX, NET_MAX,
}; };
@ -47,6 +48,12 @@ class CNetAddr
*/ */
void SetRaw(Network network, const uint8_t *data); void SetRaw(Network network, const uint8_t *data);
/**
* Transform an arbitrary string into a non-routable ipv6 address.
* Useful for mapping resolved addresses back to their source.
*/
bool SetInternal(const std::string& name);
bool SetSpecial(const std::string &strName); // for Tor addresses bool SetSpecial(const std::string &strName); // for Tor addresses
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) 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 IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
@ -66,6 +73,7 @@ class CNetAddr
bool IsTor() const; bool IsTor() const;
bool IsLocal() const; bool IsLocal() const;
bool IsRoutable() const; bool IsRoutable() const;
bool IsInternal() const;
bool IsValid() const; bool IsValid() const;
enum Network GetNetwork() const; enum Network GetNetwork() const;
std::string ToString() const; std::string ToString() const;

View File

@ -108,17 +108,22 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
struct addrinfo *aiTrav = aiRes; struct addrinfo *aiTrav = aiRes;
while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
{ {
CNetAddr resolved;
if (aiTrav->ai_family == AF_INET) if (aiTrav->ai_family == AF_INET)
{ {
assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in));
vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr)); resolved = CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr);
} }
if (aiTrav->ai_family == AF_INET6) if (aiTrav->ai_family == AF_INET6)
{ {
assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6));
struct sockaddr_in6* s6 = (struct sockaddr_in6*) aiTrav->ai_addr; struct sockaddr_in6* s6 = (struct sockaddr_in6*) aiTrav->ai_addr;
vIP.push_back(CNetAddr(s6->sin6_addr, s6->sin6_scope_id)); resolved = CNetAddr(s6->sin6_addr, s6->sin6_scope_id);
}
/* Never allow resolving to an internal address. Consider any such result invalid */
if (!resolved.IsInternal()) {
vIP.push_back(resolved);
} }
aiTrav = aiTrav->ai_next; aiTrav = aiTrav->ai_next;

View File

@ -398,7 +398,7 @@ static UniValue GetNetworksInfo()
for(int n=0; n<NET_MAX; ++n) for(int n=0; n<NET_MAX; ++n)
{ {
enum Network network = static_cast<enum Network>(n); enum Network network = static_cast<enum Network>(n);
if(network == NET_UNROUTABLE) if(network == NET_UNROUTABLE || network == NET_INTERNAL)
continue; continue;
proxyType proxy; proxyType proxy;
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);

View File

@ -26,6 +26,13 @@ static CSubNet ResolveSubNet(const char* subnet)
return ret; return ret;
} }
static CNetAddr CreateInternal(const char* host)
{
CNetAddr addr;
addr.SetInternal(host);
return addr;
}
BOOST_AUTO_TEST_CASE(netbase_networks) BOOST_AUTO_TEST_CASE(netbase_networks)
{ {
BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE); BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE);
@ -33,6 +40,7 @@ BOOST_AUTO_TEST_CASE(netbase_networks)
BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4); BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4);
BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6); BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6);
BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR); BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL);
} }
@ -59,6 +67,8 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable()); BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable());
BOOST_CHECK(ResolveIP("2001::1").IsRoutable()); BOOST_CHECK(ResolveIP("2001::1").IsRoutable());
BOOST_CHECK(ResolveIP("127.0.0.1").IsValid()); BOOST_CHECK(ResolveIP("127.0.0.1").IsValid());
BOOST_CHECK(CreateInternal("FD6B:88C0:8724:edb1:8e4:3588:e546:35ca").IsInternal());
BOOST_CHECK(CreateInternal("bar.com").IsInternal());
} }
@ -104,6 +114,11 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
BOOST_CHECK(TestParse("[::]:9999", "[::]:9999")); BOOST_CHECK(TestParse("[::]:9999", "[::]:9999"));
BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535")); BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535"));
BOOST_CHECK(TestParse(":::", "[::]:0")); BOOST_CHECK(TestParse(":::", "[::]:0"));
// verify that an internal address fails to resolve
BOOST_CHECK(TestParse("[fd6b:88c0:8724:1:2:3:4:5]", "[::]:0"));
// and that a one-off resolves correctly
BOOST_CHECK(TestParse("[fd6c:88c0:8724:1:2:3:4:5]", "[fd6c:88c0:8724:1:2:3:4:5]:65535"));
} }
BOOST_AUTO_TEST_CASE(onioncat_test) BOOST_AUTO_TEST_CASE(onioncat_test)
@ -282,6 +297,9 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup)
BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net
BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6 BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6
// baz.net sha256 hash: 12929400eb4607c4ac075f087167e75286b179c693eb059a01774b864e8fe505
std::vector<unsigned char> internal_group = {NET_INTERNAL, 0x12, 0x92, 0x94, 0x00, 0xeb, 0x46, 0x07, 0xc4, 0xac, 0x07};
BOOST_CHECK(CreateInternal("baz.net").GetGroup() == internal_group);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()