%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ares__socket.c |
/* 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_setup.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> #include "ares.h" #include "ares_private.h" ares_ssize_t ares__socket_recvfrom(ares_channel_t *channel, ares_socket_t s, void *data, size_t data_len, int flags, struct sockaddr *from, ares_socklen_t *from_len) { if (channel->sock_funcs && channel->sock_funcs->arecvfrom) { return channel->sock_funcs->arecvfrom(s, data, data_len, flags, from, from_len, channel->sock_func_cb_data); } #ifdef HAVE_RECVFROM return (ares_ssize_t)recvfrom(s, data, (RECVFROM_TYPE_ARG3)data_len, flags, from, from_len); #else return sread(s, data, data_len); #endif } ares_ssize_t ares__socket_recv(ares_channel_t *channel, ares_socket_t s, void *data, size_t data_len) { if (channel->sock_funcs && channel->sock_funcs->arecvfrom) { return channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0, channel->sock_func_cb_data); } /* sread() is a wrapper for read() or recv() depending on the system */ return sread(s, data, data_len); } /* * 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(WIN32) /* 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 static int configure_socket(ares_socket_t s, struct server_state *server) { union { struct sockaddr sa; struct sockaddr_in sa4; struct sockaddr_in6 sa6; } local; ares_socklen_t bindlen = 0; ares_channel_t *channel = server->channel; /* do not set options for user-managed sockets */ if (channel->sock_funcs && channel->sock_funcs->asocket) { return 0; } (void)setsocknonblock(s, 1); #if defined(FD_CLOEXEC) && !defined(MSDOS) /* Configure the socket fd as close-on-exec. */ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { return -1; /* LCOV_EXCL_LINE */ } #endif /* Set the socket's send and receive buffer sizes. */ if ((channel->socket_send_buffer_size > 0) && setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *)&channel->socket_send_buffer_size, sizeof(channel->socket_send_buffer_size)) == -1) { return -1; } if ((channel->socket_receive_buffer_size > 0) && setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *)&channel->socket_receive_buffer_size, sizeof(channel->socket_receive_buffer_size)) == -1) { return -1; } #ifdef SO_BINDTODEVICE if (channel->local_dev_name[0] && setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, channel->local_dev_name, sizeof(channel->local_dev_name))) { /* Only root can do this, and usually not fatal if it doesn't work, so */ /* just continue on. */ } #endif if (server->addr.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 (server->addr.family == AF_INET6 && server->ll_scope == 0 && 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 && bind(s, &local.sa, bindlen) < 0) { return -1; } if (server->addr.family == AF_INET6) { set_ipv6_v6only(s, 0); } return 0; } ares_status_t ares__open_connection(ares_channel_t *channel, struct server_state *server, ares_bool_t is_tcp) { ares_socket_t s; int opt; ares_socklen_t salen; union { struct sockaddr_in sa4; struct sockaddr_in6 sa6; } saddr; struct sockaddr *sa; struct server_connection *conn; ares__llist_node_t *node; int type = is_tcp ? SOCK_STREAM : SOCK_DGRAM; #ifdef __OpenBSD__ if ((is_tcp && server->tcp_port == 53) || (!is_tcp && server->udp_port == 53)) { type |= SOCK_DNS; } #endif switch (server->addr.family) { case AF_INET: sa = (void *)&saddr.sa4; salen = sizeof(saddr.sa4); memset(sa, 0, (size_t)salen); saddr.sa4.sin_family = AF_INET; saddr.sa4.sin_port = htons(is_tcp ? server->tcp_port : server->udp_port); memcpy(&saddr.sa4.sin_addr, &server->addr.addr.addr4, sizeof(saddr.sa4.sin_addr)); break; case AF_INET6: sa = (void *)&saddr.sa6; salen = sizeof(saddr.sa6); memset(sa, 0, (size_t)salen); saddr.sa6.sin6_family = AF_INET6; saddr.sa6.sin6_port = htons(is_tcp ? server->tcp_port : server->udp_port); memcpy(&saddr.sa6.sin6_addr, &server->addr.addr.addr6, sizeof(saddr.sa6.sin6_addr)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID saddr.sa6.sin6_scope_id = server->ll_scope; #endif break; default: return ARES_EBADFAMILY; /* LCOV_EXCL_LINE */ } /* Acquire a socket. */ s = ares__open_socket(channel, server->addr.family, type, 0); if (s == ARES_SOCKET_BAD) { return ARES_ECONNREFUSED; } /* Configure it. */ if (configure_socket(s, server) < 0) { ares__close_socket(channel, s); return ARES_ECONNREFUSED; } #ifdef TCP_NODELAY if (is_tcp) { /* * Disable the Nagle algorithm (only relevant for TCP sockets, and thus not * in configure_socket). In general, in DNS lookups we're pretty much * interested in firing off a single request and then waiting for a reply, * so batching isn't very interesting. */ opt = 1; if ((!channel->sock_funcs || !channel->sock_funcs->asocket) && setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)) == -1) { ares__close_socket(channel, s); return ARES_ECONNREFUSED; } } #endif if (channel->sock_config_cb) { int err = channel->sock_config_cb(s, type, channel->sock_config_cb_data); if (err < 0) { ares__close_socket(channel, s); return ARES_ECONNREFUSED; } } /* Connect to the server. */ if (ares__connect_socket(channel, s, sa, salen) == -1) { int err = SOCKERRNO; if (err != EINPROGRESS && err != EWOULDBLOCK) { ares__close_socket(channel, s); return ARES_ECONNREFUSED; } } if (channel->sock_create_cb) { int err = channel->sock_create_cb(s, type, channel->sock_create_cb_data); if (err < 0) { ares__close_socket(channel, s); return ARES_ECONNREFUSED; } } conn = ares_malloc(sizeof(*conn)); if (conn == NULL) { ares__close_socket(channel, s); return ARES_ENOMEM; } memset(conn, 0, sizeof(*conn)); conn->fd = s; conn->server = server; conn->queries_to_conn = ares__llist_create(NULL); conn->is_tcp = is_tcp; if (conn->queries_to_conn == NULL) { ares__close_socket(channel, s); ares_free(conn); return ARES_ENOMEM; } /* TCP connections are thrown to the end as we don't spawn multiple TCP * connections. UDP connections are put on front where the newest connection * can be quickly pulled */ if (is_tcp) { node = ares__llist_insert_last(server->connections, conn); } else { node = ares__llist_insert_first(server->connections, conn); } if (node == NULL) { ares__close_socket(channel, s); ares__llist_destroy(conn->queries_to_conn); ares_free(conn); return ARES_ENOMEM; } /* Register globally to quickly map event on file descriptor to connection * node object */ if (!ares__htable_asvp_insert(channel->connnode_by_socket, s, node)) { ares__close_socket(channel, s); ares__llist_destroy(conn->queries_to_conn); ares__llist_node_claim(node); ares_free(conn); return ARES_ENOMEM; } SOCK_STATE_CALLBACK(channel, s, 1, 0); if (is_tcp) { server->tcp_conn = conn; } return ARES_SUCCESS; } ares_socket_t ares__open_socket(ares_channel_t *channel, int af, int type, int protocol) { if (channel->sock_funcs && channel->sock_funcs->asocket) { return channel->sock_funcs->asocket(af, type, protocol, channel->sock_func_cb_data); } return socket(af, type, protocol); } int ares__connect_socket(ares_channel_t *channel, ares_socket_t sockfd, const struct sockaddr *addr, ares_socklen_t addrlen) { if (channel->sock_funcs && channel->sock_funcs->aconnect) { return channel->sock_funcs->aconnect(sockfd, addr, addrlen, channel->sock_func_cb_data); } return connect(sockfd, addr, addrlen); } void ares__close_socket(ares_channel_t *channel, ares_socket_t s) { if (s == ARES_SOCKET_BAD) { return; } if (channel->sock_funcs && channel->sock_funcs->aclose) { channel->sock_funcs->aclose(s, channel->sock_func_cb_data); } else { sclose(s); } } #ifndef HAVE_WRITEV /* Structure for scatter/gather I/O. */ struct iovec { void *iov_base; /* Pointer to data. */ size_t iov_len; /* Length of data. */ }; #endif ares_ssize_t ares__socket_write(ares_channel_t *channel, ares_socket_t s, const void *data, size_t len) { if (channel->sock_funcs && channel->sock_funcs->asendv) { struct iovec vec; vec.iov_base = (void *)((size_t)data); /* Cast off const */ vec.iov_len = len; return channel->sock_funcs->asendv(s, &vec, 1, channel->sock_func_cb_data); } return swrite(s, data, len); } void ares_set_socket_callback(ares_channel_t *channel, ares_sock_create_callback cb, void *data) { 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_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; }