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