blob: 4f6dc1c6cd270d41e98d8bd8e04e89093f79f3f1 [file] [log] [blame]
// 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 "net/dns/mock_host_resolver.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/pattern.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/host_cache.h"
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
#endif
namespace net {
namespace {
// Cache size for the MockCachingHostResolver.
const unsigned kMaxCacheEntries = 100;
// TTL for the successful resolutions. Failures are not cached.
const unsigned kCacheEntryTTLSeconds = 60;
} // namespace
int ParseAddressList(const std::string& host_list,
const std::string& canonical_name,
AddressList* addrlist) {
*addrlist = AddressList();
addrlist->set_canonical_name(canonical_name);
for (const base::StringPiece& address : base::SplitStringPiece(
host_list, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(address)) {
LOG(WARNING) << "Not a supported IP literal: " << address.as_string();
return ERR_UNEXPECTED;
}
addrlist->push_back(IPEndPoint(ip_address, 0));
}
return OK;
}
class MockHostResolverBase::RequestImpl : public HostResolver::Request {
public:
RequestImpl(const RequestInfo& req_info,
AddressList* addr,
const CompletionCallback& cb,
MockHostResolverBase* resolver,
size_t id)
: info_(req_info),
addresses_(addr),
callback_(cb),
resolver_(resolver),
id_(id) {}
~RequestImpl() override {
if (resolver_)
resolver_->DetachRequest(id_);
}
void ChangeRequestPriority(RequestPriority priority) override {}
void OnResolveCompleted(MockHostResolverBase* resolver, int error) {
DCHECK_EQ(resolver_, resolver);
resolver_ = nullptr;
addresses_ = nullptr;
base::ResetAndReturn(&callback_).Run(error);
}
RequestInfo info() { return info_; }
AddressList* addresses() { return addresses_; }
private:
RequestInfo info_;
AddressList* addresses_;
CompletionCallback callback_;
MockHostResolverBase* resolver_;
size_t id_;
DISALLOW_COPY_AND_ASSIGN(RequestImpl);
};
MockHostResolverBase::~MockHostResolverBase() {
DCHECK(requests_.empty());
}
int MockHostResolverBase::Resolve(const RequestInfo& info,
RequestPriority priority,
AddressList* addresses,
const CompletionCallback& callback,
std::unique_ptr<Request>* request,
const BoundNetLog& net_log) {
DCHECK(CalledOnValidThread());
DCHECK(request);
last_request_priority_ = priority;
num_resolve_++;
size_t id = next_request_id_++;
int rv = ResolveFromIPLiteralOrCache(info, addresses);
if (rv != ERR_DNS_CACHE_MISS) {
return rv;
}
if (synchronous_mode_) {
return ResolveProc(info, addresses);
}
// Store the request for asynchronous resolution
std::unique_ptr<RequestImpl> req(
new RequestImpl(info, addresses, callback, this, id));
requests_[id] = req.get();
*request = std::move(req);
if (!ondemand_mode_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&MockHostResolverBase::ResolveNow, AsWeakPtr(), id));
}
return ERR_IO_PENDING;
}
int MockHostResolverBase::ResolveFromCache(const RequestInfo& info,
AddressList* addresses,
const BoundNetLog& net_log) {
num_resolve_from_cache_++;
DCHECK(CalledOnValidThread());
next_request_id_++;
int rv = ResolveFromIPLiteralOrCache(info, addresses);
return rv;
}
void MockHostResolverBase::DetachRequest(size_t id) {
RequestMap::iterator it = requests_.find(id);
CHECK(it != requests_.end());
requests_.erase(it);
}
HostCache* MockHostResolverBase::GetHostCache() {
return cache_.get();
}
void MockHostResolverBase::ResolveAllPending() {
DCHECK(CalledOnValidThread());
DCHECK(ondemand_mode_);
for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&MockHostResolverBase::ResolveNow, AsWeakPtr(), i->first));
}
}
// start id from 1 to distinguish from NULL RequestHandle
MockHostResolverBase::MockHostResolverBase(bool use_caching)
: last_request_priority_(DEFAULT_PRIORITY),
synchronous_mode_(false),
ondemand_mode_(false),
next_request_id_(1),
num_resolve_(0),
num_resolve_from_cache_(0) {
rules_ = CreateCatchAllHostResolverProc();
if (use_caching) {
cache_.reset(new HostCache(kMaxCacheEntries));
}
}
int MockHostResolverBase::ResolveFromIPLiteralOrCache(const RequestInfo& info,
AddressList* addresses) {
IPAddress ip_address;
if (ip_address.AssignFromIPLiteral(info.hostname())) {
// This matches the behavior HostResolverImpl.
if (info.address_family() != ADDRESS_FAMILY_UNSPECIFIED &&
info.address_family() != GetAddressFamily(ip_address)) {
return ERR_NAME_NOT_RESOLVED;
}
*addresses = AddressList::CreateFromIPAddress(ip_address, info.port());
if (info.host_resolver_flags() & HOST_RESOLVER_CANONNAME)
addresses->SetDefaultCanonicalName();
return OK;
}
int rv = ERR_DNS_CACHE_MISS;
if (cache_.get() && info.allow_cached_response()) {
HostCache::Key key(info.hostname(),
info.address_family(),
info.host_resolver_flags());
const HostCache::Entry* entry = cache_->Lookup(key, base::TimeTicks::Now());
if (entry) {
rv = entry->error();
if (rv == OK)
*addresses = AddressList::CopyWithPort(entry->addresses(), info.port());
}
}
return rv;
}
int MockHostResolverBase::ResolveProc(const RequestInfo& info,
AddressList* addresses) {
AddressList addr;
int rv = rules_->Resolve(info.hostname(), info.address_family(),
info.host_resolver_flags(), &addr, nullptr);
if (cache_.get()) {
HostCache::Key key(info.hostname(),
info.address_family(),
info.host_resolver_flags());
// Storing a failure with TTL 0 so that it overwrites previous value.
base::TimeDelta ttl;
if (rv == OK)
ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
cache_->Set(key, HostCache::Entry(rv, addr), base::TimeTicks::Now(), ttl);
}
if (rv == OK)
*addresses = AddressList::CopyWithPort(addr, info.port());
return rv;
}
void MockHostResolverBase::ResolveNow(size_t id) {
RequestMap::iterator it = requests_.find(id);
if (it == requests_.end())
return; // was canceled
RequestImpl* req = it->second;
requests_.erase(it);
int error = ResolveProc(req->info(), req->addresses());
req->OnResolveCompleted(this, error);
}
//-----------------------------------------------------------------------------
struct RuleBasedHostResolverProc::Rule {
enum ResolverType {
kResolverTypeFail,
kResolverTypeSystem,
kResolverTypeIPLiteral,
};
ResolverType resolver_type;
std::string host_pattern;
AddressFamily address_family;
HostResolverFlags host_resolver_flags;
std::string replacement;
std::string canonical_name;
int latency_ms; // In milliseconds.
Rule(ResolverType resolver_type,
const std::string& host_pattern,
AddressFamily address_family,
HostResolverFlags host_resolver_flags,
const std::string& replacement,
const std::string& canonical_name,
int latency_ms)
: resolver_type(resolver_type),
host_pattern(host_pattern),
address_family(address_family),
host_resolver_flags(host_resolver_flags),
replacement(replacement),
canonical_name(canonical_name),
latency_ms(latency_ms) {}
};
RuleBasedHostResolverProc::RuleBasedHostResolverProc(HostResolverProc* previous)
: HostResolverProc(previous) {
}
void RuleBasedHostResolverProc::AddRule(const std::string& host_pattern,
const std::string& replacement) {
AddRuleForAddressFamily(host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
replacement);
}
void RuleBasedHostResolverProc::AddRuleForAddressFamily(
const std::string& host_pattern,
AddressFamily address_family,
const std::string& replacement) {
DCHECK(!replacement.empty());
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeSystem,
host_pattern,
address_family,
flags,
replacement,
std::string(),
0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AddIPLiteralRule(
const std::string& host_pattern,
const std::string& ip_literal,
const std::string& canonical_name) {
// Literals are always resolved to themselves by HostResolverImpl,
// consequently we do not support remapping them.
IPAddress ip_address;
DCHECK(!ip_address.AssignFromIPLiteral(host_pattern));
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
if (!canonical_name.empty())
flags |= HOST_RESOLVER_CANONNAME;
Rule rule(Rule::kResolverTypeIPLiteral, host_pattern,
ADDRESS_FAMILY_UNSPECIFIED, flags, ip_literal, canonical_name,
0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AddRuleWithLatency(
const std::string& host_pattern,
const std::string& replacement,
int latency_ms) {
DCHECK(!replacement.empty());
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeSystem,
host_pattern,
ADDRESS_FAMILY_UNSPECIFIED,
flags,
replacement,
std::string(),
latency_ms);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AllowDirectLookup(
const std::string& host_pattern) {
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeSystem,
host_pattern,
ADDRESS_FAMILY_UNSPECIFIED,
flags,
std::string(),
std::string(),
0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AddSimulatedFailure(
const std::string& host_pattern) {
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeFail,
host_pattern,
ADDRESS_FAMILY_UNSPECIFIED,
flags,
std::string(),
std::string(),
0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::ClearRules() {
base::AutoLock lock(rule_lock_);
rules_.clear();
}
int RuleBasedHostResolverProc::Resolve(const std::string& host,
AddressFamily address_family,
HostResolverFlags host_resolver_flags,
AddressList* addrlist,
int* os_error) {
base::AutoLock lock(rule_lock_);
RuleList::iterator r;
for (r = rules_.begin(); r != rules_.end(); ++r) {
bool matches_address_family =
r->address_family == ADDRESS_FAMILY_UNSPECIFIED ||
r->address_family == address_family;
// Ignore HOST_RESOLVER_SYSTEM_ONLY, since it should have no impact on
// whether a rule matches.
HostResolverFlags flags = host_resolver_flags & ~HOST_RESOLVER_SYSTEM_ONLY;
// Flags match if all of the bitflags in host_resolver_flags are enabled
// in the rule's host_resolver_flags. However, the rule may have additional
// flags specified, in which case the flags should still be considered a
// match.
bool matches_flags = (r->host_resolver_flags & flags) == flags;
if (matches_flags && matches_address_family &&
base::MatchPattern(host, r->host_pattern)) {
if (r->latency_ms != 0) {
base::PlatformThread::Sleep(
base::TimeDelta::FromMilliseconds(r->latency_ms));
}
// Remap to a new host.
const std::string& effective_host =
r->replacement.empty() ? host : r->replacement;
// Apply the resolving function to the remapped hostname.
switch (r->resolver_type) {
case Rule::kResolverTypeFail:
return ERR_NAME_NOT_RESOLVED;
case Rule::kResolverTypeSystem:
#if defined(OS_WIN)
EnsureWinsockInit();
#endif
return SystemHostResolverCall(effective_host,
address_family,
host_resolver_flags,
addrlist, os_error);
case Rule::kResolverTypeIPLiteral:
return ParseAddressList(effective_host,
r->canonical_name,
addrlist);
default:
NOTREACHED();
return ERR_UNEXPECTED;
}
}
}
return ResolveUsingPrevious(host, address_family,
host_resolver_flags, addrlist, os_error);
}
RuleBasedHostResolverProc::~RuleBasedHostResolverProc() {
}
void RuleBasedHostResolverProc::AddRuleInternal(const Rule& rule) {
base::AutoLock lock(rule_lock_);
rules_.push_back(rule);
}
RuleBasedHostResolverProc* CreateCatchAllHostResolverProc() {
RuleBasedHostResolverProc* catchall = new RuleBasedHostResolverProc(NULL);
catchall->AddIPLiteralRule("*", "127.0.0.1", "localhost");
// Next add a rules-based layer the use controls.
return new RuleBasedHostResolverProc(catchall);
}
//-----------------------------------------------------------------------------
int HangingHostResolver::Resolve(const RequestInfo& info,
RequestPriority priority,
AddressList* addresses,
const CompletionCallback& callback,
std::unique_ptr<Request>* request,
const BoundNetLog& net_log) {
return ERR_IO_PENDING;
}
int HangingHostResolver::ResolveFromCache(const RequestInfo& info,
AddressList* addresses,
const BoundNetLog& net_log) {
return ERR_DNS_CACHE_MISS;
}
//-----------------------------------------------------------------------------
ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc() {}
ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc(
HostResolverProc* proc) {
Init(proc);
}
ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() {
HostResolverProc* old_proc =
HostResolverProc::SetDefault(previous_proc_.get());
// The lifetimes of multiple instances must be nested.
CHECK_EQ(old_proc, current_proc_.get());
}
void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) {
current_proc_ = proc;
previous_proc_ = HostResolverProc::SetDefault(current_proc_.get());
current_proc_->SetLastProc(previous_proc_.get());
}
} // namespace net