blob: f9f8aa8a666af94073322b5547b9f6550aa77832 [file] [edit]
/*
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (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
* https://www.openssl.org/source/license.html
*/
#include <openssl/bio.h>
#include "testutil.h"
#define TEST_FLAG_EOF_BEHAVIOUR 0x1000
static int bio_create(BIO *bio)
{
BIO_set_init(bio, 1);
return 1;
}
static int bio_destroy(BIO *bio)
{
BIO_set_init(bio, 0);
return 1;
}
/*
* Test1 & Test2 read callback (old style):
* returns 0 if TEST_FLAG_EOF_BEHAVIOUR is set, else -1.
*/
static int old_read_returns_0_or_minus1(BIO *bio, char *buf, int len)
{
(void)buf;
(void)len;
return BIO_test_flags(bio, TEST_FLAG_EOF_BEHAVIOUR) ? 0 : -1;
}
/*
* Test3 read_ex callback (new style):
* does nothing, always returns 0, sets *readbytes to 0.
*/
static int new_read_ex_always_0(BIO *bio, char *buf, size_t len, size_t *readbytes)
{
(void)bio;
(void)buf;
(void)len;
if (readbytes != NULL)
*readbytes = 0;
return 0;
}
/* Test1 ctrl: does nothing */
static long ctrl_noop(BIO *bio, int cmd, long num, void *ptr)
{
(void)bio;
(void)cmd;
(void)num;
(void)ptr;
return 0;
}
/* Test2 ctrl: BIO_CTRL_EOF always returns 1 */
static long ctrl_eof_always_1(BIO *bio, int cmd, long num, void *ptr)
{
(void)bio;
(void)num;
(void)ptr;
if (cmd == BIO_CTRL_EOF)
return 1;
return 0;
}
/* Test3 ctrl: BIO_CTRL_EOF returns 1 if TEST_FLAG_EOF_BEHAVIOUR is set */
static long ctrl_eof_depends_on_flag(BIO *bio, int cmd, long num, void *ptr)
{
(void)num;
(void)ptr;
if (cmd == BIO_CTRL_EOF)
return BIO_test_flags(bio, TEST_FLAG_EOF_BEHAVIOUR) ? 1 : 0;
return 0;
}
static BIO_METHOD *make_meth_oldread(long (*ctrl)(BIO *, int, long, void *),
const char *name)
{
BIO_METHOD *meth = NULL;
if (!TEST_ptr(meth = BIO_meth_new(BIO_TYPE_SOURCE_SINK, name)))
goto err;
if (!TEST_int_eq(BIO_meth_set_read(meth, old_read_returns_0_or_minus1), 1))
goto err;
if (!TEST_int_eq(BIO_meth_set_ctrl(meth, ctrl), 1))
goto err;
if (!TEST_int_eq(BIO_meth_set_create(meth, bio_create), 1))
goto err;
if (!TEST_int_eq(BIO_meth_set_destroy(meth, bio_destroy), 1))
goto err;
return meth;
err:
BIO_meth_free(meth);
return NULL;
}
static BIO_METHOD *make_meth_newreadex(long (*ctrl)(BIO *, int, long, void *),
const char *name)
{
BIO_METHOD *meth = NULL;
if (!TEST_ptr(meth = BIO_meth_new(BIO_TYPE_SOURCE_SINK, name)))
goto err;
if (!TEST_int_eq(BIO_meth_set_read_ex(meth, new_read_ex_always_0), 1))
goto err;
if (!TEST_int_eq(BIO_meth_set_ctrl(meth, ctrl), 1))
goto err;
if (!TEST_int_eq(BIO_meth_set_create(meth, bio_create), 1))
goto err;
if (!TEST_int_eq(BIO_meth_set_destroy(meth, bio_destroy), 1))
goto err;
return meth;
err:
BIO_meth_free(meth);
return NULL;
}
static int run_subtest(const char *label, BIO_METHOD *meth,
int set_flag, int use_read_ex,
int exp_read_ret, int exp_eof_ret)
{
BIO *bio = NULL;
char b = 0;
int r, eofr;
size_t n;
if (!TEST_ptr(bio = BIO_new(meth)))
goto err;
if (set_flag)
BIO_set_flags(bio, TEST_FLAG_EOF_BEHAVIOUR);
else
BIO_clear_flags(bio, TEST_FLAG_EOF_BEHAVIOUR);
if (use_read_ex) {
r = BIO_read_ex(bio, &b, 1, &n);
if (!TEST_int_eq(r, exp_read_ret)) {
TEST_info("%s: BIO_read_ex ret=%d expected=%d", label, r, exp_read_ret);
goto err;
}
} else {
r = BIO_read(bio, &b, 1);
if (!TEST_int_eq(r, exp_read_ret)) {
TEST_info("%s: BIO_read ret=%d expected=%d", label, r, exp_read_ret);
goto err;
}
}
eofr = BIO_eof(bio);
if (!TEST_int_eq(eofr, exp_eof_ret)) {
TEST_info("%s: BIO_eof ret=%d expected=%d", label, eofr, exp_eof_ret);
goto err;
}
BIO_free(bio);
return 1;
err:
BIO_free(bio);
return 0;
}
static int old_style_read_without_eof_ctrl(void)
{
int ok = 1;
BIO_METHOD *meth = NULL;
if (!TEST_ptr(meth = make_meth_oldread(ctrl_noop,
"Old-style read without eof ctrl")))
return 0;
ok &= run_subtest("BIO_read, eof", meth, 1, 0, 0, 1);
ok &= run_subtest("BIO_read_ex, eof", meth, 1, 1, 0, 1);
ok &= run_subtest("BIO_read, error", meth, 0, 0, -1, 0);
ok &= run_subtest("BIO_read_ex, error", meth, 0, 1, 0, 0);
BIO_meth_free(meth);
return ok;
}
static int old_style_read_with_eof_ctrl(void)
{
int ok = 1;
BIO_METHOD *meth = NULL;
if (!TEST_ptr(meth = make_meth_oldread(ctrl_eof_always_1,
"Old-stype read with eof ctrl")))
return 0;
ok &= run_subtest("BIO_read, eof", meth, 1, 0, 0, 1);
ok &= run_subtest("BIO_read_ex, eof", meth, 1, 1, 0, 1);
ok &= run_subtest("BIO_read, error", meth, 0, 0, -1, 1);
ok &= run_subtest("BIO_read_ex, error", meth, 0, 1, 0, 1);
BIO_meth_free(meth);
return ok;
}
static int new_style_read_ex(void)
{
int ok = 1;
BIO_METHOD *meth = NULL;
if (!TEST_ptr(meth = make_meth_newreadex(ctrl_eof_depends_on_flag,
"New-style read_ex")))
return 0;
ok &= run_subtest("BIO_read, eof", meth, 1, 0, 0, 1);
ok &= run_subtest("BIO_read_ex, eof", meth, 1, 1, 0, 1);
ok &= run_subtest("BIO_read, error", meth, 0, 0, -1, 0);
ok &= run_subtest("BIO_read_ex, error", meth, 0, 1, 0, 0);
BIO_meth_free(meth);
return ok;
}
int setup_tests(void)
{
ADD_TEST(old_style_read_without_eof_ctrl);
ADD_TEST(old_style_read_with_eof_ctrl);
ADD_TEST(new_style_read_ex);
return 1;
}