mirror of
https://boringssl.googlesource.com/boringssl
synced 2026-07-21 14:43:51 +00:00
Implement BIO_write_ex
This does not yet switch BoringSSL's built-in BIOs, and callers within the library to the new function. That will be done in subsequent CLs. Bug: 42290376 Change-Id: Id679908a02f04e53487ec68f5931a0dd8ec8e405 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/96109 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Rudolf Polzer <rpolzer@google.com>
This commit is contained in:
committed by
boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
65f2ef02f3
commit
01cee05c3f
+50
-13
@@ -19,6 +19,7 @@
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include <openssl/asn1.h>
|
||||
@@ -119,27 +120,56 @@ int BIO_gets(BIO *bio, char *buf, int len) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BIO_write(BIO *bio, const void *in, int inl) {
|
||||
int BIO_write_ex(BIO *bio, const void *data, size_t len, size_t *out_written) {
|
||||
if (out_written != nullptr) {
|
||||
*out_written = 0;
|
||||
}
|
||||
auto *impl = FromOpaque(bio);
|
||||
if (impl == nullptr || impl->method->bwrite == nullptr) {
|
||||
if (impl == nullptr ||
|
||||
(impl->method->bwrite == nullptr && impl->method->bwrite_ex == nullptr)) {
|
||||
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
if (!impl->init) {
|
||||
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
|
||||
return -1;
|
||||
}
|
||||
if (inl <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int ret = impl->method->bwrite(impl, reinterpret_cast<const char *>(in), inl);
|
||||
if (ret > 0) {
|
||||
impl->num_write += ret;
|
||||
} else {
|
||||
// In preparation for |BIO_write_ex|, canonicalize error returns to -1.
|
||||
ret = -1;
|
||||
// Matching OpenSSL, writing zero bytes should "successfully" write no bytes.
|
||||
if (len == 0) {
|
||||
return 1;
|
||||
}
|
||||
return ret;
|
||||
// Support `BIO_METHOD`s using either the old or new API.
|
||||
size_t written;
|
||||
if (impl->method->bwrite_ex != nullptr) {
|
||||
if (!impl->method->bwrite_ex(bio, static_cast<const char *>(data), len,
|
||||
&written)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
int ret =
|
||||
impl->method->bwrite(impl, static_cast<const char *>(data),
|
||||
static_cast<int>(std::min(len, size_t{INT_MAX})));
|
||||
if (ret <= 0) {
|
||||
return 0;
|
||||
}
|
||||
written = static_cast<size_t>(ret);
|
||||
}
|
||||
impl->num_write += written;
|
||||
if (out_written != nullptr) {
|
||||
*out_written = written;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BIO_write(BIO *bio, const void *in, int inl) {
|
||||
// Matching OpenSSL, `inl <= 0` returns zero, i.e. a "successful" write of
|
||||
// zero bytes.
|
||||
inl = std::max(inl, 0);
|
||||
size_t written;
|
||||
if (!BIO_write_ex(bio, in, static_cast<size_t>(inl), &written)) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(written);
|
||||
}
|
||||
|
||||
int BIO_write_all(BIO *bio, const void *data, size_t len) {
|
||||
@@ -613,6 +643,13 @@ int BIO_meth_set_destroy(BIO_METHOD *method, int (*destroy_func)(BIO *)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BIO_meth_set_write_ex(BIO_METHOD *method,
|
||||
int (*write_ex_func)(BIO *, const char *, size_t,
|
||||
size_t *)) {
|
||||
method->bwrite_ex = write_ex_func;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BIO_meth_set_write(BIO_METHOD *method,
|
||||
int (*write_func)(BIO *, const char *, int)) {
|
||||
method->bwrite = write_func;
|
||||
|
||||
@@ -224,9 +224,11 @@ static long mem_ctrl(BIO *bio, int cmd, long num, void *ptr) {
|
||||
}
|
||||
|
||||
static const BIO_METHOD mem_method = {
|
||||
BIO_TYPE_MEM, "memory buffer", mem_write,
|
||||
mem_read, mem_gets, mem_ctrl,
|
||||
mem_new, mem_free, /*callback_ctrl=*/nullptr,
|
||||
BIO_TYPE_MEM, "memory buffer",
|
||||
mem_write, /*bwrite_ex=*/nullptr,
|
||||
mem_read, mem_gets,
|
||||
mem_ctrl, mem_new,
|
||||
mem_free, /*callback_ctrl=*/nullptr,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_s_mem() { return &mem_method; }
|
||||
|
||||
@@ -1449,5 +1449,132 @@ TEST(BIOTest, CanonicalizeErrors) {
|
||||
EXPECT_EQ(BIO_write(bio.get(), "foo", 3), -1);
|
||||
}
|
||||
|
||||
// BIO_write should work with BIOs that implement BIO_write_ex and vice versa.
|
||||
TEST(BIOTest, WriteExConversion) {
|
||||
enum class WriteResult { kSuccess, kFail, kRetry };
|
||||
struct MockWrite {
|
||||
std::string expected_data;
|
||||
WriteResult result = WriteResult::kSuccess;
|
||||
size_t bytes_written = 0;
|
||||
int fail_return = -1; // Only used for write_method failure
|
||||
};
|
||||
|
||||
UniquePtr<BIO_METHOD> write_method(BIO_meth_new(0, nullptr));
|
||||
ASSERT_TRUE(write_method);
|
||||
BIO_meth_set_write(
|
||||
write_method.get(), [](BIO *bio, const char *in, int len) -> int {
|
||||
auto *state = static_cast<MockWrite *>(BIO_get_data(bio));
|
||||
EXPECT_EQ(std::string(in, len), state->expected_data);
|
||||
BIO_clear_retry_flags(bio);
|
||||
switch (state->result) {
|
||||
case WriteResult::kSuccess:
|
||||
return static_cast<int>(state->bytes_written);
|
||||
case WriteResult::kFail:
|
||||
return state->fail_return;
|
||||
case WriteResult::kRetry:
|
||||
BIO_set_retry_write(bio);
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
|
||||
UniquePtr<BIO_METHOD> write_ex_method(BIO_meth_new(0, nullptr));
|
||||
ASSERT_TRUE(write_ex_method);
|
||||
BIO_meth_set_write_ex(
|
||||
write_ex_method.get(),
|
||||
[](BIO *bio, const char *in, size_t len, size_t *out_written) -> int {
|
||||
auto *state = static_cast<MockWrite *>(BIO_get_data(bio));
|
||||
EXPECT_EQ(std::string(in, len), state->expected_data);
|
||||
BIO_clear_retry_flags(bio);
|
||||
switch (state->result) {
|
||||
case WriteResult::kSuccess:
|
||||
*out_written = state->bytes_written;
|
||||
return 1;
|
||||
case WriteResult::kFail:
|
||||
return 0;
|
||||
case WriteResult::kRetry:
|
||||
BIO_set_retry_write(bio);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (const BIO_METHOD *meth : {write_method.get(), write_ex_method.get()}) {
|
||||
SCOPED_TRACE(meth == write_method.get() ? "write" : "write_ex");
|
||||
|
||||
auto make_bio = [&](MockWrite *mock_write) -> UniquePtr<BIO> {
|
||||
UniquePtr<BIO> bio(BIO_new(meth));
|
||||
if (bio == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
BIO_set_data(bio.get(), mock_write);
|
||||
BIO_set_init(bio.get(), 1);
|
||||
return bio;
|
||||
};
|
||||
|
||||
MockWrite mock_write;
|
||||
static const char kInput[] = "hello";
|
||||
mock_write.expected_data = kInput;
|
||||
|
||||
// Test BIO_write
|
||||
{
|
||||
auto bio = make_bio(&mock_write);
|
||||
ASSERT_TRUE(bio);
|
||||
|
||||
mock_write.result = WriteResult::kSuccess;
|
||||
mock_write.bytes_written = 5;
|
||||
EXPECT_EQ(BIO_write(bio.get(), kInput, strlen(kInput)), 5);
|
||||
EXPECT_FALSE(BIO_should_retry(bio.get()));
|
||||
|
||||
mock_write.result = WriteResult::kFail;
|
||||
mock_write.fail_return = -1;
|
||||
EXPECT_EQ(BIO_write(bio.get(), kInput, strlen(kInput)), -1);
|
||||
EXPECT_FALSE(BIO_should_retry(bio.get()));
|
||||
|
||||
// Test both return values for |write_method|.
|
||||
mock_write.fail_return = 0;
|
||||
EXPECT_EQ(BIO_write(bio.get(), kInput, strlen(kInput)), -1);
|
||||
EXPECT_FALSE(BIO_should_retry(bio.get()));
|
||||
|
||||
mock_write.result = WriteResult::kRetry;
|
||||
EXPECT_EQ(BIO_write(bio.get(), kInput, strlen(kInput)), -1);
|
||||
EXPECT_TRUE(BIO_should_retry(bio.get()));
|
||||
EXPECT_TRUE(BIO_should_write(bio.get()));
|
||||
}
|
||||
|
||||
// Test BIO_write_ex
|
||||
{
|
||||
auto bio = make_bio(&mock_write);
|
||||
ASSERT_TRUE(bio);
|
||||
size_t written = 0;
|
||||
|
||||
mock_write.result = WriteResult::kSuccess;
|
||||
mock_write.bytes_written = 5;
|
||||
EXPECT_EQ(BIO_write_ex(bio.get(), kInput, strlen(kInput), &written), 1);
|
||||
EXPECT_EQ(written, 5u);
|
||||
EXPECT_FALSE(BIO_should_retry(bio.get()));
|
||||
|
||||
// `out_written` can be nullptr.
|
||||
EXPECT_EQ(BIO_write_ex(bio.get(), kInput, strlen(kInput), nullptr), 1);
|
||||
EXPECT_FALSE(BIO_should_retry(bio.get()));
|
||||
|
||||
mock_write.result = WriteResult::kFail;
|
||||
mock_write.fail_return = -1;
|
||||
EXPECT_EQ(BIO_write_ex(bio.get(), kInput, strlen(kInput), &written), 0);
|
||||
EXPECT_FALSE(BIO_should_retry(bio.get()));
|
||||
|
||||
// Test both return values for |write_method|.
|
||||
mock_write.fail_return = 0;
|
||||
EXPECT_EQ(BIO_write_ex(bio.get(), kInput, strlen(kInput), &written), 0);
|
||||
EXPECT_FALSE(BIO_should_retry(bio.get()));
|
||||
|
||||
mock_write.result = WriteResult::kRetry;
|
||||
EXPECT_EQ(BIO_write_ex(bio.get(), kInput, strlen(kInput), &written), 0);
|
||||
EXPECT_TRUE(BIO_should_retry(bio.get()));
|
||||
EXPECT_TRUE(BIO_should_write(bio.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
BSSL_NAMESPACE_END
|
||||
|
||||
@@ -425,9 +425,9 @@ BIO *BIO_new_connect(const char *hostname) {
|
||||
}
|
||||
|
||||
static const BIO_METHOD methods_connectp = {
|
||||
BIO_TYPE_CONNECT, "socket connect", conn_write,
|
||||
conn_read, /*gets=*/nullptr, conn_ctrl,
|
||||
conn_new, conn_free, conn_callback_ctrl,
|
||||
BIO_TYPE_CONNECT, "socket connect", conn_write, /*bwrite_ex=*/nullptr,
|
||||
conn_read, /*gets=*/nullptr, conn_ctrl, conn_new,
|
||||
conn_free, conn_callback_ctrl,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_s_connect() { return &methods_connectp; }
|
||||
|
||||
+5
-3
@@ -163,9 +163,11 @@ static int fd_gets(BIO *bp, char *buf, int size) {
|
||||
}
|
||||
|
||||
static const BIO_METHOD methods_fdp = {
|
||||
BIO_TYPE_FD, "file descriptor", fd_write,
|
||||
fd_read, fd_gets, fd_ctrl,
|
||||
fd_new, fd_free, /*callback_ctrl=*/nullptr,
|
||||
BIO_TYPE_FD, "file descriptor",
|
||||
fd_write, /*bwrite_ex=*/nullptr,
|
||||
fd_read, fd_gets,
|
||||
fd_ctrl, fd_new,
|
||||
fd_free, /*callback_ctrl=*/nullptr,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_s_fd() { return &methods_fdp; }
|
||||
|
||||
+10
-3
@@ -234,9 +234,16 @@ static int file_gets(BIO *bp, char *buf, int size) {
|
||||
}
|
||||
|
||||
static const BIO_METHOD methods_filep = {
|
||||
BIO_TYPE_FILE, "FILE pointer", file_write,
|
||||
file_read, file_gets, file_ctrl,
|
||||
/*create=*/nullptr, file_free, /*callback_ctrl=*/nullptr,
|
||||
BIO_TYPE_FILE,
|
||||
"FILE pointer",
|
||||
file_write,
|
||||
/*bwrite_ex=*/nullptr,
|
||||
file_read,
|
||||
file_gets,
|
||||
file_ctrl,
|
||||
/*create=*/nullptr,
|
||||
file_free,
|
||||
/*callback_ctrl=*/nullptr,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_s_file() { return &methods_filep; }
|
||||
|
||||
@@ -43,6 +43,7 @@ struct bio_method_st {
|
||||
int type;
|
||||
const char *name;
|
||||
int (*bwrite)(BIO *, const char *, int);
|
||||
int (*bwrite_ex)(BIO *, const char *, size_t, size_t *);
|
||||
int (*bread)(BIO *, char *, int);
|
||||
int (*bgets)(BIO *, char *, int);
|
||||
long (*ctrl)(BIO *, int, long, void *);
|
||||
|
||||
@@ -402,6 +402,7 @@ static const BIO_METHOD methods_biop = {
|
||||
BIO_TYPE_BIO,
|
||||
"BIO pair",
|
||||
bio_write,
|
||||
/*bwrite_ex=*/nullptr,
|
||||
bio_read,
|
||||
/*gets=*/nullptr,
|
||||
bio_ctrl,
|
||||
|
||||
@@ -112,15 +112,11 @@ static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) {
|
||||
}
|
||||
|
||||
static const BIO_METHOD methods_sockp = {
|
||||
BIO_TYPE_SOCKET,
|
||||
"socket",
|
||||
sock_write,
|
||||
sock_read,
|
||||
nullptr /* gets, */,
|
||||
sock_ctrl,
|
||||
nullptr /* create */,
|
||||
sock_free,
|
||||
nullptr /* callback_ctrl */,
|
||||
BIO_TYPE_SOCKET, "socket",
|
||||
sock_write, /*bwrite_ex=*/nullptr,
|
||||
sock_read, nullptr /* gets, */,
|
||||
sock_ctrl, nullptr /* create */,
|
||||
sock_free, nullptr /* callback_ctrl */,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_s_socket() { return &methods_sockp; }
|
||||
|
||||
@@ -480,9 +480,9 @@ static long b64_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) {
|
||||
}
|
||||
|
||||
static const BIO_METHOD b64_method = {
|
||||
BIO_TYPE_BASE64, "base64 encoding", b64_write, b64_read,
|
||||
/*bgets=*/nullptr, b64_ctrl, b64_new, b64_free,
|
||||
b64_callback_ctrl,
|
||||
BIO_TYPE_BASE64, "base64 encoding", b64_write, /*bwrite_ex=*/nullptr,
|
||||
b64_read, /*bgets=*/nullptr, b64_ctrl, b64_new,
|
||||
b64_free, b64_callback_ctrl,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_f_base64() { return &b64_method; }
|
||||
|
||||
@@ -82,6 +82,12 @@ OPENSSL_EXPORT int BIO_read(BIO *bio, void *data, int len);
|
||||
// outputs the bytes which were available.
|
||||
OPENSSL_EXPORT int BIO_gets(BIO *bio, char *buf, int size);
|
||||
|
||||
// BIO_write_ex writes `len` bytes from `data` to `bio`. On success, it returns
|
||||
// one and sets `*out_written` to the number of bytes written. Otherwise, it
|
||||
// returns zero. `out_written` may be NULL to ignore the value.
|
||||
OPENSSL_EXPORT int BIO_write_ex(BIO *bio, const void *data, size_t len,
|
||||
size_t *out_written);
|
||||
|
||||
// BIO_write writes `len` bytes from `data` to `bio`. It returns the number of
|
||||
// bytes written or a negative number on error.
|
||||
OPENSSL_EXPORT int BIO_write(BIO *bio, const void *data, int len);
|
||||
@@ -682,9 +688,28 @@ OPENSSL_EXPORT int BIO_meth_set_create(BIO_METHOD *method,
|
||||
OPENSSL_EXPORT int BIO_meth_set_destroy(BIO_METHOD *method,
|
||||
int (*destroy_func)(BIO *));
|
||||
|
||||
// BIO_meth_set_write_ex sets the implementation of `BIO_write_ex` for `method`
|
||||
// and returns one. `BIO_METHOD`s which implement `BIO_write_ex` should also
|
||||
// implement `BIO_CTRL_FLUSH`. (See `BIO_meth_set_ctrl`.)
|
||||
//
|
||||
// `write_ex_func` can assume `out_written` is non-NULL.
|
||||
//
|
||||
// If configured, `write_ex_func` will also be used to implement `BIO_write`,
|
||||
// with the `BIO` framework converting the conventions. `BIO_meth_set_write_ex`
|
||||
// and `BIO_meth_set_write` should not be configured on the same `BIO_METHOD`.
|
||||
// Prefer `BIO_meth_set_write_ex` for a `size_t`-based API.
|
||||
OPENSSL_EXPORT int BIO_meth_set_write_ex(
|
||||
BIO_METHOD *method,
|
||||
int (*write_ex_func)(BIO *, const char *, size_t, size_t *));
|
||||
|
||||
// BIO_meth_set_write sets the implementation of `BIO_write` for `method` and
|
||||
// returns one. `BIO_METHOD`s which implement `BIO_write` should also implement
|
||||
// `BIO_CTRL_FLUSH`. (See `BIO_meth_set_ctrl`.)
|
||||
//
|
||||
// If configured, `write_func` will also be used to implement `BIO_write_ex`,
|
||||
// with the `BIO` framework converting the conventions. `BIO_meth_set_write_ex`
|
||||
// and `BIO_meth_set_write` should not be configured on the same `BIO_METHOD`.
|
||||
// Prefer `BIO_meth_set_write_ex` for a `size_t`-based API.
|
||||
OPENSSL_EXPORT int BIO_meth_set_write(BIO_METHOD *method,
|
||||
int (*write_func)(BIO *, const char *,
|
||||
int));
|
||||
|
||||
Generated
+4
@@ -274,6 +274,7 @@
|
||||
#pragma redefine_extname BIO_meth_set_puts BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_meth_set_puts)
|
||||
#pragma redefine_extname BIO_meth_set_read BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_meth_set_read)
|
||||
#pragma redefine_extname BIO_meth_set_write BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_meth_set_write)
|
||||
#pragma redefine_extname BIO_meth_set_write_ex BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_meth_set_write_ex)
|
||||
#pragma redefine_extname BIO_method_type BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_method_type)
|
||||
#pragma redefine_extname BIO_new BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_new)
|
||||
#pragma redefine_extname BIO_new_bio_pair BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_new_bio_pair)
|
||||
@@ -337,6 +338,7 @@
|
||||
#pragma redefine_extname BIO_wpending BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_wpending)
|
||||
#pragma redefine_extname BIO_write BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_write)
|
||||
#pragma redefine_extname BIO_write_all BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_write_all)
|
||||
#pragma redefine_extname BIO_write_ex BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_write_ex)
|
||||
#pragma redefine_extname BIO_write_filename BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_write_filename)
|
||||
#pragma redefine_extname BLAKE2B256 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BLAKE2B256)
|
||||
#pragma redefine_extname BLAKE2B256_Final BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BLAKE2B256_Final)
|
||||
@@ -3388,6 +3390,7 @@
|
||||
#define BIO_meth_set_puts BORINGSSL_ADD_PREFIX(BIO_meth_set_puts)
|
||||
#define BIO_meth_set_read BORINGSSL_ADD_PREFIX(BIO_meth_set_read)
|
||||
#define BIO_meth_set_write BORINGSSL_ADD_PREFIX(BIO_meth_set_write)
|
||||
#define BIO_meth_set_write_ex BORINGSSL_ADD_PREFIX(BIO_meth_set_write_ex)
|
||||
#define BIO_method_type BORINGSSL_ADD_PREFIX(BIO_method_type)
|
||||
#define BIO_new BORINGSSL_ADD_PREFIX(BIO_new)
|
||||
#define BIO_new_bio_pair BORINGSSL_ADD_PREFIX(BIO_new_bio_pair)
|
||||
@@ -3451,6 +3454,7 @@
|
||||
#define BIO_wpending BORINGSSL_ADD_PREFIX(BIO_wpending)
|
||||
#define BIO_write BORINGSSL_ADD_PREFIX(BIO_write)
|
||||
#define BIO_write_all BORINGSSL_ADD_PREFIX(BIO_write_all)
|
||||
#define BIO_write_ex BORINGSSL_ADD_PREFIX(BIO_write_ex)
|
||||
#define BIO_write_filename BORINGSSL_ADD_PREFIX(BIO_write_filename)
|
||||
#define BLAKE2B256 BORINGSSL_ADD_PREFIX(BLAKE2B256)
|
||||
#define BLAKE2B256_Final BORINGSSL_ADD_PREFIX(BLAKE2B256_Final)
|
||||
|
||||
+5
-2
@@ -184,8 +184,11 @@ static long ssl_callback_ctrl(BIO *bio, int cmd, BIO_info_cb *fp) {
|
||||
}
|
||||
|
||||
static const BIO_METHOD ssl_method = {
|
||||
BIO_TYPE_SSL, "SSL", ssl_write, ssl_read, /*bgets=*/nullptr,
|
||||
ssl_ctrl, ssl_new, ssl_free, ssl_callback_ctrl,
|
||||
BIO_TYPE_SSL, "SSL",
|
||||
ssl_write, /*bwrite_ex=*/nullptr,
|
||||
ssl_read, /*bgets=*/nullptr,
|
||||
ssl_ctrl, ssl_new,
|
||||
ssl_free, ssl_callback_ctrl,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_f_ssl() { return &ssl_method; }
|
||||
|
||||
Reference in New Issue
Block a user