1 /* 2 * Copyright (C) 2011 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 required 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 #define LOG_TAG "ResolverController" 18 #define DBG 0 19 20 #include <algorithm> 21 #include <cstdlib> 22 #include <map> 23 #include <mutex> 24 #include <set> 25 #include <string> 26 #include <thread> 27 #include <utility> 28 #include <vector> 29 #include <cutils/log.h> 30 #include <net/if.h> 31 #include <sys/socket.h> 32 #include <netdb.h> 33 34 #include <arpa/inet.h> 35 // NOTE: <resolv_netid.h> is a private C library header that provides 36 // declarations for _resolv_set_nameservers_for_net and 37 // _resolv_flush_cache_for_net 38 #include <resolv_netid.h> 39 #include <resolv_params.h> 40 #include <resolv_stats.h> 41 42 #include <android-base/strings.h> 43 #include <android-base/thread_annotations.h> 44 #include <android/net/INetd.h> 45 #include <android/net/metrics/INetdEventListener.h> 46 47 #include "DumpWriter.h" 48 #include "EventReporter.h" 49 #include "NetdConstants.h" 50 #include "ResolverController.h" 51 #include "ResolverStats.h" 52 #include "dns/DnsTlsTransport.h" 53 #include "dns/DnsTlsServer.h" 54 #include "netdutils/BackoffSequence.h" 55 56 namespace android { 57 58 using netdutils::BackoffSequence; 59 60 namespace net { 61 namespace { 62 63 std::string addrToString(const sockaddr_storage* addr) { 64 char out[INET6_ADDRSTRLEN] = {0}; 65 getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), out, 66 INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); 67 return std::string(out); 68 } 69 70 bool parseServer(const char* server, sockaddr_storage* parsed) { 71 addrinfo hints = { 72 .ai_family = AF_UNSPEC, 73 .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV 74 }; 75 addrinfo* res; 76 77 int err = getaddrinfo(server, "853", &hints, &res); 78 if (err != 0) { 79 ALOGW("Failed to parse server address (%s): %s", server, gai_strerror(err)); 80 return false; 81 } 82 83 memcpy(parsed, res->ai_addr, res->ai_addrlen); 84 freeaddrinfo(res); 85 return true; 86 } 87 88 const char* getPrivateDnsModeString(PrivateDnsMode mode) { 89 switch (mode) { 90 case PrivateDnsMode::OFF: return "OFF"; 91 case PrivateDnsMode::OPPORTUNISTIC: return "OPPORTUNISTIC"; 92 case PrivateDnsMode::STRICT: return "STRICT"; 93 } 94 } 95 96 constexpr const char* validationStatusToString(ResolverController::Validation value) { 97 switch (value) { 98 case ResolverController::Validation::in_process: return "in_process"; 99 case ResolverController::Validation::success: return "success"; 100 case ResolverController::Validation::fail: return "fail"; 101 case ResolverController::Validation::unknown_server: return "unknown_server"; 102 case ResolverController::Validation::unknown_netid: return "unknown_netid"; 103 default: return "unknown_status"; 104 } 105 } 106 107 108 class PrivateDnsConfiguration { 109 public: 110 typedef ResolverController::PrivateDnsStatus PrivateDnsStatus; 111 typedef ResolverController::Validation Validation; 112 typedef std::map<DnsTlsServer, Validation, AddressComparator> PrivateDnsTracker; 113 114 int set(int32_t netId, const std::vector<std::string>& servers, const std::string& name, 115 const std::set<std::vector<uint8_t>>& fingerprints) { 116 if (DBG) { 117 ALOGD("PrivateDnsConfiguration::set(%u, %zu, %s, %zu)", 118 netId, servers.size(), name.c_str(), fingerprints.size()); 119 } 120 121 const bool explicitlyConfigured = !name.empty() || !fingerprints.empty(); 122 123 // Parse the list of servers that has been passed in 124 std::set<DnsTlsServer> tlsServers; 125 for (size_t i = 0; i < servers.size(); ++i) { 126 sockaddr_storage parsed; 127 if (!parseServer(servers[i].c_str(), &parsed)) { 128 return -EINVAL; 129 } 130 DnsTlsServer server(parsed); 131 server.name = name; 132 server.fingerprints = fingerprints; 133 tlsServers.insert(server); 134 } 135 136 std::lock_guard<std::mutex> guard(mPrivateDnsLock); 137 if (explicitlyConfigured) { 138 mPrivateDnsModes[netId] = PrivateDnsMode::STRICT; 139 } else if (!tlsServers.empty()) { 140 mPrivateDnsModes[netId] = PrivateDnsMode::OPPORTUNISTIC; 141 } else { 142 mPrivateDnsModes[netId] = PrivateDnsMode::OFF; 143 mPrivateDnsTransports.erase(netId); 144 return 0; 145 } 146 147 // Create the tracker if it was not present 148 auto netPair = mPrivateDnsTransports.find(netId); 149 if (netPair == mPrivateDnsTransports.end()) { 150 // No TLS tracker yet for this netId. 151 bool added; 152 std::tie(netPair, added) = mPrivateDnsTransports.emplace(netId, PrivateDnsTracker()); 153 if (!added) { 154 ALOGE("Memory error while recording private DNS for netId %d", netId); 155 return -ENOMEM; 156 } 157 } 158 auto& tracker = netPair->second; 159 160 // Remove any servers from the tracker that are not in |servers| exactly. 161 for (auto it = tracker.begin(); it != tracker.end();) { 162 if (tlsServers.count(it->first) == 0) { 163 it = tracker.erase(it); 164 } else { 165 ++it; 166 } 167 } 168 169 // Add any new or changed servers to the tracker, and initiate async checks for them. 170 for (const auto& server : tlsServers) { 171 // Don't probe a server more than once. This means that the only way to 172 // re-check a failed server is to remove it and re-add it from the netId. 173 if (tracker.count(server) == 0) { 174 validatePrivateDnsProvider(server, tracker, netId); 175 } 176 } 177 return 0; 178 } 179 180 PrivateDnsStatus getStatus(unsigned netId) { 181 PrivateDnsStatus status{PrivateDnsMode::OFF, {}}; 182 183 // This mutex is on the critical path of every DNS lookup. 184 // 185 // If the overhead of mutex acquisition proves too high, we could reduce 186 // it by maintaining an atomic_int32_t counter of TLS-enabled netids, or 187 // by using an RWLock. 188 std::lock_guard<std::mutex> guard(mPrivateDnsLock); 189 190 const auto mode = mPrivateDnsModes.find(netId); 191 if (mode == mPrivateDnsModes.end()) return status; 192 status.mode = mode->second; 193 194 const auto netPair = mPrivateDnsTransports.find(netId); 195 if (netPair != mPrivateDnsTransports.end()) { 196 for (const auto& serverPair : netPair->second) { 197 if (serverPair.second == Validation::success) { 198 status.validatedServers.push_back(serverPair.first); 199 } 200 } 201 } 202 203 return status; 204 } 205 206 void clear(unsigned netId) { 207 if (DBG) { 208 ALOGD("PrivateDnsConfiguration::clear(%u)", netId); 209 } 210 std::lock_guard<std::mutex> guard(mPrivateDnsLock); 211 mPrivateDnsModes.erase(netId); 212 mPrivateDnsTransports.erase(netId); 213 } 214 215 void dump(DumpWriter& dw, unsigned netId) { 216 std::lock_guard<std::mutex> guard(mPrivateDnsLock); 217 218 const auto& mode = mPrivateDnsModes.find(netId); 219 dw.println("Private DNS mode: %s", getPrivateDnsModeString( 220 (mode != mPrivateDnsModes.end()) ? mode->second : PrivateDnsMode::OFF)); 221 const auto& netPair = mPrivateDnsTransports.find(netId); 222 if (netPair == mPrivateDnsTransports.end()) { 223 dw.println("No Private DNS servers configured"); 224 } else { 225 const auto& tracker = netPair->second; 226 dw.println("Private DNS configuration (%zu entries)", tracker.size()); 227 dw.incIndent(); 228 for (const auto& kv : tracker) { 229 const auto& server = kv.first; 230 const auto status = kv.second; 231 dw.println("%s name{%s} status{%s}", 232 addrToString(&(server.ss)).c_str(), 233 server.name.c_str(), 234 validationStatusToString(status)); 235 } 236 dw.decIndent(); 237 } 238 } 239 240 private: 241 void validatePrivateDnsProvider(const DnsTlsServer& server, PrivateDnsTracker& tracker, 242 unsigned netId) REQUIRES(mPrivateDnsLock) { 243 if (DBG) { 244 ALOGD("validatePrivateDnsProvider(%s, %u)", addrToString(&(server.ss)).c_str(), netId); 245 } 246 247 tracker[server] = Validation::in_process; 248 if (DBG) { 249 ALOGD("Server %s marked as in_process. Tracker now has size %zu", 250 addrToString(&(server.ss)).c_str(), tracker.size()); 251 } 252 // Note that capturing |server| and |netId| in this lambda create copies. 253 std::thread validate_thread([this, server, netId] { 254 // cat /proc/sys/net/ipv4/tcp_syn_retries yields "6". 255 // 256 // Start with a 1 minute delay and backoff to once per hour. 257 // 258 // Assumptions: 259 // [1] Each TLS validation is ~10KB of certs+handshake+payload. 260 // [2] Network typically provision clients with <=4 nameservers. 261 // [3] Average month has 30 days. 262 // 263 // Each validation pass in a given hour is ~1.2MB of data. And 24 264 // such validation passes per day is about ~30MB per month, in the 265 // worst case. Otherwise, this will cost ~600 SYNs per month 266 // (6 SYNs per ip, 4 ips per validation pass, 24 passes per day). 267 auto backoff = BackoffSequence<>::Builder() 268 .withInitialRetransmissionTime(std::chrono::seconds(60)) 269 .withMaximumRetransmissionTime(std::chrono::seconds(3600)) 270 .build(); 271 272 while (true) { 273 // ::validate() is a blocking call that performs network operations. 274 // It can take milliseconds to minutes, up to the SYN retry limit. 275 const bool success = DnsTlsTransport::validate(server, netId); 276 if (DBG) { 277 ALOGD("validateDnsTlsServer returned %d for %s", success, 278 addrToString(&(server.ss)).c_str()); 279 } 280 281 const bool needs_reeval = this->recordPrivateDnsValidation(server, netId, success); 282 if (!needs_reeval) { 283 break; 284 } 285 286 if (backoff.hasNextTimeout()) { 287 std::this_thread::sleep_for(backoff.getNextTimeout()); 288 } else { 289 break; 290 } 291 } 292 }); 293 validate_thread.detach(); 294 } 295 296 bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success) { 297 constexpr bool NEEDS_REEVALUATION = true; 298 constexpr bool DONT_REEVALUATE = false; 299 300 std::lock_guard<std::mutex> guard(mPrivateDnsLock); 301 302 auto netPair = mPrivateDnsTransports.find(netId); 303 if (netPair == mPrivateDnsTransports.end()) { 304 ALOGW("netId %u was erased during private DNS validation", netId); 305 return DONT_REEVALUATE; 306 } 307 308 bool reevaluationStatus = success ? DONT_REEVALUATE : NEEDS_REEVALUATION; 309 310 auto& tracker = netPair->second; 311 auto serverPair = tracker.find(server); 312 if (serverPair == tracker.end()) { 313 ALOGW("Server %s was removed during private DNS validation", 314 addrToString(&(server.ss)).c_str()); 315 success = false; 316 reevaluationStatus = DONT_REEVALUATE; 317 } else if (!(serverPair->first == server)) { 318 // TODO: It doesn't seem correct to overwrite the tracker entry for 319 // |server| down below in this circumstance... Fix this. 320 ALOGW("Server %s was changed during private DNS validation", 321 addrToString(&(server.ss)).c_str()); 322 success = false; 323 reevaluationStatus = DONT_REEVALUATE; 324 } 325 326 // Send a validation event to NetdEventListenerService. 327 if (mNetdEventListener == nullptr) { 328 mNetdEventListener = mEventReporter.getNetdEventListener(); 329 } 330 if (mNetdEventListener != nullptr) { 331 const String16 ipLiteral(addrToString(&(server.ss)).c_str()); 332 const String16 hostname(server.name.empty() ? "" : server.name.c_str()); 333 mNetdEventListener->onPrivateDnsValidationEvent(netId, ipLiteral, hostname, success); 334 if (DBG) { 335 ALOGD("Sending validation %s event on netId %u for %s with hostname %s", 336 success ? "success" : "failure", netId, 337 addrToString(&(server.ss)).c_str(), server.name.c_str()); 338 } 339 } else { 340 ALOGE("Validation event not sent since NetdEventListenerService is unavailable."); 341 } 342 343 if (success) { 344 tracker[server] = Validation::success; 345 if (DBG) { 346 ALOGD("Validation succeeded for %s! Tracker now has %zu entries.", 347 addrToString(&(server.ss)).c_str(), tracker.size()); 348 } 349 } else { 350 // Validation failure is expected if a user is on a captive portal. 351 // TODO: Trigger a second validation attempt after captive portal login 352 // succeeds. 353 tracker[server] = Validation::fail; 354 if (DBG) { 355 ALOGD("Validation failed for %s!", addrToString(&(server.ss)).c_str()); 356 } 357 } 358 359 return reevaluationStatus; 360 } 361 362 EventReporter mEventReporter; 363 364 std::mutex mPrivateDnsLock; 365 std::map<unsigned, PrivateDnsMode> mPrivateDnsModes GUARDED_BY(mPrivateDnsLock); 366 // Structure for tracking the validation status of servers on a specific netId. 367 // Using the AddressComparator ensures at most one entry per IP address. 368 std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock); 369 android::sp<android::net::metrics::INetdEventListener> 370 mNetdEventListener GUARDED_BY(mPrivateDnsLock); 371 } privateDnsConfiguration; 372 373 } // namespace 374 375 int ResolverController::setDnsServers(unsigned netId, const char* searchDomains, 376 const char** servers, int numservers, const __res_params* params) { 377 if (DBG) { 378 ALOGD("setDnsServers netId = %u, numservers = %d", netId, numservers); 379 } 380 return -_resolv_set_nameservers_for_net(netId, servers, numservers, searchDomains, params); 381 } 382 383 ResolverController::PrivateDnsStatus 384 ResolverController::getPrivateDnsStatus(unsigned netId) const { 385 return privateDnsConfiguration.getStatus(netId); 386 } 387 388 int ResolverController::clearDnsServers(unsigned netId) { 389 _resolv_set_nameservers_for_net(netId, NULL, 0, "", NULL); 390 if (DBG) { 391 ALOGD("clearDnsServers netId = %u\n", netId); 392 } 393 privateDnsConfiguration.clear(netId); 394 return 0; 395 } 396 397 int ResolverController::flushDnsCache(unsigned netId) { 398 if (DBG) { 399 ALOGD("flushDnsCache netId = %u\n", netId); 400 } 401 402 _resolv_flush_cache_for_net(netId); 403 404 return 0; 405 } 406 407 int ResolverController::getDnsInfo(unsigned netId, std::vector<std::string>* servers, 408 std::vector<std::string>* domains, __res_params* params, 409 std::vector<android::net::ResolverStats>* stats) { 410 using android::net::ResolverStats; 411 using android::net::INetd; 412 static_assert(ResolverStats::STATS_SUCCESSES == INetd::RESOLVER_STATS_SUCCESSES && 413 ResolverStats::STATS_ERRORS == INetd::RESOLVER_STATS_ERRORS && 414 ResolverStats::STATS_TIMEOUTS == INetd::RESOLVER_STATS_TIMEOUTS && 415 ResolverStats::STATS_INTERNAL_ERRORS == INetd::RESOLVER_STATS_INTERNAL_ERRORS && 416 ResolverStats::STATS_RTT_AVG == INetd::RESOLVER_STATS_RTT_AVG && 417 ResolverStats::STATS_LAST_SAMPLE_TIME == INetd::RESOLVER_STATS_LAST_SAMPLE_TIME && 418 ResolverStats::STATS_USABLE == INetd::RESOLVER_STATS_USABLE && 419 ResolverStats::STATS_COUNT == INetd::RESOLVER_STATS_COUNT, 420 "AIDL and ResolverStats.h out of sync"); 421 int nscount = -1; 422 sockaddr_storage res_servers[MAXNS]; 423 int dcount = -1; 424 char res_domains[MAXDNSRCH][MAXDNSRCHPATH]; 425 __res_stats res_stats[MAXNS]; 426 servers->clear(); 427 domains->clear(); 428 *params = __res_params{}; 429 stats->clear(); 430 int revision_id = android_net_res_stats_get_info_for_net(netId, &nscount, res_servers, &dcount, 431 res_domains, params, res_stats); 432 433 // If the netId is unknown (which can happen for valid net IDs for which no DNS servers have 434 // yet been configured), there is no revision ID. In this case there is no data to return. 435 if (revision_id < 0) { 436 return 0; 437 } 438 439 // Verify that the returned data is sane. 440 if (nscount < 0 || nscount > MAXNS || dcount < 0 || dcount > MAXDNSRCH) { 441 ALOGE("%s: nscount=%d, dcount=%d", __FUNCTION__, nscount, dcount); 442 return -ENOTRECOVERABLE; 443 } 444 445 // Determine which servers are considered usable by the resolver. 446 bool valid_servers[MAXNS]; 447 std::fill_n(valid_servers, MAXNS, false); 448 android_net_res_stats_get_usable_servers(params, res_stats, nscount, valid_servers); 449 450 // Convert the server sockaddr structures to std::string. 451 stats->resize(nscount); 452 for (int i = 0 ; i < nscount ; ++i) { 453 char hbuf[NI_MAXHOST]; 454 int rv = getnameinfo(reinterpret_cast<const sockaddr*>(&res_servers[i]), 455 sizeof(res_servers[i]), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST); 456 std::string server_str; 457 if (rv == 0) { 458 server_str.assign(hbuf); 459 } else { 460 ALOGE("getnameinfo() failed for server #%d: %s", i, gai_strerror(rv)); 461 server_str.assign("<invalid>"); 462 } 463 servers->push_back(std::move(server_str)); 464 android::net::ResolverStats& cur_stats = (*stats)[i]; 465 android_net_res_stats_aggregate(&res_stats[i], &cur_stats.successes, &cur_stats.errors, 466 &cur_stats.timeouts, &cur_stats.internal_errors, &cur_stats.rtt_avg, 467 &cur_stats.last_sample_time); 468 cur_stats.usable = valid_servers[i]; 469 } 470 471 // Convert the stack-allocated search domain strings to std::string. 472 for (int i = 0 ; i < dcount ; ++i) { 473 domains->push_back(res_domains[i]); 474 } 475 return 0; 476 } 477 478 int ResolverController::setResolverConfiguration(int32_t netId, 479 const std::vector<std::string>& servers, const std::vector<std::string>& domains, 480 const std::vector<int32_t>& params, const std::string& tlsName, 481 const std::vector<std::string>& tlsServers, 482 const std::set<std::vector<uint8_t>>& tlsFingerprints) { 483 using android::net::INetd; 484 if (params.size() != INetd::RESOLVER_PARAMS_COUNT) { 485 ALOGE("%s: params.size()=%zu", __FUNCTION__, params.size()); 486 return -EINVAL; 487 } 488 489 const int err = privateDnsConfiguration.set(netId, tlsServers, tlsName, tlsFingerprints); 490 if (err != 0) { 491 return err; 492 } 493 494 // Convert network-assigned server list to bionic's format. 495 auto server_count = std::min<size_t>(MAXNS, servers.size()); 496 std::vector<const char*> server_ptrs; 497 for (size_t i = 0 ; i < server_count ; ++i) { 498 server_ptrs.push_back(servers[i].c_str()); 499 } 500 501 std::string domains_str; 502 if (!domains.empty()) { 503 domains_str = domains[0]; 504 for (size_t i = 1 ; i < domains.size() ; ++i) { 505 domains_str += " " + domains[i]; 506 } 507 } 508 509 __res_params res_params; 510 res_params.sample_validity = params[INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY]; 511 res_params.success_threshold = params[INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD]; 512 res_params.min_samples = params[INetd::RESOLVER_PARAMS_MIN_SAMPLES]; 513 res_params.max_samples = params[INetd::RESOLVER_PARAMS_MAX_SAMPLES]; 514 515 return setDnsServers(netId, domains_str.c_str(), server_ptrs.data(), server_ptrs.size(), 516 &res_params); 517 } 518 519 int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* servers, 520 std::vector<std::string>* domains, std::vector<int32_t>* params, 521 std::vector<int32_t>* stats) { 522 using android::net::ResolverStats; 523 using android::net::INetd; 524 __res_params res_params; 525 std::vector<ResolverStats> res_stats; 526 int ret = getDnsInfo(netId, servers, domains, &res_params, &res_stats); 527 if (ret != 0) { 528 return ret; 529 } 530 531 // Serialize the information for binder. 532 ResolverStats::encodeAll(res_stats, stats); 533 534 params->resize(INetd::RESOLVER_PARAMS_COUNT); 535 (*params)[INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY] = res_params.sample_validity; 536 (*params)[INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD] = res_params.success_threshold; 537 (*params)[INetd::RESOLVER_PARAMS_MIN_SAMPLES] = res_params.min_samples; 538 (*params)[INetd::RESOLVER_PARAMS_MAX_SAMPLES] = res_params.max_samples; 539 return 0; 540 } 541 542 void ResolverController::dump(DumpWriter& dw, unsigned netId) { 543 // No lock needed since Bionic's resolver locks all accessed data structures internally. 544 using android::net::ResolverStats; 545 std::vector<std::string> servers; 546 std::vector<std::string> domains; 547 __res_params params; 548 std::vector<ResolverStats> stats; 549 time_t now = time(nullptr); 550 int rv = getDnsInfo(netId, &servers, &domains, ¶ms, &stats); 551 dw.incIndent(); 552 if (rv != 0) { 553 dw.println("getDnsInfo() failed for netid %u", netId); 554 } else { 555 if (servers.empty()) { 556 dw.println("No DNS servers defined"); 557 } else { 558 dw.println("DNS servers: # IP (total, successes, errors, timeouts, internal errors, " 559 "RTT avg, last sample)"); 560 dw.incIndent(); 561 for (size_t i = 0 ; i < servers.size() ; ++i) { 562 if (i < stats.size()) { 563 const ResolverStats& s = stats[i]; 564 int total = s.successes + s.errors + s.timeouts + s.internal_errors; 565 if (total > 0) { 566 int time_delta = (s.last_sample_time > 0) ? now - s.last_sample_time : -1; 567 dw.println("%s (%d, %d, %d, %d, %d, %dms, %ds)%s", servers[i].c_str(), 568 total, s.successes, s.errors, s.timeouts, s.internal_errors, 569 s.rtt_avg, time_delta, s.usable ? "" : " BROKEN"); 570 } else { 571 dw.println("%s <no data>", servers[i].c_str()); 572 } 573 } else { 574 dw.println("%s <no stats>", servers[i].c_str()); 575 } 576 } 577 dw.decIndent(); 578 } 579 if (domains.empty()) { 580 dw.println("No search domains defined"); 581 } else { 582 std::string domains_str = android::base::Join(domains, ", "); 583 dw.println("search domains: %s", domains_str.c_str()); 584 } 585 if (params.sample_validity != 0) { 586 dw.println("DNS parameters: sample validity = %us, success threshold = %u%%, " 587 "samples (min, max) = (%u, %u)", params.sample_validity, 588 static_cast<unsigned>(params.success_threshold), 589 static_cast<unsigned>(params.min_samples), 590 static_cast<unsigned>(params.max_samples)); 591 } 592 593 privateDnsConfiguration.dump(dw, netId); 594 } 595 dw.decIndent(); 596 } 597 598 } // namespace net 599 } // namespace android 600