From fa4b82cc7cc556c03c652d40bd966ef3d4445592 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Wed, 6 Sep 2017 08:30:00 +0200 Subject: [PATCH] add callback handler for setting DTLS timer interval Reviewed-by: Matt Caswell Reviewed-by: Bernd Edlinger (Merged from https://github.com/openssl/openssl/pull/4011) --- doc/man3/DTLS_set_timer_cb.pod | 40 +++++++++++++++++++++++++++ include/openssl/ssl.h | 6 +++++ ssl/d1_lib.c | 49 ++++++++++++++++++++++++++++------ ssl/ssl_locl.h | 6 ++++- test/dtlstest.c | 20 ++++++++++++++ util/libssl.num | 1 + util/private.num | 1 + 7 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 doc/man3/DTLS_set_timer_cb.pod diff --git a/doc/man3/DTLS_set_timer_cb.pod b/doc/man3/DTLS_set_timer_cb.pod new file mode 100644 index 0000000000..6e1347213e --- /dev/null +++ b/doc/man3/DTLS_set_timer_cb.pod @@ -0,0 +1,40 @@ +=pod + +=head1 NAME + +DTLS_timer_cb, +DTLS_set_timer_cb +- Set callback for controlling DTLS timer duration + +=head1 SYNOPSIS + + #include + + typedef unsigned int (*DTLS_timer_cb)(SSL *s, unsigned int timer_us); + + void DTLS_set_timer_cb(SSL *s, DTLS_timer_cb cb); + +=head1 DESCRIPTION + +This function sets an optional callback function for controlling the +timeout interval on the DTLS protocol. The callback function will be +called by DTLS for every new DTLS packet that is sent. + +=head1 RETURN VALUES + +Returns void. + +=head1 HISTORY + +This function was added in OpenSSL 1.1.1 + +=head1 COPYRIGHT + +Copyright 2017 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 248408f691..da1fa0ff35 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2260,6 +2260,12 @@ extern const char SSL_version_str[]; int ERR_load_SSL_strings(void); + +typedef unsigned int (*DTLS_timer_cb)(SSL *s, unsigned int timer_us); + +void DTLS_set_timer_cb(SSL *s, DTLS_timer_cb cb); + + # ifdef __cplusplus } # endif diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index d839e1ab72..a8ea097825 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -161,6 +161,8 @@ int dtls1_clear(SSL *s) DTLS_RECORD_LAYER_clear(&s->rlayer); if (s->d1) { + DTLS_timer_cb timer_cb = s->d1->timer_cb; + buffered_messages = s->d1->buffered_messages; sent_messages = s->d1->sent_messages; mtu = s->d1->mtu; @@ -170,6 +172,9 @@ int dtls1_clear(SSL *s) memset(s->d1, 0, sizeof(*s->d1)); + /* Restore the timer callback from previous state */ + s->d1->timer_cb = timer_cb; + if (s->server) { s->d1->cookie_len = sizeof(s->d1->cookie); } @@ -236,6 +241,8 @@ long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg) void dtls1_start_timer(SSL *s) { + unsigned int sec, usec; + #ifndef OPENSSL_NO_SCTP /* Disable timer for SCTP */ if (BIO_dgram_is_sctp(SSL_get_wbio(s))) { @@ -244,16 +251,34 @@ void dtls1_start_timer(SSL *s) } #endif - /* If timer is not set, initialize duration with 1 second */ + /* + * If timer is not set, initialize duration with 1 second or + * a user-specified value if the timer callback is installed. + */ if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) { - s->d1->timeout_duration = 1; + + if (s->d1->timer_cb != NULL) + s->d1->timeout_duration_us = s->d1->timer_cb(s, 0); + else + s->d1->timeout_duration_us = 1000000; } /* Set timeout to current time */ get_current_time(&(s->d1->next_timeout)); /* Add duration to current time */ - s->d1->next_timeout.tv_sec += s->d1->timeout_duration; + + sec = s->d1->timeout_duration_us / 1000000; + usec = s->d1->timeout_duration_us - (sec * 1000000); + + s->d1->next_timeout.tv_sec += sec; + s->d1->next_timeout.tv_usec += usec; + + if (s->d1->next_timeout.tv_usec >= 1000000) { + s->d1->next_timeout.tv_sec++; + s->d1->next_timeout.tv_usec -= 1000000; + } + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout)); } @@ -318,9 +343,9 @@ int dtls1_is_timer_expired(SSL *s) void dtls1_double_timeout(SSL *s) { - s->d1->timeout_duration *= 2; - if (s->d1->timeout_duration > 60) - s->d1->timeout_duration = 60; + s->d1->timeout_duration_us *= 2; + if (s->d1->timeout_duration_us > 60000000) + s->d1->timeout_duration_us = 60000000; dtls1_start_timer(s); } @@ -329,7 +354,7 @@ void dtls1_stop_timer(SSL *s) /* Reset everything */ memset(&s->d1->timeout, 0, sizeof(s->d1->timeout)); memset(&s->d1->next_timeout, 0, sizeof(s->d1->next_timeout)); - s->d1->timeout_duration = 1; + s->d1->timeout_duration_us = 1000000; BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout)); /* Clear retransmission buffer */ @@ -367,7 +392,10 @@ int dtls1_handle_timeout(SSL *s) return 0; } - dtls1_double_timeout(s); + if (s->d1->timer_cb != NULL) + s->d1->timeout_duration_us = s->d1->timer_cb(s, s->d1->timeout_duration_us); + else + dtls1_double_timeout(s); if (dtls1_check_timeout_num(s) < 0) return -1; @@ -952,3 +980,8 @@ size_t DTLS_get_data_mtu(const SSL *s) return mtu; } + +void DTLS_set_timer_cb(SSL *s, DTLS_timer_cb cb) +{ + s->d1->timer_cb = cb; +} diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index a0127cf6a0..59fba61a99 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1624,11 +1624,15 @@ typedef struct dtls1_state_st { */ struct timeval next_timeout; /* Timeout duration */ - unsigned short timeout_duration; + unsigned int timeout_duration_us; + unsigned int retransmitting; # ifndef OPENSSL_NO_SCTP int shutdown_received; # endif + + DTLS_timer_cb timer_cb; + } DTLS1_STATE; # ifndef OPENSSL_NO_EC diff --git a/test/dtlstest.c b/test/dtlstest.c index 1bf173509e..7e511f7d6f 100644 --- a/test/dtlstest.c +++ b/test/dtlstest.c @@ -17,6 +17,7 @@ static char *cert = NULL; static char *privkey = NULL; +static unsigned int timer_cb_count; #define NUM_TESTS 2 @@ -40,6 +41,16 @@ static unsigned char certstatus[] = { #define RECORD_SEQUENCE 10 +static unsigned int timer_cb(SSL *s, unsigned int timer_us) +{ + ++timer_cb_count; + + if (timer_us == 0) + return 1000000; + else + return 2 * timer_us; +} + static int test_dtls_unprocessed(int testidx) { SSL_CTX *sctx = NULL, *cctx = NULL; @@ -47,6 +58,8 @@ static int test_dtls_unprocessed(int testidx) BIO *c_to_s_fbio, *c_to_s_mempacket; int testresult = 0; + timer_cb_count = 0; + if (!TEST_true(create_ssl_ctx_pair(DTLS_server_method(), DTLS_client_method(), &sctx, &cctx, cert, privkey))) @@ -64,6 +77,8 @@ static int test_dtls_unprocessed(int testidx) NULL, c_to_s_fbio))) goto end; + DTLS_set_timer_cb(clientssl1, timer_cb); + if (testidx == 1) certstatus[RECORD_SEQUENCE] = 0xff; @@ -83,6 +98,11 @@ static int test_dtls_unprocessed(int testidx) SSL_ERROR_NONE))) goto end; + if (timer_cb_count == 0) { + printf("timer_callback was not called.\n"); + goto end; + } + testresult = 1; end: SSL_free(serverssl1); diff --git a/util/libssl.num b/util/libssl.num index 14a70234e3..efbd079f4b 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -469,3 +469,4 @@ SSL_SESSION_set_max_early_data 469 1_1_1 EXIST::FUNCTION: SSL_SESSION_set1_alpn_selected 470 1_1_1 EXIST::FUNCTION: SSL_SESSION_set1_hostname 471 1_1_1 EXIST::FUNCTION: SSL_SESSION_get0_alpn_selected 472 1_1_1 EXIST::FUNCTION: +DTLS_set_timer_cb 473 1_1_1 EXIST::FUNCTION: diff --git a/util/private.num b/util/private.num index a757357801..242de12e5f 100644 --- a/util/private.num +++ b/util/private.num @@ -18,6 +18,7 @@ BIO_lookup_type datatype CRYPTO_EX_dup datatype CRYPTO_EX_free datatype CRYPTO_EX_new datatype +DTLS_timer_cb datatype EVP_PKEY_gen_cb datatype EVP_PKEY_METHOD datatype GEN_SESSION_CB datatype -- 2.25.1