From 377fc3160cf39f7acc8010e930b5044cbff42ae2 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Mon, 26 Jan 2015 00:22:12 -0500 Subject: [PATCH] Document DTLS timeout API and add current_time_cb hook. This is so the tests needn't be sensitive to the clock. It is, unfortunately, a test-only hook, but the DTLS retransmit/timeout logic more-or-less requires it currently. Use this hook to, for now, freeze the clock at zero. This makes the tests deterministic. It might be worth designing a saner API in the future. The current one, notably, requires that the caller's clock be compatible with the one we internally use. It's also not clear whether the caller needs to call DTLSv1_handle_timeout or can just rely on the state machine doing it internally (as it does do). But mock clocks are relatively tame and WebRTC wants to compile against upstream OpenSSL for now, so we're limited in how much new API we can build. Change-Id: I7aad51570596f69275ed0fc1a8892393e4b7ba13 Reviewed-on: https://boringssl-review.googlesource.com/3210 Reviewed-by: Adam Langley --- include/openssl/dtls1.h | 12 ------------ include/openssl/ssl.h | 34 ++++++++++++++++++++++++++++++++++ ssl/d1_lib.c | 19 ++++++++++++------- ssl/test/bssl_shim.cc | 6 ++++++ 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/include/openssl/dtls1.h b/include/openssl/dtls1.h index 0fc3ae6f..17321b0e 100644 --- a/include/openssl/dtls1.h +++ b/include/openssl/dtls1.h @@ -87,18 +87,6 @@ extern "C" { #ifndef OPENSSL_NO_SSL_INTERN -#if defined(OPENSSL_WINDOWS) -/* Because of Windows header issues, we can't get the normal declaration of - * timeval. */ -typedef struct OPENSSL_timeval_st { - long tv_sec; - long tv_usec; -} OPENSSL_timeval; -#else -#include -typedef struct timeval OPENSSL_timeval; -#endif - typedef struct dtls1_bitmap_st { /* map is a bit mask of the last 64 sequence numbers. Bit diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 649e38fb..5af1777e 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -361,6 +361,18 @@ struct ssl_session_st { char extended_master_secret; }; +#if defined(OPENSSL_WINDOWS) +/* Because of Windows header issues, we can't get the normal declaration of + * timeval. */ +typedef struct OPENSSL_timeval_st { + long tv_sec; + long tv_usec; +} OPENSSL_timeval; +#else +#include +typedef struct timeval OPENSSL_timeval; +#endif + /* SSL_OP_LEGACY_SERVER_CONNECT allows initial connection to servers that don't * support RI */ #define SSL_OP_LEGACY_SERVER_CONNECT 0x00000004L @@ -893,6 +905,10 @@ struct ssl_ctx_st { /* If not NULL, session key material will be logged to this BIO for debugging * purposes. The format matches NSS's and is readable by Wireshark. */ BIO *keylog_bio; + + /* current_time_cb, if not NULL, is the function to use to get the current + * time. It sets |*out_clock| to the current time. */ + void (*current_time_cb)(SSL *ssl, OPENSSL_timeval *out_clock); }; #define SSL_SESS_CACHE_OFF 0x0000 @@ -1596,8 +1612,26 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) #define SSL_CTRL_FALLBACK_SCSV 120 +/* DTLSv1_get_timeout queries the next DTLS handshake timeout. If there is a + * timeout in progress, it sets |*((OPENSSL_timeval*)arg)| to the time remaining + * and returns one. Otherwise, it returns zero. + * + * When the timeout expires, call |DTLSv1_handle_timeout| to handle the + * retransmit behavior. + * + * NOTE: This function must be queried again whenever the handshake state + * machine changes, including when |DTLSv1_handle_timeout| is called. */ #define DTLSv1_get_timeout(ssl, arg) \ SSL_ctrl(ssl, DTLS_CTRL_GET_TIMEOUT, 0, (void *)arg) + +/* 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. + * + * NOTE: The caller's external timer should be compatible with the one |ssl| + * queries within some fudge factor. Otherwise, the call will be a no-op, but + * |DTLSv1_get_timeout| will return an updated timeout. */ #define DTLSv1_handle_timeout(ssl) \ SSL_ctrl(ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, NULL) diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index 8244cb94..a41a4395 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -72,7 +72,7 @@ #include "ssl_locl.h" -static void get_current_time(OPENSSL_timeval *t); +static void get_current_time(SSL *ssl, OPENSSL_timeval *out_clock); static OPENSSL_timeval *dtls1_get_timeout(SSL *s, OPENSSL_timeval *timeleft); static void dtls1_set_handshake_header(SSL *s, int type, unsigned long len); static int dtls1_handshake_write(SSL *s); @@ -272,7 +272,7 @@ void dtls1_start_timer(SSL *s) { } /* Set timeout to current time */ - get_current_time(&s->d1->next_timeout); + get_current_time(s, &s->d1->next_timeout); /* Add duration to current time */ s->d1->next_timeout.tv_sec += s->d1->timeout_duration; @@ -289,7 +289,7 @@ static OPENSSL_timeval *dtls1_get_timeout(SSL *s, OPENSSL_timeval *timeleft) { } /* Get current time */ - get_current_time(&timenow); + get_current_time(s, &timenow); /* If timer already expired, set remaining time to 0 */ if (s->d1->next_timeout.tv_sec < timenow.tv_sec || @@ -396,14 +396,19 @@ int dtls1_handle_timeout(SSL *s) { return dtls1_retransmit_buffered_messages(s); } -static void get_current_time(OPENSSL_timeval *t) { +static void get_current_time(SSL *ssl, OPENSSL_timeval *out_clock) { + if (ssl->ctx->current_time_cb != NULL) { + ssl->ctx->current_time_cb(ssl, out_clock); + return; + } + #if defined(OPENSSL_WINDOWS) struct _timeb time; _ftime(&time); - t->tv_sec = time.time; - t->tv_usec = time.millitm * 1000; + out_clock->tv_sec = time.time; + out_clock->tv_usec = time.millitm * 1000; #else - gettimeofday(t, NULL); + gettimeofday(out_clock, NULL); #endif } diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index 37891b99..33881fc1 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc @@ -228,6 +228,10 @@ static unsigned psk_server_callback(SSL *ssl, const char *identity, return config->psk.size(); } +static void current_time_cb(SSL *ssl, OPENSSL_timeval *out_clock) { + memset(out_clock, 0, sizeof(*out_clock)); +} + static SSL_CTX *setup_ctx(const TestConfig *config) { SSL_CTX *ssl_ctx = NULL; DH *dh = NULL; @@ -278,6 +282,8 @@ static SSL_CTX *setup_ctx(const TestConfig *config) { ssl_ctx->tlsext_channel_id_enabled_new = 1; + ssl_ctx->current_time_cb = current_time_cb; + DH_free(dh); return ssl_ctx;