Skip to content

Commit

Permalink
DTLS: Use bio callback to get fragment packet.
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Jun 3, 2023
1 parent 27f9db9 commit 6becadd
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 51 deletions.
107 changes: 56 additions & 51 deletions trunk/src/app/srs_app_rtc_dtls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,40 @@ SrsDtlsImpl::~SrsDtlsImpl()
}
}

long srs_dtls_bio_out_callback(BIO* bio, int cmd, const char* argp, int argi, long argl, long ret)
{
long r0 = (BIO_CB_RETURN & cmd) ? ret : 1;
if (cmd == BIO_CB_WRITE && argp && argi > 0) {
SrsDtlsImpl* dtls = (SrsDtlsImpl*)BIO_get_callback_arg(bio);
srs_error_t err = dtls->write_dtls_data((void*)argp, argi);
if (err != srs_success) {
srs_warn("ignore err %s", srs_error_desc(err).c_str());
}
srs_freep(err);
}
return r0;
}

srs_error_t SrsDtlsImpl::write_dtls_data(void* data, int size)
{
srs_error_t err = srs_success;

// Callback for the final output data, before send-out.
if ((err = on_final_out_data((uint8_t*)data, size)) != srs_success) {
return srs_error_wrap(err, "handle");
}

if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) {
return srs_error_wrap(err, "dtls send size=%u, data=[%s]", size,
srs_string_dumps_hex((char*)data, size, 32).c_str());
}

// Logging when got SSL original data.
state_trace((uint8_t*)data, size, false, 0, 0, false);

return err;
}

srs_error_t SrsDtlsImpl::initialize(std::string version, std::string role)
{
srs_error_t err = srs_success;
Expand All @@ -446,10 +480,13 @@ srs_error_t SrsDtlsImpl::initialize(std::string version, std::string role)
SSL_set_ex_data(dtls, 0, this);
SSL_set_info_callback(dtls, ssl_on_info);

// set dtls fragment
// We have set the MTU to fragment the DTLS packet. It is important to note that the packet is split
// to ensure that each handshake packet is smaller than the MTU.
// @see https://stackoverflow.com/questions/62413602/openssl-server-packets-get-fragmented-into-270-bytes-per-packet
SSL_set_options(dtls, SSL_OP_NO_QUERY_MTU);
SSL_set_mtu(dtls, DTLS_FRAGMENT_MAX_SIZE);
// See https://github.com/versatica/mediasoup/pull/217
DTLS_set_link_mtu(dtls, DTLS_FRAGMENT_MAX_SIZE);

// @see https://linux.die.net/man/3/openssl_version_number
// MM NN FF PP S
Expand All @@ -464,6 +501,7 @@ srs_error_t SrsDtlsImpl::initialize(std::string version, std::string role)
DTLS_set_timer_cb(dtls, dtls_timer_cb);
#endif

// Setup memory BIO.
if ((bio_in = BIO_new(BIO_s_mem())) == NULL) {
return srs_error_new(ERROR_OpenSslBIONew, "BIO_new in");
}
Expand All @@ -473,6 +511,21 @@ srs_error_t SrsDtlsImpl::initialize(std::string version, std::string role)
return srs_error_new(ERROR_OpenSslBIONew, "BIO_new out");
}

// Please be aware that it is necessary to use a callback to obtain the packet to be written out. It is
// imperative that BIO_get_mem_data is not used to retrieve the packet, as it returns all the bytes that
// need to be sent out.
// For example, if MTU is set to 1200, and we got two DTLS packets to sendout:
// ServerHello, 95bytes.
// Certificate, 1105+143=1248bytes.
// If use BIO_get_mem_data, it will return 95+1248=1343bytes, which is larger than MTU 1200.
// If use callback, it will return two UDP packets:
// ServerHello+Certificate(Frament) = 95+1105=1200bytes.
// Certificate(Fragment) = 143bytes.
// Note that there should be more packets in real world, like ServerKeyExchange, CertificateRequest,
// and ServerHelloDone. Here we just use two packets for example.
BIO_set_callback(bio_out, srs_dtls_bio_out_callback);
BIO_set_callback_arg(bio_out, (char*)this);

SSL_set_bio(dtls, bio_in, bio_out);

