blob: 26ad9ad3a86565ec2f85ea4245ced6cd9b9e6be3 [file] [log] [blame]
// Copyright 2019 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 "android_webview/browser/aw_pac_processor.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <cstddef>
#include <memory>
#include <string>
#include "android_webview/browser_jni_headers/AwPacProcessor_jni.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/thread_restrictions.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_errors.h"
#include "net/base/network_isolation_key.h"
#include "net/proxy_resolution/pac_file_data.h"
#include "net/proxy_resolution/proxy_info.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
class NetworkIsolationKey;
namespace android_webview {
namespace {
// TODO(amalova): We could use a separate thread or thread pool for executing
// blocking DNS queries, to get better performance.
class HostResolver : public proxy_resolver::ProxyHostResolver {
class RequestImpl : public Request {
public:
RequestImpl(const std::string& hostname,
net::ProxyResolveDnsOperation operation)
: hostname_(hostname), operation_(operation) {}
~RequestImpl() override = default;
int Start(net::CompletionOnceCallback callback) override {
bool success = false;
switch (operation_) {
case net::ProxyResolveDnsOperation::DNS_RESOLVE:
success = DnsResolveImpl(hostname_);
break;
case net::ProxyResolveDnsOperation::DNS_RESOLVE_EX:
success = DnsResolveExImpl(hostname_);
break;
case net::ProxyResolveDnsOperation::MY_IP_ADDRESS:
success = MyIpAddressImpl();
break;
case net::ProxyResolveDnsOperation::MY_IP_ADDRESS_EX:
success = MyIpAddressExImpl();
break;
}
return success ? net::OK : net::ERR_NAME_RESOLUTION_FAILED;
}
const std::vector<net::IPAddress>& GetResults() const override {
return results_;
}
private:
bool MyIpAddressImpl() {
std::string my_hostname = GetHostName();
if (my_hostname.empty())
return false;
return DnsResolveImpl(my_hostname);
}
bool MyIpAddressExImpl() {
std::string my_hostname = GetHostName();
if (my_hostname.empty())
return false;
return DnsResolveExImpl(my_hostname);
}
net::IPAddress StringToIPAddress(const std::string& address) {
net::IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(std::string(address))) {
LOG(ERROR) << "Not a supported IP literal: " << std::string(address);
}
return ip_address;
}
bool DnsResolveImpl(const std::string& host) {
struct hostent* he = gethostbyname(host.c_str());
// TODO(amalova): handle IPv6 (AF_INET6)
if (he == nullptr || he->h_addr == nullptr || he->h_addrtype != AF_INET) {
return false;
}
char tmp[INET_ADDRSTRLEN];
if (inet_ntop(he->h_addrtype, he->h_addr, tmp, sizeof(tmp)) == nullptr) {
return false;
}
results_.push_back(StringToIPAddress(std::string(tmp)));
return true;
}
bool DnsResolveExImpl(const std::string& host) {
struct hostent* he = gethostbyname(host.c_str());
// TODO(amalova): handle IPv6 (AF_INET6)
if (he == nullptr || he->h_addr_list == nullptr ||
he->h_addrtype != AF_INET) {
return false;
}
char tmp[INET_ADDRSTRLEN];
for (char** addr = he->h_addr_list; *addr != nullptr; ++addr) {
if (inet_ntop(he->h_addrtype, *addr, tmp, sizeof(tmp)) == nullptr) {
return false;
}
results_.push_back(StringToIPAddress(std::string(tmp)));
}
return true;
}
std::string GetHostName() {
char buffer[HOST_NAME_MAX + 1];
if (gethostname(buffer, HOST_NAME_MAX + 1) != 0) {
return std::string();
}
// It's unspecified whether gethostname NULL-terminates if the hostname
// must be truncated and no error is returned if that happens.
buffer[HOST_NAME_MAX] = '\0';
return std::string(buffer);
}
const std::string hostname_;
const net::ProxyResolveDnsOperation operation_;
std::vector<net::IPAddress> results_;
};
public:
HostResolver() = default;
std::unique_ptr<Request> CreateRequest(
const std::string& hostname,
net::ProxyResolveDnsOperation operation,
const net::NetworkIsolationKey&) override {
return std::make_unique<RequestImpl>(hostname, operation);
}
};
class Bindings : public proxy_resolver::ProxyResolverV8Tracing::Bindings {
public:
void Alert(const base::string16& message) override {}
void OnError(int line_number, const base::string16& message) override {}
proxy_resolver::ProxyHostResolver* GetHostResolver() override {
return AwPacProcessor::Get()->host_resolver();
}
net::NetLogWithSource GetNetLogWithSource() override {
return net::NetLogWithSource();
}
};
} // namespace
class Job {
public:
virtual ~Job() = default;
bool ExecSync() {
AwPacProcessor::Get()->task_runner_->PostTask(FROM_HERE, std::move(task_));
event_.Wait();
return net_error_ == net::OK;
}
virtual void Cancel() = 0;
void OnSignal(int net_error) {
net_error_ = net_error;
// Both SetProxyScriptJob#request_ and MakeProxyRequestJob#request_
// have to be destroyed on the same thread on which they are created.
// If we destroy them before callback is called the request_ is cancelled.
// Reset them here on the correct thread when the job is already finished
// so no cancellation occurs.
Cancel();
event_.Signal();
}
base::OnceClosure task_;
int net_error_;
base::WaitableEvent event_;
};
class SetProxyScriptJob : public Job {
public:
SetProxyScriptJob(std::string script) {
task_ = base::BindOnce(
&AwPacProcessor::SetProxyScriptNative,
base::Unretained(AwPacProcessor::Get()), &request_, std::move(script),
base::BindOnce(&SetProxyScriptJob::OnSignal, base::Unretained(this)));
}
void Cancel() override { request_.reset(); }
private:
std::unique_ptr<net::ProxyResolverFactory::Request> request_;
};
class MakeProxyRequestJob : public Job {
public:
MakeProxyRequestJob(std::string url) {
task_ = base::BindOnce(
&AwPacProcessor::MakeProxyRequestNative,
base::Unretained(AwPacProcessor::Get()), &request_, std::move(url),
&proxy_info_,
base::BindOnce(&MakeProxyRequestJob::OnSignal, base::Unretained(this)));
}
void Cancel() override { request_.reset(); }
net::ProxyInfo proxy_info() { return proxy_info_; }
private:
net::ProxyInfo proxy_info_;
std::unique_ptr<net::ProxyResolver::Request> request_;
};
// static
AwPacProcessor* AwPacProcessor::Get() {
static base::NoDestructor<AwPacProcessor> instance;
return instance.get();
}
AwPacProcessor::AwPacProcessor() : thread_("AwPacResolver") {
thread_.Start();
task_runner_ = thread_.task_runner();
proxy_resolver_factory_ =
proxy_resolver::ProxyResolverV8TracingFactory::Create();
host_resolver_ = std::make_unique<HostResolver>();
}
AwPacProcessor::~AwPacProcessor() = default;
void AwPacProcessor::SetProxyScriptNative(
std::unique_ptr<net::ProxyResolverFactory::Request>* request,
const std::string& script,
net::CompletionOnceCallback complete) {
DCHECK(task_runner_->BelongsToCurrentThread());
proxy_resolver_factory_->CreateProxyResolverV8Tracing(
net::PacFileData::FromUTF8(script), std::make_unique<Bindings>(),
&proxy_resolver_, std::move(complete), request);
}
void AwPacProcessor::MakeProxyRequestNative(
std::unique_ptr<net::ProxyResolver::Request>* request,
const std::string& url,
net::ProxyInfo* proxy_info,
net::CompletionOnceCallback complete) {
DCHECK(task_runner_->BelongsToCurrentThread());
if (proxy_resolver_) {
proxy_resolver_->GetProxyForURL(GURL(url), net::NetworkIsolationKey(),
proxy_info, std::move(complete), request,
std::make_unique<Bindings>());
} else {
std::move(complete).Run(net::ERR_FAILED);
}
}
bool AwPacProcessor::SetProxyScript(std::string script) {
SetProxyScriptJob job(script);
bool success = job.ExecSync();
DCHECK(proxy_resolver_);
return success;
}
jboolean AwPacProcessor::SetProxyScript(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jstring>& jscript) {
std::string script = ConvertJavaStringToUTF8(env, jscript);
return SetProxyScript(script);
}
std::string AwPacProcessor::MakeProxyRequest(std::string url) {
MakeProxyRequestJob job(url);
bool success = job.ExecSync();
return success ? job.proxy_info().ToPacString() : nullptr;
}
ScopedJavaLocalRef<jstring> AwPacProcessor::MakeProxyRequest(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jstring>& jurl) {
std::string url = ConvertJavaStringToUTF8(env, jurl);
return ConvertUTF8ToJavaString(env, MakeProxyRequest(url));
}
static jlong JNI_AwPacProcessor_GetDefaultPacProcessor(JNIEnv* env) {
return reinterpret_cast<intptr_t>(AwPacProcessor::Get());
}
static void JNI_AwPacProcessor_InitializeEnvironment(JNIEnv* env) {
base::ThreadPoolInstance::CreateAndStartWithDefaultParams("AwPacProcessor");
}
} // namespace android_webview