mirror of https://github.com/getdnsapi/getdns.git
Rewrote the continuous query organization to use the LRU cache instead of an RB tree.
This commit is contained in:
parent
1587e2f8f5
commit
6d3e0c7ca2
|
@ -351,7 +351,6 @@ struct getdns_context {
|
||||||
int mdns_connection_nb; /* typically 0 or 2 for IPv4 and IPv6 */
|
int mdns_connection_nb; /* typically 0 or 2 for IPv4 and IPv6 */
|
||||||
struct mdns_network_connection * mdns_connection;
|
struct mdns_network_connection * mdns_connection;
|
||||||
struct lruhash * mdns_cache;
|
struct lruhash * mdns_cache;
|
||||||
_getdns_rbtree_t mdns_continuous_queries_by_name_rrtype;
|
|
||||||
|
|
||||||
#endif /* HAVE_MDNS_SUPPORT */
|
#endif /* HAVE_MDNS_SUPPORT */
|
||||||
}; /* getdns_context */
|
}; /* getdns_context */
|
||||||
|
|
615
src/mdns.c
615
src/mdns.c
|
@ -263,9 +263,32 @@ static void msdn_cache_delkey(void* vkey, void* vcontext)
|
||||||
GETDNS_FREE(((struct getdns_context *) vcontext)->mf, vkey);
|
GETDNS_FREE(((struct getdns_context *) vcontext)->mf, vkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** old data is deleted. This function is called: func(data, userarg). */
|
/** old data is deleted. This function is called: func(data, userarg).
|
||||||
|
* Since we use the hash table for both data and requests, need to
|
||||||
|
* terminate whatever request was ongoing. TODO: we should have some smarts
|
||||||
|
* in cache management and never drop cached entries with active requests.
|
||||||
|
*/
|
||||||
static void msdn_cache_deldata(void* vdata, void* vcontext)
|
static void msdn_cache_deldata(void* vdata, void* vcontext)
|
||||||
{
|
{
|
||||||
|
/* need to terminate the pending queries? */
|
||||||
|
getdns_mdns_cached_record_header* header = ((getdns_mdns_cached_record_header*)vdata);
|
||||||
|
|
||||||
|
while (header->netreq_first)
|
||||||
|
{
|
||||||
|
/* Need to unchain the request from that entry */
|
||||||
|
getdns_network_req* netreq = header->netreq_first;
|
||||||
|
header->netreq_first = netreq->mdns_netreq_next;
|
||||||
|
netreq->mdns_netreq_next = NULL;
|
||||||
|
|
||||||
|
/* TODO: treating as a timeout for now, may consider treating as error */
|
||||||
|
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
||||||
|
netreq->state = NET_REQ_TIMED_OUT;
|
||||||
|
if (netreq->owner->user_callback) {
|
||||||
|
(void)_getdns_context_request_timed_out(netreq->owner);
|
||||||
|
}
|
||||||
|
_getdns_check_dns_req_complete(netreq->owner);
|
||||||
|
|
||||||
|
}
|
||||||
GETDNS_FREE(((struct getdns_context *) vcontext)->mf, vdata);
|
GETDNS_FREE(((struct getdns_context *) vcontext)->mf, vdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +332,7 @@ static uint8_t * mdns_cache_create_data(
|
||||||
{
|
{
|
||||||
getdns_mdns_cached_record_header * header;
|
getdns_mdns_cached_record_header * header;
|
||||||
int current_index;
|
int current_index;
|
||||||
size_t data_size = sizeof(getdns_mdns_cached_record_header) + name_len + 4;
|
size_t data_size = sizeof(getdns_mdns_cached_record_header) + 12 + name_len + 4;
|
||||||
size_t alloc_size = mdns_util_suggest_size(data_size + record_data_len + 2 + 2 + 2 + 4 + 2);
|
size_t alloc_size = mdns_util_suggest_size(data_size + record_data_len + 2 + 2 + 2 + 4 + 2);
|
||||||
|
|
||||||
uint8_t* data = GETDNS_XMALLOC(context->mf, uint8_t, alloc_size);
|
uint8_t* data = GETDNS_XMALLOC(context->mf, uint8_t, alloc_size);
|
||||||
|
@ -320,7 +343,10 @@ static uint8_t * mdns_cache_create_data(
|
||||||
header->insertion_microsec = current_time;
|
header->insertion_microsec = current_time;
|
||||||
header->content_len = data_size;
|
header->content_len = data_size;
|
||||||
header->allocated_length = alloc_size;
|
header->allocated_length = alloc_size;
|
||||||
|
header->netreq_first = NULL;
|
||||||
current_index = sizeof(getdns_mdns_cached_record_header);
|
current_index = sizeof(getdns_mdns_cached_record_header);
|
||||||
|
memset(data + current_index, 0, 12);
|
||||||
|
current_index += 12;
|
||||||
memcpy(data + current_index, name, name_len);
|
memcpy(data + current_index, name, name_len);
|
||||||
current_index += name_len;
|
current_index += name_len;
|
||||||
data[current_index++] = (uint8_t)(record_type >> 8);
|
data[current_index++] = (uint8_t)(record_type >> 8);
|
||||||
|
@ -553,6 +579,7 @@ mdns_propose_entry_to_cache(
|
||||||
uint8_t * name, int name_len,
|
uint8_t * name, int name_len,
|
||||||
int record_type, int record_class, int ttl,
|
int record_type, int record_class, int ttl,
|
||||||
uint8_t * record_data, int record_data_len,
|
uint8_t * record_data, int record_data_len,
|
||||||
|
getdns_network_req * netreq,
|
||||||
uint64_t current_time)
|
uint64_t current_time)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -561,6 +588,7 @@ mdns_propose_entry_to_cache(
|
||||||
hashvalue_type hash;
|
hashvalue_type hash;
|
||||||
struct lruhash_entry *entry, *new_entry;
|
struct lruhash_entry *entry, *new_entry;
|
||||||
uint8_t *key, *data;
|
uint8_t *key, *data;
|
||||||
|
getdns_mdns_cached_record_header * header;
|
||||||
|
|
||||||
msdn_cache_create_key_in_buffer(temp_key, name, name_len, record_type, record_class);
|
msdn_cache_create_key_in_buffer(temp_key, name, name_len, record_type, record_class);
|
||||||
|
|
||||||
|
@ -621,10 +649,19 @@ mdns_propose_entry_to_cache(
|
||||||
|
|
||||||
if (entry != NULL)
|
if (entry != NULL)
|
||||||
{
|
{
|
||||||
ret = mdns_update_cache_ttl_and_prune(context,
|
if (record_data != NULL && record_data_len > 0)
|
||||||
(uint8_t*)entry->data, &data,
|
ret = mdns_update_cache_ttl_and_prune(context,
|
||||||
record_type, record_class, ttl, record_data, record_data_len,
|
(uint8_t*)entry->data, &data,
|
||||||
current_time);
|
record_type, record_class, ttl, record_data, record_data_len,
|
||||||
|
current_time);
|
||||||
|
|
||||||
|
if (netreq != NULL)
|
||||||
|
{
|
||||||
|
/* chain the continuous request to the cache line */
|
||||||
|
header = (getdns_mdns_cached_record_header *) entry->data;
|
||||||
|
netreq->mdns_netreq_next = header->netreq_first;
|
||||||
|
header->netreq_first = netreq;
|
||||||
|
}
|
||||||
|
|
||||||
/* then, unlock the entry */
|
/* then, unlock the entry */
|
||||||
lock_rw_unlock(entry->lock);
|
lock_rw_unlock(entry->lock);
|
||||||
|
@ -635,482 +672,6 @@ mdns_propose_entry_to_cache(
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
/*
|
|
||||||
* Add record function for the MDNS record cache.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
mdns_add_record_to_cache(struct getdns_context *context,
|
|
||||||
uint8_t * name, int name_len,
|
|
||||||
int record_type, int record_class, int ttl,
|
|
||||||
uint8_t * record_data, int record_data_len)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/* First, format a key */
|
|
||||||
|
|
||||||
/* Next, get the record from the LRU cache */
|
|
||||||
|
|
||||||
/* If there is no record, need to create one */
|
|
||||||
|
|
||||||
/* Else, just do simple update */
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* MDNS cache management.
|
|
||||||
*
|
|
||||||
* This is provisional, until we can get a proper cache by reusing the outbound code.
|
|
||||||
*
|
|
||||||
* The cache is a collection of getdns_mdns_known_record structures, each holding
|
|
||||||
* one record: name, type, class, ttl, rdata length and rdata, plus a 64 bit time stamp.
|
|
||||||
* The collection is organized as an RB tree. The comparison function in mdns_cmp_known_records
|
|
||||||
* is somewhat arbitrary: compare by numeric fields class, type, name length, and
|
|
||||||
* record length first, then by name value, and then by record value.
|
|
||||||
*
|
|
||||||
* When a message is received, it will be parsed, and each valid record in the
|
|
||||||
* ANSWER, AUTH or additional section will be added to the cache. If there is already
|
|
||||||
* a matching record, only the TTL and time stamp will be updated. If the new TTL is zero,
|
|
||||||
* the matching record will be removed from the cache.
|
|
||||||
*
|
|
||||||
* When a request is first presented, we will try to solve it from the cache. If there
|
|
||||||
* is response present, we will format a reply containing all the matching records, with
|
|
||||||
* an updated TTL. If the updated TTL is negative, the record will be deleted from the
|
|
||||||
* cache and will not be added to the query.
|
|
||||||
*
|
|
||||||
* For continuing requests, we may need to send queries with the list of known answers.
|
|
||||||
* These will be extracted from the cache.
|
|
||||||
*
|
|
||||||
* We may want to periodically check all the records in the cache and remove all those
|
|
||||||
* that have expired. This could be treated as a garbage collection task.
|
|
||||||
*
|
|
||||||
* The cache is emptied and deleted upon context deletion.
|
|
||||||
*
|
|
||||||
* In a multithreaded environment, we will assume that whoever accesses the cache holds a
|
|
||||||
* on the context. This is not ideal, but fine grain locks can lead to all kinds of
|
|
||||||
* synchronization issues.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compare function for the getdns_mdns_known_record type,
|
|
||||||
* used in the red-black tree of known records per query.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int mdns_cmp_known_records(const void * nkr1, const void * nkr2)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
getdns_mdns_known_record * kr1 = (getdns_mdns_known_record *)nkr1;
|
|
||||||
getdns_mdns_known_record * kr2 = (getdns_mdns_known_record *)nkr2;
|
|
||||||
|
|
||||||
if (kr1->record_class != kr2->record_class)
|
|
||||||
{
|
|
||||||
ret = (kr1->record_class < kr2->record_class) ? -1 : 1;
|
|
||||||
}
|
|
||||||
else if (kr1->record_type != kr2->record_type)
|
|
||||||
{
|
|
||||||
ret = (kr1->record_type < kr2->record_type) ? -1 : 1;
|
|
||||||
}
|
|
||||||
else if (kr1->name_len != kr2->name_len)
|
|
||||||
{
|
|
||||||
ret = (kr1->name_len < kr2->name_len) ? -1 : 1;
|
|
||||||
}
|
|
||||||
else if (kr1->record_data_len != kr2->record_data_len)
|
|
||||||
{
|
|
||||||
ret = (kr1->record_data_len < kr2->record_data_len) ? -1 : 1;
|
|
||||||
}
|
|
||||||
else if ((ret = memcmp((void*)kr1->name, (void*)kr2->name, kr2->name_len)) == 0)
|
|
||||||
{
|
|
||||||
ret = memcmp((const void*)kr1->record_data, (const void*)kr2->record_data, kr1->record_data_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add record function for the MDNS record cache.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
mdns_add_known_record_to_cache(
|
|
||||||
struct getdns_context *context,
|
|
||||||
uint8_t * name, int name_len,
|
|
||||||
int record_type, int record_class, int ttl,
|
|
||||||
uint8_t * record_data, int record_data_len)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
getdns_mdns_known_record temp, *record, *inserted_record;
|
|
||||||
size_t required_memory = 0;
|
|
||||||
_getdns_rbnode_t * node_found, *to_delete;
|
|
||||||
|
|
||||||
temp.name = name;
|
|
||||||
temp.name_len = name_len;
|
|
||||||
temp.record_class = record_class;
|
|
||||||
temp.record_type = record_type;
|
|
||||||
temp.record_data = record_data;
|
|
||||||
temp.record_data_len = record_data_len;
|
|
||||||
|
|
||||||
|
|
||||||
if (ttl == 0)
|
|
||||||
{
|
|
||||||
to_delete = _getdns_rbtree_delete(
|
|
||||||
&context->mdns_known_records_by_value, &temp);
|
|
||||||
|
|
||||||
if (to_delete != NULL)
|
|
||||||
{
|
|
||||||
GETDNS_FREE(context->mf, to_delete->key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
node_found = _getdns_rbtree_search(&context->mdns_known_records_by_value, &temp);
|
|
||||||
|
|
||||||
if (node_found != NULL)
|
|
||||||
{
|
|
||||||
record = (getdns_mdns_known_record *)node_found->key;
|
|
||||||
record->ttl = ttl;
|
|
||||||
record->insertion_microsec = _getdns_get_time_as_uintt64();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
required_memory = sizeof(getdns_mdns_known_record) + name_len + record_data_len;
|
|
||||||
|
|
||||||
record = (getdns_mdns_known_record *)
|
|
||||||
GETDNS_XMALLOC(context->mf, uint8_t, required_memory);
|
|
||||||
|
|
||||||
if (record == NULL)
|
|
||||||
{
|
|
||||||
ret = GETDNS_RETURN_MEMORY_ERROR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
record->node.parent = NULL;
|
|
||||||
record->node.left = NULL;
|
|
||||||
record->node.right = NULL;
|
|
||||||
record->node.key = (void*)record;
|
|
||||||
record->record_class = temp.record_class;
|
|
||||||
record->name = ((uint8_t*)record) + sizeof(getdns_mdns_known_record);
|
|
||||||
record->name_len = name_len;
|
|
||||||
record->record_class = record_class;
|
|
||||||
record->record_type = record_type;
|
|
||||||
record->record_data = record->name + name_len;
|
|
||||||
record->record_data_len = record_data_len;
|
|
||||||
record->insertion_microsec = _getdns_get_time_as_uintt64();
|
|
||||||
|
|
||||||
memcpy(record->name, name, name_len);
|
|
||||||
memcpy(record->record_data, record_data, record_data_len);
|
|
||||||
|
|
||||||
inserted_record = (getdns_mdns_known_record *)
|
|
||||||
_getdns_rbtree_insert(&context->mdns_known_records_by_value,
|
|
||||||
&record->node);
|
|
||||||
if (inserted_record == NULL)
|
|
||||||
{
|
|
||||||
/* Weird. This can only happen in a race condition */
|
|
||||||
GETDNS_FREE(context->mf, record);
|
|
||||||
ret = GETDNS_RETURN_GENERIC_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the position of the first matching record in the cache
|
|
||||||
*/
|
|
||||||
static _getdns_rbnode_t *
|
|
||||||
mdns_get_first_record_from_cache(
|
|
||||||
struct getdns_context *context,
|
|
||||||
uint8_t * name, int name_len,
|
|
||||||
int record_type, int record_class,
|
|
||||||
getdns_mdns_known_record* next_key)
|
|
||||||
{
|
|
||||||
/* First, get a search key */
|
|
||||||
getdns_mdns_known_record temp;
|
|
||||||
_getdns_rbnode_t *next_node;
|
|
||||||
|
|
||||||
if (next_key == NULL)
|
|
||||||
{
|
|
||||||
temp.name = name;
|
|
||||||
temp.name_len = name_len;
|
|
||||||
temp.record_class = record_class;
|
|
||||||
temp.record_type = record_type;
|
|
||||||
temp.record_data = name;
|
|
||||||
temp.record_data_len = 0;
|
|
||||||
|
|
||||||
next_key = &temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find the starting point
|
|
||||||
*/
|
|
||||||
if (!_getdns_rbtree_find_less_equal(
|
|
||||||
&context->mdns_known_records_by_value,
|
|
||||||
next_key,
|
|
||||||
&next_node))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The key was not an exact match. Need to find the first node larger
|
|
||||||
* than the key.
|
|
||||||
*/
|
|
||||||
if (next_node == NULL)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The key was smallest than the smallest key in the tree, or the tree is empty
|
|
||||||
*/
|
|
||||||
next_node = _getdns_rbtree_first(&context->mdns_known_records_by_value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Search retrurned a key smaller than target, so we pick the next one.
|
|
||||||
*/
|
|
||||||
next_node = _getdns_rbtree_next(next_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* At this point, we do not check that this is the right node
|
|
||||||
*/
|
|
||||||
|
|
||||||
return next_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Count the number of records for this name and type, purging those that have expired.
|
|
||||||
* Return the first valid node in the set, or NULL if there are no such nodes.
|
|
||||||
*/
|
|
||||||
static _getdns_rbnode_t *
|
|
||||||
mdns_count_and_purge_record_from_cache(
|
|
||||||
struct getdns_context *context,
|
|
||||||
uint8_t * name, int name_len,
|
|
||||||
int record_type, int record_class,
|
|
||||||
_getdns_rbnode_t* next_node,
|
|
||||||
uint64_t current_time_microsec,
|
|
||||||
int *nb_records, int *total_content)
|
|
||||||
{
|
|
||||||
int current_record = 0;
|
|
||||||
int record_length_sum = 0;
|
|
||||||
int valid_ttl = 0;
|
|
||||||
getdns_mdns_known_record *next_key;
|
|
||||||
_getdns_rbnode_t *first_valid_node = NULL, *old_node = NULL;
|
|
||||||
|
|
||||||
while (next_node)
|
|
||||||
{
|
|
||||||
next_key = (getdns_mdns_known_record *)next_node->key;
|
|
||||||
if (next_key->name_len != name_len ||
|
|
||||||
next_key->record_class != record_class ||
|
|
||||||
next_key->record_type != record_type ||
|
|
||||||
memcmp(next_key->name, name, name_len) != 0)
|
|
||||||
{
|
|
||||||
next_node = NULL;
|
|
||||||
next_key = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
old_node = next_node;
|
|
||||||
next_node = _getdns_rbtree_next(next_node);
|
|
||||||
|
|
||||||
if (next_key->insertion_microsec + (((uint64_t)next_key->ttl) << 20) >=
|
|
||||||
current_time_microsec)
|
|
||||||
{
|
|
||||||
current_record++;
|
|
||||||
record_length_sum += next_key->record_data_len;
|
|
||||||
|
|
||||||
if (first_valid_node == NULL)
|
|
||||||
{
|
|
||||||
first_valid_node = old_node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_getdns_rbnode_t * deleted = _getdns_rbtree_delete(
|
|
||||||
&context->mdns_known_records_by_value, next_key);
|
|
||||||
|
|
||||||
if (deleted != NULL)
|
|
||||||
{
|
|
||||||
GETDNS_FREE(context->mf, next_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*nb_records = current_record;
|
|
||||||
*total_content = record_length_sum;
|
|
||||||
|
|
||||||
return first_valid_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fill a response buffer with the records present in the set,
|
|
||||||
* up to a limit
|
|
||||||
*/
|
|
||||||
_getdns_rbnode_t *
|
|
||||||
mdns_fill_response_buffer_from_cache(
|
|
||||||
struct getdns_context *context,
|
|
||||||
uint8_t * name, int name_len,
|
|
||||||
int record_type, int record_class,
|
|
||||||
uint8_t * response, int* response_len, int response_len_max,
|
|
||||||
int * nb_records,
|
|
||||||
_getdns_rbnode_t* next_node,
|
|
||||||
uint64_t current_time_microsec,
|
|
||||||
int name_offset
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int current_length = 0;
|
|
||||||
getdns_mdns_known_record * next_key;
|
|
||||||
int64_t ttl_64;
|
|
||||||
int32_t current_ttl;
|
|
||||||
int current_record = 0;
|
|
||||||
int coding_length;
|
|
||||||
int name_coding_length = (name_offset < 0) ? name_len : 2;
|
|
||||||
|
|
||||||
while (next_node)
|
|
||||||
{
|
|
||||||
next_key = (getdns_mdns_known_record *)next_node->key;
|
|
||||||
if (next_key->name_len != name_len ||
|
|
||||||
next_key->record_class != record_class ||
|
|
||||||
next_key->record_type != record_type ||
|
|
||||||
memcmp(next_key->name, name, name_len) != 0)
|
|
||||||
{
|
|
||||||
next_node = NULL;
|
|
||||||
next_key = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compute the required size.
|
|
||||||
*/
|
|
||||||
coding_length = name_len + name_coding_length + 2 + 4 + 2 + next_key->record_data_len;
|
|
||||||
|
|
||||||
if (current_length + coding_length > response_len_max)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* encode the record with the updated TTL
|
|
||||||
*/
|
|
||||||
|
|
||||||
ttl_64 = current_time_microsec - next_key->insertion_microsec;
|
|
||||||
ttl_64 += (((uint64_t)next_key->ttl) << 20);
|
|
||||||
|
|
||||||
if (ttl_64 > 0)
|
|
||||||
{
|
|
||||||
current_ttl = max(ttl_64 >> 20, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current_ttl = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name_offset >= 0)
|
|
||||||
{
|
|
||||||
/* Perform name compression, per DNS spec */
|
|
||||||
response[current_length++] = (uint8_t)((0xC0 | (name_offset >> 8)) & 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)(name_offset & 0xFF);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(response + current_length, name, name_len);
|
|
||||||
current_length += name_len;
|
|
||||||
}
|
|
||||||
response[current_length++] = (uint8_t)((record_type >> 8) & 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((record_type)& 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((record_class >> 8) & 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((record_class)& 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((current_ttl >> 24) & 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((current_ttl >> 16) & 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((current_ttl >> 8) & 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((current_ttl)& 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((next_key->record_data_len >> 8) & 0xFF);
|
|
||||||
response[current_length++] = (uint8_t)((next_key->record_data_len)& 0xFF);
|
|
||||||
memcpy(response + current_length, next_key->record_data, next_key->record_data_len);
|
|
||||||
current_length += next_key->record_data_len;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* continue with the next node
|
|
||||||
*/
|
|
||||||
current_record++;
|
|
||||||
next_node = _getdns_rbtree_next(next_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
*response_len = current_length;
|
|
||||||
*nb_records = current_record;
|
|
||||||
|
|
||||||
return next_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compose a response from the MDNS record cache.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
mdns_compose_response_from_cache(
|
|
||||||
struct getdns_context *context,
|
|
||||||
uint8_t * name, int name_len,
|
|
||||||
int record_type, int record_class,
|
|
||||||
uint8_t ** response, int* response_len, int response_len_max,
|
|
||||||
int query_id,
|
|
||||||
getdns_mdns_known_record** next_key)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
/* First, get a search key */
|
|
||||||
int nb_records = 0;
|
|
||||||
int total_content = 0;
|
|
||||||
int total_length = 0;
|
|
||||||
uint64_t current_time = _getdns_get_time_as_uintt64();
|
|
||||||
|
|
||||||
_getdns_rbnode_t * first_node = mdns_get_first_record_from_cache(
|
|
||||||
context, name, name_len, record_type, record_class, *next_key);
|
|
||||||
/* Purge the expired records and compute the desired length */
|
|
||||||
first_node = mdns_count_and_purge_record_from_cache(
|
|
||||||
context, name, name_len, record_type, record_class, first_node, current_time,
|
|
||||||
&nb_records, &total_content);
|
|
||||||
|
|
||||||
/* todo: check whether encoding an empty message is OK */
|
|
||||||
/* todo: check whether something special is needed for continuation records */
|
|
||||||
|
|
||||||
/* Allocate the required memory */
|
|
||||||
total_length = 12 /* DNS header */
|
|
||||||
+ name_len + 4 /* Query */
|
|
||||||
+ total_content + (2 + 2 + 2 + 4 + 2)*nb_records /* answers */;
|
|
||||||
/* TODO: do we need EDNS encoding? */
|
|
||||||
|
|
||||||
if (response_len_max == 0)
|
|
||||||
{
|
|
||||||
/* setting this parameter to zero indicates that a full buffer allocation is desired */
|
|
||||||
{
|
|
||||||
if (*response == NULL)
|
|
||||||
{
|
|
||||||
*response = GETDNS_XMALLOC(context->mf, uint8_t, total_length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*response = GETDNS_XREALLOC(context->mf, *response, uint8_t, total_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*response == NULL)
|
|
||||||
{
|
|
||||||
ret = GETDNS_RETURN_MEMORY_ERROR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response_len_max = total_length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ret == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now, proceed with the encoding
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* if 0, remove the RB based cache code */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare function for the mdns_continuous_query_by_name_rrtype,
|
* Compare function for the mdns_continuous_query_by_name_rrtype,
|
||||||
* used in the red-black tree of all ongoing queries.
|
* used in the red-black tree of all ongoing queries.
|
||||||
|
@ -1344,7 +905,7 @@ static getdns_return_t mdns_delayed_network_init(struct getdns_context *context)
|
||||||
|
|
||||||
if (context->mdns_extended_support == 2)
|
if (context->mdns_extended_support == 2)
|
||||||
{
|
{
|
||||||
context->mdns_cache = lruhash_create(100, 1000,
|
context->mdns_cache = lruhash_create(128, 10000000,
|
||||||
mdns_cache_entry_size, mdns_cache_key_comp,
|
mdns_cache_entry_size, mdns_cache_key_comp,
|
||||||
msdn_cache_delkey, msdn_cache_deldata,
|
msdn_cache_delkey, msdn_cache_deldata,
|
||||||
context);
|
context);
|
||||||
|
@ -1435,64 +996,12 @@ static getdns_return_t mdns_delayed_network_init(struct getdns_context *context)
|
||||||
static getdns_return_t mdns_initialize_continuous_request(getdns_network_req *netreq)
|
static getdns_return_t mdns_initialize_continuous_request(getdns_network_req *netreq)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
getdns_mdns_continuous_query temp_query, *continuous_query, *inserted_query;
|
|
||||||
getdns_dns_req *dnsreq = netreq->owner;
|
getdns_dns_req *dnsreq = netreq->owner;
|
||||||
struct getdns_context *context = dnsreq->context;
|
struct getdns_context *context = dnsreq->context;
|
||||||
_getdns_rbnode_t * node_found;
|
|
||||||
|
|
||||||
/*
|
ret = mdns_propose_entry_to_cache(context, dnsreq->name, dnsreq->name_len,
|
||||||
* Fill the target request, but only initialize name and request_type
|
netreq->request_type, dnsreq->request_class, 1, NULL, 0,
|
||||||
*/
|
netreq, _getdns_get_time_as_uintt64());
|
||||||
temp_query.request_class = dnsreq->request_class;
|
|
||||||
temp_query.request_type = netreq->request_type;
|
|
||||||
temp_query.name_len = dnsreq->name_len;
|
|
||||||
/* TODO: check that dnsreq is in canonical form */
|
|
||||||
memcpy(temp_query.name, dnsreq->name, dnsreq->name_len);
|
|
||||||
/*
|
|
||||||
* Check whether the continuous query is already in the RB tree.
|
|
||||||
* if there is not, create one.
|
|
||||||
* TODO: should lock the context object when doing that.
|
|
||||||
*/
|
|
||||||
node_found = _getdns_rbtree_search(&context->mdns_continuous_queries_by_name_rrtype, &temp_query);
|
|
||||||
|
|
||||||
if (node_found != NULL)
|
|
||||||
{
|
|
||||||
continuous_query = (getdns_mdns_continuous_query *)node_found->key;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continuous_query = (getdns_mdns_continuous_query *)
|
|
||||||
GETDNS_MALLOC(context->mf, getdns_mdns_continuous_query);
|
|
||||||
if (continuous_query != NULL)
|
|
||||||
{
|
|
||||||
continuous_query->node.parent = NULL;
|
|
||||||
continuous_query->node.left = NULL;
|
|
||||||
continuous_query->node.right = NULL;
|
|
||||||
continuous_query->node.key = (void*)continuous_query;
|
|
||||||
continuous_query->request_class = temp_query.request_class;
|
|
||||||
continuous_query->request_type = temp_query.request_type;
|
|
||||||
continuous_query->name_len = temp_query.name_len;
|
|
||||||
memcpy(continuous_query->name, temp_query.name, temp_query.name_len);
|
|
||||||
continuous_query->netreq_first = NULL;
|
|
||||||
/* Add the new continuous query to the context */
|
|
||||||
inserted_query = (getdns_mdns_continuous_query *)
|
|
||||||
_getdns_rbtree_insert(&context->mdns_continuous_queries_by_name_rrtype,
|
|
||||||
&continuous_query->node);
|
|
||||||
if (inserted_query == NULL)
|
|
||||||
{
|
|
||||||
/* Weird. This can only happen in a race condition */
|
|
||||||
GETDNS_FREE(context->mf, &continuous_query);
|
|
||||||
ret = GETDNS_RETURN_GENERIC_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret = GETDNS_RETURN_MEMORY_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* insert netreq into query list */
|
|
||||||
netreq->mdns_netreq_next = continuous_query->netreq_first;
|
|
||||||
continuous_query->netreq_first = netreq;
|
|
||||||
|
|
||||||
/* to do: queue message request to socket */
|
/* to do: queue message request to socket */
|
||||||
|
|
||||||
|
@ -1508,8 +1017,6 @@ void _getdns_mdns_context_init(struct getdns_context *context)
|
||||||
context->mdns_connection = NULL;
|
context->mdns_connection = NULL;
|
||||||
context->mdns_connection_nb = 0;
|
context->mdns_connection_nb = 0;
|
||||||
context->mdns_cache = NULL;
|
context->mdns_cache = NULL;
|
||||||
_getdns_rbtree_init(&context->mdns_continuous_queries_by_name_rrtype
|
|
||||||
, mdns_cmp_continuous_queries_by_name_rrtype);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1517,11 +1024,31 @@ void _getdns_mdns_context_init(struct getdns_context *context)
|
||||||
*/
|
*/
|
||||||
void _getdns_mdns_context_destroy(struct getdns_context *context)
|
void _getdns_mdns_context_destroy(struct getdns_context *context)
|
||||||
{
|
{
|
||||||
/* Close the sockets */
|
/* Clear all the cached records. This will terminate all pending network requests */
|
||||||
|
if (context->mdns_cache != NULL)
|
||||||
|
{
|
||||||
|
lruhash_delete(context->mdns_cache);
|
||||||
|
context->mdns_cache = NULL;
|
||||||
|
}
|
||||||
|
/* Close the connections */
|
||||||
|
if (context->mdns_connection != NULL)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < context->mdns_connection_nb; i++)
|
||||||
|
{
|
||||||
|
/* suppress the receive event */
|
||||||
|
GETDNS_CLEAR_EVENT(context->extension, &context->mdns_connection[i].event);
|
||||||
|
/* close the socket */
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
closesocket(context->mdns_connection[i].fd);
|
||||||
|
#else
|
||||||
|
close(context->mdns_connection[i].fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear all the continuous queries */
|
GETDNS_FREE(context->mf, context->mdns_connection);
|
||||||
|
context->mdns_connection = NULL;
|
||||||
/* Clear all the cached records */
|
context->mdns_connection_nb = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: actualy delete what is required.. */
|
/* TODO: actualy delete what is required.. */
|
||||||
|
|
|
@ -72,6 +72,8 @@ typedef struct getdns_mdns_cached_record_header
|
||||||
uint64_t insertion_microsec;
|
uint64_t insertion_microsec;
|
||||||
uint32_t content_len;
|
uint32_t content_len;
|
||||||
uint32_t allocated_length;
|
uint32_t allocated_length;
|
||||||
|
/* list of user queries */
|
||||||
|
getdns_network_req *netreq_first;
|
||||||
} getdns_mdns_cached_record_header;
|
} getdns_mdns_cached_record_header;
|
||||||
|
|
||||||
typedef struct getdns_mdns_continuous_query
|
typedef struct getdns_mdns_continuous_query
|
||||||
|
|
|
@ -192,12 +192,9 @@ typedef struct getdns_network_req
|
||||||
_getdns_rbnode_t node;
|
_getdns_rbnode_t node;
|
||||||
#ifdef HAVE_MDNS_SUPPORT
|
#ifdef HAVE_MDNS_SUPPORT
|
||||||
/*
|
/*
|
||||||
* for storage in continuous query context. We never
|
* for storage of continuous query context in hash table of cached results.
|
||||||
* expect much more than one query per msdn context,
|
|
||||||
* so no need for RB Tree.
|
|
||||||
*/
|
*/
|
||||||
struct getdns_network_req * mdns_netreq_next;
|
struct getdns_network_req * mdns_netreq_next;
|
||||||
struct getdns_mdns_continuous_query * mdns_continuous_query;
|
|
||||||
#endif /* HAVE_MDNS_SUPPORT */
|
#endif /* HAVE_MDNS_SUPPORT */
|
||||||
/* the async_id from unbound */
|
/* the async_id from unbound */
|
||||||
int unbound_id;
|
int unbound_id;
|
||||||
|
|
Loading…
Reference in New Issue