OpenVPN 3 Core Library
Loading...
Searching...
No Matches
dns.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) 2022- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12
13#pragma once
14
15#include <vector>
16#include <cstdint>
17#include <algorithm>
18#include <sstream>
19
22
23#ifdef HAVE_JSON
25#endif
26
27namespace openvpn {
28
34{
35 DnsOptionsParser(const OptionList &opt, bool use_dhcp_search_domains_as_split_domains)
36 {
38 parse_dhcp_options(opt, use_dhcp_search_domains_as_split_domains, !servers.empty());
39 if (!parse_errors.empty())
40 {
41 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, parse_errors);
42 }
43 }
44
45 static int parse_priority(const std::string &prio_str)
46 {
47 const auto min_prio = std::numeric_limits<std::int8_t>::min();
48 const auto max_prio = std::numeric_limits<std::int8_t>::max();
49
50 int priority;
51 if (!parse_number_validate<int>(prio_str, 4, min_prio, max_prio, &priority))
52 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, "dns server priority '" << prio_str << "' invalid");
53 return priority;
54 }
55
56 protected:
57 std::string parse_errors;
58
60 {
61 auto indices = opt.get_index_ptr("dns");
62 if (indices == nullptr)
63 {
64 return;
65 }
66
67 for (const auto i : *indices)
68 {
69 try
70 {
71 const auto &o = opt[i];
72 if (o.size() >= 3 && o.ref(1) == "search-domains")
73 {
74 for (std::size_t j = 2; j < o.size(); j++)
75 {
76 search_domains.push_back({o.ref(j)});
77 }
78 }
79 else if (o.size() >= 5 && o.ref(1) == "server")
80 {
81 auto priority = parse_priority(o.ref(2));
82 auto &server = get_server(priority);
83
84 const auto &server_suboption = o.ref(3);
85 if (server_suboption == "address" && o.size() <= 12)
86 {
87 for (std::size_t j = 4; j < o.size(); j++)
88 {
89 IP::Addr addr;
90 unsigned int port = 0;
91 std::string addr_str = o.ref(j);
92
93 const bool v4_port_found = addr_str.find(':') != std::string::npos
94 && addr_str.find(':') == addr_str.rfind(':');
95
96 if (addr_str[0] == '[' || v4_port_found)
97 {
98 const auto &addr_port_str = o.ref(j);
99 std::string port_str;
100 if (!HostPort::split_host_port(addr_port_str, addr_str, port_str, "", false, &port))
101 {
102 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, "dns server " << priority << " invalid address: " << addr_port_str);
103 }
104 }
105
106 try
107 {
108 addr = IP::Addr(addr_str);
109 }
110 catch (const IP::ip_exception &)
111 {
112 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, "dns server " << priority << " invalid address: " << addr_str);
113 }
114
115 server.addresses.push_back({addr.to_string(), port});
116 }
117 }
118 else if (server_suboption == "resolve-domains")
119 {
120 for (std::size_t j = 4; j < o.size(); j++)
121 {
122 server.domains.push_back({o.ref(j)});
123 }
124 }
125 else if (server_suboption == "dnssec" && o.size() == 5)
126 {
127 const auto &dnssec_value = o.ref(4);
128 if (dnssec_value == "yes")
129 {
130 server.dnssec = DnsServer::Security::Yes;
131 }
132 else if (dnssec_value == "no")
133 {
134 server.dnssec = DnsServer::Security::No;
135 }
136 else if (dnssec_value == "optional")
137 {
138 server.dnssec = DnsServer::Security::Optional;
139 }
140 else
141 {
142 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, "dns server " << priority << " dnssec setting '" << dnssec_value << "' invalid");
143 }
144 }
145 else if (server_suboption == "transport" && o.size() == 5)
146 {
147 const auto &transport_value = o.ref(4);
148 if (transport_value == "plain")
149 {
150 server.transport = DnsServer::Transport::Plain;
151 }
152 else if (transport_value == "DoH")
153 {
154 server.transport = DnsServer::Transport::HTTPS;
155 }
156 else if (transport_value == "DoT")
157 {
158 server.transport = DnsServer::Transport::TLS;
159 }
160 else
161 {
162 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, "dns server " << priority << " transport '" << transport_value << "' invalid");
163 }
164 }
165 else if (server_suboption == "sni" && o.size() == 5)
166 {
167 server.sni = o.ref(4);
168 }
169 else
170 {
171 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, "dns server " << priority << " option '" << server_suboption << "' unknown or too many parameters");
172 }
173 }
174 else
175 {
176 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, "dns option unknown or invalid number of parameters " << o.render(Option::RENDER_TRUNC_64 | Option::RENDER_BRACKET));
177 }
178 }
179 catch (const std::exception &e)
180 {
181 parse_errors += "\n";
182 parse_errors += e.what();
183 }
184 }
185
186 // Check and remove servers without an address
187 std::vector<int> remove_servers;
188 for (const auto &[priority, server] : servers)
189 {
190 if (server.addresses.empty())
191 {
192 parse_errors += "\n";
193 parse_errors += "dns server " + std::to_string(priority) + " does not have an address assigned";
194 remove_servers.push_back(priority);
195 }
196 }
197 for (const auto &prio : remove_servers)
198 {
199 servers.erase(prio);
200 }
201
202 // Clear search domains when no servers were configured
203 if (servers.empty())
204 {
205 search_domains.clear();
206 }
207 }
208
209 void parse_dhcp_options(const OptionList &opt, bool use_search_as_split_domains, bool ignore_values)
210 {
211 auto dhcp_map = opt.map().find("dhcp-option");
212 if (dhcp_map == opt.map().end())
213 {
214 return;
215 }
216
217 // Example:
218 // [dhcp-option] [DNS] [172.16.0.23]
219 // [dhcp-option] [DOMAIN] [openvpn.net]
220 // [dhcp-option] [DOMAIN] [example.com]
221 // [dhcp-option] [DOMAIN] [foo1.com foo2.com foo3.com ...]
222 // [dhcp-option] [DOMAIN] [bar1.com] [bar2.com] [bar3.com] ...
223 // [dhcp-option] [ADAPTER_DOMAIN_SUFFIX] [mycompany.com]
224 std::string adapter_domain_suffix;
225 for (const auto &i : dhcp_map->second)
226 {
227 try
228 {
229
230 const Option &o = opt[i];
231 const std::string &type = o.get(1, 64);
232 if (type == "DNS" || type == "DNS6")
233 {
234 o.exact_args(3);
235 try
236 {
237 const IP::Addr addr = IP::Addr::from_string(o.get(2, 256), "dns-server-ip");
238 if (!ignore_values)
239 {
240 auto &server = get_server(0);
241 server.addresses.push_back({addr.to_string(), 0});
242 from_dhcp_options = true;
243 }
244 }
245 catch (const IP::ip_exception &)
246 {
247 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_DNS, o.render(Option::RENDER_BRACKET) << " invalid address");
248 }
249 }
250 else if (type == "DOMAIN" || type == "DOMAIN-SEARCH")
251 {
252 o.min_args(3);
253 for (size_t i = 2; i < o.size(); ++i)
254 {
255 using StrVec = std::vector<std::string>;
256 StrVec domains = Split::by_space<StrVec, StandardLex, SpaceMatch, Split::NullLimit>(o.get(i, 256));
257 if (ignore_values)
258 {
259 continue;
260 }
261 for (const auto &domain : domains)
262 {
263 from_dhcp_options = true;
264 if (use_search_as_split_domains)
265 {
266 auto &server = get_server(0);
267 server.domains.push_back({domain});
268 }
269 else
270 {
271 search_domains.push_back({domain});
272 }
273 }
274 }
275 }
276 else if (type == "ADAPTER_DOMAIN_SUFFIX")
277 {
278 o.exact_args(3);
279 if (!ignore_values)
280 {
281 adapter_domain_suffix = o.ref(2);
282 from_dhcp_options = true;
283 }
284 }
285 }
286 catch (const std::exception &e)
287 {
288 parse_errors += "\n";
289 parse_errors += e.what();
290 }
291 }
292
293 if (!adapter_domain_suffix.empty())
294 {
295 search_domains.insert(search_domains.begin(), {std::move(adapter_domain_suffix)});
296 }
297
298 if (!ignore_values && servers.size() && servers[0].addresses.empty())
299 {
300 parse_errors += "\n";
301 parse_errors += "dns server does not have an address assigned";
302 servers.clear();
303 }
304 }
305};
306
308{
309 using PriorityList = std::vector<std::int8_t>;
310
312 {
313 DnsFilter(PriorityList &&pushed_prios)
314 : pushed_prios_(std::forward<PriorityList>(pushed_prios))
315 {
316 }
317
318 bool filter(const Option &opt) override
319 {
320 if (opt.empty()
321 || opt.size() < 3
322 || opt.ref(0) != "dns"
323 || opt.ref(1) != "server")
324 {
325 return true;
326 }
327 const auto priority = DnsOptionsParser::parse_priority(opt.ref(2));
328 const auto it = std::find(pushed_prios_.begin(), pushed_prios_.end(), priority);
329
330 // Filter out server option if an option with this priority was pushed
331 return it == pushed_prios_.end() ? true : false;
332 }
333
334 protected:
336 };
337
338 void merge(OptionList &pushed, const OptionList &config) const override
339 {
340 PriorityList pushed_prios;
341
342 auto indices = pushed.get_index_ptr("dns");
343 if (indices)
344 {
345 for (const auto i : *indices)
346 {
347 if (pushed[i].size() < 3 || pushed[i].ref(1) != "server")
348 continue;
349 const auto priority = DnsOptionsParser::parse_priority(pushed[i].ref(2));
350 pushed_prios.emplace_back(priority);
351 }
352 }
353
354 DnsFilter filter(std::move(pushed_prios));
355 pushed.extend(config, &filter);
356 }
357};
358
359} // namespace openvpn
static Addr from_string(const std::string &ipstr, const TITLE &title, const Version required_version)
Definition ip.hpp:105
std::string to_string() const
Definition ip.hpp:528
const IndexMap & map() const
Definition options.hpp:1562
void extend(const OptionList &other, FilterBase *filt=nullptr)
Definition options.hpp:1123
const IndexList * get_index_ptr(const std::string &name) const
Definition options.hpp:1276
void exact_args(const size_t n) const
Definition options.hpp:136
const std::string & get(const size_t index, const size_t max_len) const
Definition options.hpp:187
size_t size() const
Definition options.hpp:327
void min_args(const size_t n) const
Definition options.hpp:129
bool empty() const
Definition options.hpp:331
const std::string & ref(const size_t i) const
Definition options.hpp:353
std::string render(const unsigned int flags) const
Definition options.hpp:264
#define OPENVPN_THROW_ARG1(exc, arg, stuff)
bool split_host_port(const std::string &str, std::string &host, std::string &port, const std::string &default_port, const bool allow_unix, unsigned int *port_save=nullptr)
Definition hostport.hpp:99
DnsFilter(PriorityList &&pushed_prios)
Definition dns.hpp:313
const PriorityList pushed_prios_
Definition dns.hpp:335
bool filter(const Option &opt) override
Definition dns.hpp:318
std::vector< std::int8_t > PriorityList
Definition dns.hpp:309
void merge(OptionList &pushed, const OptionList &config) const override
Definition dns.hpp:338
void parse_dns_options(const OptionList &opt)
Definition dns.hpp:59
static int parse_priority(const std::string &prio_str)
Definition dns.hpp:45
std::string parse_errors
Definition dns.hpp:57
void parse_dhcp_options(const OptionList &opt, bool use_search_as_split_domains, bool ignore_values)
Definition dns.hpp:209
DnsOptionsParser(const OptionList &opt, bool use_dhcp_search_domains_as_split_domains)
Definition dns.hpp:35
All DNS options set with the –dns or –dhcp-option directive.
std::map< int, DnsServer > servers
DnsServer & get_server(const int priority)
std::vector< DnsDomain > search_domains
proxy_host_port port
static const char config[]