diff --git a/trunk/src/utest/srs_utest_rtc.cpp b/trunk/src/utest/srs_utest_rtc.cpp index bfb758170..fb56237f1 100644 --- a/trunk/src/utest/srs_utest_rtc.cpp +++ b/trunk/src/utest/srs_utest_rtc.cpp @@ -563,619 +563,6 @@ VOID TEST(KernelRTCTest, StringDumpHexTest) } } -extern SSL_CTX* srs_build_dtls_ctx(SrsDtlsVersion version, std::string role); - -class MockDtls -{ -public: - SSL_CTX* dtls_ctx; - SSL* dtls; - BIO* bio_in; - BIO* bio_out; - ISrsDtlsCallback* callback_; - bool handshake_done_for_us; - SrsDtlsRole role_; - SrsDtlsVersion version_; -public: - MockDtls(ISrsDtlsCallback* callback); - virtual ~MockDtls(); - srs_error_t initialize(std::string role, std::string version); - srs_error_t start_active_handshake(); - srs_error_t on_dtls(char* data, int nb_data); - srs_error_t do_handshake(); -}; - -MockDtls::MockDtls(ISrsDtlsCallback* callback) -{ - dtls_ctx = NULL; - dtls = NULL; - - callback_ = callback; - handshake_done_for_us = false; - - role_ = SrsDtlsRoleServer; - version_ = SrsDtlsVersionAuto; -} - -MockDtls::~MockDtls() -{ - if (dtls_ctx) { - SSL_CTX_free(dtls_ctx); - dtls_ctx = NULL; - } - - if (dtls) { - SSL_free(dtls); - dtls = NULL; - } -} - -srs_error_t MockDtls::initialize(std::string role, std::string version) -{ - role_ = SrsDtlsRoleServer; - if (role == "active") { - role_ = SrsDtlsRoleClient; - } - - if (version == "dtls1.0") { - version_ = SrsDtlsVersion1_0; - } else if (version == "dtls1.2") { - version_ = SrsDtlsVersion1_2; - } else { - version_ = SrsDtlsVersionAuto; - } - - dtls_ctx = srs_build_dtls_ctx(version_, role); - dtls = SSL_new(dtls_ctx); - srs_assert(dtls); - - if (role_ == SrsDtlsRoleClient) { - SSL_set_connect_state(dtls); - SSL_set_max_send_fragment(dtls, kRtpPacketSize); - } else { - SSL_set_accept_state(dtls); - } - - bio_in = BIO_new(BIO_s_mem()); - srs_assert(bio_in); - - bio_out = BIO_new(BIO_s_mem()); - srs_assert(bio_out); - - SSL_set_bio(dtls, bio_in, bio_out); - return srs_success; -} - -srs_error_t MockDtls::start_active_handshake() -{ - if (role_ == SrsDtlsRoleClient) { - return do_handshake(); - } - return srs_success; -} - -srs_error_t MockDtls::on_dtls(char* data, int nb_data) -{ - srs_error_t err = srs_success; - - srs_assert(BIO_reset(bio_in) == 1); - srs_assert(BIO_reset(bio_out) == 1); - srs_assert(BIO_write(bio_in, data, nb_data) > 0); - - if ((err = do_handshake()) != srs_success) { - return srs_error_wrap(err, "do handshake"); - } - - while (BIO_ctrl_pending(bio_in) > 0) { - char buf[8092]; - int nb = SSL_read(dtls, buf, sizeof(buf)); - if (nb <= 0) { - continue; - } - - if ((err = callback_->on_dtls_application_data(buf, nb)) != srs_success) { - return srs_error_wrap(err, "on DTLS data, size=%u", nb); - } - } - - return err; -} - -srs_error_t MockDtls::do_handshake() -{ - srs_error_t err = srs_success; - - int r0 = SSL_do_handshake(dtls); - int r1 = SSL_get_error(dtls, r0); - if (r0 < 0 && (r1 != SSL_ERROR_NONE && r1 != SSL_ERROR_WANT_READ && r1 != SSL_ERROR_WANT_WRITE)) { - return srs_error_new(ERROR_RTC_DTLS, "handshake r0=%d, r1=%d", r0, r1); - } - if (r1 == SSL_ERROR_NONE) { - handshake_done_for_us = true; - } - - uint8_t* data = NULL; - int size = BIO_get_mem_data(bio_out, &data); - - if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) { - return srs_error_wrap(err, "dtls send size=%u", size); - } - - if (handshake_done_for_us) { - return callback_->on_dtls_handshake_done(); - } - - return err; -} - -class MockDtlsCallback : public ISrsDtlsCallback, public ISrsCoroutineHandler -{ -public: - SrsDtls* peer; - MockDtls* peer2; - SrsCoroutine* trd; - srs_error_t r0; - bool done; - std::vector samples; -public: - int nn_client_hello_lost; - int nn_server_hello_lost; - int nn_certificate_lost; - int nn_new_session_lost; - int nn_change_cipher_lost; -public: - // client -> server - int nn_client_hello; - // server -> client - int nn_server_hello; - // client -> server - int nn_certificate; - // server -> client - int nn_new_session; - int nn_change_cipher; -public: - MockDtlsCallback(); - virtual ~MockDtlsCallback(); - virtual srs_error_t on_dtls_handshake_done(); - virtual srs_error_t on_dtls_application_data(const char* data, const int len); - virtual srs_error_t write_dtls_data(void* data, int size); - virtual srs_error_t on_dtls_alert(std::string type, std::string desc); - virtual srs_error_t cycle(); -}; - -MockDtlsCallback::MockDtlsCallback() -{ - peer = NULL; - peer2 = NULL; - r0 = srs_success; - done = false; - trd = new SrsSTCoroutine("mock", this); - srs_assert(trd->start() == srs_success); - - nn_client_hello_lost = 0; - nn_server_hello_lost = 0; - nn_certificate_lost = 0; - nn_new_session_lost = 0; - nn_change_cipher_lost = 0; - - nn_client_hello = 0; - nn_server_hello = 0; - nn_certificate = 0; - nn_new_session = 0; - nn_change_cipher = 0; -} - -MockDtlsCallback::~MockDtlsCallback() -{ - srs_freep(trd); - srs_freep(r0); - for (vector::iterator it = samples.begin(); it != samples.end(); ++it) { - delete[] it->bytes; - } -} - -srs_error_t MockDtlsCallback::on_dtls_handshake_done() -{ - done = true; - return srs_success; -} - -srs_error_t MockDtlsCallback::on_dtls_application_data(const char* data, const int len) -{ - return srs_success; -} - -srs_error_t MockDtlsCallback::write_dtls_data(void* data, int size) -{ - int nn_lost = 0; - if (true) { - uint8_t content_type = 0; - if (size >= 1) { - content_type = (uint8_t)((uint8_t*)data)[0]; - } - - uint8_t handshake_type = 0; - if (size >= 14) { - handshake_type = (uint8_t)((uint8_t*)data)[13]; - } - - if (content_type == 22) { - if (handshake_type == 1) { - nn_lost = nn_client_hello_lost--; - nn_client_hello++; - } else if (handshake_type == 2) { - nn_lost = nn_server_hello_lost--; - nn_server_hello++; - } else if (handshake_type == 11) { - nn_lost = nn_certificate_lost--; - nn_certificate++; - } else if (handshake_type == 4) { - nn_lost = nn_new_session_lost--; - nn_new_session++; - } - } else if (content_type == 20) { - nn_lost = nn_change_cipher_lost--; - nn_change_cipher++; - } - } - - // Simulate to drop packet. - if (nn_lost > 0) { - return srs_success; - } - - // Send out it. - char* cp = new char[size]; - memcpy(cp, data, size); - - samples.push_back(SrsSample((char*)cp, size)); - - return srs_success; -} - -srs_error_t MockDtlsCallback::on_dtls_alert(std::string type, std::string desc) -{ - return srs_success; -} - -srs_error_t MockDtlsCallback::cycle() -{ - srs_error_t err = srs_success; - - while (err == srs_success) { - if ((err = trd->pull()) != srs_success) { - break; - } - - if (samples.empty()) { - srs_usleep(0); - continue; - } - - SrsSample p = samples.at(0); - samples.erase(samples.begin()); - - if (peer) { - err = peer->on_dtls((char*)p.bytes, p.size); - } else if (peer2) { - err = peer2->on_dtls((char*)p.bytes, p.size); - } - - srs_freepa(p.bytes); - } - - // Copy it for utest to check it. - r0 = srs_error_copy(err); - - return err; -} - -// Wait for mock io to done, try to switch to coroutine many times. -void mock_wait_dtls_io_done(SrsDtlsImpl* client_impl, int count = 100, int interval = 0) -{ - for (int i = 0; i < count; i++) { - if (client_impl) { - dynamic_cast(client_impl)->reset_timer_ = true; - } - srs_usleep(interval * SRS_UTIME_MILLISECONDS); - } -} - -// To avoid the crash when peer or peer2 is freed before io. -class MockBridgeDtlsIO -{ -private: - MockDtlsCallback* io_; -public: - MockBridgeDtlsIO(MockDtlsCallback* io, SrsDtls* peer, MockDtls* peer2) { - io_ = io; - io->peer = peer; - io->peer2 = peer2; - } - virtual ~MockBridgeDtlsIO() { - io_->peer = NULL; - io_->peer2 = NULL; - } -}; - -VOID TEST(KernelRTCTest, DTLSClientARQTest) -{ - srs_error_t err = srs_success; - - // No ARQ, check the number of packets. - if (true) { - MockDtlsCallback cio; SrsDtls client(&cio); - MockDtlsCallback sio; SrsDtls server(&sio); - cio.peer = &server; sio.peer = &client; - HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0")); - HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0")); - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()); - mock_wait_dtls_io_done(client.impl, 15, 5); - - EXPECT_TRUE(sio.r0 == srs_success); - EXPECT_TRUE(cio.r0 == srs_success); - - EXPECT_TRUE(cio.done); - EXPECT_TRUE(sio.done); - - EXPECT_EQ(1, cio.nn_client_hello); - EXPECT_EQ(1, sio.nn_server_hello); - EXPECT_EQ(1, cio.nn_certificate); - EXPECT_EQ(1, sio.nn_new_session); - EXPECT_EQ(0, sio.nn_change_cipher); - } - - // ClientHello lost, client retransmit the ClientHello. - if (true) { - MockDtlsCallback cio; SrsDtls client(&cio); - MockDtlsCallback sio; SrsDtls server(&sio); - MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL); - HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0")); - HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0")); - - // Lost 2 packets, total packets should be 3. - // Note that only one server hello. - cio.nn_client_hello_lost = 1; - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()); - mock_wait_dtls_io_done(client.impl, 15, 5); - - EXPECT_TRUE(sio.r0 == srs_success); - EXPECT_TRUE(cio.r0 == srs_success); - - EXPECT_TRUE(cio.done); - EXPECT_TRUE(sio.done); - - EXPECT_EQ(2, cio.nn_client_hello); - EXPECT_EQ(1, sio.nn_server_hello); - EXPECT_EQ(1, cio.nn_certificate); - EXPECT_EQ(1, sio.nn_new_session); - EXPECT_EQ(0, sio.nn_change_cipher); - } - - // Certificate lost, client retransmit the Certificate. - if (true) { - MockDtlsCallback cio; SrsDtls client(&cio); - MockDtlsCallback sio; SrsDtls server(&sio); - MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL); - HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0")); - HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0")); - - // Lost 2 packets, total packets should be 3. - // Note that only one server NewSessionTicket. - cio.nn_certificate_lost = 1; - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()); - mock_wait_dtls_io_done(client.impl, 15, 5); - - EXPECT_TRUE(sio.r0 == srs_success); - EXPECT_TRUE(cio.r0 == srs_success); - - EXPECT_TRUE(cio.done); - EXPECT_TRUE(sio.done); - - EXPECT_EQ(1, cio.nn_client_hello); - EXPECT_EQ(2, sio.nn_server_hello); - EXPECT_EQ(2, cio.nn_certificate); - EXPECT_EQ(0, sio.nn_new_session); - EXPECT_EQ(0, sio.nn_change_cipher); - } -} - -VOID TEST(KernelRTCTest, DTLSServerARQTest) -{ - srs_error_t err = srs_success; - - // No ARQ, check the number of packets. - if (true) { - MockDtlsCallback cio; SrsDtls client(&cio); - MockDtlsCallback sio; SrsDtls server(&sio); - cio.peer = &server; sio.peer = &client; - HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0")); - HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0")); - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()); - mock_wait_dtls_io_done(client.impl, 15, 5); - - EXPECT_TRUE(sio.r0 == srs_success); - EXPECT_TRUE(cio.r0 == srs_success); - - EXPECT_TRUE(cio.done); - EXPECT_TRUE(sio.done); - - EXPECT_EQ(1, cio.nn_client_hello); - EXPECT_EQ(1, sio.nn_server_hello); - EXPECT_EQ(1, cio.nn_certificate); - EXPECT_EQ(1, sio.nn_new_session); - EXPECT_EQ(0, sio.nn_change_cipher); - } - - // ServerHello lost, client retransmit the ClientHello. - if (true) { - MockDtlsCallback cio; SrsDtls client(&cio); - MockDtlsCallback sio; SrsDtls server(&sio); - MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL); - HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0")); - HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0")); - - // Lost 2 packets, total packets should be 3. - sio.nn_server_hello_lost = 1; - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()); - mock_wait_dtls_io_done(client.impl, 15, 5); - - EXPECT_TRUE(sio.r0 == srs_success); - EXPECT_TRUE(cio.r0 == srs_success); - - EXPECT_TRUE(cio.done); - EXPECT_TRUE(sio.done); - - EXPECT_EQ(2, cio.nn_client_hello); - EXPECT_EQ(2, sio.nn_server_hello); - EXPECT_EQ(1, cio.nn_certificate); - EXPECT_EQ(1, sio.nn_new_session); - EXPECT_EQ(0, sio.nn_change_cipher); - } - - // NewSessionTicket lost, client retransmit the Certificate. - if (true) { - MockDtlsCallback cio; SrsDtls client(&cio); - MockDtlsCallback sio; SrsDtls server(&sio); - MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL); - HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0")); - HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0")); - - // Lost 2 packets, total packets should be 3. - sio.nn_new_session_lost = 1; - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()); - mock_wait_dtls_io_done(client.impl, 15, 5); - - EXPECT_TRUE(sio.r0 == srs_success); - EXPECT_TRUE(cio.r0 == srs_success); - - EXPECT_TRUE(cio.done); - EXPECT_TRUE(sio.done); - - EXPECT_EQ(1, cio.nn_client_hello); - EXPECT_EQ(1, sio.nn_server_hello); - EXPECT_EQ(2, cio.nn_certificate); - EXPECT_EQ(2, sio.nn_new_session); - EXPECT_EQ(0, sio.nn_change_cipher); - } -} - -struct DTLSFlowCase -{ - int id; - - string ClientVersion; - string ServerVersion; - - bool ClientDone; - bool ServerDone; - - bool ClientError; - bool ServerError; -}; - -std::ostream& operator<< (std::ostream& stream, const DTLSFlowCase& c) -{ - stream << "Case #" << c.id - << ", client(" << c.ClientVersion << ",done=" << c.ClientDone << ",err=" << c.ClientError << ")" - << ", server(" << c.ServerVersion << ",done=" << c.ServerDone << ",err=" << c.ServerError << ")"; - return stream; -} - -VOID TEST(KernelRTCTest, DTLSClientFlowTest) -{ - srs_error_t err = srs_success; - - DTLSFlowCase cases[] = { - // OK, Client, Server: DTLS v1.0 - {0, "dtls1.0", "dtls1.0", true, true, false, false}, - // OK, Client, Server: DTLS v1.2 - {1, "dtls1.2", "dtls1.2", true, true, false, false}, - // OK, Client: DTLS v1.0, Server: DTLS auto(v1.0 or v1.2). - {2, "dtls1.0", "auto", true, true, false, false}, - // OK, Client: DTLS v1.2, Server: DTLS auto(v1.0 or v1.2). - {3, "dtls1.2", "auto", true, true, false, false}, - // OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0 - {4, "auto", "dtls1.0", true, true, false, false}, - // OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0 - {5, "auto", "dtls1.2", true, true, false, false}, - // OK?, Client: DTLS v1.0, Server: DTLS v1.2 - {6, "dtls1.0", "dtls1.2", true, true, false, false}, - // OK?, Client: DTLS v1.2, Server: DTLS v1.0 - {7, "dtls1.2", "dtls1.0", true, true, false, false}, - }; - - for (int i = 0; i < (int)(sizeof(cases) / sizeof(DTLSFlowCase)); i++) { - DTLSFlowCase c = cases[i]; - - MockDtlsCallback cio; SrsDtls client(&cio); - MockDtlsCallback sio; MockDtls server(&sio); - MockBridgeDtlsIO b0(&cio, NULL, &server); MockBridgeDtlsIO b1(&sio, &client, NULL); - HELPER_EXPECT_SUCCESS(client.initialize("active", c.ClientVersion)) << c; - HELPER_EXPECT_SUCCESS(server.initialize("passive", c.ServerVersion)) << c; - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()) << c; - mock_wait_dtls_io_done(client.impl, 15, 5); - - // Note that the cio error is generated from server, vice versa. - EXPECT_EQ(c.ClientDone, cio.done) << c; - EXPECT_EQ(c.ServerDone, sio.done) << c; - - EXPECT_EQ(c.ClientError, sio.r0 != srs_success) << c; - EXPECT_EQ(c.ServerError, cio.r0 != srs_success) << c; - } -} - -VOID TEST(KernelRTCTest, DTLSServerFlowTest) -{ - srs_error_t err = srs_success; - - DTLSFlowCase cases[] = { - // OK, Client, Server: DTLS v1.0 - {0, "dtls1.0", "dtls1.0", true, true, false, false}, - // OK, Client, Server: DTLS v1.2 - {1, "dtls1.2", "dtls1.2", true, true, false, false}, - // OK, Client: DTLS v1.0, Server: DTLS auto(v1.0 or v1.2). - {2, "dtls1.0", "auto", true, true, false, false}, - // OK, Client: DTLS v1.2, Server: DTLS auto(v1.0 or v1.2). - {3, "dtls1.2", "auto", true, true, false, false}, - // OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0 - {4, "auto", "dtls1.0", true, true, false, false}, - // OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0 - {5, "auto", "dtls1.2", true, true, false, false}, - // OK?, Client: DTLS v1.0, Server: DTLS v1.2 - {6, "dtls1.0", "dtls1.2", true, true, false, false}, - // OK?, Client: DTLS v1.2, Server: DTLS v1.0 - {7, "dtls1.2", "dtls1.0", true, true, false, false}, - }; - - for (int i = 0; i < (int)(sizeof(cases) / sizeof(DTLSFlowCase)); i++) { - DTLSFlowCase c = cases[i]; - - MockDtlsCallback cio; MockDtls client(&cio); - MockDtlsCallback sio; SrsDtls server(&sio); - MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, NULL, &client); - HELPER_EXPECT_SUCCESS(client.initialize("active", c.ClientVersion)) << c; - HELPER_EXPECT_SUCCESS(server.initialize("passive", c.ServerVersion)) << c; - - HELPER_EXPECT_SUCCESS(client.start_active_handshake()) << c; - mock_wait_dtls_io_done(NULL, 15, 5); - - // Note that the cio error is generated from server, vice versa. - EXPECT_EQ(c.ClientDone, cio.done) << c; - EXPECT_EQ(c.ServerDone, sio.done) << c; - - EXPECT_EQ(c.ClientError, sio.r0 != srs_success) << c; - EXPECT_EQ(c.ServerError, cio.r0 != srs_success) << c; - } -} - VOID TEST(KernelRTCTest, SequenceCompare) { if (true) {