blob: e9495879d7235ea7b423eab70ce8f2b75706d5e8 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "shill/resolver.h"
#include <string>
#include <vector>
#include <base/containers/contains.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include "shill/dns_util.h"
#include "shill/ipconfig.h"
#include "shill/logging.h"
#include "shill/net/ip_address.h"
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kResolver;
static std::string ObjectID(const Resolver* r) {
return "(resolver)";
}
} // namespace Logging
const char Resolver::kDefaultIgnoredSearchList[] = "gateway.2wire.net";
Resolver::Resolver() = default;
Resolver::~Resolver() = default;
Resolver* Resolver::GetInstance() {
static base::NoDestructor<Resolver> instance;
return instance.get();
}
bool Resolver::SetDNSFromLists(
const std::vector<std::string>& name_servers,
const std::vector<std::string>& domain_search_list) {
SLOG(this, 2) << __func__;
name_servers_ = name_servers;
domain_search_list_ = domain_search_list;
return Emit();
}
bool Resolver::Emit() {
if (path_.empty()) {
LOG(DFATAL) << "No path set";
return false;
}
// dns-proxy always used if set.
const auto name_servers = !dns_proxy_addr_.empty()
? std::vector<std::string>({dns_proxy_addr_})
: name_servers_;
if (name_servers.empty() && domain_search_list_.empty()) {
SLOG(this, 2) << "DNS list is empty";
return ClearDNS();
}
std::vector<std::string> lines;
for (const auto& server : name_servers) {
IPAddress addr(server);
std::string canonical_ip;
if (addr.family() != IPAddress::kFamilyUnknown &&
addr.IntoString(&canonical_ip)) {
lines.push_back("nameserver " + canonical_ip);
} else {
LOG(WARNING) << "Malformed nameserver IP: " << server;
}
}
std::vector<std::string> filtered_domain_search_list;
for (const auto& domain : domain_search_list_) {
if (base::Contains(ignored_search_list_, domain)) {
continue;
}
if (IsValidDNSDomain(domain)) {
filtered_domain_search_list.push_back(domain);
} else {
LOG(WARNING) << "Malformed search domain: " << domain;
}
}
if (!filtered_domain_search_list.empty()) {
lines.push_back("search " +
base::JoinString(filtered_domain_search_list, " "));
}
// - Send queries one-at-a-time, rather than parallelizing IPv4
// and IPv6 queries for a single host.
// - Override the default 5-second request timeout and use a
// 1-second timeout instead. (NOTE: Chrome's ADNS will use
// one second, regardless of what we put here.)
// - Allow 5 attempts, rather than the default of 2.
// - For glibc, the worst case number of queries will be
// attempts * count(servers) * (count(search domains)+1)
// - For Chrome, the worst case number of queries will be
// attempts * count(servers) + 3 * glibc
// See crbug.com/224756 for supporting data.
lines.push_back("options single-request timeout:1 attempts:5");
// Newline at end of file
lines.push_back("");
const auto contents = base::JoinString(lines, "\n");
SLOG(this, 2) << "Writing DNS out to " << path_.value();
int count = base::WriteFile(path_, contents.c_str(), contents.size());
return count == static_cast<int>(contents.size());
}
bool Resolver::SetDNSProxy(const std::string& proxy_addr) {
SLOG(this, 2) << __func__;
dns_proxy_addr_ = proxy_addr;
return Emit();
}
bool Resolver::ClearDNS() {
SLOG(this, 2) << __func__;
if (path_.empty()) {
LOG(DFATAL) << "No path set";
return false;
}
name_servers_.clear();
domain_search_list_.clear();
dns_proxy_addr_.clear();
return base::DeleteFile(path_);
}
} // namespace shill