mirror of
https://github.com/openssl/openssl.git
synced 2024-12-09 05:51:54 +08:00
cd715b7e7f
TLS device offload allows to perform zerocopy sendfile transmissions. FreeBSD provides this feature by default, and Linux 5.19 introduced it as an opt-in. Zerocopy improves the TX rate significantly, but has a side effect: if the underlying file is changed while being transmitted, and a TCP retransmission happens, the receiver may get a TLS record containing both new and old data, which leads to an authentication failure and termination of connection. This effect is the reason Linux makes a copy on sendfile by default. This commit adds support for TLS zerocopy sendfile on Linux disabled by default to avoid any unlikely backward compatibility issues on Linux, although sacrificing consistency in OpenSSL's behavior on Linux and FreeBSD. A new option called KTLSTxZerocopySendfile is added to enable the new zerocopy behavior on Linux. This option should be used when the the application guarantees that the file is not modified during transmission, or it doesn't care about breaking the connection. The related documentation is also added in this commit. The unit test added doesn't test the actual functionality (it would require specific hardware and a non-local peer), but solely checks that it's possible to set the new option flag. Signed-off-by: Maxim Mikityanskiy <maximmi@nvidia.com> Reviewed-by: Tariq Toukan <tariqt@nvidia.com> Reviewed-by: Boris Pismenny <borisp@nvidia.com> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Todd Short <todd.short@me.com> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/18650)
707 lines
20 KiB
C
707 lines
20 KiB
C
/*
|
|
* Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
|
* this file except in compliance with the License. You can obtain a copy
|
|
* in the file LICENSE in the source distribution or at
|
|
* https://www.openssl.org/source/license.html
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include "bio_local.h"
|
|
#include "internal/bio_tfo.h"
|
|
#include "internal/ktls.h"
|
|
|
|
#ifndef OPENSSL_NO_SOCK
|
|
|
|
typedef struct bio_connect_st {
|
|
int state;
|
|
int connect_family;
|
|
char *param_hostname;
|
|
char *param_service;
|
|
int connect_mode;
|
|
# ifndef OPENSSL_NO_KTLS
|
|
unsigned char record_type;
|
|
# endif
|
|
int tfo_first;
|
|
|
|
BIO_ADDRINFO *addr_first;
|
|
const BIO_ADDRINFO *addr_iter;
|
|
/*
|
|
* int socket; this will be kept in bio->num so that it is compatible
|
|
* with the bss_sock bio
|
|
*/
|
|
/*
|
|
* 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;
|
|
|
|
static int conn_write(BIO *h, const char *buf, int num);
|
|
static int conn_read(BIO *h, char *buf, int size);
|
|
static int conn_puts(BIO *h, const char *str);
|
|
static int conn_gets(BIO *h, char *buf, int size);
|
|
static long conn_ctrl(BIO *h, int cmd, long arg1, void *arg2);
|
|
static int conn_new(BIO *h);
|
|
static int conn_free(BIO *data);
|
|
static long conn_callback_ctrl(BIO *h, int cmd, BIO_info_cb *);
|
|
|
|
static int conn_state(BIO *b, BIO_CONNECT *c);
|
|
static void conn_close_socket(BIO *data);
|
|
BIO_CONNECT *BIO_CONNECT_new(void);
|
|
void BIO_CONNECT_free(BIO_CONNECT *a);
|
|
|
|
#define BIO_CONN_S_BEFORE 1
|
|
#define BIO_CONN_S_GET_ADDR 2
|
|
#define BIO_CONN_S_CREATE_SOCKET 3
|
|
#define BIO_CONN_S_CONNECT 4
|
|
#define BIO_CONN_S_OK 5
|
|
#define BIO_CONN_S_BLOCKED_CONNECT 6
|
|
#define BIO_CONN_S_CONNECT_ERROR 7
|
|
|
|
static const BIO_METHOD methods_connectp = {
|
|
BIO_TYPE_CONNECT,
|
|
"socket connect",
|
|
bwrite_conv,
|
|
conn_write,
|
|
bread_conv,
|
|
conn_read,
|
|
conn_puts,
|
|
conn_gets,
|
|
conn_ctrl,
|
|
conn_new,
|
|
conn_free,
|
|
conn_callback_ctrl,
|
|
};
|
|
|
|
static int conn_state(BIO *b, 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 (c->param_hostname == NULL && c->param_service == NULL) {
|
|
ERR_raise_data(ERR_LIB_BIO,
|
|
BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED,
|
|
"hostname=%s service=%s",
|
|
c->param_hostname, c->param_service);
|
|
goto exit_loop;
|
|
}
|
|
c->state = BIO_CONN_S_GET_ADDR;
|
|
break;
|
|
|
|
case BIO_CONN_S_GET_ADDR:
|
|
{
|
|
int family = AF_UNSPEC;
|
|
switch (c->connect_family) {
|
|
case BIO_FAMILY_IPV6:
|
|
if (1) { /* This is a trick we use to avoid bit rot.
|
|
* at least the "else" part will always be
|
|
* compiled.
|
|
*/
|
|
#if OPENSSL_USE_IPV6
|
|
family = AF_INET6;
|
|
} else {
|
|
#endif
|
|
ERR_raise(ERR_LIB_BIO, BIO_R_UNAVAILABLE_IP_FAMILY);
|
|
goto exit_loop;
|
|
}
|
|
break;
|
|
case BIO_FAMILY_IPV4:
|
|
family = AF_INET;
|
|
break;
|
|
case BIO_FAMILY_IPANY:
|
|
family = AF_UNSPEC;
|
|
break;
|
|
default:
|
|
ERR_raise(ERR_LIB_BIO, BIO_R_UNSUPPORTED_IP_FAMILY);
|
|
goto exit_loop;
|
|
}
|
|
if (BIO_lookup(c->param_hostname, c->param_service,
|
|
BIO_LOOKUP_CLIENT,
|
|
family, SOCK_STREAM, &c->addr_first) == 0)
|
|
goto exit_loop;
|
|
}
|
|
if (c->addr_first == NULL) {
|
|
ERR_raise(ERR_LIB_BIO, BIO_R_LOOKUP_RETURNED_NOTHING);
|
|
goto exit_loop;
|
|
}
|
|
c->addr_iter = c->addr_first;
|
|
c->state = BIO_CONN_S_CREATE_SOCKET;
|
|
break;
|
|
|
|
case BIO_CONN_S_CREATE_SOCKET:
|
|
ret = BIO_socket(BIO_ADDRINFO_family(c->addr_iter),
|
|
BIO_ADDRINFO_socktype(c->addr_iter),
|
|
BIO_ADDRINFO_protocol(c->addr_iter), 0);
|
|
if (ret == (int)INVALID_SOCKET) {
|
|
ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
|
|
"calling socket(%s, %s)",
|
|
c->param_hostname, c->param_service);
|
|
ERR_raise(ERR_LIB_BIO, BIO_R_UNABLE_TO_CREATE_SOCKET);
|
|
goto exit_loop;
|
|
}
|
|
b->num = ret;
|
|
c->state = BIO_CONN_S_CONNECT;
|
|
break;
|
|
|
|
case BIO_CONN_S_CONNECT:
|
|
BIO_clear_retry_flags(b);
|
|
ERR_set_mark();
|
|
ret = BIO_connect(b->num, BIO_ADDRINFO_address(c->addr_iter),
|
|
BIO_SOCK_KEEPALIVE | c->connect_mode);
|
|
b->retry_reason = 0;
|
|
if (ret == 0) {
|
|
if (BIO_sock_should_retry(ret)) {
|
|
BIO_set_retry_special(b);
|
|
c->state = BIO_CONN_S_BLOCKED_CONNECT;
|
|
b->retry_reason = BIO_RR_CONNECT;
|
|
ERR_pop_to_mark();
|
|
} else if ((c->addr_iter = BIO_ADDRINFO_next(c->addr_iter))
|
|
!= NULL) {
|
|
/*
|
|
* if there are more addresses to try, do that first
|
|
*/
|
|
BIO_closesocket(b->num);
|
|
c->state = BIO_CONN_S_CREATE_SOCKET;
|
|
ERR_pop_to_mark();
|
|
break;
|
|
} else {
|
|
ERR_clear_last_mark();
|
|
ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
|
|
"calling connect(%s, %s)",
|
|
c->param_hostname, c->param_service);
|
|
c->state = BIO_CONN_S_CONNECT_ERROR;
|
|
break;
|
|
}
|
|
goto exit_loop;
|
|
} else {
|
|
ERR_clear_last_mark();
|
|
c->state = BIO_CONN_S_OK;
|
|
}
|
|
break;
|
|
|
|
case BIO_CONN_S_BLOCKED_CONNECT:
|
|
/* wait for socket being writable, before querying BIO_sock_error */
|
|
if (BIO_socket_wait(b->num, 0, time(NULL)) == 0)
|
|
break;
|
|
i = BIO_sock_error(b->num);
|
|
if (i != 0) {
|
|
BIO_clear_retry_flags(b);
|
|
if ((c->addr_iter = BIO_ADDRINFO_next(c->addr_iter)) != NULL) {
|
|
/*
|
|
* if there are more addresses to try, do that first
|
|
*/
|
|
BIO_closesocket(b->num);
|
|
c->state = BIO_CONN_S_CREATE_SOCKET;
|
|
break;
|
|
}
|
|
ERR_raise_data(ERR_LIB_SYS, i,
|
|
"calling connect(%s, %s)",
|
|
c->param_hostname, c->param_service);
|
|
ERR_raise(ERR_LIB_BIO, BIO_R_NBIO_CONNECT_ERROR);
|
|
ret = 0;
|
|
goto exit_loop;
|
|
} else {
|
|
c->state = BIO_CONN_S_OK;
|
|
# ifndef OPENSSL_NO_KTLS
|
|
/*
|
|
* The new socket is created successfully regardless of ktls_enable.
|
|
* ktls_enable doesn't change any functionality of the socket, except
|
|
* changing the setsockopt to enable the processing of ktls_start.
|
|
* Thus, it is not a problem to call it for non-TLS sockets.
|
|
*/
|
|
ktls_enable(b->num);
|
|
# endif
|
|
}
|
|
break;
|
|
|
|
case BIO_CONN_S_CONNECT_ERROR:
|
|
ERR_raise(ERR_LIB_BIO, BIO_R_CONNECT_ERROR);
|
|
ret = 0;
|
|
goto exit_loop;
|
|
|
|
case BIO_CONN_S_OK:
|
|
ret = 1;
|
|
goto exit_loop;
|
|
default:
|
|
/* abort(); */
|
|
goto exit_loop;
|
|
}
|
|
|
|
if (cb != NULL) {
|
|
if ((ret = cb((BIO *)b, c->state, ret)) == 0)
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* Loop does not exit */
|
|
exit_loop:
|
|
if (cb != NULL)
|
|
ret = cb((BIO *)b, c->state, ret);
|
|
end:
|
|
return ret;
|
|
}
|
|
|
|
BIO_CONNECT *BIO_CONNECT_new(void)
|
|
{
|
|
BIO_CONNECT *ret;
|
|
|
|
if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
|
|
return NULL;
|
|
ret->state = BIO_CONN_S_BEFORE;
|
|
ret->connect_family = BIO_FAMILY_IPANY;
|
|
return ret;
|
|
}
|
|
|
|
void BIO_CONNECT_free(BIO_CONNECT *a)
|
|
{
|
|
if (a == NULL)
|
|
return;
|
|
OPENSSL_free(a->param_hostname);
|
|
OPENSSL_free(a->param_service);
|
|
BIO_ADDRINFO_free(a->addr_first);
|
|
OPENSSL_free(a);
|
|
}
|
|
|
|
const BIO_METHOD *BIO_s_connect(void)
|
|
{
|
|
return &methods_connectp;
|
|
}
|
|
|
|
static int conn_new(BIO *bi)
|
|
{
|
|
bi->init = 0;
|
|
bi->num = (int)INVALID_SOCKET;
|
|
bi->flags = 0;
|
|
if ((bi->ptr = (char *)BIO_CONNECT_new()) == NULL)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
static void conn_close_socket(BIO *bio)
|
|
{
|
|
BIO_CONNECT *c;
|
|
|
|
c = (BIO_CONNECT *)bio->ptr;
|
|
if (bio->num != (int)INVALID_SOCKET) {
|
|
/* Only do a shutdown if things were established */
|
|
if (c->state == BIO_CONN_S_OK)
|
|
shutdown(bio->num, 2);
|
|
BIO_closesocket(bio->num);
|
|
bio->num = (int)INVALID_SOCKET;
|
|
}
|
|
}
|
|
|
|
static int conn_free(BIO *a)
|
|
{
|
|
BIO_CONNECT *data;
|
|
|
|
if (a == NULL)
|
|
return 0;
|
|
data = (BIO_CONNECT *)a->ptr;
|
|
|
|
if (a->shutdown) {
|
|
conn_close_socket(a);
|
|
BIO_CONNECT_free(data);
|
|
a->ptr = NULL;
|
|
a->flags = 0;
|
|
a->init = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int conn_read(BIO *b, char *out, int outl)
|
|
{
|
|
int ret = 0;
|
|
BIO_CONNECT *data;
|
|
|
|
data = (BIO_CONNECT *)b->ptr;
|
|
if (data->state != BIO_CONN_S_OK) {
|
|
ret = conn_state(b, data);
|
|
if (ret <= 0)
|
|
return ret;
|
|
}
|
|
|
|
if (out != NULL) {
|
|
clear_socket_error();
|
|
# ifndef OPENSSL_NO_KTLS
|
|
if (BIO_get_ktls_recv(b))
|
|
ret = ktls_read_record(b->num, out, outl);
|
|
else
|
|
# endif
|
|
ret = readsocket(b->num, out, outl);
|
|
BIO_clear_retry_flags(b);
|
|
if (ret <= 0) {
|
|
if (BIO_sock_should_retry(ret))
|
|
BIO_set_retry_read(b);
|
|
else if (ret == 0)
|
|
b->flags |= BIO_FLAGS_IN_EOF;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int conn_write(BIO *b, const char *in, int inl)
|
|
{
|
|
int ret;
|
|
BIO_CONNECT *data;
|
|
|
|
data = (BIO_CONNECT *)b->ptr;
|
|
if (data->state != BIO_CONN_S_OK) {
|
|
ret = conn_state(b, data);
|
|
if (ret <= 0)
|
|
return ret;
|
|
}
|
|
|
|
clear_socket_error();
|
|
# ifndef OPENSSL_NO_KTLS
|
|
if (BIO_should_ktls_ctrl_msg_flag(b)) {
|
|
ret = ktls_send_ctrl_message(b->num, data->record_type, in, inl);
|
|
if (ret >= 0) {
|
|
ret = inl;
|
|
BIO_clear_ktls_ctrl_msg_flag(b);
|
|
}
|
|
} else
|
|
# endif
|
|
# if defined(OSSL_TFO_SENDTO)
|
|
if (data->tfo_first) {
|
|
int peerlen = BIO_ADDRINFO_sockaddr_size(data->addr_iter);
|
|
|
|
ret = sendto(b->num, in, inl, OSSL_TFO_SENDTO,
|
|
BIO_ADDRINFO_sockaddr(data->addr_iter), peerlen);
|
|
data->tfo_first = 0;
|
|
} else
|
|
# endif
|
|
ret = writesocket(b->num, in, inl);
|
|
BIO_clear_retry_flags(b);
|
|
if (ret <= 0) {
|
|
if (BIO_sock_should_retry(ret))
|
|
BIO_set_retry_write(b);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
|
|
{
|
|
BIO *dbio;
|
|
int *ip;
|
|
const char **pptr = NULL;
|
|
long ret = 1;
|
|
BIO_CONNECT *data;
|
|
# ifndef OPENSSL_NO_KTLS
|
|
ktls_crypto_info_t *crypto_info;
|
|
# endif
|
|
|
|
data = (BIO_CONNECT *)b->ptr;
|
|
|
|
switch (cmd) {
|
|
case BIO_CTRL_RESET:
|
|
ret = 0;
|
|
data->state = BIO_CONN_S_BEFORE;
|
|
conn_close_socket(b);
|
|
BIO_ADDRINFO_free(data->addr_first);
|
|
data->addr_first = NULL;
|
|
b->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(b, data);
|
|
else
|
|
ret = 1;
|
|
break;
|
|
case BIO_C_GET_CONNECT:
|
|
if (ptr != NULL) {
|
|
pptr = (const char **)ptr;
|
|
if (num == 0) {
|
|
*pptr = data->param_hostname;
|
|
} else if (num == 1) {
|
|
*pptr = data->param_service;
|
|
} else if (num == 2) {
|
|
*pptr = (const char *)BIO_ADDRINFO_address(data->addr_iter);
|
|
} else if (num == 3) {
|
|
switch (BIO_ADDRINFO_family(data->addr_iter)) {
|
|
# if OPENSSL_USE_IPV6
|
|
case AF_INET6:
|
|
ret = BIO_FAMILY_IPV6;
|
|
break;
|
|
# endif
|
|
case AF_INET:
|
|
ret = BIO_FAMILY_IPV4;
|
|
break;
|
|
case 0:
|
|
ret = data->connect_family;
|
|
break;
|
|
default:
|
|
ret = -1;
|
|
break;
|
|
}
|
|
} else if (num == 4) {
|
|
ret = data->connect_mode;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
break;
|
|
case BIO_C_SET_CONNECT:
|
|
if (ptr != NULL) {
|
|
b->init = 1;
|
|
if (num == 0) { /* BIO_set_conn_hostname */
|
|
char *hold_service = data->param_service;
|
|
/* We affect the hostname regardless. However, the input
|
|
* string might contain a host:service spec, so we must
|
|
* parse it, which might or might not affect the service
|
|
*/
|
|
|
|
OPENSSL_free(data->param_hostname);
|
|
data->param_hostname = NULL;
|
|
ret = BIO_parse_hostserv(ptr,
|
|
&data->param_hostname,
|
|
&data->param_service,
|
|
BIO_PARSE_PRIO_HOST);
|
|
if (hold_service != data->param_service)
|
|
OPENSSL_free(hold_service);
|
|
} else if (num == 1) { /* BIO_set_conn_port */
|
|
OPENSSL_free(data->param_service);
|
|
if ((data->param_service = OPENSSL_strdup(ptr)) == NULL)
|
|
ret = 0;
|
|
} else if (num == 2) { /* BIO_set_conn_address */
|
|
const BIO_ADDR *addr = (const BIO_ADDR *)ptr;
|
|
char *host = BIO_ADDR_hostname_string(addr, 1);
|
|
char *service = BIO_ADDR_service_string(addr, 1);
|
|
|
|
ret = host != NULL && service != NULL;
|
|
if (ret) {
|
|
OPENSSL_free(data->param_hostname);
|
|
data->param_hostname = host;
|
|
OPENSSL_free(data->param_service);
|
|
data->param_service = service;
|
|
BIO_ADDRINFO_free(data->addr_first);
|
|
data->addr_first = NULL;
|
|
data->addr_iter = NULL;
|
|
} else {
|
|
OPENSSL_free(host);
|
|
OPENSSL_free(service);
|
|
}
|
|
} else if (num == 3) { /* BIO_set_conn_ip_family */
|
|
data->connect_family = *(int *)ptr;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
}
|
|
break;
|
|
case BIO_C_SET_NBIO:
|
|
if (num != 0)
|
|
data->connect_mode |= BIO_SOCK_NONBLOCK;
|
|
else
|
|
data->connect_mode &= ~BIO_SOCK_NONBLOCK;
|
|
break;
|
|
#if defined(TCP_FASTOPEN) && !defined(OPENSSL_NO_TFO)
|
|
case BIO_C_SET_TFO:
|
|
if (num != 0) {
|
|
data->connect_mode |= BIO_SOCK_TFO;
|
|
data->tfo_first = 1;
|
|
} else {
|
|
data->connect_mode &= ~BIO_SOCK_TFO;
|
|
data->tfo_first = 0;
|
|
}
|
|
break;
|
|
#endif
|
|
case BIO_C_SET_CONNECT_MODE:
|
|
data->connect_mode = (int)num;
|
|
if (num & BIO_SOCK_TFO)
|
|
data->tfo_first = 1;
|
|
else
|
|
data->tfo_first = 0;
|
|
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_PENDING:
|
|
case BIO_CTRL_WPENDING:
|
|
ret = 0;
|
|
break;
|
|
case BIO_CTRL_FLUSH:
|
|
break;
|
|
case BIO_CTRL_DUP:
|
|
{
|
|
dbio = (BIO *)ptr;
|
|
if (data->param_hostname)
|
|
BIO_set_conn_hostname(dbio, data->param_hostname);
|
|
if (data->param_service)
|
|
BIO_set_conn_port(dbio, data->param_service);
|
|
BIO_set_conn_ip_family(dbio, data->connect_family);
|
|
BIO_set_conn_mode(dbio, data->connect_mode);
|
|
/*
|
|
* FIXME: the cast of the function seems unlikely to be a good
|
|
* idea
|
|
*/
|
|
(void)BIO_set_info_callback(dbio, data->info_callback);
|
|
}
|
|
break;
|
|
case BIO_CTRL_SET_CALLBACK:
|
|
ret = 0; /* use callback ctrl */
|
|
break;
|
|
case BIO_CTRL_GET_CALLBACK:
|
|
{
|
|
BIO_info_cb **fptr;
|
|
|
|
fptr = (BIO_info_cb **)ptr;
|
|
*fptr = data->info_callback;
|
|
}
|
|
break;
|
|
case BIO_CTRL_EOF:
|
|
ret = (b->flags & BIO_FLAGS_IN_EOF) != 0;
|
|
break;
|
|
# ifndef OPENSSL_NO_KTLS
|
|
case BIO_CTRL_SET_KTLS:
|
|
crypto_info = (ktls_crypto_info_t *)ptr;
|
|
ret = ktls_start(b->num, crypto_info, num);
|
|
if (ret)
|
|
BIO_set_ktls_flag(b, num);
|
|
break;
|
|
case BIO_CTRL_GET_KTLS_SEND:
|
|
return BIO_should_ktls_flag(b, 1) != 0;
|
|
case BIO_CTRL_GET_KTLS_RECV:
|
|
return BIO_should_ktls_flag(b, 0) != 0;
|
|
case BIO_CTRL_SET_KTLS_TX_SEND_CTRL_MSG:
|
|
BIO_set_ktls_ctrl_msg_flag(b);
|
|
data->record_type = num;
|
|
ret = 0;
|
|
break;
|
|
case BIO_CTRL_CLEAR_KTLS_TX_CTRL_MSG:
|
|
BIO_clear_ktls_ctrl_msg_flag(b);
|
|
ret = 0;
|
|
break;
|
|
case BIO_CTRL_SET_KTLS_TX_ZEROCOPY_SENDFILE:
|
|
ret = ktls_enable_tx_zerocopy_sendfile(b->num);
|
|
if (ret)
|
|
BIO_set_ktls_zerocopy_sendfile_flag(b);
|
|
break;
|
|
# endif
|
|
default:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static long conn_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
|
|
{
|
|
long ret = 1;
|
|
BIO_CONNECT *data;
|
|
|
|
data = (BIO_CONNECT *)b->ptr;
|
|
|
|
switch (cmd) {
|
|
case BIO_CTRL_SET_CALLBACK:
|
|
{
|
|
data->info_callback = fp;
|
|
}
|
|
break;
|
|
default:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int conn_puts(BIO *bp, const char *str)
|
|
{
|
|
int n, ret;
|
|
|
|
n = strlen(str);
|
|
ret = conn_write(bp, str, n);
|
|
return ret;
|
|
}
|
|
|
|
int conn_gets(BIO *bio, char *buf, int size)
|
|
{
|
|
BIO_CONNECT *data;
|
|
char *ptr = buf;
|
|
int ret = 0;
|
|
|
|
if (buf == NULL) {
|
|
ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
|
|
return -1;
|
|
}
|
|
if (size <= 0) {
|
|
ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
|
|
return -1;
|
|
}
|
|
*buf = '\0';
|
|
|
|
if (bio == NULL || bio->ptr == NULL) {
|
|
ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
|
|
return -1;
|
|
}
|
|
data = (BIO_CONNECT *)bio->ptr;
|
|
if (data->state != BIO_CONN_S_OK) {
|
|
ret = conn_state(bio, data);
|
|
if (ret <= 0)
|
|
return ret;
|
|
}
|
|
|
|
clear_socket_error();
|
|
while (size-- > 1) {
|
|
# ifndef OPENSSL_NO_KTLS
|
|
if (BIO_get_ktls_recv(bio))
|
|
ret = ktls_read_record(bio->num, ptr, 1);
|
|
else
|
|
# endif
|
|
ret = readsocket(bio->num, ptr, 1);
|
|
BIO_clear_retry_flags(bio);
|
|
if (ret <= 0) {
|
|
if (BIO_sock_should_retry(ret))
|
|
BIO_set_retry_read(bio);
|
|
else if (ret == 0)
|
|
bio->flags |= BIO_FLAGS_IN_EOF;
|
|
break;
|
|
}
|
|
if (*ptr++ == '\n')
|
|
break;
|
|
}
|
|
*ptr = '\0';
|
|
return ret > 0 || (bio->flags & BIO_FLAGS_IN_EOF) != 0 ? ptr - buf : ret;
|
|
}
|
|
|
|
BIO *BIO_new_connect(const char *str)
|
|
{
|
|
BIO *ret;
|
|
|
|
ret = BIO_new(BIO_s_connect());
|
|
if (ret == NULL)
|
|
return NULL;
|
|
if (BIO_set_conn_hostname(ret, str))
|
|
return ret;
|
|
BIO_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|