Implement TCP FastOpen (TFO) RFC7413 (#840)

TCP Fast Open (TFO) allows TCP connection establishment in 0-RTT when a
client and server have previously communicated. The SYN packet will also
contain the initial data packet from the client to the server. This
means there should be virtually no slowdown over UDP when both sides
support TCP FastOpen, which is unfortunately not always the case. For
instance, `1.1.1.1` appears to support TFO, however `8.8.8.8` does not.

This implementation supports Linux, Android, FreeBSD, MacOS, and iOS.
While Windows does have support for TCP FastOpen it does so via
completion APIs only, and that can't be used with polling APIs like used
by every other OS. We could implement it in the future if desired for
those using `ARES_OPT_EVENT_THREAD`, but it would probably require
adopting IOCP completely on Windows.

Sysctls are required to be set appropriately:
 - Linux: `net.ipv4.tcp_fastopen`:
   - `1` = client only (typically default)
   - `2` = server only
   - `3` = client and server
 - MacOS: `net.inet.tcp.fastopen`
   - `1` = client only
   - `2` = server only
   - `3` = client and server (typically default)
- FreeBSD: `net.inet.tcp.fastopen.server_enable` (boolean) and
`net.inet.tcp.fastopen.client_enable` (boolean)

This feature is always-on, when running on an OS with the capability
enabled. Though some middleboxes have impacted end-to-end TFO and caused
connectivity errors, all modern OSs perform automatic blackholing of IPs
that have issues with TFO. It is not expected this to cause any issues
in the modern day implementations.

This will also help with improving latency for future DoT and DoH
implementations.

Authored-By: Brad House (@bradh352)
pull/841/head
Brad House 7 months ago committed by GitHub
parent ac33bdc7c2
commit dc423fb856
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      .cirrus.yml
  2. 11
      .github/workflows/macos.yml
  3. 27
      .github/workflows/ubuntu.yml
  4. 1
      CMakeLists.txt
  5. 1
      configure.ac
  6. 426
      src/lib/ares__socket.c
  7. 3
      src/lib/ares_config.h.cmake
  8. 25
      src/lib/ares_private.h
  9. 130
      src/lib/ares_process.c
  10. 89
      src/lib/ares_setup.h
  11. 5
      src/lib/legacy/ares_getsock.c
  12. 12
      src/lib/record/ares_dns_private.h
  13. 98
      src/lib/record/ares_dns_write.c
  14. 2
      src/lib/str/ares__buf.c
  15. 2
      test/ares-test-mock.cc
  16. 11
      test/ares-test.cc

@ -158,6 +158,9 @@ task:
# pkg upgrade -y && \
pkg install -y bash cmake ninja googletest pkgconf llvm gmake
ln -sf /usr/local/bin/bash /bin/bash
# Enable TCP FastOpen
sysctl net.inet.tcp.fastopen.server_enable=1
sysctl net.inet.tcp.fastopen.client_enable=1
case "${BUILD_TYPE}" in
autotools)
pkg install -y autoconf automake libtool

@ -23,6 +23,8 @@ jobs:
run: brew install cmake googletest llvm autoconf automake libtool make ninja
- name: Checkout c-ares
uses: actions/checkout@v4
- name: "Make sure TCP FastOpen is enabled"
run: sudo sysctl net.inet.tcp.fastopen=3
- name: "CMake: build and test c-ares"
env:
BUILD_TYPE: CMAKE
@ -72,3 +74,12 @@ jobs:
run: |
./ci/build.sh
./ci/test.sh
- name: "CMake: No TCP FastOpen"
env:
BUILD_TYPE: CMAKE
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON"
TEST_DEBUGGER: lldb
run: |
sudo sysctl net.inet.tcp.fastopen=0
./ci/build.sh
./ci/test.sh

@ -26,6 +26,9 @@ jobs:
version: 1.0
- name: Checkout c-ares
uses: actions/checkout@v4
- name: "Make sure TCP FastOpen is enabled"
run: |
sudo sysctl -w net.ipv4.tcp_fastopen=3
- name: "CMake: build and test c-ares"
env:
BUILD_TYPE: CMAKE
@ -84,7 +87,17 @@ jobs:
run: |
./ci/build.sh
./ci/test.sh
- name: "Valgrind: build and test c-ares"
- name: "CMake: Static Analyzer: build c-ares"
env:
BUILD_TYPE: "analyze"
CC: "clang"
SCAN_WRAP: "scan-build -v --status-bugs"
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=OFF"
TEST_DEBUGGER: "lldb"
run: |
./ci/build.sh
./ci/test.sh
- name: "Valgrind: build and test c-ares (no TCP FastOpen)"
env:
BUILD_TYPE: "valgrind"
TEST_WRAP: "valgrind --leak-check=full"
@ -92,15 +105,15 @@ jobs:
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON"
TEST_DEBUGGER: none
run: |
sudo sysctl -w net.ipv4.tcp_fastopen=0
./ci/build.sh
./ci/test.sh
- name: "CMake: Static Analyzer: build c-ares"
- name: "CMake: No TCP FastOpen"
env:
BUILD_TYPE: "analyze"
CC: "clang"
SCAN_WRAP: "scan-build -v --status-bugs"
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=OFF"
TEST_DEBUGGER: "lldb"
BUILD_TYPE: CMAKE
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON"
TEST_DEBUGGER: gdb
run: |
sudo sysctl -w net.ipv4.tcp_fastopen=0
./ci/build.sh
./ci/test.sh

