Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless requied by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | * |
| 16 | */ |
| 17 | |
| 18 | #include <arpa/inet.h> |
| 19 | #include <errno.h> |
| 20 | #include <netdb.h> |
| 21 | #include <stdarg.h> |
| 22 | #include <stdio.h> |
| 23 | #include <stdlib.h> |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 24 | #include <unistd.h> |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 25 | |
| 26 | #include <cutils/sockets.h> |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 27 | #include <android-base/stringprintf.h> |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 28 | #include <private/android_filesystem_config.h> |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 29 | |
| 30 | #include <thread> |
| 31 | |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 32 | #include "NetdClient.h" |
| 33 | |
| 34 | #include <gtest/gtest.h> |
| 35 | #define LOG_TAG "resolverTest" |
| 36 | #include <utils/Log.h> |
| 37 | #include <testUtil.h> |
| 38 | |
| 39 | #include "dns_responder.h" |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 40 | #include "resolv_params.h" |
| 41 | |
| 42 | using android::base::StringPrintf; |
| 43 | using android::base::StringAppendF; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 44 | |
| 45 | // TODO: make this dynamic and stop depending on implementation details. |
| 46 | #define TEST_OEM_NETWORK "oem29" |
| 47 | #define TEST_NETID 30 |
| 48 | |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 49 | // The only response code used in this test, see |
| 50 | // frameworks/base/services/java/com/android/server/NetworkManagementService.java |
| 51 | // for others. |
| 52 | static constexpr int ResponseCodeOK = 200; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 53 | |
| 54 | // Returns ResponseCode. |
| 55 | int netdCommand(const char* sockname, const char* command) { |
| 56 | int sock = socket_local_client(sockname, |
| 57 | ANDROID_SOCKET_NAMESPACE_RESERVED, |
| 58 | SOCK_STREAM); |
| 59 | if (sock < 0) { |
| 60 | perror("Error connecting"); |
| 61 | return -1; |
| 62 | } |
| 63 | |
| 64 | // FrameworkListener expects the whole command in one read. |
| 65 | char buffer[256]; |
| 66 | int nwritten = snprintf(buffer, sizeof(buffer), "0 %s", command); |
| 67 | if (write(sock, buffer, nwritten + 1) < 0) { |
| 68 | perror("Error sending netd command"); |
| 69 | close(sock); |
| 70 | return -1; |
| 71 | } |
| 72 | |
| 73 | int nread = read(sock, buffer, sizeof(buffer)); |
| 74 | if (nread < 0) { |
| 75 | perror("Error reading response"); |
| 76 | close(sock); |
| 77 | return -1; |
| 78 | } |
| 79 | close(sock); |
| 80 | return atoi(buffer); |
| 81 | } |
| 82 | |
| 83 | |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 84 | bool expectNetdResult(int expected, const char* sockname, const char* format, ...) { |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 85 | char command[256]; |
| 86 | va_list args; |
| 87 | va_start(args, format); |
| 88 | vsnprintf(command, sizeof(command), format, args); |
| 89 | va_end(args); |
| 90 | int result = netdCommand(sockname, command); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 91 | EXPECT_EQ(expected, result) << command; |
| 92 | return (200 <= expected && expected < 300); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | |
| 96 | class ResolverTest : public ::testing::Test { |
| 97 | protected: |
| 98 | virtual void SetUp() { |
| 99 | // Ensure resolutions go via proxy. |
| 100 | setenv("ANDROID_DNS_MODE", "", 1); |
| 101 | uid = getuid(); |
| 102 | pid = getpid(); |
| 103 | SetupOemNetwork(); |
| 104 | } |
| 105 | |
| 106 | virtual void TearDown() { |
| 107 | TearDownOemNetwork(); |
| 108 | netdCommand("netd", "network destroy " TEST_OEM_NETWORK); |
| 109 | } |
| 110 | |
| 111 | void SetupOemNetwork() { |
| 112 | netdCommand("netd", "network destroy " TEST_OEM_NETWORK); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 113 | if (expectNetdResult(ResponseCodeOK, "netd", |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 114 | "network create %s", TEST_OEM_NETWORK)) { |
| 115 | oemNetId = TEST_NETID; |
| 116 | } |
| 117 | setNetworkForProcess(oemNetId); |
| 118 | ASSERT_EQ((unsigned) oemNetId, getNetworkForProcess()); |
| 119 | } |
| 120 | |
| 121 | void TearDownOemNetwork() { |
| 122 | if (oemNetId != -1) { |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 123 | expectNetdResult(ResponseCodeOK, "netd", |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 124 | "network destroy %s", TEST_OEM_NETWORK); |
| 125 | } |
| 126 | } |
| 127 | |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 128 | bool SetResolversForNetwork(const std::vector<std::string>& searchDomains, |
| 129 | const std::vector<std::string>& servers, const std::string& params) { |
| 130 | // No use case for empty domains / servers (yet). |
| 131 | if (searchDomains.empty() || servers.empty()) return false; |
| 132 | |
| 133 | std::string cmd = StringPrintf("resolver setnetdns %d \"%s", oemNetId, |
| 134 | searchDomains[0].c_str()); |
| 135 | for (size_t i = 1 ; i < searchDomains.size() ; ++i) { |
| 136 | cmd += " "; |
| 137 | cmd += searchDomains[i]; |
| 138 | } |
| 139 | cmd += "\" "; |
| 140 | |
| 141 | cmd += servers[0]; |
| 142 | for (size_t i = 1 ; i < servers.size() ; ++i) { |
| 143 | cmd += " "; |
| 144 | cmd += servers[i]; |
| 145 | } |
| 146 | |
| 147 | if (!params.empty()) { |
| 148 | cmd += " --params \""; |
| 149 | cmd += params; |
| 150 | cmd += "\""; |
| 151 | } |
| 152 | |
| 153 | int rv = netdCommand("netd", cmd.c_str()); |
| 154 | std::cout << "command: '" << cmd << "', rv = " << rv << "\n"; |
| 155 | if (rv != ResponseCodeOK) { |
| 156 | return false; |
| 157 | } |
| 158 | return true; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | bool FlushCache() const { |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 162 | return expectNetdResult(ResponseCodeOK, "netd", "resolver flushnet %d", oemNetId); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 163 | } |
| 164 | |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 165 | std::string ToString(const hostent* he) const { |
| 166 | if (he == nullptr) return "<null>"; |
| 167 | char buffer[INET6_ADDRSTRLEN]; |
| 168 | if (!inet_ntop(he->h_addrtype, he->h_addr_list[0], buffer, sizeof(buffer))) { |
| 169 | return "<invalid>"; |
| 170 | } |
| 171 | return buffer; |
Pierre Imai | ccf7b99 | 2016-02-25 16:34:29 +0900 | [diff] [blame] | 172 | } |
| 173 | |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 174 | std::string ToString(const addrinfo* ai) const { |
| 175 | if (!ai) |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 176 | return "<null>"; |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 177 | for (const auto* aip = ai ; aip != nullptr ; aip = aip->ai_next) { |
| 178 | char host[NI_MAXHOST]; |
| 179 | int rv = getnameinfo(aip->ai_addr, aip->ai_addrlen, host, sizeof(host), nullptr, 0, |
| 180 | NI_NUMERICHOST); |
| 181 | if (rv != 0) |
| 182 | return gai_strerror(rv); |
| 183 | return host; |
| 184 | } |
| 185 | return "<invalid>"; |
| 186 | } |
| 187 | |
| 188 | size_t GetNumQueries(const test::DNSResponder& dns, const char* name) const { |
| 189 | auto queries = dns.queries(); |
| 190 | size_t found = 0; |
| 191 | for (const auto& p : queries) { |
| 192 | std::cout << "query " << p.first << "\n"; |
| 193 | if (p.first == name) { |
| 194 | ++found; |
| 195 | } |
| 196 | } |
| 197 | return found; |
| 198 | } |
| 199 | |
| 200 | size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type, |
| 201 | const char* name) const { |
| 202 | auto queries = dns.queries(); |
| 203 | size_t found = 0; |
| 204 | for (const auto& p : queries) { |
| 205 | std::cout << "query " << p.first << "\n"; |
| 206 | if (p.second == type && p.first == name) { |
| 207 | ++found; |
| 208 | } |
| 209 | } |
| 210 | return found; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 211 | } |
| 212 | |
| 213 | int pid; |
| 214 | int uid; |
| 215 | int oemNetId = -1; |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 216 | const std::vector<std::string> mDefaultSearchDomains = { "example.com" }; |
| 217 | // <sample validity in s> <success threshold in percent> <min samples> <max samples> |
| 218 | const std::string mDefaultParams = "300 25 8 8"; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 219 | }; |
| 220 | |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 221 | TEST_F(ResolverTest, GetHostByName) { |
| 222 | const char* listen_addr = "127.0.0.3"; |
| 223 | const char* listen_srv = "53"; |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 224 | const char* host_name = "hello.example.com."; |
| 225 | test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail, 1.0); |
| 226 | dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.3"); |
| 227 | ASSERT_TRUE(dns.startServer()); |
| 228 | std::vector<std::string> servers = { listen_addr }; |
| 229 | ASSERT_TRUE(SetResolversForNetwork(mDefaultSearchDomains, servers, mDefaultParams)); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 230 | |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 231 | dns.clearQueries(); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 232 | const hostent* result = gethostbyname("hello"); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 233 | EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, host_name)); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 234 | ASSERT_FALSE(result == nullptr); |
| 235 | ASSERT_EQ(4, result->h_length); |
| 236 | ASSERT_FALSE(result->h_addr_list[0] == nullptr); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 237 | EXPECT_EQ("1.2.3.3", ToString(result)); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 238 | EXPECT_TRUE(result->h_addr_list[1] == nullptr); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 239 | dns.stopServer(); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 240 | } |
| 241 | |
| 242 | TEST_F(ResolverTest, GetAddrInfo) { |
| 243 | addrinfo* result = nullptr; |
| 244 | |
| 245 | const char* listen_addr = "127.0.0.4"; |
| 246 | const char* listen_srv = "53"; |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 247 | const char* host_name = "howdie.example.com."; |
| 248 | test::DNSResponder dns(listen_addr, listen_srv, 250, |
| 249 | ns_rcode::ns_r_servfail, 1.0); |
| 250 | dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); |
| 251 | dns.addMapping(host_name, ns_type::ns_t_aaaa, "::1.2.3.4"); |
| 252 | ASSERT_TRUE(dns.startServer()); |
| 253 | std::vector<std::string> servers = { listen_addr }; |
| 254 | ASSERT_TRUE(SetResolversForNetwork(mDefaultSearchDomains, servers, mDefaultParams)); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 255 | |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 256 | dns.clearQueries(); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 257 | EXPECT_EQ(0, getaddrinfo("howdie", nullptr, nullptr, &result)); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 258 | size_t found = GetNumQueries(dns, host_name); |
Pierre Imai | b19fcc7 | 2016-03-11 17:54:48 +0900 | [diff] [blame] | 259 | EXPECT_LE(1U, found); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 260 | // Could be A or AAAA |
| 261 | std::string result_str = ToString(result); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 262 | EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4") |
| 263 | << ", result_str='" << result_str << "'"; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 264 | if (result) freeaddrinfo(result); |
| 265 | result = nullptr; |
| 266 | |
| 267 | // Verify that it's cached. |
Pierre Imai | ccf7b99 | 2016-02-25 16:34:29 +0900 | [diff] [blame] | 268 | size_t old_found = found; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 269 | EXPECT_EQ(0, getaddrinfo("howdie", nullptr, nullptr, &result)); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 270 | found = GetNumQueries(dns, host_name); |
| 271 | EXPECT_LE(1U, found); |
Pierre Imai | ccf7b99 | 2016-02-25 16:34:29 +0900 | [diff] [blame] | 272 | EXPECT_EQ(old_found, found); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 273 | result_str = ToString(result); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 274 | EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4") |
| 275 | << result_str; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 276 | if (result) freeaddrinfo(result); |
| 277 | result = nullptr; |
| 278 | |
| 279 | // Verify that cache can be flushed. |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 280 | dns.clearQueries(); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 281 | ASSERT_TRUE(FlushCache()); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 282 | dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.44"); |
| 283 | dns.addMapping(host_name, ns_type::ns_t_aaaa, "::1.2.3.44"); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 284 | |
| 285 | EXPECT_EQ(0, getaddrinfo("howdie", nullptr, nullptr, &result)); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 286 | EXPECT_LE(1U, GetNumQueries(dns, host_name)); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 287 | // Could be A or AAAA |
| 288 | result_str = ToString(result); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 289 | EXPECT_TRUE(result_str == "1.2.3.44" || result_str == "::1.2.3.44") |
| 290 | << ", result_str='" << result_str << "'"; |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 291 | if (result) freeaddrinfo(result); |
| 292 | } |
| 293 | |
| 294 | TEST_F(ResolverTest, GetAddrInfoV4) { |
| 295 | addrinfo* result = nullptr; |
| 296 | |
| 297 | const char* listen_addr = "127.0.0.5"; |
| 298 | const char* listen_srv = "53"; |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 299 | const char* host_name = "hola.example.com."; |
| 300 | test::DNSResponder dns(listen_addr, listen_srv, 250, |
| 301 | ns_rcode::ns_r_servfail, 1.0); |
| 302 | dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.5"); |
| 303 | ASSERT_TRUE(dns.startServer()); |
| 304 | std::vector<std::string> servers = { listen_addr }; |
| 305 | ASSERT_TRUE(SetResolversForNetwork(mDefaultSearchDomains, servers, mDefaultParams)); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 306 | |
| 307 | addrinfo hints; |
| 308 | memset(&hints, 0, sizeof(hints)); |
| 309 | hints.ai_family = AF_INET; |
| 310 | EXPECT_EQ(0, getaddrinfo("hola", nullptr, &hints, &result)); |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 311 | EXPECT_EQ(1U, GetNumQueries(dns, host_name)); |
Pierre Imai | ccf7b99 | 2016-02-25 16:34:29 +0900 | [diff] [blame] | 312 | EXPECT_EQ("1.2.3.5", ToString(result)); |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 313 | if (result) freeaddrinfo(result); |
| 314 | } |
Pierre Imai | 95f5f94 | 2016-03-09 18:09:25 +0900 | [diff] [blame] | 315 | |
| 316 | TEST_F(ResolverTest, MultidomainResolution) { |
| 317 | std::vector<std::string> searchDomains = { "example1.com", "example2.com", "example3.com" }; |
| 318 | const char* listen_addr = "127.0.0.6"; |
| 319 | const char* listen_srv = "53"; |
| 320 | const char* host_name = "nihao.example2.com."; |
| 321 | test::DNSResponder dns(listen_addr, listen_srv, 250, |
| 322 | ns_rcode::ns_r_servfail, 1.0); |
| 323 | dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.3"); |
| 324 | ASSERT_TRUE(dns.startServer()); |
| 325 | std::vector<std::string> servers = { listen_addr }; |
| 326 | ASSERT_TRUE(SetResolversForNetwork(searchDomains, servers, mDefaultParams)); |
| 327 | |
| 328 | dns.clearQueries(); |
| 329 | const hostent* result = gethostbyname("nihao"); |
| 330 | EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, host_name)); |
| 331 | ASSERT_FALSE(result == nullptr); |
| 332 | ASSERT_EQ(4, result->h_length); |
| 333 | ASSERT_FALSE(result->h_addr_list[0] == nullptr); |
| 334 | EXPECT_EQ("1.2.3.3", ToString(result)); |
| 335 | EXPECT_TRUE(result->h_addr_list[1] == nullptr); |
| 336 | dns.stopServer(); |
| 337 | } |
| 338 | |
| 339 | TEST_F(ResolverTest, GetAddrInfoV6_failing) { |
| 340 | addrinfo* result = nullptr; |
| 341 | |
| 342 | const char* listen_addr0 = "127.0.0.7"; |
| 343 | const char* listen_addr1 = "127.0.0.8"; |
| 344 | const char* listen_srv = "53"; |
| 345 | const char* host_name = "ohayou.example.com."; |
| 346 | test::DNSResponder dns0(listen_addr0, listen_srv, 250, |
| 347 | ns_rcode::ns_r_servfail, 0.0); |
| 348 | test::DNSResponder dns1(listen_addr1, listen_srv, 250, |
| 349 | ns_rcode::ns_r_servfail, 1.0); |
| 350 | dns0.addMapping(host_name, ns_type::ns_t_aaaa, "2001:db8::5"); |
| 351 | dns1.addMapping(host_name, ns_type::ns_t_aaaa, "2001:db8::6"); |
| 352 | ASSERT_TRUE(dns0.startServer()); |
| 353 | ASSERT_TRUE(dns1.startServer()); |
| 354 | std::vector<std::string> servers = { listen_addr0, listen_addr1 }; |
| 355 | // <sample validity in s> <success threshold in percent> <min samples> <max samples> |
| 356 | unsigned sample_validity = 300; |
| 357 | int success_threshold = 25; |
| 358 | int sample_count = 8; |
| 359 | std::string params = StringPrintf("%u %d %d %d", sample_validity, success_threshold, |
| 360 | sample_count, sample_count); |
| 361 | ASSERT_TRUE(SetResolversForNetwork(mDefaultSearchDomains, servers, params)); |
| 362 | |
| 363 | // Repeatedly perform resolutions for non-existing domains until MAXNSSAMPLES resolutions have |
| 364 | // reached the dns0, which is set to fail. No more requests should then arrive at that server |
| 365 | // for the next sample_lifetime seconds. |
| 366 | // TODO: This approach is implementation-dependent, change once metrics reporting is available. |
| 367 | addrinfo hints; |
| 368 | memset(&hints, 0, sizeof(hints)); |
| 369 | hints.ai_family = AF_INET6; |
| 370 | for (int i = 0 ; i < sample_count ; ++i) { |
| 371 | std::string domain = StringPrintf("nonexistent%d", i); |
| 372 | getaddrinfo(domain.c_str(), nullptr, &hints, &result); |
| 373 | } |
| 374 | // Due to 100% errors for all possible samples, the server should be ignored from now on and |
| 375 | // only the second one used for all following queries, until NSSAMPLE_VALIDITY is reached. |
| 376 | dns0.clearQueries(); |
| 377 | dns1.clearQueries(); |
| 378 | EXPECT_EQ(0, getaddrinfo("ohayou", nullptr, &hints, &result)); |
| 379 | EXPECT_EQ(0U, GetNumQueries(dns0, host_name)); |
| 380 | EXPECT_EQ(1U, GetNumQueries(dns1, host_name)); |
| 381 | if (result) freeaddrinfo(result); |
| 382 | } |
| 383 | |
| 384 | TEST_F(ResolverTest, GetAddrInfoV6_concurrent) { |
| 385 | const char* listen_addr0 = "127.0.0.9"; |
| 386 | const char* listen_addr1 = "127.0.0.10"; |
| 387 | const char* listen_addr2 = "127.0.0.11"; |
| 388 | const char* listen_srv = "53"; |
| 389 | const char* host_name = "konbanha.example.com."; |
| 390 | test::DNSResponder dns0(listen_addr0, listen_srv, 250, |
| 391 | ns_rcode::ns_r_servfail, 1.0); |
| 392 | test::DNSResponder dns1(listen_addr1, listen_srv, 250, |
| 393 | ns_rcode::ns_r_servfail, 1.0); |
| 394 | test::DNSResponder dns2(listen_addr2, listen_srv, 250, |
| 395 | ns_rcode::ns_r_servfail, 1.0); |
| 396 | dns0.addMapping(host_name, ns_type::ns_t_aaaa, "2001:db8::5"); |
| 397 | dns1.addMapping(host_name, ns_type::ns_t_aaaa, "2001:db8::6"); |
| 398 | dns2.addMapping(host_name, ns_type::ns_t_aaaa, "2001:db8::7"); |
| 399 | ASSERT_TRUE(dns0.startServer()); |
| 400 | ASSERT_TRUE(dns1.startServer()); |
| 401 | ASSERT_TRUE(dns2.startServer()); |
| 402 | const std::vector<std::string> servers = { listen_addr0, listen_addr1, listen_addr2 }; |
| 403 | std::vector<std::thread> threads(10); |
| 404 | for (std::thread& thread : threads) { |
| 405 | thread = std::thread([this, &servers, &dns0, &dns1, &dns2]() { |
| 406 | unsigned delay = arc4random_uniform(1*1000*1000); // <= 1s |
| 407 | usleep(delay); |
| 408 | std::vector<std::string> serverSubset; |
| 409 | for (const auto& server : servers) { |
| 410 | if (arc4random_uniform(2)) { |
| 411 | serverSubset.push_back(server); |
| 412 | } |
| 413 | } |
| 414 | if (serverSubset.empty()) serverSubset = servers; |
| 415 | ASSERT_TRUE(SetResolversForNetwork(mDefaultSearchDomains, serverSubset, |
| 416 | mDefaultParams)); |
| 417 | addrinfo hints; |
| 418 | memset(&hints, 0, sizeof(hints)); |
| 419 | hints.ai_family = AF_INET6; |
| 420 | addrinfo* result = nullptr; |
| 421 | int rv = getaddrinfo("konbanha", nullptr, &hints, &result); |
| 422 | EXPECT_EQ(0, rv) << "error [" << rv << "] " << gai_strerror(rv); |
| 423 | }); |
| 424 | } |
| 425 | for (std::thread& thread : threads) { |
| 426 | thread.join(); |
| 427 | } |
| 428 | } |