Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
radicle/node: `Address` improvements
Merged lorenz opened 5 months ago

radicle/node: Address::is_local for DNS names

Address:is_local would return false for all DNS names. This is incorrect, with one counterexample being the name “localhost”, which generally resolves to a local (usually loopback) address.

The function is changed to catch “localhost”, but also the top level domain “.localhost”, which is reserved in RFC 2606, Section 2.

Address::is_routable would return true for all DNS names. While it is much harder to decide global routeability based on a domain name, as these usually have to be resolved to an address before being able to judge routability, there is one particular class of names, namely local ones (see above), which are not globally routable.

radicle/node: Revisit routeability of IP addresses

All IPv6 addresses would be considered globally routeable, even though the Rust standard library offers convenience functions to check for loopback, link-local addresses etc.

Improve checks for IPv6 routeability to catch the most obvious cases of local or unspecified addresses.

Refactor the check for IPv4 routeability to be more readable and refer to RFCs, IANA lists, and Rust stabilization tracking issues as appropriate.

2 files changed +40 -14 dc624ed5 37d4ae4a
modified crates/radicle/src/node.rs
@@ -465,8 +465,15 @@ pub struct Address(
impl Address {
    /// Check whether this address is from the local network.
    pub fn is_local(&self) -> bool {
-
        match self.0.host {
-
            HostName::Ip(ip) => address::is_local(&ip),
+
        match &self.0.host {
+
            HostName::Ip(ip) => address::is_local(ip),
+
            HostName::Dns(name) => {
+
                let name = name.strip_suffix(".").unwrap_or(name);
+

+
                // RFC 2606, Section 2
+
                // <https://datatracker.ietf.org/doc/html/rfc2606#section-2>
+
                name.ends_with(".localhost") || name == "localhost"
+
            }
            _ => false,
        }
    }
@@ -475,6 +482,7 @@ impl Address {
    pub fn is_routable(&self) -> bool {
        match self.0.host {
            HostName::Ip(ip) => address::is_routable(&ip),
+
            HostName::Dns(_) => !self.is_local(),
            _ => true,
        }
    }
modified crates/radicle/src/node/address.rs
@@ -259,33 +259,51 @@ pub fn is_local(addr: &net::IpAddr) -> bool {
        net::IpAddr::V4(addr) => {
            addr.is_private() || addr.is_loopback() || addr.is_link_local() || addr.is_unspecified()
        }
-
        net::IpAddr::V6(_) => false,
+
        net::IpAddr::V6(addr) => {
+
            addr.is_loopback() || addr.is_unicast_link_local() || addr.is_unspecified()
+
        }
    }
}

/// 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.
+
/// This implementation lacks many exceptions, and should be improved once
+
/// corresponding functions in [`std::net::Ipv4Addr`] are stabilized.
+
///
+
/// See
+
///  - <https://github.com/rust-lang/rust/issues/27709>
+
///  - <https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml>
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 {
+
    // https://datatracker.ietf.org/doc/html/rfc7723#section-4.1
+
    if *addr == net::Ipv4Addr::new(192, 0, 0, 9) {
+
        return true;
+
    }
+

+
    // https://datatracker.ietf.org/doc/html/rfc8155#section-8.1
+
    if *addr == net::Ipv4Addr::new(192, 0, 0, 10) {
        return true;
    }
+

+
    // https://datatracker.ietf.org/doc/html/rfc791#section-3.2
+
    if addr.octets()[0] == 0 {
+
        return false;
+
    }
+

    !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
+
/// This implementation lacks many exceptions, and should be improved once
+
/// corresponding functions in [`std::net::Ipv6Addr`] are stabilized.
+
///
+
/// See
+
///  - <https://github.com/rust-lang/rust/issues/27709>
+
///  - <https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml>
+
fn ipv6_is_routable(addr: &net::Ipv6Addr) -> bool {
+
    !addr.is_loopback() && !addr.is_unicast_link_local() && !addr.is_unspecified()
}