diff options
Diffstat (limited to 'chromium/components/cast_channel')
24 files changed, 723 insertions, 181 deletions
diff --git a/chromium/components/cast_channel/BUILD.gn b/chromium/components/cast_channel/BUILD.gn index 55b42ba169a..a5a972e2d6e 100644 --- a/chromium/components/cast_channel/BUILD.gn +++ b/chromium/components/cast_channel/BUILD.gn @@ -101,8 +101,11 @@ source_set("unit_tests") { ] } +# TODO(jrw): Rename target to cast_framer_ingest_fuzzer. The name +# is left unchanged for now to avoid the need to get reviews for +# various files that include it. fuzzer_test("cast_message_fuzzer") { - sources = [ "cast_message_fuzzer.cc" ] + sources = [ "cast_framer_ingest_fuzzer.cc" ] deps = [ ":test_support", "//base", @@ -115,3 +118,36 @@ fuzzer_test("cast_message_fuzzer") { # See MessageFramer::MessageHeader::max_message_size() libfuzzer_options = [ "max_len=65535" ] } + +fuzzer_test("cast_auth_util_fuzzer") { + sources = [ "cast_auth_util_fuzzer.cc" ] + dict = "fuzz.dict" + deps = [ + ":cast_channel", + "//components/cast_channel/proto:cast_channel_fuzzer_inputs_proto", + "//net/data/ssl/certificates:generate_fuzzer_cert_includes", + "//third_party/libprotobuf-mutator", + "//third_party/openscreen/src/cast/common/channel/proto:channel_proto", + ] +} + +fuzzer_test("cast_framer_serialize_fuzzer") { + sources = [ "cast_framer_serialize_fuzzer.cc" ] + deps = [ + ":cast_channel", + "//components/cast_channel/proto:cast_channel_fuzzer_inputs_proto", + "//third_party/libprotobuf-mutator", + "//third_party/openscreen/src/cast/common/channel/proto:channel_proto", + ] +} + +fuzzer_test("cast_message_util_fuzzer") { + sources = [ "cast_message_util_fuzzer.cc" ] + dict = "fuzz.dict" + deps = [ + ":cast_channel", + "//components/cast_channel/proto:cast_channel_fuzzer_inputs_proto", + "//third_party/libprotobuf-mutator", + "//third_party/openscreen/src/cast/common/channel/proto:channel_proto", + ] +} diff --git a/chromium/components/cast_channel/README.md b/chromium/components/cast_channel/README.md new file mode 100644 index 00000000000..3d5e23007f5 --- /dev/null +++ b/chromium/components/cast_channel/README.md @@ -0,0 +1,30 @@ +# How to Run a Fuzz Test + +Create an appropriate build config: + +```shell +% tools/mb/mb.py gen -m chromium.fuzz -b 'Libfuzzer Upload Linux ASan' out/libfuzzer +% gn gen out/libfuzzer +``` + +Build the fuzz target: + +```shell +% ninja -C out/libfuzzer $TEST_NAME +``` + +Create an empty corpus directory: + +```shell +% mkdir ${TEST_NAME}_corpus +``` + +Run the fuzz target, turning off detection of ODR violations that occur in +component builds: + +```shell +% export ASAN_OPTIONS=detect_odr_violation=0 +% ./out/libfuzzer/$TEST_NAME ${TEST_NAME}_corpus +``` + +For more details, refer to https://chromium.googlesource.com/chromium/src/testing/libfuzzer/+/refs/heads/master/getting_started.md diff --git a/chromium/components/cast_channel/cast_auth_util.cc b/chromium/components/cast_channel/cast_auth_util.cc index ec3aa6913b1..876e85711c0 100644 --- a/chromium/components/cast_channel/cast_auth_util.cc +++ b/chromium/components/cast_channel/cast_auth_util.cc @@ -259,9 +259,26 @@ AuthContext AuthContext::Create() { return AuthContext(CastNonce::Get()); } +// static +AuthContext AuthContext::CreateForTest(const std::string& nonce_data) { + // Given some garbage data, try to turn it into a string that at least has the + // right length. + std::string nonce; + if (nonce_data.empty()) { + nonce = std::string(kNonceSizeInBytes, '0'); + } else { + while (nonce.size() < kNonceSizeInBytes) { + nonce += nonce_data; + } + nonce.erase(kNonceSizeInBytes); + } + DCHECK(nonce.size() == kNonceSizeInBytes); + return AuthContext(nonce); +} + AuthContext::AuthContext(const std::string& nonce) : nonce_(nonce) {} -AuthContext::~AuthContext() {} +AuthContext::~AuthContext() = default; AuthResult AuthContext::VerifySenderNonce( const std::string& nonce_response) const { diff --git a/chromium/components/cast_channel/cast_auth_util.h b/chromium/components/cast_channel/cast_auth_util.h index ea989692829..0818a7d0cb5 100644 --- a/chromium/components/cast_channel/cast_auth_util.h +++ b/chromium/components/cast_channel/cast_auth_util.h @@ -77,6 +77,8 @@ class AuthContext { // The same context must be used in the challenge and reply. static AuthContext Create(); + static AuthContext CreateForTest(const std::string& nonce); + // Verifies the nonce received in the response is equivalent to the one sent. // Returns success if |nonce_response| matches nonce_ AuthResult VerifySenderNonce(const std::string& nonce_response) const; diff --git a/chromium/components/cast_channel/cast_auth_util_fuzzer.cc b/chromium/components/cast_channel/cast_auth_util_fuzzer.cc new file mode 100644 index 00000000000..c652075efb4 --- /dev/null +++ b/chromium/components/cast_channel/cast_auth_util_fuzzer.cc @@ -0,0 +1,45 @@ +// 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 <cstdlib> +#include <iostream> + +#include "base/notreached.h" +#include "components/cast_channel/cast_auth_util.h" +#include "components/cast_channel/fuzz_proto/fuzzer_inputs.pb.h" +#include "net/cert/x509_certificate.h" +#include "testing/libfuzzer/proto/lpm_interface.h" + +namespace cast_channel { +namespace fuzz { + +namespace { +const char kCertData[] = { +#include "net/data/ssl/certificates/wildcard.inc" +}; +} // namespace + +DEFINE_PROTO_FUZZER(const CastAuthUtilInputs& input_union) { + // TODO(crbug.com/796717): Add tests for AuthenticateChallengeReply and + // VerifyTLSCertificate if necessary. Refer to updates on the bug, and check + // to see if there is already coverage through BoringSSL + + switch (input_union.input_case()) { + case CastAuthUtilInputs::kAuthenticateChallengeReplyInput: { + const auto& input = input_union.authenticate_challenge_reply_input(); + cast::channel::DeviceAuthMessage auth_message = input.auth_message(); + AuthContext context = AuthContext::CreateForTest(input.nonce()); + scoped_refptr<net::X509Certificate> peer_cert = + net::X509Certificate::CreateFromBytes(kCertData, + base::size(kCertData)); + AuthenticateChallengeReply(input.cast_message(), *peer_cert, context); + break; + } + default: + NOTREACHED(); + } +} + +} // namespace fuzz +} // namespace cast_channel diff --git a/chromium/components/cast_channel/cast_framer.cc b/chromium/components/cast_channel/cast_framer.cc index 3ad9e69255a..b7e355f5838 100644 --- a/chromium/components/cast_channel/cast_framer.cc +++ b/chromium/components/cast_channel/cast_framer.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/memory/free_deleter.h" +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" #include "base/sys_byteorder.h" diff --git a/chromium/components/cast_channel/cast_message_fuzzer.cc b/chromium/components/cast_channel/cast_framer_ingest_fuzzer.cc index 72cdbae5f46..b5bd9ddee0c 100644 --- a/chromium/components/cast_channel/cast_message_fuzzer.cc +++ b/chromium/components/cast_channel/cast_framer_ingest_fuzzer.cc @@ -12,7 +12,6 @@ #include "components/cast_channel/cast_framer.h" #include "net/base/io_buffer.h" #include "third_party/openscreen/src/cast/common/channel/proto/cast_channel.pb.h" -#include "third_party/protobuf/src/google/protobuf/stubs/logging.h" // Silence logging from the protobuf library. google::protobuf::LogSilencer log_silencer; diff --git a/chromium/components/cast_channel/cast_framer_serialize_fuzzer.cc b/chromium/components/cast_channel/cast_framer_serialize_fuzzer.cc new file mode 100644 index 00000000000..6085c40a564 --- /dev/null +++ b/chromium/components/cast_channel/cast_framer_serialize_fuzzer.cc @@ -0,0 +1,24 @@ +// 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 <cstdlib> +#include <iostream> +#include <string> + +#include "components/cast_channel/cast_framer.h" +#include "testing/libfuzzer/proto/lpm_interface.h" +#include "third_party/openscreen/src/cast/common/channel/proto/cast_channel.pb.h" + +namespace cast_channel { +namespace fuzz { + +DEFINE_PROTO_FUZZER(const cast::channel::CastMessage& input) { + std::string native_input; + MessageFramer::Serialize(input, &native_input); + if (::getenv("LPM_DUMP_NATIVE_INPUT")) + std::cout << native_input << std::endl; +} + +} // namespace fuzz +} // namespace cast_channel diff --git a/chromium/components/cast_channel/cast_message_handler.cc b/chromium/components/cast_channel/cast_message_handler.cc index 43c79aca912..5bff7f19953 100644 --- a/chromium/components/cast_channel/cast_message_handler.cc +++ b/chromium/components/cast_channel/cast_message_handler.cc @@ -21,6 +21,9 @@ namespace { // The max launch timeout amount for session launch requests. constexpr base::TimeDelta kLaunchMaxTimeout = base::TimeDelta::FromMinutes(2); +// The max size of Cast Message is 64KB. +constexpr int kMaxCastMessagePayload = 64 * 1024; + void ReportParseError(const std::string& error) { DVLOG(1) << "Error parsing JSON message: " << error; } @@ -169,7 +172,7 @@ void CastMessageHandler::RequestReceiverStatus(int channel_id) { CreateReceiverStatusRequest(sender_id_, request_id)); } -void CastMessageHandler::SendBroadcastMessage( +Result CastMessageHandler::SendBroadcastMessage( int channel_id, const std::vector<std::string>& app_ids, const BroadcastRequest& request) { @@ -178,7 +181,7 @@ void CastMessageHandler::SendBroadcastMessage( CastSocket* socket = socket_service_->GetSocket(channel_id); if (!socket) { DVLOG(2) << __func__ << ": socket not found: " << channel_id; - return; + return Result::kFailed; } int request_id = NextRequestId(); @@ -189,7 +192,11 @@ void CastMessageHandler::SendBroadcastMessage( // about the response, as broadcasts are fire-and-forget. CastMessage message = CreateBroadcastRequest(sender_id_, request_id, app_ids, request); + if (message.ByteSizeLong() > kMaxCastMessagePayload) { + return Result::kFailed; + } SendCastMessageToSocket(socket, message); + return Result::kOk; } void CastMessageHandler::LaunchSession( @@ -197,7 +204,7 @@ void CastMessageHandler::LaunchSession( const std::string& app_id, base::TimeDelta launch_timeout, const std::vector<std::string>& supported_app_types, - const std::string& app_params, + const base::Optional<base::Value>& app_params, LaunchSessionCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CastSocket* socket = socket_service_->GetSocket(channel_id); @@ -213,12 +220,18 @@ void CastMessageHandler::LaunchSession( launch_timeout = std::min(launch_timeout, kLaunchMaxTimeout); DVLOG(2) << __func__ << ", channel_id: " << channel_id << ", request_id: " << request_id; + CastMessage message = CreateLaunchRequest( + sender_id_, request_id, app_id, locale_, supported_app_types, app_params); + if (message.ByteSizeLong() > kMaxCastMessagePayload) { + LaunchSessionResponse response; + response.result = LaunchSessionResponse::kError; + std::move(callback).Run(std::move(response)); + return; + } if (requests->AddLaunchRequest(std::make_unique<LaunchSessionRequest>( request_id, std::move(callback), clock_), launch_timeout)) { - SendCastMessageToSocket( - socket, CreateLaunchRequest(sender_id_, request_id, app_id, locale_, - supported_app_types)); + SendCastMessageToSocket(socket, message); } } @@ -264,6 +277,9 @@ Result CastMessageHandler::SendAppMessage(int channel_id, const CastMessage& message) { DCHECK(!IsCastInternalNamespace(message.namespace_())) << ": unexpected app message namespace: " << message.namespace_(); + if (message.ByteSizeLong() > kMaxCastMessagePayload) { + return Result::kFailed; + } return SendCastMessage(channel_id, message); } diff --git a/chromium/components/cast_channel/cast_message_handler.h b/chromium/components/cast_channel/cast_message_handler.h index f7791e7b871..32d1bef83c6 100644 --- a/chromium/components/cast_channel/cast_message_handler.h +++ b/chromium/components/cast_channel/cast_message_handler.h @@ -173,9 +173,9 @@ class CastMessageHandler : public CastSocket::Observer { // Sends a broadcast message containing |app_ids| and |request| to the socket // given by |channel_id|. - virtual void SendBroadcastMessage(int channel_id, - const std::vector<std::string>& app_ids, - const BroadcastRequest& request); + virtual Result SendBroadcastMessage(int channel_id, + const std::vector<std::string>& app_ids, + const BroadcastRequest& request); // Requests a session launch for |app_id| on the device given by |channel_id|. // |callback| will be invoked with the response or with a timed out result if @@ -185,7 +185,7 @@ class CastMessageHandler : public CastSocket::Observer { const std::string& app_id, base::TimeDelta launch_timeout, const std::vector<std::string>& supported_app_types, - const std::string& app_params, + const base::Optional<base::Value>& app_params, LaunchSessionCallback callback); // Stops the session given by |session_id| on the device given by diff --git a/chromium/components/cast_channel/cast_message_handler_unittest.cc b/chromium/components/cast_channel/cast_message_handler_unittest.cc index 81086b606ab..142fdcbb92d 100644 --- a/chromium/components/cast_channel/cast_message_handler_unittest.cc +++ b/chromium/components/cast_channel/cast_message_handler_unittest.cc @@ -33,11 +33,25 @@ namespace cast_channel { namespace { +constexpr char kAppId1[] = "0F5096E8"; +constexpr char kAppId2[] = "85CDB22F"; constexpr char kTestUserAgentString[] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/66.0.3331.0 Safari/537.36"; constexpr char kSourceId[] = "sourceId"; constexpr char kDestinationId[] = "destinationId"; +constexpr char kAppParams[] = R"( +{ + "requiredFeatures" : ["STREAM_TRANSFER"], + "launchCheckerParams" : { + "credentialsData" : { + "credentialsType" : "mobile", + "credentials" : "99843n2idsguyhga" + } + } +} +)"; +constexpr int kMaxProtocolMessageSize = 64 * 1024; data_decoder::DataDecoder::ValueOrError ParseJsonLikeDataDecoder( base::StringPiece json) { @@ -135,11 +149,11 @@ class CastMessageHandlerTest : public testing::Test { void CreatePendingRequests() { EXPECT_CALL(*transport_, SendMessage(_, _)).Times(AnyNumber()); - handler_.LaunchSession(channel_id_, "theAppId", base::TimeDelta::Max(), - {"WEB"}, /* appParams */ "", + handler_.LaunchSession(channel_id_, kAppId1, base::TimeDelta::Max(), + {"WEB"}, /* appParams */ base::nullopt, launch_session_callback_.Get()); for (int i = 0; i < 2; i++) { - handler_.RequestAppAvailability(&cast_socket_, "theAppId", + handler_.RequestAppAvailability(&cast_socket_, kAppId1, get_app_availability_callback_.Get()); handler_.SendSetVolumeRequest( channel_id_, @@ -174,11 +188,11 @@ TEST_F(CastMessageHandlerTest, VirtualConnectionCreatedOnlyOnce) { ExpectEnsureConnectionThen(CastMessageType::kGetAppAvailability, 2); handler_.RequestAppAvailability( - &cast_socket_, "AAAAAAAA", + &cast_socket_, kAppId1, base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, base::Unretained(this))); handler_.RequestAppAvailability( - &cast_socket_, "BBBBBBBB", + &cast_socket_, kAppId2, base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, base::Unretained(this))); } @@ -187,25 +201,25 @@ TEST_F(CastMessageHandlerTest, RecreateVirtualConnectionAfterError) { ExpectEnsureConnectionThen(CastMessageType::kGetAppAvailability); handler_.RequestAppAvailability( - &cast_socket_, "AAAAAAAA", + &cast_socket_, kAppId1, base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, base::Unretained(this))); - EXPECT_CALL(*this, DoOnAppAvailability("AAAAAAAA", - GetAppAvailabilityResult::kUnknown)); + EXPECT_CALL(*this, + DoOnAppAvailability(kAppId1, GetAppAvailabilityResult::kUnknown)); OnError(ChannelError::TRANSPORT_ERROR); ExpectEnsureConnectionThen(CastMessageType::kGetAppAvailability); handler_.RequestAppAvailability( - &cast_socket_, "BBBBBBBB", + &cast_socket_, kAppId2, base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, base::Unretained(this))); // The callback is invoked with kUnknown before the PendingRequests is // destroyed. - EXPECT_CALL(*this, DoOnAppAvailability("BBBBBBBB", - GetAppAvailabilityResult::kUnknown)); + EXPECT_CALL(*this, + DoOnAppAvailability(kAppId2, GetAppAvailabilityResult::kUnknown)); } TEST_F(CastMessageHandlerTest, RequestAppAvailability) { @@ -317,9 +331,10 @@ TEST_F(CastMessageHandlerTest, CloseConnectionFromReceiver) { TEST_F(CastMessageHandlerTest, LaunchSession) { ExpectEnsureConnectionThen(CastMessageType::kLaunch); + const base::Optional<base::Value> json = base::JSONReader::Read(kAppParams); + handler_.LaunchSession( - channel_id_, "AAAAAAAA", base::TimeDelta::FromSeconds(30), {"WEB"}, - /* appParams */ "", + channel_id_, kAppId1, base::TimeDelta::FromSeconds(30), {"WEB"}, json, base::BindOnce(&CastMessageHandlerTest::ExpectSessionLaunchResult, base::Unretained(this), LaunchSessionResponse::Result::kOk)); @@ -332,6 +347,9 @@ TEST_F(CastMessageHandlerTest, LaunchSession) { ASSERT_TRUE(request_id_value); int request_id = request_id_value->GetInt(); EXPECT_GT(request_id, 0); + const base::Value* app_params = + dict->FindKeyOfType("appParams", base::Value::Type::DICTIONARY); + EXPECT_EQ(json.value(), *app_params); CastMessage response; response.set_namespace_("urn:x-cast:com.google.cast.receiver"); @@ -357,8 +375,8 @@ TEST_F(CastMessageHandlerTest, LaunchSessionTimedOut) { ExpectEnsureConnectionThen(CastMessageType::kLaunch); handler_.LaunchSession( - channel_id_, "AAAAAAAA", base::TimeDelta::FromSeconds(30), {"WEB"}, - /* appParams */ "", + channel_id_, kAppId1, base::TimeDelta::FromSeconds(30), {"WEB"}, + /* appParams */ base::nullopt, base::BindOnce(&CastMessageHandlerTest::ExpectSessionLaunchResult, base::Unretained(this), LaunchSessionResponse::Result::kTimedOut)); @@ -367,6 +385,19 @@ TEST_F(CastMessageHandlerTest, LaunchSessionTimedOut) { EXPECT_EQ(1, session_launch_response_count_); } +TEST_F(CastMessageHandlerTest, LaunchSessionMessageExceedsSizeLimit) { + std::string invalid_URL(kMaxProtocolMessageSize, 'a'); + base::Value json(base::Value::Type::DICTIONARY); + json.SetKey("key", base::Value(invalid_URL)); + handler_.LaunchSession( + channel_id_, kAppId1, base::TimeDelta::FromSeconds(30), {"WEB"}, + base::make_optional<base::Value>(std::move(json)), + base::BindOnce(&CastMessageHandlerTest::ExpectSessionLaunchResult, + base::Unretained(this), + LaunchSessionResponse::Result::kError)); + EXPECT_EQ(1, session_launch_response_count_); +} + TEST_F(CastMessageHandlerTest, SendAppMessage) { base::Value body(base::Value::Type::DICTIONARY); body.SetKey("foo", base::Value("bar")); @@ -382,6 +413,16 @@ TEST_F(CastMessageHandlerTest, SendAppMessage) { EXPECT_EQ(Result::kOk, handler_.SendAppMessage(channel_id_, message)); } +TEST_F(CastMessageHandlerTest, SendAppMessageExceedsSizeLimit) { + std::string invalid_msg(kMaxProtocolMessageSize, 'a'); + base::Value body(base::Value::Type::DICTIONARY); + body.SetKey("foo", base::Value(invalid_msg)); + CastMessage message = + CreateCastMessage("namespace", body, kSourceId, kDestinationId); + + EXPECT_EQ(Result::kFailed, handler_.SendAppMessage(channel_id_, message)); +} + // Check that SendMediaRequest sends a message created by CreateMediaRequest and // returns a request ID. TEST_F(CastMessageHandlerTest, SendMediaRequest) { @@ -415,6 +456,31 @@ TEST_F(CastMessageHandlerTest, SendMediaRequest) { EXPECT_EQ(1, request_id); } +TEST_F(CastMessageHandlerTest, SendBroadcastMessage) { + BroadcastRequest request = BroadcastRequest("namespace", "message"); + CastMessage message = CreateBroadcastRequest( + "theSourceId", /* request_id */ 1, {kAppId1}, request); + { + InSequence dummy; + ExpectEnsureConnection(); + EXPECT_CALL(*transport_, + SendMessage(HasPayloadUtf8(message.payload_utf8()), _)); + } + + EXPECT_EQ(Result::kOk, + handler_.SendBroadcastMessage(channel_id_, {kAppId1}, request)); +} + +TEST_F(CastMessageHandlerTest, SendBroadcastMessageExceedsSizeLimit) { + BroadcastRequest request = + BroadcastRequest("namespace", std::string(kMaxProtocolMessageSize, 'a')); + CastMessage message = CreateBroadcastRequest( + "theSourceId", /* request_id */ 1, {kAppId1}, request); + + EXPECT_EQ(Result::kFailed, + handler_.SendBroadcastMessage(channel_id_, {kAppId1}, request)); +} + // Check that SendVolumeCommand sends a message created by CreateVolumeRequest // and registers a pending request. TEST_F(CastMessageHandlerTest, SendVolumeCommand) { @@ -460,7 +526,7 @@ TEST_F(CastMessageHandlerTest, PendingRequestsDestructor) { EXPECT_EQ(base::nullopt, response.receiver_status); }); EXPECT_CALL(get_app_availability_callback_, - Run("theAppId", GetAppAvailabilityResult::kUnknown)) + Run(kAppId1, GetAppAvailabilityResult::kUnknown)) .Times(2); EXPECT_CALL(set_volume_callback_, Run(Result::kFailed)).Times(2); EXPECT_CALL(stop_session_callback_, Run(Result::kFailed)); @@ -482,7 +548,7 @@ TEST_F(CastMessageHandlerTest, HandlePendingRequest) { testing::Optional(IsJson(R"({"foo": "bar"})"))); }); EXPECT_CALL(get_app_availability_callback_, - Run("theAppId", GetAppAvailabilityResult::kAvailable)) + Run(kAppId1, GetAppAvailabilityResult::kAvailable)) .Times(2); EXPECT_CALL(set_volume_callback_, Run(Result::kOk)).Times(2); EXPECT_CALL(stop_session_callback_, Run(Result::kOk)); @@ -498,13 +564,14 @@ TEST_F(CastMessageHandlerTest, HandlePendingRequest) { })")); // Handle both pending get app availability requests. - handler_.HandleCastInternalMessage(channel_id_, "theSourceId", - "theDestinationId", "theNamespace", - ParseJsonLikeDataDecoder(R"( + handler_.HandleCastInternalMessage( + channel_id_, "theSourceId", "theDestinationId", "theNamespace", + ParseJsonLikeDataDecoder(base::StringPrintf(R"( { "requestId": 2, - "availability": {"theAppId": "APP_AVAILABLE"}, - })")); + "availability": {"%s": "APP_AVAILABLE"}, + })", + kAppId1))); // Handle pending set volume request (1 of 2). handler_.HandleCastInternalMessage( diff --git a/chromium/components/cast_channel/cast_message_util.cc b/chromium/components/cast_channel/cast_message_util.cc index 8c61928f467..76c70d788ec 100644 --- a/chromium/components/cast_channel/cast_message_util.cc +++ b/chromium/components/cast_channel/cast_message_util.cc @@ -426,7 +426,8 @@ CastMessage CreateLaunchRequest( int request_id, const std::string& app_id, const std::string& locale, - const std::vector<std::string>& supported_app_types) { + const std::vector<std::string>& supported_app_types, + const base::Optional<base::Value>& app_params) { Value dict(Value::Type::DICTIONARY); dict.SetKey("type", Value(EnumToString<CastMessageType, CastMessageType::kLaunch>())); @@ -438,6 +439,9 @@ CastMessage CreateLaunchRequest( supported_app_types_value.push_back(Value(type)); dict.SetKey("supportedAppTypes", Value(supported_app_types_value)); + if (app_params) { + dict.SetKey("appParams", app_params.value().Clone()); + } return CreateCastMessage(kReceiverNamespace, dict, source_id, kPlatformReceiverId); } diff --git a/chromium/components/cast_channel/cast_message_util.h b/chromium/components/cast_channel/cast_message_util.h index 7d3225a040a..d32b77c1a5a 100644 --- a/chromium/components/cast_channel/cast_message_util.h +++ b/chromium/components/cast_channel/cast_message_util.h @@ -137,15 +137,23 @@ enum class V2MessageType { // Receiver App Type determines App types that can be supported by a Cast media // source. All Cast media sources support the web type. +// Please keep it in sync with the EnumTable in +// chrome/common/media_router/providers/cast/cast_media_source.cc. +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. Please keep it in sync with +// MediaRouterResponseReceiverAppType in tools/metrics/histograms/enums.xml. enum class ReceiverAppType { + kOther = 0, + // Web-based Cast receiver apps. This is supported by all Cast media source // by default. - kWeb, + kWeb = 1, // A media source may support launching an Android TV app in addition to a // Cast web app. - kAndroidTv, + kAndroidTv = 2, + // Do not reorder existing entries, and add new types above |kMaxValue|. kMaxValue = kAndroidTv, }; @@ -174,9 +182,6 @@ CastMessageType CastMessageTypeFromString(const std::string& type); // correspond to a known type. V2MessageType V2MessageTypeFromString(const std::string& type); -// Returns a human readable string for |message_proto|. -std::string CastMessageToString(const CastMessage& message_proto); - // Returns a human readable string for |message|. std::string AuthMessageToString(const DeviceAuthMessage& message); @@ -253,7 +258,8 @@ CastMessage CreateLaunchRequest( int request_id, const std::string& app_id, const std::string& locale, - const std::vector<std::string>& supported_app_types); + const std::vector<std::string>& supported_app_types, + const base::Optional<base::Value>& app_params); CastMessage CreateStopRequest(const std::string& source_id, int request_id, diff --git a/chromium/components/cast_channel/cast_message_util_fuzzer.cc b/chromium/components/cast_channel/cast_message_util_fuzzer.cc new file mode 100644 index 00000000000..753ec618b5f --- /dev/null +++ b/chromium/components/cast_channel/cast_message_util_fuzzer.cc @@ -0,0 +1,168 @@ +// 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 <string> +#include <utility> +#include <vector> + +#include "base/values.h" +#include "components/cast_channel/cast_message_util.h" +#include "components/cast_channel/enum_table.h" +#include "components/cast_channel/fuzz_proto/fuzzer_inputs.pb.h" +#include "testing/libfuzzer/proto/lpm_interface.h" + +using cast_util::EnumToString; + +namespace cast_channel { +namespace fuzz { + +namespace { + +base::Value MakeValue(const JunkValue& junk) { + base::Value result(base::Value::Type::DICTIONARY); + for (int i = 0; i < junk.field_size(); i++) { + const auto& field = junk.field(i); + base::Value field_value = field.has_int_value() + ? base::Value(field.int_value()) + : field.has_string_value() + ? base::Value(field.string_value()) + : field.has_float_value() + ? base::Value(field.float_value()) + : base::Value(field.bool_value()); + result.SetKey(field.name(), std::move(field_value)); + } + return result; +} + +template <typename Field, typename T = typename Field::value_type> +std::vector<T> MakeVector(const Field& field) { + return std::vector<T>(field.cbegin(), field.cend()); +} + +} // namespace + +DEFINE_PROTO_FUZZER(const CastMessageUtilInputs& input_union) { + // TODO(crbug.com/796717): Add test for CreateAuthChallengeMessage() + switch (input_union.input_case()) { + case CastMessageUtilInputs::kCreateBroadcastRequestInput: { + const auto& input = input_union.create_broadcast_request_input(); + CreateBroadcastRequest(input.source_id(), input.request_id(), + MakeVector(input.app_id()), + BroadcastRequest(input.broadcast_namespace(), + input.broadcast_message())); + break; + } + case CastMessageUtilInputs::kCreateLaunchRequestInput: { + const auto& input = input_union.create_launch_request_input(); + base::Optional<base::Value> app_params; + if (input.has_app_params()) + app_params = MakeValue(input.app_params()); + CreateLaunchRequest(input.source_id(), input.request_id(), input.app_id(), + input.locale(), + MakeVector(input.supported_app_types()), app_params); + break; + } + case CastMessageUtilInputs::kCreateStopRequestInput: { + const auto& input = input_union.create_stop_request_input(); + CreateStopRequest(input.source_id(), input.request_id(), + input.session_id()); + break; + } + case CastMessageUtilInputs::kCreateCastMessageInput: { + const auto& input = input_union.create_cast_message_input(); + base::Value body = MakeValue(input.body()); + CreateCastMessage(input.message_namespace(), body, input.source_id(), + input.destination_id()); + break; + } + case CastMessageUtilInputs::kCreateMediaRequestInput: { + const auto& input = input_union.create_media_request_input(); + auto type = static_cast<V2MessageType>(input.type()); + if (IsMediaRequestMessageType(type)) { + base::Value body = MakeValue(input.body()); + body.SetKey("type", base::Value(*EnumToString(type))); + CreateMediaRequest(body, input.request_id(), input.source_id(), + input.destination_id()); + } + break; + } + case CastMessageUtilInputs::kCreateSetVolumeRequestInput: { + const auto& input = input_union.create_set_volume_request_input(); + base::Value body = MakeValue(input.body()); + body.SetKey( + "type", + base::Value( + EnumToString<V2MessageType, V2MessageType::kSetVolume>())); + CreateSetVolumeRequest(body, input.request_id(), input.source_id()); + break; + } + case CastMessageUtilInputs::kIntInput: { + IsMediaRequestMessageType( + static_cast<V2MessageType>(input_union.int_input())); + ToString(static_cast<GetAppAvailabilityResult>(input_union.int_input())); + ToString(static_cast<CastMessageType>(input_union.int_input())); + ToString(static_cast<V2MessageType>(input_union.int_input())); + break; + } + case CastMessageUtilInputs::kStringInput: { + IsCastInternalNamespace(input_union.string_input()); + break; + } + case CastMessageUtilInputs::kCastMessage: { + const auto& message = input_union.cast_message(); + IsCastMessageValid(message); + IsAuthMessage(message); + IsReceiverMessage(message); + IsPlatformSenderMessage(message); + break; + } + case CastMessageUtilInputs::kCreateVirtualConnectionRequestInput: { + const auto& input = input_union.create_virtual_connection_request_input(); + CreateVirtualConnectionRequest( + input.source_id(), input.destination_id(), + static_cast<VirtualConnectionType>(input.connection_type()), + input.user_agent(), input.browser_version()); + break; + } + case CastMessageUtilInputs::kCreateGetAppAvailabilityRequestInput: { + const auto& input = + input_union.create_get_app_availability_request_input(); + CreateGetAppAvailabilityRequest(input.source_id(), input.request_id(), + input.app_id()); + break; + } + case CastMessageUtilInputs::kGetRequestIdFromResponseInput: { + const auto& input = input_union.get_request_id_from_response_input(); + base::Value payload = MakeValue(input.payload()); + if (input.has_request_id()) + payload.SetKey("requestId", base::Value(input.request_id())); + GetRequestIdFromResponse(payload); + break; + } + case CastMessageUtilInputs::kGetLaunchSessionResponseInput: { + const auto& input = input_union.get_launch_session_response_input(); + base::Value payload = MakeValue(input.payload()); + GetLaunchSessionResponse(payload); + break; + } + case CastMessageUtilInputs::kParseMessageTypeFromPayloadInput: { + const auto& input = input_union.parse_message_type_from_payload_input(); + base::Value payload = MakeValue(input.payload()); + if (input.has_type()) + payload.SetKey("type", base::Value(input.type())); + ParseMessageTypeFromPayload(payload); + break; + } + case CastMessageUtilInputs::kCreateReceiverStatusRequestInput: { + const auto& input = input_union.create_receiver_status_request_input(); + CreateReceiverStatusRequest(input.source_id(), input.request_id()); + break; + } + default: + NOTREACHED(); + } +} + +} // namespace fuzz +} // namespace cast_channel diff --git a/chromium/components/cast_channel/cast_message_util_unittest.cc b/chromium/components/cast_channel/cast_message_util_unittest.cc index 26b62252a4c..fad8867bdc5 100644 --- a/chromium/components/cast_channel/cast_message_util_unittest.cc +++ b/chromium/components/cast_channel/cast_message_util_unittest.cc @@ -4,6 +4,7 @@ #include "components/cast_channel/cast_message_util.h" +#include "base/strings/strcat.h" #include "base/test/values_test_util.h" #include "base/values.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,17 +31,27 @@ TEST(CastMessageUtilTest, CastMessageType) { } TEST(CastMessageUtilTest, GetLaunchSessionResponseOk) { - std::string payload = R"( + std::string status = R"( { - "type": "RECEIVER_STATUS", - "requestId": 123, - "status": {} + "applications": [ + { + "appId": "2FE23A98", + "universalAppId": "AD9AF8E0", + "appType": "ANDROID_TV" + } + ] } )"; + std::string payload = base::StrCat({R"( + { + "type": "RECEIVER_STATUS", + "requestId": 123, + "status": )", + status, "}"}); LaunchSessionResponse response = GetLaunchSessionResponse(ParseJson(payload)); EXPECT_EQ(LaunchSessionResponse::Result::kOk, response.result); - EXPECT_TRUE(response.receiver_status); + EXPECT_EQ(ParseJson(status), response.receiver_status); } TEST(CastMessageUtilTest, GetLaunchSessionResponseError) { diff --git a/chromium/components/cast_channel/cast_socket.cc b/chromium/components/cast_channel/cast_socket.cc index 6386c6f45dc..9ca58414254 100644 --- a/chromium/components/cast_channel/cast_socket.cc +++ b/chromium/components/cast_channel/cast_socket.cc @@ -21,7 +21,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/sys_byteorder.h" -#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/cast_channel/cast_auth_util.h" @@ -68,8 +67,8 @@ void OnConnected( mojo::ScopedDataPipeConsumerHandle receive_stream, mojo::ScopedDataPipeProducerHandle send_stream) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), result, local_addr, peer_addr, std::move(receive_stream), std::move(send_stream))); } @@ -380,8 +379,8 @@ int CastSocketImpl::DoTcpConnect() { VLOG_WITH_CONNECTION(1) << "DoTcpConnect"; SetConnectState(ConnectionState::TCP_CONNECT_COMPLETE); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(ConnectOnUIThread, network_context_getter_, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(ConnectOnUIThread, network_context_getter_, net::AddressList(open_params_.ip_endpoint), tcp_socket_.BindNewPipeAndPassReceiver(), base::BindOnce(&CastSocketImpl::OnConnect, diff --git a/chromium/components/cast_channel/cast_socket_service.cc b/chromium/components/cast_channel/cast_socket_service.cc index 687760407ee..f58fc9bf3b5 100644 --- a/chromium/components/cast_channel/cast_socket_service.cc +++ b/chromium/components/cast_channel/cast_socket_service.cc @@ -5,7 +5,6 @@ #include "components/cast_channel/cast_socket_service.h" #include "base/memory/ptr_util.h" -#include "base/task/post_task.h" #include "components/cast_channel/cast_socket.h" #include "components/cast_channel/logger.h" #include "content/public/browser/browser_task_traits.h" @@ -23,8 +22,7 @@ CastSocketService::CastSocketService() // (1) ChromeURLRequestContextGetter::GetURLRequestContext, which is // called by CastMediaSinkServiceImpl, must run on IO thread. (2) Parts of // CastChannel extension API functions run on IO thread. - task_runner_( - base::CreateSingleThreadTaskRunner({content::BrowserThread::IO})) {} + task_runner_(content::GetIOThreadTaskRunner({})) {} // This is a leaky singleton and the dtor won't be called. CastSocketService::~CastSocketService() = default; diff --git a/chromium/components/cast_channel/cast_test_util.h b/chromium/components/cast_channel/cast_test_util.h index d907a9ae07a..44ec891faa0 100644 --- a/chromium/components/cast_channel/cast_test_util.h +++ b/chromium/components/cast_channel/cast_test_util.h @@ -175,15 +175,15 @@ class MockCastMessageHandler : public CastMessageHandler { GetAppAvailabilityCallback callback)); MOCK_METHOD1(RequestReceiverStatus, void(int channel_id)); MOCK_METHOD3(SendBroadcastMessage, - void(int, - const std::vector<std::string>&, - const BroadcastRequest&)); + Result(int, + const std::vector<std::string>&, + const BroadcastRequest&)); MOCK_METHOD6(LaunchSession, void(int, const std::string&, base::TimeDelta, const std::vector<std::string>&, - const std::string&, + const base::Optional<base::Value>&, LaunchSessionCallback callback)); MOCK_METHOD4(StopSession, void(int channel_id, @@ -192,6 +192,8 @@ class MockCastMessageHandler : public CastMessageHandler { ResultCallback callback)); MOCK_METHOD2(SendAppMessage, Result(int channel_id, const CastMessage& message)); + MOCK_METHOD2(SendCastMessage, + Result(int channel_id, const CastMessage& message)); MOCK_METHOD4(SendMediaRequest, base::Optional<int>(int channel_id, const base::Value& body, diff --git a/chromium/components/cast_channel/enum_table.h b/chromium/components/cast_channel/enum_table.h index 7957f5b203f..85539057d82 100644 --- a/chromium/components/cast_channel/enum_table.h +++ b/chromium/components/cast_channel/enum_table.h @@ -8,7 +8,9 @@ #include <cstdint> #include <cstring> -#include "base/logging.h" +#include "base/check_op.h" +#include "base/macros.h" +#include "base/notreached.h" #include "base/optional.h" #include "base/strings/string_piece.h" #include "build/build_config.h" diff --git a/chromium/components/cast_channel/fuzz.dict b/chromium/components/cast_channel/fuzz.dict new file mode 100644 index 00000000000..056feeb2015 --- /dev/null +++ b/chromium/components/cast_channel/fuzz.dict @@ -0,0 +1,65 @@ +# Enum values +"ANSWER" +"APP_AVAILABLE" +"APPLICATION_BROADCAST" +"APP_UNAVAILABLE" +"CLOSE" +"CONNECT" +"EDIT_TRACKS_INFO" +"GET_APP_AVAILABILITY" +"GET_STATUS" +"LAUNCH" +"LAUNCH_ERROR" +"LOAD" +"MEDIA_GET_STATUS" +"MEDIA_SET_VOLUME" +"MEDIA_STATUS" +"OFFER" +"PAUSE" +"PING" +"PLAY" +"PONG" +"PRECACHE" +"QUEUE_INSERT" +"QUEUE_LOAD" +"QUEUE_REMOVE" +"QUEUE_REORDER" +"QUEUE_UPDATE" +"RECEIVER_STATUS" +"SEEK" +"SET_VOLUME" +"STOP" +"STOP_MEDIA" + +# Constants form cast_message_util.h +"urn:x-cast:com.google.cast." +"urn:x-cast:com.google.cast.tp.deviceauth" +"urn:x-cast:com.google.cast.tp.heartbeat" +"urn:x-cast:com.google.cast.tp.connection" +"urn:x-cast:com.google.cast.receiver" +"urn:x-cast:com.google.cast.broadcast" +"urn:x-cast:com.google.cast.media" +"sender-0" +"receiver-0" + +# JSON message fields +"appId" +"appIds" +"availability" +"browserVersion" +"connectionType" +"connType" +"language" +"message" +"namespace" +"origin" +"platform" +"requestId" +"sdkType" +"senderInfo" +"sessionId" +"status" +"systemVersion" +"type" +"userAgent" +"version" diff --git a/chromium/components/cast_channel/proto/BUILD.gn b/chromium/components/cast_channel/proto/BUILD.gn index a1f22bf4c22..ca7ba2952f8 100644 --- a/chromium/components/cast_channel/proto/BUILD.gn +++ b/chromium/components/cast_channel/proto/BUILD.gn @@ -2,11 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni") import("//third_party/protobuf/proto_library.gni") -proto_library("cast_channel_proto") { - sources = [ - "authority_keys.proto", - "cast_channel.proto", - ] +fuzzable_proto_library("cast_channel_fuzzer_inputs_proto") { + sources = [ "fuzzer_inputs.proto" ] + import_dirs = [ "//third_party/openscreen/src/cast/common/channel/proto" ] + proto_out_dir = "components/cast_channel/fuzz_proto" } diff --git a/chromium/components/cast_channel/proto/authority_keys.proto b/chromium/components/cast_channel/proto/authority_keys.proto deleted file mode 100644 index 9f62d200dc0..00000000000 --- a/chromium/components/cast_channel/proto/authority_keys.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014 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. - -syntax = "proto2"; - -option optimize_for = LITE_RUNTIME; - -package cast_channel.proto; - -message AuthorityKeys { - message Key { - required bytes fingerprint = 1; - required bytes public_key = 2; - } - repeated Key keys = 1; -} diff --git a/chromium/components/cast_channel/proto/cast_channel.proto b/chromium/components/cast_channel/proto/cast_channel.proto deleted file mode 100644 index 4cac6db6067..00000000000 --- a/chromium/components/cast_channel/proto/cast_channel.proto +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 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. - -syntax = "proto2"; - -option optimize_for = LITE_RUNTIME; - -package cast_channel; - -message CastMessage { - // Always pass a version of the protocol for future compatibility - // requirements. - enum ProtocolVersion { CASTV2_1_0 = 0; } - required ProtocolVersion protocol_version = 1; - - // source and destination ids identify the origin and destination of the - // message. They are used to route messages between endpoints that share a - // device-to-device channel. - // - // For messages between applications: - // - The sender application id is a unique identifier generated on behalf of - // the sender application. - // - The receiver id is always the the session id for the application. - // - // For messages to or from the sender or receiver platform, the special ids - // 'sender-0' and 'receiver-0' can be used. - // - // For messages intended for all endpoints using a given channel, the - // wildcard destination_id '*' can be used. - required string source_id = 2; - required string destination_id = 3; - - // This is the core multiplexing key. All messages are sent on a namespace - // and endpoints sharing a channel listen on one or more namespaces. The - // namespace defines the protocol and semantics of the message. - required string namespace = 4; - - // Encoding and payload info follows. - - // What type of data do we have in this message. - enum PayloadType { - STRING = 0; - BINARY = 1; - } - required PayloadType payload_type = 5; - - // Depending on payload_type, exactly one of the following optional fields - // will always be set. - optional string payload_utf8 = 6; - optional bytes payload_binary = 7; -} - -enum SignatureAlgorithm { - UNSPECIFIED = 0; - RSASSA_PKCS1v15 = 1; - RSASSA_PSS = 2; -} - -enum HashAlgorithm { - SHA1 = 0; - SHA256 = 1; -} - -// Messages for authentication protocol between a sender and a receiver. -message AuthChallenge { - optional SignatureAlgorithm signature_algorithm = 1 - [default = RSASSA_PKCS1v15]; - optional bytes sender_nonce = 2; - optional HashAlgorithm hash_algorithm = 3 [default = SHA1]; -} - -message AuthResponse { - required bytes signature = 1; - required bytes client_auth_certificate = 2; - repeated bytes intermediate_certificate = 3; - optional SignatureAlgorithm signature_algorithm = 4 - [default = RSASSA_PKCS1v15]; - optional bytes sender_nonce = 5; - optional HashAlgorithm hash_algorithm = 6 [default = SHA1]; - optional bytes crl = 7; -} - -message AuthError { - enum ErrorType { - INTERNAL_ERROR = 0; - NO_TLS = 1; // The underlying connection is not TLS - SIGNATURE_ALGORITHM_UNAVAILABLE = 2; - } - required ErrorType error_type = 1; -} - -message DeviceAuthMessage { - // Request fields - optional AuthChallenge challenge = 1; - // Response fields - optional AuthResponse response = 2; - optional AuthError error = 3; -} diff --git a/chromium/components/cast_channel/proto/fuzzer_inputs.proto b/chromium/components/cast_channel/proto/fuzzer_inputs.proto new file mode 100644 index 00000000000..b57cbe0866e --- /dev/null +++ b/chromium/components/cast_channel/proto/fuzzer_inputs.proto @@ -0,0 +1,166 @@ +// 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. + +// Inputs for various fuzz tests + +// NOTE(crbug.com/796717): As for this writing, the tests are +// incomplete, and its not entirely clear what additional tests need +// to be written. The general priority for tests is: +// +// 1. Messages that contain any data passed through the SDK/renderer +// process. +// 2. Messages from Cast devices. +// 3. Messages generated entirely by the native Cast MRP, since they +// are already from privileged code (at least for now). +// +// See also +// https://chromium-coverage.appspot.com/reports/763373_fuzzers_only/linux/chromium/src/components/cast_channel/report.html +// for fuzz test coverage. + +syntax = "proto2"; + +import "cast_channel.proto"; + +option optimize_for = LITE_RUNTIME; + +package cast_channel.fuzz; + +// Inputs for functions in cast_auth_utils.cc +message CastAuthUtilInputs { + message AuthenticateChallengeReplyInput { + required cast.channel.DeviceAuthMessage auth_message = 1; + required cast.channel.CastMessage cast_message = 2; + required string nonce = 3; + } + + oneof input { + AuthenticateChallengeReplyInput authenticate_challenge_reply_input = 1; + // TODO(crbug.com/796717): Add inputs for other functions to test: + // - VerifyTLSCertificate + // - VerifyCredentials + } +} + +// Inputs for functions in cast_message_utils.cc +message CastMessageUtilInputs { + message CreateBroadcastRequestInput { + required string source_id = 1; + required int32 request_id = 2; + repeated string app_id = 3; + required string broadcast_namespace = 4; + required string broadcast_message = 5; + } + + message CreateLaunchRequestInput { + required string source_id = 1; + required int32 request_id = 2; + required string app_id = 3; + required string locale = 4; + repeated string supported_app_types = 5; + optional JunkValue app_params = 6; + } + + message CreateStopRequestInput { + required string source_id = 1; + required int32 request_id = 2; + required string session_id = 3; + } + + message CreateCastMessageInput { + required string message_namespace = 1; + required string source_id = 2; + required string destination_id = 3; + required JunkValue body = 4; + } + + message CreateMediaRequestInput { + required int32 request_id = 1; + required string source_id = 2; + required string destination_id = 3; + required int32 type = 4; + required JunkValue body = 5; + } + + message CreateSetVolumeRequestInput { + required int32 request_id = 1; + required string source_id = 2; + required JunkValue body = 3; + } + + message CreateVirtualConnectionRequestInput { + required string source_id = 1; + required string destination_id = 2; + required int32 connection_type = 3; + required string user_agent = 4; + required string browser_version = 5; + } + + message CreateGetAppAvailabilityRequestInput { + required string source_id = 1; + required int32 request_id = 2; + required string app_id = 3; + } + + message GetRequestIdFromResponseInput { + optional int32 request_id = 1; + required JunkValue payload = 2; + } + + message GetAppAvailabilityResultFromResponseInput { + required JunkValue payload = 1; + required string app_id = 2; + } + + message GetLaunchSessionResponseInput { required JunkValue payload = 1; } + + message IsCastInternalNamespaceInput { + required string message_namespace = 1; + } + + message ParseMessageTypeFromPayloadInput { + optional string type = 1; + required JunkValue payload = 2; + } + + message CreateReceiverStatusRequestInput { + required string source_id = 1; + required int32 request_id = 2; + } + + oneof input { + CreateBroadcastRequestInput create_broadcast_request_input = 1; + CreateLaunchRequestInput create_launch_request_input = 2; + CreateStopRequestInput create_stop_request_input = 3; + CreateCastMessageInput create_cast_message_input = 4; + CreateMediaRequestInput create_media_request_input = 5; + CreateSetVolumeRequestInput create_set_volume_request_input = 6; + int32 int_input = 7; + string string_input = 8; + cast.channel.CastMessage cast_message = 9; + CreateVirtualConnectionRequestInput + create_virtual_connection_request_input = 10; + CreateGetAppAvailabilityRequestInput + create_get_app_availability_request_input = 11; + GetRequestIdFromResponseInput get_request_id_from_response_input = 12; + GetLaunchSessionResponseInput get_launch_session_response_input = 13; + ParseMessageTypeFromPayloadInput parse_message_type_from_payload_input = 14; + CreateReceiverStatusRequestInput create_receiver_status_request_input = 15; + } +} + +// Message used to generate a plausible but meaningless instance of +// base::Value. +message JunkValue { + message Field { + required string name = 1; + oneof value { + int32 int_value = 2; + string string_value = 3; + float float_value = 4; + bool bool_value = 5; + } + } + + repeated Field field = 1; +} |