|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <resolv.h> | 
|  |  | 
|  | #include "base/sys_byteorder.h" | 
|  | #include "net/dns/dns_config_service_posix.h" | 
|  |  | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace net { | 
|  | namespace { | 
|  |  | 
|  | // MAXNS is normally 3, but let's test 4 if possible. | 
|  | const char* kNameserversIPv4[] = { | 
|  | "8.8.8.8", | 
|  | "192.168.1.1", | 
|  | "63.1.2.4", | 
|  | "1.0.0.1", | 
|  | }; | 
|  |  | 
|  | #if defined(OS_LINUX) | 
|  | const char* kNameserversIPv6[] = { | 
|  | NULL, | 
|  | "2001:DB8:0::42", | 
|  | NULL, | 
|  | "::FFFF:129.144.52.38", | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | // Fills in |res| with sane configuration. | 
|  | void InitializeResState(res_state res) { | 
|  | memset(res, 0, sizeof(*res)); | 
|  | res->options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | | 
|  | RES_ROTATE; | 
|  | res->ndots = 2; | 
|  | res->retrans = 4; | 
|  | res->retry = 7; | 
|  |  | 
|  | const char kDnsrch[] = "chromium.org" "\0" "example.com"; | 
|  | memcpy(res->defdname, kDnsrch, sizeof(kDnsrch)); | 
|  | res->dnsrch[0] = res->defdname; | 
|  | res->dnsrch[1] = res->defdname + sizeof("chromium.org"); | 
|  |  | 
|  | for (unsigned i = 0; i < arraysize(kNameserversIPv4) && i < MAXNS; ++i) { | 
|  | struct sockaddr_in sa; | 
|  | sa.sin_family = AF_INET; | 
|  | sa.sin_port = base::HostToNet16(NS_DEFAULTPORT + i); | 
|  | inet_pton(AF_INET, kNameserversIPv4[i], &sa.sin_addr); | 
|  | res->nsaddr_list[i] = sa; | 
|  | ++res->nscount; | 
|  | } | 
|  |  | 
|  | #if defined(OS_LINUX) | 
|  | // Install IPv6 addresses, replacing the corresponding IPv4 addresses. | 
|  | unsigned nscount6 = 0; | 
|  | for (unsigned i = 0; i < arraysize(kNameserversIPv6) && i < MAXNS; ++i) { | 
|  | if (!kNameserversIPv6[i]) | 
|  | continue; | 
|  | // Must use malloc to mimick res_ninit. | 
|  | struct sockaddr_in6 *sa6; | 
|  | sa6 = (struct sockaddr_in6 *)malloc(sizeof(*sa6)); | 
|  | sa6->sin6_family = AF_INET6; | 
|  | sa6->sin6_port = base::HostToNet16(NS_DEFAULTPORT - i); | 
|  | inet_pton(AF_INET6, kNameserversIPv6[i], &sa6->sin6_addr); | 
|  | res->_u._ext.nsaddrs[i] = sa6; | 
|  | memset(&res->nsaddr_list[i], 0, sizeof res->nsaddr_list[i]); | 
|  | ++nscount6; | 
|  | } | 
|  | res->_u._ext.nscount6 = nscount6; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void CloseResState(res_state res) { | 
|  | #if defined(OS_LINUX) | 
|  | for (int i = 0; i < res->nscount; ++i) { | 
|  | if (res->_u._ext.nsaddrs[i] != NULL) | 
|  | free(res->_u._ext.nsaddrs[i]); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void InitializeExpectedConfig(DnsConfig* config) { | 
|  | config->ndots = 2; | 
|  | config->timeout = base::TimeDelta::FromSeconds(4); | 
|  | config->attempts = 7; | 
|  | config->rotate = true; | 
|  | config->edns0 = false; | 
|  | config->append_to_multi_label_name = true; | 
|  | config->search.clear(); | 
|  | config->search.push_back("chromium.org"); | 
|  | config->search.push_back("example.com"); | 
|  |  | 
|  | config->nameservers.clear(); | 
|  | for (unsigned i = 0; i < arraysize(kNameserversIPv4) && i < MAXNS; ++i) { | 
|  | IPAddressNumber ip; | 
|  | ParseIPLiteralToNumber(kNameserversIPv4[i], &ip); | 
|  | config->nameservers.push_back(IPEndPoint(ip, NS_DEFAULTPORT + i)); | 
|  | } | 
|  |  | 
|  | #if defined(OS_LINUX) | 
|  | for (unsigned i = 0; i < arraysize(kNameserversIPv6) && i < MAXNS; ++i) { | 
|  | if (!kNameserversIPv6[i]) | 
|  | continue; | 
|  | IPAddressNumber ip; | 
|  | ParseIPLiteralToNumber(kNameserversIPv6[i], &ip); | 
|  | config->nameservers[i] = IPEndPoint(ip, NS_DEFAULTPORT - i); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TEST(DnsConfigServicePosixTest, ConvertResStateToDnsConfig) { | 
|  | struct __res_state res; | 
|  | DnsConfig config; | 
|  | EXPECT_FALSE(config.IsValid()); | 
|  | InitializeResState(&res); | 
|  | ASSERT_EQ(internal::CONFIG_PARSE_POSIX_OK, | 
|  | internal::ConvertResStateToDnsConfig(res, &config)); | 
|  | CloseResState(&res); | 
|  | EXPECT_TRUE(config.IsValid()); | 
|  |  | 
|  | DnsConfig expected_config; | 
|  | EXPECT_FALSE(expected_config.EqualsIgnoreHosts(config)); | 
|  | InitializeExpectedConfig(&expected_config); | 
|  | EXPECT_TRUE(expected_config.EqualsIgnoreHosts(config)); | 
|  | } | 
|  |  | 
|  | TEST(DnsConfigServicePosixTest, RejectEmptyNameserver) { | 
|  | struct __res_state res = {}; | 
|  | res.options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; | 
|  | const char kDnsrch[] = "chromium.org"; | 
|  | memcpy(res.defdname, kDnsrch, sizeof(kDnsrch)); | 
|  | res.dnsrch[0] = res.defdname; | 
|  |  | 
|  | struct sockaddr_in sa = {}; | 
|  | sa.sin_family = AF_INET; | 
|  | sa.sin_port = base::HostToNet16(NS_DEFAULTPORT); | 
|  | sa.sin_addr.s_addr = INADDR_ANY; | 
|  | res.nsaddr_list[0] = sa; | 
|  | sa.sin_addr.s_addr = 0xCAFE1337; | 
|  | res.nsaddr_list[1] = sa; | 
|  | res.nscount = 2; | 
|  |  | 
|  | DnsConfig config; | 
|  | EXPECT_EQ(internal::CONFIG_PARSE_POSIX_NULL_ADDRESS, | 
|  | internal::ConvertResStateToDnsConfig(res, &config)); | 
|  |  | 
|  | sa.sin_addr.s_addr = 0xDEADBEEF; | 
|  | res.nsaddr_list[0] = sa; | 
|  | EXPECT_EQ(internal::CONFIG_PARSE_POSIX_OK, | 
|  | internal::ConvertResStateToDnsConfig(res, &config)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace net |