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/public/resolv_reader.h"
6
7 #include <netinet/in.h>
8 #include <resolv.h>
9 #include <sys/types.h>
10
11 #include <memory>
12 #include <type_traits>
13 #include <utility>
14 #include <vector>
15
16 #include "base/check_op.h"
17 #include "base/functional/bind.h"
18 #include "build/build_config.h"
19 #include "net/base/ip_endpoint.h"
20 #include "third_party/abseil-cpp/absl/types/optional.h"
21
22 namespace net {
23
GetResState()24 std::unique_ptr<ScopedResState> ResolvReader::GetResState() {
25 auto res = std::make_unique<ScopedResState>();
26 if (!res->IsValid())
27 return nullptr;
28 return res;
29 }
30
GetNameservers(const struct __res_state & res)31 absl::optional<std::vector<IPEndPoint>> GetNameservers(
32 const struct __res_state& res) {
33 std::vector<IPEndPoint> nameservers;
34
35 if (!(res.options & RES_INIT))
36 return absl::nullopt;
37
38 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FREEBSD)
39 union res_sockaddr_union addresses[MAXNS];
40 int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS);
41 DCHECK_GE(nscount, 0);
42 DCHECK_LE(nscount, MAXNS);
43 for (int i = 0; i < nscount; ++i) {
44 IPEndPoint ipe;
45 if (!ipe.FromSockAddr(
46 reinterpret_cast<const struct sockaddr*>(&addresses[i]),
47 sizeof addresses[i])) {
48 return absl::nullopt;
49 }
50 nameservers.push_back(ipe);
51 }
52 #elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
53 static_assert(std::extent<decltype(res.nsaddr_list)>() >= MAXNS &&
54 std::extent<decltype(res._u._ext.nsaddrs)>() >= MAXNS,
55 "incompatible libresolv res_state");
56 DCHECK_LE(res.nscount, MAXNS);
57 // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
58 // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
59 // but we have to combine the two arrays ourselves.
60 for (int i = 0; i < res.nscount; ++i) {
61 IPEndPoint ipe;
62 const struct sockaddr* addr = nullptr;
63 size_t addr_len = 0;
64 if (res.nsaddr_list[i].sin_family) { // The indicator used by res_nsend.
65 addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
66 addr_len = sizeof res.nsaddr_list[i];
67 } else if (res._u._ext.nsaddrs[i]) {
68 addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
69 addr_len = sizeof *res._u._ext.nsaddrs[i];
70 } else {
71 return absl::nullopt;
72 }
73 if (!ipe.FromSockAddr(addr, addr_len))
74 return absl::nullopt;
75 nameservers.push_back(ipe);
76 }
77 #else // !(BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
78 // || BUILDFLAG(IS_FREEBSD))
79 DCHECK_LE(res.nscount, MAXNS);
80 for (int i = 0; i < res.nscount; ++i) {
81 IPEndPoint ipe;
82 if (!ipe.FromSockAddr(
83 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
84 sizeof res.nsaddr_list[i])) {
85 return absl::nullopt;
86 }
87 nameservers.push_back(ipe);
88 }
89 #endif
90
91 return nameservers;
92 }
93
94 } // namespace net
95