| // 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. Returns OK on success. |
| int ResolveIPV6LiteralUsingGURL(const std::string& host, |
| 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); |
| 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, |
| LoadLog* load_log) { |
| if (synchronous_mode_) { |
| callback = NULL; |
| out_req = NULL; |
| } |
| return impl_->Resolve(info, addresses, callback, out_req, load_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::Shutdown() { |
| impl_->Shutdown(); |
| } |
| |
| void MockHostResolverBase::Reset(HostResolverProc* interceptor) { |
| synchronous_mode_ = false; |
| |
| // At the root of the chain, map everything to localhost. |
| scoped_refptr<RuleBasedHostResolverProc> 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, |
| }; |
| |
| ResolverType resolver_type; |
| std::string host_pattern; |
| AddressFamily address_family; |
| std::string replacement; |
| int latency_ms; // In milliseconds. |
| |
| Rule(ResolverType resolver_type, |
| const std::string& host_pattern, |
| AddressFamily address_family, |
| const std::string& replacement, |
| int latency_ms) |
| : resolver_type(resolver_type), |
| host_pattern(host_pattern), |
| address_family(address_family), |
| replacement(replacement), |
| 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, replacement, 0); |
| rules_.push_back(rule); |
| } |
| |
| void RuleBasedHostResolverProc::AddIPv6Rule(const std::string& host_pattern, |
| const std::string& ipv6_literal) { |
| Rule rule(Rule::kResolverTypeIPV6Literal, host_pattern, |
| ADDRESS_FAMILY_UNSPECIFIED, ipv6_literal, 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, 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); |
| rules_.push_back(rule); |
| } |
| |
| void RuleBasedHostResolverProc::AddSimulatedFailure( |
| const std::string& host_pattern) { |
| Rule rule(Rule::kResolverTypeFail, host_pattern, |
| ADDRESS_FAMILY_UNSPECIFIED, "", 0); |
| rules_.push_back(rule); |
| } |
| |
| int RuleBasedHostResolverProc::Resolve(const std::string& host, |
| AddressFamily address_family, |
| 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; |
| |
| if (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, |
| addrlist); |
| case Rule::kResolverTypeIPV6Literal: |
| return ResolveIPV6LiteralUsingGURL(effective_host, addrlist); |
| default: |
| NOTREACHED(); |
| return ERR_UNEXPECTED; |
| } |
| } |
| } |
| return ResolveUsingPrevious(host, address_family, 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(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 |