1use core_foundation::{
2 array::CFArray,
3 base::{CFType, TCFType, ToVoid},
4 dictionary::CFDictionary,
5 propertylist::CFPropertyList,
6 runloop::{kCFRunLoopCommonModes, CFRunLoop},
7 string::CFString,
8};
9use system_configuration::{
10 dynamic_store::{SCDynamicStore, SCDynamicStoreBuilder, SCDynamicStoreCallBackContext},
11 sys::schema_definitions::kSCPropNetDNSServerAddresses,
12};
13
14fn main() {
18 let callback_context = SCDynamicStoreCallBackContext {
19 callout: my_callback,
20 info: Context { call_count: 0 },
21 };
22
23 let store = SCDynamicStoreBuilder::new("my-watch-dns-store")
24 .callback_context(callback_context)
25 .build();
26
27 let watch_keys: CFArray<CFString> = CFArray::from_CFTypes(&[]);
28 let watch_patterns =
29 CFArray::from_CFTypes(&[CFString::from("(State|Setup):/Network/Service/.*/DNS")]);
30
31 if store.set_notification_keys(&watch_keys, &watch_patterns) {
32 println!("Registered for notifications");
33 } else {
34 panic!("Unable to register notifications");
35 }
36
37 let run_loop_source = store.create_run_loop_source();
38 let run_loop = CFRunLoop::get_current();
39 run_loop.add_source(&run_loop_source, unsafe { kCFRunLoopCommonModes });
40
41 println!("Entering run loop");
42 CFRunLoop::run_current();
43}
44
45#[derive(Debug)]
48struct Context {
49 call_count: u64,
50}
51
52#[allow(clippy::needless_pass_by_value)]
53fn my_callback(store: SCDynamicStore, changed_keys: CFArray<CFString>, context: &mut Context) {
54 context.call_count += 1;
55 println!("Callback call count: {}", context.call_count);
56
57 for key in changed_keys.iter() {
58 if let Some(addresses) = get_dns(&store, key.clone()) {
59 println!("{} changed DNS to {:?}", *key, addresses);
60 } else {
61 println!("{} removed DNS", *key);
62 }
63 }
64}
65
66fn get_dns(store: &SCDynamicStore, path: CFString) -> Option<Vec<String>> {
67 let dns_settings = store
68 .get(path)
69 .and_then(CFPropertyList::downcast_into::<CFDictionary>)?;
70 let address_array = dns_settings
71 .find(unsafe { kSCPropNetDNSServerAddresses }.to_void())
72 .map(|ptr| unsafe { CFType::wrap_under_get_rule(*ptr) })
73 .and_then(CFType::downcast_into::<CFArray>)?;
74 let mut result = Vec::with_capacity(address_array.len() as usize);
75 for address_ptr in &address_array {
76 let address =
77 unsafe { CFType::wrap_under_get_rule(*address_ptr) }.downcast_into::<CFString>()?;
78 result.push(address.to_string())
79 }
80 Some(result)
81}