2016-12-22 17:51:47 -06:00
|
|
|
/*
|
|
|
|
* Functions for MDNS resolving.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Christian Huitema <huitema@huitema.net>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "context.h"
|
|
|
|
#include "general.h"
|
|
|
|
#include "gldns/pkthdr.h"
|
2017-02-23 19:55:31 -06:00
|
|
|
#include "gldns/rrdef.h"
|
2016-12-22 17:51:47 -06:00
|
|
|
#include "util-internal.h"
|
|
|
|
#include "mdns.h"
|
|
|
|
|
2016-12-22 22:30:14 -06:00
|
|
|
#ifdef HAVE_MDNS_SUPPORT
|
|
|
|
|
2016-12-22 17:51:47 -06:00
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
typedef u_short sa_family_t;
|
|
|
|
#define _getdns_EWOULDBLOCK (WSAGetLastError() == WSATRY_AGAIN ||\
|
|
|
|
WSAGetLastError() == WSAEWOULDBLOCK)
|
|
|
|
#define _getdns_EINPROGRESS (WSAGetLastError() == WSAEINPROGRESS)
|
|
|
|
#else
|
|
|
|
#define _getdns_EWOULDBLOCK (errno == EAGAIN || errno == EWOULDBLOCK)
|
|
|
|
#define _getdns_EINPROGRESS (errno == EINPROGRESS)
|
2017-03-09 07:36:20 -06:00
|
|
|
#define SOCKADDR struct sockaddr
|
|
|
|
#define SOCKADDR_IN struct sockaddr_in
|
|
|
|
#define SOCKADDR_IN6 struct sockaddr_in6
|
|
|
|
#define SOCKET int
|
|
|
|
#define IP_MREQ struct ip_mreq
|
|
|
|
#define IPV6_MREQ struct ipv6_mreq
|
|
|
|
#define BOOL int
|
|
|
|
#define TRUE 1
|
2016-12-22 17:51:47 -06:00
|
|
|
#endif
|
|
|
|
|
|
|
|
uint64_t _getdns_get_time_as_uintt64();
|
|
|
|
|
2017-03-09 07:36:20 -06:00
|
|
|
#include "util/fptr_wlist.h"
|
|
|
|
#include "util/lookup3.h"
|
2017-02-13 22:28:46 -06:00
|
|
|
|
2016-12-22 17:51:47 -06:00
|
|
|
/*
|
|
|
|
* Constants defined in RFC 6762
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MDNS_MCAST_IPV4_LONG 0xE00000FB /* 224.0.0.251 */
|
|
|
|
#define MDNS_MCAST_PORT 5353
|
|
|
|
|
2017-02-06 22:23:35 -06:00
|
|
|
static uint8_t mdns_mcast_ipv6[] = {
|
|
|
|
0xFF, 0x02, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0xFB
|
|
|
|
};
|
2016-12-22 17:51:47 -06:00
|
|
|
|
|
|
|
static uint8_t mdns_suffix_dot_local[] = { 5, 'l', 'o', 'c', 'a', 'l', 0 };
|
|
|
|
static uint8_t mdns_suffix_254_169_in_addr_arpa[] = {
|
|
|
|
3, '2', '5', '4',
|
|
|
|
3, '1', '6', '9',
|
|
|
|
7, 'i', 'n', '-', 'a', 'd', 'd', 'r',
|
|
|
|
4, 'a', 'r', 'p', 'a', 0 };
|
|
|
|
static uint8_t mdns_suffix_8_e_f_ip6_arpa[] = {
|
|
|
|
1, '8', 1, 'e', 1, 'f',
|
2017-02-06 22:23:35 -06:00
|
|
|
3, 'i', 'p', '6',
|
2016-12-22 17:51:47 -06:00
|
|
|
4, 'a', 'r', 'p', 'a', 0 };
|
|
|
|
static uint8_t mdns_suffix_9_e_f_ip6_arpa[] = {
|
|
|
|
1, '9', 1, 'e', 1, 'f',
|
2017-02-06 22:23:35 -06:00
|
|
|
3, 'i', 'p', '6',
|
2016-12-22 17:51:47 -06:00
|
|
|
4, 'a', 'r', 'p', 'a', 0 };
|
|
|
|
static uint8_t mdns_suffix_a_e_f_ip6_arpa[] = {
|
|
|
|
1, 'a', 1, 'e', 1, 'f',
|
2017-02-06 22:23:35 -06:00
|
|
|
3, 'i', 'p', '6',
|
2016-12-22 17:51:47 -06:00
|
|
|
4, 'a', 'r', 'p', 'a', 0 };
|
|
|
|
static uint8_t mdns_suffix_b_e_f_ip6_arpa[] = {
|
|
|
|
1, 'b', 1, 'e', 1, 'f',
|
2017-02-06 22:23:35 -06:00
|
|
|
3, 'i', 'p', '6',
|
2016-12-22 17:51:47 -06:00
|
|
|
4, 'a', 'r', 'p', 'a', 0 };
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
#define MDNS_PACKET_INDEX_QCODE 2
|
|
|
|
#define MDNS_PACKET_INDEX_QUERY 4
|
|
|
|
#define MDNS_PACKET_INDEX_ANSWER 6
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
/*
|
|
|
|
* MDNS cache management using LRU Hash.
|
|
|
|
*
|
|
|
|
* Each record contains a DNS query + response, formatted as received from
|
|
|
|
* the network. By convention, there will be exactly one query, and
|
|
|
|
* a variable number of answers. Auth and AD sections will not be cached.
|
|
|
|
* For maintenance purpose, each recontains a last accessed time stamp.
|
|
|
|
*
|
|
|
|
* This structure works very well for classic DNS caches, but for MDNS we
|
|
|
|
* have to consider processing a new record for an existing cache entry. If
|
|
|
|
* the record is present, its TTL should be updated. If the record is not
|
|
|
|
* present, it should be added to the existing data.
|
|
|
|
*
|
|
|
|
* After an update, the TTL of all the records should be updated. Some
|
|
|
|
* records will end up with a TTL value of zero. These records should be
|
|
|
|
* deleted, using a "compression" procedure.
|
|
|
|
*/
|
2017-01-20 21:44:05 -06:00
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
/*
|
|
|
|
* For the data part, we want to allocate in rounded increments, so as to reduce the
|
|
|
|
* number of calls to XMALLOC
|
|
|
|
*/
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
mdns_util_suggest_size(uint32_t required_size)
|
|
|
|
{
|
|
|
|
return (required_size <= 512) ? ((required_size <= 256) ? 256 : 512) :
|
|
|
|
((required_size + 1023) & 0xFFFFFC00);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cache management utilities
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
mdns_util_skip_name(uint8_t *p)
|
|
|
|
{
|
|
|
|
int x = 0;
|
|
|
|
int l;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
l = p[x];
|
|
|
|
if (l == 0)
|
|
|
|
{
|
|
|
|
x++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (l >= 0xC0)
|
|
|
|
{
|
|
|
|
x += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x += l + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
static int
|
|
|
|
mdns_util_skip_query(uint8_t *p)
|
|
|
|
{
|
|
|
|
return mdns_util_skip_name(p) + 4;
|
|
|
|
}
|
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
/* 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;
|
|
|
|
}
|
2017-02-13 22:28:46 -06:00
|
|
|
/*
|
|
|
|
* Comparison and other functions required for cache management
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the size of an entry.
|
|
|
|
*
|
|
|
|
* size = mdns_cache_size (key, data).
|
|
|
|
*/
|
|
|
|
static size_t mdns_cache_entry_size(void* vkey, void* vdata)
|
|
|
|
{
|
|
|
|
size_t sz = 0;
|
|
|
|
|
|
|
|
if (vkey != NULL)
|
|
|
|
{
|
|
|
|
sz += sizeof(getdns_mdns_cached_key_header) + ((getdns_mdns_cached_key_header*)vkey)->name_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vdata != NULL)
|
|
|
|
{
|
|
|
|
sz += ((getdns_mdns_cached_record_header*)vdata)->allocated_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** type of function that compares two keys. return 0 if equal. */
|
|
|
|
static int mdns_cache_key_comp(void* vkey1, void* vkey2)
|
|
|
|
{
|
|
|
|
getdns_mdns_cached_key_header *header1 = (getdns_mdns_cached_key_header*)vkey1;
|
|
|
|
getdns_mdns_cached_key_header *header2 = (getdns_mdns_cached_key_header*)vkey2;
|
|
|
|
|
|
|
|
return (header1->record_type == header2->record_type &&
|
|
|
|
header1->record_class == header2->record_class &&
|
|
|
|
header1->name_len == header2->name_len)
|
|
|
|
? memcmp(((uint8_t*)vkey1) + sizeof(getdns_mdns_cached_key_header),
|
|
|
|
((uint8_t*)vkey2) + sizeof(getdns_mdns_cached_key_header),
|
|
|
|
header1->name_len)
|
|
|
|
: -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** old keys are deleted.
|
|
|
|
* markdel() is used first.
|
|
|
|
* This function is called: func(key, userarg)
|
2017-02-23 19:55:31 -06:00
|
|
|
* 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?
|
2017-01-20 21:44:05 -06:00
|
|
|
*/
|
2017-02-13 22:28:46 -06:00
|
|
|
static void msdn_cache_delkey(void* vkey, void* vcontext)
|
|
|
|
{
|
|
|
|
GETDNS_FREE(((struct getdns_context *) vcontext)->mf, vkey);
|
|
|
|
}
|
|
|
|
|
2017-02-14 15:30:29 -06:00
|
|
|
/** 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.
|
|
|
|
*/
|
2017-02-13 22:28:46 -06:00
|
|
|
static void msdn_cache_deldata(void* vdata, void* vcontext)
|
|
|
|
{
|
2017-02-14 15:30:29 -06:00
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
2017-02-13 22:28:46 -06:00
|
|
|
GETDNS_FREE(((struct getdns_context *) vcontext)->mf, vdata);
|
|
|
|
}
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
/*
|
|
|
|
* Read the number of answers in a cached record
|
|
|
|
*/
|
|
|
|
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 + MDNS_PACKET_INDEX_ANSWER] << 8) |
|
|
|
|
cached_data[message_index + MDNS_PACKET_INDEX_ANSWER + 1];
|
|
|
|
|
|
|
|
return nb_answers;
|
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
/*
|
|
|
|
* Create a key in preallocated buffer
|
|
|
|
* the allocated size of key should be >= sizeof(getdns_mdns_cached_key_header) + name_len
|
|
|
|
*/
|
|
|
|
static void msdn_cache_create_key_in_buffer(
|
|
|
|
uint8_t* key,
|
|
|
|
uint8_t * name, int name_len,
|
|
|
|
int record_type, int record_class)
|
|
|
|
{
|
|
|
|
getdns_mdns_cached_key_header * header = (getdns_mdns_cached_key_header*)key;
|
2017-02-23 19:55:31 -06:00
|
|
|
|
|
|
|
memset(key, 0, sizeof(getdns_mdns_cached_key_header));
|
2017-02-13 22:28:46 -06:00
|
|
|
header->record_type = record_type;
|
|
|
|
header->record_class = record_class;
|
|
|
|
header->name_len = name_len;
|
|
|
|
(void) memcpy(key + sizeof(getdns_mdns_cached_key_header), name, name_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t * mdns_cache_create_key(
|
|
|
|
uint8_t * name, int name_len,
|
|
|
|
int record_type, int record_class,
|
|
|
|
struct getdns_context * context)
|
|
|
|
{
|
|
|
|
uint8_t* key = GETDNS_XMALLOC(context->mf, uint8_t, sizeof(getdns_mdns_cached_key_header) + name_len);
|
|
|
|
|
|
|
|
if (key != NULL)
|
|
|
|
{
|
|
|
|
msdn_cache_create_key_in_buffer(key, name, name_len, record_type, record_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t * mdns_cache_create_data(
|
|
|
|
uint8_t * name, int name_len,
|
|
|
|
int record_type, int record_class,
|
|
|
|
int record_data_len,
|
|
|
|
uint64_t current_time,
|
|
|
|
struct getdns_context * context)
|
|
|
|
{
|
|
|
|
getdns_mdns_cached_record_header * header;
|
|
|
|
int current_index;
|
2017-02-14 15:30:29 -06:00
|
|
|
size_t data_size = sizeof(getdns_mdns_cached_record_header) + 12 + name_len + 4;
|
2017-02-13 22:28:46 -06:00
|
|
|
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);
|
|
|
|
|
|
|
|
if (data != NULL)
|
|
|
|
{
|
|
|
|
header = (getdns_mdns_cached_record_header *)data;
|
|
|
|
header->insertion_microsec = current_time;
|
|
|
|
header->content_len = data_size;
|
|
|
|
header->allocated_length = alloc_size;
|
2017-02-14 15:30:29 -06:00
|
|
|
header->netreq_first = NULL;
|
2017-02-13 22:28:46 -06:00
|
|
|
current_index = sizeof(getdns_mdns_cached_record_header);
|
2017-02-14 15:30:29 -06:00
|
|
|
memset(data + current_index, 0, 12);
|
2017-03-03 18:52:02 -06:00
|
|
|
data[current_index + MDNS_PACKET_INDEX_QUERY + 1] = 1; /* 1 query present by default */
|
2017-02-14 15:30:29 -06:00
|
|
|
current_index += 12;
|
2017-02-13 22:28:46 -06:00
|
|
|
memcpy(data + current_index, name, name_len);
|
|
|
|
current_index += name_len;
|
|
|
|
data[current_index++] = (uint8_t)(record_type >> 8);
|
|
|
|
data[current_index++] = (uint8_t)(record_type);
|
|
|
|
data[current_index++] = (uint8_t)(record_class >> 8);
|
|
|
|
data[current_index++] = (uint8_t)(record_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a record.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
mdns_add_record_to_cache_entry(struct getdns_context *context,
|
|
|
|
uint8_t * old_record, uint8_t ** new_record,
|
|
|
|
int record_type, int record_class, int ttl,
|
|
|
|
uint8_t * record_data, int record_data_len)
|
2017-01-20 21:44:05 -06:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2017-02-13 22:28:46 -06:00
|
|
|
getdns_mdns_cached_record_header *header = (getdns_mdns_cached_record_header*)old_record;
|
|
|
|
/* Compute the record length */
|
|
|
|
uint32_t record_length = 2 + 2 + 2 + 4 + 2 + record_data_len;
|
|
|
|
uint32_t current_length = header->content_len;
|
|
|
|
/* update the number of records */
|
2017-03-03 18:52:02 -06:00
|
|
|
uint8_t *start_answer_code = old_record + sizeof(getdns_mdns_cached_record_header) + MDNS_PACKET_INDEX_ANSWER;
|
2017-02-13 22:28:46 -06:00
|
|
|
uint16_t nb_answers = (start_answer_code[0] << 8) + start_answer_code[1];
|
|
|
|
nb_answers++;
|
|
|
|
start_answer_code[0] = (uint8_t)(nb_answers >> 8);
|
|
|
|
start_answer_code[1] = (uint8_t)(nb_answers&0xFF);
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
/* Update the content length and reallocate memory if needed */
|
2017-02-13 22:28:46 -06:00
|
|
|
header->content_len += record_length;
|
|
|
|
if (header->content_len > header->allocated_length)
|
|
|
|
{
|
|
|
|
/* realloc to a new length, */
|
|
|
|
do {
|
|
|
|
header->allocated_length = mdns_util_suggest_size(header->content_len);
|
|
|
|
} while (header->content_len > header->allocated_length);
|
|
|
|
|
|
|
|
*new_record = GETDNS_XREALLOC(context->mf, old_record, uint8_t, header->allocated_length);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*new_record = old_record;
|
|
|
|
}
|
2017-01-20 21:44:05 -06:00
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
if (*new_record == NULL)
|
2017-01-20 21:44:05 -06:00
|
|
|
{
|
2017-02-13 22:28:46 -06:00
|
|
|
ret = GETDNS_RETURN_MEMORY_ERROR;
|
2017-01-20 21:44:05 -06:00
|
|
|
}
|
2017-02-13 22:28:46 -06:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* copy the record */
|
|
|
|
/* First, point name relative to beginning of DNS message */
|
|
|
|
(*new_record)[current_length++] = 0xC0;
|
2017-03-03 18:52:02 -06:00
|
|
|
(*new_record)[current_length++] = 12;
|
2017-02-13 22:28:46 -06:00
|
|
|
/* encode the components of the per record header */
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((record_type >> 8) & 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((record_type)& 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((record_class >> 8) & 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((record_class)& 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((ttl >> 24) & 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((ttl >> 16) & 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((ttl >> 8) & 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((ttl)& 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((record_data_len >> 8) & 0xFF);
|
|
|
|
(*new_record)[current_length++] = (uint8_t)((record_data_len) & 0xFF);
|
|
|
|
memcpy(*new_record + current_length, record_data, record_data_len);
|
2017-03-03 18:52:02 -06:00
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
}
|
|
|
|
|
2017-01-20 21:44:05 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
static int
|
|
|
|
mdns_update_cache_ttl_and_prune(struct getdns_context *context,
|
|
|
|
uint8_t * old_record, uint8_t ** new_record,
|
|
|
|
int record_type, int record_class, int ttl,
|
|
|
|
uint8_t * record_data, int record_data_len,
|
|
|
|
uint64_t current_time)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Compute the TTL delta
|
|
|
|
*/
|
|
|
|
int ret = 0;
|
|
|
|
getdns_mdns_cached_record_header *header = (getdns_mdns_cached_record_header*)old_record;
|
|
|
|
uint32_t delta_t_sec = (uint32_t)((current_time - header->insertion_microsec) / 1000000ll);
|
|
|
|
header->insertion_microsec += delta_t_sec * 1000000;
|
|
|
|
int message_index;
|
|
|
|
int answer_index;
|
|
|
|
int nb_answers;
|
|
|
|
int nb_answers_left;
|
|
|
|
int current_record_length;
|
|
|
|
int current_record_data_len;
|
|
|
|
uint32_t current_record_ttl;
|
|
|
|
int not_matched_yet = (record_data == NULL) ? 0 : 1;
|
|
|
|
int last_copied_index;
|
2017-02-23 19:55:31 -06:00
|
|
|
int current_hole_index = 0;
|
2017-03-03 18:52:02 -06:00
|
|
|
int record_name_length = 0;
|
|
|
|
int record_ttl_index = 0;
|
2017-02-13 22:28:46 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip the query
|
|
|
|
*/
|
|
|
|
message_index = sizeof(getdns_mdns_cached_record_header);
|
2017-03-03 18:52:02 -06:00
|
|
|
nb_answers = (old_record[message_index + MDNS_PACKET_INDEX_ANSWER] << 8) |
|
|
|
|
old_record[message_index + MDNS_PACKET_INDEX_ANSWER + 1];
|
2017-02-13 22:28:46 -06:00
|
|
|
nb_answers_left = nb_answers;
|
2017-03-03 18:52:02 -06:00
|
|
|
answer_index = message_index + 12 + mdns_util_skip_query(old_record + message_index + 12);
|
2017-02-13 22:28:46 -06:00
|
|
|
last_copied_index = answer_index;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Examine each record
|
|
|
|
*/
|
|
|
|
for (int i = 0; i < nb_answers; i++)
|
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
record_name_length = mdns_util_skip_name(old_record + answer_index);
|
|
|
|
record_ttl_index = answer_index + record_name_length + 2 + 2;
|
2017-02-13 22:28:46 -06:00
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
current_record_ttl = (old_record[record_ttl_index] << 24)
|
|
|
|
| (old_record[record_ttl_index + 1] << 16)
|
|
|
|
| (old_record[record_ttl_index + 2] << 8)
|
|
|
|
| (old_record[record_ttl_index + 3]);
|
2017-02-13 22:28:46 -06:00
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
current_record_data_len = (old_record[record_ttl_index + 4] << 8)
|
|
|
|
| (old_record[record_ttl_index + 5]);
|
|
|
|
|
|
|
|
current_record_length = record_name_length + 2 + 2 + 4 + 2 + current_record_data_len;
|
2017-02-13 22:28:46 -06:00
|
|
|
|
|
|
|
if (not_matched_yet &&
|
|
|
|
current_record_data_len == record_data_len &&
|
2017-03-03 18:52:02 -06:00
|
|
|
memcmp(old_record + record_ttl_index + 4 + 2, record_data, record_data_len) == 0)
|
2017-02-13 22:28:46 -06:00
|
|
|
{
|
|
|
|
not_matched_yet = 0;
|
|
|
|
current_record_ttl = ttl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Not a match */
|
|
|
|
if (current_record_ttl > delta_t_sec)
|
|
|
|
{
|
|
|
|
current_record_ttl -= delta_t_sec;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
current_record_ttl = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
if (current_record_ttl == 0)
|
2017-02-13 22:28:46 -06:00
|
|
|
{
|
|
|
|
nb_answers_left--;
|
|
|
|
|
|
|
|
/* this record should be compacted away */
|
|
|
|
if (current_hole_index == 0)
|
|
|
|
{
|
|
|
|
/* encountering the first hole in the message,
|
|
|
|
* no need to copy anything yet.
|
|
|
|
*/
|
|
|
|
last_copied_index = answer_index;
|
|
|
|
}
|
|
|
|
else if (current_hole_index != answer_index)
|
|
|
|
{
|
|
|
|
/* copy the data from hole to answer */
|
|
|
|
memmove(old_record + last_copied_index, old_record + current_hole_index,
|
|
|
|
answer_index - current_hole_index);
|
|
|
|
last_copied_index += answer_index - current_hole_index;
|
|
|
|
}
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
/* extend the current hole */
|
2017-02-13 22:28:46 -06:00
|
|
|
current_hole_index = answer_index + current_record_length;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* keeping this record, but updating the TTL */
|
2017-03-03 18:52:02 -06:00
|
|
|
old_record[record_ttl_index] = (uint8_t)(current_record_ttl >> 24);
|
|
|
|
old_record[record_ttl_index + 1] = (uint8_t)(current_record_ttl >> 16);
|
|
|
|
old_record[record_ttl_index + 2] = (uint8_t)(current_record_ttl >> 8);
|
|
|
|
old_record[record_ttl_index + 3] = (uint8_t)(current_record_ttl);
|
2017-02-13 22:28:46 -06:00
|
|
|
}
|
|
|
|
/* progress to the next record */
|
|
|
|
answer_index += current_record_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if necessary, copy the pending data */
|
2017-03-03 18:52:02 -06:00
|
|
|
if (current_hole_index != answer_index && current_hole_index != 0)
|
2017-02-13 22:28:46 -06:00
|
|
|
{
|
|
|
|
/* copy the data from hole to last answer */
|
|
|
|
memmove(old_record + last_copied_index, old_record + current_hole_index,
|
|
|
|
answer_index - current_hole_index);
|
|
|
|
last_copied_index += answer_index - current_hole_index;
|
2017-03-03 18:52:02 -06:00
|
|
|
answer_index = last_copied_index;
|
2017-02-13 22:28:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if some records were deleted, update the record headers */
|
|
|
|
if (nb_answers != nb_answers_left)
|
|
|
|
{
|
|
|
|
header->content_len = last_copied_index;
|
2017-03-03 18:52:02 -06:00
|
|
|
old_record[message_index + MDNS_PACKET_INDEX_ANSWER] = (uint8_t)(nb_answers_left >> 8);
|
|
|
|
old_record[message_index + MDNS_PACKET_INDEX_ANSWER + 1] = (uint8_t)(nb_answers_left);
|
2017-02-13 22:28:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if the update was never seen, ask for an addition
|
|
|
|
*/
|
2017-03-03 18:52:02 -06:00
|
|
|
if (ttl > 0 && not_matched_yet)
|
2017-02-13 22:28:46 -06:00
|
|
|
{
|
|
|
|
mdns_add_record_to_cache_entry(context, old_record, new_record,
|
|
|
|
record_type, record_class, ttl, record_data, record_data_len);
|
|
|
|
nb_answers_left++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*new_record = old_record;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
/*
|
|
|
|
* Get a cached entry by name and record type .
|
|
|
|
*/
|
|
|
|
static struct lruhash_entry *
|
|
|
|
mdns_access_cached_entry_by_name(
|
|
|
|
struct getdns_context *context,
|
|
|
|
uint8_t * name, int name_len,
|
|
|
|
int record_type, int record_class)
|
2017-02-23 19:55:31 -06:00
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
uint8_t temp_key[256 + sizeof(getdns_mdns_cached_key_header)];
|
|
|
|
hashvalue_type hash;
|
|
|
|
struct lruhash_entry *entry;
|
2017-02-23 19:55:31 -06:00
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
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);
|
|
|
|
|
|
|
|
return entry;
|
2017-02-23 19:55:31 -06:00
|
|
|
}
|
2017-02-13 22:28:46 -06:00
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
/*
|
|
|
|
* Add entry function for the MDNS record cache.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
mdns_propose_entry_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,
|
2017-02-14 15:30:29 -06:00
|
|
|
getdns_network_req * netreq,
|
2017-02-13 22:28:46 -06:00
|
|
|
uint64_t current_time)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
uint8_t temp_key[256 + sizeof(getdns_mdns_cached_key_header)];
|
|
|
|
hashvalue_type hash;
|
|
|
|
struct lruhash_entry *entry, *new_entry;
|
|
|
|
uint8_t *key, *data;
|
2017-02-14 15:30:29 -06:00
|
|
|
getdns_mdns_cached_record_header * header;
|
2017-02-13 22:28:46 -06:00
|
|
|
|
|
|
|
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 && ttl != 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Create an empty entry.
|
|
|
|
*/
|
|
|
|
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,
|
|
|
|
record_data_len, current_time, context);
|
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
if (key == NULL || data == NULL)
|
2017-02-13 22:28:46 -06:00
|
|
|
{
|
|
|
|
if (key != NULL)
|
|
|
|
{
|
|
|
|
GETDNS_FREE(context->mf, key);
|
|
|
|
key = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data != NULL)
|
|
|
|
{
|
|
|
|
GETDNS_FREE(context->mf, data);
|
|
|
|
data = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-23 19:55:31 -06:00
|
|
|
new_entry = &((getdns_mdns_cached_key_header*)key)->entry;
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
memset(new_entry, 0, sizeof(struct lruhash_entry));
|
2017-03-09 07:36:20 -06:00
|
|
|
lock_rw_init(&new_entry->lock);
|
2017-02-13 22:28:46 -06:00
|
|
|
new_entry->hash = hash;
|
|
|
|
new_entry->key = key;
|
|
|
|
new_entry->data = data;
|
|
|
|
|
|
|
|
entry = lruhash_insert_or_retrieve(context->mdns_cache, hash, new_entry, data, NULL);
|
|
|
|
|
|
|
|
if (entry != new_entry)
|
|
|
|
{
|
2017-02-23 19:55:31 -06:00
|
|
|
/* There was already an entry for this name, which is really weird.
|
|
|
|
* But it can in theory happen in a race condition.
|
|
|
|
*/
|
|
|
|
GETDNS_FREE(context->mf, key);
|
|
|
|
key = NULL;
|
|
|
|
GETDNS_FREE(context->mf, data);
|
|
|
|
data = NULL;
|
2017-02-13 22:28:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry != NULL)
|
|
|
|
{
|
2017-02-14 15:30:29 -06:00
|
|
|
if (record_data != NULL && record_data_len > 0)
|
|
|
|
ret = mdns_update_cache_ttl_and_prune(context,
|
|
|
|
(uint8_t*)entry->data, &data,
|
|
|
|
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;
|
|
|
|
}
|
2017-03-03 18:52:02 -06:00
|
|
|
else
|
2017-02-23 19:55:31 -06:00
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
header = (getdns_mdns_cached_record_header *)entry->data;
|
|
|
|
|
|
|
|
/* 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 &&
|
|
|
|
header->netreq_first == NULL)
|
|
|
|
{
|
|
|
|
lru_demote(context->mdns_cache, entry);
|
|
|
|
}
|
2017-02-23 19:55:31 -06:00
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
/* then, unlock the entry */
|
2017-03-09 07:36:20 -06:00
|
|
|
lock_rw_unlock(&entry->lock);
|
2017-02-13 22:28:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
|
2017-01-20 21:44:05 -06:00
|
|
|
/*
|
2017-03-03 18:52:02 -06:00
|
|
|
* Serve a request from the cached value
|
2017-01-20 21:44:05 -06:00
|
|
|
*/
|
2017-02-23 19:55:31 -06:00
|
|
|
static int
|
2017-03-03 18:52:02 -06:00
|
|
|
mdns_complete_query_from_cache_entry(
|
|
|
|
getdns_network_req *netreq,
|
|
|
|
struct lruhash_entry *entry)
|
2017-01-20 21:44:05 -06:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2017-03-03 18:52:02 -06:00
|
|
|
uint8_t *packet = ((uint8_t *)entry->data) + sizeof(getdns_mdns_cached_record_header);
|
|
|
|
getdns_mdns_cached_record_header * header = (getdns_mdns_cached_record_header*)entry->data;
|
|
|
|
size_t packet_length = header->content_len - sizeof(getdns_mdns_cached_record_header);
|
|
|
|
getdns_network_req **prev_netreq;
|
|
|
|
int found = 0;
|
|
|
|
int nb_answers = mdns_cache_nb_records_in_entry((uint8_t *)entry->data);
|
|
|
|
|
|
|
|
/* Clear the event associated to the query */
|
|
|
|
GETDNS_CLEAR_EVENT(netreq->owner->loop, &netreq->event);
|
|
|
|
|
|
|
|
/* remove the completed query from the waiting list */
|
|
|
|
prev_netreq = &header->netreq_first;
|
|
|
|
while (*prev_netreq != NULL)
|
2017-01-20 21:44:05 -06:00
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
if (*prev_netreq == netreq)
|
2017-02-23 19:55:31 -06:00
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
*prev_netreq = netreq->mdns_netreq_next;
|
2017-02-23 19:55:31 -06:00
|
|
|
netreq->mdns_netreq_next = NULL;
|
2017-03-03 18:52:02 -06:00
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prev_netreq = &((*prev_netreq)->mdns_netreq_next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
{
|
|
|
|
if (nb_answers == 0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* copy the returned value in the response field */
|
2017-02-23 19:55:31 -06:00
|
|
|
if (packet_length > netreq->wire_data_sz)
|
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
netreq->response = GETDNS_XREALLOC(
|
|
|
|
netreq->owner->context->mf, netreq->response, uint8_t, packet_length);
|
2017-02-23 19:55:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (netreq->response != NULL)
|
|
|
|
{
|
|
|
|
memcpy(netreq->response, packet, packet_length);
|
2017-03-03 18:52:02 -06:00
|
|
|
|
|
|
|
netreq->response[MDNS_PACKET_INDEX_QCODE] = 0x84;
|
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2017-03-03 18:52:02 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Failure */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Processing of requests after cache update.
|
|
|
|
* 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_cache_complete_queries(
|
|
|
|
struct getdns_context *context,
|
|
|
|
uint8_t * name, int name_len,
|
|
|
|
int record_type, int record_class)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct lruhash_entry *entry;
|
|
|
|
getdns_mdns_cached_record_header * header;
|
|
|
|
getdns_network_req * netreq;
|
|
|
|
|
|
|
|
entry = mdns_access_cached_entry_by_name(context, name, name_len, record_type, record_class);
|
|
|
|
|
|
|
|
if (entry != NULL)
|
|
|
|
{
|
|
|
|
if (entry->data != NULL)
|
|
|
|
{
|
|
|
|
header = (getdns_mdns_cached_record_header *)entry->data;
|
|
|
|
|
|
|
|
while ((netreq = header->netreq_first) != NULL)
|
|
|
|
{
|
|
|
|
mdns_complete_query_from_cache_entry(netreq, entry);
|
|
|
|
}
|
|
|
|
}
|
2017-03-09 07:36:20 -06:00
|
|
|
lock_rw_unlock(&entry->lock);
|
2017-01-20 21:44:05 -06:00
|
|
|
}
|
2017-02-23 19:55:31 -06:00
|
|
|
|
2017-01-20 21:44:05 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
/*
|
|
|
|
* Timeout of multicast MDNS query
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
mdns_mcast_timeout_cb(void *userarg)
|
|
|
|
{
|
|
|
|
getdns_network_req *netreq = (getdns_network_req *)userarg;
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
|
|
|
getdns_context *context = dnsreq->context;
|
|
|
|
|
|
|
|
uint8_t temp_key[256 + sizeof(getdns_mdns_cached_key_header)];
|
|
|
|
hashvalue_type hash;
|
|
|
|
struct lruhash_entry *entry;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
DEBUG_MDNS("%s %-35s: MSG: %p\n",
|
|
|
|
MDNS_DEBUG_CLEANUP, __FUNCTION__, netreq);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* Open the corresponding cache entry */
|
|
|
|
entry = lruhash_lookup(context->mdns_cache, hash, temp_key, 1);
|
|
|
|
|
|
|
|
if (entry != NULL)
|
|
|
|
{
|
|
|
|
if (entry->data != NULL)
|
|
|
|
{
|
|
|
|
/* Remove entry from chain and serve the query */
|
|
|
|
found = 1;
|
|
|
|
mdns_complete_query_from_cache_entry(netreq, entry);
|
|
|
|
}
|
2017-03-09 07:36:20 -06:00
|
|
|
lock_rw_unlock(&entry->lock);
|
2017-03-03 18:52:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
/* Fail the request on timeout */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
/*
|
|
|
|
* Multicast receive event callback
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
mdns_udp_multicast_read_cb(void *userarg)
|
|
|
|
{
|
|
|
|
mdns_network_connection * cnx = (mdns_network_connection *)userarg;
|
2017-02-23 19:55:31 -06:00
|
|
|
uint64_t current_time;
|
2017-02-13 22:28:46 -06:00
|
|
|
ssize_t read;
|
|
|
|
DEBUG_MDNS("%s %-35s: CTX: %p, NET=%d \n", MDNS_DEBUG_MREAD,
|
|
|
|
__FUNCTION__, cnx->context, cnx->addr_mcast.ss_family);
|
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
current_time = _getdns_get_time_as_uintt64();
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
GETDNS_CLEAR_EVENT(
|
|
|
|
cnx->context->extension, &cnx->event);
|
|
|
|
|
|
|
|
read = recvfrom(cnx->fd, (void *)cnx->response,
|
|
|
|
sizeof(cnx->response), 0, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
if (read == -1 && _getdns_EWOULDBLOCK)
|
|
|
|
return; /* TODO: this will stop the receive loop! */
|
|
|
|
|
|
|
|
if (read >= GLDNS_HEADER_SIZE)
|
|
|
|
{
|
|
|
|
/* parse the response, find the relevant queries, submit the records to the cache */
|
2017-02-23 19:55:31 -06:00
|
|
|
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];
|
2017-02-13 22:28:46 -06:00
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
if (opcodeAndflags != 0x84)
|
|
|
|
{
|
|
|
|
/* this is not an MDNS answer packet. */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-09 07:36:20 -06:00
|
|
|
ssize_t current_index = 12;
|
2017-02-23 19:55:31 -06:00
|
|
|
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++]);
|
2017-03-03 18:52:02 -06:00
|
|
|
/* TODO: handle the cache flush bit! */
|
|
|
|
record_class = (cnx->response[current_index++] << 8)&0x7F;
|
2017-02-23 19:55:31 -06:00
|
|
|
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++]);
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
if (current_index + record_data_len <= read)
|
2017-02-23 19:55:31 -06:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-13 22:28:46 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* bogus packet.. Should log. */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Relaunch the event, so we can go read the next packet.
|
|
|
|
*/
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
cnx->context->extension, cnx->fd, 0,
|
|
|
|
getdns_eventloop_event_init(&cnx->event, cnx,
|
|
|
|
mdns_udp_multicast_read_cb, NULL, NULL));
|
|
|
|
}
|
2017-02-06 22:23:35 -06:00
|
|
|
|
|
|
|
/*
|
2017-02-13 22:28:46 -06:00
|
|
|
* Create the two required multicast sockets
|
|
|
|
*/
|
|
|
|
static int mdns_open_ipv4_multicast(SOCKADDR_STORAGE* mcast_dest, int* mcast_dest_len)
|
2017-02-06 22:23:35 -06:00
|
|
|
{
|
|
|
|
getdns_return_t ret = 0;
|
|
|
|
SOCKET fd4 = -1;
|
|
|
|
SOCKADDR_IN ipv4_dest;
|
|
|
|
SOCKADDR_IN ipv4_port;
|
2017-03-09 07:36:20 -06:00
|
|
|
BOOL so_reuse_bool = TRUE;
|
|
|
|
int so_reuse_bool_OptLen = sizeof(BOOL);
|
2017-02-06 22:23:35 -06:00
|
|
|
uint8_t ttl = 255;
|
|
|
|
IP_MREQ mreq4;
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
memset(mcast_dest, 0, sizeof(SOCKADDR_STORAGE));
|
2017-02-13 22:28:46 -06:00
|
|
|
*mcast_dest_len = 0;
|
2017-02-06 22:23:35 -06:00
|
|
|
memset(&ipv4_dest, 0, sizeof(ipv4_dest));
|
|
|
|
memset(&ipv4_port, 0, sizeof(ipv4_dest));
|
|
|
|
ipv4_dest.sin_family = AF_INET;
|
|
|
|
ipv4_dest.sin_port = htons(MDNS_MCAST_PORT);
|
2017-03-09 07:36:20 -06:00
|
|
|
ipv4_dest.sin_addr.s_addr = htonl(MDNS_MCAST_IPV4_LONG);
|
2017-02-06 22:23:35 -06:00
|
|
|
ipv4_port.sin_family = AF_INET;
|
|
|
|
ipv4_port.sin_port = htons(MDNS_MCAST_PORT);
|
2017-03-09 07:36:20 -06:00
|
|
|
/* memcpy(&ipv4_dest.sin_addr, mdns_mcast_ipv4, sizeof(mdns_mcast_ipv4)); */
|
|
|
|
|
2017-02-06 22:23:35 -06:00
|
|
|
|
|
|
|
|
|
|
|
fd4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
|
|
|
|
if (fd4 != -1)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* No need to test the output of the so_reuse call,
|
|
|
|
* since the only result that matters is that of bind.
|
|
|
|
*/
|
|
|
|
(void)setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR
|
2017-03-09 07:36:20 -06:00
|
|
|
, (const char*)&so_reuse_bool, so_reuse_bool_OptLen);
|
2017-02-06 22:23:35 -06:00
|
|
|
|
|
|
|
if (bind(fd4, (SOCKADDR*)&ipv4_port, sizeof(ipv4_port)) != 0)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mreq4.imr_multiaddr = ipv4_dest.sin_addr;
|
|
|
|
mreq4.imr_interface = ipv4_port.sin_addr;
|
|
|
|
|
|
|
|
if (setsockopt(fd4, IPPROTO_IP, IP_ADD_MEMBERSHIP
|
|
|
|
, (const char*)&mreq4, (int) sizeof(mreq4)) != 0)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
else if (setsockopt(fd4, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) != 0)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != 0 && fd4 != -1)
|
|
|
|
{
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
closesocket(fd4);
|
|
|
|
#else
|
|
|
|
close(fd4);
|
|
|
|
#endif
|
|
|
|
fd4 = -1;
|
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
if (ret == 0)
|
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
memcpy(mcast_dest, &ipv4_dest, sizeof(ipv4_dest));
|
2017-02-13 22:28:46 -06:00
|
|
|
*mcast_dest_len = sizeof(ipv4_dest);
|
|
|
|
}
|
|
|
|
|
2017-02-06 22:23:35 -06:00
|
|
|
return fd4;
|
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
static int mdns_open_ipv6_multicast(SOCKADDR_STORAGE* mcast_dest, int* mcast_dest_len)
|
2017-02-06 22:23:35 -06:00
|
|
|
{
|
|
|
|
getdns_return_t ret = 0;
|
|
|
|
SOCKET fd6 = -1;
|
|
|
|
SOCKADDR_IN6 ipv6_dest;
|
|
|
|
SOCKADDR_IN6 ipv6_port;
|
|
|
|
uint8_t so_reuse_bool = 1;
|
|
|
|
uint8_t ttl = 255;
|
|
|
|
IPV6_MREQ mreq6;
|
|
|
|
|
2017-03-03 18:52:02 -06:00
|
|
|
memset(mcast_dest, 0, sizeof(SOCKADDR_STORAGE));
|
2017-02-13 22:28:46 -06:00
|
|
|
*mcast_dest_len = 0;
|
2017-02-06 22:23:35 -06:00
|
|
|
memset(&ipv6_dest, 0, sizeof(ipv6_dest));
|
|
|
|
memset(&ipv6_port, 0, sizeof(ipv6_dest));
|
|
|
|
ipv6_dest.sin6_family = AF_INET6;
|
|
|
|
ipv6_dest.sin6_port = htons(MDNS_MCAST_PORT);
|
|
|
|
ipv6_port.sin6_family = AF_INET6;
|
|
|
|
ipv6_port.sin6_port = htons(MDNS_MCAST_PORT);
|
|
|
|
memcpy(&ipv6_dest.sin6_addr
|
|
|
|
, mdns_mcast_ipv6, sizeof(mdns_mcast_ipv6));
|
|
|
|
|
|
|
|
|
|
|
|
fd6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
|
|
|
|
if (fd6 != -1)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* No need to test the output of the so_reuse call,
|
|
|
|
* since the only result that matters is that of bind.
|
|
|
|
*/
|
|
|
|
(void)setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR
|
|
|
|
, (const char*)&so_reuse_bool, (int) sizeof(BOOL));
|
|
|
|
|
|
|
|
if (bind(fd6, (SOCKADDR*)&ipv6_port, sizeof(ipv6_port)) != 0)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy(&mreq6.ipv6mr_multiaddr
|
|
|
|
, &ipv6_dest.sin6_addr, sizeof(mreq6.ipv6mr_multiaddr));
|
|
|
|
memcpy(&mreq6.ipv6mr_interface
|
|
|
|
, &ipv6_port.sin6_addr, sizeof(mreq6.ipv6mr_interface));
|
|
|
|
|
|
|
|
if (setsockopt(fd6, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP
|
|
|
|
, (const char*)&mreq6, (int) sizeof(mreq6)) != 0)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
else if (setsockopt(fd6, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) != 0)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != 0 && fd6 != -1)
|
|
|
|
{
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
closesocket(fd6);
|
|
|
|
#else
|
|
|
|
close(fd6);
|
|
|
|
#endif
|
|
|
|
fd6 = -1;
|
|
|
|
}
|
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
if (ret == 0)
|
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
memcpy(mcast_dest, &ipv6_dest, sizeof(ipv6_dest));
|
2017-02-13 22:28:46 -06:00
|
|
|
*mcast_dest_len = sizeof(ipv6_dest);
|
|
|
|
}
|
2017-02-06 22:23:35 -06:00
|
|
|
return fd6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delayed opening of the MDNS sockets, and launch of the MDNS listeners
|
|
|
|
*/
|
|
|
|
static getdns_return_t mdns_delayed_network_init(struct getdns_context *context)
|
|
|
|
{
|
|
|
|
getdns_return_t ret = 0;
|
|
|
|
|
|
|
|
if (context->mdns_extended_support == 2)
|
|
|
|
{
|
2017-02-14 15:30:29 -06:00
|
|
|
context->mdns_cache = lruhash_create(128, 10000000,
|
2017-02-13 22:28:46 -06:00
|
|
|
mdns_cache_entry_size, mdns_cache_key_comp,
|
|
|
|
msdn_cache_delkey, msdn_cache_deldata,
|
|
|
|
context);
|
2017-02-06 22:23:35 -06:00
|
|
|
|
2017-02-13 22:28:46 -06:00
|
|
|
if (context->mdns_cache == NULL)
|
2017-02-06 22:23:35 -06:00
|
|
|
{
|
2017-02-13 22:28:46 -06:00
|
|
|
ret = GETDNS_RETURN_MEMORY_ERROR;
|
2017-02-06 22:23:35 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-13 22:28:46 -06:00
|
|
|
context->mdns_connection = (mdns_network_connection *)
|
|
|
|
GETDNS_XMALLOC(context->my_mf, mdns_network_connection, 2);
|
|
|
|
|
|
|
|
if (context->mdns_connection == NULL)
|
|
|
|
{
|
|
|
|
ret = GETDNS_RETURN_MEMORY_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
context->mdns_connection_nb = 2;
|
|
|
|
|
|
|
|
context->mdns_connection[0].fd = mdns_open_ipv4_multicast(
|
|
|
|
&context->mdns_connection[0].addr_mcast
|
|
|
|
, &context->mdns_connection[0].addr_mcast_len);
|
2017-03-03 18:52:02 -06:00
|
|
|
context->mdns_connection[0].context = context;
|
2017-02-13 22:28:46 -06:00
|
|
|
context->mdns_connection[1].fd = mdns_open_ipv6_multicast(
|
2017-03-03 18:52:02 -06:00
|
|
|
&context->mdns_connection[1].addr_mcast
|
|
|
|
, &context->mdns_connection[1].addr_mcast_len);
|
|
|
|
context->mdns_connection[1].context = context;
|
2017-02-13 22:28:46 -06:00
|
|
|
|
|
|
|
if (context->mdns_connection[0].fd == -1 ||
|
|
|
|
context->mdns_connection[1].fd == -1)
|
|
|
|
{
|
|
|
|
ret = GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* TODO: launch the receive loops */
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
GETDNS_CLEAR_EVENT(context->extension, &context->mdns_connection[i].event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
context->extension, context->mdns_connection[i].fd, 0,
|
|
|
|
getdns_eventloop_event_init(&context->mdns_connection[i].event,
|
|
|
|
&context->mdns_connection[i],
|
|
|
|
mdns_udp_multicast_read_cb, NULL, NULL));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != 0)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
if (context->mdns_connection[i].fd != -1)
|
|
|
|
{
|
|
|
|
|
|
|
|
GETDNS_CLEAR_EVENT(context->extension
|
|
|
|
, &context->mdns_connection[i].event);
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
closesocket(context->mdns_connection[i].fd);
|
|
|
|
#else
|
|
|
|
close(context->mdns_connection[i].fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GETDNS_FREE(context->my_mf, context->mdns_connection);
|
|
|
|
context->mdns_connection = NULL;
|
|
|
|
context->mdns_connection_nb = 0;
|
|
|
|
}
|
|
|
|
} /* mdns-connection != NULL */
|
|
|
|
|
|
|
|
if (ret != 0)
|
|
|
|
{
|
|
|
|
/* delete the cache that was just created, since the network connection failed */
|
|
|
|
lruhash_delete(context->mdns_cache);
|
|
|
|
context->mdns_cache = NULL;
|
|
|
|
}
|
|
|
|
} /* cache != NULL */
|
|
|
|
|
|
|
|
context->mdns_extended_support = (ret == 0) ? 1 : 0;
|
2017-02-06 22:23:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-20 21:44:05 -06:00
|
|
|
/*
|
|
|
|
* Initialize a continuous query from netreq
|
|
|
|
*/
|
2017-02-06 22:23:35 -06:00
|
|
|
static getdns_return_t mdns_initialize_continuous_request(getdns_network_req *netreq)
|
2017-01-20 21:44:05 -06:00
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
getdns_return_t ret = 0;
|
2017-01-20 21:44:05 -06:00
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
2017-02-06 22:23:35 -06:00
|
|
|
struct getdns_context *context = dnsreq->context;
|
2017-02-13 22:28:46 -06:00
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
uint8_t temp_key[256 + sizeof(getdns_mdns_cached_key_header)];
|
|
|
|
hashvalue_type hash;
|
|
|
|
struct lruhash_entry *entry;
|
|
|
|
size_t pkt_len = netreq->response - netreq->query;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2017-03-03 18:52:02 -06:00
|
|
|
/* If the query is not actually complete, are a per query timer. */
|
|
|
|
if (netreq->state < NET_REQ_FINISHED)
|
|
|
|
{
|
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
dnsreq->loop, -1, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
|
|
|
NULL, NULL, mdns_mcast_timeout_cb));
|
|
|
|
}
|
2017-02-23 19:55:31 -06:00
|
|
|
/* 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;
|
2017-03-03 18:52:02 -06:00
|
|
|
int sent = sendto(
|
2017-02-23 19:55:31 -06:00
|
|
|
context->mdns_connection[fd_index].fd
|
|
|
|
, (const void *)netreq->query, pkt_len, 0
|
|
|
|
, (SOCKADDR*)&context->mdns_connection[fd_index].addr_mcast
|
2017-03-03 18:52:02 -06:00
|
|
|
, context->mdns_connection[fd_index].addr_mcast_len);
|
|
|
|
|
2017-03-09 07:36:20 -06:00
|
|
|
if (sent < 0 || pkt_len != (size_t)sent)
|
2017-02-23 19:55:31 -06:00
|
|
|
{
|
|
|
|
ret = GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
}
|
2017-02-06 22:23:35 -06:00
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
/* TODO: update the send query time */
|
|
|
|
}
|
|
|
|
}
|
2017-01-20 21:44:05 -06:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 22:23:35 -06:00
|
|
|
/*
|
|
|
|
* Initialize the MDNS part of the context structure.
|
|
|
|
*/
|
|
|
|
void _getdns_mdns_context_init(struct getdns_context *context)
|
|
|
|
{
|
|
|
|
context->mdns_extended_support = 2; /* 0 = no support, 1 = supported, 2 = initialization needed */
|
2017-02-13 22:28:46 -06:00
|
|
|
context->mdns_connection = NULL;
|
|
|
|
context->mdns_connection_nb = 0;
|
|
|
|
context->mdns_cache = NULL;
|
2017-02-06 22:23:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete all the data allocated for MDNS in a context
|
|
|
|
*/
|
|
|
|
void _getdns_mdns_context_destroy(struct getdns_context *context)
|
|
|
|
{
|
2017-02-14 15:30:29 -06:00
|
|
|
/* 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
|
|
|
|
}
|
2017-02-06 22:23:35 -06:00
|
|
|
|
2017-02-14 15:30:29 -06:00
|
|
|
GETDNS_FREE(context->mf, context->mdns_connection);
|
|
|
|
context->mdns_connection = NULL;
|
|
|
|
context->mdns_connection_nb = 0;
|
|
|
|
}
|
2017-02-06 22:23:35 -06:00
|
|
|
}
|
|
|
|
|
2016-12-22 17:51:47 -06:00
|
|
|
/* TODO: actualy delete what is required.. */
|
|
|
|
static void
|
|
|
|
mdns_cleanup(getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
DEBUG_MDNS("%s %-35s: MSG: %p\n",
|
2017-02-19 02:55:10 -06:00
|
|
|
MDNS_DEBUG_CLEANUP, __FUNC__, netreq);
|
2016-12-22 17:51:47 -06:00
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
|
|
|
|
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_getdns_cancel_mdns_request(getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
mdns_cleanup(netreq);
|
|
|
|
if (netreq->fd >= 0) {
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
closesocket(netreq->fd);
|
|
|
|
#else
|
|
|
|
close(netreq->fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mdns_timeout_cb(void *userarg)
|
|
|
|
{
|
|
|
|
getdns_network_req *netreq = (getdns_network_req *)userarg;
|
|
|
|
DEBUG_MDNS("%s %-35s: MSG: %p\n",
|
2017-02-19 02:55:10 -06:00
|
|
|
MDNS_DEBUG_CLEANUP, __FUNC__, netreq);
|
2016-12-22 17:51:47 -06:00
|
|
|
|
|
|
|
/* TODO: do we need a retry logic here? */
|
|
|
|
|
|
|
|
/* Check the required cleanup */
|
|
|
|
mdns_cleanup(netreq);
|
|
|
|
if (netreq->fd >= 0)
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
closesocket(netreq->fd);
|
|
|
|
#else
|
|
|
|
close(netreq->fd);
|
|
|
|
#endif
|
2017-03-14 11:17:56 -05:00
|
|
|
_getdns_netreq_change_state(netreq, NET_REQ_TIMED_OUT);
|
2016-12-22 17:51:47 -06:00
|
|
|
if (netreq->owner->user_callback) {
|
|
|
|
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
|
|
|
(void)_getdns_context_request_timed_out(netreq->owner);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_getdns_check_dns_req_complete(netreq->owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
/*****************************************/
|
|
|
|
/* UDP callback functions for basic MDNS */
|
|
|
|
/*****************************************/
|
2016-12-22 17:51:47 -06:00
|
|
|
|
|
|
|
static void
|
|
|
|
mdns_udp_read_cb(void *userarg)
|
|
|
|
{
|
|
|
|
getdns_network_req *netreq = (getdns_network_req *)userarg;
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
|
|
|
ssize_t read;
|
|
|
|
DEBUG_MDNS("%s %-35s: MSG: %p \n", MDNS_DEBUG_READ,
|
2017-02-19 02:55:10 -06:00
|
|
|
__FUNC__, netreq);
|
2016-12-22 17:51:47 -06:00
|
|
|
|
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
|
|
|
|
|
|
|
read = recvfrom(netreq->fd, (void *)netreq->response,
|
|
|
|
netreq->max_udp_payload_size + 1, /* If read == max_udp_payload_size
|
|
|
|
* then all is good. If read ==
|
|
|
|
* max_udp_payload_size + 1, then
|
|
|
|
* we receive more then requested!
|
|
|
|
* i.e. overflow
|
|
|
|
*/
|
|
|
|
0, NULL, NULL);
|
|
|
|
if (read == -1 && _getdns_EWOULDBLOCK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (read < GLDNS_HEADER_SIZE)
|
|
|
|
return; /* Not DNS */
|
|
|
|
|
|
|
|
if (GLDNS_ID_WIRE(netreq->response) != netreq->query_id)
|
|
|
|
return; /* Cache poisoning attempt ;) */
|
|
|
|
|
|
|
|
// TODO: check whether EDNS server cookies are required for MDNS
|
|
|
|
|
|
|
|
// TODO: check that the source address originates from the local network.
|
|
|
|
// TODO: check TTL = 255
|
|
|
|
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
closesocket(netreq->fd);
|
|
|
|
#else
|
|
|
|
close(netreq->fd);
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* TODO: how to handle an MDNS response with TC bit set?
|
|
|
|
* Ignore it for now, as we do not support any kind of TCP fallback
|
|
|
|
* for basic MDNS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
netreq->response_len = read;
|
|
|
|
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
2017-03-14 11:17:56 -05:00
|
|
|
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
|
2016-12-22 17:51:47 -06:00
|
|
|
_getdns_check_dns_req_complete(dnsreq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mdns_udp_write_cb(void *userarg)
|
|
|
|
{
|
|
|
|
getdns_network_req *netreq = (getdns_network_req *)userarg;
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
|
|
|
size_t pkt_len = netreq->response - netreq->query;
|
|
|
|
struct sockaddr_in mdns_mcast_v4;
|
|
|
|
int ttl = 255;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
DEBUG_MDNS("%s %-35s: MSG: %p \n", MDNS_DEBUG_WRITE,
|
2017-02-19 02:55:10 -06:00
|
|
|
__FUNC__, netreq);
|
2016-12-22 17:51:47 -06:00
|
|
|
|
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
|
|
|
|
|
|
|
netreq->debug_start_time = _getdns_get_time_as_uintt64();
|
|
|
|
netreq->debug_udp = 1;
|
|
|
|
netreq->query_id = (uint16_t) arc4random();
|
|
|
|
GLDNS_ID_SET(netreq->query, netreq->query_id);
|
|
|
|
|
|
|
|
/* do we need to handle options valid in the MDNS context? */
|
|
|
|
|
|
|
|
/* Probably no need for TSIG in MDNS */
|
|
|
|
|
|
|
|
|
|
|
|
/* Always use multicast address */
|
|
|
|
mdns_mcast_v4.sin_family = AF_INET;
|
|
|
|
mdns_mcast_v4.sin_port = htons(MDNS_MCAST_PORT);
|
|
|
|
mdns_mcast_v4.sin_addr.s_addr = htonl(MDNS_MCAST_IPV4_LONG);
|
2017-03-09 07:36:20 -06:00
|
|
|
/* memcpy(&mdns_mcast_v4.sin_addr.s_addr, mdns_mcast_ipv4, sizeof(mdns_mcast_ipv4)); */
|
2016-12-22 17:51:47 -06:00
|
|
|
|
|
|
|
/* Set TTL=255 for compliance with RFC 6762 */
|
|
|
|
r = setsockopt(netreq->fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));
|
|
|
|
|
|
|
|
if (r != 0 ||
|
|
|
|
(ssize_t)pkt_len != sendto(
|
|
|
|
netreq->fd, (const void *)netreq->query, pkt_len, 0,
|
|
|
|
(struct sockaddr *)&mdns_mcast_v4,
|
|
|
|
sizeof(mdns_mcast_v4))) {
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
closesocket(netreq->fd);
|
|
|
|
#else
|
|
|
|
close(netreq->fd);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
|
|
|
mdns_udp_read_cb, NULL, mdns_timeout_cb));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MDNS Request Submission
|
|
|
|
*/
|
|
|
|
|
|
|
|
getdns_return_t
|
|
|
|
_getdns_submit_mdns_request(getdns_network_req *netreq)
|
|
|
|
{
|
2017-02-19 02:55:10 -06:00
|
|
|
DEBUG_MDNS("%s %-35s: MSG: %p TYPE: %d\n", MDNS_DEBUG_ENTRY, __FUNC__,
|
2016-12-22 17:51:47 -06:00
|
|
|
netreq, netreq->request_type);
|
|
|
|
int fd = -1;
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
2017-02-23 19:55:31 -06:00
|
|
|
struct getdns_context * context = dnsreq->context;
|
|
|
|
getdns_return_t ret = 0;
|
2016-12-22 17:51:47 -06:00
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
/*
|
|
|
|
* TO DO: depending on context type, perform basic processing versus full MDNS
|
|
|
|
*/
|
2016-12-22 17:51:47 -06:00
|
|
|
|
2017-02-23 19:55:31 -06:00
|
|
|
if (context->mdns_extended_support == 2)
|
|
|
|
{
|
|
|
|
/* Not initialize yet. Do it know before processing the query */
|
|
|
|
ret = mdns_delayed_network_init(context);
|
|
|
|
|
|
|
|
if (ret != 0)
|
|
|
|
{
|
|
|
|
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;
|
2016-12-22 17:51:47 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MDNS name space management
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
mdns_suffix_compare(register const uint8_t *d1, register const uint8_t *d2)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
uint8_t *d1_head = (uint8_t *) d1;
|
|
|
|
uint8_t *d1_current;
|
|
|
|
uint8_t *d2_current;
|
|
|
|
int is_matching = 0;
|
|
|
|
int part_length;
|
|
|
|
int i;
|
|
|
|
uint8_t c;
|
|
|
|
|
|
|
|
/* Skip the first name part, since we want at least one label before the suffix */
|
|
|
|
if (*d1_head != 0)
|
|
|
|
d1_head += *d1_head + 1;
|
|
|
|
|
|
|
|
while (*d1_head != 0)
|
|
|
|
{
|
|
|
|
/* check whether we have a match at this point */
|
|
|
|
d1_current = d1_head;
|
|
|
|
d2_current = (uint8_t *) d2;
|
|
|
|
is_matching = 0;
|
|
|
|
|
|
|
|
/* compare length and value of all successive labels */
|
|
|
|
while (*d1_current == *d2_current)
|
|
|
|
{
|
|
|
|
part_length = *d1_current;
|
|
|
|
if (part_length == 0)
|
|
|
|
{
|
|
|
|
/* We have reached the top label, there is a match */
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The label's lengths are matching, check the content */
|
|
|
|
is_matching = 1;
|
|
|
|
d1_current++;
|
|
|
|
d2_current++;
|
|
|
|
|
|
|
|
for (i = 0; i < part_length; i++)
|
|
|
|
{
|
|
|
|
c = d1_current[i];
|
|
|
|
if (isupper(c))
|
|
|
|
c = tolower(c);
|
|
|
|
if (c != d2_current[i])
|
|
|
|
{
|
|
|
|
is_matching = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* move the pointers to the next label */
|
|
|
|
if (is_matching)
|
|
|
|
{
|
|
|
|
d1_current += part_length;
|
|
|
|
d2_current += part_length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if no match found yet, move to the next label of d1 */
|
|
|
|
if (is_matching)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
d1_head += *d1_head + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getdns_return_t
|
|
|
|
_getdns_mdns_namespace_check(
|
|
|
|
getdns_dns_req *dnsreq)
|
|
|
|
{
|
|
|
|
getdns_return_t ret = GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
|
|
|
|
/* Checking the prefixes defined in RFC 6762 */
|
|
|
|
if (mdns_suffix_compare(dnsreq->name, mdns_suffix_dot_local) ||
|
|
|
|
mdns_suffix_compare(dnsreq->name, mdns_suffix_254_169_in_addr_arpa) ||
|
|
|
|
mdns_suffix_compare(dnsreq->name, mdns_suffix_8_e_f_ip6_arpa) ||
|
|
|
|
mdns_suffix_compare(dnsreq->name, mdns_suffix_9_e_f_ip6_arpa) ||
|
|
|
|
mdns_suffix_compare(dnsreq->name, mdns_suffix_a_e_f_ip6_arpa) ||
|
|
|
|
mdns_suffix_compare(dnsreq->name, mdns_suffix_b_e_f_ip6_arpa))
|
|
|
|
ret = GETDNS_RETURN_GOOD;
|
|
|
|
|
|
|
|
return ret;
|
2016-12-22 22:30:14 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_MDNS_SUPPORT */
|
2017-03-03 18:52:02 -06:00
|
|
|
|
|
|
|
#ifdef MDNS_UNIT_TEST
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test adding data to the LRU Cache
|
|
|
|
*/
|
|
|
|
|
|
|
|
static BYTE mdns_exampleRRAM[] = {
|
|
|
|
0, 0, /* Transaction ID = 0 */
|
|
|
|
0x84, 0, /* Answer: QR=1 (80), AA=1 (04) */
|
|
|
|
0, 0, /* QD Count = 0 */
|
|
|
|
0, 1, /* AN Count = 1 */
|
|
|
|
0, 0, /* NS Count = 0 */
|
|
|
|
0, 0, /* AD Count = 0 */
|
|
|
|
7, /* length of "example" name part */
|
|
|
|
'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
|
|
|
5, /* length of "local" name part */
|
|
|
|
'l', 'o', 'c', 'a', 'l',
|
|
|
|
0, /* length of the root name part */
|
|
|
|
0, 1, /* QTYPE = 1, A record */
|
|
|
|
0, 1, /* QCLASS = 1, IN */
|
|
|
|
0, 0, 0, 255, /* TTL: 255 sec */
|
|
|
|
0, 4, /* length of RDATA */
|
|
|
|
10, 0, 0, 1 /* Value of RDATA (some IPv4 address) */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t mdns_test_name[] = {
|
|
|
|
7, /* length of "example" name part */
|
|
|
|
't', 'e', 's', 't', 'i', 'n', 'g',
|
|
|
|
5, /* length of "local" name part */
|
|
|
|
'l', 'o', 'c', 'a', 'l',
|
|
|
|
0, /* length of the root name part */
|
|
|
|
};
|
|
|
|
|
|
|
|
uint8_t mdns_test_first_address[4] = { 10, 0, 0, 1 };
|
|
|
|
uint8_t mdns_test_second_address[4] = { 10, 0, 0, 2 };
|
|
|
|
uint8_t mdns_test_third_address[4] = { 10, 0, 0, 3 };
|
|
|
|
|
|
|
|
int mdns_finalize_lru_test(struct getdns_context* context,
|
|
|
|
uint8_t * name, int name_len, int record_type, int record_class,
|
|
|
|
int expected_nb_records,
|
|
|
|
uint8_t * buffer, size_t buffer_max, size_t* entry_length)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
/* verify that the entry is there */
|
|
|
|
struct lruhash_entry * entry =
|
|
|
|
mdns_access_cached_entry_by_name(context, name, name_len, record_type, record_class);
|
|
|
|
|
|
|
|
|
|
|
|
*entry_length = 0;
|
|
|
|
|
|
|
|
if (entry == NULL)
|
|
|
|
{
|
|
|
|
if (expected_nb_records != 0)
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int nbanswers = mdns_cache_nb_records_in_entry((uint8_t*)entry->data);
|
|
|
|
if (nbanswers != expected_nb_records)
|
|
|
|
{
|
|
|
|
ret = -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer != NULL)
|
|
|
|
{
|
|
|
|
getdns_mdns_cached_record_header * header =
|
|
|
|
(getdns_mdns_cached_record_header*)entry->data;
|
|
|
|
size_t record_length = header->content_len - sizeof(getdns_mdns_cached_record_header);
|
|
|
|
|
|
|
|
if (record_length > buffer_max)
|
|
|
|
{
|
|
|
|
ret = -3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy(buffer, ((uint8_t *)entry->data) + sizeof(getdns_mdns_cached_record_header),
|
|
|
|
record_length);
|
|
|
|
*entry_length = record_length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 07:36:20 -06:00
|
|
|
lock_rw_unlock(&entry->lock);
|
2017-03-03 18:52:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mdns_addition_test(struct getdns_context* context,
|
|
|
|
uint8_t * buffer, size_t buffer_max, size_t* entry_length)
|
|
|
|
{
|
|
|
|
int ret =
|
|
|
|
mdns_propose_entry_to_cache(context, mdns_test_name, sizeof(mdns_test_name), 1, 1, 255,
|
|
|
|
mdns_test_first_address, 4, NULL, _getdns_get_time_as_uintt64());
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
2017-03-17 14:19:54 -05:00
|
|
|
ret = mdns_finalize_lru_test(context, mdns_test_name, sizeof(mdns_test_name), 1, 1,
|
2017-03-03 18:52:02 -06:00
|
|
|
1, buffer, buffer_max, entry_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mdns_addition_test2(struct getdns_context* context,
|
|
|
|
uint8_t * buffer, size_t buffer_max, size_t* entry_length)
|
|
|
|
{
|
|
|
|
int ret =
|
|
|
|
mdns_propose_entry_to_cache(context, mdns_test_name, sizeof(mdns_test_name), 1, 1, 255,
|
|
|
|
mdns_test_first_address, 4, NULL, _getdns_get_time_as_uintt64());
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
/* add a second entry, with a different value */
|
|
|
|
ret =
|
|
|
|
mdns_propose_entry_to_cache(context, mdns_test_name, sizeof(mdns_test_name), 1, 1, 255,
|
|
|
|
mdns_test_second_address, 4, NULL, _getdns_get_time_as_uintt64());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
/* add a third entry, with a different value */
|
|
|
|
ret =
|
|
|
|
mdns_propose_entry_to_cache(context, mdns_test_name, sizeof(mdns_test_name), 1, 1, 255,
|
|
|
|
mdns_test_third_address, 4, NULL, _getdns_get_time_as_uintt64());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
ret = mdns_finalize_lru_test(context, mdns_test_name, sizeof(mdns_test_name), 1, 1,
|
|
|
|
3, buffer, buffer_max, entry_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mdns_deletion_test(struct getdns_context* context,
|
|
|
|
uint8_t * buffer, size_t buffer_max, size_t* entry_length)
|
|
|
|
{
|
|
|
|
/* insert data with TTL = 0 to trigger suppression */
|
|
|
|
int ret =
|
|
|
|
mdns_propose_entry_to_cache(context, mdns_test_name, sizeof(mdns_test_name), 1, 1, 0,
|
|
|
|
mdns_test_second_address, 4, NULL, _getdns_get_time_as_uintt64());
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
ret = mdns_finalize_lru_test(context, mdns_test_name, sizeof(mdns_test_name), 1, 1,
|
|
|
|
2, buffer, buffer_max, entry_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mdns_deletion_test2(struct getdns_context* context,
|
|
|
|
uint8_t * buffer, size_t buffer_max, size_t* entry_length)
|
|
|
|
{
|
|
|
|
/* insert data with TTL = 0 to trigger suppression */
|
|
|
|
int ret =
|
|
|
|
mdns_propose_entry_to_cache(context, mdns_test_name, sizeof(mdns_test_name), 1, 1, 0,
|
|
|
|
mdns_test_first_address, 4, NULL, _getdns_get_time_as_uintt64());
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
ret =
|
|
|
|
mdns_propose_entry_to_cache(context, mdns_test_name, sizeof(mdns_test_name), 1, 1, 0,
|
|
|
|
mdns_test_third_address, 4, NULL, _getdns_get_time_as_uintt64());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
ret = mdns_finalize_lru_test(context, mdns_test_name, sizeof(mdns_test_name), 1, 1,
|
|
|
|
0, buffer, buffer_max, entry_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int mdns_test_prepare(struct getdns_context* context)
|
|
|
|
{
|
|
|
|
return mdns_delayed_network_init(context);
|
|
|
|
}
|
|
|
|
|
2017-03-09 07:36:20 -06:00
|
|
|
#endif
|