Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::net_cidr::cidr_contains; ← from net.cidr ^1.0.0 · built alongside by fune
use super::net_ip_classify_data::{SpecialBlock, SPECIAL_BLOCKS}; ← this capability’s own data, compiled from data/special-purpose.json into the same file by fune build
use super::net_ipv4::is_ipv4; ← from net.ipv4 ^1.0.0 · built alongside by fune
use super::net_ipv6::{format_ipv6, is_ipv6, parse_ipv6}; ← from net.ipv6 ^1.0.0 · built alongside by fune
/// Which special-purpose block an address is in.
///
/// From the IANA IPv4 and IPv6 Special-Purpose Address Registries (RFC 6890)
/// plus the multicast ranges. The most specific block wins: 192.0.0.9 is Port
/// Control Protocol Anycast (globally reachable), not the /24 of IETF
/// assignments around it (not). An address in no block is "global".
///
/// # Panics
/// Panics when the text is not an IPv4 or IPv6 address.
pub fn classify_ip(address: &str) -> IpClassification {
let (version, canonical) = if is_ipv4(address) {
(4, address.to_string())
} else if is_ipv6(address) {
(6, format_ipv6(&parse_ipv6(address)))
} else {
panic!("\"{}\" is not an IPv4 or IPv6 address", address);
};
let mut best: Option<&SpecialBlock> = None;
let mut best_prefix: i64 = -1;
for row in SPECIAL_BLOCKS {
if row.block.contains(':') != (version == 6) {
continue;
}
if !cidr_contains(row.block, &canonical) {
continue;
}
let prefix: i64 = row.block[row.block.find('/').unwrap() + 1..].parse().unwrap();
if prefix > best_prefix {
best = Some(row);
best_prefix = prefix;
}
}
match best {
None => IpClassification {
address: canonical,
version,
category: "global".to_string(),
name: "Global unicast".to_string(),
block: None,
rfc: None,
globally_reachable: Some(true),
},
Some(row) => IpClassification {
address: canonical,
version,
category: row.category.to_string(),
name: row.name.to_string(),
block: Some(row.block.to_string()),
rfc: Some(row.rfc.to_string()),
globally_reachable: row.globally_reachable,
},
}
}
pub fn ip_classification_to_value(c: &IpClassification) -> Value {
let opt = |s: &Option<String>| match s {
Some(s) => Value::str(s),
None => Value::Null,
};
Value::obj(vec![
("address", Value::str(&c.address)),
("version", Value::Int(c.version)),
("category", Value::str(&c.category)),
("name", Value::str(&c.name)),
("block", opt(&c.block)),
("rfc", opt(&c.rfc)),
("globallyReachable", match c.globally_reachable {
Some(b) => Value::Bool(b),
None => Value::Null,
}),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
ip_classification_to_value(&classify_ip(args[0].as_str()))
}