Skip to content

Commit

Permalink
net: save the network type explicitly in CNetAddr
Browse files Browse the repository at this point in the history
Before this change, we would analyze the contents of `CNetAddr::ip[16]`
in order to tell which type is an address. Change this by introducing a
new member `CNetAddr::m_net` that explicitly tells the type of the
address.

This is necessary because in BIP155 we will not be able to tell the
address type by just looking at its raw representation (e.g. both TORv3
and I2P are "seemingly random" 32 bytes).

As a side effect of this change we no longer need to store IPv4
addresses encoded as IPv6 addresses - we can store them in proper 4
bytes (will be done in a separate commit). Also the code gets
somewhat simplified - instead of
`memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0` we can use
`m_net == NET_IPV4`.

Co-authored-by: Carl Dong <contact@carldong.me>
  • Loading branch information
vasild and dongcarl committed Jul 20, 2020
1 parent 100c64a commit 662bb25
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 40 deletions.
74 changes: 38 additions & 36 deletions src/netaddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87,
*
* @note This address is considered invalid by CNetAddr::IsValid()
*/
CNetAddr::CNetAddr()
CNetAddr::CNetAddr() : m_net(NET_IPV6)
{
memset(ip, 0, sizeof(ip));
}

void CNetAddr::SetIP(const CNetAddr& ipIn)
{
m_net = ipIn.m_net;
memcpy(ip, ipIn.ip, sizeof(ip));
}

Expand All @@ -36,10 +37,20 @@ void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
switch(network)
{
case NET_IPV4:
m_net = NET_IPV4;
memcpy(ip, pchIPv4, 12);
memcpy(ip+12, ip_in, 4);
break;
case NET_IPV6:
if (memcmp(ip_in, pchIPv4, sizeof(pchIPv4)) == 0) {
m_net = NET_IPV4;
} else if (memcmp(ip_in, pchOnionCat, sizeof(pchOnionCat)) == 0) {
m_net = NET_ONION;
} else if (memcmp(ip_in, g_internal_prefix, sizeof(g_internal_prefix)) == 0) {
m_net = NET_INTERNAL;
} else {
m_net = NET_IPV6;
}
memcpy(ip, ip_in, 16);
break;
default:
Expand All @@ -66,6 +77,7 @@ bool CNetAddr::SetInternal(const std::string &name)
if (name.empty()) {
return false;
}
m_net = NET_INTERNAL;
unsigned char hash[32] = {};
CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash);
memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix));
Expand All @@ -89,6 +101,7 @@ bool CNetAddr::SetSpecial(const std::string &strName)
std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
if (vchAddr.size() != 16-sizeof(pchOnionCat))
return false;
m_net = NET_ONION;
memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
ip[i + sizeof(pchOnionCat)] = vchAddr[i];
Expand Down Expand Up @@ -123,15 +136,9 @@ bool CNetAddr::IsBindAny() const
return true;
}

bool CNetAddr::IsIPv4() const
{
return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
}
bool CNetAddr::IsIPv4() const { return m_net == NET_IPV4; }

bool CNetAddr::IsIPv6() const
{
return (!IsIPv4() && !IsTor() && !IsInternal());
}
bool CNetAddr::IsIPv6() const { return m_net == NET_IPV6; }

bool CNetAddr::IsRFC1918() const
{
Expand Down Expand Up @@ -165,50 +172,54 @@ bool CNetAddr::IsRFC5737() const

bool CNetAddr::IsRFC3849() const
{
return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8;
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
GetByte(13) == 0x0D && GetByte(12) == 0xB8;
}

bool CNetAddr::IsRFC3964() const
{
return (GetByte(15) == 0x20 && GetByte(14) == 0x02);
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x02;
}

bool CNetAddr::IsRFC6052() const
{
static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0);
return IsIPv6() && memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0;
}

bool CNetAddr::IsRFC4380() const
{
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0);
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 &&
GetByte(12) == 0;
}

bool CNetAddr::IsRFC4862() const
{
static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0);
return IsIPv6() && memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0;
}

bool CNetAddr::IsRFC4193() const
{
return ((GetByte(15) & 0xFE) == 0xFC);
return IsIPv6() && (GetByte(15) & 0xFE) == 0xFC;
}

bool CNetAddr::IsRFC6145() const
{
static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0);
return IsIPv6() && memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0;
}

bool CNetAddr::IsRFC4843() const
{
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10;
}

bool CNetAddr::IsRFC7343() const
{
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20);
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20;
}

bool CNetAddr::IsHeNet() const
Expand All @@ -222,10 +233,7 @@ bool CNetAddr::IsHeNet() const
*
* @see CNetAddr::SetSpecial(const std::string &)
*/
bool CNetAddr::IsTor() const
{
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
}
bool CNetAddr::IsTor() const { return m_net == NET_ONION; }

