Update includes and BUILD

This commit is contained in:
Lu Chen
2020-02-05 11:21:51 -08:00
parent 5c42bf9b7f
commit ac564bb46f
50 changed files with 510 additions and 377 deletions

View File

@@ -80,6 +80,12 @@ class CertificateClientCert : public ClientCert {
widevine::ClientIdentification::TokenType type() const override {
return ClientIdentification::DRM_DEVICE_CERTIFICATE;
}
const std::string& encrypted_unique_id() const override {
return device_cert_.rot_id().encrypted_unique_id();
}
const std::string& unique_id_hash() const override {
return device_cert_.rot_id().unique_id_hash();
}
private:
std::unique_ptr<ClientCertAlgorithm> algorithm_;

View File

@@ -68,6 +68,8 @@ class ClientCert {
virtual bool signed_by_provisioner() const = 0;
virtual uint32_t system_id() const = 0;
virtual widevine::ClientIdentification::TokenType type() const = 0;
virtual const std::string& encrypted_unique_id() const = 0;
virtual const std::string& unique_id_hash() const = 0;
};
} // namespace widevine

View File

@@ -22,29 +22,50 @@ using oemcrypto_core_message::serialize::
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
using widevine::Sha256_Hash;
// TODO(user): Check the Core*RequestFromMessage and
// CreateCore*ResponseFromProto return value when b/148472911 is fixed.
namespace widevine {
namespace core_message_util {
void GetCoreProvisioningResponse(const std::string& provisioning_response,
std::string* core_message) {
bool GetCoreProvisioningResponse(
const std::string& serialized_provisioning_response,
const std::string& request_core_message,
std::string* response_core_message) {
oemcrypto_core_message::ODK_ProvisioningRequest odk_provisioning_request;
CoreProvisioningRequestFromMessage(*core_message, &odk_provisioning_request);
CreateCoreProvisioningResponseFromProto(
provisioning_response, odk_provisioning_request, core_message);
CoreProvisioningRequestFromMessage(request_core_message,
&odk_provisioning_request);
CreateCoreProvisioningResponseFromProto(serialized_provisioning_response,
odk_provisioning_request,
response_core_message);
return true;
}
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message) {
bool GetCoreRenewalOrReleaseLicenseResponse(
const std::string& request_core_message,
std::string* response_core_message) {
oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
CoreRenewalRequestFromMessage(*core_message, &odk_renewal_request);
CreateCoreRenewalResponse(odk_renewal_request, core_message);
CoreRenewalRequestFromMessage(request_core_message, &odk_renewal_request);
// TODO(user): This function is going to need to know what the renewal
// license is, and extract the renewal duration. This should be the sum of
// renewal_delay_seconds + 2 * renewal_recovery_duration_seconds. Or if you
// want, we could also create CreateCoreRenewalResponseFromProto -- is that
// better?
uint64_t renewal_duration_seconds = 3600; // I just made this up for now.
CreateCoreRenewalResponse(odk_renewal_request, renewal_duration_seconds,
response_core_message);
return true;
}
void GetCoreNewLicenseResponse(const std::string& license,
std::string* core_message) {
bool GetCoreNewLicenseResponse(const std::string& license,
const std::string& request_core_message,
const bool nonce_required,
std::string* response_core_message) {
oemcrypto_core_message::ODK_LicenseRequest odk_license_request;
CoreLicenseRequestFromMessage(*core_message, &odk_license_request);
std::string core_request_sha256 = Sha256_Hash(*core_message);
CoreLicenseRequestFromMessage(request_core_message, &odk_license_request);
std::string core_request_sha256 = Sha256_Hash(request_core_message);
CreateCoreLicenseResponseFromProto(license, odk_license_request,
core_request_sha256, core_message);
core_request_sha256, nonce_required,
response_core_message);
return true;
}
} // namespace core_message_util

View File

@@ -13,20 +13,27 @@
namespace widevine {
namespace core_message_util {
// Gets the core message from |provisioning_response|. The output is held in
// |core_message|. The provisioning response to be sent will be updated with
// this |core_message|.
void GetCoreProvisioningResponse(const std::string& provisioning_response,
std::string* core_message);
// Gets the |response_core_message| by parsing |request_core_message| and
// |serialized_provisioning_response|. The output is held in
// |response_core_message|.
bool GetCoreProvisioningResponse(
const std::string& serialized_provisioning_response,
const std::string& request_core_message,
std::string* response_core_message);
// Gets the core message for renewal or release license response. The output
// is held in |core_message|.
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message);
// Gets the |response_core_message| by parsing |request_core_message| for
// release and renewal response. The output is held in |response_core_message|.
bool GetCoreRenewalOrReleaseLicenseResponse(
const std::string& request_core_message,
std::string* response_core_message);
// Gets the core message from |license|. The output is held in |core_message|.
// The license to be sent will be updated with this |core_message|.
void GetCoreNewLicenseResponse(const std::string& license,
std::string* core_message);
// Gets the |response_core_message| by parsing |request_core_message| and
// |license| for new license response. The output is held in
// |response_core_message|.
bool GetCoreNewLicenseResponse(const std::string& license,
const std::string& request_core_message,
const bool nonce_required,
std::string* response_core_message);
} // namespace core_message_util
} // namespace widevine

View File

@@ -78,6 +78,8 @@ class MockClientCert : public ClientCert {
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
MOCK_CONST_METHOD0(encrypted_unique_id, const std::string &());
MOCK_CONST_METHOD0(unique_id_hash, const std::string &());
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message,
const std::string &signature,

View File

@@ -43,6 +43,10 @@ class KeyboxClientCert : public ClientCert {
widevine::ClientIdentification::TokenType type() const override {
return ClientIdentification::KEYBOX;
}
const std::string& encrypted_unique_id() const override {
return unimplemented_;
}
const std::string& unique_id_hash() const override { return unimplemented_; }
// Set the system-wide pre-provisioning keys; argument must be human-readable
// hex digits.

View File

@@ -11,6 +11,7 @@
#include "common/rot_id_generator.h"
#include <memory>
#include <vector>
#include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h"
@@ -109,9 +110,9 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
// Verify hashed unique id matches.
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
EXPECT_TRUE(IsRotIdRevoked(root_of_trust_id.encrypted_unique_id(),
kTestSystemId, root_of_trust_id.unique_id_hash(),
{unique_id_hash}));
EXPECT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
root_of_trust_id.encrypted_unique_id(), kTestSystemId,
root_of_trust_id.unique_id_hash(), {unique_id_hash}));
}
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
@@ -156,7 +157,7 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
// Verify hashed unique id matches.
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
EXPECT_TRUE(IsRotIdRevoked(
EXPECT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
second_root_of_trust_id.encrypted_unique_id(), kTestSystemId,
second_root_of_trust_id.unique_id_hash(), {unique_id_hash}));
}

View File

