// Copyright (c) 2009 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/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/waitable_event.h"
#include "net/base/address_list.h"
#include "net/base/host_resolver.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/base/sys_addrinfo.h"
namespace net {
namespace {
// Wrapper around HostResolver to give a sync API while running the resolve
// in async mode on |host_resolver_loop|. If |host_resolver_loop| is NULL,
// runs sync on the current thread (this mode is just used by testing).
class SyncHostResolverBridge
: public base::RefCountedThreadSafe<SyncHostResolverBridge> {
public:
SyncHostResolverBridge(HostResolver* host_resolver,
MessageLoop* host_resolver_loop)
: host_resolver_(host_resolver),
host_resolver_loop_(host_resolver_loop),
event_(false, false),
ALLOW_THIS_IN_INITIALIZER_LIST(
callback_(this, &SyncHostResolverBridge::OnResolveCompletion)) {
}
// Run the resolve on host_resolver_loop, and wait for result.
int Resolve(const std::string& hostname,
AddressFamily address_family,
net::AddressList* addresses) {
// Port number doesn't matter.
HostResolver::RequestInfo info(hostname, 80);
info.set_address_family(address_family);
// Hack for tests -- run synchronously on current thread.
if (!host_resolver_loop_)
return host_resolver_->Resolve(info, addresses, NULL, NULL, NULL);
// Otherwise start an async resolve on the resolver's thread.
host_resolver_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
&SyncHostResolverBridge::StartResolve, info, addresses));
// Wait for the resolve to complete in the resolver's thread.
event_.Wait();
return err_;
}
private:
friend class base::RefCountedThreadSafe<SyncHostResolverBridge>;
~SyncHostResolverBridge() {}
// Called on host_resolver_loop_.
void StartResolve(const HostResolver::RequestInfo& info,
net::AddressList* addresses) {
DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
int error = host_resolver_->Resolve(
info, addresses, &callback_, NULL, NULL);
if (error != ERR_IO_PENDING)
OnResolveCompletion(error); // Completed synchronously.
}
// Called on host_resolver_loop_.
void OnResolveCompletion(int result) {
DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
err_ = result;
event_.Signal();
}
scoped_refptr<HostResolver> host_resolver_;
MessageLoop* host_resolver_loop_;
// Event to notify completion of resolve request.
base::WaitableEvent event_;
// Callback for when the resolve completes on host_resolver_loop_.
net::CompletionCallbackImpl<SyncHostResolverBridge> callback_;
// The result from the result request (set by in host_resolver_loop_).
int err_;
};
// ProxyResolverJSBindings implementation.
class DefaultJSBindings : public ProxyResolverJSBindings {
public:
DefaultJSBindings(HostResolver* host_resolver,
MessageLoop* host_resolver_loop)
: host_resolver_(new SyncHostResolverBridge(
host_resolver, host_resolver_loop)) {}
// Handler for "alert(message)".
virtual void Alert(const std::string& message) {
LOG(INFO) << "PAC-alert: " << message;
}
// Handler for "myIpAddress()". Returns empty string on failure.
// TODO(eroman): Perhaps enumerate the interfaces directly, using
// getifaddrs().
virtual std::string MyIpAddress() {
// DnsResolve("") returns "", so no need to check for failure.
return DnsResolve(GetHostName());
}
// Handler for "myIpAddressEx()". Returns empty string on failure.
virtual std::string MyIpAddressEx() {
return DnsResolveEx(GetHostName());
}
// Handler for "dnsResolve(host)". Returns empty string on failure.
virtual std::string DnsResolve(const std::string& host) {
// Do a sync resolve of the hostname.
// 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.
net::AddressList address_list;
int result = host_resolver_->Resolve(host,
ADDRESS_FAMILY_IPV4,
&address_list);
if (result != OK)
return std::string(); // Failed.
if (!address_list.head())
return std::string();
// There may be multiple results; we will just use the first one.
// This returns empty string on failure.
return net::NetAddressToString(address_list.head());
}
// Handler for "dnsResolveEx(host)". Returns empty string on failure.
virtual std::string DnsResolveEx(const std::string& host) {
// Do a sync resolve of the hostname.
net::AddressList address_list;
int result = host_resolver_->Resolve(host,
ADDRESS_FAMILY_UNSPECIFIED,
&address_list);
if (result != OK)
return std::string(); // Failed.
// 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 += ";";
address_list_str += net::NetAddressToString(current_address);
current_address = current_address->ai_next;
}
return address_list_str;
}
// Handler for when an error is encountered. |line_number| may be -1.
virtual void OnError(int line_number, const std::string& message) {
if (line_number == -1)
LOG(INFO) << "PAC-error: " << message;
else
LOG(INFO) << "PAC-error: " << "line: " << line_number << ": " << message;
}
private:
scoped_refptr<SyncHostResolverBridge> host_resolver_;
};
} // namespace
// static
ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault(
HostResolver* host_resolver, MessageLoop* host_resolver_loop) {
return new DefaultJSBindings(host_resolver, host_resolver_loop);
}
} // namespace net