]> code.octet-stream.net Git - netwatcher/blob - src/list_unix.rs
Make some error cases more specific
[netwatcher] / src / list_unix.rs
1 use std::fmt::Write;
2 use std::{collections::HashMap, net::IpAddr};
3
4 use nix::{ifaddrs::getifaddrs, net::if_::if_nametoindex};
5
6 use crate::{Error, Interface, List};
7
8 struct CandidateInterface {
9 name: String,
10 index: u32,
11 hw_addr: Option<String>,
12 ips: Vec<IpAddr>,
13 }
14
15 pub(crate) fn list_interfaces() -> Result<List, Error> {
16 let addrs = getifaddrs().map_err(|e| Error::Getifaddrs(e))?;
17 let mut candidates = HashMap::new();
18
19 for addr in addrs {
20 let index =
21 if_nametoindex(addr.interface_name.as_str()).map_err(|e| Error::GetInterfaceName(e))?;
22 let candidate = candidates
23 .entry(addr.interface_name.clone())
24 .or_insert_with(|| CandidateInterface {
25 name: addr.interface_name.clone(),
26 index,
27 hw_addr: None,
28 ips: vec![],
29 });
30 if let Some(a) = addr.address {
31 if let Some(a) = a.as_link_addr() {
32 if let Some(raw_addr) = a.addr() {
33 candidate.hw_addr = Some(format_mac(&raw_addr)?);
34 }
35 }
36 if let Some(a) = a.as_sockaddr_in() {
37 candidate.ips.push(IpAddr::V4(a.ip()));
38 }
39 if let Some(a) = a.as_sockaddr_in6() {
40 candidate.ips.push(IpAddr::V6(a.ip()));
41 }
42 }
43 }
44
45 let ifs = candidates
46 .drain()
47 .flat_map(|(_, c)| {
48 c.hw_addr.map(|hw_addr| {
49 (
50 c.index,
51 Interface {
52 index: c.index,
53 hw_addr,
54 name: c.name,
55 ips: c.ips,
56 },
57 )
58 })
59 })
60 .collect();
61 Ok(List(ifs))
62 }
63
64 fn format_mac(bytes: &[u8]) -> Result<String, Error> {
65 let mut mac = String::with_capacity(bytes.len() * 3);
66 for (i, b) in bytes.iter().enumerate() {
67 if i != 0 {
68 write!(mac, ":").map_err(|_| Error::FormatMacAddress)?;
69 }
70 write!(mac, "{:02X}", b).map_err(|_| Error::FormatMacAddress)?;
71 }
72 Ok(mac)
73 }