use super::funejson::Value; use super::net_cidr::cidr_contains; use super::net_ip_classify_data::{SpecialBlock, SPECIAL_BLOCKS}; use super::net_ipv4::is_ipv4; use super::net_ipv6::{format_ipv6, is_ipv6, parse_ipv6}; /// 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| 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())) }