diff --git a/src/netaddress.cpp b/src/netaddress.cpp index c44bf481f..01536c5ec 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -1,945 +1,956 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include static const uint8_t pchIPv4[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; static const uint8_t pchOnionCat[] = {0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43}; // 0xFD + sha256("bitcoin")[0:5] static const uint8_t g_internal_prefix[] = {0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24}; /** * Construct an unspecified IPv6 network address (::/128). * * @note This address is considered invalid by CNetAddr::IsValid() */ CNetAddr::CNetAddr() { memset(ip, 0, sizeof(ip)); } void CNetAddr::SetIP(const CNetAddr &ipIn) { + m_net = ipIn.m_net; memcpy(ip, ipIn.ip, sizeof(ip)); } +void CNetAddr::SetLegacyIPv6(const uint8_t ipv6[16]) { + if (memcmp(ipv6, pchIPv4, sizeof(pchIPv4)) == 0) { + m_net = NET_IPV4; + } else if (memcmp(ipv6, pchOnionCat, sizeof(pchOnionCat)) == 0) { + m_net = NET_ONION; + } else if (memcmp(ipv6, g_internal_prefix, sizeof(g_internal_prefix)) == + 0) { + m_net = NET_INTERNAL; + } else { + m_net = NET_IPV6; + } + memcpy(ip, ipv6, 16); +} + 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: - memcpy(ip, ip_in, 16); + SetLegacyIPv6(ip_in); break; default: assert(!"invalid network"); } } /** * Try to make this a dummy address that maps the specified name into IPv6 like * so: (0xFD + %sha256("bitcoin")[0:5]) + %sha256(name)[0:10]. Such dummy * addresses have a prefix of fd6b:88c0:8724::/48 and are guaranteed to not be * publicly routable as it falls under RFC4193's fc00::/7 subnet allocated to * unique-local addresses. * * CAddrMan uses these fake addresses to keep track of which DNS seeds were * used. * * @returns Whether or not the operation was successful. * * @see CNetAddr::IsInternal(), CNetAddr::IsRFC4193() */ bool CNetAddr::SetInternal(const std::string &name) { if (name.empty()) { return false; } + m_net = NET_INTERNAL; uint8_t hash[32] = {}; CSHA256().Write((const uint8_t *)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; } /** * Try to make this a dummy address that maps the specified onion address into * IPv6 using OnionCat's range and encoding. Such dummy addresses have a prefix * of fd87:d87e:eb43::/48 and are guaranteed to not be publicly routable as they * fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses. * * @returns Whether or not the operation was successful. * * @see CNetAddr::IsTor(), CNetAddr::IsRFC4193() */ bool CNetAddr::SetSpecial(const std::string &strName) { if (strName.size() > 6 && strName.substr(strName.size() - 6, 6) == ".onion") { std::vector 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]; } return true; } return false; } CNetAddr::CNetAddr(const struct in_addr &ipv4Addr) { SetRaw(NET_IPV4, (const uint8_t *)&ipv4Addr); } CNetAddr::CNetAddr(const struct in6_addr &ipv6Addr, const uint32_t scope) { SetRaw(NET_IPV6, (const uint8_t *)&ipv6Addr); scopeId = scope; } unsigned int CNetAddr::GetByte(int n) const { return ip[15 - n]; } bool CNetAddr::IsBindAny() const { const int cmplen = IsIPv4() ? 4 : 16; for (int i = 0; i < cmplen; ++i) { if (GetByte(i)) { return false; } } return true; } bool CNetAddr::IsIPv4() const { - return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); + return m_net == NET_IPV4; } bool CNetAddr::IsIPv6() const { - return !IsIPv4() && !IsTor() && !IsInternal(); + return m_net == NET_IPV6; } bool CNetAddr::IsRFC1918() const { return IsIPv4() && (GetByte(3) == 10 || (GetByte(3) == 192 && GetByte(2) == 168) || (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31))); } bool CNetAddr::IsRFC2544() const { return IsIPv4() && GetByte(3) == 198 && (GetByte(2) == 18 || GetByte(2) == 19); } bool CNetAddr::IsRFC3927() const { return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); } bool CNetAddr::IsRFC6598() const { return IsIPv4() && GetByte(3) == 100 && GetByte(2) >= 64 && GetByte(2) <= 127; } bool CNetAddr::IsRFC5737() const { return IsIPv4() && ((GetByte(3) == 192 && GetByte(2) == 0 && GetByte(1) == 2) || (GetByte(3) == 198 && GetByte(2) == 51 && GetByte(1) == 100) || (GetByte(3) == 203 && GetByte(2) == 0 && GetByte(1) == 113)); } 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 uint8_t 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 uint8_t 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 uint8_t 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 { return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70); } /** * @returns Whether or not this is a dummy address that maps an onion address * into IPv6. * * @see CNetAddr::SetSpecial(const std::string &) */ bool CNetAddr::IsTor() const { - return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); + return m_net == NET_ONION; } bool CNetAddr::IsLocal() const { // IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8) if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) { return true; } // IPv6 loopback (::1/128) static const uint8_t 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; } /** * @returns Whether or not this network address is a valid address that @a could * be used to refer to an actual host. * * @note A valid address may or may not be publicly routable on the global * internet. As in, the set of valid addresses is a superset of the set of * publicly routable addresses. * * @see CNetAddr::IsRoutable() */ bool CNetAddr::IsValid() const { // Cleanup 3-byte shifted addresses caused by garbage in size field of addr // messages from versions before 0.2.9 checksum. // Two consecutive addr messages look like this: // 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) uint8_t ipNone6[16] = {}; - if (memcmp(ip, ipNone6, 16) == 0) { + if (IsIPv6() && memcmp(ip, ipNone6, 16) == 0) { return false; } // documentation IPv6 address if (IsRFC3849()) { return false; } if (IsInternal()) { return false; } if (IsIPv4()) { // INADDR_NONE uint32_t ipNone = INADDR_NONE; if (memcmp(ip + 12, &ipNone, 4) == 0) { return false; } // 0 ipNone = 0; if (memcmp(ip + 12, &ipNone, 4) == 0) { return false; } } return true; } /** * @returns Whether or not this network address is publicly routable on the * global internet. * * @note A routable address is always valid. As in, the set of routable * addresses is a subset of the set of valid addresses. * * @see CNetAddr::IsValid() */ bool CNetAddr::IsRoutable() const { return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsRFC7343() || IsLocal() || IsInternal()); } /** * @returns Whether or not this is a dummy address that maps a name into IPv6. * * @see CNetAddr::SetInternal(const std::string &) */ 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 { if (IsInternal()) { return NET_INTERNAL; } 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 { if (IsTor()) { 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); struct sockaddr_storage sockaddr; socklen_t socklen = sizeof(sockaddr); if (serv.GetSockAddr((struct sockaddr *)&sockaddr, &socklen)) { char name[1025] = ""; if (!getnameinfo((const struct sockaddr *)&sockaddr, socklen, name, sizeof(name), nullptr, 0, NI_NUMERICHOST)) { return std::string(name); } } if (IsIPv4()) { return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); } return strprintf("%x:%x:%x:%x:%x:%x:%x:%x", GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12), GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8), GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4), GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0)); } std::string CNetAddr::ToString() const { return ToStringIP(); } 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); } /** * Try to get our IPv4 address. * * @param[out] pipv4Addr The in_addr struct to which to copy. * * @returns Whether or not the operation was successful, in particular, whether * or not our address was an IPv4 address. * * @see CNetAddr::IsIPv4() */ bool CNetAddr::GetInAddr(struct in_addr *pipv4Addr) const { if (!IsIPv4()) { return false; } memcpy(pipv4Addr, ip + 12, 4); return true; } /** * Try to get our IPv6 address. * * @param[out] pipv6Addr The in6_addr struct to which to copy. * * @returns Whether or not the operation was successful, in particular, whether * or not our address was an IPv6 address. * * @see CNetAddr::IsIPv6() */ bool CNetAddr::GetIn6Addr(struct in6_addr *pipv6Addr) const { if (!IsIPv6()) { return false; } memcpy(pipv6Addr, ip, 16); return true; } bool CNetAddr::HasLinkedIPv4() const { return IsRoutable() && (IsIPv4() || IsRFC6145() || IsRFC6052() || IsRFC3964() || IsRFC4380()); } uint32_t CNetAddr::GetLinkedIPv4() const { if (IsIPv4() || IsRFC6145() || IsRFC6052()) { // IPv4, mapped IPv4, SIIT translated IPv4: the IPv4 address is the last // 4 bytes of the address return ReadBE32(ip + 12); } else if (IsRFC3964()) { // 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6 return ReadBE32(ip + 2); } else if (IsRFC4380()) { // Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the // address, but bitflipped return ~ReadBE32(ip + 12); } assert(false); } uint32_t CNetAddr::GetNetClass() const { uint32_t net_class = NET_IPV6; if (IsLocal()) { net_class = 255; } if (IsInternal()) { net_class = NET_INTERNAL; } else if (!IsRoutable()) { net_class = NET_UNROUTABLE; } else if (HasLinkedIPv4()) { net_class = NET_IPV4; } else if (IsTor()) { net_class = NET_ONION; } return net_class; } uint32_t CNetAddr::GetMappedAS(const std::vector &asmap) const { uint32_t net_class = GetNetClass(); if (asmap.size() == 0 || (net_class != NET_IPV4 && net_class != NET_IPV6)) { return 0; // Indicates not found, safe because AS0 is reserved per // RFC7607. } std::vector ip_bits(128); if (HasLinkedIPv4()) { // For lookup, treat as if it was just an IPv4 address (pchIPv4 prefix + // IPv4 bits) for (int8_t byte_i = 0; byte_i < 12; ++byte_i) { for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) { ip_bits[byte_i * 8 + bit_i] = (pchIPv4[byte_i] >> (7 - bit_i)) & 1; } } uint32_t ipv4 = GetLinkedIPv4(); for (int i = 0; i < 32; ++i) { ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1; } } else { // Use all 128 bits of the IPv6 address otherwise for (int8_t byte_i = 0; byte_i < 16; ++byte_i) { uint8_t cur_byte = GetByte(15 - byte_i); for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) { ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1; } } } uint32_t mapped_as = Interpret(asmap, ip_bits); return mapped_as; } /** * Get the canonical identifier of our network group * * The groups are assigned in a way where it should be costly for an attacker to * obtain addresses with many different group identifiers, even if it is cheap * to obtain addresses with the same identifier. * * @note No two connections will be attempted to addresses with the same network * group. */ std::vector CNetAddr::GetGroup(const std::vector &asmap) const { std::vector vchRet; uint32_t net_class = GetNetClass(); // If non-empty asmap is supplied and the address is IPv4/IPv6, // return ASN to be used for bucketing. uint32_t asn = GetMappedAS(asmap); if (asn != 0) { // Either asmap was empty, or address has non-asmappable net // class (e.g. TOR). vchRet.push_back(NET_IPV6); // IPv4 and IPv6 with same ASN should be in // the same bucket for (int i = 0; i < 4; i++) { vchRet.push_back((asn >> (8 * i)) & 0xFF); } return vchRet; } vchRet.push_back(net_class); int nStartByte = 0; int nBits = 16; if (IsLocal()) { // all local addresses belong to the same group nBits = 0; } else if (IsInternal()) { // all internal-usage addresses get their own group nStartByte = sizeof(g_internal_prefix); nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8; } else if (!IsRoutable()) { // all other unroutable addresses belong to the same group nBits = 0; } else if (HasLinkedIPv4()) { // IPv4 addresses (and mapped IPv4 addresses) use /16 groups uint32_t ipv4 = GetLinkedIPv4(); vchRet.push_back((ipv4 >> 24) & 0xFF); vchRet.push_back((ipv4 >> 16) & 0xFF); return vchRet; } else if (IsTor()) { nStartByte = 6; nBits = 4; } else if (IsHeNet()) { // for he.net, use /36 groups nBits = 36; } else { // for the rest of the IPv6 network, use /32 groups nBits = 32; } // push our ip onto vchRet byte by byte... while (nBits >= 8) { vchRet.push_back(GetByte(15 - nStartByte)); nStartByte++; nBits -= 8; } // ...for the last byte, push nBits and for the rest of the byte push 1's if (nBits > 0) { vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1)); } return vchRet; } uint64_t CNetAddr::GetHash() const { uint256 hash = Hash(ip); uint64_t nRet; memcpy(&nRet, &hash, sizeof(nRet)); return nRet; } // private extensions to enum Network, only returned by GetExtNetwork, and only // used in GetReachabilityFrom static const int NET_UNKNOWN = NET_MAX + 0; static const int NET_TEREDO = NET_MAX + 1; static int GetExtNetwork(const CNetAddr *addr) { if (addr == nullptr) { return NET_UNKNOWN; } if (addr->IsRFC4380()) { return NET_TEREDO; } return addr->GetNetwork(); } /** Calculates a metric for how reachable (*this) is from a given partner */ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const { enum Reachability { REACH_UNREACHABLE, REACH_DEFAULT, REACH_TEREDO, REACH_IPV6_WEAK, REACH_IPV4, REACH_IPV6_STRONG, REACH_PRIVATE }; if (!IsRoutable() || IsInternal()) { return REACH_UNREACHABLE; } int ourNet = GetExtNetwork(this); int theirNet = GetExtNetwork(paddrPartner); bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); switch (theirNet) { case NET_IPV4: switch (ourNet) { default: return REACH_DEFAULT; case NET_IPV4: return REACH_IPV4; } case NET_IPV6: switch (ourNet) { default: return REACH_DEFAULT; case NET_TEREDO: return REACH_TEREDO; case NET_IPV4: return REACH_IPV4; // only prefer giving our IPv6 address if it's not tunnelled case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; } case NET_ONION: switch (ourNet) { default: return REACH_DEFAULT; // Tor users can connect to IPv4 as well case NET_IPV4: return REACH_IPV4; case NET_ONION: return REACH_PRIVATE; } case NET_TEREDO: switch (ourNet) { default: return REACH_DEFAULT; case NET_TEREDO: return REACH_TEREDO; case NET_IPV6: return REACH_IPV6_WEAK; case NET_IPV4: return REACH_IPV4; } case NET_UNKNOWN: case NET_UNROUTABLE: default: switch (ourNet) { default: return REACH_DEFAULT; case NET_TEREDO: return REACH_TEREDO; case NET_IPV6: return REACH_IPV6_WEAK; case NET_IPV4: return REACH_IPV4; // either from Tor, or don't care about our address case NET_ONION: return REACH_PRIVATE; } } } CService::CService() : port(0) {} CService::CService(const CNetAddr &cip, unsigned short portIn) : CNetAddr(cip), port(portIn) {} CService::CService(const struct in_addr &ipv4Addr, unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn) {} CService::CService(const struct in6_addr &ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn) {} CService::CService(const struct sockaddr_in &addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port)) { assert(addr.sin_family == AF_INET); } CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr, addr.sin6_scope_id), port(ntohs(addr.sin6_port)) { assert(addr.sin6_family == AF_INET6); } bool CService::SetSockAddr(const struct sockaddr *paddr) { switch (paddr->sa_family) { case AF_INET: *this = CService(*reinterpret_cast(paddr)); return true; case AF_INET6: *this = CService(*reinterpret_cast(paddr)); return true; default: return false; } } unsigned short CService::GetPort() const { return port; } bool operator==(const CService &a, const CService &b) { return static_cast(a) == static_cast(b) && a.port == b.port; } bool operator<(const CService &a, const CService &b) { return static_cast(a) < static_cast(b) || (static_cast(a) == static_cast(b) && a.port < b.port); } /** * Obtain the IPv4/6 socket address this represents. * * @param[out] paddr The obtained socket address. * @param[in,out] addrlen The size, in bytes, of the address structure pointed * to by paddr. The value that's pointed to by this * parameter might change after calling this function if * the size of the corresponding address structure * changed. * * @returns Whether or not the operation was successful. */ bool CService::GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const { if (IsIPv4()) { if (*addrlen < (socklen_t)sizeof(struct sockaddr_in)) { return false; } *addrlen = sizeof(struct sockaddr_in); struct sockaddr_in *paddrin = reinterpret_cast(paddr); memset(paddrin, 0, *addrlen); if (!GetInAddr(&paddrin->sin_addr)) { return false; } paddrin->sin_family = AF_INET; paddrin->sin_port = htons(port); return true; } if (IsIPv6()) { if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6)) { return false; } *addrlen = sizeof(struct sockaddr_in6); struct sockaddr_in6 *paddrin6 = reinterpret_cast(paddr); memset(paddrin6, 0, *addrlen); if (!GetIn6Addr(&paddrin6->sin6_addr)) { return false; } paddrin6->sin6_scope_id = scopeId; paddrin6->sin6_family = AF_INET6; paddrin6->sin6_port = htons(port); return true; } return false; } /** * @returns An identifier unique to this service's address and port number. */ std::vector CService::GetKey() const { auto key = GetAddrBytes(); // most significant byte of our port key.push_back(port / 0x100); // least significant byte of our port key.push_back(port & 0x0FF); return key; } std::string CService::ToStringPort() const { return strprintf("%u", port); } std::string CService::ToStringIPPort() const { if (IsIPv4() || IsTor() || IsInternal()) { return ToStringIP() + ":" + ToStringPort(); } else { return "[" + ToStringIP() + "]:" + ToStringPort(); } } std::string CService::ToString() const { return ToStringIPPort(); } CSubNet::CSubNet() : valid(false) { memset(netmask, 0, sizeof(netmask)); } CSubNet::CSubNet(const CNetAddr &addr, int32_t mask) { valid = true; network = addr; // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address memset(netmask, 255, sizeof(netmask)); // IPv4 addresses start at offset 12, and first 12 bytes must match, so just // offset n const int astartofs = network.IsIPv4() ? 12 : 0; // Only valid if in range of bits of address int32_t n = mask; if (n >= 0 && n <= (128 - astartofs * 8)) { n += astartofs * 8; // Clear bits [n..127] for (; n < 128; ++n) { netmask[n >> 3] &= ~(1 << (7 - (n & 7))); } } else { valid = false; } // Normalize network according to netmask for (int x = 0; x < 16; ++x) { network.ip[x] &= netmask[x]; } } CSubNet::CSubNet(const CNetAddr &addr, const CNetAddr &mask) { valid = true; network = addr; // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address memset(netmask, 255, sizeof(netmask)); // IPv4 addresses start at offset 12, and first 12 bytes must match, so just // offset n const int astartofs = network.IsIPv4() ? 12 : 0; for (int x = astartofs; x < 16; ++x) { netmask[x] = mask.ip[x]; } // Normalize network according to netmask for (int x = 0; x < 16; ++x) { network.ip[x] &= netmask[x]; } } CSubNet::CSubNet(const CNetAddr &addr) : valid(addr.IsValid()) { memset(netmask, 255, sizeof(netmask)); network = addr; } /** * @returns True if this subnet is valid, the specified address is valid, and * the specified address belongs in this subnet. */ 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]) { return false; } } return true; } /** * @returns The number of 1-bits in the prefix of the specified subnet mask. If * the specified subnet mask is not a valid one, -1. */ static inline int NetmaskBits(uint8_t x) { switch (x) { case 0x00: return 0; case 0x80: return 1; case 0xc0: return 2; case 0xe0: return 3; case 0xf0: return 4; case 0xf8: return 5; case 0xfc: return 6; case 0xfe: return 7; case 0xff: return 8; default: return -1; } } std::string CSubNet::ToString() const { /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */ int cidr = 0; bool valid_cidr = true; int n = network.IsIPv4() ? 12 : 0; for (; n < 16 && netmask[n] == 0xff; ++n) { cidr += 8; } if (n < 16) { int bits = NetmaskBits(netmask[n]); if (bits < 0) { valid_cidr = false; } else { cidr += bits; } ++n; } for (; n < 16 && valid_cidr; ++n) { if (netmask[n] != 0x00) { valid_cidr = false; } } /* Format output */ std::string strNetmask; if (valid_cidr) { strNetmask = strprintf("%u", cidr); } else { if (network.IsIPv4()) { strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]); } else { strNetmask = strprintf( "%x:%x:%x:%x:%x:%x:%x:%x", netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3], netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7], netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11], netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]); } } return network.ToString() + "/" + strNetmask; } bool CSubNet::IsValid() const { return valid; } bool operator==(const CSubNet &a, const CSubNet &b) { return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16); } bool operator<(const CSubNet &a, const CSubNet &b) { return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0)); } bool SanityCheckASMap(const std::vector &asmap) { // For IP address lookups, the input is 128 bits return SanityCheckASMap(asmap, 128); } diff --git a/src/netaddress.h b/src/netaddress.h index 8f7664c61..55680bcea 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -1,203 +1,255 @@ // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_NETADDRESS_H #define BITCOIN_NETADDRESS_H #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include +/** + * A network type. + * @note An address may belong to more than one network, for example `10.0.0.1` + * belongs to both `NET_UNROUTABLE` and `NET_IPV4`. + * Keep these sequential starting from 0 and `NET_MAX` as the last entry. + * We have loops like `for (int i = 0; i < NET_MAX; i++)` that expect to iterate + * over all enum values and also `GetExtNetwork()` "extends" this enum by + * introducing standalone constants starting from `NET_MAX`. + */ enum Network { + /// Addresses from these networks are not publicly routable on the global + /// Internet. NET_UNROUTABLE = 0, + + /// IPv4 NET_IPV4, + + /// IPv6 NET_IPV6, + + /// TORv2 NET_ONION, + + /// A set of dummy addresses that map a name to an IPv6 address. These + /// addresses belong to RFC4193's fc00::/7 subnet (unique-local addresses). + /// We use them to map a string or FQDN to an IPv6 address in CAddrMan to + /// keep track of which DNS seeds were used. NET_INTERNAL, + /// Dummy value to indicate the number of NET_* constants. 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{NET_IPV6}; + // in network byte order uint8_t ip[16]; // for scoped/link-local ipv6 addresses uint32_t scopeId{0}; public: CNetAddr(); explicit CNetAddr(const struct in_addr &ipv4Addr); void SetIP(const CNetAddr &ip); + /** + * Set from a legacy IPv6 address. + * Legacy IPv6 address may be a normal IPv6 address, or another address + * (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy + * `addr` encoding. + */ + void SetLegacyIPv6(const uint8_t ipv6[16]); + /** * Set raw IPv4 or IPv6 address (in network byte order) * @note Only NET_IPV4 and NET_IPV6 are allowed for network. */ void SetRaw(Network network, const uint8_t *data); bool SetInternal(const std::string &name); // for Tor addresses bool SetSpecial(const std::string &strName); // INADDR_ANY equivalent bool IsBindAny() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv4() const; // IPv6 address (not mapped IPv4, not Tor) bool IsIPv6() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) bool IsRFC1918() const; // IPv4 inter-network communications (198.18.0.0/15) bool IsRFC2544() const; // IPv4 ISP-level NAT (100.64.0.0/10) bool IsRFC6598() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, // 203.0.113.0/24) bool IsRFC5737() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3849() const; // IPv4 autoconfig (169.254.0.0/16) bool IsRFC3927() const; // IPv6 6to4 tunnelling (2002::/16) bool IsRFC3964() const; // IPv6 unique local (FC00::/7) bool IsRFC4193() const; // IPv6 Teredo tunnelling (2001::/32) bool IsRFC4380() const; // IPv6 ORCHID (deprecated) (2001:10::/28) bool IsRFC4843() const; // IPv6 ORCHIDv2 (2001:20::/28) bool IsRFC7343() const; // IPv6 autoconfig (FE80::/64) bool IsRFC4862() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96) bool IsRFC6052() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in // RFC2765) bool IsRFC6145() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36) bool IsHeNet() const; bool IsTor() const; bool IsLocal() const; bool IsRoutable() const; bool IsInternal() const; bool IsValid() const; enum Network GetNetwork() const; std::string ToString() const; std::string ToStringIP() const; unsigned int GetByte(int n) const; uint64_t GetHash() const; bool GetInAddr(struct in_addr *pipv4Addr) const; uint32_t GetNetClass() const; //! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled //! addresses, return the relevant IPv4 address as a uint32. uint32_t GetLinkedIPv4() const; //! Whether this address has a linked IPv4 address (see GetLinkedIPv4()). bool HasLinkedIPv4() const; // The AS on the BGP path to the node we use to diversify // peers in AddrMan bucketing based on the AS infrastructure. // The ip->AS mapping depends on how asmap is constructed. uint32_t GetMappedAS(const std::vector &asmap) const; std::vector GetGroup(const std::vector &asmap) const; std::vector GetAddrBytes() const { return {std::begin(ip), std::end(ip)}; } int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; explicit CNetAddr(const struct in6_addr &pipv6Addr, const uint32_t scope = 0); bool GetIn6Addr(struct in6_addr *pipv6Addr) const; friend bool operator==(const CNetAddr &a, const CNetAddr &b); 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 void Serialize(Stream &s) const { s << ip; } + + /** + * Unserialize from a stream. + */ + template void Unserialize(Stream &s) { + uint8_t ip_temp[sizeof(ip)]; + s >> ip_temp; + // Use SetLegacyIPv6() so that m_net is set correctly. For example + // ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4). + SetLegacyIPv6(ip_temp); + } friend class CSubNet; }; class CSubNet { protected: /// Network (base) address CNetAddr network; /// Netmask, in network byte order uint8_t netmask[16]; /// Is this value valid? (only used to signal parse errors) bool valid; public: CSubNet(); CSubNet(const CNetAddr &addr, int32_t mask); CSubNet(const CNetAddr &addr, const CNetAddr &mask); // constructor for single ip subnet (/32 or /128) explicit CSubNet(const CNetAddr &addr); bool Match(const CNetAddr &addr) const; std::string ToString() const; bool IsValid() const; friend bool operator==(const CSubNet &a, const CSubNet &b); friend bool operator!=(const CSubNet &a, const CSubNet &b) { return !(a == b); } friend bool operator<(const CSubNet &a, const CSubNet &b); SERIALIZE_METHODS(CSubNet, obj) { READWRITE(obj.network, obj.netmask, obj.valid); } }; /** A combination of a network address (CNetAddr) and a (TCP) port */ class CService : public CNetAddr { protected: // host order uint16_t port; public: CService(); CService(const CNetAddr &ip, unsigned short port); CService(const struct in_addr &ipv4Addr, unsigned short port); explicit CService(const struct sockaddr_in &addr); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const; bool SetSockAddr(const struct sockaddr *paddr); friend bool operator==(const CService &a, const CService &b); friend bool operator!=(const CService &a, const CService &b) { return !(a == b); } friend bool operator<(const CService &a, const CService &b); std::vector GetKey() const; std::string ToString() const; std::string ToStringPort() const; std::string ToStringIPPort() const; CService(const struct in6_addr &ipv6Addr, unsigned short port); explicit CService(const struct sockaddr_in6 &addr); SERIALIZE_METHODS(CService, obj) { READWRITEAS(CNetAddr, obj); READWRITE(Using>(obj.port)); } }; bool SanityCheckASMap(const std::vector &asmap); #endif // BITCOIN_NETADDRESS_H diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 674881953..a835137a2 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -1,505 +1,514 @@ // Copyright (c) 2012-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include BOOST_FIXTURE_TEST_SUITE(netbase_tests, BasicTestingSetup) static CNetAddr ResolveIP(const std::string &ip) { CNetAddr addr; LookupHost(ip, addr, false); return addr; } static CSubNet ResolveSubNet(const std::string &subnet) { CSubNet ret; LookupSubNet(subnet, ret); return ret; } static CNetAddr CreateInternal(const std::string &host) { CNetAddr addr; addr.SetInternal(host); return addr; } BOOST_AUTO_TEST_CASE(netbase_networks) { BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE); BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE); BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4); BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6); BOOST_CHECK( ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_ONION); BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL); } BOOST_AUTO_TEST_CASE(netbase_properties) { BOOST_CHECK(ResolveIP("127.0.0.1").IsIPv4()); BOOST_CHECK(ResolveIP("::FFFF:192.168.1.1").IsIPv4()); BOOST_CHECK(ResolveIP("::1").IsIPv6()); BOOST_CHECK(ResolveIP("10.0.0.1").IsRFC1918()); BOOST_CHECK(ResolveIP("192.168.1.1").IsRFC1918()); BOOST_CHECK(ResolveIP("172.31.255.255").IsRFC1918()); BOOST_CHECK(ResolveIP("198.18.0.0").IsRFC2544()); BOOST_CHECK(ResolveIP("198.19.255.255").IsRFC2544()); BOOST_CHECK(ResolveIP("2001:0DB8::").IsRFC3849()); BOOST_CHECK(ResolveIP("169.254.1.1").IsRFC3927()); BOOST_CHECK(ResolveIP("2002::1").IsRFC3964()); BOOST_CHECK(ResolveIP("FC00::").IsRFC4193()); BOOST_CHECK(ResolveIP("2001::2").IsRFC4380()); BOOST_CHECK(ResolveIP("2001:10::").IsRFC4843()); BOOST_CHECK(ResolveIP("2001:20::").IsRFC7343()); BOOST_CHECK(ResolveIP("FE80::").IsRFC4862()); BOOST_CHECK(ResolveIP("64:FF9B::").IsRFC6052()); BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor()); BOOST_CHECK(ResolveIP("127.0.0.1").IsLocal()); BOOST_CHECK(ResolveIP("::1").IsLocal()); BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable()); BOOST_CHECK(ResolveIP("2001::1").IsRoutable()); 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()); } static bool TestSplitHost(std::string test, std::string host, int port) { std::string hostOut; int portOut = -1; SplitHostPort(test, portOut, hostOut); return hostOut == host && port == portOut; } BOOST_AUTO_TEST_CASE(netbase_splithost) { BOOST_CHECK(TestSplitHost("www.bitcoin.org", "www.bitcoin.org", -1)); BOOST_CHECK(TestSplitHost("[www.bitcoin.org]", "www.bitcoin.org", -1)); BOOST_CHECK(TestSplitHost("www.bitcoin.org:80", "www.bitcoin.org", 80)); BOOST_CHECK(TestSplitHost("[www.bitcoin.org]:80", "www.bitcoin.org", 80)); BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1)); BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333)); BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1)); BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333)); BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1)); BOOST_CHECK( TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333)); BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333)); BOOST_CHECK(TestSplitHost("::8333", "::8333", -1)); BOOST_CHECK(TestSplitHost(":8333", "", 8333)); BOOST_CHECK(TestSplitHost("[]:8333", "", 8333)); BOOST_CHECK(TestSplitHost("", "", -1)); } static bool TestParse(std::string src, std::string canon) { CService addr(LookupNumeric(src, 65535)); return canon == addr.ToString(); } BOOST_AUTO_TEST_CASE(netbase_lookupnumeric) { BOOST_CHECK(TestParse("127.0.0.1", "127.0.0.1:65535")); BOOST_CHECK(TestParse("127.0.0.1:8333", "127.0.0.1:8333")); BOOST_CHECK(TestParse("::ffff:127.0.0.1", "127.0.0.1:65535")); BOOST_CHECK(TestParse("::", "[::]:65535")); BOOST_CHECK(TestParse("[::]:8333", "[::]:8333")); BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535")); 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) { // values from // https://web.archive.org/web/20121122003543/http://www.cypherpunk.at/onioncat/wiki/OnionCat CNetAddr addr1(ResolveIP("5wyqrzbvrdsumnok.onion")); CNetAddr addr2(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca")); BOOST_CHECK(addr1 == addr2); BOOST_CHECK(addr1.IsTor()); BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion"); BOOST_CHECK(addr1.IsRoutable()); } +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) { BOOST_CHECK(ResolveSubNet("1.2.3.0/24") == ResolveSubNet("1.2.3.0/255.255.255.0")); BOOST_CHECK(ResolveSubNet("1.2.3.0/24") != ResolveSubNet("1.2.4.0/255.255.255.0")); BOOST_CHECK(ResolveSubNet("1.2.3.0/24").Match(ResolveIP("1.2.3.4"))); BOOST_CHECK(!ResolveSubNet("1.2.2.0/24").Match(ResolveIP("1.2.3.4"))); BOOST_CHECK(ResolveSubNet("1.2.3.4").Match(ResolveIP("1.2.3.4"))); BOOST_CHECK(ResolveSubNet("1.2.3.4/32").Match(ResolveIP("1.2.3.4"))); BOOST_CHECK(!ResolveSubNet("1.2.3.4").Match(ResolveIP("5.6.7.8"))); BOOST_CHECK(!ResolveSubNet("1.2.3.4/32").Match(ResolveIP("5.6.7.8"))); BOOST_CHECK( ResolveSubNet("::ffff:127.0.0.1").Match(ResolveIP("127.0.0.1"))); BOOST_CHECK( ResolveSubNet("1:2:3:4:5:6:7:8").Match(ResolveIP("1:2:3:4:5:6:7:8"))); BOOST_CHECK( !ResolveSubNet("1:2:3:4:5:6:7:8").Match(ResolveIP("1:2:3:4:5:6:7:9"))); BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:0/112") .Match(ResolveIP("1:2:3:4:5:6:7:1234"))); BOOST_CHECK( ResolveSubNet("192.168.0.1/24").Match(ResolveIP("192.168.0.2"))); BOOST_CHECK( ResolveSubNet("192.168.0.20/29").Match(ResolveIP("192.168.0.18"))); 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) BOOST_CHECK(!CSubNet().Match(ResolveIP("1.2.3.4"))); BOOST_CHECK(!ResolveSubNet("").Match(ResolveIP("4.5.6.7"))); BOOST_CHECK(!ResolveSubNet("bloop").Match(ResolveIP("0.0.0.0"))); BOOST_CHECK(!ResolveSubNet("bloop").Match(ResolveIP("hab"))); // Check valid/invalid BOOST_CHECK(ResolveSubNet("1.2.3.0/0").IsValid()); BOOST_CHECK(!ResolveSubNet("1.2.3.0/-1").IsValid()); BOOST_CHECK(ResolveSubNet("1.2.3.0/32").IsValid()); BOOST_CHECK(!ResolveSubNet("1.2.3.0/33").IsValid()); BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/0").IsValid()); BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/33").IsValid()); BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8/-1").IsValid()); BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/128").IsValid()); BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8/129").IsValid()); BOOST_CHECK(!ResolveSubNet("fuzzy").IsValid()); // CNetAddr constructor test BOOST_CHECK(CSubNet(ResolveIP("127.0.0.1")).IsValid()); BOOST_CHECK(CSubNet(ResolveIP("127.0.0.1")).Match(ResolveIP("127.0.0.1"))); BOOST_CHECK(!CSubNet(ResolveIP("127.0.0.1")).Match(ResolveIP("127.0.0.2"))); BOOST_CHECK(CSubNet(ResolveIP("127.0.0.1")).ToString() == "127.0.0.1/32"); CSubNet subnet = CSubNet(ResolveIP("1.2.3.4"), 32); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32"); subnet = CSubNet(ResolveIP("1.2.3.4"), 8); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8"); subnet = CSubNet(ResolveIP("1.2.3.4"), 0); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0"); subnet = CSubNet(ResolveIP("1.2.3.4"), ResolveIP("255.255.255.255")); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32"); subnet = CSubNet(ResolveIP("1.2.3.4"), ResolveIP("255.0.0.0")); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8"); subnet = CSubNet(ResolveIP("1.2.3.4"), ResolveIP("0.0.0.0")); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0"); BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).IsValid()); BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")) .Match(ResolveIP("1:2:3:4:5:6:7:8"))); BOOST_CHECK(!CSubNet(ResolveIP("1:2:3:4:5:6:7:8")) .Match(ResolveIP("1:2:3:4:5:6:7:9"))); BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128"); subnet = ResolveSubNet("1.2.3.4/255.255.255.255"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32"); subnet = ResolveSubNet("1.2.3.4/255.255.255.254"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/31"); subnet = ResolveSubNet("1.2.3.4/255.255.255.252"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/30"); subnet = ResolveSubNet("1.2.3.4/255.255.255.248"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/29"); subnet = ResolveSubNet("1.2.3.4/255.255.255.240"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/28"); subnet = ResolveSubNet("1.2.3.4/255.255.255.224"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/27"); subnet = ResolveSubNet("1.2.3.4/255.255.255.192"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/26"); subnet = ResolveSubNet("1.2.3.4/255.255.255.128"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/25"); subnet = ResolveSubNet("1.2.3.4/255.255.255.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/24"); subnet = ResolveSubNet("1.2.3.4/255.255.254.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.2.0/23"); subnet = ResolveSubNet("1.2.3.4/255.255.252.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/22"); subnet = ResolveSubNet("1.2.3.4/255.255.248.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/21"); subnet = ResolveSubNet("1.2.3.4/255.255.240.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/20"); subnet = ResolveSubNet("1.2.3.4/255.255.224.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/19"); subnet = ResolveSubNet("1.2.3.4/255.255.192.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/18"); subnet = ResolveSubNet("1.2.3.4/255.255.128.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/17"); subnet = ResolveSubNet("1.2.3.4/255.255.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/16"); subnet = ResolveSubNet("1.2.3.4/255.254.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/15"); subnet = ResolveSubNet("1.2.3.4/255.252.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/14"); subnet = ResolveSubNet("1.2.3.4/255.248.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/13"); subnet = ResolveSubNet("1.2.3.4/255.240.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/12"); subnet = ResolveSubNet("1.2.3.4/255.224.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/11"); subnet = ResolveSubNet("1.2.3.4/255.192.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/10"); subnet = ResolveSubNet("1.2.3.4/255.128.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/9"); subnet = ResolveSubNet("1.2.3.4/255.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8"); subnet = ResolveSubNet("1.2.3.4/254.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/7"); subnet = ResolveSubNet("1.2.3.4/252.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/6"); subnet = ResolveSubNet("1.2.3.4/248.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/5"); subnet = ResolveSubNet("1.2.3.4/240.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/4"); subnet = ResolveSubNet("1.2.3.4/224.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/3"); subnet = ResolveSubNet("1.2.3.4/192.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/2"); subnet = ResolveSubNet("1.2.3.4/128.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/1"); subnet = ResolveSubNet("1.2.3.4/0.0.0.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0"); subnet = ResolveSubNet( "1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/128"); subnet = ResolveSubNet( "1:2:3:4:5:6:7:8/ffff:0000:0000:0000:0000:0000:0000:0000"); BOOST_CHECK_EQUAL(subnet.ToString(), "1::/16"); subnet = ResolveSubNet( "1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000"); BOOST_CHECK_EQUAL(subnet.ToString(), "::/0"); subnet = ResolveSubNet("1.2.3.4/255.255.232.0"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/255.255.232.0"); subnet = ResolveSubNet( "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f"); BOOST_CHECK_EQUAL( subnet.ToString(), "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f"); } BOOST_AUTO_TEST_CASE(netbase_getgroup) { // use /16 std::vector asmap; typedef std::vector Vec8; // Local -> !Routable() BOOST_CHECK(ResolveIP("127.0.0.1").GetGroup(asmap) == Vec8{0}); // !Valid -> !Routable() BOOST_CHECK(ResolveIP("257.0.0.1").GetGroup(asmap) == Vec8{0}); // RFC1918 -> !Routable() BOOST_CHECK(ResolveIP("10.0.0.1").GetGroup(asmap) == Vec8{0}); // RFC3927 -> !Routable() BOOST_CHECK(ResolveIP("169.254.1.1").GetGroup(asmap) == Vec8{0}); // IPv4 BOOST_CHECK(ResolveIP("1.2.3.4").GetGroup(asmap) == Vec8({NET_IPV4, 1, 2})); // RFC6145 BOOST_CHECK(ResolveIP("::FFFF:0:102:304").GetGroup(asmap) == Vec8({NET_IPV4, 1, 2})); // RFC6052 BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup(asmap) == Vec8({NET_IPV4, 1, 2})); // RFC3964 BOOST_CHECK( ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup(asmap) == Vec8({NET_IPV4, 1, 2})); // RFC4380 BOOST_CHECK( ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup(asmap) == Vec8({NET_IPV4, 1, 2})); // Tor BOOST_CHECK( ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup(asmap) == Vec8({NET_ONION, 239})); // he.net BOOST_CHECK( ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup(asmap) == Vec8({NET_IPV6, 32, 1, 4, 112, 175})); // IPv6 BOOST_CHECK( ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup(asmap) == Vec8({NET_IPV6, 32, 1, 32, 1})); // baz.net sha256 hash: // 12929400eb4607c4ac075f087167e75286b179c693eb059a01774b864e8fe505 Vec8 internal_group = {NET_INTERNAL, 0x12, 0x92, 0x94, 0x00, 0xeb, 0x46, 0x07, 0xc4, 0xac, 0x07}; BOOST_CHECK(CreateInternal("baz.net").GetGroup(asmap) == internal_group); } BOOST_AUTO_TEST_CASE(netbase_parsenetwork) { BOOST_CHECK_EQUAL(ParseNetwork("ipv4"), NET_IPV4); BOOST_CHECK_EQUAL(ParseNetwork("ipv6"), NET_IPV6); BOOST_CHECK_EQUAL(ParseNetwork("onion"), NET_ONION); BOOST_CHECK_EQUAL(ParseNetwork("tor"), NET_ONION); BOOST_CHECK_EQUAL(ParseNetwork("IPv4"), NET_IPV4); BOOST_CHECK_EQUAL(ParseNetwork("IPv6"), NET_IPV6); BOOST_CHECK_EQUAL(ParseNetwork("ONION"), NET_ONION); BOOST_CHECK_EQUAL(ParseNetwork("TOR"), NET_ONION); BOOST_CHECK_EQUAL(ParseNetwork(":)"), NET_UNROUTABLE); BOOST_CHECK_EQUAL(ParseNetwork("tÖr"), NET_UNROUTABLE); BOOST_CHECK_EQUAL(ParseNetwork("\xfe\xff"), NET_UNROUTABLE); BOOST_CHECK_EQUAL(ParseNetwork(""), NET_UNROUTABLE); } BOOST_AUTO_TEST_CASE(netpermissions_test) { bilingual_str error; NetWhitebindPermissions whitebindPermissions; NetWhitelistPermissions whitelistPermissions; // Detect invalid white bind BOOST_CHECK( !NetWhitebindPermissions::TryParse("", whitebindPermissions, error)); BOOST_CHECK(error.original.find("Cannot resolve -whitebind address") != std::string::npos); BOOST_CHECK(!NetWhitebindPermissions::TryParse( "127.0.0.1", whitebindPermissions, error)); BOOST_CHECK(error.original.find("Need to specify a port with -whitebind") != std::string::npos); BOOST_CHECK( !NetWhitebindPermissions::TryParse("", whitebindPermissions, error)); // If no permission flags, assume backward compatibility BOOST_CHECK(NetWhitebindPermissions::TryParse("1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK(error.empty()); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ISIMPLICIT); BOOST_CHECK( NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT)); NetPermissions::ClearFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT); BOOST_CHECK( !NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE); NetPermissions::AddFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT); BOOST_CHECK( NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT)); // Can set one permission BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER); BOOST_CHECK(NetWhitebindPermissions::TryParse("@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE); // Happy path, can parse flags BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay@1.2.3.4:32", whitebindPermissions, error)); // forcerelay should also activate the relay permission BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY); BOOST_CHECK(NetWhitebindPermissions::TryParse( "bloom,relay,noban@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN); BOOST_CHECK(NetWhitebindPermissions::TryParse( "bloom,forcerelay,noban@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK(NetWhitebindPermissions::TryParse("all@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ALL); // Allow dups BOOST_CHECK(NetWhitebindPermissions::TryParse( "bloom,relay,noban,noban@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN); // Allow empty BOOST_CHECK(NetWhitebindPermissions::TryParse( "bloom,relay,,noban@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN); BOOST_CHECK(NetWhitebindPermissions::TryParse(",@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE); BOOST_CHECK(NetWhitebindPermissions::TryParse(",,@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE); // Detect invalid flag BOOST_CHECK(!NetWhitebindPermissions::TryParse( "bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK(error.original.find("Invalid P2P permission") != std::string::npos); // Check whitelist error BOOST_CHECK(!NetWhitelistPermissions::TryParse( "bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, error)); BOOST_CHECK( error.original.find("Invalid netmask specified in -whitelist") != std::string::npos); // Happy path for whitelist parsing BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error)); BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_NOBAN); BOOST_CHECK(NetWhitelistPermissions::TryParse( "bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, error)); BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_NOBAN | PF_RELAY); BOOST_CHECK(error.empty()); BOOST_CHECK_EQUAL(whitelistPermissions.m_subnet.ToString(), "1.2.3.4/32"); BOOST_CHECK(NetWhitelistPermissions::TryParse( "bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error)); const auto strings = NetPermissions::ToStrings(PF_ALL); BOOST_CHECK_EQUAL(strings.size(), 5); BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") != strings.end()); BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") != strings.end()); BOOST_CHECK(std::find(strings.begin(), strings.end(), "relay") != strings.end()); BOOST_CHECK(std::find(strings.begin(), strings.end(), "noban") != strings.end()); BOOST_CHECK(std::find(strings.begin(), strings.end(), "mempool") != strings.end()); } BOOST_AUTO_TEST_CASE( netbase_dont_resolve_strings_with_embedded_nul_characters) { CNetAddr addr; BOOST_CHECK(LookupHost(std::string("127.0.0.1", 9), addr, false)); BOOST_CHECK(!LookupHost(std::string("127.0.0.1\0", 10), addr, false)); BOOST_CHECK( !LookupHost(std::string("127.0.0.1\0example.com", 21), addr, false)); BOOST_CHECK( !LookupHost(std::string("127.0.0.1\0example.com\0", 22), addr, false)); CSubNet ret; BOOST_CHECK(LookupSubNet(std::string("1.2.3.0/24", 10), ret)); BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0", 11), ret)); BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com", 22), ret)); BOOST_CHECK( !LookupSubNet(std::string("1.2.3.0/24\0example.com\0", 23), ret)); BOOST_CHECK(LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret)); BOOST_CHECK( !LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0", 23), ret)); BOOST_CHECK(!LookupSubNet( std::string("5wyqrzbvrdsumnok.onion\0example.com", 34), ret)); BOOST_CHECK(!LookupSubNet( std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret)); } BOOST_AUTO_TEST_SUITE_END()