Changeset View
Changeset View
Standalone View
Standalone View
src/netaddress.cpp
Show All 12 Lines | |||||
#include <util/strencodings.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 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}; | static const uint8_t pchOnionCat[] = {0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43}; | ||||
// 0xFD + sha256("bitcoin")[0:5] | // 0xFD + sha256("bitcoin")[0:5] | ||||
static const uint8_t g_internal_prefix[] = {0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24}; | 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() { | CNetAddr::CNetAddr() { | ||||
memset(ip, 0, sizeof(ip)); | memset(ip, 0, sizeof(ip)); | ||||
} | } | ||||
void CNetAddr::SetIP(const CNetAddr &ipIn) { | void CNetAddr::SetIP(const CNetAddr &ipIn) { | ||||
memcpy(ip, ipIn.ip, sizeof(ip)); | memcpy(ip, ipIn.ip, sizeof(ip)); | ||||
} | } | ||||
void CNetAddr::SetRaw(Network network, const uint8_t *ip_in) { | void CNetAddr::SetRaw(Network network, const uint8_t *ip_in) { | ||||
switch (network) { | switch (network) { | ||||
case NET_IPV4: | case NET_IPV4: | ||||
memcpy(ip, pchIPv4, 12); | memcpy(ip, pchIPv4, 12); | ||||
memcpy(ip + 12, ip_in, 4); | memcpy(ip + 12, ip_in, 4); | ||||
break; | break; | ||||
case NET_IPV6: | case NET_IPV6: | ||||
memcpy(ip, ip_in, 16); | memcpy(ip, ip_in, 16); | ||||
break; | break; | ||||
default: | default: | ||||
assert(!"invalid network"); | 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) { | bool CNetAddr::SetInternal(const std::string &name) { | ||||
if (name.empty()) { | if (name.empty()) { | ||||
return false; | return false; | ||||
} | } | ||||
uint8_t hash[32] = {}; | uint8_t hash[32] = {}; | ||||
CSHA256().Write((const uint8_t *)name.data(), name.size()).Finalize(hash); | CSHA256().Write((const uint8_t *)name.data(), name.size()).Finalize(hash); | ||||
memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix)); | memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix)); | ||||
memcpy(ip + sizeof(g_internal_prefix), hash, | memcpy(ip + sizeof(g_internal_prefix), hash, | ||||
sizeof(ip) - sizeof(g_internal_prefix)); | sizeof(ip) - sizeof(g_internal_prefix)); | ||||
return true; | 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) { | bool CNetAddr::SetSpecial(const std::string &strName) { | ||||
if (strName.size() > 6 && | if (strName.size() > 6 && | ||||
strName.substr(strName.size() - 6, 6) == ".onion") { | strName.substr(strName.size() - 6, 6) == ".onion") { | ||||
std::vector<uint8_t> vchAddr = | std::vector<uint8_t> vchAddr = | ||||
DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); | DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); | ||||
if (vchAddr.size() != 16 - sizeof(pchOnionCat)) { | if (vchAddr.size() != 16 - sizeof(pchOnionCat)) { | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && | ||||
(GetByte(12) & 0xF0) == 0x10); | (GetByte(12) & 0xF0) == 0x10); | ||||
} | } | ||||
bool CNetAddr::IsRFC7343() const { | bool CNetAddr::IsRFC7343() const { | ||||
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && | return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && | ||||
(GetByte(12) & 0xF0) == 0x20); | (GetByte(12) & 0xF0) == 0x20); | ||||
} | } | ||||
/** | |||||
* @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 { | bool CNetAddr::IsTor() const { | ||||
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); | return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); | ||||
} | } | ||||
bool CNetAddr::IsLocal() const { | bool CNetAddr::IsLocal() const { | ||||
// IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8) | // IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8) | ||||
if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) { | if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) { | ||||
return true; | return true; | ||||
} | } | ||||
// IPv6 loopback (::1/128) | // IPv6 loopback (::1/128) | ||||
static const uint8_t pchLocal[16] = {0, 0, 0, 0, 0, 0, 0, 0, | static const uint8_t pchLocal[16] = {0, 0, 0, 0, 0, 0, 0, 0, | ||||
0, 0, 0, 0, 0, 0, 0, 1}; | 0, 0, 0, 0, 0, 0, 0, 1}; | ||||
if (memcmp(ip, pchLocal, 16) == 0) { | if (memcmp(ip, pchLocal, 16) == 0) { | ||||
return true; | return true; | ||||
} | } | ||||
return false; | 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 { | bool CNetAddr::IsValid() const { | ||||
// Cleanup 3-byte shifted addresses caused by garbage in size field of addr | // Cleanup 3-byte shifted addresses caused by garbage in size field of addr | ||||
// messages from versions before 0.2.9 checksum. | // messages from versions before 0.2.9 checksum. | ||||
// Two consecutive addr messages look like this: | // Two consecutive addr messages look like this: | ||||
// header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 | // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 | ||||
// addr26 addr26... so if the first length field is garbled, it reads the | // addr26 addr26... so if the first length field is garbled, it reads the | ||||
// second batch of addr misaligned by 3 bytes. | // second batch of addr misaligned by 3 bytes. | ||||
if (memcmp(ip, pchIPv4 + 3, sizeof(pchIPv4) - 3) == 0) { | if (memcmp(ip, pchIPv4 + 3, sizeof(pchIPv4) - 3) == 0) { | ||||
Show All 27 Lines | if (IsIPv4()) { | ||||
if (memcmp(ip + 12, &ipNone, 4) == 0) { | if (memcmp(ip + 12, &ipNone, 4) == 0) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | 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 { | bool CNetAddr::IsRoutable() const { | ||||
return IsValid() && | return IsValid() && | ||||
!(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || | !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || | ||||
IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || | IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || | ||||
IsRFC4843() || IsRFC7343() || IsLocal() || IsInternal()); | 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 { | bool CNetAddr::IsInternal() const { | ||||
return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0; | return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0; | ||||
} | } | ||||
enum Network CNetAddr::GetNetwork() const { | enum Network CNetAddr::GetNetwork() const { | ||||
if (IsInternal()) { | if (IsInternal()) { | ||||
return NET_INTERNAL; | return NET_INTERNAL; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
bool operator==(const CNetAddr &a, const CNetAddr &b) { | bool operator==(const CNetAddr &a, const CNetAddr &b) { | ||||
return (memcmp(a.ip, b.ip, 16) == 0); | return (memcmp(a.ip, b.ip, 16) == 0); | ||||
} | } | ||||
bool operator<(const CNetAddr &a, const CNetAddr &b) { | bool operator<(const CNetAddr &a, const CNetAddr &b) { | ||||
return (memcmp(a.ip, b.ip, 16) < 0); | return (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 { | bool CNetAddr::GetInAddr(struct in_addr *pipv4Addr) const { | ||||
if (!IsIPv4()) { | if (!IsIPv4()) { | ||||
return false; | return false; | ||||
} | } | ||||
memcpy(pipv4Addr, ip + 12, 4); | memcpy(pipv4Addr, ip + 12, 4); | ||||
return true; | 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 { | bool CNetAddr::GetIn6Addr(struct in6_addr *pipv6Addr) const { | ||||
if (!IsIPv6()) { | if (!IsIPv6()) { | ||||
return false; | return false; | ||||
} | } | ||||
memcpy(pipv6Addr, ip, 16); | memcpy(pipv6Addr, ip, 16); | ||||
return true; | return true; | ||||
} | } | ||||
// get canonical identifier of an address' group no two connections will be | /** | ||||
// attempted to addresses with the same group | * 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<uint8_t> CNetAddr::GetGroup() const { | std::vector<uint8_t> CNetAddr::GetGroup() const { | ||||
std::vector<uint8_t> vchRet; | std::vector<uint8_t> vchRet; | ||||
int nClass = NET_IPV6; | int nClass = NET_IPV6; | ||||
int nStartByte = 0; | int nStartByte = 0; | ||||
int nBits = 16; | int nBits = 16; | ||||
// all local addresses belong to the same group | // all local addresses belong to the same group | ||||
if (IsLocal()) { | if (IsLocal()) { | ||||
Show All 35 Lines | if (IsInternal()) { | ||||
// for he.net, use /36 groups | // for he.net, use /36 groups | ||||
nBits = 36; | nBits = 36; | ||||
} else { | } else { | ||||
// for the rest of the IPv6 network, use /32 groups | // for the rest of the IPv6 network, use /32 groups | ||||
nBits = 32; | nBits = 32; | ||||
} | } | ||||
vchRet.push_back(nClass); | vchRet.push_back(nClass); | ||||
// push our ip onto vchRet byte by byte... | |||||
while (nBits >= 8) { | while (nBits >= 8) { | ||||
vchRet.push_back(GetByte(15 - nStartByte)); | vchRet.push_back(GetByte(15 - nStartByte)); | ||||
nStartByte++; | nStartByte++; | ||||
nBits -= 8; | nBits -= 8; | ||||
} | } | ||||
// ...for the last byte, push nBits and for the rest of the byte push 1's | |||||
if (nBits > 0) { | if (nBits > 0) { | ||||
vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1)); | vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1)); | ||||
} | } | ||||
return vchRet; | return vchRet; | ||||
} | } | ||||
uint64_t CNetAddr::GetHash() const { | uint64_t CNetAddr::GetHash() const { | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
bool operator<(const CService &a, const CService &b) { | bool operator<(const CService &a, const CService &b) { | ||||
return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || | return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || | ||||
(static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && | (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && | ||||
a.port < b.port); | 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 { | bool CService::GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const { | ||||
if (IsIPv4()) { | if (IsIPv4()) { | ||||
if (*addrlen < (socklen_t)sizeof(struct sockaddr_in)) { | if (*addrlen < (socklen_t)sizeof(struct sockaddr_in)) { | ||||
return false; | return false; | ||||
} | } | ||||
*addrlen = sizeof(struct sockaddr_in); | *addrlen = sizeof(struct sockaddr_in); | ||||
struct sockaddr_in *paddrin = | struct sockaddr_in *paddrin = | ||||
reinterpret_cast<struct sockaddr_in *>(paddr); | reinterpret_cast<struct sockaddr_in *>(paddr); | ||||
Show All 19 Lines | if (IsIPv6()) { | ||||
paddrin6->sin6_scope_id = scopeId; | paddrin6->sin6_scope_id = scopeId; | ||||
paddrin6->sin6_family = AF_INET6; | paddrin6->sin6_family = AF_INET6; | ||||
paddrin6->sin6_port = htons(port); | paddrin6->sin6_port = htons(port); | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
/** | |||||
* @returns An identifier unique to this service's address and port number. | |||||
*/ | |||||
std::vector<uint8_t> CService::GetKey() const { | std::vector<uint8_t> CService::GetKey() const { | ||||
std::vector<uint8_t> vKey; | std::vector<uint8_t> vKey; | ||||
vKey.resize(18); | vKey.resize(18); | ||||
memcpy(vKey.data(), ip, 16); | memcpy(vKey.data(), ip, 16); | ||||
// most significant byte of our port | |||||
vKey[16] = port / 0x100; | vKey[16] = port / 0x100; | ||||
// least significant byte of our port | |||||
vKey[17] = port & 0x0FF; | vKey[17] = port & 0x0FF; | ||||
return vKey; | return vKey; | ||||
} | } | ||||
std::string CService::ToStringPort() const { | std::string CService::ToStringPort() const { | ||||
return strprintf("%u", port); | return strprintf("%u", port); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | CSubNet::CSubNet(const CNetAddr &addr, const CNetAddr &mask) { | ||||
} | } | ||||
} | } | ||||
CSubNet::CSubNet(const CNetAddr &addr) : valid(addr.IsValid()) { | CSubNet::CSubNet(const CNetAddr &addr) : valid(addr.IsValid()) { | ||||
memset(netmask, 255, sizeof(netmask)); | memset(netmask, 255, sizeof(netmask)); | ||||
network = addr; | 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 { | bool CSubNet::Match(const CNetAddr &addr) const { | ||||
if (!valid || !addr.IsValid()) { | if (!valid || !addr.IsValid()) { | ||||
return false; | return false; | ||||
} | } | ||||
for (int x = 0; x < 16; ++x) { | for (int x = 0; x < 16; ++x) { | ||||
if ((addr.ip[x] & netmask[x]) != network.ip[x]) { | if ((addr.ip[x] & netmask[x]) != network.ip[x]) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | 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) { | static inline int NetmaskBits(uint8_t x) { | ||||
switch (x) { | switch (x) { | ||||
case 0x00: | case 0x00: | ||||
return 0; | return 0; | ||||
case 0x80: | case 0x80: | ||||
return 1; | return 1; | ||||
case 0xc0: | case 0xc0: | ||||
return 2; | return 2; | ||||
▲ Show 20 Lines • Show All 74 Lines • Show Last 20 Lines |