]> code.octet-stream.net Git - netwatcher/blob - src/lib.rs
Clean up race condition in initial watch on Windows
[netwatcher] / src / lib.rs
1 use std::{
2 collections::{HashMap, HashSet},
3 net::{IpAddr, Ipv4Addr, Ipv6Addr},
4 ops::Sub,
5 };
6
7 #[cfg_attr(windows, path = "list_win.rs")]
8 #[cfg_attr(target_vendor = "apple", path = "list_mac.rs")]
9 mod list;
10
11 #[cfg_attr(windows, path = "watch_win.rs")]
12 #[cfg_attr(target_vendor = "apple", path = "watch_mac.rs")]
13 mod watch;
14
15 #[cfg(target_vendor = "apple")]
16 mod util;
17
18 type IfIndex = u32;
19
20 #[derive(Debug, Clone, PartialEq, Eq)]
21 pub struct Interface {
22 pub index: u32,
23 pub name: String,
24 pub hw_addr: String,
25 pub ips: Vec<IpAddr>,
26 }
27
28 impl Interface {
29 pub fn ipv4_ips(&self) -> impl Iterator<Item = &Ipv4Addr> {
30 self.ips.iter().filter_map(|ip| match ip {
31 IpAddr::V4(v4) => Some(v4),
32 IpAddr::V6(_) => None,
33 })
34 }
35
36 pub fn ipv6_ips(&self) -> impl Iterator<Item = &Ipv6Addr> {
37 self.ips.iter().filter_map(|ip| match ip {
38 IpAddr::V4(_) => None,
39 IpAddr::V6(v6) => Some(v6),
40 })
41 }
42 }
43
44 #[derive(Debug, Clone, PartialEq, Eq)]
45 pub struct Update {
46 pub interfaces: HashMap<IfIndex, Interface>,
47 pub diff: UpdateDiff,
48 }
49
50 #[derive(Debug, Clone, PartialEq, Eq, Default)]
51 pub struct UpdateDiff {
52 pub added: Vec<IfIndex>,
53 pub removed: Vec<IfIndex>,
54 pub modified: HashMap<IfIndex, InterfaceDiff>,
55 }
56
57 #[derive(Debug, Clone, PartialEq, Eq, Default)]
58 pub struct InterfaceDiff {
59 pub hw_addr_changed: bool,
60 pub addrs_added: Vec<IpAddr>,
61 pub addrs_removed: Vec<IpAddr>,
62 }
63
64 #[derive(Debug, Clone, PartialEq, Eq)]
65 pub enum Error {
66 // TODO: handle all cases with proper sources
67 Internal,
68 }
69
70 #[derive(Default, PartialEq, Eq)]
71 struct List(HashMap<IfIndex, Interface>);
72
73 impl List {
74 fn diff_from(&self, prev: &List) -> UpdateDiff {
75 let prev_index_set: HashSet<IfIndex> = prev.0.keys().cloned().collect();
76 let curr_index_set: HashSet<IfIndex> = self.0.keys().cloned().collect();
77 let added = curr_index_set.sub(&prev_index_set).into_iter().collect();
78 let removed = prev_index_set.sub(&curr_index_set).into_iter().collect();
79 let mut modified = HashMap::new();
80 for index in curr_index_set.intersection(&prev_index_set) {
81 if prev.0[index] == self.0[index] {
82 continue;
83 }
84 let prev_addr_set: HashSet<&IpAddr> = prev.0[index].ips.iter().collect();
85 let curr_addr_set: HashSet<&IpAddr> = self.0[index].ips.iter().collect();
86 let addrs_added: Vec<IpAddr> = curr_addr_set
87 .sub(&prev_addr_set)
88 .iter()
89 .cloned()
90 .cloned()
91 .collect();
92 let addrs_removed: Vec<IpAddr> = prev_addr_set
93 .sub(&curr_addr_set)
94 .iter()
95 .cloned()
96 .cloned()
97 .collect();
98 let hw_addr_changed = prev.0[index].hw_addr != self.0[index].hw_addr;
99 modified.insert(
100 *index,
101 InterfaceDiff {
102 hw_addr_changed,
103 addrs_added,
104 addrs_removed,
105 },
106 );
107 }
108 UpdateDiff {
109 added,
110 removed,
111 modified,
112 }
113 }
114 }
115
116 pub struct WatchHandle {
117 _inner: watch::WatchHandle,
118 }
119
120 pub fn list_interfaces() -> Result<HashMap<IfIndex, Interface>, Error> {
121 list::list_interfaces().map(|list| list.0)
122 }
123
124 pub fn watch_interfaces<F: FnMut(Update) + 'static>(callback: F) -> Result<WatchHandle, Error> {
125 watch::watch_interfaces(callback).map(|handle| WatchHandle { _inner: handle })
126 }