aboutsummaryrefslogtreecommitdiff
path: root/src/netaddress.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/netaddress.h')
-rw-r--r--src/netaddress.h357
1 files changed, 331 insertions, 26 deletions
diff --git a/src/netaddress.h b/src/netaddress.h
index 0365907d44..f35b01d202 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -9,14 +9,29 @@
#include <config/bitcoin-config.h>
#endif
+#include <attributes.h>
#include <compat.h>
+#include <prevector.h>
#include <serialize.h>
+#include <tinyformat.h>
+#include <util/strencodings.h>
+#include <util/string.h>
+#include <array>
#include <cstdint>
+#include <ios>
#include <string>
#include <vector>
/**
+ * A flag that is ORed into the protocol version to designate that addresses
+ * should be serialized in (unserialized from) v2 format (BIP155).
+ * Make sure that this does not collide with any of the values in `version.h`
+ * or with `SERIALIZE_TRANSACTION_NO_WITNESS`.
+ */
+static const int ADDRV2_FORMAT = 0x20000000;
+
+/**
* 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`.
@@ -36,19 +51,68 @@ enum Network
/// IPv6
NET_IPV6,
- /// TORv2
+ /// TOR (v2 or v3)
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.
+ /// I2P
+ NET_I2P,
+
+ /// CJDNS
+ NET_CJDNS,
+
+ /// A set of addresses that represent the hash of a string or FQDN. We use
+ /// them in CAddrMan to keep track of which DNS seeds were used.
NET_INTERNAL,
/// Dummy value to indicate the number of NET_* constants.
NET_MAX,
};
+/// Prefix of an IPv6 address when it contains an embedded IPv4 address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
+};
+
+/// Prefix of an IPv6 address when it contains an embedded TORv2 address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
+/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{
+ 0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43
+};
+
+/// Prefix of an IPv6 address when it contains an embedded "internal" address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+/// The prefix comes from 0xFD + SHA256("bitcoin")[0:5].
+/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
+/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{
+ 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
+};
+
+/// Size of IPv4 address (in bytes).
+static constexpr size_t ADDR_IPV4_SIZE = 4;
+
+/// Size of IPv6 address (in bytes).
+static constexpr size_t ADDR_IPV6_SIZE = 16;
+
+/// Size of TORv2 address (in bytes).
+static constexpr size_t ADDR_TORV2_SIZE = 10;
+
+/// Size of TORv3 address (in bytes). This is the length of just the address
+/// as used in BIP155, without the checksum and the version byte.
+static constexpr size_t ADDR_TORV3_SIZE = 32;
+
+/// Size of I2P address (in bytes).
+static constexpr size_t ADDR_I2P_SIZE = 32;
+
+/// Size of CJDNS address (in bytes).
+static constexpr size_t ADDR_CJDNS_SIZE = 16;
+
+/// Size of "internal" (NET_INTERNAL) address (in bytes).
+static constexpr size_t ADDR_INTERNAL_SIZE = 10;
+
/**
* Network address.
*/
@@ -56,12 +120,21 @@ class CNetAddr
{
protected:
/**
+ * Raw representation of the network address.
+ * In network byte order (big endian) for IPv4 and IPv6.
+ */
+ prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0};
+
+ /**
* Network to which this address belongs.
*/
Network m_net{NET_IPV6};
- unsigned char ip[16]; // in network byte order
- uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
+ /**
+ * Scope id if scoped/link-local IPV6 address.
+ * See https://tools.ietf.org/html/rfc4007
+ */
+ uint32_t m_scope_id{0};
public:
CNetAddr();
@@ -74,13 +147,7 @@ class CNetAddr
* (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);
+ void SetLegacyIPv6(Span<const uint8_t> ipv6);
bool SetInternal(const std::string& name);
@@ -104,17 +171,24 @@ class CNetAddr
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
bool IsTor() const;
+ bool IsI2P() const;
+ bool IsCJDNS() const;
bool IsLocal() const;
bool IsRoutable() const;
bool IsInternal() const;
bool IsValid() const;
+
+ /**
+ * Check if the current object can be serialized in pre-ADDRv2/BIP155 format.
+ */
+ bool IsAddrV1Compatible() 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;
+ Network 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;
@@ -127,7 +201,7 @@ class CNetAddr
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
- std::vector<unsigned char> GetAddrBytes() const { return {std::begin(ip), std::end(ip)}; }
+ std::vector<unsigned char> GetAddrBytes() const;
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
@@ -138,12 +212,24 @@ class CNetAddr
friend bool operator<(const CNetAddr& a, const CNetAddr& b);
/**
+ * Whether this address should be relayed to other peers even if we can't reach it ourselves.
+ */
+ bool IsRelayable() const
+ {
+ return IsIPv4() || IsIPv6() || IsTor();
+ }
+
+ /**
* Serialize to a stream.
*/
template <typename Stream>
void Serialize(Stream& s) const
{
- s << ip;
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ SerializeV2Stream(s);
+ } else {
+ SerializeV1Stream(s);
+ }
}
/**
@@ -152,14 +238,215 @@ class CNetAddr
template <typename Stream>
void Unserialize(Stream& s)
{
- unsigned char ip_temp[sizeof(ip)];
- s >> ip_temp;
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ UnserializeV2Stream(s);
+ } else {
+ UnserializeV1Stream(s);
+ }
+ }
+
+ friend class CSubNet;
+
+ private:
+ /**
+ * BIP155 network ids recognized by this software.
+ */
+ enum BIP155Network : uint8_t {
+ IPV4 = 1,
+ IPV6 = 2,
+ TORV2 = 3,
+ TORV3 = 4,
+ I2P = 5,
+ CJDNS = 6,
+ };
+
+ /**
+ * Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
+ */
+ static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
+
+ /**
+ * Maximum size of an address as defined in BIP155 (in bytes).
+ * This is only the size of the address, not the entire CNetAddr object
+ * when serialized.
+ */
+ static constexpr size_t MAX_ADDRV2_SIZE = 512;
+
+ /**
+ * Get the BIP155 network id of this address.
+ * Must not be called for IsInternal() objects.
+ * @returns BIP155 network id
+ */
+ BIP155Network GetBIP155Network() const;
+
+ /**
+ * Set `m_net` from the provided BIP155 network id and size after validation.
+ * @retval true the network was recognized, is valid and `m_net` was set
+ * @retval false not recognised (from future?) and should be silently ignored
+ * @throws std::ios_base::failure if the network is one of the BIP155 founding
+ * networks (id 1..6) with wrong address size.
+ */
+ bool SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size);
+
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to an array.
+ */
+ void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
+ {
+ size_t prefix_size;
+
+ switch (m_net) {
+ case NET_IPV6:
+ assert(m_addr.size() == sizeof(arr));
+ memcpy(arr, m_addr.data(), m_addr.size());
+ return;
+ case NET_IPV4:
+ prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_ONION:
+ if (m_addr.size() == ADDR_TORV3_SIZE) {
+ break;
+ }
+ prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_INTERNAL:
+ prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_I2P:
+ break;
+ case NET_CJDNS:
+ break;
+ case NET_UNROUTABLE:
+ case NET_MAX:
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ // Serialize TORv3, I2P and CJDNS as all-zeros.
+ memset(arr, 0x0, V1_SERIALIZATION_SIZE);
+ }
+
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to a stream.
+ */
+ template <typename Stream>
+ void SerializeV1Stream(Stream& s) const
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+
+ SerializeV1Array(serialized);
+
+ s << serialized;
+ }
+
+ /**
+ * Serialize as ADDRv2 / BIP155.
+ */
+ template <typename Stream>
+ void SerializeV2Stream(Stream& s) const
+ {
+ if (IsInternal()) {
+ // Serialize NET_INTERNAL as embedded in IPv6. We need to
+ // serialize such addresses from addrman.
+ s << static_cast<uint8_t>(BIP155Network::IPV6);
+ s << COMPACTSIZE(ADDR_IPV6_SIZE);
+ SerializeV1Stream(s);
+ return;
+ }
+
+ s << static_cast<uint8_t>(GetBIP155Network());
+ s << m_addr;
+ }
+
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from an array.
+ */
+ void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
+ {
// 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);
+ SetLegacyIPv6(arr);
}
- friend class CSubNet;
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from a stream.
+ */
+ template <typename Stream>
+ void UnserializeV1Stream(Stream& s)
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+
+ s >> serialized;
+
+ UnserializeV1Array(serialized);
+ }
+
+ /**
+ * Unserialize from a ADDRv2 / BIP155 format.
+ */
+ template <typename Stream>
+ void UnserializeV2Stream(Stream& s)
+ {
+ uint8_t bip155_net;
+ s >> bip155_net;
+
+ size_t address_size;
+ s >> COMPACTSIZE(address_size);
+
+ if (address_size > MAX_ADDRV2_SIZE) {
+ throw std::ios_base::failure(strprintf(
+ "Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE));
+ }
+
+ m_scope_id = 0;
+
+ if (SetNetFromBIP155Network(bip155_net, address_size)) {
+ m_addr.resize(address_size);
+ s >> MakeSpan(m_addr);
+
+ if (m_net != NET_IPV6) {
+ return;
+ }
+
+ // Do some special checks on IPv6 addresses.
+
+ // Recognize NET_INTERNAL embedded in IPv6, such addresses are not
+ // gossiped but could be coming from addrman, when unserializing from
+ // disk.
+ if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
+ m_net = NET_INTERNAL;
+ memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
+ ADDR_INTERNAL_SIZE);
+ m_addr.resize(ADDR_INTERNAL_SIZE);
+ return;
+ }
+
+ if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) &&
+ !HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) {
+ return;
+ }
+
+ // IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
+ // encoding). Unserialize as !IsValid(), thus ignoring them.
+ } else {
+ // If we receive an unknown BIP155 network id (from the future?) then
+ // ignore the address - unserialize as !IsValid().
+ s.ignore(address_size);
+ }
+
+ // Mimic a default-constructed CNetAddr object which is !IsValid() and thus
+ // will not be gossiped, but continue reading next addresses from the stream.
+ m_net = NET_IPV6;
+ m_addr.assign(ADDR_IPV6_SIZE, 0x0);
+ }
};
class CSubNet
@@ -172,13 +459,15 @@ class CSubNet
/// Is this value valid? (only used to signal parse errors)
bool valid;
+ bool SanityCheck() const;
+
public:
CSubNet();
- CSubNet(const CNetAddr &addr, int32_t mask);
- CSubNet(const CNetAddr &addr, const CNetAddr &mask);
+ CSubNet(const CNetAddr& addr, uint8_t mask);
+ CSubNet(const CNetAddr& addr, const CNetAddr& mask);
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
- explicit CSubNet(const CNetAddr &addr);
+ explicit CSubNet(const CNetAddr& addr);
bool Match(const CNetAddr &addr) const;
@@ -189,7 +478,23 @@ class CSubNet
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); }
+ SERIALIZE_METHODS(CSubNet, obj)
+ {
+ READWRITE(obj.network);
+ if (obj.network.IsIPv4()) {
+ // Before commit 102867c587f5f7954232fb8ed8e85cda78bb4d32, CSubNet used the last 4 bytes of netmask
+ // to store the relevant bytes for an IPv4 mask. For compatiblity reasons, keep doing so in
+ // serialized form.
+ unsigned char dummy[12] = {0};
+ READWRITE(dummy);
+ READWRITE(MakeSpan(obj.netmask).first(4));
+ } else {
+ READWRITE(obj.netmask);
+ }
+ READWRITE(obj.valid);
+ // Mark invalid if the result doesn't pass sanity checking.
+ SER_READ(obj, if (obj.valid) obj.valid = obj.SanityCheck());
+ }
};
/** A combination of a network address (CNetAddr) and a (TCP) port */