aboutsummaryrefslogtreecommitdiff
path: root/src/netaddress.h
diff options
context:
space:
mode:
authorVasil Dimov <vd@FreeBSD.org>2020-05-19 15:01:50 +0200
committerVasil Dimov <vd@FreeBSD.org>2020-09-17 22:17:17 +0200
commite0d73573a37bf4b519f6f61e5678572d48a64517 (patch)
tree62d7661d4afc81dc8b3547b4d57a31f49fd9cc8f /src/netaddress.h
parentfe42411b4b07b99c591855f5f00ad45dfeec8e30 (diff)
downloadbitcoin-e0d73573a37bf4b519f6f61e5678572d48a64517.tar.xz
net: CNetAddr: add support to (un)serialize as ADDRv2
Co-authored-by: Carl Dong <contact@carldong.me>
Diffstat (limited to 'src/netaddress.h')
-rw-r--r--src/netaddress.h134
1 files changed, 132 insertions, 2 deletions
diff --git a/src/netaddress.h b/src/netaddress.h
index d00f5a6f55..08857ae729 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -13,13 +13,25 @@
#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`.
@@ -177,7 +189,11 @@ class CNetAddr
template <typename Stream>
void Serialize(Stream& s) const
{
- SerializeV1Stream(s);
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ SerializeV2Stream(s);
+ } else {
+ SerializeV1Stream(s);
+ }
}
/**
@@ -186,18 +202,54 @@ class CNetAddr
template <typename Stream>
void Unserialize(Stream& s)
{
- UnserializeV1Stream(s);
+ 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,
+ };
+
+ /**
* 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 recognized by this software (id 1..3) and has wrong address size.
+ */
+ bool SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size);
+
+ /**
* Serialize in pre-ADDRv2/BIP155 format to an array.
* Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
*/
@@ -251,6 +303,25 @@ class CNetAddr
}
/**
+ * 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])
@@ -272,6 +343,65 @@ class CNetAddr
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));
+ }
+
+ scopeId = 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