/* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/rtp_rtcp/source/rtcp_packet/sdes.h" #include "test/gtest.h" #include "test/rtcp_packet_parser.h" using webrtc::rtcp::Sdes; namespace webrtc { namespace { const uint32_t kSenderSsrc = 0x12345678; const uint8_t kPadding = 0; const uint8_t kTerminatorTag = 0; const uint8_t kCnameTag = 1; const uint8_t kNameTag = 2; const uint8_t kEmailTag = 3; } // namespace TEST(RtcpPacketSdesTest, CreateAndParseWithoutChunks) { Sdes sdes; rtc::Buffer packet = sdes.Build(); Sdes parsed; EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed)); EXPECT_EQ(0u, parsed.chunks().size()); } TEST(RtcpPacketSdesTest, CreateAndParseWithOneChunk) { const std::string kCname = "alice@host"; Sdes sdes; EXPECT_TRUE(sdes.AddCName(kSenderSsrc, kCname)); rtc::Buffer packet = sdes.Build(); Sdes sdes_parsed; EXPECT_TRUE(test::ParseSinglePacket(packet, &sdes_parsed)); const Sdes& parsed = sdes_parsed; // Ensure accessors are const. EXPECT_EQ(1u, parsed.chunks().size()); EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc); EXPECT_EQ(kCname, parsed.chunks()[0].cname); } TEST(RtcpPacketSdesTest, CreateAndParseWithMultipleChunks) { Sdes sdes; EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 0, "a")); EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 1, "ab")); EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 2, "abc")); EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 3, "abcd")); EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 4, "abcde")); EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 5, "abcdef")); rtc::Buffer packet = sdes.Build(); Sdes parsed; EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed)); EXPECT_EQ(6u, parsed.chunks().size()); EXPECT_EQ(kSenderSsrc + 5, parsed.chunks()[5].ssrc); EXPECT_EQ("abcdef", parsed.chunks()[5].cname); } TEST(RtcpPacketSdesTest, CreateWithTooManyChunks) { const size_t kMaxChunks = (1 << 5) - 1; Sdes sdes; for (size_t i = 0; i < kMaxChunks; ++i) { uint32_t ssrc = kSenderSsrc + i; std::ostringstream oss; oss << "cname" << i; EXPECT_TRUE(sdes.AddCName(ssrc, oss.str())); } EXPECT_FALSE(sdes.AddCName(kSenderSsrc + kMaxChunks, "foo")); } TEST(RtcpPacketSdesTest, CreateAndParseCnameItemWithEmptyString) { Sdes sdes; EXPECT_TRUE(sdes.AddCName(kSenderSsrc, "")); rtc::Buffer packet = sdes.Build(); Sdes parsed; EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed)); EXPECT_EQ(1u, parsed.chunks().size()); EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc); EXPECT_EQ("", parsed.chunks()[0].cname); } TEST(RtcpPacketSdesTest, ParseSkipsNonCNameField) { const uint8_t kName[] = "abc"; const uint8_t kCname[] = "de"; const uint8_t kValidPacket[] = {0x81, 202, 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, kNameTag, 3, kName[0], kName[1], kName[2], kCnameTag, 2, kCname[0], kCname[1], kTerminatorTag, kPadding, kPadding}; // Sanity checks packet was assembled correctly. ASSERT_EQ(0u, sizeof(kValidPacket) % 4); ASSERT_EQ(kValidPacket[3] + 1u, sizeof(kValidPacket) / 4); Sdes parsed; EXPECT_TRUE(test::ParseSinglePacket(kValidPacket, &parsed)); EXPECT_EQ(1u, parsed.chunks().size()); EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc); EXPECT_EQ("de", parsed.chunks()[0].cname); } TEST(RtcpPacketSdesTest, ParseSkipsChunksWithoutCName) { const uint8_t kName[] = "ab"; const uint8_t kEmail[] = "de"; const uint8_t kCname[] = "def"; const uint8_t kPacket[] = {0x82, 202, 0x00, 0x07, 0x12, 0x34, 0x56, 0x78, // 1st chunk. kNameTag, 3, kName[0], kName[1], kName[2], kEmailTag, 2, kEmail[0], kEmail[1], kTerminatorTag, kPadding, kPadding, 0x23, 0x45, 0x67, 0x89, // 2nd chunk. kCnameTag, 3, kCname[0], kCname[1], kCname[2], kTerminatorTag, kPadding, kPadding}; // Sanity checks packet was assembled correctly. ASSERT_EQ(0u, sizeof(kPacket) % 4); ASSERT_EQ(kPacket[3] + 1u, sizeof(kPacket) / 4); Sdes parsed; EXPECT_TRUE(test::ParseSinglePacket(kPacket, &parsed)); ASSERT_EQ(1u, parsed.chunks().size()); EXPECT_EQ(0x23456789u, parsed.chunks()[0].ssrc); EXPECT_EQ("def", parsed.chunks()[0].cname); } TEST(RtcpPacketSdesTest, ParseFailsWithoutChunkItemTerminator) { const uint8_t kName[] = "abc"; const uint8_t kCname[] = "d"; // No place for next chunk item. const uint8_t kInvalidPacket[] = {0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, kNameTag, 3, kName[0], kName[1], kName[2], kCnameTag, 1, kCname[0]}; // Sanity checks packet was assembled correctly. ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4); ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4); Sdes parsed; EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed)); } TEST(RtcpPacketSdesTest, ParseFailsWithDamagedChunkItem) { const uint8_t kName[] = "ab"; const uint8_t kCname[] = "d"; // Next chunk item has non-terminator type, but not the size. const uint8_t kInvalidPacket[] = {0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, kNameTag, 2, kName[0], kName[1], kCnameTag, 1, kCname[0], kEmailTag}; // Sanity checks packet was assembled correctly. ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4); ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4); Sdes parsed; EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed)); } TEST(RtcpPacketSdesTest, ParseFailsWithTooLongChunkItem) { const uint8_t kName[] = "abc"; const uint8_t kCname[] = "d"; // Last chunk item has length that goes beyond the buffer end. const uint8_t kInvalidPacket[] = {0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, kNameTag, 3, kName[0], kName[1], kName[2], kCnameTag, 2, kCname[0]}; // Sanity checks packet was assembled correctly. ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4); ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4); Sdes parsed; EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed)); } TEST(RtcpPacketSdesTest, ParseFailsWithTwoCNames) { const uint8_t kCname1[] = "a"; const uint8_t kCname2[] = "de"; const uint8_t kInvalidPacket[] = {0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, kCnameTag, 1, kCname1[0], kCnameTag, 2, kCname2[0], kCname2[1], kTerminatorTag}; // Sanity checks packet was assembled correctly. ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4); ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4); Sdes parsed; EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed)); } TEST(RtcpPacketSdesTest, ParseFailsWithTooLittleSpaceForNextChunk) { const uint8_t kCname[] = "a"; const uint8_t kEmail[] = "de"; // Two chunks are promised in the header, but no place for the second chunk. const uint8_t kInvalidPacket[] = {0x82, 202, 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, // 1st chunk. kCnameTag, 1, kCname[0], kEmailTag, 2, kEmail[0], kEmail[1], kTerminatorTag, 0x23, 0x45, 0x67, 0x89}; // 2nd chunk. // Sanity checks packet was assembled correctly. ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4); ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4); Sdes parsed; EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed)); } TEST(RtcpPacketSdesTest, ParsedSdesCanBeReusedForBuilding) { Sdes source; const std::string kAlice = "alice@host"; const std::string kBob = "bob@host"; source.AddCName(kSenderSsrc, kAlice); rtc::Buffer packet1 = source.Build(); Sdes middle; test::ParseSinglePacket(packet1, &middle); EXPECT_EQ(source.BlockLength(), middle.BlockLength()); middle.AddCName(kSenderSsrc + 1, kBob); rtc::Buffer packet2 = middle.Build(); Sdes destination; test::ParseSinglePacket(packet2, &destination); EXPECT_EQ(middle.BlockLength(), destination.BlockLength()); EXPECT_EQ(2u, destination.chunks().size()); EXPECT_EQ(kSenderSsrc, destination.chunks()[0].ssrc); EXPECT_EQ(kAlice, destination.chunks()[0].cname); EXPECT_EQ(kSenderSsrc + 1, destination.chunks()[1].ssrc); EXPECT_EQ(kBob, destination.chunks()[1].cname); } } // namespace webrtc