mirror of https://github.com/c-ares/c-ares.git
cleanups: split functions out into different files, rename some to be more logical (#589)
parent
3326d331d1
commit
58e6f1fa81
16 changed files with 1297 additions and 824 deletions
@ -0,0 +1,195 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2023 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_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares__htable.h" |
||||
#include "ares__htable_strvp.h" |
||||
|
||||
struct ares__htable_strvp { |
||||
ares__htable_strvp_val_free_t free_val; |
||||
ares__htable_t *hash; |
||||
}; |
||||
|
||||
typedef struct { |
||||
char *key; |
||||
void *val; |
||||
ares__htable_strvp_t *parent; |
||||
} ares__htable_strvp_bucket_t; |
||||
|
||||
void ares__htable_strvp_destroy(ares__htable_strvp_t *htable) |
||||
{ |
||||
if (htable == NULL) { |
||||
return; |
||||
} |
||||
|
||||
ares__htable_destroy(htable->hash); |
||||
ares_free(htable); |
||||
} |
||||
|
||||
static unsigned int hash_func(const void *key, unsigned int seed) |
||||
{ |
||||
const char *arg = key; |
||||
return ares__htable_hash_FNV1a_casecmp((const unsigned char *)arg, |
||||
ares_strlen(arg), seed); |
||||
} |
||||
|
||||
static const void *bucket_key(const void *bucket) |
||||
{ |
||||
const ares__htable_strvp_bucket_t *arg = bucket; |
||||
return arg->key; |
||||
} |
||||
|
||||
static void bucket_free(void *bucket) |
||||
{ |
||||
ares__htable_strvp_bucket_t *arg = bucket; |
||||
|
||||
if (arg->parent->free_val) { |
||||
arg->parent->free_val(arg->val); |
||||
} |
||||
ares_free(arg->key); |
||||
ares_free(arg); |
||||
} |
||||
|
||||
static ares_bool_t key_eq(const void *key1, const void *key2) |
||||
{ |
||||
const char *k1 = key1; |
||||
const char *k2 = key2; |
||||
|
||||
if (strcmp(k1, k2) == 0) { |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
ares__htable_strvp_t * |
||||
ares__htable_strvp_create(ares__htable_strvp_val_free_t val_free) |
||||
{ |
||||
ares__htable_strvp_t *htable = ares_malloc(sizeof(*htable)); |
||||
if (htable == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
htable->hash = |
||||
ares__htable_create(hash_func, bucket_key, bucket_free, key_eq); |
||||
if (htable->hash == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
htable->free_val = val_free; |
||||
|
||||
return htable; |
||||
|
||||
fail: |
||||
if (htable) { |
||||
ares__htable_destroy(htable->hash); |
||||
ares_free(htable); |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
ares_bool_t ares__htable_strvp_insert(ares__htable_strvp_t *htable, |
||||
const char *key, |
||||
void *val) |
||||
{ |
||||
ares__htable_strvp_bucket_t *bucket = NULL; |
||||
|
||||
if (htable == NULL || key == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
bucket = ares_malloc(sizeof(*bucket)); |
||||
if (bucket == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
bucket->parent = htable; |
||||
bucket->key = ares_strdup(key); |
||||
bucket->val = val; |
||||
|
||||
if (!ares__htable_insert(htable->hash, bucket)) { |
||||
goto fail; |
||||
} |
||||
|
||||
return ARES_TRUE; |
||||
|
||||
fail: |
||||
if (bucket) { |
||||
ares_free(bucket); |
||||
} |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
ares_bool_t ares__htable_strvp_get(const ares__htable_strvp_t *htable, |
||||
const char *key, void **val) |
||||
{ |
||||
ares__htable_strvp_bucket_t *bucket = NULL; |
||||
|
||||
if (val) { |
||||
*val = NULL; |
||||
} |
||||
|
||||
if (htable == NULL || key == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
bucket = ares__htable_get(htable->hash, &key); |
||||
if (bucket == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
if (val) { |
||||
*val = bucket->val; |
||||
} |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
void *ares__htable_strvp_get_direct(const ares__htable_strvp_t *htable, |
||||
const char *key) |
||||
{ |
||||
void *val = NULL; |
||||
ares__htable_strvp_get(htable, key, &val); |
||||
return val; |
||||
} |
||||
|
||||
ares_bool_t ares__htable_strvp_remove(ares__htable_strvp_t *htable, |
||||
const char *key) |
||||
{ |
||||
if (htable == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
return ares__htable_remove(htable->hash, key); |
||||
} |
||||
|
||||
size_t ares__htable_strvp_num_keys(const ares__htable_strvp_t *htable) |
||||
{ |
||||
if (htable == NULL) { |
||||
return 0; |
||||
} |
||||
return ares__htable_num_keys(htable->hash); |
||||
} |
@ -0,0 +1,118 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2023 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 |
||||
*/ |
||||
#ifndef __ARES__HTABLE_STRVP_H |
||||
#define __ARES__HTABLE_STRVP_H |
||||
|
||||
/*! \addtogroup ares__htable_strvp HashTable with string Key and void pointer
|
||||
* Value |
||||
* |
||||
* This data structure wraps the base ares__htable data structure in order to |
||||
* split the key and value data types as string and void pointer, respectively. |
||||
* |
||||
* Average time complexity: |
||||
* - Insert: O(1) |
||||
* - Search: O(1) |
||||
* - Delete: O(1) |
||||
* |
||||
* @{ |
||||
*/ |
||||
|
||||
struct ares__htable_strvp; |
||||
|
||||
/*! Opaque data type for size_t key, void pointer hash table implementation */ |
||||
typedef struct ares__htable_strvp ares__htable_strvp_t; |
||||
|
||||
/*! Callback to free value stored in hashtable
|
||||
* |
||||
* \param[in] val user-supplied value |
||||
*/ |
||||
typedef void (*ares__htable_strvp_val_free_t)(void *val); |
||||
|
||||
/*! Destroy hashtable
|
||||
* |
||||
* \param[in] htable Initialized hashtable |
||||
*/ |
||||
void ares__htable_strvp_destroy(ares__htable_strvp_t *htable); |
||||
|
||||
/*! Create string, void pointer value hash table
|
||||
* |
||||
* \param[in] val_free Optional. Call back to free user-supplied value. If |
||||
* NULL it is expected the caller will clean up any user |
||||
* supplied values. |
||||
*/ |
||||
ares__htable_strvp_t * |
||||
ares__htable_strvp_create(ares__htable_strvp_val_free_t val_free); |
||||
|
||||
/*! Insert key/value into hash table
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to associate with value |
||||
* \param[in] val value to store (takes ownership). May be NULL. |
||||
* \return ARES_TRUE on success, ARES_FALSE on failure or out of memory |
||||
*/ |
||||
ares_bool_t ares__htable_strvp_insert(ares__htable_strvp_t *htable, |
||||
const char *key, void *val); |
||||
|
||||
/*! Retrieve value from hashtable based on key
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to use to search |
||||
* \param[out] val Optional. Pointer to store value. |
||||
* \return ARES_TRUE on success, ARES_FALSE on failure |
||||
*/ |
||||
ares_bool_t ares__htable_strvp_get(const ares__htable_strvp_t *htable, |
||||
const char *key, void **val); |
||||
|
||||
/*! Retrieve value from hashtable directly as return value. Caveat to this
|
||||
* function over ares__htable_strvp_get() is that if a NULL value is stored |
||||
* you cannot determine if the key is not found or the value is NULL. |
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to use to search |
||||
* \return value associated with key in hashtable or NULL |
||||
*/ |
||||
void *ares__htable_strvp_get_direct(const ares__htable_strvp_t *htable, |
||||
const char *key); |
||||
|
||||
/*! Remove a value from the hashtable by key
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to use to search |
||||
* \return ARES_TRUE if found, ARES_FALSE if not |
||||
*/ |
||||
ares_bool_t ares__htable_strvp_remove(ares__htable_strvp_t *htable, |
||||
const char *key); |
||||
|
||||
/*! Retrieve the number of keys stored in the hash table
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \return count |
||||
*/ |
||||
size_t ares__htable_strvp_num_keys(const ares__htable_strvp_t *htable); |
||||
|
||||
/*! @} */ |
||||
|
||||
#endif /* __ARES__HTABLE_STVP_H */ |
@ -0,0 +1,463 @@ |
||||
/* 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 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 recvfrom(s, data, data_len, flags, from, from_len); |
||||
#else |
||||
return sread(s, data, data_len); |
||||
#endif |
||||
} |
||||
|
||||
ares_ssize_t ares__socket_recv(ares_channel channel, ares_socket_t s, |
||||
void *data, size_t data_len) |
||||
{ |
||||
if (channel->sock_funcs && channel->sock_funcs->arecvfrom) { |
||||
return channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0, |
||||
channel->sock_func_cb_data); |
||||
} |
||||
|
||||
return 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 (FALSE != 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, 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, int family, ares_channel channel) |
||||
{ |
||||
union { |
||||
struct sockaddr sa; |
||||
struct sockaddr_in sa4; |
||||
struct sockaddr_in6 sa6; |
||||
} local; |
||||
|
||||
/* do not set options for user-managed sockets */ |
||||
if (channel->sock_funcs && channel->sock_funcs->asocket) { |
||||
return 0; |
||||
} |
||||
|
||||
(void)setsocknonblock(s, TRUE); |
||||
|
||||
#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]) { |
||||
if (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 (family == AF_INET) { |
||||
if (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); |
||||
if (bind(s, &local.sa, sizeof(local.sa4)) < 0) { |
||||
return -1; |
||||
} |
||||
} |
||||
} else if (family == AF_INET6) { |
||||
if (memcmp(channel->local_ip6, ares_in6addr_any._S6_un._S6_u8, |
||||
sizeof(channel->local_ip6)) != 0) { |
||||
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)); |
||||
if (bind(s, &local.sa, sizeof(local.sa6)) < 0) { |
||||
return -1; |
||||
} |
||||
} |
||||
set_ipv6_v6only(s, 0); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
ares_status_t ares__open_connection(ares_channel 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; |
||||
unsigned short port; |
||||
struct server_connection *conn; |
||||
ares__llist_node_t *node; |
||||
int type = is_tcp ? SOCK_STREAM : SOCK_DGRAM; |
||||
|
||||
if (is_tcp) { |
||||
port = server->addr.tcp_port ? server->addr.tcp_port : channel->tcp_port; |
||||
} else { |
||||
port = server->addr.udp_port ? server->addr.udp_port : channel->udp_port; |
||||
} |
||||
|
||||
switch (server->addr.family) { |
||||
case AF_INET: |
||||
sa = (void *)&saddr.sa4; |
||||
salen = sizeof(saddr.sa4); |
||||
memset(sa, 0, salen); |
||||
saddr.sa4.sin_family = AF_INET; |
||||
saddr.sa4.sin_port = port; |
||||
memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4, |
||||
sizeof(server->addr.addrV4)); |
||||
break; |
||||
case AF_INET6: |
||||
sa = (void *)&saddr.sa6; |
||||
salen = sizeof(saddr.sa6); |
||||
memset(sa, 0, salen); |
||||
saddr.sa6.sin6_family = AF_INET6; |
||||
saddr.sa6.sin6_port = port; |
||||
memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6, |
||||
sizeof(server->addr.addrV6)); |
||||
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->addr.family, channel) < 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) { |
||||
if (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_connection_generation = ++channel->tcp_connection_generation; |
||||
server->tcp_conn = conn; |
||||
} |
||||
|
||||
return ARES_SUCCESS; |
||||
} |
||||
|
||||
ares_socket_t ares__open_socket(ares_channel 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 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 channel, ares_socket_t s) |
||||
{ |
||||
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 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 *)data; |
||||
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 channel, |
||||
ares_sock_create_callback cb, void *data) |
||||
{ |
||||
channel->sock_create_cb = cb; |
||||
channel->sock_create_cb_data = data; |
||||
} |
||||
|
||||
void ares_set_socket_configure_callback(ares_channel channel, |
||||
ares_sock_config_callback cb, |
||||
void *data) |
||||
{ |
||||
channel->sock_config_cb = cb; |
||||
channel->sock_config_cb_data = data; |
||||
} |
||||
|
||||
void ares_set_socket_functions(ares_channel channel, |
||||
const struct ares_socket_functions *funcs, |
||||
void *data) |
||||
{ |
||||
channel->sock_funcs = funcs; |
||||
channel->sock_func_cb_data = data; |
||||
} |
||||
|
Loading…
Reference in new issue