1 // Copyright 2012 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/dns/dns_hosts.h"
11
12 #include "base/test/metrics/histogram_tester.h"
13 #include "base/trace_event/memory_usage_estimator.h"
14 #include "build/build_config.h"
15 #include "net/base/cronet_buildflags.h"
16 #include "net/base/ip_address.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace net {
20
21 namespace {
22
23 struct ExpectedHostsEntry {
24 const char* host;
25 AddressFamily family;
26 const char* ip;
27 };
28
PopulateExpectedHosts(const ExpectedHostsEntry * entries,size_t num_entries,DnsHosts * expected_hosts_out)29 void PopulateExpectedHosts(const ExpectedHostsEntry* entries,
30 size_t num_entries,
31 DnsHosts* expected_hosts_out) {
32 for (size_t i = 0; i < num_entries; ++i) {
33 DnsHostsKey key(entries[i].host, entries[i].family);
34 IPAddress& ip_ref = (*expected_hosts_out)[key];
35 ASSERT_TRUE(ip_ref.empty());
36 ASSERT_TRUE(ip_ref.AssignFromIPLiteral(entries[i].ip));
37 ASSERT_EQ(ip_ref.size(),
38 (entries[i].family == ADDRESS_FAMILY_IPV4) ? 4u : 16u);
39 }
40 }
41
TEST(DnsHostsTest,ParseHosts)42 TEST(DnsHostsTest, ParseHosts) {
43 const std::string kContents =
44 "127.0.0.1 localhost # standard\n"
45 "\n"
46 "1.0.0.1 localhost # ignored, first hit above\n"
47 "fe00::x example company # ignored, malformed IPv6\n"
48 "1.0.0.300 company # ignored, malformed IPv4\n"
49 "1.0.0.1 # ignored, missing hostname\n"
50 "1.0.0.1\t CoMpANy # normalized to 'company' \n"
51 "::1\tlocalhost ip6-localhost ip6-loopback # comment # within a comment\n"
52 "\t fe00::0 ip6-localnet\r\n"
53 "2048::2 example\n"
54 "2048::1 company example # ignored for 'example' \n"
55 "127.0.0.1 cache1\n"
56 "127.0.0.1 cache2 # should reuse parsed IP\n"
57 "256.0.0.0 cache3 # bogus IP should not clear parsed IP cache\n"
58 "127.0.0.1 cache4 # should still be reused\n"
59 "127.0.0.2 cache5\n"
60 "127.0.0.3 .foo # entries with leading dot are ignored\n"
61 "127.0.0.3 . # just a dot is ignored\n"
62 "127.0.0.4 bar. # trailing dot is allowed, for now\n"
63 "gibberish\n"
64 "127.0.0.5 fóó.test # canonicalizes to 'xn--f-vgaa.test' due to RFC3490\n"
65 "127.0.0.6 127.0.0.1 # ignore IP host\n"
66 "2048::3 [::1] # ignore IP host";
67
68 const ExpectedHostsEntry kEntries[] = {
69 {"localhost", ADDRESS_FAMILY_IPV4, "127.0.0.1"},
70 {"company", ADDRESS_FAMILY_IPV4, "1.0.0.1"},
71 {"localhost", ADDRESS_FAMILY_IPV6, "::1"},
72 {"ip6-localhost", ADDRESS_FAMILY_IPV6, "::1"},
73 {"ip6-loopback", ADDRESS_FAMILY_IPV6, "::1"},
74 {"ip6-localnet", ADDRESS_FAMILY_IPV6, "fe00::0"},
75 {"company", ADDRESS_FAMILY_IPV6, "2048::1"},
76 {"example", ADDRESS_FAMILY_IPV6, "2048::2"},
77 {"cache1", ADDRESS_FAMILY_IPV4, "127.0.0.1"},
78 {"cache2", ADDRESS_FAMILY_IPV4, "127.0.0.1"},
79 {"cache4", ADDRESS_FAMILY_IPV4, "127.0.0.1"},
80 {"cache5", ADDRESS_FAMILY_IPV4, "127.0.0.2"},
81 {"bar.", ADDRESS_FAMILY_IPV4, "127.0.0.4"},
82 {"xn--f-vgaa.test", ADDRESS_FAMILY_IPV4, "127.0.0.5"},
83 };
84
85 DnsHosts expected_hosts, actual_hosts;
86 PopulateExpectedHosts(kEntries, std::size(kEntries), &expected_hosts);
87
88 base::HistogramTester histograms;
89 ParseHosts(kContents, &actual_hosts);
90 ASSERT_EQ(expected_hosts, actual_hosts);
91 histograms.ExpectUniqueSample("Net.DNS.DnsHosts.Count", std::size(kEntries),
92 1);
93 #if !BUILDFLAG(CRONET_BUILD)
94 histograms.ExpectUniqueSample(
95 "Net.DNS.DnsHosts.EstimateMemoryUsage",
96 base::trace_event::EstimateMemoryUsage(actual_hosts), 1);
97 #endif
98 }
99
TEST(DnsHostsTest,ParseHosts_CommaIsToken)100 TEST(DnsHostsTest, ParseHosts_CommaIsToken) {
101 const std::string kContents = "127.0.0.1 comma1,comma2";
102
103 const ExpectedHostsEntry kEntries[] = {
104 { "comma1,comma2", ADDRESS_FAMILY_IPV4, "127.0.0.1" },
105 };
106
107 DnsHosts expected_hosts, actual_hosts;
108 PopulateExpectedHosts(kEntries, std::size(kEntries), &expected_hosts);
109 ParseHostsWithCommaModeForTesting(
110 kContents, &actual_hosts, PARSE_HOSTS_COMMA_IS_TOKEN);
111 ASSERT_EQ(0UL, actual_hosts.size());
112 }
113
TEST(DnsHostsTest,ParseHosts_CommaIsWhitespace)114 TEST(DnsHostsTest, ParseHosts_CommaIsWhitespace) {
115 std::string kContents = "127.0.0.1 comma1,comma2";
116
117 const ExpectedHostsEntry kEntries[] = {
118 { "comma1", ADDRESS_FAMILY_IPV4, "127.0.0.1" },
119 { "comma2", ADDRESS_FAMILY_IPV4, "127.0.0.1" },
120 };
121
122 DnsHosts expected_hosts, actual_hosts;
123 PopulateExpectedHosts(kEntries, std::size(kEntries), &expected_hosts);
124 ParseHostsWithCommaModeForTesting(
125 kContents, &actual_hosts, PARSE_HOSTS_COMMA_IS_WHITESPACE);
126 ASSERT_EQ(expected_hosts, actual_hosts);
127 }
128
129 // Test that the right comma mode is used on each platform.
TEST(DnsHostsTest,ParseHosts_CommaModeByPlatform)130 TEST(DnsHostsTest, ParseHosts_CommaModeByPlatform) {
131 std::string kContents = "127.0.0.1 comma1,comma2";
132 DnsHosts actual_hosts;
133 ParseHosts(kContents, &actual_hosts);
134
135 #if BUILDFLAG(IS_APPLE)
136 const ExpectedHostsEntry kEntries[] = {
137 { "comma1", ADDRESS_FAMILY_IPV4, "127.0.0.1" },
138 { "comma2", ADDRESS_FAMILY_IPV4, "127.0.0.1" },
139 };
140 DnsHosts expected_hosts;
141 PopulateExpectedHosts(kEntries, std::size(kEntries), &expected_hosts);
142 ASSERT_EQ(expected_hosts, actual_hosts);
143 #else
144 ASSERT_EQ(0UL, actual_hosts.size());
145 #endif
146 }
147
TEST(DnsHostsTest,HostsParser_Empty)148 TEST(DnsHostsTest, HostsParser_Empty) {
149 DnsHosts hosts;
150 ParseHosts("", &hosts);
151 EXPECT_EQ(0u, hosts.size());
152 }
153
TEST(DnsHostsTest,HostsParser_OnlyWhitespace)154 TEST(DnsHostsTest, HostsParser_OnlyWhitespace) {
155 DnsHosts hosts;
156 ParseHosts(" ", &hosts);
157 EXPECT_EQ(0u, hosts.size());
158 }
159
TEST(DnsHostsTest,HostsParser_EndsWithNothing)160 TEST(DnsHostsTest, HostsParser_EndsWithNothing) {
161 DnsHosts hosts;
162 ParseHosts("127.0.0.1 localhost", &hosts);
163 EXPECT_EQ(1u, hosts.size());
164 }
165
TEST(DnsHostsTest,HostsParser_EndsWithWhitespace)166 TEST(DnsHostsTest, HostsParser_EndsWithWhitespace) {
167 DnsHosts hosts;
168 ParseHosts("127.0.0.1 localhost ", &hosts);
169 EXPECT_EQ(1u, hosts.size());
170 }
171
TEST(DnsHostsTest,HostsParser_EndsWithComment)172 TEST(DnsHostsTest, HostsParser_EndsWithComment) {
173 DnsHosts hosts;
174 ParseHosts("127.0.0.1 localhost # comment", &hosts);
175 EXPECT_EQ(1u, hosts.size());
176 }
177
TEST(DnsHostsTest,HostsParser_EndsWithNewline)178 TEST(DnsHostsTest, HostsParser_EndsWithNewline) {
179 DnsHosts hosts;
180 ParseHosts("127.0.0.1 localhost\n", &hosts);
181 EXPECT_EQ(1u, hosts.size());
182 }
183
TEST(DnsHostsTest,HostsParser_EndsWithTwoNewlines)184 TEST(DnsHostsTest, HostsParser_EndsWithTwoNewlines) {
185 DnsHosts hosts;
186 ParseHosts("127.0.0.1 localhost\n\n", &hosts);
187 EXPECT_EQ(1u, hosts.size());
188 }
189
TEST(DnsHostsTest,HostsParser_EndsWithNewlineAndWhitespace)190 TEST(DnsHostsTest, HostsParser_EndsWithNewlineAndWhitespace) {
191 DnsHosts hosts;
192 ParseHosts("127.0.0.1 localhost\n ", &hosts);
193 EXPECT_EQ(1u, hosts.size());
194 }
195
TEST(DnsHostsTest,HostsParser_EndsWithNewlineAndToken)196 TEST(DnsHostsTest, HostsParser_EndsWithNewlineAndToken) {
197 DnsHosts hosts;
198 ParseHosts("127.0.0.1 localhost\ntoken", &hosts);
199 EXPECT_EQ(1u, hosts.size());
200 }
201
202 } // namespace
203
204 } // namespace net
205