mirror of https://github.com/getdnsapi/getdns.git
Code almost complete for the MDNS multicast + cache.
Of course, we still need a lot of testing.
This commit is contained in:
parent
6d3e0c7ca2
commit
03307a7b71
700
src/mdns.c
700
src/mdns.c
|
@ -24,6 +24,7 @@
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "general.h"
|
#include "general.h"
|
||||||
#include "gldns/pkthdr.h"
|
#include "gldns/pkthdr.h"
|
||||||
|
#include "gldns/rrdef.h"
|
||||||
#include "util-internal.h"
|
#include "util-internal.h"
|
||||||
#include "mdns.h"
|
#include "mdns.h"
|
||||||
|
|
||||||
|
@ -165,6 +166,34 @@ struct lruhash_entry* entry, void* data, void* cb_arg)
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Another missing LRU hash function: demote, move an entry to the bottom
|
||||||
|
* of the LRU pile.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
lru_demote(struct lruhash* table, struct lruhash_entry* entry)
|
||||||
|
{
|
||||||
|
log_assert(table && entry);
|
||||||
|
if (entry == table->lru_end)
|
||||||
|
return; /* nothing to do */
|
||||||
|
/* remove from current lru position */
|
||||||
|
lru_remove(table, entry);
|
||||||
|
/* add at end */
|
||||||
|
entry->lru_next = NULL;
|
||||||
|
entry->lru_prev = table->lru_end;
|
||||||
|
|
||||||
|
if (table->lru_end == NULL)
|
||||||
|
{
|
||||||
|
table->lru_start = entry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
table->lru_end->lru_next = entry;
|
||||||
|
}
|
||||||
|
table->lru_end = entry;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For the data part, we want to allocate in rounded increments, so as to reduce the
|
* For the data part, we want to allocate in rounded increments, so as to reduce the
|
||||||
* number of calls to XMALLOC
|
* number of calls to XMALLOC
|
||||||
|
@ -206,12 +235,298 @@ mdns_util_skip_name(uint8_t *p)
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
mdns_util_copy_name(uint8_t * message, size_t message_length, size_t current_index,
|
||||||
|
uint8_t *name, int name_len_max, int name_index, int * name_len)
|
||||||
|
{
|
||||||
|
uint8_t l;
|
||||||
|
size_t recursive_index;
|
||||||
|
|
||||||
|
*name_len = 0;
|
||||||
|
while (current_index < message_length && name_index < name_len_max) {
|
||||||
|
l = message[current_index++];
|
||||||
|
if (l == 0)
|
||||||
|
{
|
||||||
|
name[name_index++] = 0;
|
||||||
|
*name_len = name_index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (l >= 0xC0)
|
||||||
|
{
|
||||||
|
if (current_index < message_length)
|
||||||
|
{
|
||||||
|
recursive_index = ((l & 63) << 8) | message[current_index++];
|
||||||
|
|
||||||
|
(void) mdns_util_copy_name(message, message_length,
|
||||||
|
recursive_index, name, name_len_max, name_index, name_len);
|
||||||
|
|
||||||
|
if (*name_len == 0)
|
||||||
|
{
|
||||||
|
current_index = message_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (current_index + l < message_length &&
|
||||||
|
name_index + l + 1 < name_len_max)
|
||||||
|
{
|
||||||
|
name[name_index++] = l;
|
||||||
|
|
||||||
|
memcpy(name + name_index, message + current_index, l);
|
||||||
|
name_index += l;
|
||||||
|
current_index += l;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current_index = message_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_index;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mdns_util_skip_query(uint8_t *p)
|
mdns_util_skip_query(uint8_t *p)
|
||||||
{
|
{
|
||||||
return mdns_util_skip_name(p) + 4;
|
return mdns_util_skip_name(p) + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Single copy procedure for many record types
|
||||||
|
* copy N octets, then the canonical value of the name.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mdns_util_canonical_flags_and_name(uint8_t *message, int message_length,
|
||||||
|
int record_length,
|
||||||
|
int current_index,
|
||||||
|
int nb_octets_to_copy,
|
||||||
|
uint8_t *buffer, int buffer_max,
|
||||||
|
uint8_t **actual_record, int *actual_length)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int buffer_index = 0;
|
||||||
|
int name_len = 0;
|
||||||
|
|
||||||
|
if (buffer_max <= nb_octets_to_copy || record_length <= nb_octets_to_copy)
|
||||||
|
{
|
||||||
|
/* incorrect buffer */
|
||||||
|
ret = GETDNS_RETURN_GENERIC_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < nb_octets_to_copy; i++)
|
||||||
|
{
|
||||||
|
buffer[buffer_index++] = message[current_index++];
|
||||||
|
}
|
||||||
|
|
||||||
|
current_index = mdns_util_copy_name(message, message_length, current_index, buffer, buffer_max, buffer_index, &name_len);
|
||||||
|
if (current_index == record_length)
|
||||||
|
{
|
||||||
|
buffer_index += name_len;
|
||||||
|
*actual_record = buffer;
|
||||||
|
*actual_length = buffer_index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* something went wrong. */
|
||||||
|
ret = GETDNS_RETURN_BAD_DOMAIN_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Set record value to canonical form
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mdns_util_canonical_record(uint8_t *message, int message_length,
|
||||||
|
int record_type, int record_class, int record_length,
|
||||||
|
int record_index,
|
||||||
|
uint8_t *buffer, int buffer_max,
|
||||||
|
uint8_t **actual_record, int *actual_length)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int current_index = record_index;
|
||||||
|
int buffer_index = 0;
|
||||||
|
int name_len = 0;
|
||||||
|
/* Check whether the record needs canonization */
|
||||||
|
*actual_record = message + record_index;
|
||||||
|
*actual_length = record_length;
|
||||||
|
|
||||||
|
if (record_class != GLDNS_RR_CLASS_IN)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No attempt at canonization outside the IN class.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (record_type)
|
||||||
|
{
|
||||||
|
|
||||||
|
case GLDNS_RR_TYPE_NS:
|
||||||
|
case GLDNS_RR_TYPE_CNAME:
|
||||||
|
case GLDNS_RR_TYPE_PTR:
|
||||||
|
case GLDNS_RR_TYPE_MD:
|
||||||
|
case GLDNS_RR_TYPE_MB:
|
||||||
|
case GLDNS_RR_TYPE_MF:
|
||||||
|
case GLDNS_RR_TYPE_MG:
|
||||||
|
case GLDNS_RR_TYPE_MR:
|
||||||
|
case GLDNS_RR_TYPE_NSAP_PTR:
|
||||||
|
/*
|
||||||
|
* copy the name in canonical form
|
||||||
|
*/
|
||||||
|
ret = mdns_util_canonical_flags_and_name(message, message_length,
|
||||||
|
record_length, current_index, 0,
|
||||||
|
buffer, buffer_max, actual_record, actual_length);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLDNS_RR_TYPE_A:
|
||||||
|
case GLDNS_RR_TYPE_AAAA:
|
||||||
|
case GLDNS_RR_TYPE_TXT:
|
||||||
|
case GLDNS_RR_TYPE_HINFO:
|
||||||
|
case GLDNS_RR_TYPE_MINFO:
|
||||||
|
case GLDNS_RR_TYPE_NULL:
|
||||||
|
case GLDNS_RR_TYPE_WKS:
|
||||||
|
case GLDNS_RR_TYPE_X25:
|
||||||
|
case GLDNS_RR_TYPE_ISDN:
|
||||||
|
case GLDNS_RR_TYPE_NSAP:
|
||||||
|
case GLDNS_RR_TYPE_SIG:
|
||||||
|
case GLDNS_RR_TYPE_KEY:
|
||||||
|
case GLDNS_RR_TYPE_GPOS:
|
||||||
|
case GLDNS_RR_TYPE_LOC:
|
||||||
|
case GLDNS_RR_TYPE_EID:
|
||||||
|
case GLDNS_RR_TYPE_NIMLOC:
|
||||||
|
/* leave the content as is, no domain name in content */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLDNS_RR_TYPE_SRV:
|
||||||
|
/*
|
||||||
|
* Copy 6 octets for weight(2), priority(2) and port(2),
|
||||||
|
* then copy the name.
|
||||||
|
*/
|
||||||
|
ret = mdns_util_canonical_flags_and_name(message, message_length,
|
||||||
|
record_length, current_index, 6,
|
||||||
|
buffer, buffer_max, actual_record, actual_length);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLDNS_RR_TYPE_MX:
|
||||||
|
case GLDNS_RR_TYPE_RT:
|
||||||
|
case GLDNS_RR_TYPE_AFSDB:
|
||||||
|
/*
|
||||||
|
* copy two bytes preference or subtype, then
|
||||||
|
* copy the name in canonical form
|
||||||
|
*/
|
||||||
|
ret = mdns_util_canonical_flags_and_name(message, message_length,
|
||||||
|
record_length, current_index, 2,
|
||||||
|
buffer, buffer_max, actual_record, actual_length);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLDNS_RR_TYPE_NAPTR:
|
||||||
|
case GLDNS_RR_TYPE_SOA:
|
||||||
|
case GLDNS_RR_TYPE_RP:
|
||||||
|
case GLDNS_RR_TYPE_NXT:
|
||||||
|
case GLDNS_RR_TYPE_PX:
|
||||||
|
case GLDNS_RR_TYPE_ATMA:
|
||||||
|
/*
|
||||||
|
* Group of record types that are complex, and also
|
||||||
|
* unexpected in MDNS/DNS-SD operation. Copying the
|
||||||
|
* record directly will work as long as the sender
|
||||||
|
* does not attempt name compression.
|
||||||
|
* TODO: log some kind of error.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
/** RFC2915 */
|
||||||
|
GLDNS_RR_TYPE_NAPTR = 35,
|
||||||
|
/** RFC2230 */
|
||||||
|
GLDNS_RR_TYPE_KX = 36,
|
||||||
|
/** RFC2538 */
|
||||||
|
GLDNS_RR_TYPE_CERT = 37,
|
||||||
|
/** RFC2874 */
|
||||||
|
GLDNS_RR_TYPE_A6 = 38,
|
||||||
|
/** RFC2672 */
|
||||||
|
GLDNS_RR_TYPE_DNAME = 39,
|
||||||
|
/** dnsind-kitchen-sink-02.txt */
|
||||||
|
GLDNS_RR_TYPE_SINK = 40,
|
||||||
|
/** Pseudo OPT record... */
|
||||||
|
GLDNS_RR_TYPE_OPT = 41,
|
||||||
|
/** RFC3123 */
|
||||||
|
GLDNS_RR_TYPE_APL = 42,
|
||||||
|
/** RFC4034, RFC3658 */
|
||||||
|
GLDNS_RR_TYPE_DS = 43,
|
||||||
|
/** SSH Key Fingerprint */
|
||||||
|
GLDNS_RR_TYPE_SSHFP = 44, /* RFC 4255 */
|
||||||
|
/** IPsec Key */
|
||||||
|
GLDNS_RR_TYPE_IPSECKEY = 45, /* RFC 4025 */
|
||||||
|
/** DNSSEC */
|
||||||
|
GLDNS_RR_TYPE_RRSIG = 46, /* RFC 4034 */
|
||||||
|
GLDNS_RR_TYPE_NSEC = 47, /* RFC 4034 */
|
||||||
|
GLDNS_RR_TYPE_DNSKEY = 48, /* RFC 4034 */
|
||||||
|
|
||||||
|
GLDNS_RR_TYPE_DHCID = 49, /* RFC 4701 */
|
||||||
|
/* NSEC3 */
|
||||||
|
GLDNS_RR_TYPE_NSEC3 = 50, /* RFC 5155 */
|
||||||
|
GLDNS_RR_TYPE_NSEC3PARAM = 51, /* RFC 5155 */
|
||||||
|
GLDNS_RR_TYPE_NSEC3PARAMS = 51,
|
||||||
|
GLDNS_RR_TYPE_TLSA = 52, /* RFC 6698 */
|
||||||
|
GLDNS_RR_TYPE_SMIMEA = 53, /* draft-ietf-dane-smime, TLSA-like but may
|
||||||
|
be extended */
|
||||||
|
|
||||||
|
GLDNS_RR_TYPE_HIP = 55, /* RFC 5205 */
|
||||||
|
|
||||||
|
/** draft-reid-dnsext-zs */
|
||||||
|
GLDNS_RR_TYPE_NINFO = 56,
|
||||||
|
/** draft-reid-dnsext-rkey */
|
||||||
|
GLDNS_RR_TYPE_RKEY = 57,
|
||||||
|
/** draft-ietf-dnsop-trust-history */
|
||||||
|
GLDNS_RR_TYPE_TALINK = 58,
|
||||||
|
GLDNS_RR_TYPE_CDS = 59, /** RFC 7344 */
|
||||||
|
GLDNS_RR_TYPE_CDNSKEY = 60, /** RFC 7344 */
|
||||||
|
GLDNS_RR_TYPE_OPENPGPKEY = 61, /* RFC 7929 */
|
||||||
|
GLDNS_RR_TYPE_CSYNC = 62, /* RFC 7477 */
|
||||||
|
|
||||||
|
GLDNS_RR_TYPE_SPF = 99, /* RFC 4408 */
|
||||||
|
|
||||||
|
GLDNS_RR_TYPE_UINFO = 100,
|
||||||
|
GLDNS_RR_TYPE_UID = 101,
|
||||||
|
GLDNS_RR_TYPE_GID = 102,
|
||||||
|
GLDNS_RR_TYPE_UNSPEC = 103,
|
||||||
|
|
||||||
|
GLDNS_RR_TYPE_NID = 104, /* RFC 6742 */
|
||||||
|
GLDNS_RR_TYPE_L32 = 105, /* RFC 6742 */
|
||||||
|
GLDNS_RR_TYPE_L64 = 106, /* RFC 6742 */
|
||||||
|
GLDNS_RR_TYPE_LP = 107, /* RFC 6742 */
|
||||||
|
|
||||||
|
/** draft-jabley-dnsext-eui48-eui64-rrtypes */
|
||||||
|
GLDNS_RR_TYPE_EUI48 = 108,
|
||||||
|
GLDNS_RR_TYPE_EUI64 = 109,
|
||||||
|
|
||||||
|
GLDNS_RR_TYPE_TKEY = 249, /* RFC 2930 */
|
||||||
|
GLDNS_RR_TYPE_TSIG = 250,
|
||||||
|
GLDNS_RR_TYPE_IXFR = 251,
|
||||||
|
GLDNS_RR_TYPE_AXFR = 252,
|
||||||
|
/** A request for mailbox-related records (MB, MG or MR) */
|
||||||
|
GLDNS_RR_TYPE_MAILB = 253,
|
||||||
|
/** A request for mail agent RRs (Obsolete - see MX) */
|
||||||
|
GLDNS_RR_TYPE_MAILA = 254,
|
||||||
|
/** any type (wildcard) */
|
||||||
|
GLDNS_RR_TYPE_ANY = 255,
|
||||||
|
GLDNS_RR_TYPE_URI = 256, /* RFC 7553 */
|
||||||
|
GLDNS_RR_TYPE_CAA = 257, /* RFC 6844 */
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* Unknown record type. Not expected in MDNS/DNS-SD. Just keep the current value.
|
||||||
|
* TODO: log some kind of error.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Comparison and other functions required for cache management
|
* Comparison and other functions required for cache management
|
||||||
*/
|
*/
|
||||||
|
@ -256,7 +571,8 @@ static int mdns_cache_key_comp(void* vkey1, void* vkey2)
|
||||||
/** old keys are deleted.
|
/** old keys are deleted.
|
||||||
* markdel() is used first.
|
* markdel() is used first.
|
||||||
* This function is called: func(key, userarg)
|
* This function is called: func(key, userarg)
|
||||||
* the userarg is set to the context in which the LRU hash table was created
|
* the userarg is set to the context in which the LRU hash table was created.
|
||||||
|
* TODO: is there a need to free the lock in the embedded hash entry structure?
|
||||||
*/
|
*/
|
||||||
static void msdn_cache_delkey(void* vkey, void* vcontext)
|
static void msdn_cache_delkey(void* vkey, void* vcontext)
|
||||||
{
|
{
|
||||||
|
@ -270,7 +586,6 @@ static void msdn_cache_delkey(void* vkey, void* vcontext)
|
||||||
*/
|
*/
|
||||||
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);
|
getdns_mdns_cached_record_header* header = ((getdns_mdns_cached_record_header*)vdata);
|
||||||
|
|
||||||
while (header->netreq_first)
|
while (header->netreq_first)
|
||||||
|
@ -302,6 +617,8 @@ static void msdn_cache_create_key_in_buffer(
|
||||||
int record_type, int record_class)
|
int record_type, int record_class)
|
||||||
{
|
{
|
||||||
getdns_mdns_cached_key_header * header = (getdns_mdns_cached_key_header*)key;
|
getdns_mdns_cached_key_header * header = (getdns_mdns_cached_key_header*)key;
|
||||||
|
|
||||||
|
memset(key, 0, sizeof(getdns_mdns_cached_key_header));
|
||||||
header->record_type = record_type;
|
header->record_type = record_type;
|
||||||
header->record_class = record_class;
|
header->record_class = record_class;
|
||||||
header->name_len = name_len;
|
header->name_len = name_len;
|
||||||
|
@ -447,7 +764,7 @@ mdns_update_cache_ttl_and_prune(struct getdns_context *context,
|
||||||
int not_matched_yet = (record_data == NULL) ? 0 : 1;
|
int not_matched_yet = (record_data == NULL) ? 0 : 1;
|
||||||
int current_record_match;
|
int current_record_match;
|
||||||
int last_copied_index;
|
int last_copied_index;
|
||||||
int current_hole_index;
|
int current_hole_index = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skip the query
|
* Skip the query
|
||||||
|
@ -562,13 +879,17 @@ mdns_update_cache_ttl_and_prune(struct getdns_context *context,
|
||||||
*new_record = old_record;
|
*new_record = old_record;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: if there are no record left standing, return a signal that the cache should be pruned.
|
|
||||||
*/
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mdns_cache_nb_records_in_entry(uint8_t * cached_data)
|
||||||
|
{
|
||||||
|
int message_index = sizeof(getdns_mdns_cached_record_header);
|
||||||
|
int nb_answers = (cached_data[message_index + 4] << 8) | cached_data[message_index + 5];
|
||||||
|
|
||||||
|
return nb_answers;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add entry function for the MDNS record cache.
|
* Add entry function for the MDNS record cache.
|
||||||
|
@ -606,9 +927,8 @@ mdns_propose_entry_to_cache(
|
||||||
key = mdns_cache_create_key(name, name_len, record_type, record_class, context);
|
key = mdns_cache_create_key(name, name_len, record_type, record_class, context);
|
||||||
data = mdns_cache_create_data(name, name_len, record_type, record_class,
|
data = mdns_cache_create_data(name, name_len, record_type, record_class,
|
||||||
record_data_len, current_time, context);
|
record_data_len, current_time, context);
|
||||||
new_entry = GETDNS_XMALLOC(context->mf, struct lruhash_entry, 1);
|
|
||||||
|
|
||||||
if (key == NULL || data == NULL || new_entry == NULL)
|
if (key == NULL || data == NULL)
|
||||||
{
|
{
|
||||||
if (key != NULL)
|
if (key != NULL)
|
||||||
{
|
{
|
||||||
|
@ -621,16 +941,11 @@ mdns_propose_entry_to_cache(
|
||||||
GETDNS_FREE(context->mf, data);
|
GETDNS_FREE(context->mf, data);
|
||||||
data = NULL;
|
data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_entry != NULL)
|
|
||||||
{
|
|
||||||
GETDNS_FREE(context->mf, new_entry);
|
|
||||||
new_entry = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
new_entry = &((getdns_mdns_cached_key_header*)key)->entry;
|
||||||
|
|
||||||
memset(new_entry, 0, sizeof(struct lruhash_entry));
|
memset(new_entry, 0, sizeof(struct lruhash_entry));
|
||||||
lock_rw_init(new_entry->lock);
|
lock_rw_init(new_entry->lock);
|
||||||
new_entry->hash = hash;
|
new_entry->hash = hash;
|
||||||
|
@ -641,8 +956,13 @@ mdns_propose_entry_to_cache(
|
||||||
|
|
||||||
if (entry != new_entry)
|
if (entry != new_entry)
|
||||||
{
|
{
|
||||||
lock_rw_destroy(new_entry->lock);
|
/* There was already an entry for this name, which is really weird.
|
||||||
GETDNS_FREE(context->mf, new_entry);
|
* But it can in theory happen in a race condition.
|
||||||
|
*/
|
||||||
|
GETDNS_FREE(context->mf, key);
|
||||||
|
key = NULL;
|
||||||
|
GETDNS_FREE(context->mf, data);
|
||||||
|
data = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -663,41 +983,87 @@ mdns_propose_entry_to_cache(
|
||||||
header->netreq_first = netreq;
|
header->netreq_first = netreq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if the entry is empty, move it to the bottom of the LRU */
|
||||||
|
if (mdns_cache_nb_records_in_entry((uint8_t*)(entry->data)) == 0)
|
||||||
|
{
|
||||||
|
lru_demote(context->mdns_cache, entry);
|
||||||
|
}
|
||||||
|
|
||||||
/* then, unlock the entry */
|
/* then, unlock the entry */
|
||||||
lock_rw_unlock(entry->lock);
|
lock_rw_unlock(entry->lock);
|
||||||
|
|
||||||
/* TODO: if the entry was marked for deletion, move it to the bottom of the LRU */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare function for the mdns_continuous_query_by_name_rrtype,
|
* Processing of requests after cache update.
|
||||||
* used in the red-black tree of all ongoing queries.
|
* This is coded as synchronous processing, under lock. This is probably wrong.
|
||||||
|
* It would be better to just collate the responses for now, and
|
||||||
|
* process the queries out of the loop.
|
||||||
*/
|
*/
|
||||||
static int mdns_cmp_continuous_queries_by_name_rrtype(const void * nqnr1, const void * nqnr2)
|
static int
|
||||||
|
mdns_cache_complete_queries(
|
||||||
|
struct getdns_context *context,
|
||||||
|
uint8_t * name, int name_len,
|
||||||
|
int record_type, int record_class)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
getdns_mdns_continuous_query * qnr1 = (getdns_mdns_continuous_query *)nqnr1;
|
size_t required_memory = 0;
|
||||||
getdns_mdns_continuous_query * qnr2 = (getdns_mdns_continuous_query *)nqnr2;
|
uint8_t temp_key[256 + sizeof(getdns_mdns_cached_key_header)];
|
||||||
|
hashvalue_type hash;
|
||||||
|
struct lruhash_entry *entry;
|
||||||
|
uint8_t *packet;
|
||||||
|
int packet_length;
|
||||||
|
getdns_mdns_cached_record_header * header;
|
||||||
|
getdns_network_req * netreq;
|
||||||
|
|
||||||
if (qnr1->request_class != qnr2->request_class)
|
msdn_cache_create_key_in_buffer(temp_key, name, name_len, record_type, record_class);
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: make hash init value a random number in the context, for defense against DOS */
|
||||||
|
hash = hashlittle(temp_key, name_len + sizeof(getdns_mdns_cached_key_header), 0xCAC8E);
|
||||||
|
|
||||||
|
entry = lruhash_lookup(context->mdns_cache, hash, temp_key, 1);
|
||||||
|
|
||||||
|
if (entry != NULL && entry->data != NULL)
|
||||||
{
|
{
|
||||||
ret = (qnr1->request_class < qnr2->request_class) ? -1 : 1;
|
header = (getdns_mdns_cached_record_header *)entry->data;
|
||||||
}
|
|
||||||
else if (qnr1->request_type != qnr2->request_type)
|
packet = ((uint8_t *)entry->data) + sizeof(getdns_mdns_cached_record_header);
|
||||||
{
|
packet_length = header->content_len; /* TODO: check that */
|
||||||
ret = (qnr1->request_type < qnr2->request_type) ? -1 : 1;
|
|
||||||
}
|
while ((netreq = header->netreq_first) != NULL)
|
||||||
else if (qnr1->name_len != qnr2->name_len)
|
{
|
||||||
{
|
header->netreq_first = netreq->mdns_netreq_next;
|
||||||
ret = (qnr1->name_len < qnr2->name_len) ? -1 : 1;
|
netreq->mdns_netreq_next = NULL;
|
||||||
}
|
/* TODO: copy the returned value in the response field */
|
||||||
else
|
if (packet_length > netreq->wire_data_sz)
|
||||||
{
|
{
|
||||||
ret = memcmp((void*)qnr1->name, (void*)qnr2->name, qnr1->name_len);
|
/* TODO: allocation. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (netreq->response != NULL)
|
||||||
|
{
|
||||||
|
memcpy(netreq->response, packet, packet_length);
|
||||||
|
/* TODO: process the query */
|
||||||
|
netreq->response_len = packet_length;
|
||||||
|
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
||||||
|
netreq->state = NET_REQ_FINISHED;
|
||||||
|
_getdns_check_dns_req_complete(netreq->owner);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Fail the query? */
|
||||||
|
netreq->response_len = 0;
|
||||||
|
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
||||||
|
netreq->state = NET_REQ_ERRORED;
|
||||||
|
_getdns_check_dns_req_complete(netreq->owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock_rw_unlock(entry->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,10 +1074,13 @@ static void
|
||||||
mdns_udp_multicast_read_cb(void *userarg)
|
mdns_udp_multicast_read_cb(void *userarg)
|
||||||
{
|
{
|
||||||
mdns_network_connection * cnx = (mdns_network_connection *)userarg;
|
mdns_network_connection * cnx = (mdns_network_connection *)userarg;
|
||||||
|
uint64_t current_time;
|
||||||
ssize_t read;
|
ssize_t read;
|
||||||
DEBUG_MDNS("%s %-35s: CTX: %p, NET=%d \n", MDNS_DEBUG_MREAD,
|
DEBUG_MDNS("%s %-35s: CTX: %p, NET=%d \n", MDNS_DEBUG_MREAD,
|
||||||
__FUNCTION__, cnx->context, cnx->addr_mcast.ss_family);
|
__FUNCTION__, cnx->context, cnx->addr_mcast.ss_family);
|
||||||
|
|
||||||
|
current_time = _getdns_get_time_as_uintt64();
|
||||||
|
|
||||||
GETDNS_CLEAR_EVENT(
|
GETDNS_CLEAR_EVENT(
|
||||||
cnx->context->extension, &cnx->event);
|
cnx->context->extension, &cnx->event);
|
||||||
|
|
||||||
|
@ -725,13 +1094,128 @@ mdns_udp_multicast_read_cb(void *userarg)
|
||||||
if (read >= GLDNS_HEADER_SIZE)
|
if (read >= GLDNS_HEADER_SIZE)
|
||||||
{
|
{
|
||||||
/* parse the response, find the relevant queries, submit the records to the cache */
|
/* parse the response, find the relevant queries, submit the records to the cache */
|
||||||
|
int opcodeAndflags = cnx->response[2];
|
||||||
|
int nb_queries = (cnx->response[4] << 8) | cnx->response[5];
|
||||||
|
int nb_responses = (cnx->response[6] << 8) | cnx->response[7];
|
||||||
|
|
||||||
/*
|
if (opcodeAndflags != 0x84)
|
||||||
netreq->response_len = read;
|
{
|
||||||
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
/* this is not an MDNS answer packet. */
|
||||||
netreq->state = NET_REQ_FINISHED;
|
}
|
||||||
_getdns_check_dns_req_complete(dnsreq);
|
else
|
||||||
*/
|
{
|
||||||
|
size_t current_index = 12;
|
||||||
|
uint8_t name[256];
|
||||||
|
int name_len;
|
||||||
|
int record_type;
|
||||||
|
int record_class;
|
||||||
|
int record_ttl;
|
||||||
|
int record_data_len;
|
||||||
|
int nb_records = 0;
|
||||||
|
int nb_queries_skipped = 0;
|
||||||
|
int start_of_records;
|
||||||
|
int signalled_records = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In normal mDNS operation, there should not be any query here.
|
||||||
|
* But just in case, we can skip the queries...
|
||||||
|
*/
|
||||||
|
while (current_index < read && nb_queries_skipped < nb_queries)
|
||||||
|
{
|
||||||
|
current_index += mdns_util_skip_query(&cnx->response[current_index]);
|
||||||
|
nb_queries_skipped++;
|
||||||
|
}
|
||||||
|
start_of_records = current_index;
|
||||||
|
/*
|
||||||
|
* Parse the answers and propose them to the cache
|
||||||
|
*/
|
||||||
|
|
||||||
|
while (current_index < read && nb_records < nb_responses)
|
||||||
|
{
|
||||||
|
/* Copy and skip the name */
|
||||||
|
current_index = mdns_util_copy_name(cnx->response, read, current_index,
|
||||||
|
name, sizeof(name), 0, &name_len);
|
||||||
|
if (current_index + 12 >= read)
|
||||||
|
{
|
||||||
|
/* bogus packet.. Should log. */
|
||||||
|
current_index = read;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Parse the record header */
|
||||||
|
record_type = (cnx->response[current_index++] << 8);
|
||||||
|
record_type |= (cnx->response[current_index++]);
|
||||||
|
record_class = (cnx->response[current_index++] << 8);
|
||||||
|
record_class |= (cnx->response[current_index++]);
|
||||||
|
record_ttl = (cnx->response[current_index++] << 24);
|
||||||
|
record_ttl |= (cnx->response[current_index++] << 16);
|
||||||
|
record_ttl |= (cnx->response[current_index++] << 8);
|
||||||
|
record_ttl |= (cnx->response[current_index++]);
|
||||||
|
record_data_len = (cnx->response[current_index++] << 8);
|
||||||
|
record_data_len |= (cnx->response[current_index++]);
|
||||||
|
|
||||||
|
if (current_index + record_data_len < read)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Set the record to canonical form. This is required, since
|
||||||
|
* MDNS software commonly uses name compression for PTR or SRV records.
|
||||||
|
*/
|
||||||
|
int actual_length;
|
||||||
|
uint8_t *actual_record;
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
|
||||||
|
/* TODO: do something in case of canonization failures */
|
||||||
|
(void) mdns_util_canonical_record(cnx->response, read,
|
||||||
|
record_type, record_class, record_data_len, current_index,
|
||||||
|
buffer, sizeof(buffer), &actual_record, &actual_length);
|
||||||
|
|
||||||
|
|
||||||
|
/* Submit to the cache. As a side effect, may signal that a continuous request is done. */
|
||||||
|
(void) mdns_propose_entry_to_cache(cnx->context, name, name_len,
|
||||||
|
record_type, record_class, record_ttl,
|
||||||
|
actual_record, actual_length, NULL,
|
||||||
|
current_time);
|
||||||
|
|
||||||
|
current_index += record_data_len;
|
||||||
|
nb_records++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* bogus packet.. Should log. */
|
||||||
|
current_index = read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go over the queries that were mentioned in the update, and prepare returns. */
|
||||||
|
current_index = start_of_records;
|
||||||
|
while (current_index < read && signalled_records < nb_responses)
|
||||||
|
{
|
||||||
|
/* copy the name */
|
||||||
|
current_index = mdns_util_copy_name(cnx->response, read, current_index,
|
||||||
|
name, sizeof(name), 0, &name_len);
|
||||||
|
if (current_index + 12 >= read)
|
||||||
|
{
|
||||||
|
/* bogus packet.. Should log. */
|
||||||
|
current_index = read;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Parse the record header */
|
||||||
|
record_type = (cnx->response[current_index++] << 8);
|
||||||
|
record_type |= (cnx->response[current_index++]);
|
||||||
|
record_class = (cnx->response[current_index++] << 8);
|
||||||
|
record_class |= (cnx->response[current_index++]);
|
||||||
|
current_index += 4;
|
||||||
|
record_data_len = (cnx->response[current_index++] << 8);
|
||||||
|
record_data_len |= (cnx->response[current_index++]);
|
||||||
|
current_index += record_data_len;
|
||||||
|
|
||||||
|
/* process the pending requests */
|
||||||
|
(void)mdns_cache_complete_queries(cnx->context, name, name_len, record_type, record_class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -999,11 +1483,68 @@ static getdns_return_t mdns_initialize_continuous_request(getdns_network_req *ne
|
||||||
getdns_dns_req *dnsreq = netreq->owner;
|
getdns_dns_req *dnsreq = netreq->owner;
|
||||||
struct getdns_context *context = dnsreq->context;
|
struct getdns_context *context = dnsreq->context;
|
||||||
|
|
||||||
ret = mdns_propose_entry_to_cache(context, dnsreq->name, dnsreq->name_len,
|
size_t required_memory = 0;
|
||||||
netreq->request_type, dnsreq->request_class, 1, NULL, 0,
|
uint8_t temp_key[256 + sizeof(getdns_mdns_cached_key_header)];
|
||||||
netreq, _getdns_get_time_as_uintt64());
|
hashvalue_type hash;
|
||||||
|
struct lruhash_entry *entry;
|
||||||
|
size_t pkt_len = netreq->response - netreq->query;
|
||||||
|
|
||||||
/* to do: queue message request to socket */
|
msdn_cache_create_key_in_buffer(temp_key, dnsreq->name, dnsreq->name_len,
|
||||||
|
netreq->request_type, dnsreq->request_class);
|
||||||
|
|
||||||
|
/* TODO: make hash init value a random number in the context, for defense against DOS */
|
||||||
|
hash = hashlittle(temp_key, dnsreq->name_len + sizeof(getdns_mdns_cached_key_header), 0xCAC8E);
|
||||||
|
|
||||||
|
entry = lruhash_lookup(context->mdns_cache, hash, temp_key, 1);
|
||||||
|
|
||||||
|
if (entry == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* First, create an entry for the query
|
||||||
|
*/
|
||||||
|
|
||||||
|
ret = mdns_propose_entry_to_cache(context, dnsreq->name, dnsreq->name_len,
|
||||||
|
netreq->request_type, dnsreq->request_class, 1, NULL, 0,
|
||||||
|
netreq, _getdns_get_time_as_uintt64());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check whether the cache entry is recent.
|
||||||
|
* If yes, just return it, but first update the entry tracking in the cache entry.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* and unlock the entry!
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
/* If the entry was created less than 1 sec ago, send a query */
|
||||||
|
|
||||||
|
if (context->mdns_connection_nb <= 0)
|
||||||
|
{
|
||||||
|
/* oops, no network! */
|
||||||
|
ret = GETDNS_RETURN_GENERIC_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* TODO? Set TTL=255 for compliance with RFC 6762 */
|
||||||
|
int fd_index = context->mdns_connection_nb - 1;
|
||||||
|
|
||||||
|
if ((ssize_t)pkt_len != sendto(
|
||||||
|
context->mdns_connection[fd_index].fd
|
||||||
|
, (const void *)netreq->query, pkt_len, 0
|
||||||
|
, (SOCKADDR*)&context->mdns_connection[fd_index].addr_mcast
|
||||||
|
, context->mdns_connection[fd_index].addr_mcast_len))
|
||||||
|
{
|
||||||
|
ret = GETDNS_RETURN_GENERIC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: update the send query time */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1103,9 +1644,9 @@ mdns_timeout_cb(void *userarg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************/
|
/*****************************************/
|
||||||
/* UDP callback functions */
|
/* UDP callback functions for basic MDNS */
|
||||||
/**************************/
|
/*****************************************/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mdns_udp_read_cb(void *userarg)
|
mdns_udp_read_cb(void *userarg)
|
||||||
|
@ -1220,21 +1761,50 @@ _getdns_submit_mdns_request(getdns_network_req *netreq)
|
||||||
netreq, netreq->request_type);
|
netreq, netreq->request_type);
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
getdns_dns_req *dnsreq = netreq->owner;
|
getdns_dns_req *dnsreq = netreq->owner;
|
||||||
|
struct getdns_context * context = dnsreq->context;
|
||||||
|
getdns_return_t ret = 0;
|
||||||
|
|
||||||
/* Open the UDP socket required for the request */
|
/*
|
||||||
if ((fd = socket(
|
* TO DO: depending on context type, perform basic processing versus full MDNS
|
||||||
AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
|
*/
|
||||||
return -1;
|
|
||||||
/* TODO: do we need getdns_sock_nonblock(fd); */
|
|
||||||
|
|
||||||
/* Schedule the MDNS request */
|
if (context->mdns_extended_support == 2)
|
||||||
netreq->fd = fd;
|
{
|
||||||
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
/* Not initialize yet. Do it know before processing the query */
|
||||||
GETDNS_SCHEDULE_EVENT(
|
ret = mdns_delayed_network_init(context);
|
||||||
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
|
|
||||||
getdns_eventloop_event_init(&netreq->event, netreq,
|
if (ret != 0)
|
||||||
NULL, mdns_udp_write_cb, mdns_timeout_cb));
|
{
|
||||||
return GETDNS_RETURN_GOOD;
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->mdns_extended_support == 1)
|
||||||
|
{
|
||||||
|
/* extended DNS support */
|
||||||
|
ret = mdns_initialize_continuous_request(netreq);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* basic MDNS request */
|
||||||
|
|
||||||
|
/* Open the UDP socket required for the request */
|
||||||
|
if ((fd = socket(
|
||||||
|
AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
|
||||||
|
return -1;
|
||||||
|
/* TODO: do we need getdns_sock_nonblock(fd); */
|
||||||
|
|
||||||
|
/* Schedule the MDNS request */
|
||||||
|
netreq->fd = fd;
|
||||||
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
||||||
|
GETDNS_SCHEDULE_EVENT(
|
||||||
|
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
|
||||||
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
||||||
|
NULL, mdns_udp_write_cb, mdns_timeout_cb));
|
||||||
|
ret = GETDNS_RETURN_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
11
src/mdns.h
11
src/mdns.h
|
@ -24,6 +24,7 @@
|
||||||
#ifdef HAVE_MDNS_SUPPORT
|
#ifdef HAVE_MDNS_SUPPORT
|
||||||
#include "getdns/getdns.h"
|
#include "getdns/getdns.h"
|
||||||
#include "types-internal.h"
|
#include "types-internal.h"
|
||||||
|
#include "util/storage/lruhash.h"
|
||||||
|
|
||||||
getdns_return_t
|
getdns_return_t
|
||||||
_getdns_submit_mdns_request(getdns_network_req *netreq);
|
_getdns_submit_mdns_request(getdns_network_req *netreq);
|
||||||
|
@ -51,20 +52,26 @@ typedef struct getdns_mdns_known_record
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each entry in the hash table is keyed by type, class and name.
|
* Each entry in the hash table is keyed by type, class and name.
|
||||||
|
* The key structure also contains the LRU hash entry structure.
|
||||||
* The data part contains:
|
* The data part contains:
|
||||||
* - 64 bit time stamp
|
* - 64 bit time stamp
|
||||||
* - 32 bit word describing the record size
|
* - 32 bit word describing the record size
|
||||||
* - 32 bit word describing teh allocated memory size
|
* - 32 bit word describing teh allocated memory size
|
||||||
* - valid DNS response, including 1 query and N answers, 0 AUTH, 0 AD.
|
* - valid DNS response, including 1 query and N answers, 0 AUTH, 0 AD.
|
||||||
* For economy, all answers are encoded using header compression, pointing
|
* For economy, the names of all answers are encoded using header compression, pointing
|
||||||
* to the name in the query, i.e. offset 12 from beginning of message
|
* to the name in the query, i.e. offset 12 from beginning of message.
|
||||||
|
* For stability, the names included in the data part of records are not compressed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct getdns_mdns_cached_key_header
|
typedef struct getdns_mdns_cached_key_header
|
||||||
{
|
{
|
||||||
|
/* embedded entry, for LRU hash */
|
||||||
|
struct lruhash_entry entry;
|
||||||
|
/* identification */
|
||||||
uint16_t record_type;
|
uint16_t record_type;
|
||||||
uint16_t record_class;
|
uint16_t record_class;
|
||||||
int name_len;
|
int name_len;
|
||||||
|
/* the octets following this structure contain the name */
|
||||||
} getdns_mdns_cached_key_header;
|
} getdns_mdns_cached_key_header;
|
||||||
|
|
||||||
typedef struct getdns_mdns_cached_record_header
|
typedef struct getdns_mdns_cached_record_header
|
||||||
|
|
Loading…
Reference in New Issue