// Copyright (c) 2010 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/base/mock_host_resolver.h" #include "base/string_util.h" #include "base/platform_thread.h" #include "base/ref_counted.h" #include "googleurl/src/url_canon_ip.h" #include "net/base/net_errors.h" namespace net { namespace { // Fills |*addrlist| with a socket address for |host| which should be an // IPv6 literal without enclosing brackets. If |canonical_name| is non-empty // it is used as the DNS canonical name for the host. Returns OK on success, // ERR_UNEXPECTED otherwise. int CreateIPv6Address(const std::string& host, const std::string& canonical_name, AddressList* addrlist) { // GURL expects the hostname to be surrounded with brackets. std::string host_brackets = "[" + host + "]"; url_parse::Component host_comp(0, host_brackets.size()); // Try parsing the hostname as an IPv6 literal. unsigned char ipv6_addr[16]; // 128 bits. bool ok = url_canon::IPv6AddressToNumber(host_brackets.data(), host_comp, ipv6_addr); if (!ok) { LOG(WARNING) << "Not an IPv6 literal: " << host; return ERR_UNEXPECTED; } *addrlist = AddressList::CreateIPv6Address(ipv6_addr, canonical_name); return OK; } // Fills |*addrlist| with a socket address for |host| which should be an // IPv4 literal. If |canonical_name| is non-empty it is used as the DNS // canonical name for the host. Returns OK on success, ERR_UNEXPECTED otherwise. int CreateIPv4Address(const std::string& host, const std::string& canonical_name, AddressList* addrlist) { unsigned char ipv4_addr[4]; url_parse::Component host_comp(0, host.size()); int num_components; url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber( host.data(), host_comp, ipv4_addr, &num_components); if (family != url_canon::CanonHostInfo::IPV4) { LOG(WARNING) << "Not an IPv4 literal: " << host; return ERR_UNEXPECTED; } *addrlist = AddressList::CreateIPv4Address(ipv4_addr, canonical_name); return OK; } } // namespace MockHostResolverBase::MockHostResolverBase(bool use_caching) : use_caching_(use_caching) { Reset(NULL); } int MockHostResolverBase::Resolve(const RequestInfo& info, AddressList* addresses, CompletionCallback* callback, RequestHandle* out_req, const BoundNetLog& net_log) { if (synchronous_mode_) { callback = NULL; out_req = NULL; } return impl_->Resolve(info, addresses, callback, out_req, net_log); } void MockHostResolverBase::CancelRequest(RequestHandle req) { impl_->CancelRequest(req); } void MockHostResolverBase::AddObserver(Observer* observer) { impl_->AddObserver(observer); } void MockHostResolverBase::RemoveObserver(Observer* observer) { impl_->RemoveObserver(observer); } void MockHostResolverBase::Reset(HostResolverProc* interceptor) { synchronous_mode_ = false; // At the root of the chain, map everything to localhost. scoped_refptr catchall = new RuleBasedHostResolverProc(NULL); catchall->AddRule("*", "127.0.0.1"); // Next add a rules-based layer the use controls. rules_ = new RuleBasedHostResolverProc(catchall); HostResolverProc* proc = rules_; // Lastly add the provided interceptor to the front of the chain. if (interceptor) { interceptor->SetPreviousProc(proc); proc = interceptor; } HostCache* cache = NULL; if (use_caching_) { cache = new HostCache( 100, // max entries. base::TimeDelta::FromMinutes(1), base::TimeDelta::FromSeconds(0)); } impl_ = new HostResolverImpl(proc, cache, NULL, 50u); } //----------------------------------------------------------------------------- struct RuleBasedHostResolverProc::Rule { enum ResolverType { kResolverTypeFail, kResolverTypeSystem, kResolverTypeIPV6Literal, kResolverTypeIPV4Literal, }; 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) { } RuleBasedHostResolverProc::~RuleBasedHostResolverProc() { } 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()); Rule rule(Rule::kResolverTypeSystem, host_pattern, address_family, 0, replacement, "", 0); rules_.push_back(rule); } void RuleBasedHostResolverProc::AddIPv4Rule(const std::string& host_pattern, const std::string& ipv4_literal, const std::string& canonical_name) { Rule rule(Rule::kResolverTypeIPV4Literal, host_pattern, ADDRESS_FAMILY_UNSPECIFIED, canonical_name.empty() ? 0 : HOST_RESOLVER_CANONNAME, ipv4_literal, canonical_name, 0); rules_.push_back(rule); } void RuleBasedHostResolverProc::AddIPv6Rule(const std::string& host_pattern, const std::string& ipv6_literal, const std::string& canonical_name) { Rule rule(Rule::kResolverTypeIPV6Literal, host_pattern, ADDRESS_FAMILY_UNSPECIFIED, canonical_name.empty() ? 0 : HOST_RESOLVER_CANONNAME, ipv6_literal, canonical_name, 0); rules_.push_back(rule); } void RuleBasedHostResolverProc::AddRuleWithLatency( const std::string& host_pattern, const std::string& replacement, int latency_ms) { DCHECK(!replacement.empty()); Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED, 0, replacement, "", latency_ms); rules_.push_back(rule); } void RuleBasedHostResolverProc::AllowDirectLookup( const std::string& host_pattern) { Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED, 0, "", "", 0); rules_.push_back(rule); } void RuleBasedHostResolverProc::AddSimulatedFailure( const std::string& host_pattern) { Rule rule(Rule::kResolverTypeFail, host_pattern, ADDRESS_FAMILY_UNSPECIFIED, 0, "", "", 0); rules_.push_back(rule); } int RuleBasedHostResolverProc::Resolve(const std::string& host, AddressFamily address_family, HostResolverFlags host_resolver_flags, AddressList* addrlist) { 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; // 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 & host_resolver_flags) == host_resolver_flags; if (matches_flags && matches_address_family && MatchPatternASCII(host, r->host_pattern)) { if (r->latency_ms != 0) PlatformThread::Sleep(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: return SystemHostResolverProc(effective_host, address_family, host_resolver_flags, addrlist); case Rule::kResolverTypeIPV6Literal: return CreateIPv6Address(effective_host, r->canonical_name, addrlist); case Rule::kResolverTypeIPV4Literal: return CreateIPv4Address(effective_host, r->canonical_name, addrlist); default: NOTREACHED(); return ERR_UNEXPECTED; } } } return ResolveUsingPrevious(host, address_family, host_resolver_flags, addrlist); } //----------------------------------------------------------------------------- ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc( HostResolverProc* proc) { Init(proc); } ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() { HostResolverProc* old_proc = HostResolverProc::SetDefault(previous_proc_); // The lifetimes of multiple instances must be nested. CHECK_EQ(old_proc, current_proc_); } void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) { current_proc_ = proc; previous_proc_ = HostResolverProc::SetDefault(current_proc_); current_proc_->SetLastProc(previous_proc_); } } // namespace net