summaryrefslogtreecommitdiffstats
path: root/crypto/bio
diff options
context:
space:
mode:
authorTodd Short <tshort@akamai.com>2021-09-08 16:23:04 -0400
committerTodd Short <todd.short@me.com>2022-03-10 10:42:43 -0500
commita3e53d56831adb60d6875297b3339a4251f735d2 (patch)
treec931c5b2cc9a63f80e4f3ae3a366b70064b897ae /crypto/bio
parent97896f744d9ee4f2e821e3383caac8e8c5f226cf (diff)
Add TFO support to socket BIO and s_client/s_server
Supports Linux, MacOS and FreeBSD Disabled by default, enabled via `enabled-tfo` Some tests Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Tim Hudson <tjh@openssl.org> (Merged from https://github.com/openssl/openssl/pull/8692)
Diffstat (limited to 'crypto/bio')
-rw-r--r--crypto/bio/bio_addr.c12
-rw-r--r--crypto/bio/bio_err.c4
-rw-r--r--crypto/bio/bio_sock2.c113
-rw-r--r--crypto/bio/bss_acpt.c4
-rw-r--r--crypto/bio/bss_conn.c28
-rw-r--r--crypto/bio/bss_sock.c61
6 files changed, 218 insertions, 4 deletions
diff --git a/crypto/bio/bio_addr.c b/crypto/bio/bio_addr.c
index 8c7139691b..5f335c14d8 100644
--- a/crypto/bio/bio_addr.c
+++ b/crypto/bio/bio_addr.c
@@ -67,6 +67,18 @@ void BIO_ADDR_free(BIO_ADDR *ap)
OPENSSL_free(ap);
}
+BIO_ADDR *BIO_ADDR_dup(const BIO_ADDR *ap)
+{
+ BIO_ADDR *ret = NULL;
+
+ if (ap != NULL) {
+ ret = BIO_ADDR_new();
+ if (ret != NULL)
+ memcpy(ret, ap, sizeof(BIO_ADDR));
+ }
+ return ret;
+}
+
void BIO_ADDR_clear(BIO_ADDR *ap)
{
memset(ap, 0, sizeof(*ap));
diff --git a/crypto/bio/bio_err.c b/crypto/bio/bio_err.c
index 7a36c61148..cbe9d30785 100644
--- a/crypto/bio/bio_err.c
+++ b/crypto/bio/bio_err.c
@@ -46,6 +46,9 @@ static const ERR_STRING_DATA BIO_str_reasons[] = {
"no hostname or service specified"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NO_PORT_DEFINED), "no port defined"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NO_SUCH_FILE), "no such file"},
+ {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_TFO_DISABLED), "tfo disabled"},
+ {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_TFO_NO_KERNEL_SUPPORT),
+ "tfo no kernel support"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_TRANSFER_ERROR), "transfer error"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_TRANSFER_TIMEOUT), "transfer timeout"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNABLE_TO_BIND_SOCKET),
@@ -59,6 +62,7 @@ static const ERR_STRING_DATA BIO_str_reasons[] = {
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNABLE_TO_NODELAY), "unable to nodelay"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNABLE_TO_REUSEADDR),
"unable to reuseaddr"},
+ {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNABLE_TO_TFO), "unable to tfo"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNAVAILABLE_IP_FAMILY),
"unavailable ip family"},
{ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNINITIALIZED), "uninitialized"},
diff --git a/crypto/bio/bio_sock2.c b/crypto/bio/bio_sock2.c
index b6c95913ce..36b071c8e1 100644
--- a/crypto/bio/bio_sock2.c
+++ b/crypto/bio/bio_sock2.c
@@ -13,6 +13,7 @@
#include "bio_local.h"
#include "internal/ktls.h"
+#include "internal/bio_tfo.h"
#include <openssl/err.h>
@@ -79,6 +80,7 @@ int BIO_socket(int domain, int socktype, int protocol, int options)
* - BIO_SOCK_KEEPALIVE: enable regularly sending keep-alive messages.
* - BIO_SOCK_NONBLOCK: Make the socket non-blocking.
* - BIO_SOCK_NODELAY: don't delay small messages.
+ * - BIO_SOCK_TFO: use TCP Fast Open
*
* options holds BIO socket options that can be used
* You should call this for every address returned by BIO_lookup
@@ -118,6 +120,68 @@ int BIO_connect(int sock, const BIO_ADDR *addr, int options)
return 0;
}
}
+ if (options & BIO_SOCK_TFO) {
+# if defined(OSSL_TFO_CLIENT_FLAG)
+# if defined(OSSL_TFO_SYSCTL_CLIENT)
+ int enabled = 0;
+ size_t enabledlen = sizeof(enabled);
+
+ /* Later FreeBSD */
+ if (sysctlbyname(OSSL_TFO_SYSCTL_CLIENT, &enabled, &enabledlen, NULL, 0) < 0) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_NO_KERNEL_SUPPORT);
+ return 0;
+ }
+ /* Need to check for client flag */
+ if (!(enabled & OSSL_TFO_CLIENT_FLAG)) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_DISABLED);
+ return 0;
+ }
+# elif defined(OSSL_TFO_SYSCTL)
+ int enabled = 0;
+ size_t enabledlen = sizeof(enabled);
+
+ /* macOS */
+ if (sysctlbyname(OSSL_TFO_SYSCTL, &enabled, &enabledlen, NULL, 0) < 0) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_NO_KERNEL_SUPPORT);
+ return 0;
+ }
+ /* Need to check for client flag */
+ if (!(enabled & OSSL_TFO_CLIENT_FLAG)) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_DISABLED);
+ return 0;
+ }
+# endif
+# endif
+# if defined(OSSL_TFO_CONNECTX)
+ sa_endpoints_t sae;
+
+ memset(&sae, 0, sizeof(sae));
+ sae.sae_dstaddr = BIO_ADDR_sockaddr(addr);
+ sae.sae_dstaddrlen = BIO_ADDR_sockaddr_size(addr);
+ if (connectx(sock, &sae, SAE_ASSOCID_ANY,
+ CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
+ NULL, 0, NULL, NULL) == -1) {
+ if (!BIO_sock_should_retry(-1)) {
+ ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
+ "calling connectx()");
+ ERR_raise(ERR_LIB_BIO, BIO_R_CONNECT_ERROR);
+ }
+ return 0;
+ }
+# endif
+# if defined(OSSL_TFO_CLIENT_SOCKOPT)
+ if (setsockopt(sock, IPPROTO_TCP, OSSL_TFO_CLIENT_SOCKOPT,
+ (const void *)&on, sizeof(on)) != 0) {
+ ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
+ "calling setsockopt()");
+ ERR_raise(ERR_LIB_BIO, BIO_R_UNABLE_TO_TFO);
+ return 0;
+ }
+# endif
+# if defined(OSSL_TFO_DO_NOT_CONNECT)
+ return 1;
+# endif
+ }
if (connect(sock, BIO_ADDR_sockaddr(addr),
BIO_ADDR_sockaddr_size(addr)) == -1) {
@@ -201,6 +265,7 @@ int BIO_bind(int sock, const BIO_ADDR *addr, int options)
* for a recently closed port.
* - BIO_SOCK_V6_ONLY: When creating an IPv6 socket, make it listen only
* for IPv6 addresses and not IPv4 addresses mapped to IPv6.
+ * - BIO_SOCK_TFO: accept TCP fast open (set TCP_FASTOPEN)
*
* It's recommended that you set up both an IPv6 and IPv4 listen socket, and
* then check both for new clients that connect to it. You want to set up
@@ -292,6 +357,54 @@ int BIO_listen(int sock, const BIO_ADDR *addr, int options)
return 0;
}
+# if defined(OSSL_TFO_SERVER_SOCKOPT)
+ /*
+ * Must do it explicitly after listen() for macOS, still
+ * works fine on other OS's
+ */
+ if ((options & BIO_SOCK_TFO) && socktype != SOCK_DGRAM) {
+ int q = OSSL_TFO_SERVER_SOCKOPT_VALUE;
+# if defined(OSSL_TFO_CLIENT_FLAG)
+# if defined(OSSL_TFO_SYSCTL_SERVER)
+ int enabled = 0;
+ size_t enabledlen = sizeof(enabled);
+
+ /* Later FreeBSD */
+ if (sysctlbyname(OSSL_TFO_SYSCTL_SERVER, &enabled, &enabledlen, NULL, 0) < 0) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_NO_KERNEL_SUPPORT);
+ return 0;
+ }
+ /* Need to check for server flag */
+ if (!(enabled & OSSL_TFO_SERVER_FLAG)) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_DISABLED);
+ return 0;
+ }
+# elif defined(OSSL_TFO_SYSCTL)
+ int enabled = 0;
+ size_t enabledlen = sizeof(enabled);
+
+ /* Early FreeBSD, macOS */
+ if (sysctlbyname(OSSL_TFO_SYSCTL, &enabled, &enabledlen, NULL, 0) < 0) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_NO_KERNEL_SUPPORT);
+ return 0;
+ }
+ /* Need to check for server flag */
+ if (!(enabled & OSSL_TFO_SERVER_FLAG)) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_TFO_DISABLED);
+ return 0;
+ }
+# endif
+# endif
+ if (setsockopt(sock, IPPROTO_TCP, OSSL_TFO_SERVER_SOCKOPT,
+ (void *)&q, sizeof(q)) < 0) {
+ ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
+ "calling setsockopt()");
+ ERR_raise(ERR_LIB_BIO, BIO_R_UNABLE_TO_TFO);
+ return 0;
+ }
+ }
+# endif
+
return 1;
}
diff --git a/crypto/bio/bss_acpt.c b/crypto/bio/bss_acpt.c
index 1cda967335..eeac7ca4e1 100644
--- a/crypto/bio/bss_acpt.c
+++ b/crypto/bio/bss_acpt.c
@@ -452,10 +452,14 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr)
data->bio_chain = (BIO *)ptr;
} else if (num == 4) {
data->accept_family = *(int *)ptr;
+ } else if (num == 5) {
+ data->bind_mode |= BIO_SOCK_TFO;
}
} else {
if (num == 2) {
data->bind_mode &= ~BIO_SOCK_NONBLOCK;
+ } else if (num == 5) {
+ data->bind_mode &= ~BIO_SOCK_TFO;
}
}
break;
diff --git a/crypto/bio/bss_conn.c b/crypto/bio/bss_conn.c
index 8bc53548ca..3c61bc91c5 100644
--- a/crypto/bio/bss_conn.c
+++ b/crypto/bio/bss_conn.c
@@ -11,6 +11,7 @@
#include <errno.h>
#include "bio_local.h"
+#include "internal/bio_tfo.h"
#include "internal/ktls.h"
#ifndef OPENSSL_NO_SOCK
@@ -24,6 +25,7 @@ typedef struct bio_connect_st {
# ifndef OPENSSL_NO_KTLS
unsigned char record_type;
# endif
+ int tfo_first;
BIO_ADDRINFO *addr_first;
const BIO_ADDRINFO *addr_iter;
@@ -361,6 +363,15 @@ static int conn_write(BIO *b, const char *in, int inl)
}
} 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) {
@@ -425,6 +436,8 @@ static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
ret = -1;
break;
}
+ } else if (num == 4) {
+ ret = data->connect_mode;
} else {
ret = 0;
}
@@ -485,8 +498,23 @@ static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
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) {
diff --git a/crypto/bio/bss_sock.c b/crypto/bio/bss_sock.c
index f5d8810230..201bc9df2b 100644
--- a/crypto/bio/bss_sock.c
+++ b/crypto/bio/bss_sock.c
@@ -10,6 +10,7 @@
#include <stdio.h>
#include <errno.h>
#include "bio_local.h"
+#include "internal/bio_tfo.h"
#include "internal/cryptlib.h"
#include "internal/ktls.h"
@@ -27,6 +28,14 @@
# define sock_puts SockPuts
# endif
+struct bss_sock_st {
+ BIO_ADDR tfo_peer;
+ int tfo_first;
+#ifndef OPENSSL_NO_KTLS
+ unsigned char ktls_record_type;
+#endif
+};
+
static int sock_write(BIO *h, const char *buf, int num);
static int sock_read(BIO *h, char *buf, int size);
static int sock_puts(BIO *h, const char *str);
@@ -81,8 +90,10 @@ static int sock_new(BIO *bi)
{
bi->init = 0;
bi->num = 0;
- bi->ptr = NULL;
bi->flags = 0;
+ bi->ptr = OPENSSL_zalloc(sizeof(struct bss_sock_st));
+ if (bi->ptr == NULL)
+ return 0;
return 1;
}
@@ -97,6 +108,8 @@ static int sock_free(BIO *a)
a->init = 0;
a->flags = 0;
}
+ OPENSSL_free(a->ptr);
+ a->ptr = NULL;
return 1;
}
@@ -126,11 +139,14 @@ static int sock_read(BIO *b, char *out, int outl)
static int sock_write(BIO *b, const char *in, int inl)
{
int ret = 0;
+# if !defined(OPENSSL_NO_KTLS) || defined(OSSL_TFO_SENDTO)
+ struct bss_sock_st *data = (struct bss_sock_st *)b->ptr;
+# endif
clear_socket_error();
# ifndef OPENSSL_NO_KTLS
if (BIO_should_ktls_ctrl_msg_flag(b)) {
- unsigned char record_type = (intptr_t)b->ptr;
+ unsigned char record_type = data->ktls_record_type;
ret = ktls_send_ctrl_message(b->num, record_type, in, inl);
if (ret >= 0) {
ret = inl;
@@ -138,6 +154,16 @@ static int sock_write(BIO *b, const char *in, int inl)
}
} else
# endif
+# if defined(OSSL_TFO_SENDTO)
+ if (data->tfo_first) {
+ struct bss_sock_st *data = (struct bss_sock_st *)b->ptr;
+ socklen_t peerlen = BIO_ADDR_sockaddr_size(&data->tfo_peer);
+
+ ret = sendto(b->num, in, inl, OSSL_TFO_SENDTO,
+ BIO_ADDR_sockaddr(&data->tfo_peer), peerlen);
+ data->tfo_first = 0;
+ } else
+# endif
ret = writesocket(b->num, in, inl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
@@ -151,16 +177,24 @@ static long sock_ctrl(BIO *b, int cmd, long num, void *ptr)
{
long ret = 1;
int *ip;
+ struct bss_sock_st *data = (struct bss_sock_st *)b->ptr;
# ifndef OPENSSL_NO_KTLS
ktls_crypto_info_t *crypto_info;
# endif
switch (cmd) {
case BIO_C_SET_FD:
- sock_free(b);
+ /* minimal sock_free() */
+ if (b->shutdown) {
+ if (b->init)
+ BIO_closesocket(b->num);
+ b->flags = 0;
+ }
b->num = *((int *)ptr);
b->shutdown = (int)num;
b->init = 1;
+ data->tfo_first = 0;
+ memset(&data->tfo_peer, 0, sizeof(data->tfo_peer));
break;
case BIO_C_GET_FD:
if (b->init) {
@@ -194,7 +228,7 @@ static long sock_ctrl(BIO *b, int cmd, long num, void *ptr)
return BIO_should_ktls_flag(b, 0) != 0;
case BIO_CTRL_SET_KTLS_TX_SEND_CTRL_MSG:
BIO_set_ktls_ctrl_msg_flag(b);
- b->ptr = (void *)num;
+ data->ktls_record_type = (unsigned char)num;
ret = 0;
break;
case BIO_CTRL_CLEAR_KTLS_TX_CTRL_MSG:
@@ -205,6 +239,25 @@ static long sock_ctrl(BIO *b, int cmd, long num, void *ptr)
case BIO_CTRL_EOF:
ret = (b->flags & BIO_FLAGS_IN_EOF) != 0;
break;
+ case BIO_C_GET_CONNECT:
+ if (ptr != NULL && num == 2) {
+ const char **pptr = (const char **)ptr;
+
+ *pptr = (const char *)&data->tfo_peer;
+ } else {
+ ret = 0;
+ }
+ break;
+ case BIO_C_SET_CONNECT:
+ if (ptr != NULL && num == 2) {
+ ret = BIO_ADDR_make(&data->tfo_peer,
+ BIO_ADDR_sockaddr((const BIO_ADDR *)ptr));
+ if (ret)
+ data->tfo_first = 1;
+ } else {
+ ret = 0;
+ }
+ break;
default:
ret = 0;
break;