summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/buffers.c15
-rw-r--r--lib/gnutls_int.h5
-rw-r--r--lib/includes/Makefile.am2
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/record.c41
-rw-r--r--lib/state.c4
-rw-r--r--lib/system.c80
-rw-r--r--lib/system.h4
8 files changed, 139 insertions, 13 deletions
diff --git a/lib/buffers.c b/lib/buffers.c
index 756969903e..7e4f4abf1c 100644
--- a/lib/buffers.c
+++ b/lib/buffers.c
@@ -496,8 +496,19 @@ _gnutls_writev(gnutls_session_t session, const giovec_t * giovec,
}
if (no_writev == 0) {
- i = session->internals.vec_push_func(fd, giovec,
- giovec_cnt);
+ if (session->internals.connect_addr) {
+#ifdef HAVE_WRITEV
+ if (session->internals.vec_push_func == system_writev)
+ i = system_writev_tfo(session, giovec, giovec_cnt);
+#ifdef MSG_NOSIGNAL
+ else if (session->internals.vec_push_func == system_writev_nosignal)
+ i = system_writev_nosignal_tfo(session, giovec, giovec_cnt);
+#endif
+ else
+#endif
+ i = session->internals.vec_push_func(fd, giovec, giovec_cnt);
+ } else
+ i = session->internals.vec_push_func(fd, giovec, giovec_cnt);
} else {
i = _gnutls_writev_emu(session, fd, giovec, giovec_cnt, 1);
}
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index bb3739c04e..efb6991223 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -92,6 +92,7 @@ typedef struct {
#include <gnutls/gnutls.h>
#include <gnutls/dtls.h>
#include <gnutls/abstract.h>
+#include <gnutls/socket.h>
#include <system.h>
/* in case we compile with system headers taking priority, we
@@ -1022,6 +1023,10 @@ typedef struct {
bool false_start_used; /* non-zero if false start was used for appdata */
+ /* Needed for TCP Fast Open (TFO), set by gnutls_transport_set_fastopen() */
+ struct sockaddr *connect_addr;
+ socklen_t connect_addrlen;
+
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
*/
} internals_st;
diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am
index 09665afe57..6319ddbc5a 100644
--- a/lib/includes/Makefile.am
+++ b/lib/includes/Makefile.am
@@ -22,7 +22,7 @@ nobase_include_HEADERS = gnutls/x509.h gnutls/pkcs12.h gnutls/compat.h \
gnutls/openpgp.h gnutls/crypto.h gnutls/pkcs11.h \
gnutls/abstract.h gnutls/dtls.h gnutls/ocsp.h gnutls/tpm.h \
gnutls/x509-ext.h gnutls/self-test.h gnutls/system-keys.h \
- gnutls/urls.h gnutls/pkcs7.h
+ gnutls/urls.h gnutls/pkcs7.h gnutls/socket.h
if ENABLE_CXX
nobase_include_HEADERS += gnutls/gnutlsxx.h
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 03ec8f1ebe..af2353ff05 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1100,6 +1100,7 @@ GNUTLS_3_4
gnutls_x509_crt_set_crq_extension_by_oid;
gnutls_x509_tlsfeatures_check_crt;
gnutls_x509_crq_set_extension_by_oid;
+ gnutls_transport_set_fastopen;
local:
*;
};
diff --git a/lib/record.c b/lib/record.c
index 4e462b9eac..d33c9c25e1 100644
--- a/lib/record.c
+++ b/lib/record.c
@@ -146,6 +146,47 @@ gnutls_transport_set_int2(gnutls_session_t session,
(gnutls_transport_ptr_t) (long) recv_int;
}
+/**
+ * gnutls_transport_set_fastopen:
+ * @session: is a #gnutls_session_t type.
+ * @connect_addr: is the address we want to connect to
+ * @connect_addrlen: is the length of @connect_addr
+ *
+ * Enables TCP Fast Open (TFO) when @connect_addr and @connect_addrlen are set
+ * before the transport socket has been connected.
+ *
+ * TFO only works for TCP sockets of type AF_INET and AF_INET6.
+ * Not every OS supports TFO yet.
+ *
+ * Overriding push functions via gnutls_transport_set_vec_push_function()
+ * or gnutls_transport_set_push_function() effectively disables the internal
+ * support for TFO. In this case the user's write function has to care about
+ * the implementation of TFO.
+ *
+ * On GNU/Linux TFO has to be enabled at the system layer, that is
+ * in /proc/sys/net/ipv4/tcp_fastopen, bit 0 has to be set.
+ *
+ * Since: 3.5.3
+ **/
+void
+gnutls_transport_set_fastopen(gnutls_session_t session,
+ struct sockaddr *connect_addr, socklen_t connect_addrlen)
+{
+ gnutls_free(session->internals.connect_addr);
+
+ if (connect_addr && connect_addrlen) {
+ session->internals.connect_addr = gnutls_malloc(connect_addrlen);
+ if (session->internals.connect_addr) {
+ memcpy(session->internals.connect_addr, connect_addr, connect_addrlen);
+ session->internals.connect_addrlen = connect_addrlen;
+ } else
+ gnutls_assert();
+ } else {
+ session->internals.connect_addr = NULL;
+ session->internals.connect_addrlen = 0;
+ }
+}
+
#if 0
/* this will be a macro */
/**
diff --git a/lib/state.c b/lib/state.c
index b45237680f..c7d18f7248 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -303,6 +303,10 @@ void _gnutls_handshake_internal_state_clear(gnutls_session_t session)
session->internals.handshake_endtime = 0;
session->internals.handshake_in_progress = 0;
+
+ gnutls_free(session->internals.connect_addr);
+ session->internals.connect_addr = NULL;
+ session->internals.connect_addrlen = 0;
}
/**
diff --git a/lib/system.c b/lib/system.c
index 5c9bc6e68f..e08510d283 100644
--- a/lib/system.c
+++ b/lib/system.c
@@ -31,6 +31,11 @@
#include <sys/types.h>
#include <c-ctype.h>
+/* Get TCP_FASTOPEN */
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
#ifdef _WIN32
# include <windows.h>
# include <wincrypt.h>
@@ -111,10 +116,9 @@ int system_errno(gnutls_transport_ptr_t ptr)
return errno;
}
-#ifdef MSG_NOSIGNAL
-ssize_t
-system_writev_nosignal(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
- int iovec_cnt)
+static ssize_t
+_system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
+ int iovec_cnt, int flags)
{
struct msghdr hdr;
@@ -122,21 +126,77 @@ system_writev_nosignal(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
hdr.msg_iov = (struct iovec *)iovec;
hdr.msg_iovlen = iovec_cnt;
- return sendmsg(GNUTLS_POINTER_TO_INT(ptr), &hdr, MSG_NOSIGNAL);
+ return sendmsg(GNUTLS_POINTER_TO_INT(ptr), &hdr, flags);
}
-#endif
-ssize_t
-system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
- int iovec_cnt)
+static ssize_t
+_system_writev_tfo(gnutls_session_t session, const giovec_t * iovec,
+ int iovec_cnt, int flags)
{
+ int fd = GNUTLS_POINTER_TO_INT(session->internals.transport_send_ptr);
+ int ret, on = 1;
+
struct msghdr hdr;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &on, sizeof(on)) == -1)
+ _gnutls_debug_log("Failed to set socket option FASTOPEN\n");
+
memset(&hdr, 0, sizeof(hdr));
+ hdr.msg_name = session->internals.connect_addr;
+ hdr.msg_namelen = session->internals.connect_addrlen;
hdr.msg_iov = (struct iovec *)iovec;
hdr.msg_iovlen = iovec_cnt;
- return sendmsg(GNUTLS_POINTER_TO_INT(ptr), &hdr, 0);
+ ret = sendmsg(fd, &hdr, flags | MSG_FASTOPEN);
+
+ if (ret < 0) {
+ if (errno == EINPROGRESS) {
+ errno = EAGAIN; // GnuTLS does not handle EINPROGRESS
+ } else if (errno == EOPNOTSUPP) {
+ // fallback from fastopen, e.g. when fastopen is disabled in system
+ _gnutls_debug_log("Fallback from TCP Fast Open... TFO is not enabled at system level\n");
+ ret = connect(fd, session->internals.connect_addr, session->internals.connect_addrlen);
+ if (errno == ENOTCONN || errno == EINPROGRESS)
+ errno = EAGAIN;
+ }
+ }
+
+ /* This function has to be called just once, connect info not needed any more */
+ gnutls_free(session->internals.connect_addr);
+ session->internals.connect_addr = NULL;
+ session->internals.connect_addrlen = 0;
+
+ return ret;
+}
+
+#ifdef MSG_NOSIGNAL
+ssize_t
+system_writev_nosignal(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
+ int iovec_cnt)
+{
+ return _system_writev(ptr, iovec, iovec_cnt, MSG_NOSIGNAL);
+}
+
+ssize_t
+system_writev_nosignal_tfo(gnutls_session_t session, const giovec_t * iovec,
+ int iovec_cnt)
+{
+ return _system_writev_tfo(session, iovec, iovec_cnt, MSG_NOSIGNAL);
+}
+#endif
+
+ssize_t
+system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
+ int iovec_cnt)
+{
+ return _system_writev(ptr, iovec, iovec_cnt, 0);
+}
+
+ssize_t
+system_writev_tfo(gnutls_session_t session, const giovec_t * iovec,
+ int iovec_cnt)
+{
+ return _system_writev_tfo(session, iovec, iovec_cnt, 0);
}
#endif
diff --git a/lib/system.h b/lib/system.h
index dd77365a1c..11803047ea 100644
--- a/lib/system.h
+++ b/lib/system.h
@@ -50,6 +50,10 @@ ssize_t system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
int iovec_cnt);
ssize_t system_writev_nosignal(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
int iovec_cnt);
+ssize_t system_writev_tfo(gnutls_session_t ptr, const giovec_t * iovec,
+ int iovec_cnt);
+ssize_t system_writev_nosignal_tfo(gnutls_session_t ptr, const giovec_t * iovec,
+ int iovec_cnt);
#endif
ssize_t system_read(gnutls_transport_ptr_t ptr, void *data,
size_t data_size);