summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml14
-rw-r--r--.github/workflows/run-checker-daily.yml2
-rw-r--r--CHANGES.md5
-rwxr-xr-xConfigure5
-rw-r--r--INSTALL.md4
-rw-r--r--NEWS.md2
-rw-r--r--apps/include/s_apps.h7
-rw-r--r--apps/lib/s_socket.c26
-rw-r--r--apps/s_client.c35
-rw-r--r--apps/s_server.c17
-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
-rw-r--r--crypto/err/openssl.txt3
-rw-r--r--doc/man1/openssl-s_client.pod.in7
-rw-r--r--doc/man1/openssl-s_server.pod.in8
-rw-r--r--doc/man3/BIO_ADDR.pod14
-rw-r--r--doc/man3/BIO_connect.pod9
-rw-r--r--doc/man3/BIO_ctrl.pod30
-rw-r--r--doc/man3/BIO_s_accept.pod16
-rw-r--r--doc/man7/bio.pod32
-rw-r--r--include/internal/bio_tfo.h151
-rw-r--r--include/openssl/bio.h.in9
-rw-r--r--include/openssl/bioerr.h3
-rw-r--r--test/bio_tfo_test.c418
-rw-r--r--test/build.info7
-rw-r--r--test/recipes/04-test_bio_tfo.t18
-rw-r--r--test/recipes/82-test_tfo_cli.t87
-rw-r--r--util/libcrypto.num1
-rw-r--r--util/missingmacro.txt1
-rw-r--r--util/other.syms4
34 files changed, 1129 insertions, 28 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7f40ae893d..5bd32341f2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -216,6 +216,20 @@ jobs:
- name: make test
run: make test HARNESS_JOBS=${HARNESS_JOBS:-4}
+ enable-tfo:
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest ]
+ runs-on: ${{matrix.os}}
+ steps:
+ - uses: actions/checkout@v2
+ - name: config
+ run: CC=gcc ./config --banner=Configured enable-tfo --strict-warnings && perl configdata.pm --dump
+ - name: make
+ run: make -s -j4
+ - name: make test
+ run: make test HARNESS_JOBS=${HARNESS_JOBS:-4}
+
buildtest:
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/run-checker-daily.yml b/.github/workflows/run-checker-daily.yml
index d16eb41dab..a255a0ed01 100644
--- a/.github/workflows/run-checker-daily.yml
+++ b/.github/workflows/run-checker-daily.yml
@@ -104,6 +104,7 @@ jobs:
no-sm2,
no-sm3,
no-sm4,
+ no-sock,
no-sse2,
no-ssl,
no-ssl3,
@@ -111,6 +112,7 @@ jobs:
no-ssl-trace,
no-static-engine no-shared,
no-stdio,
+ enable-tfo,
no-tls1,
no-tls1_1,
no-tls1_1-method,
diff --git a/CHANGES.md b/CHANGES.md
index 185340d8c1..c7e3391d4b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -24,6 +24,11 @@ OpenSSL 3.1
### Changes between 3.0 and 3.1 [xx XXX xxxx]
+ * Add support for TCP Fast Open (RFC7413) to macOS, Linux, and FreeBSD where
+ supported and enabled.
+
+ *Todd Short*
+
* Add ciphersuites based on DHE_PSK (RFC 4279) and ECDHE_PSK (RFC 5489)
to the list of ciphersuites providing Perfect Forward Secrecy as
required by SECLEVEL >= 3.
diff --git a/Configure b/Configure
index 34a487a0a2..355969356e 100755
--- a/Configure
+++ b/Configure
@@ -492,6 +492,7 @@ my @disablables = (
"static-engine",
"stdio",
"tests",
+ "tfo",
"threads",
"tls",
"trace",
@@ -551,6 +552,7 @@ our %disabled = ( # "what" => "comment"
"sctp" => "default",
"ssl3" => "default",
"ssl3-method" => "default",
+ "tfo" => "default",
"trace" => "default",
"ubsan" => "default",
"unit-test" => "default",
@@ -576,6 +578,7 @@ my @disable_cascades = (
"seed", "siphash", "siv",
"sm3", "sm4", "srp",
"srtp", "ssl3-method", "ssl-trace",
+ "tfo",
"ts", "ui-console", "whirlpool",
"fips-securitychecks" ],
sub { $config{processor} eq "386" }
@@ -586,7 +589,7 @@ my @disable_cascades = (
"des" => [ "mdc2" ],
"ec" => [ "ec2m", "ecdsa", "ecdh", "sm2", "gost" ],
"dgram" => [ "dtls", "sctp" ],
- "sock" => [ "dgram" ],
+ "sock" => [ "dgram", "tfo" ],
"dtls" => [ @dtls ],
sub { 0 == scalar grep { !$disabled{$_} } @dtls }
=> [ "dtls" ],
diff --git a/INSTALL.md b/INSTALL.md
index 09ea2d678f..8d7d7e466a 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -878,6 +878,10 @@ tests also use the command line applications, the tests will also be skipped.
Don't build test programs or run any tests.
+### enable-tfo
+
+Build with support for TCP Fast Open (RFC7413). Supported on Linux, macOS and FreeBSD.
+
### no-threads
Don't build with support for multi-threaded applications.
diff --git a/NEWS.md b/NEWS.md
index 18fa374acb..9129f1c9f4 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -23,6 +23,8 @@ OpenSSL 3.1
* Subject or issuer names in X.509 objects are now displayed as UTF-8 strings
by default.
+ * TCP Fast Open (RFC7413) support is available on Linux, macOS, and FreeBSD
+ where enabled and supported.
OpenSSL 3.0
-----------
diff --git a/apps/include/s_apps.h b/apps/include/s_apps.h
index 5b188b9892..3f00302721 100644
--- a/apps/include/s_apps.h
+++ b/apps/include/s_apps.h
@@ -23,8 +23,8 @@ void get_sock_info_address(int asock, char **hostname, char **service);
int report_server_accept(BIO *out, int asock, int with_address, int with_pid);
int do_server(int *accept_sock, const char *host, const char *port,
int family, int type, int protocol, do_server_cb cb,
- unsigned char *context, int naccept, BIO *bio_s_out);
-
+ unsigned char *context, int naccept, BIO *bio_s_out,
+ int tfo);
int verify_callback(int ok, X509_STORE_CTX *ctx);
int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file);
@@ -36,7 +36,8 @@ int ssl_print_groups(BIO *out, SSL *s, int noshared);
int ssl_print_tmp_key(BIO *out, SSL *s);
int init_client(int *sock, const char *host, const char *port,
const char *bindhost, const char *bindport,
- int family, int type, int protocol);
+ int family, int type, int protocol, int tfo,
+ BIO_ADDR **ba_ret);
int should_retry(int i);
void do_ssl_shutdown(SSL *ssl);
diff --git a/apps/lib/s_socket.c b/apps/lib/s_socket.c
index e5802d5054..1ed5a213af 100644
--- a/apps/lib/s_socket.c
+++ b/apps/lib/s_socket.c
@@ -64,6 +64,8 @@ BIO_ADDR *ourpeer = NULL;
* AF_UNSPEC
* @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
* @protocol: socket protocol, e.g. IPPROTO_TCP or IPPROTO_UDP (or 0 for any)
+ * @tfo: flag to enable TCP Fast Open
+ * @ba_ret: BIO_ADDR that was connected to for TFO, to be freed by caller
*
* This will create a socket and use it to connect to a host:port, or if
* family == AF_UNIX, to the path found in host.
@@ -76,7 +78,8 @@ BIO_ADDR *ourpeer = NULL;
*/
int init_client(int *sock, const char *host, const char *port,
const char *bindhost, const char *bindport,
- int family, int type, int protocol)
+ int family, int type, int protocol, int tfo,
+ BIO_ADDR **ba_ret)
{
BIO_ADDRINFO *res = NULL;
BIO_ADDRINFO *bindaddr = NULL;
@@ -84,6 +87,10 @@ int init_client(int *sock, const char *host, const char *port,
const BIO_ADDRINFO *bi = NULL;
int found = 0;
int ret;
+ int options = 0;
+
+ if (tfo && ba_ret != NULL)
+ *ba_ret = NULL;
if (BIO_sock_init() != 1)
return 0;
@@ -160,14 +167,22 @@ int init_client(int *sock, const char *host, const char *port,
BIO_free(tmpbio);
}
#endif
+ if (BIO_ADDRINFO_protocol(ai) == IPPROTO_TCP) {
+ options |= BIO_SOCK_NODELAY;
+ if (tfo)
+ options |= BIO_SOCK_TFO;
+ }
- if (!BIO_connect(*sock, BIO_ADDRINFO_address(ai),
- BIO_ADDRINFO_protocol(ai) == IPPROTO_TCP ? BIO_SOCK_NODELAY : 0)) {
+ if (!BIO_connect(*sock, BIO_ADDRINFO_address(ai), options)) {
BIO_closesocket(*sock);
*sock = INVALID_SOCKET;
continue;
}
+ /* Save the address */
+ if (tfo && ba_ret != NULL)
+ *ba_ret = BIO_ADDR_dup(BIO_ADDRINFO_address(ai));
+
/* Success, don't try any more addresses */
break;
}
@@ -278,7 +293,8 @@ int report_server_accept(BIO *out, int asock, int with_address, int with_pid)
*/
int do_server(int *accept_sock, const char *host, const char *port,
int family, int type, int protocol, do_server_cb cb,
- unsigned char *context, int naccept, BIO *bio_s_out)
+ unsigned char *context, int naccept, BIO *bio_s_out,
+ int tfo)
{
int asock = 0;
int sock;
@@ -312,6 +328,8 @@ int do_server(int *accept_sock, const char *host, const char *port,
sock_protocol = BIO_ADDRINFO_protocol(res);
sock_address = BIO_ADDRINFO_address(res);
next = BIO_ADDRINFO_next(res);
+ if (tfo && sock_type == SOCK_STREAM)
+ sock_options |= BIO_SOCK_TFO;
#ifdef AF_INET6
if (sock_family == AF_INET6)
sock_options |= BIO_SOCK_V6_ONLY;
diff --git a/apps/s_client.c b/apps/s_client.c
index 105a243b8e..f78404c63f 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -456,6 +456,7 @@ typedef enum OPTION_choice {
OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_PROTOHOST,
OPT_MAXFRAGLEN, OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES,
OPT_READ_BUF, OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_REQCAFILE,
+ OPT_TFO,
OPT_V_ENUM,
OPT_X_ENUM,
OPT_S_ENUM, OPT_IGNORE_UNEXPECTED_EOF,
@@ -539,6 +540,9 @@ const OPTIONS s_client_options[] = {
"Do not load certificates from the default certificates store"},
{"requestCAfile", OPT_REQCAFILE, '<',
"PEM format file of CA names to send to the server"},
+#if defined(TCP_FASTOPEN) && !defined(OPENSSL_NO_TFO)
+ {"tfo", OPT_TFO, '-', "Connect using TCP Fast Open"},
+#endif
{"dane_tlsa_domain", OPT_DANE_TLSA_DOMAIN, 's', "DANE TLSA base domain"},
{"dane_tlsa_rrdata", OPT_DANE_TLSA_RRDATA, 's',
"DANE TLSA rrdata presentation form"},
@@ -899,6 +903,8 @@ int s_client_main(int argc, char **argv)
#ifndef OPENSSL_NO_KTLS
int enable_ktls = 0;
#endif
+ int tfo = 0;
+ BIO_ADDR *tfo_addr = NULL;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
@@ -1413,6 +1419,9 @@ int s_client_main(int argc, char **argv)
if (!opt_pair(opt_arg(), services, &starttls_proto))
goto end;
break;
+ case OPT_TFO:
+ tfo = 1;
+ break;
case OPT_SERVERNAME:
servername = opt_arg();
break;
@@ -2035,10 +2044,18 @@ int s_client_main(int argc, char **argv)
"-dane_tlsa_domain option.\n", prog);
goto end;
}
+#ifndef OPENSSL_NO_DTLS
+ if (isdtls && tfo) {
+ BIO_printf(bio_err, "%s: DTLS does not support the -tfo option\n", prog);
+ goto end;
+ }
+#endif
+ if (tfo)
+ BIO_printf(bio_c_out, "Connecting via TFO\n");
re_start:
if (init_client(&sock, host, port, bindhost, bindport, socket_family,
- socket_type, protocol) == 0) {
+ socket_type, protocol, tfo, &tfo_addr) == 0) {
BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error());
BIO_closesocket(sock);
goto end;
@@ -2120,6 +2137,12 @@ int s_client_main(int argc, char **argv)
goto end;
}
+ /* Now that we're using a BIO... */
+ if (tfo_addr != NULL)
+ (void)BIO_set_conn_address(sbio, tfo_addr);
+ if (tfo)
+ (void)BIO_set_tfo(sbio, 1);
+
if (nbio_test) {
BIO *test;
@@ -2909,9 +2932,12 @@ int s_client_main(int argc, char **argv)
case SSL_ERROR_SYSCALL:
if ((k != 0) || (cbuf_len != 0)) {
- BIO_printf(bio_err, "write:errno=%d\n",
- get_last_socket_error());
- goto shut;
+ int sockerr = get_last_socket_error();
+
+ if (!tfo || sockerr != EISCONN) {
+ BIO_printf(bio_err, "write:errno=%d\n", sockerr);
+ goto shut;
+ }
} else {
read_tty = 1;
write_ssl = 0;
@@ -3131,6 +3157,7 @@ int s_client_main(int argc, char **argv)
OPENSSL_free(srp_arg.srppassin);
#endif
OPENSSL_free(sname_alloc);
+ BIO_ADDR_free(tfo_addr);
OPENSSL_free(connectstr);
OPENSSL_free(bindstr);
OPENSSL_free(bindhost);
diff --git a/apps/s_server.c b/apps/s_server.c
index 2036d51795..736d8498d1 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -717,6 +717,7 @@ typedef enum OPTION_choice {
OPT_KEYLOG_FILE, OPT_MAX_EARLY, OPT_RECV_MAX_EARLY, OPT_EARLY_DATA,
OPT_S_NUM_TICKETS, OPT_ANTI_REPLAY, OPT_NO_ANTI_REPLAY, OPT_SCTP_LABEL_BUG,
OPT_HTTP_SERVER_BINMODE, OPT_NOCANAMES, OPT_IGNORE_UNEXPECTED_EOF, OPT_KTLS,
+ OPT_TFO,
OPT_R_ENUM,
OPT_S_ENUM,
OPT_V_ENUM,
@@ -747,6 +748,9 @@ const OPTIONS s_server_options[] = {
#endif
{"4", OPT_4, '-', "Use IPv4 only"},
{"6", OPT_6, '-', "Use IPv6 only"},
+#if defined(TCP_FASTOPEN) && !defined(OPENSSL_NO_TFO)
+ {"tfo", OPT_TFO, '-', "Listen for TCP Fast Open connections"},
+#endif
OPT_SECTION("Identity"),
{"context", OPT_CONTEXT, 's', "Set session ID context"},
@@ -1057,6 +1061,7 @@ int s_server_main(int argc, char *argv[])
#ifndef OPENSSL_NO_KTLS
int enable_ktls = 0;
#endif
+ int tfo = 0;
/* Init of few remaining global variables */
local_argc = argc;
@@ -1649,6 +1654,9 @@ int s_server_main(int argc, char *argv[])
case OPT_IGNORE_UNEXPECTED_EOF:
ignore_unexpected_eof = 1;
break;
+ case OPT_TFO:
+ tfo = 1;
+ break;
}
}
@@ -1677,6 +1685,11 @@ int s_server_main(int argc, char *argv[])
}
#endif
+ if (tfo && socket_type != SOCK_STREAM) {
+ BIO_printf(bio_err, "Can only use -tfo with TLS\n");
+ goto end;
+ }
+
if (stateless && socket_type != SOCK_STREAM) {
BIO_printf(bio_err, "Can only use --stateless with TLS\n");
goto end;
@@ -2240,8 +2253,10 @@ int s_server_main(int argc, char *argv[])
&& unlink_unix_path)
unlink(host);
#endif
+ if (tfo)
+ BIO_printf(bio_s_out, "Listening for TFO\n");
do_server(&accept_socket, host, port, socket_family, socket_type, protocol,
- server_cb, context, naccept, bio_s_out);
+ server_cb, context, naccept, bio_s_out, tfo);
print_stats(bio_s_out, ctx);
ret = 0;
end:
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) {