Commit dec90199 authored by Robert Ogden's avatar Robert Ogden Committed by Commit Bot

Add TLS Socket Connection as an Origin Prober Mechanism

Creates a TLS socket connection to probe connectivity to the origin.
This probe must also complete a DNS resolution in order to complete
successfully, in order to get the IPAddress of the probe url.

Bug: 1115731
Change-Id: I862ef52d353c4572e45d8596f5809af1b0b60081
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2358490Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Reviewed-by: default avatarRyan Sturm <ryansturm@chromium.org>
Commit-Queue: Robert Ogden <robertogden@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801570}
parent 4fe98ecb
...@@ -14,16 +14,47 @@ ...@@ -14,16 +14,47 @@
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/host_port_pair.h" #include "net/base/host_port_pair.h"
#include "net/base/isolation_info.h" #include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h" #include "net/base/network_isolation_key.h"
#include "services/network/public/mojom/host_resolver.mojom.h" #include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/tls_socket.mojom.h"
#include "url/origin.h" #include "url/origin.h"
namespace { namespace {
net::NetworkTrafficAnnotationTag GetProbingTrafficAnnotation() {
return net::DefineNetworkTrafficAnnotation("isolated_prerender_probe", R"(
semantics {
sender: "Isolated Prerender Probe Loader"
description:
"Verifies the end to end connection between Chrome and the "
"origin site that the user is currently navigating to. This is "
"done during a navigation that was previously prerendered over a "
"proxy to check that the site is not blocked by middleboxes. "
"Such prerenders will be used to prefetch render-blocking "
"content before being navigated by the user without impacting "
"privacy."
trigger:
"Used for sites off of Google SRPs (Search Result Pages) only "
"for Lite mode users when the experimental feature flag is "
"enabled."
data: "None."
destination: WEBSITE
}
policy {
cookies_allowed: NO
setting:
"Users can control Lite mode on Android via the settings menu. "
"Lite mode is not available on iOS, and on desktop only for "
"developer testing."
policy_exception_justification: "Not implemented."
})");
}
class DNSProber : public network::mojom::ResolveHostClient { class DNSProber : public network::mojom::ResolveHostClient {
public: public:
using OnDNSResultsCallback = base::OnceCallback< using OnDNSResultsCallback = base::OnceCallback<
...@@ -57,6 +88,90 @@ class DNSProber : public network::mojom::ResolveHostClient { ...@@ -57,6 +88,90 @@ class DNSProber : public network::mojom::ResolveHostClient {
OnDNSResultsCallback callback_; OnDNSResultsCallback callback_;
}; };
void TLSDropHandler(base::OnceClosure ui_only_callback) {
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
std::move(ui_only_callback));
}
class TLSProber {
public:
TLSProber(const GURL& url,
IsolatedPrerenderOriginProber::OnProbeResultCallback callback)
: url_(url), callback_(std::move(callback)) {}
~TLSProber() { DCHECK(!callback_); }
network::mojom::NetworkContext::CreateTCPConnectedSocketCallback
GetOnTCPConnectedCallback() {
network::mojom::NetworkContext::CreateTCPConnectedSocketCallback
tcp_handler = base::BindOnce(&TLSProber::OnTCPConnected,
weak_factory_.GetWeakPtr());
return mojo::WrapCallbackWithDropHandler(std::move(tcp_handler),
GetDropHandler());
}
mojo::PendingReceiver<network::mojom::TCPConnectedSocket>
GetTCPSocketReceiver() {
return tcp_socket_.BindNewPipeAndPassReceiver();
}
private:
void OnTCPConnected(int result,
const base::Optional<net::IPEndPoint>& local_addr,
const base::Optional<net::IPEndPoint>& peer_addr,
mojo::ScopedDataPipeConsumerHandle receive_stream,
mojo::ScopedDataPipeProducerHandle send_stream) {
if (result != net::OK) {
HandleFailure();
return;
}
network::mojom::TCPConnectedSocket::UpgradeToTLSCallback tls_handler =
base::BindOnce(&TLSProber::OnUpgradeToTLS, weak_factory_.GetWeakPtr());
tcp_socket_->UpgradeToTLS(
net::HostPortPair::FromURL(url_), /*options=*/nullptr,
net::MutableNetworkTrafficAnnotationTag(GetProbingTrafficAnnotation()),
tls_socket_.BindNewPipeAndPassReceiver(),
/*observer=*/mojo::NullRemote(),
mojo::WrapCallbackWithDropHandler(std::move(tls_handler),
GetDropHandler()));
}
void OnUpgradeToTLS(int result,
mojo::ScopedDataPipeConsumerHandle receive_stream,
mojo::ScopedDataPipeProducerHandle send_stream,
const base::Optional<net::SSLInfo>& ssl_info) {
std::move(callback_).Run(result == net::OK);
delete this;
}
base::OnceClosure GetDropHandler() {
// The drop handler is not guaranteed to be run on the original thread. Use
// the anon method above to fix that.
return base::BindOnce(
&TLSDropHandler,
base::BindOnce(&TLSProber::HandleFailure, weak_factory_.GetWeakPtr()));
}
void HandleFailure() {
std::move(callback_).Run(false);
delete this;
}
// The URL of the resource being probed. Only the host:port is used.
const GURL url_;
// The callback to run when the probe is complete.
IsolatedPrerenderOriginProber::OnProbeResultCallback callback_;
// Mojo sockets. We only test that both can be connected.
mojo::Remote<network::mojom::TCPConnectedSocket> tcp_socket_;
mojo::Remote<network::mojom::TLSClientSocket> tls_socket_;
base::WeakPtrFactory<TLSProber> weak_factory_{this};
};
void HTTPProbeHelper( void HTTPProbeHelper(
std::unique_ptr<AvailabilityProber> prober, std::unique_ptr<AvailabilityProber> prober,
IsolatedPrerenderOriginProber::OnProbeResultCallback callback, IsolatedPrerenderOriginProber::OnProbeResultCallback callback,
...@@ -220,6 +335,9 @@ void IsolatedPrerenderOriginProber::Probe(const GURL& url, ...@@ -220,6 +335,9 @@ void IsolatedPrerenderOriginProber::Probe(const GURL& url,
case IsolatedPrerenderOriginProbeType::kHttpHead: case IsolatedPrerenderOriginProbeType::kHttpHead:
HTTPProbe(probe_url, std::move(callback)); HTTPProbe(probe_url, std::move(callback));
return; return;
case IsolatedPrerenderOriginProbeType::kTls:
TLSProbe(probe_url, std::move(callback));
return;
} }
} }
...@@ -228,6 +346,11 @@ void IsolatedPrerenderOriginProber::DNSProbe(const GURL& url, ...@@ -228,6 +346,11 @@ void IsolatedPrerenderOriginProber::DNSProbe(const GURL& url,
StartDNSResolution(url, std::move(callback), /*also_do_tls_connect=*/false); StartDNSResolution(url, std::move(callback), /*also_do_tls_connect=*/false);
} }
void IsolatedPrerenderOriginProber::TLSProbe(const GURL& url,
OnProbeResultCallback callback) {
StartDNSResolution(url, std::move(callback), /*also_do_tls_connect=*/true);
}
void IsolatedPrerenderOriginProber::StartDNSResolution( void IsolatedPrerenderOriginProber::StartDNSResolution(
const GURL& url, const GURL& url,
OnProbeResultCallback callback, OnProbeResultCallback callback,
...@@ -257,34 +380,6 @@ void IsolatedPrerenderOriginProber::StartDNSResolution( ...@@ -257,34 +380,6 @@ void IsolatedPrerenderOriginProber::StartDNSResolution(
void IsolatedPrerenderOriginProber::HTTPProbe(const GURL& url, void IsolatedPrerenderOriginProber::HTTPProbe(const GURL& url,
OnProbeResultCallback callback) { OnProbeResultCallback callback) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("isolated_prerender_probe", R"(
semantics {
sender: "Isolated Prerender Probe Loader"
description:
"Verifies the end to end connection between Chrome and the "
"origin site that the user is currently navigating to. This is "
"done during a navigation that was previously prerendered over a "
"proxy to check that the site is not blocked by middleboxes. "
"Such prerenders will be used to prefetch render-blocking "
"content before being navigated by the user without impacting "
"privacy."
trigger:
"Used for sites off of Google SRPs (Search Result Pages) only "
"for Lite mode users when the experimental feature flag is "
"enabled."
data: "None."
destination: WEBSITE
}
policy {
cookies_allowed: NO
setting:
"Users can control Lite mode on Android via the settings menu. "
"Lite mode is not available on iOS, and on desktop only for "
"developer testing."
policy_exception_justification: "Not implemented."
})");
AvailabilityProber::TimeoutPolicy timeout_policy; AvailabilityProber::TimeoutPolicy timeout_policy;
timeout_policy.base_timeout = IsolatedPrerenderProbeTimeout(); timeout_policy.base_timeout = IsolatedPrerenderProbeTimeout();
AvailabilityProber::RetryPolicy retry_policy; AvailabilityProber::RetryPolicy retry_policy;
...@@ -298,7 +393,7 @@ void IsolatedPrerenderOriginProber::HTTPProbe(const GURL& url, ...@@ -298,7 +393,7 @@ void IsolatedPrerenderOriginProber::HTTPProbe(const GURL& url,
nullptr /* pref_service */, nullptr /* pref_service */,
AvailabilityProber::ClientName::kIsolatedPrerenderOriginCheck, url, AvailabilityProber::ClientName::kIsolatedPrerenderOriginCheck, url,
AvailabilityProber::HttpMethod::kHead, net::HttpRequestHeaders(), AvailabilityProber::HttpMethod::kHead, net::HttpRequestHeaders(),
retry_policy, timeout_policy, traffic_annotation, retry_policy, timeout_policy, GetProbingTrafficAnnotation(),
0 /* max_cache_entries */, 0 /* max_cache_entries */,
base::TimeDelta::FromSeconds(0) /* revalidate_cache_after */); base::TimeDelta::FromSeconds(0) /* revalidate_cache_after */);
AvailabilityProber* prober_ptr = prober.get(); AvailabilityProber* prober_ptr = prober.get();
...@@ -332,6 +427,28 @@ void IsolatedPrerenderOriginProber::OnDNSResolved( ...@@ -332,6 +427,28 @@ void IsolatedPrerenderOriginProber::OnDNSResolved(
return; return;
} }
// TODO(robertogden): Handle also_do_tls_connect. DoTLSProbeAfterDNSResolution(url, std::move(callback), *resolved_addresses);
NOTREACHED(); }
void IsolatedPrerenderOriginProber::DoTLSProbeAfterDNSResolution(
const GURL& url,
OnProbeResultCallback callback,
const net::AddressList& addresses) {
DCHECK(!addresses.empty());
std::unique_ptr<TLSProber> prober =
std::make_unique<TLSProber>(url, std::move(callback));
content::BrowserContext::GetDefaultStoragePartition(profile_)
->GetNetworkContext()
->CreateTCPConnectedSocket(
/*local_addr=*/base::nullopt, addresses,
/*tcp_connected_socket_options=*/nullptr,
net::MutableNetworkTrafficAnnotationTag(
GetProbingTrafficAnnotation()),
prober->GetTCPSocketReceiver(),
/*observer=*/mojo::NullRemote(), prober->GetOnTCPConnectedCallback());
// |prober| manages its own lifetime, using the mojo pipes.
prober.release();
} }
...@@ -48,6 +48,7 @@ class IsolatedPrerenderOriginProber { ...@@ -48,6 +48,7 @@ class IsolatedPrerenderOriginProber {
private: private:
void DNSProbe(const GURL& url, OnProbeResultCallback callback); void DNSProbe(const GURL& url, OnProbeResultCallback callback);
void HTTPProbe(const GURL& url, OnProbeResultCallback callback); void HTTPProbe(const GURL& url, OnProbeResultCallback callback);
void TLSProbe(const GURL& url, OnProbeResultCallback callback);
// Does a DNS resolution for a DNS or TLS probe, passing all the arguments to // Does a DNS resolution for a DNS or TLS probe, passing all the arguments to
// |OnDNSResolved|. // |OnDNSResolved|.
...@@ -55,6 +56,12 @@ class IsolatedPrerenderOriginProber { ...@@ -55,6 +56,12 @@ class IsolatedPrerenderOriginProber {
OnProbeResultCallback callback, OnProbeResultCallback callback,
bool also_do_tls_connect); bool also_do_tls_connect);
// Both DNS and TLS probes need to resolve DNS. This starts the TLS probe with
// the |addresses| from the DNS resolution.
void DoTLSProbeAfterDNSResolution(const GURL& url,
OnProbeResultCallback callback,
const net::AddressList& addresses);
// If the DNS resolution was successful, this will either run |callback| for a // If the DNS resolution was successful, this will either run |callback| for a
// DNS probe, or start the TLS socket for a TLS probe. This is determined by // DNS probe, or start the TLS socket for a TLS probe. This is determined by
// |also_do_tls_connect|. If the DNS resolution failed, |callback| is run with // |also_do_tls_connect|. If the DNS resolution failed, |callback| is run with
......
...@@ -131,6 +131,9 @@ IsolatedPrerenderOriginProbeType IsolatedPrerenderOriginProbeMechanism() { ...@@ -131,6 +131,9 @@ IsolatedPrerenderOriginProbeType IsolatedPrerenderOriginProbeMechanism() {
return IsolatedPrerenderOriginProbeType::kDns; return IsolatedPrerenderOriginProbeType::kDns;
if (param == "http_head") if (param == "http_head")
return IsolatedPrerenderOriginProbeType::kHttpHead; return IsolatedPrerenderOriginProbeType::kHttpHead;
if (param == "tls")
return IsolatedPrerenderOriginProbeType::kTls;
// Most restrictive by default.
return IsolatedPrerenderOriginProbeType::kHttpHead; return IsolatedPrerenderOriginProbeType::kHttpHead;
} }
...@@ -65,6 +65,7 @@ base::TimeDelta IsolatedPrerenderCanaryCheckCacheLifetime(); ...@@ -65,6 +65,7 @@ base::TimeDelta IsolatedPrerenderCanaryCheckCacheLifetime();
enum class IsolatedPrerenderOriginProbeType { enum class IsolatedPrerenderOriginProbeType {
kDns, kDns,
kHttpHead, kHttpHead,
kTls,
}; };
IsolatedPrerenderOriginProbeType IsolatedPrerenderOriginProbeMechanism(); IsolatedPrerenderOriginProbeType IsolatedPrerenderOriginProbeMechanism();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment