OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_dns.cpp
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
13#include "test_common.hpp"
14
15#include <json/value.h>
17
18using namespace openvpn;
19
20namespace unittests {
21
22TEST(Dns, Options)
23{
25
27 "dns server 1 address 1.1.1.1\n"
28 "dns search-domains domain0 domain1\n"
29 "dns server -2 address [2.2.2.2]:5353\n"
30 "dns server -2 resolve-domains rdom0\n"
31 "dns server 1 address [1::1]:5353\n"
32 "dns search-domains domain2\n"
33 "dns server -2 resolve-domains rdom1\n"
34 "dns server -2 dnssec optional\n"
35 "dns server -2 transport DoT\n"
36 "dns server -2 sni hostname\n"
37 "dns server 3 address 3::3 3.2.1.0:4242 [3:3::3:3]:3333\n"
38 "dns server 3 dnssec no\n"
39 "dns server 3 transport DoH\n",
40 nullptr);
41 config.update_map();
42
43 DnsOptionsParser dns(config, false);
44
45 ASSERT_EQ(dns.search_domains.size(), 3U);
46 ASSERT_EQ(dns.search_domains[0].to_string(), "domain0");
47 ASSERT_EQ(dns.search_domains[1].to_string(), "domain1");
48 ASSERT_EQ(dns.search_domains[2].to_string(), "domain2");
49
50 ASSERT_EQ(dns.servers.size(), 3U);
51
52 int i = 1;
53 for (const auto &keyval : dns.servers)
54 {
55 auto priority = keyval.first;
56 auto &server = keyval.second;
57
58 if (priority == -2)
59 {
60 ASSERT_EQ(i, 1);
61
62 ASSERT_TRUE(server.addresses.size() == 1U);
63 ASSERT_EQ(server.addresses[0].address, "2.2.2.2");
64 ASSERT_EQ(server.addresses[0].port, 5353U);
65
66 ASSERT_EQ(server.domains.size(), 2U);
67 ASSERT_EQ(server.domains[0].to_string(), "rdom0");
68 ASSERT_EQ(server.domains[1].to_string(), "rdom1");
69
70 ASSERT_EQ(server.dnssec, DnsServer::Security::Optional);
71
72 ASSERT_EQ(server.transport, DnsServer::Transport::TLS);
73 ASSERT_EQ(server.sni, "hostname");
74 }
75 else if (priority == 1)
76 {
77 ASSERT_EQ(i, 2);
78
79 ASSERT_TRUE(server.addresses.size() == 2U);
80 ASSERT_EQ(server.addresses[0].address, "1.1.1.1");
81 ASSERT_EQ(server.addresses[0].port, 0U);
82
83 ASSERT_EQ(server.addresses[1].address, "1::1");
84 ASSERT_EQ(server.addresses[1].port, 5353U);
85
86 ASSERT_EQ(server.domains.size(), 0U);
87
88 ASSERT_EQ(server.dnssec, DnsServer::Security::Unset);
89
90 ASSERT_EQ(server.transport, DnsServer::Transport::Unset);
91 ASSERT_TRUE(server.sni.empty());
92 }
93 else if (priority == 3)
94 {
95 ASSERT_EQ(i, 3);
96
97 ASSERT_TRUE(server.addresses.size() == 3U);
98 ASSERT_EQ(server.addresses[0].address, "3::3");
99 ASSERT_EQ(server.addresses[0].port, 0U);
100
101 ASSERT_EQ(server.addresses[1].address, "3.2.1.0");
102 ASSERT_EQ(server.addresses[1].port, 4242U);
103
104 ASSERT_EQ(server.addresses[2].address, "3:3::3:3");
105 ASSERT_EQ(server.addresses[2].port, 3333U);
106
107 ASSERT_EQ(server.domains.size(), 0U);
108
109 ASSERT_EQ(server.dnssec, DnsServer::Security::No);
110
111 ASSERT_EQ(server.transport, DnsServer::Transport::HTTPS);
112 ASSERT_TRUE(server.sni.empty());
113 }
114
115 i++;
116 }
117}
118
119TEST(Dns, OptionsMerger)
120{
121 OptionList pushed;
123 const DnsOptionsMerger merger;
124
125 pushed.parse_from_config("dns server 1 address ::1", nullptr);
126 config.parse_from_config("dns server 1 address 1.1.1.1\n"
127 "dns server -2 address 2.2.2.2\n",
128 nullptr);
129 pushed.update_map();
130 config.update_map();
131
132 merger.merge(pushed, config);
133 ASSERT_EQ(config.size(), 2U);
134 ASSERT_EQ(pushed.size(), 2U);
135 ASSERT_EQ(pushed[0].ref(4), "::1");
136 ASSERT_EQ(pushed[1].ref(4), "2.2.2.2");
137}
138
139TEST(Dns, DnsAddressTostring)
140{
149 const DnsAddress ipv4_addr("192.168.0.1");
150 EXPECT_STREQ(ipv4_addr.to_string().c_str(), "192.168.0.1");
151 const DnsAddress parse_ipv4_addr(ipv4_addr.to_string());
152 EXPECT_STREQ(parse_ipv4_addr.to_string().c_str(), "192.168.0.1");
153
154 const DnsAddress ipv4_port("192.168.20.1:9876");
155 EXPECT_STREQ(ipv4_port.to_string().c_str(), "192.168.20.1:9876");
156 const DnsAddress parse_ipv4_port(ipv4_port.to_string());
157 EXPECT_STREQ(parse_ipv4_port.to_string().c_str(), "192.168.20.1:9876");
158
159 const DnsAddress ipv6_addr("2001:db8:5678::1");
160 EXPECT_STREQ(ipv6_addr.to_string().c_str(), "2001:db8:5678::1");
161 const DnsAddress parse_ipv6_addr(ipv6_addr.to_string());
162 EXPECT_STREQ(parse_ipv6_addr.to_string().c_str(), "2001:db8:5678::1")
163 << "parse_ipv6_addr failed";
164
165 const DnsAddress ipv6_port("[2001:db8:1234::1]:5678");
166 EXPECT_STREQ(ipv6_port.to_string().c_str(), "[2001:db8:1234::1]:5678");
167 const DnsAddress parse_ipv6_port(ipv6_port.to_string());
168 EXPECT_STREQ(parse_ipv6_port.to_string().c_str(), "[2001:db8:1234::1]:5678")
169 << "parse_ipv6_port failed";
170
171
172#ifndef OPENVPN_PLATFORM_WIN
173 // Test disabled on Windows. Windows allows partial IP address strings.
174 // On Linux, this error happens inside the ASIO library when calling
175 // inet_pton() in asio::ip::make_address_v4()
176 OVPN_EXPECT_THROW(DnsAddress invalid1("192.168.0"),
178 "Invalid address '192.168.0'");
179#endif
180
181 OVPN_EXPECT_THROW(DnsAddress invalid2("192.168.200.1::1234"),
183 "Invalid address '192.168.200.1::1234'");
184
185 OVPN_EXPECT_THROW(DnsAddress invalid3("192.168.200.1:blabla"),
187 "Invalid address:port format '192.168.200.1:blabla'");
188
189 OVPN_EXPECT_THROW(DnsAddress invalid4("192.168.200.1:77701"),
191 "Invalid address:port format '192.168.200.1:77701'");
192
193 OVPN_EXPECT_THROW(DnsAddress invalid5("2001:defg:1234:1234::"),
195 "Invalid address '2001:defg:1234:1234::'");
196
197 OVPN_EXPECT_THROW(DnsAddress invalid6("2001:abcd:1234:12345::1"),
199 "Invalid address '2001:abcd:1234:12345::1'");
200
201 OVPN_EXPECT_THROW(DnsAddress invalid7("[2001:abcd:1234:::]"),
203 "Invalid address '2001:abcd:1234:::'");
204
205 OVPN_EXPECT_THROW(DnsAddress invalid8("[2001:abcd:1234::]:65547"),
207 "Invalid address:port format '[2001:abcd:1234::]:65547");
208}
209
210
211TEST(Dns, ServerNoAddress)
212{
214 config.parse_from_config("dns server 0 resolve-domains dom0\n", nullptr);
215 config.update_map();
217 option_error,
218 "dns server 0 does not have an address assigned");
219}
220
221TEST(Dns, ServerEightAddresses)
222{
224 config.parse_from_config("dns server 0 address 1::1 2::2 3::3 4::4 5::5 6::6 7::7 8::8\n", nullptr);
225 config.update_map();
226 DnsOptionsParser dns(config, false);
227 ASSERT_EQ(dns.servers.size(), 1U);
228 ASSERT_EQ(dns.servers[0].addresses.size(), 8U);
229}
230
231TEST(Dns, ServerTooManyAddresses)
232{
234 config.parse_from_config("dns server 0 address 1::1 2::2 3::3 4::4 5::5 6::6 7::7 8::8 9::9\n", nullptr);
235 config.update_map();
237 option_error,
238 "dns server 0 option 'address' unknown or too many parameters");
239}
240
241TEST(Dns, ServerInvalidAddress)
242{
244 config.parse_from_config("dns server 0 address 1.1.1.1 foobar\n", nullptr);
245 config.update_map();
247 option_error,
248 "dns server 0 error: Invalid address 'foobar'");
249}
250
251TEST(Dns, ServerInvalidDnssec)
252{
253 {
255 config.parse_from_config("dns server 0 dnssec foo\n", nullptr);
256 config.update_map();
258 option_error,
259 "dns server 0 error: Invalid DNSSEC value 'foo'");
260 }
261 {
263 config.parse_from_config("dns server 0 dnssec yes no\n", nullptr);
264 config.update_map();
266 option_error,
267 "dns server 0 option 'dnssec' unknown or too many parameters");
268 }
269}
270
271TEST(Dns, ServerInvalidTransport)
272{
273 {
275 config.parse_from_config("dns server 0 transport avian-carrier\n", nullptr);
276 config.update_map();
278 option_error,
279 "dns server 0 error: Invalid transport value 'avian-carrier'");
280 }
281 {
283 config.parse_from_config("dns server 0 transport DoT D'oh\n", nullptr);
284 config.update_map();
286 option_error,
287 "dns server 0 option 'transport' unknown or too many parameters");
288 }
289}
290
291TEST(Dns, DhcpOptions)
292{
295 "dhcp-option DNS 1.1.1.1\n"
296 "dhcp-option DNS6 1::1\n"
297 "dhcp-option DOMAIN domain0\n"
298 "dhcp-option DOMAIN-SEARCH domain1\n"
299 "dhcp-option ADAPTER_DOMAIN_SUFFIX adsX\n"
300 "dhcp-option ADAPTER_DOMAIN_SUFFIX ads\n",
301 nullptr);
302 config.update_map();
303
304 DnsOptionsParser dns(config, false);
305
306 ASSERT_TRUE(dns.from_dhcp_options);
307
308 ASSERT_EQ(dns.search_domains.size(), 3U);
309 ASSERT_EQ(dns.search_domains[0].to_string(), "ads");
310 ASSERT_EQ(dns.search_domains[1].to_string(), "domain0");
311 ASSERT_EQ(dns.search_domains[2].to_string(), "domain1");
312
313 ASSERT_EQ(dns.servers.size(), 1U);
314 ASSERT_TRUE(dns.servers[0].addresses.size() == 2U);
315 ASSERT_EQ(dns.servers[0].addresses[0].address, "1.1.1.1");
316 ASSERT_EQ(dns.servers[0].addresses[0].port, 0U);
317
318 ASSERT_EQ(dns.servers[0].addresses[1].address, "1::1");
319 ASSERT_EQ(dns.servers[0].addresses[1].port, 0U);
320}
321
322TEST(Dns, DhcpOptionsWithSplitDomains)
323{
326 "dhcp-option DNS 1.1.1.1\n"
327 "dhcp-option DNS6 1::1\n"
328 "dhcp-option DOMAIN domain0\n"
329 "dhcp-option DOMAIN-SEARCH domain1\n"
330 "dhcp-option ADAPTER_DOMAIN_SUFFIX adsX\n"
331 "dhcp-option ADAPTER_DOMAIN_SUFFIX ads\n",
332 nullptr);
333 config.update_map();
334
335 DnsOptionsParser dns(config, true);
336
337 ASSERT_TRUE(dns.from_dhcp_options);
338
339 ASSERT_EQ(dns.search_domains.size(), 1U);
340 ASSERT_EQ(dns.search_domains[0].to_string(), "ads");
341
342 ASSERT_EQ(dns.servers.size(), 1U);
343
344 ASSERT_TRUE(dns.servers[0].addresses.size() == 2U);
345 ASSERT_EQ(dns.servers[0].addresses[0].address, "1.1.1.1");
346 ASSERT_EQ(dns.servers[0].addresses[0].port, 0U);
347 ASSERT_EQ(dns.servers[0].addresses[1].address, "1::1");
348 ASSERT_EQ(dns.servers[0].addresses[1].port, 0U);
349
350 ASSERT_TRUE(dns.servers[0].domains.size() == 2U);
351 ASSERT_EQ(dns.servers[0].domains[0].domain, "domain0");
352 ASSERT_EQ(dns.servers[0].domains[1].domain, "domain1");
353}
354
355TEST(Dns, DhcpOptionsIgnored)
356{
359 "dhcp-option DNS 1.1.1.1\n"
360 "dhcp-option DNS6 1::1\n"
361 "dhcp-option DOMAIN domain0\n"
362 "dhcp-option DOMAIN-SEARCH domain1\n"
363 "dhcp-option ADAPTER_DOMAIN_SUFFIX adsX\n"
364 "dns server 123 address 123::123\n"
365 "dhcp-option ADAPTER_DOMAIN_SUFFIX ads\n",
366 nullptr);
367 config.update_map();
368
369 DnsOptionsParser dns(config, true);
370
371 ASSERT_FALSE(dns.from_dhcp_options);
372 ASSERT_TRUE(dns.search_domains.empty());
373 ASSERT_EQ(dns.servers.size(), 1U);
374
375 ASSERT_TRUE(dns.servers[123].domains.empty());
376 ASSERT_TRUE(dns.servers[123].addresses.size() == 1U);
377 ASSERT_EQ(dns.servers[123].addresses[0].address, "123::123");
378 ASSERT_EQ(dns.servers[123].addresses[0].port, 0U);
379}
380
381TEST(Dns, ToStringMinValuesSet)
382{
384 config.parse_from_config("dns server 10 address 1::1\n", nullptr);
385 config.update_map();
386 const DnsOptionsParser dns(config, false);
387 ASSERT_EQ(dns.to_string(),
388 "DNS Servers:\n"
389 " Priority: 10\n"
390 " Addresses:\n"
391 " 1::1\n"
392 "Values from dhcp-options: false\n");
393}
394
395TEST(Dns, ToStringValuesFromDhcpOptions)
396{
398 config.parse_from_config("dhcp-option DNS6 1::1\n", nullptr);
399 config.update_map();
400 const DnsOptionsParser dns(config, false);
401 ASSERT_EQ(dns.to_string(),
402 "DNS Servers:\n"
403 " Priority: 0\n"
404 " Addresses:\n"
405 " 1::1\n"
406 "Values from dhcp-options: true\n");
407}
408
409TEST(Dns, ToStringAllValuesSet)
410{
413 "dns search-domains dom1 dom2 dom3\n"
414 "dns server 10 address 1::1 1.1.1.1\n"
415 "dns server 10 resolve-domains rdom11 rdom12\n"
416 "dns server 10 transport DoT\n"
417 "dns server 10 sni snidom1\n"
418 "dns server 10 dnssec optional\n"
419 "dns server 20 address 2::2 2.2.2.2\n"
420 "dns server 20 resolve-domains rdom21 rdom22\n"
421 "dns server 20 transport DoH\n"
422 "dns server 20 sni snidom2\n"
423 "dns server 20 dnssec yes\n",
424 nullptr);
425 config.update_map();
426 const DnsOptionsParser dns(config, false);
427 ASSERT_EQ(dns.to_string(),
428 "DNS Servers:\n"
429 " Priority: 10\n"
430 " Addresses:\n"
431 " 1::1\n"
432 " 1.1.1.1\n"
433 " Domains:\n"
434 " rdom11\n"
435 " rdom12\n"
436 " DNSSEC: Optional\n"
437 " Transport: TLS\n"
438 " SNI: snidom1\n"
439 " Priority: 20\n"
440 " Addresses:\n"
441 " 2::2\n"
442 " 2.2.2.2\n"
443 " Domains:\n"
444 " rdom21\n"
445 " rdom22\n"
446 " DNSSEC: Yes\n"
447 " Transport: HTTPS\n"
448 " SNI: snidom2\n"
449 "DNS Search Domains:\n"
450 " dom1\n"
451 " dom2\n"
452 " dom3\n"
453 "Values from dhcp-options: false\n");
454}
455
456TEST(Dns, JsonRoundtripMinValuesSet)
457{
459 config.parse_from_config("dns server 10 address 1::1\n", nullptr);
460 config.update_map();
461 const DnsOptionsParser toJson(config, false);
462 const Json::Value json = toJson.to_json();
463 Json::StreamWriterBuilder builder;
464 builder["indentation"] = " ";
465 ASSERT_EQ(Json::writeString(builder, json),
466 "{\n"
467 " \"from_dhcp_options\" : false,\n"
468 " \"servers\" : \n"
469 " {\n"
470 " \"10\" : \n"
471 " {\n"
472 " \"addresses\" : \n"
473 " [\n"
474 " {\n"
475 " \"address\" : \"1::1\"\n"
476 " }\n"
477 " ]\n"
478 " }\n"
479 " }\n"
480 "}");
481
482 DnsOptions fromJson;
483 fromJson.from_json(json, "json test");
484 ASSERT_EQ(fromJson.to_string(),
485 "DNS Servers:\n"
486 " Priority: 10\n"
487 " Addresses:\n"
488 " 1::1\n"
489 "Values from dhcp-options: false\n");
490}
491
492TEST(Dns, JsonRoundtripValuesFromDhcpOption)
493{
495 config.parse_from_config("dhcp-option DNS6 1::1\n", nullptr);
496 config.update_map();
497 const DnsOptionsParser toJson(config, false);
498 const Json::Value json = toJson.to_json();
499 Json::StreamWriterBuilder builder;
500 builder["indentation"] = " ";
501 ASSERT_EQ(Json::writeString(builder, json),
502 "{\n"
503 " \"from_dhcp_options\" : true,\n"
504 " \"servers\" : \n"
505 " {\n"
506 " \"0\" : \n"
507 " {\n"
508 " \"addresses\" : \n"
509 " [\n"
510 " {\n"
511 " \"address\" : \"1::1\"\n"
512 " }\n"
513 " ]\n"
514 " }\n"
515 " }\n"
516 "}");
517
518 DnsOptions fromJson;
519 fromJson.from_json(json, "json test");
520 ASSERT_EQ(fromJson.to_string(),
521 "DNS Servers:\n"
522 " Priority: 0\n"
523 " Addresses:\n"
524 " 1::1\n"
525 "Values from dhcp-options: true\n");
526}
527
528TEST(Dns, JsonRoundtripAllValuesSet)
529{
532 "dns search-domains dom1 dom2 dom3\n"
533 "dns server 10 address 1::1 1.1.1.1\n"
534 "dns server 10 resolve-domains rdom11 rdom12\n"
535 "dns server 10 transport DoT\n"
536 "dns server 10 sni snidom1\n"
537 "dns server 10 dnssec optional\n"
538 "dns server 20 address [2::2]:5353 2.2.2.2:5353\n"
539 "dns server 20 resolve-domains rdom21 rdom22\n"
540 "dns server 20 transport DoH\n"
541 "dns server 20 sni snidom2\n"
542 "dns server 20 dnssec yes\n",
543 nullptr);
544 config.update_map();
545 const DnsOptionsParser toJson(config, false);
546 const Json::Value json = toJson.to_json();
547 Json::StreamWriterBuilder builder;
548 builder["indentation"] = " ";
549 ASSERT_EQ(Json::writeString(builder, json),
550 "{\n"
551 " \"from_dhcp_options\" : false,\n"
552 " \"search_domains\" : \n"
553 " [\n"
554 " \"dom1\",\n"
555 " \"dom2\",\n"
556 " \"dom3\"\n"
557 " ],\n"
558 " \"servers\" : \n"
559 " {\n"
560 " \"10\" : \n"
561 " {\n"
562 " \"addresses\" : \n"
563 " [\n"
564 " {\n"
565 " \"address\" : \"1::1\"\n"
566 " },\n"
567 " {\n"
568 " \"address\" : \"1.1.1.1\"\n"
569 " }\n"
570 " ],\n"
571 " \"dnssec\" : \"Optional\",\n"
572 " \"domains\" : \n"
573 " [\n"
574 " \"rdom11\",\n"
575 " \"rdom12\"\n"
576 " ],\n"
577 " \"sni\" : \"snidom1\",\n"
578 " \"transport\" : \"TLS\"\n"
579 " },\n"
580 " \"20\" : \n"
581 " {\n"
582 " \"addresses\" : \n"
583 " [\n"
584 " {\n"
585 " \"address\" : \"2::2\",\n"
586 " \"port\" : 5353\n"
587 " },\n"
588 " {\n"
589 " \"address\" : \"2.2.2.2\",\n"
590 " \"port\" : 5353\n"
591 " }\n"
592 " ],\n"
593 " \"dnssec\" : \"Yes\",\n"
594 " \"domains\" : \n"
595 " [\n"
596 " \"rdom21\",\n"
597 " \"rdom22\"\n"
598 " ],\n"
599 " \"sni\" : \"snidom2\",\n"
600 " \"transport\" : \"HTTPS\"\n"
601 " }\n"
602 " }\n"
603 "}");
604
605 DnsOptions fromJson;
606 fromJson.from_json(json, "json test");
607 ASSERT_EQ(fromJson.to_string(),
608 "DNS Servers:\n"
609 " Priority: 10\n"
610 " Addresses:\n"
611 " 1::1\n"
612 " 1.1.1.1\n"
613 " Domains:\n"
614 " rdom11\n"
615 " rdom12\n"
616 " DNSSEC: Optional\n"
617 " Transport: TLS\n"
618 " SNI: snidom1\n"
619 " Priority: 20\n"
620 " Addresses:\n"
621 " [2::2]:5353\n"
622 " 2.2.2.2:5353\n"
623 " Domains:\n"
624 " rdom21\n"
625 " rdom22\n"
626 " DNSSEC: Yes\n"
627 " Transport: HTTPS\n"
628 " SNI: snidom2\n"
629 "DNS Search Domains:\n"
630 " dom1\n"
631 " dom2\n"
632 " dom3\n"
633 "Values from dhcp-options: false\n");
634}
635
636} // namespace unittests
void parse_from_config(const std::string &str, Limits *lim)
Definition options.hpp:973
TEST(CPUTime, CpuTimePid)
A name server address and optional port.
std::string to_string() const
Return string representation of the IP address and port stored in the DnsAddress object.
void merge(OptionList &pushed, const OptionList &config) const override
Definition dns.hpp:309
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.
void from_json(const Json::Value &root, const std::string &title)
std::vector< DnsDomain > search_domains
List of global DNS search domains to use.
bool from_dhcp_options
Set to true if the DNS options comes from –dhcp-option options.
std::string to_string() const
Json::Value to_json() const
#define OVPN_EXPECT_THROW(statement, expected_exception, expected_text)
#define JY_EXPECT_THROW
static const char config[]