chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,982 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/thread.h>
#include "../internal.h"
// |callback_fn_wrap_ex| adapts the legacy callback interface |BIO_callback_fn| to the
// extended callback interface |BIO_callback_fn_ex|. This function should only be
// called when |callback_ex| is not available and the legacy callback is set.
//
// The extended interface parameters |len| and |processed| are mapped to the legacy
// interface parameters |argi| and |bio_ret| respectively.
//
// Returns -1 on NULL |BIO| or callback, otherwise returns the result of the legacy
// callback.
static long callback_fn_wrap_ex(BIO *bio, int oper, const char *argp,
size_t len, int argi, long argl, int bio_ret,
size_t *processed) {
assert(bio != NULL);
assert(bio->callback != NULL);
assert(bio->callback_ex == NULL);
/* Strip off any BIO_CB_RETURN flag */
int bareoper = oper & ~BIO_CB_RETURN;
if (bareoper == BIO_CB_READ || bareoper == BIO_CB_WRITE
|| bareoper == BIO_CB_GETS) {
/* In this case |len| is set, and should be used instead of |argi| */
if (len > INT_MAX) {
return -1;
}
argi = (int)len;
}
if (bio_ret > 0 && (oper & BIO_CB_RETURN) && bareoper != BIO_CB_CTRL) {
if (*processed > INT_MAX) {
return -1;
}
bio_ret = *processed;
}
long ret = bio->callback(bio, oper, argp, argi, argl, bio_ret);
if (ret > 0 && (oper & BIO_CB_RETURN) && bareoper != BIO_CB_CTRL) {
*processed = (size_t)ret;
ret = 1;
}
return ret;
}
// |get_callback| returns the appropriate callback function for a given |BIO|, preferring
// the extended interface |callback_ex| over the legacy interface.
//
// When only the legacy callback is available, it is wrapped in the extended format
// via |callback_fn_wrap_ex| to provide a consistent interface. The extended callback
// provides additional parameters for length and bytes processed tracking.
//
// Returns the |callback_ex| function if available, a wrapped legacy callback if only
// |callback| is set, or NULL if no callbacks are set.
static BIO_callback_fn_ex get_callback(BIO *bio) {
assert(bio != NULL);
if (bio->callback_ex != NULL) {
return bio->callback_ex;
}
if (bio->callback != NULL) {
// Wrap old-style callback in extended format
return callback_fn_wrap_ex;
}
return NULL;
}
// Helper function to handle return values from |BIO_read|, |BIO_write|,
// |BIO_gets|, and |BIO_puts| operations.
static int handle_callback_return(BIO *bio, int oper, const void *buf,
int len, int ret) {
size_t processed = 0;
if (ret > 0) {
if (oper == BIO_CB_READ || oper == BIO_CB_GETS) {
bio->num_read += ret;
} else if (oper == BIO_CB_WRITE || oper == BIO_CB_PUTS) {
bio->num_write += ret;
}
// |callback_ex| receives the number of bytes processed via the |processed| parameter,
// while the legacy callback receives this information through both |argi| and |ret|.
// When using the legacy callback, the |processed| value will be mapped back to |ret|.
processed = ret;
ret = 1;
}
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, oper | BIO_CB_RETURN, buf, len, 0, 0L, ret, &processed);
if (callback_ret > INT_MAX || callback_ret < INT_MIN) {
return -1;
}
ret = (int)callback_ret;
}
if (ret > 0) {
if (processed > INT_MAX) {
ret = -1; // Value too large to represent as int
} else {
ret = (int)processed;
}
}
return ret;
}
static CRYPTO_EX_DATA_CLASS g_ex_data_class =
CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA;
BIO *BIO_new(const BIO_METHOD *method) {
BIO *ret = OPENSSL_zalloc(sizeof(BIO));
if (ret == NULL) {
return NULL;
}
ret->method = method;
ret->shutdown = 1;
ret->references = 1;
ret->callback_ex = NULL;
ret->callback = NULL;
CRYPTO_new_ex_data(&ret->ex_data);
if (method->create != NULL && !method->create(ret)) {
OPENSSL_free(ret);
return NULL;
}
return ret;
}
int BIO_free(BIO *bio) {
BIO *next_bio;
for (; bio != NULL; bio = next_bio) {
if (!CRYPTO_refcount_dec_and_test_zero(&bio->references)) {
return 0;
}
next_bio = BIO_pop(bio);
if (bio->method != NULL && bio->method->destroy != NULL) {
bio->method->destroy(bio);
}
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long ret = cb(bio, BIO_CB_FREE, NULL, 0, 0, 0L, 1L, NULL);
if (ret <= 0) {
if (ret >= INT_MIN) {
return (int)ret;
}
return INT_MIN;
}
}
CRYPTO_free_ex_data(&g_ex_data_class, bio, &bio->ex_data);
OPENSSL_free(bio);
}
return 1;
}
int BIO_up_ref(BIO *bio) {
CRYPTO_refcount_inc(&bio->references);
return 1;
}
void BIO_vfree(BIO *bio) {
BIO_free(bio);
}
void BIO_free_all(BIO *bio) {
BIO_free(bio);
}
int BIO_read(BIO *bio, void *buf, int len) {
if (bio == NULL || bio->method == NULL || bio->method->bread == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
if (len <= 0) {
return 0;
}
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_READ, buf, len, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
}
return INT_MIN;
}
}
if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
int ret = bio->method->bread(bio, buf, len);
return handle_callback_return(bio, BIO_CB_READ, buf, len, ret);
}
int BIO_read_ex(BIO *bio, void *data, size_t data_len, size_t *read_bytes) {
if (bio == NULL || read_bytes == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_NULL_PARAMETER);
return 0;
}
int read_len = (int)data_len;
if (data_len > INT_MAX) {
read_len = INT_MAX;
}
int ret = BIO_read(bio, data, read_len);
if (ret > 0) {
*read_bytes = ret;
return 1;
} else {
*read_bytes = 0;
return 0;
}
}
int BIO_gets(BIO *bio, char *buf, int len) {
if (bio == NULL || bio->method == NULL || bio->method->bgets == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
if (len <= 0) {
return 0;
}
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_GETS, buf, len, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
}
return INT_MIN;
}
}
if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
int ret = bio->method->bgets(bio, buf, len);
return handle_callback_return(bio, BIO_CB_GETS, buf, len, ret);
}
int BIO_write(BIO *bio, const void *in, int inl) {
if (bio == NULL || bio->method == NULL || bio->method->bwrite == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
if (inl <= 0) {
return 0;
}
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_WRITE, in, inl, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
}
return INT_MIN;
}
}
if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
int ret = bio->method->bwrite(bio, in, inl);
return handle_callback_return(bio, BIO_CB_WRITE, in, inl, ret);
}
int BIO_write_ex(BIO *bio, const void *data, size_t data_len, size_t *written_bytes) {
if (bio == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_NULL_PARAMETER);
return 0;
}
int write_len = (int)data_len;
if (data_len > INT_MAX) {
write_len = INT_MAX;
}
int ret = BIO_write(bio, data, write_len);
if (ret > 0) {
if (written_bytes != NULL) {
*written_bytes = ret;
}
return 1;
} else {
if (written_bytes != NULL) {
*written_bytes = 0;
}
return 0;
}
}
int BIO_write_all(BIO *bio, const void *data, size_t len) {
const uint8_t *data_u8 = data;
while (len > 0) {
const int write_len = ((len > INT_MAX) ? INT_MAX : (int)len);
int ret = BIO_write(bio, data_u8, write_len);
assert(ret <= write_len);
if (ret <= 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BUFFER_TOO_SMALL);
return 0;
}
data_u8 += ret;
len -= ret;
}
return 1;
}
int BIO_puts(BIO *bio, const char *in) {
// Check for bwrites here since we use that if bputs is NULL
if (bio == NULL || bio->method == NULL || (bio->method->bwrite == NULL &&
bio->method->bputs == NULL)) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_PUTS, in, 0, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
}
return INT_MIN;
}
}
if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
int ret = 0;
if (bio->method->bputs != NULL) {
ret = bio->method->bputs(bio, in);
} else {
const size_t len = strlen(in);
if (len > INT_MAX) {
// |BIO_write| and the return value both assume the string fits in |int|.
OPENSSL_PUT_ERROR(BIO, ERR_R_OVERFLOW);
return -1;
}
ret = bio->method->bwrite(bio, in, len);
}
return handle_callback_return(bio, BIO_CB_PUTS, in, 0, ret);
}
int BIO_flush(BIO *bio) {
return (int)BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, NULL);
}
long BIO_ctrl(BIO *bio, int cmd, long larg, void *parg) {
if (bio == NULL) {
return 0;
}
if (bio->method == NULL || bio->method->ctrl == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
long ret = 0;
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
ret = cb(bio, BIO_CB_CTRL, parg, 0, cmd, larg, 1L, NULL);
if (ret <= 0) {
return ret;
}
}
ret = bio->method->ctrl(bio, cmd, larg, parg);
cb = get_callback(bio);
if (cb != NULL) {
ret = cb(bio, BIO_CB_CTRL | BIO_CB_RETURN, parg, 0, cmd, larg,
ret, NULL);
}
return ret;
}
char *BIO_ptr_ctrl(BIO *b, int cmd, long larg) {
char *p = NULL;
if (BIO_ctrl(b, cmd, larg, (void *)&p) <= 0) {
return NULL;
}
return p;
}
long BIO_int_ctrl(BIO *b, int cmd, long larg, int iarg) {
int i = iarg;
return BIO_ctrl(b, cmd, larg, (void *)&i);
}
int BIO_reset(BIO *bio) {
return (int)BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL);
}
int BIO_eof(BIO *bio) {
return (int)BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL);
}
void BIO_set_flags(BIO *bio, int flags) {
bio->flags |= flags;
}
int BIO_test_flags(const BIO *bio, int flags) {
return bio->flags & flags;
}
int BIO_should_read(const BIO *bio) {
return BIO_test_flags(bio, BIO_FLAGS_READ);
}
int BIO_should_write(const BIO *bio) {
return BIO_test_flags(bio, BIO_FLAGS_WRITE);
}
int BIO_should_retry(const BIO *bio) {
return BIO_test_flags(bio, BIO_FLAGS_SHOULD_RETRY);
}
int BIO_should_io_special(const BIO *bio) {
return BIO_test_flags(bio, BIO_FLAGS_IO_SPECIAL);
}
int BIO_get_retry_reason(const BIO *bio) { return bio->retry_reason; }
void BIO_set_retry_reason(BIO *bio, int reason) { bio->retry_reason = reason; }
void BIO_clear_flags(BIO *bio, int flags) {
bio->flags &= ~flags;
}
void BIO_set_retry_read(BIO *bio) {
bio->flags |= BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY;
}
void BIO_set_retry_write(BIO *bio) {
bio->flags |= BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY;
}
static const int kRetryFlags = BIO_FLAGS_RWS | BIO_FLAGS_SHOULD_RETRY;
int BIO_get_retry_flags(BIO *bio) {
return bio->flags & kRetryFlags;
}
void BIO_clear_retry_flags(BIO *bio) {
bio->flags &= ~kRetryFlags;
bio->retry_reason = 0;
}
int BIO_method_type(const BIO *bio) { return bio->method->type; }
const char *BIO_method_name(const BIO *bio) { return bio->method->name; }
void BIO_copy_next_retry(BIO *bio) {
BIO_clear_retry_flags(bio);
BIO_set_flags(bio, BIO_get_retry_flags(bio->next_bio));
bio->retry_reason = bio->next_bio->retry_reason;
}
long BIO_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
if (bio == NULL) {
return 0;
}
if (bio->method == NULL || bio->method->callback_ctrl == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return 0;
}
return bio->method->callback_ctrl(bio, cmd, fp);
}
size_t BIO_pending(const BIO *bio) {
const long r = BIO_ctrl((BIO *) bio, BIO_CTRL_PENDING, 0, NULL);
assert(r >= 0);
if (r < 0) {
return 0;
}
return r;
}
size_t BIO_ctrl_pending(const BIO *bio) {
return BIO_pending(bio);
}
size_t BIO_wpending(const BIO *bio) {
const long r = BIO_ctrl((BIO *) bio, BIO_CTRL_WPENDING, 0, NULL);
assert(r >= 0);
if (r < 0) {
return 0;
}
return r;
}
int BIO_set_close(BIO *bio, int close_flag) {
return (int)BIO_ctrl(bio, BIO_CTRL_SET_CLOSE, close_flag, NULL);
}
int BIO_get_close(BIO *bio) {
return (int)BIO_ctrl(bio, BIO_CTRL_GET_CLOSE, 0, NULL);
}
OPENSSL_EXPORT uint64_t BIO_number_read(const BIO *bio) {
return bio->num_read;
}
OPENSSL_EXPORT uint64_t BIO_number_written(const BIO *bio) {
return bio->num_write;
}
BIO *BIO_push(BIO *bio, BIO *appended_bio) {
BIO *last_bio;
if (bio == NULL) {
return bio;
}
last_bio = bio;
while (last_bio->next_bio != NULL) {
last_bio = last_bio->next_bio;
}
last_bio->next_bio = appended_bio;
return bio;
}
BIO *BIO_pop(BIO *bio) {
BIO *ret;
if (bio == NULL) {
return NULL;
}
ret = bio->next_bio;
bio->next_bio = NULL;
return ret;
}
BIO *BIO_next(BIO *bio) {
if (!bio) {
return NULL;
}
return bio->next_bio;
}
BIO *BIO_find_type(BIO *bio, int type) {
int method_type, mask;
if (!bio) {
return NULL;
}
mask = type & 0xff;
do {
if (bio->method != NULL) {
method_type = bio->method->type;
if (!mask) {
if (method_type & type) {
return bio;
}
} else if (method_type == type) {
return bio;
}
}
bio = bio->next_bio;
} while (bio != NULL);
return NULL;
}
int BIO_indent(BIO *bio, unsigned indent, unsigned max_indent) {
if (indent > max_indent) {
indent = max_indent;
}
while (indent--) {
if (BIO_puts(bio, " ") != 1) {
return 0;
}
}
return 1;
}
static int print_bio(const char *str, size_t len, void *bio) {
return BIO_write_all((BIO *)bio, str, len);
}
void ERR_print_errors(BIO *bio) {
ERR_print_errors_cb(print_bio, bio);
}
// bio_read_all reads everything from |bio| and prepends |prefix| to it. On
// success, |*out| is set to an allocated buffer (which should be freed with
// |OPENSSL_free|), |*out_len| is set to its length and one is returned. The
// buffer will contain |prefix| followed by the contents of |bio|. On failure,
// zero is returned.
//
// The function will fail if the size of the output would equal or exceed
// |max_len|.
static int bio_read_all(BIO *bio, uint8_t **out, size_t *out_len,
const uint8_t *prefix, size_t prefix_len,
size_t max_len) {
static const size_t kChunkSize = 4096;
size_t len = prefix_len + kChunkSize;
if (len > max_len) {
len = max_len;
}
if (len < prefix_len) {
return 0;
}
*out = OPENSSL_malloc(len);
if (*out == NULL) {
return 0;
}
OPENSSL_memcpy(*out, prefix, prefix_len);
size_t done = prefix_len;
for (;;) {
if (done == len) {
OPENSSL_free(*out);
return 0;
}
size_t todo = len - done;
if (todo > INT_MAX) {
todo = INT_MAX;
}
const int n = BIO_read(bio, *out + done, (int)todo);
if (n == 0) {
*out_len = done;
return 1;
} else if (n == -1) {
OPENSSL_free(*out);
return 0;
}
done += n;
if (len < max_len && len - done < kChunkSize / 2) {
len += kChunkSize;
if (len < kChunkSize || len > max_len) {
len = max_len;
}
uint8_t *new_buf = OPENSSL_realloc(*out, len);
if (new_buf == NULL) {
OPENSSL_free(*out);
return 0;
}
*out = new_buf;
}
}
}
// bio_read_full reads |len| bytes |bio| and writes them into |out|. It
// tolerates partial reads from |bio| and returns one on success or zero if a
// read fails before |len| bytes are read. On failure, it additionally sets
// |*out_eof_on_first_read| to whether the error was due to |bio| returning zero
// on the first read. |out_eof_on_first_read| may be NULL to discard the value.
static int bio_read_full(BIO *bio, uint8_t *out, int *out_eof_on_first_read,
size_t len) {
int first_read = 1;
while (len > 0) {
int todo = len <= INT_MAX ? (int)len : INT_MAX;
int ret = BIO_read(bio, out, todo);
if (ret <= 0) {
if (out_eof_on_first_read != NULL) {
*out_eof_on_first_read = first_read && ret == 0;
}
return 0;
}
out += ret;
len -= (size_t)ret;
first_read = 0;
}
return 1;
}
// For compatibility with existing |d2i_*_bio| callers, |BIO_read_asn1| uses
// |ERR_LIB_ASN1| errors.
OPENSSL_DECLARE_ERROR_REASON(ASN1, ASN1_R_DECODE_ERROR)
OPENSSL_DECLARE_ERROR_REASON(ASN1, ASN1_R_HEADER_TOO_LONG)
OPENSSL_DECLARE_ERROR_REASON(ASN1, ASN1_R_NOT_ENOUGH_DATA)
OPENSSL_DECLARE_ERROR_REASON(ASN1, ASN1_R_TOO_LONG)
int BIO_read_asn1(BIO *bio, uint8_t **out, size_t *out_len, size_t max_len) {
uint8_t header[6];
static const size_t kInitialHeaderLen = 2;
int eof_on_first_read;
if (!bio_read_full(bio, header, &eof_on_first_read, kInitialHeaderLen)) {
if (eof_on_first_read) {
// Historically, OpenSSL returned |ASN1_R_HEADER_TOO_LONG| when
// |d2i_*_bio| could not read anything. CPython conditions on this to
// determine if |bio| was empty.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_HEADER_TOO_LONG);
} else {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
}
return 0;
}
const uint8_t tag = header[0];
const uint8_t length_byte = header[1];
if ((tag & 0x1f) == 0x1f) {
// Long form tags are not supported.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return 0;
}
size_t len, header_len;
if ((length_byte & 0x80) == 0) {
// Short form length.
len = length_byte;
header_len = kInitialHeaderLen;
} else {
const size_t num_bytes = length_byte & 0x7f;
if ((tag & 0x20 /* constructed */) != 0 && num_bytes == 0) {
// indefinite length.
if (!bio_read_all(bio, out, out_len, header, kInitialHeaderLen,
max_len)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
return 0;
}
return 1;
}
if (num_bytes == 0 || num_bytes > 4) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return 0;
}
if (!bio_read_full(bio, header + kInitialHeaderLen, NULL, num_bytes)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
return 0;
}
header_len = kInitialHeaderLen + num_bytes;
uint32_t len32 = 0;
for (unsigned i = 0; i < num_bytes; i++) {
len32 <<= 8;
len32 |= header[kInitialHeaderLen + i];
}
if (len32 < 128) {
// Length should have used short-form encoding.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return 0;
}
if ((len32 >> ((num_bytes-1)*8)) == 0) {
// Length should have been at least one byte shorter.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return 0;
}
len = len32;
}
if (len + header_len < len ||
len + header_len > max_len ||
len > INT_MAX) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
return 0;
}
len += header_len;
*out_len = len;
*out = OPENSSL_malloc(len);
if (*out == NULL) {
return 0;
}
OPENSSL_memcpy(*out, header, header_len);
if (!bio_read_full(bio, (*out) + header_len, NULL, len - header_len)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
OPENSSL_free(*out);
return 0;
}
return 1;
}
void BIO_set_retry_special(BIO *bio) {
bio->flags |= BIO_FLAGS_READ | BIO_FLAGS_IO_SPECIAL;
}
int BIO_set_write_buffer_size(BIO *bio, int buffer_size) { return 0; }
static struct CRYPTO_STATIC_MUTEX g_index_lock = CRYPTO_STATIC_MUTEX_INIT;
static int g_index = BIO_TYPE_START;
int BIO_get_new_index(void) {
CRYPTO_STATIC_MUTEX_lock_write(&g_index_lock);
// If |g_index| exceeds 255, it will collide with the flags bits.
int ret = g_index > 255 ? -1 : g_index++;
CRYPTO_STATIC_MUTEX_unlock_write(&g_index_lock);
return ret;
}
BIO_METHOD *BIO_meth_new(int type, const char *name) {
BIO_METHOD *method = OPENSSL_zalloc(sizeof(BIO_METHOD));
if (method == NULL) {
return NULL;
}
method->type = type;
method->name = name;
return method;
}
void BIO_meth_free(BIO_METHOD *method) {
OPENSSL_free(method);
}
int BIO_meth_set_create(BIO_METHOD *method,
int (*create)(BIO *)) {
method->create = create;
return 1;
}
int (*BIO_meth_get_create(const BIO_METHOD *method)) (BIO *) {
return method->create;
}
int BIO_meth_set_destroy(BIO_METHOD *method,
int (*destroy)(BIO *)) {
method->destroy = destroy;
return 1;
}
int (*BIO_meth_get_destroy(const BIO_METHOD *method)) (BIO *) {
return method->destroy;
}
int BIO_meth_set_write(BIO_METHOD *method,
int (*write)(BIO *, const char *, int)) {
method->bwrite = write;
return 1;
}
int BIO_meth_set_read(BIO_METHOD *method,
int (*read)(BIO *, char *, int)) {
method->bread = read;
return 1;
}
int BIO_meth_set_gets(BIO_METHOD *method,
int (*gets)(BIO *, char *, int)) {
method->bgets = gets;
return 1;
}
int (*BIO_meth_get_gets(const BIO_METHOD *method)) (BIO *, char *, int) {
return method->bgets;
}
int BIO_meth_set_ctrl(BIO_METHOD *method,
long (*ctrl)(BIO *, int, long, void *)) {
method->ctrl = ctrl;
return 1;
}
long (*BIO_meth_get_ctrl(const BIO_METHOD *method)) (BIO *, int, long, void *) {
return method->ctrl;
}
int BIO_meth_set_callback_ctrl(BIO_METHOD *method,
long (*callback_ctrl)(BIO *, int, bio_info_cb)) {
method->callback_ctrl = callback_ctrl;
return 1;
}
long (*BIO_meth_get_callback_ctrl(const BIO_METHOD *method)) (BIO *, int, bio_info_cb) {
return method->callback_ctrl;
}
void BIO_set_data(BIO *bio, void *ptr) { bio->ptr = ptr; }
void *BIO_get_data(BIO *bio) { return bio->ptr; }
void BIO_set_init(BIO *bio, int init) { bio->init = init; }
int BIO_get_init(BIO *bio) { return bio->init; }
void BIO_set_shutdown(BIO *bio, int shutdown) { bio->shutdown = shutdown; }
int BIO_get_shutdown(BIO *bio) { return bio->shutdown; }
int BIO_meth_set_puts(BIO_METHOD *method, int (*puts)(BIO *, const char *)) {
method->bputs = puts;
return 1;
}
int (*BIO_meth_get_puts(const BIO_METHOD *method)) (BIO *, const char *) {
return method->bputs;
}
void BIO_set_callback_ex(BIO *bio, BIO_callback_fn_ex callback) {
bio->callback_ex = callback;
}
void BIO_set_callback(BIO *bio, BIO_callback_fn callback) {
bio->callback = callback;
}
void BIO_set_callback_arg(BIO *bio, char *arg) {
bio->cb_arg = arg;
}
char *BIO_get_callback_arg(const BIO *bio) {
return bio->cb_arg;
}
int BIO_get_ex_new_index(long argl, void *argp,
CRYPTO_EX_unused *unused,
CRYPTO_EX_dup *dup_unused,
CRYPTO_EX_free *free_func) {
int index;
if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp,
free_func)) {
return -1;
}
return index;
}
int BIO_set_ex_data(BIO *bio, int idx, void *data) {
return CRYPTO_set_ex_data(&bio->ex_data, idx, data);
}
void *BIO_get_ex_data(const BIO *bio, int idx) {
return CRYPTO_get_ex_data(&bio->ex_data, idx);
}

