summaryrefslogtreecommitdiff
path: root/vendor/system-configuration/examples/watch_dns.rs
blob: d145c2a6e1b2e1a5e0219db48557dc8fb62878fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use core_foundation::{
    array::CFArray,
    base::{CFType, TCFType, ToVoid},
    dictionary::CFDictionary,
    propertylist::CFPropertyList,
    runloop::{kCFRunLoopCommonModes, CFRunLoop},
    string::CFString,
};
use system_configuration::{
    dynamic_store::{SCDynamicStore, SCDynamicStoreBuilder, SCDynamicStoreCallBackContext},
    sys::schema_definitions::kSCPropNetDNSServerAddresses,
};

// This example will watch the dynamic store for changes to any DNS setting. As soon as a change
// is detected, it will be printed to stdout.

fn main() {
    let callback_context = SCDynamicStoreCallBackContext {
        callout: my_callback,
        info: Context { call_count: 0 },
    };

    let store = SCDynamicStoreBuilder::new("my-watch-dns-store")
        .callback_context(callback_context)
        .build();

    let watch_keys: CFArray<CFString> = CFArray::from_CFTypes(&[]);
    let watch_patterns =
        CFArray::from_CFTypes(&[CFString::from("(State|Setup):/Network/Service/.*/DNS")]);

    if store.set_notification_keys(&watch_keys, &watch_patterns) {
        println!("Registered for notifications");
    } else {
        panic!("Unable to register notifications");
    }

    let run_loop_source = store.create_run_loop_source();
    let run_loop = CFRunLoop::get_current();
    run_loop.add_source(&run_loop_source, unsafe { kCFRunLoopCommonModes });

    println!("Entering run loop");
    CFRunLoop::run_current();
}

/// This struct acts as a user provided context/payload to each notification callback.
/// Here one can store any type of data or state needed in the callback function.
#[derive(Debug)]
struct Context {
    call_count: u64,
}

#[allow(clippy::needless_pass_by_value)]
fn my_callback(store: SCDynamicStore, changed_keys: CFArray<CFString>, context: &mut Context) {
    context.call_count += 1;
    println!("Callback call count: {}", context.call_count);

    for key in changed_keys.iter() {
        if let Some(addresses) = get_dns(&store, key.clone()) {
            println!("{} changed DNS to {:?}", *key, addresses);
        } else {
            println!("{} removed DNS", *key);
        }
    }
}

fn get_dns(store: &SCDynamicStore, path: CFString) -> Option<Vec<String>> {
    let dns_settings = store
        .get(path)
        .and_then(CFPropertyList::downcast_into::<CFDictionary>)?;
    let address_array = dns_settings
        .find(unsafe { kSCPropNetDNSServerAddresses }.to_void())
        .map(|ptr| unsafe { CFType::wrap_under_get_rule(*ptr) })
        .and_then(CFType::downcast_into::<CFArray>)?;
    let mut result = Vec::with_capacity(address_array.len() as usize);
    for address_ptr in &address_array {
        let address =
            unsafe { CFType::wrap_under_get_rule(*address_ptr) }.downcast_into::<CFString>()?;
        result.push(address.to_string())
    }
    Some(result)
}