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 2, 2023
1 parent 27f9db9 commit cbb4584
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 26 deletions.
84 changes: 58 additions & 26 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,11 +480,6 @@ 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
// @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://linux.die.net/man/3/openssl_version_number
// MM NN FF PP S
// 0x1010102fL = 0x1 01 01 02 fL // 1.1.1b release
Expand All @@ -464,6 +493,15 @@ srs_error_t SrsDtlsImpl::initialize(std::string version, std::string role)
DTLS_set_timer_cb(dtls, dtls_timer_cb);
#endif

// 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);

// 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 @@ -590,26 +643,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 +742,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
2 changes: 2 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,8 @@ class SrsDtlsImpl
public:
SrsDtlsImpl(ISrsDtlsCallback* callback);
virtual ~SrsDtlsImpl();
public:
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 cbb4584

Please sign in to comment.