Update mdns dependency to latest development release

This commit is contained in:
James Buren 2024-08-05 13:40:34 -05:00
parent 4bae567c4c
commit 6157668057

View file

@ -94,6 +94,7 @@ typedef struct mdns_record_ptr_t mdns_record_ptr_t;
typedef struct mdns_record_a_t mdns_record_a_t; typedef struct mdns_record_a_t mdns_record_a_t;
typedef struct mdns_record_aaaa_t mdns_record_aaaa_t; typedef struct mdns_record_aaaa_t mdns_record_aaaa_t;
typedef struct mdns_record_txt_t mdns_record_txt_t; typedef struct mdns_record_txt_t mdns_record_txt_t;
typedef struct mdns_query_t mdns_query_t;
#ifdef _WIN32 #ifdef _WIN32
typedef int mdns_size_t; typedef int mdns_size_t;
@ -154,6 +155,8 @@ struct mdns_record_t {
mdns_record_aaaa_t aaaa; mdns_record_aaaa_t aaaa;
mdns_record_txt_t txt; mdns_record_txt_t txt;
} data; } data;
uint16_t rclass;
uint32_t ttl;
}; };
struct mdns_header_t { struct mdns_header_t {
@ -165,6 +168,12 @@ struct mdns_header_t {
uint16_t additional_rrs; uint16_t additional_rrs;
}; };
struct mdns_query_t {
mdns_record_type_t type;
const char* name;
size_t length;
};
// mDNS/DNS-SD public API // mDNS/DNS-SD public API
//! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass //! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass
@ -233,7 +242,19 @@ static inline int
mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer,
size_t capacity, uint16_t query_id); size_t capacity, uint16_t query_id);
//! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering //! Send a multicast mDNS query on the given socket for the given service names. The supplied buffer
//! will be used to build the query packet and must be 32 bit aligned. The query ID can be set to
//! non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for
//! multicast queries. Each additional service name query consists of a triplet - a record type
//! (mdns_record_type_t), a name string pointer (const char*) and a name length (size_t). The list
//! of variable arguments should be terminated with a record type of 0. The query will request a
//! unicast response if the socket is bound to an ephemeral port, or a multicast response if the
//! socket is bound to mDNS port 5353. Returns the used query ID, or <0 if error.
static inline int
mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer,
size_t capacity, uint16_t query_id);
//! Receive unicast responses to a mDNS query sent with mdns_[multi]query_send, optionally filtering
//! out any responses not matching the given query ID. Set the query ID to 0 to parse all responses, //! out any responses not matching the given query ID. Set the query ID to 0 to parse all responses,
//! even if it is not matching the query ID set in a specific query. Any data will be piped to the //! even if it is not matching the query ID set in a specific query. Any data will be piped to the
//! given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback //! given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback
@ -251,8 +272,8 @@ static inline int
mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer,
size_t capacity, uint16_t query_id, mdns_record_type_t record_type, size_t capacity, uint16_t query_id, mdns_record_type_t record_type,
const char* name, size_t name_length, mdns_record_t answer, const char* name, size_t name_length, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, const mdns_record_t* authority, size_t authority_count,
mdns_record_t* additional, size_t additional_count); const mdns_record_t* additional, size_t additional_count);
//! Send a variable multicast mDNS query answer to any question with variable number of records. Use //! Send a variable multicast mDNS query answer to any question with variable number of records. Use
//! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine //! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine
@ -260,23 +281,23 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo
//! aligned. Returns 0 if success, or <0 if error. //! aligned. Returns 0 if success, or <0 if error.
static inline int static inline int
mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, const mdns_record_t* authority, size_t authority_count,
mdns_record_t* additional, size_t additional_count); const mdns_record_t* additional, size_t additional_count);
//! Send a variable multicast mDNS announcement (as an unsolicited answer) with variable number of //! Send a variable multicast mDNS announcement (as an unsolicited answer) with variable number of
//! records.Buffer must be 32 bit aligned. Returns 0 if success, or <0 if error. Use this on service //! records.Buffer must be 32 bit aligned. Returns 0 if success, or <0 if error. Use this on service
//! startup to announce your instance to the local network. //! startup to announce your instance to the local network.
static inline int static inline int
mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, mdns_record_t* additional, const mdns_record_t* authority, size_t authority_count,
size_t additional_count); const mdns_record_t* additional, size_t additional_count);
//! Send a variable multicast mDNS announcement. Use this on service end for removing the resource //! Send a variable multicast mDNS announcement. Use this on service end for removing the resource
//! from the local network. The records must be identical to the according announcement. //! from the local network. The records must be identical to the according announcement.
static inline int static inline int
mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, mdns_record_t* additional, const mdns_record_t* authority, size_t authority_count,
size_t additional_count); const mdns_record_t* additional, size_t additional_count);
// Parse records functions // Parse records functions
@ -317,6 +338,9 @@ mdns_string_skip(const void* buffer, size_t size, size_t* offset);
static inline size_t static inline size_t
mdns_string_find(const char* str, size_t length, char c, size_t offset); mdns_string_find(const char* str, size_t length, char c, size_t offset);
//! Compare if two strings are equal. If the strings are equal it returns >0 and the offset variables are
//! updated to the end of the corresponding strings. If the strings are not equal it returns 0 and
//! the offset variables are NOT updated.
static inline int static inline int
mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs, mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs,
size_t size_rhs, size_t* ofs_rhs); size_t size_rhs, size_t* ofs_rhs);
@ -829,7 +853,7 @@ mdns_multicast_send(int sock, const void* buffer, size_t size) {
return 0; return 0;
} }
static inline const uint8_t mdns_services_query[] = { static const uint8_t mdns_services_query[] = {
// Query ID // Query ID
0x00, 0x00, 0x00, 0x00,
// Flags // Flags
@ -882,20 +906,18 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac
// It seems some implementations do not fill the correct questions field, // It seems some implementations do not fill the correct questions field,
// so ignore this check for now and only validate answer string // so ignore this check for now and only validate answer string
/* // if (questions != 1)
if (questions != 1) // return 0;
return 0;
*/
int i; int i;
for (i = 0; i < questions; ++i) { for (i = 0; i < questions; ++i) {
size_t ofs = MDNS_POINTER_DIFF(data, buffer); size_t offset = MDNS_POINTER_DIFF(data, buffer);
size_t verify_ofs = 12; size_t verify_offset = 12;
// Verify it's our question, _services._dns-sd._udp.local. // Verify it's our question, _services._dns-sd._udp.local.
if (!mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, if (!mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
sizeof(mdns_services_query), &verify_ofs)) sizeof(mdns_services_query), &verify_offset))
return 0; return 0;
data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, ofs); data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset);
uint16_t rtype = mdns_ntohs(data++); uint16_t rtype = mdns_ntohs(data++);
uint16_t rclass = mdns_ntohs(data++); uint16_t rclass = mdns_ntohs(data++);
@ -906,31 +928,33 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac
} }
for (i = 0; i < answer_rrs; ++i) { for (i = 0; i < answer_rrs; ++i) {
size_t ofs = MDNS_POINTER_DIFF(data, buffer); size_t offset = MDNS_POINTER_DIFF(data, buffer);
size_t verify_ofs = 12; size_t verify_offset = 12;
// Verify it's an answer to our question, _services._dns-sd._udp.local. // Verify it's an answer to our question, _services._dns-sd._udp.local.
size_t name_offset = ofs; size_t name_offset = offset;
int is_answer = mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, int is_answer = mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
sizeof(mdns_services_query), &verify_ofs); sizeof(mdns_services_query), &verify_offset);
size_t name_length = ofs - name_offset; if (!is_answer && !mdns_string_skip(buffer, data_size, &offset))
if ((ofs + 10) > data_size) break;
size_t name_length = offset - name_offset;
if ((offset + 10) > data_size)
return records; return records;
data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, ofs); data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset);
uint16_t rtype = mdns_ntohs(data++); uint16_t rtype = mdns_ntohs(data++);
uint16_t rclass = mdns_ntohs(data++); uint16_t rclass = mdns_ntohs(data++);
uint32_t ttl = mdns_ntohl(data); uint32_t ttl = mdns_ntohl(data);
data += 2; data += 2;
uint16_t length = mdns_ntohs(data++); uint16_t length = mdns_ntohs(data++);
if (length > (data_size - ofs)) if (length > (data_size - offset))
return 0; return 0;
if (is_answer) { if (is_answer) {
++records; ++records;
ofs = MDNS_POINTER_DIFF(data, buffer); offset = MDNS_POINTER_DIFF(data, buffer);
if (callback && if (callback &&
callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl,
buffer, data_size, name_offset, name_length, ofs, length, user_data)) buffer, data_size, name_offset, name_length, offset, length, user_data))
return records; return records;
} }
data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length); data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length);
@ -975,27 +999,22 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback
uint16_t query_id = mdns_ntohs(data++); uint16_t query_id = mdns_ntohs(data++);
uint16_t flags = mdns_ntohs(data++); uint16_t flags = mdns_ntohs(data++);
uint16_t questions = mdns_ntohs(data++); uint16_t questions = mdns_ntohs(data++);
/*
This data is unused at the moment, skip
uint16_t answer_rrs = mdns_ntohs(data++); uint16_t answer_rrs = mdns_ntohs(data++);
uint16_t authority_rrs = mdns_ntohs(data++); uint16_t authority_rrs = mdns_ntohs(data++);
uint16_t additional_rrs = mdns_ntohs(data++); uint16_t additional_rrs = mdns_ntohs(data++);
*/
data += 3;
size_t parsed = 0; size_t records;
size_t total_records = 0;
for (int iquestion = 0; iquestion < questions; ++iquestion) { for (int iquestion = 0; iquestion < questions; ++iquestion) {
size_t question_offset = MDNS_POINTER_DIFF(data, buffer); size_t question_offset = MDNS_POINTER_DIFF(data, buffer);
size_t offset = question_offset; size_t offset = question_offset;
size_t verify_ofs = 12; size_t verify_offset = 12;
int dns_sd = 0; int dns_sd = 0;
if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
sizeof(mdns_services_query), &verify_ofs)) { sizeof(mdns_services_query), &verify_offset)) {
dns_sd = 1; dns_sd = 1;
} else { } else if (!mdns_string_skip(buffer, data_size, &offset)) {
offset = question_offset; break;
if (!mdns_string_skip(buffer, data_size, &offset))
break;
} }
size_t length = offset - question_offset; size_t length = offset - question_offset;
data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset);
@ -1004,7 +1023,7 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback
uint16_t rclass = mdns_ntohs(data++); uint16_t rclass = mdns_ntohs(data++);
uint16_t class_without_flushbit = rclass & ~MDNS_CACHE_FLUSH; uint16_t class_without_flushbit = rclass & ~MDNS_CACHE_FLUSH;
// Make sure we get a question of class IN // Make sure we get a question of class IN or ANY
if (!((class_without_flushbit == MDNS_CLASS_IN) || if (!((class_without_flushbit == MDNS_CLASS_IN) ||
(class_without_flushbit == MDNS_CLASS_ANY))) { (class_without_flushbit == MDNS_CLASS_ANY))) {
break; break;
@ -1013,20 +1032,48 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback
if (dns_sd && flags) if (dns_sd && flags)
continue; continue;
++parsed; ++total_records;
if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype,
rclass, 0, buffer, data_size, question_offset, length, rclass, 0, buffer, data_size, question_offset, length,
question_offset, length, user_data)) question_offset, length, user_data))
break; return total_records;
} }
return parsed; size_t offset = MDNS_POINTER_DIFF(data, buffer);
records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data);
total_records += records;
if (records != answer_rrs)
return total_records;
records =
mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data);
total_records += records;
if (records != authority_rrs)
return total_records;
records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback,
user_data);
return total_records;
} }
static inline int static inline int
mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer,
size_t capacity, uint16_t query_id) { size_t capacity, uint16_t query_id) {
if (capacity < (17 + length)) mdns_query_t query;
query.type = type;
query.name = name;
query.length = length;
return mdns_multiquery_send(sock, &query, 1, buffer, capacity, query_id);
}
static inline int
mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, size_t capacity,
uint16_t query_id) {
if (!count || (capacity < (sizeof(struct mdns_header_t) + (6 * count))))
return -1; return -1;
// Ask for a unicast response since it's a one-shot query // Ask for a unicast response since it's a one-shot query
@ -1046,25 +1093,30 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng
struct mdns_header_t* header = (struct mdns_header_t*)buffer; struct mdns_header_t* header = (struct mdns_header_t*)buffer;
// Query ID // Query ID
header->query_id = htons(query_id); header->query_id = htons((unsigned short)query_id);
// Flags // Flags
header->flags = 0; header->flags = 0;
// Questions // Questions
header->questions = htons(1); header->questions = htons((unsigned short)count);
// No answer, authority or additional RRs // No answer, authority or additional RRs
header->answer_rrs = 0; header->answer_rrs = 0;
header->authority_rrs = 0; header->authority_rrs = 0;
header->additional_rrs = 0; header->additional_rrs = 0;
// Fill in question // Fill in questions
// Name string
void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t));
data = mdns_string_make(buffer, capacity, data, name, length, 0); for (size_t iq = 0; iq < count; ++iq) {
if (!data) // Name string
return -1; data = mdns_string_make(buffer, capacity, data, query[iq].name, query[iq].length, 0);
// Record type if (!data)
data = mdns_htons(data, type); return -1;
//! Optional unicast response based on local port, class IN size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer);
data = mdns_htons(data, rclass); if (remain < 4)
return -1;
// Record type
data = mdns_htons(data, query[iq].type);
//! Optional unicast response based on local port, class IN
data = mdns_htons(data, rclass);
}
size_t tosend = MDNS_POINTER_DIFF(data, buffer); size_t tosend = MDNS_POINTER_DIFF(data, buffer);
if (mdns_multicast_send(sock, buffer, (size_t)tosend)) if (mdns_multicast_send(sock, buffer, (size_t)tosend))
@ -1100,19 +1152,16 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn
if ((only_query_id > 0) && (query_id != only_query_id)) if ((only_query_id > 0) && (query_id != only_query_id))
return 0; // Not a reply to the wanted one-shot query return 0; // Not a reply to the wanted one-shot query
if (questions > 1)
return 0;
// Skip questions part // Skip questions part
int i; int i;
for (i = 0; i < questions; ++i) { for (i = 0; i < questions; ++i) {
size_t ofs = MDNS_POINTER_DIFF(data, buffer); size_t offset = MDNS_POINTER_DIFF(data, buffer);
if (!mdns_string_skip(buffer, data_size, &ofs)) if (!mdns_string_skip(buffer, data_size, &offset))
return 0; return 0;
data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, ofs); data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset);
/* Record type and class not used, skip // Record type and class not used, skip
uint16_t rtype = mdns_ntohs(data++); // uint16_t rtype = mdns_ntohs(data++);
uint16_t rclass = mdns_ntohs(data++);*/ // uint16_t rclass = mdns_ntohs(data++);
data += 2; data += 2;
} }
@ -1161,7 +1210,7 @@ mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data,
static inline void* static inline void*
mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_record_t record, mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_record_t record,
uint16_t rclass, uint32_t ttl, mdns_string_table_t* string_table) { mdns_string_table_t* string_table) {
data = mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table); data = mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table);
if (!data) if (!data)
return 0; return 0;
@ -1170,20 +1219,20 @@ mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_re
return 0; return 0;
data = mdns_htons(data, record.type); data = mdns_htons(data, record.type);
data = mdns_htons(data, rclass); data = mdns_htons(data, record.rclass);
data = mdns_htonl(data, ttl); data = mdns_htonl(data, record.ttl);
data = mdns_htons(data, 0); // Length, to be filled later data = mdns_htons(data, 0); // Length, to be filled later
return data; return data;
} }
static inline void* static inline void*
mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t record, mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t record,
uint16_t rclass, uint32_t ttl, mdns_string_table_t* string_table) { mdns_string_table_t* string_table) {
// TXT records will be coalesced into one record later // TXT records will be coalesced into one record later
if (!data || (record.type == MDNS_RECORDTYPE_TXT)) if (!data || (record.type == MDNS_RECORDTYPE_TXT))
return data; return data;
data = mdns_answer_add_record_header(buffer, capacity, data, record, rclass, ttl, string_table); data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table);
if (!data) if (!data)
return 0; return 0;
@ -1234,8 +1283,20 @@ mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t
return data; return data;
} }
static inline void
mdns_record_update_rclass_ttl(mdns_record_t* record, uint16_t rclass, uint32_t ttl) {
if (!record->rclass)
record->rclass = rclass;
if (!record->ttl || !ttl)
record->ttl = ttl;
record->rclass &= (uint16_t)(MDNS_CLASS_IN | MDNS_CACHE_FLUSH);
// Never flush PTR record
if (record->type == MDNS_RECORDTYPE_PTR)
record->rclass &= ~(uint16_t)MDNS_CACHE_FLUSH;
}
static inline void* static inline void*
mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_record_t* records, mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, const mdns_record_t* records,
size_t record_count, uint16_t rclass, uint32_t ttl, size_t record_count, uint16_t rclass, uint32_t ttl,
mdns_string_table_t* string_table) { mdns_string_table_t* string_table) {
// Pointer to length of record to be filled at end // Pointer to length of record to be filled at end
@ -1247,17 +1308,19 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor
if (records[irec].type != MDNS_RECORDTYPE_TXT) if (records[irec].type != MDNS_RECORDTYPE_TXT)
continue; continue;
mdns_record_t record = records[irec];
mdns_record_update_rclass_ttl(&record, rclass, ttl);
if (!record_data) { if (!record_data) {
data = mdns_answer_add_record_header(buffer, capacity, data, records[irec], rclass, ttl, data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table);
string_table); if (!data)
return data;
record_length = MDNS_POINTER_OFFSET(data, -2); record_length = MDNS_POINTER_OFFSET(data, -2);
record_data = data; record_data = data;
} }
// TXT strings are unlikely to be shared, just make then raw. Also need one byte for // TXT strings are unlikely to be shared, just make then raw. Also need one byte for
// termination, thus the <= check // termination, thus the <= check
size_t string_length = size_t string_length = record.data.txt.key.length + record.data.txt.value.length + 1;
records[irec].data.txt.key.length + records[irec].data.txt.value.length + 1;
if (!data) if (!data)
return 0; return 0;
remain = capacity - MDNS_POINTER_DIFF(data, buffer); remain = capacity - MDNS_POINTER_DIFF(data, buffer);
@ -1266,11 +1329,11 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor
unsigned char* strdata = (unsigned char*)data; unsigned char* strdata = (unsigned char*)data;
*strdata++ = (unsigned char)string_length; *strdata++ = (unsigned char)string_length;
memcpy(strdata, records[irec].data.txt.key.str, records[irec].data.txt.key.length); memcpy(strdata, record.data.txt.key.str, record.data.txt.key.length);
strdata += records[irec].data.txt.key.length; strdata += record.data.txt.key.length;
*strdata++ = '='; *strdata++ = '=';
memcpy(strdata, records[irec].data.txt.value.str, records[irec].data.txt.value.length); memcpy(strdata, record.data.txt.value.str, record.data.txt.value.length);
strdata += records[irec].data.txt.value.length; strdata += record.data.txt.value.length;
data = strdata; data = strdata;
} }
@ -1283,7 +1346,7 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor
} }
static inline uint16_t static inline uint16_t
mdns_answer_get_record_count(mdns_record_t* records, size_t record_count) { mdns_answer_get_record_count(const mdns_record_t* records, size_t record_count) {
// TXT records will be coalesced into one record // TXT records will be coalesced into one record
uint16_t total_count = 0; uint16_t total_count = 0;
uint16_t txt_record = 0; uint16_t txt_record = 0;
@ -1300,12 +1363,15 @@ static inline int
mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer,
size_t capacity, uint16_t query_id, mdns_record_type_t record_type, size_t capacity, uint16_t query_id, mdns_record_type_t record_type,
const char* name, size_t name_length, mdns_record_t answer, const char* name, size_t name_length, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, const mdns_record_t* authority, size_t authority_count,
mdns_record_t* additional, size_t additional_count) { const mdns_record_t* additional, size_t additional_count) {
if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) if (capacity < (sizeof(struct mdns_header_t) + 32 + 4))
return -1; return -1;
uint16_t rclass = MDNS_CACHE_FLUSH | MDNS_CLASS_IN; // According to RFC 6762:
// The cache-flush bit MUST NOT be set in any resource records in a response message
// sent in legacy unicast responses to UDP ports other than 5353.
uint16_t rclass = MDNS_CLASS_IN;
uint32_t ttl = 10; uint32_t ttl = 10;
// Basic answer structure // Basic answer structure
@ -1325,21 +1391,31 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo
&string_table); &string_table);
// Fill in answer // Fill in answer
data = mdns_answer_add_record(buffer, capacity, data, answer, rclass, ttl, &string_table); answer.rclass = rclass;
answer.ttl = ttl;
data = mdns_answer_add_record(buffer, capacity, data, answer, &string_table);
// Fill in authority records // Fill in authority records
for (size_t irec = 0; data && (irec < authority_count); ++irec) for (size_t irec = 0; data && (irec < authority_count); ++irec) {
data = mdns_answer_add_record(buffer, capacity, data, authority[irec], rclass, ttl, mdns_record_t record = authority[irec];
&string_table); record.rclass = rclass;
data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, if (!record.ttl)
ttl, &string_table); record.ttl = ttl;
data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
}
data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count,
rclass, ttl, &string_table);
// Fill in additional records // Fill in additional records
for (size_t irec = 0; data && (irec < additional_count); ++irec) for (size_t irec = 0; data && (irec < additional_count); ++irec) {
data = mdns_answer_add_record(buffer, capacity, data, additional[irec], rclass, ttl, mdns_record_t record = additional[irec];
&string_table); record.rclass = rclass;
data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, if (!record.ttl)
ttl, &string_table); record.ttl = ttl;
data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
}
data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count,
rclass, ttl, &string_table);
if (!data) if (!data)
return -1; return -1;
@ -1347,11 +1423,11 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo
return mdns_unicast_send(sock, address, address_size, buffer, tosend); return mdns_unicast_send(sock, address, address_size, buffer, tosend);
} }
static inline int static inline int
mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, uint16_t rclass, mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, mdns_record_t answer,
mdns_record_t answer, mdns_record_t* authority, size_t authority_count, const mdns_record_t* authority, size_t authority_count,
mdns_record_t* additional, size_t additional_count, uint32_t ttl) { const mdns_record_t* additional, size_t additional_count,
uint16_t rclass, uint32_t ttl) {
if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) if (capacity < (sizeof(struct mdns_header_t) + 32 + 4))
return -1; return -1;
@ -1368,21 +1444,27 @@ mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, uint16
void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t));
// Fill in answer // Fill in answer
data = mdns_answer_add_record(buffer, capacity, data, answer, rclass, ttl, &string_table); mdns_record_t record = answer;
mdns_record_update_rclass_ttl(&record, rclass, ttl);
data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
// Fill in authority records // Fill in authority records
for (size_t irec = 0; data && (irec < authority_count); ++irec) for (size_t irec = 0; data && (irec < authority_count); ++irec) {
data = mdns_answer_add_record(buffer, capacity, data, authority[irec], rclass, ttl, record = authority[irec];
&string_table); mdns_record_update_rclass_ttl(&record, rclass, ttl);
data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
ttl, &string_table); }
data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count,
rclass, ttl, &string_table);
// Fill in additional records // Fill in additional records
for (size_t irec = 0; data && (irec < additional_count); ++irec) for (size_t irec = 0; data && (irec < additional_count); ++irec) {
data = mdns_answer_add_record(buffer, capacity, data, additional[irec], rclass, ttl, record = additional[irec];
&string_table); mdns_record_update_rclass_ttl(&record, rclass, ttl);
data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
ttl, &string_table); }
data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count,
rclass, ttl, &string_table);
if (!data) if (!data)
return -1; return -1;
@ -1390,39 +1472,32 @@ mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, uint16
return mdns_multicast_send(sock, buffer, tosend); return mdns_multicast_send(sock, buffer, tosend);
} }
static inline int
mdns_answer_multicast_rclass(int sock, void* buffer, size_t capacity, uint16_t rclass,
mdns_record_t answer, mdns_record_t* authority, size_t authority_count,
mdns_record_t* additional, size_t additional_count) {
return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, rclass, answer, authority,
authority_count, additional, additional_count, 60);
}
static inline int static inline int
mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, const mdns_record_t* authority, size_t authority_count,
mdns_record_t* additional, size_t additional_count) { const mdns_record_t* additional, size_t additional_count) {
uint16_t rclass = MDNS_CLASS_IN; return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority,
return mdns_answer_multicast_rclass(sock, buffer, capacity, rclass, answer, authority, authority_count, additional, additional_count,
authority_count, additional, additional_count); MDNS_CLASS_IN, 60);
} }
static inline int static inline int
mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, mdns_record_t* additional, const mdns_record_t* authority, size_t authority_count,
size_t additional_count) { const mdns_record_t* additional, size_t additional_count) {
uint16_t rclass = MDNS_CLASS_IN | MDNS_CACHE_FLUSH; return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority,
return mdns_answer_multicast_rclass(sock, buffer, capacity, rclass, answer, authority, authority_count, additional, additional_count,
authority_count, additional, additional_count); MDNS_CLASS_IN | MDNS_CACHE_FLUSH, 60);
} }
static inline int static inline int
mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
mdns_record_t* authority, size_t authority_count, mdns_record_t* additional, const mdns_record_t* authority, size_t authority_count,
size_t additional_count) { const mdns_record_t* additional, size_t additional_count) {
uint16_t rclass = MDNS_CLASS_IN | MDNS_CACHE_FLUSH; // Goodbye should have ttl of 0
return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, rclass, answer, authority, return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority,
authority_count, additional, additional_count, 0); authority_count, additional, additional_count,
MDNS_CLASS_IN, 0);
} }
static inline mdns_string_t static inline mdns_string_t
@ -1497,14 +1572,19 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len
strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset); strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset);
size_t sublength = *(const unsigned char*)strdata; size_t sublength = *(const unsigned char*)strdata;
if (sublength >= (end - offset))
break;
++strdata; ++strdata;
offset += sublength + 1; offset += sublength + 1;
size_t separator = 0; size_t separator = sublength;
for (size_t c = 0; c < sublength; ++c) { for (size_t c = 0; c < sublength; ++c) {
// DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E]
if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) {
separator = 0;
break; break;
}
if (strdata[c] == '=') { if (strdata[c] == '=') {
separator = c; separator = c;
break; break;
@ -1522,6 +1602,8 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len
} else { } else {
records[parsed].key.str = strdata; records[parsed].key.str = strdata;
records[parsed].key.length = sublength; records[parsed].key.length = sublength;
records[parsed].value.str = 0;
records[parsed].value.length = 0;
} }
++parsed; ++parsed;