Add a test for retries when sending app data

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22473)
This commit is contained in:
Matt Caswell 2023-10-23 12:16:44 +01:00
parent b9b9f4886f
commit dbbdb940d4
3 changed files with 204 additions and 1 deletions

View File

@ -29,13 +29,15 @@ static int tls_dump_gets(BIO *bp, char *buf, int size);
static int tls_dump_puts(BIO *bp, const char *str);
/* Choose a sufficiently large type likely to be unused for this custom BIO */
#define BIO_TYPE_TLS_DUMP_FILTER (0x80 | BIO_TYPE_FILTER)
#define BIO_TYPE_TLS_DUMP_FILTER (0x80 | BIO_TYPE_FILTER)
#define BIO_TYPE_MEMPACKET_TEST 0x81
#define BIO_TYPE_ALWAYS_RETRY 0x82
#define BIO_TYPE_MAYBE_RETRY (0x83 | BIO_TYPE_FILTER)
static BIO_METHOD *method_tls_dump = NULL;
static BIO_METHOD *meth_mem = NULL;
static BIO_METHOD *meth_always_retry = NULL;
static BIO_METHOD *meth_maybe_retry = NULL;
static int retry_err = -1;
/* Note: Not thread safe! */
@ -804,6 +806,100 @@ static int always_retry_puts(BIO *bio, const char *str)
return retry_err;
}
struct maybe_retry_data_st {
unsigned int retrycnt;
};
static int maybe_retry_new(BIO *bi);
static int maybe_retry_free(BIO *a);
static int maybe_retry_write(BIO *b, const char *in, int inl);
static long maybe_retry_ctrl(BIO *b, int cmd, long num, void *ptr);
const BIO_METHOD *bio_s_maybe_retry(void)
{
if (meth_maybe_retry == NULL) {
if (!TEST_ptr(meth_maybe_retry = BIO_meth_new(BIO_TYPE_MAYBE_RETRY,
"Maybe Retry"))
|| !TEST_true(BIO_meth_set_write(meth_maybe_retry,
maybe_retry_write))
|| !TEST_true(BIO_meth_set_ctrl(meth_maybe_retry,
maybe_retry_ctrl))
|| !TEST_true(BIO_meth_set_create(meth_maybe_retry,
maybe_retry_new))
|| !TEST_true(BIO_meth_set_destroy(meth_maybe_retry,
maybe_retry_free)))
return NULL;
}
return meth_maybe_retry;
}
void bio_s_maybe_retry_free(void)
{
BIO_meth_free(meth_maybe_retry);
}
static int maybe_retry_new(BIO *bio)
{
struct maybe_retry_data_st *data = OPENSSL_zalloc(sizeof(*data));
if (data == NULL)
return 0;
BIO_set_data(bio, data);
BIO_set_init(bio, 1);
return 1;
}
static int maybe_retry_free(BIO *bio)
{
struct maybe_retry_data_st *data = BIO_get_data(bio);
OPENSSL_free(data);
BIO_set_data(bio, NULL);
BIO_set_init(bio, 0);
return 1;
}
static int maybe_retry_write(BIO *bio, const char *in, int inl)
{
struct maybe_retry_data_st *data = BIO_get_data(bio);
if (data == NULL)
return -1;
if (data->retrycnt == 0) {
BIO_set_retry_write(bio);
return -1;
}
data->retrycnt--;
return BIO_write(BIO_next(bio), in, inl);
}
static long maybe_retry_ctrl(BIO *bio, int cmd, long num, void *ptr)
{
struct maybe_retry_data_st *data = BIO_get_data(bio);
if (data == NULL)
return 0;
switch (cmd) {
case MAYBE_RETRY_CTRL_SET_RETRY_AFTER_CNT:
data->retrycnt = num;
return 1;
case BIO_CTRL_FLUSH:
if (data->retrycnt == 0) {
BIO_set_retry_write(bio);
return -1;
}
data->retrycnt--;
/* fall through */
default:
return BIO_ctrl(BIO_next(bio), cmd, num, ptr);
}
}
int create_ssl_ctx_pair(OSSL_LIB_CTX *libctx, const SSL_METHOD *sm,
const SSL_METHOD *cm, int min_proto_version,
int max_proto_version, SSL_CTX **sctx, SSL_CTX **cctx,

View File

@ -44,6 +44,15 @@ const BIO_METHOD *bio_s_always_retry(void);
void bio_s_always_retry_free(void);
void set_always_retry_err_val(int err);
/*
* Maybe retry BIO ctrls. We make them large enough to not clash with standard
* BIO ctrl codes.
*/
#define MAYBE_RETRY_CTRL_SET_RETRY_AFTER_CNT (1 << 15)
const BIO_METHOD *bio_s_maybe_retry(void);
void bio_s_maybe_retry_free(void);
/* Packet types - value 0 is reserved */
#define INJECT_PACKET 1
#define INJECT_PACKET_IGNORE_REC_SEQ 2

View File

@ -11138,6 +11138,102 @@ end:
return testresult;
}
/*
* Test that receiving retries when writing application data works as expected
*/
static int test_data_retry(void)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int testresult = 0;
unsigned char inbuf[1200], outbuf[1200];
size_t i;
BIO *tmp = NULL;
BIO *bretry = BIO_new(bio_s_maybe_retry());
size_t written, readbytes, totread = 0;
if (!TEST_ptr(bretry))
goto end;
for (i = 0; i < sizeof(inbuf); i++)
inbuf[i] = i;
memset(outbuf, 0, sizeof(outbuf));
if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
TLS_client_method(), 0, 0, &sctx, &cctx,
cert, privkey)))
goto end;
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL,
NULL)))
goto end;
if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
goto end;
/* Smallest possible max send fragment is 512 */
if (!TEST_true(SSL_set_max_send_fragment(clientssl, 512)))
goto end;
tmp = SSL_get_wbio(clientssl);
if (!TEST_ptr(tmp))
goto end;
if (!TEST_true(BIO_up_ref(tmp)))
goto end;
BIO_push(bretry, tmp);
tmp = NULL;
SSL_set0_wbio(clientssl, bretry);
if (!BIO_up_ref(bretry)) {
bretry = NULL;
goto end;
}
for (i = 0; i < 3; i++) {
/* We expect this call to make no progress and indicate retry */
if (!TEST_false(SSL_write_ex(clientssl, inbuf, sizeof(inbuf), &written)))
goto end;
if (!TEST_int_eq(SSL_get_error(clientssl, 0), SSL_ERROR_WANT_WRITE))
goto end;
/* Allow one write to progess, but the next one to signal retry */
if (!TEST_true(BIO_ctrl(bretry, MAYBE_RETRY_CTRL_SET_RETRY_AFTER_CNT, 1,
NULL)))
goto end;
if (i == 2)
break;
/*
* This call will hopefully make progress but will still indicate retry
* because there is more data than will fit into a single record.
*/
if (!TEST_false(SSL_write_ex(clientssl, inbuf, sizeof(inbuf), &written)))
goto end;
if (!TEST_int_eq(SSL_get_error(clientssl, 0), SSL_ERROR_WANT_WRITE))
goto end;
}
/* The final call should write the last chunk of data and succeed */
if (!TEST_true(SSL_write_ex(clientssl, inbuf, sizeof(inbuf), &written)))
goto end;
/* Read all the data available */
while (SSL_read_ex(serverssl, outbuf + totread, sizeof(outbuf) - totread,
&readbytes))
totread += readbytes;
if (!TEST_mem_eq(inbuf, sizeof(inbuf), outbuf, totread))
goto end;
testresult = 1;
end:
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
BIO_free_all(bretry);
BIO_free(tmp);
return testresult;
}
OPT_TEST_DECLARE_USAGE("certfile privkeyfile srpvfile tmpfile provider config dhfile\n")
int setup_tests(void)
@ -11444,6 +11540,7 @@ int setup_tests(void)
ADD_ALL_TESTS(test_version, 6);
ADD_TEST(test_rstate_string);
ADD_ALL_TESTS(test_handshake_retry, 16);
ADD_TEST(test_data_retry);
return 1;
err:
@ -11473,6 +11570,7 @@ void cleanup_tests(void)
OPENSSL_free(privkey8192);
bio_s_mempacket_test_free();
bio_s_always_retry_free();
bio_s_maybe_retry_free();
OSSL_PROVIDER_unload(defctxnull);
OSSL_LIB_CTX_free(libctx);
}