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/nsswitch_reader.h"
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "base/check.h"
12 #include "base/functional/bind.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace net {
17 namespace {
18
19 class TestFileReader {
20 public:
TestFileReader(std::string text)21 explicit TestFileReader(std::string text) : text_(std::move(text)) {}
22 TestFileReader(const TestFileReader&) = delete;
23 TestFileReader& operator=(const TestFileReader&) = delete;
24
GetFileReadCall()25 NsswitchReader::FileReadCall GetFileReadCall() {
26 return base::BindRepeating(&TestFileReader::ReadFile,
27 base::Unretained(this));
28 }
29
ReadFile()30 std::string ReadFile() {
31 CHECK(!already_read_);
32
33 already_read_ = true;
34 return text_;
35 }
36
37 private:
38 std::string text_;
39 bool already_read_ = false;
40 };
41
42 class NsswitchReaderTest : public testing::Test {
43 public:
44 NsswitchReaderTest() = default;
45 NsswitchReaderTest(const NsswitchReaderTest&) = delete;
46 NsswitchReaderTest& operator=(const NsswitchReaderTest&) = delete;
47
48 protected:
49 NsswitchReader reader_;
50 };
51
52 // Attempt to load the actual nsswitch.conf for the test machine and run
53 // rationality checks for the result.
TEST_F(NsswitchReaderTest,ActualReadAndParseHosts)54 TEST_F(NsswitchReaderTest, ActualReadAndParseHosts) {
55 std::vector<NsswitchReader::ServiceSpecification> services =
56 reader_.ReadAndParseHosts();
57
58 // Assume nobody will ever run this on a machine with more than 1000
59 // configured services.
60 EXPECT_THAT(services, testing::SizeIs(testing::Le(1000u)));
61
62 // Assume no service will ever have more than 10 configured actions per
63 // service.
64 for (const NsswitchReader::ServiceSpecification& service : services) {
65 EXPECT_THAT(service.actions, testing::SizeIs(testing::Le(10u)));
66 }
67 }
68
TEST_F(NsswitchReaderTest,FileReadErrorResultsInDefault)69 TEST_F(NsswitchReaderTest, FileReadErrorResultsInDefault) {
70 TestFileReader file_reader("");
71 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
72
73 std::vector<NsswitchReader::ServiceSpecification> services =
74 reader_.ReadAndParseHosts();
75
76 // Expect "files dns".
77 EXPECT_THAT(
78 services,
79 testing::ElementsAre(
80 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
81 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
82 }
83
TEST_F(NsswitchReaderTest,MissingHostsResultsInDefault)84 TEST_F(NsswitchReaderTest, MissingHostsResultsInDefault) {
85 const std::string kFile =
86 "passwd: files ldap\nshadow: files\ngroup: files ldap\n";
87 TestFileReader file_reader(kFile);
88 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
89
90 std::vector<NsswitchReader::ServiceSpecification> services =
91 reader_.ReadAndParseHosts();
92
93 // Expect "files dns".
94 EXPECT_THAT(
95 services,
96 testing::ElementsAre(
97 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
98 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
99 }
100
TEST_F(NsswitchReaderTest,ParsesAllKnownServices)101 TEST_F(NsswitchReaderTest, ParsesAllKnownServices) {
102 const std::string kFile =
103 "hosts: files dns mdns mdns4 mdns6 mdns_minimal mdns4_minimal "
104 "mdns6_minimal myhostname resolve nis";
105 TestFileReader file_reader(kFile);
106 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
107
108 std::vector<NsswitchReader::ServiceSpecification> services =
109 reader_.ReadAndParseHosts();
110
111 EXPECT_THAT(
112 services,
113 testing::ElementsAre(
114 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
115 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns),
116 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns),
117 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns4),
118 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns6),
119 NsswitchReader::ServiceSpecification(
120 NsswitchReader::Service::kMdnsMinimal),
121 NsswitchReader::ServiceSpecification(
122 NsswitchReader::Service::kMdns4Minimal),
123 NsswitchReader::ServiceSpecification(
124 NsswitchReader::Service::kMdns6Minimal),
125 NsswitchReader::ServiceSpecification(
126 NsswitchReader::Service::kMyHostname),
127 NsswitchReader::ServiceSpecification(
128 NsswitchReader::Service::kResolve),
129 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kNis)));
130 }
131
TEST_F(NsswitchReaderTest,ParsesRepeatedServices)132 TEST_F(NsswitchReaderTest, ParsesRepeatedServices) {
133 const std::string kFile = "hosts: mdns4 mdns6 mdns6 myhostname";
134 TestFileReader file_reader(kFile);
135 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
136
137 std::vector<NsswitchReader::ServiceSpecification> services =
138 reader_.ReadAndParseHosts();
139
140 EXPECT_THAT(
141 services,
142 testing::ElementsAre(
143 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns4),
144 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns6),
145 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns6),
146 NsswitchReader::ServiceSpecification(
147 NsswitchReader::Service::kMyHostname)));
148 }
149
TEST_F(NsswitchReaderTest,ParsesAllKnownActions)150 TEST_F(NsswitchReaderTest, ParsesAllKnownActions) {
151 const std::string kFile =
152 "hosts: files [UNAVAIL=RETURN] [UNAVAIL=CONTINUE] [UNAVAIL=MERGE]";
153 TestFileReader file_reader(kFile);
154 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
155
156 std::vector<NsswitchReader::ServiceSpecification> services =
157 reader_.ReadAndParseHosts();
158
159 EXPECT_THAT(services,
160 testing::ElementsAre(NsswitchReader::ServiceSpecification(
161 NsswitchReader::Service::kFiles,
162 {{/*negated=*/false, NsswitchReader::Status::kUnavailable,
163 NsswitchReader::Action::kReturn},
164 {/*negated=*/false, NsswitchReader::Status::kUnavailable,
165 NsswitchReader::Action::kContinue},
166 {/*negated=*/false, NsswitchReader::Status::kUnavailable,
167 NsswitchReader::Action::kMerge}})));
168 }
169
TEST_F(NsswitchReaderTest,ParsesAllKnownStatuses)170 TEST_F(NsswitchReaderTest, ParsesAllKnownStatuses) {
171 const std::string kFile =
172 "hosts: dns [SUCCESS=RETURN] [NOTFOUND=RETURN] [UNAVAIL=RETURN] "
173 "[TRYAGAIN=RETURN]";
174 TestFileReader file_reader(kFile);
175 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
176
177 std::vector<NsswitchReader::ServiceSpecification> services =
178 reader_.ReadAndParseHosts();
179
180 EXPECT_THAT(services,
181 testing::ElementsAre(NsswitchReader::ServiceSpecification(
182 NsswitchReader::Service::kDns,
183 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
184 NsswitchReader::Action::kReturn},
185 {/*negated=*/false, NsswitchReader::Status::kNotFound,
186 NsswitchReader::Action::kReturn},
187 {/*negated=*/false, NsswitchReader::Status::kUnavailable,
188 NsswitchReader::Action::kReturn},
189 {/*negated=*/false, NsswitchReader::Status::kTryAgain,
190 NsswitchReader::Action::kReturn}})));
191 }
192
TEST_F(NsswitchReaderTest,ParsesRepeatedActions)193 TEST_F(NsswitchReaderTest, ParsesRepeatedActions) {
194 const std::string kFile =
195 "hosts: nis [!SUCCESS=RETURN] [NOTFOUND=RETURN] [NOTFOUND=RETURN] "
196 "[!UNAVAIL=RETURN]";
197 TestFileReader file_reader(kFile);
198 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
199
200 std::vector<NsswitchReader::ServiceSpecification> services =
201 reader_.ReadAndParseHosts();
202
203 EXPECT_THAT(services,
204 testing::ElementsAre(NsswitchReader::ServiceSpecification(
205 NsswitchReader::Service::kNis,
206 {{/*negated=*/true, NsswitchReader::Status::kSuccess,
207 NsswitchReader::Action::kReturn},
208 {/*negated=*/false, NsswitchReader::Status::kNotFound,
209 NsswitchReader::Action::kReturn},
210 {/*negated=*/false, NsswitchReader::Status::kNotFound,
211 NsswitchReader::Action::kReturn},
212 {/*negated=*/true, NsswitchReader::Status::kUnavailable,
213 NsswitchReader::Action::kReturn}})));
214 }
215
TEST_F(NsswitchReaderTest,ParsesCombinedActionLists)216 TEST_F(NsswitchReaderTest, ParsesCombinedActionLists) {
217 const std::string kFile =
218 "hosts: dns [SUCCESS=RETURN !NOTFOUND=RETURN UNAVAIL=RETURN] files";
219 TestFileReader file_reader(kFile);
220 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
221
222 std::vector<NsswitchReader::ServiceSpecification> services =
223 reader_.ReadAndParseHosts();
224
225 EXPECT_THAT(services,
226 testing::ElementsAre(
227 NsswitchReader::ServiceSpecification(
228 NsswitchReader::Service::kDns,
229 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
230 NsswitchReader::Action::kReturn},
231 {/*negated=*/true, NsswitchReader::Status::kNotFound,
232 NsswitchReader::Action::kReturn},
233 {/*negated=*/false, NsswitchReader::Status::kUnavailable,
234 NsswitchReader::Action::kReturn}}),
235 NsswitchReader::ServiceSpecification(
236 NsswitchReader::Service::kFiles)));
237 }
238
TEST_F(NsswitchReaderTest,HandlesAtypicalWhitespace)239 TEST_F(NsswitchReaderTest, HandlesAtypicalWhitespace) {
240 const std::string kFile =
241 " database: service \n\n hosts: files\tdns mdns4 \t mdns6 \t "
242 "\t\n\t\n";
243 TestFileReader file_reader(kFile);
244 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
245
246 std::vector<NsswitchReader::ServiceSpecification> services =
247 reader_.ReadAndParseHosts();
248
249 EXPECT_THAT(
250 services,
251 testing::ElementsAre(
252 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
253 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns),
254 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns4),
255 NsswitchReader::ServiceSpecification(
256 NsswitchReader::Service::kMdns6)));
257 }
258
TEST_F(NsswitchReaderTest,HandlesAtypicalWhitespaceInActions)259 TEST_F(NsswitchReaderTest, HandlesAtypicalWhitespaceInActions) {
260 const std::string kFile =
261 "hosts: dns [ !UNAVAIL=MERGE \t NOTFOUND=RETURN\t][ UNAVAIL=CONTINUE]";
262 TestFileReader file_reader(kFile);
263 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
264
265 std::vector<NsswitchReader::ServiceSpecification> services =
266 reader_.ReadAndParseHosts();
267
268 EXPECT_THAT(services,
269 testing::ElementsAre(NsswitchReader::ServiceSpecification(
270 NsswitchReader::Service::kDns,
271 {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
272 NsswitchReader::Action::kMerge},
273 {/*negated=*/false, NsswitchReader::Status::kNotFound,
274 NsswitchReader::Action::kReturn},
275 {/*negated=*/false, NsswitchReader::Status::kUnavailable,
276 NsswitchReader::Action::kContinue}})));
277 }
278
TEST_F(NsswitchReaderTest,ParsesActionsWithoutService)279 TEST_F(NsswitchReaderTest, ParsesActionsWithoutService) {
280 const std::string kFile = "hosts: [SUCCESS=RETURN]";
281 TestFileReader file_reader(kFile);
282 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
283
284 std::vector<NsswitchReader::ServiceSpecification> services =
285 reader_.ReadAndParseHosts();
286
287 EXPECT_THAT(services,
288 testing::ElementsAre(NsswitchReader::ServiceSpecification(
289 NsswitchReader::Service::kUnknown,
290 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
291 NsswitchReader::Action::kReturn}})));
292 }
293
TEST_F(NsswitchReaderTest,ParsesNegatedActions)294 TEST_F(NsswitchReaderTest, ParsesNegatedActions) {
295 const std::string kFile =
296 "hosts: mdns_minimal [!UNAVAIL=RETURN] [NOTFOUND=CONTINUE] "
297 "[!TRYAGAIN=CONTINUE]";
298 TestFileReader file_reader(kFile);
299 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
300
301 std::vector<NsswitchReader::ServiceSpecification> services =
302 reader_.ReadAndParseHosts();
303
304 EXPECT_THAT(services,
305 testing::ElementsAre(NsswitchReader::ServiceSpecification(
306 NsswitchReader::Service::kMdnsMinimal,
307 {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
308 NsswitchReader::Action::kReturn},
309 {/*negated=*/false, NsswitchReader::Status::kNotFound,
310 NsswitchReader::Action::kContinue},
311 {/*negated=*/true, NsswitchReader::Status::kTryAgain,
312 NsswitchReader::Action::kContinue}})));
313 }
314
TEST_F(NsswitchReaderTest,ParsesUnrecognizedServiceAsUnknown)315 TEST_F(NsswitchReaderTest, ParsesUnrecognizedServiceAsUnknown) {
316 const std::string kFile =
317 "passwd: files\nhosts: files super_awesome_service myhostname";
318 TestFileReader file_reader(kFile);
319 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
320
321 std::vector<NsswitchReader::ServiceSpecification> services =
322 reader_.ReadAndParseHosts();
323
324 EXPECT_THAT(services,
325 testing::ElementsAre(NsswitchReader::ServiceSpecification(
326 NsswitchReader::Service::kFiles),
327 NsswitchReader::ServiceSpecification(
328 NsswitchReader::Service::kUnknown),
329 NsswitchReader::ServiceSpecification(
330 NsswitchReader::Service::kMyHostname)));
331 }
332
TEST_F(NsswitchReaderTest,ParsesUnrecognizedStatusAsUnknown)333 TEST_F(NsswitchReaderTest, ParsesUnrecognizedStatusAsUnknown) {
334 const std::string kFile =
335 "hosts: nis [HELLO=CONTINUE]\nshadow: service\ndatabase: cheese";
336 TestFileReader file_reader(kFile);
337 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
338
339 std::vector<NsswitchReader::ServiceSpecification> services =
340 reader_.ReadAndParseHosts();
341
342 EXPECT_THAT(services,
343 testing::ElementsAre(NsswitchReader::ServiceSpecification(
344 NsswitchReader::Service::kNis,
345 {{/*negated=*/false, NsswitchReader::Status::kUnknown,
346 NsswitchReader::Action::kContinue}})));
347 }
348
TEST_F(NsswitchReaderTest,ParsesUnrecognizedActionAsUnknown)349 TEST_F(NsswitchReaderTest, ParsesUnrecognizedActionAsUnknown) {
350 const std::string kFile =
351 "more: service\nhosts: mdns6 [!UNAVAIL=HI]\nshadow: service";
352 TestFileReader file_reader(kFile);
353 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
354
355 std::vector<NsswitchReader::ServiceSpecification> services =
356 reader_.ReadAndParseHosts();
357
358 EXPECT_THAT(services,
359 testing::ElementsAre(NsswitchReader::ServiceSpecification(
360 NsswitchReader::Service::kMdns6,
361 {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
362 NsswitchReader::Action::kUnknown}})));
363 }
364
TEST_F(NsswitchReaderTest,ParsesInvalidActionsAsUnknown)365 TEST_F(NsswitchReaderTest, ParsesInvalidActionsAsUnknown) {
366 const std::string kFile = "hosts: mdns_minimal [a=b=c] nis";
367 TestFileReader file_reader(kFile);
368 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
369
370 std::vector<NsswitchReader::ServiceSpecification> services =
371 reader_.ReadAndParseHosts();
372
373 EXPECT_THAT(
374 services,
375 testing::ElementsAre(
376 NsswitchReader::ServiceSpecification(
377 NsswitchReader::Service::kMdnsMinimal,
378 {{/*negated=*/false, NsswitchReader::Status::kUnknown,
379 NsswitchReader::Action::kUnknown}}),
380 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kNis)));
381 }
382
TEST_F(NsswitchReaderTest,IgnoresInvalidlyClosedActions)383 TEST_F(NsswitchReaderTest, IgnoresInvalidlyClosedActions) {
384 const std::string kFile = "hosts: myhostname [SUCCESS=MERGE";
385 TestFileReader file_reader(kFile);
386 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
387
388 std::vector<NsswitchReader::ServiceSpecification> services =
389 reader_.ReadAndParseHosts();
390
391 EXPECT_THAT(services,
392 testing::ElementsAre(NsswitchReader::ServiceSpecification(
393 NsswitchReader::Service::kMyHostname,
394 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
395 NsswitchReader::Action::kMerge}})));
396 }
397
TEST_F(NsswitchReaderTest,ParsesServicesAfterInvalidlyClosedActionsAsUnknown)398 TEST_F(NsswitchReaderTest, ParsesServicesAfterInvalidlyClosedActionsAsUnknown) {
399 const std::string kFile = "hosts: resolve [SUCCESS=CONTINUE dns";
400 TestFileReader file_reader(kFile);
401 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
402
403 std::vector<NsswitchReader::ServiceSpecification> services =
404 reader_.ReadAndParseHosts();
405
406 EXPECT_THAT(services,
407 testing::ElementsAre(NsswitchReader::ServiceSpecification(
408 NsswitchReader::Service::kResolve,
409 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
410 NsswitchReader::Action::kContinue},
411 {/*negated=*/false, NsswitchReader::Status::kUnknown,
412 NsswitchReader::Action::kUnknown}})));
413 }
414
TEST_F(NsswitchReaderTest,IgnoresComments)415 TEST_F(NsswitchReaderTest, IgnoresComments) {
416 const std::string kFile =
417 "#hosts: files super_awesome_service myhostname\nnetmask: service";
418 TestFileReader file_reader(kFile);
419 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
420
421 std::vector<NsswitchReader::ServiceSpecification> services =
422 reader_.ReadAndParseHosts();
423
424 // Expect "files dns" due to not finding an uncommented "hosts:" row.
425 EXPECT_THAT(
426 services,
427 testing::ElementsAre(
428 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
429 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
430 }
431
TEST_F(NsswitchReaderTest,IgnoresEndOfLineComments)432 TEST_F(NsswitchReaderTest, IgnoresEndOfLineComments) {
433 const std::string kFile =
434 "hosts: files super_awesome_service myhostname # dns";
435 TestFileReader file_reader(kFile);
436 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
437
438 std::vector<NsswitchReader::ServiceSpecification> services =
439 reader_.ReadAndParseHosts();
440
441 EXPECT_THAT(services,
442 testing::ElementsAre(NsswitchReader::ServiceSpecification(
443 NsswitchReader::Service::kFiles),
444 NsswitchReader::ServiceSpecification(
445 NsswitchReader::Service::kUnknown),
446 NsswitchReader::ServiceSpecification(
447 NsswitchReader::Service::kMyHostname)));
448 }
449
TEST_F(NsswitchReaderTest,IgnoresCapitalization)450 TEST_F(NsswitchReaderTest, IgnoresCapitalization) {
451 const std::string kFile = "HoStS: mDNS6 [!uNaVaIl=MeRgE]";
452 TestFileReader file_reader(kFile);
453 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
454
455 std::vector<NsswitchReader::ServiceSpecification> services =
456 reader_.ReadAndParseHosts();
457
458 EXPECT_THAT(services,
459 testing::ElementsAre(NsswitchReader::ServiceSpecification(
460 NsswitchReader::Service::kMdns6,
461 {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
462 NsswitchReader::Action::kMerge}})));
463 }
464
TEST_F(NsswitchReaderTest,IgnoresEmptyActions)465 TEST_F(NsswitchReaderTest, IgnoresEmptyActions) {
466 const std::string kFile = "hosts: mdns_minimal [ \t ][] [ ]";
467 TestFileReader file_reader(kFile);
468 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
469
470 std::vector<NsswitchReader::ServiceSpecification> services =
471 reader_.ReadAndParseHosts();
472
473 EXPECT_THAT(services,
474 testing::ElementsAre(NsswitchReader::ServiceSpecification(
475 NsswitchReader::Service::kMdnsMinimal)));
476 }
477
TEST_F(NsswitchReaderTest,IgnoresRepeatedActionBrackets)478 TEST_F(NsswitchReaderTest, IgnoresRepeatedActionBrackets) {
479 const std::string kFile = "hosts: mdns [[SUCCESS=RETURN]]]dns";
480 TestFileReader file_reader(kFile);
481 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
482
483 std::vector<NsswitchReader::ServiceSpecification> services =
484 reader_.ReadAndParseHosts();
485
486 EXPECT_THAT(
487 services,
488 testing::ElementsAre(
489 NsswitchReader::ServiceSpecification(
490 NsswitchReader::Service::kMdns,
491 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
492 NsswitchReader::Action::kReturn}}),
493 NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
494 }
495
TEST_F(NsswitchReaderTest,IgnoresRepeatedActionBracketsWithWhitespace)496 TEST_F(NsswitchReaderTest, IgnoresRepeatedActionBracketsWithWhitespace) {
497 const std::string kFile = "hosts: mdns [ [ SUCCESS=RETURN ]\t] ]\t mdns6";
498 TestFileReader file_reader(kFile);
499 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
500
501 std::vector<NsswitchReader::ServiceSpecification> services =
502 reader_.ReadAndParseHosts();
503
504 EXPECT_THAT(services,
505 testing::ElementsAre(
506 NsswitchReader::ServiceSpecification(
507 NsswitchReader::Service::kMdns,
508 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
509 NsswitchReader::Action::kReturn}}),
510 NsswitchReader::ServiceSpecification(
511 NsswitchReader::Service::kMdns6)));
512 }
513
TEST_F(NsswitchReaderTest,RejectsNonSensicalActionBrackets)514 TEST_F(NsswitchReaderTest, RejectsNonSensicalActionBrackets) {
515 const std::string kFile = "hosts: mdns4 [UNAVAIL[=MERGE]]";
516 TestFileReader file_reader(kFile);
517 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
518
519 std::vector<NsswitchReader::ServiceSpecification> services =
520 reader_.ReadAndParseHosts();
521
522 EXPECT_THAT(services,
523 testing::ElementsAre(NsswitchReader::ServiceSpecification(
524 NsswitchReader::Service::kMdns4,
525 {{/*negated=*/false, NsswitchReader::Status::kUnknown,
526 NsswitchReader::Action::kMerge}})));
527 }
528
TEST_F(NsswitchReaderTest,RejectsServicesWithBrackets)529 TEST_F(NsswitchReaderTest, RejectsServicesWithBrackets) {
530 const std::string kFile = "hosts: se]r[vice[name";
531 TestFileReader file_reader(kFile);
532 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
533
534 std::vector<NsswitchReader::ServiceSpecification> services =
535 reader_.ReadAndParseHosts();
536
537 EXPECT_THAT(services,
538 testing::ElementsAre(NsswitchReader::ServiceSpecification(
539 NsswitchReader::Service::kUnknown)));
540 }
541
542 // Other than the case of repeating opening brackets, nested brackets are not
543 // valid and should just get treated as part of an action label.
TEST_F(NsswitchReaderTest,RejectsNestedActionBrackets)544 TEST_F(NsswitchReaderTest, RejectsNestedActionBrackets) {
545 const std::string kFile =
546 "hosts: nis [SUCCESS=RETURN [NOTFOUND=CONTINUE] UNAVAIL=MERGE]";
547 TestFileReader file_reader(kFile);
548 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
549
550 std::vector<NsswitchReader::ServiceSpecification> services =
551 reader_.ReadAndParseHosts();
552
553 EXPECT_THAT(services,
554 testing::ElementsAre(
555 NsswitchReader::ServiceSpecification(
556 NsswitchReader::Service::kNis,
557 {{/*negated=*/false, NsswitchReader::Status::kSuccess,
558 NsswitchReader::Action::kReturn},
559 {/*negated=*/false, NsswitchReader::Status::kUnknown,
560 NsswitchReader::Action::kContinue}}),
561 NsswitchReader::ServiceSpecification(
562 NsswitchReader::Service::kUnknown)));
563 }
564
TEST_F(NsswitchReaderTest,IgnoresEmptyActionWithRepeatedBrackets)565 TEST_F(NsswitchReaderTest, IgnoresEmptyActionWithRepeatedBrackets) {
566 const std::string kFile = "hosts: files [[[]]]] mdns";
567 TestFileReader file_reader(kFile);
568 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
569
570 std::vector<NsswitchReader::ServiceSpecification> services =
571 reader_.ReadAndParseHosts();
572
573 EXPECT_THAT(services,
574 testing::ElementsAre(NsswitchReader::ServiceSpecification(
575 NsswitchReader::Service::kFiles),
576 NsswitchReader::ServiceSpecification(
577 NsswitchReader::Service::kMdns)));
578 }
579
TEST_F(NsswitchReaderTest,IgnoresEmptyActionAtEndOfString)580 TEST_F(NsswitchReaderTest, IgnoresEmptyActionAtEndOfString) {
581 const std::string kFile = "hosts: dns [[";
582 TestFileReader file_reader(kFile);
583 reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
584
585 std::vector<NsswitchReader::ServiceSpecification> services =
586 reader_.ReadAndParseHosts();
587
588 EXPECT_THAT(services,
589 testing::ElementsAre(NsswitchReader::ServiceSpecification(
590 NsswitchReader::Service::kDns)));
591 }
592
593 } // namespace
594 } // namespace net
595