From cddab64d66140450386ec25d663474692e64a8c0 Mon Sep 17 00:00:00 2001 From: Brad House Date: Mon, 7 Oct 2024 20:29:41 -0400 Subject: [PATCH] more implementation --- include/ares.h | 69 +++++++- src/lib/ares_conn.c | 35 ++-- src/lib/ares_process.c | 11 +- src/lib/ares_set_socket_functions.c | 126 +++++++++++++- src/lib/ares_socket.c | 245 ++++------------------------ src/lib/ares_socket.h | 93 ++++++++++- 6 files changed, 332 insertions(+), 247 deletions(-) diff --git a/include/ares.h b/include/ares.h index b3be02da6f..47cd6c9f41 100644 --- a/include/ares.h +++ b/include/ares.h @@ -575,11 +575,45 @@ CARES_EXTERN CARES_DEPRECATED_FOR( *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 ENOTIMP. + */ + 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; + /*! 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 @@ -605,6 +639,25 @@ struct ares_socket_functions_ex { * such as EBADF */ 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 set. If errno is ENOTIMP 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. @@ -612,6 +665,7 @@ struct ares_socket_functions_ex { * \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 @@ -624,7 +678,8 @@ struct ares_socket_functions_ex { * retrieve the appropriate error). */ int (*aconnect)(ares_socket_t sock, const struct sockaddr *address, - ares_socklen_t address_len, void *user_data); + ares_socklen_t address_len, unsigned int flags, + void *user_data); /*! REQUIRED. Attempt to read data from the remote. * @@ -681,6 +736,18 @@ struct ares_socket_functions_ex { ares_socklen_t *address_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. + */ CARES_EXTERN ares_status_t ares_set_socket_functions_ex( ares_channel_t *channel, const struct ares_socket_functions_ex *funcs, void *user_data); diff --git a/src/lib/ares_conn.c b/src/lib/ares_conn.c index f02e816d09..4c049aa553 100644 --- a/src/lib/ares_conn.c +++ b/src/lib/ares_conn.c @@ -167,6 +167,9 @@ ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len, ares_channel_t *channel = conn->server->channel; ares_bool_t is_tfo = ARES_FALSE; 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; @@ -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; } + /* On initial write during TFO we need to send an address */ if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) { - struct sockaddr_storage sa_storage; - ares_socklen_t salen = sizeof(sa_storage); - struct sockaddr *sa = (struct sockaddr *)&sa_storage; + salen = sizeof(sa_storage); + sa = (struct sockaddr *)&sa_storage; conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL); is_tfo = ARES_TRUE; @@ -188,13 +191,15 @@ 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) { return ARES_CONN_ERR_FAILURE; } + } - err = - ares_socket_write_tfo(channel, conn->fd, data, len, written, sa, salen); - if (err != ARES_CONN_ERR_SUCCESS) { - goto done; - } + err = + ares_socket_write(channel, conn->fd, data, len, written, sa, salen); + if (err != ARES_CONN_ERR_SUCCESS) { + goto done; + } + if (is_tfo) { /* If using TFO, we might not have been able to get an IP earlier, since * we hadn't informed the OS of the destination. When using sendto() * now we have so we should be able to fetch it */ @@ -202,11 +207,6 @@ ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len, goto done; } - err = ares_socket_write(channel, conn->fd, data, len, written); - if (err != ARES_CONN_ERR_SUCCESS) { - goto done; - } - done: if (err == ARES_CONN_ERR_SUCCESS && len == *written) { /* Wrote all data, make sure we're not listening for write events unless @@ -366,10 +366,9 @@ ares_status_t ares_open_connection(ares_conn_t **conn_out, /* LCOV_EXCL_STOP */ } - /* Enable TFO if the OS supports it and we were passed in data to send during - * the connect. It might be disabled later if an error is encountered. Make - * sure a user isn't overriding anything. */ - if (conn->flags & ARES_CONN_FLAG_TCP && ares_socket_tfo_supported(channel)) { + /* Try to enable TFO always if using TCP. it will fail later on if its + * really not supported when we try to enable it on the socket. */ + if (conn->flags & ARES_CONN_FLAG_TCP) { conn->flags |= ARES_CONN_FLAG_TFO; } @@ -386,7 +385,7 @@ ares_status_t ares_open_connection(ares_conn_t **conn_out, goto done; } - /* Configure it. */ + /* Configure channel configured options */ status = ares_socket_configure( channel, server->addr.family, (conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE, conn->fd); diff --git a/src/lib/ares_process.c b/src/lib/ares_process.c index f39a9dd922..da067bf630 100644 --- a/src/lib/ares_process.c +++ b/src/lib/ares_process.c @@ -442,8 +442,9 @@ void ares_process_pending_write(ares_channel_t *channel) static ares_status_t read_conn_packets(ares_conn_t *conn) { - ares_bool_t read_again; - ares_conn_err_t err; + ares_bool_t read_again; + ares_conn_err_t err; + const ares_channel_t *channel = conn->server->channel; do { size_t count; @@ -482,12 +483,10 @@ static ares_status_t read_conn_packets(ares_conn_t *conn) /* Record amount of data read */ 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 */ read_again = ARES_FALSE; - if (!(conn->flags & ARES_CONN_FLAG_TCP)) { - read_again = ARES_TRUE; - } else if (count == len) { + if (channel->sock_funcs.flags & ARES_SOCKFUNC_FLAG_NONBLOCKING && (!(conn->flags & ARES_CONN_FLAG_TCP) || count == len)) { read_again = ARES_TRUE; } diff --git a/src/lib/ares_set_socket_functions.c b/src/lib/ares_set_socket_functions.c index 3b8f849ac1..d9947631be 100644 --- a/src/lib/ares_set_socket_functions.c +++ b/src/lib/ares_set_socket_functions.c @@ -54,6 +54,30 @@ #include #include + +#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 + + ares_status_t ares_set_socket_functions_ex(ares_channel_t *channel, const struct ares_socket_functions_ex *funcs, @@ -82,13 +106,16 @@ ares_status_t * 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; @@ -234,12 +261,86 @@ static ares_socket_t default_asocket(int domain, int type, int protocol, return -1; } +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, val_size)) { + SET_SOCKERRNO(EINVAL); + return -1; + } + return setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, val, val_size); + + case ARES_SOCKET_OPT_TCP_FASTOPEN: + if (val_size != sizeof(ares_bool_t)) { + SET_SOCKERRNO(EINVAL); + return -1; + } +#if defined(TFO_CLIENT_SOCKOPT) + { + int opt; + ares_bool_t *pval = val; + opt = *pval; + return setsockopt(sock, IPPROTO_TCP, TFO_CLIENT_SOCKOPT, (void *)&opt, + sizeof(opt)); + } +#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, void *user_data) + ares_socklen_t address_len, unsigned int flags, + void *user_data) { (void)user_data; - return connect(sock, address, address_len); +#if defined(TFO_SKIP_CONNECT) && TFO_SKIP_CONNECT + if (flags & ARES_SOCKET_CONN_TCP_FASTOPEN) { + return 0; + } +#endif + +#if 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 + return connect(sock, address, address_len); +#endif } static ares_ssize_t default_arecvfrom(ares_socket_t sock, void *buffer, @@ -291,8 +392,10 @@ static int default_agetsockname(ares_socket_t sock, struct sockaddr *address, 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, @@ -331,8 +434,21 @@ static ares_socket_t legacycb_asocket(int domain, int type, int protocol, 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, void *user_data) + ares_socklen_t address_len, unsigned int flags, + void *user_data) { ares_channel_t *channel = user_data; @@ -342,7 +458,7 @@ static int legacycb_aconnect(ares_socket_t sock, const struct sockaddr *address, sock, address, address_len, channel->legacy_sock_funcs_cb_data); } - return default_aconnect(sock, address, address_len, NULL); + return default_aconnect(sock, address, address_len, flags, NULL); } static ares_ssize_t legacycb_arecvfrom(ares_socket_t sock, void *buffer, @@ -392,8 +508,10 @@ static ares_ssize_t legacycb_asendto(ares_socket_t sock, const void *buffer, static const struct ares_socket_functions_ex legacy_socket_functions = { 1, + 0, legacycb_asocket, legacycb_aclose, + legacycb_asetsockopt, legacycb_aconnect, legacycb_arecvfrom, legacycb_asendto, diff --git a/src/lib/ares_socket.c b/src/lib/ares_socket.c index 2e291694a9..dd9ee90942 100644 --- a/src/lib/ares_socket.c +++ b/src/lib/ares_socket.c @@ -55,117 +55,6 @@ #include #include -#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 { @@ -174,17 +63,6 @@ struct iovec { }; #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 - - return ARES_TRUE; -#endif -} - static ares_conn_err_t ares_socket_deref_error(int err) { switch (err) { @@ -258,7 +136,8 @@ 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, - 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; ares_ssize_t rv; @@ -268,7 +147,7 @@ ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd, flags |= MSG_NOSIGNAL; #endif - rv = channel->sock_funcs.asendto(fd, data, len, flags, NULL, 0, + rv = channel->sock_funcs.asendto(fd, data, len, flags, sa, salen, channel->sock_func_cb_data); if (rv <= 0) { err = ares_socket_deref_error(SOCKERRNO); @@ -278,44 +157,6 @@ ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd, 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)channel->sock_funcs.sendto(fd, data, len, 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_bool_t is_tcp, void *data, size_t data_len, size_t *read_bytes) @@ -378,24 +219,13 @@ ares_conn_err_t ares_socket_recvfrom(ares_channel_t *channel, ares_socket_t s, ares_conn_err_t ares_socket_enable_tfo(const ares_channel_t *channel, ares_socket_t fd) { -#if defined(TFO_CLIENT_SOCKOPT) - int opt = 1; + ares_bool_t opt = ARES_TRUE; - if (!ares_socket_tfo_supported(channel)) { + if (channel->sock_funcs.asetsockopt(fd, ARES_SOCKET_OPT_TCP_FASTOPEN, (void *)&opt, + sizeof(opt), channel->sock_func_cb_data) != 0) { return ARES_CONN_ERR_NOTIMP; } - if (setsockopt(fd, IPPROTO_TCP, TFO_CLIENT_SOCKOPT, (void *)&opt, - sizeof(opt)) != 0) { - return ARES_CONN_ERR_NOTIMP; - } -#else - if (!ares_socket_tfo_supported(channel)) { - return ARES_CONN_ERR_NOTIMP; - } - - (void)fd; -#endif return ARES_CONN_ERR_SUCCESS; } @@ -409,31 +239,38 @@ ares_status_t ares_socket_configure(ares_channel_t *channel, int family, } local; ares_socklen_t bindlen = 0; - - /* XXX: fix me for custom funcs */ + int rv; /* Set the socket's send and receive buffer sizes. */ - if (channel->socket_send_buffer_size > 0 && - setsockopt(fd, SOL_SOCKET, SO_SNDBUF, + if (channel->socket_send_buffer_size > 0) { + rv = channel->sock_funcs.asetsockopt(fd, ARES_SOCKET_OPT_SENDBUF_SIZE, (void *)&channel->socket_send_buffer_size, - sizeof(channel->socket_send_buffer_size)) != 0) { - 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 && - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, + if (channel->socket_receive_buffer_size > 0) { + rv = channel->sock_funcs.asetsockopt(fd, ARES_SOCKET_OPT_RECVBUF_SIZE, (void *)&channel->socket_receive_buffer_size, - sizeof(channel->socket_receive_buffer_size)) != 0) { - 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)) { - (void)setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, channel->local_dev_name, - sizeof(channel->local_dev_name)); + rv = channel->sock_funcs.asetsockopt(fd, ARES_SOCKET_OPT_BIND_DEVICE, + channel->local_dev_name, sizeof(channel->local_dev_name), + channel->sock_func_cb_data); + if (rv != 0 && SOCKERRNO != ENOSYS) { + return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */ + } } -#endif + /* Bind to ip address if configured */ if (family == AF_INET && channel->local_ip4) { memset(&local.sa4, 0, sizeof(local.sa4)); local.sa4.sin_family = AF_INET; @@ -524,36 +361,18 @@ ares_conn_err_t ares_socket_connect(ares_channel_t *channel, const struct sockaddr *addr, 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) { - return ARES_CONN_ERR_SUCCESS; + flags |= ARES_SOCKET_CONN_TCP_FASTOPEN; } -#endif do { int rv; - 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 = channel->sock_funcs.aconnect(sockfd, addr, addrlen, - channel->sock_func_cb_data); -#endif - } else { - rv = channel->sock_funcs.aconnect(sockfd, addr, addrlen, - channel->sock_func_cb_data); - } + rv = channel->sock_funcs.aconnect(sockfd, addr, addrlen, flags, + channel->sock_func_cb_data); if (rv < 0) { err = ares_socket_deref_error(SOCKERRNO); diff --git a/src/lib/ares_socket.h b/src/lib/ares_socket.h index 2145ec22b5..561042c467 100644 --- a/src/lib/ares_socket.h +++ b/src/lib/ares_socket.h @@ -27,6 +27,94 @@ #ifndef __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 +# 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 */ typedef enum { ARES_CONN_ERR_SUCCESS = 0, /*!< Success */ @@ -50,8 +138,6 @@ typedef enum { ARES_CONN_ERR_FAILURE = 99 /*!< Generic failure */ } 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, const struct ares_addr *aa); ares_status_t ares_socket_configure(ares_channel_t *channel, int family, @@ -70,9 +156,6 @@ ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr, unsigned short *port, const struct sockaddr *sockaddr); ares_conn_err_t ares_socket_write(ares_channel_t *channel, ares_socket_t fd, - const void *data, size_t len, - size_t *written); -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,