Skip to main content

zebra_network/protocol/external/addr/
v1.rs

1//! Zcash `addr` (v1) message node address serialization.
2//!
3//! The [`AddrV1`] format serializes all IP addresses as IPv6 addresses.
4//! IPv4 addresses are converted to an [IPv4-mapped IPv6 address] before serialization.
5//!
6//! [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
7
8use std::{
9    io::{Read, Write},
10    net::{IpAddr, Ipv6Addr, SocketAddrV6},
11};
12
13use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
14
15use zebra_chain::serialization::{
16    DateTime32, SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashDeserializeInto,
17    ZcashSerialize,
18};
19
20use crate::{meta_addr::MetaAddr, protocol::external::types::PeerServices, PeerSocketAddr};
21
22use super::canonical_peer_addr;
23
24#[cfg(any(test, feature = "proptest-impl"))]
25use proptest_derive::Arbitrary;
26
27#[cfg(any(test, feature = "proptest-impl"))]
28use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
29
30/// The first format used for Bitcoin node addresses.
31/// Contains a node address, its advertised services, and last-seen time.
32/// This struct is serialized and deserialized into `addr` (v1) messages.
33///
34/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#Network_address)
35#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
36#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
37pub(in super::super) struct AddrV1 {
38    /// The unverified "last seen time" gossiped by the remote peer that sent us
39    /// this address.
40    ///
41    /// See the [`MetaAddr::last_seen`] method for details.
42    untrusted_last_seen: DateTime32,
43
44    /// The unverified services for the peer at `addr`.
45    ///
46    /// These services were advertised by the peer at `addr`,
47    /// then gossiped via another peer.
48    ///
49    /// ## Security
50    ///
51    /// `untrusted_services` on gossiped peers may be invalid due to outdated
52    /// records, older peer versions, or buggy or malicious peers.
53    untrusted_services: PeerServices,
54
55    /// The peer's canonical socket address.
56    /// IPv4 addresses are serialized as an [IPv4-mapped IPv6 address].
57    ///
58    /// [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
59    #[cfg_attr(
60        any(test, feature = "proptest-impl"),
61        proptest(strategy = "canonical_peer_addr_strategy()")
62    )]
63    addr: PeerSocketAddr,
64}
65
66impl From<MetaAddr> for AddrV1 {
67    fn from(meta_addr: MetaAddr) -> Self {
68        let addr = canonical_peer_addr(meta_addr.addr);
69
70        let untrusted_services = meta_addr.services.expect(
71            "unexpected MetaAddr with missing peer services: \
72             MetaAddrs should be sanitized before serialization",
73        );
74        let untrusted_last_seen = meta_addr.last_seen().expect(
75            "unexpected MetaAddr with missing last seen time: \
76             MetaAddrs should be sanitized before serialization",
77        );
78
79        AddrV1 {
80            untrusted_last_seen,
81            untrusted_services,
82            addr,
83        }
84    }
85}
86
87impl From<AddrV1> for MetaAddr {
88    fn from(addr: AddrV1) -> Self {
89        MetaAddr::new_gossiped_meta_addr(
90            addr.addr,
91            addr.untrusted_services,
92            addr.untrusted_last_seen,
93        )
94    }
95}
96
97impl ZcashSerialize for AddrV1 {
98    fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
99        self.untrusted_last_seen.zcash_serialize(&mut writer)?;
100        writer.write_u64::<LittleEndian>(self.untrusted_services.bits())?;
101
102        let ipv6_addr = ipv6_mapped_ip_addr(self.addr.ip());
103        ipv6_addr.zcash_serialize(&mut writer)?;
104        writer.write_u16::<BigEndian>(self.addr.port())?;
105
106        Ok(())
107    }
108}
109
110impl ZcashDeserialize for AddrV1 {
111    fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
112        let untrusted_last_seen = (&mut reader).zcash_deserialize_into()?;
113        let untrusted_services =
114            PeerServices::from_bits_truncate(reader.read_u64::<LittleEndian>()?);
115
116        let ipv6_addr = (&mut reader).zcash_deserialize_into()?;
117        let port = reader.read_u16::<BigEndian>()?;
118
119        // `0` is the default unspecified value for these fields.
120        let ipv6_addr = SocketAddrV6::new(ipv6_addr, port, 0, 0);
121
122        Ok(AddrV1 {
123            addr: canonical_peer_addr(ipv6_addr),
124            untrusted_services,
125            untrusted_last_seen,
126        })
127    }
128}
129
130impl TrustedPreallocate for AddrV1 {
131    fn max_allocation() -> u64 {
132        // The protocol caps addr messages at 1,000 entries.
133        // <https://zips.z.cash/zip-0155#specification>
134        //
135        // Previously this was derived from MAX_PROTOCOL_MESSAGE_LEN / ADDR_V1_SIZE = 69,904,
136        // which allowed a remote peer to force a multi-megabyte allocation before the cap
137        // was checked. See GHSA-xr93-pcq3-pxf8.
138        crate::constants::MAX_ADDRS_IN_MESSAGE as u64
139    }
140}
141
142/// Transform an `IpAddr` into an IPv6-mapped IPv4 addresses.
143///
144/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
145///
146/// [`canonical_ip_addr`]: super::canonical::canonical_ip_addr
147pub(in super::super) fn ipv6_mapped_ip_addr(ip_addr: IpAddr) -> Ipv6Addr {
148    use IpAddr::*;
149
150    match ip_addr {
151        V4(v4_addr) => v4_addr.to_ipv6_mapped(),
152        V6(v6_addr) => v6_addr,
153    }
154}