add callback handler for setting DTLS timer interval
authorAlfred E. Heggestad <alfred.heggestad@gmail.com>
Wed, 6 Sep 2017 06:30:00 +0000 (08:30 +0200)
committerBernd Edlinger <bernd.edlinger@hotmail.de>
Wed, 6 Sep 2017 06:30:00 +0000 (08:30 +0200)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Bernd Edlinger <bernd.edlinger@hotmail.de>
(Merged from https://github.com/openssl/openssl/pull/4011)

doc/man3/DTLS_set_timer_cb.pod [new file with mode: 0644]
include/openssl/ssl.h
ssl/d1_lib.c
ssl/ssl_locl.h
test/dtlstest.c
util/libssl.num
util/private.num

diff --git a/doc/man3/DTLS_set_timer_cb.pod b/doc/man3/DTLS_set_timer_cb.pod
new file mode 100644 (file)
index 0000000..6e13472
--- /dev/null
@@ -0,0 +1,40 @@
+=pod
+
+=head1 NAME
+
+DTLS_timer_cb,
+DTLS_set_timer_cb
+- Set callback for controlling DTLS timer duration
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ 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<https://www.openssl.org/source/license.html>.
+
+=cut
index 248408f..da1fa0f 100644 (file)
@@ -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
index d839e1a..a8ea097 100644 (file)
@@ -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;
+}
index a0127cf..59fba61 100644 (file)
@@ -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
index 1bf1735..7e511f7 100644 (file)
@@ -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);
index 14a7023..efbd079 100644 (file)
@@ -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:
index a757357..242de12 100644 (file)
@@ -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