View File

@@ -0,0 +1,184 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/bio.h>
#if !defined(OPENSSL_NO_SOCK)
#include <openssl/mem.h>
#include "../internal.h"
#include "./internal.h"
BIO_ADDR *BIO_ADDR_new(void) {
BIO_ADDR *ret = OPENSSL_zalloc(sizeof(BIO_ADDR));
if (ret == NULL) {
return NULL;
}
ret->sa.sa_family = AF_UNSPEC;
return ret;
}
void BIO_ADDR_free(BIO_ADDR *ap) { OPENSSL_free(ap); }
int BIO_ADDR_copy(BIO_ADDR *dst, const BIO_ADDR *src) {
GUARD_PTR(dst);
GUARD_PTR(src);
if (src->sa.sa_family == AF_UNSPEC) {
BIO_ADDR_clear(dst);
return 1;
}
if (src->sa.sa_family == AF_INET) {
OPENSSL_memcpy(&dst->s_in, &src->sa, sizeof(dst->s_in));
return 1;
}
#ifdef AF_INET6
if (src->sa.sa_family == AF_INET6) {
OPENSSL_memcpy(&dst->s_in6, &src->sa, sizeof(dst->s_in6));
return 1;
}
#endif
#ifdef AWS_LC_HAS_AF_UNIX
if (src->sa.sa_family == AF_UNIX) {
OPENSSL_memcpy(&dst->s_un, &src->sa, sizeof(dst->s_un));
return 1;
}
#endif
return 0;
}
BIO_ADDR *BIO_ADDR_dup(const BIO_ADDR *ap) {
GUARD_PTR(ap);
BIO_ADDR *ret = BIO_ADDR_new();
if (ret == NULL) {
return NULL;
}
if (1 != BIO_ADDR_copy(ret, ap)) {
BIO_ADDR_free(ret);
ret = NULL;
}
return ret;
}
void BIO_ADDR_clear(BIO_ADDR *ap) {
if (ap == NULL) {
return;
}
OPENSSL_cleanse(ap, sizeof(BIO_ADDR));
ap->sa.sa_family = AF_UNSPEC;
}
int BIO_ADDR_family(const BIO_ADDR *ap) {
GUARD_PTR(ap);
return ap->sa.sa_family;
}
int BIO_ADDR_rawmake(BIO_ADDR *ap, int family, const void *where,
size_t wherelen, unsigned short port) {
GUARD_PTR(ap);
if (family == AF_INET) {
if (wherelen != sizeof(struct in_addr)) {
return 0;
}
OPENSSL_cleanse(&ap->s_in, sizeof(ap->s_in));
ap->s_in.sin_family = family;
ap->s_in.sin_port = port;
ap->s_in.sin_addr = *(struct in_addr *)where;
return 1;
}
#ifdef AF_INET6
if (family == AF_INET6) {
if (wherelen != sizeof(struct in6_addr)) {
return 0;
}
OPENSSL_cleanse(&ap->s_in6, sizeof(ap->s_in6));
ap->s_in6.sin6_family = family;
ap->s_in6.sin6_port = port;
ap->s_in6.sin6_addr = *(struct in6_addr *)where;
return 1;
}
#endif
#ifdef AWS_LC_HAS_AF_UNIX
if (family == AF_UNIX) {
GUARD_PTR(where);
// wherelen is expected to be the length of the path string
// not including the terminating NUL.
if (wherelen + 1 > sizeof(ap->s_un.sun_path)) {
return 0;
}
OPENSSL_cleanse(&ap->s_un, sizeof(ap->s_un));
ap->s_un.sun_family = family;
OPENSSL_strlcpy(ap->s_un.sun_path, where, wherelen);
return 1;
}
#endif
return 0;
}
int BIO_ADDR_rawaddress(const BIO_ADDR *ap, void *p, size_t *l) {
size_t len = 0;
const void *addrptr = NULL;
GUARD_PTR(ap);
if (ap->sa.sa_family == AF_INET) {
len = sizeof(ap->s_in.sin_addr);
addrptr = &ap->s_in.sin_addr;
}
#ifdef AF_INET6
else if (ap->sa.sa_family == AF_INET6) {
len = sizeof(ap->s_in6.sin6_addr);
addrptr = &ap->s_in6.sin6_addr;
}
#endif
#ifdef AWS_LC_HAS_AF_UNIX
else if (ap->sa.sa_family == AF_UNIX) {
// len does not include the null
len = OPENSSL_strnlen(ap->s_un.sun_path, sizeof(ap->s_un.sun_path));
addrptr = &ap->s_un.sun_path;
}
#endif
if (addrptr == NULL) {
return 0;
}
if (l != NULL) {
*l = len;
}
if (p != NULL) {
OPENSSL_memcpy(p, addrptr, len);
if (ap->sa.sa_family == AF_UNIX) {
// OpenSSL does not write the terminating NUL
((char *)p)[len] = '\0';
}
}
return 1;
}
unsigned short BIO_ADDR_rawport(const BIO_ADDR *ap)
{
GUARD_PTR(ap);
if (ap->sa.sa_family == AF_INET) {
return ap->s_in.sin_port;
}
#ifdef AF_INET6
if (ap->sa.sa_family == AF_INET6) {
return ap->s_in6.sin6_port;
}
#endif
return 0;
}
#endif // !defined(OPENSSL_NO_SOCK)

