r_lanlib/
network.rs

1//! Provides helpers for selecting a network interface on the current host
2//! through which to preform network scanning
3
4use pnet::{
5    datalink::NetworkInterface as PNetNetworkInterface, ipnetwork::IpNetwork, util::MacAddr,
6};
7use std::{
8    error::Error,
9    io,
10    net::{Ipv4Addr, TcpListener},
11    str::FromStr,
12};
13
14/// Represents a network interface on current host
15pub struct NetworkInterface {
16    /// The name of the network interface i.e. "en0"
17    pub name: String,
18    /// A description of the network interface
19    pub description: String,
20    /// The cidr block associated with interface
21    pub cidr: String,
22    /// The assigned IPV4 address on the interface
23    pub ipv4: Ipv4Addr,
24    /// The IpNetwork of the interface
25    pub ips: Vec<IpNetwork>,
26    /// The MAC address of the interface
27    pub mac: MacAddr,
28    /// Any defined flags on the interface
29    pub flags: u32,
30    /// The index of the interface
31    pub index: u32,
32}
33
34impl TryFrom<PNetNetworkInterface> for NetworkInterface {
35    type Error = Box<dyn Error>;
36
37    fn try_from(value: PNetNetworkInterface) -> Result<Self, Self::Error> {
38        let mac = value.mac.ok_or("failed to get mac address for interface")?;
39        let (ip, cidr) =
40            get_interface_ipv4_and_cidr(&value).ok_or("failed to get ip and cidr for interface")?;
41        let ipv4 = Ipv4Addr::from_str(ip.as_str())?;
42
43        Ok(Self {
44            name: value.name,
45            description: value.description,
46            flags: value.flags,
47            index: value.index,
48            mac,
49            ips: value.ips,
50            cidr,
51            ipv4,
52        })
53    }
54}
55
56impl From<&NetworkInterface> for PNetNetworkInterface {
57    fn from(value: &NetworkInterface) -> Self {
58        Self {
59            name: value.name.clone(),
60            flags: value.flags,
61            description: value.description.clone(),
62            index: value.index,
63            ips: value.ips.clone(),
64            mac: Some(value.mac),
65        }
66    }
67}
68
69/// Finds and returns a NetworkInterface by name for current host
70pub fn get_interface(name: &str) -> Option<NetworkInterface> {
71    let iface = pnet::datalink::interfaces()
72        .into_iter()
73        .find(|i| i.name == name)?;
74    NetworkInterface::try_from(iface).ok()
75}
76
77/// Finds and returns the default NetworkInterface for current host
78pub fn get_default_interface() -> Option<NetworkInterface> {
79    let iface = pnet::datalink::interfaces()
80        .into_iter()
81        .find(|e| e.is_up() && !e.is_loopback() && e.ips.iter().any(|i| i.is_ipv4()))?;
82    NetworkInterface::try_from(iface).ok()
83}
84
85/// Finds an available port on the current host. This is useful when setting the
86/// listening port on a scanner where packets will be received.
87pub fn get_available_port() -> Result<u16, io::Error> {
88    let listener = TcpListener::bind(("127.0.0.1", 0))?;
89    let addr = listener.local_addr()?;
90    Ok(addr.port())
91}
92
93fn get_interface_ipv4_and_cidr(interface: &PNetNetworkInterface) -> Option<(String, String)> {
94    let ipnet = interface.ips.iter().find(|i| i.is_ipv4())?;
95    let ip = ipnet.ip().to_string();
96    let base = ipnet.network().to_string();
97    let prefix = ipnet.prefix().to_string();
98    let cidr = format!("{base}/{prefix}");
99    Some((ip, cidr))
100}
101
102#[cfg(test)]
103#[path = "./network_tests.rs"]
104mod tests;