@@ -21,25 +21,6 @@
namespace widevine {
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
const std::string& rot_id_hash,
const std::vector<std::string>& revoked_ids) {
// This could conceivably happen for legacy DRM certificates without a ROT id.
// No need to match if there's nothing to match against.
if (encrypted_unique_id.empty() || rot_id_hash.empty()) {
return false;
}
for (const auto& revoked_id : revoked_ids) {
std::string revoked_hash =
GenerateRotIdHash(encrypted_unique_id, system_id, revoked_id);
if (rot_id_hash == revoked_hash) {
return true;
}
}
return false;
}
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
const std::string& unique_id_hash) {
if (salt.empty() || unique_id_hash.empty()) {

View File

@@ -21,15 +21,6 @@
namespace widevine {
// Helper function that compares the |rot_id_hash| to a hash of each of the
// |revoked_ids|. The |revoked_ids| are the unique id hash (aka inner hash)
// values as defined in the spec at go/wv-kb-id. The |encrypted_unique_id| and
// |system_id| are used to compute the hash of each of the |revoked_ids|.
// Returns true if any of the revoked_ids match.
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
const std::string& rot_id_hash,
const std::vector<std::string>& revoked_ids);
// Helper function that generates the hash for the ROT id from the
// |unique_id_hash|, the |system_id| and the |salt|. |salt| is typically an
// encrypted unique id. Since we use an ephemeral eliptic curve key as part of
@@ -41,5 +32,28 @@ bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
const std::string& unique_id_hash);
// Helper function that compares the |rot_id_hash| to a hash of each of the
// |revoked_ids|. The |revoked_ids| are the unique id hash (aka inner hash)
// values as defined in the spec at go/wv-kb-id. The |encrypted_unique_id| and
// |system_id| are used to compute the hash of each of the |revoked_ids|.
// Returns true if any of the revoked_ids match.
template <typename V>
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
const std::string& rot_id_hash, const V& revoked_ids) {
// This could conceivably happen for legacy DRM certificates without a ROT id.
// No need to match if there's nothing to match against.
if (encrypted_unique_id.empty() || rot_id_hash.empty()) {
return false;
}
for (const auto& revoked_id : revoked_ids) {
if (GenerateRotIdHash(encrypted_unique_id, system_id, revoked_id) ==
rot_id_hash) {
return true;
}
}
return false;
}
} // namespace widevine
#endif // COMMON_ROT_ID_UTIL_H_

View File

@@ -31,28 +31,28 @@ constexpr uint32_t kOtherFakeSystemId = 9876;
namespace widevine {
TEST(RotIdUtilTest, IsRotIdRevokedMatches) {
ASSERT_TRUE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
ASSERT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
kFakeEncryptedId, kFakeSystemId, absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
}
TEST(RotIdUtilTest, IsRotIdRevokedNoMatchSystemId) {
ASSERT_FALSE(
IsRotIdRevoked(kFakeEncryptedId, kOtherFakeSystemId,
absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
kFakeEncryptedId, kOtherFakeSystemId,
absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
}
TEST(RotIdUtilTest, IsRotIdRevokedNoMatch) {
ASSERT_FALSE(IsRotIdRevoked(
ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
{"NO MATCH UNIQUE ID HASH 1", "NO MATCH UNIQUE ID HASH 2"}));
}
TEST(RotIdUtilTest, IsRotIdRevokedEmptyList) {
ASSERT_FALSE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
kFakeUniqueIdHash,
{/* Intentionally empty vector */}));
ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
{/* Intentionally empty vector */}));
}
// This test really only ensures the stability of the implementation. If the

View File

@@ -86,10 +86,10 @@ class SecurityProfileList {
mutable absl::Mutex mutex_;
// Widevine security profiles
std::vector<SecurityProfile> security_profiles_ GUARDED_BY(mutex_);
std::vector<SecurityProfile> security_profiles_ ABSL_GUARDED_BY(mutex_);
// Custom security profiles
std::map<std::string, SecurityProfile> custom_security_profiles_
GUARDED_BY(mutex_);
ABSL_GUARDED_BY(mutex_);
};
} // namespace widevine

View File

@@ -46,7 +46,6 @@ cc_binary(
srcs = ["wv_cas_ecm_example.cc"],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
"//media_cas_packager_sdk/public:wv_cas_ecm",
"//media_cas_packager_sdk/public:wv_cas_types",
],
@@ -57,6 +56,8 @@ cc_binary(
srcs = ["wv_cas_key_fetcher_example.cc"],
deps = [
"//base",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/flags:parse",
"//common:status",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//protos/public:media_cas_encryption_cc_proto",
@@ -66,9 +67,5 @@ cc_binary(
cc_binary(
name = "wv_cas_types_example",
srcs = ["wv_cas_types_example.cc"],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
"//media_cas_packager_sdk/public:wv_cas_types",
],
deps = ["//media_cas_packager_sdk/public:wv_cas_types"],
)

View File

@@ -384,6 +384,30 @@ constexpr char kTestStreamErrorResponse[] = {
'\x00', '\x00' // parameter_value: actual value varies.
};
constexpr char kTestEcmgChannelSetupWrongParameterLength[] = {
'\x03', // protocol_version
'\x00', '\x01', // message_type - Channel_setup
'\x00', '\x0e', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
'\x00', '\x02', // parameter_length -- Should be \x04
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
};
constexpr char kTestEcmgChannelSetupWrongMessageLength[] = {
'\x03', // protocol_version
'\x00', '\x01', // message_type - Channel_setup
'\x00', '\xee', // message_length -- Should be \x0e
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
'\x00', '\x04', // parameter_length -- Should be \x04
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
};
} // namespace cas
} // namespace widevine

View File

@@ -91,7 +91,10 @@ const char kTestEmmgDataProvision[] = {
'\x00', '\x01', // parameter_value
'\x00', '\x05', // parameter_type - datagram
'\x00', '\xbc', // parameter_length
'\x47', '\x5f', '\xff', '\x10', '\x00', '\x82', '\x70', '\x61', '\x00',
'\x47', '\x40', '\x00', '\x10', '\x0a', '\x0d', '\x77', '\x69', '\x64',
'\x65', '\x76', '\x69', '\x6e', '\x65', '\x5f', '\x74', '\x65', '\x73',
'\x74', '\x12', '\x09', '\x43', '\x61', '\x73', '\x54', '\x73', '\x46',
'\x61', '\x6b', '\x65', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
@@ -102,16 +105,13 @@ const char kTestEmmgDataProvision[] = {
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x83', '\x70', '\x61',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01'};
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};
const char kTestEmmgStreamCloseRequest[] = {
'\x02', // protocol_version

View File

@@ -8,11 +8,15 @@
// Example of how to use the wv_cas_ecm library.
#include <stddef.h>
#include <stdio.h>
#include <cassert>
#include <fstream>
#include <iostream>
#include <string>
#include <cstdint>
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
@@ -96,11 +100,11 @@ int main(int argc, char** argv) {
std::cout << std::endl;
}
// Write ECM TS Packet to a file.
std::ofstream file;
file.open(kOutputFile, std::ios_base::binary);
assert(file.is_open());
file.write(reinterpret_cast<char *>(packet), kTsPacketSize);
file.close();
std::ofstream file;
file.open(kOutputFile, std::ios_base::binary);
assert(file.is_open());
file.write(reinterpret_cast<char*>(packet), kTsPacketSize);
file.close();
return 0;
}

