OpenVPN 3 Core Library
Loading...
Searching...
No Matches
macdns_watchdog.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// DNS utilities for Mac
13
14#ifndef OPENVPN_TUN_MAC_MACDNS_WATCHDOG_H
15#define OPENVPN_TUN_MAC_MACDNS_WATCHDOG_H
16
17#include <thread>
18#include <mutex>
19
25
26namespace openvpn {
27OPENVPN_EXCEPTION(macdns_watchdog_error);
28
29class MacDNSWatchdog : public RC<thread_unsafe_refcount>
30{
31 public:
33
34 // flags
35 enum
36 {
37 ENABLE_WATCHDOG = (1 << 0),
38 SYNCHRONOUS = (1 << 1),
39 FLUSH_RECONFIG = (1 << 2),
40 };
41
42 class DNSAction : public Action
43 {
44 public:
46
47 DNSAction(const MacDNSWatchdog::Ptr &parent_arg,
48 const MacDNS::Config::Ptr &config_arg,
49 const unsigned int flags_arg)
50 : parent(parent_arg),
51 config(config_arg),
52 flags(flags_arg)
53 {
54 }
55
56 void execute(std::ostream &os) override
57 {
58 os << to_string() << std::endl;
59 if (parent)
60 parent->setdns(config, flags);
61 }
62
63 std::string to_string() const override
64 {
65 std::ostringstream os;
66 os << "MacDNSAction: FLAGS=";
68 os << 'E';
69 if (flags & SYNCHRONOUS)
70 os << 'S';
72 os << 'F';
73 if (config)
74 os << ' ' << config->to_string();
75 return os.str();
76 }
77
78 private:
81 const unsigned int flags;
82 };
83
85 : macdns(new MacDNS("OpenVPNConnect")),
86 thread(nullptr)
87 {
88 }
89
91 {
93 }
94
95 static void add_actions(const MacDNS::Config::Ptr &dns,
96 const unsigned int flags,
97 ActionList &create,
98 ActionList &destroy)
99 {
101 MacDNS::Config::Ptr dns_remove;
102 DNSAction::Ptr create_action(new DNSAction(watchdog, dns, flags));
103 DNSAction::Ptr destroy_action(new DNSAction(watchdog, dns_remove, flags));
104 create.add(create_action);
105 destroy.add(destroy_action);
106 }
107
108 private:
109 bool setdns(const MacDNS::Config::Ptr &config, const unsigned int flags)
110 {
111 bool mod = false;
112 if (config)
113 {
114 if ((flags & SYNCHRONOUS) || !(flags & ENABLE_WATCHDOG))
115 stop_thread();
116 config_ = config;
118 {
119 if (!thread)
120 {
121 mod = macdns->setdns(*config_);
122 thread = new std::thread(&MacDNSWatchdog::thread_func, this);
123 }
124 else
125 {
126 if (runloop.defined())
128 else
129 OPENVPN_LOG("MacDNSWatchdog::setdns: runloop undefined");
130 }
131 }
132 else
133 {
134 mod = macdns->setdns(*config_);
135 }
136 }
137 else
138 {
139 stop_thread();
140 config_.reset();
141 mod = macdns->resetdns();
142 }
143 if (mod && (flags & FLUSH_RECONFIG))
144 {
147 }
148 return mod;
149 }
150
151 std::string to_string() const
152 {
154 if (config)
155 return config->to_string();
156 else
157 return std::string("UNDEF");
158 }
159
161 {
162 if (thread)
163 {
164 if (runloop.defined())
165 CFRunLoopStop(runloop());
166 thread->join();
167 delete thread;
168 thread = nullptr;
169 }
170 }
171
172 // All methods below this point called in the context of watchdog thread
173 // except for schedule_push_timer which may be called from parent thread
174 // as well.
176 {
177 runloop.reset(CFRunLoopGetCurrent(), CF::GET);
178 Log::Context logctx(logwrap);
179
180 try
181 {
182 SCDynamicStoreContext context = {0, this, nullptr, nullptr, nullptr};
183 CF::DynamicStore ds(SCDynamicStoreCreate(kCFAllocatorDefault,
184 CFSTR("OpenVPN_MacDNSWatchdog"),
186 &context));
187 if (!ds.defined())
188 throw macdns_watchdog_error("SCDynamicStoreCreate");
189 const CF::Array watched_keys(macdns->dskey_array());
190 if (!watched_keys.defined())
191 throw macdns_watchdog_error("watched_keys is undefined");
192 if (!SCDynamicStoreSetNotificationKeys(ds(),
193 watched_keys(),
194 nullptr))
195 throw macdns_watchdog_error("SCDynamicStoreSetNotificationKeys failed");
196 CF::RunLoopSource rls(SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, ds(), 0));
197 if (!rls.defined())
198 throw macdns_watchdog_error("SCDynamicStoreCreateRunLoopSource failed");
199 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls(), kCFRunLoopDefaultMode);
200
201 // process event loop until CFRunLoopStop is called from parent thread
202 CFRunLoopRun();
203 }
204 catch (const std::exception &e)
205 {
206 OPENVPN_LOG("MacDNSWatchdog::thread_func exception: " << e.what());
207 }
209 }
210
211 static void callback_static(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
212 {
213 MacDNSWatchdog *self = (MacDNSWatchdog *)arg;
214 self->callback(store, changedKeys);
215 }
216
217 void callback(SCDynamicStoreRef store, CFArrayRef changedKeys)
218 {
219 // DNS Watchdog delay from the time that change is detected
220 // to when we forcibly revert it (seconds).
222 }
223
224 void schedule_push_timer(const int seconds)
225 {
226 std::lock_guard<std::mutex> lock(push_timer_lock);
227 CFRunLoopTimerContext context = {0, this, nullptr, nullptr, nullptr};
229 push_timer.reset(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + seconds, 0, 0, 0, push_timer_callback_static, &context));
230 if (push_timer.defined())
231 CFRunLoopAddTimer(runloop(), push_timer(), kCFRunLoopCommonModes);
232 else
233 OPENVPN_LOG("MacDNSWatchdog::schedule_push_timer: failed to create timer");
234 }
235
237 {
238 if (push_timer.defined())
239 {
240 CFRunLoopTimerInvalidate(push_timer());
241 push_timer.reset(nullptr);
242 }
243 }
244
246 {
247 std::lock_guard<std::mutex> lock(push_timer_lock);
249 }
250
251 static void push_timer_callback_static(CFRunLoopTimerRef timer, void *info)
252 {
253 MacDNSWatchdog *self = (MacDNSWatchdog *)info;
254 self->push_timer_callback(timer);
255 }
256
257 void push_timer_callback(CFRunLoopTimerRef timer)
258 {
259 try
260 {
261 // reset DNS settings after watcher detected modifications by third party
263 if (macdns->setdns(*config))
264 OPENVPN_LOG("MacDNSWatchdog: updated DNS settings");
265 }
266 catch (const std::exception &e)
267 {
268 OPENVPN_LOG("MacDNSWatchdog::push_timer_callback exception: " << e.what());
269 }
270 }
271
272
275
276 std::thread *thread; // watcher thread
277 CF::RunLoop runloop; // run loop in watcher thread
278 CF::Timer push_timer; // watcher thread timer
279 std::mutex push_timer_lock;
280 Log::Context::Wrapper logwrap; // used to carry forward the log context from parent thread
281};
282} // namespace openvpn
283
284#endif
void add(Action *action)
Definition action.hpp:57
const MacDNSWatchdog::Ptr parent
void execute(std::ostream &os) override
const MacDNS::Config::Ptr config
std::string to_string() const override
DNSAction(const MacDNSWatchdog::Ptr &parent_arg, const MacDNS::Config::Ptr &config_arg, const unsigned int flags_arg)
void callback(SCDynamicStoreRef store, CFArrayRef changedKeys)
RCPtr< MacDNSWatchdog > Ptr
bool setdns(const MacDNS::Config::Ptr &config, const unsigned int flags)
static void add_actions(const MacDNS::Config::Ptr &dns, const unsigned int flags, ActionList &create, ActionList &destroy)
void push_timer_callback(CFRunLoopTimerRef timer)
static void push_timer_callback_static(CFRunLoopTimerRef timer, void *info)
std::string to_string() const
Log::Context::Wrapper logwrap
static void callback_static(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
MacDNS::Config::Ptr config_
void schedule_push_timer(const int seconds)
CF::Array dskey_array() const
Definition macdns.hpp:309
bool setdns(const Config &config)
Definition macdns.hpp:177
void flush_cache()
Definition macdns.hpp:148
bool signal_network_reconfiguration()
Definition macdns.hpp:172
bool resetdns()
Definition macdns.hpp:269
The smart pointer class.
Definition rc.hpp:119
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:912
#define OPENVPN_EXCEPTION(C)
#define OPENVPN_LOG(args)
Argument to construct a Context in a different thread.
Scoped RAII for the global_log pointer.
reroute_gw flags
std::ostringstream os
static const char config[]