OpenVPN 3 Core Library
Loading...
Searching...
No Matches
nrpt.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
33#pragma once
34
35#include <string>
36#include <sstream>
37#include <vector>
38#include <array>
39#include <algorithm>
40
45#include <openvpn/win/reg.hpp>
48
49namespace openvpn::TunWin {
50
57template <typename REG, typename NETAPI>
58class Nrpt
59{
60 public:
61 OPENVPN_EXCEPTION(nrpt_error);
62
75 static void create_rule(const std::string &rule_id,
76 const std::wstring &domains,
77 const std::wstring &servers,
78 bool dnssec)
79 {
80 LSTATUS status;
81
82 // Open / create the key
83 typename REG::Key nrpt = open_nrpt_base_key();
84 if (!nrpt.defined())
85 {
86 throw nrpt_error("cannot open NRPT base key");
87 }
88 typename REG::Key rule_key(nrpt(), wstring::from_utf8(rule_id), true);
89 if (!rule_key.defined())
90 {
91 throw nrpt_error("cannot create NRPT rule subkey");
92 }
93
94 // Name
95 status = REG::set_multi_string(rule_key, L"Name", domains);
96 check_reg_error<nrpt_error>(status, "Name");
97
98 // GenericDNSServers
99 status = REG::set_string(rule_key, L"GenericDNSServers", servers);
100 check_reg_error<nrpt_error>(status, "GenericDNSServers");
101
102 // DNSSEC
103 if (dnssec)
104 {
105 status = REG::set_dword(rule_key, L"DNSSECValidationRequired", 1);
106 check_reg_error<nrpt_error>(status, "DNSSECValidationRequired");
107
108 status = REG::set_dword(rule_key, L"DNSSECQueryIPSECRequired", 0);
109 check_reg_error<nrpt_error>(status, "DNSSECQueryIPSECRequired");
110
111 status = REG::set_dword(rule_key, L"DNSSECQueryIPSECEncryption", 0);
112 check_reg_error<nrpt_error>(status, "DNSSECQueryIPSECEncryption");
113 }
114
115 // ConfigOptions
116 // 0x8: Only the Generic DNS server option is specified.
117 // 0xA: The Generic DNS server option and the DNSSEC options are specified
118 status = REG::set_dword(rule_key, L"ConfigOptions", dnssec ? 0xA : 0x8);
119 check_reg_error<nrpt_error>(status, "ConfigOptions");
120
121 // Version
122 status = REG::set_dword(rule_key, L"Version", 2);
123 check_reg_error<nrpt_error>(status, "Version");
124 }
125
136 static void create_exclude_rules(DWORD process_id, const std::vector<std::wstring> &search_domains)
137 {
138 std::uint32_t n = 0;
139 const auto data = collect_exclude_rule_data(search_domains);
140 for (const auto &exclude : data)
141 {
142 const auto id = exclude_rule_id(process_id, n++);
143 create_rule(id, exclude.domains, string::join(exclude.addresses, L";"), false);
144 }
145 }
146
157 static void delete_rules(DWORD process_id)
158 {
159 std::vector<std::wstring> del_subkeys;
160 static constexpr std::array<PCWSTR, 2> nrpt_subkeys{
161 REG::gpol_nrpt_subkey, REG::local_nrpt_subkey};
162 // Only find rules to delete, so that the iterator stays valid
163 for (const auto &nrpt_subkey : nrpt_subkeys)
164 {
165 const auto pid = L"-" + std::to_wstring(process_id);
166 typename REG::Key nrpt_key(nrpt_subkey);
167 typename REG::KeyEnumerator nrpt_rules(nrpt_key);
168
169 for (const auto &nrpt_rule_id : nrpt_rules)
170 {
171 // remove only own policies
172 if (nrpt_rule_id.find(wstring::from_utf8(id_prefix())) != 0)
173 continue;
174 if (process_id && nrpt_rule_id.rfind(pid) != (nrpt_rule_id.size() - pid.size()))
175 continue;
176
177 std::wostringstream rule_subkey;
178 rule_subkey << nrpt_subkey << L"\\" << nrpt_rule_id;
179 del_subkeys.push_back(rule_subkey.str());
180 }
181 }
182 // Now delete the rules
183 for (const auto &subkey : del_subkeys)
184 {
185 REG::delete_subkey(subkey);
186 }
187 }
188
189 private:
195 {
196 std::wstring domains;
197 std::vector<std::wstring> addresses;
198 };
199
206 static std::vector<std::wstring> interface_ipv4_dns_servers(const std::wstring &itf_guid)
207 {
208 typename REG::Key itf_key(std::wstring(REG::subkey_ipv4_itfs) + L"\\" + itf_guid);
209
210 auto [servers, error] = REG::get_string(itf_key, L"NameServer");
211 if (!error && !servers.empty())
212 {
213 return string::split(servers, ',');
214 }
215
216 if (dhcp_enabled_on_itf<REG>(itf_key))
217 {
218 auto [servers, error] = REG::get_string(itf_key, L"DhcpNameServer");
219 if (!error && !servers.empty())
220 {
221 return string::split(servers, ' ');
222 }
223 }
224
225 return {};
226 }
227
234 static std::vector<std::wstring> interface_ipv6_dns_servers(const std::wstring &itf_guid)
235 {
236 typename REG::Key itf_key(std::wstring(REG::subkey_ipv6_itfs) + L"\\" + itf_guid);
237
238 auto [servers, error] = REG::get_string(itf_key, L"NameServer");
239 if (!error && !servers.empty())
240 {
241 return string::split(servers, ',');
242 }
243
244 if (dhcp_enabled_on_itf<REG>(itf_key))
245 {
246 auto [in6_addrs, error] = REG::get_binary(itf_key, L"Dhcpv6DNSServers");
247 if (!error)
248 {
249 std::vector<std::wstring> addresses;
250 size_t in6_addr_count = in6_addrs.size() / sizeof(IN6_ADDR);
251 for (size_t i = 0; i < in6_addr_count; ++i)
252 {
253 WCHAR ipv6[64];
254 IN6_ADDR *in6_addr = reinterpret_cast<IN6_ADDR *>(in6_addrs.data()) + i;
255 if (::InetNtopW(AF_INET6, in6_addr, ipv6, _countof(ipv6)))
256 {
257 addresses.emplace_back(ipv6);
258 }
259 }
260 return addresses;
261 }
262 }
263
264 return {};
265 }
266
279 static std::vector<ExcludeRuleData> collect_exclude_rule_data(const std::vector<std::wstring> &sd)
280 {
281 std::vector<ExcludeRuleData> data;
282 typename REG::Key itfs(REG::subkey_ipv4_itfs);
283 typename REG::KeyEnumerator itf_guids(itfs);
284 for (const auto &itf_guid : itf_guids)
285 {
286 // Ignore interfaces that are not connected or disabled
287 if (!NETAPI::interface_connected(itf_guid))
288 {
289 continue;
290 }
291
292 std::wstring domain = interface_dns_domain<REG>(itf_guid);
293 if (domain.empty() || std::find(sd.begin(), sd.end(), domain) != sd.end())
294 {
295 continue;
296 }
297
298 // Get the DNS server addresses for the interface domain
299 auto addresses = interface_ipv4_dns_servers(itf_guid);
300 const auto addr6 = interface_ipv6_dns_servers(itf_guid);
301 addresses.insert(addresses.end(), addr6.begin(), addr6.end());
302 if (addresses.empty())
303 {
304 continue;
305 }
306
307 // Add a leading '.' to the domain and convert it to MULTI_SZ
308 domain.resize(domain.size() + 3);
309 domain.insert(domain.begin(), L'.');
310 domain.push_back(L'\0');
311 domain.push_back(L'\0');
312
313 data.push_back({domain, addresses});
314 }
315 return data;
316 }
317
328 static typename REG::Key open_nrpt_base_key()
329 {
330 typename REG::Key key(REG::gpol_nrpt_subkey);
331 if (key.defined())
332 {
333 return key;
334 }
335 return typename REG::Key(REG::local_nrpt_subkey);
336 }
337
343 static const char *id_prefix()
344 {
345 static const char prefix[] = "OpenVPNDNSRouting";
346 return prefix;
347 }
348
357 static std::string gen_rule_id(DWORD process_id, bool exclude_rule, std::uint32_t n)
358 {
359 std::ostringstream ss;
360 ss << id_prefix();
361 if (exclude_rule)
362 {
363 ss << "X-" << n;
364 }
365 ss << "-" << process_id;
366 return ss.str();
367 }
368
369 public:
376 static inline std::string rule_id(DWORD process_id)
377 {
378 return gen_rule_id(process_id, false, 0u);
379 }
380
388 static inline std::string exclude_rule_id(DWORD process_id, std::uint32_t n)
389 {
390 return gen_rule_id(process_id, true, n);
391 }
392 class ActionCreate : public Action
393 {
394 public:
395 ActionCreate(DWORD process_id,
396 const std::vector<std::string> &split_domains,
397 const std::vector<std::string> &dns_servers,
398 const std::vector<std::wstring> &search_domains,
399 bool dnssec)
400 : process_id_(process_id),
401 split_domains_(split_domains),
402 dns_servers_(dns_servers),
404 dnssec_(dnssec)
405 {
406 }
407
417 void execute(std::ostream &log) override
418 {
419 // Convert domains into a wide MULTI_SZ string
420 std::wstring domains;
421 if (split_domains_.empty())
422 {
423 // --dns options did not specify any domains to resolve.
424 domains = L".";
425 domains.push_back(L'\0');
426 domains.push_back(L'\0');
428 }
429 else
430 {
431 domains = wstring::pack_string_vector(split_domains_);
432 }
433
434 const std::string id = rule_id(process_id_);
435 const std::wstring servers = wstring::from_utf8(string::join(dns_servers_, ";"));
436 log << to_string() << " id=[" << id << "]" << std::endl;
437 create_rule(id, domains, servers, dnssec_);
438 }
439
445 std::string to_string() const override
446 {
447 std::ostringstream os;
448 os << "NRPT::ActionCreate"
449 << " pid=[" << process_id_ << "]"
450 << " domains=[" << string::join(split_domains_, ",") << "]"
451 << " dns_servers=[" << string::join(dns_servers_, ",") << "]"
452 << " dnssec=[" << dnssec_ << "]";
453 return os.str();
454 }
455
456 private:
458 const std::vector<std::string> split_domains_;
459 const std::vector<std::string> dns_servers_;
460 const std::vector<std::wstring> search_domains_;
461 const bool dnssec_;
462 };
463
464 class ActionDelete : public Action
465 {
466 public:
467 ActionDelete(DWORD process_id)
468 : process_id_(process_id)
469 {
470 }
471
480 void execute(std::ostream &log) override
481 {
482 log << to_string() << std::endl;
484 }
485
491 std::string to_string() const override
492 {
493 std::ostringstream ss;
494 ss << "NRPT::ActionDelete pid=[" << process_id_ << "]";
495 return ss.str();
496 }
497
498 protected:
500 };
501};
502
504
505} // namespace openvpn::TunWin
const std::vector< std::string > split_domains_
Definition nrpt.hpp:458
void execute(std::ostream &log) override
Apply NRPT data to the registry.
Definition nrpt.hpp:417
const std::vector< std::string > dns_servers_
Definition nrpt.hpp:459
std::string to_string() const override
Produce a textual representating of the NRPT data.
Definition nrpt.hpp:445
ActionCreate(DWORD process_id, const std::vector< std::string > &split_domains, const std::vector< std::string > &dns_servers, const std::vector< std::wstring > &search_domains, bool dnssec)
Definition nrpt.hpp:395
const std::vector< std::wstring > search_domains_
Definition nrpt.hpp:460
ActionDelete(DWORD process_id)
Definition nrpt.hpp:467
std::string to_string() const override
Return the log message.
Definition nrpt.hpp:491
void execute(std::ostream &log) override
Delete all rules this process has set.
Definition nrpt.hpp:480
Manage NRPT rules for Windows.
Definition nrpt.hpp:59
static std::vector< ExcludeRuleData > collect_exclude_rule_data(const std::vector< std::wstring > &sd)
Get all the data necessary for excluding local domains from the tunnel.
Definition nrpt.hpp:279
static const char * id_prefix()
Return the rule id prefix any rule starts with.
Definition nrpt.hpp:343
static void delete_rules(DWORD process_id)
Remove our NRPT rules from the registry.
Definition nrpt.hpp:157
static std::string exclude_rule_id(DWORD process_id, std::uint32_t n)
Return a NRPT exclude rule id.
Definition nrpt.hpp:388
static std::vector< std::wstring > interface_ipv6_dns_servers(const std::wstring &itf_guid)
Get IPv6 DNS server addresses of an interface.
Definition nrpt.hpp:234
static void create_exclude_rules(DWORD process_id, const std::vector< std::wstring > &search_domains)
Definition nrpt.hpp:136
static REG::Key open_nrpt_base_key()
Open the NRPT key to store our rules at.
Definition nrpt.hpp:328
static void create_rule(const std::string &rule_id, const std::wstring &domains, const std::wstring &servers, bool dnssec)
Create a NRPT rule in the registry.
Definition nrpt.hpp:75
static std::string rule_id(DWORD process_id)
Return a NRPT rule id.
Definition nrpt.hpp:376
static std::string gen_rule_id(DWORD process_id, bool exclude_rule, std::uint32_t n)
Generate a rule id string.
Definition nrpt.hpp:357
static std::vector< std::wstring > interface_ipv4_dns_servers(const std::wstring &itf_guid)
Get IPv4 DNS server addresses of an interface.
Definition nrpt.hpp:206
OPENVPN_EXCEPTION(nrpt_error)
DNS utilities for Windows.
auto join(const T &strings, const typename T::value_type &delim, const bool tail=false)
Definition string.hpp:521
std::vector< T > split(const T &str, const typename T::value_type sep, const int maxsplit=-1)
Definition string.hpp:492
std::vector< std::wstring > addresses
Definition nrpt.hpp:197
remote_address ipv6
dns_options search_domains
dns_options servers[0]
std::ostringstream os