OpenVPN 3 Core Library
Loading...
Searching...
No Matches
dns_options.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 <map>
16#include <vector>
17#include <algorithm>
18#include <sstream>
19
24#include <openvpn/addr/ip.hpp>
25
26#ifdef HAVE_JSON
27#ifndef OPENVPN_JSON
28#include <json/json.h>
29#endif
31#endif
32
33namespace openvpn {
34
40{
41 DnsAddress() = default;
42 virtual ~DnsAddress() noexcept = default;
43
55 explicit DnsAddress(const std::string &address_input)
56 {
57 IP::Addr addr;
58 std::string addr_str = address_input;
59 port = 0;
60
61 bool ipv6_bracket_encaps = address_input.length() > 2
62 && address_input[0] == '['
63 && address_input.rfind(']') != std::string::npos;
64
65 auto first_colon_pos = address_input.find(':');
66 auto last_colon_pos = address_input.rfind(':');
67 const bool v6_port_found = ipv6_bracket_encaps
68 && last_colon_pos > address_input.rfind(']');
69
70 const bool v4_port_found = first_colon_pos != std::string::npos
71 && first_colon_pos == addr_str.rfind(':');
72
73 if (v6_port_found || v4_port_found)
74 {
75 std::string port_str;
76 if (!HostPort::split_host_port(address_input, addr_str, port_str, "", false, &port))
77 {
78 OPENVPN_THROW_EXCEPTION("Invalid address:port format '" << address_input << "'");
79 }
80
81 // If it was an IPv6 address, the bracket encapsulation has been removed
82 // by HostPort::split_host_port()
83 ipv6_bracket_encaps = false;
84 }
85
86 try
87 {
88 if (ipv6_bracket_encaps)
89 {
90 addr_str = addr_str.substr(1, addr_str.length() - 2);
91 }
92 addr = IP::Addr(addr_str, "dns-ip-address");
93 }
94 catch (const IP::ip_exception &)
95 {
96 OPENVPN_THROW_EXCEPTION("Invalid address '" << addr_str << "'");
97 }
98 address = addr.to_string();
99 }
100
101
111 std::string to_string() const
112 {
113 std::ostringstream os;
114
115 const IP::Addr addr(address);
116 os << (addr.is_ipv6() && port ? "[" : "")
117 << address
118 << (addr.is_ipv6() && port ? "]" : "");
119
120 if (port)
121 {
122 os << ":" << port;
123 }
124 return os.str();
125 }
126
127 void validate(const std::string &title) const
128 {
130 }
131
132#ifdef HAVE_JSON
133 Json::Value to_json() const
134 {
135 Json::Value root(Json::objectValue);
136 root["address"] = Json::Value(address);
137 if (port)
138 {
139 root["port"] = Json::Value(port);
140 }
141 return root;
142 }
143
144 void from_json(const Json::Value &root, const std::string &title)
145 {
146 json::assert_dict(root, title);
147 json::to_uint_optional(root, port, "port", 0U, title);
148
149 std::string addr_str;
150 json::to_string(root, addr_str, "address", title);
152 }
153#endif
154
155 bool operator==(const DnsAddress &) const = default;
156
157 std::string address;
158 unsigned int port = 0;
159};
160
166{
167 DnsDomain() = default;
168 virtual ~DnsDomain() noexcept = default;
169
170 explicit DnsDomain(const std::string &domain_)
171 {
172 domain = domain_;
173 }
174
180 std::string to_string() const
181 {
182 return domain;
183 }
184
185 void validate(const std::string &title) const
186 {
188 }
189
190#ifdef HAVE_JSON
191 Json::Value to_json() const
192 {
193 return Json::Value(domain);
194 }
195
196 void from_json(const Json::Value &value, const std::string &title)
197 {
198 if (!value.isString())
199 {
200 throw json::json_parse("string " + title + " is of incorrect type");
201 }
202 domain = value.asString();
203 }
204#endif
205
206 std::string domain;
207
208 bool operator==(const DnsDomain &) const = default;
209};
210
216{
217 enum class Security
218 {
219 Unset,
220 No,
221 Yes,
222 Optional
223 };
224
232 static std::string dnssec_string(const Security dnssec)
233 {
234 switch (dnssec)
235 {
236 case Security::No:
237 return "No";
238 case Security::Yes:
239 return "Yes";
241 return "Optional";
242 default:
243 return "Unset";
244 }
245 }
246
247
254 std::string dnssec_string() const
255 {
256 return dnssec_string(dnssec);
257 }
258
259
268 void parse_dnssec_value(const std::string &dnssec_value)
269 {
270 if (dnssec_value == "yes")
271 {
273 }
274 else if (dnssec_value == "no")
275 {
277 }
278 else if (dnssec_value == "optional")
279 {
281 }
282 else
283 {
284 OPENVPN_THROW_EXCEPTION("Invalid DNSSEC value '" << dnssec_value << "'");
285 }
286 }
287
288 enum class Transport
289 {
290 Unset,
291 Plain,
292 HTTPS,
293 TLS
294 };
295
303 static std::string transport_string(const Transport transport)
304 {
305 switch (transport)
306 {
307 case Transport::Plain:
308 return "Plain";
309 case Transport::HTTPS:
310 return "HTTPS";
311 case Transport::TLS:
312 return "TLS";
313 default:
314 return "Unset";
315 }
316 }
317
324 std::string transport_string() const
325 {
327 }
328
337 void parse_transport_value(const std::string &transport_value)
338 {
339 if (transport_value == "plain")
340 {
342 }
343 else if (transport_value == "DoH")
344 {
346 }
347 else if (transport_value == "DoT")
348 {
350 }
351 else
352 {
353 OPENVPN_THROW_EXCEPTION("Invalid transport value '" << transport_value << "'");
354 }
355 }
356
357 DnsServer() = default;
358 virtual ~DnsServer() noexcept = default;
359
360#ifdef HAVE_JSON
368 explicit DnsServer(const Json::Value &root, const std::string &title = "")
369 {
370 from_json(root, title);
371 }
372#endif
373
380 std::string to_string(const char *prefix = "") const
381 {
382 std::ostringstream os;
383 os << prefix << "Addresses:\n";
384 for (const auto &address : addresses)
385 {
386 os << prefix << " " << address.to_string() << '\n';
387 }
388 if (!domains.empty())
389 {
390 os << prefix << "Domains:\n";
391 for (const auto &domain : domains)
392 {
393 os << prefix << " " << domain.to_string() << '\n';
394 }
395 }
396 if (dnssec != Security::Unset)
397 {
398 os << prefix << "DNSSEC: " << dnssec_string(dnssec) << '\n';
399 }
401 {
402 os << prefix << "Transport: " << transport_string(transport) << '\n';
403 }
404 if (!sni.empty())
405 {
406 os << prefix << "SNI: " << sni << '\n';
407 }
408 return os.str();
409 }
410
411#ifdef HAVE_JSON
423 Json::Value to_json() const
424 {
425 Json::Value server(Json::objectValue);
426 json::from_vector(server, addresses, "addresses");
427 if (!domains.empty())
428 {
429 json::from_vector(server, domains, "domains");
430 }
431 if (dnssec != Security::Unset)
432 {
433 server["dnssec"] = Json::Value(dnssec_string(dnssec));
434 }
436 {
437 server["transport"] = Json::Value(transport_string(transport));
438 }
439 if (!sni.empty())
440 {
441 server["sni"] = Json::Value(sni);
442 }
443 return server;
444 }
445
454 void from_json(const Json::Value &root, const std::string &title)
455 {
456 json::assert_dict(root, title);
457 json::to_vector(root, addresses, "addresses", title);
458 if (json::exists(root, "domains"))
459 {
460 json::to_vector(root, domains, "domains", title);
461 }
462 if (json::exists(root, "dnssec"))
463 {
464 std::string dnssec_str;
465 json::to_string(root, dnssec_str, "dnssec", title);
466 if (dnssec_str == "Optional")
467 {
469 }
470 else if (dnssec_str == "Yes")
471 {
473 }
474 else if (dnssec_str == "No")
475 {
477 }
478 else
479 {
480 throw json::json_parse("dnssec value " + dnssec_str + "is unknown");
481 }
482 }
483 if (json::exists(root, "transport"))
484 {
485 std::string transport_str;
486 json::to_string(root, transport_str, "transport", title);
487 if (transport_str == "Plain")
488 {
490 }
491 else if (transport_str == "HTTPS")
492 {
494 }
495 else if (transport_str == "TLS")
496 {
498 }
499 else
500 {
501 throw json::json_parse("transport value " + transport_str + "is unknown");
502 }
503 }
504 json::to_string_optional(root, sni, "sni", "", title);
505 }
506#endif
507
508 bool operator==(const DnsServer &at) const = default;
509
511 std::vector<DnsAddress> addresses;
512
514 std::vector<DnsDomain> domains;
515
518
521
523 std::string sni;
524};
525
531{
532 DnsOptions() = default;
533 ~DnsOptions() noexcept = default;
534
535#ifdef HAVE_JSON
543 explicit DnsOptions(const Json::Value &root, const std::string &title = "")
544 {
545 from_json(root, title);
546 }
547#endif
548
555 std::string to_string() const
556 {
557 std::ostringstream os;
558 if (!servers.empty())
559 {
560 os << "DNS Servers:\n";
561 for (const auto &[priority, server] : servers)
562 {
563 os << " Priority: " << priority << '\n';
564 os << server.to_string(" ");
565 }
566 }
567 if (!search_domains.empty())
568 {
569 os << "DNS Search Domains:\n";
570 for (const auto &domain : search_domains)
571 {
572 os << " " << domain.to_string() << '\n';
573 }
574 }
575 os << "Values from dhcp-options: " << (from_dhcp_options ? "true" : "false") << '\n';
576 return os.str();
577 }
578
579#ifdef HAVE_JSON
591 Json::Value to_json() const
592 {
593 Json::Value root(Json::objectValue);
594 Json::Value servers_json(Json::objectValue);
595 for (const auto &[prio, server] : servers)
596 {
597 servers_json[std::to_string(prio)] = server.to_json();
598 }
599 root["servers"] = std::move(servers_json);
600 json::from_vector(root, search_domains, "search_domains");
601 root["from_dhcp_options"] = Json::Value(from_dhcp_options);
602 return root;
603 }
604
613 void from_json(const Json::Value &root, const std::string &title)
614 {
615 json::assert_dict(root, title);
616 json::assert_dict(root["servers"], title);
617 for (const auto &prio : root["servers"].getMemberNames())
618 {
619 DnsServer server(root["servers"][prio], title);
620 servers[std::stoi(prio)] = std::move(server);
621 }
622 json::to_vector(root, search_domains, "search_domains", title);
623 json::to_bool(root, from_dhcp_options, "from_dhcp_options", title);
624 }
625#endif
626
627 bool operator==(const DnsOptions &at) const = default;
628
629 bool from_dhcp_options = false;
630 std::vector<DnsDomain> search_domains;
631 std::map<int, DnsServer> servers;
632
633 protected:
641 DnsServer &get_server(const int priority)
642 {
643 auto it = servers.insert(std::make_pair(priority, DnsServer())).first;
644 return (*it).second;
645 }
646};
647
648} // 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
static std::string validate(const std::string &ipstr, const TITLE &title, const Version required_version)
Definition ip.hpp:131
bool is_ipv6() const
Definition ip.hpp:949
#define OPENVPN_THROW_EXCEPTION(stuff)
void validate_host(const std::string &host, const std::string &title)
Definition hostport.hpp:93
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
void assert_dict(const Json::Value &obj, const TITLE &title)
void to_string_optional(const Json::Value &root, std::string &dest, const NAME &name, const std::string &default_value, const TITLE &title)
void to_string(const Json::Value &root, std::string &dest, const NAME &name, const TITLE &title)
void to_vector(const Json::Value &root, T &vec, const NAME &name, const TITLE &title)
bool exists(const Json::Value &root, const NAME &name)
void to_bool(const Json::Value &root, bool &dest, const NAME &name, const TITLE &title)
void from_vector(Json::Value &root, const T &vec, const NAME &name)
void to_uint_optional(const Json::Value &root, unsigned int &dest, const NAME &name, const unsigned int default_value, const TITLE &title)
A name server address and optional port.
bool operator==(const DnsAddress &) const =default
Json::Value to_json() const
virtual ~DnsAddress() noexcept=default
void from_json(const Json::Value &root, const std::string &title)
void validate(const std::string &title) const
std::string to_string() const
Return string representation of the IP address and port stored in the DnsAddress object.
A DNS domain name.
bool operator==(const DnsDomain &) const =default
void validate(const std::string &title) const
std::string to_string() const
Return string representation of the DnsDomain object.
Json::Value to_json() const
virtual ~DnsDomain() noexcept=default
void from_json(const Json::Value &value, const std::string &title)
All DNS options set with the –dns or –dhcp-option directive.
std::map< int, DnsServer > servers
List of DNS servers to use, according to the list of priority.
DnsServer & get_server(const int priority)
void from_json(const Json::Value &root, const std::string &title)
std::vector< DnsDomain > search_domains
List of global DNS search domains to use.
~DnsOptions() noexcept=default
bool from_dhcp_options
Set to true if the DNS options comes from –dhcp-option options.
DnsOptions(const Json::Value &root, const std::string &title="")
bool operator==(const DnsOptions &at) const =default
std::string to_string() const
Json::Value to_json() const
DNS settings for a name server.
std::vector< DnsAddress > addresses
Parsed from –dns server n address ADDRESS[:PORT] [...] or –dhcp-option DNS/DNS6.
DnsServer(const Json::Value &root, const std::string &title="")
std::vector< DnsDomain > domains
Parsed from –dns server n resolve-domains DOMAIN [...] or –dhcp-option DOMAIN/DOMAIN-SEARCH if use_se...
void parse_transport_value(const std::string &transport_value)
std::string transport_string() const
Return string representation of a given DnsServer::Transport value.
static std::string transport_string(const Transport transport)
Return string representation of a given DnsServer::Transport value.
static std::string dnssec_string(const Security dnssec)
Return string representation of a given DnsServer::Security value.
std::string to_string(const char *prefix="") const
bool operator==(const DnsServer &at) const =default
std::string sni
Parsed from –dns server n sni.
virtual ~DnsServer() noexcept=default
Security dnssec
Parsed from –dns server n dnssec {yes,optional,no}.
@ Yes
Enforce using DNSSEC.
@ No
Do not use DNSSEC.
@ Unset
Undefined setting; default value when not set.
@ Optional
Try to use DNSSEC opportunistically. If it fails, the DNS resolver may ignore DNSSEC.
Json::Value to_json() const
std::string dnssec_string() const
Return string representation of the dnssec value in this DnsServer object.
@ TLS
Use DNS-over-TLS (DoT)
@ HTTPS
Use DNS-over-HTTPS (DoH)
@ Plain
Use the classic unencrypted DNS protocol.
@ Unset
Undefined setting; default value when not set.
void from_json(const Json::Value &root, const std::string &title)
Transport transport
Parsed from –dns server n transport {plain,DoT,DoH}.
void parse_dnssec_value(const std::string &dnssec_value)
TunBuilderCapture::RemoteAddress from_json
remote_address address
std::ostringstream os