xref: /aosp_15_r20/external/cronet/net/dns/address_info_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 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/address_info.h"
6 
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include <array>
12 #include <memory>
13 #include <optional>
14 #include <string_view>
15 
16 #include "base/check_op.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/sys_byteorder.h"
19 #include "build/build_config.h"
20 #include "net/base/address_list.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/sys_addrinfo.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 namespace net {
27 
28 namespace {
29 
30 class MockAddrInfoGetter : public AddrInfoGetter {
31  public:
32   std::unique_ptr<addrinfo, FreeAddrInfoFunc> getaddrinfo(
33       const std::string& host,
34       const addrinfo* hints,
35       int* out_os_error,
36       handles::NetworkHandle network) override;
37 
38  private:
39   struct IpAndPort {
40     struct Ip {
41       uint8_t a;
42       uint8_t b;
43       uint8_t c;
44       uint8_t d;
45     };
46     Ip ip;
47     int port;
48   };
49 
50   // Initialises `addr` and `ai` from `ip_and_port`, `canonical_name` and
51   // `ai_next`.
52   static void InitializeAddrinfo(const IpAndPort& ip_and_port,
53                                  char* canonical_name,
54                                  addrinfo* ai_next,
55                                  sockaddr_in* addr,
56                                  addrinfo* ai);
57 
58   // Allocates and initialises an addrinfo structure containing the ip addresses
59   // and ports from `ipp` and the name `canonical_name`. This function is
60   // designed to be used within getaddrinfo(), which returns a raw pointer even
61   // though it transfers ownership. So this function does the same. Since
62   // addrinfo is a C-style variable-sized structure it cannot be allocated with
63   // new. It is allocated with malloc() instead, so it must be freed with
64   // free().
65   template <size_t N>
66   static std::unique_ptr<addrinfo, FreeAddrInfoFunc> MakeAddrInfoList(
67       const IpAndPort (&ipp)[N],
68       std::string_view canonical_name);
69 
70   static std::unique_ptr<addrinfo, FreeAddrInfoFunc> MakeAddrInfo(
71       IpAndPort ipp,
72       std::string_view canonical_name);
73 };
74 
75 template <size_t N>
76 std::unique_ptr<addrinfo, FreeAddrInfoFunc>
MakeAddrInfoList(const IpAndPort (& ipp)[N],std::string_view canonical_name)77 MockAddrInfoGetter::MakeAddrInfoList(const IpAndPort (&ipp)[N],
78                                      std::string_view canonical_name) {
79   struct Buffer {
80     addrinfo ai[N];
81     sockaddr_in addr[N];
82     char canonical_name[256];
83   };
84 
85   CHECK_LE(canonical_name.size(), 255u);
86 
87   Buffer* const buffer = new Buffer();
88   memset(buffer, 0x0, sizeof(Buffer));
89 
90   // At least one trailing nul byte on buffer->canonical_name was added by
91   // memset() above.
92   memcpy(buffer->canonical_name, canonical_name.data(), canonical_name.size());
93 
94   for (size_t i = 0; i < N; ++i) {
95     InitializeAddrinfo(ipp[i], buffer->canonical_name,
96                        i + 1 < N ? buffer->ai + i + 1 : nullptr,
97                        buffer->addr + i, buffer->ai + i);
98   }
99 
100   return {reinterpret_cast<addrinfo*>(buffer),
101           [](addrinfo* ai) { delete reinterpret_cast<Buffer*>(ai); }};
102 }
103 
MakeAddrInfo(IpAndPort ipp,std::string_view canonical_name)104 std::unique_ptr<addrinfo, FreeAddrInfoFunc> MockAddrInfoGetter::MakeAddrInfo(
105     IpAndPort ipp,
106     std::string_view canonical_name) {
107   return MakeAddrInfoList({ipp}, canonical_name);
108 }
109 
InitializeAddrinfo(const IpAndPort & ip_and_port,char * canonical_name,addrinfo * ai_next,sockaddr_in * addr,addrinfo * ai)110 void MockAddrInfoGetter::InitializeAddrinfo(const IpAndPort& ip_and_port,
111                                             char* canonical_name,
112                                             addrinfo* ai_next,
113                                             sockaddr_in* addr,
114                                             addrinfo* ai) {
115   const uint8_t ip[4] = {ip_and_port.ip.a, ip_and_port.ip.b, ip_and_port.ip.c,
116                          ip_and_port.ip.d};
117   memcpy(&addr->sin_addr, ip, 4);
118   addr->sin_family = AF_INET;
119   addr->sin_port =
120       base::HostToNet16(base::checked_cast<uint16_t>(ip_and_port.port));
121 
122   ai->ai_family = AF_INET;
123   ai->ai_socktype = SOCK_STREAM;
124   ai->ai_addrlen = sizeof(sockaddr_in);
125   ai->ai_addr = reinterpret_cast<sockaddr*>(addr);
126   ai->ai_canonname =
127       reinterpret_cast<decltype(ai->ai_canonname)>(canonical_name);
128   if (ai_next)
129     ai->ai_next = ai_next;
130 }
131 
getaddrinfo(const std::string & host,const addrinfo *,int * out_os_error,handles::NetworkHandle)132 std::unique_ptr<addrinfo, FreeAddrInfoFunc> MockAddrInfoGetter::getaddrinfo(
133     const std::string& host,
134     const addrinfo* /* hints */,
135     int* out_os_error,
136     handles::NetworkHandle) {
137   // Presume success
138   *out_os_error = 0;
139 
140   if (host == std::string("canonical.bar.com"))
141     return MakeAddrInfo({{1, 2, 3, 4}, 80}, "canonical.bar.com");
142   else if (host == "iteration.test")
143     return MakeAddrInfoList({{{10, 20, 30, 40}, 80},
144                              {{11, 21, 31, 41}, 81},
145                              {{12, 22, 32, 42}, 82}},
146                             "iteration.test");
147   else if (host == "alllocalhost.com")
148     return MakeAddrInfoList(
149         {{{127, 0, 0, 1}, 80}, {{127, 0, 0, 2}, 80}, {{127, 0, 0, 3}, 80}},
150         "alllocalhost.com");
151   else if (host == "not.alllocalhost.com")
152     return MakeAddrInfoList(
153         {{{128, 0, 0, 1}, 80}, {{127, 0, 0, 2}, 80}, {{127, 0, 0, 3}, 80}},
154         "not.alllocalhost.com");
155   else if (host == "www.example.com")
156     return MakeAddrInfo({{8, 8, 8, 8}, 80}, "www.example.com");
157 
158   // Failure
159   *out_os_error = 1;
160 
161   return {nullptr, [](addrinfo*) {}};
162 }
163 
MakeHints(AddressFamily address_family,HostResolverFlags host_resolver_flags)164 std::unique_ptr<addrinfo> MakeHints(AddressFamily address_family,
165                                     HostResolverFlags host_resolver_flags) {
166   auto hints = std::make_unique<addrinfo>();
167   *hints = {0};
168 
169   switch (address_family) {
170     case ADDRESS_FAMILY_IPV4:
171       hints->ai_family = AF_INET;
172       break;
173     case ADDRESS_FAMILY_IPV6:
174       hints->ai_family = AF_INET6;
175       break;
176     case ADDRESS_FAMILY_UNSPECIFIED:
177       hints->ai_family = AF_UNSPEC;
178       break;
179   }
180 
181   if (host_resolver_flags & HOST_RESOLVER_CANONNAME)
182     hints->ai_flags |= AI_CANONNAME;
183 
184   hints->ai_socktype = SOCK_STREAM;
185 
186   return hints;
187 }
188 
TEST(AddressInfoTest,Failure)189 TEST(AddressInfoTest, Failure) {
190   auto getter = std::make_unique<MockAddrInfoGetter>();
191   auto [ai, err, os_error] = AddressInfo::Get(
192       "failure.com", *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
193       std::move(getter));
194 
195   EXPECT_FALSE(ai);
196   EXPECT_NE(err, OK);
197   EXPECT_NE(os_error, 0);
198 }
199 
200 #if BUILDFLAG(IS_WIN)
201 // Note: this test is descriptive, not prescriptive.
TEST(AddressInfoTest,FailureWin)202 TEST(AddressInfoTest, FailureWin) {
203   auto getter = std::make_unique<MockAddrInfoGetter>();
204   auto [ai, err, os_error] = AddressInfo::Get(
205       "failure.com", *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
206       std::move(getter));
207 
208   EXPECT_FALSE(ai);
209   EXPECT_EQ(err, ERR_NAME_RESOLUTION_FAILED);
210   EXPECT_NE(os_error, 0);
211 }
212 #endif  // BUILDFLAG(IS_WIN)
213 
214 #if BUILDFLAG(IS_ANDROID)
215 // Note: this test is descriptive, not prescriptive.
TEST(AddressInfoTest,FailureAndroid)216 TEST(AddressInfoTest, FailureAndroid) {
217   auto getter = std::make_unique<MockAddrInfoGetter>();
218   auto [ai, err, os_error] = AddressInfo::Get(
219       "failure.com", *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
220       std::move(getter));
221 
222   EXPECT_FALSE(ai);
223   EXPECT_EQ(err, ERR_NAME_NOT_RESOLVED);
224   EXPECT_NE(os_error, 0);
225 }
226 #endif  // BUILDFLAG(IS_ANDROID)
227 
TEST(AddressInfoTest,Canonical)228 TEST(AddressInfoTest, Canonical) {
229   auto [ai, err, os_error] =
230       AddressInfo::Get("canonical.bar.com",
231                        *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
232                        std::make_unique<MockAddrInfoGetter>());
233 
234   EXPECT_TRUE(ai);
235   EXPECT_EQ(err, OK);
236   EXPECT_EQ(os_error, 0);
237   EXPECT_THAT(ai->GetCanonicalName(),
238               std::optional<std::string>("canonical.bar.com"));
239 }
240 
TEST(AddressInfoTest,Iteration)241 TEST(AddressInfoTest, Iteration) {
242   auto [ai, err, os_error] =
243       AddressInfo::Get("iteration.test",
244                        *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
245                        std::make_unique<MockAddrInfoGetter>());
246 
247   EXPECT_TRUE(ai);
248   EXPECT_EQ(err, OK);
249   EXPECT_EQ(os_error, 0);
250 
251   {
252     int count = 0;
253     for (const auto& addr_info : *ai) {
254       const sockaddr_in* addr =
255           reinterpret_cast<sockaddr_in*>(addr_info.ai_addr);
256       EXPECT_EQ(base::HostToNet16(addr->sin_port) % 10, count % 10);
257       ++count;
258     }
259 
260     EXPECT_EQ(count, 3);
261   }
262 
263   {
264     int count = 0;
265     for (auto&& aii : ai.value()) {
266       const sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(aii.ai_addr);
267       EXPECT_EQ(base::HostToNet16(addr->sin_port) % 10, count % 10);
268       ++count;
269     }
270 
271     EXPECT_EQ(count, 3);
272   }
273 }
274 
TEST(AddressInfoTest,IsAllLocalhostOfOneFamily)275 TEST(AddressInfoTest, IsAllLocalhostOfOneFamily) {
276   auto [ai, err, os_error] =
277       AddressInfo::Get("alllocalhost.com",
278                        *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
279                        std::make_unique<MockAddrInfoGetter>());
280 
281   EXPECT_TRUE(ai);
282   EXPECT_EQ(err, OK);
283   EXPECT_EQ(os_error, 0);
284   EXPECT_TRUE(ai->IsAllLocalhostOfOneFamily());
285 }
286 
TEST(AddressInfoTest,IsAllLocalhostOfOneFamilyFalse)287 TEST(AddressInfoTest, IsAllLocalhostOfOneFamilyFalse) {
288   auto [ai, err, os_error] =
289       AddressInfo::Get("not.alllocalhost.com",
290                        *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
291                        std::make_unique<MockAddrInfoGetter>());
292 
293   EXPECT_TRUE(ai);
294   EXPECT_EQ(err, OK);
295   EXPECT_EQ(os_error, 0);
296   EXPECT_FALSE(ai->IsAllLocalhostOfOneFamily());
297 }
298 
TEST(AddressInfoTest,CreateAddressList)299 TEST(AddressInfoTest, CreateAddressList) {
300   auto [ai, err, os_error] =
301       AddressInfo::Get("www.example.com",
302                        *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
303                        std::make_unique<MockAddrInfoGetter>());
304 
305   EXPECT_TRUE(ai);
306   EXPECT_EQ(err, OK);
307   EXPECT_EQ(os_error, 0);
308 
309   AddressList list = ai->CreateAddressList();
310 
311   // Verify one result.
312   ASSERT_EQ(1u, list.size());
313   ASSERT_EQ(ADDRESS_FAMILY_IPV4, list[0].GetFamily());
314 
315   // Check if operator= works.
316   AddressList copy;
317   copy = list;
318   ASSERT_EQ(1u, copy.size());
319 }
320 
321 }  // namespace
322 }  // namespace net
323