cleanups: split functions out into different files, rename some to be more logical (#589)

pull/593/head
Brad House 1 year ago committed by GitHub
parent 3326d331d1
commit 58e6f1fa81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      src/lib/Makefile.inc
  2. 184
      src/lib/ares__buf.c
  3. 45
      src/lib/ares__buf.h
  4. 195
      src/lib/ares__htable_strvp.c
  5. 118
      src/lib/ares__htable_strvp.h
  6. 38
      src/lib/ares__htable_szvp.c
  7. 26
      src/lib/ares__htable_szvp.h
  8. 463
      src/lib/ares__socket.c
  9. 4
      src/lib/ares_destroy.c
  10. 2
      src/lib/ares_getaddrinfo.c
  11. 315
      src/lib/ares_init.c
  12. 281
      src/lib/ares_options.c
  13. 25
      src/lib/ares_private.h
  14. 414
      src/lib/ares_process.c
  15. 2
      src/lib/ares_query.c
  16. 2
      src/lib/ares_send.c

@ -8,12 +8,14 @@ CSOURCES = ares__addrinfo2hostent.c \
ares__get_hostent.c \
ares__htable.c \
ares__htable_asvp.c \
ares__htable_stvp.c \
ares__htable_strvp.c \
ares__htable_szvp.c \
ares__llist.c \
ares__parse_into_addrinfo.c \
ares__read_line.c \
ares__readaddrinfo.c \
ares__slist.c \
ares__socket.c \
ares__sortaddrinfo.c \
ares__timeval.c \
ares_android.c \
@ -72,7 +74,8 @@ CSOURCES = ares__addrinfo2hostent.c \
HHEADERS = ares__buf.h \
ares__htable.h \
ares__htable_asvp.h \
ares__htable_stvp.h \
ares__htable_strvp.h \
ares__htable_szvp.h \
ares__llist.h \
ares__slist.h \
ares_android.h \

@ -45,6 +45,69 @@ struct ares__buf {
* SIZE_MAX if not set. */
};
/* Reserved characters for names that need to be escaped */
static ares_bool_t is_reservedch(int ch)
{
switch (ch) {
case '"':
case '.':
case ';':
case '\\':
case '(':
case ')':
case '@':
case '$':
return ARES_TRUE;
default:
break;
}
return ARES_FALSE;
}
static ares_bool_t ares__isprint(int ch)
{
if (ch >= 0x20 && ch <= 0x7E) {
return ARES_TRUE;
}
return ARES_FALSE;
}
/* Character set allowed by hostnames. This is to include the normal
* domain name character set plus:
* - underscores which are used in SRV records.
* - Forward slashes such as are used for classless in-addr.arpa
* delegation (CNAMEs)
* - Asterisks may be used for wildcard domains in CNAMEs as seen in the
* real world.
* While RFC 2181 section 11 does state not to do validation,
* that applies to servers, not clients. Vulnerabilities have been
* reported when this validation is not performed. Security is more
* important than edge-case compatibility (which is probably invalid
* anyhow). */
static ares_bool_t is_hostnamech(int ch)
{
/* [A-Za-z0-9-*._/]
* Don't use isalnum() as it is locale-specific
*/
if (ch >= 'A' && ch <= 'Z') {
return ARES_TRUE;
}
if (ch >= 'a' && ch <= 'z') {
return ARES_TRUE;
}
if (ch >= '0' && ch <= '9') {
return ARES_TRUE;
}
if (ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '*') {
return ARES_TRUE;
}
return ARES_FALSE;
}
ares__buf_t *ares__buf_create(void)
{
ares__buf_t *buf = ares_malloc_zero(sizeof(*buf));
@ -324,6 +387,62 @@ const unsigned char *ares__buf_tag_fetch(const ares__buf_t *buf, size_t *len)
return buf->data + buf->tag_offset;
}
size_t ares__buf_tag_length(const ares__buf_t *buf)
{
if (buf == NULL || buf->tag_offset == SIZE_MAX)
return 0;
return buf->offset - buf->tag_offset;
}
ares_status_t ares__buf_tag_fetch_bytes(const ares__buf_t *buf,
unsigned char *bytes, size_t *len)
{
size_t ptr_len = 0;
const unsigned char *ptr = ares__buf_tag_fetch(buf, &ptr_len);
if (ptr == NULL || bytes == NULL || len == NULL)
return ARES_EFORMERR;
if (*len < ptr_len)
return ARES_EFORMERR;
*len = ptr_len;
if (ptr_len > 0) {
memcpy(bytes, ptr, ptr_len);
}
return ARES_SUCCESS;
}
ares_status_t ares__buf_tag_fetch_string(const ares__buf_t *buf, char *str,
size_t len)
{
size_t out_len;
ares_status_t status;
size_t i;
if (str == NULL || len == 0)
return ARES_EFORMERR;
/* Space for NULL terminator */
out_len = len - 1;
status = ares__buf_tag_fetch_bytes(buf, (unsigned char *)str, &out_len);
if (status != ARES_SUCCESS)
return status;
/* NULL terminate */
str[out_len] = 0;
/* Validate string is printable */
for (i=0; i<out_len; i++) {
if (!ares__isprint(str[i]))
return ARES_EBADSTR;
}
return ARES_SUCCESS;
}
static const unsigned char *ares__buf_fetch(const ares__buf_t *buf, size_t *len)
{
if (len != NULL) {
@ -454,66 +573,6 @@ ares_status_t ares__buf_fetch_bytes_into_buf(ares__buf_t *buf,
return ares__buf_consume(buf, len);
}
/* Reserved characters for names that need to be escaped */
static ares_bool_t is_reservedch(int ch)
{
switch (ch) {
case '"':
case '.':
case ';':
case '\\':
case '(':
case ')':
case '@':
case '$':
return ARES_TRUE;
default:
break;
}
return ARES_FALSE;
}
static ares_bool_t ares__isprint(int ch)
{
if (ch >= 0x20 && ch <= 0x7E) {
return ARES_TRUE;
}
return ARES_FALSE;
}
/* Character set allowed by hostnames. This is to include the normal
* domain name character set plus:
* - underscores which are used in SRV records.
* - Forward slashes such as are used for classless in-addr.arpa
* delegation (CNAMEs)
* - Asterisks may be used for wildcard domains in CNAMEs as seen in the
* real world.
* While RFC 2181 section 11 does state not to do validation,
* that applies to servers, not clients. Vulnerabilities have been
* reported when this validation is not performed. Security is more
* important than edge-case compatibility (which is probably invalid
* anyhow). */
static ares_bool_t is_hostnamech(int ch)
{
/* [A-Za-z0-9-*._/]
* Don't use isalnum() as it is locale-specific
*/
if (ch >= 'A' && ch <= 'Z') {
return ARES_TRUE;
}
if (ch >= 'a' && ch <= 'z') {
return ARES_TRUE;
}
if (ch >= '0' && ch <= '9') {
return ARES_TRUE;
}
if (ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '*') {
return ARES_TRUE;
}
return ARES_FALSE;
}
static ares_status_t ares__buf_fetch_dnsname_into_buf(ares__buf_t *buf,
ares__buf_t *dest,
@ -582,7 +641,8 @@ fail:
return status;
}
size_t ares__buf_consume_whitespace(ares__buf_t *buf, int include_linefeed)
size_t ares__buf_consume_whitespace(ares__buf_t *buf,
ares_bool_t include_linefeed)
{
size_t remaining_len = 0;
const unsigned char *ptr = ares__buf_fetch(buf, &remaining_len);
@ -648,7 +708,7 @@ done:
return i;
}
size_t ares__buf_consume_line(ares__buf_t *buf, int include_linefeed)
size_t ares__buf_consume_line(ares__buf_t *buf, ares_bool_t include_linefeed)
{
size_t remaining_len = 0;
const unsigned char *ptr = ares__buf_fetch(buf, &remaining_len);

@ -176,6 +176,40 @@ ares_status_t ares__buf_tag_clear(ares__buf_t *buf);
*/
const unsigned char *ares__buf_tag_fetch(const ares__buf_t *buf, size_t *len);
/*! Get the length of the current tag offset to the current position.
*
* \param[in] buf Initialized buffer object
* \return length
*/
size_t ares__buf_tag_length(const ares__buf_t *buf);
/*! Fetch the bytes starting from the tagged position up to the _current_
* position using the provided buffer. It will not unset the tagged position.
*
* \param[in] buf Initialized buffer object
* \param[in,out] bytes Buffer to hold data
* \param[in,out] len On input, buffer size, on output, bytes place in
* buffer.
* \return ARES_SUCCESS if fetched, ARES_EFORMERR if insufficient buffer size
*/
ares_status_t ares__buf_tag_fetch_bytes(const ares__buf_t *buf,
unsigned char *bytes, size_t *len);
/*! Fetch the bytes starting from the tagged position up to the _current_
* position as a NULL-terminated string using the provided buffer. The data
* is validated to be ASCII-printable data. It will not unset the tagged
* poition.
*
* \param[in] buf Initialized buffer object
* \param[in,out] str Buffer to hold data
* \param[in] len On input, buffer size, on output, bytes place in
* buffer.
* \return ARES_SUCCESS if fetched, ARES_EFORMERR if insufficient buffer size,
* ARES_EBADSTR if not printable ASCII
*/
ares_status_t ares__buf_tag_fetch_string(const ares__buf_t *buf, char *str,
size_t len);
/*! Consume the given number of bytes without reading them.
*
* \param[in] buf Initialized buffer object
@ -249,10 +283,12 @@ ares_status_t ares__buf_fetch_str_dup(ares__buf_t *buf, size_t len, char **str);
* 0x0A).
*
* \param[in] buf Initialized buffer object
* \param[in] include_linefeed 1 to include consuming 0x0A, 0 otherwise.
* \param[in] include_linefeed ARES_TRUE to include consuming 0x0A,
* ARES_FALSE otherwise.
* \return number of whitespace characters consumed
*/
size_t ares__buf_consume_whitespace(ares__buf_t *buf, int include_linefeed);
size_t ares__buf_consume_whitespace(ares__buf_t *buf,
ares_bool_t include_linefeed);
/*! Consume any non-whitespace character (anything other than 0x09, 0x0B, 0x0C,
@ -267,10 +303,11 @@ size_t ares__buf_consume_nonwhitespace(ares__buf_t *buf);
* the end of line character (0x0A) itself.
*
* \param[in] buf Initialized buffer object
* \param[in] include_linefeed 1 to include consuming 0x0A, 0 otherwise.
* \param[in] include_linefeed ARES_TRUE to include consuming 0x0A,
* ARES_FALSE otherwise.
* \return number of characters consumed
*/
size_t ares__buf_consume_line(ares__buf_t *buf, int include_linefeed);
size_t ares__buf_consume_line(ares__buf_t *buf, ares_bool_t include_linefeed);
/*! Check the unprocessed buffer to see if it begins with the sequence of

@ -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 */

@ -27,20 +27,20 @@
#include "ares.h"
#include "ares_private.h"
#include "ares__htable.h"
#include "ares__htable_stvp.h"
#include "ares__htable_szvp.h"
struct ares__htable_stvp {
ares__htable_stvp_val_free_t free_val;
struct ares__htable_szvp {
ares__htable_szvp_val_free_t free_val;
ares__htable_t *hash;
};
typedef struct {
size_t key;
void *val;
ares__htable_stvp_t *parent;
} ares__htable_stvp_bucket_t;
ares__htable_szvp_t *parent;
} ares__htable_szvp_bucket_t;
void ares__htable_stvp_destroy(ares__htable_stvp_t *htable)
void ares__htable_szvp_destroy(ares__htable_szvp_t *htable)
{
if (htable == NULL) {
return;
@ -59,13 +59,13 @@ static unsigned int hash_func(const void *key, unsigned int seed)
static const void *bucket_key(const void *bucket)
{
const ares__htable_stvp_bucket_t *arg = bucket;
const ares__htable_szvp_bucket_t *arg = bucket;
return &arg->key;
}
static void bucket_free(void *bucket)
{
ares__htable_stvp_bucket_t *arg = bucket;
ares__htable_szvp_bucket_t *arg = bucket;
if (arg->parent->free_val) {
arg->parent->free_val(arg->val);
@ -86,10 +86,10 @@ static ares_bool_t key_eq(const void *key1, const void *key2)
return ARES_FALSE;
}
ares__htable_stvp_t *
ares__htable_stvp_create(ares__htable_stvp_val_free_t val_free)
ares__htable_szvp_t *
ares__htable_szvp_create(ares__htable_szvp_val_free_t val_free)
{
ares__htable_stvp_t *htable = ares_malloc(sizeof(*htable));
ares__htable_szvp_t *htable = ares_malloc(sizeof(*htable));
if (htable == NULL) {
goto fail;
}
@ -112,10 +112,10 @@ fail:
return NULL;
}
ares_bool_t ares__htable_stvp_insert(ares__htable_stvp_t *htable, size_t key,
ares_bool_t ares__htable_szvp_insert(ares__htable_szvp_t *htable, size_t key,
void *val)
{
ares__htable_stvp_bucket_t *bucket = NULL;
ares__htable_szvp_bucket_t *bucket = NULL;
if (htable == NULL) {
goto fail;
@ -143,10 +143,10 @@ fail:
return ARES_FALSE;
}
ares_bool_t ares__htable_stvp_get(const ares__htable_stvp_t *htable, size_t key,
ares_bool_t ares__htable_szvp_get(const ares__htable_szvp_t *htable, size_t key,
void **val)
{
ares__htable_stvp_bucket_t *bucket = NULL;
ares__htable_szvp_bucket_t *bucket = NULL;
if (val) {
*val = NULL;
@ -167,15 +167,15 @@ ares_bool_t ares__htable_stvp_get(const ares__htable_stvp_t *htable, size_t key,
return ARES_TRUE;
}
void *ares__htable_stvp_get_direct(const ares__htable_stvp_t *htable,
void *ares__htable_szvp_get_direct(const ares__htable_szvp_t *htable,
size_t key)
{
void *val = NULL;
ares__htable_stvp_get(htable, key, &val);
ares__htable_szvp_get(htable, key, &val);
return val;
}
ares_bool_t ares__htable_stvp_remove(ares__htable_stvp_t *htable, size_t key)
ares_bool_t ares__htable_szvp_remove(ares__htable_szvp_t *htable, size_t key)
{
if (htable == NULL) {
return ARES_FALSE;
@ -184,7 +184,7 @@ ares_bool_t ares__htable_stvp_remove(ares__htable_stvp_t *htable, size_t key)
return ares__htable_remove(htable->hash, &key);
}
size_t ares__htable_stvp_num_keys(const ares__htable_stvp_t *htable)
size_t ares__htable_szvp_num_keys(const ares__htable_szvp_t *htable)
{
if (htable == NULL) {
return 0;

@ -26,7 +26,7 @@
#ifndef __ARES__HTABLE_STVP_H
#define __ARES__HTABLE_STVP_H
/*! \addtogroup ares__htable_stvp HashTable with size_t Key and void pointer
/*! \addtogroup ares__htable_szvp HashTable with size_t Key and void pointer
* Value
*
* This data structure wraps the base ares__htable data structure in order to
@ -40,22 +40,22 @@
* @{
*/
struct ares__htable_stvp;
struct ares__htable_szvp;
/*! Opaque data type for size_t key, void pointer hash table implementation */
typedef struct ares__htable_stvp ares__htable_stvp_t;
typedef struct ares__htable_szvp ares__htable_szvp_t;
/*! Callback to free value stored in hashtable
*
* \param[in] val user-supplied value
*/
typedef void (*ares__htable_stvp_val_free_t)(void *val);
typedef void (*ares__htable_szvp_val_free_t)(void *val);
/*! Destroy hashtable
*
* \param[in] htable Initialized hashtable
*/
void ares__htable_stvp_destroy(ares__htable_stvp_t *htable);
void ares__htable_szvp_destroy(ares__htable_szvp_t *htable);
/*! Create size_t key, void pointer value hash table
*
@ -63,8 +63,8 @@ void ares__htable_stvp_destroy(ares__htable_stvp_t *htable);
* NULL it is expected the caller will clean up any user
* supplied values.
*/
ares__htable_stvp_t *
ares__htable_stvp_create(ares__htable_stvp_val_free_t val_free);
ares__htable_szvp_t *
ares__htable_szvp_create(ares__htable_szvp_val_free_t val_free);
/*! Insert key/value into hash table
*
@ -73,7 +73,7 @@ ares__htable_stvp_t *
* \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_stvp_insert(ares__htable_stvp_t *htable, size_t key,
ares_bool_t ares__htable_szvp_insert(ares__htable_szvp_t *htable, size_t key,
void *val);
/*! Retrieve value from hashtable based on key
@ -83,18 +83,18 @@ ares_bool_t ares__htable_stvp_insert(ares__htable_stvp_t *htable, size_t key,
* \param[out] val Optional. Pointer to store value.
* \return ARES_TRUE on success, ARES_FALSE on failure
*/
ares_bool_t ares__htable_stvp_get(const ares__htable_stvp_t *htable, size_t key,
ares_bool_t ares__htable_szvp_get(const ares__htable_szvp_t *htable, size_t key,
void **val);
/*! Retrieve value from hashtable directly as return value. Caveat to this
* function over ares__htable_stvp_get() is that if a NULL value is stored
* function over ares__htable_szvp_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_stvp_get_direct(const ares__htable_stvp_t *htable,
void *ares__htable_szvp_get_direct(const ares__htable_szvp_t *htable,
size_t key);
/*! Remove a value from the hashtable by key
@ -103,14 +103,14 @@ void *ares__htable_stvp_get_direct(const ares__htable_stvp_t *htable,
* \param[in] key key to use to search
* \return ARES_TRUE if found, ARES_FALSE if not
*/
ares_bool_t ares__htable_stvp_remove(ares__htable_stvp_t *htable, size_t key);
ares_bool_t ares__htable_szvp_remove(ares__htable_szvp_t *htable, size_t key);
/*! Retrieve the number of keys stored in the hash table
*
* \param[in] htable Initialized hash table
* \return count
*/
size_t ares__htable_stvp_num_keys(const ares__htable_stvp_t *htable);
size_t ares__htable_szvp_num_keys(const ares__htable_szvp_t *htable);
/*! @} */

@ -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;
}

@ -87,7 +87,7 @@ void ares_destroy(ares_channel channel)
* so all query lists should be empty now.
*/
assert(ares__llist_len(channel->all_queries) == 0);
assert(ares__htable_stvp_num_keys(channel->queries_by_qid) == 0);
assert(ares__htable_szvp_num_keys(channel->queries_by_qid) == 0);
assert(ares__slist_len(channel->queries_by_timeout) == 0);
#endif
@ -106,7 +106,7 @@ void ares_destroy(ares_channel channel)
ares__llist_destroy(channel->all_queries);
ares__slist_destroy(channel->queries_by_timeout);
ares__htable_stvp_destroy(channel->queries_by_qid);
ares__htable_szvp_destroy(channel->queries_by_qid);
ares__htable_asvp_destroy(channel->connnode_by_socket);
if (channel->sortlist) {

@ -570,7 +570,7 @@ static void terminate_retries(struct host_query *hquery, unsigned short qid)
return;
}
query = ares__htable_stvp_get_direct(channel->queries_by_qid, term_qid);
query = ares__htable_szvp_get_direct(channel->queries_by_qid, term_qid);
if (query == NULL) {
return;
}

@ -71,9 +71,6 @@
#endif
static ares_status_t init_by_options(ares_channel channel,
const struct ares_options *options,
int optmask);
static ares_status_t init_by_environment(ares_channel channel);
static ares_status_t init_by_resolv_conf(ares_channel channel);
static ares_status_t init_by_defaults(ares_channel channel);
@ -101,9 +98,7 @@ static ares_status_t config_lookup(ares_channel channel, const char *str,
static char *try_config(char *s, const char *opt, char scc);
#endif
#define ARES_CONFIG_CHECK(x) \
(x->lookups && x->nservers > 0 && x->ndots > 0 && x->timeout > 0 && \
x->tries > 0)
int ares_init(ares_channel *channelptr)
{
@ -167,7 +162,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
goto done;
}
channel->queries_by_qid = ares__htable_stvp_create(NULL);
channel->queries_by_qid = ares__htable_szvp_create(NULL);
if (channel->queries_by_qid == NULL) {
status = ARES_ENOMEM;
goto done;
@ -190,7 +185,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
* precedence to lowest.
*/
status = init_by_options(channel, options, optmask);
status = ares__init_by_options(channel, options, optmask);
if (status != ARES_SUCCESS) {
DEBUGF(fprintf(stderr, "Error: init_by_options failed: %s\n",
ares_strerror(status)));
@ -256,7 +251,7 @@ done:
ares__destroy_rand_state(channel->rand_state);
}
ares__htable_stvp_destroy(channel->queries_by_qid);
ares__htable_szvp_destroy(channel->queries_by_qid);
ares__llist_destroy(channel->all_queries);
ares__slist_destroy(channel->queries_by_timeout);
ares__htable_asvp_destroy(channel->connnode_by_socket);
@ -341,286 +336,6 @@ int ares_dup(ares_channel *dest, ares_channel src)
return ARES_SUCCESS; /* everything went fine */
}
/* Save options from initialized channel */
int ares_save_options(ares_channel channel, struct ares_options *options,
int *optmask)
{
size_t i;
size_t j;
size_t ipv4_nservers = 0;
/* Zero everything out */
memset(options, 0, sizeof(struct ares_options));
if (!ARES_CONFIG_CHECK(channel)) {
return ARES_ENODATA;
}
/* Traditionally the optmask wasn't saved in the channel struct so it was
recreated here. ROTATE is the first option that has no struct field of
its own in the public config struct */
(*optmask) = (ARES_OPT_FLAGS | ARES_OPT_TRIES | ARES_OPT_NDOTS |
ARES_OPT_UDP_PORT | ARES_OPT_TCP_PORT | ARES_OPT_SOCK_STATE_CB |
ARES_OPT_SERVERS | ARES_OPT_DOMAINS | ARES_OPT_LOOKUPS |
ARES_OPT_SORTLIST | ARES_OPT_TIMEOUTMS);
(*optmask) |= (channel->rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE);
if (channel->resolvconf_path) {
(*optmask) |= ARES_OPT_RESOLVCONF;
}
if (channel->hosts_path) {
(*optmask) |= ARES_OPT_HOSTS_FILE;
}
/* Copy easy stuff */
options->flags = (int)channel->flags;
/* We return full millisecond resolution but that's only because we don't
set the ARES_OPT_TIMEOUT anymore, only the new ARES_OPT_TIMEOUTMS */
options->timeout = (int)channel->timeout;
options->tries = (int)channel->tries;
options->ndots = (int)channel->ndots;
options->udp_port = ntohs(channel->udp_port);
options->tcp_port = ntohs(channel->tcp_port);
options->sock_state_cb = channel->sock_state_cb;
options->sock_state_cb_data = channel->sock_state_cb_data;
/* Copy IPv4 servers that use the default port */
if (channel->nservers) {
for (i = 0; i < channel->nservers; i++) {
if ((channel->servers[i].addr.family == AF_INET) &&
(channel->servers[i].addr.udp_port == 0) &&
(channel->servers[i].addr.tcp_port == 0)) {
ipv4_nservers++;
}
}
if (ipv4_nservers) {
options->servers = ares_malloc(ipv4_nservers * sizeof(struct in_addr));
if (!options->servers) {
return ARES_ENOMEM;
}
for (i = j = 0; i < channel->nservers; i++) {
if ((channel->servers[i].addr.family == AF_INET) &&
(channel->servers[i].addr.udp_port == 0) &&
(channel->servers[i].addr.tcp_port == 0)) {
memcpy(&options->servers[j++], &channel->servers[i].addr.addrV4,
sizeof(channel->servers[i].addr.addrV4));
}
}
}
}
options->nservers = (int)ipv4_nservers;
/* copy domains */
if (channel->ndomains) {
options->domains = ares_malloc(channel->ndomains * sizeof(char *));
if (!options->domains) {
return ARES_ENOMEM;
}
for (i = 0; i < channel->ndomains; i++) {
options->domains[i] = ares_strdup(channel->domains[i]);
if (!options->domains[i]) {
options->ndomains = (int)i;
return ARES_ENOMEM;
}
}
}
options->ndomains = (int)channel->ndomains;
/* copy lookups */
if (channel->lookups) {
options->lookups = ares_strdup(channel->lookups);
if (!options->lookups && channel->lookups) {
return ARES_ENOMEM;
}
}
/* copy sortlist */
if (channel->nsort) {
options->sortlist = ares_malloc(channel->nsort * sizeof(struct apattern));
if (!options->sortlist) {
return ARES_ENOMEM;
}
for (i = 0; i < channel->nsort; i++) {
options->sortlist[i] = channel->sortlist[i];
}
}
options->nsort = (int)channel->nsort;
/* copy path for resolv.conf file */
if (channel->resolvconf_path) {
options->resolvconf_path = ares_strdup(channel->resolvconf_path);
if (!options->resolvconf_path) {
return ARES_ENOMEM;
}
}
/* copy path for hosts file */
if (channel->hosts_path) {
options->hosts_path = ares_strdup(channel->hosts_path);
if (!options->hosts_path) {
return ARES_ENOMEM;
}
}
if (channel->udp_max_queries > 0) {
(*optmask) |= ARES_OPT_UDP_MAX_QUERIES;
options->udp_max_queries = (int)channel->udp_max_queries;
}
return ARES_SUCCESS;
}
static ares_status_t init_by_options(ares_channel channel,
const struct ares_options *options,
int optmask)
{
size_t i;
/* Easy stuff. */
if (optmask & ARES_OPT_FLAGS) {
channel->flags = (unsigned int)options->flags;
}
if (optmask & ARES_OPT_TIMEOUTMS) {
channel->timeout = (unsigned int)options->timeout;
} else if (optmask & ARES_OPT_TIMEOUT) {
channel->timeout = (unsigned int)options->timeout * 1000;
}
if (optmask & ARES_OPT_TRIES) {
channel->tries = (size_t)options->tries;
}
if (optmask & ARES_OPT_NDOTS) {
channel->ndots = (size_t)options->ndots;
}
if (optmask & ARES_OPT_ROTATE) {
channel->rotate = ARES_TRUE;
}
if (optmask & ARES_OPT_NOROTATE) {
channel->rotate = ARES_FALSE;
}
if ((optmask & ARES_OPT_UDP_PORT) && channel->udp_port == 0) {
channel->udp_port = htons(options->udp_port);
}
if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == 0) {
channel->tcp_port = htons(options->tcp_port);
}
if ((optmask & ARES_OPT_SOCK_STATE_CB) && channel->sock_state_cb == NULL) {
channel->sock_state_cb = options->sock_state_cb;
channel->sock_state_cb_data = options->sock_state_cb_data;
}
if (optmask & ARES_OPT_SOCK_SNDBUF && options->socket_send_buffer_size > 0) {
channel->socket_send_buffer_size = options->socket_send_buffer_size;
}
if (optmask & ARES_OPT_SOCK_RCVBUF &&
channel->socket_receive_buffer_size > 0) {
channel->socket_receive_buffer_size = options->socket_receive_buffer_size;
}
if (optmask & ARES_OPT_EDNSPSZ) {
channel->ednspsz = (size_t)options->ednspsz;
}
/* Copy the IPv4 servers, if given. */
if (optmask & ARES_OPT_SERVERS) {
/* Avoid zero size allocations at any cost */
if (options->nservers > 0) {
channel->servers =
ares_malloc((size_t)options->nservers * sizeof(*channel->servers));
if (!channel->servers) {
return ARES_ENOMEM;
}
memset(channel->servers, 0,
(size_t)options->nservers * sizeof(*channel->servers));
for (i = 0; i < (size_t)options->nservers; i++) {
channel->servers[i].addr.family = AF_INET;
channel->servers[i].addr.udp_port = 0;
channel->servers[i].addr.tcp_port = 0;
memcpy(&channel->servers[i].addr.addrV4, &options->servers[i],
sizeof(channel->servers[i].addr.addrV4));
}
}
channel->nservers = (size_t)options->nservers;
}
/* Copy the domains, if given. Keep channel->ndomains consistent so
* we can clean up in case of error.
*/
if (optmask & ARES_OPT_DOMAINS) {
/* Avoid zero size allocations at any cost */
if (options->ndomains > 0) {
channel->domains =
ares_malloc((size_t)options->ndomains * sizeof(char *));
if (!channel->domains) {
return ARES_ENOMEM;
}
for (i = 0; i < (size_t)options->ndomains; i++) {
channel->domains[i] = ares_strdup(options->domains[i]);
if (!channel->domains[i]) {
return ARES_ENOMEM;
}
}
}
channel->ndomains = (size_t)options->ndomains;
}
/* Set lookups, if given. */
if ((optmask & ARES_OPT_LOOKUPS) && !channel->lookups) {
channel->lookups = ares_strdup(options->lookups);
if (!channel->lookups) {
return ARES_ENOMEM;
}
}
/* copy sortlist */
if (optmask & ARES_OPT_SORTLIST && options->nsort > 0) {
channel->nsort = (size_t)options->nsort;
channel->sortlist =
ares_malloc((size_t)options->nsort * sizeof(struct apattern));
if (!channel->sortlist) {
return ARES_ENOMEM;
}
for (i = 0; i < (size_t)options->nsort; i++) {
channel->sortlist[i] = options->sortlist[i];
}
}
/* Set path for resolv.conf file, if given. */
if ((optmask & ARES_OPT_RESOLVCONF) && !channel->resolvconf_path) {
channel->resolvconf_path = ares_strdup(options->resolvconf_path);
if (!channel->resolvconf_path && options->resolvconf_path) {
return ARES_ENOMEM;
}
}
/* Set path for hosts file, if given. */
if ((optmask & ARES_OPT_HOSTS_FILE) && !channel->hosts_path) {
channel->hosts_path = ares_strdup(options->hosts_path);
if (!channel->hosts_path && options->hosts_path) {
return ARES_ENOMEM;
}
}
if (optmask & ARES_OPT_UDP_MAX_QUERIES) {
channel->udp_max_queries = (size_t)options->udp_max_queries;
}
channel->optmask = (unsigned int)optmask;
return ARES_SUCCESS;
}
static ares_status_t init_by_environment(ares_channel channel)
{
@ -2456,28 +2171,6 @@ void ares_set_local_dev(ares_channel channel, const char *local_dev_name)
channel->local_dev_name[sizeof(channel->local_dev_name) - 1] = 0;
}
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;
}
int ares_set_sortlist(ares_channel channel, const char *sortstr)
{

@ -403,3 +403,284 @@ int ares_set_servers_ports_csv(ares_channel channel, const char *_csv)
{
return (int)set_servers_csv(channel, _csv, TRUE);
}
/* Save options from initialized channel */
int ares_save_options(ares_channel channel, struct ares_options *options,
int *optmask)
{
size_t i;
size_t j;
size_t ipv4_nservers = 0;
/* Zero everything out */
memset(options, 0, sizeof(struct ares_options));
if (!ARES_CONFIG_CHECK(channel)) {
return ARES_ENODATA;
}
/* Traditionally the optmask wasn't saved in the channel struct so it was
recreated here. ROTATE is the first option that has no struct field of
its own in the public config struct */
(*optmask) = (ARES_OPT_FLAGS | ARES_OPT_TRIES | ARES_OPT_NDOTS |
ARES_OPT_UDP_PORT | ARES_OPT_TCP_PORT | ARES_OPT_SOCK_STATE_CB |
ARES_OPT_SERVERS | ARES_OPT_DOMAINS | ARES_OPT_LOOKUPS |
ARES_OPT_SORTLIST | ARES_OPT_TIMEOUTMS);
(*optmask) |= (channel->rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE);
if (channel->resolvconf_path) {
(*optmask) |= ARES_OPT_RESOLVCONF;
}
if (channel->hosts_path) {
(*optmask) |= ARES_OPT_HOSTS_FILE;
}
/* Copy easy stuff */
options->flags = (int)channel->flags;
/* We return full millisecond resolution but that's only because we don't
set the ARES_OPT_TIMEOUT anymore, only the new ARES_OPT_TIMEOUTMS */
options->timeout = (int)channel->timeout;
options->tries = (int)channel->tries;
options->ndots = (int)channel->ndots;
options->udp_port = ntohs(channel->udp_port);
options->tcp_port = ntohs(channel->tcp_port);
options->sock_state_cb = channel->sock_state_cb;
options->sock_state_cb_data = channel->sock_state_cb_data;
/* Copy IPv4 servers that use the default port */
if (channel->nservers) {
for (i = 0; i < channel->nservers; i++) {
if ((channel->servers[i].addr.family == AF_INET) &&
(channel->servers[i].addr.udp_port == 0) &&
(channel->servers[i].addr.tcp_port == 0)) {
ipv4_nservers++;
}
}
if (ipv4_nservers) {
options->servers = ares_malloc(ipv4_nservers * sizeof(struct in_addr));
if (!options->servers) {
return ARES_ENOMEM;
}
for (i = j = 0; i < channel->nservers; i++) {
if ((channel->servers[i].addr.family == AF_INET) &&
(channel->servers[i].addr.udp_port == 0) &&
(channel->servers[i].addr.tcp_port == 0)) {
memcpy(&options->servers[j++], &channel->servers[i].addr.addrV4,
sizeof(channel->servers[i].addr.addrV4));
}
}
}
}
options->nservers = (int)ipv4_nservers;
/* copy domains */
if (channel->ndomains) {
options->domains = ares_malloc(channel->ndomains * sizeof(char *));
if (!options->domains) {
return ARES_ENOMEM;
}
for (i = 0; i < channel->ndomains; i++) {
options->domains[i] = ares_strdup(channel->domains[i]);
if (!options->domains[i]) {
options->ndomains = (int)i;
return ARES_ENOMEM;
}
}
}
options->ndomains = (int)channel->ndomains;
/* copy lookups */
if (channel->lookups) {
options->lookups = ares_strdup(channel->lookups);
if (!options->lookups && channel->lookups) {
return ARES_ENOMEM;
}
}
/* copy sortlist */
if (channel->nsort) {
options->sortlist = ares_malloc(channel->nsort * sizeof(struct apattern));
if (!options->sortlist) {
return ARES_ENOMEM;
}
for (i = 0; i < channel->nsort; i++) {
options->sortlist[i] = channel->sortlist[i];
}
}
options->nsort = (int)channel->nsort;
/* copy path for resolv.conf file */
if (channel->resolvconf_path) {
options->resolvconf_path = ares_strdup(channel->resolvconf_path);
if (!options->resolvconf_path) {
return ARES_ENOMEM;
}
}
/* copy path for hosts file */
if (channel->hosts_path) {
options->hosts_path = ares_strdup(channel->hosts_path);
if (!options->hosts_path) {
return ARES_ENOMEM;
}
}
if (channel->udp_max_queries > 0) {
(*optmask) |= ARES_OPT_UDP_MAX_QUERIES;
options->udp_max_queries = (int)channel->udp_max_queries;
}
return ARES_SUCCESS;
}
ares_status_t ares__init_by_options(ares_channel channel,
const struct ares_options *options,
int optmask)
{
size_t i;
/* Easy stuff. */
if (optmask & ARES_OPT_FLAGS) {
channel->flags = (unsigned int)options->flags;
}
if (optmask & ARES_OPT_TIMEOUTMS) {
channel->timeout = (unsigned int)options->timeout;
} else if (optmask & ARES_OPT_TIMEOUT) {
channel->timeout = (unsigned int)options->timeout * 1000;
}
if (optmask & ARES_OPT_TRIES) {
channel->tries = (size_t)options->tries;
}
if (optmask & ARES_OPT_NDOTS) {
channel->ndots = (size_t)options->ndots;
}
if (optmask & ARES_OPT_ROTATE) {
channel->rotate = ARES_TRUE;
}
if (optmask & ARES_OPT_NOROTATE) {
channel->rotate = ARES_FALSE;
}
if ((optmask & ARES_OPT_UDP_PORT) && channel->udp_port == 0) {
channel->udp_port = htons(options->udp_port);
}
if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == 0) {
channel->tcp_port = htons(options->tcp_port);
}
if ((optmask & ARES_OPT_SOCK_STATE_CB) && channel->sock_state_cb == NULL) {
channel->sock_state_cb = options->sock_state_cb;
channel->sock_state_cb_data = options->sock_state_cb_data;
}
if (optmask & ARES_OPT_SOCK_SNDBUF && options->socket_send_buffer_size > 0) {
channel->socket_send_buffer_size = options->socket_send_buffer_size;
}
if (optmask & ARES_OPT_SOCK_RCVBUF &&
channel->socket_receive_buffer_size > 0) {
channel->socket_receive_buffer_size = options->socket_receive_buffer_size;
}
if (optmask & ARES_OPT_EDNSPSZ) {
channel->ednspsz = (size_t)options->ednspsz;
}
/* Copy the IPv4 servers, if given. */
if (optmask & ARES_OPT_SERVERS) {
/* Avoid zero size allocations at any cost */
if (options->nservers > 0) {
channel->servers =
ares_malloc((size_t)options->nservers * sizeof(*channel->servers));
if (!channel->servers) {
return ARES_ENOMEM;
}
memset(channel->servers, 0,
(size_t)options->nservers * sizeof(*channel->servers));
for (i = 0; i < (size_t)options->nservers; i++) {
channel->servers[i].addr.family = AF_INET;
channel->servers[i].addr.udp_port = 0;
channel->servers[i].addr.tcp_port = 0;
memcpy(&channel->servers[i].addr.addrV4, &options->servers[i],
sizeof(channel->servers[i].addr.addrV4));
}
}
channel->nservers = (size_t)options->nservers;
}
/* Copy the domains, if given. Keep channel->ndomains consistent so
* we can clean up in case of error.
*/
if (optmask & ARES_OPT_DOMAINS) {
/* Avoid zero size allocations at any cost */
if (options->ndomains > 0) {
channel->domains =
ares_malloc((size_t)options->ndomains * sizeof(char *));
if (!channel->domains) {
return ARES_ENOMEM;
}
for (i = 0; i < (size_t)options->ndomains; i++) {
channel->domains[i] = ares_strdup(options->domains[i]);
if (!channel->domains[i]) {
return ARES_ENOMEM;
}
}
}
channel->ndomains = (size_t)options->ndomains;
}
/* Set lookups, if given. */
if ((optmask & ARES_OPT_LOOKUPS) && !channel->lookups) {
channel->lookups = ares_strdup(options->lookups);
if (!channel->lookups) {
return ARES_ENOMEM;
}
}
/* copy sortlist */
if (optmask & ARES_OPT_SORTLIST && options->nsort > 0) {
channel->nsort = (size_t)options->nsort;
channel->sortlist =
ares_malloc((size_t)options->nsort * sizeof(struct apattern));
if (!channel->sortlist) {
return ARES_ENOMEM;
}
for (i = 0; i < (size_t)options->nsort; i++) {
channel->sortlist[i] = options->sortlist[i];
}
}
/* Set path for resolv.conf file, if given. */
if ((optmask & ARES_OPT_RESOLVCONF) && !channel->resolvconf_path) {
channel->resolvconf_path = ares_strdup(options->resolvconf_path);
if (!channel->resolvconf_path && options->resolvconf_path) {
return ARES_ENOMEM;
}
}
/* Set path for hosts file, if given. */
if ((optmask & ARES_OPT_HOSTS_FILE) && !channel->hosts_path) {
channel->hosts_path = ares_strdup(options->hosts_path);
if (!channel->hosts_path && options->hosts_path) {
return ARES_ENOMEM;
}
}
if (optmask & ARES_OPT_UDP_MAX_QUERIES) {
channel->udp_max_queries = (size_t)options->udp_max_queries;
}
channel->optmask = (unsigned int)optmask;
return ARES_SUCCESS;
}

@ -117,7 +117,7 @@ typedef struct ares_rand_state ares_rand_state;
#include "ares__llist.h"
#include "ares__slist.h"
#include "ares__htable_stvp.h"
#include "ares__htable_szvp.h"
#include "ares__htable_asvp.h"
#include "ares__buf.h"
#include "ares_dns_record.h"
@ -309,7 +309,7 @@ struct ares_channeldata {
/* All active queries in a single list */
ares__llist_t *all_queries;
/* Queries bucketed by qid, for quickly dispatching DNS responses: */
ares__htable_stvp_t *queries_by_qid;
ares__htable_szvp_t *queries_by_qid;
/* Queries bucketed by timeout, for quickly handling timeouts: */
ares__slist_t *queries_by_timeout;
@ -395,6 +395,9 @@ ares_status_t ares_expand_string_ex(const unsigned char *encoded,
const unsigned char *abuf, size_t alen,
unsigned char **s, size_t *enclen);
ares_status_t ares__init_servers_state(ares_channel channel);
ares_status_t ares__init_by_options(ares_channel channel,
const struct ares_options *options,
int optmask);
void ares__destroy_servers_state(ares_channel channel);
ares_status_t ares__single_domain(ares_channel channel, const char *name,
char **s);
@ -440,12 +443,23 @@ ares_status_t ares__addrinfo2addrttl(const struct ares_addrinfo *ai, int family,
ares_status_t ares__addrinfo_localhost(const char *name, unsigned short port,
const struct ares_addrinfo_hints *hints,
struct ares_addrinfo *ai);
ares_status_t ares__open_connection(ares_channel channel,
struct server_state *server,
ares_bool_t is_tcp);
ares_socket_t ares__open_socket(ares_channel channel, int af, int type,
int protocol);
ares_ssize_t ares__socket_write(ares_channel channel, ares_socket_t s,
const void *data, size_t len);
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);
ares_ssize_t ares__socket_recv(ares_channel channel, ares_socket_t s,
void *data, size_t data_len);
void ares__close_socket(ares_channel, ares_socket_t);
int ares__connect_socket(ares_channel channel, ares_socket_t sockfd,
const struct sockaddr *addr, ares_socklen_t addrlen);
const struct sockaddr *addr,
ares_socklen_t addrlen);
#define ARES_SWAP_BYTE(a, b) \
do { \
@ -461,6 +475,9 @@ int ares__connect_socket(ares_channel channel, ares_socket_t sockfd,
} \
} while (0)
#define ARES_CONFIG_CHECK(x) \
(x->lookups && x->nservers > 0 && x->ndots > 0 && x->timeout > 0 && \
x->tries > 0)
size_t ares__round_up_pow2(size_t n);
size_t ares__log2(size_t n);

@ -27,23 +27,6 @@
#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
#include "ares_nameser.h"
#ifdef HAVE_STRINGS_H
# include <strings.h>
@ -60,8 +43,9 @@
#include <limits.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_private.h"
#include "ares_nameser.h"
#include "ares_dns.h"
static ares_bool_t try_again(int errnum);
static void write_tcp_data(ares_channel channel, fd_set *write_fds,
@ -77,9 +61,6 @@ static void skip_server(ares_channel channel, struct query *query,
struct server_state *server);
static ares_status_t next_server(ares_channel channel, struct query *query,
struct timeval *now);
static ares_status_t open_socket(ares_channel channel,
struct server_state *server,
ares_bool_t is_tcp);
static ares_bool_t same_questions(const unsigned char *qbuf, size_t qlen,
ares_dns_record_t *arec);
static ares_bool_t same_address(struct sockaddr *sa, struct ares_addr *aa);
@ -87,8 +68,7 @@ static ares_bool_t has_opt_rr(ares_dns_record_t *arec);
static void end_query(ares_channel channel, struct query *query,
ares_status_t status, const unsigned char *abuf,
size_t alen);
static ares_ssize_t ares__socket_write(ares_channel channel, ares_socket_t s,
const void *data, size_t len);
/* return true if now is exactly check time or later */
ares_bool_t ares__timedout(struct timeval *now, struct timeval *check)
@ -240,33 +220,6 @@ static void write_tcp_data(ares_channel channel, fd_set *write_fds,
}
}
static ares_ssize_t 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
}
static ares_ssize_t 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);
}
/* If any TCP socket selects true for reading, read some data,
* allocate a buffer if we finish reading the length word, and process
@ -289,7 +242,7 @@ static void read_tcp_data(ares_channel channel, struct server_connection *conn,
}
/* Read from socket */
count = socket_recv(channel, conn->fd, ptr, ptr_len);
count = ares__socket_recv(channel, conn->fd, ptr, ptr_len);
if (count <= 0) {
ares__buf_append_finish(server->tcp_parser, 0);
if (!(count == -1 && try_again(SOCKERRNO))) {
@ -428,8 +381,8 @@ static void read_udp_packets_fd(ares_channel channel,
} else {
fromlen = sizeof(from.sa6);
}
read_len = socket_recvfrom(channel, conn->fd, (void *)buf, sizeof(buf), 0,
&from.sa, &fromlen);
read_len = ares__socket_recvfrom(channel, conn->fd, (void *)buf,
sizeof(buf), 0, &from.sa, &fromlen);
}
if (read_len == 0) {
@ -582,7 +535,7 @@ static void process_answer(ares_channel channel, const unsigned char *abuf,
/* Find the query corresponding to this packet. The queries are
* hashed/bucketed by query id, so this lookup should be quick.
*/
query = ares__htable_stvp_get_direct(channel->queries_by_qid,
query = ares__htable_szvp_get_direct(channel->queries_by_qid,
ares_dns_record_get_id(dnsrec));
if (!query) {
goto cleanup;
@ -776,7 +729,7 @@ ares_status_t ares__send_query(ares_channel channel, struct query *query,
* a send request.
*/
if (server->tcp_conn == NULL) {
status = open_socket(channel, server, 1);
status = ares__open_connection(channel, server, ARES_TRUE);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:
@ -829,7 +782,7 @@ ares_status_t ares__send_query(ares_channel channel, struct query *query,
}
if (node == NULL) {
status = open_socket(channel, server, 0);
status = ares__open_connection(channel, server, ARES_FALSE);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:
@ -905,305 +858,7 @@ ares_status_t ares__send_query(ares_channel channel, struct query *query,
return ARES_SUCCESS;
}
/*
* 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;
}
static ares_status_t open_socket(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;
}
static ares_bool_t same_questions(const unsigned char *qbuf, size_t qlen,
ares_dns_record_t *arec)
@ -1297,7 +952,7 @@ static ares_bool_t has_opt_rr(ares_dns_record_t *arec)
static void ares_detach_query(struct query *query)
{
/* Remove the query from all the lists in which it is linked */
ares__htable_stvp_remove(query->channel->queries_by_qid, query->qid);
ares__htable_szvp_remove(query->channel->queries_by_qid, query->qid);
ares__slist_node_destroy(query->node_queries_by_timeout);
ares__llist_node_destroy(query->node_queries_to_conn);
ares__llist_node_destroy(query->node_all_queries);
@ -1334,53 +989,4 @@ void ares__free_query(struct query *query)
ares_free(query);
}
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
static 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);
}

@ -56,7 +56,7 @@ static unsigned short generate_unique_id(ares_channel channel)
do {
id = ares__generate_new_id(channel->rand_state);
} while (ares__htable_stvp_get(channel->queries_by_qid, id, NULL));
} while (ares__htable_szvp_get(channel->queries_by_qid, id, NULL));
return (unsigned short)id;
}

@ -134,7 +134,7 @@ ares_status_t ares_send_ex(ares_channel channel, const unsigned char *qbuf,
/* Keep track of queries bucketed by qid, so we can process DNS
* responses quickly.
*/
if (!ares__htable_stvp_insert(channel->queries_by_qid, query->qid, query)) {
if (!ares__htable_szvp_insert(channel->queries_by_qid, query->qid, query)) {
callback(arg, ARES_ENOMEM, 0, NULL, 0);
ares__free_query(query);
return ARES_ENOMEM;

Loading…
Cancel
Save