From 61e96557f9eae0258074c9cec7ad6aa1b9dde1df Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Thu, 3 May 2018 12:06:38 +0100 Subject: Add a DTLS test for dropped records Drop a record from a handshake and check that we can still complete the handshake. Repeat for all records in the handshake. Reviewed-by: Rich Salz (Merged from https://github.com/openssl/openssl/pull/6170) --- test/ssltestlib.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) (limited to 'test/ssltestlib.c') diff --git a/test/ssltestlib.c b/test/ssltestlib.c index 959e3296a8..041ae26676 100644 --- a/test/ssltestlib.c +++ b/test/ssltestlib.c @@ -12,6 +12,34 @@ #include "internal/nelem.h" #include "ssltestlib.h" #include "testutil.h" +#include "e_os.h" + +#ifdef OPENSSL_SYS_UNIX +# include + +static ossl_inline void ossl_sleep(unsigned int millis) { + usleep(millis * 1000); +} +#elif defined(_WIN32) +# include + +static ossl_inline void ossl_sleep(unsigned int millis) { + Sleep(millis); +} +#else +/* Fallback to a busy wait */ +static ossl_inline void ossl_sleep(unsigned int millis) { + struct timeval start, now; + unsigned int elapsedms; + + gettimeofday(&start, NULL); + do { + gettimeofday(&now, NULL); + elapsedms = (((now.tv_sec - start.tv_sec) * 1000000) + + now.tv_usec - start.tv_usec) / 1000; + } while (elapsedms < millis); +} +#endif static int tls_dump_new(BIO *bi); static int tls_dump_free(BIO *a); @@ -252,7 +280,10 @@ typedef struct mempacket_test_ctx_st { unsigned int currrec; unsigned int currpkt; unsigned int lastpkt; + unsigned int injected; unsigned int noinject; + unsigned int dropepoch; + int droprec; } MEMPACKET_TEST_CTX; static int mempacket_test_new(BIO *bi); @@ -295,6 +326,8 @@ static int mempacket_test_new(BIO *bio) OPENSSL_free(ctx); return 0; } + ctx->dropepoch = 0; + ctx->droprec = -1; BIO_set_init(bio, 1); BIO_set_data(bio, ctx); return 1; @@ -312,8 +345,8 @@ static int mempacket_test_free(BIO *bio) } /* Record Header values */ -#define EPOCH_HI 4 -#define EPOCH_LO 5 +#define EPOCH_HI 3 +#define EPOCH_LO 4 #define RECORD_SEQUENCE 10 #define RECORD_LEN_HI 11 #define RECORD_LEN_LO 12 @@ -341,15 +374,15 @@ static int mempacket_test_read(BIO *bio, char *out, int outl) if (outl > thispkt->len) outl = thispkt->len; - if (thispkt->type != INJECT_PACKET_IGNORE_REC_SEQ) { + if (thispkt->type != INJECT_PACKET_IGNORE_REC_SEQ + && (ctx->injected || ctx->droprec >= 0)) { /* * Overwrite the record sequence number. We strictly number them in * the order received. Since we are actually a reliable transport * we know that there won't be any re-ordering. We overwrite to deal * with any packets that have been injected */ - for (rem = thispkt->len, rec = thispkt->data - ; rem > 0; rec += len, rem -= len) { + for (rem = thispkt->len, rec = thispkt->data; rem > 0; rem -= len) { if (rem < DTLS1_RT_HEADER_LENGTH) return -1; epoch = (rec[EPOCH_HI] << 8) | rec[EPOCH_LO]; @@ -364,10 +397,23 @@ static int mempacket_test_read(BIO *bio, char *out, int outl) seq >>= 8; offset++; } while (seq > 0); - ctx->currrec++; len = ((rec[RECORD_LEN_HI] << 8) | rec[RECORD_LEN_LO]) + DTLS1_RT_HEADER_LENGTH; + if (rem < (int)len) + return -1; + if (ctx->droprec == (int)ctx->currrec && ctx->dropepoch == epoch) { + if (rem > (int)len) + memmove(rec, rec + len, rem - len); + outl -= len; + ctx->droprec = -1; + if (outl == 0) + BIO_set_retry_read(bio); + } else { + rec += len; + } + + ctx->currrec++; } } @@ -390,6 +436,7 @@ int mempacket_test_inject(BIO *bio, const char *in, int inl, int pktnum, if (pktnum >= 0) { if (ctx->noinject) return -1; + ctx->injected = 1; } else { ctx->noinject = 1; } @@ -488,6 +535,15 @@ static long mempacket_test_ctrl(BIO *bio, int cmd, long num, void *ptr) case BIO_CTRL_FLUSH: ret = 1; break; + case MEMPACKET_CTRL_SET_DROP_EPOCH: + ctx->dropepoch = (unsigned int)num; + break; + case MEMPACKET_CTRL_SET_DROP_REC: + ctx->droprec = (int)num; + break; + case MEMPACKET_CTRL_GET_DROP_REC: + ret = ctx->droprec; + break; case BIO_CTRL_RESET: case BIO_CTRL_DUP: case BIO_CTRL_PUSH: @@ -627,6 +683,7 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want) int clienterr = 0, servererr = 0; unsigned char buf; size_t readbytes; + int isdtls = SSL_is_dtls(serverssl); do { err = SSL_ERROR_WANT_WRITE; @@ -658,10 +715,24 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want) return 0; if (clienterr && servererr) return 0; + if (isdtls) { + if (rets > 0 && retc <= 0) + DTLSv1_handle_timeout(serverssl); + if (retc > 0 && rets <= 0) + DTLSv1_handle_timeout(clientssl); + } if (++abortctr == MAXLOOPS) { TEST_info("No progress made"); return 0; } + if (isdtls && abortctr <= 50 && (abortctr % 10) == 0) { + /* + * It looks like we're just spinning. Pause for a short period to + * give the DTLS timer a chance to do something. We only do this for + * the first few times to prevent hangs. + */ + ossl_sleep(50); + } } while (retc <=0 || rets <= 0); /* -- cgit v1.2.3