From d439e3634432664bb477fbd8c159deff09e013a8 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 4 Aug 2020 17:03:25 +0800 Subject: [PATCH] RTC: Covert basic packet identify for DTLS/STUN/RTP/RTCP --- trunk/src/app/srs_app_rtc_server.cpp | 33 +++++---- trunk/src/utest/srs_utest_rtc.cpp | 103 +++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 12 deletions(-) diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index 38a20856e..413dea282 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -97,6 +97,7 @@ void SrsRtcBlackhole::sendto(void* data, int len) return; } + // For blackhole, we ignore any error. srs_sendto(blackhole_stfd, data, len, (sockaddr*)blackhole_addr, sizeof(sockaddr_in), SRS_UTIME_NO_TIMEOUT); } @@ -105,24 +106,32 @@ SrsRtcBlackhole* _srs_blackhole = new SrsRtcBlackhole(); // @global dtls certficate for rtc module. SrsDtlsCertificate* _srs_rtc_dtls_certificate = new SrsDtlsCertificate(); -static bool is_stun(const uint8_t* data, const int size) +// TODO: Should support error response. +// For STUN packet, 0x00 is binding request, 0x01 is binding success response. +bool srs_is_stun(const uint8_t* data, size_t size) { - return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); + return size > 0 && (data[0] == 0 || data[0] == 1); } -static bool is_dtls(const uint8_t* data, size_t len) +// change_cipher_spec(20), alert(21), handshake(22), application_data(23) +// @see https://tools.ietf.org/html/rfc2246#section-6.2.1 +bool srs_is_dtls(const uint8_t* data, size_t len) { - return (len >= 13 && (data[0] > 19 && data[0] < 64)); + return (len >= 13 && (data[0] > 19 && data[0] < 64)); } -static bool is_rtp_or_rtcp(const uint8_t* data, size_t len) +// For RTP or RTCP, the V=2 which is in the high 2bits, 0xC0 (1100 0000) +bool srs_is_rtp_or_rtcp(const uint8_t* data, size_t len) { - return (len >= 12 && (data[0] & 0xC0) == 0x80); + return (len >= 12 && (data[0] & 0xC0) == 0x80); } -static bool is_rtcp(const uint8_t* data, size_t len) +// For RTCP, PT is [128, 223] (or without marker [0, 95]). +// Literally, RTCP starts from 64 not 0, so PT is [192, 223] (or without marker [64, 95]). +// @note For RTP, the PT is [96, 127], or [224, 255] with marker. +bool srs_is_rtcp(const uint8_t* data, size_t len) { - return (len >= 12) && (data[0] & 0x80) && (data[1] >= 200 && data[1] <= 209); + return (len >= 12) && (data[0] & 0x80) && (data[1] >= 192 && data[1] <= 223); } static std::vector get_candidate_ips() @@ -301,7 +310,7 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt) } // For STUN, the peer address may change. - if (is_stun((uint8_t*)data, size)) { + if (srs_is_stun((uint8_t*)data, size)) { SrsStunPacket ping; if ((err = ping.decode(data, size)) != srs_success) { return srs_error_wrap(err, "decode stun packet failed"); @@ -332,10 +341,10 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt) return srs_error_new(ERROR_RTC_STUN, "no session, peer_id=%s", peer_id.c_str()); } - if (is_dtls((uint8_t*)data, size)) { + if (srs_is_dtls((uint8_t*)data, size)) { return session->on_dtls(data, size); - } else if (is_rtp_or_rtcp((uint8_t*)data, size)) { - if (is_rtcp((uint8_t*)data, size)) { + } else if (srs_is_rtp_or_rtcp((uint8_t*)data, size)) { + if (srs_is_rtcp((uint8_t*)data, size)) { return session->on_rtcp(data, size); } return session->on_rtp(data, size); diff --git a/trunk/src/utest/srs_utest_rtc.cpp b/trunk/src/utest/srs_utest_rtc.cpp index 7ceeb8d96..1d22ac897 100644 --- a/trunk/src/utest/srs_utest_rtc.cpp +++ b/trunk/src/utest/srs_utest_rtc.cpp @@ -29,6 +29,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include +using namespace std; + VOID TEST(KernelRTCTest, SequenceCompare) { if (true) { @@ -125,6 +128,106 @@ VOID TEST(KernelRTCTest, SequenceCompare) } } +extern bool srs_is_stun(const uint8_t* data, size_t size); +extern bool srs_is_dtls(const uint8_t* data, size_t len); +extern bool srs_is_rtp_or_rtcp(const uint8_t* data, size_t len); +extern bool srs_is_rtcp(const uint8_t* data, size_t len); + +#define mock_arr_push(arr, elem) arr.push_back(vector(elem, elem + sizeof(elem))) + +VOID TEST(KernelRTCTest, TestPacketType) +{ + // DTLS packet. + vector< vector > dtlss; + if (true) { uint8_t data[13] = {20}; mock_arr_push(dtlss, data); } // change_cipher_spec(20) + if (true) { uint8_t data[13] = {21}; mock_arr_push(dtlss, data); } // alert(21) + if (true) { uint8_t data[13] = {22}; mock_arr_push(dtlss, data); } // handshake(22) + if (true) { uint8_t data[13] = {23}; mock_arr_push(dtlss, data); } // application_data(23) + for (int i = 0; i < (int)dtlss.size(); i++) { + vector elem = dtlss.at(i); + EXPECT_TRUE(srs_is_dtls(&elem[0], (size_t)elem.size())); + } + + for (int i = 0; i < (int)dtlss.size(); i++) { + vector elem = dtlss.at(i); + EXPECT_FALSE(srs_is_dtls(&elem[0], 1)); + + // All DTLS should not be other packets. + EXPECT_FALSE(srs_is_stun(&elem[0], (size_t)elem.size())); + EXPECT_TRUE(srs_is_dtls(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size())); + } + + // STUN packet. + vector< vector > stuns; + if (true) { uint8_t data[1] = {0}; mock_arr_push(stuns, data); } // binding request. + if (true) { uint8_t data[1] = {1}; mock_arr_push(stuns, data); } // binding success response. + for (int i = 0; i < (int)stuns.size(); i++) { + vector elem = stuns.at(i); + EXPECT_TRUE(srs_is_stun(&elem[0], (size_t)elem.size())); + } + + for (int i = 0; i < (int)stuns.size(); i++) { + vector elem = stuns.at(i); + EXPECT_FALSE(srs_is_stun(&elem[0], 0)); + + // All STUN should not be other packets. + EXPECT_TRUE(srs_is_stun(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_dtls(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size())); + } + + // RTCP packet. + vector< vector > rtcps; + if (true) { uint8_t data[12] = {0x80, 192}; mock_arr_push(rtcps, data); } + if (true) { uint8_t data[12] = {0x80, 200}; mock_arr_push(rtcps, data); } // SR + if (true) { uint8_t data[12] = {0x80, 201}; mock_arr_push(rtcps, data); } // RR + if (true) { uint8_t data[12] = {0x80, 202}; mock_arr_push(rtcps, data); } // SDES + if (true) { uint8_t data[12] = {0x80, 203}; mock_arr_push(rtcps, data); } // BYE + if (true) { uint8_t data[12] = {0x80, 204}; mock_arr_push(rtcps, data); } // APP + if (true) { uint8_t data[12] = {0x80, 223}; mock_arr_push(rtcps, data); } + for (int i = 0; i < (int)rtcps.size(); i++) { + vector elem = rtcps.at(i); + EXPECT_TRUE(srs_is_rtcp(&elem[0], (size_t)elem.size())); + } + + for (int i = 0; i < (int)rtcps.size(); i++) { + vector elem = rtcps.at(i); + EXPECT_FALSE(srs_is_rtcp(&elem[0], 2)); + + // All RTCP should not be other packets. + EXPECT_FALSE(srs_is_stun(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_dtls(&elem[0], (size_t)elem.size())); + EXPECT_TRUE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size())); + EXPECT_TRUE(srs_is_rtcp(&elem[0], (size_t)elem.size())); + } + + // RTP packet. + vector< vector > rtps; + if (true) { uint8_t data[12] = {0x80, 96}; mock_arr_push(rtps, data); } + if (true) { uint8_t data[12] = {0x80, 127}; mock_arr_push(rtps, data); } + if (true) { uint8_t data[12] = {0x80, 224}; mock_arr_push(rtps, data); } + if (true) { uint8_t data[12] = {0x80, 255}; mock_arr_push(rtps, data); } + for (int i = 0; i < (int)rtps.size(); i++) { + vector elem = rtps.at(i); + EXPECT_TRUE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size())); + } + + for (int i = 0; i < (int)rtps.size(); i++) { + vector elem = rtps.at(i); + EXPECT_FALSE(srs_is_rtp_or_rtcp(&elem[0], 2)); + + // All RTP should not be other packets. + EXPECT_FALSE(srs_is_stun(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_dtls(&elem[0], (size_t)elem.size())); + EXPECT_TRUE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size())); + EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size())); + } +} + VOID TEST(KernelRTCTest, DefaultTrackStatus) { // By default, track is disabled.