bogon/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3//! Functions for checking whether an IP address is bogus.
4//!
5//! Here "bogus" or "bogon" means an IP address that is not valid for use on the
6//! public internet. This includes private IP addresses, loopback addresses, and
7//! other reserved addresses.
8//!
9//! # Cargo Features
10//!
11//! - `download`: Download the latest IPv6 address allocations from the IANA website during the build process. Requires a network connection.
12//!
13//! # Example
14//!
15//! ```
16//! use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
17//!
18//! assert_eq!(bogon::is_bogon_str("127.0.0.1"), Ok(true));
19//! assert_eq!(bogon::is_bogon_str("8.8.8.8"), Ok(false));
20//! assert_eq!(bogon::is_bogon_str("::1"), Ok(true));
21//! assert!(bogon::is_bogon_str("foo").is_err());
22//!
23//! assert_eq!(bogon::is_bogon_v4(Ipv4Addr::new(127, 0, 0, 1)), true);
24//! assert_eq!(bogon::is_bogon_v4(Ipv4Addr::new(8, 8, 8, 8)), false);
25//! assert_eq!(bogon::is_bogon_v6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), true);
26//!
27//! assert_eq!(bogon::is_bogon(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), true);
28//! assert_eq!(bogon::is_bogon(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))), false);
29//! assert_eq!(bogon::is_bogon(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))), true);
30//!
31//! use bogon::BogonExt;
32//!
33//! assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_bogon(), true);
34//! assert_eq!(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)).is_bogon(), false);
35//! assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)).is_bogon(), true);
36//! ```
37use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
38
39pub use ext::BogonExt;
40use network::FourByteNetwork;
41
42mod ext;
43#[cfg(test)]
44mod net_tests;
45mod network;
46
47mod ipv6_unicast_address_allocations {
48    include!(concat!(
49        env!("OUT_DIR"),
50        "/ipv6-unicast-address-allocations.rs"
51    ));
52}
53
54// Bogus IPv4 networks.
55//
56// SAFETY: FourByteNetwork::new_unchecked is safe here as long as the prefix length is less than or equal to 32
57static V4_BOGON_NETWORKS: [FourByteNetwork; 15] = [
58    // "This Network"
59    FourByteNetwork::new(Ipv4Addr::new(0, 0, 0, 0).to_bits(), 8),
60    // Private-Use
61    FourByteNetwork::new(Ipv4Addr::new(10, 0, 0, 0).to_bits(), 8),
62    // Shared Address Space
63    FourByteNetwork::new(Ipv4Addr::new(100, 64, 0, 0).to_bits(), 10),
64    // Loopback
65    FourByteNetwork::new(Ipv4Addr::new(127, 0, 0, 0).to_bits(), 8),
66    // Link Local
67    FourByteNetwork::new(Ipv4Addr::new(169, 254, 0, 0).to_bits(), 16),
68    // Private-Use
69    FourByteNetwork::new(Ipv4Addr::new(172, 16, 0, 0).to_bits(), 12),
70    // IETF Protocol Assignments
71    FourByteNetwork::new(Ipv4Addr::new(192, 0, 0, 0).to_bits(), 24),
72    // Documentation (TEST-NET-1)
73    FourByteNetwork::new(Ipv4Addr::new(192, 0, 2, 0).to_bits(), 24),
74    // Private-Use
75    FourByteNetwork::new(Ipv4Addr::new(192, 168, 0, 0).to_bits(), 16),
76    // "Benchmarking"
77    FourByteNetwork::new(Ipv4Addr::new(198, 18, 0, 0).to_bits(), 15),
78    // TEST-NET-2
79    FourByteNetwork::new(Ipv4Addr::new(198, 51, 100, 0).to_bits(), 24),
80    // TEST-NET-3
81    FourByteNetwork::new(Ipv4Addr::new(203, 0, 113, 0).to_bits(), 24),
82    // Multicast
83    FourByteNetwork::new(Ipv4Addr::new(224, 0, 0, 0).to_bits(), 4),
84    // Reserved
85    FourByteNetwork::new(Ipv4Addr::new(240, 0, 0, 0).to_bits(), 4),
86    // Limited Broadcast
87    FourByteNetwork::new(Ipv4Addr::new(255, 255, 255, 255).to_bits(), 32),
88];
89
90/// Returns a boolean indicating whether an IP address is bogus.
91///
92/// Returns `true` if the IP address is bogus.
93/// Returns `false` if the IP address is good.
94///
95/// # Examples
96///
97/// ```
98/// use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
99/// use bogon::is_bogon;
100///
101/// assert_eq!(is_bogon(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), true);
102/// assert_eq!(is_bogon(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))), false);
103/// assert_eq!(is_bogon(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))), true);
104/// assert_eq!(is_bogon(IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0x1111, 0, 0, 0, 2))), false);
105/// ```
106#[inline]
107pub fn is_bogon(ip_address: IpAddr) -> bool {
108    match ip_address {
109        IpAddr::V4(ip) => is_bogon_v4(ip),
110        IpAddr::V6(ip) => is_bogon_v6(ip),
111    }
112}
113
114/// Returns a boolean indicating whether an IP address is bogus.
115///
116/// Returns an error if the IP address is invalid.
117/// Returns `Ok(true)` if the IP address is bogus.
118/// Returns `Ok(false)` if the IP address is good.
119///
120/// # Examples
121///
122/// ```
123/// use bogon::is_bogon_str;
124///
125/// assert_eq!(is_bogon_str("127.0.0.1"), Ok(true));
126/// assert_eq!(is_bogon_str("8.8.8.8"), Ok(false));
127/// assert_eq!(is_bogon_str("::1"), Ok(true));
128/// assert_eq!(is_bogon_str("2606:4700:4700:1111::2"), Ok(false));
129///
130/// assert!(is_bogon_str("foo").is_err());
131/// ```
132#[inline]
133pub fn is_bogon_str(ip_address: impl AsRef<str>) -> Result<bool, core::net::AddrParseError> {
134    ip_address.as_ref().parse().map(is_bogon)
135}
136
137/// Returns a boolean indicating whether an IPv4 address is bogus.
138///
139/// Returns `true` if the IP address is bogus.
140/// Returns `false` if the IP address is good.
141///
142/// # Examples
143///
144/// ```
145/// use core::net::Ipv4Addr;
146/// use bogon::is_bogon_v4;
147///
148/// assert_eq!(is_bogon_v4(Ipv4Addr::new(127, 0, 0, 1)), true);
149/// assert_eq!(is_bogon_v4(Ipv4Addr::new(8, 8, 8, 8)), false);
150/// ```
151#[inline]
152pub fn is_bogon_v4(ip_address: Ipv4Addr) -> bool {
153    V4_BOGON_NETWORKS
154        .iter()
155        .any(|network| network.contains_v4(ip_address))
156}
157
158/// Returns a boolean indicating whether an IPv6 address is bogus.
159///
160/// Returns `true` if the IP address is bogus.
161/// Returns `false` if the IP address is good.
162///
163/// # Examples
164///
165/// ```
166/// use core::net::Ipv6Addr;
167/// use bogon::is_bogon_v6;
168///
169/// assert_eq!(is_bogon_v6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), true);
170/// assert_eq!(is_bogon_v6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0x1111, 0, 0, 0, 2)), false);
171/// ```
172#[inline]
173pub fn is_bogon_v6(ip_address: Ipv6Addr) -> bool {
174    // If the IP is outside 2000::/3, it is not a global unicast address.
175    if ip_address.segments()[0] & 0xe000 != 0x2000 {
176        return true;
177    }
178
179    // Bring the IP address into the IPv4 space for comparison.
180    !ipv6_unicast_address_allocations::V6_ALLOCATIONS
181        .iter()
182        .any(|network| network.contains_v6(ip_address))
183}