Skip to content

Commit 102867c

Browse files
vasilddongcarl
andcommitted
net: change CNetAddr::ip to have flexible size
Before this change `CNetAddr::ip` was a fixed-size array of 16 bytes, not being able to store larger addresses (e.g. TORv3) and encoded smaller ones as 16-byte IPv6 addresses. Change its type to `prevector`, so that it can hold larger addresses and do not disguise non-IPv6 addresses as IPv6. So the IPv4 address `1.2.3.4` is now encoded as `01020304` instead of `00000000000000000000FFFF01020304`. Rename `CNetAddr::ip` to `CNetAddr::m_addr` because it is not an "IP" or "IP address" (TOR addresses are not IP addresses). In order to preserve backward compatibility with serialization (where e.g. `1.2.3.4` is serialized as `00000000000000000000FFFF01020304`) introduce `CNetAddr` dedicated legacy serialize/unserialize methods. Adjust `CSubNet` accordingly. Still use `CSubNet::netmask[]` of fixed 16 bytes, but use the first 4 for IPv4 (not the last 4). Only allow subnetting for IPv4 and IPv6. Co-authored-by: Carl Dong <[email protected]>
1 parent 1ea57ad commit 102867c

File tree

10 files changed

+450
-215
lines changed

10 files changed

+450
-215
lines changed

src/netaddress.cpp

Lines changed: 198 additions & 183 deletions
Large diffs are not rendered by default.

src/netaddress.h

Lines changed: 134 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
#include <config/bitcoin-config.h>
1010
#endif
1111

12+
#include <attributes.h>
1213
#include <compat.h>
14+
#include <prevector.h>
1315
#include <serialize.h>
1416

17+
#include <array>
1518
#include <cstdint>
1619
#include <string>
1720
#include <vector>
@@ -39,28 +42,66 @@ enum Network
3942
/// TORv2
4043
NET_ONION,
4144

42-
/// A set of dummy addresses that map a name to an IPv6 address. These
43-
/// addresses belong to RFC4193's fc00::/7 subnet (unique-local addresses).
44-
/// We use them to map a string or FQDN to an IPv6 address in CAddrMan to
45-
/// keep track of which DNS seeds were used.
45+
/// A set of addresses that represent the hash of a string or FQDN. We use
46+
/// them in CAddrMan to keep track of which DNS seeds were used.
4647
NET_INTERNAL,
4748

4849
/// Dummy value to indicate the number of NET_* constants.
4950
NET_MAX,
5051
};
5152

53+
/// Prefix of an IPv6 address when it contains an embedded IPv4 address.
54+
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
55+
static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{
56+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
57+
};
58+
59+
/// Prefix of an IPv6 address when it contains an embedded TORv2 address.
60+
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
61+
/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
62+
/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
63+
static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{
64+
0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43
65+
};
66+
67+
/// Prefix of an IPv6 address when it contains an embedded "internal" address.
68+
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
69+
/// The prefix comes from 0xFD + SHA256("bitcoin")[0:5].
70+
/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
71+
/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
72+
static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{
73+
0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
74+
};
75+
76+
/// Size of IPv4 address (in bytes).
77+
static constexpr size_t ADDR_IPV4_SIZE = 4;
78+
79+
/// Size of IPv6 address (in bytes).
80+
static constexpr size_t ADDR_IPV6_SIZE = 16;
81+
82+
/// Size of TORv2 address (in bytes).
83+
static constexpr size_t ADDR_TORV2_SIZE = 10;
84+
85+
/// Size of "internal" (NET_INTERNAL) address (in bytes).
86+
static constexpr size_t ADDR_INTERNAL_SIZE = 10;
87+
5288
/**
5389
* Network address.
5490
*/
5591
class CNetAddr
5692
{
5793
protected:
94+
/**
95+
* Raw representation of the network address.
96+
* In network byte order (big endian) for IPv4 and IPv6.
97+
*/
98+
prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0};
99+
58100
/**
59101
* Network to which this address belongs.
60102
*/
61103
Network m_net{NET_IPV6};
62104

63-
unsigned char ip[16]; // in network byte order
64105
uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
65106

