Configuration option to limit number of UDP queries per ephemeral port (#549)
Add a new ARES_OPT_UDP_MAX_QUERIES option with udp_max_queries parameter that can be passed to ares_init_options(). This value defaults to 0 (unlimited) to maintain existing compatibility, any positive number will cause new UDP ephemeral ports to be created once the threshold is reached, we'll call these 'connections' even though its technically wrong for UDP. Implementation Details: * Each server entry in a channel now has a linked-list of connections/ports for udp and tcp. The first connection in the list is the one most likely to be eligible to accept new queries. * Queries are now tracked by connection rather than by server. * Every time a query is detached from a connection, the connection that it was attached to will be checked to see if it needs to be cleaned up. * Insertion, lookup, and searching for connections has been implemented as O(1) complexity so the number of connections will not impact performance. * Remove is_broken from the server, it appears it would be set and immediately unset, so must have been invalidated via a prior patch. A future patch should probably track consecutive server errors and de-prioritize such servers. The code right now will always try servers in the order of configuration, so a bad server in the list will always be tried and may rely on timeout logic to try the next. * Various other cleanups to remove code duplication and for clarification. Fixes Bug: #444 Fix By: Brad House (@bradh352)pull/553/head
parent
7f3262312f
commit
dd93f30082
20 changed files with 1286 additions and 746 deletions
@ -0,0 +1,186 @@ |
||||
/* Copyright (C) 2023 by Brad House
|
||||
* |
||||
* Permission to use, copy, modify, and distribute this |
||||
* software and its documentation for any purpose and without |
||||
* fee is hereby granted, provided that the above copyright |
||||
* notice appear in all copies and that both that copyright |
||||
* notice and this permission notice appear in supporting |
||||
* documentation, and that the name of M.I.T. not be used in |
||||
* advertising or publicity pertaining to distribution of the |
||||
* software without specific, written prior permission. |
||||
* M.I.T. makes no representations about the suitability of |
||||
* this software for any purpose. It is provided "as is" |
||||
* without express or implied warranty. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares__htable.h" |
||||
#include "ares__htable_asvp.h" |
||||
|
||||
|
||||
struct ares__htable_asvp { |
||||
ares__htable_asvp_val_free_t free_val; |
||||
ares__htable_t *hash; |
||||
}; |
||||
|
||||
|
||||
typedef struct { |
||||
ares_socket_t key; |
||||
void *val; |
||||
ares__htable_asvp_t *parent; |
||||
} ares__htable_asvp_bucket_t; |
||||
|
||||
|
||||
void ares__htable_asvp_destroy(ares__htable_asvp_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 ares_socket_t *arg = key; |
||||
return ares__htable_hash_FNV1a((const unsigned char *)arg, sizeof(*arg), |
||||
seed); |
||||
} |
||||
|
||||
|
||||
static const void *bucket_key(const void *bucket) |
||||
{ |
||||
const ares__htable_asvp_bucket_t *arg = bucket; |
||||
return &arg->key; |
||||
} |
||||
|
||||
|
||||
static void bucket_free(void *bucket) |
||||
{ |
||||
ares__htable_asvp_bucket_t *arg = bucket; |
||||
|
||||
if (arg->parent->free_val) |
||||
arg->parent->free_val(arg->val); |
||||
|
||||
ares_free(arg); |
||||
} |
||||
|
||||
|
||||
static unsigned int key_eq(const void *key1, const void *key2) |
||||
{ |
||||
const ares_socket_t *k1 = key1; |
||||
const ares_socket_t *k2 = key2; |
||||
|
||||
if (*k1 == *k2) |
||||
return 1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
ares__htable_asvp_t *ares__htable_asvp_create( |
||||
ares__htable_asvp_val_free_t val_free) |
||||
{ |
||||
ares__htable_asvp_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; |
||||
} |
||||
|
||||
|
||||
unsigned int ares__htable_asvp_insert(ares__htable_asvp_t *htable, |
||||
ares_socket_t key, void *val) |
||||
{ |
||||
ares__htable_asvp_bucket_t *bucket = NULL; |
||||
|
||||
if (htable == NULL) |
||||
goto fail; |
||||
|
||||
bucket = ares_malloc(sizeof(*bucket)); |
||||
if (bucket == NULL) |
||||
goto fail; |
||||
|
||||
bucket->parent = htable; |
||||
bucket->key = key; |
||||
bucket->val = val; |
||||
|
||||
if (!ares__htable_insert(htable->hash, bucket)) |
||||
goto fail; |
||||
|
||||
return 1; |
||||
|
||||
fail: |
||||
if (bucket) { |
||||
ares_free(bucket); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
unsigned int ares__htable_asvp_get(ares__htable_asvp_t *htable, |
||||
ares_socket_t key, void **val) |
||||
{ |
||||
ares__htable_asvp_bucket_t *bucket = NULL; |
||||
|
||||
if (val) |
||||
*val = NULL; |
||||
|
||||
if (htable == NULL) |
||||
return 0; |
||||
|
||||
bucket = ares__htable_get(htable->hash, &key); |
||||
if (bucket == NULL) |
||||
return 0; |
||||
|
||||
if (val) |
||||
*val = bucket->val; |
||||
return 1; |
||||
} |
||||
|
||||
|
||||
void *ares__htable_asvp_get_direct(ares__htable_asvp_t *htable, |
||||
ares_socket_t key) |
||||
{ |
||||
void *val = NULL; |
||||
ares__htable_asvp_get(htable, key, &val); |
||||
return val; |
||||
} |
||||
|
||||
|
||||
unsigned int ares__htable_asvp_remove(ares__htable_asvp_t *htable, |
||||
ares_socket_t key) |
||||
{ |
||||
if (htable == NULL) |
||||
return 0; |
||||
|
||||
return ares__htable_remove(htable->hash, &key); |
||||
} |
||||
|
||||
|
||||
size_t ares__htable_asvp_num_keys(ares__htable_asvp_t *htable) |
||||
{ |
||||
if (htable == NULL) |
||||
return 0; |
||||
return ares__htable_num_keys(htable->hash); |
||||
} |
@ -0,0 +1,111 @@ |
||||
/* Copyright (C) 2023 by Brad House
|
||||
* |
||||
* Permission to use, copy, modify, and distribute this |
||||
* software and its documentation for any purpose and without |
||||
* fee is hereby granted, provided that the above copyright |
||||
* notice appear in all copies and that both that copyright |
||||
* notice and this permission notice appear in supporting |
||||
* documentation, and that the name of M.I.T. not be used in |
||||
* advertising or publicity pertaining to distribution of the |
||||
* software without specific, written prior permission. |
||||
* M.I.T. makes no representations about the suitability of |
||||
* this software for any purpose. It is provided "as is" |
||||
* without express or implied warranty. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#ifndef __ARES__HTABLE_ASVP_H |
||||
#define __ARES__HTABLE_ASVP_H |
||||
|
||||
/*! \addtogroup ares__htable_asvp HashTable with ares_socket_t 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 ares_socket_t and void pointer, |
||||
* respectively. |
||||
* |
||||
* Average time complexity: |
||||
* - Insert: O(1) |
||||
* - Search: O(1) |
||||
* - Delete: O(1) |
||||
* |
||||
* @{ |
||||
*/ |
||||
|
||||
struct ares__htable_asvp; |
||||
|
||||
/*! Opaque data type for ares_socket_t key, void pointer hash table
|
||||
* implementation */ |
||||
typedef struct ares__htable_asvp ares__htable_asvp_t; |
||||
|
||||
/*! Callback to free value stored in hashtable
|
||||
*
|
||||
* \param[in] val user-supplied value |
||||
*/ |
||||
typedef void (*ares__htable_asvp_val_free_t)(void *val); |
||||
|
||||
/*! Destroy hashtable
|
||||
*
|
||||
* \param[in] htable Initialized hashtable |
||||
*/ |
||||
void ares__htable_asvp_destroy(ares__htable_asvp_t *htable); |
||||
|
||||
/*! Create size_t key, 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_asvp_t *ares__htable_asvp_create( |
||||
ares__htable_asvp_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 1 on success, 0 on out of memory or misuse |
||||
*/ |
||||
unsigned int ares__htable_asvp_insert(ares__htable_asvp_t *htable, |
||||
ares_socket_t 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 1 on success, 0 on failure |
||||
*/ |
||||
unsigned int ares__htable_asvp_get(ares__htable_asvp_t *htable, |
||||
ares_socket_t key, void **val); |
||||
|
||||
/*! Retrieve value from hashtable directly as return value. Caveat to this
|
||||
* function over ares__htable_asvp_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_asvp_get_direct(ares__htable_asvp_t *htable, |
||||
ares_socket_t key); |
||||
|
||||
/*! Remove a value from the hashtable by key
|
||||
*
|
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to use to search |
||||
* \return 1 if found, 0 if not |
||||
*/ |
||||
unsigned int ares__htable_asvp_remove(ares__htable_asvp_t *htable, |
||||
ares_socket_t key); |
||||
|
||||
/*! Retrieve the number of keys stored in the hash table
|
||||
*
|
||||
* \param[in] htable Initialized hash table |
||||
* \return count |
||||
*/ |
||||
size_t ares__htable_asvp_num_keys(ares__htable_asvp_t *htable); |
||||
|
||||
/*! @} */ |
||||
|
||||
#endif /* __ARES__HTABLE_ASVP_H */ |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue