blob: b510d2a9bb4e86d975364787869672bbfba51856 [file] [log] [blame]
// Copyright 2020 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 "chrome/browser/sharing/webrtc/sharing_webrtc_connection_host.h"
#include <memory>
#include "base/callback.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "chrome/browser/sharing/sharing_constants.h"
#include "chrome/browser/sharing/sharing_handler_registry.h"
#include "chrome/browser/sharing/sharing_message_handler.h"
#include "chrome/browser/sharing/sharing_metrics.h"
#include "chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.h"
#include "chrome/services/sharing/public/cpp/sharing_webrtc_metrics.h"
#include "components/gcm_driver/crypto/gcm_decryption_result.h"
#include "components/gcm_driver/crypto/gcm_encryption_result.h"
#include "components/gcm_driver/gcm_driver.h"
namespace {
bool IsValidSharingWebRtcPayloadCase(
chrome_browser_sharing::SharingMessage::PayloadCase payload_case) {
// WebRTC signalling messages should only be received via FCM.
return payload_case != chrome_browser_sharing::SharingMessage::
kPeerConnectionOfferMessage &&
payload_case != chrome_browser_sharing::SharingMessage::
kPeerConnectionIceCandidatesMessage;
}
} // namespace
SharingWebRtcConnectionHost::SharingWebRtcConnectionHost(
std::unique_ptr<WebRtcSignallingHostFCM> signalling_host,
SharingHandlerRegistry* handler_registry,
gcm::GCMDriver* gcm_driver,
EncryptionInfo encryption_info,
base::OnceClosure on_closed,
mojo::PendingReceiver<sharing::mojom::SharingWebRtcConnectionDelegate>
delegate,
mojo::PendingRemote<sharing::mojom::SharingWebRtcConnection> connection,
mojo::PendingReceiver<network::mojom::P2PTrustedSocketManagerClient>
socket_manager_client,
mojo::PendingRemote<network::mojom::P2PTrustedSocketManager> socket_manager)
: signalling_host_(std::move(signalling_host)),
handler_registry_(handler_registry),
gcm_driver_(gcm_driver),
encryption_info_(std::move(encryption_info)),
on_closed_(std::move(on_closed)),
delegate_(this, std::move(delegate)),
connection_(std::move(connection)),
socket_manager_client_(this, std::move(socket_manager_client)),
socket_manager_(std::move(socket_manager)),
timeout_state_(sharing::WebRtcTimeoutState::kConnecting),
timeout_timer_(FROM_HERE,
kSharingWebRtcTimeout,
this,
&SharingWebRtcConnectionHost::OnConnectionTimeout) {
delegate_.set_disconnect_handler(
base::BindOnce(&SharingWebRtcConnectionHost::OnConnectionClosing,
weak_ptr_factory_.GetWeakPtr()));
connection_.set_disconnect_handler(
base::BindOnce(&SharingWebRtcConnectionHost::OnConnectionClosing,
weak_ptr_factory_.GetWeakPtr()));
socket_manager_client_.set_disconnect_handler(
base::BindOnce(&SharingWebRtcConnectionHost::OnConnectionClosed,
weak_ptr_factory_.GetWeakPtr()));
socket_manager_.set_disconnect_handler(
base::BindOnce(&SharingWebRtcConnectionHost::OnConnectionClosed,
weak_ptr_factory_.GetWeakPtr()));
timeout_timer_.Reset();
}
SharingWebRtcConnectionHost::~SharingWebRtcConnectionHost() = default;
void SharingWebRtcConnectionHost::OnMessageReceived(
const std::vector<uint8_t>& message) {
// TODO(crbug.com/1045408): hook this up to a fuzzer.
// TODO(crbug.com/1046333): consider a different mojo interface to avoid copy
std::string raw_data(message.data(), message.data() + message.size());
gcm_driver_->DecryptMessage(
kSharingFCMAppID, encryption_info_.authorized_entity, raw_data,
base::BindOnce(&SharingWebRtcConnectionHost::OnMessageDecrypted,
weak_ptr_factory_.GetWeakPtr()));
}
void SharingWebRtcConnectionHost::OnMessageDecrypted(
gcm::GCMDecryptionResult result,
std::string message) {
chrome_browser_sharing::WebRtcMessage sharing_message;
if (result != gcm::GCMDecryptionResult::DECRYPTED_DRAFT_08) {
LogSharingWebRtcOnMessageReceivedResult(
sharing::WebRtcOnMessageReceivedResult::kDecryptionFailed);
return;
}
if (!sharing_message.ParseFromString(message)) {
LogSharingWebRtcOnMessageReceivedResult(
sharing::WebRtcOnMessageReceivedResult::kParseFailed);
return;
}
auto payload_case = sharing_message.message().payload_case();
if (!IsValidSharingWebRtcPayloadCase(payload_case)) {
LogSharingWebRtcOnMessageReceivedResult(
sharing::WebRtcOnMessageReceivedResult::kInvalidPayload);
return;
}
auto* handler = handler_registry_->GetSharingHandler(payload_case);
if (!handler) {
LogSharingWebRtcOnMessageReceivedResult(
sharing::WebRtcOnMessageReceivedResult::kHandlerNotFound);
return;
}
LogSharingWebRtcOnMessageReceivedResult(
sharing::WebRtcOnMessageReceivedResult::kSuccess);
timeout_state_ = sharing::WebRtcTimeoutState::kMessageReceived;
timeout_timer_.Reset();
std::string original_message_id = sharing_message.message_guid();
chrome_browser_sharing::MessageType original_message_type =
SharingPayloadCaseToMessageType(payload_case);
handler->OnMessage(
std::move(sharing_message.message()),
base::BindOnce(&SharingWebRtcConnectionHost::OnMessageHandled,
weak_ptr_factory_.GetWeakPtr(), original_message_id,
original_message_type));
}
void SharingWebRtcConnectionHost::OnMessageHandled(
const std::string& original_message_id,
chrome_browser_sharing::MessageType original_message_type,
std::unique_ptr<chrome_browser_sharing::ResponseMessage> response) {
if (original_message_type ==
chrome_browser_sharing::MessageType::ACK_MESSAGE) {
OnConnectionClosing();
return;
}
chrome_browser_sharing::WebRtcMessage message;
auto* sharing_message = message.mutable_message();
auto* ack_message = sharing_message->mutable_ack_message();
ack_message->set_original_message_id(original_message_id);
if (response)
ack_message->set_allocated_response_message(response.release());
SendMessage(std::move(message),
base::BindOnce(&SharingWebRtcConnectionHost::OnAckSent,
weak_ptr_factory_.GetWeakPtr()));
}
void SharingWebRtcConnectionHost::OnAckSent(
sharing::mojom::SendMessageResult result) {
OnConnectionClosing();
}
void SharingWebRtcConnectionHost::OnConnectionClosing() {
timeout_state_ = sharing::WebRtcTimeoutState::kDisconnecting;
timeout_timer_.Reset();
connection_.reset();
delegate_.reset();
}
void SharingWebRtcConnectionHost::OnConnectionClosed() {
if (on_closed_)
std::move(on_closed_).Run();
}
void SharingWebRtcConnectionHost::OnConnectionTimeout() {
sharing::LogWebRtcTimeout(timeout_state_);
OnConnectionClosing();
OnConnectionClosed();
}
void SharingWebRtcConnectionHost::SendMessage(
chrome_browser_sharing::WebRtcMessage message,
sharing::mojom::SharingWebRtcConnection::SendMessageCallback callback) {
std::string serialized_message;
if (!message.SerializeToString(&serialized_message)) {
std::move(callback).Run(sharing::mojom::SendMessageResult::kError);
return;
}
gcm_driver_->EncryptMessage(
kSharingFCMAppID, encryption_info_.authorized_entity,
encryption_info_.p256dh, encryption_info_.auth_secret, serialized_message,
base::BindOnce(&SharingWebRtcConnectionHost::OnMessageEncrypted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void SharingWebRtcConnectionHost::OnMessageEncrypted(
sharing::mojom::SharingWebRtcConnection::SendMessageCallback callback,
gcm::GCMEncryptionResult result,
std::string message) {
if (result != gcm::GCMEncryptionResult::ENCRYPTED_DRAFT_08) {
std::move(callback).Run(sharing::mojom::SendMessageResult::kError);
return;
}
timeout_state_ = sharing::WebRtcTimeoutState::kMessageSent;
timeout_timer_.Reset();
// TODO(crbug.com/1045406): encrypt |serialized_message|.
// TODO(crbug.com/1046333): consider a different mojo interface to avoid copy
std::vector<uint8_t> serialized_message(message.begin(), message.end());
connection_->SendMessage(serialized_message, std::move(callback));
}
void SharingWebRtcConnectionHost::OnOfferReceived(
const std::string& offer,
base::OnceCallback<void(const std::string&)> callback) {
signalling_host_->OnOfferReceived(offer, std::move(callback));
}
void SharingWebRtcConnectionHost::OnIceCandidatesReceived(
std::vector<sharing::mojom::IceCandidatePtr> ice_candidates) {
signalling_host_->OnIceCandidatesReceived(std::move(ice_candidates));
}
void SharingWebRtcConnectionHost::InvalidSocketPortRangeRequested() {
// TODO(crbug.com/1021984): Add metrics for this.
}
void SharingWebRtcConnectionHost::DumpPacket(
const std::vector<uint8_t>& packet_header,
uint64_t packet_length,
bool incoming) {
NOTIMPLEMENTED();
}