View File

@@ -0,0 +1,270 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <gtest/gtest.h>
#include "stdlib.h"
#include <openssl/bio.h>
#include <openssl/bytestring.h>
#include <openssl/x509.h>
#include "../test/test_util.h"
#include "../internal.h"
#define MESSAGE_LEN 8 * 1024
struct MessageDigestParams {
const char name[40];
const EVP_MD *(*md)(void);
};
static const struct MessageDigestParams MessageDigests[] = {
{"MD5", EVP_md5},
{"SHA1", EVP_sha1},
{"SHA224", EVP_sha224},
{"SHA256", EVP_sha256},
{"SHA284", EVP_sha384},
{"SHA512", EVP_sha512},
{"SHA512_224", EVP_sha512_224},
{"SHA512_256", EVP_sha512_256},
{"SHA3_224", EVP_sha3_224},
{"SHA3_256", EVP_sha3_256},
{"SHA3_384", EVP_sha3_384},
{"SHA3_512", EVP_sha3_512},
};
class BIOMessageDigestTest
: public testing::TestWithParam<MessageDigestParams> {};
INSTANTIATE_TEST_SUITE_P(
PKCS7Test, BIOMessageDigestTest, testing::ValuesIn(MessageDigests),
[](const testing::TestParamInfo<MessageDigestParams> &params)
-> std::string { return params.param.name; });
TEST_P(BIOMessageDigestTest, Basic) {
uint8_t message[MESSAGE_LEN];
uint8_t buf[2 * MESSAGE_LEN];
std::vector<uint8_t> message_vec;
std::vector<uint8_t> buf_vec;
bssl::UniquePtr<BIO> bio;
bssl::UniquePtr<BIO> bio_md;
bssl::UniquePtr<BIO> bio_mem;
bssl::UniquePtr<EVP_MD_CTX> ctx;
EVP_MD *get_md = nullptr;
OPENSSL_memset(message, 'A', sizeof(message));
OPENSSL_memset(buf, '\0', sizeof(buf));
const EVP_MD *md = GetParam().md();
ASSERT_TRUE(md);
// Simple initialization and error cases
bio_md.reset(BIO_new(BIO_f_md()));
ASSERT_TRUE(bio_md);
EXPECT_FALSE(BIO_get_md(bio_md.get(), &get_md));
EXPECT_FALSE(BIO_reset(bio_md.get()));
EXPECT_TRUE(BIO_set_md(bio_md.get(), md));
EVP_MD_CTX *ctx_tmp; // |bio_md| owns the context, we just take a ref here
EXPECT_TRUE(BIO_get_md_ctx(bio_md.get(), &ctx_tmp));
EXPECT_EQ(EVP_MD_type(md), EVP_MD_CTX_type(ctx_tmp));
EXPECT_EQ(md, EVP_MD_CTX_md(ctx_tmp)); // for static *EVP_MD_CTX, ptrs equal
// The following should fail due to the passing of NULL as the argument.
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_C_GET_MD, 0, nullptr));
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_C_SET_MD, 0, nullptr));
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_C_GET_MD_CTX, 0, nullptr));
// The following should fail due to no support for the underlying |BIO_ctrl|
// implementations.
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_C_SET_MD_CTX, 0, nullptr));
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_C_DO_STATE_MACHINE, 0, nullptr));
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_CTRL_DUP, 0, nullptr));
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_CTRL_GET_CALLBACK, 0, nullptr));
EXPECT_FALSE(BIO_ctrl(bio_md.get(), BIO_CTRL_SET_CALLBACK, 0, nullptr));
// More error cases.
EXPECT_FALSE(BIO_read(bio_md.get(), buf, 0));
EXPECT_FALSE(BIO_write(bio_md.get(), buf, 0));
EXPECT_EQ(0UL, BIO_number_read(bio_md.get()));
EXPECT_EQ(0UL, BIO_number_written(bio_md.get()));
EXPECT_FALSE(BIO_gets(bio_md.get(), (char *)buf, EVP_MD_size(md) - 1));
// Briefly test |BIO_get_md|.
EXPECT_TRUE(BIO_get_md(bio_md.get(), &get_md));
EXPECT_EQ(md, get_md);
// Pre-initialization IO should fail, but |BIO_get_md_ctx| should do init
bio_md.reset(BIO_new(BIO_f_md()));
ASSERT_TRUE(bio_md);
bio_mem.reset(BIO_new(BIO_s_mem()));
ASSERT_TRUE(bio_mem);
bio.reset(BIO_push(bio_md.get(), bio_mem.get()));
ASSERT_TRUE(bio);
EXPECT_GT(1, BIO_write(bio.get(), message, sizeof(message)));
EXPECT_GT(1, BIO_read(bio.get(), message, sizeof(message)));
EXPECT_TRUE(BIO_get_md_ctx(bio_md.get(), &ctx_tmp));
ASSERT_TRUE(EVP_DigestInit_ex(ctx_tmp, md, NULL));
EXPECT_TRUE(BIO_write(bio.get(), message, sizeof(message)));
EXPECT_TRUE(BIO_read(bio.get(), message, sizeof(message)));
bio_md.release(); // |bio| took ownership
bio_mem.release(); // |bio| took ownership
// Write-through digest BIO
bio_md.reset(BIO_new(BIO_f_md()));
ASSERT_TRUE(bio_md);
EXPECT_TRUE(BIO_set_md(bio_md.get(), md));
bio_mem.reset(BIO_new(BIO_s_mem()));
ASSERT_TRUE(bio_mem);
bio.reset(BIO_push(bio_md.get(), bio_mem.get()));
ASSERT_TRUE(bio);
EXPECT_TRUE(BIO_write(bio.get(), message, sizeof(message)));
int digest_len = BIO_gets(bio_md.get(), (char *)buf, sizeof(buf));
ASSERT_GE(digest_len, 0);
buf_vec.clear();
buf_vec.insert(buf_vec.begin(), buf, buf + digest_len);
OPENSSL_memset(buf, '\0', sizeof(buf));
message_vec.clear();
int rsize;
while ((rsize = BIO_read(bio_mem.get(), buf, sizeof(buf))) > 0) {
message_vec.insert(message_vec.end(), buf, buf + rsize);
}
ctx.reset(EVP_MD_CTX_new());
ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), md, NULL));
ASSERT_TRUE(
EVP_DigestUpdate(ctx.get(), message_vec.data(), message_vec.size()));
ASSERT_TRUE(EVP_DigestFinal_ex(ctx.get(), buf, reinterpret_cast<unsigned int*>(&digest_len)));
EXPECT_EQ(Bytes(buf_vec.data(), buf_vec.size()), Bytes(buf, digest_len));
bio_md.release(); // |bio| took ownership
bio_mem.release(); // |bio| took ownership
// Read-through digest BIO
bio_md.reset(BIO_new(BIO_f_md()));
ASSERT_TRUE(bio_md);
EXPECT_TRUE(BIO_set_md(bio_md.get(), md));
bio_mem.reset(BIO_new_mem_buf(message, sizeof(message)));
ASSERT_TRUE(bio_mem);
bio.reset(BIO_push(bio_md.get(), bio_mem.get()));
ASSERT_TRUE(bio);
message_vec.clear();
OPENSSL_memset(buf, '\0', sizeof(buf));
while ((rsize = BIO_read(bio.get(), buf, sizeof(buf))) > 0) {
message_vec.insert(message_vec.begin(), buf, buf + rsize);
}
EXPECT_EQ(Bytes(message_vec.data(), message_vec.size()),
Bytes(message, sizeof(message)));
digest_len = BIO_gets(bio_md.get(), (char *)buf, sizeof(buf));
ASSERT_GE(digest_len, 0);
buf_vec.clear();
buf_vec.insert(buf_vec.begin(), buf, buf + digest_len);
ctx.reset(EVP_MD_CTX_new());
ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), md, NULL));
ASSERT_TRUE(
EVP_DigestUpdate(ctx.get(), message_vec.data(), message_vec.size()));
ASSERT_TRUE(EVP_DigestFinal_ex(ctx.get(), buf, reinterpret_cast<unsigned int*>(&digest_len)));
EXPECT_EQ(Bytes(buf, digest_len), Bytes(buf_vec.data(), buf_vec.size()));
EXPECT_EQ(Bytes(buf_vec.data(), buf_vec.size()), Bytes(buf, digest_len));
// Resetting |bio_md| should reset digest state, elicit different digest
// output
EXPECT_TRUE(BIO_reset(bio.get()));
digest_len = BIO_gets(bio_md.get(), (char *)buf, sizeof(buf));
EXPECT_NE(Bytes(buf_vec.data(), buf_vec.size()), Bytes(buf, digest_len));
bio_md.release(); // |bio| took ownership
bio_mem.release(); // |bio| took ownership
}
TEST_P(BIOMessageDigestTest, Randomized) {
uint8_t message_buf[MESSAGE_LEN];
uint8_t digest_buf[EVP_MAX_MD_SIZE];
std::vector<uint8_t> message;
std::vector<uint8_t> expected_digest;
bssl::UniquePtr<BIO> bio;
bssl::UniquePtr<BIO> bio_md;
bssl::UniquePtr<BIO> bio_mem;
bssl::UniquePtr<EVP_MD_CTX> ctx;
const EVP_MD *md = GetParam().md();
ASSERT_TRUE(md);
const size_t block_size = EVP_MD_block_size(md);
srand(42);
std::vector<std::vector<size_t>> io_patterns = {
{},
{0},
{1},
{8, 8, 8, 8},
{block_size - 1, 1, block_size + 1, block_size, block_size - 1},
{4, 1, 5, 3, 2, 0, 1, MESSAGE_LEN, 133, 4555, 22, 4, 7964, 1234},
};
std::vector<size_t> v(1000);
std::generate(v.begin(), v.end(), [] { return rand() % MESSAGE_LEN; }); // NOLINT(clang-analyzer-security.insecureAPI.rand)
io_patterns.push_back(std::move(v));
for (auto io_pattern : io_patterns) {
message.clear();
expected_digest.clear();
ctx.reset(EVP_MD_CTX_new());
EVP_DigestInit_ex(ctx.get(), md, NULL);
// Construct overall message and its expected expected_digest
for (auto io_size : io_pattern) {
ASSERT_LE(io_size, sizeof(message_buf));
char c = rand() % 256; // NOLINT(clang-analyzer-security.insecureAPI.rand)
OPENSSL_memset(message_buf, c, io_size);
message.insert(message.end(), &message_buf[0], &message_buf[io_size]);
}
EVP_DigestUpdate(ctx.get(), message.data(), message.size());
int digest_size;
EVP_DigestFinal_ex(ctx.get(), digest_buf, reinterpret_cast<unsigned int*>(&digest_size));
ASSERT_EQ(EVP_MD_CTX_size(ctx.get()), (unsigned int)digest_size);
expected_digest.insert(expected_digest.begin(), &digest_buf[0],
&digest_buf[digest_size]);
OPENSSL_cleanse(digest_buf, sizeof(digest_buf));
// Write-through digest BIO, check against expectation
bio_md.reset(BIO_new(BIO_f_md()));
ASSERT_TRUE(bio_md);
EXPECT_TRUE(BIO_set_md(bio_md.get(), md));
bio_mem.reset(BIO_new(BIO_s_mem()));
ASSERT_TRUE(bio_mem);
bio.reset(BIO_push(bio_md.get(), bio_mem.get()));
ASSERT_TRUE(bio);
int pos = 0;
for (auto io_size : io_pattern) {
int wsize = BIO_write(bio.get(), (char *)(message.data() + pos), io_size);
EXPECT_EQ((int)io_size, wsize);
pos += io_size;
}
digest_size =
BIO_gets(bio_md.get(), (char *)digest_buf, sizeof(digest_buf));
ASSERT_GE(digest_size, 0);
ASSERT_EQ(EVP_MD_CTX_size(ctx.get()), (unsigned int)digest_size);
EXPECT_EQ(Bytes(expected_digest.data(), expected_digest.size()),
Bytes(digest_buf, digest_size));
OPENSSL_cleanse(digest_buf, sizeof(digest_buf));
bio_md.release(); // |bio| took ownership
bio_mem.release(); // |bio| took ownership
// Read-through digest BIO, check against expectation
bio_md.reset(BIO_new(BIO_f_md()));
ASSERT_TRUE(bio_md);
EXPECT_TRUE(BIO_set_md(bio_md.get(), md));
bio_mem.reset(BIO_new_mem_buf(message.data(), message.size()));
ASSERT_TRUE(bio_mem);
bio.reset(BIO_push(bio_md.get(), bio_mem.get()));
ASSERT_TRUE(bio);
for (auto io_size : io_pattern) {
int rsize = BIO_read(bio.get(), message_buf, io_size);
EXPECT_EQ((int)io_size, rsize);
}
EXPECT_TRUE(BIO_eof(bio.get()));
digest_size =
BIO_gets(bio_md.get(), (char *)digest_buf, sizeof(digest_buf));
ASSERT_GE(digest_size, 0);
ASSERT_EQ(EVP_MD_CTX_size(ctx.get()), (unsigned int)digest_size);
EXPECT_EQ(Bytes(expected_digest.data(), expected_digest.size()),
Bytes(digest_buf, digest_size));
OPENSSL_cleanse(digest_buf, sizeof(digest_buf));
bio_md.release(); // |bio| took ownership
bio_mem.release(); // |bio| took ownership
}
}

View File