View File

@@ -7,36 +7,39 @@
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string>
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "common/status.h"
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
#include "protos/public/media_cas_encryption.pb.h"
DEFINE_string(content_id, "21140844", "Content ID");
DEFINE_bool(key_rotation, true, "Whether key rotation is enabled");
DEFINE_string(track_type, "SD", "Provider name");
ABSL_FLAG(std::string, content_id, "21140844", "Content ID");
ABSL_FLAG(bool, key_rotation, true, "Whether key rotation is enabled");
ABSL_FLAG(std::string, track_type, "SD", "Provider name");
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty())
absl::ParseCommandLine(argc, argv);
CHECK(!absl::GetFlag(FLAGS_content_id).empty() &&
!absl::GetFlag(FLAGS_track_type).empty())
<< "Flags 'content_id' and 'track_type' are required";
// Required flags in key fetcher.
CHECK(!FLAGS_license_server.empty() && !FLAGS_signing_provider.empty() &&
!FLAGS_signing_key.empty() && !FLAGS_signing_iv.empty())
CHECK(!absl::GetFlag(FLAGS_license_server).empty() &&
!absl::GetFlag(FLAGS_signing_provider).empty() &&
!absl::GetFlag(FLAGS_signing_key).empty() &&
!absl::GetFlag(FLAGS_signing_iv).empty())
<< "Flags 'license_server', 'signing_provider', 'signing_key' "
"and 'signing_iv' are required";
std::string request_str;
widevine::CasEncryptionRequest request;
request.set_provider(FLAGS_signing_provider);
request.set_content_id(FLAGS_content_id);
request.set_key_rotation(FLAGS_key_rotation);
request.set_provider(absl::GetFlag(FLAGS_signing_provider));
request.set_content_id(absl::GetFlag(FLAGS_content_id));
request.set_key_rotation(absl::GetFlag(FLAGS_key_rotation));
// Only 1 track in this example.
request.add_track_types(FLAGS_track_type);
request.add_track_types(absl::GetFlag(FLAGS_track_type));
LOG(INFO) << "Request: " << request.ShortDebugString();
if (!request.SerializeToString(&request_str)) {
LOG(ERROR) << "Failed to serialize request";

View File

@@ -10,6 +10,7 @@
#include <iostream>
#include <string>
#include <vector>
#include "media_cas_packager_sdk/public/wv_cas_types.h"

View File

@@ -26,13 +26,11 @@ cc_library(
hdrs = ["ecm.h"],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//common:aes_cbc_util",
"//common:status",
"//common:string_util",
"//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_cc_proto",
"//protos/public:media_cas_encryption_cc_proto",
],
)
@@ -44,8 +42,6 @@ cc_test(
deps = [
":ecm",
"//testing:gunit_main",
"//common:status",
"//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_encryption_cc_proto",
],
)
@@ -57,7 +53,6 @@ cc_library(
deps = [
":ecm",
"//base",
"@abseil_repo//absl/base:core_headers",
"//common:status",
],
)
@@ -91,15 +86,12 @@ cc_library(
":simulcrypt_util",
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/container:node_hash_map",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/strings:str_format",
"//common:crypto_util",
"//common:random_util",
"//common:status",
"//example:constants",
"//media_cas_packager_sdk/public:wv_cas_ecm",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//media_cas_packager_sdk/public:wv_cas_types",
@@ -132,12 +124,13 @@ cc_library(
],
deps = [
":simulcrypt_util",
":ts_packet",
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/strings:str_format",
"//common:status",
"//example:test_emmg_messages",
"//protos/public:media_cas_cc_proto",
],
)
@@ -182,10 +175,7 @@ cc_library(
hdrs = [
"mpeg2ts.h",
],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
],
deps = ["//base"],
)
cc_library(
@@ -198,8 +188,6 @@ cc_library(
deps = [
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"//common:status",
],
)
@@ -215,7 +203,6 @@ cc_library(
deps = [
":mpeg2ts",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//common:status",
"//common:string_util",
@@ -227,7 +214,6 @@ cc_test(
size = "small",
srcs = ["ts_packet_test.cc"],
deps = [
":mpeg2ts",
":ts_packet",
"//base",
"//testing:gunit_main",
@@ -245,7 +231,6 @@ cc_library(
":mpeg2ts",
":ts_packet",
"//base",
"@abseil_repo//absl/base:core_headers",
"//common:status",
],
)
@@ -258,6 +243,8 @@ cc_test(
],
deps = [
":util",
"//base",
"//testing:gunit_main",
"//common:status",
],
)

View File

@@ -8,14 +8,13 @@
#include "media_cas_packager_sdk/internal/ecm.h"
#include <stddef.h>
#include <bitset>
#include <utility>
#include <vector>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "common/aes_cbc_util.h"
#include "common/status.h"
#include "common/string_util.h"
#include "protos/public/media_cas_encryption.pb.h"

View File

@@ -16,9 +16,9 @@
#include <utility>
#include <vector>
#include <cstdint>
#include "common/status.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "protos/public/media_cas.pb.h"
namespace widevine {
namespace cas {

View File

@@ -8,9 +8,6 @@
#include "media_cas_packager_sdk/internal/ecm_generator.h"
#include <memory>
#include <string>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/memory/memory.h"

View File

@@ -8,14 +8,8 @@
#include "media_cas_packager_sdk/internal/ecm.h"
#include <stddef.h>
#include <string>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "common/status.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "protos/public/media_cas_encryption.pb.h"
using ::testing::Return;

View File

@@ -8,22 +8,13 @@
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "common/crypto_util.h"
#include "common/random_util.h"
#include "common/status.h"
#include "example/constants.h"
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
@@ -32,7 +23,6 @@
#include "media_cas_packager_sdk/internal/util.h"
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
// CA System ID for Widevine.
static constexpr uint16_t kWidevineSystemId = 0x4AD4;
@@ -96,7 +86,7 @@ Status ProcessPrivateParameters(const char* const request, uint16_t param_type,
break;
case AGE_RESTRICTION:
if (param_length != AGE_RESTRICTION_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -106,7 +96,7 @@ Status ProcessPrivateParameters(const char* const request, uint16_t param_type,
case ENTITLEMENT_ID_KEY_COMBINATION: {
if (param_length !=
kEntitlementKeyIdSizeBytes + kEntitlementKeyValueSizeBytes) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -185,7 +175,7 @@ Status HandleParameters(const char* const request, size_t request_length,
}
case CP_DURATION:
if (param_length != CP_DURATION_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -194,7 +184,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break;
case CP_NUMBER:
if (param_length != CP_NUMBER_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -208,7 +198,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break;
case ECM_CHANNEL_ID:
if (param_length != ECM_CHANNEL_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -217,7 +207,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break;
case ECM_ID:
if (param_length != ECM_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -226,7 +216,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break;
case ECM_STREAM_ID:
if (param_length != ECM_STREAM_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -235,7 +225,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break;
case NOMINAL_CP_DURATION:
if (param_length != NOMINAL_CP_DURATION_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -244,7 +234,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break;
case SUPER_CAS_ID:
if (param_length != SUPER_CAS_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
@@ -357,10 +347,14 @@ uint16_t StatusToDvbErrorCode(const Status& status) {
return 0;
}
switch (status.error_code()) {
case error::FAILED_PRECONDITION:
return INCONSISTENT_LENGTH_FOR_DVB_PARAMETER;
case error::INVALID_ARGUMENT:
return INVALID_VALUE_FOR_DVB_PARAMETER;
case error::NOT_FOUND:
return MISSING_MANDATORY_DVB_PARAMETER;
case error::UNIMPLEMENTED:
return UNKNOWN_PARAMETER_TYPE_VALUE;
case error::INTERNAL:
default:
return UNKNOWN_ERROR;
@@ -454,22 +448,8 @@ void EcmgClientHandler::HandleRequest(const char* const request, char* response,
Status status = HandleParameters(request + offset, request_length, &params);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
switch (status.error_code()) {
case error::INVALID_ARGUMENT:
// TODO(user): Should use INCONSISTENT_LENGTH_FOR_DVB_PARAMETER in most
// cases.
BuildChannelError(params.ecm_channel_id,
INVALID_VALUE_FOR_DVB_PARAMETER,
status.error_message(), response, response_length);
break;
case error::UNIMPLEMENTED:
BuildChannelError(params.ecm_channel_id, UNKNOWN_PARAMETER_TYPE_VALUE,
status.error_message(), response, response_length);
break;
default:
BuildChannelError(params.ecm_channel_id, UNKNOWN_ERROR,
status.error_message(), response, response_length);
}
BuildChannelError(params.ecm_channel_id, StatusToDvbErrorCode(status),
status.error_message(), response, response_length);
return;
}
switch (request_type) {
@@ -535,7 +515,7 @@ void EcmgClientHandler::HandleChannelSetup(const EcmgParameters& params,
channel_id_ = params.ecm_channel_id;
channel_id_set_ = true;
Status status = UpdatePrivateParameters(params, false);
Status status = UpdateChannelPrivateParameters(params);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
BuildChannelError(params.ecm_channel_id, StatusToDvbErrorCode(status),
@@ -595,7 +575,7 @@ void EcmgClientHandler::HandleStreamSetup(const EcmgParameters& params,
streams_info_[params.ecm_stream_id] = absl::make_unique<EcmgStreamInfo>();
streams_info_[params.ecm_stream_id]->ecm_id = params.ecm_id;
Status status = UpdatePrivateParameters(params, true);
Status status = UpdateStreamPrivateParameters(params);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
@@ -684,7 +664,7 @@ void EcmgClientHandler::HandleCwProvision(const EcmgParameters& params,
}
// Update private parameters based on access_criteria if any.
Status status = UpdatePrivateParameters(params, true);
Status status = UpdateStreamPrivateParameters(params);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
@@ -721,47 +701,23 @@ void EcmgClientHandler::HandleCwProvision(const EcmgParameters& params,
params.cp_number, ecm_datagram, response, response_length);
}
Status EcmgClientHandler::UpdatePrivateParameters(const EcmgParameters& params,
bool stream_specific) {
EcmgStreamInfo* stream_info =
stream_specific ? streams_info_[params.ecm_stream_id].get() : nullptr;
Status EcmgClientHandler::UpdateCommonPrivateParameters(
const EcmgParameters& params) {
if (params.age_restriction != 0xff) {
if (params.age_restriction > kMaxAllowedAgeRestriction) {
return {error::INVALID_ARGUMENT, "Age restriction too large."};
}
age_restriction_ = params.age_restriction;
}
if (!params.crypto_mode.empty()) {
if (!StringToCryptoMode(params.crypto_mode,
stream_specific ? &stream_info->crypto_mode
: &ecmg_config_->crypto_mode)) {
return {error::INVALID_ARGUMENT,
absl::StrCat("Unknown crypto mode: ", params.crypto_mode, ".")};
}
}
if (!params.track_types.empty()) {
track_types_.assign(params.track_types.begin(), params.track_types.end());
}
if (!params.stream_track_type.empty()) {
if (stream_specific) {
stream_info->track_type = params.stream_track_type;
} else {
LOG(WARNING) << "Ignoring stream track type received in channel config.";
}
}
if (!params.content_id.empty()) {
content_id_ = params.content_id;
}
if (!params.content_provider.empty()) {
content_provider_ = params.content_provider;
}
if (!params.content_ivs.empty()) {
if (params.content_ivs.size() < ecmg_config_->number_of_content_keys) {
return {error::INVALID_ARGUMENT,
@@ -778,19 +734,54 @@ Status EcmgClientHandler::UpdatePrivateParameters(const EcmgParameters& params,
return {error::INVALID_ARGUMENT, "Size of content ivs must match."};
}
}
if (stream_specific) {
stream_info->content_ivs.assign(params.content_ivs.begin(),
params.content_ivs.end());
} else {
content_ivs_.assign(params.content_ivs.begin(), params.content_ivs.end());
}
}
if (!params.entitlement_comb.empty()) {
entitlement_comb_.assign(params.entitlement_comb.begin(),
params.entitlement_comb.end());
}
return OkStatus();
}
Status EcmgClientHandler::UpdateChannelPrivateParameters(
const EcmgParameters& params) {
Status status = UpdateCommonPrivateParameters(params);
if (!status.ok()) {
return status;
}
if (!params.crypto_mode.empty()) {
if (!StringToCryptoMode(params.crypto_mode, &ecmg_config_->crypto_mode)) {
return {error::INVALID_ARGUMENT,
absl::StrCat("Unknown crypto mode: ", params.crypto_mode, ".")};
}
}
if (!params.content_ivs.empty()) {
content_ivs_.assign(params.content_ivs.begin(), params.content_ivs.end());
}
return OkStatus();
}
Status EcmgClientHandler::UpdateStreamPrivateParameters(
const EcmgParameters& params) {
DCHECK(streams_info_.contains(params.ecm_stream_id));
EcmgStreamInfo* stream_info = streams_info_[params.ecm_stream_id].get();
Status status = UpdateCommonPrivateParameters(params);
if (!status.ok()) {
return status;
}
if (!params.crypto_mode.empty()) {
if (!StringToCryptoMode(params.crypto_mode, &stream_info->crypto_mode)) {
return {error::INVALID_ARGUMENT,
absl::StrCat("Unknown crypto mode: ", params.crypto_mode, ".")};
}
}
if (!params.stream_track_type.empty()) {
stream_info->track_type = params.stream_track_type;
}
if (!params.content_ivs.empty()) {
stream_info->content_ivs.assign(params.content_ivs.begin(),
params.content_ivs.end());
}
return OkStatus();
}

View File

@@ -10,13 +10,8 @@
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
#include <cstddef>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include <cstdint>
@@ -116,12 +111,12 @@ class EcmgClientHandler {
size_t* response_length);
void HandleCwProvision(const EcmgParameters& params, char* response,
size_t* response_length);
// Update private paprameters using |params|. |stream_specific| indicates if
// |params| is for a single stream or the whole channel. If |stream_specific|
// is true, |params| will only affect values of this stream. If it is false,
// |param| will affect all streams of the channel.
Status UpdatePrivateParameters(const EcmgParameters& params,
bool stream_specific);
// Update private parameters using |params|.
Status UpdateChannelPrivateParameters(const EcmgParameters& params);
Status UpdateStreamPrivateParameters(const EcmgParameters& params);
Status UpdateCommonPrivateParameters(const EcmgParameters& params);
// Check if all required parameters have been set. If so, initialize |ecm_| by
// fetching entitlement keys.
Status CheckAndInitializeEcm(const EcmgParameters& params);

View File

@@ -8,9 +8,10 @@
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
#include <string>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
@@ -455,6 +456,81 @@ TEST_F(EcmgClientHandlerTest, WrongParameterChannelId) {
CheckStreamError(UNKNOWN_ECM_CHANNEL_ID_VALUE, response_, response_len_);
}
TEST_F(EcmgClientHandlerTest, WrongParameterCryptoMode) {
BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
"someCryptoMode", {kTrackTypesHD, kTrackTypesSD},
kContentId, kContentProvider, /*entitlements*/ {},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
CheckChannelError(INVALID_VALUE_FOR_DVB_PARAMETER, response_, response_len_);
}
TEST_F(EcmgClientHandlerTest, WrongParameterLengthSuperCasId) {
// Setup a channel with an unexpected super cas id length (2 instead of 4).
handler_->HandleRequest(kTestEcmgChannelSetupWrongParameterLength, response_,
&response_len_);
CheckChannelError(INCONSISTENT_LENGTH_FOR_DVB_PARAMETER, response_,
response_len_);
}
TEST_F(EcmgClientHandlerTest, NoEntitlementsNoContentIdProvider) {
BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
kCryptoMode, {kTrackTypesHD, kTrackTypesSD},
/*ContentId*/ "", /*ContentProvider*/ "",
/*entitlements*/ {}, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
BuildStreamSetupRequest(kChannelId, kStreamId, kEcmId, kNominalCpDuration,
kTrackTypesSD, {kContentKeyEven, kContentKeyEven},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));
const std::vector<EcmgCpCwCombination> cp_cw_combination = {
{kCpNumber, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}};
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
CheckStreamError(MISSING_MANDATORY_DVB_PARAMETER, response_, response_len_);
}
TEST_F(EcmgClientHandlerTest, NotEnoughInjectedEntitlements) {
BuildChannelSetupRequest(
kChannelId, kSuperCasId, kAgeRestriction, kCryptoMode,
{kTrackTypesHD, kTrackTypesSD},
/*ContentId*/ "", /*ContentProvider*/ "",
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
BuildStreamSetupRequest(kChannelId, kStreamId, kEcmId, kNominalCpDuration,
kTrackTypesSD, {kContentKeyEven, kContentKeyEven},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));
const std::vector<EcmgCpCwCombination> cp_cw_combination = {
{kCpNumber, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}};
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
CheckStreamError(MISSING_MANDATORY_DVB_PARAMETER, response_, response_len_);
}
TEST_F(EcmgClientHandlerTest, WrongMessageLength) {
// Setup a channel with a wrong message length specified (too large).
handler_->HandleRequest(kTestEcmgChannelSetupWrongMessageLength, response_,
&response_len_);
CheckChannelError(UNKNOWN_PARAMETER_TYPE_VALUE, response_, response_len_);
}
} // namespace
} // namespace cas
} // namespace widevine

View File

@@ -10,18 +10,19 @@
#include <sys/socket.h>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <iostream>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "example/test_emmg_messages.h"
#include "media_cas_packager_sdk/internal/emmg_constants.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
#include "media_cas_packager_sdk/internal/ts_packet.h"
#include "media_cas_packager_sdk/internal/util.h"
#include "protos/public/media_cas.pb.h"
namespace widevine {
namespace cas {
@@ -125,6 +126,36 @@ void Emmg::BuildStreamSetup() {
Host16ToBigEndian(request_ + 3, &total_param_length);
}
Status Emmg::GeneratePrivateData(const std::string& content_provider,
const std::string& content_id, uint8_t* buffer) {
DCHECK(buffer);
// Generate payload.
CaDescriptorPrivateData private_data;
private_data.set_provider(content_provider);
private_data.set_content_id(content_id);
std::string private_data_str = private_data.SerializeAsString();
std::string payload_filler(kMaxTsPayloadSize - private_data_str.size(), 0);
// Wrap the data with a TS header.
TsPacket ecm_packet;
ecm_packet.set_payload_unit_start_indicator(true);
ecm_packet.set_pid(0);
ecm_packet.set_payload(absl::StrCat(private_data_str, payload_filler));
ecm_packet.set_adaptation_field_control(TsPacket::kPayloadOnly);
ecm_packet.set_continuity_counter(continuity_counter_);
continuity_counter_ = ++continuity_counter_ & 0xf;
// And write the packet.
std::string ecm_ts_packet;
Status status = ecm_packet.Write(&ecm_ts_packet);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
return status;
}
memcpy(buffer, ecm_ts_packet.data(), ecm_ts_packet.size());
return OkStatus();
}
void Emmg::BuildDataProvision() {
bzero(request_, BUFFER_SIZE);
request_length_ = 0;
@@ -142,15 +173,11 @@ void Emmg::BuildDataProvision() {
simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_,
&request_length_);
// Add a fake TS packet.
uint16_t datagram_type = EMMG_DATAGRAM;
Host16ToBigEndian(request_ + request_length_, &datagram_type);
request_length_ += 2;
uint16_t param_length = sizeof(kTestEmmgTsPacket); // Should be 188.
Host16ToBigEndian(request_ + request_length_, &param_length);
request_length_ += 2;
memcpy(request_ + request_length_, kTestEmmgTsPacket, param_length);
request_length_ += param_length;
uint8_t datagram[kTsPacketSize];
GeneratePrivateData(emmg_config_->content_provider, emmg_config_->content_id,
datagram);
simulcrypt_util::AddParam(EMMG_DATAGRAM, datagram, kTsPacketSize, request_,
&request_length_);
uint16_t total_param_length = request_length_ - 5;
Host16ToBigEndian(request_ + 3, &total_param_length);

View File

@@ -10,13 +10,9 @@
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "glog/logging.h"
#include "common/status.h"
#define BUFFER_SIZE (1024)
@@ -32,6 +28,8 @@ struct EmmgConfig {
uint16_t data_stream_id;
uint16_t data_id;
uint8_t data_type;
std::string content_provider;
std::string content_id;
};
// A class that sends EMMG/PDG message to the MUX server.
@@ -62,6 +60,8 @@ class Emmg {
void SendStreamCloseRequest();
void SendChannelClose();
Status GeneratePrivateData(const std::string& content_provider,
const std::string& content_id, uint8_t* buffer);
void ReceiveResponseAndVerify(uint16_t expected_type);
void Send(uint16_t message_type);
@@ -72,6 +72,8 @@ class Emmg {
// |server_socket_fd| is a file descriptor we can use to communicate
// with the MUX server.
int server_socket_fd_;
// |continuity_counter| is incremented each time private data is generated.
int continuity_counter_ = 0;
};
} // namespace cas

View File

@@ -8,6 +8,12 @@
#include "media_cas_packager_sdk/internal/emmg.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <memory>
#include "testing/gunit.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_format.h"
@@ -38,6 +44,8 @@ class EmmgTest : public ::testing::Test {
config_.data_stream_id = 0x0001;
config_.data_id = 0x0001;
config_.data_type = 0x01;
config_.content_provider = "widevine_test";
config_.content_id = "CasTsFake";
emmg_ = absl::make_unique<TestableEmmg>(&config_);
}

View File

@@ -8,7 +8,6 @@
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h"
namespace widevine {

View File

@@ -9,6 +9,9 @@
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_
#include <string>
#include "common/status.h"
#include "media_cas_packager_sdk/internal/key_fetcher.h"
namespace widevine {

View File

@@ -13,7 +13,6 @@
#include <cstddef>
#include <cstdint>
#include "base/macros.h"
namespace widevine {
namespace cas {

View File

@@ -8,7 +8,6 @@
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
#include <cstddef>
#include <cstring>
#include "glog/logging.h"

View File

@@ -12,13 +12,8 @@
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "common/status.h"
namespace widevine {
namespace cas {

View File

@@ -12,7 +12,6 @@
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "common/status.h"
#include "common/string_util.h"
namespace widevine {

View File

@@ -33,11 +33,9 @@
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
#include <memory>
#include <string>
#include <cstdint>
#include "base/macros.h"
#include "absl/strings/string_view.h"
#include "common/status.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"

View File

@@ -8,12 +8,9 @@
#include "media_cas_packager_sdk/internal/ts_packet.h"
#include <string>
#include "base/macros.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
namespace widevine {
namespace cas {

View File

@@ -9,6 +9,7 @@
#include "media_cas_packager_sdk/internal/util.h"
#include <netinet/in.h>
#include <cstddef>
#include <cstring>

View File

@@ -60,7 +60,6 @@ cc_library(
deps = [
":wv_cas_types",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//common:status",
"//common:string_util",
@@ -77,7 +76,6 @@ cc_test(
srcs = ["wv_cas_ca_descriptor_test.cc"],
deps = [
":wv_cas_ca_descriptor",
":wv_cas_types",
"//testing:gunit_main",
"//protos/public:media_cas_cc_proto",
],
@@ -113,7 +111,6 @@ cc_test(
srcs = ["wv_cas_ecm_test.cc"],
deps = [
":wv_cas_ecm",
":wv_cas_types",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//media_cas_packager_sdk/internal:mpeg2ts",
@@ -131,10 +128,9 @@ cc_library(
deps = [
"//base",
"//external:protobuf",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/strings",
"@curl_repo//:curl",
"//util:error_space",
"//common:signature_util",
"//common:status",
"//media_cas_packager_sdk/internal:key_fetcher",
@@ -153,7 +149,9 @@ cc_test(
"//base",
"//external:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/strings",
"//common:status",
"//protos/public:media_cas_encryption_cc_proto",
],
)
@@ -189,8 +187,8 @@ cc_binary(
deps = [
":wv_cas_types",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/flags:parse",
"//media_cas_packager_sdk/internal:ecmg_client_handler",
],
)
@@ -200,6 +198,8 @@ cc_binary(
srcs = ["wv_emmg.cc"],
deps = [
"//base",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/flags:parse",
"//media_cas_packager_sdk/internal:emmg",
],
)

View File

@@ -14,7 +14,6 @@
#include "absl/strings/str_cat.h"
#include "common/status.h"
#include "common/string_util.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "protos/public/media_cas.pb.h"
namespace widevine {

View File

@@ -10,8 +10,10 @@
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_
#include <stddef.h>
#include <string>
#include <cstdint>
#include "media_cas_packager_sdk/public/wv_cas_types.h"
namespace widevine {

View File

@@ -8,10 +8,7 @@
#include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h"
#include <string>
#include "testing/gunit.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "protos/public/media_cas.pb.h"
using ::testing::Test;

View File

@@ -8,13 +8,15 @@
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <memory>
#include <utility>
#include <vector>
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "common/crypto_util.h"
#include "common/status.h"
#include "example/constants.h"
@@ -22,7 +24,6 @@
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
#include "media_cas_packager_sdk/internal/util.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
namespace widevine {
namespace cas {

View File

@@ -9,13 +9,9 @@
#ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_
#include <cstddef>
#include <list>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "media_cas_packager_sdk/public/wv_cas_types.h"
namespace widevine {

View File

@@ -8,11 +8,11 @@
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include <string.h>
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
using ::testing::Test;

View File

@@ -10,32 +10,32 @@
#include <stddef.h>
#include <string.h>
#include <string>
#include "gflags/gflags.h"
#include <cstdint>
#include "glog/logging.h"
#include "google/protobuf/util/json_util.h"
#include "absl/flags/flag.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "curl/curl.h"
#include "curl/easy.h"
#include "common/signature_util.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h"
using google::protobuf::util::JsonPrintOptions;
using google::protobuf::util::JsonStringToMessage;
using google::protobuf::util::MessageToJsonString;
DEFINE_string(
license_server, "",
"HTTP URL to the license server for making CAS encryption request");
DEFINE_string(signing_provider, "",
"Name of the provider signing the CAS encryption request");
DEFINE_string(signing_key, "",
"AES key (in hex) for signing the CAS encryption request");
DEFINE_string(signing_iv, "",
"AES iv (in hex) for signing the CAS encryption request");
ABSL_FLAG(std::string, license_server, "",
"HTTP URL to the license server for making CAS encryption request.");
ABSL_FLAG(std::string, signing_provider, "",
"Name of the provider signing the CAS encryption request.");
ABSL_FLAG(std::string, signing_key, "",
"AES key (in hex) for signing the CAS encryption request.");
ABSL_FLAG(std::string, signing_iv, "",
"AES iv (in hex) for signing the CAS encryption request.");
namespace widevine {
namespace cas {
@@ -43,8 +43,9 @@ namespace cas {
Status WvCasKeyFetcher::RequestEntitlementKey(
const std::string& request_string,
std::string* signed_response_string) const {
if (FLAGS_signing_provider.empty() || FLAGS_signing_key.empty() ||
FLAGS_signing_iv.empty()) {
if (absl::GetFlag(FLAGS_signing_provider).empty() ||
absl::GetFlag(FLAGS_signing_key).empty() ||
absl::GetFlag(FLAGS_signing_iv).empty()) {
return Status(
error::INVALID_ARGUMENT,
"Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty");
@@ -73,13 +74,14 @@ Status WvCasKeyFetcher::RequestEntitlementKey(
signed_request.set_request(request_json);
std::string signature;
if (!signature_util::GenerateAesSignature(
request_json, absl::HexStringToBytes(FLAGS_signing_key),
absl::HexStringToBytes(FLAGS_signing_iv), &signature)
request_json,
absl::HexStringToBytes(absl::GetFlag(FLAGS_signing_key)),
absl::HexStringToBytes(absl::GetFlag(FLAGS_signing_iv)), &signature)
.ok()) {
return Status(error::INTERNAL, "Failed to sign the request.");
}
signed_request.set_signature(signature);
signed_request.set_signer(FLAGS_signing_provider);
signed_request.set_signer(absl::GetFlag(FLAGS_signing_provider));
std::string signed_request_json;
// NOTE: MessageToJsonString will automatically converts the 'request' and
// 'signature' fields in SignedCasEncryptionRequest to base64, because they
@@ -126,14 +128,15 @@ size_t AppendToString(void* ptr, size_t size, size_t count,
Status WvCasKeyFetcher::MakeHttpRequest(const std::string& signed_request_json,
std::string* http_response_json) const {
CHECK(http_response_json);
if (FLAGS_license_server.empty()) {
if (absl::GetFlag(FLAGS_license_server).empty()) {
return Status(error::INVALID_ARGUMENT, "Flag 'license_server' is empty");
}
CURL* curl;
CURLcode curl_code;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, FLAGS_license_server.c_str());
curl_easy_setopt(curl, CURLOPT_URL,
absl::GetFlag(FLAGS_license_server).c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, signed_request_json.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, http_response_json);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &AppendToString);

View File

@@ -11,14 +11,10 @@
#include <string>
#include "gflags/gflags.h"
#include "absl/flags/declare.h"
#include "common/status.h"
#include "media_cas_packager_sdk/internal/key_fetcher.h"
DECLARE_string(license_server);
DECLARE_string(signing_provider);
DECLARE_string(signing_key);
DECLARE_string(signing_iv);
namespace widevine {
namespace cas {
@@ -55,4 +51,10 @@ class WvCasKeyFetcher : public KeyFetcher {
} // namespace cas
} // namespace widevine
// Exposed for testing and example.
ABSL_DECLARE_FLAG(std::string, license_server);
ABSL_DECLARE_FLAG(std::string, signing_provider);
ABSL_DECLARE_FLAG(std::string, signing_key);
ABSL_DECLARE_FLAG(std::string, signing_iv);
#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_KEY_FETCHER_H_

View File

@@ -8,12 +8,13 @@
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "google/protobuf/text_format.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/flags/flag.h"
#include "absl/strings/escaping.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h"
using testing::_;
@@ -63,9 +64,9 @@ class WvCasKeyFetcherTest : public ::testing::Test {
public:
WvCasKeyFetcherTest() {}
void SetUp() override {
FLAGS_signing_provider = kSigningProvider;
FLAGS_signing_key = kSingingKey;
FLAGS_signing_iv = kSingingIv;
absl::SetFlag(&FLAGS_signing_provider, kSigningProvider);
absl::SetFlag(&FLAGS_signing_key, kSingingKey);
absl::SetFlag(&FLAGS_signing_iv, kSingingIv);
CHECK(
google::protobuf::TextFormat::ParseFromString(kCasEncryptionRequest, &request_));

View File

@@ -8,7 +8,6 @@
#include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
namespace widevine {

View File

@@ -9,6 +9,7 @@
// Widevine MediaCAS ECMG server.
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
@@ -19,54 +20,37 @@
#include <iostream>
#include <string>
#include "gflags/gflags.h"
#include <cstdint>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
static constexpr int32_t kDefaultDelayStart = 200;
static constexpr int32_t kDefaultDelayStop = 200;
static constexpr int32_t kDefaultEcmRepPeriod = 100;
static constexpr int32_t kDefaultMaxCompTime = 100;
static constexpr int32_t kAccessCriteriaTransferMode = 0;
static constexpr int32_t kNumberOfContentKeys = 2;
static constexpr char kDefaultCryptoMode[] = "AesCtr";
static constexpr bool kDefaultUseFixedFetcher = false;
DEFINE_int32(port, 0, "Server port number");
ABSL_FLAG(int32_t, port, 1234, "Server port number.");
// ECMG related flags.
// TODO(user): Consider adding flags 'ac_delay_start', 'ac_delay_stop',
// 'transition_delay_start', 'transition_delay_stop'.
DEFINE_int32(
delay_start, kDefaultDelayStart,
"This flag sets the DVB SimulCrypt delay_start parameter, in milliseconds");
DEFINE_int32(
delay_stop, kDefaultDelayStop,
"This flag sets the DVB SimulCrypt delay_stop parameter, in milliseconds");
DEFINE_int32(
ecm_rep_period, kDefaultEcmRepPeriod,
"It sets the DVB SimulCrypt parameter ECM_rep_period, in milliseconds");
DEFINE_int32(
max_comp_time, kDefaultMaxCompTime,
"It sets the DVB SimulCrypt parameter max_comp_time, in milliseconds.");
DEFINE_int32(
access_criteria_transfer_mode, kAccessCriteriaTransferMode,
ABSL_FLAG(int32_t, delay_start, 200, "delay_start, in milliseconds.");
ABSL_FLAG(int32_t, delay_stop, 200, "delay_stop, in milliseconds.");
ABSL_FLAG(int32_t, ecm_rep_period, 100, "ECM_rep_period, in milliseconds.");
ABSL_FLAG(int32_t, max_comp_time, 100, "max_comp_time, in milliseconds.");
ABSL_FLAG(
int32_t, access_criteria_transfer_mode, 0,
"If it equals 0, it indicates that the access_criteria parameter is "
"required in the CW_provision message only when the contents of this "
"parameter change. If it equals 1, it indicates that the ECMG requires the "
"access_criteria parameter be present in each CW_provision message.");
DEFINE_int32(number_of_content_keys, kNumberOfContentKeys,
"It sets the number of content keys (CwPerMsg). Must be 1 (single "
"key) or 2 (key rotation enabled). It also sets LeadCw as "
"number_of_content_keys - 1.");
DEFINE_string(crypto_mode, kDefaultCryptoMode,
"Encryption mode. Choices are \"AesCtr\", \"AesCbc\", "
"\"DvbCsa2\", \"DvbCsa3\", \"AesOfb\", \"AesScte\".");
DEFINE_bool(use_fixed_fetcher, kDefaultUseFixedFetcher,
"Use fixed fetcher to fetch mocked entitlement licenses for "
"testing purposes.");
ABSL_FLAG(int32_t, number_of_content_keys, 2,
"It sets the number of content keys (CwPerMsg). Must be 1 (single "
"key) or 2 (key rotation enabled). It also sets LeadCw as "
"number_of_content_keys - 1.");
ABSL_FLAG(std::string, crypto_mode, "AesCtr",
"Encryption mode. Choices are \"AesCtr\", \"AesCbc\", "
"\"DvbCsa2\", \"DvbCsa3\", \"AesOfb\", \"AesScte\".");
ABSL_FLAG(bool, use_fixed_fetcher, false,
"If set, use fixed fetcher to fetch mocked entitlement licenses when "
"needed for testing purposes.");
#define LISTEN_QUEUE_SIZE (20)
#define BUFFER_SIZE (1024)
@@ -76,15 +60,20 @@ using widevine::cas::EcmgConfig;
void BuildEcmgConfig(EcmgConfig* config) {
DCHECK(config);
config->delay_start = FLAGS_delay_start;
config->delay_stop = FLAGS_delay_stop;
config->ecm_rep_period = FLAGS_ecm_rep_period;
config->max_comp_time = FLAGS_max_comp_time;
config->access_criteria_transfer_mode = FLAGS_access_criteria_transfer_mode;
config->number_of_content_keys = FLAGS_number_of_content_keys;
CHECK(StringToCryptoMode(FLAGS_crypto_mode, &config->crypto_mode))
config->delay_start = absl::GetFlag(FLAGS_delay_start);
config->delay_stop = absl::GetFlag(FLAGS_delay_stop);
config->ecm_rep_period = absl::GetFlag(FLAGS_ecm_rep_period);
config->max_comp_time = absl::GetFlag(FLAGS_max_comp_time);
config->access_criteria_transfer_mode =
absl::GetFlag(FLAGS_access_criteria_transfer_mode);
CHECK(absl::GetFlag(FLAGS_number_of_content_keys) == 1 ||
absl::GetFlag(FLAGS_number_of_content_keys) == 2)
<< "--number_of_content_keys must be 1 or 2.";
config->number_of_content_keys = absl::GetFlag(FLAGS_number_of_content_keys);
CHECK(StringToCryptoMode(absl::GetFlag(FLAGS_crypto_mode),
&config->crypto_mode))
<< "Unknown crypto mode.";
config->use_fixed_fetcher = FLAGS_use_fixed_fetcher;
config->use_fixed_fetcher = absl::GetFlag(FLAGS_use_fixed_fetcher);
}
void PrintMessage(const std::string& description, const char* const message,
@@ -126,11 +115,7 @@ void ServeClient(int socket_fd, EcmgClientHandler* ecmg) {
}
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(FLAGS_port != 0) << "need --port";
CHECK(FLAGS_number_of_content_keys == 1 || FLAGS_number_of_content_keys == 2)
<< "--number_of_content_keys must be 1 or 2.";
absl::ParseCommandLine(argc, argv);
EcmgConfig ecmg_config;
BuildEcmgConfig(&ecmg_config);
@@ -139,7 +124,7 @@ int main(int argc, char** argv) {
bzero(reinterpret_cast<char*>(&server_address), sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(FLAGS_port);
server_address.sin_port = htons(absl::GetFlag(FLAGS_port));
// Create a listening socket.
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);

View File

@@ -12,54 +12,69 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include "gflags/gflags.h"
#include <cstring>
#include <string>
#include <cstdint>
#include "glog/logging.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "media_cas_packager_sdk/internal/emmg.h"
DEFINE_string(mux_address, "", "Mux server address");
DEFINE_int32(mux_port, 0, "Mux server port number");
// EMMG specfic configs.
DEFINE_int32(client_id, 0, "EMMG client_id");
DEFINE_int32(section_tspkt_flag, 1, "EMMG section_tspkt_flag");
DEFINE_int32(data_channel_id, 0, "EMMG data_channel_id");
DEFINE_int32(data_stream_id, 0, "EMMG data_stream_id");
DEFINE_int32(data_id, 0, "EMMG data_id");
ABSL_FLAG(std::string, mux_address, "", "Mux server address.");
ABSL_FLAG(int32_t, mux_port, 0, "Mux server port number.");
// EMMG specific configs.
// To facilitate uniqueness of client_id, the following rules apply:
// in the case of EMMs or other CA related data, the two first bytes of the
// client_id should be equal to the two bytes of the corresponding CA_system_id.
ABSL_FLAG(int32_t, client_id, 0x4AD40000, "EMMG client_id.");
// TODO(user): Currently it can only support section_tspkt_flag = 1.
ABSL_FLAG(int32_t, section_tspkt_flag, 1,
"EMMG section_tspkt_flag. Currently it can only be set to 1.");
ABSL_FLAG(int32_t, data_channel_id, 0, "EMMG data_channel_id.");
ABSL_FLAG(int32_t, data_stream_id, 0, "EMMG data_stream_id.");
ABSL_FLAG(int32_t, data_id, 0, "EMMG data_id.");
// data_type: type of data carried in the datagram in the stream:
// 0x00: EMM;
// 0x01: private data;
// 0x02: DVB reserved (ECM);
// other values: DVB reserved.
DEFINE_int32(data_type, 0, "EMMG data_type");
ABSL_FLAG(int32_t, data_type, 1, "EMMG data_type");
ABSL_FLAG(std::string, content_provider, "", "Content provider");
ABSL_FLAG(std::string, content_id, "", "Content id");
#define BUFFER_SIZE (1024)
void BuildEmmgConfig(widevine::cas::EmmgConfig *config) {
CHECK(config);
config->client_id = FLAGS_client_id;
config->section_tspkt_flag = FLAGS_section_tspkt_flag;
config->data_channel_id = FLAGS_data_channel_id;
config->data_stream_id = FLAGS_data_stream_id;
config->data_id = FLAGS_data_id;
config->data_type = FLAGS_data_type;
config->client_id = absl::GetFlag(FLAGS_client_id);
config->section_tspkt_flag = absl::GetFlag(FLAGS_section_tspkt_flag);
config->data_channel_id = absl::GetFlag(FLAGS_data_channel_id);
config->data_stream_id = absl::GetFlag(FLAGS_data_stream_id);
config->data_id = absl::GetFlag(FLAGS_data_id);
config->data_type = absl::GetFlag(FLAGS_data_type);
config->content_provider = absl::GetFlag(FLAGS_content_provider);
config->content_id = absl::GetFlag(FLAGS_content_id);
}
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(!FLAGS_mux_address.empty()) << "flag --mux_address is required";
CHECK(FLAGS_mux_port != 0) << "flag --mux_port is required";
absl::ParseCommandLine(argc, argv);
CHECK(!absl::GetFlag(FLAGS_mux_address).empty())
<< "flag --mux_address is required";
CHECK(absl::GetFlag(FLAGS_mux_port) != 0) << "flag --mux_port is required";
// Create server address.
struct hostent ret;
struct hostent *server;
char buffer[BUFFER_SIZE];
int h_errnop = 0;
int return_val = gethostbyname_r(FLAGS_mux_address.c_str(), &ret, buffer,
sizeof(buffer), &server, &h_errnop);
int return_val =
gethostbyname_r(absl::GetFlag(FLAGS_mux_address).c_str(), &ret, buffer,
sizeof(buffer), &server, &h_errnop);
if (return_val != 0 || server == nullptr) {
LOG(FATAL) << "gethostbyname_r failed for " << FLAGS_mux_address;
LOG(FATAL) << "gethostbyname_r failed for "
<< absl::GetFlag(FLAGS_mux_address);
}
struct sockaddr_in server_address;
bzero(reinterpret_cast<char *>(&server_address), sizeof(server_address));
@@ -67,7 +82,7 @@ int main(int argc, char **argv) {
bcopy(server->h_addr,
reinterpret_cast<char *>(&server_address.sin_addr.s_addr),
server->h_length);
server_address.sin_port = htons(FLAGS_mux_port);
server_address.sin_port = htons(absl::GetFlag(FLAGS_mux_port));
// Connect to server.
int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);