bool CNetAddr::IsLocal() const
{
Expand All @@ -235,7 +243,7 @@ bool CNetAddr::IsLocal() const

// IPv6 loopback (::1/128)
static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
if (memcmp(ip, pchLocal, 16) == 0)
if (IsIPv6() && memcmp(ip, pchLocal, 16) == 0)
return true;

return false;
Expand All @@ -259,12 +267,12 @@ bool CNetAddr::IsValid() const
// header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
// so if the first length field is garbled, it reads the second batch
// of addr misaligned by 3 bytes.
if (memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
if (IsIPv6() && memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
return false;

// unspecified IPv6 address (::/128)
unsigned char ipNone6[16] = {};
if (memcmp(ip, ipNone6, 16) == 0)
if (IsIPv6() && memcmp(ip, ipNone6, 16) == 0)
return false;

// documentation IPv6 address
Expand Down Expand Up @@ -311,7 +319,7 @@ bool CNetAddr::IsRoutable() const
*/
bool CNetAddr::IsInternal() const
{
return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0;
return m_net == NET_INTERNAL;
}

enum Network CNetAddr::GetNetwork() const
Expand All @@ -322,13 +330,7 @@ enum Network CNetAddr::GetNetwork() const
if (!IsRoutable())
return NET_UNROUTABLE;

if (IsIPv4())
return NET_IPV4;

if (IsTor())
return NET_ONION;

return NET_IPV6;
return m_net;
}

std::string CNetAddr::ToStringIP() const
Expand Down Expand Up @@ -362,12 +364,12 @@ std::string CNetAddr::ToString() const

bool operator==(const CNetAddr& a, const CNetAddr& b)
{
return (memcmp(a.ip, b.ip, 16) == 0);
return a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) == 0;
}

bool operator<(const CNetAddr& a, const CNetAddr& b)
{
return (memcmp(a.ip, b.ip, 16) < 0);
return a.m_net < b.m_net || (a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) < 0);
}

/**
Expand Down Expand Up @@ -813,7 +815,7 @@ CSubNet::CSubNet(const CNetAddr &addr):
*/
bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid())
if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
return false;
for(int x=0; x<16; ++x)
if ((addr.ip[x] & netmask[x]) != network.ip[x])
Expand Down
31 changes: 29 additions & 2 deletions src/netaddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,17 @@ enum Network
NET_MAX,
};

/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */
/**
* Network address.
*/
class CNetAddr
{
protected:
/**
* Network to which this address belongs.
*/
Network m_net;

unsigned char ip[16]; // in network byte order
uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses

Expand Down Expand Up @@ -122,7 +129,27 @@ class CNetAddr
friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
friend bool operator<(const CNetAddr& a, const CNetAddr& b);

SERIALIZE_METHODS(CNetAddr, obj) { READWRITE(obj.ip); }
/**
* Serialize to a stream.
*/
template <typename Stream>
void Serialize(Stream& s) const
{
s << ip;
}

/**
* Unserialize from a stream.
*/
template <typename Stream>
void Unserialize(Stream& s)
{
unsigned char ip_temp[sizeof(ip)];
s >> ip_temp;
// Use SetRaw() so that m_net is set correctly. For example
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
SetRaw(NET_IPV6, ip_temp);
}

friend class CSubNet;
};
Expand Down
13 changes: 11 additions & 2 deletions src/test/netbase_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ BOOST_AUTO_TEST_CASE(onioncat_test)

}

BOOST_AUTO_TEST_CASE(embedded_test)
{
CNetAddr addr1(ResolveIP("1.2.3.4"));
CNetAddr addr2(ResolveIP("::FFFF:0102:0304"));
BOOST_CHECK(addr2.IsIPv4());
BOOST_CHECK_EQUAL(addr1.ToString(), addr2.ToString());
}

BOOST_AUTO_TEST_CASE(subnet_test)
{

Expand All @@ -158,12 +166,13 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(ResolveSubNet("1.2.2.1/24").Match(ResolveIP("1.2.2.4")));
BOOST_CHECK(ResolveSubNet("1.2.2.110/31").Match(ResolveIP("1.2.2.111")));
BOOST_CHECK(ResolveSubNet("1.2.2.20/26").Match(ResolveIP("1.2.2.63")));
// All-Matching IPv6 Matches arbitrary IPv4 and IPv6
// All-Matching IPv6 Matches arbitrary IPv6
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
// But not `::` or `0.0.0.0` because they are considered invalid addresses
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("::")));
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("0.0.0.0")));
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
// Addresses from one network (IPv4) don't belong to subnets of another network (IPv6)
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
// All-Matching IPv4 does not Match IPv6
BOOST_CHECK(!ResolveSubNet("0.0.0.0/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
// Invalid subnets Match nothing (not even invalid addresses)
Expand Down

0 comments on commit 662bb25

Please sign in to comment.