]> code.octet-stream.net Git - netwatcher/blob - src/watch_win.rs
Update licence in Cargo.toml
[netwatcher] / src / watch_win.rs
1 use std::ffi::c_void;
2 use std::pin::Pin;
3 use std::sync::Mutex;
4
5 use windows::Win32::Foundation::ERROR_INVALID_HANDLE;
6 use windows::Win32::Foundation::ERROR_INVALID_PARAMETER;
7 use windows::Win32::Foundation::ERROR_NOT_ENOUGH_MEMORY;
8 use windows::Win32::Foundation::NO_ERROR;
9 use windows::Win32::NetworkManagement::IpHelper::CancelMibChangeNotify2;
10 use windows::Win32::NetworkManagement::IpHelper::MIB_NOTIFICATION_TYPE;
11 use windows::Win32::NetworkManagement::IpHelper::MIB_UNICASTIPADDRESS_ROW;
12 use windows::Win32::{
13 Foundation::{BOOLEAN, HANDLE},
14 NetworkManagement::IpHelper::NotifyUnicastIpAddressChange,
15 Networking::WinSock::AF_UNSPEC,
16 };
17
18 use crate::Error;
19 use crate::List;
20 use crate::Update;
21
22 struct WatchState {
23 /// The last result that we captured, for diffing
24 prev_list: List,
25 /// User's callback
26 cb: Box<dyn FnMut(Update) + Send + 'static>,
27 }
28
29 pub(crate) struct WatchHandle {
30 hnd: HANDLE,
31 _state: Pin<Box<Mutex<WatchState>>>,
32 }
33
34 impl Drop for WatchHandle {
35 fn drop(&mut self) {
36 unsafe {
37 let _ = CancelMibChangeNotify2(self.hnd);
38 }
39 }
40 }
41
42 pub(crate) fn watch_interfaces<F: FnMut(Update) + Send + 'static>(
43 callback: F,
44 ) -> Result<WatchHandle, Error> {
45 let state = Box::pin(Mutex::new(WatchState {
46 prev_list: List::default(),
47 cb: Box::new(callback),
48 }));
49 let state_ctx = &*state.as_ref() as *const _ as *const c_void;
50
51 let mut hnd = HANDLE::default();
52 let res = unsafe {
53 NotifyUnicastIpAddressChange(
54 AF_UNSPEC,
55 Some(notif),
56 Some(state_ctx),
57 BOOLEAN(0),
58 &mut hnd,
59 )
60 };
61 match res {
62 NO_ERROR => {
63 // Trigger an initial update
64 handle_notif(&mut state.lock().unwrap(), crate::list::list_interfaces()?);
65 // Then return the handle
66 Ok(WatchHandle { hnd, _state: state })
67 }
68 ERROR_INVALID_HANDLE => Err(Error::InvalidHandle),
69 ERROR_INVALID_PARAMETER => Err(Error::InvalidParameter),
70 ERROR_NOT_ENOUGH_MEMORY => Err(Error::NotEnoughMemory),
71 _ => Err(Error::UnexpectedWindowsResult(res.0)),
72 }
73 }
74
75 unsafe extern "system" fn notif(
76 ctx: *const c_void,
77 _row: *const MIB_UNICASTIPADDRESS_ROW,
78 _notification_type: MIB_NOTIFICATION_TYPE,
79 ) {
80 let state_ptr = ctx as *const Mutex<WatchState>;
81 unsafe {
82 let state_guard = &mut *state_ptr
83 .as_ref()
84 .expect("callback ctx should never be null")
85 .lock()
86 .unwrap();
87 let Ok(new_list) = crate::list::list_interfaces() else {
88 return;
89 };
90 handle_notif(state_guard, new_list);
91 }
92 }
93
94 fn handle_notif(state: &mut WatchState, new_list: List) {
95 if new_list == state.prev_list {
96 return;
97 }
98 let update = Update {
99 interfaces: new_list.0.clone(),
100 diff: new_list.diff_from(&state.prev_list),
101 };
102 (state.cb)(update);
103 state.prev_list = new_list;
104 }