@@ -0,0 +1,343 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#include <limits.h>
#include <string.h>
#include <openssl/buf.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include "../internal.h"
typedef struct bio_buf_mem_st {
struct buf_mem_st *buf; /* allocated buffer */
size_t read_off; /* read pointer offset from current buffer position */
} BIO_BUF_MEM;
BIO *BIO_new_mem_buf(const void *buf, ossl_ssize_t len) {
BIO *ret;
BUF_MEM *b;
BIO_BUF_MEM *bbm;
const size_t size = (len < 0) ? strlen((char *)buf) : (size_t)len;
if (!buf && len != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_NULL_PARAMETER);
return NULL;
}
ret = BIO_new(BIO_s_mem());
if (ret == NULL) {
return NULL;
}
bbm = (BIO_BUF_MEM *)ret->ptr;
b = bbm->buf;
// BIO_FLAGS_MEM_RDONLY ensures |b->data| is not written to.
b->data = (void *)buf;
b->length = size;
b->max = size;
ret->flags |= BIO_FLAGS_MEM_RDONLY;
// |num| is used to store the value that this BIO will return when it runs
// out of data. If it's negative then the retry flags will also be set. Since
// this is static data, retrying wont help
ret->num = 0;
return ret;
}
static int mem_new(BIO *bio) {
BIO_BUF_MEM *bbm = OPENSSL_zalloc(sizeof(*bbm));
if (bbm == NULL) {
return 0;
}
bbm->buf = BUF_MEM_new();
if (bbm->buf == NULL) {
OPENSSL_free(bbm);
return 0;
}
// |shutdown| is used to store the close flag: whether the BIO has ownership
// of the BUF_MEM.
bbm->read_off = 0;
bio->shutdown = 1;
bio->init = 1;
bio->num = -1;
bio->ptr = (char *)bbm;
return 1;
}
static int mem_buf_free(BIO *bio) {
if (bio == NULL) {
return 0;
}
if (!bio->shutdown || !bio->init || bio->ptr == NULL) {
return 1;
}
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)bio->ptr;
BUF_MEM *b = bbm->buf;
if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
b->data = NULL;
}
BUF_MEM_free(b);
return 1;
}
static int mem_free(BIO *bio) {
if (bio == NULL) {
return 0;
}
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)bio->ptr;
if (!mem_buf_free(bio)) {
return 0;
}
bio->ptr = NULL;
OPENSSL_free(bbm);
return 1;
}
static void mem_buf_sync(BIO *bio) {
if (bio->init != 0 && bio->ptr != NULL) {
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *) bio->ptr;
BUF_MEM *b = bbm->buf;
if (b->data != NULL) {
if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
b->data += bbm->read_off;
} else {
OPENSSL_memmove(b->data, &b->data[bbm->read_off], b->length);
}
bbm->read_off = 0;
}
}
}
static int mem_read(BIO *bio, char *out, int outl) {
BIO_clear_retry_flags(bio);
if (outl <= 0) {
return 0;
}
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *) bio->ptr;
BUF_MEM *b = bbm->buf;
int ret = outl;
if ((size_t)ret > b->length) {
ret = (int)b->length;
}
if (ret > 0) {
OPENSSL_memcpy(out, &b->data[bbm->read_off], ret);
b->length -= ret;
bbm->read_off += ret;
} else if (b->length == 0) {
ret = bio->num;
if (ret != 0) {
BIO_set_retry_read(bio);
}
}
return ret;
}
static int mem_write(BIO *bio, const char *in, int inl) {
BIO_clear_retry_flags(bio);
if (inl <= 0) {
return 0; // Successfully write zero bytes.
}
if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
OPENSSL_PUT_ERROR(BIO, BIO_R_WRITE_TO_READ_ONLY_BIO);
return -1;
}
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *) bio->ptr;
BUF_MEM *b = bbm->buf;
mem_buf_sync(bio);
if (!BUF_MEM_append(b, in, inl)) {
return -1;
}
return inl;
}
static int mem_gets(BIO *bio, char *buf, int size) {
BIO_clear_retry_flags(bio);
if (size <= 0) {
return 0;
}
// The buffer size includes space for the trailing NUL, so we can read at most
// one fewer byte.
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *) bio->ptr;
BUF_MEM *b = bbm->buf;
int ret = size - 1;
if ((size_t)ret > b->length) {
ret = (int)b->length;
}
// Stop at the first newline.
if (b->data != NULL) {
char *readp = &b->data[bbm->read_off];
const char *newline = OPENSSL_memchr(readp, '\n', ret);
if (newline != NULL) {
ret = (int)(newline - readp + 1);
}
}
ret = mem_read(bio, buf, ret);
if (ret >= 0) {
buf[ret] = '\0';
}
return ret;
}
static long mem_ctrl(BIO *bio, int cmd, long num, void *ptr) {
long ret = 1;
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *) bio->ptr;
BUF_MEM *b = bbm->buf;
switch (cmd) {
case BIO_CTRL_RESET:
if (b->data != NULL) {
// For read only case reset to the start again
if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
b->data -= b->max - b->length - bbm->read_off;
b->length = b->max;
} else {
OPENSSL_cleanse(b->data, b->max);
b->length = 0;
}
bbm->read_off = 0;
}
break;
case BIO_C_FILE_SEEK:
if (num < 0 || (size_t)num > b->max) {
ret = -1;
break;
}
if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
b->data -= b->max - b->length - bbm->read_off;
b->length = b->max - num;
} else {
if ((size_t)num > bbm->read_off + b->length) {
ret = -1;
break;
}
b->length = (b->length + bbm->read_off) - num;
}
bbm->read_off = num;
ret = num;
break;
case BIO_CTRL_EOF:
ret = (long)(b->length == 0);
break;
case BIO_C_SET_BUF_MEM_EOF_RETURN:
bio->num = (int)num;
break;
case BIO_CTRL_INFO:
ret = (long)b->length;
if (ptr != NULL) {
char **pptr = ptr;
*pptr = (b->data != NULL) ? &b->data[bbm->read_off] : NULL;
}
break;
case BIO_C_SET_BUF_MEM:
mem_free(bio);
bio->shutdown = (int)num;
bio->ptr = ptr;
break;
case BIO_C_GET_BUF_MEM_PTR:
if (ptr != NULL) {
mem_buf_sync(bio);
BUF_MEM **pptr = ptr;
*pptr = b;
}
break;
case BIO_CTRL_GET_CLOSE:
ret = (long)bio->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
bio->shutdown = (int)num;
break;
case BIO_CTRL_WPENDING:
ret = 0L;
break;
case BIO_CTRL_PENDING:
ret = (long)b->length;
break;
case BIO_CTRL_FLUSH:
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
static const BIO_METHOD mem_method = {
BIO_TYPE_MEM, "memory buffer",
mem_write, mem_read,
NULL /* puts */, mem_gets,
mem_ctrl, mem_new,
mem_free, NULL /* callback_ctrl */,
};
const BIO_METHOD *BIO_s_mem(void) { return &mem_method; }
int BIO_mem_contents(const BIO *bio, const uint8_t **out_contents,
size_t *out_len) {
if (!bio || bio->method != &mem_method) {
return 0;
}
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *) bio->ptr;
const BUF_MEM *b = bbm->buf;
mem_buf_sync((BIO *)bio);
if (out_contents != NULL) {
*out_contents = (uint8_t *)b->data;
}
if(out_len) {
*out_len = b->length;
}
return 1;
}
int BIO_get_mem_ptr(BIO *bio, BUF_MEM **out) {
return (int)BIO_ctrl(bio, BIO_C_GET_BUF_MEM_PTR, 0, out);
}
int BIO_set_mem_buf(BIO *bio, BUF_MEM *b, int take_ownership) {
return (int)BIO_ctrl(bio, BIO_C_SET_BUF_MEM, take_ownership, b);
}
int BIO_set_mem_eof_return(BIO *bio, int eof_value) {
return (int)BIO_ctrl(bio, BIO_C_SET_BUF_MEM_EOF_RETURN, eof_value, NULL);
}
const BIO_METHOD *BIO_s_secmem(void) {
return BIO_s_mem();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,485 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#if !defined(OPENSSL_NO_SOCK)
#include <assert.h>
#include <errno.h>
#include <string.h>
#if !defined(OPENSSL_WINDOWS)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#else
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <winsock2.h>
#include <ws2tcpip.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
#endif
#include <openssl/err.h>
#include <openssl/mem.h>
#include "internal.h"
#include "../internal.h"
enum {
BIO_CONN_S_BEFORE,
BIO_CONN_S_BLOCKED_CONNECT,
BIO_CONN_S_OK,
};
typedef struct bio_connect_st {
int state;
char *param_hostname;
char *param_port;
int nbio;
unsigned short port;
struct sockaddr_storage them;
socklen_t them_length;
// the file descriptor is kept in bio->num in order to match the socket
// BIO.
// info_callback is called when the connection is initially made
// callback(BIO,state,ret); The callback should return 'ret', state is for
// compatibility with the SSL info_callback.
bio_info_cb info_callback;
} BIO_CONNECT;
#if !defined(OPENSSL_WINDOWS)
static int closesocket(int sock) {
return close(sock);
}
#endif
// split_host_and_port sets |*out_host| and |*out_port| to the host and port
// parsed from |name|. It returns one on success or zero on error. Even when
// successful, |*out_port| may be NULL on return if no port was specified.
static int split_host_and_port(char **out_host, char **out_port,
const char *name) {
const char *host, *port = NULL;
size_t host_len = 0;
*out_host = NULL;
*out_port = NULL;
if (name[0] == '[') { // bracketed IPv6 address
const char *close = strchr(name, ']');
if (close == NULL) {
return 0;
}
host = name + 1;
host_len = close - host;
if (close[1] == ':') { // [IP]:port
port = close + 2;
} else if (close[1] != 0) {
return 0;
}
} else {
const char *colon = strchr(name, ':');
if (colon == NULL || strchr(colon + 1, ':') != NULL) { // IPv6 address
host = name;
host_len = strlen(name);
} else { // host:port
host = name;
host_len = colon - name;
port = colon + 1;
}
}
*out_host = OPENSSL_strndup(host, host_len);
if (*out_host == NULL) {
return 0;
}
if (port == NULL) {
*out_port = NULL;
return 1;
}
*out_port = OPENSSL_strdup(port);
if (*out_port == NULL) {
OPENSSL_free(*out_host);
*out_host = NULL;
return 0;
}
return 1;
}
static int conn_state(BIO *bio, BIO_CONNECT *c) {
int ret = -1, i;
bio_info_cb cb = NULL;
if (c->info_callback != NULL) {
cb = c->info_callback;
}
for (;;) {
switch (c->state) {
case BIO_CONN_S_BEFORE:
// If there's a hostname and a port, assume that both are
// exactly what they say. If there is only a hostname, try
// (just once) to split it into a hostname and port.
if (c->param_hostname == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_NO_HOSTNAME_SPECIFIED);
goto exit_loop;
}
if (c->param_port == NULL) {
char *host, *port;
if (!split_host_and_port(&host, &port, c->param_hostname) ||
port == NULL) {
OPENSSL_free(host);
OPENSSL_free(port);
OPENSSL_PUT_ERROR(BIO, BIO_R_NO_PORT_SPECIFIED);
ERR_add_error_data(2, "host=", c->param_hostname);
goto exit_loop;
}
OPENSSL_free(c->param_port);
c->param_port = port;
OPENSSL_free(c->param_hostname);
c->param_hostname = host;
}
if (!bio_ip_and_port_to_socket_and_addr(
&bio->num, &c->them, &c->them_length, c->param_hostname,
c->param_port)) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNABLE_TO_CREATE_SOCKET);
ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port);
goto exit_loop;
}
if (c->nbio) {
if (!bio_socket_nbio(bio->num, 1)) {
OPENSSL_PUT_ERROR(BIO, BIO_R_ERROR_SETTING_NBIO);
ERR_add_error_data(4, "host=", c->param_hostname, ":",
c->param_port);
goto exit_loop;
}
}
i = 1;
ret = setsockopt(bio->num, SOL_SOCKET, SO_KEEPALIVE, (char *)&i,
sizeof(i));
if (ret < 0) {
OPENSSL_PUT_SYSTEM_ERROR();
OPENSSL_PUT_ERROR(BIO, BIO_R_KEEPALIVE);
ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port);
goto exit_loop;
}
BIO_clear_retry_flags(bio);
ret = connect(bio->num, (struct sockaddr*) &c->them, c->them_length);
if (ret < 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY));
c->state = BIO_CONN_S_BLOCKED_CONNECT;
bio->retry_reason = BIO_RR_CONNECT;
} else {
OPENSSL_PUT_SYSTEM_ERROR();
OPENSSL_PUT_ERROR(BIO, BIO_R_CONNECT_ERROR);
ERR_add_error_data(4, "host=", c->param_hostname, ":",
c->param_port);
}
goto exit_loop;
} else {
c->state = BIO_CONN_S_OK;
}
break;
case BIO_CONN_S_BLOCKED_CONNECT:
i = bio_sock_error_get_and_clear(bio->num);
if (i) {
if (bio_socket_should_retry(ret)) {
BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY));
c->state = BIO_CONN_S_BLOCKED_CONNECT;
bio->retry_reason = BIO_RR_CONNECT;
ret = -1;
} else {
BIO_clear_retry_flags(bio);
OPENSSL_PUT_SYSTEM_ERROR();
OPENSSL_PUT_ERROR(BIO, BIO_R_NBIO_CONNECT_ERROR);
ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port);
ret = 0;
}
goto exit_loop;
} else {
c->state = BIO_CONN_S_OK;
}
break;
case BIO_CONN_S_OK:
ret = 1;
goto exit_loop;
default:
assert(0);
goto exit_loop;
}
if (cb != NULL) {
ret = cb((BIO *)bio, c->state, ret);
if (ret == 0) {
goto end;
}
}
}
exit_loop:
if (cb != NULL) {
ret = cb((BIO *)bio, c->state, ret);
}
end:
return ret;
}
static BIO_CONNECT *BIO_CONNECT_new(void) {
BIO_CONNECT *ret = OPENSSL_zalloc(sizeof(BIO_CONNECT));
if (ret == NULL) {
return NULL;
}
ret->state = BIO_CONN_S_BEFORE;
return ret;
}
static void BIO_CONNECT_free(BIO_CONNECT *c) {
if (c == NULL) {
return;
}
OPENSSL_free(c->param_hostname);
OPENSSL_free(c->param_port);
OPENSSL_free(c);
}
static int conn_new(BIO *bio) {
bio->init = 0;
bio->num = -1;
bio->flags = 0;
bio->ptr = BIO_CONNECT_new();
return bio->ptr != NULL;
}
static void conn_close_socket(BIO *bio) {
BIO_CONNECT *c = (BIO_CONNECT *) bio->ptr;
if (bio->num == -1) {
return;
}
// Only do a shutdown if things were established
if (c->state == BIO_CONN_S_OK) {
shutdown(bio->num, 2);
}
closesocket(bio->num);
bio->num = -1;
}
static int conn_free(BIO *bio) {
if (bio->shutdown) {
conn_close_socket(bio);
}
BIO_CONNECT_free((BIO_CONNECT*) bio->ptr);
return 1;
}
static int conn_read(BIO *bio, char *out, int out_len) {
int ret = 0;
BIO_CONNECT *data;
data = (BIO_CONNECT *)bio->ptr;
if (data->state != BIO_CONN_S_OK) {
ret = conn_state(bio, data);
if (ret <= 0) {
return ret;
}
}
bio_clear_socket_error(bio->num);
ret = (int)recv(bio->num, out, out_len, 0);
BIO_clear_retry_flags(bio);
if (ret <= 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_read(bio);
}
}
return ret;
}
static int conn_write(BIO *bio, const char *in, int in_len) {
int ret;
BIO_CONNECT *data;
data = (BIO_CONNECT *)bio->ptr;
if (data->state != BIO_CONN_S_OK) {
ret = conn_state(bio, data);
if (ret <= 0) {
return ret;
}
}
bio_clear_socket_error(bio->num);
ret = (int)send(bio->num, in, in_len, 0);
BIO_clear_retry_flags(bio);
if (ret <= 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_write(bio);
}
}
return ret;
}
static long conn_ctrl(BIO *bio, int cmd, long num, void *ptr) {
int *ip;
long ret = 1;
BIO_CONNECT *data;
data = (BIO_CONNECT *)bio->ptr;
switch (cmd) {
case BIO_CTRL_RESET:
ret = 0;
data->state = BIO_CONN_S_BEFORE;
conn_close_socket(bio);
bio->flags = 0;
break;
case BIO_C_DO_STATE_MACHINE:
// use this one to start the connection
if (data->state != BIO_CONN_S_OK) {
ret = (long)conn_state(bio, data);
} else {
ret = 1;
}
break;
case BIO_C_SET_CONNECT:
if (ptr != NULL) {
bio->init = 1;
if (num == 0) {
OPENSSL_free(data->param_hostname);
data->param_hostname = OPENSSL_strdup(ptr);
if (data->param_hostname == NULL) {
ret = 0;
}
} else if (num == 1) {
OPENSSL_free(data->param_port);
data->param_port = OPENSSL_strdup(ptr);
if (data->param_port == NULL) {
ret = 0;
}
} else {
ret = 0;
}
}
break;
case BIO_C_SET_NBIO:
data->nbio = (int)num;
break;
case BIO_C_GET_FD:
if (bio->init) {
ip = (int *)ptr;
if (ip != NULL) {
*ip = bio->num;
}
ret = bio->num;
} else {
ret = -1;
}
break;
case BIO_CTRL_GET_CLOSE:
ret = bio->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
bio->shutdown = (int)num;
break;
case BIO_CTRL_PENDING:
case BIO_CTRL_WPENDING:
ret = 0;
break;
case BIO_CTRL_FLUSH:
break;
case BIO_CTRL_GET_CALLBACK: {
bio_info_cb *fptr = ptr;
*fptr = data->info_callback;
} break;
default:
ret = 0;
break;
}
return ret;
}
static long conn_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
long ret = 1;
BIO_CONNECT *data;
data = (BIO_CONNECT *)bio->ptr;
switch (cmd) {
case BIO_CTRL_SET_CALLBACK:
data->info_callback = fp;
break;
default:
ret = 0;
break;
}
return ret;
}
BIO *BIO_new_connect(const char *hostname) {
BIO *ret;
ret = BIO_new(BIO_s_connect());
if (ret == NULL) {
return NULL;
}
if (!BIO_set_conn_hostname(ret, hostname)) {
BIO_free(ret);
return NULL;
}
return ret;
}
static const BIO_METHOD methods_connectp = {
BIO_TYPE_CONNECT, "socket connect", conn_write, conn_read,
NULL /* puts */, NULL /* gets */, conn_ctrl, conn_new,
conn_free, conn_callback_ctrl,
};
const BIO_METHOD *BIO_s_connect(void) { return &methods_connectp; }
int BIO_set_conn_hostname(BIO *bio, const char *name) {
return (int)BIO_ctrl(bio, BIO_C_SET_CONNECT, 0, (void*) name);
}
int BIO_set_conn_port(BIO *bio, const char *port_str) {
return (int)BIO_ctrl(bio, BIO_C_SET_CONNECT, 1, (void*) port_str);
}
int BIO_set_conn_int_port(BIO *bio, const int *port) {
char buf[DECIMAL_SIZE(int) + 1];
snprintf(buf, sizeof(buf), "%d", *port);
return BIO_set_conn_port(bio, buf);
}
int BIO_set_nbio(BIO *bio, int on) {
return (int)BIO_ctrl(bio, BIO_C_SET_NBIO, on, NULL);
}
int BIO_do_connect(BIO *bio) {
return (int)BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL);
}
#endif // OPENSSL_NO_SOCK

