summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Rühsen <tim.ruehsen@gmx.de>2016-07-14 12:31:55 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2016-07-15 13:46:49 +0200
commit1f98f7ceab7c331b8281ab39ffe9cd53c7ae765f (patch)
tree21015e52a6bd611b6f0ac9254c17aefdd83bb024
parentc2e839247e14415b72c824726ddbc59fa54f941f (diff)
downloadgnutls-fast-open.tar.gz
Support TCP Fast Openfast-open
New function gnutls_transport_set_fastopen() with example usage in gnutls-cli (enabled with new --fastopen option).
-rw-r--r--NEWS4
-rw-r--r--configure.ac3
-rw-r--r--doc/Makefile.am2
-rw-r--r--doc/manpages/Makefile.am1
-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
-rw-r--r--src/cli-args.def6
-rw-r--r--src/cli.c26
-rw-r--r--src/socket.c23
-rw-r--r--src/socket.h7
16 files changed, 202 insertions, 22 deletions
diff --git a/NEWS b/NEWS
index d91128ffb7..299b536c16 100644
--- a/NEWS
+++ b/NEWS
@@ -13,8 +13,12 @@ See the end for copying conditions.
template options. This allows specifying arbitrary extensions into
certificates and certificate requests.
+** gnutls-cli: Added the --fastopen option.
+
** API and ABI modifications:
gnutls_x509_crq_set_extension_by_oid: Added
+gnutls_transport_set_fastopen: Added
+
* Version 3.5.2 (released 2016-07-06)
diff --git a/configure.ac b/configure.ac
index b8e6bbad6b..0009704f9f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -853,6 +853,9 @@ dnl Some variables needed in makefiles
YEAR=`date +%Y`
AC_SUBST([YEAR], $YEAR)
+dnl Need netinet/tcp.h for TCP_FASTOPEN
+AC_CHECK_HEADERS([netinet/tcp.h])
+
AC_CONFIG_FILES([guile/pre-inst-guile], [chmod +x guile/pre-inst-guile])
AC_CONFIG_FILES([
Makefile
diff --git a/doc/Makefile.am b/doc/Makefile.am
index b7df15af9c..70b0dce99e 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -2055,6 +2055,8 @@ FUNCS += functions/gnutls_transport_set_errno
FUNCS += functions/gnutls_transport_set_errno.short
FUNCS += functions/gnutls_transport_set_errno_function
FUNCS += functions/gnutls_transport_set_errno_function.short
+FUNCS += functions/gnutls_transport_set_fastopen
+FUNCS += functions/gnutls_transport_set_fastopen.short
FUNCS += functions/gnutls_transport_set_int
FUNCS += functions/gnutls_transport_set_int.short
FUNCS += functions/gnutls_transport_set_int2
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index 852d923357..0d633594c0 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -831,6 +831,7 @@ APIMANS += gnutls_transport_get_ptr.3
APIMANS += gnutls_transport_get_ptr2.3
APIMANS += gnutls_transport_set_errno.3
APIMANS += gnutls_transport_set_errno_function.3
+APIMANS += gnutls_transport_set_fastopen.3
APIMANS += gnutls_transport_set_int.3
APIMANS += gnutls_transport_set_int2.3
APIMANS += gnutls_transport_set_ptr.3
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);
diff --git a/src/cli-args.def b/src/cli-args.def
index 96e11073ad..451f80f293 100644
--- a/src/cli-args.def
+++ b/src/cli-args.def
@@ -122,6 +122,12 @@ flag = {
};
flag = {
+ name = fastopen;
+ descrip = "Enable TCP Fast Open";
+ doc = "";
+};
+
+flag = {
name = x509fmtder;
descrip = "Use DER format for certificates to read from";
doc = "";
diff --git a/src/cli.c b/src/cli.c
index 6e87abdc99..8072adab16 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -41,6 +41,11 @@
#include <netdb.h>
#include <ctype.h>
+/* Get TCP_FASTOPEN */
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
#include <gnutls/dtls.h>
@@ -48,6 +53,7 @@
#include <gnutls/openpgp.h>
#include <gnutls/pkcs11.h>
#include <gnutls/crypto.h>
+#include <gnutls/socket.h>
/* Gnulib portability files. */
#include <read-file.h>
@@ -78,6 +84,7 @@ char service[32]="";
int record_max_size;
int fingerprint;
int crlf;
+int fastopen;
unsigned int verbose = 0;
int print_cert;
@@ -908,7 +915,7 @@ static int try_resume(socket_st * hd)
printf
("\n\n- Connecting again- trying to resume previous session\n");
- socket_open(hd, hostname, service, udp, CONNECT_MSG);
+ socket_open(hd, hostname, service, udp | (fastopen << 1), CONNECT_MSG);
if (HAVE_OPT(STARTTLS_PROTO))
socket_starttls(hd, OPT_ARG(STARTTLS_PROTO));
@@ -1211,7 +1218,7 @@ int main(int argc, char **argv)
canonicalize_host(hostname, service, sizeof(service));
- socket_open(&hd, hostname, service, udp, CONNECT_MSG);
+ socket_open(&hd, hostname, service, udp | (fastopen << 1), CONNECT_MSG);
hd.verbose = verbose;
if (HAVE_OPT(STARTTLS_PROTO))
@@ -1623,6 +1630,15 @@ static void cmd_parser(int argc, char **argv)
crlf = HAVE_OPT(CRLF);
+#ifdef TCP_FASTOPEN
+ fastopen = HAVE_OPT(FASTOPEN);
+#else
+ if (HAVE_OPT(FASTOPEN)) {
+ fprintf(stderr, "TCP Fast Open not supported for this OS\n");
+ exit(1);
+ }
+#endif
+
if (rest != NULL)
hostname = rest;
@@ -1661,6 +1677,12 @@ static int do_handshake(socket_st * socket)
gnutls_transport_set_int(socket->session, socket->fd);
set_read_funcs(socket->session);
+ if (fastopen && socket->connect_addr) {
+ gnutls_transport_set_fastopen(socket->session, socket->connect_addr, socket->connect_addrlen);
+ socket->connect_addr = NULL;
+ socket->connect_addrlen = 0;
+ }
+
do {
gnutls_handshake_set_timeout(socket->session,
GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
diff --git a/src/socket.c b/src/socket.c
index bbb97f1fd4..6845ff2407 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -318,6 +318,8 @@ void socket_bye(socket_st * socket)
freeaddrinfo(socket->addr_info);
socket->addr_info = socket->ptr = NULL;
+ socket->connect_addr = NULL;
+ socket->connect_addrlen = 0;
free(socket->ip);
free(socket->hostname);
@@ -353,10 +355,12 @@ void canonicalize_host(char *hostname, char *service, unsigned service_size)
void
socket_open(socket_st * hd, const char *hostname, const char *service,
- int udp, const char *msg)
+ int flags, const char *msg)
{
struct addrinfo hints, *res, *ptr;
int sd, err = 0;
+ int udp = flags & 1;
+ int fastopen = flags & 2;
char buffer[MAX_BUF + 1];
char portname[16] = { 0 };
char *a_hostname = (char*)hostname;
@@ -416,14 +420,21 @@ socket_open(socket_st * hd, const char *hostname, const char *service,
#endif
}
+ if (fastopen && ptr->ai_protocol == SOCK_STREAM
+ && (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6)) {
+ hd->connect_addr = ptr->ai_addr;
+ hd->connect_addrlen = ptr->ai_addrlen;
- if (msg)
- printf("%s '%s:%s'...\n", msg, buffer, portname);
+ if (msg)
+ printf("%s '%s:%s' (TFO)...\n", msg, buffer, portname);
+ } else {
+ if (msg)
+ printf("%s '%s:%s'...\n", msg, buffer, portname);
- err = connect(sd, ptr->ai_addr, ptr->ai_addrlen);
- if (err < 0) {
- continue;
+ if ((err = connect(sd, ptr->ai_addr, ptr->ai_addrlen)) < 0)
+ continue;
}
+
break;
}
diff --git a/src/socket.h b/src/socket.h
index 4928065f3b..829daf2f66 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -1,4 +1,5 @@
#include <gnutls/gnutls.h>
+#include <gnutls/socket.h>
typedef struct {
int fd;
@@ -11,6 +12,10 @@ typedef struct {
struct addrinfo *addr_info;
int verbose;
+ /* Needed for TCP Fast Open */
+ struct sockaddr *connect_addr;
+ socklen_t connect_addrlen;
+
/* resumption data */
gnutls_datum_t rdata;
} socket_st;
@@ -24,7 +29,7 @@ ssize_t socket_send(const socket_st * socket, const void *buffer,
ssize_t socket_send_range(const socket_st * socket, const void *buffer,
int buffer_size, gnutls_range_st * range);
void socket_open(socket_st * hd, const char *hostname, const char *service,
- int udp, const char *msg);
+ int flags, const char *msg);
void socket_starttls(socket_st * hd, const char *app_proto);
void socket_bye(socket_st * socket);