]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add a test for retries when sending app data
authorMatt Caswell <matt@openssl.org>
Mon, 23 Oct 2023 11:16:44 +0000 (12:16 +0100)
committerMatt Caswell <matt@openssl.org>
Tue, 24 Oct 2023 16:37:19 +0000 (17:37 +0100)
Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22473)

test/helpers/ssltestlib.c
test/helpers/ssltestlib.h
test/sslapitest.c

index 3ae3e7d4eaf47ee1996a9e5c6ef435b811e08161..ddc6bb7b64ab508d1e726267d5466bf2cfaea2fd 100644 (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,
index c513769ddd956027957dd7022af260a84e6e47ba..eb54b04f2c662112837832c579ecb7505c668ee4 100644 (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
index 9539b4cf3a658ac477b418fbe479974b30580cc5..2fbe76f97980368e2f510fb102dc32595ed15c84 100644 (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);
 }