xref: /aosp_15_r20/external/cronet/net/dns/dns_config_service_linux_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/dns/dns_config_service_linux.h"
6 
7 #include <arpa/inet.h>
8 #include <resolv.h>
9 
10 #include <memory>
11 #include <optional>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/cancelable_callback.h"
16 #include "base/check.h"
17 #include "base/files/file_util.h"
18 #include "base/functional/bind.h"
19 #include "base/memory/raw_ptr.h"
20 #include "base/memory/scoped_refptr.h"
21 #include "base/run_loop.h"
22 #include "base/sys_byteorder.h"
23 #include "base/task/sequenced_task_runner.h"
24 #include "base/task/single_thread_task_runner.h"
25 #include "base/task/task_traits.h"
26 #include "base/task/thread_pool.h"
27 #include "base/test/metrics/histogram_tester.h"
28 #include "base/test/task_environment.h"
29 #include "base/test/test_waitable_event.h"
30 #include "net/base/ip_address.h"
31 #include "net/base/test_completion_callback.h"
32 #include "net/dns/dns_config.h"
33 #include "net/dns/dns_config_service.h"
34 #include "net/dns/nsswitch_reader.h"
35 #include "net/dns/public/dns_protocol.h"
36 #include "net/test/test_with_task_environment.h"
37 #include "testing/gmock/include/gmock/gmock.h"
38 #include "testing/gtest/include/gtest/gtest.h"
39 
40 namespace net {
41 
42 namespace {
43 
44 // MAXNS is normally 3, but let's test 4 if possible.
45 const char* const kNameserversIPv4[] = {
46     "8.8.8.8",
47     "192.168.1.1",
48     "63.1.2.4",
49     "1.0.0.1",
50 };
51 
52 const char* const kNameserversIPv6[] = {
53     nullptr,
54     "2001:db8::42",
55     nullptr,
56     "::FFFF:129.144.52.38",
57 };
58 
59 const std::vector<NsswitchReader::ServiceSpecification> kBasicNsswitchConfig = {
60     NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
61     NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)};
62 
DummyConfigCallback(const DnsConfig & config)63 void DummyConfigCallback(const DnsConfig& config) {
64   // Do nothing
65 }
66 
67 // Fills in |res| with sane configuration.
InitializeResState(res_state res)68 void InitializeResState(res_state res) {
69   memset(res, 0, sizeof(*res));
70   res->options =
71       RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | RES_ROTATE;
72   res->ndots = 2;
73   res->retrans = 4;
74   res->retry = 7;
75 
76   const char kDnsrch[] =
77       "chromium.org"
78       "\0"
79       "example.com";
80   memcpy(res->defdname, kDnsrch, sizeof(kDnsrch));
81   res->dnsrch[0] = res->defdname;
82   res->dnsrch[1] = res->defdname + sizeof("chromium.org");
83 
84   for (unsigned i = 0; i < std::size(kNameserversIPv4) && i < MAXNS; ++i) {
85     struct sockaddr_in sa;
86     sa.sin_family = AF_INET;
87     sa.sin_port = base::HostToNet16(NS_DEFAULTPORT + i);
88     inet_pton(AF_INET, kNameserversIPv4[i], &sa.sin_addr);
89     res->nsaddr_list[i] = sa;
90     ++res->nscount;
91   }
92 
93   // Install IPv6 addresses, replacing the corresponding IPv4 addresses.
94   unsigned nscount6 = 0;
95   for (unsigned i = 0; i < std::size(kNameserversIPv6) && i < MAXNS; ++i) {
96     if (!kNameserversIPv6[i])
97       continue;
98     // Must use malloc to mimic res_ninit. Expect to be freed in
99     // `TestResolvReader::CloseResState()`.
100     struct sockaddr_in6* sa6;
101     sa6 = static_cast<sockaddr_in6*>(malloc(sizeof(*sa6)));
102     sa6->sin6_family = AF_INET6;
103     sa6->sin6_port = base::HostToNet16(NS_DEFAULTPORT - i);
104     inet_pton(AF_INET6, kNameserversIPv6[i], &sa6->sin6_addr);
105     res->_u._ext.nsaddrs[i] = sa6;
106     memset(&res->nsaddr_list[i], 0, sizeof res->nsaddr_list[i]);
107     ++nscount6;
108   }
109   res->_u._ext.nscount6 = nscount6;
110 }
111 
InitializeExpectedConfig(DnsConfig * config)112 void InitializeExpectedConfig(DnsConfig* config) {
113   config->ndots = 2;
114   config->fallback_period = kDnsDefaultFallbackPeriod;
115   config->attempts = 7;
116   config->rotate = true;
117   config->append_to_multi_label_name = true;
118   config->search.clear();
119   config->search.push_back("chromium.org");
120   config->search.push_back("example.com");
121 
122   config->nameservers.clear();
123   for (unsigned i = 0; i < std::size(kNameserversIPv4) && i < MAXNS; ++i) {
124     IPAddress ip;
125     EXPECT_TRUE(ip.AssignFromIPLiteral(kNameserversIPv4[i]));
126     config->nameservers.emplace_back(ip, NS_DEFAULTPORT + i);
127   }
128 
129   for (unsigned i = 0; i < std::size(kNameserversIPv6) && i < MAXNS; ++i) {
130     if (!kNameserversIPv6[i])
131       continue;
132     IPAddress ip;
133     EXPECT_TRUE(ip.AssignFromIPLiteral(kNameserversIPv6[i]));
134     config->nameservers[i] = IPEndPoint(ip, NS_DEFAULTPORT - i);
135   }
136 }
137 
138 class CallbackHelper {
139  public:
WaitForResult()140   std::optional<DnsConfig> WaitForResult() {
141     run_loop_.Run();
142     return GetResult();
143   }
144 
GetResult()145   std::optional<DnsConfig> GetResult() {
146     std::optional<DnsConfig> result = std::move(config_);
147     return result;
148   }
149 
GetCallback()150   DnsConfigService::CallbackType GetCallback() {
151     return base::BindRepeating(&CallbackHelper::OnComplete,
152                                base::Unretained(this));
153   }
154 
155  private:
OnComplete(const DnsConfig & config)156   void OnComplete(const DnsConfig& config) {
157     config_ = config;
158     run_loop_.Quit();
159   }
160 
161   std::optional<DnsConfig> config_;
162   base::RunLoop run_loop_;
163 };
164 
165 // Helper to allow blocking on some point in the ThreadPool.
166 class BlockingHelper {
167  public:
~BlockingHelper()168   ~BlockingHelper() { EXPECT_EQ(state_, State::kUnblocked); }
169 
170   // Called by the test code to wait for the block point to be reached.
WaitUntilBlocked()171   void WaitUntilBlocked() {
172     CHECK_EQ(state_, State::kUnblocked);
173     state_ = State::kRunningUntilBlock;
174 
175     CHECK(!run_loop_ || !run_loop_->running());
176     run_loop_.emplace();
177     run_loop_->Run();
178 
179     CHECK_EQ(state_, State::kBlocked);
180   }
181 
182   // Called by the ThreadPool code on reaching the block point.
WaitUntilUnblocked()183   void WaitUntilUnblocked() {
184     block_event_.Reset();
185     task_runner_->PostTask(FROM_HERE,
186                            base::BindOnce(&BlockingHelper::OnBlockedCallback,
187                                           base::Unretained(this)));
188     block_event_.Wait();
189     blocker_event_.Signal();
190   }
191 
192   // Called by the test code to unblock the ThreadPool code.
Unblock()193   void Unblock() {
194     CHECK_EQ(state_, State::kBlocked);
195     CHECK(!block_event_.IsSignaled());
196 
197     state_ = State::kUnblocked;
198 
199     blocker_event_.Reset();
200     block_event_.Signal();
201     blocker_event_.Wait();
202   }
203 
204  private:
205   enum class State {
206     kRunningUntilBlock,
207     kBlocked,
208     kUnblocked,
209   };
210 
OnBlockedCallback()211   void OnBlockedCallback() {
212     CHECK_EQ(state_, State::kRunningUntilBlock);
213     CHECK(run_loop_.has_value());
214     CHECK(run_loop_->running());
215 
216     state_ = State::kBlocked;
217     run_loop_->Quit();
218   }
219 
220   State state_ = State::kUnblocked;
221   std::optional<base::RunLoop> run_loop_;
222   base::TestWaitableEvent block_event_;
223   base::TestWaitableEvent blocker_event_;
224   scoped_refptr<base::SingleThreadTaskRunner> task_runner_ =
225       base::SingleThreadTaskRunner::GetCurrentDefault();
226 };
227 
228 class TestScopedResState : public ScopedResState {
229  public:
TestScopedResState(std::unique_ptr<struct __res_state> res)230   explicit TestScopedResState(std::unique_ptr<struct __res_state> res)
231       : res_(std::move(res)) {}
232 
~TestScopedResState()233   ~TestScopedResState() override {
234     if (res_) {
235       // Assume `res->_u._ext.nsaddrs` memory allocated via malloc, e.g. by
236       // `InitializeResState()`.
237       for (int i = 0; i < res_->nscount; ++i) {
238         if (res_->_u._ext.nsaddrs[i] != nullptr)
239           free(res_->_u._ext.nsaddrs[i]);
240       }
241     }
242   }
243 
state() const244   const struct __res_state& state() const override {
245     EXPECT_TRUE(res_);
246     return *res_;
247   }
248 
249  private:
250   std::unique_ptr<struct __res_state> res_;
251 };
252 
253 class TestResolvReader : public ResolvReader {
254  public:
255   ~TestResolvReader() override = default;
256 
set_value(std::unique_ptr<struct __res_state> value)257   void set_value(std::unique_ptr<struct __res_state> value) {
258     CHECK(!value_);
259     value_ = std::make_unique<TestScopedResState>(std::move(value));
260   }
261 
closed()262   bool closed() { return !value_; }
263 
264   // ResolvReader:
GetResState()265   std::unique_ptr<ScopedResState> GetResState() override {
266     if (blocking_helper_)
267       blocking_helper_->WaitUntilUnblocked();
268 
269     CHECK(value_);
270     return std::move(value_);
271   }
272 
set_blocking_helper(BlockingHelper * blocking_helper)273   void set_blocking_helper(BlockingHelper* blocking_helper) {
274     blocking_helper_ = blocking_helper;
275   }
276 
277  private:
278   std::unique_ptr<TestScopedResState> value_;
279   raw_ptr<BlockingHelper> blocking_helper_ = nullptr;
280 };
281 
282 class TestNsswitchReader : public NsswitchReader {
283  public:
set_value(std::vector<ServiceSpecification> value)284   void set_value(std::vector<ServiceSpecification> value) {
285     value_ = std::move(value);
286   }
287 
288   // NsswitchReader:
ReadAndParseHosts()289   std::vector<ServiceSpecification> ReadAndParseHosts() override {
290     return value_;
291   }
292 
293  private:
294   std::vector<ServiceSpecification> value_;
295 };
296 
297 class DnsConfigServiceLinuxTest : public ::testing::Test,
298                                   public WithTaskEnvironment {
299  public:
DnsConfigServiceLinuxTest()300   DnsConfigServiceLinuxTest()
301       : WithTaskEnvironment(
302             base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
303     auto resolv_reader = std::make_unique<TestResolvReader>();
304     resolv_reader_ = resolv_reader.get();
305     service_.set_resolv_reader_for_testing(std::move(resolv_reader));
306 
307     auto nsswitch_reader = std::make_unique<TestNsswitchReader>();
308     nsswitch_reader_ = nsswitch_reader.get();
309     service_.set_nsswitch_reader_for_testing(std::move(nsswitch_reader));
310   }
311 
312  protected:
313   internal::DnsConfigServiceLinux service_;
314   raw_ptr<TestResolvReader> resolv_reader_;
315   raw_ptr<TestNsswitchReader> nsswitch_reader_;
316 };
317 
318 // Regression test to verify crash does not occur if DnsConfigServiceLinux
319 // instance is destroyed without calling WatchConfig()
TEST_F(DnsConfigServiceLinuxTest,CreateAndDestroy)320 TEST_F(DnsConfigServiceLinuxTest, CreateAndDestroy) {
321   auto service = std::make_unique<internal::DnsConfigServiceLinux>();
322   service.reset();
323   RunUntilIdle();
324 }
325 
TEST_F(DnsConfigServiceLinuxTest,ConvertResStateToDnsConfig)326 TEST_F(DnsConfigServiceLinuxTest, ConvertResStateToDnsConfig) {
327   auto res = std::make_unique<struct __res_state>();
328   InitializeResState(res.get());
329   resolv_reader_->set_value(std::move(res));
330   nsswitch_reader_->set_value(kBasicNsswitchConfig);
331 
332   CallbackHelper callback_helper;
333   service_.ReadConfig(callback_helper.GetCallback());
334   std::optional<DnsConfig> config = callback_helper.WaitForResult();
335 
336   ASSERT_TRUE(config.has_value());
337   EXPECT_TRUE(config->IsValid());
338 
339   DnsConfig expected_config;
340   EXPECT_FALSE(expected_config.EqualsIgnoreHosts(config.value()));
341   InitializeExpectedConfig(&expected_config);
342   EXPECT_TRUE(expected_config.EqualsIgnoreHosts(config.value()));
343 
344   EXPECT_TRUE(resolv_reader_->closed());
345 }
346 
TEST_F(DnsConfigServiceLinuxTest,RejectEmptyNameserver)347 TEST_F(DnsConfigServiceLinuxTest, RejectEmptyNameserver) {
348   auto res = std::make_unique<struct __res_state>();
349   res->options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
350   const char kDnsrch[] = "chromium.org";
351   memcpy(res->defdname, kDnsrch, sizeof(kDnsrch));
352   res->dnsrch[0] = res->defdname;
353 
354   struct sockaddr_in sa = {};
355   sa.sin_family = AF_INET;
356   sa.sin_port = base::HostToNet16(NS_DEFAULTPORT);
357   sa.sin_addr.s_addr = INADDR_ANY;
358   res->nsaddr_list[0] = sa;
359   sa.sin_addr.s_addr = 0xCAFE1337;
360   res->nsaddr_list[1] = sa;
361   res->nscount = 2;
362 
363   resolv_reader_->set_value(std::move(res));
364   nsswitch_reader_->set_value(kBasicNsswitchConfig);
365 
366   CallbackHelper callback_helper;
367   service_.ReadConfig(callback_helper.GetCallback());
368   RunUntilIdle();
369   std::optional<DnsConfig> config = callback_helper.GetResult();
370 
371   EXPECT_FALSE(config.has_value());
372   EXPECT_TRUE(resolv_reader_->closed());
373 }
374 
TEST_F(DnsConfigServiceLinuxTest,AcceptNonEmptyNameserver)375 TEST_F(DnsConfigServiceLinuxTest, AcceptNonEmptyNameserver) {
376   auto res = std::make_unique<struct __res_state>();
377   res->options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
378   const char kDnsrch[] = "chromium.org";
379   memcpy(res->defdname, kDnsrch, sizeof(kDnsrch));
380   res->dnsrch[0] = res->defdname;
381 
382   struct sockaddr_in sa = {};
383   sa.sin_family = AF_INET;
384   sa.sin_port = base::HostToNet16(NS_DEFAULTPORT);
385   sa.sin_addr.s_addr = 0xDEADBEEF;
386   res->nsaddr_list[0] = sa;
387   sa.sin_addr.s_addr = 0xCAFE1337;
388   res->nsaddr_list[1] = sa;
389   res->nscount = 2;
390 
391   resolv_reader_->set_value(std::move(res));
392   nsswitch_reader_->set_value(kBasicNsswitchConfig);
393 
394   CallbackHelper callback_helper;
395   service_.ReadConfig(callback_helper.GetCallback());
396   std::optional<DnsConfig> config = callback_helper.WaitForResult();
397 
398   EXPECT_TRUE(config.has_value());
399   EXPECT_TRUE(resolv_reader_->closed());
400 }
401 
402 // Regression test to verify crash does not occur if DnsConfigServiceLinux
403 // instance is destroyed while SerialWorker jobs have posted to worker pool.
TEST_F(DnsConfigServiceLinuxTest,DestroyWhileJobsWorking)404 TEST_F(DnsConfigServiceLinuxTest, DestroyWhileJobsWorking) {
405   auto service = std::make_unique<internal::DnsConfigServiceLinux>();
406   // Call WatchConfig() which also tests ReadConfig().
407   service->WatchConfig(base::BindRepeating(&DummyConfigCallback));
408   service.reset();
409   FastForwardUntilNoTasksRemain();
410 }
411 
412 // Regression test to verify crash does not occur if DnsConfigServiceLinux
413 // instance is destroyed on another thread.
TEST_F(DnsConfigServiceLinuxTest,DestroyOnDifferentThread)414 TEST_F(DnsConfigServiceLinuxTest, DestroyOnDifferentThread) {
415   scoped_refptr<base::SequencedTaskRunner> runner =
416       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
417   std::unique_ptr<internal::DnsConfigServiceLinux, base::OnTaskRunnerDeleter>
418       service(new internal::DnsConfigServiceLinux(),
419               base::OnTaskRunnerDeleter(runner));
420 
421   runner->PostTask(FROM_HERE,
422                    base::BindOnce(&internal::DnsConfigServiceLinux::WatchConfig,
423                                   base::Unretained(service.get()),
424                                   base::BindRepeating(&DummyConfigCallback)));
425   service.reset();
426   RunUntilIdle();
427 }
428 
TEST_F(DnsConfigServiceLinuxTest,AcceptsBasicNsswitchConfig)429 TEST_F(DnsConfigServiceLinuxTest, AcceptsBasicNsswitchConfig) {
430   auto res = std::make_unique<struct __res_state>();
431   InitializeResState(res.get());
432   resolv_reader_->set_value(std::move(res));
433   nsswitch_reader_->set_value(kBasicNsswitchConfig);
434 
435   CallbackHelper callback_helper;
436   service_.ReadConfig(callback_helper.GetCallback());
437   std::optional<DnsConfig> config = callback_helper.WaitForResult();
438   EXPECT_TRUE(resolv_reader_->closed());
439 
440   ASSERT_TRUE(config.has_value());
441   EXPECT_TRUE(config->IsValid());
442   EXPECT_FALSE(config->unhandled_options);
443 }
444 
TEST_F(DnsConfigServiceLinuxTest,IgnoresBasicNsswitchConfigIfResolvConfigUnhandled)445 TEST_F(DnsConfigServiceLinuxTest,
446        IgnoresBasicNsswitchConfigIfResolvConfigUnhandled) {
447   auto res = std::make_unique<struct __res_state>();
448   InitializeResState(res.get());
449   res->options |= RES_USE_DNSSEC;  // Expect unhandled.
450   resolv_reader_->set_value(std::move(res));
451   nsswitch_reader_->set_value(kBasicNsswitchConfig);
452 
453   CallbackHelper callback_helper;
454   service_.ReadConfig(callback_helper.GetCallback());
455   std::optional<DnsConfig> config = callback_helper.WaitForResult();
456   EXPECT_TRUE(resolv_reader_->closed());
457 
458   ASSERT_TRUE(config.has_value());
459   EXPECT_TRUE(config->IsValid());
460   EXPECT_TRUE(config->unhandled_options);
461 }
462 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchWithoutFiles)463 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchWithoutFiles) {
464   auto res = std::make_unique<struct __res_state>();
465   InitializeResState(res.get());
466   resolv_reader_->set_value(std::move(res));
467 
468   nsswitch_reader_->set_value(
469       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
470 
471   CallbackHelper callback_helper;
472   service_.ReadConfig(callback_helper.GetCallback());
473   std::optional<DnsConfig> config = callback_helper.WaitForResult();
474   EXPECT_TRUE(resolv_reader_->closed());
475 
476   ASSERT_TRUE(config.has_value());
477   EXPECT_TRUE(config->IsValid());
478   EXPECT_TRUE(config->unhandled_options);
479 }
480 
TEST_F(DnsConfigServiceLinuxTest,RejectsWithExtraFiles)481 TEST_F(DnsConfigServiceLinuxTest, RejectsWithExtraFiles) {
482   auto res = std::make_unique<struct __res_state>();
483   InitializeResState(res.get());
484   resolv_reader_->set_value(std::move(res));
485 
486   nsswitch_reader_->set_value(
487       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
488        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
489        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
490 
491   CallbackHelper callback_helper;
492   service_.ReadConfig(callback_helper.GetCallback());
493   std::optional<DnsConfig> config = callback_helper.WaitForResult();
494   EXPECT_TRUE(resolv_reader_->closed());
495 
496   ASSERT_TRUE(config.has_value());
497   EXPECT_TRUE(config->IsValid());
498   EXPECT_TRUE(config->unhandled_options);
499 }
500 
TEST_F(DnsConfigServiceLinuxTest,IgnoresRedundantActions)501 TEST_F(DnsConfigServiceLinuxTest, IgnoresRedundantActions) {
502   auto res = std::make_unique<struct __res_state>();
503   InitializeResState(res.get());
504   resolv_reader_->set_value(std::move(res));
505 
506   nsswitch_reader_->set_value(
507       {NsswitchReader::ServiceSpecification(
508            NsswitchReader::Service::kFiles,
509            {{/*negated=*/false, NsswitchReader::Status::kSuccess,
510              NsswitchReader::Action::kReturn},
511             {/*negated=*/true, NsswitchReader::Status::kSuccess,
512              NsswitchReader::Action::kContinue}}),
513        NsswitchReader::ServiceSpecification(
514            NsswitchReader::Service::kDns,
515            {{/*negated=*/false, NsswitchReader::Status::kSuccess,
516              NsswitchReader::Action::kReturn},
517             {/*negated=*/true, NsswitchReader::Status::kSuccess,
518              NsswitchReader::Action::kContinue}})});
519 
520   CallbackHelper callback_helper;
521   service_.ReadConfig(callback_helper.GetCallback());
522   std::optional<DnsConfig> config = callback_helper.WaitForResult();
523   EXPECT_TRUE(resolv_reader_->closed());
524 
525   ASSERT_TRUE(config.has_value());
526   EXPECT_TRUE(config->IsValid());
527   EXPECT_FALSE(config->unhandled_options);
528 }
529 
TEST_F(DnsConfigServiceLinuxTest,RejectsInconsistentActions)530 TEST_F(DnsConfigServiceLinuxTest, RejectsInconsistentActions) {
531   auto res = std::make_unique<struct __res_state>();
532   InitializeResState(res.get());
533   resolv_reader_->set_value(std::move(res));
534 
535   nsswitch_reader_->set_value(
536       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
537        NsswitchReader::ServiceSpecification(
538            NsswitchReader::Service::kDns,
539            {{/*negated=*/false, NsswitchReader::Status::kUnavailable,
540              NsswitchReader::Action::kReturn},
541             {/*negated=*/true, NsswitchReader::Status::kSuccess,
542              NsswitchReader::Action::kContinue}})});
543 
544   CallbackHelper callback_helper;
545   service_.ReadConfig(callback_helper.GetCallback());
546   std::optional<DnsConfig> config = callback_helper.WaitForResult();
547   EXPECT_TRUE(resolv_reader_->closed());
548 
549   ASSERT_TRUE(config.has_value());
550   EXPECT_TRUE(config->IsValid());
551   EXPECT_TRUE(config->unhandled_options);
552 }
553 
TEST_F(DnsConfigServiceLinuxTest,RejectsWithBadFilesSuccessAction)554 TEST_F(DnsConfigServiceLinuxTest, RejectsWithBadFilesSuccessAction) {
555   auto res = std::make_unique<struct __res_state>();
556   InitializeResState(res.get());
557   resolv_reader_->set_value(std::move(res));
558 
559   nsswitch_reader_->set_value(
560       {NsswitchReader::ServiceSpecification(
561            NsswitchReader::Service::kFiles,
562            {{/*negated=*/false, NsswitchReader::Status::kSuccess,
563              NsswitchReader::Action::kContinue}}),
564        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
565 
566   CallbackHelper callback_helper;
567   service_.ReadConfig(callback_helper.GetCallback());
568   std::optional<DnsConfig> config = callback_helper.WaitForResult();
569   EXPECT_TRUE(resolv_reader_->closed());
570 
571   ASSERT_TRUE(config.has_value());
572   EXPECT_TRUE(config->IsValid());
573   EXPECT_TRUE(config->unhandled_options);
574 }
575 
TEST_F(DnsConfigServiceLinuxTest,RejectsWithBadFilesNotFoundAction)576 TEST_F(DnsConfigServiceLinuxTest, RejectsWithBadFilesNotFoundAction) {
577   auto res = std::make_unique<struct __res_state>();
578   InitializeResState(res.get());
579   resolv_reader_->set_value(std::move(res));
580 
581   nsswitch_reader_->set_value(
582       {NsswitchReader::ServiceSpecification(
583            NsswitchReader::Service::kFiles,
584            {{/*negated=*/false, NsswitchReader::Status::kNotFound,
585              NsswitchReader::Action::kReturn}}),
586        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
587 
588   CallbackHelper callback_helper;
589   service_.ReadConfig(callback_helper.GetCallback());
590   std::optional<DnsConfig> config = callback_helper.WaitForResult();
591   EXPECT_TRUE(resolv_reader_->closed());
592 
593   ASSERT_TRUE(config.has_value());
594   EXPECT_TRUE(config->IsValid());
595   EXPECT_TRUE(config->unhandled_options);
596 }
597 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchWithoutDns)598 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchWithoutDns) {
599   auto res = std::make_unique<struct __res_state>();
600   InitializeResState(res.get());
601   resolv_reader_->set_value(std::move(res));
602 
603   nsswitch_reader_->set_value(
604       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles)});
605 
606   CallbackHelper callback_helper;
607   service_.ReadConfig(callback_helper.GetCallback());
608   std::optional<DnsConfig> config = callback_helper.WaitForResult();
609   EXPECT_TRUE(resolv_reader_->closed());
610 
611   ASSERT_TRUE(config.has_value());
612   EXPECT_TRUE(config->IsValid());
613   EXPECT_TRUE(config->unhandled_options);
614 }
615 
TEST_F(DnsConfigServiceLinuxTest,RejectsWithBadDnsSuccessAction)616 TEST_F(DnsConfigServiceLinuxTest, RejectsWithBadDnsSuccessAction) {
617   auto res = std::make_unique<struct __res_state>();
618   InitializeResState(res.get());
619   resolv_reader_->set_value(std::move(res));
620 
621   nsswitch_reader_->set_value(
622       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
623        NsswitchReader::ServiceSpecification(
624            NsswitchReader::Service::kDns,
625            {{/*negated=*/false, NsswitchReader::Status::kSuccess,
626              NsswitchReader::Action::kContinue}})});
627 
628   CallbackHelper callback_helper;
629   service_.ReadConfig(callback_helper.GetCallback());
630   std::optional<DnsConfig> config = callback_helper.WaitForResult();
631   EXPECT_TRUE(resolv_reader_->closed());
632 
633   ASSERT_TRUE(config.has_value());
634   EXPECT_TRUE(config->IsValid());
635   EXPECT_TRUE(config->unhandled_options);
636 }
637 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchWithMisorderedServices)638 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchWithMisorderedServices) {
639   auto res = std::make_unique<struct __res_state>();
640   InitializeResState(res.get());
641   resolv_reader_->set_value(std::move(res));
642 
643   nsswitch_reader_->set_value(
644       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns),
645        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles)});
646 
647   CallbackHelper callback_helper;
648   service_.ReadConfig(callback_helper.GetCallback());
649   std::optional<DnsConfig> config = callback_helper.WaitForResult();
650   EXPECT_TRUE(resolv_reader_->closed());
651 
652   ASSERT_TRUE(config.has_value());
653   EXPECT_TRUE(config->IsValid());
654   EXPECT_TRUE(config->unhandled_options);
655 }
656 
TEST_F(DnsConfigServiceLinuxTest,AcceptsIncompatibleNsswitchServicesAfterDns)657 TEST_F(DnsConfigServiceLinuxTest, AcceptsIncompatibleNsswitchServicesAfterDns) {
658   auto res = std::make_unique<struct __res_state>();
659   InitializeResState(res.get());
660   resolv_reader_->set_value(std::move(res));
661 
662   nsswitch_reader_->set_value(
663       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
664        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns),
665        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns)});
666 
667   CallbackHelper callback_helper;
668   service_.ReadConfig(callback_helper.GetCallback());
669   std::optional<DnsConfig> config = callback_helper.WaitForResult();
670   EXPECT_TRUE(resolv_reader_->closed());
671 
672   ASSERT_TRUE(config.has_value());
673   EXPECT_TRUE(config->IsValid());
674   EXPECT_FALSE(config->unhandled_options);
675 }
676 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchMdns)677 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchMdns) {
678   auto res = std::make_unique<struct __res_state>();
679   InitializeResState(res.get());
680   resolv_reader_->set_value(std::move(res));
681 
682   nsswitch_reader_->set_value(
683       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
684        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns),
685        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
686 
687   CallbackHelper callback_helper;
688   service_.ReadConfig(callback_helper.GetCallback());
689   std::optional<DnsConfig> config = callback_helper.WaitForResult();
690   EXPECT_TRUE(resolv_reader_->closed());
691 
692   ASSERT_TRUE(config.has_value());
693   EXPECT_TRUE(config->IsValid());
694   EXPECT_TRUE(config->unhandled_options);
695 }
696 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchMdns4)697 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchMdns4) {
698   auto res = std::make_unique<struct __res_state>();
699   InitializeResState(res.get());
700   resolv_reader_->set_value(std::move(res));
701 
702   nsswitch_reader_->set_value(
703       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
704        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns4),
705        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
706 
707   CallbackHelper callback_helper;
708   service_.ReadConfig(callback_helper.GetCallback());
709   std::optional<DnsConfig> config = callback_helper.WaitForResult();
710   EXPECT_TRUE(resolv_reader_->closed());
711 
712   ASSERT_TRUE(config.has_value());
713   EXPECT_TRUE(config->IsValid());
714   EXPECT_TRUE(config->unhandled_options);
715 }
716 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchMdns6)717 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchMdns6) {
718   auto res = std::make_unique<struct __res_state>();
719   InitializeResState(res.get());
720   resolv_reader_->set_value(std::move(res));
721 
722   nsswitch_reader_->set_value(
723       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
724        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns6),
725        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
726 
727   CallbackHelper callback_helper;
728   service_.ReadConfig(callback_helper.GetCallback());
729   std::optional<DnsConfig> config = callback_helper.WaitForResult();
730   EXPECT_TRUE(resolv_reader_->closed());
731 
732   ASSERT_TRUE(config.has_value());
733   EXPECT_TRUE(config->IsValid());
734   EXPECT_TRUE(config->unhandled_options);
735 }
736 
TEST_F(DnsConfigServiceLinuxTest,AcceptsNsswitchMdnsMinimal)737 TEST_F(DnsConfigServiceLinuxTest, AcceptsNsswitchMdnsMinimal) {
738   auto res = std::make_unique<struct __res_state>();
739   InitializeResState(res.get());
740   resolv_reader_->set_value(std::move(res));
741 
742   nsswitch_reader_->set_value(
743       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
744        NsswitchReader::ServiceSpecification(
745            NsswitchReader::Service::kMdnsMinimal),
746        NsswitchReader::ServiceSpecification(
747            NsswitchReader::Service::kMdns4Minimal),
748        NsswitchReader::ServiceSpecification(
749            NsswitchReader::Service::kMdns6Minimal),
750        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
751 
752   CallbackHelper callback_helper;
753   service_.ReadConfig(callback_helper.GetCallback());
754   std::optional<DnsConfig> config = callback_helper.WaitForResult();
755   EXPECT_TRUE(resolv_reader_->closed());
756 
757   ASSERT_TRUE(config.has_value());
758   EXPECT_TRUE(config->IsValid());
759   EXPECT_FALSE(config->unhandled_options);
760 }
761 
762 // mdns*_minimal is often paired with [!UNAVAIL=RETURN] or [NOTFOUND=RETURN]
763 // actions. Ensure that is accepted.
TEST_F(DnsConfigServiceLinuxTest,AcceptsNsswitchMdnsMinimalWithCommonActions)764 TEST_F(DnsConfigServiceLinuxTest, AcceptsNsswitchMdnsMinimalWithCommonActions) {
765   auto res = std::make_unique<struct __res_state>();
766   InitializeResState(res.get());
767   resolv_reader_->set_value(std::move(res));
768 
769   nsswitch_reader_->set_value(
770       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
771        NsswitchReader::ServiceSpecification(
772            NsswitchReader::Service::kMdnsMinimal,
773            {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
774              NsswitchReader::Action::kReturn}}),
775        NsswitchReader::ServiceSpecification(
776            NsswitchReader::Service::kMdns4Minimal,
777            {{/*negated=*/false, NsswitchReader::Status::kNotFound,
778              NsswitchReader::Action::kReturn}}),
779        NsswitchReader::ServiceSpecification(
780            NsswitchReader::Service::kMdns6Minimal,
781            {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
782              NsswitchReader::Action::kReturn}}),
783        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
784 
785   CallbackHelper callback_helper;
786   service_.ReadConfig(callback_helper.GetCallback());
787   std::optional<DnsConfig> config = callback_helper.WaitForResult();
788   EXPECT_TRUE(resolv_reader_->closed());
789 
790   ASSERT_TRUE(config.has_value());
791   EXPECT_TRUE(config->IsValid());
792   EXPECT_FALSE(config->unhandled_options);
793 }
794 
TEST_F(DnsConfigServiceLinuxTest,RejectsWithBadMdnsMinimalUnavailableAction)795 TEST_F(DnsConfigServiceLinuxTest, RejectsWithBadMdnsMinimalUnavailableAction) {
796   auto res = std::make_unique<struct __res_state>();
797   InitializeResState(res.get());
798   resolv_reader_->set_value(std::move(res));
799 
800   nsswitch_reader_->set_value(
801       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
802        NsswitchReader::ServiceSpecification(
803            NsswitchReader::Service::kMdnsMinimal,
804            {{/*negated=*/false, NsswitchReader::Status::kUnavailable,
805              NsswitchReader::Action::kReturn}}),
806        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
807 
808   CallbackHelper callback_helper;
809   service_.ReadConfig(callback_helper.GetCallback());
810   std::optional<DnsConfig> config = callback_helper.WaitForResult();
811   EXPECT_TRUE(resolv_reader_->closed());
812 
813   ASSERT_TRUE(config.has_value());
814   EXPECT_TRUE(config->IsValid());
815   EXPECT_TRUE(config->unhandled_options);
816 }
817 
TEST_F(DnsConfigServiceLinuxTest,AcceptsNsswitchMyHostname)818 TEST_F(DnsConfigServiceLinuxTest, AcceptsNsswitchMyHostname) {
819   auto res = std::make_unique<struct __res_state>();
820   InitializeResState(res.get());
821   resolv_reader_->set_value(std::move(res));
822 
823   nsswitch_reader_->set_value(
824       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
825        NsswitchReader::ServiceSpecification(
826            NsswitchReader::Service::kMyHostname),
827        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
828 
829   CallbackHelper callback_helper;
830   service_.ReadConfig(callback_helper.GetCallback());
831   std::optional<DnsConfig> config = callback_helper.WaitForResult();
832   EXPECT_TRUE(resolv_reader_->closed());
833 
834   ASSERT_TRUE(config.has_value());
835   EXPECT_TRUE(config->IsValid());
836   EXPECT_FALSE(config->unhandled_options);
837 }
838 
TEST_F(DnsConfigServiceLinuxTest,RejectsWithBadMyHostnameNotFoundAction)839 TEST_F(DnsConfigServiceLinuxTest, RejectsWithBadMyHostnameNotFoundAction) {
840   auto res = std::make_unique<struct __res_state>();
841   InitializeResState(res.get());
842   resolv_reader_->set_value(std::move(res));
843 
844   nsswitch_reader_->set_value(
845       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
846        NsswitchReader::ServiceSpecification(
847            NsswitchReader::Service::kMyHostname,
848            {{/*negated=*/false, NsswitchReader::Status::kNotFound,
849              NsswitchReader::Action::kReturn}}),
850        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
851 
852   CallbackHelper callback_helper;
853   service_.ReadConfig(callback_helper.GetCallback());
854   std::optional<DnsConfig> config = callback_helper.WaitForResult();
855   EXPECT_TRUE(resolv_reader_->closed());
856 
857   ASSERT_TRUE(config.has_value());
858   EXPECT_TRUE(config->IsValid());
859   EXPECT_TRUE(config->unhandled_options);
860 }
861 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchResolve)862 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchResolve) {
863   auto res = std::make_unique<struct __res_state>();
864   InitializeResState(res.get());
865   resolv_reader_->set_value(std::move(res));
866 
867   nsswitch_reader_->set_value(
868       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
869        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kResolve),
870        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
871 
872   CallbackHelper callback_helper;
873   service_.ReadConfig(callback_helper.GetCallback());
874   std::optional<DnsConfig> config = callback_helper.WaitForResult();
875   EXPECT_TRUE(resolv_reader_->closed());
876 
877   ASSERT_TRUE(config.has_value());
878   EXPECT_TRUE(config->IsValid());
879   EXPECT_TRUE(config->unhandled_options);
880 }
881 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchNis)882 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchNis) {
883   auto res = std::make_unique<struct __res_state>();
884   InitializeResState(res.get());
885   resolv_reader_->set_value(std::move(res));
886 
887   nsswitch_reader_->set_value(
888       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
889        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kNis),
890        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
891 
892   CallbackHelper callback_helper;
893   service_.ReadConfig(callback_helper.GetCallback());
894   std::optional<DnsConfig> config = callback_helper.WaitForResult();
895   EXPECT_TRUE(resolv_reader_->closed());
896 
897   ASSERT_TRUE(config.has_value());
898   EXPECT_TRUE(config->IsValid());
899   EXPECT_TRUE(config->unhandled_options);
900 }
901 
TEST_F(DnsConfigServiceLinuxTest,RejectsWithBadNisNotFoundAction)902 TEST_F(DnsConfigServiceLinuxTest, RejectsWithBadNisNotFoundAction) {
903   auto res = std::make_unique<struct __res_state>();
904   InitializeResState(res.get());
905   resolv_reader_->set_value(std::move(res));
906 
907   nsswitch_reader_->set_value(
908       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
909        NsswitchReader::ServiceSpecification(
910            NsswitchReader::Service::kNis,
911            {{/*negated=*/false, NsswitchReader::Status::kNotFound,
912              NsswitchReader::Action::kReturn}}),
913        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
914 
915   CallbackHelper callback_helper;
916   service_.ReadConfig(callback_helper.GetCallback());
917   std::optional<DnsConfig> config = callback_helper.WaitForResult();
918   EXPECT_TRUE(resolv_reader_->closed());
919 
920   ASSERT_TRUE(config.has_value());
921   EXPECT_TRUE(config->IsValid());
922   EXPECT_TRUE(config->unhandled_options);
923 }
924 
TEST_F(DnsConfigServiceLinuxTest,RejectsNsswitchUnknown)925 TEST_F(DnsConfigServiceLinuxTest, RejectsNsswitchUnknown) {
926   auto res = std::make_unique<struct __res_state>();
927   InitializeResState(res.get());
928   resolv_reader_->set_value(std::move(res));
929 
930   nsswitch_reader_->set_value(
931       {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
932        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kUnknown),
933        NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)});
934 
935   CallbackHelper callback_helper;
936   service_.ReadConfig(callback_helper.GetCallback());
937   std::optional<DnsConfig> config = callback_helper.WaitForResult();
938   EXPECT_TRUE(resolv_reader_->closed());
939 
940   ASSERT_TRUE(config.has_value());
941   EXPECT_TRUE(config->IsValid());
942   EXPECT_TRUE(config->unhandled_options);
943 }
944 
TEST_F(DnsConfigServiceLinuxTest,FreshReadsAfterAdditionalTriggers)945 TEST_F(DnsConfigServiceLinuxTest, FreshReadsAfterAdditionalTriggers) {
946   BlockingHelper blocking_helper;
947   resolv_reader_->set_blocking_helper(&blocking_helper);
948 
949   CallbackHelper callback_helper;
950   service_.ReadConfig(callback_helper.GetCallback());
951 
952   // Expect work to be blocked.
953   blocking_helper.WaitUntilBlocked();
954   ASSERT_FALSE(callback_helper.GetResult());
955 
956   // Signal config changes (trigger a few times to confirm only one fresh read
957   // is performed).
958   service_.TriggerOnConfigChangedForTesting(/*succeeded=*/true);
959   service_.TriggerOnConfigChangedForTesting(/*succeeded=*/true);
960   service_.TriggerOnConfigChangedForTesting(/*succeeded=*/true);
961 
962   // Initial results (expect to be replaced with second read)
963   auto res = std::make_unique<struct __res_state>();
964   InitializeResState(res.get());
965   resolv_reader_->set_value(std::move(res));
966   nsswitch_reader_->set_value(kBasicNsswitchConfig);
967 
968   // Unblock first read (expect no completion because second read should begin
969   // immediately)
970   blocking_helper.Unblock();
971   blocking_helper.WaitUntilBlocked();
972   ASSERT_FALSE(callback_helper.GetResult());
973   EXPECT_TRUE(resolv_reader_->closed());
974 
975   // Setup a new config to confirm a fresh read is performed.
976   res = std::make_unique<struct __res_state>();
977   res->options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
978   struct sockaddr_in sa = {};
979   sa.sin_family = AF_INET;
980   sa.sin_port = base::HostToNet16(1000);
981   inet_pton(AF_INET, "1.2.3.4", &sa.sin_addr);
982   res->nsaddr_list[0] = sa;
983   res->nscount = 1;
984   resolv_reader_->set_value(std::move(res));
985 
986   // Unblock second read (expect completion)
987   blocking_helper.Unblock();
988   std::optional<DnsConfig> config = callback_helper.WaitForResult();
989 
990   ASSERT_TRUE(config.has_value());
991   EXPECT_TRUE(config->IsValid());
992 
993   IPEndPoint expected(IPAddress(1, 2, 3, 4), 1000);
994   EXPECT_THAT(config.value().nameservers, testing::ElementsAre(expected));
995 
996   EXPECT_TRUE(resolv_reader_->closed());
997 }
998 
999 }  // namespace
1000 
1001 }  // namespace net
1002