diff options
Diffstat (limited to 'lib/system.c')
-rw-r--r-- | lib/system.c | 80 |
1 files changed, 70 insertions, 10 deletions
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 |