66107
public:
@@ -74,13 +115,7 @@ class CNetAddr
74115
* (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
75116
* `addr` encoding.
76117
*/
77-
void SetLegacyIPv6(const uint8_t ipv6[16]);
78-
79-
/**
80-
* Set raw IPv4 or IPv6 address (in network byte order)
81-
* @note Only NET_IPV4 and NET_IPV6 are allowed for network.
82-
*/
83-
void SetRaw(Network network, const uint8_t *data);
118+
void SetLegacyIPv6(Span<const uint8_t> ipv6);
84119

85120
bool SetInternal(const std::string& name);
86121

@@ -111,7 +146,6 @@ class CNetAddr
111146
enum Network GetNetwork() const;
112147
std::string ToString() const;
113148
std::string ToStringIP() const;
114-
unsigned int GetByte(int n) const;
115149
uint64_t GetHash() const;
116150
bool GetInAddr(struct in_addr* pipv4Addr) const;
117151
uint32_t GetNetClass() const;
@@ -127,7 +161,7 @@ class CNetAddr
127161
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
128162

129163
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
130-
std::vector<unsigned char> GetAddrBytes() const { return {std::begin(ip), std::end(ip)}; }
164+
std::vector<unsigned char> GetAddrBytes() const;
131165
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
132166

133167
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
@@ -143,7 +177,7 @@ class CNetAddr
143177
template <typename Stream>
144178
void Serialize(Stream& s) const
145179
{
146-
s << ip;
180+
SerializeV1Stream(s);
147181
}
148182

149183
/**
@@ -152,14 +186,92 @@ class CNetAddr
152186
template <typename Stream>
153187
void Unserialize(Stream& s)
154188
{
155-
unsigned char ip_temp[sizeof(ip)];
156-
s >> ip_temp;
189+
UnserializeV1Stream(s);
190+
}
191+
192+
friend class CSubNet;
193+
194+
private:
195+
/**
196+
* Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
197+
*/
198+
static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
199+
200+
/**
201+
* Serialize in pre-ADDRv2/BIP155 format to an array.
202+
* Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
203+
*/
204+
void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
205+
{
206+
size_t prefix_size;
207+
208+
switch (m_net) {
209+
case NET_IPV6:
210+
assert(m_addr.size() == sizeof(arr));
211+
memcpy(arr, m_addr.data(), m_addr.size());
212+
return;
213+
case NET_IPV4:
214+
prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
215+
assert(prefix_size + m_addr.size() == sizeof(arr));
216+
memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
217+
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
218+
return;
219+
case NET_ONION:
220+
prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
221+
assert(prefix_size + m_addr.size() == sizeof(arr));
222+
memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
223+
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
224+
return;
225+
case NET_INTERNAL:
226+
prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
227+
assert(prefix_size + m_addr.size() == sizeof(arr));
228+
memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
229+
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
230+
return;
231+
case NET_UNROUTABLE:
232+
case NET_MAX:
233+
assert(false);
234+
} // no default case, so the compiler can warn about missing cases
235+
236+
assert(false);
237+
}
238+
239+
/**
240+
* Serialize in pre-ADDRv2/BIP155 format to a stream.
241+
* Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
242+
*/
243+
template <typename Stream>
244+
void SerializeV1Stream(Stream& s) const
245+
{
246+
uint8_t serialized[V1_SERIALIZATION_SIZE];
247+
248+
SerializeV1Array(serialized);
249+
250+
s << serialized;
251+
}
252+
253+
/**
254+
* Unserialize from a pre-ADDRv2/BIP155 format from an array.
255+
*/
256+
void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
257+
{
157258
// Use SetLegacyIPv6() so that m_net is set correctly. For example
158259
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
159-
SetLegacyIPv6(ip_temp);
260+
SetLegacyIPv6(arr);
160261
}
161262

162-
friend class CSubNet;
263+
/**
264+
* Unserialize from a pre-ADDRv2/BIP155 format from a stream.
265+
*/
266+
template <typename Stream>
267+
void UnserializeV1Stream(Stream& s)
268+
{
269+
uint8_t serialized[V1_SERIALIZATION_SIZE];
270+
271+
s >> serialized;
272+
273+
UnserializeV1Array(serialized);
274+
}
163275
};
164276

