net: add an internal subnet for representing unresolved hostnames
We currently do two resolves for dns seeds: one for the results, and one to serve in addrman as the source for those addresses. There's no requirement that the source hostname resolves to the stored identifier, only that the mapping is unique. So rather than incurring the second lookup, combine a private subnet with a hash of the hostname. The resulting v6 ip is guaranteed not to be publicy routable, and has only a negligible chance of colliding with a user's internal network (which would be of no consequence anyway).
This commit is contained in:
parent
fbf5d3ba15
commit
7f31762cb6
5 changed files with 64 additions and 9 deletions
|
@ -240,7 +240,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;
|
||||||
|
|
|
@ -15,6 +15,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 };
|
||||||
|
|
||||||
void CNetAddr::Init()
|
void CNetAddr::Init()
|
||||||
{
|
{
|
||||||
memset(ip, 0, sizeof(ip));
|
memset(ip, 0, sizeof(ip));
|
||||||
|
@ -42,6 +45,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") {
|
||||||
|
@ -84,7 +99,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
|
||||||
|
@ -199,6 +214,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
|
||||||
|
@ -217,11 +235,19 @@ bool CNetAddr::IsValid() const
|
||||||
|
|
||||||
bool CNetAddr::IsRoutable() const
|
bool CNetAddr::IsRoutable() const
|
||||||
{
|
{
|
||||||
return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal());
|
return IsValid() && !(IsRFC1918() || 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;
|
||||||
|
|
||||||
|
@ -238,6 +264,8 @@ std::string CNetAddr::ToStringIP() 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";
|
||||||
CService serv(*this, 0);
|
CService serv(*this, 0);
|
||||||
struct sockaddr_storage sockaddr;
|
struct sockaddr_storage sockaddr;
|
||||||
socklen_t socklen = sizeof(sockaddr);
|
socklen_t socklen = sizeof(sockaddr);
|
||||||
|
@ -305,9 +333,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;
|
||||||
|
@ -393,7 +427,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);
|
||||||
|
@ -552,7 +586,7 @@ std::string CService::ToStringPort() const
|
||||||
|
|
||||||
std::string CService::ToStringIPPort() const
|
std::string CService::ToStringIPPort() const
|
||||||
{
|
{
|
||||||
if (IsIPv4() || IsTor()) {
|
if (IsIPv4() || IsTor() || IsInternal()) {
|
||||||
return ToStringIP() + ":" + ToStringPort();
|
return ToStringIP() + ":" + ToStringPort();
|
||||||
} else {
|
} else {
|
||||||
return "[" + ToStringIP() + "]:" + ToStringPort();
|
return "[" + ToStringIP() + "]:" + ToStringPort();
|
||||||
|
|
|
@ -22,6 +22,7 @@ enum Network
|
||||||
NET_IPV4,
|
NET_IPV4,
|
||||||
NET_IPV6,
|
NET_IPV6,
|
||||||
NET_TOR,
|
NET_TOR,
|
||||||
|
NET_INTERNAL,
|
||||||
|
|
||||||
NET_MAX,
|
NET_MAX,
|
||||||
};
|
};
|
||||||
|
@ -45,6 +46,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)
|
||||||
|
@ -64,6 +71,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;
|
||||||
|
|
|
@ -397,7 +397,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);
|
||||||
|
|
|
@ -25,6 +25,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);
|
||||||
|
@ -32,6 +39,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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +66,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());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,6 +291,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()
|
||||||
|
|
Loading…
Reference in a new issue