ipflag/ip.rs
1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2
3/// High-level IP classification used by IP Flag.
4///
5/// This classification is intentionally simple and UI-oriented:
6/// - `Private`: local network / internal addresses (LAN), never resolved
7/// - `Special`: loopback/multicast/documentation/unspecified/broadcast, never resolved
8/// - `Public`: eligible for resolution via [`crate::IpResolver`]
9///
10/// Note: Different applications may want deeper classification. IP Flag keeps this minimal.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum IpScope {
13 /// Private/local/internal ranges (e.g., 192.168.x.x, 10.x.x.x, fc00::/7)
14 Private,
15
16 /// Non-public special ranges (loopback, multicast, documentation ranges, etc.)
17 Special,
18
19 /// Public IP addresses. Only these are passed to the resolver.
20 Public,
21}
22
23/// Parse a string into [`IpAddr`].
24///
25/// This is a small helper around `str::parse::<IpAddr>()`.
26///
27/// # Example
28///
29/// ```rust
30/// use ipflag::parse_ip;
31///
32/// assert!(parse_ip("8.8.8.8").is_ok());
33/// assert!(parse_ip("not-an-ip").is_err());
34/// ```
35pub fn parse_ip(ip: &str) -> Result<IpAddr, std::net::AddrParseError> {
36 ip.parse::<IpAddr>()
37}
38
39/// Classify an [`IpAddr`] into [`IpScope`].
40///
41/// IP Flag will only call user resolvers for `Public` addresses.
42///
43/// # Example
44///
45/// ```rust
46/// use ipflag::{classify_ip, parse_ip, IpScope};
47///
48/// let ip = parse_ip("192.168.0.1").unwrap();
49/// assert_eq!(classify_ip(ip), IpScope::Private);
50/// ```
51pub fn classify_ip(ip: IpAddr) -> IpScope {
52 match ip {
53 IpAddr::V4(v4) => classify_v4(v4),
54 IpAddr::V6(v6) => classify_v6(v6),
55 }
56}
57
58/// Classify an IPv4 address into [`IpScope`].
59///
60/// This function is public so that downstream crates can reuse
61/// IP Flag's exact IPv4 classification logic without reimplementing it.
62///
63/// Classification rules:
64/// - Private: RFC1918 + link-local ranges
65/// - Special: loopback, multicast, documentation, unspecified, broadcast
66/// - Public: everything else
67///
68/// This function never performs resolution by itself.
69pub fn classify_v4(v4: Ipv4Addr) -> IpScope {
70 // Private + link-local are treated as Private for UI purposes.
71 if v4.is_private() || v4.is_link_local() {
72 return IpScope::Private;
73 }
74
75 // Special ranges are never resolved.
76 if v4.is_loopback()
77 || v4.is_multicast()
78 || v4.is_unspecified()
79 || v4.is_documentation()
80 || v4.is_broadcast()
81 {
82 return IpScope::Special;
83 }
84
85 IpScope::Public
86}
87
88/// Classify an IPv6 address into [`IpScope`].
89///
90/// This mirrors the behavior of [`classify_v4`] but for IPv6:
91/// - Private: unique-local (fc00::/7) and unicast link-local (fe80::/10)
92/// - Special: loopback, multicast, unspecified
93/// - Public: all other global unicast addresses
94///
95/// This function is public to allow reuse by custom resolvers
96/// or higher-level network tooling.
97pub fn classify_v6(v6: Ipv6Addr) -> IpScope {
98 // Unique local + unicast link-local are treated as Private.
99 if v6.is_unique_local() || v6.is_unicast_link_local() {
100 return IpScope::Private;
101 }
102
103 // Special ranges are never resolved.
104 if v6.is_loopback() || v6.is_multicast() || v6.is_unspecified() {
105 return IpScope::Special;
106 }
107
108 IpScope::Public
109}