return err;
Expand Down Expand Up @@ -500,16 +553,8 @@ srs_error_t SrsDtlsImpl::do_on_dtls(char* data, int nb_data)
srs_info("DTLS: After done, got %d bytes", nb_data);
}

int r0 = 0;
// TODO: FIXME: Why reset it before writing?
if ((r0 = BIO_reset(bio_in)) != 1) {
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset r0=%d", r0);
}
if ((r0 = BIO_reset(bio_out)) != 1) {
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset r0=%d", r0);
}

// Trace the detail of DTLS packet.
int r0 = 0;
state_trace((uint8_t*)data, nb_data, true, r0, SSL_ERROR_NONE, false);

if ((r0 = BIO_write(bio_in, data, nb_data)) <= 0) {
Expand Down Expand Up @@ -590,26 +635,6 @@ srs_error_t SrsDtlsImpl::do_handshake()
// OK, Handshake is done, note that it maybe done many times.
if (r1 == SSL_ERROR_NONE) {
handshake_done_for_us = true;
}

// The data to send out to peer.
uint8_t* data = NULL;
int size = BIO_get_mem_data(bio_out, (char**)&data);

// Logging when got SSL original data.
state_trace((uint8_t*)data, size, false, r0, r1, false);

// Callback for the final output data, before send-out.
if ((err = on_final_out_data(data, size)) != srs_success) {
return srs_error_wrap(err, "handle");
}

if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) {
return srs_error_wrap(err, "dtls send size=%u, data=[%s]", size,
srs_string_dumps_hex((char*)data, size, 32).c_str());
}

if (handshake_done_for_us) {
if (((err = on_handshake_done()) != srs_success)) {
return srs_error_wrap(err, "done");
}
Expand Down Expand Up @@ -709,7 +734,6 @@ srs_error_t SrsDtlsClientImpl::initialize(std::string version, std::string role)

// Dtls setup active, as client role.
SSL_set_connect_state(dtls);
SSL_set_max_send_fragment(dtls, DTLS_FRAGMENT_MAX_SIZE);

return err;
}
Expand Down Expand Up @@ -863,36 +887,17 @@ srs_error_t SrsDtlsClientImpl::cycle()
continue;
}

// The timeout is 0, so there must be a ARQ packet to transmit in openssl.
r0 = BIO_reset(bio_out); int r1 = SSL_get_error(dtls, r0); ERR_clear_error();
if (r0 != 1) {
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset r0=%d, r1=%d", r0, r1);
}

// DTLSv1_handle_timeout is called when a DTLS handshake timeout expires. If no timeout
// had expired, it returns 0. Otherwise, it retransmits the previous flight of handshake
// messages and returns 1. If too many timeouts had expired without progress or an error
// occurs, it returns -1.
r0 = DTLSv1_handle_timeout(dtls); r1 = SSL_get_error(dtls, r0); ERR_clear_error();
r0 = DTLSv1_handle_timeout(dtls); int r1 = SSL_get_error(dtls, r0); ERR_clear_error();
if (r0 == 0) {
continue; // No timeout had expired.
}
if (r0 != 1) {
return srs_error_new(ERROR_RTC_DTLS, "ARQ r0=%d, r1=%d", r0, r1);
}

// The data to send out to peer.
uint8_t* data = NULL;
int size = BIO_get_mem_data(bio_out, (char**)&data);

arq_count++;
nn_arq_packets++;
state_trace((uint8_t*)data, size, false, r0, r1, true);

if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) {
return srs_error_wrap(err, "dtls send size=%u, data=[%s]", size,
srs_string_dumps_hex((char*)data, size, 32).c_str());
}
}

return err;
Expand Down
3 changes: 3 additions & 0 deletions trunk/src/app/srs_app_rtc_dtls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ class SrsDtlsImpl
public:
SrsDtlsImpl(ISrsDtlsCallback* callback);
virtual ~SrsDtlsImpl();
public:
// Internal API for sending DTLS packets.
srs_error_t write_dtls_data(void* data, int size);
public:
virtual srs_error_t initialize(std::string version, std::string role);
virtual srs_error_t start_active_handshake() = 0;
Expand Down

0 comments on commit 6becadd

Please sign in to comment.