zebra_network/protocol/external/addr/v2.rs
1//! Zcash `addrv2` message node address serialization.
2//!
3//! Zebra parses received IPv4 and IPv6 addresses in the [`AddrV2`] format.
4//! But it ignores all other address types.
5//!
6//! Zebra never sends `addrv2` messages, because peers still accept `addr` (v1) messages.
7
8use std::{
9 io::Read,
10 net::{IpAddr, SocketAddr},
11};
12
13use byteorder::{BigEndian, ReadBytesExt};
14use thiserror::Error;
15
16use zebra_chain::serialization::{
17 zcash_deserialize_bytes_external_count, CompactSize64, CompactSizeMessage, DateTime32,
18 SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashDeserializeInto,
19};
20
21use crate::{meta_addr::MetaAddr, protocol::external::types::PeerServices, PeerSocketAddr};
22
23use super::canonical_peer_addr;
24
25#[cfg(any(test, feature = "proptest-impl"))]
26use proptest_derive::Arbitrary;
27
28#[cfg(test)]
29use byteorder::WriteBytesExt;
30#[cfg(test)]
31use std::io::Write;
32#[cfg(test)]
33use zebra_chain::serialization::{zcash_serialize_bytes, ZcashSerialize};
34
35/// The maximum permitted size of the `addr` field in `addrv2` messages.
36///
37/// > Field addr has a variable length, with a maximum of 512 bytes (4096 bits).
38/// > Clients MUST reject messages with a longer addr field, irrespective of the network ID.
39///
40/// <https://zips.z.cash/zip-0155#specification>
41pub const MAX_ADDR_V2_ADDR_SIZE: usize = 512;
42
43/// The network ID of [`Ipv4Addr`]s in `addrv2` messages.
44///
45/// > 0x01 IPV4 4 IPv4 address (globally routed internet)
46///
47/// <https://zips.z.cash/zip-0155#specification>
48///
49/// [`Ipv4Addr`]: std::net::Ipv4Addr
50pub const ADDR_V2_IPV4_NETWORK_ID: u8 = 0x01;
51
52/// The size of [`Ipv4Addr`]s in `addrv2` messages.
53///
54/// <https://zips.z.cash/zip-0155#specification>
55///
56/// [`Ipv4Addr`]: std::net::Ipv4Addr
57pub const ADDR_V2_IPV4_ADDR_SIZE: usize = 4;
58
59/// The network ID of [`Ipv6Addr`]s in `addrv2` messages.
60///
61/// > 0x02 IPV6 16 IPv6 address (globally routed internet)
62///
63/// <https://zips.z.cash/zip-0155#specification>
64///
65/// [`Ipv6Addr`]: std::net::Ipv6Addr
66pub const ADDR_V2_IPV6_NETWORK_ID: u8 = 0x02;
67
68/// The size of [`Ipv6Addr`]s in `addrv2` messages.
69///
70/// <https://zips.z.cash/zip-0155#specification>
71///
72/// [`Ipv6Addr`]: std::net::Ipv6Addr
73pub const ADDR_V2_IPV6_ADDR_SIZE: usize = 16;
74
75/// The second format used for Bitcoin node addresses.
76/// Contains a node address, its advertised services, and last-seen time.
77/// This struct is serialized and deserialized into `addrv2` messages.
78///
79/// [ZIP 155](https://zips.z.cash/zip-0155#specification)
80#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
81#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
82pub(in super::super) enum AddrV2 {
83 /// An IPv4 or IPv6 node address, in `addrv2` format.
84 IpAddr {
85 /// The unverified "last seen time" gossiped by the remote peer that sent us
86 /// this address.
87 ///
88 /// See the [`MetaAddr::last_seen`] method for details.
89 untrusted_last_seen: DateTime32,
90
91 /// The unverified services for the peer at `ip_addr`:`port`.
92 ///
93 /// These services were advertised by the peer at that address,
94 /// then gossiped via another peer.
95 ///
96 /// ## Security
97 ///
98 /// `untrusted_services` on gossiped peers may be invalid due to outdated
99 /// records, older peer versions, or buggy or malicious peers.
100 untrusted_services: PeerServices,
101
102 /// The peer's canonical IP address and port.
103 ///
104 /// Unlike [`AddrV1`], this can be an IPv4 or IPv6 address.
105 ///
106 /// [`AddrV1`]: super::v1::AddrV1
107 addr: PeerSocketAddr,
108 },
109
110 /// A node address with an unsupported `networkID`, in `addrv2` format.
111 //
112 // TODO: when we add more address types, make sure their addresses aren't logged,
113 // in a similar way to `PeerSocketAddr`
114 Unsupported,
115}
116
117// Just serialize in the tests for now.
118//
119// We can't guarantee that peers support addrv2 until it activates,
120// and outdated peers are excluded from the network by a network upgrade.
121// (Likely NU5 on mainnet, and NU6 on testnet.)
122// https://zips.z.cash/zip-0155#deployment
123//
124// And Zebra doesn't use different codecs for different peer versions.
125#[cfg(test)]
126impl From<MetaAddr> for AddrV2 {
127 fn from(meta_addr: MetaAddr) -> Self {
128 let untrusted_services = meta_addr.services.expect(
129 "unexpected MetaAddr with missing peer services: \
130 MetaAddrs should be sanitized before serialization",
131 );
132 let untrusted_last_seen = meta_addr.last_seen().expect(
133 "unexpected MetaAddr with missing last seen time: \
134 MetaAddrs should be sanitized before serialization",
135 );
136
137 AddrV2::IpAddr {
138 untrusted_last_seen,
139 untrusted_services,
140 addr: canonical_peer_addr(meta_addr.addr()),
141 }
142 }
143}
144
145/// The error returned when converting `AddrV2::Unsupported` fails.
146#[derive(Error, Copy, Clone, Debug, Eq, PartialEq, Hash)]
147#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
148#[error("can not parse this addrv2 variant: unimplemented or unrecognised AddrV2 network ID")]
149pub struct UnsupportedAddrV2NetworkIdError;
150
151impl TryFrom<AddrV2> for MetaAddr {
152 type Error = UnsupportedAddrV2NetworkIdError;
153
154 fn try_from(addr: AddrV2) -> Result<MetaAddr, UnsupportedAddrV2NetworkIdError> {
155 if let AddrV2::IpAddr {
156 untrusted_last_seen,
157 untrusted_services,
158 addr,
159 } = addr
160 {
161 Ok(MetaAddr::new_gossiped_meta_addr(
162 addr,
163 untrusted_services,
164 untrusted_last_seen,
165 ))
166 } else {
167 Err(UnsupportedAddrV2NetworkIdError)
168 }
169 }
170}
171
172impl AddrV2 {
173 /// Deserialize `addr_bytes` as an IPv4 or IPv6 address, using the `addrv2` format.
174 /// Returns the corresponding [`IpAddr`].
175 ///
176 /// The returned IP version is chosen based on `IP_ADDR_SIZE`,
177 /// which should be [`ADDR_V2_IPV4_ADDR_SIZE`] or [`ADDR_V2_IPV6_ADDR_SIZE`].
178 #[allow(clippy::unwrap_in_result)]
179 fn ip_addr_from_bytes<const IP_ADDR_SIZE: usize>(
180 addr_bytes: Vec<u8>,
181 ) -> Result<IpAddr, SerializationError>
182 where
183 IpAddr: From<[u8; IP_ADDR_SIZE]>,
184 {
185 // > Clients MUST reject messages that contain addresses that have
186 // > a different length than specified in this table for a specific network ID,
187 // > as these are meaningless.
188 if addr_bytes.len() != IP_ADDR_SIZE {
189 let error_msg = if IP_ADDR_SIZE == ADDR_V2_IPV4_ADDR_SIZE {
190 "IP address field length did not match expected IPv4 address size in addrv2 message"
191 } else if IP_ADDR_SIZE == ADDR_V2_IPV6_ADDR_SIZE {
192 "IP address field length did not match expected IPv6 address size in addrv2 message"
193 } else {
194 unreachable!("unexpected IP address size when converting from bytes");
195 };
196
197 return Err(SerializationError::Parse(error_msg));
198 };
199
200 // > The IPV4 and IPV6 network IDs use addresses encoded in the usual way
201 // > for binary IPv4 and IPv6 addresses in network byte order (big endian).
202 let ip: [u8; IP_ADDR_SIZE] = addr_bytes.try_into().expect("just checked length");
203
204 Ok(IpAddr::from(ip))
205 }
206}
207
208// Just serialize in the tests for now.
209//
210// See the detailed note about ZIP-155 activation above.
211#[cfg(test)]
212impl ZcashSerialize for AddrV2 {
213 fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
214 if let AddrV2::IpAddr {
215 untrusted_last_seen,
216 untrusted_services,
217 addr,
218 } = self
219 {
220 // > uint32 Time that this node was last seen as connected to the network.
221 untrusted_last_seen.zcash_serialize(&mut writer)?;
222
223 // > Service bits. A CompactSize-encoded bit field that is 64 bits wide.
224 let untrusted_services: CompactSize64 = untrusted_services.bits().into();
225 untrusted_services.zcash_serialize(&mut writer)?;
226
227 match addr.ip() {
228 IpAddr::V4(ip) => {
229 // > Network identifier. An 8-bit value that specifies which network is addressed.
230 writer.write_u8(ADDR_V2_IPV4_NETWORK_ID)?;
231
232 // > The IPV4 and IPV6 network IDs use addresses encoded in the usual way
233 // > for binary IPv4 and IPv6 addresses in network byte order (big endian).
234 let ip: [u8; ADDR_V2_IPV4_ADDR_SIZE] = ip.octets();
235 // > CompactSize The length in bytes of addr.
236 // > uint8[sizeAddr] Network address. The interpretation depends on networkID.
237 zcash_serialize_bytes(&ip.to_vec(), &mut writer)?;
238
239 // > uint16 Network port. If not relevant for the network this MUST be 0.
240 writer.write_u16::<BigEndian>(addr.port())?;
241 }
242 IpAddr::V6(ip) => {
243 writer.write_u8(ADDR_V2_IPV6_NETWORK_ID)?;
244
245 let ip: [u8; ADDR_V2_IPV6_ADDR_SIZE] = ip.octets();
246 zcash_serialize_bytes(&ip.to_vec(), &mut writer)?;
247
248 writer.write_u16::<BigEndian>(addr.port())?;
249 }
250 }
251 } else {
252 unreachable!("unexpected AddrV2 variant: {:?}", self);
253 }
254
255 Ok(())
256 }
257}
258
259/// Deserialize an `addrv2` entry according to:
260/// <https://zips.z.cash/zip-0155#specification>
261///
262/// Unimplemented and unrecognised addresses are deserialized as [`AddrV2::Unsupported`].
263/// (Deserialization consumes the correct number of bytes for unsupported addresses.)
264impl ZcashDeserialize for AddrV2 {
265 fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
266 // > uint32 Time that this node was last seen as connected to the network.
267 let untrusted_last_seen = (&mut reader).zcash_deserialize_into()?;
268
269 // > Service bits. A CompactSize-encoded bit field that is 64 bits wide.
270 let untrusted_services: CompactSize64 = (&mut reader).zcash_deserialize_into()?;
271 let untrusted_services = PeerServices::from_bits_truncate(untrusted_services.into());
272
273 // > Network identifier. An 8-bit value that specifies which network is addressed.
274 //
275 // See the list of reserved network IDs in ZIP 155.
276 let network_id = reader.read_u8()?;
277
278 // > CompactSize The length in bytes of addr.
279 let addr_len: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
280 let addr_len: usize = addr_len.into();
281 if addr_len > MAX_ADDR_V2_ADDR_SIZE {
282 return Err(SerializationError::Parse(
283 "addr field longer than MAX_ADDR_V2_ADDR_SIZE in addrv2 message",
284 ));
285 }
286
287 // > uint8[sizeAddr] Network address. The interpretation depends on networkID.
288 let addr: Vec<u8> = zcash_deserialize_bytes_external_count(addr_len, &mut reader)?;
289
290 // > uint16 Network port. If not relevant for the network this MUST be 0.
291 let port = reader.read_u16::<BigEndian>()?;
292
293 let ip = if network_id == ADDR_V2_IPV4_NETWORK_ID {
294 AddrV2::ip_addr_from_bytes::<ADDR_V2_IPV4_ADDR_SIZE>(addr)?
295 } else if network_id == ADDR_V2_IPV6_NETWORK_ID {
296 AddrV2::ip_addr_from_bytes::<ADDR_V2_IPV6_ADDR_SIZE>(addr)?
297 } else {
298 // unimplemented or unrecognised network ID, just consume the bytes
299 //
300 // > Clients MUST NOT gossip addresses from unknown networks,
301 // > because they have no means to validate those addresses
302 // > and so can be tricked to gossip invalid addresses.
303
304 return Ok(AddrV2::Unsupported);
305 };
306
307 Ok(AddrV2::IpAddr {
308 untrusted_last_seen,
309 untrusted_services,
310 addr: canonical_peer_addr(SocketAddr::new(ip, port)),
311 })
312 }
313}
314
315impl TrustedPreallocate for AddrV2 {
316 fn max_allocation() -> u64 {
317 // The protocol caps addrv2 messages at 1,000 entries.
318 // <https://zips.z.cash/zip-0155#specification>
319 //
320 // Previously this was derived from MAX_PROTOCOL_MESSAGE_LEN / ADDR_V2_MIN_SIZE = 233,016,
321 // which allowed a remote peer to force a ~10.7 MiB heap allocation before the cap
322 // was checked. See GHSA-xr93-pcq3-pxf8.
323 crate::constants::MAX_ADDRS_IN_MESSAGE as u64
324 }
325}