Skip to main content

asmap/
lib.rs

1mod interpret;
2mod validate;
3
4use std::fmt;
5use std::fs;
6use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7use std::path::Path;
8
9/// Errors that can occur when loading asmap data.
10#[derive(Debug)]
11pub enum AsmapError {
12    Io(std::io::Error),
13    Invalid,
14}
15
16impl fmt::Display for AsmapError {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        match self {
19            AsmapError::Io(e) => write!(f, "failed to read asmap file: {e}"),
20            AsmapError::Invalid => write!(f, "asmap data failed validation"),
21        }
22    }
23}
24
25impl std::error::Error for AsmapError {
26    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
27        match self {
28            AsmapError::Io(e) => Some(e),
29            AsmapError::Invalid => None,
30        }
31    }
32}
33
34impl From<std::io::Error> for AsmapError {
35    fn from(e: std::io::Error) -> Self {
36        AsmapError::Io(e)
37    }
38}
39
40/// A validated asmap that maps IP addresses to Autonomous System Numbers (ASNs).
41///
42/// The asmap is validated on construction. Lookups are infallible — an unmapped
43/// address returns ASN 0.
44///
45/// # Example
46///
47/// ```no_run
48/// use asmap::Asmap;
49/// use std::net::IpAddr;
50///
51/// let map = Asmap::from_file("path/to/asmap.dat").unwrap();
52/// let asn = map.lookup("8.8.8.8".parse::<IpAddr>().unwrap());
53/// println!("ASN: {asn}");
54/// ```
55#[derive(Debug)]
56pub struct Asmap {
57    data: Vec<u8>,
58}
59
60impl Asmap {
61    /// Load and validate an asmap from a file.
62    pub fn from_file(path: impl AsRef<Path>) -> Result<Self, AsmapError> {
63        let data = fs::read(path)?;
64        Self::from_bytes(data)
65    }
66
67    /// Validate and wrap raw asmap bytes.
68    pub fn from_bytes(data: Vec<u8>) -> Result<Self, AsmapError> {
69        if !validate::sanity_check(&data, 128) {
70            return Err(AsmapError::Invalid);
71        }
72        Ok(Asmap { data })
73    }
74
75    /// Look up the ASN for an IP address. Returns 0 if unmapped.
76    pub fn lookup(&self, addr: IpAddr) -> u32 {
77        let ip6 = match addr {
78            IpAddr::V4(v4) => v4.to_ipv6_mapped(),
79            IpAddr::V6(v6) => v6,
80        };
81        interpret::interpret(&self.data, &ip6.octets())
82    }
83
84    /// Look up the ASN for an IPv4 address. Returns 0 if unmapped.
85    pub fn lookup_v4(&self, addr: Ipv4Addr) -> u32 {
86        self.lookup(IpAddr::V4(addr))
87    }
88
89    /// Look up the ASN for an IPv6 address. Returns 0 if unmapped.
90    pub fn lookup_v6(&self, addr: Ipv6Addr) -> u32 {
91        self.lookup(IpAddr::V6(addr))
92    }
93
94    /// Returns the raw asmap data.
95    pub fn as_bytes(&self) -> &[u8] {
96        &self.data
97    }
98}