View File

@@ -0,0 +1,483 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/bio.h>
#if !defined(OPENSSL_NO_SOCK)
#include <openssl/mem.h>
#include "../internal.h"
#include "./internal.h"
#if !defined(OPENSSL_WINDOWS)
#include <sys/time.h>
static int closesocket(const int sock) { return close(sock); }
#else
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <winsock2.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
#endif
typedef struct bio_dgram_data_st {
BIO_ADDR peer;
unsigned int connected;
unsigned int _errno;
} bio_dgram_data;
static socklen_t BIO_ADDR_sockaddr_size(const BIO_ADDR *bap) {
GUARD_PTR(bap);
if (bap->sa.sa_family == AF_INET) {
return sizeof(bap->s_in);
}
#ifdef AF_INET6
if (bap->sa.sa_family == AF_INET6) {
return sizeof(bap->s_in6);
}
#endif
#ifdef AWS_LC_HAS_AF_UNIX
if (bap->sa.sa_family == AF_UNIX) {
return sizeof(bap->s_un);
}
#endif
return sizeof(*bap);
}
static struct sockaddr *BIO_ADDR_sockaddr_noconst(BIO_ADDR *bap) {
GUARD_PTR(bap);
return &bap->sa;
}
static const struct sockaddr *BIO_ADDR_sockaddr(const BIO_ADDR *bap) {
GUARD_PTR(bap);
return &bap->sa;
}
static int dgram_get_errno(void) {
#if defined(OPENSSL_WINDOWS)
return WSAGetLastError();
#else
return errno;
#endif
}
static int dgram_write(BIO *bp, const char *in, const int in_len) {
GUARD_PTR(bp);
GUARD_PTR(in);
ssize_t result;
bio_dgram_data *data = bp->ptr;
GUARD_PTR(data);
if (in_len < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
return -1;
}
bio_clear_socket_error(bp->num);
if (data->connected) {
// With a zero flags argument, send() is equivalent to write(2).
result = send(bp->num, in, in_len, 0);
} else {
// If a peer address has been pre-specified, sendto may return -1 and set
// errno to[EISCONN].
const socklen_t peerlen = BIO_ADDR_sockaddr_size(&data->peer);
result =
sendto(bp->num, in, in_len, 0, BIO_ADDR_sockaddr(&data->peer), peerlen);
}
if (result < INT_MIN || result > INT_MAX) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
return -1;
}
const int ret = result;
BIO_clear_retry_flags(bp);
if (ret <= 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_write(bp);
}
data->_errno = dgram_get_errno();
}
return ret;
}
static int dgram_read(BIO *bp, char *out, const int out_len) {
GUARD_PTR(bp);
GUARD_PTR(out);
GUARD_PTR(bp->ptr);
BIO_ADDR peer;
// Might be modified by call to `recvfrom`.
socklen_t len = sizeof(peer);
bio_dgram_data *data = bp->ptr;
bio_clear_socket_error(bp->num);
if (out_len < 0) {
// out_len is cast to `size_t` below.
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
return -1;
}
// recvfrom may be used to receive data on a socket regardless of whether
// it's connection-oriented.
const ssize_t result = recvfrom(bp->num, out, out_len, 0,
BIO_ADDR_sockaddr_noconst(&peer), &len);
if (result < INT_MIN || result > INT_MAX) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
return -1;
}
const int ret = result;
if (!data->connected && ret >= 0) {
if (1 != BIO_dgram_set_peer(bp, &peer)) {
// The operation does not fail if peer not set.
}
}
BIO_clear_retry_flags(bp);
if (ret < 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_read(bp);
}
data->_errno = dgram_get_errno();
}
return ret;
}
static int dgram_puts(BIO *bp, const char *str) {
GUARD_PTR(bp);
GUARD_PTR(str);
const int len = OPENSSL_strnlen(str, INT_MAX);
return dgram_write(bp, str, len);
}
static int dgram_free(BIO *bp) {
GUARD_PTR(bp);
if (bp->shutdown && bp->init) {
if (0 != closesocket(bp->num)) {
// the show must go on
}
}
bp->init = 0;
bp->num = -1;
bp->flags = 0;
OPENSSL_free(bp->ptr);
bp->ptr = NULL;
return 1;
}
static int dgram_new(BIO *bio) {
bio->init = 0;
bio->num = -1;
bio->ptr = OPENSSL_zalloc(sizeof(bio_dgram_data));
return bio->ptr != NULL;
}
static long dgram_ctrl(BIO *bp, const int cmd, const long num, void *ptr) {
GUARD_PTR(bp);
bio_dgram_data *data = bp->ptr;
long ret = 1;
switch (cmd) {
case BIO_C_SET_FD:
GUARD_PTR(ptr);
int fd = *(int *)ptr;
if (fd < 0) {
// file descriptors must be non-negative.
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
return 0;
}
dgram_free(bp);
dgram_new(bp);
bp->num = fd;
bp->shutdown = (int)num;
bp->init = 1;
break;
case BIO_C_GET_FD:
if (bp->init) {
int *ip = ptr;
if (ip) {
*ip = bp->num;
}
ret = bp->num;
} else {
ret = -1;
}
break;
case BIO_CTRL_GET_CLOSE:
ret = bp->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
bp->shutdown = num != 0;
break;
case BIO_CTRL_FLUSH:
ret = 1;
break;
case BIO_CTRL_DGRAM_SET_CONNECTED:
GUARD_PTR(data);
if (ptr != NULL) {
data->connected = 1;
ret = BIO_ADDR_copy(&data->peer, ptr);
} else {
data->connected = 0;
OPENSSL_cleanse(&data->peer, sizeof(data->peer));
ret = 1;
}
break;
case BIO_CTRL_DGRAM_GET_PEER: {
GUARD_PTR(data);
GUARD_PTR(ptr);
const socklen_t size = BIO_ADDR_sockaddr_size(&data->peer);
if (num == 0 || num >= (long)size) {
OPENSSL_memcpy(ptr, &data->peer, size);
ret = size;
} else {
ret = 0;
}
break;
}
case BIO_CTRL_DGRAM_CONNECT:
case BIO_CTRL_DGRAM_SET_PEER:
GUARD_PTR(data);
GUARD_PTR(ptr);
ret = BIO_ADDR_copy(&data->peer, ptr);
break;
case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: {
GUARD_PTR(data);
int d_errno = 0;
#ifdef OPENSSL_WINDOWS
d_errno = (data->_errno == WSAETIMEDOUT);
#else
/*
* if no data has been transferred and the timeout has been reached,
* then -1 is returned with errno set to EAGAIN or EWOULDBLOCK,
* or EINPROGRESS (for connect(2)) just as if the socket was specified
* to be nonblocking.
*/
d_errno = data->_errno == EAGAIN || data->_errno == EWOULDBLOCK ||
data->_errno == EINPROGRESS;
#endif
if (d_errno) {
ret = 1;
data->_errno = 0;
} else {
ret = 0;
}
break;
}
#if defined(SO_RCVTIMEO)
case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: {
GUARD_PTR(ptr);
struct timeval *tv = (struct timeval *)ptr;
// Check for negative values in timeval
if (tv->tv_sec < 0 || tv->tv_usec < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
ret = -1;
break;
}
#ifdef OPENSSL_WINDOWS
DWORD timeout = (DWORD)(tv->tv_sec * 1000 + tv->tv_usec / 1000);
if (setsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO,
(void *)&timeout, sizeof(timeout)) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#else
if (setsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
sizeof(struct timeval)) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#endif
break;
}
case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: {
GUARD_PTR(ptr);
union {
size_t s;
int i;
} sz = {0};
#ifdef OPENSSL_WINDOWS
int timeout;
struct timeval *tv = (struct timeval *)ptr;
sz.i = sizeof(timeout);
if (getsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO,
(void *)&timeout, &sz.i) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else {
tv->tv_sec = timeout / 1000;
tv->tv_usec = (timeout % 1000) * 1000;
ret = sizeof(*tv);
}
#else
sz.i = sizeof(struct timeval);
if (getsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO,
ptr, (void *)&sz) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
ret = (int)sz.s;
} else {
ret = sz.i;
}
#endif
break;
}
#endif
#if defined(SO_SNDTIMEO)
case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: {
GUARD_PTR(ptr);
struct timeval *tv = (struct timeval *)ptr;
// Check for negative values in timeval
if (tv->tv_sec < 0 || tv->tv_usec < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
ret = -1;
break;
}
#ifdef OPENSSL_WINDOWS
DWORD timeout = (DWORD)(tv->tv_sec * 1000 + tv->tv_usec / 1000);
if (setsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO,
(void *)&timeout, sizeof(timeout)) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#else
if (setsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
sizeof(struct timeval)) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#endif
break;
}
case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: {
GUARD_PTR(ptr);
union {
size_t s;
int i;
} sz = {0};
#ifdef OPENSSL_WINDOWS
int timeout;
struct timeval *tv = (struct timeval *)ptr;
sz.i = sizeof(timeout);
if (getsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO,
(void *)&timeout, &sz.i) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else {
tv->tv_sec = timeout / 1000;
tv->tv_usec = (timeout % 1000) * 1000;
ret = sizeof(*tv);
}
#else
sz.i = sizeof(struct timeval);
if (getsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO,
ptr, (void *)&sz) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
ret = (int)sz.s;
} else {
ret = sz.i;
}
#endif
break;
}
#endif
default:
ret = 0;
break;
}
return ret;
}
static const BIO_METHOD methods_dgramp = {
.type = BIO_TYPE_DGRAM,
.name = "datagram socket",
.bwrite = dgram_write,
.bread = dgram_read,
.bputs = dgram_puts,
.bgets = NULL,
.ctrl = dgram_ctrl,
.create = dgram_new,
.destroy = dgram_free,
.callback_ctrl = NULL,
};
const BIO_METHOD *BIO_s_datagram(void) { return &methods_dgramp; }
BIO *BIO_new_dgram(int fd, int close_flag) {
BIO *ret = BIO_new(BIO_s_datagram());
if (ret == NULL) {
return NULL;
}
int result = BIO_set_fd(ret, fd, close_flag);
if (result <= 0) {
BIO_free(ret);
return NULL;
}
return ret;
}
int BIO_ctrl_dgram_connect(BIO *bp, const BIO_ADDR *peer) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_CONNECT, 0, (BIO_ADDR *)peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_ctrl_set_connected(BIO *bp, const BIO_ADDR *peer) {
const long ret =
BIO_ctrl(bp, BIO_CTRL_DGRAM_SET_CONNECTED, 0, (BIO_ADDR *)peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_recv_timedout(BIO *bp) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP, 0, NULL);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_send_timedout(BIO *bp) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP, 0, NULL);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_get_peer(BIO *bp, BIO_ADDR *peer) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_GET_PEER, 0, peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_set_peer(BIO *bp, const BIO_ADDR *peer) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_SET_PEER, 0, (BIO_ADDR *)peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
#endif

