diff --git a/include/tins/pdu.h b/include/tins/pdu.h index dffcad66..6402a129 100644 --- a/include/tins/pdu.h +++ b/include/tins/pdu.h @@ -181,6 +181,7 @@ class TINS_API PDU { MPLS, DOT11_CONTROL_TA, VXLAN, + RTP, UNKNOWN = 999, USER_DEFINED_PDU = 1000 }; diff --git a/include/tins/rtp.h b/include/tins/rtp.h new file mode 100644 index 00000000..0040387a --- /dev/null +++ b/include/tins/rtp.h @@ -0,0 +1,328 @@ +#ifndef TINS_RTP_H +#define TINS_RTP_H + +#include +#include +#include +#include + +namespace Tins { + +/** + * \class RTP + * \brief Represents a RTP PDU. + * + * This class represents a RTP PDU. + * + * \sa RawPDU + */ +class TINS_API RTP : public PDU { +public: + /** + * \brief This PDU's flag. + */ + static const PDU::PDUType pdu_flag = PDU::RTP; + + /** + * The type used to store CSRC identifiers. + */ + typedef std::vector csrc_ids_type; + + /** + * The type used to store extension header data. + */ + typedef std::vector extension_header_data_type; + + /** + * Default constructor. + */ + RTP(); + + /** + * \brief Constructs a RTP object from a buffer. + * + * \param data The buffer from which this PDU will be constructed. + * \param size The size of the data buffer. + */ + RTP(const uint8_t* data, uint32_t size); + + /** + * \brief Getter for the version. + */ + small_uint<2> version() const { return header_.version; } + + /** + * \brief Getter for the padding bit. + */ + small_uint<1> padding_bit() const { return header_.padding; } + + /** + * \brief Getter for the extension bit. + */ + small_uint<1> extension_bit() const { return header_.extension; } + + /** + * \brief Getter for the CSRC count. + */ + small_uint<4> csrc_count() const { return header_.csrc_count; } + + /** + * \brief Getter for the marker bit. + */ + small_uint<1> marker_bit() const { return header_.marker; } + + /** + * \brief Getter for the payload type. + */ + small_uint<7> payload_type() const { return header_.payload_type; } + + /** + * \brief Getter for the sequence number. + */ + uint16_t sequence_number() const { return Endian::be_to_host(header_.seq_num); } + + /** + * \brief Getter for the timestamp. + */ + uint32_t timestamp() const { return Endian::be_to_host(header_.timestamp); } + + /** + * \brief Getter for the SSRC identifier. + */ + uint32_t ssrc_id() const { return Endian::be_to_host(header_.ssrc_id); } + + /** + * \brief Getter for the CSRC identifiers. + */ + const csrc_ids_type& csrc_ids() const { + return csrc_ids_; + } + + /** + * \brief Getter for the padding size. + */ + uint8_t padding_size() const { return padding_size_; } + + /** + * \brief Getter for the extension header profile. + */ + uint16_t extension_profile() const { return Endian::be_to_host(ext_header_.profile); } + + /** + * \brief Getter for the extension header length. + */ + uint16_t extension_length() const { return Endian::be_to_host(ext_header_.length); } + + /** + * \brief Getter for the extension header data. + */ + const extension_header_data_type& extension_data() const { + return ext_data_; + } + + /** + * \brief Setter for the version. + * \param version The new version. + */ + void version(small_uint<2> version) { header_.version = version; } + + /** + * \brief Setter for the extension bit. + * \param extension The new extension bit. + */ + void extension_bit(small_uint<1> extension) { header_.extension = extension; } + + /** + * \brief Setter for the marker bit. + * \param marker The new marker bit. + */ + void marker_bit(small_uint<1> marker) { header_.marker = marker; } + + /** + * \brief Setter for the payload type. + * \param payload_type The new payload type. + */ + void payload_type(small_uint<7> payload_type) { header_.payload_type = payload_type; } + + /** + * \brief Setter for the sequence number. + * \param seq_num The new sequence number. + */ + void sequence_number(uint16_t seq_num) { header_.seq_num = Endian::host_to_be(seq_num); } + + /** + * \brief Setter for the timestamp. + * \param timestamp The new timestamp. + */ + void timestamp(uint32_t timestamp) { header_.timestamp = Endian::host_to_be(timestamp); } + + /** + * \brief Setter for the SSRC identifier. + * \param ssrc_id The new SSRC identifier. + */ + void ssrc_id(uint32_t ssrc_id) { header_.ssrc_id = Endian::host_to_be(ssrc_id); } + + /** + * \brief Setter for the padding size. + * \param size The new padding size. + */ + void padding_size(uint8_t size) { + padding_bit(size > 0); + padding_size_ = size; + } + + /** + * \brief Setter for the extension header profile. + * \param profile The new extension header profile. + */ + void extension_profile(uint16_t profile) { ext_header_.profile = Endian::host_to_be(profile); } + + /** + * \brief Adds a word of extension header data. + * + * The word is added after the last word of extension header data. + * + * \param value The value of the extension header data to be added. + */ + void add_extension_data(const uint32_t value); + + /** + * \brief Removes a word of extension header data. + * + * If there are multiple words of extension header data of the given value, + * only the first one will be removed. + * + * \param value The value of the extension header data to be removed. + * \return true if the extension header data was removed, false otherwise. + */ + bool remove_extension_data(const uint32_t value); + + /** + * \brief Searches for extension header data that matches the given value. + * \param value The extension header data to be searched. + * \return true if the extension header data was found, false otherwise. + */ + bool search_extension_data(const uint32_t value); + + /** + * \brief Adds a CSRC identifier. + * + * The CSRC identifier is added after the last CSRC identifier in the extension + * header. + * + * \param csrc_id The CSRC identifier to be added + */ + void add_csrc_id(const uint32_t csrc_id); + + /** + * \brief Removes a CSRC identifier. + * + * If there are multiple CSRC identifiers of the given value, only the first one + * will be removed. + * + * \param value The value of the CSRC identifier to be removed. + * \return true if the CSRC identifier was removed, false otherwise. + */ + bool remove_csrc_id(const uint32_t value); + + /** + * \brief Searches for a CSRC identifier that matches the given value. + * \param value The CSRC identifier to be searched. + * \return true if the CSRC identifier was found, false otherwise. + */ + bool search_csrc_id(const uint32_t value); + + /** + * \brief Returns the RTP packet's header length. + * + * This method overrides PDU::header_size. + * + * \return An uint32_t with the header's size. + * \sa PDU::header_size + */ + uint32_t header_size() const; + + /** + * \brief Returns the RTP packet's trailer length. + * + * This method overrides PDU::trailer_size. + * + * \return An uint32_t with the trailer's size. + * \sa PDU::trailer_size + */ + uint32_t trailer_size() const { return static_cast(padding_size_); } + + /** + * \brief Getter for the PDU's type. + * \sa PDU::pdu_type + */ + PDUType pdu_type() const { return pdu_flag; } + + /** + * \sa PDU::clone + */ + RTP *clone() const { return new RTP(*this); } + +private: + TINS_BEGIN_PACK + struct rtp_header { + #if TINS_IS_BIG_ENDIAN + uint16_t version:2, + padding:1, + extension:1, + csrc_count:4, + marker:1, + payload_type:7; + #elif TINS_IS_LITTLE_ENDIAN + uint16_t csrc_count:4, + extension:1, + padding:1, + version:2, + payload_type:7, + marker:1; + #endif + uint16_t seq_num; + uint32_t timestamp; + uint32_t ssrc_id; + } TINS_END_PACK; + + TINS_BEGIN_PACK + struct rtp_extension_header { + uint16_t profile; + uint16_t length; + } TINS_END_PACK; + + void write_serialization(uint8_t* buffer, uint32_t size); + csrc_ids_type::const_iterator search_csrc_id_iterator(const uint32_t csrc_id) const; + csrc_ids_type::iterator search_csrc_id_iterator(const uint32_t csrc_id); + extension_header_data_type::const_iterator search_extension_data_iterator(const uint32_t data) const; + extension_header_data_type::iterator search_extension_data_iterator(const uint32_t data); + + /** + * \brief Setter for the padding bit. + * \param padding The new padding bit. + */ + void padding_bit(small_uint<1> padding) { header_.padding = padding; } + + /** + * \brief Setter for the CSRC count. Hidden from the public interface. + * \param csrc_count The new CSRC count. + */ + void csrc_count(small_uint<4> csrc_count) { header_.csrc_count = csrc_count; } + + /** + * \brief Setter for the extension header length. Hidden from the public interface. + * \param length The new extension header length. + */ + void extension_length(uint16_t length) { ext_header_.length = Endian::host_to_be(length); } + + rtp_header header_; + csrc_ids_type csrc_ids_; + rtp_extension_header ext_header_; + extension_header_data_type ext_data_; + uint8_t padding_size_; +}; + +} // Tins + +#endif // TINS_RTP_H diff --git a/include/tins/tins.h b/include/tins/tins.h index 0ddbbc7f..6526c50e 100644 --- a/include/tins/tins.h +++ b/include/tins/tins.h @@ -80,5 +80,6 @@ #include #include #include +#include #endif // TINS_TINS_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 88f47a29..8dc4fc82 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(SOURCES radiotap.cpp rawpdu.cpp rsn_information.cpp + rtp.cpp sll.cpp snap.cpp stp.cpp @@ -131,6 +132,7 @@ set(HEADERS ${LIBTINS_INCLUDE_DIR}/tins/radiotap.h ${LIBTINS_INCLUDE_DIR}/tins/rawpdu.h ${LIBTINS_INCLUDE_DIR}/tins/rsn_information.h + ${LIBTINS_INCLUDE_DIR}/tins/rtp.h ${LIBTINS_INCLUDE_DIR}/tins/sll.h ${LIBTINS_INCLUDE_DIR}/tins/small_uint.h ${LIBTINS_INCLUDE_DIR}/tins/snap.h diff --git a/src/detail/pdu_helpers.cpp b/src/detail/pdu_helpers.cpp index bb8f4e54..bdda65a4 100644 --- a/src/detail/pdu_helpers.cpp +++ b/src/detail/pdu_helpers.cpp @@ -169,6 +169,8 @@ Tins::PDU* pdu_from_flag(PDU::PDUType type, const uint8_t* buffer, uint32_t size return new Tins::IEEE802_3(buffer, size); case Tins::PDU::PPPOE: return new Tins::PPPoE(buffer, size); + case Tins::PDU::RAW: + return new Tins::RawPDU(buffer, size); #ifdef TINS_HAVE_DOT11 case Tins::PDU::RADIOTAP: return new Tins::RadioTap(buffer, size); diff --git a/src/rtp.cpp b/src/rtp.cpp new file mode 100644 index 00000000..951755b7 --- /dev/null +++ b/src/rtp.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include + +using std::logic_error; +using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; + +namespace Tins { + +RTP::RTP() +: header_(), ext_header_(), padding_size_(0) { + version(2); +} + +RTP::RTP(const uint8_t* buffer, uint32_t total_sz) { + InputMemoryStream stream(buffer, total_sz); + stream.read(header_); + + small_uint<4> csrc_count_ = csrc_count(); + + for (uint32_t i = 0; i < csrc_count_; ++i) { + uint32_t csrc_id; + stream.read(csrc_id); + csrc_ids_.push_back(csrc_id); + } + + if (extension_bit() == 1) { + stream.read(ext_header_); + for (uint32_t i = 0; i < extension_length(); ++i) { + uint32_t data; + stream.read(data); + ext_data_.push_back(data); + } + } + + padding_size_ = 0; + + const uint8_t* data_ptr = stream.pointer(); + const size_t data_size = stream.size(); + + if (padding_bit() == 1) { + if (data_size > 0) { + stream.skip(data_size - sizeof(uint8_t)); + stream.read(padding_size_); + } else { + throw malformed_packet(); + } + + if (padding_size() == 0) { + throw malformed_packet(); + } + } + + if (padding_size() > data_size) { + throw malformed_packet(); + } + + if (data_size > padding_size()) { + inner_pdu( + Internals::pdu_from_flag( + PDU::RAW, + data_ptr, + data_size - padding_size() + ) + ); + } +} + +uint32_t RTP::header_size() const { + uint32_t extension_size = 0; + if (extension_bit() == 1) { + extension_size = sizeof(ext_header_) + (extension_length() * sizeof(uint32_t)); + } + return static_cast(sizeof(header_) + (csrc_ids_.size() * sizeof(uint32_t)) + extension_size); +} + +void RTP::add_csrc_id(const uint32_t csrc_id) { + small_uint<4> csrc_count_ = csrc_count(); + if (TINS_UNLIKELY(csrc_count_ >= 15)) { + throw logic_error("Maximum number of CSRC IDs reached"); + } + + csrc_ids_.push_back(Endian::host_to_be(csrc_id)); + csrc_count(csrc_count_ + 1); +} + +bool RTP::remove_csrc_id(const uint32_t csrc_id) { + small_uint<4> csrc_count_ = csrc_count(); + if (csrc_count_ == 0) { + return false; + } + + csrc_ids_type::iterator iter = search_csrc_id_iterator(Endian::host_to_be(csrc_id)); + if (iter == csrc_ids_.end()) { + return false; + } + + csrc_ids_.erase(iter); + csrc_count(csrc_count_ - 1); + return true; +} + +bool RTP::search_csrc_id(const uint32_t csrc_id) { + csrc_ids_type::const_iterator iter = search_csrc_id_iterator(Endian::host_to_be(csrc_id)); + return (iter != csrc_ids_.cend()); +} + +RTP::csrc_ids_type::const_iterator RTP::search_csrc_id_iterator(const uint32_t csrc_id) const { + return std::find(csrc_ids_.cbegin(), csrc_ids_.cend(), csrc_id); +} + +RTP::csrc_ids_type::iterator RTP::search_csrc_id_iterator(const uint32_t csrc_id) { + return std::find(csrc_ids_.begin(), csrc_ids_.end(), csrc_id); +} + +void RTP::add_extension_data(const uint32_t value) { + if (TINS_UNLIKELY(extension_length() >= 65535)) { + throw logic_error("Maximum number of extension data reached"); + } + + extension_bit(1); + ext_data_.push_back(Endian::host_to_be(value)); + extension_length(extension_length() + 1); +} + +bool RTP::remove_extension_data(const uint32_t value) { + if (extension_bit() == 0 || extension_length() == 0) { + return false; + } + + extension_header_data_type::iterator iter = search_extension_data_iterator(Endian::host_to_be(value)); + if (iter == ext_data_.end()) { + return false; + } + + ext_data_.erase(iter); + + extension_length(extension_length() - 1); + + if (extension_length() == 0) { + extension_bit(0); + } + + return true; +} + +bool RTP::search_extension_data(const uint32_t value) { + if (extension_bit() == 0 || extension_length() == 0) { + return false; + } + + extension_header_data_type::const_iterator iter = search_extension_data_iterator(Endian::host_to_be(value)); + return (iter != ext_data_.cend()); +} + +RTP::extension_header_data_type::const_iterator RTP::search_extension_data_iterator(const uint32_t data) const { + return std::find(ext_data_.cbegin(), ext_data_.cend(), data); +} + +RTP::extension_header_data_type::iterator RTP::search_extension_data_iterator(const uint32_t data) { + return std::find(ext_data_.begin(), ext_data_.end(), data); +} + +void RTP::write_serialization(uint8_t* buffer, uint32_t total_sz) { + OutputMemoryStream stream(buffer, total_sz); + stream.write(header_); + + for (auto csrc_id : csrc_ids_) { + stream.write(csrc_id); + } + + if (extension_bit() == 1) { + stream.write(ext_header_); + for (auto data : ext_data_) { + stream.write(data); + } + } + + if (padding_bit() == 1) { + if (padding_size() > 0) { + if (inner_pdu()) { + stream.skip(inner_pdu()->size()); + } + stream.fill(padding_size() - 1, 0); + stream.write(padding_size()); + } else { + throw pdu_not_serializable(); + } + } +} + +} // Tins diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index 3e3ea4bc..aa0c3777 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -63,6 +63,7 @@ CREATE_TEST(pppoe) CREATE_TEST(raw_pdu) CREATE_TEST(rc4_eapol) CREATE_TEST(rsn_eapol) +CREATE_TEST(rtp) CREATE_TEST(sll) CREATE_TEST(snap) CREATE_TEST(stp) diff --git a/tests/src/rtp_test.cpp b/tests/src/rtp_test.cpp new file mode 100644 index 00000000..7837f076 --- /dev/null +++ b/tests/src/rtp_test.cpp @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PACKET_SIZE 60ul +#define CSRC_COUNT 5 +#define EXTENSION_LENGTH 2 +#define PAYLOAD_SIZE 12 + +using namespace std; +using namespace Tins; + +class RTPTest : public testing::Test { +public: + static const uint8_t expected_packet[PACKET_SIZE]; + static const uint8_t invalid_packet_one[]; + static const uint8_t invalid_packet_two[]; + static const uint8_t packet_with_zero_padding_value[]; + static const uint8_t packet_without_data_one[]; + static const uint8_t packet_without_data_two[]; + static const uint8_t packet_with_zero_extension_length[]; + static const small_uint<2> version; + static const small_uint<1> padding; + static const small_uint<1> extension; + static const small_uint<4> csrc_count; + static const small_uint<1> marker; + static const small_uint<7> payload_type; + static const uint16_t sequence_number; + static const uint32_t timestamp; + static const uint32_t ssrc_id; + static const uint32_t csrc_ids[CSRC_COUNT]; + static const uint16_t profile; + static const uint16_t extension_length; + static const uint32_t extension_data[EXTENSION_LENGTH]; + static const uint8_t padding_size; + static const uint8_t payload[PAYLOAD_SIZE]; + static const uint16_t dport, sport; + static const IP::address_type dst_ip, src_ip; + static const EthernetII::address_type dst_addr, src_addr; +}; + +const uint8_t RTPTest::expected_packet[PACKET_SIZE] = { + 0xb5, 0xaa, 0xa4, 0x10, + 0xde, 0xad, 0xbe, 0xef, + 0xab, 0xcd, 0xad, 0xbc, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x05, + 0x01, 0x01, 0x00, 0x02, + 0x77, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, + 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, + 0x00, 0x00, 0x00, 0x04, +}; + +const uint8_t RTPTest::invalid_packet_one[] = { + 160, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0xff, +}; + +const uint8_t RTPTest::invalid_packet_two[] = { + 160, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +const uint8_t RTPTest::packet_with_zero_padding_value[] = { + 160, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, +}; + +const uint8_t RTPTest::packet_without_data_one[] = { + 128, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +const uint8_t RTPTest::packet_without_data_two[] = { + 160, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 7, +}; + +const uint8_t RTPTest::packet_with_zero_extension_length[] = { + 144, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0x56, 0x97, 0, 0, +}; + +const small_uint<2> RTPTest::version = 2; +const small_uint<1> RTPTest::padding = 1; +const small_uint<1> RTPTest::extension = 1; +const small_uint<4> RTPTest::csrc_count = CSRC_COUNT; +const small_uint<1> RTPTest::marker = 1; +const small_uint<7> RTPTest::payload_type = 42; +const uint16_t RTPTest::sequence_number = 42000; +const uint32_t RTPTest::timestamp = 0xdeadbeef; +const uint32_t RTPTest::ssrc_id = 0xabcdadbc; +const uint32_t RTPTest::csrc_ids[CSRC_COUNT] = { 1, 2, 3, 4, 5 }; +const uint16_t RTPTest::profile = 0x0101; +const uint16_t RTPTest::extension_length = EXTENSION_LENGTH; +const uint32_t RTPTest::extension_data[EXTENSION_LENGTH] = { 0x77000000, 0x88000000 }; +const uint8_t RTPTest::padding_size = 4; +const uint8_t RTPTest::payload[PAYLOAD_SIZE] = { + 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, +}; +const uint16_t RTPTest::dport = 5004; +const uint16_t RTPTest::sport = 30000; +const IP::address_type RTPTest::dst_ip = IP::address_type{"2.2.2.2"}; +const IP::address_type RTPTest::src_ip = IP::address_type{"1.1.1.1"}; +const EthernetII::address_type RTPTest::dst_addr = EthernetII::address_type{"aa:bb:cc:dd:ee:ff"}; +const EthernetII::address_type RTPTest::src_addr = EthernetII::address_type{"8a:8b:8c:8d:8e:8f"}; + +TEST_F(RTPTest, DefaultConstructor) { + auto const rtp = RTP{}; + EXPECT_EQ(rtp.version(), version); + EXPECT_EQ(rtp.padding_bit(), 0); + EXPECT_EQ(rtp.extension_bit(), 0); + EXPECT_EQ(rtp.csrc_count(), 0); + EXPECT_EQ(rtp.marker_bit(), 0); + EXPECT_EQ(rtp.payload_type(), 0); + EXPECT_EQ(rtp.sequence_number(), 0); + EXPECT_EQ(rtp.timestamp(), 0); + EXPECT_EQ(rtp.ssrc_id(), 0); + EXPECT_EQ(rtp.csrc_ids().size(), 0); + EXPECT_EQ(rtp.extension_profile(), 0); + EXPECT_EQ(rtp.extension_length(), 0); + EXPECT_EQ(rtp.extension_data().size(), 0); + EXPECT_EQ(rtp.padding_size(), 0); + EXPECT_EQ(rtp.header_size(), 12); + EXPECT_EQ(rtp.trailer_size(), 0); +} + +TEST_F(RTPTest, Serialize) { + auto rtp = RTP{}; + rtp.version(version); + rtp.padding_size(padding_size); + rtp.extension_bit(extension); + rtp.marker_bit(marker); + rtp.payload_type(payload_type); + rtp.sequence_number(sequence_number); + rtp.timestamp(timestamp); + rtp.ssrc_id(ssrc_id); + + for (auto csrc_id : csrc_ids) { + rtp.add_csrc_id(csrc_id); + } + + rtp.extension_profile(profile); + + for (auto data : extension_data) { + rtp.add_extension_data(data); + } + + auto raw_pdu = RawPDU(payload, PAYLOAD_SIZE); + rtp.inner_pdu(raw_pdu); + + EXPECT_EQ(rtp.header_size(), PACKET_SIZE - PAYLOAD_SIZE - padding_size); + EXPECT_EQ(rtp.trailer_size(), padding_size); + + auto serialized = rtp.serialize(); + ASSERT_EQ(serialized.size(), PACKET_SIZE); + EXPECT_TRUE(std::equal(serialized.begin(), serialized.end(), expected_packet)); +} + +TEST_F(RTPTest, ConstructorFromBuffer) { + auto rtp = RTP{expected_packet, PACKET_SIZE}; + EXPECT_EQ(rtp.version(), version); + EXPECT_EQ(rtp.padding_bit(), padding); + EXPECT_EQ(rtp.extension_bit(), extension); + EXPECT_EQ(rtp.csrc_count(), csrc_count); + EXPECT_EQ(rtp.marker_bit(), marker); + EXPECT_EQ(rtp.payload_type(), payload_type); + EXPECT_EQ(rtp.sequence_number(), sequence_number); + EXPECT_EQ(rtp.timestamp(), timestamp); + EXPECT_EQ(rtp.ssrc_id(), ssrc_id); + + auto csrc_id_values = rtp.csrc_ids(); + for (size_t i = 0; i < csrc_count; ++i) { + EXPECT_EQ(csrc_id_values[i], Endian::host_to_be(csrc_ids[i])); + } + + EXPECT_EQ(rtp.extension_profile(), profile); + EXPECT_EQ(rtp.extension_length(), extension_length); + + auto extension_data_values = rtp.extension_data(); + for (size_t i = 0; i < extension_length; ++i) { + EXPECT_EQ(extension_data_values[i], Endian::host_to_be(extension_data[i])); + } + + EXPECT_EQ(rtp.padding_size(), padding_size); + EXPECT_EQ(rtp.header_size(), PACKET_SIZE - PAYLOAD_SIZE - padding_size); + + auto inner_pdu_payload = rtp.inner_pdu()->serialize(); + EXPECT_TRUE(std::equal(inner_pdu_payload.begin(), inner_pdu_payload.end(), payload)); + + auto raw_pdu = RawPDU(payload, PAYLOAD_SIZE); + auto raw_pdu_payload = raw_pdu.serialize(); + EXPECT_EQ(rtp.inner_pdu()->size(), raw_pdu.size()); + EXPECT_EQ(inner_pdu_payload, raw_pdu_payload); +} + +TEST_F(RTPTest, SearchAndRemoveCSRCID) { + auto rtp = RTP{}; + + for (auto csrc_id : csrc_ids) { + rtp.add_csrc_id(csrc_id); + } + + for (size_t i = 0; i < csrc_count; ++i) { + EXPECT_TRUE(rtp.search_csrc_id(csrc_ids[i])); + } + + EXPECT_FALSE(rtp.search_csrc_id(0)); + EXPECT_FALSE(rtp.remove_csrc_id(0)); + EXPECT_TRUE(rtp.remove_csrc_id(csrc_ids[0])); + EXPECT_FALSE(rtp.search_csrc_id(csrc_ids[0])); +} + +TEST_F(RTPTest, SearchAndRemoveExtensionData) { + auto rtp = RTP{}; + + for (auto data : extension_data) { + rtp.add_extension_data(data); + } + + for (size_t i = 0; i < extension_length; ++i) { + EXPECT_TRUE(rtp.search_extension_data(extension_data[i])); + } + + EXPECT_FALSE(rtp.search_extension_data(0)); + EXPECT_FALSE(rtp.remove_extension_data(0)); + EXPECT_TRUE(rtp.remove_extension_data(extension_data[0])); + EXPECT_FALSE(rtp.search_extension_data(extension_data[0])); +} + +TEST_F(RTPTest, OuterUDP) { + auto pkt = EthernetII{dst_addr, src_addr} / IP{dst_ip, src_ip} / UDP{dport, sport} / RTP{expected_packet, PACKET_SIZE}; + + auto udp = pkt.find_pdu(); + ASSERT_TRUE(udp != nullptr); + EXPECT_EQ(udp->dport(), dport); + EXPECT_EQ(udp->sport(), sport); + + auto rtp = udp->find_pdu(); + ASSERT_TRUE(rtp != nullptr); + EXPECT_EQ(rtp->header_size(), PACKET_SIZE - PAYLOAD_SIZE - padding_size); + EXPECT_EQ(rtp->trailer_size(), padding_size); + EXPECT_EQ(rtp->size(), PACKET_SIZE); + EXPECT_EQ(rtp->inner_pdu()->size(), PAYLOAD_SIZE); + auto inner_pdu_payload = rtp->inner_pdu()->serialize(); + EXPECT_TRUE(std::equal(inner_pdu_payload.begin(), inner_pdu_payload.end(), payload)); +} + +TEST_F(RTPTest, PaddingSizeTooLarge) { + EXPECT_THROW((RTP{invalid_packet_one, sizeof(invalid_packet_one)}), malformed_packet); +} + +TEST_F(RTPTest, PaddingBitSetWithoutPadding) { + EXPECT_THROW((RTP{invalid_packet_two, sizeof(invalid_packet_two)}), malformed_packet); +} + +TEST_F(RTPTest, PacketWithInvalidZeroPaddingValue) { + EXPECT_THROW((RTP{packet_with_zero_padding_value, sizeof(packet_with_zero_padding_value)}), malformed_packet); +} + +TEST_F(RTPTest, PacketWithoutData) { + auto rtp = RTP{packet_without_data_one, sizeof(packet_without_data_one)}; + EXPECT_EQ(rtp.size(), sizeof(packet_without_data_one)); + EXPECT_EQ(rtp.header_size(), sizeof(packet_without_data_one)); + EXPECT_EQ(rtp.inner_pdu(), nullptr); + EXPECT_EQ(rtp.padding_size(), 0); + + const uint8_t padding_size_ = 7; + rtp = RTP{packet_without_data_two, sizeof(packet_without_data_two)}; + EXPECT_EQ(rtp.size(), sizeof(packet_without_data_two)); + EXPECT_EQ(rtp.header_size(), sizeof(packet_without_data_two) - padding_size_); + EXPECT_EQ(rtp.inner_pdu(), nullptr); + EXPECT_EQ(rtp.padding_size(), padding_size_); +} + +TEST_F(RTPTest, PacketWithZeroExtensionLength) { + auto rtp = RTP{packet_with_zero_extension_length, sizeof(packet_with_zero_extension_length)}; + EXPECT_EQ(rtp.size(), sizeof(packet_with_zero_extension_length)); + EXPECT_EQ(rtp.header_size(), sizeof(packet_with_zero_extension_length)); + EXPECT_EQ(rtp.extension_profile(), 0x5697); + EXPECT_EQ(rtp.extension_length(), 0); + EXPECT_EQ(rtp.extension_data().size(), 0); +}