use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// This code is the unstable ip code from the rust std /// Since I would like to build on the stable rust, I just copied it yolo #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] pub enum Ipv6MulticastScope { InterfaceLocal, LinkLocal, RealmLocal, AdminLocal, SiteLocal, OrganizationLocal, Global, } pub trait IpAddrExt { fn ext_is_global(&self) -> bool; fn ext_is_documentation(&self) -> bool; } impl IpAddrExt for IpAddr { /// Returns [`true`] if the address appears to be globally routable. /// /// See the documentation for [`Ipv4Addr::is_global`][IPv4] and /// [`Ipv6Addr::is_global`][IPv6] for more details. /// /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_global /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_global /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// use torment_core::ip::IpAddrExt; /// /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).ext_is_global(), true); /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).ext_is_global(), true); /// ``` fn ext_is_global(&self) -> bool { match self { IpAddr::V4(ip) => ip.ext_is_global(), IpAddr::V6(ip) => ip.ext_is_global(), } } /// Returns [`true`] if this address is in a range designated for documentation. /// /// See the documentation for [`Ipv4Addr::is_documentation`][IPv4] and /// [`Ipv6Addr::is_documentation`][IPv6] for more details. /// /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_documentation /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_documentation /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// use torment_core::ip::IpAddrExt; /// /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).ext_is_documentation(), true); /// assert_eq!( /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).ext_is_documentation(), /// true /// ); /// ``` fn ext_is_documentation(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_documentation(), IpAddr::V6(ip) => ip.ext_is_documentation(), } } } pub trait Ipv4AddrExt { fn ext_is_global(&self) -> bool; fn ext_is_shared(&self) -> bool; fn ext_is_ietf_protocol_assignment(&self) -> bool; fn ext_is_benchmarking(&self) -> bool; fn ext_is_reserved(&self) -> bool; } impl Ipv4AddrExt for Ipv4Addr { /// Returns [`true`] if the address appears to be globally routable. /// See [iana-ipv4-special-registry][ipv4-sr]. /// /// The following return false: /// /// - private addresses (see [`is_private()`](#method.is_private)) /// - the loopback address (see [`is_loopback()`](#method.is_loopback)) /// - the link-local address (see [`is_link_local()`](#method.is_link_local)) /// - the broadcast address (see [`is_broadcast()`](#method.is_broadcast)) /// - addresses used for documentation (see [`is_documentation()`](#method.is_documentation)) /// - the unspecified address (see [`is_unspecified()`](#method.is_unspecified)), and the whole /// 0.0.0.0/8 block /// - addresses reserved for future protocols (see /// [`is_ietf_protocol_assignment()`](#method.is_ietf_protocol_assignment), except /// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable /// - addresses reserved for future use (see [`is_reserved()`](#method.is_reserved) /// - addresses reserved for networking devices benchmarking (see /// [`is_benchmarking`](#method.is_benchmarking)) /// /// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::Ipv4Addr; /// use torment_core::ip::Ipv4AddrExt; /// /// // private addresses are not global /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).ext_is_global(), false); /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).ext_is_global(), false); /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).ext_is_global(), false); /// /// // the 0.0.0.0/8 block is not global /// assert_eq!(Ipv4Addr::new(0, 1, 2, 3).ext_is_global(), false); /// // in particular, the unspecified address is not global /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).ext_is_global(), false); /// /// // the loopback address is not global /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).ext_is_global(), false); /// /// // link local addresses are not global /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).ext_is_global(), false); /// /// // the broadcast address is not global /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).ext_is_global(), false); /// /// // the address space designated for documentation is not global /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).ext_is_global(), false); /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).ext_is_global(), false); /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).ext_is_global(), false); /// /// // shared addresses are not global /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).ext_is_global(), false); /// /// // addresses reserved for protocol assignment are not global /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).ext_is_global(), false); /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).ext_is_global(), false); /// /// // addresses reserved for future use are not global /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).ext_is_global(), false); /// /// // addresses reserved for network devices benchmarking are not global /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).ext_is_global(), false); /// /// // All the other addresses are global /// assert_eq!(Ipv4Addr::new(1, 1, 1, 1).ext_is_global(), true); /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).ext_is_global(), true); /// ``` fn ext_is_global(&self) -> 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(*self) == 0xc0000009 || u32::from(*self) == 0xc000000a { return true; } !self.is_private() && !self.is_loopback() && !self.is_link_local() && !self.is_broadcast() && !self.is_documentation() && !self.ext_is_shared() && !self.ext_is_ietf_protocol_assignment() && !self.ext_is_reserved() && !self.ext_is_benchmarking() // Make sure the address is not in 0.0.0.0/8 && self.octets()[0] != 0 } /// Returns [`true`] if this address is part of the Shared Address Space defined in /// [IETF RFC 6598] (`100.64.0.0/10`). /// /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::Ipv4Addr; /// use torment_core::ip::Ipv4AddrExt; /// /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).ext_is_shared(), true); /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).ext_is_shared(), true); /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).ext_is_shared(), false); /// ``` fn ext_is_shared(&self) -> bool { self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) } /// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to /// IANA for IETF protocol assignments, as documented in [IETF RFC 6890]. /// /// Note that parts of this block are in use: /// /// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600]) /// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723]) /// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155]) /// /// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890 /// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600 /// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723 /// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155 /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::Ipv4Addr; /// use torment_core::ip::Ipv4AddrExt; /// /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).ext_is_ietf_protocol_assignment(), true); /// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).ext_is_ietf_protocol_assignment(), true); /// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).ext_is_ietf_protocol_assignment(), true); /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).ext_is_ietf_protocol_assignment(), true); /// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).ext_is_ietf_protocol_assignment(), false); /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).ext_is_ietf_protocol_assignment(), false); /// ``` fn ext_is_ietf_protocol_assignment(&self) -> bool { self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0 } /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. /// /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 /// [errata 423]: https://www.rfc-editor.org/errata/eid423 /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::Ipv4Addr; /// use torment_core::ip::Ipv4AddrExt; /// /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).ext_is_benchmarking(), false); /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).ext_is_benchmarking(), true); /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).ext_is_benchmarking(), true); /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).ext_is_benchmarking(), false); /// ``` fn ext_is_benchmarking(&self) -> bool { self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 } /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since /// it is obviously not reserved for future use. /// /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 /// [`true`]: ../../std/primitive.bool.html /// /// # Warning /// /// As IANA assigns new addresses, this method will be /// updated. This may result in non-reserved addresses being /// treated as reserved in code that relies on an outdated version /// of this method. /// /// # Examples /// /// ``` /// use std::net::Ipv4Addr; /// use torment_core::ip::Ipv4AddrExt; /// /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).ext_is_reserved(), true); /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).ext_is_reserved(), true); /// /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).ext_is_reserved(), false); /// // The broadcast address is not considered as reserved for future use by this implementation /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).ext_is_reserved(), false); /// ``` fn ext_is_reserved(&self) -> bool { self.octets()[0] & 240 == 240 && !self.is_broadcast() } } pub trait Ipv6AddrExt { fn ext_is_global(&self) -> bool; fn ext_is_unique_local(&self) -> bool; fn ext_is_unicast_link_local_strict(&self) -> bool; fn ext_is_unicast_link_local(&self) -> bool; fn ext_is_unicast_site_local(&self) -> bool; fn ext_is_unicast_global(&self) -> bool; fn ext_is_documentation(&self) -> bool; fn ext_multicast_scope(&self) -> Option; } impl Ipv6AddrExt for Ipv6Addr { /// Returns [`true`] if the address appears to be globally routable. /// /// The following return [`false`]: /// /// - the loopback address /// - link-local and unique local unicast addresses /// - interface-, link-, realm-, admin- and site-local multicast addresses /// /// [`true`]: ../../std/primitive.bool.html /// [`false`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// use torment_core::ip::Ipv6AddrExt; /// /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_global(), true); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).ext_is_global(), false); /// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).ext_is_global(), true); /// ``` fn ext_is_global(&self) -> bool { match Ipv6AddrExt::ext_multicast_scope(self) { Some(Ipv6MulticastScope::Global) => true, None => self.ext_is_unicast_global(), _ => false, } } /// Returns [`true`] if this is a unique local address (`fc00::/7`). /// /// This property is defined in [IETF RFC 4193]. /// /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// use torment_core::ip::Ipv6AddrExt; /// /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_unique_local(), false); /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).ext_is_unique_local(), true); /// ``` fn ext_is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } /// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`). /// /// A common mis-conception is to think that "unicast link-local addresses start with /// `fe80::`", but the [IETF RFC 4291] actually defines a stricter format for these addresses: /// /// ```no_rust /// | 10 | /// | bits | 54 bits | 64 bits | /// +----------+-------------------------+----------------------------+ /// |1111111010| 0 | interface ID | /// +----------+-------------------------+----------------------------+ /// ``` /// /// This method validates the format defined in the RFC and won't recognize the following /// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example. /// If you need a less strict validation use [`is_unicast_link_local()`] instead. /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// use torment_core::ip::Ipv6AddrExt; /// /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); /// assert!(ip.ext_is_unicast_link_local_strict()); /// /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); /// assert!(ip.ext_is_unicast_link_local_strict()); /// /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); /// assert!(!ip.ext_is_unicast_link_local_strict()); /// assert!(ip.ext_is_unicast_link_local()); /// /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); /// assert!(!ip.ext_is_unicast_link_local_strict()); /// assert!(ip.ext_is_unicast_link_local()); /// ``` /// /// # See also /// /// - [IETF RFC 4291 section 2.5.6] /// - [RFC 4291 errata 4406] /// - [`is_unicast_link_local()`] /// /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 /// [IETF RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 /// [`true`]: ../../std/primitive.bool.html /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 /// [`is_unicast_link_local()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local /// fn ext_is_unicast_link_local_strict(&self) -> bool { (self.segments()[0] & 0xffff) == 0xfe80 && (self.segments()[1] & 0xffff) == 0 && (self.segments()[2] & 0xffff) == 0 && (self.segments()[3] & 0xffff) == 0 } /// Returns [`true`] if the address is a unicast link-local address (`fe80::/10`). /// /// This method returns [`true`] for addresses in the range reserved by [RFC 4291 section 2.4], /// i.e. addresses with the following format: /// /// ```no_rust /// | 10 | /// | bits | 54 bits | 64 bits | /// +----------+-------------------------+----------------------------+ /// |1111111010| arbitratry value | interface ID | /// +----------+-------------------------+----------------------------+ /// ``` /// /// As a result, this method consider addresses such as `fe80:0:0:1::` or `fe81::` to be /// unicast link-local addresses, whereas [`is_unicast_link_local_strict()`] does not. If you /// need a strict validation fully compliant with the RFC, use /// [`is_unicast_link_local_strict()`]. /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// use torment_core::ip::Ipv6AddrExt; /// /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); /// assert!(ip.ext_is_unicast_link_local()); /// /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); /// assert!(ip.ext_is_unicast_link_local()); /// /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); /// assert!(ip.ext_is_unicast_link_local()); /// assert!(!ip.ext_is_unicast_link_local_strict()); /// /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); /// assert!(ip.ext_is_unicast_link_local()); /// assert!(!ip.ext_is_unicast_link_local_strict()); /// ``` /// /// # See also /// /// - [IETF RFC 4291 section 2.4] /// - [RFC 4291 errata 4406] /// /// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 /// [`true`]: ../../std/primitive.bool.html /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 /// [`is_unicast_link_local_strict()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local_strict /// fn ext_is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } /// Returns [`true`] if this is a deprecated unicast site-local address (fec0::/10). The /// unicast site-local address format is defined in [RFC 4291 section 2.5.7] as: /// /// ```no_rust /// | 10 | /// | bits | 54 bits | 64 bits | /// +----------+-------------------------+----------------------------+ /// |1111111011| subnet ID | interface ID | /// +----------+-------------------------+----------------------------+ /// ``` /// /// [`true`]: ../../std/primitive.bool.html /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// use torment_core::ip::Ipv6AddrExt; /// /// assert_eq!( /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_unicast_site_local(), /// false /// ); /// assert_eq!(Ipv6Addr::new(0xfec2, 0, 0, 0, 0, 0, 0, 0).ext_is_unicast_site_local(), true); /// ``` /// /// # Warning /// /// As per [RFC 3879], the whole `FEC0::/10` prefix is /// deprecated. New software must not support site-local /// addresses. /// /// [RFC 3879]: https://tools.ietf.org/html/rfc3879 fn ext_is_unicast_site_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfec0 } /// Returns [`true`] if the address is a globally routable unicast address. /// /// The following return false: /// /// - the loopback address /// - the link-local addresses /// - unique local addresses /// - the unspecified address /// - the address range reserved for documentation /// /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7] /// /// ```no_rust /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer /// be supported in new implementations (i.e., new implementations must treat this prefix as /// Global Unicast). /// ``` /// /// [`true`]: ../../std/primitive.bool.html /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// use torment_core::ip::Ipv6AddrExt; /// /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).ext_is_unicast_global(), false); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_unicast_global(), true); /// ``` fn ext_is_unicast_global(&self) -> bool { !self.is_multicast() && !self.is_loopback() && !self.ext_is_unicast_link_local() && !self.ext_is_unique_local() && !self.is_unspecified() && !self.ext_is_documentation() } /// Returns [`true`] if this is an address reserved for documentation /// (2001:db8::/32). /// /// This property is defined in [IETF RFC 3849]. /// /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 /// [`true`]: ../../std/primitive.bool.html /// /// # Examples /// /// ``` /// use std::net::Ipv6Addr; /// use torment_core::ip::Ipv6AddrExt; /// /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_documentation(), false); /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).ext_is_documentation(), true); /// ``` fn ext_is_documentation(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } /// Returns the address's multicast scope if the address is multicast. /// /// # Examples /// /// ``` /// use std::net::{Ipv6Addr}; /// use torment_core::ip::{Ipv6AddrExt, Ipv6MulticastScope}; /// /// assert_eq!( /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).ext_multicast_scope(), /// Some(Ipv6MulticastScope::Global) /// ); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_multicast_scope(), None); /// ``` fn ext_multicast_scope(&self) -> Option { if self.is_multicast() { match self.segments()[0] & 0x000f { 1 => Some(Ipv6MulticastScope::InterfaceLocal), 2 => Some(Ipv6MulticastScope::LinkLocal), 3 => Some(Ipv6MulticastScope::RealmLocal), 4 => Some(Ipv6MulticastScope::AdminLocal), 5 => Some(Ipv6MulticastScope::SiteLocal), 8 => Some(Ipv6MulticastScope::OrganizationLocal), 14 => Some(Ipv6MulticastScope::Global), _ => None, } } else { None } } }