// 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/proxy/proxy_resolver_js_bindings.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/base/sys_addrinfo.h"
#include "net/proxy/proxy_resolver_request_context.h"
namespace net {
namespace {
// Event parameters for a PAC error message (line number + message).
class ErrorNetlogParams : public NetLog::EventParameters {
public:
ErrorNetlogParams(int line_number,
const string16& message)
: line_number_(line_number),
message_(message) {
}
virtual Value* ToValue() const {
DictionaryValue* dict = new DictionaryValue();
dict->SetInteger("line_number", line_number_);
dict->SetString("message", message_);
return dict;
}
private:
const int line_number_;
const string16 message_;
DISALLOW_COPY_AND_ASSIGN(ErrorNetlogParams);
};
// Event parameters for a PAC alert().
class AlertNetlogParams : public NetLog::EventParameters {
public:
explicit AlertNetlogParams(const string16& message) : message_(message) {
}
virtual Value* ToValue() const {
DictionaryValue* dict = new DictionaryValue();
dict->SetString("message", message_);
return dict;
}
private:
const string16 message_;
DISALLOW_COPY_AND_ASSIGN(AlertNetlogParams);
};
// ProxyResolverJSBindings implementation.
class DefaultJSBindings : public ProxyResolverJSBindings {
public:
DefaultJSBindings(HostResolver* host_resolver, NetLog* net_log)
: host_resolver_(host_resolver),
net_log_(net_log) {
}
// Handler for "alert(message)".
virtual void Alert(const string16& message) {
VLOG(1) << "PAC-alert: " << message;
// Send to the NetLog.
LogEventToCurrentRequestAndGlobally(NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
new AlertNetlogParams(message));
}
// Handler for "myIpAddress()".
// TODO(eroman): Perhaps enumerate the interfaces directly, using
// getifaddrs().
virtual bool MyIpAddress(std::string* first_ip_address) {
LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS,
NULL);
bool ok = MyIpAddressImpl(first_ip_address);
LogEventToCurrentRequest(NetLog::PHASE_END,
NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS,
NULL);
return ok;
}
// Handler for "myIpAddressEx()".
virtual bool MyIpAddressEx(std::string* ip_address_list) {
LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX,
NULL);
bool ok = MyIpAddressExImpl(ip_address_list);
LogEventToCurrentRequest(NetLog::PHASE_END,
NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX,
NULL);
return ok;
}
// Handler for "dnsResolve(host)".
virtual bool DnsResolve(const std::string& host,
std::string* first_ip_address) {
LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE,
NULL);
bool ok = DnsResolveImpl(host, first_ip_address);
LogEventToCurrentRequest(NetLog::PHASE_END,
NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE,
NULL);
return ok;
}
// Handler for "dnsResolveEx(host)".
virtual bool DnsResolveEx(const std::string& host,
std::string* ip_address_list) {
LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX,
NULL);
bool ok = DnsResolveExImpl(host, ip_address_list);
LogEventToCurrentRequest(NetLog::PHASE_END,
NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX,
NULL);
return ok;
}
// Handler for when an error is encountered. |line_number| may be -1.
virtual void OnError(int line_number, const string16& message) {
// 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,
new ErrorNetlogParams(line_number, message));
}
virtual void Shutdown() {
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 = net::NetAddressToString(address_list.head());
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;
const struct addrinfo* current_address = address_list.head();
while (current_address) {
if (!address_list_str.empty())
address_list_str += ";";
const std::string address_string = NetAddressToString(current_address);
if (address_string.empty())
return false;
address_list_str += address_string;
current_address = current_address->ai_next;
}
*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 resolver.
int result = host_resolver_->Resolve(info, address_list, NULL, NULL,
BoundNetLog());
// Save the result back to the per-request DNS cache.
if (host_cache) {
host_cache->Set(cache_key, result, *address_list,
base::TimeTicks::Now());
}
return result;
}
void LogEventToCurrentRequest(
NetLog::EventPhase phase,
NetLog::EventType type,
scoped_refptr<NetLog::EventParameters> params) {
if (current_request_context() && current_request_context()->net_log)
current_request_context()->net_log->AddEntry(type, phase, params);
}
void LogEventToCurrentRequestAndGlobally(
NetLog::EventType type,
scoped_refptr<NetLog::EventParameters> params) {
LogEventToCurrentRequest(NetLog::PHASE_NONE, type, params);
// Emit to the global NetLog event stream.
if (net_log_) {
net_log_->AddEntry(
type,
base::TimeTicks::Now(),
NetLog::Source(),
NetLog::PHASE_NONE,
params);
}
}
HostResolver* const host_resolver_;
NetLog* net_log_;
};
} // namespace
// static
ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault(
HostResolver* host_resolver, NetLog* net_log) {
return new DefaultJSBindings(host_resolver, net_log);
}
} // namespace net