165277
class CSubNet
@@ -174,11 +286,11 @@ class CSubNet
174286

175287
public:
176288
CSubNet();
177-
CSubNet(const CNetAddr &addr, int32_t mask);
178-
CSubNet(const CNetAddr &addr, const CNetAddr &mask);
289+
CSubNet(const CNetAddr& addr, uint8_t mask);
290+
CSubNet(const CNetAddr& addr, const CNetAddr& mask);
179291

180292
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
181-
explicit CSubNet(const CNetAddr &addr);
293+
explicit CSubNet(const CNetAddr& addr);
182294

183295
bool Match(const CNetAddr &addr) const;
184296

src/netbase.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <atomic>
1515
#include <cstdint>
16+
#include <limits>
1617

1718
#ifndef WIN32
1819
#include <fcntl.h>
@@ -838,8 +839,8 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
838839
if (slash != strSubnet.npos)
839840
{
840841
std::string strNetmask = strSubnet.substr(slash + 1);
841-
int32_t n;
842-
if (ParseInt32(strNetmask, &n)) {
842+
uint8_t n;
843+
if (ParseUInt8(strNetmask, &n)) {
843844
// If valid number, assume CIDR variable-length subnet masking
844845
ret = CSubNet(network, n);
845846
return ret.IsValid();

src/test/fuzz/asmap.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
3333
if (buffer.size() < 1 + 3 + 4) return;
3434
int asmap_size = 3 + (buffer[0] & 127);
3535
bool ipv6 = buffer[0] & 128;
36-
int addr_size = ipv6 ? 16 : 4;
36+
const size_t addr_size = ipv6 ? ADDR_IPV6_SIZE : ADDR_IPV4_SIZE;
3737
if (buffer.size() < size_t(1 + asmap_size + addr_size)) return;
3838
std::vector<bool> asmap = ipv6 ? IPV6_PREFIX_ASMAP : IPV4_PREFIX_ASMAP;
3939
asmap.reserve(asmap.size() + 8 * asmap_size);
@@ -43,7 +43,17 @@ void test_one_input(const std::vector<uint8_t>& buffer)
4343
}
4444
}
4545
if (!SanityCheckASMap(asmap)) return;
46+
47+
const uint8_t* addr_data = buffer.data() + 1 + asmap_size;
4648
CNetAddr net_addr;
47-
net_addr.SetRaw(ipv6 ? NET_IPV6 : NET_IPV4, buffer.data() + 1 + asmap_size);
49+
if (ipv6) {
50+
assert(addr_size == ADDR_IPV6_SIZE);
51+
net_addr.SetLegacyIPv6(Span<const uint8_t>(addr_data, addr_size));
52+
} else {
53+
assert(addr_size == ADDR_IPV4_SIZE);
54+
in_addr ipv4;
55+
memcpy(&ipv4, addr_data, addr_size);
56+
net_addr.SetIP(CNetAddr{ipv4});
57+
}
4858
(void)net_addr.GetMappedAS(asmap);
4959
}

src/test/fuzz/netaddress.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
1717
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
1818

1919
const CNetAddr net_addr = ConsumeNetAddr(fuzzed_data_provider);
20-
for (int i = 0; i < 15; ++i) {
21-
(void)net_addr.GetByte(i);
22-
}
2320
(void)net_addr.GetHash();
2421
(void)net_addr.GetNetClass();
2522
if (net_addr.GetNetwork() == Network::NET_IPV4) {
@@ -78,7 +75,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
7875
(void)net_addr.ToString();
7976
(void)net_addr.ToStringIP();
8077

81-
const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<int32_t>()};
78+
const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
8279
(void)sub_net.IsValid();
8380
(void)sub_net.ToString();
8481

src/test/fuzz/util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
257257

258258
CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
259259
{
260-
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()};
260+
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
261261
}
262262

263263
void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST)

0 commit comments

Comments
 (0)