aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/c-ares/src/lib/ares_socket.c
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2024-10-19 17:59:18 +0000
committerAlexander Smirnov <alex@ydb.tech>2024-10-19 17:59:18 +0000
commitceddbfe68f6ec7949a4062716c8f9840a59c6888 (patch)
treeabfecadbb9c1e5aea40701dd20d902cb7bccd962 /contrib/libs/c-ares/src/lib/ares_socket.c
parent07f2e60d02d95eab14a86a4b9469db1af7795001 (diff)
parentd920c750e476fa2dc80c45f990d9456b1afeadd1 (diff)
downloadydb-ceddbfe68f6ec7949a4062716c8f9840a59c6888.tar.gz
Merge branch 'rightlib' into mergelibs-241019-1758
Diffstat (limited to 'contrib/libs/c-ares/src/lib/ares_socket.c')
-rw-r--r--contrib/libs/c-ares/src/lib/ares_socket.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/contrib/libs/c-ares/src/lib/ares_socket.c b/contrib/libs/c-ares/src/lib/ares_socket.c
new file mode 100644
index 0000000000..df02fd61b6
--- /dev/null
+++ b/contrib/libs/c-ares/src/lib/ares_socket.c
@@ -0,0 +1,424 @@
+/* MIT License
+ *
+ * Copyright (c) Massachusetts Institute of Technology
+ * Copyright (c) The c-ares project and its contributors
+ *
+ * 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>
+
+static ares_conn_err_t ares_socket_deref_error(int err)
+{
+ switch (err) {
+#if defined(EWOULDBLOCK)
+ case EWOULDBLOCK:
+ return ARES_CONN_ERR_WOULDBLOCK;
+#endif
+#if defined(EAGAIN) && (!defined(EWOULDBLOCK) || EAGAIN != EWOULDBLOCK)
+ case EAGAIN:
+ return ARES_CONN_ERR_WOULDBLOCK;
+#endif
+ case EINPROGRESS:
+ return ARES_CONN_ERR_WOULDBLOCK;
+ case ENETDOWN:
+ return ARES_CONN_ERR_NETDOWN;
+ case ENETUNREACH:
+ return ARES_CONN_ERR_NETUNREACH;
+ case ECONNABORTED:
+ return ARES_CONN_ERR_CONNABORTED;
+ case ECONNRESET:
+ return ARES_CONN_ERR_CONNRESET;
+ case ECONNREFUSED:
+ return ARES_CONN_ERR_CONNREFUSED;
+ case ETIMEDOUT:
+ return ARES_CONN_ERR_CONNTIMEDOUT;
+ case EHOSTDOWN:
+ return ARES_CONN_ERR_HOSTDOWN;
+ case EHOSTUNREACH:
+ return ARES_CONN_ERR_HOSTUNREACH;
+ case EINTR:
+ return ARES_CONN_ERR_INTERRUPT;
+ case EAFNOSUPPORT:
+ return ARES_CONN_ERR_AFNOSUPPORT;
+ case EADDRNOTAVAIL:
+ return ARES_CONN_ERR_BADADDR;
+ default:
+ break;
+ }
+
+ return ARES_CONN_ERR_FAILURE;
+}
+
+ares_bool_t ares_sockaddr_addr_eq(const struct sockaddr *sa,
+ const struct ares_addr *aa)
+{
+ const void *addr1;
+ const void *addr2;
+
+ if (sa->sa_family == aa->family) {
+ switch (aa->family) {
+ case AF_INET:
+ addr1 = &aa->addr.addr4;
+ addr2 = &(CARES_INADDR_CAST(const struct sockaddr_in *, sa))->sin_addr;
+ if (memcmp(addr1, addr2, sizeof(aa->addr.addr4)) == 0) {
+ return ARES_TRUE; /* match */
+ }
+ break;
+ case AF_INET6:
+ addr1 = &aa->addr.addr6;
+ addr2 =
+ &(CARES_INADDR_CAST(const struct sockaddr_in6 *, sa))->sin6_addr;
+ if (memcmp(addr1, addr2, sizeof(aa->addr.addr6)) == 0) {
+ return ARES_TRUE; /* match */
+ }
+ break;
+ default:
+ break; /* LCOV_EXCL_LINE */
+ }
+ }
+ return ARES_FALSE; /* different */
+}
+
+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 struct sockaddr *sa,
+ ares_socklen_t salen)
+{
+ int flags = 0;
+ ares_ssize_t rv;
+ ares_conn_err_t err = ARES_CONN_ERR_SUCCESS;
+
+#ifdef HAVE_MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+#endif
+
+ 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);
+ } else {
+ *written = (size_t)rv;
+ }
+ 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)
+{
+ ares_ssize_t rv;
+
+ *read_bytes = 0;
+
+ rv = channel->sock_funcs.arecvfrom(s, data, data_len, 0, NULL, 0,
+ channel->sock_func_cb_data);
+
+ if (rv > 0) {
+ *read_bytes = (size_t)rv;
+ return ARES_CONN_ERR_SUCCESS;
+ }
+
+ if (rv == 0) {
+ /* UDP allows 0-byte packets and is connectionless, so this is success */
+ if (!is_tcp) {
+ return ARES_CONN_ERR_SUCCESS;
+ } else {
+ return ARES_CONN_ERR_CONNCLOSED;
+ }
+ }
+
+ /* If we're here, rv<0 */
+ return ares_socket_deref_error(SOCKERRNO);
+}
+
+ares_conn_err_t ares_socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
+ ares_bool_t is_tcp, void *data,
+ size_t data_len, int flags,
+ struct sockaddr *from,
+ ares_socklen_t *from_len,
+ size_t *read_bytes)
+{
+ ares_ssize_t rv;
+
+ rv = channel->sock_funcs.arecvfrom(s, data, data_len, flags, from, from_len,
+ channel->sock_func_cb_data);
+
+ if (rv > 0) {
+ *read_bytes = (size_t)rv;
+ return ARES_CONN_ERR_SUCCESS;
+ }
+
+ if (rv == 0) {
+ /* UDP allows 0-byte packets and is connectionless, so this is success */
+ if (!is_tcp) {
+ return ARES_CONN_ERR_SUCCESS;
+ } else {
+ return ARES_CONN_ERR_CONNCLOSED;
+ }
+ }
+
+ /* If we're here, rv<0 */
+ return ares_socket_deref_error(SOCKERRNO);
+}
+
+ares_conn_err_t ares_socket_enable_tfo(const ares_channel_t *channel,
+ ares_socket_t fd)
+{
+ ares_bool_t opt = ARES_TRUE;
+
+ 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;
+ }
+
+ return ARES_CONN_ERR_SUCCESS;
+}
+
+ares_status_t ares_socket_configure(ares_channel_t *channel, int family,
+ ares_bool_t is_tcp, ares_socket_t fd)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ } local;
+
+ ares_socklen_t bindlen = 0;
+ int rv;
+ unsigned int bind_flags = 0;
+
+ /* Set the socket's send and receive buffer sizes. */
+ 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), channel->sock_func_cb_data);
+ if (rv != 0 && SOCKERRNO != ENOSYS) {
+ return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */
+ }
+ }
+
+ 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), channel->sock_func_cb_data);
+ if (rv != 0 && SOCKERRNO != ENOSYS) {
+ return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */
+ }
+ }
+
+ /* Bind to network interface if configured */
+ if (ares_strlen(channel->local_dev_name)) {
+ /* Prior versions silently ignored failure, so we need to maintain that
+ * compatibility */
+ (void)channel->sock_funcs.asetsockopt(
+ fd, ARES_SOCKET_OPT_BIND_DEVICE, channel->local_dev_name,
+ sizeof(channel->local_dev_name), channel->sock_func_cb_data);
+ }
+
+ /* 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;
+ local.sa4.sin_addr.s_addr = htonl(channel->local_ip4);
+ bindlen = sizeof(local.sa4);
+ } else if (family == AF_INET6 &&
+ memcmp(channel->local_ip6, ares_in6addr_any._S6_un._S6_u8,
+ sizeof(channel->local_ip6)) != 0) {
+ /* Only if not link-local and an ip other than "::" is specified */
+ memset(&local.sa6, 0, sizeof(local.sa6));
+ local.sa6.sin6_family = AF_INET6;
+ memcpy(&local.sa6.sin6_addr, channel->local_ip6,
+ sizeof(channel->local_ip6));
+ bindlen = sizeof(local.sa6);
+ }
+
+
+ if (bindlen && channel->sock_funcs.abind != NULL) {
+ bind_flags |= ARES_SOCKET_BIND_CLIENT;
+ if (is_tcp) {
+ bind_flags |= ARES_SOCKET_BIND_TCP;
+ }
+ if (channel->sock_funcs.abind(fd, bind_flags, &local.sa, bindlen,
+ channel->sock_func_cb_data) != 0) {
+ return ARES_ECONNREFUSED;
+ }
+ }
+
+ return ARES_SUCCESS;
+}
+
+ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr,
+ unsigned short *port,
+ const struct sockaddr *sockaddr)
+{
+ if (sockaddr->sa_family == AF_INET) {
+ /* NOTE: memcpy sockaddr_in due to alignment issues found by UBSAN due to
+ * dnsinfo packing on MacOS */
+ struct sockaddr_in sockaddr_in;
+ memcpy(&sockaddr_in, sockaddr, sizeof(sockaddr_in));
+
+ ares_addr->family = AF_INET;
+ memcpy(&ares_addr->addr.addr4, &(sockaddr_in.sin_addr),
+ sizeof(ares_addr->addr.addr4));
+
+ if (port) {
+ *port = ntohs(sockaddr_in.sin_port);
+ }
+ return ARES_TRUE;
+ }
+
+ if (sockaddr->sa_family == AF_INET6) {
+ /* NOTE: memcpy sockaddr_in6 due to alignment issues found by UBSAN due to
+ * dnsinfo packing on MacOS */
+ struct sockaddr_in6 sockaddr_in6;
+ memcpy(&sockaddr_in6, sockaddr, sizeof(sockaddr_in6));
+
+ ares_addr->family = AF_INET6;
+ memcpy(&ares_addr->addr.addr6, &(sockaddr_in6.sin6_addr),
+ sizeof(ares_addr->addr.addr6));
+ if (port) {
+ *port = ntohs(sockaddr_in6.sin6_port);
+ }
+ return ARES_TRUE;
+ }
+
+ return ARES_FALSE;
+}
+
+ares_conn_err_t ares_socket_open(ares_socket_t *sock, ares_channel_t *channel,
+ int af, int type, int protocol)
+{
+ ares_socket_t s;
+
+ *sock = ARES_SOCKET_BAD;
+
+ s =
+ channel->sock_funcs.asocket(af, type, protocol, channel->sock_func_cb_data);
+
+ if (s == ARES_SOCKET_BAD) {
+ return ares_socket_deref_error(SOCKERRNO);
+ }
+
+ *sock = s;
+
+ return ARES_CONN_ERR_SUCCESS;
+}
+
+ares_conn_err_t ares_socket_connect(ares_channel_t *channel,
+ ares_socket_t sockfd, ares_bool_t is_tfo,
+ const struct sockaddr *addr,
+ ares_socklen_t addrlen)
+{
+ ares_conn_err_t err = ARES_CONN_ERR_SUCCESS;
+ unsigned int flags = 0;
+
+ if (is_tfo) {
+ flags |= ARES_SOCKET_CONN_TCP_FASTOPEN;
+ }
+
+ do {
+ int rv;
+
+ rv = channel->sock_funcs.aconnect(sockfd, addr, addrlen, flags,
+ channel->sock_func_cb_data);
+
+ if (rv < 0) {
+ err = ares_socket_deref_error(SOCKERRNO);
+ } else {
+ err = ARES_CONN_ERR_SUCCESS;
+ }
+ } while (err == ARES_CONN_ERR_INTERRUPT);
+
+ return err;
+}
+
+void ares_socket_close(ares_channel_t *channel, ares_socket_t s)
+{
+ if (channel == NULL || s == ARES_SOCKET_BAD) {
+ return;
+ }
+
+ channel->sock_funcs.aclose(s, channel->sock_func_cb_data);
+}
+
+void ares_set_socket_callback(ares_channel_t *channel,
+ ares_sock_create_callback cb, void *data)
+{
+ if (channel == NULL) {
+ return;
+ }
+ channel->sock_create_cb = cb;
+ channel->sock_create_cb_data = data;
+}
+
+void ares_set_socket_configure_callback(ares_channel_t *channel,
+ ares_sock_config_callback cb,
+ void *data)
+{
+ if (channel == NULL || channel->optmask & ARES_OPT_EVENT_THREAD) {
+ return;
+ }
+ channel->sock_config_cb = cb;
+ channel->sock_config_cb_data = data;
+}
+
+void ares_set_pending_write_cb(ares_channel_t *channel,
+ ares_pending_write_cb callback, void *user_data)
+{
+ if (channel == NULL || channel->optmask & ARES_OPT_EVENT_THREAD) {
+ return;
+ }
+ channel->notify_pending_write_cb = callback;
+ channel->notify_pending_write_cb_data = user_data;
+}