]> code.octet-stream.net Git - netwatcher/blob - src/watch_win.rs
First go at a Windows watch implementation
[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 pub 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) + 'static>,
27 }
28
29 pub 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) + 'static>(
43 mut callback: F,
44 ) -> Result<WatchHandle, Error> {
45 let null_list = List::default();
46 let prev_list = crate::list::list_interfaces()?;
47 callback(Update {
48 interfaces: prev_list.0.clone(),
49 diff: prev_list.diff_from(&null_list),
50 });
51
52 // TODO: Can wo do something about the race condition?
53 let state = Box::pin(Mutex::new(WatchState {
54 prev_list,
55 cb: Box::new(callback),
56 }));
57 let state_ctx = &*state.as_ref() as *const _ as *const c_void;
58
59 let mut hnd = HANDLE::default();
60 let res = unsafe {
61 NotifyUnicastIpAddressChange(
62 AF_UNSPEC,
63 Some(notif),
64 Some(state_ctx),
65 BOOLEAN(0),
66 &mut hnd,
67 )
68 };
69 match res {
70 NO_ERROR => Ok(WatchHandle { hnd, _state: state }),
71 ERROR_INVALID_HANDLE => Err(Error::Internal),
72 ERROR_INVALID_PARAMETER => Err(Error::Internal),
73 ERROR_NOT_ENOUGH_MEMORY => Err(Error::Internal),
74 _ => Err(Error::Internal), // TODO: Use FormatMessage and get real error
75 }
76 }
77
78 unsafe extern "system" fn notif(
79 ctx: *const c_void,
80 _row: *const MIB_UNICASTIPADDRESS_ROW,
81 _notification_type: MIB_NOTIFICATION_TYPE,
82 ) {
83 println!("There was a change!");
84 let Ok(new_list) = crate::list::list_interfaces() else {
85 println!("Failed to get list of interfaces on change");
86 return;
87 };
88 let state_ptr = ctx as *const Mutex<WatchState>;
89 unsafe {
90 let state_guard = &mut *state_ptr.as_ref().unwrap().lock().unwrap();
91 if new_list == state_guard.prev_list {
92 // TODO: Hitting this a lot, is it true?
93 println!("Interfaces seem to be the same, ignoring");
94 return;
95 }
96 let update = Update {
97 interfaces: new_list.0.clone(),
98 diff: new_list.diff_from(&state_guard.prev_list),
99 };
100 (state_guard.cb)(update);
101 state_guard.prev_list = new_list;
102 }
103 }