View File

@@ -0,0 +1,39 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#include <errno.h>
#include "internal.h"
int bio_errno_should_retry(int return_value) {
if (return_value != -1) {
return 0;
}
return
#ifdef EWOULDBLOCK
errno == EWOULDBLOCK ||
#endif
#ifdef ENOTCONN
errno == ENOTCONN ||
#endif
#ifdef EINTR
errno == EINTR ||
#endif
#ifdef EAGAIN
errno == EAGAIN ||
#endif
#ifdef EPROTO
errno == EPROTO ||
#endif
#ifdef EINPROGRESS
errno == EINPROGRESS ||
#endif
#ifdef EALREADY
errno == EALREADY ||
#endif
0;
}

182
vendor/aws-lc-sys/aws-lc/crypto/bio/fd.c vendored Normal file
View File

@@ -0,0 +1,182 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#if !defined(OPENSSL_NO_POSIX_IO)
#include <errno.h>
#include <string.h>
#if !defined(OPENSSL_WINDOWS)
#include <unistd.h>
#else
#include <io.h>
#endif
#include <openssl/err.h>
#include <openssl/mem.h>
#include "internal.h"
#include "../internal.h"
#if defined(OPENSSL_WINDOWS)
#define BORINGSSL_CLOSE _close
#define BORINGSSL_LSEEK _lseek
#define BORINGSSL_READ _read
#define BORINGSSL_WRITE _write
#else
#define BORINGSSL_CLOSE close
#define BORINGSSL_LSEEK lseek
#define BORINGSSL_READ read
#define BORINGSSL_WRITE write
#endif
BIO *BIO_new_fd(int fd, int close_flag) {
BIO *ret = BIO_new(BIO_s_fd());
if (ret == NULL) {
return NULL;
}
BIO_set_fd(ret, fd, close_flag);
return ret;
}
static int fd_new(BIO *bio) {
// num is used to store the file descriptor.
bio->num = -1;
return 1;
}
static int fd_free(BIO *bio) {
if (bio->shutdown) {
if (bio->init) {
BORINGSSL_CLOSE(bio->num);
}
bio->init = 0;
}
return 1;
}
static int fd_read(BIO *b, char *out, int outl) {
int ret = 0;
ret = (int)BORINGSSL_READ(b->num, out, outl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_errno_should_retry(ret)) {
BIO_set_retry_read(b);
}
}
return ret;
}
static int fd_write(BIO *b, const char *in, int inl) {
int ret = (int)BORINGSSL_WRITE(b->num, in, inl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_errno_should_retry(ret)) {
BIO_set_retry_write(b);
}
}
return ret;
}
static long fd_ctrl(BIO *b, int cmd, long num, void *ptr) {
long ret = 1;
int *ip;
switch (cmd) {
case BIO_CTRL_RESET:
num = 0;
OPENSSL_FALLTHROUGH;
case BIO_C_FILE_SEEK:
ret = 0;
if (b->init) {
ret = (long)BORINGSSL_LSEEK(b->num, num, SEEK_SET);
}
break;
case BIO_C_FILE_TELL:
case BIO_CTRL_INFO:
ret = 0;
if (b->init) {
ret = (long)BORINGSSL_LSEEK(b->num, 0, SEEK_CUR);
}
break;
case BIO_C_SET_FD:
fd_free(b);
b->num = *((int *)ptr);
b->shutdown = (int)num;
b->init = 1;
break;
case BIO_C_GET_FD:
if (b->init) {
ip = (int *)ptr;
if (ip != NULL) {
*ip = b->num;
}
return b->num;
} else {
ret = -1;
}
break;
case BIO_CTRL_GET_CLOSE:
ret = b->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
b->shutdown = (int)num;
break;
case BIO_CTRL_PENDING:
case BIO_CTRL_WPENDING:
ret = 0;
break;
case BIO_CTRL_FLUSH:
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
static int fd_gets(BIO *bp, char *buf, int size) {
if (size <= 0) {
return 0;
}
char *ptr = buf;
char *end = buf + size - 1;
while (ptr < end && fd_read(bp, ptr, 1) > 0) {
char c = ptr[0];
ptr++;
if (c == '\n') {
break;
}
}
ptr[0] = '\0';
// The output length is bounded by |size|.
return (int)(ptr - buf);
}
static const BIO_METHOD methods_fdp = {
BIO_TYPE_FD, "file descriptor", fd_write, fd_read, NULL /* puts */,
fd_gets, fd_ctrl, fd_new, fd_free, NULL /* callback_ctrl */,
};
const BIO_METHOD *BIO_s_fd(void) { return &methods_fdp; }
#endif // OPENSSL_NO_POSIX_IO
int BIO_set_fd(BIO *bio, int fd, int close_flag) {
return (int)BIO_int_ctrl(bio, BIO_C_SET_FD, close_flag, fd);
}
int BIO_get_fd(BIO *bio, int *out_fd) {
return (int)BIO_ctrl(bio, BIO_C_GET_FD, 0, (char *) out_fd);
}

View File

@@ -0,0 +1,282 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#if defined(__linux) || defined(__sun) || defined(__hpux)
// Following definition aliases fopen to fopen64 on above mentioned
// platforms. This makes it possible to open and sequentially access
// files larger than 2GB from 32-bit application. It does not allow to
// traverse them beyond 2GB with fseek/ftell, but on the other hand *no*
// 32-bit platform permits that, not with fseek/ftell. Not to mention
// that breaking 2GB limit for seeking would require surgery to *our*
// API. But sequential access suffices for practical cases when you
// can run into large files, such as fingerprinting, so we can let API
// alone. For reference, the list of 32-bit platforms which allow for
// sequential access of large files without extra "magic" comprise *BSD,
// Darwin, IRIX...
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#endif
#include <openssl/bio.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#if defined(OPENSSL_WINDOWS)
#include <fcntl.h>
#include <io.h>
#endif // OPENSSL_WINDOWS
#include <openssl/err.h>
#include <openssl/mem.h>
#include "../internal.h"
#define BIO_FP_READ 0x02
#define BIO_FP_WRITE 0x04
#define BIO_FP_APPEND 0x08
#if !defined(OPENSSL_NO_FILESYSTEM)
#define fopen_if_available fopen
#else
static FILE *fopen_if_available(const char *path, const char *mode) {
errno = ENOENT;
return NULL;
}
#endif
BIO *BIO_new_file(const char *filename, const char *mode) {
BIO *ret;
FILE *file;
file = fopen_if_available(filename, mode);
if (file == NULL) {
OPENSSL_PUT_SYSTEM_ERROR();
ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
if (errno == ENOENT) {
OPENSSL_PUT_ERROR(BIO, BIO_R_NO_SUCH_FILE);
} else {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
}
return NULL;
}
ret = BIO_new_fp(file, BIO_CLOSE);
if (ret == NULL) {
fclose(file);
return NULL;
}
return ret;
}
BIO *BIO_new_fp(FILE *stream, int close_flag) {
BIO *ret = BIO_new(BIO_s_file());
if (ret == NULL) {
return NULL;
}
BIO_set_fp(ret, stream, close_flag);
return ret;
}
static int file_free(BIO *bio) {
if (!bio->shutdown) {
return 1;
}
if (bio->init && bio->ptr != NULL) {
fclose(bio->ptr);
bio->ptr = NULL;
}
bio->init = 0;
return 1;
}
static int file_read(BIO *b, char *out, int outl) {
if (!b->init) {
return 0;
}
size_t ret = fread(out, 1, outl, (FILE *)b->ptr);
if (ret == 0 && ferror((FILE *)b->ptr)) {
OPENSSL_PUT_SYSTEM_ERROR();
OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
return -1;
}
// fread reads at most |outl| bytes, so |ret| fits in an int.
return (int)ret;
}
static int file_write(BIO *b, const char *in, int inl) {
if (!b->init) {
return 0;
}
int ret = (int)fwrite(in, inl, 1, (FILE *)b->ptr);
if (ret > 0) {
ret = inl;
}
return ret;
}
static long file_ctrl(BIO *b, int cmd, long num, void *ptr) {
long ret = 1;
FILE *fp = (FILE *)b->ptr;
FILE **fpp;
switch (cmd) {
case BIO_CTRL_RESET:
num = 0;
OPENSSL_FALLTHROUGH;
case BIO_C_FILE_SEEK:
ret = (long)fseek(fp, num, 0);
break;
case BIO_CTRL_EOF:
ret = (long)feof(fp);
break;
case BIO_C_FILE_TELL:
case BIO_CTRL_INFO:
ret = ftell(fp);
break;
case BIO_C_SET_FILE_PTR:
file_free(b);
b->shutdown = (int)num & BIO_CLOSE;
b->ptr = ptr;
b->init = 1;
#if defined(OPENSSL_WINDOWS)
// Windows differentiates between "text" and "binary" file modes, so set
// the file to text mode if caller specifies BIO_FP_TEXT flag. If
// |BIO_FP_TEXT| is not set, we still call |_setmode| with |_O_BINARY| to
// match OpenSSL's behavior. BoringSSL, on the other hand, does nothing in
// this case.
//
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setmode?view=msvc-170#remarks
// https://github.com/google/boringssl/commit/5ee4e9512e9a99f97c4a3fad397034028b3457c2
_setmode(_fileno(b->ptr), num & BIO_FP_TEXT ? _O_TEXT : _O_BINARY);
#endif
break;
case BIO_C_SET_FILENAME:
file_free(b);
b->shutdown = (int)num & BIO_CLOSE;
const char *mode;
if (num & BIO_FP_APPEND) {
if (num & BIO_FP_READ) {
mode = "ab+";
} else {
mode = "ab";
}
} else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE)) {
mode = "rb+";
} else if (num & BIO_FP_WRITE) {
mode = "wb";
} else if (num & BIO_FP_READ) {
mode = "rb";
} else {
OPENSSL_PUT_ERROR(BIO, BIO_R_BAD_FOPEN_MODE);
ret = 0;
break;
}
fp = fopen_if_available(ptr, mode);
if (fp == NULL) {
OPENSSL_PUT_SYSTEM_ERROR();
ERR_add_error_data(5, "fopen('", ptr, "','", mode, "')");
OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
ret = 0;
break;
}
b->ptr = fp;
b->init = 1;
break;
case BIO_C_GET_FILE_PTR:
// the ptr parameter is actually a FILE ** in this case.
if (ptr != NULL) {
fpp = (FILE **)ptr;
*fpp = (FILE *)b->ptr;
}
break;
case BIO_CTRL_GET_CLOSE:
ret = (long)b->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
b->shutdown = (int)num;
break;
case BIO_CTRL_FLUSH:
ret = 0 == fflush((FILE *)b->ptr);
break;
case BIO_CTRL_WPENDING:
case BIO_CTRL_PENDING:
default:
ret = 0;
break;
}
return ret;
}
static int file_gets(BIO *bp, char *buf, int size) {
if (size == 0) {
return 0;
}
if (!fgets(buf, size, (FILE *)bp->ptr)) {
buf[0] = 0;
// TODO(davidben): This doesn't distinguish error and EOF. This should check
// |ferror| as in |file_read|.
return 0;
}
return (int)strlen(buf);
}
static const BIO_METHOD methods_filep = {
BIO_TYPE_FILE, "FILE pointer",
file_write, file_read,
NULL /* puts */, file_gets,
file_ctrl, NULL /* create */,
file_free, NULL /* callback_ctrl */,
};
const BIO_METHOD *BIO_s_file(void) { return &methods_filep; }
int BIO_get_fp(BIO *bio, FILE **out_file) {
return (int)BIO_ctrl(bio, BIO_C_GET_FILE_PTR, 0, (char *)out_file);
}
int BIO_set_fp(BIO *bio, FILE *file, int flags) {
return (int)BIO_ctrl(bio, BIO_C_SET_FILE_PTR, flags, (char *)file);
}
int BIO_read_filename(BIO *bio, const char *filename) {
return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_READ,
(char *)filename);
}
int BIO_write_filename(BIO *bio, const char *filename) {
return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_WRITE,
(char *)filename);
}
int BIO_append_filename(BIO *bio, const char *filename) {
return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_APPEND,
(char *)filename);
}
int BIO_rw_filename(BIO *bio, const char *filename) {
return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME,
BIO_CLOSE | BIO_FP_READ | BIO_FP_WRITE,
(char *)filename);
}
long BIO_tell(BIO *bio) { return BIO_ctrl(bio, BIO_C_FILE_TELL, 0, NULL); }
long BIO_seek(BIO *bio, long offset) {
return BIO_ctrl(bio, BIO_C_FILE_SEEK, offset, NULL);
}

View File

