| // 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/proxy/proxy_resolver_js_bindings.h" |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| #include "base/values.h" |
| #include "net/base/address_list.h" |
| #include "net/base/host_cache.h" |
| #include "net/base/host_resolver.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_log.h" |
| #include "net/base/net_util.h" |
| #include "net/proxy/proxy_resolver_error_observer.h" |
| #include "net/proxy/proxy_resolver_request_context.h" |
| #include "net/proxy/sync_host_resolver.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // TTL for the per-request DNS cache. Applies to both successful and failed |
| // DNS resolutions. |
| const unsigned kCacheEntryTTLSeconds = 5 * 60; |
| |
| // Returns event parameters for a PAC error message (line number + message). |
| Value* NetLogErrorCallback(int line_number, |
| const string16* message, |
| NetLog::LogLevel /* log_level */) { |
| DictionaryValue* dict = new DictionaryValue(); |
| dict->SetInteger("line_number", line_number); |
| dict->SetString("message", *message); |
| return dict; |
| } |
| |
| // ProxyResolverJSBindings implementation. |
| class DefaultJSBindings : public ProxyResolverJSBindings { |
| public: |
| DefaultJSBindings(SyncHostResolver* host_resolver, |
| NetLog* net_log, |
| ProxyResolverErrorObserver* error_observer) |
| : host_resolver_(host_resolver), |
| net_log_(net_log), |
| error_observer_(error_observer) { |
| } |
| |
| // Handler for "alert(message)". |
| virtual void Alert(const string16& message) OVERRIDE { |
| VLOG(1) << "PAC-alert: " << message; |
| |
| // Send to the NetLog. |
| LogEventToCurrentRequestAndGlobally( |
| NetLog::TYPE_PAC_JAVASCRIPT_ALERT, |
| NetLog::StringCallback("message", &message)); |
| } |
| |
| // Handler for "myIpAddress()". |
| // TODO(eroman): Perhaps enumerate the interfaces directly, using |
| // getifaddrs(). |
| virtual bool MyIpAddress(std::string* first_ip_address) OVERRIDE { |
| LogEventToCurrentRequest(NetLog::PHASE_BEGIN, |
| NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS); |
| |
| bool ok = MyIpAddressImpl(first_ip_address); |
| |
| LogEventToCurrentRequest(NetLog::PHASE_END, |
| NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS); |
| return ok; |
| } |
| |
| // Handler for "myIpAddressEx()". |
| virtual bool MyIpAddressEx(std::string* ip_address_list) OVERRIDE { |
| LogEventToCurrentRequest(NetLog::PHASE_BEGIN, |
| NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX); |
| |
| bool ok = MyIpAddressExImpl(ip_address_list); |
| |
| LogEventToCurrentRequest(NetLog::PHASE_END, |
| NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX); |
| return ok; |
| } |
| |
| // Handler for "dnsResolve(host)". |
| virtual bool DnsResolve(const std::string& host, |
| std::string* first_ip_address) OVERRIDE { |
| LogEventToCurrentRequest(NetLog::PHASE_BEGIN, |
| NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE); |
| |
| bool ok = DnsResolveImpl(host, first_ip_address); |
| |
| LogEventToCurrentRequest(NetLog::PHASE_END, |
| NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE); |
| return ok; |
| } |
| |
| // Handler for "dnsResolveEx(host)". |
| virtual bool DnsResolveEx(const std::string& host, |
| std::string* ip_address_list) OVERRIDE { |
| LogEventToCurrentRequest(NetLog::PHASE_BEGIN, |
| NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX); |
| |
| bool ok = DnsResolveExImpl(host, ip_address_list); |
| |
| LogEventToCurrentRequest(NetLog::PHASE_END, |
| NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX); |
| return ok; |
| } |
| |
| // Handler for when an error is encountered. |line_number| may be -1. |
| virtual void OnError(int line_number, const string16& message) OVERRIDE { |
| // Send to the chrome log. |
| if (line_number == -1) |
| VLOG(1) << "PAC-error: " << message; |
| else |
| VLOG(1) << "PAC-error: " << "line: " << line_number << ": " << message; |
| |
| // Send the error to the NetLog. |
| LogEventToCurrentRequestAndGlobally( |
| NetLog::TYPE_PAC_JAVASCRIPT_ERROR, |
| base::Bind(&NetLogErrorCallback, line_number, &message)); |
| |
| if (error_observer_.get()) |
| error_observer_->OnPACScriptError(line_number, message); |
| } |
| |
| virtual void Shutdown() OVERRIDE { |
| host_resolver_->Shutdown(); |
| } |
| |
| private: |
| bool MyIpAddressImpl(std::string* first_ip_address) { |
| std::string my_hostname = GetHostName(); |
| if (my_hostname.empty()) |
| return false; |
| return DnsResolveImpl(my_hostname, first_ip_address); |
| } |
| |
| bool MyIpAddressExImpl(std::string* ip_address_list) { |
| std::string my_hostname = GetHostName(); |
| if (my_hostname.empty()) |
| return false; |
| return DnsResolveExImpl(my_hostname, ip_address_list); |
| } |
| |
| bool DnsResolveImpl(const std::string& host, |
| std::string* first_ip_address) { |
| // Do a sync resolve of the hostname (port doesn't matter). |
| // Disable IPv6 results. We do this because the PAC specification isn't |
| // really IPv6 friendly, and Internet Explorer also restricts to IPv4. |
| // Consequently a lot of existing PAC scripts assume they will only get |
| // IPv4 results, and will misbehave if they get an IPv6 result. |
| // See http://crbug.com/24641 for more details. |
| HostResolver::RequestInfo info(HostPortPair(host, 80)); |
| info.set_address_family(ADDRESS_FAMILY_IPV4); |
| AddressList address_list; |
| |
| int result = DnsResolveHelper(info, &address_list); |
| if (result != OK) |
| return false; |
| |
| // There may be multiple results; we will just use the first one. |
| // This returns empty string on failure. |
| *first_ip_address = address_list.front().ToStringWithoutPort(); |
| if (first_ip_address->empty()) |
| return false; |
| |
| return true; |
| } |
| |
| bool DnsResolveExImpl(const std::string& host, |
| std::string* ip_address_list) { |
| // Do a sync resolve of the hostname (port doesn't matter). |
| HostResolver::RequestInfo info(HostPortPair(host, 80)); |
| AddressList address_list; |
| int result = DnsResolveHelper(info, &address_list); |
| |
| if (result != OK) |
| return false; |
| |
| // Stringify all of the addresses in the address list, separated |
| // by semicolons. |
| std::string address_list_str; |
| for (AddressList::const_iterator iter = address_list.begin(); |
| iter != address_list.end(); ++iter) { |
| if (!address_list_str.empty()) |
| address_list_str += ";"; |
| const std::string address_string = iter->ToStringWithoutPort(); |
| if (address_string.empty()) |
| return false; |
| address_list_str += address_string; |
| } |
| |
| *ip_address_list = address_list_str; |
| return true; |
| } |
| |
| // Helper to execute a synchronous DNS resolve, using the per-request |
| // DNS cache if there is one. |
| int DnsResolveHelper(const HostResolver::RequestInfo& info, |
| AddressList* address_list) { |
| HostCache::Key cache_key(info.hostname(), |
| info.address_family(), |
| info.host_resolver_flags()); |
| |
| HostCache* host_cache = current_request_context() ? |
| current_request_context()->host_cache : NULL; |
| |
| // First try to service this request from the per-request DNS cache. |
| // (we cache DNS failures much more aggressively within the context |
| // of a FindProxyForURL() request). |
| if (host_cache) { |
| const HostCache::Entry* entry = |
| host_cache->Lookup(cache_key, base::TimeTicks::Now()); |
| if (entry) { |
| if (entry->error == OK) |
| *address_list = entry->addrlist; |
| return entry->error; |
| } |
| } |
| |
| // Otherwise ask the host resolver. |
| const BoundNetLog* net_log = GetNetLogForCurrentRequest(); |
| int result = host_resolver_->Resolve(info, |
| address_list, |
| net_log ? *net_log : BoundNetLog()); |
| |
| // Save the result back to the per-request DNS cache. |
| if (host_cache) { |
| host_cache->Set(cache_key, HostCache::Entry(result, *address_list), |
| base::TimeTicks::Now(), |
| base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds)); |
| } |
| |
| return result; |
| } |
| |
| // May return NULL. |
| const BoundNetLog* GetNetLogForCurrentRequest() { |
| if (!current_request_context()) |
| return NULL; |
| return current_request_context()->net_log; |
| } |
| |
| void LogEventToCurrentRequest( |
| NetLog::EventPhase phase, |
| NetLog::EventType type) { |
| const BoundNetLog* net_log = GetNetLogForCurrentRequest(); |
| if (net_log) |
| net_log->AddEntry(type, phase); |
| } |
| |
| void LogEventToCurrentRequest( |
| NetLog::EventPhase phase, |
| NetLog::EventType type, |
| const NetLog::ParametersCallback& parameters_callback) { |
| const BoundNetLog* net_log = GetNetLogForCurrentRequest(); |
| if (net_log) |
| net_log->AddEntry(type, phase, parameters_callback); |
| } |
| |
| void LogEventToCurrentRequestAndGlobally( |
| NetLog::EventType type, |
| const NetLog::ParametersCallback& parameters_callback) { |
| LogEventToCurrentRequest(NetLog::PHASE_NONE, type, parameters_callback); |
| |
| // Emit to the global NetLog event stream. |
| if (net_log_) |
| net_log_->AddGlobalEntry(type, parameters_callback); |
| } |
| |
| scoped_ptr<SyncHostResolver> host_resolver_; |
| NetLog* net_log_; |
| scoped_ptr<ProxyResolverErrorObserver> error_observer_; |
| DISALLOW_COPY_AND_ASSIGN(DefaultJSBindings); |
| }; |
| |
| } // namespace |
| |
| // static |
| ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault( |
| SyncHostResolver* host_resolver, |
| NetLog* net_log, |
| ProxyResolverErrorObserver* error_observer) { |
| return new DefaultJSBindings(host_resolver, net_log, error_observer); |
| } |
| |
| } // namespace net |