Files
echoip-slatecave/src/ipinfo.rs
2025-03-20 00:04:34 +01:00

122 lines
3.6 KiB
Rust

/*
* The code in this file should provide a simple way to categorize
* IP-Address ranges (based on the rust tandard library) and return the
* result in a template ready format that can be used for custom messages
* depending on the type of IP-Address.
*/
use std::net::{IpAddr, Ipv4Addr};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Default, PartialEq, Clone)]
#[serde(rename_all="lowercase")]
pub enum AddressCast {
Unspecified,
#[default]
Unicast,
Multicast,
Broadcast,
}
#[derive(Serialize, Deserialize, Default, PartialEq, Clone)]
#[serde(rename_all="lowercase")]
pub enum AddressScope {
Global,
Private,
Shared,
LinkLocal,
Loopback,
Reserved,
Documentation,
Nat64,
#[default]
Unknown,
}
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct AddressInfo {
pub is_v6_address: bool,
pub cast: AddressCast,
pub scope: AddressScope,
}
impl AddressInfo {
pub fn new(address: &IpAddr) -> Self {
let mut is_v6_address = false;
let mut address_cast = AddressCast::Unicast;
let mut address_scope = AddressScope::Unknown;
match address {
IpAddr::V4(addr) => {
let naddr : u32 = (*addr).into();
let shared_net : u32 = Ipv4Addr::new(100, 64, 0, 0).into();
let v4_10_mask : u32 = 0xffc00000;
if addr.is_documentation() {
address_scope = AddressScope::Documentation;
//test if this is a shared address
} else if naddr & v4_10_mask == shared_net {
address_scope = AddressScope::Shared;
} else if addr.is_link_local() {
address_scope = AddressScope::LinkLocal;
} else if addr.is_private() {
address_scope = AddressScope::Private;
} else if addr.is_broadcast() {
address_cast = AddressCast::Broadcast;
address_scope = AddressScope::LinkLocal;
} else {
address_scope = AddressScope::Global;
}
},
IpAddr::V6(addr) => {
is_v6_address = true;
let segments = addr.segments();
// for std::net these are still nightly only api … 😕
// Test for unique local addresses fc00::/7
if segments[0] & 0xfe00 == 0xfc00 {
address_scope = AddressScope::Private;
// Test for link local addresses fe80::/10
} else if segments[0] & 0xffc0 == 0xfe80 {
address_scope = AddressScope::LinkLocal;
// Test for the documentation address 2001:db8::/32
} else if segments[0]==0x2001 && segments[1]==0x0db8 && segments[2]==0 && segments[3]==0 {
address_scope = AddressScope::Documentation;
// Test for NAT64 address 64:ff9b::/96
} else if segments[0]==0x64 && segments[1]==0xff9b {
address_scope = AddressScope::Nat64;
// Test for multicase scope
} else if addr.is_multicast() {
address_cast = AddressCast::Multicast;
let cast_type = segments[1] & 0x0f;
match cast_type {
1 => address_scope = AddressScope::Loopback,
2 => address_scope = AddressScope::LinkLocal,
4 | 5 | 8 => address_scope = AddressScope::Private,
0xe => address_scope = AddressScope::Global,
0 | 3 | 0xf => address_scope = AddressScope::Reserved,
_ => address_scope = AddressScope::Unknown,
}
} else if segments[0] & 0xe000 == 0x2000 {
address_scope = AddressScope::Global;
}
}
}
if address.is_loopback() {
address_scope = AddressScope::Loopback;
address_cast = AddressCast::Unicast;
} else if address.is_multicast() {
address_cast = AddressCast::Multicast;
} else if address.is_unspecified() {
address_scope = AddressScope::Unknown;
address_cast = AddressCast::Unspecified;
}
AddressInfo {
is_v6_address: is_v6_address,
cast: address_cast,
scope: address_scope
}
}
}