@@ -0,0 +1,173 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#include <limits.h>
#include <string.h>
#include "../internal.h"
// hexdump_ctx contains the state of a hexdump.
struct hexdump_ctx {
BIO *bio;
char right_chars[18]; // the contents of the right-hand side, ASCII dump.
unsigned used; // number of bytes in the current line.
size_t n; // number of bytes total.
unsigned indent;
};
static void hexbyte(char *out, uint8_t b) {
static const char hextable[] = "0123456789abcdef";
out[0] = hextable[b>>4];
out[1] = hextable[b&0x0f];
}
static char to_char(uint8_t b) {
if (b < 32 || b > 126) {
return '.';
}
return b;
}
// hexdump_write adds |len| bytes of |data| to the current hex dump described by
// |ctx|.
static int hexdump_write(struct hexdump_ctx *ctx, const uint8_t *data,
size_t len) {
char buf[10];
unsigned l;
// Output lines look like:
// 00000010 2e 2f 30 31 32 33 34 35 36 37 38 ... 3c 3d // |./0123456789:;<=|
// ^ offset ^ extra space ^ ASCII of line
for (size_t i = 0; i < len; i++) {
if (ctx->used == 0) {
// The beginning of a line.
if (!BIO_indent(ctx->bio, ctx->indent, UINT_MAX)) {
return 0;
}
hexbyte(&buf[0], ctx->n >> 24);
hexbyte(&buf[2], ctx->n >> 16);
hexbyte(&buf[4], ctx->n >> 8);
hexbyte(&buf[6], ctx->n);
buf[8] = buf[9] = ' ';
if (BIO_write(ctx->bio, buf, 10) < 0) {
return 0;
}
}
hexbyte(buf, data[i]);
buf[2] = ' ';
l = 3;
if (ctx->used == 7) {
// There's an additional space after the 8th byte.
buf[3] = ' ';
l = 4;
} else if (ctx->used == 15) {
// At the end of the line there's an extra space and the bar for the
// right column.
buf[3] = ' ';
buf[4] = '|';
l = 5;
}
if (BIO_write(ctx->bio, buf, l) < 0) {
return 0;
}
ctx->right_chars[ctx->used] = to_char(data[i]);
ctx->used++;
ctx->n++;
if (ctx->used == 16) {
ctx->right_chars[16] = '|';
ctx->right_chars[17] = '\n';
if (BIO_write(ctx->bio, ctx->right_chars, sizeof(ctx->right_chars)) < 0) {
return 0;
}
ctx->used = 0;
}
}
return 1;
}
// finish flushes any buffered data in |ctx|.
static int finish(struct hexdump_ctx *ctx) {
// See the comments in |hexdump| for the details of this format.
const unsigned n_bytes = ctx->used;
unsigned l;
char buf[5];
if (n_bytes == 0) {
return 1;
}
OPENSSL_memset(buf, ' ', 4);
buf[4] = '|';
for (; ctx->used < 16; ctx->used++) {
l = 3;
if (ctx->used == 7) {
l = 4;
} else if (ctx->used == 15) {
l = 5;
}
if (BIO_write(ctx->bio, buf, l) < 0) {
return 0;
}
}
ctx->right_chars[n_bytes] = '|';
ctx->right_chars[n_bytes + 1] = '\n';
if (BIO_write(ctx->bio, ctx->right_chars, n_bytes + 2) < 0) {
return 0;
}
return 1;
}
int BIO_hexdump(BIO *bio, const uint8_t *data, size_t len, unsigned indent) {
struct hexdump_ctx ctx;
OPENSSL_memset(&ctx, 0, sizeof(ctx));
ctx.bio = bio;
ctx.indent = indent;
if (!hexdump_write(&ctx, data, len) || !finish(&ctx)) {
return 0;
}
return 1;
}
int BIO_dump(BIO *bio, const void *data, int len) {
if (bio == NULL || data == NULL || len < 0) {
return -1;
}
// Use a temporary memory BIO to capture the formatted output
int ret = -1;
BIO *mbio = BIO_new(BIO_s_mem());
if (mbio == NULL) {
return -1;
}
// Generate the hexdump to the memory BIO
if (!BIO_hexdump(mbio, (const uint8_t *)data, (size_t)len, 0)) {
goto err;
}
// Get the formatted content
const uint8_t *contents = NULL;
size_t content_len = 0;
if (!BIO_mem_contents(mbio, &contents, &content_len)) {
goto err;
}
// Write to the original BIO and return the exact bytes written
ret = BIO_write(bio, contents, content_len);
err:
BIO_free(mbio);
return ret;
}

View File

@@ -0,0 +1,91 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENSSL_HEADER_BIO_INTERNAL_H
#define OPENSSL_HEADER_BIO_INTERNAL_H
#include <openssl/bio.h>
#if !defined(OPENSSL_NO_SOCK)
#if !defined(OPENSSL_WINDOWS)
#if defined(OPENSSL_PNACL)
// newlib uses u_short in socket.h without defining it.
typedef unsigned short u_short;
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <unistd.h>
#else
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <winsock2.h>
#include <ws2ipdef.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
typedef int socklen_t;
#if !defined(_SSIZE_T_DEFINED)
typedef SSIZE_T ssize_t;
#endif
#endif
#endif // !OPENSSL_NO_SOCK
#if defined(__cplusplus)
extern "C" {
#endif
#if !defined(OPENSSL_NO_SOCK)
// bio_ip_and_port_to_socket_and_addr creates a socket and fills in |*out_addr|
// and |*out_addr_length| with the correct values for connecting to |hostname|
// on |port_str|. It returns one on success or zero on error.
int bio_ip_and_port_to_socket_and_addr(int *out_sock,
struct sockaddr_storage *out_addr,
socklen_t *out_addr_length,
const char *hostname,
const char *port_str);
// bio_socket_nbio sets whether |sock| is non-blocking. It returns one on
// success and zero otherwise.
int bio_socket_nbio(int sock, int on);
// bio_clear_socket_error clears the last socket error on |sock|.
void bio_clear_socket_error(int sock);
// bio_sock_error_get_and_clear clears and returns the last socket error on |sock|.
int bio_sock_error_get_and_clear(int sock);
// bio_socket_should_retry returns non-zero if |return_value| indicates an error
// and the last socket error indicates that it's non-fatal.
int bio_socket_should_retry(int return_value);
#if defined(AF_UNIX) && !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_ANDROID)
// Winsock2 APIs don't support AF_UNIX.
// > The values currently supported are AF_INET or AF_INET6, which are the
// > Internet address family formats for IPv4 and IPv6.
// https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket
#define AWS_LC_HAS_AF_UNIX 1
#endif
union bio_addr_st {
struct sockaddr sa;
#ifdef AF_INET6
struct sockaddr_in6 s_in6;
#endif
struct sockaddr_in s_in;
#if AWS_LC_HAS_AF_UNIX
struct sockaddr_un s_un;
#endif
};
#endif // !OPENSSL_NO_SOCK
// bio_errno_should_retry returns non-zero if |return_value| indicates an error
// and |errno| indicates that it's non-fatal.
int bio_errno_should_retry(int return_value);
#if defined(__cplusplus)
} // extern C
#endif
#endif // OPENSSL_HEADER_BIO_INTERNAL_H

205
vendor/aws-lc-sys/aws-lc/crypto/bio/md.c vendored Normal file
View File

@@ -0,0 +1,205 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <errno.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/pkcs7.h>
#include "internal.h"
#include "../internal.h"
static int md_new(BIO *b) {
GUARD_PTR(b);
EVP_MD_CTX *ctx;
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
return 0;
}
BIO_set_data(b, ctx);
return 1;
}
static int md_free(BIO *b) {
GUARD_PTR(b);
EVP_MD_CTX_free(BIO_get_data(b));
BIO_set_data(b, NULL);
BIO_set_init(b, 0);
return 1;
}
static int md_read(BIO *b, char *out, int outl) {
GUARD_PTR(b);
GUARD_PTR(out);
int ret = 0;
EVP_MD_CTX *ctx;
BIO *next;
ctx = BIO_get_data(b);
next = BIO_next(b);
if ((ctx == NULL) || (next == NULL) || outl <= 0) {
return 0;
}
ret = BIO_read(next, out, outl);
if (ret > 0) {
if (EVP_DigestUpdate(ctx, (unsigned char *)out, ret) <= 0) {
ret = -1;
}
}
BIO_clear_retry_flags(b);
BIO_copy_next_retry(b);
return ret;
}
static int md_write(BIO *b, const char *in, int inl) {
GUARD_PTR(b);
GUARD_PTR(in);
int ret = 0;
EVP_MD_CTX *ctx;
BIO *next;
ctx = BIO_get_data(b);
next = BIO_next(b);
if ((ctx == NULL) || (next == NULL) || inl <= 0) {
return 0;
}
ret = BIO_write(next, in, inl);
if (ret > 0) {
if (EVP_DigestUpdate(ctx, (const unsigned char *)in, ret) <= 0) {
ret = -1;
}
}
BIO_clear_retry_flags(b);
BIO_copy_next_retry(b);
return ret;
}
static long md_ctrl(BIO *b, int cmd, long num, void *ptr) {
GUARD_PTR(b);
EVP_MD_CTX *ctx, **pctx;
EVP_MD *md;
long ret = 1;
BIO *next;
ctx = BIO_get_data(b);
next = BIO_next(b);
switch (cmd) {
case BIO_CTRL_RESET:
if (BIO_get_init(b)) {
ret = EVP_DigestInit_ex(ctx, EVP_MD_CTX_md(ctx), NULL);
} else {
ret = 0;
}
if (ret > 0) {
ret = BIO_ctrl(next, cmd, num, ptr);
}
break;
case BIO_C_GET_MD_CTX:
GUARD_PTR(ptr);
pctx = ptr;
*pctx = ctx;
BIO_set_init(b, 1);
break;
case BIO_C_SET_MD:
GUARD_PTR(ptr);
md = ptr;
ret = EVP_DigestInit_ex(ctx, md, NULL);
if (ret > 0) {
BIO_set_init(b, 1);
}
break;
case BIO_C_GET_MD:
GUARD_PTR(ptr);
if (BIO_get_init(b)) {
const EVP_MD **ppmd = ptr;
*ppmd = EVP_MD_CTX_md(ctx);
} else {
ret = 0;
}
break;
// |BIO_C_SET_MD_CTX| and |BIO_set_md_ctx| are not implemented due to
// complexity with |BIO_f_md|'s automatic |EVP_MD_CTX| allocation.
// OpenSSL users would need to manually free the existing |EVP_MD_CTX|
// before setting a new one.
// Open an issue to AWS-LC if this functionality is ever needed.
case BIO_C_SET_MD_CTX:
// |BIO_C_DO_STATE_MACHINE| is used by |BIO_do_handshake| for most |BIO|s in
// OpenSSL. This functionality is only properly supported by |BIO_s_connect|
// within AWS-LC.
case BIO_C_DO_STATE_MACHINE:
// |BIO_CTRL_DUP| is used by |BIO_dup_state| for most |BIO|s in OpenSSL,
// which AWS-LC doesn't have support for. We can implement support here (or
// with a direct function call) if we ever have the need to do so.
case BIO_CTRL_DUP:
// |BIO_CTRL_GET/SET_CALLBACK| are routed to by |BIO_get/set_info_callback|
// in OpenSSL, which AWS-LC doesn't have support for. We can implement
// support here (or with a direct function call) if we ever have the need to
// do so.
case BIO_CTRL_GET_CALLBACK:
case BIO_CTRL_SET_CALLBACK:
OPENSSL_PUT_ERROR(PKCS7, ERR_R_BIO_LIB);
return 0;
default:
ret = BIO_ctrl(next, cmd, num, ptr);
break;
}
return ret;
}
static int md_gets(BIO *b, char *buf, int size) {
GUARD_PTR(b);
GUARD_PTR(buf);
EVP_MD_CTX *ctx;
unsigned int ret;
ctx = BIO_get_data(b);
if (((size_t)size) < EVP_MD_CTX_size(ctx)) {
return 0;
}
if (EVP_DigestFinal_ex(ctx, (unsigned char *)buf, &ret) <= 0) {
return -1;
}
return ret;
}
static const BIO_METHOD methods_md = {
BIO_TYPE_MD, // type
"message digest", // name
md_write, // bwrite
md_read, // bread
NULL, // bputs
md_gets, // bgets
md_ctrl, // ctrl
md_new, // create
md_free, // destroy
NULL, // callback_ctrl
};
const BIO_METHOD *BIO_f_md(void) { return &methods_md; }
int BIO_get_md_ctx(BIO *b, EVP_MD_CTX **ctx) {
return BIO_ctrl(b, BIO_C_GET_MD_CTX, 0, ctx);
}
int BIO_set_md(BIO *b, const EVP_MD *md) {
return BIO_ctrl(b, BIO_C_SET_MD, 0, (EVP_MD*)md);
}
int BIO_get_md(BIO *b, EVP_MD **md) {
return BIO_ctrl(b, BIO_C_GET_MD, 0, md);
}

View File

