// Copyright 2015 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 "services/network/proxy_resolver_factory_mojo.h" #include #include #include #include #include "base/bind.h" #include "base/callback.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" #include "base/sequence_checker.h" #include "base/strings/utf_string_conversions.h" #include "base/task/task_runner.h" #include "base/task/thread_pool.h" #include "base/values.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/base/ip_address.h" #include "net/base/load_states.h" #include "net/base/net_errors.h" #include "net/base/network_isolation_key.h" #include "net/log/net_log.h" #include "net/log/net_log_capture_mode.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_with_source.h" #include "net/proxy_resolution/pac_file_data.h" #include "net/proxy_resolution/proxy_info.h" #include "net/proxy_resolution/proxy_resolve_dns_operation.h" #include "net/proxy_resolution/proxy_resolver.h" #include "net/proxy_resolution/proxy_resolver_error_observer.h" #include "services/network/mojo_host_resolver_impl.h" #include "services/network/proxy_auto_config_library.h" #include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h" namespace net { class NetworkIsolationKey; } namespace network { namespace { base::Value NetLogErrorParams(int line_number, const std::string& message) { base::Value::Dict dict; dict.Set("line_number", line_number); dict.Set("message", message); return base::Value(std::move(dict)); } // Implementation for myIpAddress() and myIpAddressEx() that is expected to run // on a worker thread. Will notify |client| on completion. void DoMyIpAddressOnWorker( bool is_ex, mojo::PendingRemote client_remote) { // Resolve the list of IP addresses. std::vector my_ip_addresses = is_ex ? PacMyIpAddressEx() : PacMyIpAddress(); mojo::Remote client( std::move(client_remote)); // TODO(eroman): Note that this code always returns a success response (with // loopback) rather than passing forward the error. This is to ensure that the // response gets cached on the proxy resolver process side, since this layer // here does not currently do any caching or de-duplication. This should be // cleaned up once the interfaces are refactored. Lastly note that for // myIpAddress() this doesn't change the final result. However for // myIpAddressEx() it means we return 127.0.0.1 rather than empty string. if (my_ip_addresses.empty()) my_ip_addresses.push_back(net::IPAddress::IPv4Localhost()); client->ReportResult(net::OK, my_ip_addresses); } // A mixin that forwards logging to (Bound)NetLog and ProxyResolverErrorObserver // and DNS requests to a MojoHostResolverImpl, which is implemented in terms of // a HostResolver, or myIpAddress[Ex]() which is implemented by //net. template class ClientMixin : public ClientInterface { public: ClientMixin(net::HostResolver* host_resolver, net::ProxyResolverErrorObserver* error_observer, net::NetLog* net_log, const net::NetLogWithSource& net_log_with_source) : host_resolver_(host_resolver, net_log_with_source), error_observer_(error_observer), net_log_(net_log), net_log_with_source_(net_log_with_source) {} // Overridden from ClientInterface: void Alert(const std::string& message) override { net_log_with_source_.AddEventWithStringParams( net::NetLogEventType::PAC_JAVASCRIPT_ALERT, "message", message); if (net_log_) net_log_->AddGlobalEntryWithStringParams( net::NetLogEventType::PAC_JAVASCRIPT_ALERT, "message", message); } void OnError(int32_t line_number, const std::string& message) override { net_log_with_source_.AddEvent( net::NetLogEventType::PAC_JAVASCRIPT_ERROR, [&] { return NetLogErrorParams(line_number, message); }); if (net_log_) net_log_->AddGlobalEntry(net::NetLogEventType::PAC_JAVASCRIPT_ERROR, [&] { return NetLogErrorParams(line_number, message); }); if (error_observer_) { error_observer_->OnPACScriptError(line_number, base::UTF8ToUTF16(message)); } } // TODO(eroman): Split the client interfaces so ResolveDns() does not also // carry the myIpAddress(Ex) requests. void ResolveDns( const std::string& hostname, net::ProxyResolveDnsOperation operation, const net::NetworkIsolationKey& network_isolation_key, mojo::PendingRemote client) override { bool is_ex = operation == net::ProxyResolveDnsOperation::DNS_RESOLVE_EX || operation == net::ProxyResolveDnsOperation::MY_IP_ADDRESS_EX; if (operation == net::ProxyResolveDnsOperation::MY_IP_ADDRESS || operation == net::ProxyResolveDnsOperation::MY_IP_ADDRESS_EX) { GetMyIpAddressTaskRuner()->PostTask( FROM_HERE, base::BindOnce(&DoMyIpAddressOnWorker, is_ex, std::move(client))); } else { // Request was for dnsResolve() or dnsResolveEx(). host_resolver_.Resolve(hostname, network_isolation_key, is_ex, std::move(client)); } } protected: // TODO(eroman): This doesn't track being blocked in myIpAddress(Ex) handler. bool dns_request_in_progress() { return host_resolver_.request_in_progress(); } // Returns a task runner used to run the code for myIpAddress[Ex]. static scoped_refptr GetMyIpAddressTaskRuner() { // TODO(eroman): While these tasks are expected to normally run quickly, // it would be prudent to enforce a bound on outstanding tasks, and maybe // de-duplication of requests. // // However the better place to focus on is de-duplication and caching on the // proxy service side (which currently caches but doesn't de-duplicate). return base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::TaskPriority::USER_VISIBLE}); } private: MojoHostResolverImpl host_resolver_; const raw_ptr error_observer_; const raw_ptr net_log_; const net::NetLogWithSource net_log_with_source_; }; // Implementation of ProxyResolver that connects to a Mojo service to evaluate // PAC scripts. This implementation only knows about Mojo services, and // therefore that service may live in or out of process. // // This implementation reports disconnections from the Mojo service (i.e. if the // service is out-of-process and that process crashes) using the error code // ERR_PAC_SCRIPT_TERMINATED. class ProxyResolverMojo : public net::ProxyResolver { public: // Constructs a ProxyResolverMojo that connects to a mojo proxy resolver // implementation using |resolver_remote|. The implementation uses // |host_resolver| as the DNS resolver, using |host_resolver_binding| to // communicate with it. ProxyResolverMojo( mojo::PendingRemote resolver_remote, net::HostResolver* host_resolver, std::unique_ptr error_observer, net::NetLog* net_log); ProxyResolverMojo(const ProxyResolverMojo&) = delete; ProxyResolverMojo& operator=(const ProxyResolverMojo&) = delete; ~ProxyResolverMojo() override; // ProxyResolver implementation: int GetProxyForURL(const GURL& url, const net::NetworkIsolationKey& network_isolation_key, net::ProxyInfo* results, net::CompletionOnceCallback callback, std::unique_ptr* request, const net::NetLogWithSource& net_log) override; private: class Job; SEQUENCE_CHECKER(sequence_checker_); // Mojo disconnect handler. void OnMojoDisconnect(); // Connection to the Mojo proxy resolver. mojo::Remote mojo_proxy_resolver_remote_; raw_ptr host_resolver_; std::unique_ptr error_observer_; raw_ptr net_log_; }; class ProxyResolverMojo::Job : public ProxyResolver::Request, public ClientMixin { public: Job(ProxyResolverMojo* resolver, const GURL& url, const net::NetworkIsolationKey& network_isolation_key, net::ProxyInfo* results, net::CompletionOnceCallback callback, const net::NetLogWithSource& net_log); Job(const Job&) = delete; Job& operator=(const Job&) = delete; ~Job() override; // Returns the LoadState of this job. net::LoadState GetLoadState() override; private: // Mojo disconnection handler. void OnMojoDisconnect(); // Overridden from proxy_resolver::mojom::ProxyResolverRequestClient: void ReportResult(int32_t error, const net::ProxyInfo& proxy_info) override; // Completes a request with a result code. void CompleteRequest(int result); const GURL url_; raw_ptr results_; net::CompletionOnceCallback callback_; SEQUENCE_CHECKER(sequence_checker_); mojo::Receiver receiver_{ this}; }; ProxyResolverMojo::Job::Job( ProxyResolverMojo* resolver, const GURL& url, const net::NetworkIsolationKey& network_isolation_key, net::ProxyInfo* results, net::CompletionOnceCallback callback, const net::NetLogWithSource& net_log) : ClientMixin( resolver->host_resolver_, resolver->error_observer_.get(), resolver->net_log_, net_log), url_(url), results_(results), callback_(std::move(callback)) { resolver->mojo_proxy_resolver_remote_->GetProxyForUrl( url_, network_isolation_key, receiver_.BindNewPipeAndPassRemote()); receiver_.set_disconnect_handler(base::BindOnce( &ProxyResolverMojo::Job::OnMojoDisconnect, base::Unretained(this))); } ProxyResolverMojo::Job::~Job() {} net::LoadState ProxyResolverMojo::Job::GetLoadState() { return dns_request_in_progress() ? net::LOAD_STATE_RESOLVING_HOST_IN_PAC_FILE : net::LOAD_STATE_RESOLVING_PROXY_FOR_URL; } void ProxyResolverMojo::Job::OnMojoDisconnect() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "ProxyResolverMojo::Job::OnMojoDisconnect"; CompleteRequest(net::ERR_PAC_SCRIPT_TERMINATED); } void ProxyResolverMojo::Job::CompleteRequest(int result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); net::CompletionOnceCallback callback = std::move(callback_); receiver_.reset(); std::move(callback).Run(result); } void ProxyResolverMojo::Job::ReportResult(int32_t error, const net::ProxyInfo& proxy_info) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "ProxyResolverMojo::Job::ReportResult: " << error; if (error == net::OK) { *results_ = proxy_info; DVLOG(1) << "Servers: " << results_->ToPacString(); } CompleteRequest(error); } ProxyResolverMojo::ProxyResolverMojo( mojo::PendingRemote resolver_remote, net::HostResolver* host_resolver, std::unique_ptr error_observer, net::NetLog* net_log) : mojo_proxy_resolver_remote_(std::move(resolver_remote)), host_resolver_(host_resolver), error_observer_(std::move(error_observer)), net_log_(net_log) { mojo_proxy_resolver_remote_.set_disconnect_handler(base::BindOnce( &ProxyResolverMojo::OnMojoDisconnect, base::Unretained(this))); } ProxyResolverMojo::~ProxyResolverMojo() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } void ProxyResolverMojo::OnMojoDisconnect() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "ProxyResolverMojo::OnMojoDisconnect"; // Disconnect from the Mojo proxy resolver service. mojo_proxy_resolver_remote_.reset(); } int ProxyResolverMojo::GetProxyForURL( const GURL& url, const net::NetworkIsolationKey& network_isolation_key, net::ProxyInfo* results, net::CompletionOnceCallback callback, std::unique_ptr* request, const net::NetLogWithSource& net_log) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!mojo_proxy_resolver_remote_) return net::ERR_PAC_SCRIPT_TERMINATED; *request = std::make_unique(this, url, network_isolation_key, results, std::move(callback), net_log); return net::ERR_IO_PENDING; } } // namespace // A Job to create a ProxyResolver instance. // // Note: a Job instance is not tied to a particular resolve request, and hence // there is no per-request logging to be done (any netlog events are only sent // globally) so this always uses an empty NetLogWithSource. class ProxyResolverFactoryMojo::Job : public ClientMixin< proxy_resolver::mojom::ProxyResolverFactoryRequestClient>, public ProxyResolverFactory::Request { public: Job(ProxyResolverFactoryMojo* factory, const scoped_refptr& pac_script, std::unique_ptr* resolver, net::CompletionOnceCallback callback, std::unique_ptr error_observer) : ClientMixin( factory->host_resolver_, error_observer.get(), factory->net_log_, net::NetLogWithSource()), factory_(factory), resolver_(resolver), callback_(std::move(callback)), error_observer_(std::move(error_observer)) { factory_->mojo_proxy_factory_->CreateResolver( base::UTF16ToUTF8(pac_script->utf16()), resolver_remote_.InitWithNewPipeAndPassReceiver(), receiver_.BindNewPipeAndPassRemote()); receiver_.set_disconnect_handler( base::BindOnce(&ProxyResolverFactoryMojo::Job::OnMojoDisconnect, base::Unretained(this))); } void OnMojoDisconnect() { ReportResult(net::ERR_PAC_SCRIPT_TERMINATED); } private: void ReportResult(int32_t error) override { // Prevent any other messages arriving unexpectedly, in the case |this| // isn't destroyed immediately. receiver_.reset(); if (error == net::OK) { *resolver_ = std::make_unique( std::move(resolver_remote_), factory_->host_resolver_, std::move(error_observer_), factory_->net_log_); } std::move(callback_).Run(error); } const raw_ptr factory_; raw_ptr> resolver_; net::CompletionOnceCallback callback_; mojo::PendingRemote resolver_remote_; mojo::Receiver receiver_{this}; std::unique_ptr error_observer_; }; ProxyResolverFactoryMojo::ProxyResolverFactoryMojo( mojo::PendingRemote mojo_proxy_factory, net::HostResolver* host_resolver, const base::RepeatingCallback< std::unique_ptr()>& error_observer_factory, net::NetLog* net_log) : ProxyResolverFactory(true), mojo_proxy_factory_(std::move(mojo_proxy_factory)), host_resolver_(host_resolver), error_observer_factory_(error_observer_factory), net_log_(net_log) {} ProxyResolverFactoryMojo::~ProxyResolverFactoryMojo() = default; int ProxyResolverFactoryMojo::CreateProxyResolver( const scoped_refptr& pac_script, std::unique_ptr* resolver, net::CompletionOnceCallback callback, std::unique_ptr* request) { DCHECK(resolver); DCHECK(request); if (pac_script->type() != net::PacFileData::TYPE_SCRIPT_CONTENTS || pac_script->utf16().empty()) { return net::ERR_PAC_SCRIPT_FAILED; } *request = std::make_unique( this, pac_script, resolver, std::move(callback), error_observer_factory_.is_null() ? nullptr : error_observer_factory_.Run()); return net::ERR_IO_PENDING; } } // namespace network