diff options
author | Tim Rühsen <tim.ruehsen@gmx.de> | 2016-07-14 12:31:55 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-07-15 13:46:49 +0200 |
commit | 1f98f7ceab7c331b8281ab39ffe9cd53c7ae765f (patch) | |
tree | 21015e52a6bd611b6f0ac9254c17aefdd83bb024 | |
parent | c2e839247e14415b72c824726ddbc59fa54f941f (diff) | |
download | gnutls-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-- | NEWS | 4 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | doc/Makefile.am | 2 | ||||
-rw-r--r-- | doc/manpages/Makefile.am | 1 | ||||
-rw-r--r-- | lib/buffers.c | 15 | ||||
-rw-r--r-- | lib/gnutls_int.h | 5 | ||||
-rw-r--r-- | lib/includes/Makefile.am | 2 | ||||
-rw-r--r-- | lib/libgnutls.map | 1 | ||||
-rw-r--r-- | lib/record.c | 41 | ||||
-rw-r--r-- | lib/state.c | 4 | ||||
-rw-r--r-- | lib/system.c | 80 | ||||
-rw-r--r-- | lib/system.h | 4 | ||||
-rw-r--r-- | src/cli-args.def | 6 | ||||
-rw-r--r-- | src/cli.c | 26 | ||||
-rw-r--r-- | src/socket.c | 23 | ||||
-rw-r--r-- | src/socket.h | 7 |
16 files changed, 202 insertions, 22 deletions
@@ -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 = ""; @@ -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); |