Improve socket function replacement subsystem (#894)

Some external users swap out the entire IP stack, such as
[Seastar](https://github.com/scylladb/seastar) that uses DPDK. So we
need to allow them to override all network stack functions used by
c-ares. Since we don't know what network functions may be needed in the
future, we need to make a versioned structure that can be expanded.
We'll need to disable any features in the future if the versioned
interface is less than is required to support any new features.

We recently had a regression when we added DNS Cookie support as we
relied on `getsockname()` to get the local source ip address, but we
were calling the native version since there was no registered callback
available. We will mark this as optional and just skip this step in DNS
Cookie invalidation if its not available for the legacy api users.

Supersedes #893 
Authored-By: Brad House (@bradh352)
pull/895/head
Brad House 2 months ago committed by GitHub
parent 759343ae00
commit 02745437e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CMakeLists.txt
  2. 1
      configure.ac
  3. 319
      docs/ares_set_socket_functions.3
  4. 3
      docs/ares_set_socket_functions_ex.3
  5. 249
      include/ares.h
  6. 8
      include/ares_version.h
  7. 1
      src/lib/Makefile.inc
  8. 3
      src/lib/ares_config.h.cmake
  9. 40
      src/lib/ares_conn.c
  10. 4
      src/lib/ares_init.c
  11. 2
      src/lib/ares_ipv6.h
  12. 27
      src/lib/ares_private.h
  13. 11
      src/lib/ares_process.c
  14. 586
      src/lib/ares_set_socket_functions.c
  15. 13
      src/lib/ares_setup.h
  16. 459
      src/lib/ares_socket.c
  17. 101
      src/lib/ares_socket.h
  18. 4
      src/lib/ares_sortaddrinfo.c
  19. 59
      src/lib/ares_sysconfig.c
  20. 50
      src/lib/ares_sysconfig_files.c
  21. 28
      src/lib/ares_sysconfig_mac.c
  22. 6
      src/lib/ares_sysconfig_win.c
  23. 34
      src/lib/ares_update_servers.c
  24. 1
      src/lib/config-dos.h
  25. 3
      src/lib/config-win32.h
  26. 4
      src/lib/util/ares_iface_ips.c
  27. 6
      src/lib/util/ares_iface_ips.h
  28. 8
      test/ares-test-internal.cc
  29. 10
      test/ares-test.h

@ -443,6 +443,7 @@ CHECK_SYMBOL_EXISTS (IoctlSocket "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_IOCTLSO
CHECK_SYMBOL_EXISTS (recv "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_RECV) CHECK_SYMBOL_EXISTS (recv "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_RECV)
CHECK_SYMBOL_EXISTS (recvfrom "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_RECVFROM) CHECK_SYMBOL_EXISTS (recvfrom "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_RECVFROM)
CHECK_SYMBOL_EXISTS (send "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SEND) CHECK_SYMBOL_EXISTS (send "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SEND)
CHECK_SYMBOL_EXISTS (sendto "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SENDTO)
CHECK_SYMBOL_EXISTS (setsockopt "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SETSOCKOPT) CHECK_SYMBOL_EXISTS (setsockopt "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SETSOCKOPT)
CHECK_SYMBOL_EXISTS (socket "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SOCKET) CHECK_SYMBOL_EXISTS (socket "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SOCKET)
CHECK_SYMBOL_EXISTS (strcasecmp "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_STRCASECMP) CHECK_SYMBOL_EXISTS (strcasecmp "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_STRCASECMP)

@ -544,6 +544,7 @@ AC_CHECK_DECL(memmem, [AC_DEFINE([HAVE_MEMMEM], 1, [Define t
AC_CHECK_DECL(recv, [AC_DEFINE([HAVE_RECV], 1, [Define to 1 if you have `recv`] )], [], $cares_all_includes) AC_CHECK_DECL(recv, [AC_DEFINE([HAVE_RECV], 1, [Define to 1 if you have `recv`] )], [], $cares_all_includes)
AC_CHECK_DECL(recvfrom, [AC_DEFINE([HAVE_RECVFROM], 1, [Define to 1 if you have `recvfrom`] )], [], $cares_all_includes) AC_CHECK_DECL(recvfrom, [AC_DEFINE([HAVE_RECVFROM], 1, [Define to 1 if you have `recvfrom`] )], [], $cares_all_includes)
AC_CHECK_DECL(send, [AC_DEFINE([HAVE_SEND], 1, [Define to 1 if you have `send`] )], [], $cares_all_includes) AC_CHECK_DECL(send, [AC_DEFINE([HAVE_SEND], 1, [Define to 1 if you have `send`] )], [], $cares_all_includes)
AC_CHECK_DECL(sendto, [AC_DEFINE([HAVE_SENDTO], 1, [Define to 1 if you have `sendto`] )], [], $cares_all_includes)
AC_CHECK_DECL(getnameinfo, [AC_DEFINE([HAVE_GETNAMEINFO], 1, [Define to 1 if you have `getnameinfo`] )], [], $cares_all_includes) 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(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(connect, [AC_DEFINE([HAVE_CONNECT], 1, [Define to 1 if you have `connect`] )], [], $cares_all_includes)

@ -1,12 +1,63 @@
.\" Copyright (C) Daniel Stenberg .\" Copyright (C) Daniel Stenberg
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.TH ARES_SET_SOCKET_FUNCTIONS 3 "13 Dec 2016" .TH ARES_SET_SOCKET_FUNCTIONS 3 "8 Oct 2024"
.SH NAME .SH NAME
ares_set_socket_functions \- Set socket io callbacks ares_set_socket_functions, ares_set_socket_functions_ex \- Set socket io callbacks
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
#include <ares.h> #include <ares.h>
typedef enum {
ARES_SOCKFUNC_FLAG_NONBLOCKING = 1 << 0
} ares_sockfunc_flags_t;
typedef enum {
ARES_SOCKET_OPT_SENDBUF_SIZE,
ARES_SOCKET_OPT_RECVBUF_SIZE,
ARES_SOCKET_OPT_BIND_DEVICE,
ARES_SOCKET_OPT_TCP_FASTOPEN
} ares_socket_opt_t;
typedef enum {
ARES_SOCKET_CONN_TCP_FASTOPEN = 1 << 0
} ares_socket_connect_flags_t;
typedef enum {
ARES_SOCKET_BIND_TCP = 1 << 0,
ARES_SOCKET_BIND_CLIENT = 1 << 1
} ares_socket_bind_flags_t;
struct ares_socket_functions_ex {
unsigned int version; /* ABI Version: must be "1" */
unsigned int flags;
ares_socket_t (*asocket)(int domain, int type, int protocol, void *user_data);
int (*aclose)(ares_socket_t sock, void *user_data);
int (*asetsockopt)(ares_socket_t sock, ares_socket_opt_t opt, void *val,
ares_socklen_t val_size, void *user_data);
int (*aconnect)(ares_socket_t sock, const struct sockaddr *address,
ares_socklen_t address_len, unsigned int flags,
void *user_data);
ares_ssize_t (*arecvfrom)(ares_socket_t sock, void *buffer, size_t length,
int flags, struct sockaddr *address,
ares_socklen_t *address_len, void *user_data);
ares_ssize_t (*asendto)(ares_socket_t sock, const void *buffer, size_t length,
int flags, const struct sockaddr *address,
ares_socklen_t address_len, void *user_data);
int (*agetsockname)(ares_socket_t sock, struct sockaddr *address,
ares_socklen_t *address_len, void *user_data);
int (*abind)(ares_socket_t sock, unsigned int flags,
const struct sockaddr *address, socklen_t address_len,
void *user_data);
unsigned int (*aif_nametoindex)(const char *ifname, void *user_data);
const char *(*aif_indextoname)(unsigned int ifindex, char *ifname_buf,
size_t ifname_buf_len, void *user_data);
};
ares_status_t ares_set_socket_functions_ex(ares_channel_t *channel,
const struct ares_socket_functions_ex *funcs, void *user_data);
struct ares_socket_functions { struct ares_socket_functions {
ares_socket_t (*\fIasocket\fP)(int, int, int, void *); ares_socket_t (*\fIasocket\fP)(int, int, int, void *);
int (*\fIaclose\fP)(ares_socket_t, void *); int (*\fIaclose\fP)(ares_socket_t, void *);
@ -22,80 +73,266 @@ void ares_set_socket_functions(ares_channel_t *\fIchannel\fP,
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
.PP .PP
This function sets a set of callback \fIfunctions\fP in the given ares channel handle.
Cannot be used when \fBARES_OPT_EVENT_THREAD\fP is passed to \fIares_init_options(3)\fP. \fBares_set_socket_functions_ex(3)\fP sets a set of callback \fIfunctions\fP in
the given ares channel handle. Cannot be used when \fBARES_OPT_EVENT_THREAD\fP
These callback functions will be invoked to create/destroy socket objects and perform is passed to \fIares_init_options(3)\fP. This function replaces the now
io, instead of the normal system calls. A client application can override normal network deprecated \fBares_set_socket_functions(3)\fP call.
operation fully through this functionality, and provide its own transport layer. You
can choose to only implement some of the socket functions, and provide NULL to any These callback functions will be invoked to create/destroy socket objects and
others and c-ares will use its built-in system functions in that case. perform io, instead of the normal system calls. A client application can
override normal network operation fully through this functionality, and provide
its own transport layer.
Some callbacks may be optional and are documented as such below, but failing
to implement such callbacks will disable certain features within c-ares. It
is strongly recommended to implement all callbacks.
All callback functions are expected to operate like their system equivalents,
and to set \fBerrno(3)\fP or \fBWSASetLastError(3)\fP to an appropriate error
code on failure. It is strongly recommended that io callbacks are implemented
to be asynchronous and indicated as such in the \fIflags\fP member. The io
callbacks can return error codes of \fBEAGAIN\fP, \fBEWOULDBLOCK\fP, or
\fBWSAEWOULDBLOCK\fP when they would otherwise block.
The \fIuser_data\fP value is provided to each callback function invocation to
serve as context.
The \fBares_socket_functions_ex(3)\fP must provide the following structure
members and callbacks (which are different from the
\fBares_socket_functions_(3)\fP members and callbacks):
.TP 18
.B \fIversion\fP
.br
ABI Version of structure. Must be set to a value of "1".
.TP 18
.B \fIflags\fP
.br
Flags available are specified in \fIares_sockfunc_flags_t\fP.
.TP 18
.B \fIasocket\fP
.B ares_socket_t(*)(int \fIdomain\fP, int \fItype\fP, int \fIprotocol\fP, void * \fIuser_data\fP)
.br
\fBRequired\fP. Creates an endpoint for communication and returns a descriptor. \fIdomain\fP,
\fItype\fP, and \fIprotocol\fP each correspond to the parameters of
\fBsocket(2)\fP. Returns a handle to the newly created socket, or
\fBARES_SOCKET_BAD\fP on error.
.TP 18
.B \fIaclose\fP
.B int(*)(ares_socket_t \fIfd\fP, void * \fIuser_data\fP)
.br
\fBRequired\fP. Closes the socket endpoint indicated by \fIfd\fP. See \fBclose(2)\fP.
.TP 18
.B \fIasetsockopt\fP
.B int(*)(ares_socket_t \fIfd\fP, ares_socket_opt_t \fIopt\fP, void * \fIval\fP, ares_socklen_t \fIval_size\fP, void * \fIuser_data\fP)
.br
\fBRequired\fP. Set socket option. This shares a similar syntax to the BSD \fIsetsockopt(2)\fP
call, however c-ares uses different options for portability. The value is
a pointer to the desired value, and each option has its own data type listed
in the options below defined in \fIares_socket_opt_t\fP.
.TP 18
.B \fIaconnect\fP
.B int(*)(ares_socket_t \fIfd\fP, const struct sockaddr * \fIaddr\fP, ares_socklen_t \fIaddr_len\fP, unsigned int \fIflags\fP, void * \fIuser_data\fP)
.br
\fBRequried\fP. Initiate a connection to the address indicated by \fIaddr\fP on
a socket. Additional flags controlling behavior are in
\fIares_socket_connect_flags_t\fP. See \fBconnect(2)\fP.
.TP 18
.B \fIarecvfrom\fP
.B ares_ssize_t(*)(ares_socket_t \fIfd\fP, void * \fIbuffer\fP, size_t \fIbuf_size\fP, int \fIflags\fP, struct sockaddr * \fIaddr\fP, ares_socklen_t * \fIaddr_len\fP, void * \fIuser_data\fP)
.br
\fBRequired\fP. Receives data from remote socket endpoint, if available. If the
\fIaddr\fP parameter is not NULL and the connection protocol provides the source
address, the callback should fill this in. The \fIflags\fP parameter is
currently unused. See \fBrecvfrom(2)\fP.
.TP 18
.B \fIasendto\fP
.B ares_ssize_t(*)(ares_socket_t \fIfd\fP, const void * \fIbuffer\fP, size_t \fIlength\fP, int \fIflags\fP, const struct sockaddr * \fIaddress\fP, ares_socklen_t \fIaddress_len\fP, void * \fIuser_data\fP)
.br
\fBRequired\fP. Send data, as provided by the \fIbuffer\fP, to the socket
endpoint. The \fIflags\fP member may be used on systems that have
\fBMSG_NOSIGNAL\fP defined but is otherwise unused. An \fIaddress\fP is
provided primarily to support TCP FastOpen scenarios, which will be NULL in
other circumstances. See \fBsendto(2)\fP.
.TP 18
.B \fIagetsockname\fP
.B int(*)(ares_socket_t \fIfd\fP, struct sockaddr * \fIaddress\fP, ares_socklen_t * \fIaddress_len\fP, void * \fIuser_data\fP)
.br
Optional. Retrieve the local address of a socket and store it into the provided
\fIaddress\fP buffer. May impact DNS Cookies if not provided. See
\fBgetsockname(2)\fP.
.TP 18
.B \fIabind\fP
.B int(*)(ares_socket_t \fIfd\fP, unsigned int \fIflags\fP, const struct sockaddr * \fIaddress\fP, ares_socklen_t \fIaddress_len\fP, void * \fIuser_data\fP)
.br
Optional. Bind the socket to an address. This can be used for client
connections to bind the source address for packets before connect, or
for server connections to bind to an address and port before listening.
Currently c-ares only supports client connections. \fIflags\fP from
\fIares_socket_bind_flags_t\fP can be specified. See \fBbind(2)\fP.
.TP 18
.B \fIaif_nametoindex\fP
.B unsigned int(*)(const char * \fIifname\fP, void * \fIuser_data\fP)
.br
Optional. Convert an interface name into the interface index. If this
callback is not specified, then IPv6 Link-Local DNS servers cannot be used.
See \fBif_nametoindex(2)\fP.
.TP 18
.B \fIaif_indextoname\fP
.B const char *(*)(unsigned int \fIifindex\fP, char * \fIifname_buf\fP, size_t \fIifname_buf_len\fP, void * \fIuser_data\fP)
.br
Optional. Convert an interface index into the interface name. If this
callback is not specified, then IPv6 Link-Local DNS servers cannot be used.
\fIifname_buf\fP must be at least \fBIF_NAMESIZE\fP or \fBIFNAMSIZ\fP in size.
See \fBif_indextoname(2)\fP.
.PP .PP
All callback functions are expected to operate like their system equivalents, and to \fBares_sockfunc_flags_t\fP values:
set
.BR errno(3) .TP 31
to an appropriate error code on failure. C-ares also expects all io functions to behave .B \fIARES_SOCKFUNC_FLAG_NONBLOCKING\fP
asynchronously, i.e. as if the socket object has been set to non-blocking mode. Thus .br
read/write calls (for TCP connections) are expected to often generate Used to indicate the implementation of the io functions are asynchronous.
.BR EAGAIN
or
.BR EWOULDBLOCK.
.PP .PP
The \fIuser_data\fP value is provided to each callback function invocation to serve as \fBares_socket_opt_t\fP values:
context.
.TP 31
.B \fIARES_SOCKET_OPT_SENDBUF_SIZE\fP
.br
Set the Send Buffer size. Value is a pointer to an int. (SO_SNDBUF).
.TP 31
.B \fIARES_SOCKET_OPT_RECVBUF_SIZE\fP
.br
Set the Receive Buffer size. Value is a pointer to an int. (SO_RCVBUF).
.TP 31
.B \fIARES_SOCKET_OPT_BIND_DEVICE\fP
.br
Set the network interface to use as the source for communication. Value is a C
string. (SO_BINDTODEVICE)
.TP 31
.B \fIARES_SOCKET_OPT_TCP_FASTOPEN\fP
.br
Enable TCP Fast Open. Value is a pointer to an \fIares_bool_t\fP. On some
systems this could be a no-op if it is known it is on by default and
return success. Other systems may be a no-op if known the system does
not support the feature and returns failure with errno set to \fBENOSYS\fP or
\fBWSASetLastError(WSAEOPNOTSUPP);\fP.
.PP
\fBares_socket_connect_flags_t\fP values:
.TP 31
.B \fIARES_SOCKET_CONN_TCP_FASTOPEN\fP
.br
Connect using TCP Fast Open.
.PP
\fBares_socket_bind_flags_t\fP values:
.TP 31
.B \fIARES_SOCKET_BIND_TCP\fP
.br
Bind is for a TCP connection.
.TP 31
.B \fIARES_SOCKET_BIND_CLIENT\fP
.br
Bind is for a client connection, not server.
.PP .PP
The
.B ares_socket_functions \fBares_set_socket_functions(3)\fP sets a set of callback \fIfunctions\fP in the
must provide the following callbacks: given ares channel handle. Cannot be used when \fBARES_OPT_EVENT_THREAD\fP is
passed to \fIares_init_options(3)\fP. This function is deprecated as of
c-ares 1.34.0 in favor of \fIares_set_socket_functions_ex(3)\fP.
\fBares_set_socket_functions(3)\fP allows you to choose to only implement
some of the socket functions, and provide NULL to any others and c-ares will use
its built-in system functions in that case.
.PP
All callback functions are expected to operate like their system equivalents,
and to set \fBerrno(3)\fP or \fBWSASetLastError(3)\fP to an appropriate error
code on failure. It is strongly recommended all io functions behave
asynchronously and return error codes of \fBEAGAIN\fP, \fBEWOULDBLOCK\fP, or
\fBWSAEWOULDBLOCK\fP when they would otherwise block.
.PP
The \fIuser_data\fP value is provided to each callback function invocation to
serve as context.
.PP
The \fBares_socket_functions(3)\fP must provide the following callbacks (which
are different from the \fBares_socket_functions_ex(3)\fP callbacks):
.TP 18 .TP 18
.B \fIasocket\fP .B \fIasocket\fP
.B ares_socket_t(*)(int \fIdomain\fP, int \fItype\fP, int \fIprotocol\fP, void * \fIuser_data\fP) .B ares_socket_t(*)(int \fIdomain\fP, int \fItype\fP, int \fIprotocol\fP, void * \fIuser_data\fP)
.br .br
Creates an endpoint for communication and returns a descriptor. \fIdomain\fP, \fItype\fP, and \fIprotocol\fP Creates an endpoint for communication and returns a descriptor. \fIdomain\fP, \fItype\fP, and \fIprotocol\fP
each correspond to the parameters of each correspond to the parameters of \fBsocket(2)\fP. Returns ahandle to the
.BR socket(2). newly created socket, or -1 on error.
Returns ahandle to the newly created socket, or -1 on error.
.TP 18 .TP 18
.B \fIaclose\fP .B \fIaclose\fP
.B int(*)(ares_socket_t \fIfd\fP, void * \fIuser_data\fP) .B int(*)(ares_socket_t \fIfd\fP, void * \fIuser_data\fP)
.br .br
Closes the socket endpoint indicated by \fIfd\fP. See Closes the socket endpoint indicated by \fIfd\fP. See \fBclose(2)\fP.
.BR close(2)
.TP 18 .TP 18
.B \fIaconnect\fP .B \fIaconnect\fP
.B int(*)(ares_socket_t \fIfd\fP, const struct sockaddr * \fIaddr\fP, ares_socklen_t \fIaddr_len\fP, void * \fIuser_data\fP) .B int(*)(ares_socket_t \fIfd\fP, const struct sockaddr * \fIaddr\fP, ares_socklen_t \fIaddr_len\fP, void * \fIuser_data\fP)
.br .br
Initiate a connection to the address indicated by \fIaddr\fP on a socket. See Initiate a connection to the address indicated by \fIaddr\fP on a socket. See
.BR connect(2) \fBconnect(2)\fP
.TP 18 .TP 18
.B \fIarecvfrom\fP .B \fIarecvfrom\fP
.B ares_ssize_t(*)(ares_socket_t \fIfd\fP, void * \fIbuffer\fP, size_t \fIbuf_size\fP, int \fIflags\fP, struct sockaddr * \fIaddr\fP, ares_socklen_t * \fIaddr_len\fP, void * \fIuser_data\fP) .B ares_ssize_t(*)(ares_socket_t \fIfd\fP, void * \fIbuffer\fP, size_t \fIbuf_size\fP, int \fIflags\fP, struct sockaddr * \fIaddr\fP, ares_socklen_t * \fIaddr_len\fP, void * \fIuser_data\fP)
.br .br
Receives data from remote socket endpoint, if available. If the \fIaddr\fP parameter is not NULL and the connection protocol provides the source address, the callback should fill this in. See Receives data from remote socket endpoint, if available. If the \fIaddr\fP
.BR recvfrom(2) parameter is not NULL and the connection protocol provides the source address,
the callback should fill this in. See \fBrecvfrom(2)\fP
.TP 18 .TP 18
.B \fIasendv\fP .B \fIasendv\fP
.B ares_ssize_t(*)(ares_socket_t \fIfd\fP, const struct iovec * \fIdata\fP, int \fIlen\fP, void * \fIuser_data\fP) .B ares_ssize_t(*)(ares_socket_t \fIfd\fP, const struct iovec * \fIdata\fP, int \fIlen\fP, void * \fIuser_data\fP)
.br .br
Send data, as provided by the iovec array \fIdata\fP, to the socket endpoint. See Send data, as provided by the iovec array \fIdata\fP, to the socket endpoint.
.BR writev(2), See \fBwritev(2)\fP
.PP .PP
The The \fBares_socket_functions(3)\fP struct provided is not copied but directly
.B ares_socket_functions referenced, and must thus remain valid through out the channels and any created
struct provided is not copied but directly referenced, socket's lifetime. However, the \fBares_socket_functions_ex(3)\fP struct is
and must thus remain valid through out the channels and any created socket's lifetime. duplicated and does not need to survive past the call to the function.
.SH AVAILABILITY .SH AVAILABILITY
Added in c-ares 1.13.0 ares_socket_functions added in c-ares 1.13.0, ares_socket_functions_ex added in
c-ares 1.34.0
.SH SEE ALSO .SH SEE ALSO
.BR ares_init_options (3), .BR ares_init_options (3),
.BR socket (2), .BR socket (2),
.BR close (2), .BR close (2),
.BR connect (2), .BR connect (2),
.BR recv (2),
.BR recvfrom (2), .BR recvfrom (2),
.BR send (2), .BR sendto (2),
.BR bind (2),
.BR getsockname (2),
.BR setsockopt (2),
.BR writev (2) .BR writev (2)

@ -0,0 +1,3 @@
.\" Copyright (C) 2024 The c-ares project and its contributors.
.\" SPDX-License-Identifier: MIT
.so man3/ares_set_socket_functions.3

@ -567,10 +567,251 @@ struct ares_socket_functions {
ares_ssize_t (*asendv)(ares_socket_t, const struct iovec *, int, void *); ares_ssize_t (*asendv)(ares_socket_t, const struct iovec *, int, void *);
}; };
CARES_EXTERN void CARES_EXTERN CARES_DEPRECATED_FOR(
ares_set_socket_functions(ares_channel_t *channel, ares_set_socket_functions_ex) void ares_set_socket_functions(ares_channel_t
const struct ares_socket_functions *funcs, *channel,
void *user_data); const struct
ares_socket_functions
*funcs,
void *user_data);
/*! Flags defining behavior of socket functions */
typedef enum {
/*! Strongly recommended to create sockets as non-blocking and set this
* flag */
ARES_SOCKFUNC_FLAG_NONBLOCKING = 1 << 0
} ares_sockfunc_flags_t;
/*! Socket options in request to asetsockopt() in struct
* ares_socket_functions_ex */
typedef enum {
/*! Set the send buffer size. Value is a pointer to an int. (SO_SNDBUF) */
ARES_SOCKET_OPT_SENDBUF_SIZE,
/*! Set the recv buffer size. Value is a pointer to an int. (SO_RCVBUF) */
ARES_SOCKET_OPT_RECVBUF_SIZE,
/*! Set the network interface to use as the source for communication.
* Value is a C string. (SO_BINDTODEVICE) */
ARES_SOCKET_OPT_BIND_DEVICE,
/*! Enable TCP Fast Open. Value is a pointer to an ares_bool_t. On some
* systems this could be a no-op if it is known it is on by default and
* return success. Other systems may be a no-op if known the system does
* not support the feature and returns failure with errno set to ENOSYS or
* WSASetLastError(WSAEOPNOTSUPP).
*/
ARES_SOCKET_OPT_TCP_FASTOPEN
} ares_socket_opt_t;
/*! Flags for behavior during connect */
typedef enum {
/*! Connect using TCP Fast Open */
ARES_SOCKET_CONN_TCP_FASTOPEN = 1 << 0
} ares_socket_connect_flags_t;
/*! Flags for behavior during bind */
typedef enum {
/*! Bind is for a TCP connection */
ARES_SOCKET_BIND_TCP = 1 << 0,
/*! Bind is for a client connection, not server */
ARES_SOCKET_BIND_CLIENT = 1 << 1
} ares_socket_bind_flags_t;
/*! Socket functions to call rather than using OS-native functions */
struct ares_socket_functions_ex {
/*! ABI Version: must be "1" */
unsigned int version;
/*! Flags indicating behavior of the subsystem. One or more
* ares_sockfunc_flags_t */
unsigned int flags;
/*! REQUIRED. Create a new socket file descriptor. The file descriptor must
* be opened in non-blocking mode (so that reads and writes never block).
* Recommended other options would be to disable signals on write errors
* (SO_NOSIGPIPE), Disable the Nagle algorithm on SOCK_STREAM (TCP_NODELAY),
* and to automatically close file descriptors on exec (FD_CLOEXEC).
*
* \param[in] domain Socket domain. Valid values are AF_INET, AF_INET6.
* \param[in] type Socket type. Valid values are SOCK_STREAM (tcp) and
* SOCK_DGRAM (udp).
* \param[in] protocol In general this should be ignored, may be passed as
* 0 (use as default for type), or may be IPPROTO_UDP
* or IPPROTO_TCP.
* \param[in] user_data Pointer provided to ares_set_socket_functions_ex().
* \return ARES_SOCKET_BAD on error, or socket file descriptor on success.
* On error, it is expected to set errno (or WSASetLastError()) to an
* appropriate reason code such as EAFNOSUPPORT / WSAAFNOSUPPORT. */
ares_socket_t (*asocket)(int domain, int type, int protocol, void *user_data);
/*! REQUIRED. Close a socket file descriptor.
* \param[in] sock Socket file descriptor returned from asocket.
* \param[in] user_data Pointer provided to ares_set_socket_functions_ex().
* \return 0 on success. On failure, should set errno (or WSASetLastError)
* to an appropriate code such as EBADF / WSAEBADF */
int (*aclose)(ares_socket_t sock, void *user_data);
/*! REQUIRED. Set socket option. This shares a similar syntax to the BSD
* setsockopt() call, however we use our own options. The value is typically
* a pointer to the desired value and each option has its own data type it
* will express in the documentation.
*
* \param[in] sock Socket file descriptor returned from asocket.
* \param[in] opt Option to set.
* \param[in] val Pointer to value for option.
* \param[in] val_size Size of value.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return Return 0 on success, otherwise -1 should be returned with an
* appropriate errno (or WSASetLastError()) set. If error is ENOSYS /
* WSAEOPNOTSUPP an error will not be propagated as it will take it
* to mean it is an intentional decision to not support the feature.
*/
int (*asetsockopt)(ares_socket_t sock, ares_socket_opt_t opt, void *val,
ares_socklen_t val_size, void *user_data);
/*! REQUIRED. Connect to the remote using the supplied address. For UDP
* sockets this will bind the file descriptor to only send and receive packets
* from the remote address provided.
*
* \param[in] sock Socket file descriptor returned from asocket.
* \param[in] address Address to connect to
* \param[in] address_len Size of address structure passed
* \param[in] flags One or more ares_socket_connect_flags_t
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return Return 0 upon successful establishement, otherwise -1 should be
* returned with an appropriate errno (or WSASetLastError()) set. It
* is generally expected that most TCP connections (not using TCP Fast Open)
* will return -1 with an error of EINPROGRESS / WSAEINPROGRESS due to the
* non-blocking nature of the connection. It is then the responsibility of
* the implementation to notify of writability on the socket to indicate the
* connection has succeeded (or readability on failure to retrieve the
* appropriate error).
*/
int (*aconnect)(ares_socket_t sock, const struct sockaddr *address,
ares_socklen_t address_len, unsigned int flags,
void *user_data);
/*! REQUIRED. Attempt to read data from the remote.
*
* \param[in] sock Socket file descriptor returned from asocket.
* \param[in,out] buffer Allocated buffer to place data read from
* socket.
* \param[in] length Size of buffer
* \param[in] flags Unused, always 0.
* \param[in,out] address Buffer to hold address data was received from.
* May be NULL if address not desired.
* \param[in,out] address_len Input size of address buffer, output actual
* written size. Must be NULL if address is NULL.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return -1 on error with appropriate errno (or WSASetLastError()) set,
* such as EWOULDBLOCK / EAGAIN / WSAEWOULDBLOCK, or ECONNRESET /
* WSAECONNRESET.
*/
ares_ssize_t (*arecvfrom)(ares_socket_t sock, void *buffer, size_t length,
int flags, struct sockaddr *address,
ares_socklen_t *address_len, void *user_data);
/*! REQUIRED. Attempt to send data to the remote. Optional address may be
* specified which may be useful on unbound UDP sockets (though currently not
* used), and TCP FastOpen where the connection is delayed until first write.
*
* \param[in] sock Socket file descriptor returned from asocket.
* \param[in] buffer Containing data to place onto wire.
* \param[in] length Size of buffer
* \param[in] flags Flags for writing. Currently only used flag is
* MSG_NOSIGNAL if the host OS has such a flag. In
* general flags can be ignored.
* \param[in] address Buffer containing address to send data to. May
* be NULL.
* \param[in,out] address_len Size of address buffer. Must be 0 if address
* is NULL.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return Number of bytes written. -1 on error with appropriate errno (or
* WSASetLastError()) set.
*/
ares_ssize_t (*asendto)(ares_socket_t sock, const void *buffer, size_t length,
int flags, const struct sockaddr *address,
ares_socklen_t address_len, void *user_data);
/*! Optional. Retrieve the local address of the socket.
*
* \param[in] sock Socket file descriptor returned from asocket
* \param[in,out] address Buffer to hold address
* \param[in,out] address_len Size of address buffer on input, written size
* on output.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return 0 on success. -1 on error with an appropriate errno (or
* WSASetLastError()) set.
*/
int (*agetsockname)(ares_socket_t sock, struct sockaddr *address,
ares_socklen_t *address_len, void *user_data);
/*! Optional. Bind the socket to an address. This can be used for client
* connections to bind the source address for packets before connect, or
* for server connections to bind to an address and port before listening.
* Currently c-ares only supports client connections.
*
* \param[in] sock Socket file descriptor returned from asocket
* \param[in] flags ares_socket_bind_flags_t flags.
* \param[in] address Buffer containing address.
* \param[in] address_len Size of address buffer.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return 0 on success. -1 on error with an appropriate errno (or
* WSASetLastError()) set.
*/
int (*abind)(ares_socket_t sock, unsigned int flags,
const struct sockaddr *address, socklen_t address_len,
void *user_data);
/* Optional. Convert an interface name into the interface index. If this
* callback is not specified, then IPv6 Link-Local DNS servers cannot be used.
*
* \param[in] ifname Interface Name as NULL-terminated string.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return 0 on failure, otherwise interface index.
*/
unsigned int (*aif_nametoindex)(const char *ifname, void *user_data);
/* Optional. Convert an interface index into the interface name. If this
* callback is not specified, then IPv6 Link-Local DNS servers cannot be used.
*
* \param[in] ifindex Interface index, must be > 0
* \param[in] ifname_buf Buffer to hold interface name. Must be at least
* IFNAMSIZ in length or 32 bytes if IFNAMSIZ isn't
* defined.
* \param[in] ifname_buf_len Size of ifname_buf for verification.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return NULL on failure, otherwise pointer to provided ifname_buf
*/
const char *(*aif_indextoname)(unsigned int ifindex, char *ifname_buf,
size_t ifname_buf_len, void *user_data);
};
/*! Override the native socket functions for the OS with the provided set.
* An optional user data thunk may be specified which will be passed to
* each registered callback. Replaces ares_set_socket_functions().
*
* \param[in] channel An initialized c-ares channel.
* \param[in] funcs Structure registering the implementations for the
* various functions. See the structure definition.
* This will be duplicated and does not need to exist
* past the life of this call.
* \param[in] user_data User data thunk which will be passed to each call of
* the registered callbacks.
* \return ARES_SUCCESS on success, or another error code such as ARES_EFORMERR
* on misuse.
*/
CARES_EXTERN ares_status_t ares_set_socket_functions_ex(
ares_channel_t *channel, const struct ares_socket_functions_ex *funcs,
void *user_data);
CARES_EXTERN CARES_DEPRECATED_FOR(ares_send_dnsrec) void ares_send( CARES_EXTERN CARES_DEPRECATED_FOR(ares_send_dnsrec) void ares_send(
ares_channel_t *channel, const unsigned char *qbuf, int qlen, ares_channel_t *channel, const unsigned char *qbuf, int qlen,

@ -42,10 +42,10 @@
/* Need a level of indirection due to argument prescan to stringify a macro /* Need a level of indirection due to argument prescan to stringify a macro
* value. */ * value. */
#define ARES_STRINGIFY_PRE(s) #s #define ARES_STRINGIFY_PRE(s) #s
#define ARES_STRINGIFY(s) ARES_STRINGIFY_PRE(s) #define ARES_STRINGIFY(s) ARES_STRINGIFY_PRE(s)
#define ARES_VERSION_STR ARES_STRINGIFY(ARES_VERSION_MAJOR) "." \ #define ARES_VERSION_STR \
ARES_STRINGIFY(ARES_VERSION_MINOR) "." \ ARES_STRINGIFY(ARES_VERSION_MAJOR) \
ARES_STRINGIFY(ARES_VERSION_PATCH) "." ARES_STRINGIFY(ARES_VERSION_MINOR) "." ARES_STRINGIFY(ARES_VERSION_PATCH)
#endif #endif

@ -29,6 +29,7 @@ CSOURCES = ares_addrinfo2hostent.c \
ares_query.c \ ares_query.c \
ares_search.c \ ares_search.c \
ares_send.c \ ares_send.c \
ares_set_socket_functions.c \
ares_socket.c \ ares_socket.c \
ares_sortaddrinfo.c \ ares_sortaddrinfo.c \
ares_strerror.c \ ares_strerror.c \

@ -245,6 +245,9 @@
/* Define to 1 if you have the send function. */ /* Define to 1 if you have the send function. */
#cmakedefine HAVE_SEND 1 #cmakedefine HAVE_SEND 1
/* Define to 1 if you have the sendto function. */
#cmakedefine HAVE_SENDTO 1
/* Define to 1 if you have the setsockopt function. */ /* Define to 1 if you have the setsockopt function. */
#cmakedefine HAVE_SETSOCKOPT 1 #cmakedefine HAVE_SETSOCKOPT 1

@ -164,9 +164,12 @@ static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn, ares_bool_t early)
ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len, ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
size_t *written) size_t *written)
{ {
ares_channel_t *channel = conn->server->channel; ares_channel_t *channel = conn->server->channel;
ares_bool_t is_tfo = ARES_FALSE; ares_bool_t is_tfo = ARES_FALSE;
ares_conn_err_t err = ARES_CONN_ERR_SUCCESS; ares_conn_err_t err = ARES_CONN_ERR_SUCCESS;
struct sockaddr_storage sa_storage;
ares_socklen_t salen = 0;
struct sockaddr *sa = NULL;
*written = 0; *written = 0;
@ -177,10 +180,10 @@ ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
return ARES_CONN_ERR_WOULDBLOCK; return ARES_CONN_ERR_WOULDBLOCK;
} }
/* On initial write during TFO we need to send an address */
if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) { if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
struct sockaddr_storage sa_storage; salen = sizeof(sa_storage);
ares_socklen_t salen = sizeof(sa_storage); sa = (struct sockaddr *)&sa_storage;
struct sockaddr *sa = (struct sockaddr *)&sa_storage;
conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL); conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL);
is_tfo = ARES_TRUE; is_tfo = ARES_TRUE;
@ -188,13 +191,14 @@ ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
if (ares_conn_set_sockaddr(conn, sa, &salen) != ARES_SUCCESS) { if (ares_conn_set_sockaddr(conn, sa, &salen) != ARES_SUCCESS) {
return ARES_CONN_ERR_FAILURE; return ARES_CONN_ERR_FAILURE;
} }
}
err = err = ares_socket_write(channel, conn->fd, data, len, written, sa, salen);
ares_socket_write_tfo(channel, conn->fd, data, len, written, sa, salen); if (err != ARES_CONN_ERR_SUCCESS) {
if (err != ARES_CONN_ERR_SUCCESS) { goto done;
goto done; }
}
if (is_tfo) {
/* If using TFO, we might not have been able to get an IP earlier, since /* 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() * we hadn't informed the OS of the destination. When using sendto()
* now we have so we should be able to fetch it */ * now we have so we should be able to fetch it */
@ -202,11 +206,6 @@ ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
goto done; goto done;
} }
err = ares_socket_write(channel, conn->fd, data, len, written);
if (err != ARES_CONN_ERR_SUCCESS) {
goto done;
}
done: done:
if (err == ARES_CONN_ERR_SUCCESS && len == *written) { if (err == ARES_CONN_ERR_SUCCESS && len == *written) {
/* Wrote all data, make sure we're not listening for write events unless /* Wrote all data, make sure we're not listening for write events unless
@ -366,10 +365,9 @@ ares_status_t ares_open_connection(ares_conn_t **conn_out,
/* LCOV_EXCL_STOP */ /* LCOV_EXCL_STOP */
} }
/* Enable TFO if the OS supports it and we were passed in data to send during /* Try to enable TFO always if using TCP. it will fail later on if its
* the connect. It might be disabled later if an error is encountered. Make * really not supported when we try to enable it on the socket. */
* sure a user isn't overriding anything. */ if (conn->flags & ARES_CONN_FLAG_TCP) {
if (conn->flags & ARES_CONN_FLAG_TCP && ares_socket_tfo_supported(channel)) {
conn->flags |= ARES_CONN_FLAG_TFO; conn->flags |= ARES_CONN_FLAG_TFO;
} }
@ -386,7 +384,7 @@ ares_status_t ares_open_connection(ares_conn_t **conn_out,
goto done; goto done;
} }
/* Configure it. */ /* Configure channel configured options */
status = ares_socket_configure( status = ares_socket_configure(
channel, server->addr.family, channel, server->addr.family,
(conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE, conn->fd); (conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE, conn->fd);

@ -157,7 +157,7 @@ static ares_status_t init_by_defaults(ares_channel_t *channel)
addr.family = AF_INET; addr.family = AF_INET;
addr.addr.addr4.s_addr = htonl(INADDR_LOOPBACK); addr.addr.addr4.s_addr = htonl(INADDR_LOOPBACK);
rc = ares_sconfig_append(&sconfig, &addr, 0, 0, NULL); rc = ares_sconfig_append(channel, &sconfig, &addr, 0, 0, NULL);
if (rc != ARES_SUCCESS) { if (rc != ARES_SUCCESS) {
goto error; /* LCOV_EXCL_LINE: OutOfMemory */ goto error; /* LCOV_EXCL_LINE: OutOfMemory */
} }
@ -346,6 +346,8 @@ int ares_init_options(ares_channel_t **channelptr,
goto done; goto done;
} }
ares_set_socket_functions_default(channel);
/* Initialize the event thread */ /* Initialize the event thread */
if (channel->optmask & ARES_OPT_EVENT_THREAD) { if (channel->optmask & ARES_OPT_EVENT_THREAD) {
ares_event_thread_t *e = NULL; ares_event_thread_t *e = NULL;

@ -94,7 +94,7 @@ struct addrinfo {
# ifdef IFNAMSIZ # ifdef IFNAMSIZ
# define IF_NAMESIZE IFNAMSIZ # define IF_NAMESIZE IFNAMSIZ
# else # else
# define IF_NAMESIZE 256 # define IF_NAMESIZE 32
# endif # endif
#endif #endif

@ -260,8 +260,10 @@ struct ares_channeldata {
ares_sock_config_callback sock_config_cb; ares_sock_config_callback sock_config_cb;
void *sock_config_cb_data; void *sock_config_cb_data;
const struct ares_socket_functions *sock_funcs; struct ares_socket_functions_ex sock_funcs;
void *sock_func_cb_data; void *sock_func_cb_data;
const struct ares_socket_functions *legacy_sock_funcs;
void *legacy_sock_funcs_cb_data;
ares_pending_write_cb notify_pending_write_cb; ares_pending_write_cb notify_pending_write_cb;
void *notify_pending_write_cb_data; void *notify_pending_write_cb_data;
@ -365,6 +367,7 @@ ares_status_t ares_init_by_options(ares_channel_t *channel,
const struct ares_options *options, const struct ares_options *options,
int optmask); int optmask);
ares_status_t ares_init_by_sysconfig(ares_channel_t *channel); ares_status_t ares_init_by_sysconfig(ares_channel_t *channel);
void ares_set_socket_functions_default(ares_channel_t *channel);
typedef struct { typedef struct {
ares_llist_t *sconfig; ares_llist_t *sconfig;
@ -388,10 +391,12 @@ ares_status_t ares_init_by_environment(ares_sysconfig_t *sysconfig);
ares_status_t ares_init_sysconfig_files(const ares_channel_t *channel, ares_status_t ares_init_sysconfig_files(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig); ares_sysconfig_t *sysconfig);
#ifdef __APPLE__ #ifdef __APPLE__
ares_status_t ares_init_sysconfig_macos(ares_sysconfig_t *sysconfig); ares_status_t ares_init_sysconfig_macos(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig);
#endif #endif
#ifdef USE_WINSOCK #ifdef USE_WINSOCK
ares_status_t ares_init_sysconfig_windows(ares_sysconfig_t *sysconfig); ares_status_t ares_init_sysconfig_windows(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig);
#endif #endif
ares_status_t ares_parse_sortlist(struct apattern **sortlist, size_t *nsort, ares_status_t ares_parse_sortlist(struct apattern **sortlist, size_t *nsort,
@ -449,14 +454,14 @@ ares_status_t ares_addrinfo_localhost(const char *name, unsigned short port,
ares_status_t ares_servers_update(ares_channel_t *channel, ares_status_t ares_servers_update(ares_channel_t *channel,
ares_llist_t *server_list, ares_llist_t *server_list,
ares_bool_t user_specified); ares_bool_t user_specified);
ares_status_t ares_sconfig_append(ares_llist_t **sconfig, ares_status_t
const struct ares_addr *addr, ares_sconfig_append(const ares_channel_t *channel, ares_llist_t **sconfig,
unsigned short udp_port, const struct ares_addr *addr, unsigned short udp_port,
unsigned short tcp_port, unsigned short tcp_port, const char *ll_iface);
const char *ll_iface); ares_status_t ares_sconfig_append_fromstr(const ares_channel_t *channel,
ares_status_t ares_sconfig_append_fromstr(ares_llist_t **sconfig, ares_llist_t **sconfig,
const char *str, const char *str,
ares_bool_t ignore_invalid); ares_bool_t ignore_invalid);
ares_status_t ares_in_addr_to_sconfig_llist(const struct in_addr *servers, ares_status_t ares_in_addr_to_sconfig_llist(const struct in_addr *servers,
size_t nservers, size_t nservers,
ares_llist_t **llist); ares_llist_t **llist);

@ -483,15 +483,12 @@ static ares_status_t read_conn_packets(ares_conn_t *conn)
/* Record amount of data read */ /* Record amount of data read */
ares_buf_append_finish(conn->in_buf, count); ares_buf_append_finish(conn->in_buf, count);
/* Only loop if we're not overwriting socket functions, and are using UDP /* Only loop if sockets support non-blocking operation, and are using UDP
* or are using TCP and read the maximum buffer size */ * or are using TCP and read the maximum buffer size */
read_again = ARES_FALSE; read_again = ARES_FALSE;
if (channel->sock_funcs == NULL) { if (channel->sock_funcs.flags & ARES_SOCKFUNC_FLAG_NONBLOCKING &&
if (!(conn->flags & ARES_CONN_FLAG_TCP)) { (!(conn->flags & ARES_CONN_FLAG_TCP) || count == len)) {
read_again = ARES_TRUE; read_again = ARES_TRUE;
} else if (count == len) {
read_again = ARES_TRUE;
}
} }
/* If UDP, overwrite length */ /* If UDP, overwrite length */

@ -0,0 +1,586 @@
/* MIT License
*
* Copyright (c) 2024 Brad House
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include "ares_private.h"
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef NETWARE
# include <sys/filio.h>
#endif
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#if defined(__linux__) && defined(TCP_FASTOPEN_CONNECT)
# 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
ares_status_t
ares_set_socket_functions_ex(ares_channel_t *channel,
const struct ares_socket_functions_ex *funcs,
void *user_data)
{
unsigned int known_versions[] = { 1 };
size_t i;
if (channel == NULL || funcs == NULL) {
return ARES_EFORMERR;
}
/* Check to see if we know the version referenced */
for (i = 0; i < sizeof(known_versions) / sizeof(*known_versions); i++) {
if (funcs->version == known_versions[i]) {
break;
}
}
if (i == sizeof(known_versions) / sizeof(*known_versions)) {
return ARES_EFORMERR;
}
memset(&channel->sock_funcs, 0, sizeof(channel->sock_funcs));
/* Copy individually for ABI compliance. memcpy() with a sizeof would do
* invalid reads */
if (funcs->version >= 1) {
if (funcs->asocket == NULL || funcs->aclose == NULL ||
funcs->asetsockopt == NULL || funcs->aconnect == NULL ||
funcs->arecvfrom == NULL || funcs->asendto == NULL) {
return ARES_EFORMERR;
}
channel->sock_funcs.version = funcs->version;
channel->sock_funcs.flags = funcs->flags;
channel->sock_funcs.asocket = funcs->asocket;
channel->sock_funcs.aclose = funcs->aclose;
channel->sock_funcs.asetsockopt = funcs->asetsockopt;
channel->sock_funcs.aconnect = funcs->aconnect;
channel->sock_funcs.arecvfrom = funcs->arecvfrom;
channel->sock_funcs.asendto = funcs->asendto;
channel->sock_funcs.agetsockname = funcs->agetsockname;
channel->sock_funcs.abind = funcs->abind;
}
/* Implement newer versions here ...*/
channel->sock_func_cb_data = user_data;
return ARES_SUCCESS;
}
static int setsocknonblock(ares_socket_t sockfd, /* operate on this */
int nonblock /* TRUE or FALSE */)
{
#if defined(HAVE_FCNTL_O_NONBLOCK)
/* most recent unix versions */
int flags;
flags = fcntl(sockfd, F_GETFL, 0);
if (nonblock) {
return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
} else {
return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); /* LCOV_EXCL_LINE */
}
#elif defined(HAVE_IOCTL_FIONBIO)
/* older unix versions */
int flags = nonblock ? 1 : 0;
return ioctl(sockfd, FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
# ifdef WATT32
char flags = nonblock ? 1 : 0;
# else
/* Windows */
unsigned long flags = nonblock ? 1UL : 0UL;
# endif
return ioctlsocket(sockfd, (long)FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
/* Amiga */
long flags = nonblock ? 1L : 0L;
return IoctlSocket(sockfd, FIONBIO, flags);
#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
/* BeOS */
long b = nonblock ? 1L : 0L;
return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
#else
# error "no non-blocking method was found/used/set"
#endif
}
static int default_aclose(ares_socket_t sock, void *user_data)
{
(void)user_data;
#if defined(HAVE_CLOSESOCKET)
return closesocket(sock);
#elif defined(HAVE_CLOSESOCKET_CAMEL)
return CloseSocket(sock);
#elif defined(HAVE_CLOSE_S)
return close_s(sock);
#else
return close(sock);
#endif
}
static ares_socket_t default_asocket(int domain, int type, int protocol,
void *user_data)
{
ares_socket_t s;
(void)user_data;
s = socket(domain, type, protocol);
if (s == ARES_SOCKET_BAD) {
return s;
}
if (setsocknonblock(s, 1) != 0) {
goto fail; /* LCOV_EXCL_LINE */
}
#if defined(FD_CLOEXEC) && !defined(MSDOS)
/* Configure the socket fd as close-on-exec. */
if (fcntl(s, F_SETFD, FD_CLOEXEC) != 0) {
goto fail; /* LCOV_EXCL_LINE */
}
#endif
/* No need to emit SIGPIPE on socket errors */
#if defined(SO_NOSIGPIPE)
{
int opt = 1;
(void)setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt));
}
#endif
if (type == SOCK_STREAM) {
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(s, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)) !=
0) {
goto fail;
}
#endif
}
#if defined(IPV6_V6ONLY) && defined(USE_WINSOCK)
/* Support for IPv4-mapped IPv6 addresses.
* Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
* Windows Vista and later: default is on;
* DragonFly BSD: acts like off, and dummy setting;
* OpenBSD and earlier Windows: unsupported.
* Linux: controlled by /proc/sys/net/ipv6/bindv6only.
*/
if (domain == PF_INET6) {
int on = 0;
(void)setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
}
#endif
return s;
fail:
default_aclose(s, user_data);
return ARES_SOCKET_BAD;
}
static int default_asetsockopt(ares_socket_t sock, ares_socket_opt_t opt,
void *val, ares_socklen_t val_size,
void *user_data)
{
switch (opt) {
case ARES_SOCKET_OPT_SENDBUF_SIZE:
if (val_size != sizeof(int)) {
SET_SOCKERRNO(EINVAL);
return -1;
}
return setsockopt(sock, SOL_SOCKET, SO_SNDBUF, val, val_size);
case ARES_SOCKET_OPT_RECVBUF_SIZE:
if (val_size != sizeof(int)) {
SET_SOCKERRNO(EINVAL);
return -1;
}
return setsockopt(sock, SOL_SOCKET, SO_RCVBUF, val, val_size);
case ARES_SOCKET_OPT_BIND_DEVICE:
if (!ares_str_isprint(val, (size_t)val_size)) {
SET_SOCKERRNO(EINVAL);
return -1;
}
#ifdef SO_BINDTODEVICE
return setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, val, val_size);
#else
SET_SOCKERRNO(ENOSYS);
return -1;
#endif
case ARES_SOCKET_OPT_TCP_FASTOPEN:
if (val_size != sizeof(ares_bool_t)) {
SET_SOCKERRNO(EINVAL);
return -1;
}
#if defined(TFO_CLIENT_SOCKOPT)
{
int oval;
ares_bool_t *pval = val;
oval = (int)*pval;
return setsockopt(sock, IPPROTO_TCP, TFO_CLIENT_SOCKOPT, (void *)&oval,
sizeof(oval));
}
#elif TFO_SUPPORTED
return 0;
#else
SET_SOCKERRNO(ENOSYS);
return -1;
#endif
};
(void)user_data;
SET_SOCKERRNO(ENOSYS);
return -1;
}
static int default_aconnect(ares_socket_t sock, const struct sockaddr *address,
ares_socklen_t address_len, unsigned int flags,
void *user_data)
{
(void)user_data;
#if defined(TFO_SKIP_CONNECT) && TFO_SKIP_CONNECT
if (flags & ARES_SOCKET_CONN_TCP_FASTOPEN) {
return 0;
}
return connect(sock, address, address_len);
#elif defined(TFO_USE_CONNECTX) && TFO_USE_CONNECTX
if (flags & ARES_SOCKET_CONN_TCP_FASTOPEN) {
sa_endpoints_t endpoints;
memset(&endpoints, 0, sizeof(endpoints));
endpoints.sae_dstaddr = address;
endpoints.sae_dstaddrlen = address_len;
return connectx(sock, &endpoints, SAE_ASSOCID_ANY,
CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
NULL, 0, NULL, NULL);
} else {
return connect(sock, address, address_len);
}
#else
(void)flags;
return connect(sock, address, address_len);
#endif
}
static ares_ssize_t default_arecvfrom(ares_socket_t sock, void *buffer,
size_t length, int flags,
struct sockaddr *address,
ares_socklen_t *address_len,
void *user_data)
{
(void)user_data;
#ifdef HAVE_RECVFROM
return (ares_ssize_t)recvfrom(sock, buffer, (RECVFROM_TYPE_ARG3)length, flags,
address, address_len);
#else
if (address != NULL && address_len != NULL) {
memset(address, 0, (size_t)*address_len);
address->sa_family = AF_UNSPEC;
}
return (ares_ssize_t)recv(sock, buffer, (RECVFROM_TYPE_ARG3)length, flags);
#endif
}
static ares_ssize_t default_asendto(ares_socket_t sock, const void *buffer,
size_t length, int flags,
const struct sockaddr *address,
ares_socklen_t address_len, void *user_data)
{
(void)user_data;
if (address != NULL) {
#ifdef HAVE_SENDTO
return (ares_ssize_t)sendto((SEND_TYPE_ARG1)sock, (SEND_TYPE_ARG2)buffer,
(SEND_TYPE_ARG3)length, (SEND_TYPE_ARG4)flags,
address, address_len);
#else
(void)address_len;
#endif
}
return (ares_ssize_t)send((SEND_TYPE_ARG1)sock, (SEND_TYPE_ARG2)buffer,
(SEND_TYPE_ARG3)length, (SEND_TYPE_ARG4)flags);
}
static int default_agetsockname(ares_socket_t sock, struct sockaddr *address,
ares_socklen_t *address_len, void *user_data)
{
(void)user_data;
return getsockname(sock, address, address_len);
}
static int default_abind(ares_socket_t sock, unsigned int flags,
const struct sockaddr *address, socklen_t address_len,
void *user_data)
{
(void)user_data;
#ifdef IP_BIND_ADDRESS_NO_PORT
if (flags & ARES_SOCKET_BIND_TCP && flags & ARES_SOCKET_BIND_CLIENT) {
int opt = 1;
(void)setsockopt(sock, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &opt, sizeof(opt));
}
#else
(void)flags;
#endif
return bind(sock, address, address_len);
}
static unsigned int default_aif_nametoindex(const char *ifname, void *user_data)
{
(void)user_data;
return ares_os_if_nametoindex(ifname);
}
static const char *default_aif_indextoname(unsigned int ifindex,
char *ifname_buf,
size_t ifname_buf_len,
void *user_data)
{
(void)user_data;
return ares_os_if_indextoname(ifindex, ifname_buf, ifname_buf_len);
}
static const struct ares_socket_functions_ex default_socket_functions = {
1,
ARES_SOCKFUNC_FLAG_NONBLOCKING,
default_asocket,
default_aclose,
default_asetsockopt,
default_aconnect,
default_arecvfrom,
default_asendto,
default_agetsockname,
default_abind,
default_aif_nametoindex,
default_aif_indextoname
};
void ares_set_socket_functions_default(ares_channel_t *channel)
{
ares_set_socket_functions_ex(channel, &default_socket_functions, NULL);
}
static int legacycb_aclose(ares_socket_t sock, void *user_data)
{
ares_channel_t *channel = user_data;
if (channel->legacy_sock_funcs != NULL &&
channel->legacy_sock_funcs->aclose != NULL) {
return channel->legacy_sock_funcs->aclose(
sock, channel->legacy_sock_funcs_cb_data);
}
return default_aclose(sock, NULL);
}
static ares_socket_t legacycb_asocket(int domain, int type, int protocol,
void *user_data)
{
ares_channel_t *channel = user_data;
if (channel->legacy_sock_funcs != NULL &&
channel->legacy_sock_funcs->asocket != NULL) {
return channel->legacy_sock_funcs->asocket(
domain, type, protocol, channel->legacy_sock_funcs_cb_data);
}
return default_asocket(domain, type, protocol, NULL);
}
static int legacycb_asetsockopt(ares_socket_t sock, ares_socket_opt_t opt,
void *val, ares_socklen_t val_size,
void *user_data)
{
(void)sock;
(void)opt;
(void)val;
(void)val_size;
(void)user_data;
SET_SOCKERRNO(ENOSYS);
return -1;
}
static int legacycb_aconnect(ares_socket_t sock, const struct sockaddr *address,
ares_socklen_t address_len, unsigned int flags,
void *user_data)
{
ares_channel_t *channel = user_data;
if (channel->legacy_sock_funcs != NULL &&
channel->legacy_sock_funcs->aconnect != NULL) {
return channel->legacy_sock_funcs->aconnect(
sock, address, address_len, channel->legacy_sock_funcs_cb_data);
}
return default_aconnect(sock, address, address_len, flags, NULL);
}
static ares_ssize_t legacycb_arecvfrom(ares_socket_t sock, void *buffer,
size_t length, int flags,
struct sockaddr *address,
ares_socklen_t *address_len,
void *user_data)
{
ares_channel_t *channel = user_data;
if (channel->legacy_sock_funcs != NULL &&
channel->legacy_sock_funcs->arecvfrom != NULL) {
if (address != NULL && address_len != NULL) {
memset(address, 0, (size_t)*address_len);
address->sa_family = AF_UNSPEC;
}
return channel->legacy_sock_funcs->arecvfrom(
sock, buffer, length, flags, address, address_len,
channel->legacy_sock_funcs_cb_data);
}
return default_arecvfrom(sock, buffer, length, flags, address, address_len,
NULL);
}
static ares_ssize_t legacycb_asendto(ares_socket_t sock, const void *buffer,
size_t length, int flags,
const struct sockaddr *address,
ares_socklen_t address_len,
void *user_data)
{
ares_channel_t *channel = user_data;
if (channel->legacy_sock_funcs != NULL &&
channel->legacy_sock_funcs->asendv != NULL) {
struct iovec vec;
vec.iov_base = (void *)((size_t)buffer); /* Cast off const */
vec.iov_len = length;
return channel->legacy_sock_funcs->asendv(
sock, &vec, 1, channel->legacy_sock_funcs_cb_data);
}
return default_asendto(sock, buffer, length, flags, address, address_len,
NULL);
}
static const struct ares_socket_functions_ex legacy_socket_functions = {
1,
0,
legacycb_asocket,
legacycb_aclose,
legacycb_asetsockopt,
legacycb_aconnect,
legacycb_arecvfrom,
legacycb_asendto,
NULL, /* agetsockname */
NULL, /* abind */
NULL, /* aif_nametoindex */
NULL /* aif_indextoname */
};
void ares_set_socket_functions(ares_channel_t *channel,
const struct ares_socket_functions *funcs,
void *data)
{
if (channel == NULL || channel->optmask & ARES_OPT_EVENT_THREAD) {
return;
}
channel->legacy_sock_funcs = funcs;
channel->legacy_sock_funcs_cb_data = data;
ares_set_socket_functions_ex(channel, &legacy_socket_functions, channel);
}

@ -208,19 +208,6 @@ struct timeval {
}; };
#endif #endif
/*
* Function-like macro definition used to close a socket.
*/
#if defined(HAVE_CLOSESOCKET)
# define sclose(x) closesocket((x))
#elif defined(HAVE_CLOSESOCKET_CAMEL)
# define sclose(x) CloseSocket((x))
#elif defined(HAVE_CLOSE_S)
# define sclose(x) close_s((x))
#else
# define sclose(x) close((x))
#endif
/* /*
* Macro used to include code only in debug builds. * Macro used to include code only in debug builds.

@ -55,139 +55,6 @@
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#if defined(__linux__) && defined(TCP_FASTOPEN_CONNECT)
# 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
/* Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno
* (or equivalent) on this platform to hide platform details to code using it.
*/
#ifdef USE_WINSOCK
# define SOCKERRNO ((int)WSAGetLastError())
# define SET_SOCKERRNO(x) (WSASetLastError((int)(x)))
#else
# define SOCKERRNO (errno)
# define SET_SOCKERRNO(x) (errno = (x))
#endif
/* Portable error number symbolic names defined to Winsock error codes. */
#ifdef USE_WINSOCK
# undef EBADF /* override definition in errno.h */
# define EBADF WSAEBADF
# undef EINTR /* override definition in errno.h */
# define EINTR WSAEINTR
# undef EINVAL /* override definition in errno.h */
# define EINVAL WSAEINVAL
# undef EWOULDBLOCK /* override definition in errno.h */
# define EWOULDBLOCK WSAEWOULDBLOCK
# undef EINPROGRESS /* override definition in errno.h */
# define EINPROGRESS WSAEINPROGRESS
# undef EALREADY /* override definition in errno.h */
# define EALREADY WSAEALREADY
# undef ENOTSOCK /* override definition in errno.h */
# define ENOTSOCK WSAENOTSOCK
# undef EDESTADDRREQ /* override definition in errno.h */
# define EDESTADDRREQ WSAEDESTADDRREQ
# undef EMSGSIZE /* override definition in errno.h */
# define EMSGSIZE WSAEMSGSIZE
# undef EPROTOTYPE /* override definition in errno.h */
# define EPROTOTYPE WSAEPROTOTYPE
# undef ENOPROTOOPT /* override definition in errno.h */
# define ENOPROTOOPT WSAENOPROTOOPT
# undef EPROTONOSUPPORT /* override definition in errno.h */
# define EPROTONOSUPPORT WSAEPROTONOSUPPORT
# define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
# undef EOPNOTSUPP /* override definition in errno.h */
# define EOPNOTSUPP WSAEOPNOTSUPP
# define EPFNOSUPPORT WSAEPFNOSUPPORT
# undef EAFNOSUPPORT /* override definition in errno.h */
# define EAFNOSUPPORT WSAEAFNOSUPPORT
# undef EADDRINUSE /* override definition in errno.h */
# define EADDRINUSE WSAEADDRINUSE
# undef EADDRNOTAVAIL /* override definition in errno.h */
# define EADDRNOTAVAIL WSAEADDRNOTAVAIL
# undef ENETDOWN /* override definition in errno.h */
# define ENETDOWN WSAENETDOWN
# undef ENETUNREACH /* override definition in errno.h */
# define ENETUNREACH WSAENETUNREACH
# undef ENETRESET /* override definition in errno.h */
# define ENETRESET WSAENETRESET
# undef ECONNABORTED /* override definition in errno.h */
# define ECONNABORTED WSAECONNABORTED
# undef ECONNRESET /* override definition in errno.h */
# define ECONNRESET WSAECONNRESET
# undef ENOBUFS /* override definition in errno.h */
# define ENOBUFS WSAENOBUFS
# undef EISCONN /* override definition in errno.h */
# define EISCONN WSAEISCONN
# undef ENOTCONN /* override definition in errno.h */
# define ENOTCONN WSAENOTCONN
# define ESHUTDOWN WSAESHUTDOWN
# define ETOOMANYREFS WSAETOOMANYREFS
# undef ETIMEDOUT /* override definition in errno.h */
# define ETIMEDOUT WSAETIMEDOUT
# undef ECONNREFUSED /* override definition in errno.h */
# define ECONNREFUSED WSAECONNREFUSED
# undef ELOOP /* override definition in errno.h */
# define ELOOP WSAELOOP
# ifndef ENAMETOOLONG /* possible previous definition in errno.h */
# define ENAMETOOLONG WSAENAMETOOLONG
# endif
# define EHOSTDOWN WSAEHOSTDOWN
# undef EHOSTUNREACH /* override definition in errno.h */
# define EHOSTUNREACH WSAEHOSTUNREACH
# ifndef ENOTEMPTY /* possible previous definition in errno.h */
# define ENOTEMPTY WSAENOTEMPTY
# endif
# define EPROCLIM WSAEPROCLIM
# define EUSERS WSAEUSERS
# define EDQUOT WSAEDQUOT
# define ESTALE WSAESTALE
# define EREMOTE WSAEREMOTE
#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
ares_bool_t ares_socket_tfo_supported(const ares_channel_t *channel)
{
#if defined(TFO_SUPPORTED) && !TFO_SUPPORTED
(void)channel;
return ARES_FALSE;
#else
if (channel->sock_funcs != NULL && channel->sock_funcs->asendv != NULL) {
return ARES_FALSE;
}
return ARES_TRUE;
#endif
}
static ares_conn_err_t ares_socket_deref_error(int err) static ares_conn_err_t ares_socket_deref_error(int err)
{ {
switch (err) { switch (err) {
@ -261,7 +128,9 @@ ares_bool_t ares_sockaddr_addr_eq(const struct sockaddr *sa,
} }
ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd, ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd,
const void *data, size_t len, size_t *written) const void *data, size_t len, size_t *written,
const struct sockaddr *sa,
ares_socklen_t salen)
{ {
int flags = 0; int flags = 0;
ares_ssize_t rv; ares_ssize_t rv;
@ -271,21 +140,8 @@ ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd,
flags |= MSG_NOSIGNAL; flags |= MSG_NOSIGNAL;
#endif #endif
if (channel->sock_funcs && channel->sock_funcs->asendv) { rv = channel->sock_funcs.asendto(fd, data, len, flags, sa, salen,
struct iovec vec; channel->sock_func_cb_data);
vec.iov_base = (void *)((size_t)data); /* Cast off const */
vec.iov_len = len;
rv = channel->sock_funcs->asendv(fd, &vec, 1, channel->sock_func_cb_data);
if (rv <= 0) {
err = ares_socket_deref_error(SOCKERRNO);
} else {
*written = (size_t)rv;
}
return err;
}
rv = (ares_ssize_t)send((SEND_TYPE_ARG1)fd, (SEND_TYPE_ARG2)data,
(SEND_TYPE_ARG3)len, (SEND_TYPE_ARG4)flags);
if (rv <= 0) { if (rv <= 0) {
err = ares_socket_deref_error(SOCKERRNO); err = ares_socket_deref_error(SOCKERRNO);
} else { } else {
@ -294,45 +150,6 @@ ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd,
return err; return err;
} }
ares_conn_err_t ares_socket_write_tfo(ares_channel_t *channel, ares_socket_t fd,
const void *data, size_t len,
size_t *written,
const struct sockaddr *sa,
ares_socklen_t salen)
{
ares_conn_err_t err;
if (!ares_socket_tfo_supported(channel)) {
return ARES_CONN_ERR_NOTIMP;
}
#if defined(TFO_USE_SENDTO) && TFO_USE_SENDTO
{
ares_ssize_t rv;
int flags = 0;
# ifdef HAVE_MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
# endif
err = ARES_CONN_ERR_SUCCESS;
rv = (ares_ssize_t)sendto((SEND_TYPE_ARG1)fd, (SEND_TYPE_ARG2)data,
(SEND_TYPE_ARG3)len, (SEND_TYPE_ARG4)flags, sa,
salen);
if (rv <= 0) {
err = ares_socket_deref_error(SOCKERRNO);
} else {
*written = (size_t)rv;
}
}
#else
(void)sa;
(void)salen;
err = ares_socket_write(channel, fd, data, len, written);
#endif
return err;
}
ares_conn_err_t ares_socket_recv(ares_channel_t *channel, ares_socket_t s, ares_conn_err_t ares_socket_recv(ares_channel_t *channel, ares_socket_t s,
ares_bool_t is_tcp, void *data, ares_bool_t is_tcp, void *data,
size_t data_len, size_t *read_bytes) size_t data_len, size_t *read_bytes)
@ -341,13 +158,8 @@ ares_conn_err_t ares_socket_recv(ares_channel_t *channel, ares_socket_t s,
*read_bytes = 0; *read_bytes = 0;
if (channel->sock_funcs && channel->sock_funcs->arecvfrom) { rv = channel->sock_funcs.arecvfrom(s, data, data_len, 0, NULL, 0,
rv = channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0, channel->sock_func_cb_data);
channel->sock_func_cb_data);
} else {
rv = (ares_ssize_t)recv((RECV_TYPE_ARG1)s, (RECV_TYPE_ARG2)data,
(RECV_TYPE_ARG3)data_len, (RECV_TYPE_ARG4)(0));
}
if (rv > 0) { if (rv > 0) {
*read_bytes = (size_t)rv; *read_bytes = (size_t)rv;
@ -376,17 +188,8 @@ ares_conn_err_t ares_socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
{ {
ares_ssize_t rv; ares_ssize_t rv;
if (channel->sock_funcs && channel->sock_funcs->arecvfrom) { rv = channel->sock_funcs.arecvfrom(s, data, data_len, flags, from, from_len,
rv = channel->sock_funcs->arecvfrom(s, data, data_len, flags, from, channel->sock_func_cb_data);
from_len, channel->sock_func_cb_data);
} else {
#ifdef HAVE_RECVFROM
rv = (ares_ssize_t)recvfrom(s, data, (RECVFROM_TYPE_ARG3)data_len, flags,
from, from_len);
#else
return ares_socket_recv(channel, s, is_udp, data, data_len);
#endif
}
if (rv > 0) { if (rv > 0) {
*read_bytes = (size_t)rv; *read_bytes = (size_t)rv;
@ -406,99 +209,17 @@ ares_conn_err_t ares_socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
return ares_socket_deref_error(SOCKERRNO); return ares_socket_deref_error(SOCKERRNO);
} }
/*
* setsocknonblock sets the given socket to either blocking or non-blocking
* mode based on the 'nonblock' boolean argument. This function is highly
* portable.
*/
static int setsocknonblock(ares_socket_t sockfd, /* operate on this */
int nonblock /* TRUE or FALSE */)
{
#if defined(USE_BLOCKING_SOCKETS)
return 0; /* returns success */
#elif defined(HAVE_FCNTL_O_NONBLOCK)
/* most recent unix versions */
int flags;
flags = fcntl(sockfd, F_GETFL, 0);
if (nonblock) {
return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
} else {
return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); /* LCOV_EXCL_LINE */
}
#elif defined(HAVE_IOCTL_FIONBIO)
/* older unix versions */
int flags = nonblock ? 1 : 0;
return ioctl(sockfd, FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
# ifdef WATT32
char flags = nonblock ? 1 : 0;
# else
/* Windows */
unsigned long flags = nonblock ? 1UL : 0UL;
# endif
return ioctlsocket(sockfd, (long)FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
/* Amiga */
long flags = nonblock ? 1L : 0L;
return IoctlSocket(sockfd, FIONBIO, flags);
#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
/* BeOS */
long b = nonblock ? 1L : 0L;
return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
#else
# error "no non-blocking method was found/used/set"
#endif
}
#if defined(IPV6_V6ONLY) && defined(USE_WINSOCK)
/* It makes support for IPv4-mapped IPv6 addresses.
* Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
* Windows Vista and later: default is on;
* DragonFly BSD: acts like off, and dummy setting;
* OpenBSD and earlier Windows: unsupported.
* Linux: controlled by /proc/sys/net/ipv6/bindv6only.
*/
static void set_ipv6_v6only(ares_socket_t sockfd, int on)
{
(void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
}
#else
# define set_ipv6_v6only(s, v)
#endif
ares_conn_err_t ares_socket_enable_tfo(const ares_channel_t *channel, ares_conn_err_t ares_socket_enable_tfo(const ares_channel_t *channel,
ares_socket_t fd) ares_socket_t fd)
{ {
#if defined(TFO_CLIENT_SOCKOPT) ares_bool_t opt = ARES_TRUE;
int opt = 1;
if (!ares_socket_tfo_supported(channel)) {
return ARES_CONN_ERR_NOTIMP;
}
if (setsockopt(fd, IPPROTO_TCP, TFO_CLIENT_SOCKOPT, (void *)&opt, if (channel->sock_funcs.asetsockopt(fd, ARES_SOCKET_OPT_TCP_FASTOPEN,
sizeof(opt)) != 0) { (void *)&opt, sizeof(opt),
return ARES_CONN_ERR_NOTIMP; channel->sock_func_cb_data) != 0) {
}
#else
if (!ares_socket_tfo_supported(channel)) {
return ARES_CONN_ERR_NOTIMP; return ARES_CONN_ERR_NOTIMP;
} }
(void)fd;
#endif
return ARES_CONN_ERR_SUCCESS; return ARES_CONN_ERR_SUCCESS;
} }
@ -512,53 +233,40 @@ ares_status_t ares_socket_configure(ares_channel_t *channel, int family,
} local; } local;
ares_socklen_t bindlen = 0; ares_socklen_t bindlen = 0;
int rv;
/* do not set options for user-managed sockets */ unsigned int bind_flags = 0;
if (channel->sock_funcs && channel->sock_funcs->asocket) {
return ARES_SUCCESS;
}
(void)setsocknonblock(fd, 1);
#if defined(FD_CLOEXEC) && !defined(MSDOS)
/* Configure the socket fd as close-on-exec. */
if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE */
}
#endif
/* No need to emit SIGPIPE on socket errors */
#if defined(SO_NOSIGPIPE)
{
int opt = 1;
(void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt));
}
#endif
/* Set the socket's send and receive buffer sizes. */ /* Set the socket's send and receive buffer sizes. */
if (channel->socket_send_buffer_size > 0 && if (channel->socket_send_buffer_size > 0) {
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, rv = channel->sock_funcs.asetsockopt(
(void *)&channel->socket_send_buffer_size, fd, ARES_SOCKET_OPT_SENDBUF_SIZE,
sizeof(channel->socket_send_buffer_size)) != 0) { (void *)&channel->socket_send_buffer_size,
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */ sizeof(channel->socket_send_buffer_size), channel->sock_func_cb_data);
if (rv != 0 && SOCKERRNO != ENOSYS) {
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */
}
} }
if (channel->socket_receive_buffer_size > 0 && if (channel->socket_receive_buffer_size > 0) {
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, rv = channel->sock_funcs.asetsockopt(
(void *)&channel->socket_receive_buffer_size, fd, ARES_SOCKET_OPT_RECVBUF_SIZE,
sizeof(channel->socket_receive_buffer_size)) != 0) { (void *)&channel->socket_receive_buffer_size,
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */ sizeof(channel->socket_receive_buffer_size), channel->sock_func_cb_data);
if (rv != 0 && SOCKERRNO != ENOSYS) {
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */
}
} }
#ifdef SO_BINDTODEVICE /* Bind to network interface if configured */
if (ares_strlen(channel->local_dev_name)) { if (ares_strlen(channel->local_dev_name)) {
/* Only root can do this, and usually not fatal if it doesn't work, so /* Prior versions silently ignored failure, so we need to maintain that
* just continue on. */ * compatibility */
(void)setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, channel->local_dev_name, (void)channel->sock_funcs.asetsockopt(
sizeof(channel->local_dev_name)); fd, ARES_SOCKET_OPT_BIND_DEVICE, channel->local_dev_name,
sizeof(channel->local_dev_name), channel->sock_func_cb_data);
} }
#endif
/* Bind to ip address if configured */
if (family == AF_INET && channel->local_ip4) { if (family == AF_INET && channel->local_ip4) {
memset(&local.sa4, 0, sizeof(local.sa4)); memset(&local.sa4, 0, sizeof(local.sa4));
local.sa4.sin_family = AF_INET; local.sa4.sin_family = AF_INET;
@ -574,35 +282,17 @@ ares_status_t ares_socket_configure(ares_channel_t *channel, int family,
sizeof(channel->local_ip6)); sizeof(channel->local_ip6));
bindlen = sizeof(local.sa6); bindlen = sizeof(local.sa6);
} }
#ifdef IP_BIND_ADDRESS_NO_PORT
if (is_tcp && bindlen) {
int opt = 1;
(void) setsockopt(fd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &opt, sizeof(opt));
}
#endif
if (bindlen && bind(fd, &local.sa, bindlen) < 0) {
return ARES_ECONNREFUSED;
}
if (family == AF_INET6) {
set_ipv6_v6only(fd, 0);
}
if (is_tcp) { if (bindlen && channel->sock_funcs.abind != NULL) {
int opt = 1; bind_flags |= ARES_SOCKET_BIND_CLIENT;
if (is_tcp) {
#ifdef TCP_NODELAY bind_flags |= ARES_SOCKET_BIND_TCP;
/* }
* Disable the Nagle algorithm (only relevant for TCP sockets, and thus not if (channel->sock_funcs.abind(fd, bind_flags, &local.sa, bindlen,
* in configure_socket). In general, in DNS lookups we're pretty much channel->sock_func_cb_data) != 0) {
* interested in firing off a single request and then waiting for a reply,
* so batching isn't very interesting.
*/
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)) !=
0) {
return ARES_ECONNREFUSED; return ARES_ECONNREFUSED;
} }
#endif
} }
return ARES_SUCCESS; return ARES_SUCCESS;
@ -653,12 +343,8 @@ ares_conn_err_t ares_socket_open(ares_socket_t *sock, ares_channel_t *channel,
*sock = ARES_SOCKET_BAD; *sock = ARES_SOCKET_BAD;
if (channel->sock_funcs && channel->sock_funcs->asocket) { s =
s = channel->sock_funcs->asocket(af, type, protocol, channel->sock_funcs.asocket(af, type, protocol, channel->sock_func_cb_data);
channel->sock_func_cb_data);
} else {
s = socket(af, type, protocol);
}
if (s == ARES_SOCKET_BAD) { if (s == ARES_SOCKET_BAD) {
return ares_socket_deref_error(SOCKERRNO); return ares_socket_deref_error(SOCKERRNO);
@ -674,38 +360,18 @@ ares_conn_err_t ares_socket_connect(ares_channel_t *channel,
const struct sockaddr *addr, const struct sockaddr *addr,
ares_socklen_t addrlen) ares_socklen_t addrlen)
{ {
ares_conn_err_t err = ARES_CONN_ERR_SUCCESS; ares_conn_err_t err = ARES_CONN_ERR_SUCCESS;
unsigned int flags = 0;
#if defined(TFO_SKIP_CONNECT) && TFO_SKIP_CONNECT
if (is_tfo) { if (is_tfo) {
return ARES_CONN_ERR_SUCCESS; flags |= ARES_SOCKET_CONN_TCP_FASTOPEN;
} }
#endif
do { do {
int rv; int rv;
if (channel->sock_funcs && channel->sock_funcs->aconnect) {
rv = channel->sock_funcs->aconnect(sockfd, addr, addrlen, rv = channel->sock_funcs.aconnect(sockfd, addr, addrlen, flags,
channel->sock_func_cb_data); channel->sock_func_cb_data);
} else {
if (is_tfo) {
#if defined(TFO_USE_CONNECTX) && TFO_USE_CONNECTX
sa_endpoints_t endpoints;
memset(&endpoints, 0, sizeof(endpoints));
endpoints.sae_dstaddr = addr;
endpoints.sae_dstaddrlen = addrlen;
rv = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
NULL, 0, NULL, NULL);
#else
rv = connect(sockfd, addr, addrlen);
#endif
} else {
rv = connect(sockfd, addr, addrlen);
}
}
if (rv < 0) { if (rv < 0) {
err = ares_socket_deref_error(SOCKERRNO); err = ares_socket_deref_error(SOCKERRNO);
@ -719,15 +385,11 @@ ares_conn_err_t ares_socket_connect(ares_channel_t *channel,
void ares_socket_close(ares_channel_t *channel, ares_socket_t s) void ares_socket_close(ares_channel_t *channel, ares_socket_t s)
{ {
if (s == ARES_SOCKET_BAD) { if (channel == NULL || s == ARES_SOCKET_BAD) {
return; return;
} }
if (channel->sock_funcs && channel->sock_funcs->aclose) { channel->sock_funcs.aclose(s, channel->sock_func_cb_data);
channel->sock_funcs->aclose(s, channel->sock_func_cb_data);
} else {
sclose(s);
}
} }
void ares_set_socket_callback(ares_channel_t *channel, void ares_set_socket_callback(ares_channel_t *channel,
@ -751,17 +413,6 @@ void ares_set_socket_configure_callback(ares_channel_t *channel,
channel->sock_config_cb_data = data; channel->sock_config_cb_data = data;
} }
void ares_set_socket_functions(ares_channel_t *channel,
const struct ares_socket_functions *funcs,
void *data)
{
if (channel == NULL || channel->optmask & ARES_OPT_EVENT_THREAD) {
return;
}
channel->sock_funcs = funcs;
channel->sock_func_cb_data = data;
}
void ares_set_pending_write_cb(ares_channel_t *channel, void ares_set_pending_write_cb(ares_channel_t *channel,
ares_pending_write_cb callback, void *user_data) ares_pending_write_cb callback, void *user_data)
{ {

@ -27,6 +27,95 @@
#ifndef __ARES_SOCKET_H #ifndef __ARES_SOCKET_H
#define __ARES_SOCKET_H #define __ARES_SOCKET_H
/* Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno
* (or equivalent) on this platform to hide platform details to code using it.
*/
#ifdef USE_WINSOCK
# define SOCKERRNO ((int)WSAGetLastError())
# define SET_SOCKERRNO(x) (WSASetLastError((int)(x)))
#else
# define SOCKERRNO (errno)
# define SET_SOCKERRNO(x) (errno = (x))
#endif
/* Portable error number symbolic names defined to Winsock error codes. */
#ifdef USE_WINSOCK
# undef EBADF /* override definition in errno.h */
# define EBADF WSAEBADF
# undef EINTR /* override definition in errno.h */
# define EINTR WSAEINTR
# undef EINVAL /* override definition in errno.h */
# define EINVAL WSAEINVAL
# undef EWOULDBLOCK /* override definition in errno.h */
# define EWOULDBLOCK WSAEWOULDBLOCK
# undef EINPROGRESS /* override definition in errno.h */
# define EINPROGRESS WSAEINPROGRESS
# undef EALREADY /* override definition in errno.h */
# define EALREADY WSAEALREADY
# undef ENOTSOCK /* override definition in errno.h */
# define ENOTSOCK WSAENOTSOCK
# undef EDESTADDRREQ /* override definition in errno.h */
# define EDESTADDRREQ WSAEDESTADDRREQ
# undef EMSGSIZE /* override definition in errno.h */
# define EMSGSIZE WSAEMSGSIZE
# undef EPROTOTYPE /* override definition in errno.h */
# define EPROTOTYPE WSAEPROTOTYPE
# undef ENOPROTOOPT /* override definition in errno.h */
# define ENOPROTOOPT WSAENOPROTOOPT
# undef EPROTONOSUPPORT /* override definition in errno.h */
# define EPROTONOSUPPORT WSAEPROTONOSUPPORT
# define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
# undef EOPNOTSUPP /* override definition in errno.h */
# define EOPNOTSUPP WSAEOPNOTSUPP
# undef ENOSYS /* override definition in errno.h */
# define ENOSYS WSAEOPNOTSUPP
# define EPFNOSUPPORT WSAEPFNOSUPPORT
# undef EAFNOSUPPORT /* override definition in errno.h */
# define EAFNOSUPPORT WSAEAFNOSUPPORT
# undef EADDRINUSE /* override definition in errno.h */
# define EADDRINUSE WSAEADDRINUSE
# undef EADDRNOTAVAIL /* override definition in errno.h */
# define EADDRNOTAVAIL WSAEADDRNOTAVAIL
# undef ENETDOWN /* override definition in errno.h */
# define ENETDOWN WSAENETDOWN
# undef ENETUNREACH /* override definition in errno.h */
# define ENETUNREACH WSAENETUNREACH
# undef ENETRESET /* override definition in errno.h */
# define ENETRESET WSAENETRESET
# undef ECONNABORTED /* override definition in errno.h */
# define ECONNABORTED WSAECONNABORTED
# undef ECONNRESET /* override definition in errno.h */
# define ECONNRESET WSAECONNRESET
# undef ENOBUFS /* override definition in errno.h */
# define ENOBUFS WSAENOBUFS
# undef EISCONN /* override definition in errno.h */
# define EISCONN WSAEISCONN
# undef ENOTCONN /* override definition in errno.h */
# define ENOTCONN WSAENOTCONN
# define ESHUTDOWN WSAESHUTDOWN
# define ETOOMANYREFS WSAETOOMANYREFS
# undef ETIMEDOUT /* override definition in errno.h */
# define ETIMEDOUT WSAETIMEDOUT
# undef ECONNREFUSED /* override definition in errno.h */
# define ECONNREFUSED WSAECONNREFUSED
# undef ELOOP /* override definition in errno.h */
# define ELOOP WSAELOOP
# ifndef ENAMETOOLONG /* possible previous definition in errno.h */
# define ENAMETOOLONG WSAENAMETOOLONG
# endif
# define EHOSTDOWN WSAEHOSTDOWN
# undef EHOSTUNREACH /* override definition in errno.h */
# define EHOSTUNREACH WSAEHOSTUNREACH
# ifndef ENOTEMPTY /* possible previous definition in errno.h */
# define ENOTEMPTY WSAENOTEMPTY
# endif
# define EPROCLIM WSAEPROCLIM
# define EUSERS WSAEUSERS
# define EDQUOT WSAEDQUOT
# define ESTALE WSAESTALE
# define EREMOTE WSAEREMOTE
#endif
/*! Socket errors */ /*! Socket errors */
typedef enum { typedef enum {
ARES_CONN_ERR_SUCCESS = 0, /*!< Success */ ARES_CONN_ERR_SUCCESS = 0, /*!< Success */
@ -50,8 +139,6 @@ typedef enum {
ARES_CONN_ERR_FAILURE = 99 /*!< Generic failure */ ARES_CONN_ERR_FAILURE = 99 /*!< Generic failure */
} ares_conn_err_t; } ares_conn_err_t;
ares_bool_t ares_socket_tfo_supported(const ares_channel_t *channel);
ares_bool_t ares_sockaddr_addr_eq(const struct sockaddr *sa, ares_bool_t ares_sockaddr_addr_eq(const struct sockaddr *sa,
const struct ares_addr *aa); const struct ares_addr *aa);
ares_status_t ares_socket_configure(ares_channel_t *channel, int family, ares_status_t ares_socket_configure(ares_channel_t *channel, int family,
@ -70,11 +157,7 @@ ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr,
unsigned short *port, unsigned short *port,
const struct sockaddr *sockaddr); const struct sockaddr *sockaddr);
ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd, ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd,
const void *data, size_t len, const void *data, size_t len, size_t *written,
size_t *written); const struct sockaddr *sa,
ares_conn_err_t ares_socket_write_tfo(ares_channel_t *channel, ares_socket_t fd, ares_socklen_t salen);
const void *data, size_t len,
size_t *written,
const struct sockaddr *sa,
ares_socklen_t salen);
#endif #endif

@ -375,7 +375,9 @@ static int find_src_addr(ares_channel_t *channel, const struct sockaddr *addr,
return 0; return 0;
} }
if (getsockname(sock, src_addr, &len) != 0) { if (channel->sock_funcs.agetsockname == NULL ||
channel->sock_funcs.agetsockname(sock, src_addr, &len,
channel->sock_func_cb_data) != 0) {
ares_socket_close(channel, sock); ares_socket_close(channel, sock);
return -1; return -1;
} }

@ -59,7 +59,8 @@
#if defined(__MVS__) #if defined(__MVS__)
static ares_status_t ares_init_sysconfig_mvs(ares_sysconfig_t *sysconfig) static ares_status_t ares_init_sysconfig_mvs(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{ {
struct __res_state *res = 0; struct __res_state *res = 0;
size_t count4; size_t count4;
@ -98,9 +99,9 @@ static ares_status_t ares_init_sysconfig_mvs(ares_sysconfig_t *sysconfig)
addr.addr.addr4.s_addr = addr_in->sin_addr.s_addr; addr.addr.addr4.s_addr = addr_in->sin_addr.s_addr;
addr.family = AF_INET; addr.family = AF_INET;
status = status = ares_sconfig_append(channel, &sysconfig->sconfig, &addr,
ares_sconfig_append(&sysconfig->sconfig, &addr, htons(addr_in->sin_port), htons(addr_in->sin_port),
htons(addr_in->sin_port), NULL); htons(addr_in->sin_port), NULL);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
return status; return status;
@ -115,9 +116,9 @@ static ares_status_t ares_init_sysconfig_mvs(ares_sysconfig_t *sysconfig)
memcpy(&(addr.addr.addr6), &(addr_in->sin6_addr), memcpy(&(addr.addr.addr6), &(addr_in->sin6_addr),
sizeof(addr_in->sin6_addr)); sizeof(addr_in->sin6_addr));
status = status = ares_sconfig_append(channel, &sysconfig->sconfig, &addr,
ares_sconfig_append(&sysconfig->sconfig, &addr, htons(addr_in->sin_port), htons(addr_in->sin_port),
htons(addr_in->sin_port), NULL); htons(addr_in->sin_port), NULL);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
return status; return status;
@ -129,7 +130,8 @@ static ares_status_t ares_init_sysconfig_mvs(ares_sysconfig_t *sysconfig)
#endif #endif
#if defined(__riscos__) #if defined(__riscos__)
static ares_status_t ares_init_sysconfig_riscos(ares_sysconfig_t *sysconfig) static ares_status_t ares_init_sysconfig_riscos(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{ {
char *line; char *line;
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
@ -152,7 +154,8 @@ static ares_status_t ares_init_sysconfig_riscos(ares_sysconfig_t *sysconfig)
if (space) { if (space) {
*space = '\0'; *space = '\0';
} }
status = ares_sconfig_append_fromstr(&sysconfig->sconfig, pos, ARES_TRUE); status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, pos,
ARES_TRUE);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
break; break;
} }
@ -167,7 +170,8 @@ static ares_status_t ares_init_sysconfig_riscos(ares_sysconfig_t *sysconfig)
#endif #endif
#if defined(WATT32) #if defined(WATT32)
static ares_status_t ares_init_sysconfig_watt32(ares_sysconfig_t *sysconfig) static ares_status_t ares_init_sysconfig_watt32(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{ {
size_t i; size_t i;
ares_status_t status; ares_status_t status;
@ -180,7 +184,8 @@ static ares_status_t ares_init_sysconfig_watt32(ares_sysconfig_t *sysconfig)
addr.family = AF_INET; addr.family = AF_INET;
addr.addr.addr4.s_addr = htonl(def_nameservers[i]); addr.addr.addr4.s_addr = htonl(def_nameservers[i]);
status = ares_sconfig_append(&sysconfig->sconfig, &addr, 0, 0, NULL); status =
ares_sconfig_append(channel, &sysconfig->sconfig, &addr, 0, 0, NULL);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
return status; return status;
@ -192,7 +197,8 @@ static ares_status_t ares_init_sysconfig_watt32(ares_sysconfig_t *sysconfig)
#endif #endif
#if defined(ANDROID) || defined(__ANDROID__) #if defined(ANDROID) || defined(__ANDROID__)
static ares_status_t ares_init_sysconfig_android(ares_sysconfig_t *sysconfig) static ares_status_t ares_init_sysconfig_android(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{ {
size_t i; size_t i;
char **dns_servers; char **dns_servers;
@ -209,8 +215,8 @@ static ares_status_t ares_init_sysconfig_android(ares_sysconfig_t *sysconfig)
dns_servers = ares_get_android_server_list(MAX_DNS_PROPERTIES, &num_servers); dns_servers = ares_get_android_server_list(MAX_DNS_PROPERTIES, &num_servers);
if (dns_servers != NULL) { if (dns_servers != NULL) {
for (i = 0; i < num_servers; i++) { for (i = 0; i < num_servers; i++) {
status = ares_sconfig_append_fromstr(&sysconfig->sconfig, dns_servers[i], status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig,
ARES_TRUE); dns_servers[i], ARES_TRUE);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
return status; return status;
} }
@ -241,8 +247,8 @@ static ares_status_t ares_init_sysconfig_android(ares_sysconfig_t *sysconfig)
if (__system_property_get(propname, propvalue) < 1) { if (__system_property_get(propname, propvalue) < 1) {
break; break;
} }
status = status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig,
ares_sconfig_append_fromstr(&sysconfig->sconfig, propvalue, ARES_TRUE); propvalue, ARES_TRUE);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
return status; return status;
} }
@ -255,7 +261,9 @@ static ares_status_t ares_init_sysconfig_android(ares_sysconfig_t *sysconfig)
#endif #endif
#if defined(CARES_USE_LIBRESOLV) #if defined(CARES_USE_LIBRESOLV)
static ares_status_t ares_init_sysconfig_libresolv(ares_sysconfig_t *sysconfig) static ares_status_t
ares_init_sysconfig_libresolv(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{ {
struct __res_state res; struct __res_state res;
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
@ -343,7 +351,8 @@ static ares_status_t ares_init_sysconfig_libresolv(ares_sysconfig_t *sysconfig)
goto done; goto done;
} }
status = ares_sconfig_append_fromstr(&sysconfig->sconfig, ipstr, ARES_TRUE); status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, ipstr,
ARES_TRUE);
ares_free(ipstr); ares_free(ipstr);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
@ -494,19 +503,19 @@ ares_status_t ares_init_by_sysconfig(ares_channel_t *channel)
sysconfig.ndots = 1; /* Default value if not otherwise set */ sysconfig.ndots = 1; /* Default value if not otherwise set */
#if defined(USE_WINSOCK) #if defined(USE_WINSOCK)
status = ares_init_sysconfig_windows(&sysconfig); status = ares_init_sysconfig_windows(channel, &sysconfig);
#elif defined(__MVS__) #elif defined(__MVS__)
status = ares_init_sysconfig_mvs(&sysconfig); status = ares_init_sysconfig_mvs(channel, &sysconfig);
#elif defined(__riscos__) #elif defined(__riscos__)
status = ares_init_sysconfig_riscos(&sysconfig); status = ares_init_sysconfig_riscos(channel, &sysconfig);
#elif defined(WATT32) #elif defined(WATT32)
status = ares_init_sysconfig_watt32(&sysconfig); status = ares_init_sysconfig_watt32(channel, &sysconfig);
#elif defined(ANDROID) || defined(__ANDROID__) #elif defined(ANDROID) || defined(__ANDROID__)
status = ares_init_sysconfig_android(&sysconfig); status = ares_init_sysconfig_android(channel, &sysconfig);
#elif defined(__APPLE__) #elif defined(__APPLE__)
status = ares_init_sysconfig_macos(&sysconfig); status = ares_init_sysconfig_macos(channel, &sysconfig);
#elif defined(CARES_USE_LIBRESOLV) #elif defined(CARES_USE_LIBRESOLV)
status = ares_init_sysconfig_libresolv(&sysconfig); status = ares_init_sysconfig_libresolv(channel, &sysconfig);
#else #else
status = ares_init_sysconfig_files(channel, &sysconfig); status = ares_init_sysconfig_files(channel, &sysconfig);
#endif #endif

@ -549,8 +549,9 @@ ares_status_t ares_init_by_environment(ares_sysconfig_t *sysconfig)
/* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other
* conditions are ignored. Users may mess up config files, but we want to * conditions are ignored. Users may mess up config files, but we want to
* process anything we can. */ * process anything we can. */
static ares_status_t parse_resolvconf_line(ares_sysconfig_t *sysconfig, static ares_status_t parse_resolvconf_line(const ares_channel_t *channel,
ares_buf_t *line) ares_sysconfig_t *sysconfig,
ares_buf_t *line)
{ {
char option[32]; char option[32];
char value[512]; char value[512];
@ -600,7 +601,8 @@ static ares_status_t parse_resolvconf_line(ares_sysconfig_t *sysconfig,
} else if (ares_streq(option, "search")) { } else if (ares_streq(option, "search")) {
status = config_search(sysconfig, value, 0); status = config_search(sysconfig, value, 0);
} else if (ares_streq(option, "nameserver")) { } else if (ares_streq(option, "nameserver")) {
status = ares_sconfig_append_fromstr(&sysconfig->sconfig, value, ARES_TRUE); status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, value,
ARES_TRUE);
} else if (ares_streq(option, "sortlist")) { } else if (ares_streq(option, "sortlist")) {
/* Ignore all failures except ENOMEM. If the sysadmin set a bad /* Ignore all failures except ENOMEM. If the sysadmin set a bad
* sortlist, just ignore the sortlist, don't cause an inoperable * sortlist, just ignore the sortlist, don't cause an inoperable
@ -620,8 +622,9 @@ static ares_status_t parse_resolvconf_line(ares_sysconfig_t *sysconfig,
/* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other
* conditions are ignored. Users may mess up config files, but we want to * conditions are ignored. Users may mess up config files, but we want to
* process anything we can. */ * process anything we can. */
static ares_status_t parse_nsswitch_line(ares_sysconfig_t *sysconfig, static ares_status_t parse_nsswitch_line(const ares_channel_t *channel,
ares_buf_t *line) ares_sysconfig_t *sysconfig,
ares_buf_t *line)
{ {
char option[32]; char option[32];
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
@ -629,6 +632,8 @@ static ares_status_t parse_nsswitch_line(ares_sysconfig_t *sysconfig,
ares_buf_t **bufptr; ares_buf_t **bufptr;
ares_buf_t *buf; ares_buf_t *buf;
(void)channel;
/* Ignore lines beginning with a comment */ /* Ignore lines beginning with a comment */
if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) { if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) {
return ARES_SUCCESS; return ARES_SUCCESS;
@ -671,8 +676,9 @@ done:
/* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other
* conditions are ignored. Users may mess up config files, but we want to * conditions are ignored. Users may mess up config files, but we want to
* process anything we can. */ * process anything we can. */
static ares_status_t parse_svcconf_line(ares_sysconfig_t *sysconfig, static ares_status_t parse_svcconf_line(const ares_channel_t *channel,
ares_buf_t *line) ares_sysconfig_t *sysconfig,
ares_buf_t *line)
{ {
char option[32]; char option[32];
ares_buf_t **bufptr; ares_buf_t **bufptr;
@ -680,6 +686,8 @@ static ares_status_t parse_svcconf_line(ares_sysconfig_t *sysconfig,
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
ares_array_t *sects = NULL; ares_array_t *sects = NULL;
(void)channel;
/* Ignore lines beginning with a comment */ /* Ignore lines beginning with a comment */
if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) { if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) {
return ARES_SUCCESS; return ARES_SUCCESS;
@ -718,8 +726,9 @@ done:
return status; return status;
} }
typedef ares_status_t (*line_callback_t)(ares_sysconfig_t *sysconfig, typedef ares_status_t (*line_callback_t)(const ares_channel_t *channel,
ares_buf_t *line); ares_sysconfig_t *sysconfig,
ares_buf_t *line);
/* Should only return: /* Should only return:
* ARES_ENOTFOUND - file not found * ARES_ENOTFOUND - file not found
@ -728,9 +737,10 @@ typedef ares_status_t (*line_callback_t)(ares_sysconfig_t *sysconfig,
* ARES_SUCCESS - file processed, doesn't necessarily mean it was a good * ARES_SUCCESS - file processed, doesn't necessarily mean it was a good
* file, but we're not erroring out if we can't parse * file, but we're not erroring out if we can't parse
* something (or anything at all) */ * something (or anything at all) */
static ares_status_t process_config_lines(const char *filename, static ares_status_t process_config_lines(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig, const char *filename,
line_callback_t cb) ares_sysconfig_t *sysconfig,
line_callback_t cb)
{ {
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
ares_array_t *lines = NULL; ares_array_t *lines = NULL;
@ -760,7 +770,7 @@ static ares_status_t process_config_lines(const char *filename,
ares_buf_t **bufptr = ares_array_at(lines, i); ares_buf_t **bufptr = ares_array_at(lines, i);
ares_buf_t *line = *bufptr; ares_buf_t *line = *bufptr;
status = cb(sysconfig, line); status = cb(channel, sysconfig, line);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
goto done; goto done;
} }
@ -779,7 +789,8 @@ ares_status_t ares_init_sysconfig_files(const ares_channel_t *channel,
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
/* Resolv.conf */ /* Resolv.conf */
status = process_config_lines((channel->resolvconf_path != NULL) status = process_config_lines(channel,
(channel->resolvconf_path != NULL)
? channel->resolvconf_path ? channel->resolvconf_path
: PATH_RESOLV_CONF, : PATH_RESOLV_CONF,
sysconfig, parse_resolvconf_line); sysconfig, parse_resolvconf_line);
@ -788,21 +799,22 @@ ares_status_t ares_init_sysconfig_files(const ares_channel_t *channel,
} }
/* Nsswitch.conf */ /* Nsswitch.conf */
status = status = process_config_lines(channel, "/etc/nsswitch.conf", sysconfig,
process_config_lines("/etc/nsswitch.conf", sysconfig, parse_nsswitch_line); parse_nsswitch_line);
if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
goto done; goto done;
} }
/* netsvc.conf */ /* netsvc.conf */
status = status = process_config_lines(channel, "/etc/netsvc.conf", sysconfig,
process_config_lines("/etc/netsvc.conf", sysconfig, parse_svcconf_line); parse_svcconf_line);
if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
goto done; goto done;
} }
/* svc.conf */ /* svc.conf */
status = process_config_lines("/etc/svc.conf", sysconfig, parse_svcconf_line); status = process_config_lines(channel, "/etc/svc.conf", sysconfig,
parse_svcconf_line);
if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
goto done; goto done;
} }

@ -161,7 +161,8 @@ static ares_bool_t search_is_duplicate(const ares_sysconfig_t *sysconfig,
return ARES_FALSE; return ARES_FALSE;
} }
static ares_status_t read_resolver(const dns_resolver_t *resolver, static ares_status_t read_resolver(const ares_channel_t *channel,
const dns_resolver_t *resolver,
ares_sysconfig_t *sysconfig) ares_sysconfig_t *sysconfig)
{ {
int i; int i;
@ -269,7 +270,7 @@ static ares_status_t read_resolver(const dns_resolver_t *resolver,
unsigned short addrport; unsigned short addrport;
const struct sockaddr *sockaddr; const struct sockaddr *sockaddr;
char if_name_str[256] = ""; char if_name_str[256] = "";
const char *if_name; const char *if_name = NULL;
/* UBSAN alignment workaround to fetch memory address */ /* UBSAN alignment workaround to fetch memory address */
memcpy(&sockaddr, resolver->nameserver + i, sizeof(sockaddr)); memcpy(&sockaddr, resolver->nameserver + i, sizeof(sockaddr));
@ -282,10 +283,14 @@ static ares_status_t read_resolver(const dns_resolver_t *resolver,
addrport = port; addrport = port;
} }
if_name = if (channel->sock_funcs.aif_indextoname != NULL) {
ares_if_indextoname(resolver->if_index, if_name_str, sizeof(if_name_str)); if_name = channel->sock_funcs.aif_indextoname(
status = ares_sconfig_append(&sysconfig->sconfig, &addr, addrport, addrport, resolver->if_index, if_name_str, sizeof(if_name_str),
if_name); channel->sock_func_cb_data);
}
status = ares_sconfig_append(channel, &sysconfig->sconfig, &addr, addrport,
addrport, if_name);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
return status; return status;
} }
@ -294,7 +299,8 @@ static ares_status_t read_resolver(const dns_resolver_t *resolver,
return status; return status;
} }
static ares_status_t read_resolvers(dns_resolver_t **resolvers, int nresolvers, static ares_status_t read_resolvers(const ares_channel_t *channel,
dns_resolver_t **resolvers, int nresolvers,
ares_sysconfig_t *sysconfig) ares_sysconfig_t *sysconfig)
{ {
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
@ -309,13 +315,14 @@ static ares_status_t read_resolvers(dns_resolver_t **resolvers, int nresolvers,
*/ */
memcpy(&resolver_ptr, resolvers + i, sizeof(resolver_ptr)); memcpy(&resolver_ptr, resolvers + i, sizeof(resolver_ptr));
status = read_resolver(resolver_ptr, sysconfig); status = read_resolver(channel, resolver_ptr, sysconfig);
} }
return status; return status;
} }
ares_status_t ares_init_sysconfig_macos(ares_sysconfig_t *sysconfig) ares_status_t ares_init_sysconfig_macos(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{ {
dnsinfo_t *dnsinfo = NULL; dnsinfo_t *dnsinfo = NULL;
dns_config_t *sc_dns = NULL; dns_config_t *sc_dns = NULL;
@ -343,7 +350,8 @@ ares_status_t ares_init_sysconfig_macos(ares_sysconfig_t *sysconfig)
* Likely this wasn't available via `/etc/resolv.conf` nor `libresolv` anyhow * Likely this wasn't available via `/etc/resolv.conf` nor `libresolv` anyhow
* so its not worse to prior configuration methods, worst case. */ * so its not worse to prior configuration methods, worst case. */
status = read_resolvers(sc_dns->resolver, sc_dns->n_resolver, sysconfig); status =
read_resolvers(channel, sc_dns->resolver, sc_dns->n_resolver, sysconfig);
done: done:
if (dnsinfo) { if (dnsinfo) {

@ -584,13 +584,15 @@ static ares_bool_t get_SuffixList_Windows(char **outptr)
return *outptr != NULL ? ARES_TRUE : ARES_FALSE; return *outptr != NULL ? ARES_TRUE : ARES_FALSE;
} }
ares_status_t ares_init_sysconfig_windows(ares_sysconfig_t *sysconfig) ares_status_t ares_init_sysconfig_windows(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{ {
char *line = NULL; char *line = NULL;
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
if (get_DNS_Windows(&line)) { if (get_DNS_Windows(&line)) {
status = ares_sconfig_append_fromstr(&sysconfig->sconfig, line, ARES_TRUE); status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, line,
ARES_TRUE);
ares_free(line); ares_free(line);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
goto done; goto done;

@ -389,15 +389,20 @@ static ares_status_t parse_nameserver(ares_buf_t *buf, ares_sconfig_t *sconfig)
return ARES_SUCCESS; return ARES_SUCCESS;
} }
static ares_status_t ares_sconfig_linklocal(ares_sconfig_t *s, static ares_status_t ares_sconfig_linklocal(const ares_channel_t *channel,
const char *ll_iface) ares_sconfig_t *s,
const char *ll_iface)
{ {
unsigned int ll_scope = 0; unsigned int ll_scope = 0;
if (ares_str_isnum(ll_iface)) { if (ares_str_isnum(ll_iface)) {
char ifname[IF_NAMESIZE] = ""; char ifname[IF_NAMESIZE] = "";
ll_scope = (unsigned int)atoi(ll_iface); ll_scope = (unsigned int)atoi(ll_iface);
if (ares_if_indextoname(ll_scope, ifname, sizeof(ifname)) == NULL) { if (channel->sock_funcs.aif_indextoname == NULL ||
channel->sock_funcs.aif_indextoname(ll_scope, ifname, sizeof(ifname),
channel->sock_func_cb_data) ==
NULL) {
DEBUGF(fprintf(stderr, "Interface %s for ipv6 Link Local not found\n", DEBUGF(fprintf(stderr, "Interface %s for ipv6 Link Local not found\n",
ll_iface)); ll_iface));
return ARES_ENOTFOUND; return ARES_ENOTFOUND;
@ -407,7 +412,10 @@ static ares_status_t ares_sconfig_linklocal(ares_sconfig_t *s,
return ARES_SUCCESS; return ARES_SUCCESS;
} }
ll_scope = ares_if_nametoindex(ll_iface); if (channel->sock_funcs.aif_nametoindex != NULL) {
ll_scope =
channel->sock_funcs.aif_nametoindex(ll_iface, channel->sock_func_cb_data);
}
if (ll_scope == 0) { if (ll_scope == 0) {
DEBUGF(fprintf(stderr, "Interface %s for ipv6 Link Local not found\n", DEBUGF(fprintf(stderr, "Interface %s for ipv6 Link Local not found\n",
ll_iface)); ll_iface));
@ -418,7 +426,8 @@ static ares_status_t ares_sconfig_linklocal(ares_sconfig_t *s,
return ARES_SUCCESS; return ARES_SUCCESS;
} }
ares_status_t ares_sconfig_append(ares_llist_t **sconfig, ares_status_t ares_sconfig_append(const ares_channel_t *channel,
ares_llist_t **sconfig,
const struct ares_addr *addr, const struct ares_addr *addr,
unsigned short udp_port, unsigned short udp_port,
unsigned short tcp_port, const char *ll_iface) unsigned short tcp_port, const char *ll_iface)
@ -460,7 +469,7 @@ ares_status_t ares_sconfig_append(ares_llist_t **sconfig,
status = ARES_SUCCESS; status = ARES_SUCCESS;
goto fail; goto fail;
} }
status = ares_sconfig_linklocal(s, ll_iface); status = ares_sconfig_linklocal(channel, s, ll_iface);
/* Silently ignore this entry, we can't validate the interface */ /* Silently ignore this entry, we can't validate the interface */
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
status = ARES_SUCCESS; status = ARES_SUCCESS;
@ -497,9 +506,10 @@ fail:
* *
* Returns an error code on failure, else ARES_SUCCESS. * Returns an error code on failure, else ARES_SUCCESS.
*/ */
ares_status_t ares_sconfig_append_fromstr(ares_llist_t **sconfig, ares_status_t ares_sconfig_append_fromstr(const ares_channel_t *channel,
const char *str, ares_llist_t **sconfig,
ares_bool_t ignore_invalid) const char *str,
ares_bool_t ignore_invalid)
{ {
ares_status_t status = ARES_SUCCESS; ares_status_t status = ARES_SUCCESS;
ares_buf_t *buf = NULL; ares_buf_t *buf = NULL;
@ -541,8 +551,8 @@ ares_status_t ares_sconfig_append_fromstr(ares_llist_t **sconfig,
} }
} }
status = status = ares_sconfig_append(channel, sconfig, &s.addr, s.udp_port,
ares_sconfig_append(sconfig, &s.addr, s.udp_port, s.tcp_port, s.ll_iface); s.tcp_port, s.ll_iface);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
goto done; /* LCOV_EXCL_LINE: OutOfMemory */ goto done; /* LCOV_EXCL_LINE: OutOfMemory */
} }
@ -1275,7 +1285,7 @@ static ares_status_t set_servers_csv(ares_channel_t *channel, const char *_csv)
return status; return status;
} }
status = ares_sconfig_append_fromstr(&slist, _csv, ARES_FALSE); status = ares_sconfig_append_fromstr(channel, &slist, _csv, ARES_FALSE);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
ares_llist_destroy(slist); ares_llist_destroy(slist);
return status; return status;

@ -21,6 +21,7 @@
#define HAVE_RECV 1 #define HAVE_RECV 1
#define HAVE_RECVFROM 1 #define HAVE_RECVFROM 1
#define HAVE_SEND 1 #define HAVE_SEND 1
#define HAVE_SENDTO 1
#define HAVE_STRDUP 1 #define HAVE_STRDUP 1
#define HAVE_STRICMP 1 #define HAVE_STRICMP 1
#define HAVE_STRUCT_IN6_ADDR 1 #define HAVE_STRUCT_IN6_ADDR 1

@ -187,6 +187,9 @@
/* Define if you have the send function. */ /* Define if you have the send function. */
#define HAVE_SEND 1 #define HAVE_SEND 1
/* Define if you have the sendto function. */
#define HAVE_SENDTO 1
/* Define to the type of arg 1 for send. */ /* Define to the type of arg 1 for send. */
#define SEND_TYPE_ARG1 SOCKET #define SEND_TYPE_ARG1 SOCKET

@ -543,7 +543,7 @@ static ares_status_t ares_iface_ips_enumerate(ares_iface_ips_t *ips,
#endif #endif
unsigned int ares_if_nametoindex(const char *name) unsigned int ares_os_if_nametoindex(const char *name)
{ {
#ifdef HAVE_IF_NAMETOINDEX #ifdef HAVE_IF_NAMETOINDEX
if (name == NULL) { if (name == NULL) {
@ -579,7 +579,7 @@ done:
#endif #endif
} }
const char *ares_if_indextoname(unsigned int index, char *name, size_t name_len) const char *ares_os_if_indextoname(unsigned int index, char *name, size_t name_len)
{ {
#ifdef HAVE_IF_INDEXTONAME #ifdef HAVE_IF_INDEXTONAME
if (name_len < IF_NAMESIZE) { if (name_len < IF_NAMESIZE) {

@ -124,7 +124,7 @@ unsigned int ares_iface_ips_get_ll_scope(const ares_iface_ips_t *ips,
* \param[in] name Interface name * \param[in] name Interface name
* \return 0 on failure, index otherwise * \return 0 on failure, index otherwise
*/ */
unsigned int ares_if_nametoindex(const char *name); unsigned int ares_os_if_nametoindex(const char *name);
/*! Retrieves the interface name from the index (aka link local scope) /*! Retrieves the interface name from the index (aka link local scope)
* *
@ -133,7 +133,7 @@ unsigned int ares_if_nametoindex(const char *name);
* \param[in] name_len Length of provided buffer, must be at least IF_NAMESIZE * \param[in] name_len Length of provided buffer, must be at least IF_NAMESIZE
* \return NULL on failure, or pointer to name on success * \return NULL on failure, or pointer to name on success
*/ */
const char *ares_if_indextoname(unsigned int index, char *name, const char *ares_os_if_indextoname(unsigned int index, char *name,
size_t name_len); size_t name_len);
#endif #endif

@ -305,10 +305,10 @@ TEST_F(LibraryTest, IfaceIPs) {
} else { } else {
EXPECT_EQ(0, ares_iface_ips_get_ll_scope(ips, i)); EXPECT_EQ(0, ares_iface_ips_get_ll_scope(ips, i));
} }
unsigned int idx = ares_if_nametoindex(name); unsigned int idx = ares_os_if_nametoindex(name);
EXPECT_NE(0, idx); EXPECT_NE(0, idx);
char namebuf[256]; char namebuf[256];
EXPECT_EQ(std::string(ares_if_indextoname(idx, namebuf, sizeof(namebuf))), std::string(name)); EXPECT_EQ(std::string(ares_os_if_indextoname(idx, namebuf, sizeof(namebuf))), std::string(name));
} }
@ -327,8 +327,8 @@ TEST_F(LibraryTest, IfaceIPs) {
ares_iface_ips_get_netmask(NULL, 0); ares_iface_ips_get_netmask(NULL, 0);
ares_iface_ips_get_ll_scope(NULL, 0); ares_iface_ips_get_ll_scope(NULL, 0);
ares_iface_ips_destroy(NULL); ares_iface_ips_destroy(NULL);
ares_if_nametoindex(NULL); ares_os_if_nametoindex(NULL);
ares_if_indextoname(0, NULL, 0); ares_os_if_indextoname(0, NULL, 0);
ares_iface_ips_destroy(ips); ares_iface_ips_destroy(ips);
} }

10
test/ares-test.h vendored

@ -51,6 +51,16 @@
#include <vector> #include <vector>
#include <chrono> #include <chrono>
#if defined(HAVE_CLOSESOCKET)
# define sclose(x) closesocket(x)
#elif defined(HAVE_CLOSESOCKET_CAMEL)
# define sclose(x) CloseSocket(x)
#elif defined(HAVE_CLOSE_S)
# define sclose(x) close_s(x)
#else
# define sclose(x) close(x)
#endif
namespace ares { namespace ares {
typedef unsigned char byte; typedef unsigned char byte;

Loading…
Cancel
Save