@ -410,6 +410,7 @@ CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" sin6_scope_id "${CMAKE_EXTRA_INCLU
CHECK_SYMBOL_EXISTS (closesocket "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_CLOSESOCKET)
CHECK_SYMBOL_EXISTS (CloseSocket "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_CLOSESOCKET_CAMEL)
CHECK_SYMBOL_EXISTS (connect "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_CONNECT)
CHECK_SYMBOL_EXISTS (connectx "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_CONNECTX)
CHECK_SYMBOL_EXISTS (fcntl "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_FCNTL)
CHECK_SYMBOL_EXISTS (freeaddrinfo "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_FREEADDRINFO)
CHECK_SYMBOL_EXISTS (getaddrinfo "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETADDRINFO)

@ -538,6 +538,7 @@ AC_CHECK_DECL(send, [AC_DEFINE([HAVE_SEND], 1, [Define t
AC_CHECK_DECL(getnameinfo, [AC_DEFINE([HAVE_GETNAMEINFO], 1, [Define to 1 if you have `getnameinfo`] )], [], $cares_all_includes)
AC_CHECK_DECL(gethostname, [AC_DEFINE([HAVE_GETHOSTNAME], 1, [Define to 1 if you have `gethostname`] )], [], $cares_all_includes)
AC_CHECK_DECL(connect, [AC_DEFINE([HAVE_CONNECT], 1, [Define to 1 if you have `connect`] )], [], $cares_all_includes)
AC_CHECK_DECL(connectx, [AC_DEFINE([HAVE_CONNECTX], 1, [Define to 1 if you have `connectx`] )], [], $cares_all_includes)
AC_CHECK_DECL(closesocket, [AC_DEFINE([HAVE_CLOSESOCKET], 1, [Define to 1 if you have `closesocket`] )], [], $cares_all_includes)
AC_CHECK_DECL(CloseSocket, [AC_DEFINE([HAVE_CLOSESOCKET_CAMEL], 1, [Define to 1 if you have `CloseSocket`] )], [], $cares_all_includes)
AC_CHECK_DECL(fcntl, [AC_DEFINE([HAVE_FCNTL], 1, [Define to 1 if you have `fcntl`] )], [], $cares_all_includes)

@ -56,6 +56,78 @@
#include <fcntl.h>
#include <limits.h>
#if defined(__linux__) && defined(MSG_FASTOPEN)
# define TFO_SUPPORTED 1
# define TFO_SKIP_CONNECT 0
# define TFO_USE_SENDTO 0
# define TFO_USE_CONNECTX 0
# define TFO_CLIENT_SOCKOPT TCP_FASTOPEN_CONNECT
#elif defined(__FreeBSD__) && defined(TCP_FASTOPEN)
# define TFO_SUPPORTED 1
# define TFO_SKIP_CONNECT 1
# define TFO_USE_SENDTO 1
# define TFO_USE_CONNECTX 0
# define TFO_CLIENT_SOCKOPT TCP_FASTOPEN
#elif defined(__APPLE__) && defined(HAVE_CONNECTX)
# define TFO_SUPPORTED 1
# define TFO_SKIP_CONNECT 0
# define TFO_USE_SENDTO 0
# define TFO_USE_CONNECTX 1
# undef TFO_CLIENT_SOCKOPT
#else
# define TFO_SUPPORTED 0
#endif
#ifndef HAVE_WRITEV
/* Structure for scatter/gather I/O. */
struct iovec {
void *iov_base; /* Pointer to data. */
size_t iov_len; /* Length of data. */
};
#endif
/* Return 1 if the specified error number describes a readiness error, or 0
* otherwise. This is mostly for HP-UX, which could return EAGAIN or
* EWOULDBLOCK. See this man page
*
* http://devrsrc1.external.hp.com/STKS/cgi-bin/man2html?
* manpage=/usr/share/man/man2.Z/send.2
*/
ares_bool_t ares__socket_try_again(int errnum)
{
#if !defined EWOULDBLOCK && !defined EAGAIN
# error "Neither EWOULDBLOCK nor EAGAIN defined"
#endif
#ifdef EWOULDBLOCK
if (errnum == EWOULDBLOCK) {
return ARES_TRUE;
}
#endif
#if defined EAGAIN && EAGAIN != EWOULDBLOCK
if (errnum == EAGAIN) {
return ARES_TRUE;
}
#endif
return ARES_FALSE;
}
ares_ssize_t ares__socket_recv(ares_channel_t *channel, ares_socket_t s,
void *data, size_t data_len)
{
if (channel->sock_funcs && channel->sock_funcs->arecvfrom) {
return channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0,
channel->sock_func_cb_data);
}
return (ares_ssize_t)recv((RECV_TYPE_ARG1)s, (RECV_TYPE_ARG2)data,
(RECV_TYPE_ARG3)data_len, (RECV_TYPE_ARG4)(0));
}
ares_ssize_t ares__socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
void *data, size_t data_len, int flags,
struct sockaddr *from,
@ -70,20 +142,144 @@ ares_ssize_t ares__socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
return (ares_ssize_t)recvfrom(s, data, (RECVFROM_TYPE_ARG3)data_len, flags,
from, from_len);
#else
return sread(s, data, data_len);
return ares__socket_recv(channel, s, data, data_len);
#endif
}
ares_ssize_t ares__socket_recv(ares_channel_t *channel, ares_socket_t s,
void *data, size_t data_len)
/* Use like:
* struct sockaddr_storage sa_storage;
* ares_socklen_t salen = sizeof(sa_storage);
* struct sockaddr *sa = (struct sockaddr *)&sa_storage;
* ares__conn_set_sockaddr(conn, sa, &salen);
*/
static ares_status_t ares__conn_set_sockaddr(const ares_conn_t *conn,
struct sockaddr *sa,
ares_socklen_t *salen)
{
if (channel->sock_funcs && channel->sock_funcs->arecvfrom) {
return channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0,
channel->sock_func_cb_data);
ares_server_t *server = conn->server;
unsigned short port =
conn->flags & ARES_CONN_FLAG_TCP ? server->tcp_port : server->udp_port;
switch (server->addr.family) {
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa;
if (*salen < (ares_socklen_t)sizeof(*sin)) {
return ARES_EFORMERR;
}
*salen = sizeof(*sin);
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
memcpy(&sin->sin_addr, &server->addr.addr.addr4, sizeof(sin->sin_addr));
}
return ARES_SUCCESS;
case AF_INET6:
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)sa;
if (*salen < (ares_socklen_t)sizeof(*sin6)) {
return ARES_EFORMERR;
}
*salen = sizeof(*sin6);
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
memcpy(&sin6->sin6_addr, &server->addr.addr.addr6,
sizeof(sin6->sin6_addr));
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
sin6->sin6_scope_id = server->ll_scope;
#endif
}
return ARES_SUCCESS;
default:
break;
}
/* sread() is a wrapper for read() or recv() depending on the system */
return sread(s, data, data_len);
return ARES_EBADFAMILY;
}
static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn, ares_bool_t early)
{
struct sockaddr_storage sa_storage;
int rv;
ares_socklen_t len = sizeof(sa_storage);
/* We call this twice on TFO, if we already have the IP we can go ahead and
* skip processing */
if (!early && conn->self_ip.family != AF_UNSPEC) {
return ARES_SUCCESS;
}
memset(&sa_storage, 0, sizeof(sa_storage));
rv = getsockname(conn->fd, (struct sockaddr *)(void *)&sa_storage, &len);
if (rv != 0) {
/* During TCP FastOpen, we can't get the IP this early since connect()
* may not be called. That's ok, we'll try again later */
if (early && conn->flags & ARES_CONN_FLAG_TCP &&
conn->flags & ARES_CONN_FLAG_TFO) {
memset(&conn->self_ip, 0, sizeof(conn->self_ip));
return ARES_SUCCESS;
}
return ARES_ECONNREFUSED;
}
if (!ares_sockaddr_to_ares_addr(&conn->self_ip, NULL,
(struct sockaddr *)(void *)&sa_storage)) {
return ARES_ECONNREFUSED;
}
return ARES_SUCCESS;
}
ares_ssize_t ares__conn_write(ares_conn_t *conn, const void *data, size_t len)
{
ares_channel_t *channel = conn->server->channel;
int flags = 0;
#ifdef HAVE_MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
if (channel->sock_funcs && channel->sock_funcs->asendv) {
struct iovec vec;
vec.iov_base = (void *)((size_t)data); /* Cast off const */
vec.iov_len = len;
return channel->sock_funcs->asendv(conn->fd, &vec, 1,
channel->sock_func_cb_data);
}
if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL);
#if defined(TFO_USE_SENDTO) && TFO_USE_SENDTO
{
struct sockaddr_storage sa_storage;
ares_socklen_t salen = sizeof(sa_storage);
struct sockaddr *sa = (struct sockaddr *)&sa_storage;
ares_status_t status;
ares_ssize_t rv;
status = ares__conn_set_sockaddr(conn, sa, &salen);
if (status != ARES_SUCCESS) {
return status;
}
rv = (ares_ssize_t)sendto((SEND_TYPE_ARG1)conn->fd, (SEND_TYPE_ARG2)data,
(SEND_TYPE_ARG3)len, (SEND_TYPE_ARG4)flags, sa,
salen);
/* If using TFO, we might not have been able to get an IP earlier, since
* we hadn't informed the OS of the destination. When using sendto()
* now we have so we should be able to fetch it */
ares_conn_set_self_ip(conn, ARES_TRUE);
return rv;
}
#endif
}
return (ares_ssize_t)send((SEND_TYPE_ARG1)conn->fd, (SEND_TYPE_ARG2)data,
(SEND_TYPE_ARG3)len, (SEND_TYPE_ARG4)flags);
}
/*
@ -192,22 +388,6 @@ static ares_status_t configure_socket(ares_conn_t *conn)
}
#endif
#ifdef TCP_NODELAY
if (conn->flags & ARES_CONN_FLAG_TCP) {
/*
* Disable the Nagle algorithm (only relevant for TCP sockets, and thus not
* in configure_socket). In general, in DNS lookups we're pretty much
* interested in firing off a single request and then waiting for a reply,
* so batching isn't very interesting.
*/
int opt = 1;
if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (void *)&opt,
sizeof(opt)) != 0) {
return ARES_ECONNREFUSED;
}
}
#endif
/* Set the socket's send and receive buffer sizes. */
if (channel->socket_send_buffer_size > 0 &&
setsockopt(conn->fd, SOL_SOCKET, SO_SNDBUF,
@ -225,10 +405,10 @@ static ares_status_t configure_socket(ares_conn_t *conn)
#ifdef SO_BINDTODEVICE
if (ares_strlen(channel->local_dev_name)) {
/* Only root can do this, and usually not fatal if it doesn't work, so
* just continue on. */
setsockopt(conn->fd, SOL_SOCKET, SO_BINDTODEVICE, channel->local_dev_name,
sizeof(channel->local_dev_name));
/* Only root can do this, and usually not fatal if it doesn't work, so
* just continue on. */
setsockopt(conn->fd, SOL_SOCKET, SO_BINDTODEVICE, channel->local_dev_name,
sizeof(channel->local_dev_name));
}
#endif
@ -256,6 +436,33 @@ static ares_status_t configure_socket(ares_conn_t *conn)
set_ipv6_v6only(conn->fd, 0);
}
if (conn->flags & ARES_CONN_FLAG_TCP) {
int opt = 1;
#ifdef TCP_NODELAY
/*
* Disable the Nagle algorithm (only relevant for TCP sockets, and thus not
* in configure_socket). In general, in DNS lookups we're pretty much
* interested in firing off a single request and then waiting for a reply,
* so batching isn't very interesting.
*/
if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (void *)&opt,
sizeof(opt)) != 0) {
return ARES_ECONNREFUSED;
}
#endif
if (conn->flags & ARES_CONN_FLAG_TFO) {
#if defined(TFO_CLIENT_SOCKOPT)
if (setsockopt(conn->fd, IPPROTO_TCP, TFO_CLIENT_SOCKOPT, (void *)&opt,
sizeof(opt)) != 0) {
/* Disable TFO if flag can't be set. */
conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO);
}
#endif
}
}
return ARES_SUCCESS;
}
@ -297,61 +504,81 @@ ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr,
return ARES_FALSE;
}
static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn)
static ares_status_t ares__conn_connect(ares_conn_t *conn, struct sockaddr *sa,
ares_socklen_t salen)
{
/* Some old systems might not have sockaddr_storage, so we make a union
* that's guaranteed to be large enough */
union {
struct sockaddr sa;
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
} from;
/* Normal non TCPFastOpen style connect */
if (!(conn->flags & ARES_CONN_FLAG_TFO)) {
return ares__connect_socket(conn->server->channel, conn->fd, sa, salen);
}
int rv;
ares_socklen_t len = sizeof(from);
/* FreeBSD don't want any sort of connect() so skip */
#if defined(TFO_SKIP_CONNECT) && TFO_SKIP_CONNECT
return ARES_SUCCESS;
#elif defined(TFO_USE_CONNECTX) && TFO_USE_CONNECTX
{
int rv;
int err;
memset(&from, 0, sizeof(from));
do {
sa_endpoints_t endpoints;
memset(&endpoints, 0, sizeof(endpoints));
endpoints.sae_dstaddr = sa;
endpoints.sae_dstaddrlen = salen;
rv = getsockname(conn->fd, &from.sa, &len);
if (rv != 0) {
return ARES_ECONNREFUSED;
}
rv = connectx(conn->fd, &endpoints, SAE_ASSOCID_ANY,
CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
NULL, 0, NULL, NULL);
if (!ares_sockaddr_to_ares_addr(&conn->self_ip, NULL, &from.sa)) {
return ARES_ECONNREFUSED;
}
err = SOCKERRNO;
if (rv == -1 && err != EINPROGRESS && err != EWOULDBLOCK) {
return ARES_ECONNREFUSED;
}
} while (rv == -1 && err == EINTR);
}
return ARES_SUCCESS;
#elif defined(TFO_SUPPORTED) && TFO_SUPPORTED
return ares__connect_socket(conn->server->channel, conn->fd, sa, salen);
#else
/* Shouldn't be possible */
return ARES_ECONNREFUSED;
#endif
}
ares_status_t ares__open_connection(ares_conn_t **conn_out,
ares_channel_t *channel,
ares_server_t *server, ares_query_t *query)
ares_server_t *server, ares_bool_t is_tcp)
{
ares_socklen_t salen;
ares_status_t status;
ares_bool_t is_tcp = query->using_tcp;
union {
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
} saddr;
struct sockaddr *sa;
ares_conn_t *conn;
ares__llist_node_t *node = NULL;
int sock_type = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
ares_status_t status;
struct sockaddr_storage sa_storage;
ares_socklen_t salen = sizeof(sa_storage);
struct sockaddr *sa = (struct sockaddr *)&sa_storage;
ares_conn_t *conn;
ares__llist_node_t *node = NULL;
int stype = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
*conn_out = NULL;
conn = ares_malloc(sizeof(*conn));
if (conn == NULL) {
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
}
memset(conn, 0, sizeof(*conn));
conn->fd = ARES_SOCKET_BAD;
conn->server = server;
conn->queries_to_conn = ares__llist_create(NULL);
conn->flags = is_tcp?ARES_CONN_FLAG_TCP:ARES_CONN_FLAG_NONE;
conn->flags = is_tcp ? ARES_CONN_FLAG_TCP : ARES_CONN_FLAG_NONE;
/* Enable TFO if the OS supports it and we were passed in data to send during
* the connect. It might be disabled later if an error is encountered. Make
* sure a user isn't overriding anything. */
if (conn->flags & ARES_CONN_FLAG_TCP && channel->sock_funcs == NULL &&
TFO_SUPPORTED) {
conn->flags |= ARES_CONN_FLAG_TFO;
}
if (conn->queries_to_conn == NULL) {
/* LCOV_EXCL_START: OutOfMemory */
status = ARES_ENOMEM;
@ -359,36 +586,14 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
/* LCOV_EXCL_STOP */
}
switch (server->addr.family) {
case AF_INET:
sa = (void *)&saddr.sa4;
salen = sizeof(saddr.sa4);
memset(sa, 0, (size_t)salen);
saddr.sa4.sin_family = AF_INET;
saddr.sa4.sin_port = htons(is_tcp ? server->tcp_port : server->udp_port);
memcpy(&saddr.sa4.sin_addr, &server->addr.addr.addr4,
sizeof(saddr.sa4.sin_addr));
break;
case AF_INET6:
sa = (void *)&saddr.sa6;
salen = sizeof(saddr.sa6);
memset(sa, 0, (size_t)salen);
saddr.sa6.sin6_family = AF_INET6;
saddr.sa6.sin6_port = htons(is_tcp ? server->tcp_port : server->udp_port);
memcpy(&saddr.sa6.sin6_addr, &server->addr.addr.addr6,
sizeof(saddr.sa6.sin6_addr));
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
saddr.sa6.sin6_scope_id = server->ll_scope;
#endif
break;
default:
status = ARES_EBADFAMILY; /* LCOV_EXCL_LINE */
goto done;
/* Convert into the struct sockaddr structure needed by the OS */
status = ares__conn_set_sockaddr(conn, sa, &salen);
if (status != ARES_SUCCESS) {
goto done;
}
/* Acquire a socket. */
conn->fd = ares__open_socket(channel, server->addr.family, sock_type, 0);
conn->fd = ares__open_socket(channel, server->addr.family, stype, 0);
if (conn->fd == ARES_SOCKET_BAD) {
status = ARES_ECONNREFUSED;
goto done;
@ -401,29 +606,36 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
}
if (channel->sock_config_cb) {
int err = channel->sock_config_cb(conn->fd, sock_type, channel->sock_config_cb_data);
int err =
channel->sock_config_cb(conn->fd, stype, channel->sock_config_cb_data);
if (err < 0) {
status = ARES_ECONNREFUSED;
goto done;
}
}
/* Connect to the server. */
status = ares__connect_socket(channel, conn->fd, sa, salen);
/* Connect */
status = ares__conn_connect(conn, sa, salen);
if (status != ARES_SUCCESS) {
goto done;
}
if (channel->sock_create_cb) {
int err = channel->sock_create_cb(conn->fd, sock_type, channel->sock_create_cb_data);
int err =
channel->sock_create_cb(conn->fd, stype, channel->sock_create_cb_data);
if (err < 0) {
status = ARES_ECONNREFUSED;
goto done;
}
}
/* Let the connection know we haven't written our first packet yet for TFO */
if (conn->flags & ARES_CONN_FLAG_TFO) {
conn->flags |= ARES_CONN_FLAG_TFO_INITIAL;
}
/* Need to store our own ip for DNS cookie support */
status = ares_conn_set_self_ip(conn);
status = ares_conn_set_self_ip(conn, ARES_FALSE);
if (status != ARES_SUCCESS) {
goto done; /* LCOV_EXCL_LINE: UntestablePath */
}
@ -452,7 +664,7 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
/* LCOV_EXCL_STOP */
}
SOCK_STATE_CALLBACK(channel, conn->fd, 1, 0);
SOCK_STATE_CALLBACK(channel, conn->fd, 1, is_tcp ? 1 : 0);
if (is_tcp) {
server->tcp_conn = conn;
@ -481,10 +693,10 @@ ares_socket_t ares__open_socket(ares_channel_t *channel, int af, int type,
return socket(af, type, protocol);
}
ares_status_t ares__connect_socket(ares_channel_t *channel,
ares_socket_t sockfd,
ares_status_t ares__connect_socket(ares_channel_t *channel,
ares_socket_t sockfd,
const struct sockaddr *addr,
ares_socklen_t addrlen)
ares_socklen_t addrlen)
{
int rv;
int err;
@ -521,26 +733,6 @@ void ares__close_socket(ares_channel_t *channel, ares_socket_t s)
}
}
#ifndef HAVE_WRITEV
/* Structure for scatter/gather I/O. */
struct iovec {
void *iov_base; /* Pointer to data. */
size_t iov_len; /* Length of data. */
};
#endif
ares_ssize_t ares__socket_write(ares_channel_t *channel, ares_socket_t s,
const void *data, size_t len)
{
if (channel->sock_funcs && channel->sock_funcs->asendv) {
struct iovec vec;
vec.iov_base = (void *)((size_t)data); /* Cast off const */
vec.iov_len = len;
return channel->sock_funcs->asendv(s, &vec, 1, channel->sock_func_cb_data);
}
return swrite(s, data, len);
}
void ares_set_socket_callback(ares_channel_t *channel,
ares_sock_create_callback cb, void *data)
{

@ -67,6 +67,9 @@
/* Define to 1 if you have the connect function. */
#cmakedefine HAVE_CONNECT 1
/* Define to 1 if you have the connectx function. */
#cmakedefine HAVE_CONNECTX 1
/* define if the compiler supports basic C++11 syntax */
#cmakedefine HAVE_CXX11 1

@ -160,8 +160,15 @@ struct ares_conn;
typedef struct ares_conn ares_conn_t;
typedef enum {
ARES_CONN_FLAG_NONE = 0, /*!< No flags */
ARES_CONN_FLAG_TCP = 1 << 0 /*!< TCP not UDP */
/*! No flags */
ARES_CONN_FLAG_NONE = 0,
/*! TCP connection, not UDP */
ARES_CONN_FLAG_TCP = 1 << 0,
/*! TCP Fast Open is enabled and being used if supported by the OS */
ARES_CONN_FLAG_TFO = 1 << 1,
/*! TCP Fast Open has not yet sent its first packet. Gets unset on first
* write to a connection */
ARES_CONN_FLAG_TFO_INITIAL = 1 << 2
} ares_conn_flags_t;
struct ares_conn {
@ -597,14 +604,14 @@ ares_status_t ares__addrinfo_localhost(const char *name, unsigned short port,
struct ares_addrinfo *ai);
ares_status_t ares__open_connection(ares_conn_t **conn_out,
ares_channel_t *channel,
ares_server_t *server, ares_query_t *query);
ares_server_t *server, ares_bool_t is_tcp);
ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr,
unsigned short *port,
const struct sockaddr *sockaddr);
ares_socket_t ares__open_socket(ares_channel_t *channel, int af, int type,
int protocol);
ares_ssize_t ares__socket_write(ares_channel_t *channel, ares_socket_t s,
const void *data, size_t len);
ares_bool_t ares__socket_try_again(int errnum);
ares_ssize_t ares__conn_write(ares_conn_t *conn, const void *data, size_t len);
ares_ssize_t ares__socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
void *data, size_t data_len, int flags,
struct sockaddr *from,
@ -612,11 +619,11 @@ ares_ssize_t ares__socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
ares_ssize_t ares__socket_recv(ares_channel_t *channel, ares_socket_t s,
void *data, size_t data_len);
void ares__close_socket(ares_channel_t *channel, ares_socket_t s);
ares_status_t ares__connect_socket(ares_channel_t *channel,
ares_socket_t sockfd,
ares_status_t ares__connect_socket(ares_channel_t *channel,
ares_socket_t sockfd,
const struct sockaddr *addr,
ares_socklen_t addrlen);
void ares__destroy_server(ares_server_t *server);
ares_socklen_t addrlen);
void ares__destroy_server(ares_server_t *server);
ares_status_t ares__servers_update(ares_channel_t *channel,
ares__llist_t *server_list,

@ -46,7 +46,6 @@
static void timeadd(ares_timeval_t *now, size_t millisecs);
static ares_bool_t try_again(int errnum);
static void write_tcp_data(ares_channel_t *channel, fd_set *write_fds,
ares_socket_t write_fd);
static void read_packets(ares_channel_t *channel, fd_set *read_fds,
@ -236,34 +235,6 @@ void ares_process_fd(ares_channel_t *channel,
processfds(channel, NULL, read_fd, NULL, write_fd);
}
/* Return 1 if the specified error number describes a readiness error, or 0
* otherwise. This is mostly for HP-UX, which could return EAGAIN or
* EWOULDBLOCK. See this man page
*
* http://devrsrc1.external.hp.com/STKS/cgi-bin/man2html?
* manpage=/usr/share/man/man2.Z/send.2
*/
static ares_bool_t try_again(int errnum)
{
#if !defined EWOULDBLOCK && !defined EAGAIN
# error "Neither EWOULDBLOCK nor EAGAIN defined"
#endif
#ifdef EWOULDBLOCK
if (errnum == EWOULDBLOCK) {
return ARES_TRUE;
}
#endif
#if defined EAGAIN && EAGAIN != EWOULDBLOCK
if (errnum == EAGAIN) {
return ARES_TRUE;
}
#endif
return ARES_FALSE;
}
/* If any TCP sockets select true for writing, write out queued data
* we have for them.
*/
@ -310,9 +281,9 @@ static void write_tcp_data(ares_channel_t *channel, fd_set *write_fds,
}
data = ares__buf_peek(server->tcp_send, &data_len);
count = ares__socket_write(channel, server->tcp_conn->fd, data, data_len);
count = ares__conn_write(server->tcp_conn, data, data_len);
if (count <= 0) {
if (!try_again(SOCKERRNO)) {
if (!ares__socket_try_again(SOCKERRNO)) {
handle_conn_error(server->tcp_conn, ARES_TRUE, ARES_ECONNREFUSED);
}
continue;
@ -355,7 +326,7 @@ static void read_tcp_data(ares_channel_t *channel, ares_conn_t *conn,
count = ares__socket_recv(channel, conn->fd, ptr, ptr_len);
if (count <= 0) {
ares__buf_append_finish(server->tcp_parser, 0);
if (!(count == -1 && try_again(SOCKERRNO))) {
if (!(count == -1 && ares__socket_try_again(SOCKERRNO))) {
handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED);
}
return;
@ -508,7 +479,7 @@ static void read_udp_packets_fd(ares_channel_t *channel, ares_conn_t *conn,
* tcp */
continue;
} else if (read_len < 0) {
if (try_again(SOCKERRNO)) {
if (ares__socket_try_again(SOCKERRNO)) {
break;
}
@ -797,8 +768,8 @@ static void handle_conn_error(ares_conn_t *conn, ares_bool_t critical_failure,
/* Increment failures first before requeue so it is unlikely to requeue
* to the same server */
if (critical_failure) {
server_increment_failures(server,
(conn->flags & ARES_CONN_FLAG_TCP)?ARES_TRUE:ARES_FALSE);
server_increment_failures(
server, (conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE);
}
/* This will requeue any connections automatically */
@ -932,7 +903,6 @@ static ares_server_t *ares__failover_server(ares_channel_t *channel)
return first_server;
}
static size_t ares__calc_query_timeout(const ares_query_t *query,
const ares_server_t *server,
const ares_timeval_t *now)
@ -1017,63 +987,80 @@ static ares_conn_t *ares__fetch_connection(ares_channel_t *channel,
return conn;
}
static ares_status_t ares__query_write(ares_conn_t *conn, ares_query_t *query,
const ares_timeval_t *now)
static ares_status_t ares__conn_query_write(ares_conn_t *conn,
ares_query_t *query,
const ares_timeval_t *now)
{
unsigned char *qbuf = NULL;
size_t qbuf_len = 0;
ares_server_t *server = conn->server;
ares_channel_t *channel = server->channel;
ares_ssize_t len;
ares_server_t *server = conn->server;
ares_channel_t *channel = server->channel;
ares_status_t status;
status = ares_cookie_apply(query->query, conn, now);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write(query->query, &qbuf, &qbuf_len);
if (status != ARES_SUCCESS) {
goto done;
return status;
}
if (conn->flags & ARES_CONN_FLAG_TCP) {
size_t prior_len = ares__buf_len(server->tcp_send);
status =
ares__buf_append_be16(server->tcp_send, (unsigned short)qbuf_len);
status = ares_dns_write_buf_tcp(query->query, server->tcp_send);
if (status != ARES_SUCCESS) {
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
return status;
}
status = ares__buf_append(server->tcp_send, qbuf, qbuf_len);
if (status != ARES_SUCCESS) {
goto done;
if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
/* When using TFO, we need to put it on the wire immediately. */
size_t data_len;
const unsigned char *data = NULL;
data = ares__buf_peek(server->tcp_send, &data_len);
len = ares__conn_write(conn, data, data_len);
if (len <= 0) {
if (ares__socket_try_again(SOCKERRNO)) {
/* This means we must not have qualified for TFO, keep the data
* buffered, wait on write signal. */
return ARES_SUCCESS;
}
/* TCP TFO might delay failure. Reflect that here */
return ARES_ECONNREFUSED;
}
/* Consume what was written */
ares__buf_consume(server->tcp_send, (size_t)len);
return ARES_SUCCESS;
}
if (prior_len == 0) {
SOCK_STATE_CALLBACK(channel, conn->fd, 1, 1);
}
} else {
if (ares__socket_write(channel, conn->fd, qbuf, qbuf_len) ==
-1) {
if (try_again(SOCKERRNO)) {
status = ARES_ESERVFAIL;
goto done;
} else {
/* UDP is connection-less, but we might receive an ICMP unreachable which
* means we can't talk to the remote host at all and that will be
* reflected here */
status = ARES_ECONNREFUSED;
goto done;
}
}
return ARES_SUCCESS;
}
status = ARES_SUCCESS;
/* UDP Here */
status = ares_dns_write(query->query, &qbuf, &qbuf_len);
if (status != ARES_SUCCESS) {
return status;
}
done:
len = ares__conn_write(conn, qbuf, qbuf_len);
ares_free(qbuf);
return status;
if (len == -1) {
if (ares__socket_try_again(SOCKERRNO)) {
return ARES_ESERVFAIL;
}
/* UDP is connection-less, but we might receive an ICMP unreachable which
* means we can't talk to the remote host at all and that will be
* reflected here */
return ARES_ECONNREFUSED;
}
return ARES_SUCCESS;
}
ares_status_t ares__send_query(ares_query_t *query, const ares_timeval_t *now)
@ -1100,7 +1087,7 @@ ares_status_t ares__send_query(ares_query_t *query, const ares_timeval_t *now)
conn = ares__fetch_connection(channel, server, query);
if (conn == NULL) {
status = ares__open_connection(&conn, channel, server, query);
status = ares__open_connection(&conn, channel, server, query->using_tcp);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:
@ -1120,7 +1107,8 @@ ares_status_t ares__send_query(ares_query_t *query, const ares_timeval_t *now)
}
}
status = ares__query_write(conn, query, now);
/* Write the query */
status = ares__conn_query_write(conn, query, now);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:

@ -231,95 +231,6 @@ struct timeval {
};
#endif
/*
* If we have the MSG_NOSIGNAL define, make sure we use
* it as the fourth argument of function send()
*/
#ifdef HAVE_MSG_NOSIGNAL
# define SEND_4TH_ARG MSG_NOSIGNAL
#else
# define SEND_4TH_ARG 0
#endif
#if defined(__minix)
/* Minix doesn't support recv on TCP sockets */
# define sread(x, y, z) \
(ares_ssize_t) \
read((RECV_TYPE_ARG1)(x), (RECV_TYPE_ARG2)(y), (RECV_TYPE_ARG3)(z))
#elif defined(HAVE_RECV)
/*
* The definitions for the return type and arguments types
* of functions recv() and send() belong and come from the
* configuration file. Do not define them in any other place.
*
* HAVE_RECV is defined if you have a function named recv()
* which is used to read incoming data from sockets. If your
* function has another name then don't define HAVE_RECV.
*
* If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2,
* RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also
* be defined.
*
* HAVE_SEND is defined if you have a function named send()
* which is used to write outgoing data on a connected socket.
* If yours has another name then don't define HAVE_SEND.
*
* If HAVE_SEND is defined then SEND_TYPE_ARG1,
* SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and
* SEND_TYPE_RETV must also be defined.
*/
# if !defined(RECV_TYPE_ARG1) || !defined(RECV_TYPE_ARG2) || \
!defined(RECV_TYPE_ARG3) || !defined(RECV_TYPE_ARG4) || \
!defined(RECV_TYPE_RETV)
/* */
Error Missing_definition_of_return_and_arguments_types_of_recv
/* */
# else
# define sread(x, y, z) \
(ares_ssize_t) recv((RECV_TYPE_ARG1)(x), (RECV_TYPE_ARG2)(y), \
(RECV_TYPE_ARG3)(z), (RECV_TYPE_ARG4)(0))
# endif
#else /* HAVE_RECV */
# ifndef sread
/* */
Error Missing_definition_of_macro_sread
/* */
# endif
#endif /* HAVE_RECV */
#if defined(__minix)
/* Minix doesn't support send on TCP sockets */
# define swrite(x, y, z) \
(ares_ssize_t) \
write((SEND_TYPE_ARG1)(x), (SEND_TYPE_ARG2)(y), (SEND_TYPE_ARG3)(z))
#elif defined(HAVE_SEND)
# if !defined(SEND_TYPE_ARG1) || !defined(SEND_TYPE_ARG2) || \
!defined(SEND_TYPE_ARG3) || !defined(SEND_TYPE_ARG4) || \
!defined(SEND_TYPE_RETV)
/* */
Error Missing_definition_of_return_and_arguments_types_of_send
/* */
# else
# define swrite(x, y, z) \
(ares_ssize_t) send((SEND_TYPE_ARG1)(x), (SEND_TYPE_ARG2)(y), \
(SEND_TYPE_ARG3)(z), (SEND_TYPE_ARG4)(SEND_4TH_ARG))
# endif
#else /* HAVE_SEND */
# ifndef swrite
/* */
Error Missing_definition_of_macro_swrite
/* */
# endif
#endif /* HAVE_SEND */
/*
* Function-like macro definition used to close a socket.
*/

@ -67,12 +67,11 @@ int ares_getsock(const ares_channel_t *channel, ares_socket_t *socks,
socks[sockindex] = conn->fd;
if (active_queries || (conn->flags & ARES_CONN_FLAG_TCP)) {
if (active_queries || conn->flags & ARES_CONN_FLAG_TCP) {
bitmap |= ARES_GETSOCK_READABLE(setbits, sockindex);
}
if ((conn->flags & ARES_CONN_FLAG_TCP) &&
ares__buf_len(server->tcp_send)) {
if (conn->flags & ARES_CONN_FLAG_TCP && ares__buf_len(server->tcp_send)) {
/* then the tcp socket is also writable! */
bitmap |= ARES_GETSOCK_WRITABLE(setbits, sockindex);
}

@ -53,8 +53,16 @@ ares_status_t ares_dns_record_rr_prealloc(ares_dns_record_t *dnsrec,
ares_dns_section_t sect, size_t cnt);
ares_dns_rr_t *ares_dns_get_opt_rr(ares_dns_record_t *rec);
const ares_dns_rr_t *ares_dns_get_opt_rr_const(const ares_dns_record_t *rec);
void ares_dns_record_write_ttl_decrement(ares_dns_record_t *dnsrec,
unsigned int ttl_decrement);
void ares_dns_record_write_ttl_decrement(ares_dns_record_t *dnsrec,
unsigned int ttl_decrement);
/* Same as ares_dns_write() but appends to an existing buffer object */
ares_status_t ares_dns_write_buf(const ares_dns_record_t *dnsrec,
ares__buf_t *buf);
/* Same as ares_dns_write_buf(), but prepends a 16bit length */
ares_status_t ares_dns_write_buf_tcp(const ares_dns_record_t *dnsrec,
ares__buf_t *buf);
/*! Create a DNS record object for a query. The arguments are the same as
* those for ares_create_query().

@ -1090,52 +1090,118 @@ static ares_status_t ares_dns_write_rr(const ares_dns_record_t *dnsrec,
return ARES_SUCCESS;
}
ares_status_t ares_dns_write(const ares_dns_record_t *dnsrec,
unsigned char **buf, size_t *buf_len)
ares_status_t ares_dns_write_buf(const ares_dns_record_t *dnsrec,
ares__buf_t *buf)
{
ares__buf_t *b = NULL;
ares_status_t status;
ares__llist_t *namelist = NULL;
size_t orig_len;
ares_status_t status;
if (buf == NULL || buf_len == NULL || dnsrec == NULL) {
if (dnsrec == NULL || buf == NULL) {
return ARES_EFORMERR;
}
*buf = NULL;
*buf_len = 0;
orig_len = ares__buf_len(buf);
b = ares__buf_create();
if (b == NULL) {
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
status = ares_dns_write_header(dnsrec, buf);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write_header(dnsrec, b);
status = ares_dns_write_questions(dnsrec, &namelist, buf);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write_questions(dnsrec, &namelist, b);
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ANSWER, buf);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ANSWER, b);
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_AUTHORITY, buf);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_AUTHORITY, b);
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ADDITIONAL, buf);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ADDITIONAL, b);
done:
ares__llist_destroy(namelist);
if (status != ARES_SUCCESS) {
ares__buf_set_length(buf, orig_len);
}
return status;
}
ares_status_t ares_dns_write_buf_tcp(const ares_dns_record_t *dnsrec,
ares__buf_t *buf)
{
ares_status_t status;
size_t orig_len;
size_t msg_len;
size_t len;
if (dnsrec == NULL || buf == NULL) {
return ARES_EFORMERR;
}
orig_len = ares__buf_len(buf);
/* Write placeholder for length */
status = ares__buf_append_be16(buf, 0);
if (status != ARES_SUCCESS) {
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
}
/* Write message */
status = ares_dns_write_buf(dnsrec, buf);
if (status != ARES_SUCCESS) {
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
}
len = ares__buf_len(buf);
msg_len = len - orig_len - 2;
if (msg_len > 65535) {
status = ARES_EBADQUERY;
goto done;
}
/* Now we need to overwrite the length, so we jump back to the original
* message length, overwrite the section and jump back */
ares__buf_set_length(buf, orig_len);
ares__buf_append_be16(buf, (unsigned short)(msg_len & 0xFFFF));
ares__buf_set_length(buf, len);
done:
ares__llist_destroy(namelist);
if (status != ARES_SUCCESS) {
ares__buf_set_length(buf, orig_len);
}
return status;
}
ares_status_t ares_dns_write(const ares_dns_record_t *dnsrec,
unsigned char **buf, size_t *buf_len)
{
ares__buf_t *b = NULL;
ares_status_t status;
if (buf == NULL || buf_len == NULL || dnsrec == NULL) {
return ARES_EFORMERR;
}
*buf = NULL;
*buf_len = 0;
b = ares__buf_create();
if (b == NULL) {
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
}
status = ares_dns_write_buf(dnsrec, b);
if (status != ARES_SUCCESS) {
ares__buf_destroy(b);

@ -204,7 +204,7 @@ ares_status_t ares__buf_set_length(ares__buf_t *buf, size_t len)
return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
}
buf->data_len = len;
buf->data_len = len + buf->offset;
return ARES_SUCCESS;
}

@ -876,6 +876,8 @@ TEST_P(MockChannelTest, SearchDomainsBare) {
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
Process();
EXPECT_TRUE(result.done_);
EXPECT_EQ(0, result.timeouts_);
std::stringstream ss;
ss << result.host_;
EXPECT_EQ("{'www' aliases=[] addrs=[2.3.4.5]}", ss.str());

11
test/ares-test.cc vendored

@ -437,6 +437,17 @@ MockServer::MockServer(int family, unsigned short port)
setsockopt(tcpfd_, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, sizeof(optval));
#endif
/* Test system enable TCP FastOpen */
#if defined(TCP_FASTOPEN)
# ifdef __linux__
int qlen = 32;
setsockopt(tcpfd_, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
# else
int on = 1;
setsockopt(tcpfd_, IPPROTO_TCP, TCP_FASTOPEN, BYTE_CAST &on, sizeof(on));
# endif
#endif
// Create a UDP socket to receive data on.
udpfd_ = socket(family, SOCK_DGRAM, 0);
EXPECT_NE(ARES_SOCKET_BAD, udpfd_);

Loading…
Cancel
Save