@@ -0,0 +1,436 @@
// Copyright (c) 1998-2003 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#include <assert.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include "../internal.h"
struct bio_bio_st {
BIO *peer; // NULL if buf == NULL.
// If peer != NULL, then peer->ptr is also a bio_bio_st,
// and its "peer" member points back to us.
// peer != NULL iff init != 0 in the BIO.
// This is for what we write (i.e. reading uses peer's struct):
int closed; // valid iff peer != NULL
size_t len; // valid iff buf != NULL; 0 if peer == NULL
size_t offset; // valid iff buf != NULL; 0 if len == 0
size_t size;
uint8_t *buf; // "size" elements (if != NULL)
size_t request; // valid iff peer != NULL; 0 if len != 0,
// otherwise set by peer to number of bytes
// it (unsuccessfully) tried to read,
// never more than buffer space (size-len) warrants.
};
static int bio_new(BIO *bio) {
struct bio_bio_st *b = OPENSSL_zalloc(sizeof *b);
if (b == NULL) {
return 0;
}
b->size = 17 * 1024; // enough for one TLS record (just a default)
bio->ptr = b;
return 1;
}
static void bio_destroy_pair(BIO *bio) {
struct bio_bio_st *b = bio->ptr;
BIO *peer_bio;
struct bio_bio_st *peer_b;
if (b == NULL) {
return;
}
peer_bio = b->peer;
if (peer_bio == NULL) {
return;
}
peer_b = peer_bio->ptr;
assert(peer_b != NULL);
assert(peer_b->peer == bio);
peer_b->peer = NULL;
peer_bio->init = 0;
assert(peer_b->buf != NULL);
peer_b->len = 0;
peer_b->offset = 0;
b->peer = NULL;
bio->init = 0;
assert(b->buf != NULL);
b->len = 0;
b->offset = 0;
}
static int bio_free(BIO *bio) {
struct bio_bio_st *b = bio->ptr;
assert(b != NULL);
if (b->peer) {
bio_destroy_pair(bio);
}
OPENSSL_free(b->buf);
OPENSSL_free(b);
return 1;
}
static int bio_read(BIO *bio, char *buf, int size_) {
size_t size = size_;
size_t rest;
struct bio_bio_st *b, *peer_b;
BIO_clear_retry_flags(bio);
if (!bio->init) {
return 0;
}
b = bio->ptr;
assert(b != NULL);
assert(b->peer != NULL);
peer_b = b->peer->ptr;
assert(peer_b != NULL);
assert(peer_b->buf != NULL);
peer_b->request = 0; // will be set in "retry_read" situation
if (buf == NULL || size == 0) {
return 0;
}
if (peer_b->len == 0) {
if (peer_b->closed) {
return 0; // writer has closed, and no data is left
} else {
BIO_set_retry_read(bio); // buffer is empty
if (size <= peer_b->size) {
peer_b->request = size;
} else {
// don't ask for more than the peer can
// deliver in one write
peer_b->request = peer_b->size;
}
return -1;
}
}
// we can read
if (peer_b->len < size) {
size = peer_b->len;
}
// now read "size" bytes
rest = size;
assert(rest > 0);
// one or two iterations
do {
size_t chunk;
assert(rest <= peer_b->len);
if (peer_b->offset + rest <= peer_b->size) {
chunk = rest;
} else {
// wrap around ring buffer
chunk = peer_b->size - peer_b->offset;
}
assert(peer_b->offset + chunk <= peer_b->size);
OPENSSL_memcpy(buf, peer_b->buf + peer_b->offset, chunk);
peer_b->len -= chunk;
if (peer_b->len) {
peer_b->offset += chunk;
assert(peer_b->offset <= peer_b->size);
if (peer_b->offset == peer_b->size) {
peer_b->offset = 0;
}
buf += chunk;
} else {
// buffer now empty, no need to advance "buf"
assert(chunk == rest);
peer_b->offset = 0;
}
rest -= chunk;
} while (rest);
// |size| is bounded by the buffer size, which fits in |int|.
return (int)size;
}
static int bio_write(BIO *bio, const char *buf, int num_) {
size_t num = num_;
size_t rest;
struct bio_bio_st *b;
BIO_clear_retry_flags(bio);
if (!bio->init || buf == NULL || num == 0) {
return 0;
}
b = bio->ptr;
assert(b != NULL);
assert(b->peer != NULL);
assert(b->buf != NULL);
b->request = 0;
if (b->closed) {
// we already closed
OPENSSL_PUT_ERROR(BIO, BIO_R_BROKEN_PIPE);
return -1;
}
assert(b->len <= b->size);
if (b->len == b->size) {
BIO_set_retry_write(bio); // buffer is full
return -1;
}
// we can write
if (num > b->size - b->len) {
num = b->size - b->len;
}
// now write "num" bytes
rest = num;
assert(rest > 0);
// one or two iterations
do {
size_t write_offset;
size_t chunk;
assert(b->len + rest <= b->size);
write_offset = b->offset + b->len;
if (write_offset >= b->size) {
write_offset -= b->size;
}
// b->buf[write_offset] is the first byte we can write to.
if (write_offset + rest <= b->size) {
chunk = rest;
} else {
// wrap around ring buffer
chunk = b->size - write_offset;
}
OPENSSL_memcpy(b->buf + write_offset, buf, chunk);
b->len += chunk;
assert(b->len <= b->size);
rest -= chunk;
buf += chunk;
} while (rest);
// |num| is bounded by the buffer size, which fits in |int|.
return (int)num;
}
static int bio_make_pair(BIO *bio1, BIO *bio2, size_t writebuf1_len,
size_t writebuf2_len) {
struct bio_bio_st *b1, *b2;
assert(bio1 != NULL);
assert(bio2 != NULL);
b1 = bio1->ptr;
b2 = bio2->ptr;
if (b1->peer != NULL || b2->peer != NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_IN_USE);
return 0;
}
if (b1->buf == NULL) {
if (writebuf1_len) {
b1->size = writebuf1_len;
}
b1->buf = OPENSSL_malloc(b1->size);
if (b1->buf == NULL) {
return 0;
}
b1->len = 0;
b1->offset = 0;
}
if (b2->buf == NULL) {
if (writebuf2_len) {
b2->size = writebuf2_len;
}
b2->buf = OPENSSL_malloc(b2->size);
if (b2->buf == NULL) {
return 0;
}
b2->len = 0;
b2->offset = 0;
}
b1->peer = bio2;
b1->closed = 0;
b1->request = 0;
b2->peer = bio1;
b2->closed = 0;
b2->request = 0;
bio1->init = 1;
bio2->init = 1;
return 1;
}
static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr) {
long ret;
struct bio_bio_st *b = bio->ptr;
assert(b != NULL);
switch (cmd) {
// specific CTRL codes
case BIO_C_GET_WRITE_BUF_SIZE:
ret = (long)b->size;
break;
case BIO_C_GET_WRITE_GUARANTEE:
// How many bytes can the caller feed to the next write
// without having to keep any?
if (b->peer == NULL || b->closed) {
ret = 0;
} else {
ret = (long)b->size - b->len;
}
break;
case BIO_C_GET_READ_REQUEST:
// If the peer unsuccessfully tried to read, how many bytes
// were requested? (As with BIO_CTRL_PENDING, that number
// can usually be treated as boolean.)
ret = (long)b->request;
break;
case BIO_C_RESET_READ_REQUEST:
// Reset request. (Can be useful after read attempts
// at the other side that are meant to be non-blocking,
// e.g. when probing SSL_read to see if any data is
// available.)
b->request = 0;
ret = 1;
break;
case BIO_C_SHUTDOWN_WR:
// similar to shutdown(..., SHUT_WR)
b->closed = 1;
ret = 1;
break;
// standard CTRL codes follow
case BIO_CTRL_GET_CLOSE:
ret = bio->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
bio->shutdown = (int)num;
ret = 1;
break;
case BIO_CTRL_PENDING:
if (b->peer != NULL) {
struct bio_bio_st *peer_b = b->peer->ptr;
ret = (long)peer_b->len;
} else {
ret = 0;
}
break;
case BIO_CTRL_WPENDING:
ret = 0;
if (b->buf != NULL) {
ret = (long)b->len;
}
break;
case BIO_CTRL_FLUSH:
ret = 1;
break;
case BIO_CTRL_EOF: {
ret = 1;
if (b->peer) {
struct bio_bio_st *peer_b = b->peer->ptr;
assert(peer_b != NULL);
ret = peer_b->len == 0 && peer_b->closed;
}
} break;
default:
ret = 0;
}
return ret;
}
static const BIO_METHOD methods_biop = {
BIO_TYPE_BIO, "BIO pair", bio_write, bio_read, NULL /* puts */,
NULL /* gets */, bio_ctrl, bio_new, bio_free, NULL /* callback_ctrl */,
};
static const BIO_METHOD *bio_s_bio(void) { return &methods_biop; }
int BIO_new_bio_pair(BIO** bio1_p, size_t writebuf1_len,
BIO** bio2_p, size_t writebuf2_len) {
BIO *bio1 = BIO_new(bio_s_bio());
BIO *bio2 = BIO_new(bio_s_bio());
if (bio1 == NULL || bio2 == NULL ||
!bio_make_pair(bio1, bio2, writebuf1_len, writebuf2_len)) {
BIO_free(bio1);
BIO_free(bio2);
*bio1_p = NULL;
*bio2_p = NULL;
return 0;
}
*bio1_p = bio1;
*bio2_p = bio2;
return 1;
}
int BIO_destroy_bio_pair(BIO *b) {
if (b == NULL) {
return 0;
}
bio_destroy_pair(b);
return 1;
}
size_t BIO_ctrl_get_read_request(BIO *bio) {
return BIO_ctrl(bio, BIO_C_GET_READ_REQUEST, 0, NULL);
}
size_t BIO_ctrl_get_write_guarantee(BIO *bio) {
return BIO_ctrl(bio, BIO_C_GET_WRITE_GUARANTEE, 0, NULL);
}
int BIO_shutdown_wr(BIO *bio) {
return (int)BIO_ctrl(bio, BIO_C_SHUTDOWN_WR, 0, NULL);
}

View File

@@ -0,0 +1,48 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <openssl/err.h>
#include <openssl/mem.h>
int BIO_printf(BIO *bio, const char *format, ...) {
va_list args;
char buf[256], *out, out_malloced = 0;
int out_len, ret;
va_start(args, format);
out_len = vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
if (out_len < 0) {
return -1;
}
if ((size_t) out_len >= sizeof(buf)) {
const size_t requested_len = (size_t)out_len;
// The output was truncated. Note that vsnprintf's return value does not
// include a trailing NUL, but the buffer must be sized for it.
out = OPENSSL_malloc(requested_len + 1);
out_malloced = 1;
if (out == NULL) {
return -1;
}
va_start(args, format);
out_len = vsnprintf(out, requested_len + 1, format, args);
va_end(args);
assert(out_len == (int)requested_len);
} else {
out = buf;
}
ret = BIO_write(bio, out, out_len);
if (out_malloced) {
OPENSSL_free(out);
}
return ret;
}

View File

@@ -0,0 +1,134 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/bio.h>
#if !defined(OPENSSL_NO_SOCK)
#include <fcntl.h>
#include <string.h>
#if !defined(OPENSSL_WINDOWS)
#include <unistd.h>
#else
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <winsock2.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
#endif
#include "internal.h"
#if !defined(OPENSSL_WINDOWS)
static int closesocket(int sock) {
return close(sock);
}
#endif
static int sock_free(BIO *bio) {
if (bio->shutdown) {
if (bio->init) {
closesocket(bio->num);
}
bio->init = 0;
bio->flags = 0;
}
return 1;
}
static int sock_read(BIO *b, char *out, int outl) {
if (out == NULL) {
return 0;
}
bio_clear_socket_error(b->num);
#if defined(OPENSSL_WINDOWS)
int ret = recv(b->num, out, outl, 0);
#else
int ret = (int)read(b->num, out, outl);
#endif
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_read(b);
}
}
return ret;
}
static int sock_write(BIO *b, const char *in, int inl) {
bio_clear_socket_error(b->num);
#if defined(OPENSSL_WINDOWS)
int ret = send(b->num, in, inl, 0);
#else
int ret = (int)write(b->num, in, inl);
#endif
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_write(b);
}
}
return ret;
}
static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) {
long ret = 1;
int *ip;
switch (cmd) {
case BIO_C_SET_FD:
sock_free(b);
b->num = *((int *)ptr);
b->shutdown = (int)num;
b->init = 1;
break;
case BIO_C_GET_FD:
if (b->init) {
ip = (int *)ptr;
if (ip != NULL) {
*ip = b->num;
}
ret = b->num;
} else {
ret = -1;
}
break;
case BIO_CTRL_GET_CLOSE:
ret = b->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
b->shutdown = (int)num;
break;
case BIO_CTRL_FLUSH:
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
static const BIO_METHOD methods_sockp = {
BIO_TYPE_SOCKET, "socket",
sock_write, sock_read,
NULL /* puts */, NULL /* gets, */,
sock_ctrl, NULL /* create */,
sock_free, NULL /* callback_ctrl */,
};
const BIO_METHOD *BIO_s_socket(void) { return &methods_sockp; }
BIO *BIO_new_socket(int fd, int close_flag) {
BIO *ret;
ret = BIO_new(BIO_s_socket());
if (ret == NULL) {
return NULL;
}
BIO_set_fd(ret, fd, close_flag);
return ret;
}
#endif // OPENSSL_NO_SOCK

View File

@@ -0,0 +1,125 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#if defined(__linux__)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <openssl/bio.h>
#include <openssl/err.h>
#if !defined(OPENSSL_NO_SOCK)
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#if !defined(OPENSSL_WINDOWS)
#include <netdb.h>
#include <unistd.h>
#else
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <winsock2.h>
#include <ws2tcpip.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
#endif
#include "internal.h"
#include "../internal.h"
int bio_ip_and_port_to_socket_and_addr(int *out_sock,
struct sockaddr_storage *out_addr,
socklen_t *out_addr_length,
const char *hostname,
const char *port_str) {
struct addrinfo hint, *result, *cur;
int ret;
*out_sock = -1;
OPENSSL_cleanse(&hint,sizeof(hint));
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
ret = getaddrinfo(hostname, port_str, &hint, &result);
if (ret != 0) {
OPENSSL_PUT_ERROR(SYS, 0);
#if defined(OPENSSL_WINDOWS)
ERR_add_error_data(1, gai_strerrorA(ret));
#else
ERR_add_error_data(1, gai_strerror(ret));
#endif
return 0;
}
ret = 0;
for (cur = result; cur; cur = cur->ai_next) {
if ((size_t) cur->ai_addrlen > sizeof(struct sockaddr_storage)) {
continue;
}
OPENSSL_cleanse(out_addr, sizeof(struct sockaddr_storage));
OPENSSL_memcpy(out_addr, cur->ai_addr, cur->ai_addrlen);
*out_addr_length = cur->ai_addrlen;
*out_sock = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
if (*out_sock < 0) {
OPENSSL_PUT_SYSTEM_ERROR();
goto out;
}
ret = 1;
break;
}
out:
freeaddrinfo(result);
return ret;
}
int bio_socket_nbio(int sock, int on) {
#if defined(OPENSSL_WINDOWS)
u_long arg = on;
return 0 == ioctlsocket(sock, FIONBIO, &arg);
#else
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {
return 0;
}
if (!on) {
flags &= ~O_NONBLOCK;
} else {
flags |= O_NONBLOCK;
}
return fcntl(sock, F_SETFL, flags) == 0;
#endif
}
void bio_clear_socket_error(int sock) {
bio_sock_error_get_and_clear(sock);
}
int bio_sock_error_get_and_clear(int sock) {
int error;
socklen_t error_size = sizeof(error);
// Get and clear the pending socket error. The SO_ERROR option is read-only.
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &error_size) < 0) {
return 1;
}
return error;
}
int bio_socket_should_retry(int return_value) {
#if defined(OPENSSL_WINDOWS)
return return_value == -1 && (WSAGetLastError() == WSAEWOULDBLOCK);
#else
// On POSIX platforms, sockets and fds are the same.
return bio_errno_should_retry(return_value);
#endif
}
#endif // OPENSSL_NO_SOCK