diff --git a/radicle-node/src/service.rs b/radicle-node/src/service.rs index 22925291..06a287b1 100644 --- a/radicle-node/src/service.rs +++ b/radicle-node/src/service.rs @@ -828,6 +828,7 @@ where pub fn handle_announcement( &mut self, relayer: &NodeId, + relayer_addr: &Address, announcement: &Announcement, ) -> Result { if !announcement.verify() { @@ -1023,6 +1024,7 @@ where timestamp, addresses .iter() + .filter(|a| a.is_routable() || relayer_addr.is_local()) .map(|a| KnownAddress::new(a.clone(), address::Source::Peer)), ) { Ok(updated) => { @@ -1100,12 +1102,13 @@ where match (&mut peer.state, message) { // Process a peer announcement. - (session::State::Connected { .. }, Message::Announcement(ann)) => { + (session::State::Connected { addr, .. }, Message::Announcement(ann)) => { let relayer = peer.id; + let relayer_addr = addr.clone(); let announcer = ann.node; // Returning true here means that the message should be relayed. - if self.handle_announcement(&relayer, &ann)? { + if self.handle_announcement(&relayer, &relayer_addr, &ann)? { // Choose peers we should relay this message to. // 1. Don't relay to the peer who sent us this message. // 2. Don't relay to the peer who signed this announcement. diff --git a/radicle/src/node.rs b/radicle/src/node.rs index e20fa331..08c2554a 100644 --- a/radicle/src/node.rs +++ b/radicle/src/node.rs @@ -262,6 +262,24 @@ impl From for Result { #[wrapper_mut(DerefMut)] pub struct Address(#[serde(with = "crate::serde_ext::string")] NetAddr); +impl Address { + /// Check whether this address is local. + pub fn is_local(&self) -> bool { + match self.0.host { + HostName::Ip(ip) => address::is_local(&ip), + _ => false, + } + } + + /// Check whether this address is globally routable. + pub fn is_routable(&self) -> bool { + match self.0.host { + HostName::Ip(ip) => address::is_routable(&ip), + _ => true, + } + } +} + impl cyphernet::addr::Host for Address { fn requires_proxy(&self) -> bool { self.0.requires_proxy() diff --git a/radicle/src/node/address.rs b/radicle/src/node/address.rs index 0e69ef43..f3c6ceb3 100644 --- a/radicle/src/node/address.rs +++ b/radicle/src/node/address.rs @@ -50,3 +50,47 @@ impl TryFrom for AddressType { } } } +/// Check whether an IP address is globally routable. +pub fn is_routable(addr: &net::IpAddr) -> bool { + match addr { + net::IpAddr::V4(addr) => ipv4_is_routable(addr), + net::IpAddr::V6(addr) => ipv6_is_routable(addr), + } +} + +/// Check whether an IP address is locally routable. +pub fn is_local(addr: &net::IpAddr) -> bool { + match addr { + net::IpAddr::V4(addr) => { + addr.is_private() || addr.is_loopback() || addr.is_link_local() || addr.is_unspecified() + } + net::IpAddr::V6(_) => false, + } +} + +/// Check whether an IPv4 address is globally routable. +/// +/// This code is adapted from the Rust standard library's `net::Ipv4Addr::is_global`. It can be +/// replaced once that function is stabilized. +fn ipv4_is_routable(addr: &net::Ipv4Addr) -> bool { + // Check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two + // globally routable addresses in the 192.0.0.0/24 range. + if u32::from(*addr) == 0xc0000009 || u32::from(*addr) == 0xc000000a { + return true; + } + !addr.is_private() + && !addr.is_loopback() + && !addr.is_link_local() + && !addr.is_broadcast() + && !addr.is_documentation() + // Make sure the address is not in 0.0.0.0/8. + && addr.octets()[0] != 0 +} + +/// Check whether an IPv6 address is globally routable. +/// +/// For now, this always returns `true`, as IPv6 addresses +/// are not fully supported. +fn ipv6_is_routable(_addr: &net::Ipv6Addr) -> bool { + true +}