blob: 84dcafda4dca6f680f2c79086c264c9b5ead1457 [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/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