| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/direct_sockets/direct_sockets_service_impl.h" |
| |
| #include "base/bind.h" |
| #include "base/callback_forward.h" |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "content/browser/direct_sockets/direct_udp_socket_impl.h" |
| #include "content/browser/direct_sockets/resolve_host_and_open_socket.h" |
| #include "content/browser/renderer_host/frame_tree_node.h" |
| #include "content/browser/url_loader_factory_params_helper.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/direct_sockets_delegate.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_features.h" |
| #include "mojo/public/cpp/bindings/message.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "mojo/public/cpp/system/data_pipe.h" |
| #include "net/base/address_list.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/net_buildflags.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "services/network/public/cpp/resolve_host_client_base.h" |
| #include "services/network/public/cpp/simple_url_loader.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "services/network/public/mojom/tcp_socket.mojom.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom-shared.h" |
| #include "url/url_constants.h" |
| |
| using blink::mojom::DirectSocketFailureType; |
| |
| namespace content { |
| |
| namespace { |
| |
| constexpr net::NetworkTrafficAnnotationTag kDirectSocketsTrafficAnnotation = |
| net::DefineNetworkTrafficAnnotation("direct_sockets", R"( |
| semantics { |
| sender: "Direct Sockets API" |
| description: "Web app request to communicate with network device" |
| trigger: "User completes network connection dialog" |
| data: "Any data sent by web app" |
| destination: OTHER |
| destination_other: "Address entered by user in connection dialog" |
| } |
| policy { |
| cookies_allowed: NO |
| setting: "This feature cannot yet be controlled by settings." |
| policy_exception_justification: "To be implemented" |
| } |
| )"); |
| |
| constexpr int32_t kMaxBufferSize = 32 * 1024 * 1024; |
| |
| network::mojom::NetworkContext*& GetNetworkContextForTesting() { |
| static network::mojom::NetworkContext* network_context = nullptr; |
| return network_context; |
| } |
| |
| bool IsFrameSufficientlyIsolated(content::RenderFrameHost* frame) { |
| return frame->GetWebExposedIsolationLevel() >= |
| content::RenderFrameHost::WebExposedIsolationLevel:: |
| kMaybeIsolatedApplication; |
| } |
| |
| } // namespace |
| |
| DirectSocketsServiceImpl::DirectSocketsServiceImpl(RenderFrameHost& frame_host) |
| : WebContentsObserver(WebContents::FromRenderFrameHost(&frame_host)), |
| frame_host_(&frame_host) {} |
| |
| DirectSocketsServiceImpl::~DirectSocketsServiceImpl() = default; |
| |
| // static |
| void DirectSocketsServiceImpl::CreateForFrame( |
| RenderFrameHost* frame, |
| mojo::PendingReceiver<blink::mojom::DirectSocketsService> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!IsFrameSufficientlyIsolated(frame)) { |
| return; |
| } |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<DirectSocketsServiceImpl>(*frame), std::move(receiver)); |
| } |
| |
| content::DirectSocketsDelegate* DirectSocketsServiceImpl::GetDelegate() { |
| return GetContentClient()->browser()->GetDirectSocketsDelegate(); |
| } |
| |
| void DirectSocketsServiceImpl::OpenTcpSocket( |
| blink::mojom::DirectSocketOptionsPtr options, |
| mojo::PendingReceiver<network::mojom::TCPConnectedSocket> receiver, |
| mojo::PendingRemote<network::mojom::SocketObserver> observer, |
| OpenTcpSocketCallback callback) { |
| if (!IsFrameSufficientlyIsolated(frame_host_)) { |
| mojo::ReportBadMessage("Insufficient isolation to open socket"); |
| return; |
| } |
| |
| if (!GetNetworkContext()) { |
| mojo::ReportBadMessage("Invalid request to open socket"); |
| return; |
| } |
| |
| if (auto* delegate = GetDelegate(); |
| delegate && |
| !delegate->ValidateAddressAndPort( |
| frame_host_, options->remote_hostname, options->remote_port, |
| blink::mojom::DirectSocketProtocolType::kTcp)) { |
| std::move(callback).Run(net::ERR_ACCESS_DENIED, absl::nullopt, |
| absl::nullopt, mojo::ScopedDataPipeConsumerHandle(), |
| mojo::ScopedDataPipeProducerHandle()); |
| return; |
| } |
| |
| ResolveHostAndOpenSocket* resolver = new ResolveHostAndOpenTCPSocket( |
| weak_ptr_factory_.GetWeakPtr(), std::move(options), std::move(receiver), |
| std::move(observer), std::move(callback)); |
| resolver->Start(); |
| } |
| |
| void DirectSocketsServiceImpl::OpenUdpSocket( |
| blink::mojom::DirectSocketOptionsPtr options, |
| mojo::PendingReceiver<blink::mojom::DirectUDPSocket> receiver, |
| mojo::PendingRemote<network::mojom::UDPSocketListener> listener, |
| OpenUdpSocketCallback callback) { |
| if (!IsFrameSufficientlyIsolated(frame_host_)) { |
| mojo::ReportBadMessage("Insufficient isolation to open socket"); |
| return; |
| } |
| |
| if (!GetNetworkContext()) { |
| mojo::ReportBadMessage("Invalid request to open socket"); |
| return; |
| } |
| |
| if (auto* delegate = GetDelegate(); |
| delegate && |
| !delegate->ValidateAddressAndPort( |
| frame_host_, options->remote_hostname, options->remote_port, |
| blink::mojom::DirectSocketProtocolType::kUdp)) { |
| std::move(callback).Run(net::ERR_ACCESS_DENIED, absl::nullopt, |
| absl::nullopt); |
| return; |
| } |
| |
| ResolveHostAndOpenSocket* resolver = new ResolveHostAndOpenUDPSocket( |
| weak_ptr_factory_.GetWeakPtr(), std::move(options), std::move(receiver), |
| std::move(listener), std::move(callback)); |
| resolver->Start(); |
| } |
| |
| // static |
| net::MutableNetworkTrafficAnnotationTag |
| DirectSocketsServiceImpl::MutableTrafficAnnotation() { |
| return net::MutableNetworkTrafficAnnotationTag( |
| kDirectSocketsTrafficAnnotation); |
| } |
| |
| // static |
| net::NetworkTrafficAnnotationTag DirectSocketsServiceImpl::TrafficAnnotation() { |
| return kDirectSocketsTrafficAnnotation; |
| } |
| |
| // static |
| int32_t DirectSocketsServiceImpl::GetMaxBufferSize() { |
| return kMaxBufferSize; |
| } |
| |
| // static |
| void DirectSocketsServiceImpl::SetNetworkContextForTesting( |
| network::mojom::NetworkContext* network_context) { |
| GetNetworkContextForTesting() = network_context; |
| } |
| |
| // static |
| absl::optional<net::IPEndPoint> |
| DirectSocketsServiceImpl::GetLocalAddrForTesting( |
| const blink::mojom::DirectSocketOptions& options) { |
| if (!options.local_hostname) { |
| return {}; |
| } |
| if (net::IPAddress address; |
| address.AssignFromIPLiteral(*options.local_hostname)) { |
| return net::IPEndPoint(std::move(address), options.local_port); |
| } |
| return {}; |
| } |
| |
| void DirectSocketsServiceImpl::RenderFrameDeleted( |
| RenderFrameHost* render_frame_host) { |
| if (render_frame_host == frame_host_) |
| frame_host_ = nullptr; |
| } |
| |
| void DirectSocketsServiceImpl::WebContentsDestroyed() { |
| frame_host_ = nullptr; |
| } |
| |
| network::mojom::NetworkContext* DirectSocketsServiceImpl::GetNetworkContext() { |
| if (GetNetworkContextForTesting()) |
| return GetNetworkContextForTesting(); |
| |
| if (!frame_host_) |
| return nullptr; |
| |
| return frame_host_->GetStoragePartition()->GetNetworkContext(); |
| } |
| |
| RenderFrameHost* DirectSocketsServiceImpl::GetFrameHost() { |
| return frame_host_; |
| } |
| |
| void DirectSocketsServiceImpl::AddDirectUDPSocketReceiver( |
| std::unique_ptr<DirectUDPSocketImpl> socket, |
| mojo::PendingReceiver<blink::mojom::DirectUDPSocket> receiver) { |
| direct_udp_socket_receivers_.Add(std::move(socket), std::move(receiver)); |
| } |
| |
| } // namespace content |