Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F12910282
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
39 KB
Subscribers
None
View Options
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 116e6c42d..16084d43e 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -1,736 +1,735 @@
// 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.
#ifdef HAVE_CONFIG_H
#include <config/bitcoin-config.h>
#endif
#include <hash.h>
#include <netaddress.h>
#include <tinyformat.h>
#include <util/strencodings.h>
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};
CNetAddr::CNetAddr() {
memset(ip, 0, sizeof(ip));
- scopeId = 0;
}
void CNetAddr::SetIP(const CNetAddr &ipIn) {
memcpy(ip, ipIn.ip, sizeof(ip));
}
void CNetAddr::SetRaw(Network network, const uint8_t *ip_in) {
switch (network) {
case NET_IPV4:
memcpy(ip, pchIPv4, 12);
memcpy(ip + 12, ip_in, 4);
break;
case NET_IPV6:
memcpy(ip, ip_in, 16);
break;
default:
assert(!"invalid network");
}
}
bool CNetAddr::SetInternal(const std::string &name) {
if (name.empty()) {
return false;
}
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;
}
bool CNetAddr::SetSpecial(const std::string &strName) {
if (strName.size() > 6 &&
strName.substr(strName.size() - 6, 6) == ".onion") {
std::vector<uint8_t> vchAddr =
DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
if (vchAddr.size() != 16 - sizeof(pchOnionCat)) {
return false;
}
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::IsIPv4() const {
return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
}
bool CNetAddr::IsIPv6() const {
return !IsIPv4() && !IsTor() && !IsInternal();
}
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;
}
bool CNetAddr::IsRFC3964() const {
return (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);
}
bool CNetAddr::IsRFC4380() const {
return (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);
}
bool CNetAddr::IsRFC4193() const {
return ((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);
}
bool CNetAddr::IsRFC4843() const {
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 &&
(GetByte(12) & 0xF0) == 0x10);
}
bool CNetAddr::IsTor() const {
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
}
bool CNetAddr::IsLocal() const {
// IPv4 loopback
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) return true;
return false;
}
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) {
return false;
}
// unspecified IPv6 address (::/128)
uint8_t ipNone6[16] = {};
if (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;
}
bool CNetAddr::IsRoutable() const {
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 {
if (IsInternal()) {
return NET_INTERNAL;
}
if (!IsRoutable()) {
return NET_UNROUTABLE;
}
if (IsIPv4()) {
return NET_IPV4;
}
if (IsTor()) {
return NET_ONION;
}
return NET_IPV6;
}
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);
}
bool operator<(const CNetAddr &a, const CNetAddr &b) {
return (memcmp(a.ip, b.ip, 16) < 0);
}
bool CNetAddr::GetInAddr(struct in_addr *pipv4Addr) const {
if (!IsIPv4()) {
return false;
}
memcpy(pipv4Addr, ip + 12, 4);
return true;
}
bool CNetAddr::GetIn6Addr(struct in6_addr *pipv6Addr) const {
memcpy(pipv6Addr, ip, 16);
return true;
}
// get canonical identifier of an address' group no two connections will be
// attempted to addresses with the same group
std::vector<uint8_t> CNetAddr::GetGroup() const {
std::vector<uint8_t> vchRet;
int nClass = NET_IPV6;
int nStartByte = 0;
int nBits = 16;
// all local addresses belong to the same group
if (IsLocal()) {
nClass = 255;
nBits = 0;
}
if (IsInternal()) {
// all internal-usage addresses get their own group
nClass = NET_INTERNAL;
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
nClass = NET_UNROUTABLE;
nBits = 0;
} else if (IsIPv4() || IsRFC6145() || IsRFC6052()) {
// for IPv4 addresses, '1' + the 16 higher-order bits of the IP includes
// mapped IPv4, SIIT translated IPv4, and the well-known prefix
nClass = NET_IPV4;
nStartByte = 12;
} else if (IsRFC3964()) {
// for 6to4 tunnelled addresses, use the encapsulated IPv4 address
nClass = NET_IPV4;
nStartByte = 2;
} else if (IsRFC4380()) {
// for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4
// address
vchRet.push_back(NET_IPV4);
vchRet.push_back(GetByte(3) ^ 0xFF);
vchRet.push_back(GetByte(2) ^ 0xFF);
return vchRet;
} else if (IsTor()) {
nClass = NET_ONION;
nStartByte = 6;
nBits = 4;
} else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
GetByte(13) == 0x04 && GetByte(12) == 0x70) {
// for he.net, use /36 groups
nBits = 36;
} else {
// for the rest of the IPv6 network, use /32 groups
nBits = 32;
}
vchRet.push_back(nClass);
while (nBits >= 8) {
vchRet.push_back(GetByte(15 - nStartByte));
nStartByte++;
nBits -= 8;
}
if (nBits > 0) {
vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1));
}
return vchRet;
}
uint64_t CNetAddr::GetHash() const {
uint256 hash = Hash(&ip[0], &ip[16]);
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<const struct sockaddr_in *>(paddr));
return true;
case AF_INET6:
*this =
CService(*reinterpret_cast<const struct sockaddr_in6 *>(paddr));
return true;
default:
return false;
}
}
unsigned short CService::GetPort() const {
return port;
}
bool operator==(const CService &a, const CService &b) {
return static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) &&
a.port == b.port;
}
bool operator<(const CService &a, const CService &b) {
return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) ||
(static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) &&
a.port < b.port);
}
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<struct sockaddr_in *>(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<struct sockaddr_in6 *>(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;
}
std::vector<uint8_t> CService::GetKey() const {
std::vector<uint8_t> vKey;
vKey.resize(18);
memcpy(vKey.data(), ip, 16);
vKey[16] = port / 0x100;
vKey[17] = port & 0x0FF;
return vKey;
}
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;
}
bool CSubNet::Match(const CNetAddr &addr) const {
if (!valid || !addr.IsValid()) {
return false;
}
for (int x = 0; x < 16; ++x) {
if ((addr.ip[x] & netmask[x]) != network.ip[x]) {
return false;
}
}
return true;
}
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));
}
diff --git a/src/netaddress.h b/src/netaddress.h
index 0fad0040d..b8b46fe4d 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -1,202 +1,202 @@
// 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 <config/bitcoin-config.h>
#endif
#include <compat.h>
#include <serialize.h>
#include <span.h>
#include <cstdint>
#include <string>
#include <vector>
enum Network {
NET_UNROUTABLE = 0,
NET_IPV4,
NET_IPV6,
NET_ONION,
NET_INTERNAL,
NET_MAX,
};
/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */
class CNetAddr {
protected:
// in network byte order
uint8_t ip[16];
// for scoped/link-local ipv6 addresses
- uint32_t scopeId;
+ uint32_t scopeId{0};
public:
CNetAddr();
explicit CNetAddr(const struct in_addr &ipv4Addr);
void SetIP(const CNetAddr &ip);
private:
/**
* 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);
public:
/**
* 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);
// for Tor addresses
bool SetSpecial(const std::string &strName);
// 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 (192.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 (2001:10::/28)
bool IsRFC4843() const;
// IPv6 autoconfig (FE80::/64)
bool IsRFC4862() const;
// IPv6 well-known prefix (64:FF9B::/96)
bool IsRFC6052() const;
// IPv6 IPv4-translated address (::FFFF:0:0:0/96)
bool IsRFC6145() 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;
std::vector<uint8_t> GetGroup() const;
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);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
READWRITE(ip);
}
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 (<ipv4>/32 or <ipv6>/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);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
READWRITE(network);
READWRITE(netmask);
READWRITE(valid);
}
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
class CService : public CNetAddr {
protected:
// host order
unsigned short 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<uint8_t> 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);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
READWRITE(ip);
unsigned short portN = htons(port);
READWRITE(Span<uint8_t>((uint8_t *)&portN, 2));
if (ser_action.ForRead()) {
port = ntohs(portN);
}
}
};
#endif // BITCOIN_NETADDRESS_H
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 23875b19f..64077f0f4 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -1,299 +1,340 @@
// Copyright (c) 2012-2016 The Bitcoin Core developers
// Copyright (c) 2017-2018 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <net.h>
#include <addrman.h>
#include <chainparams.h>
#include <clientversion.h>
#include <config.h>
#include <hash.h>
#include <netbase.h>
#include <serialize.h>
#include <streams.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <string>
class CAddrManSerializationMock : public CAddrMan {
public:
virtual void Serialize(CDataStream &s) const = 0;
//! Ensure that bucket placement is always the same for testing purposes.
void MakeDeterministic() {
nKey.SetNull();
insecure_rand = FastRandomContext(true);
}
};
class CAddrManUncorrupted : public CAddrManSerializationMock {
public:
void Serialize(CDataStream &s) const override { CAddrMan::Serialize(s); }
};
class CAddrManCorrupted : public CAddrManSerializationMock {
public:
void Serialize(CDataStream &s) const override {
// Produces corrupt output that claims addrman has 20 addrs when it only
// has one addr.
uint8_t nVersion = 1;
s << nVersion;
s << uint8_t(32);
s << nKey;
s << 10; // nNew
s << 10; // nTried
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
s << nUBuckets;
CService serv;
Lookup("252.1.1.1", serv, 7777, false);
CAddress addr = CAddress(serv, NODE_NONE);
CNetAddr resolved;
LookupHost("252.2.2.2", resolved, false);
CAddrInfo info = CAddrInfo(addr, resolved);
s << info;
}
};
class NetTestConfig : public DummyConfig {
public:
bool SetMaxBlockSize(uint64_t maxBlockSize) override {
nMaxBlockSize = maxBlockSize;
return true;
}
uint64_t GetMaxBlockSize() const override { return nMaxBlockSize; }
private:
uint64_t nMaxBlockSize;
};
static CDataStream AddrmanToStream(CAddrManSerializationMock &_addrman) {
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
ssPeersIn << Params().DiskMagic();
ssPeersIn << _addrman;
std::string str = ssPeersIn.str();
std::vector<uint8_t> vchData(str.begin(), str.end());
return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}
BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(cnode_listen_port) {
// test default
unsigned short port = GetListenPort();
BOOST_CHECK(port == Params().GetDefaultPort());
// test set port
unsigned short altPort = 12345;
gArgs.SoftSetArg("-port", std::to_string(altPort));
port = GetListenPort();
BOOST_CHECK(port == altPort);
}
BOOST_AUTO_TEST_CASE(caddrdb_read) {
CAddrManUncorrupted addrmanUncorrupted;
addrmanUncorrupted.MakeDeterministic();
CService addr1, addr2, addr3;
Lookup("250.7.1.1", addr1, 8333, false);
Lookup("250.7.2.2", addr2, 9999, false);
Lookup("250.7.3.3", addr3, 9999, false);
// Add three addresses to new table.
CService source;
Lookup("252.5.1.1", source, 8333, false);
addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source);
addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source);
addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source);
// Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
bool exceptionThrown = false;
CAddrMan addrman1;
BOOST_CHECK(addrman1.size() == 0);
try {
uint8_t pchMsgTmp[4];
ssPeers1 >> pchMsgTmp;
ssPeers1 >> addrman1;
} catch (const std::exception &e) {
exceptionThrown = true;
}
BOOST_CHECK(addrman1.size() == 3);
BOOST_CHECK(exceptionThrown == false);
// Test that CAddrDB::Read creates an addrman with the correct number of
// addrs.
CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);
CAddrMan addrman2;
CAddrDB adb(Params());
BOOST_CHECK(addrman2.size() == 0);
adb.Read(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3);
}
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) {
CAddrManCorrupted addrmanCorrupted;
addrmanCorrupted.MakeDeterministic();
// Test that the de-serialization of corrupted addrman throws an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
bool exceptionThrown = false;
CAddrMan addrman1;
BOOST_CHECK(addrman1.size() == 0);
try {
uint8_t pchMsgTmp[4];
ssPeers1 >> pchMsgTmp;
ssPeers1 >> addrman1;
} catch (const std::exception &e) {
exceptionThrown = true;
}
// Even through de-serialization failed addrman is not left in a clean
// state.
BOOST_CHECK(addrman1.size() == 1);
BOOST_CHECK(exceptionThrown);
// Test that CAddrDB::Read leaves addrman in a clean state if
// de-serialization fails.
CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);
CAddrMan addrman2;
CAddrDB adb(Params());
BOOST_CHECK(addrman2.size() == 0);
adb.Read(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 0);
}
BOOST_AUTO_TEST_CASE(cnode_simple_test) {
SOCKET hSocket = INVALID_SOCKET;
NodeId id = 0;
int height = 0;
in_addr ipv4Addr;
ipv4Addr.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest;
bool fInboundIn = false;
// Test that fFeeler is false by default.
auto pnode1 =
std::make_unique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0,
CAddress(), pszDest, fInboundIn);
BOOST_CHECK(pnode1->fInbound == false);
BOOST_CHECK(pnode1->fFeeler == false);
fInboundIn = true;
auto pnode2 =
std::make_unique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1,
CAddress(), pszDest, fInboundIn);
BOOST_CHECK(pnode2->fInbound == true);
BOOST_CHECK(pnode2->fFeeler == false);
}
BOOST_AUTO_TEST_CASE(test_getSubVersionEB) {
BOOST_CHECK_EQUAL(getSubVersionEB(13800000000), "13800.0");
BOOST_CHECK_EQUAL(getSubVersionEB(3800000000), "3800.0");
BOOST_CHECK_EQUAL(getSubVersionEB(14000000), "14.0");
BOOST_CHECK_EQUAL(getSubVersionEB(1540000), "1.5");
BOOST_CHECK_EQUAL(getSubVersionEB(1560000), "1.5");
BOOST_CHECK_EQUAL(getSubVersionEB(210000), "0.2");
BOOST_CHECK_EQUAL(getSubVersionEB(10000), "0.0");
BOOST_CHECK_EQUAL(getSubVersionEB(0), "0.0");
}
BOOST_AUTO_TEST_CASE(test_userAgent) {
NetTestConfig config;
config.SetMaxBlockSize(8000000);
const std::string uacomment = "A very nice comment";
gArgs.ForceSetMultiArg("-uacomment", uacomment);
const std::string versionMessage =
"/Bitcoin ABC:" + std::to_string(CLIENT_VERSION_MAJOR) + "." +
std::to_string(CLIENT_VERSION_MINOR) + "." +
std::to_string(CLIENT_VERSION_REVISION) + "(EB8.0; " + uacomment + ")/";
BOOST_CHECK_EQUAL(userAgent(config), versionMessage);
}
BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network) {
BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);
SetReachable(NET_IPV4, false);
SetReachable(NET_IPV6, false);
SetReachable(NET_ONION, false);
BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), false);
BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), false);
BOOST_CHECK_EQUAL(IsReachable(NET_ONION), false);
SetReachable(NET_IPV4, true);
SetReachable(NET_IPV6, true);
SetReachable(NET_ONION, true);
BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);
}
BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal) {
BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true);
BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);
SetReachable(NET_UNROUTABLE, false);
SetReachable(NET_INTERNAL, false);
// Ignored for both networks
BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true);
BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);
}
CNetAddr UtilBuildAddress(uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4) {
uint8_t ip[] = {p1, p2, p3, p4};
struct sockaddr_in sa;
// initialize the memory block
memset(&sa, 0, sizeof(sockaddr_in));
memcpy(&(sa.sin_addr), &ip, sizeof(ip));
return CNetAddr(sa.sin_addr);
}
BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr) {
// 1.1.1.1
CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001);
SetReachable(NET_IPV4, true);
BOOST_CHECK_EQUAL(IsReachable(addr), true);
SetReachable(NET_IPV4, false);
BOOST_CHECK_EQUAL(IsReachable(addr), false);
// have to reset this, because this is stateful.
SetReachable(NET_IPV4, true);
}
BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle) {
// 2.1.1.1:1000
CService addr =
CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000);
SetReachable(NET_IPV4, true);
BOOST_CHECK_EQUAL(IsLocal(addr), false);
BOOST_CHECK_EQUAL(AddLocal(addr, 1000), true);
BOOST_CHECK_EQUAL(IsLocal(addr), true);
RemoveLocal(addr);
BOOST_CHECK_EQUAL(IsLocal(addr), false);
}
+// prior to PR #14728, this test triggers an undefined behavior
+BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test) {
+ // set up local addresses; all that's necessary to reproduce the bug is
+ // that a normal IPv4 address is among the entries, but if this address is
+ // !IsRoutable the undefined behavior is easier to trigger deterministically
+ {
+ LOCK(cs_mapLocalHost);
+ in_addr ipv4AddrLocal;
+ ipv4AddrLocal.s_addr = 0x0100007f;
+ CNetAddr addr = CNetAddr(ipv4AddrLocal);
+ LocalServiceInfo lsi;
+ lsi.nScore = 23;
+ lsi.nPort = 42;
+ mapLocalHost[addr] = lsi;
+ }
+
+ // create a peer with an IPv4 address
+ in_addr ipv4AddrPeer;
+ ipv4AddrPeer.s_addr = 0xa0b0c001;
+ CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
+ std::unique_ptr<CNode> pnode =
+ std::make_unique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0,
+ CAddress{}, std::string{}, false);
+ pnode->fSuccessfullyConnected.store(true);
+
+ // the peer claims to be reaching us via IPv6
+ in6_addr ipv6AddrLocal;
+ memset(ipv6AddrLocal.s6_addr, 0, 16);
+ ipv6AddrLocal.s6_addr[0] = 0xcc;
+ CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK);
+ pnode->SetAddrLocal(addrLocal);
+
+ // before patch, this causes undefined behavior detectable with clang's
+ // -fsanitize=memory
+ AdvertiseLocal(&*pnode);
+
+ // suppress no-checks-run warning; if this test fails, it's by triggering a
+ // sanitizer
+ BOOST_CHECK(1);
+}
+
BOOST_AUTO_TEST_SUITE_END()
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jan 29, 17:07 (11 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5053371
Default Alt Text
(39 KB)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment