mirror of
https://github.com/mfontanini/libtins
synced 2026-01-22 18:25:57 +01:00
Add RTP support (#530)
This commit adds support for the Real-time Transport Protocol (RTP) as defined in RFC 3550. Some tests have also been added to ensure that the RTP PDU class functionality is working as expected. Signed-off-by: James Raphael Tiovalen <jamestiotio@gmail.com>
This commit is contained in:
@@ -181,6 +181,7 @@ public:
|
|||||||
MPLS,
|
MPLS,
|
||||||
DOT11_CONTROL_TA,
|
DOT11_CONTROL_TA,
|
||||||
VXLAN,
|
VXLAN,
|
||||||
|
RTP,
|
||||||
UNKNOWN = 999,
|
UNKNOWN = 999,
|
||||||
USER_DEFINED_PDU = 1000
|
USER_DEFINED_PDU = 1000
|
||||||
};
|
};
|
||||||
|
|||||||
328
include/tins/rtp.h
Normal file
328
include/tins/rtp.h
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
#ifndef TINS_RTP_H
|
||||||
|
#define TINS_RTP_H
|
||||||
|
|
||||||
|
#include <tins/endianness.h>
|
||||||
|
#include <tins/pdu.h>
|
||||||
|
#include <tins/pdu_option.h>
|
||||||
|
#include <tins/small_uint.h>
|
||||||
|
|
||||||
|
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<uint32_t> csrc_ids_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type used to store extension header data.
|
||||||
|
*/
|
||||||
|
typedef std::vector<uint32_t> 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<uint32_t>(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
|
||||||
@@ -80,5 +80,6 @@
|
|||||||
#include <tins/ip_reassembler.h>
|
#include <tins/ip_reassembler.h>
|
||||||
#include <tins/pdu_iterator.h>
|
#include <tins/pdu_iterator.h>
|
||||||
#include <tins/vxlan.h>
|
#include <tins/vxlan.h>
|
||||||
|
#include <tins/rtp.h>
|
||||||
|
|
||||||
#endif // TINS_TINS_H
|
#endif // TINS_TINS_H
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ set(SOURCES
|
|||||||
radiotap.cpp
|
radiotap.cpp
|
||||||
rawpdu.cpp
|
rawpdu.cpp
|
||||||
rsn_information.cpp
|
rsn_information.cpp
|
||||||
|
rtp.cpp
|
||||||
sll.cpp
|
sll.cpp
|
||||||
snap.cpp
|
snap.cpp
|
||||||
stp.cpp
|
stp.cpp
|
||||||
@@ -131,6 +132,7 @@ set(HEADERS
|
|||||||
${LIBTINS_INCLUDE_DIR}/tins/radiotap.h
|
${LIBTINS_INCLUDE_DIR}/tins/radiotap.h
|
||||||
${LIBTINS_INCLUDE_DIR}/tins/rawpdu.h
|
${LIBTINS_INCLUDE_DIR}/tins/rawpdu.h
|
||||||
${LIBTINS_INCLUDE_DIR}/tins/rsn_information.h
|
${LIBTINS_INCLUDE_DIR}/tins/rsn_information.h
|
||||||
|
${LIBTINS_INCLUDE_DIR}/tins/rtp.h
|
||||||
${LIBTINS_INCLUDE_DIR}/tins/sll.h
|
${LIBTINS_INCLUDE_DIR}/tins/sll.h
|
||||||
${LIBTINS_INCLUDE_DIR}/tins/small_uint.h
|
${LIBTINS_INCLUDE_DIR}/tins/small_uint.h
|
||||||
${LIBTINS_INCLUDE_DIR}/tins/snap.h
|
${LIBTINS_INCLUDE_DIR}/tins/snap.h
|
||||||
|
|||||||
@@ -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);
|
return new Tins::IEEE802_3(buffer, size);
|
||||||
case Tins::PDU::PPPOE:
|
case Tins::PDU::PPPOE:
|
||||||
return new Tins::PPPoE(buffer, size);
|
return new Tins::PPPoE(buffer, size);
|
||||||
|
case Tins::PDU::RAW:
|
||||||
|
return new Tins::RawPDU(buffer, size);
|
||||||
#ifdef TINS_HAVE_DOT11
|
#ifdef TINS_HAVE_DOT11
|
||||||
case Tins::PDU::RADIOTAP:
|
case Tins::PDU::RADIOTAP:
|
||||||
return new Tins::RadioTap(buffer, size);
|
return new Tins::RadioTap(buffer, size);
|
||||||
|
|||||||
195
src/rtp.cpp
Normal file
195
src/rtp.cpp
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <tins/exceptions.h>
|
||||||
|
#include <tins/internals.h>
|
||||||
|
#include <tins/memory_helpers.h>
|
||||||
|
#include <tins/rtp.h>
|
||||||
|
|
||||||
|
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<uint32_t>(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
|
||||||
@@ -63,6 +63,7 @@ CREATE_TEST(pppoe)
|
|||||||
CREATE_TEST(raw_pdu)
|
CREATE_TEST(raw_pdu)
|
||||||
CREATE_TEST(rc4_eapol)
|
CREATE_TEST(rc4_eapol)
|
||||||
CREATE_TEST(rsn_eapol)
|
CREATE_TEST(rsn_eapol)
|
||||||
|
CREATE_TEST(rtp)
|
||||||
CREATE_TEST(sll)
|
CREATE_TEST(sll)
|
||||||
CREATE_TEST(snap)
|
CREATE_TEST(snap)
|
||||||
CREATE_TEST(stp)
|
CREATE_TEST(stp)
|
||||||
|
|||||||
292
tests/src/rtp_test.cpp
Normal file
292
tests/src/rtp_test.cpp
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <string>
|
||||||
|
#include <tins/endianness.h>
|
||||||
|
#include <tins/ethernetII.h>
|
||||||
|
#include <tins/ip.h>
|
||||||
|
#include <tins/udp.h>
|
||||||
|
#include <tins/pdu.h>
|
||||||
|
#include <tins/rawpdu.h>
|
||||||
|
#include <tins/small_uint.h>
|
||||||
|
#include <tins/rtp.h>
|
||||||
|
|
||||||
|
#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<UDP>();
|
||||||
|
ASSERT_TRUE(udp != nullptr);
|
||||||
|
EXPECT_EQ(udp->dport(), dport);
|
||||||
|
EXPECT_EQ(udp->sport(), sport);
|
||||||
|
|
||||||
|
auto rtp = udp->find_pdu<RTP>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user