From 4f298134af3bd0c5b5e11d9ad2df78c821652ba0 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 11 Dec 2019 17:56:31 +0800 Subject: [PATCH] Fix #1445, limit the createStream recursive depth. 3.0.70 --- README.md | 2 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 3 +- trunk/src/protocol/srs_protocol_utility.cpp | 4 +- trunk/src/protocol/srs_rtmp_stack.cpp | 10 +- trunk/src/protocol/srs_rtmp_stack.hpp | 2 +- trunk/src/utest/srs_utest_protostack.cpp | 313 +++++++++++++++++++- 7 files changed, 327 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7199fa5a0..56215a474 100755 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ For previous versions, please read: ## V3 changes +* v3.0, 2019-12-11, Fix [#1445][bug #1445], limit the createStream recursive depth. 3.0.70 * v3.0, 2019-12-11, For [#1042][bug #1042], cover RTMP handshake protocol. * v3.0, 2019-12-11, Fix [#1229][bug #1229], fix the security risk in logger. 3.0.69 * v3.0, 2019-12-11, For [#1229][bug #1229], fix the security risk in HDS. 3.0.69 @@ -1515,6 +1516,7 @@ Winlin [bug #1501]: https://github.com/ossrs/srs/issues/1501 [bug #1229]: https://github.com/ossrs/srs/issues/1229 [bug #1042]: https://github.com/ossrs/srs/issues/1042 +[bug #1445]: https://github.com/ossrs/srs/issues/1445 [bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 71b3e7c98..24a702933 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // The version config. #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 69 +#define VERSION_REVISION 70 // The macros generated by configure script. #include diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index a3e56e19f..ae543662f 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -177,7 +177,8 @@ #define ERROR_HTTP_HIJACK 2052 #define ERROR_RTMP_MESSAGE_CREATE 2053 #define ERROR_RTMP_PROXY_EXCEED 2054 -// +#define ERROR_RTMP_CREATE_STREAM_DEPTH 2055 +// // The system control message, // It's not an error, but special control logic. // diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index 57a369dff..8245e1fc3 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -200,9 +200,9 @@ string srs_generate_stream_with_query(string host, string vhost, string stream, // Remove the start & when param is empty. srs_string_trim_start(query, "&"); - + // Prefix query with ?. - if (!srs_string_starts_with(query, "?")) { + if (!query.empty() && !srs_string_starts_with(query, "?")) { url += "?"; } diff --git a/trunk/src/protocol/srs_rtmp_stack.cpp b/trunk/src/protocol/srs_rtmp_stack.cpp index f2009a477..7fd74fa3c 100644 --- a/trunk/src/protocol/srs_rtmp_stack.cpp +++ b/trunk/src/protocol/srs_rtmp_stack.cpp @@ -2538,7 +2538,7 @@ srs_error_t SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, SrsAutoFree(SrsPacket, pkt); if (dynamic_cast(pkt)) { - return identify_create_stream_client(dynamic_cast(pkt), stream_id, type, stream_name, duration); + return identify_create_stream_client(dynamic_cast(pkt), stream_id, 3, type, stream_name, duration); } if (dynamic_cast(pkt)) { return identify_fmle_publish_client(dynamic_cast(pkt), type, stream_name); @@ -2909,9 +2909,13 @@ srs_error_t SrsRtmpServer::start_flash_publish(int stream_id) return err; } -srs_error_t SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, string& stream_name, srs_utime_t& duration) +srs_error_t SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, int depth, SrsRtmpConnType& type, string& stream_name, srs_utime_t& duration) { srs_error_t err = srs_success; + + if (depth <= 0) { + return srs_error_new(ERROR_RTMP_CREATE_STREAM_DEPTH, "create stream recursive depth"); + } if (true) { SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); @@ -2952,7 +2956,7 @@ srs_error_t SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* return identify_flash_publish_client(dynamic_cast(pkt), type, stream_name); } if (dynamic_cast(pkt)) { - return identify_create_stream_client(dynamic_cast(pkt), stream_id, type, stream_name, duration); + return identify_create_stream_client(dynamic_cast(pkt), stream_id, depth-1, type, stream_name, duration); } if (dynamic_cast(pkt)) { return identify_haivision_publish_client(dynamic_cast(pkt), type, stream_name); diff --git a/trunk/src/protocol/srs_rtmp_stack.hpp b/trunk/src/protocol/srs_rtmp_stack.hpp index b1ffadebd..2db39b521 100644 --- a/trunk/src/protocol/srs_rtmp_stack.hpp +++ b/trunk/src/protocol/srs_rtmp_stack.hpp @@ -775,7 +775,7 @@ public: return protocol->expect_message(pmsg, ppacket); } private: - virtual srs_error_t identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, std::string& stream_name, srs_utime_t& duration); + virtual srs_error_t identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, int depth, SrsRtmpConnType& type, std::string& stream_name, srs_utime_t& duration); virtual srs_error_t identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, std::string& stream_name); virtual srs_error_t identify_haivision_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, std::string& stream_name); virtual srs_error_t identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, std::string& stream_name); diff --git a/trunk/src/utest/srs_utest_protostack.cpp b/trunk/src/utest/srs_utest_protostack.cpp index dbe3fc990..889c7fa21 100644 --- a/trunk/src/utest/srs_utest_protostack.cpp +++ b/trunk/src/utest/srs_utest_protostack.cpp @@ -1594,12 +1594,323 @@ VOID TEST(ProtoStackTest, ServerCommandMessage) SrsCommonMessage* msg = NULL; SrsSetWindowAckSizePacket* pkt = NULL; - HELPER_EXPECT_SUCCESS(p.expect_message(&msg, &pkt)); + HELPER_ASSERT_SUCCESS(p.expect_message(&msg, &pkt)); EXPECT_EQ(1024, pkt->ackowledgement_window_size); srs_freep(msg); srs_freep(pkt); } } + + // Response ConnectApp. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + SrsRequest req; + req.objectEncoding = 3.0; + + const char* ip = "1.2.3.4"; + HELPER_EXPECT_SUCCESS(r.response_connect_app(&req, ip)); + + if (true) { + MockBufferIO tio; + tio.in_buffer.append(&io.out_buffer); + + SrsProtocol p(&tio); + + // In order to receive and decode the response, + // we have to send out a ConnectApp request. + if (true) { + SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); + HELPER_EXPECT_SUCCESS(p.send_and_free_packet(pkt, 0)); + } + + SrsCommonMessage* msg = NULL; + SrsConnectAppResPacket* pkt = NULL; + HELPER_ASSERT_SUCCESS(p.expect_message(&msg, &pkt)); + + SrsAmf0Any* prop = pkt->info->get_property("objectEncoding"); + ASSERT_TRUE(prop && prop->is_number()); + EXPECT_EQ(3.0, prop->to_number()); + + prop = pkt->info->get_property("data"); + ASSERT_TRUE(prop && prop->is_ecma_array()); + + SrsAmf0EcmaArray* arr = prop->to_ecma_array(); + prop = arr->get_property("srs_server_ip"); + ASSERT_TRUE(prop && prop->is_string()); + EXPECT_STREQ("1.2.3.4", prop->to_str().c_str()); + + srs_freep(msg); + srs_freep(pkt); + } + } + + // Response ConnectApp rejected. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + const char* desc = "Rejected"; + r.response_connect_reject(NULL, desc); + + if (true) { + MockBufferIO tio; + tio.in_buffer.append(&io.out_buffer); + + SrsProtocol p(&tio); + + SrsCommonMessage* msg = NULL; + SrsCallPacket* pkt = NULL; + HELPER_ASSERT_SUCCESS(p.expect_message(&msg, &pkt)); + + SrsAmf0Any* prop = pkt->arguments; + ASSERT_TRUE(prop && prop->is_object()); + + prop = prop->to_object()->get_property(StatusDescription); + ASSERT_TRUE(prop && prop->is_string()); + EXPECT_STREQ(desc, prop->to_str().c_str()); + + srs_freep(msg); + srs_freep(pkt); + } + } + + // Response OnBWDone + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + HELPER_EXPECT_SUCCESS(r.on_bw_done()); + EXPECT_TRUE(io.out_length() > 0); + } + + // Set peer chunk size. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + HELPER_EXPECT_SUCCESS(r.set_chunk_size(1024)); + + if (true) { + MockBufferIO tio; + tio.in_buffer.append(&io.out_buffer); + + SrsProtocol p(&tio); + + SrsCommonMessage* msg = NULL; + HELPER_EXPECT_SUCCESS(p.recv_message(&msg)); + srs_freep(msg); + + EXPECT_EQ(1024, p.in_chunk_size); + } + } +} + +VOID TEST(ProtoStackTest, ServerRedirect) +{ + srs_error_t err; + + // Redirect without response. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + SrsRequest req; + req.app = "live"; + req.stream = "livestream"; + + string host = "target.net"; + int port = 8888; + bool accepted = false; + HELPER_EXPECT_SUCCESS(r.redirect(&req, host, port, accepted)); + + if (true) { + MockBufferIO tio; + tio.in_buffer.append(&io.out_buffer); + + SrsProtocol p(&tio); + + SrsCommonMessage* msg = NULL; + SrsCallPacket* pkt = NULL; + HELPER_ASSERT_SUCCESS(p.expect_message(&msg, &pkt)); + + SrsAmf0Any* prop = pkt->arguments; + ASSERT_TRUE(prop && prop->is_object()); + + prop = prop->to_object()->get_property("ex"); + ASSERT_TRUE(prop && prop->is_object()); + SrsAmf0Object* ex = prop->to_object(); + + prop = ex->get_property("code"); + ASSERT_TRUE(prop && prop->is_number()); + EXPECT_EQ(302, prop->to_number()); + + prop = ex->get_property("redirect"); + ASSERT_TRUE(prop && prop->is_string()); + EXPECT_STREQ("rtmp://target.net:8888/live/livestream", prop->to_str().c_str()); + + srs_freep(msg); + srs_freep(pkt); + } + } + + // Redirect with response. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + if (true) { + MockBufferIO tio; + SrsProtocol p(&tio); + + SrsCallPacket* call = new SrsCallPacket(); + call->command_name = "redirected"; + call->command_object = SrsAmf0Any::object(); + call->arguments = SrsAmf0Any::str("OK"); + HELPER_EXPECT_SUCCESS(p.send_and_free_packet(call, 0)); + + io.in_buffer.append(&tio.out_buffer); + } + + SrsRequest req; + req.app = "live"; + req.stream = "livestream"; + + string host = "target.net"; + int port = 8888; + bool accepted = false; + HELPER_EXPECT_SUCCESS(r.redirect(&req, host, port, accepted)); + EXPECT_TRUE(accepted); + + if (true) { + MockBufferIO tio; + tio.in_buffer.append(&io.out_buffer); + + SrsProtocol p(&tio); + + SrsCommonMessage* msg = NULL; + SrsCallPacket* pkt = NULL; + HELPER_ASSERT_SUCCESS(p.expect_message(&msg, &pkt)); + + SrsAmf0Any* prop = pkt->arguments; + ASSERT_TRUE(prop && prop->is_object()); + + prop = prop->to_object()->get_property("ex"); + ASSERT_TRUE(prop && prop->is_object()); + SrsAmf0Object* ex = prop->to_object(); + + prop = ex->get_property("code"); + ASSERT_TRUE(prop && prop->is_number()); + EXPECT_EQ(302, prop->to_number()); + + prop = ex->get_property("redirect"); + ASSERT_TRUE(prop && prop->is_string()); + EXPECT_STREQ("rtmp://target.net:8888/live/livestream", prop->to_str().c_str()); + + srs_freep(msg); + srs_freep(pkt); + } + } +} + +VOID TEST(ProtoStackTest, ServerIdentify) +{ + srs_error_t err; + + // Identify failed. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + string stream_name; + SrsRtmpConnType tp; + srs_utime_t duration = 0; + HELPER_EXPECT_FAILED(r.identify_client(1, tp, stream_name, duration)); + } + + // Identify by CreateStream, Play. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + if (true) { + MockBufferIO tio; + SrsProtocol p(&tio); + + SrsCreateStreamPacket* call = new SrsCreateStreamPacket(); + HELPER_EXPECT_SUCCESS(p.send_and_free_packet(call, 0)); + + SrsPlayPacket* play = new SrsPlayPacket(); + play->stream_name = "livestream"; + play->duration = 100; + HELPER_EXPECT_SUCCESS(p.send_and_free_packet(play, 0)); + + io.in_buffer.append(&tio.out_buffer); + } + + string stream_name; + SrsRtmpConnType tp; + srs_utime_t duration = 0; + HELPER_EXPECT_SUCCESS(r.identify_client(1, tp, stream_name, duration)); + EXPECT_EQ(SrsRtmpConnPlay, tp); + EXPECT_STREQ("livestream", stream_name.c_str()); + EXPECT_EQ(100000, duration); + } + + // Identify by CreateStream, CreateStream, CreateStream, Play. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + if (true) { + MockBufferIO tio; + SrsProtocol p(&tio); + + for (int i = 0; i < 3; i++) { + SrsCreateStreamPacket* call = new SrsCreateStreamPacket(); + HELPER_EXPECT_SUCCESS(p.send_and_free_packet(call, 0)); + } + + SrsPlayPacket* play = new SrsPlayPacket(); + play->stream_name = "livestream"; + play->duration = 100; + HELPER_EXPECT_SUCCESS(p.send_and_free_packet(play, 0)); + + io.in_buffer.append(&tio.out_buffer); + } + + string stream_name; + SrsRtmpConnType tp; + srs_utime_t duration = 0; + HELPER_EXPECT_SUCCESS(r.identify_client(1, tp, stream_name, duration)); + EXPECT_EQ(SrsRtmpConnPlay, tp); + EXPECT_STREQ("livestream", stream_name.c_str()); + EXPECT_EQ(100000, duration); + } + + // For N*CreateStream and N>3, it should fail. + + // Identify by CreateStream, CreateStream, CreateStream, Play. + if (true) { + MockBufferIO io; + SrsRtmpServer r(&io); + + if (true) { + MockBufferIO tio; + SrsProtocol p(&tio); + + for (int i = 0; i < 4; i++) { + SrsCreateStreamPacket* call = new SrsCreateStreamPacket(); + HELPER_EXPECT_SUCCESS(p.send_and_free_packet(call, 0)); + } + + io.in_buffer.append(&tio.out_buffer); + } + + string stream_name; + SrsRtmpConnType tp; + srs_utime_t duration = 0; + HELPER_EXPECT_FAILED(r.identify_client(1, tp, stream_name, duration)); + } }