From 63125b465d076afb5da02af11a25385b3382017a Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Thu, 31 Oct 2024 12:10:24 +0200 Subject: [PATCH 01/37] Use original d927858d347dd2f7a2295bd38d43495330e88cfb from repo - to reduce diffs/formatting. --- src/mdns.h | 1682 ++++++++++++++++++++++++++-------------------------- 1 file changed, 840 insertions(+), 842 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 7164c54..283a441 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -12,8 +12,6 @@ * */ -// clang-format off - #pragma once #include @@ -50,24 +48,24 @@ extern "C" { #define MDNS_CACHE_FLUSH 0x8000U enum mdns_record_type { - MDNS_RECORDTYPE_IGNORE = 0, - // Address - MDNS_RECORDTYPE_A = 1, - // Domain Name pointer - MDNS_RECORDTYPE_PTR = 12, - // Arbitrary text string - MDNS_RECORDTYPE_TXT = 16, - // IP6 Address [Thomson] - MDNS_RECORDTYPE_AAAA = 28, - // Server Selection [RFC2782] - MDNS_RECORDTYPE_SRV = 33 + MDNS_RECORDTYPE_IGNORE = 0, + // Address + MDNS_RECORDTYPE_A = 1, + // Domain Name pointer + MDNS_RECORDTYPE_PTR = 12, + // Arbitrary text string + MDNS_RECORDTYPE_TXT = 16, + // IP6 Address [Thomson] + MDNS_RECORDTYPE_AAAA = 28, + // Server Selection [RFC2782] + MDNS_RECORDTYPE_SRV = 33 }; enum mdns_entry_type { - MDNS_ENTRYTYPE_QUESTION = 0, - MDNS_ENTRYTYPE_ANSWER = 1, - MDNS_ENTRYTYPE_AUTHORITY = 2, - MDNS_ENTRYTYPE_ADDITIONAL = 3 + MDNS_ENTRYTYPE_QUESTION = 0, + MDNS_ENTRYTYPE_ANSWER = 1, + MDNS_ENTRYTYPE_AUTHORITY = 2, + MDNS_ENTRYTYPE_ADDITIONAL = 3 }; enum mdns_class { MDNS_CLASS_IN = 1 }; @@ -94,35 +92,35 @@ typedef size_t mdns_size_t; #endif struct mdns_string_t { - const char* str; - size_t length; + const char* str; + size_t length; }; struct mdns_string_pair_t { - size_t offset; - size_t length; - int ref; + size_t offset; + size_t length; + int ref; }; struct mdns_record_srv_t { - uint16_t priority; - uint16_t weight; - uint16_t port; - mdns_string_t name; + uint16_t priority; + uint16_t weight; + uint16_t port; + mdns_string_t name; }; struct mdns_record_txt_t { - mdns_string_t key; - mdns_string_t value; + mdns_string_t key; + mdns_string_t value; }; struct mdns_header_t { - uint16_t query_id; - uint16_t flags; - uint16_t questions; - uint16_t answer_rrs; - uint16_t authority_rrs; - uint16_t additional_rrs; + uint16_t query_id; + uint16_t flags; + uint16_t questions; + uint16_t answer_rrs; + uint16_t authority_rrs; + uint16_t additional_rrs; }; // mDNS/DNS-SD public API @@ -262,412 +260,412 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len static int mdns_socket_open_ipv4(struct sockaddr_in* saddr) { - int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) - return -1; - if (mdns_socket_setup_ipv4(sock, saddr)) { - mdns_socket_close(sock); - return -1; - } - return sock; + int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv4(sock, saddr)) { + mdns_socket_close(sock); + return -1; + } + return sock; } static int mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr) { - unsigned char ttl = 1; - unsigned char loopback = 1; - unsigned int reuseaddr = 1; - struct ip_mreq req; + unsigned char ttl = 1; + unsigned char loopback = 1; + unsigned int reuseaddr = 1; + struct ip_mreq req; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); #ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); #endif - setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)); - setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); - - memset(&req, 0, sizeof(req)); - req.imr_multiaddr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); - if (saddr) - req.imr_interface = saddr->sin_addr; - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof(req))) - return -1; - - struct sockaddr_in sock_addr; - if (!saddr) { - saddr = &sock_addr; - memset(saddr, 0, sizeof(struct sockaddr_in)); - saddr->sin_family = AF_INET; - saddr->sin_addr.s_addr = INADDR_ANY; + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)); + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); + + memset(&req, 0, sizeof(req)); + req.imr_multiaddr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); + if (saddr) + req.imr_interface = saddr->sin_addr; + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof(req))) + return -1; + + struct sockaddr_in sock_addr; + if (!saddr) { + saddr = &sock_addr; + memset(saddr, 0, sizeof(struct sockaddr_in)); + saddr->sin_family = AF_INET; + saddr->sin_addr.s_addr = INADDR_ANY; #ifdef __APPLE__ - saddr->sin_len = sizeof(struct sockaddr_in); + saddr->sin_len = sizeof(struct sockaddr_in); #endif - } else { - setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&saddr->sin_addr, - sizeof(saddr->sin_addr)); + } else { + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&saddr->sin_addr, + sizeof(saddr->sin_addr)); #ifndef _WIN32 - saddr->sin_addr.s_addr = INADDR_ANY; + saddr->sin_addr.s_addr = INADDR_ANY; #endif - } + } - if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in))) - return -1; + if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in))) + return -1; #ifdef _WIN32 - unsigned long param = 1; - ioctlsocket(sock, FIONBIO, ¶m); + unsigned long param = 1; + ioctlsocket(sock, FIONBIO, ¶m); #else - const int flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); + const int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); #endif - return 0; + return 0; } static int mdns_socket_open_ipv6(struct sockaddr_in6* saddr) { - int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) - return -1; - if (mdns_socket_setup_ipv6(sock, saddr)) { - mdns_socket_close(sock); - return -1; - } - return sock; + int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv6(sock, saddr)) { + mdns_socket_close(sock); + return -1; + } + return sock; } static int mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr) { - int hops = 1; - unsigned int loopback = 1; - unsigned int reuseaddr = 1; - struct ipv6_mreq req; + int hops = 1; + unsigned int loopback = 1; + unsigned int reuseaddr = 1; + struct ipv6_mreq req; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); #ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); #endif - setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops)); - setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); - - memset(&req, 0, sizeof(req)); - req.ipv6mr_multiaddr.s6_addr[0] = 0xFF; - req.ipv6mr_multiaddr.s6_addr[1] = 0x02; - req.ipv6mr_multiaddr.s6_addr[15] = 0xFB; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req))) - return -1; - - struct sockaddr_in6 sock_addr; - if (!saddr) { - saddr = &sock_addr; - memset(saddr, 0, sizeof(struct sockaddr_in6)); - saddr->sin6_family = AF_INET6; - saddr->sin6_addr = in6addr_any; + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops)); + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); + + memset(&req, 0, sizeof(req)); + req.ipv6mr_multiaddr.s6_addr[0] = 0xFF; + req.ipv6mr_multiaddr.s6_addr[1] = 0x02; + req.ipv6mr_multiaddr.s6_addr[15] = 0xFB; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req))) + return -1; + + struct sockaddr_in6 sock_addr; + if (!saddr) { + saddr = &sock_addr; + memset(saddr, 0, sizeof(struct sockaddr_in6)); + saddr->sin6_family = AF_INET6; + saddr->sin6_addr = in6addr_any; #ifdef __APPLE__ - saddr->sin6_len = sizeof(struct sockaddr_in6); + saddr->sin6_len = sizeof(struct sockaddr_in6); #endif - } else { - unsigned int ifindex = 0; - setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&ifindex, sizeof(ifindex)); + } else { + unsigned int ifindex = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&ifindex, sizeof(ifindex)); #ifndef _WIN32 - saddr->sin6_addr = in6addr_any; + saddr->sin6_addr = in6addr_any; #endif - } + } - if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in6))) - return -1; + if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in6))) + return -1; #ifdef _WIN32 - unsigned long param = 1; - ioctlsocket(sock, FIONBIO, ¶m); + unsigned long param = 1; + ioctlsocket(sock, FIONBIO, ¶m); #else - const int flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); + const int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); #endif - return 0; + return 0; } static void mdns_socket_close(int sock) { #ifdef _WIN32 - closesocket(sock); + closesocket(sock); #else - close(sock); + close(sock); #endif } static int mdns_is_string_ref(uint8_t val) { - return (0xC0 == (val & 0xC0)); + return (0xC0 == (val & 0xC0)); } static mdns_string_pair_t mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { - const uint8_t* buffer = (const uint8_t*)rawdata; - mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0}; - if (!buffer[offset]) { - pair.offset = offset; - return pair; - } - if (mdns_is_string_ref(buffer[offset])) { - if (size < offset + 2) - return pair; - - offset = 0x3fff & ntohs(*(uint16_t*)MDNS_POINTER_OFFSET(buffer, offset)); - if (offset >= size) - return pair; - - pair.ref = 1; - } - - size_t length = (size_t)buffer[offset++]; - if (size < offset + length) - return pair; - - pair.offset = offset; - pair.length = length; - - return pair; + const uint8_t* buffer = (const uint8_t*)rawdata; + mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0}; + if (!buffer[offset]) { + pair.offset = offset; + return pair; + } + if (mdns_is_string_ref(buffer[offset])) { + if (size < offset + 2) + return pair; + + offset = 0x3fff & ntohs(*(uint16_t*)MDNS_POINTER_OFFSET(buffer, offset)); + if (offset >= size) + return pair; + + pair.ref = 1; + } + + size_t length = (size_t)buffer[offset++]; + if (size < offset + length) + return pair; + + pair.offset = offset; + pair.length = length; + + return pair; } static int mdns_string_skip(const void* buffer, size_t size, size_t* offset) { - size_t cur = *offset; - mdns_string_pair_t substr; - do { - substr = mdns_get_next_substring(buffer, size, cur); - if (substr.offset == MDNS_INVALID_POS) - return 0; - if (substr.ref) { - *offset = cur + 2; - return 1; - } - cur = substr.offset + substr.length; - } while (substr.length); - - *offset = cur + 1; - return 1; + size_t cur = *offset; + mdns_string_pair_t substr; + do { + substr = mdns_get_next_substring(buffer, size, cur); + if (substr.offset == MDNS_INVALID_POS) + return 0; + if (substr.ref) { + *offset = cur + 2; + return 1; + } + cur = substr.offset + substr.length; + } while (substr.length); + + *offset = cur + 1; + return 1; } static int 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 lhs_cur = *ofs_lhs; - size_t rhs_cur = *ofs_rhs; - size_t lhs_end = MDNS_INVALID_POS; - size_t rhs_end = MDNS_INVALID_POS; - mdns_string_pair_t lhs_substr; - mdns_string_pair_t rhs_substr; - do { - lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur); - rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur); - if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS)) - return 0; - if (lhs_substr.length != rhs_substr.length) - return 0; - if (strncasecmp((const char*)buffer_rhs + rhs_substr.offset, - (const char*)buffer_lhs + lhs_substr.offset, rhs_substr.length)) - return 0; - if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS)) - lhs_end = lhs_cur + 2; - if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS)) - rhs_end = rhs_cur + 2; - lhs_cur = lhs_substr.offset + lhs_substr.length; - rhs_cur = rhs_substr.offset + rhs_substr.length; - } while (lhs_substr.length); - - if (lhs_end == MDNS_INVALID_POS) - lhs_end = lhs_cur + 1; - *ofs_lhs = lhs_end; - - if (rhs_end == MDNS_INVALID_POS) - rhs_end = rhs_cur + 1; - *ofs_rhs = rhs_end; - - return 1; + size_t lhs_cur = *ofs_lhs; + size_t rhs_cur = *ofs_rhs; + size_t lhs_end = MDNS_INVALID_POS; + size_t rhs_end = MDNS_INVALID_POS; + mdns_string_pair_t lhs_substr; + mdns_string_pair_t rhs_substr; + do { + lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur); + rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur); + if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS)) + return 0; + if (lhs_substr.length != rhs_substr.length) + return 0; + if (strncasecmp((const char*)buffer_rhs + rhs_substr.offset, + (const char*)buffer_lhs + lhs_substr.offset, rhs_substr.length)) + return 0; + if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS)) + lhs_end = lhs_cur + 2; + if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS)) + rhs_end = rhs_cur + 2; + lhs_cur = lhs_substr.offset + lhs_substr.length; + rhs_cur = rhs_substr.offset + rhs_substr.length; + } while (lhs_substr.length); + + if (lhs_end == MDNS_INVALID_POS) + lhs_end = lhs_cur + 1; + *ofs_lhs = lhs_end; + + if (rhs_end == MDNS_INVALID_POS) + rhs_end = rhs_cur + 1; + *ofs_rhs = rhs_end; + + return 1; } static mdns_string_t mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity) { - size_t cur = *offset; - size_t end = MDNS_INVALID_POS; - mdns_string_pair_t substr; - mdns_string_t result; - result.str = str; - result.length = 0; - char* dst = str; - size_t remain = capacity; - do { - substr = mdns_get_next_substring(buffer, size, cur); - if (substr.offset == MDNS_INVALID_POS) - return result; - if (substr.ref && (end == MDNS_INVALID_POS)) - end = cur + 2; - if (substr.length) { - size_t to_copy = (substr.length < remain) ? substr.length : remain; - memcpy(dst, (const char*)buffer + substr.offset, to_copy); - dst += to_copy; - remain -= to_copy; - if (remain) { - *dst++ = '.'; - --remain; - } - } - cur = substr.offset + substr.length; - } while (substr.length); - - if (end == MDNS_INVALID_POS) - end = cur + 1; - *offset = end; - - result.length = capacity - remain; - return result; + size_t cur = *offset; + size_t end = MDNS_INVALID_POS; + mdns_string_pair_t substr; + mdns_string_t result; + result.str = str; + result.length = 0; + char* dst = str; + size_t remain = capacity; + do { + substr = mdns_get_next_substring(buffer, size, cur); + if (substr.offset == MDNS_INVALID_POS) + return result; + if (substr.ref && (end == MDNS_INVALID_POS)) + end = cur + 2; + if (substr.length) { + size_t to_copy = (substr.length < remain) ? substr.length : remain; + memcpy(dst, (const char*)buffer + substr.offset, to_copy); + dst += to_copy; + remain -= to_copy; + if (remain) { + *dst++ = '.'; + --remain; + } + } + cur = substr.offset + substr.length; + } while (substr.length); + + if (end == MDNS_INVALID_POS) + end = cur + 1; + *offset = end; + + result.length = capacity - remain; + return result; } static size_t mdns_string_find(const char* str, size_t length, char c, size_t offset) { - const void* found; - if (offset >= length) - return MDNS_INVALID_POS; - found = memchr(str + offset, c, length - offset); - if (found) - return (size_t)((const char*)found - str); - return MDNS_INVALID_POS; + const void* found; + if (offset >= length) + return MDNS_INVALID_POS; + found = memchr(str + offset, c, length - offset); + if (found) + return (size_t)((const char*)found - str); + return MDNS_INVALID_POS; } static void* mdns_string_make(void* data, size_t capacity, const char* name, size_t length) { - size_t pos = 0; - size_t last_pos = 0; - size_t remain = capacity; - unsigned char* dest = (unsigned char*)data; - while ((last_pos < length) && - ((pos = mdns_string_find(name, length, '.', last_pos)) != MDNS_INVALID_POS)) { - size_t sublength = pos - last_pos; - if (sublength < remain) { - *dest = (unsigned char)sublength; - memcpy(dest + 1, name + last_pos, sublength); - dest += sublength + 1; - remain -= sublength + 1; - } else { - return 0; - } - last_pos = pos + 1; - } - if (last_pos < length) { - size_t sublength = length - last_pos; - if (sublength < remain) { - *dest = (unsigned char)sublength; - memcpy(dest + 1, name + last_pos, sublength); - dest += sublength + 1; - remain -= sublength + 1; - } else { - return 0; - } - } - if (!remain) - return 0; - *dest++ = 0; - return dest; + size_t pos = 0; + size_t last_pos = 0; + size_t remain = capacity; + unsigned char* dest = (unsigned char*)data; + while ((last_pos < length) && + ((pos = mdns_string_find(name, length, '.', last_pos)) != MDNS_INVALID_POS)) { + size_t sublength = pos - last_pos; + if (sublength < remain) { + *dest = (unsigned char)sublength; + memcpy(dest + 1, name + last_pos, sublength); + dest += sublength + 1; + remain -= sublength + 1; + } else { + return 0; + } + last_pos = pos + 1; + } + if (last_pos < length) { + size_t sublength = length - last_pos; + if (sublength < remain) { + *dest = (unsigned char)sublength; + memcpy(dest + 1, name + last_pos, sublength); + dest += sublength + 1; + remain -= sublength + 1; + } else { + return 0; + } + } + if (!remain) + return 0; + *dest++ = 0; + return dest; } static void* mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { - if (capacity < 2) - return 0; - uint16_t* udata = (uint16_t*)data; - *udata++ = htons(0xC000 | (uint16_t)ref_offset); - return udata; + if (capacity < 2) + return 0; + uint16_t* udata = (uint16_t*)data; + *udata++ = htons(0xC000 | (uint16_t)ref_offset); + return udata; } static void* mdns_string_make_with_ref(void* data, size_t capacity, const char* name, size_t length, size_t ref_offset) { - void* remaindata = mdns_string_make(data, capacity, name, length); - capacity -= MDNS_POINTER_DIFF(remaindata, data); - if (!data || !capacity) - return 0; - return mdns_string_make_ref(MDNS_POINTER_OFFSET(remaindata, -1), capacity + 1, ref_offset); + void* remaindata = mdns_string_make(data, capacity, name, length); + capacity -= MDNS_POINTER_DIFF(remaindata, data); + if (!data || !capacity) + return 0; + return mdns_string_make_ref(MDNS_POINTER_OFFSET(remaindata, -1), capacity + 1, ref_offset); } static size_t mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const void* buffer, size_t size, size_t* offset, mdns_entry_type_t type, uint16_t query_id, size_t records, mdns_record_callback_fn callback, void* user_data) { - size_t parsed = 0; - int do_callback = (callback ? 1 : 0); - for (size_t i = 0; i < records; ++i) { - size_t name_offset = *offset; - mdns_string_skip(buffer, size, offset); - size_t name_length = (*offset) - name_offset; - const uint16_t* data = (const uint16_t*)((const char*)buffer + (*offset)); - - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - uint32_t ttl = ntohl(*(const uint32_t*)(const void*)data); - data += 2; - uint16_t length = ntohs(*data++); - - *offset += 10; - - if (do_callback) { - ++parsed; - if (callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size, - name_offset, name_length, *offset, length, user_data)) - do_callback = 0; - } - - *offset += length; - } - return parsed; + size_t parsed = 0; + int do_callback = (callback ? 1 : 0); + for (size_t i = 0; i < records; ++i) { + size_t name_offset = *offset; + mdns_string_skip(buffer, size, offset); + size_t name_length = (*offset) - name_offset; + const uint16_t* data = (const uint16_t*)((const char*)buffer + (*offset)); + + uint16_t rtype = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + uint32_t ttl = ntohl(*(const uint32_t*)(const void*)data); + data += 2; + uint16_t length = ntohs(*data++); + + *offset += 10; + + if (do_callback) { + ++parsed; + if (callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size, + name_offset, name_length, *offset, length, user_data)) + do_callback = 0; + } + + *offset += length; + } + return parsed; } static int mdns_unicast_send(int sock, const void* address, size_t address_size, const void* buffer, size_t size) { - if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address, - (socklen_t)address_size) < 0) - return -1; - return 0; + if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address, + (socklen_t)address_size) < 0) + return -1; + return 0; } static int mdns_multicast_send(int sock, const void* buffer, size_t size) { - struct sockaddr_storage addr_storage; - struct sockaddr_in addr; - struct sockaddr_in6 addr6; - struct sockaddr* saddr = (struct sockaddr*)&addr_storage; - socklen_t saddrlen = sizeof(struct sockaddr_storage); - if (getsockname(sock, saddr, &saddrlen)) - return -1; - if (saddr->sa_family == AF_INET6) { - memset(&addr6, 0, sizeof(addr6)); - addr6.sin6_family = AF_INET6; + struct sockaddr_storage addr_storage; + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + struct sockaddr* saddr = (struct sockaddr*)&addr_storage; + socklen_t saddrlen = sizeof(struct sockaddr_storage); + if (getsockname(sock, saddr, &saddrlen)) + return -1; + if (saddr->sa_family == AF_INET6) { + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; #ifdef __APPLE__ - addr6.sin6_len = sizeof(addr6); + addr6.sin6_len = sizeof(addr6); #endif - addr6.sin6_addr.s6_addr[0] = 0xFF; - addr6.sin6_addr.s6_addr[1] = 0x02; - addr6.sin6_addr.s6_addr[15] = 0xFB; - addr6.sin6_port = htons((unsigned short)MDNS_PORT); - saddr = (struct sockaddr*)&addr6; - saddrlen = sizeof(addr6); - } else { - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; + addr6.sin6_addr.s6_addr[0] = 0xFF; + addr6.sin6_addr.s6_addr[1] = 0x02; + addr6.sin6_addr.s6_addr[15] = 0xFB; + addr6.sin6_port = htons((unsigned short)MDNS_PORT); + saddr = (struct sockaddr*)&addr6; + saddrlen = sizeof(addr6); + } else { + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; #ifdef __APPLE__ - addr.sin_len = sizeof(addr); + addr.sin_len = sizeof(addr); #endif - addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); - addr.sin_port = htons((unsigned short)MDNS_PORT); - saddr = (struct sockaddr*)&addr; - saddrlen = sizeof(addr); - } - - if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0) - return -1; - return 0; + addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); + addr.sin_port = htons((unsigned short)MDNS_PORT); + saddr = (struct sockaddr*)&addr; + saddrlen = sizeof(addr); + } + + if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0) + return -1; + return 0; } static const uint8_t mdns_services_query[] = { @@ -689,307 +687,307 @@ static const uint8_t mdns_services_query[] = { static int mdns_discovery_send(int sock) { - return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query)); + return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query)); } static size_t mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data) { - struct sockaddr_in6 addr; - struct sockaddr* saddr = (struct sockaddr*)&addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); #ifdef __APPLE__ - saddr->sa_len = sizeof(addr); + saddr->sa_len = sizeof(addr); #endif - int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); - if (ret <= 0) - return 0; - - size_t data_size = (size_t)ret; - size_t records = 0; - uint16_t* data = (uint16_t*)buffer; - - uint16_t query_id = ntohs(*data++); - uint16_t flags = ntohs(*data++); - uint16_t questions = ntohs(*data++); - uint16_t answer_rrs = ntohs(*data++); - uint16_t authority_rrs = ntohs(*data++); - uint16_t additional_rrs = ntohs(*data++); - - // According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case) - if (query_id || (flags != 0x8400)) - return 0; // Not a reply to our question - - // It seems some implementations do not fill the correct questions field, - // so ignore this check for now and only validate answer string - /* - if (questions != 1) - return 0; - */ - - int i; - for (i = 0; i < questions; ++i) { - size_t ofs = (size_t)((char*)data - (char*)buffer); - size_t verify_ofs = 12; - // Verify it's our question, _services._dns-sd._udp.local. - if (!mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, - sizeof(mdns_services_query), &verify_ofs)) - return 0; - data = (uint16_t*)((char*)buffer + ofs); - - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - - // Make sure we get a reply based on our PTR question for class IN - if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN)) - return 0; - } - - int do_callback = 1; - for (i = 0; i < answer_rrs; ++i) { - size_t ofs = (size_t)((char*)data - (char*)buffer); - size_t verify_ofs = 12; - // Verify it's an answer to our question, _services._dns-sd._udp.local. - size_t name_offset = ofs; - int is_answer = mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, - sizeof(mdns_services_query), &verify_ofs); - size_t name_length = ofs - name_offset; - data = (uint16_t*)((char*)buffer + ofs); - - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - uint32_t ttl = ntohl(*(uint32_t*)(void*)data); - data += 2; - uint16_t length = ntohs(*data++); - if (length >= (data_size - ofs)) - return 0; - - if (is_answer && do_callback) { - ++records; - ofs = (size_t)((char*)data - (char*)buffer); - if (callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, - buffer, data_size, name_offset, name_length, ofs, length, user_data)) - do_callback = 0; - } - data = (uint16_t*)((char*)data + length); - } - - size_t offset = (size_t)((char*)data - (char*)buffer); - records += - mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); - records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, - user_data); - - return records; + int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + size_t records = 0; + uint16_t* data = (uint16_t*)buffer; + + uint16_t query_id = ntohs(*data++); + uint16_t flags = ntohs(*data++); + uint16_t questions = ntohs(*data++); + uint16_t answer_rrs = ntohs(*data++); + uint16_t authority_rrs = ntohs(*data++); + uint16_t additional_rrs = ntohs(*data++); + + // According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case) + if (query_id || (flags != 0x8400)) + return 0; // Not a reply to our question + + // It seems some implementations do not fill the correct questions field, + // so ignore this check for now and only validate answer string + /* + if (questions != 1) + return 0; + */ + + int i; + for (i = 0; i < questions; ++i) { + size_t ofs = (size_t)((char*)data - (char*)buffer); + size_t verify_ofs = 12; + // Verify it's our question, _services._dns-sd._udp.local. + if (!mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, + sizeof(mdns_services_query), &verify_ofs)) + return 0; + data = (uint16_t*)((char*)buffer + ofs); + + uint16_t rtype = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + + // Make sure we get a reply based on our PTR question for class IN + if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN)) + return 0; + } + + int do_callback = 1; + for (i = 0; i < answer_rrs; ++i) { + size_t ofs = (size_t)((char*)data - (char*)buffer); + size_t verify_ofs = 12; + // Verify it's an answer to our question, _services._dns-sd._udp.local. + size_t name_offset = ofs; + int is_answer = mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, + sizeof(mdns_services_query), &verify_ofs); + size_t name_length = ofs - name_offset; + data = (uint16_t*)((char*)buffer + ofs); + + uint16_t rtype = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + uint32_t ttl = ntohl(*(uint32_t*)(void*)data); + data += 2; + uint16_t length = ntohs(*data++); + if (length >= (data_size - ofs)) + return 0; + + if (is_answer && do_callback) { + ++records; + ofs = (size_t)((char*)data - (char*)buffer); + if (callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, + buffer, data_size, name_offset, name_length, ofs, length, user_data)) + do_callback = 0; + } + data = (uint16_t*)((char*)data + length); + } + + size_t offset = (size_t)((char*)data - (char*)buffer); + records += + mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); + records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, + user_data); + + return records; } static size_t mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data) { - struct sockaddr_in6 addr; - struct sockaddr* saddr = (struct sockaddr*)&addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); #ifdef __APPLE__ - saddr->sa_len = sizeof(addr); + saddr->sa_len = sizeof(addr); #endif - int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); - if (ret <= 0) - return 0; - - size_t data_size = (size_t)ret; - uint16_t* data = (uint16_t*)buffer; - - uint16_t query_id = ntohs(*data++); - uint16_t flags = ntohs(*data++); - uint16_t questions = ntohs(*data++); - /* - This data is unused at the moment, skip - uint16_t answer_rrs = ntohs(*data++); - uint16_t authority_rrs = ntohs(*data++); - uint16_t additional_rrs = ntohs(*data++); - */ - data += 3; - - size_t parsed = 0; - for (int iquestion = 0; iquestion < questions; ++iquestion) { - size_t question_offset = (size_t)((char*)data - (char*)buffer); - size_t offset = question_offset; - size_t verify_ofs = 12; - if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, - sizeof(mdns_services_query), &verify_ofs)) { - if (flags || (questions != 1)) - return 0; - } else { - offset = question_offset; - if (!mdns_string_skip(buffer, data_size, &offset)) - break; - } - size_t length = offset - question_offset; - data = (uint16_t*)((char*)buffer + offset); - - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - - // Make sure we get a question of class IN - if ((rclass & 0x7FFF) != MDNS_CLASS_IN) - return 0; - - if (callback) - callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, rclass, 0, - buffer, data_size, question_offset, length, question_offset, length, - user_data); - - ++parsed; - } - - return parsed; + int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + uint16_t* data = (uint16_t*)buffer; + + uint16_t query_id = ntohs(*data++); + uint16_t flags = ntohs(*data++); + uint16_t questions = ntohs(*data++); + /* + This data is unused at the moment, skip + uint16_t answer_rrs = ntohs(*data++); + uint16_t authority_rrs = ntohs(*data++); + uint16_t additional_rrs = ntohs(*data++); + */ + data += 3; + + size_t parsed = 0; + for (int iquestion = 0; iquestion < questions; ++iquestion) { + size_t question_offset = (size_t)((char*)data - (char*)buffer); + size_t offset = question_offset; + size_t verify_ofs = 12; + if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, + sizeof(mdns_services_query), &verify_ofs)) { + if (flags || (questions != 1)) + return 0; + } else { + offset = question_offset; + if (!mdns_string_skip(buffer, data_size, &offset)) + break; + } + size_t length = offset - question_offset; + data = (uint16_t*)((char*)buffer + offset); + + uint16_t rtype = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + + // Make sure we get a question of class IN + if ((rclass & 0x7FFF) != MDNS_CLASS_IN) + return 0; + + if (callback) + callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, rclass, 0, + buffer, data_size, question_offset, length, question_offset, length, + user_data); + + ++parsed; + } + + return parsed; } static int mdns_discovery_answer(int sock, const void* address, size_t address_size, void* buffer, size_t capacity, const char* record, size_t length) { - if (capacity < (sizeof(mdns_services_query) + 32 + length)) - return -1; - - uint16_t* data = (uint16_t*)buffer; - // Basic reply structure - memcpy(data, mdns_services_query, sizeof(mdns_services_query)); - // Flags - uint16_t* flags = data + 1; - *flags = htons(0x8400U); - // One answer - uint16_t* answers = data + 3; - *answers = htons(1); - - // Fill in answer PTR record - data = (uint16_t*)((char*)buffer + sizeof(mdns_services_query)); - // Reference _services._dns-sd._udp.local. string in question - *data++ = htons(0xC000U | 12U); - // Type - *data++ = htons(MDNS_RECORDTYPE_PTR); - // Rclass - *data++ = htons(MDNS_CLASS_IN); - // TTL - *(uint32_t*)data = htonl(10); - data += 2; - // Record string length - uint16_t* record_length = data++; - uint8_t* record_data = (uint8_t*)data; - size_t remain = capacity - (sizeof(mdns_services_query) + 10); - record_data = (uint8_t*)mdns_string_make(record_data, remain, record, length); - *record_length = htons((uint16_t)(record_data - (uint8_t*)data)); - *record_data++ = 0; - - ptrdiff_t tosend = (char*)record_data - (char*)buffer; - return mdns_unicast_send(sock, address, address_size, buffer, (size_t)tosend); + if (capacity < (sizeof(mdns_services_query) + 32 + length)) + return -1; + + uint16_t* data = (uint16_t*)buffer; + // Basic reply structure + memcpy(data, mdns_services_query, sizeof(mdns_services_query)); + // Flags + uint16_t* flags = data + 1; + *flags = htons(0x8400U); + // One answer + uint16_t* answers = data + 3; + *answers = htons(1); + + // Fill in answer PTR record + data = (uint16_t*)((char*)buffer + sizeof(mdns_services_query)); + // Reference _services._dns-sd._udp.local. string in question + *data++ = htons(0xC000U | 12U); + // Type + *data++ = htons(MDNS_RECORDTYPE_PTR); + // Rclass + *data++ = htons(MDNS_CLASS_IN); + // TTL + *(uint32_t*)data = htonl(10); + data += 2; + // Record string length + uint16_t* record_length = data++; + uint8_t* record_data = (uint8_t*)data; + size_t remain = capacity - (sizeof(mdns_services_query) + 10); + record_data = (uint8_t*)mdns_string_make(record_data, remain, record, length); + *record_length = htons((uint16_t)(record_data - (uint8_t*)data)); + *record_data++ = 0; + + ptrdiff_t tosend = (char*)record_data - (char*)buffer; + return mdns_unicast_send(sock, address, address_size, buffer, (size_t)tosend); } static int 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) { - if (capacity < (17 + length)) - return -1; - - uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE; - - struct sockaddr_storage addr_storage; - struct sockaddr* saddr = (struct sockaddr*)&addr_storage; - socklen_t saddrlen = sizeof(addr_storage); - if (getsockname(sock, saddr, &saddrlen) == 0) { - if ((saddr->sa_family == AF_INET) && - (ntohs(((struct sockaddr_in*)saddr)->sin_port) == MDNS_PORT)) - rclass &= ~MDNS_UNICAST_RESPONSE; - else if ((saddr->sa_family == AF_INET6) && - (ntohs(((struct sockaddr_in6*)saddr)->sin6_port) == MDNS_PORT)) - rclass &= ~MDNS_UNICAST_RESPONSE; - } - - uint16_t* data = (uint16_t*)buffer; - // Query ID - *data++ = htons(query_id); - // Flags - *data++ = 0; - // Questions - *data++ = htons(1); - // No answer, authority or additional RRs - *data++ = 0; - *data++ = 0; - *data++ = 0; - // Fill in question - // Name string - data = (uint16_t*)mdns_string_make(data, capacity - 17, name, length); - if (!data) - return -1; - // Record type - *data++ = htons(type); - //! Optional unicast response based on local port, class IN - *data++ = htons(rclass); - - ptrdiff_t tosend = (char*)data - (char*)buffer; - if (mdns_multicast_send(sock, buffer, (size_t)tosend)) - return -1; - return query_id; + if (capacity < (17 + length)) + return -1; + + uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE; + + struct sockaddr_storage addr_storage; + struct sockaddr* saddr = (struct sockaddr*)&addr_storage; + socklen_t saddrlen = sizeof(addr_storage); + if (getsockname(sock, saddr, &saddrlen) == 0) { + if ((saddr->sa_family == AF_INET) && + (ntohs(((struct sockaddr_in*)saddr)->sin_port) == MDNS_PORT)) + rclass &= ~MDNS_UNICAST_RESPONSE; + else if ((saddr->sa_family == AF_INET6) && + (ntohs(((struct sockaddr_in6*)saddr)->sin6_port) == MDNS_PORT)) + rclass &= ~MDNS_UNICAST_RESPONSE; + } + + uint16_t* data = (uint16_t*)buffer; + // Query ID + *data++ = htons(query_id); + // Flags + *data++ = 0; + // Questions + *data++ = htons(1); + // No answer, authority or additional RRs + *data++ = 0; + *data++ = 0; + *data++ = 0; + // Fill in question + // Name string + data = (uint16_t*)mdns_string_make(data, capacity - 17, name, length); + if (!data) + return -1; + // Record type + *data++ = htons(type); + //! Optional unicast response based on local port, class IN + *data++ = htons(rclass); + + ptrdiff_t tosend = (char*)data - (char*)buffer; + if (mdns_multicast_send(sock, buffer, (size_t)tosend)) + return -1; + return query_id; } static size_t mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data, int only_query_id) { - struct sockaddr_in6 addr; - struct sockaddr* saddr = (struct sockaddr*)&addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); #ifdef __APPLE__ - saddr->sa_len = sizeof(addr); + saddr->sa_len = sizeof(addr); #endif - int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); - if (ret <= 0) - return 0; - - size_t data_size = (size_t)ret; - uint16_t* data = (uint16_t*)buffer; - - uint16_t query_id = ntohs(*data++); - uint16_t flags = ntohs(*data++); - uint16_t questions = ntohs(*data++); - uint16_t answer_rrs = ntohs(*data++); - uint16_t authority_rrs = ntohs(*data++); - uint16_t additional_rrs = ntohs(*data++); - (void)sizeof(flags); - - if ((only_query_id > 0) && (query_id != only_query_id)) - return 0; // Not a reply to the wanted one-shot query - - if (questions > 1) - return 0; - - // Skip questions part - int i; - for (i = 0; i < questions; ++i) { - size_t ofs = (size_t)((char*)data - (char*)buffer); - if (!mdns_string_skip(buffer, data_size, &ofs)) - return 0; - data = (uint16_t*)((char*)buffer + ofs); - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - (void)sizeof(rtype); - (void)sizeof(rclass); - } - - size_t records = 0; - 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); - records += - mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); - records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, - user_data); - return records; + int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + uint16_t* data = (uint16_t*)buffer; + + uint16_t query_id = ntohs(*data++); + uint16_t flags = ntohs(*data++); + uint16_t questions = ntohs(*data++); + uint16_t answer_rrs = ntohs(*data++); + uint16_t authority_rrs = ntohs(*data++); + uint16_t additional_rrs = ntohs(*data++); + (void)sizeof(flags); + + if ((only_query_id > 0) && (query_id != only_query_id)) + return 0; // Not a reply to the wanted one-shot query + + if (questions > 1) + return 0; + + // Skip questions part + int i; + for (i = 0; i < questions; ++i) { + size_t ofs = (size_t)((char*)data - (char*)buffer); + if (!mdns_string_skip(buffer, data_size, &ofs)) + return 0; + data = (uint16_t*)((char*)buffer + ofs); + uint16_t rtype = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + (void)sizeof(rtype); + (void)sizeof(rclass); + } + + size_t records = 0; + 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); + records += + mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); + records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, + user_data); + return records; } static int @@ -997,268 +995,268 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff uint16_t query_id, const char* service, size_t service_length, const char* hostname, size_t hostname_length, uint32_t ipv4, const uint8_t* ipv6, uint16_t port, const char* txt, size_t txt_length) { - if (capacity < (sizeof(struct mdns_header_t) + 32 + service_length + hostname_length)) - return -1; - - int unicast = (address_size ? 1 : 0); - int use_ipv4 = (ipv4 != 0); - int use_ipv6 = (ipv6 != 0); - int use_txt = (txt && txt_length && (txt_length <= 255)); - - uint16_t question_rclass = (unicast ? MDNS_UNICAST_RESPONSE : 0) | MDNS_CLASS_IN; - uint16_t rclass = (unicast ? MDNS_CACHE_FLUSH : 0) | MDNS_CLASS_IN; - uint32_t ttl = (unicast ? 10 : 60); - uint32_t a_ttl = ttl; - - // Basic answer structure - struct mdns_header_t* header = (struct mdns_header_t*)buffer; - header->query_id = (address_size ? htons(query_id) : 0); - header->flags = htons(0x8400); - header->questions = htons(unicast ? 1 : 0); - header->answer_rrs = htons(1); - header->authority_rrs = 0; - header->additional_rrs = htons((unsigned short)(1 + use_ipv4 + use_ipv6 + use_txt)); - - void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - uint16_t* udata; - size_t remain, service_offset = 0, local_offset = 0, full_offset, host_offset; - - // Fill in question if unicast - if (unicast) { - service_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - service_offset; - data = mdns_string_make(data, remain, service, service_length); - local_offset = MDNS_POINTER_DIFF(data, buffer) - 7; - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 4)) - return -1; - - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_PTR); - *udata++ = htons(question_rclass); - data = udata; - } - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - - // Fill in answers - // PTR record for service - if (unicast) { - data = mdns_string_make_ref(data, remain, service_offset); - } else { - service_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - service_offset; - data = mdns_string_make(data, remain, service, service_length); - local_offset = MDNS_POINTER_DIFF(data, buffer) - 7; - } - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) - return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_PTR); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(ttl); - udata += 2; - uint16_t* record_length = udata++; // length - data = udata; - // Make a string ..local. - full_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - full_offset; - data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, service_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) - return -1; - *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1)); - - // Fill in additional records - // SRV record for ..local. - data = mdns_string_make_ref(data, remain, full_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) - return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_SRV); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(ttl); - udata += 2; - record_length = udata++; // length - *udata++ = htons(0); // priority - *udata++ = htons(0); // weight - *udata++ = htons(port); // port - // Make a string .local. - data = udata; - host_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - host_offset; - data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, local_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) - return -1; - *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1)); - - // A record for .local. - if (use_ipv4) { - data = mdns_string_make_ref(data, remain, host_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 14)) - return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_A); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(a_ttl); - udata += 2; - *udata++ = htons(4); // length - *(uint32_t*)udata = ipv4; // ipv4 address - udata += 2; - data = udata; - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - } - - // AAAA record for .local. - if (use_ipv6) { - data = mdns_string_make_ref(data, remain, host_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 26)) - return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_AAAA); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(a_ttl); - udata += 2; - *udata++ = htons(16); // length - memcpy(udata, ipv6, 16); // ipv6 address - data = MDNS_POINTER_OFFSET(udata, 16); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - } - - // TXT record for ..local. - if (use_txt) { - data = mdns_string_make_ref(data, remain, full_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= (11 + txt_length))) - return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_TXT); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(ttl); - udata += 2; - *udata++ = htons((unsigned short)(txt_length + 1)); // length - char* txt_record = (char*)udata; - *txt_record++ = (char)txt_length; - memcpy(txt_record, txt, txt_length); // txt record - data = MDNS_POINTER_OFFSET(txt_record, txt_length); - // Unused until multiple txt records are supported - // remain = capacity - MDNS_POINTER_DIFF(data, buffer); - } - - size_t tosend = MDNS_POINTER_DIFF(data, buffer); - if (address_size) - return mdns_unicast_send(sock, address, address_size, buffer, tosend); - return mdns_multicast_send(sock, buffer, tosend); + if (capacity < (sizeof(struct mdns_header_t) + 32 + service_length + hostname_length)) + return -1; + + int unicast = (address_size ? 1 : 0); + int use_ipv4 = (ipv4 != 0); + int use_ipv6 = (ipv6 != 0); + int use_txt = (txt && txt_length && (txt_length <= 255)); + + uint16_t question_rclass = (unicast ? MDNS_UNICAST_RESPONSE : 0) | MDNS_CLASS_IN; + uint16_t rclass = (unicast ? MDNS_CACHE_FLUSH : 0) | MDNS_CLASS_IN; + uint32_t ttl = (unicast ? 10 : 60); + uint32_t a_ttl = ttl; + + // Basic answer structure + struct mdns_header_t* header = (struct mdns_header_t*)buffer; + header->query_id = (address_size ? htons(query_id) : 0); + header->flags = htons(0x8400); + header->questions = htons(unicast ? 1 : 0); + header->answer_rrs = htons(1); + header->authority_rrs = 0; + header->additional_rrs = htons((unsigned short)(1 + use_ipv4 + use_ipv6 + use_txt)); + + void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); + uint16_t* udata; + size_t remain, service_offset = 0, local_offset = 0, full_offset, host_offset; + + // Fill in question if unicast + if (unicast) { + service_offset = MDNS_POINTER_DIFF(data, buffer); + remain = capacity - service_offset; + data = mdns_string_make(data, remain, service, service_length); + local_offset = MDNS_POINTER_DIFF(data, buffer) - 7; + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 4)) + return -1; + + udata = (uint16_t*)data; + *udata++ = htons(MDNS_RECORDTYPE_PTR); + *udata++ = htons(question_rclass); + data = udata; + } + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + + // Fill in answers + // PTR record for service + if (unicast) { + data = mdns_string_make_ref(data, remain, service_offset); + } else { + service_offset = MDNS_POINTER_DIFF(data, buffer); + remain = capacity - service_offset; + data = mdns_string_make(data, remain, service, service_length); + local_offset = MDNS_POINTER_DIFF(data, buffer) - 7; + } + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 10)) + return -1; + udata = (uint16_t*)data; + *udata++ = htons(MDNS_RECORDTYPE_PTR); + *udata++ = htons(rclass); + *(uint32_t*)udata = htonl(ttl); + udata += 2; + uint16_t* record_length = udata++; // length + data = udata; + // Make a string ..local. + full_offset = MDNS_POINTER_DIFF(data, buffer); + remain = capacity - full_offset; + data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, service_offset); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 10)) + return -1; + *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1)); + + // Fill in additional records + // SRV record for ..local. + data = mdns_string_make_ref(data, remain, full_offset); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 10)) + return -1; + udata = (uint16_t*)data; + *udata++ = htons(MDNS_RECORDTYPE_SRV); + *udata++ = htons(rclass); + *(uint32_t*)udata = htonl(ttl); + udata += 2; + record_length = udata++; // length + *udata++ = htons(0); // priority + *udata++ = htons(0); // weight + *udata++ = htons(port); // port + // Make a string .local. + data = udata; + host_offset = MDNS_POINTER_DIFF(data, buffer); + remain = capacity - host_offset; + data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, local_offset); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 10)) + return -1; + *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1)); + + // A record for .local. + if (use_ipv4) { + data = mdns_string_make_ref(data, remain, host_offset); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 14)) + return -1; + udata = (uint16_t*)data; + *udata++ = htons(MDNS_RECORDTYPE_A); + *udata++ = htons(rclass); + *(uint32_t*)udata = htonl(a_ttl); + udata += 2; + *udata++ = htons(4); // length + *(uint32_t*)udata = ipv4; // ipv4 address + udata += 2; + data = udata; + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + } + + // AAAA record for .local. + if (use_ipv6) { + data = mdns_string_make_ref(data, remain, host_offset); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 26)) + return -1; + udata = (uint16_t*)data; + *udata++ = htons(MDNS_RECORDTYPE_AAAA); + *udata++ = htons(rclass); + *(uint32_t*)udata = htonl(a_ttl); + udata += 2; + *udata++ = htons(16); // length + memcpy(udata, ipv6, 16); // ipv6 address + data = MDNS_POINTER_OFFSET(udata, 16); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + } + + // TXT record for ..local. + if (use_txt) { + data = mdns_string_make_ref(data, remain, full_offset); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= (11 + txt_length))) + return -1; + udata = (uint16_t*)data; + *udata++ = htons(MDNS_RECORDTYPE_TXT); + *udata++ = htons(rclass); + *(uint32_t*)udata = htonl(ttl); + udata += 2; + *udata++ = htons((unsigned short)(txt_length + 1)); // length + char* txt_record = (char*)udata; + *txt_record++ = (char)txt_length; + memcpy(txt_record, txt, txt_length); // txt record + data = MDNS_POINTER_OFFSET(txt_record, txt_length); + // Unused until multiple txt records are supported + // remain = capacity - MDNS_POINTER_DIFF(data, buffer); + } + + size_t tosend = MDNS_POINTER_DIFF(data, buffer); + if (address_size) + return mdns_unicast_send(sock, address, address_size, buffer, tosend); + return mdns_multicast_send(sock, buffer, tosend); } static mdns_string_t mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { - // PTR record is just a string - if ((size >= offset + length) && (length >= 2)) - return mdns_string_extract(buffer, size, &offset, strbuffer, capacity); - mdns_string_t empty = {0, 0}; - return empty; + // PTR record is just a string + if ((size >= offset + length) && (length >= 2)) + return mdns_string_extract(buffer, size, &offset, strbuffer, capacity); + mdns_string_t empty = {0, 0}; + return empty; } static mdns_record_srv_t mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { - mdns_record_srv_t srv; - memset(&srv, 0, sizeof(mdns_record_srv_t)); - // Read the priority, weight, port number and the discovery name - // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): - // 2 bytes network-order unsigned priority - // 2 bytes network-order unsigned weight - // 2 bytes network-order unsigned port - // string: discovery (domain) name, minimum 2 bytes when compressed - if ((size >= offset + length) && (length >= 8)) { - const uint16_t* recorddata = (const uint16_t*)((const char*)buffer + offset); - srv.priority = ntohs(*recorddata++); - srv.weight = ntohs(*recorddata++); - srv.port = ntohs(*recorddata++); - offset += 6; - srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity); - } - return srv; + mdns_record_srv_t srv; + memset(&srv, 0, sizeof(mdns_record_srv_t)); + // Read the priority, weight, port number and the discovery name + // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): + // 2 bytes network-order unsigned priority + // 2 bytes network-order unsigned weight + // 2 bytes network-order unsigned port + // string: discovery (domain) name, minimum 2 bytes when compressed + if ((size >= offset + length) && (length >= 8)) { + const uint16_t* recorddata = (const uint16_t*)((const char*)buffer + offset); + srv.priority = ntohs(*recorddata++); + srv.weight = ntohs(*recorddata++); + srv.port = ntohs(*recorddata++); + offset += 6; + srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity); + } + return srv; } static struct sockaddr_in* mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in* addr) { - memset(addr, 0, sizeof(struct sockaddr_in)); - addr->sin_family = AF_INET; + memset(addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; #ifdef __APPLE__ - addr->sin_len = sizeof(struct sockaddr_in); + addr->sin_len = sizeof(struct sockaddr_in); #endif - if ((size >= offset + length) && (length == 4)) - addr->sin_addr.s_addr = *(const uint32_t*)((const char*)buffer + offset); - return addr; + if ((size >= offset + length) && (length == 4)) + addr->sin_addr.s_addr = *(const uint32_t*)((const char*)buffer + offset); + return addr; } static struct sockaddr_in6* mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in6* addr) { - memset(addr, 0, sizeof(struct sockaddr_in6)); - addr->sin6_family = AF_INET6; + memset(addr, 0, sizeof(struct sockaddr_in6)); + addr->sin6_family = AF_INET6; #ifdef __APPLE__ - addr->sin6_len = sizeof(struct sockaddr_in6); + addr->sin6_len = sizeof(struct sockaddr_in6); #endif - if ((size >= offset + length) && (length == 16)) - addr->sin6_addr = *(const struct in6_addr*)((const char*)buffer + offset); - return addr; + if ((size >= offset + length) && (length == 16)) + addr->sin6_addr = *(const struct in6_addr*)((const char*)buffer + offset); + return addr; } static size_t mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, mdns_record_txt_t* records, size_t capacity) { - size_t parsed = 0; - const char* strdata; - size_t separator, sublength; - size_t end = offset + length; - - if (size < end) - end = size; - - while ((offset < end) && (parsed < capacity)) { - strdata = (const char*)buffer + offset; - sublength = *(const unsigned char*)strdata; - - ++strdata; - offset += sublength + 1; - - separator = 0; - for (size_t c = 0; c < sublength; ++c) { - // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] - if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) - break; - if (strdata[c] == '=') { - separator = c; - break; - } - } - - if (!separator) - continue; - - if (separator < sublength) { - records[parsed].key.str = strdata; - records[parsed].key.length = separator; - records[parsed].value.str = strdata + separator + 1; - records[parsed].value.length = sublength - (separator + 1); - } else { - records[parsed].key.str = strdata; - records[parsed].key.length = sublength; - } - - ++parsed; - } - - return parsed; + size_t parsed = 0; + const char* strdata; + size_t separator, sublength; + size_t end = offset + length; + + if (size < end) + end = size; + + while ((offset < end) && (parsed < capacity)) { + strdata = (const char*)buffer + offset; + sublength = *(const unsigned char*)strdata; + + ++strdata; + offset += sublength + 1; + + separator = 0; + for (size_t c = 0; c < sublength; ++c) { + // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] + if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) + break; + if (strdata[c] == '=') { + separator = c; + break; + } + } + + if (!separator) + continue; + + if (separator < sublength) { + records[parsed].key.str = strdata; + records[parsed].key.length = separator; + records[parsed].value.str = strdata + separator + 1; + records[parsed].value.length = sublength - (separator + 1); + } else { + records[parsed].key.str = strdata; + records[parsed].key.length = sublength; + } + + ++parsed; + } + + return parsed; } #ifdef _WIN32 @@ -1267,4 +1265,4 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len #ifdef __cplusplus } -#endif \ No newline at end of file +#endif From e28d84ebb96177c504928ee913e8446014f26244 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Thu, 31 Oct 2024 12:15:08 +0200 Subject: [PATCH 02/37] mdns.h - use latest commit with non-breaking API. (4c64fbaa4a5dcac567c63f182cac2d7a4adc903c) --- src/mdns.h | 349 +++++++++++++++++++++++++++++------------------------ 1 file changed, 189 insertions(+), 160 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 283a441..2caa78a 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -46,6 +46,7 @@ extern "C" { #define MDNS_PORT 5353 #define MDNS_UNICAST_RESPONSE 0x8000U #define MDNS_CACHE_FLUSH 0x8000U +#define MDNS_MAX_SUBSTRINGS 64 enum mdns_record_type { MDNS_RECORDTYPE_IGNORE = 0, @@ -87,8 +88,10 @@ typedef struct mdns_record_txt_t mdns_record_txt_t; #ifdef _WIN32 typedef int mdns_size_t; +typedef int mdns_ssize_t; #else typedef size_t mdns_size_t; +typedef ssize_t mdns_ssize_t; #endif struct mdns_string_t { @@ -162,8 +165,8 @@ static void mdns_socket_close(int sock); //! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been -// opened on port MDNS_PORT using one of the mdns open or setup socket functions. Returns the -// number of queries parsed. +// opened on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be +// 32 bit aligned. Returns the number of queries parsed. static size_t mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); @@ -174,23 +177,24 @@ static int mdns_discovery_send(int sock); //! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to -// the given callback for parsing. Returns the number of responses parsed. +// the given callback for parsing. Buffer must be 32 bit aligned. Returns the number of +// responses parsed. static size_t mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); -//! Send a unicast DNS-SD answer with a single record to the given address. Returns 0 if success, -// or <0 if error. +//! Send a unicast DNS-SD answer with a single record to the given address. Buffer must be 32 bit +// aligned. Returns 0 if success, or <0 if error. static int mdns_discovery_answer(int sock, const void* address, size_t address_size, void* buffer, size_t capacity, const char* record, size_t length); //! Send a multicast mDNS query on the given socket for the given service name. The supplied buffer -// will be used to build the query packet. 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. 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. +// 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. 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 int 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); @@ -198,7 +202,8 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng //! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering // 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 given callback for parsing. Returns the number of responses parsed. +// be piped to the given callback for parsing. Buffer must be 32 bit aligned. Returns the number +// of responses parsed. static size_t mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data, int query_id); @@ -206,8 +211,8 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn //! Send a unicast or multicast mDNS query answer with a single record to the given address. The // answer will be sent multicast if address size is 0, otherwise it will be sent unicast to the // given address. Use the top bit of the query class field (MDNS_UNICAST_RESPONSE) to determine -// if the answer should be sent unicast (bit set) or multicast (bit not set). -// Returns 0 if success, or <0 if error. +// if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be +// 32 bit aligned. Returns 0 if success, or <0 if error. static int mdns_query_answer(int sock, const void* address, size_t address_size, void* buffer, size_t capacity, uint16_t query_id, const char* service, size_t service_length, @@ -258,6 +263,34 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len // Implementations +static uint16_t +mdns_ntohs(const void* data) { + uint16_t aligned; + memcpy(&aligned, data, sizeof(uint16_t)); + return ntohs(aligned); +} + +static uint32_t +mdns_ntohl(const void* data) { + uint32_t aligned; + memcpy(&aligned, data, sizeof(uint32_t)); + return ntohl(aligned); +} + +static void* +mdns_htons(void* data, uint16_t val) { + val = htons(val); + memcpy(data, &val, sizeof(uint16_t)); + return MDNS_POINTER_OFFSET(data, sizeof(uint16_t)); +} + +static void* +mdns_htonl(void* data, uint32_t val) { + val = htonl(val); + memcpy(data, &val, sizeof(uint32_t)); + return MDNS_POINTER_OFFSET(data, sizeof(uint32_t)); +} + static int mdns_socket_open_ipv4(struct sockaddr_in* saddr) { int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -404,6 +437,8 @@ static mdns_string_pair_t mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { const uint8_t* buffer = (const uint8_t*)rawdata; mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0}; + if (offset >= size) + return pair; if (!buffer[offset]) { pair.offset = offset; return pair; @@ -412,7 +447,7 @@ mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { if (size < offset + 2) return pair; - offset = 0x3fff & ntohs(*(uint16_t*)MDNS_POINTER_OFFSET(buffer, offset)); + offset = mdns_ntohs(MDNS_POINTER_OFFSET(buffer, offset)) & 0x3fff; if (offset >= size) return pair; @@ -433,9 +468,10 @@ static int mdns_string_skip(const void* buffer, size_t size, size_t* offset) { size_t cur = *offset; mdns_string_pair_t substr; + unsigned int counter = 0; do { substr = mdns_get_next_substring(buffer, size, cur); - if (substr.offset == MDNS_INVALID_POS) + if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS)) return 0; if (substr.ref) { *offset = cur + 2; @@ -457,15 +493,18 @@ mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, cons size_t rhs_end = MDNS_INVALID_POS; mdns_string_pair_t lhs_substr; mdns_string_pair_t rhs_substr; + unsigned int counter = 0; do { lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur); rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur); - if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS)) + if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS) || + (counter++ > MDNS_MAX_SUBSTRINGS)) return 0; if (lhs_substr.length != rhs_substr.length) return 0; - if (strncasecmp((const char*)buffer_rhs + rhs_substr.offset, - (const char*)buffer_lhs + lhs_substr.offset, rhs_substr.length)) + if (strncasecmp((const char*)MDNS_POINTER_OFFSET_CONST(buffer_rhs, rhs_substr.offset), + (const char*)MDNS_POINTER_OFFSET_CONST(buffer_lhs, lhs_substr.offset), + rhs_substr.length)) return 0; if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS)) lhs_end = lhs_cur + 2; @@ -495,10 +534,11 @@ mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, result.str = str; result.length = 0; char* dst = str; + unsigned int counter = 0; size_t remain = capacity; do { substr = mdns_get_next_substring(buffer, size, cur); - if (substr.offset == MDNS_INVALID_POS) + if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS)) return result; if (substr.ref && (end == MDNS_INVALID_POS)) end = cur + 2; @@ -530,7 +570,7 @@ mdns_string_find(const char* str, size_t length, char c, size_t offset) { return MDNS_INVALID_POS; found = memchr(str + offset, c, length - offset); if (found) - return (size_t)((const char*)found - str); + return (size_t)MDNS_POINTER_DIFF(found, str); return MDNS_INVALID_POS; } @@ -574,9 +614,7 @@ static void* mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { if (capacity < 2) return 0; - uint16_t* udata = (uint16_t*)data; - *udata++ = htons(0xC000 | (uint16_t)ref_offset); - return udata; + return mdns_htons(data, 0xC000 | (uint16_t)ref_offset); } static void* @@ -598,18 +636,20 @@ mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const for (size_t i = 0; i < records; ++i) { size_t name_offset = *offset; mdns_string_skip(buffer, size, offset); + if (((*offset) + 10) > size) + return parsed; size_t name_length = (*offset) - name_offset; - const uint16_t* data = (const uint16_t*)((const char*)buffer + (*offset)); + const uint16_t* data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, *offset); - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - uint32_t ttl = ntohl(*(const uint32_t*)(const void*)data); + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); + uint32_t ttl = mdns_ntohl(data); data += 2; - uint16_t length = ntohs(*data++); + uint16_t length = mdns_ntohs(data++); *offset += 10; - if (do_callback) { + if (do_callback && (length <= (size - (*offset)))) { ++parsed; if (callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size, name_offset, name_length, *offset, length, user_data)) @@ -700,20 +740,20 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac #ifdef __APPLE__ saddr->sa_len = sizeof(addr); #endif - int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); if (ret <= 0) return 0; size_t data_size = (size_t)ret; size_t records = 0; - uint16_t* data = (uint16_t*)buffer; + const uint16_t* data = (uint16_t*)buffer; - uint16_t query_id = ntohs(*data++); - uint16_t flags = ntohs(*data++); - uint16_t questions = ntohs(*data++); - uint16_t answer_rrs = ntohs(*data++); - uint16_t authority_rrs = ntohs(*data++); - uint16_t additional_rrs = ntohs(*data++); + uint16_t query_id = mdns_ntohs(data++); + uint16_t flags = mdns_ntohs(data++); + uint16_t questions = mdns_ntohs(data++); + uint16_t answer_rrs = mdns_ntohs(data++); + uint16_t authority_rrs = mdns_ntohs(data++); + uint16_t additional_rrs = mdns_ntohs(data++); // According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case) if (query_id || (flags != 0x8400)) @@ -728,52 +768,54 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac int i; for (i = 0; i < questions; ++i) { - size_t ofs = (size_t)((char*)data - (char*)buffer); + size_t ofs = MDNS_POINTER_DIFF(data, buffer); size_t verify_ofs = 12; // Verify it's our question, _services._dns-sd._udp.local. if (!mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, sizeof(mdns_services_query), &verify_ofs)) return 0; - data = (uint16_t*)((char*)buffer + ofs); + data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, ofs); - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); // Make sure we get a reply based on our PTR question for class IN if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN)) return 0; } - int do_callback = 1; + int do_callback = (callback ? 1 : 0); for (i = 0; i < answer_rrs; ++i) { - size_t ofs = (size_t)((char*)data - (char*)buffer); + size_t ofs = MDNS_POINTER_DIFF(data, buffer); size_t verify_ofs = 12; // Verify it's an answer to our question, _services._dns-sd._udp.local. size_t name_offset = ofs; int is_answer = mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, sizeof(mdns_services_query), &verify_ofs); size_t name_length = ofs - name_offset; - data = (uint16_t*)((char*)buffer + ofs); + if ((ofs + 10) > data_size) + return records; + data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, ofs); - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - uint32_t ttl = ntohl(*(uint32_t*)(void*)data); + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); + uint32_t ttl = mdns_ntohl(data); data += 2; - uint16_t length = ntohs(*data++); - if (length >= (data_size - ofs)) + uint16_t length = mdns_ntohs(data++); + if (length > (data_size - ofs)) return 0; if (is_answer && do_callback) { ++records; - ofs = (size_t)((char*)data - (char*)buffer); + ofs = MDNS_POINTER_DIFF(data, buffer); if (callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, buffer, data_size, name_offset, name_length, ofs, length, user_data)) do_callback = 0; } - data = (uint16_t*)((char*)data + length); + data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length); } - size_t offset = (size_t)((char*)data - (char*)buffer); + size_t offset = MDNS_POINTER_DIFF(data, buffer); records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); @@ -794,27 +836,27 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback #ifdef __APPLE__ saddr->sa_len = sizeof(addr); #endif - int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); if (ret <= 0) return 0; size_t data_size = (size_t)ret; - uint16_t* data = (uint16_t*)buffer; + const uint16_t* data = (const uint16_t*)buffer; - uint16_t query_id = ntohs(*data++); - uint16_t flags = ntohs(*data++); - uint16_t questions = ntohs(*data++); + uint16_t query_id = mdns_ntohs(data++); + uint16_t flags = mdns_ntohs(data++); + uint16_t questions = mdns_ntohs(data++); /* This data is unused at the moment, skip - uint16_t answer_rrs = ntohs(*data++); - uint16_t authority_rrs = ntohs(*data++); - uint16_t additional_rrs = ntohs(*data++); + uint16_t answer_rrs = mdns_ntohs(data++); + uint16_t authority_rrs = mdns_ntohs(data++); + uint16_t additional_rrs = mdns_ntohs(data++); */ data += 3; size_t parsed = 0; for (int iquestion = 0; iquestion < questions; ++iquestion) { - size_t question_offset = (size_t)((char*)data - (char*)buffer); + size_t question_offset = MDNS_POINTER_DIFF(data, buffer); size_t offset = question_offset; size_t verify_ofs = 12; if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, @@ -827,10 +869,10 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback break; } size_t length = offset - question_offset; - data = (uint16_t*)((char*)buffer + offset); + data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); // Make sure we get a question of class IN if ((rclass & 0x7FFF) != MDNS_CLASS_IN) @@ -853,37 +895,35 @@ mdns_discovery_answer(int sock, const void* address, size_t address_size, void* if (capacity < (sizeof(mdns_services_query) + 32 + length)) return -1; - uint16_t* data = (uint16_t*)buffer; + void* data = buffer; // Basic reply structure memcpy(data, mdns_services_query, sizeof(mdns_services_query)); // Flags - uint16_t* flags = data + 1; - *flags = htons(0x8400U); + mdns_htons(MDNS_POINTER_OFFSET(data, 2), 0x8400U); // One answer - uint16_t* answers = data + 3; - *answers = htons(1); + mdns_htons(MDNS_POINTER_OFFSET(data, 6), 1); // Fill in answer PTR record - data = (uint16_t*)((char*)buffer + sizeof(mdns_services_query)); + data = MDNS_POINTER_OFFSET(buffer, sizeof(mdns_services_query)); // Reference _services._dns-sd._udp.local. string in question - *data++ = htons(0xC000U | 12U); + data = mdns_htons(data, 0xC000U | 12U); // Type - *data++ = htons(MDNS_RECORDTYPE_PTR); + data = mdns_htons(data, MDNS_RECORDTYPE_PTR); // Rclass - *data++ = htons(MDNS_CLASS_IN); + data = mdns_htons(data, MDNS_CLASS_IN); // TTL - *(uint32_t*)data = htonl(10); - data += 2; + data = mdns_htonl(data, 10); // Record string length - uint16_t* record_length = data++; + void* record_length = data; + data = mdns_htons(data, 0); uint8_t* record_data = (uint8_t*)data; size_t remain = capacity - (sizeof(mdns_services_query) + 10); record_data = (uint8_t*)mdns_string_make(record_data, remain, record, length); - *record_length = htons((uint16_t)(record_data - (uint8_t*)data)); + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(record_data, data)); *record_data++ = 0; - ptrdiff_t tosend = (char*)record_data - (char*)buffer; - return mdns_unicast_send(sock, address, address_size, buffer, (size_t)tosend); + size_t tosend = MDNS_POINTER_DIFF(record_data, buffer); + return mdns_unicast_send(sock, address, address_size, buffer, tosend); } static int @@ -906,28 +946,29 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng rclass &= ~MDNS_UNICAST_RESPONSE; } - uint16_t* data = (uint16_t*)buffer; + struct mdns_header_t* header = (struct mdns_header_t*)buffer; // Query ID - *data++ = htons(query_id); + header->query_id = htons(query_id); // Flags - *data++ = 0; + header->flags = 0; // Questions - *data++ = htons(1); + header->questions = htons(1); // No answer, authority or additional RRs - *data++ = 0; - *data++ = 0; - *data++ = 0; + header->answer_rrs = 0; + header->authority_rrs = 0; + header->additional_rrs = 0; // Fill in question // Name string - data = (uint16_t*)mdns_string_make(data, capacity - 17, name, length); + void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); + data = mdns_string_make(data, capacity - 17, name, length); if (!data) return -1; // Record type - *data++ = htons(type); + data = mdns_htons(data, type); //! Optional unicast response based on local port, class IN - *data++ = htons(rclass); + data = mdns_htons(data, rclass); - ptrdiff_t tosend = (char*)data - (char*)buffer; + size_t tosend = MDNS_POINTER_DIFF(data, buffer); if (mdns_multicast_send(sock, buffer, (size_t)tosend)) return -1; return query_id; @@ -943,19 +984,19 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn #ifdef __APPLE__ saddr->sa_len = sizeof(addr); #endif - int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); if (ret <= 0) return 0; size_t data_size = (size_t)ret; - uint16_t* data = (uint16_t*)buffer; - - uint16_t query_id = ntohs(*data++); - uint16_t flags = ntohs(*data++); - uint16_t questions = ntohs(*data++); - uint16_t answer_rrs = ntohs(*data++); - uint16_t authority_rrs = ntohs(*data++); - uint16_t additional_rrs = ntohs(*data++); + const uint16_t* data = (const uint16_t*)buffer; + + uint16_t query_id = mdns_ntohs(data++); + uint16_t flags = mdns_ntohs(data++); + uint16_t questions = mdns_ntohs(data++); + uint16_t answer_rrs = mdns_ntohs(data++); + uint16_t authority_rrs = mdns_ntohs(data++); + uint16_t additional_rrs = mdns_ntohs(data++); (void)sizeof(flags); if ((only_query_id > 0) && (query_id != only_query_id)) @@ -967,14 +1008,14 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn // Skip questions part int i; for (i = 0; i < questions; ++i) { - size_t ofs = (size_t)((char*)data - (char*)buffer); + size_t ofs = MDNS_POINTER_DIFF(data, buffer); if (!mdns_string_skip(buffer, data_size, &ofs)) return 0; - data = (uint16_t*)((char*)buffer + ofs); - uint16_t rtype = ntohs(*data++); - uint16_t rclass = ntohs(*data++); - (void)sizeof(rtype); - (void)sizeof(rclass); + data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, ofs); + /* Record type and class not used, skip + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++);*/ + data += 2; } size_t records = 0; @@ -1018,7 +1059,6 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff header->additional_rrs = htons((unsigned short)(1 + use_ipv4 + use_ipv6 + use_txt)); void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - uint16_t* udata; size_t remain, service_offset = 0, local_offset = 0, full_offset, host_offset; // Fill in question if unicast @@ -1031,10 +1071,8 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff if (!data || (remain <= 4)) return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_PTR); - *udata++ = htons(question_rclass); - data = udata; + data = mdns_htons(data, MDNS_RECORDTYPE_PTR); + data = mdns_htons(data, question_rclass); } remain = capacity - MDNS_POINTER_DIFF(data, buffer); @@ -1051,21 +1089,20 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (!data || (remain <= 10)) return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_PTR); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(ttl); - udata += 2; - uint16_t* record_length = udata++; // length - data = udata; + data = mdns_htons(data, MDNS_RECORDTYPE_PTR); + data = mdns_htons(data, rclass); + data = mdns_htonl(data, ttl); + void* record_length = data; // length + data = mdns_htons(data, 0); // Make a string ..local. + void* record_data = data; full_offset = MDNS_POINTER_DIFF(data, buffer); remain = capacity - full_offset; data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, service_offset); remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (!data || (remain <= 10)) return -1; - *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1)); + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); // Fill in additional records // SRV record for ..local. @@ -1073,24 +1110,23 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (!data || (remain <= 10)) return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_SRV); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(ttl); - udata += 2; - record_length = udata++; // length - *udata++ = htons(0); // priority - *udata++ = htons(0); // weight - *udata++ = htons(port); // port + data = mdns_htons(data, MDNS_RECORDTYPE_SRV); + data = mdns_htons(data, rclass); + data = mdns_htonl(data, ttl); + record_length = data; + data = mdns_htons(data, 0); // length + record_data = data; + data = mdns_htons(data, 0); // priority + data = mdns_htons(data, 0); // weight + data = mdns_htons(data, port); // port // Make a string .local. - data = udata; host_offset = MDNS_POINTER_DIFF(data, buffer); remain = capacity - host_offset; data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, local_offset); remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (!data || (remain <= 10)) return -1; - *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1)); + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); // A record for .local. if (use_ipv4) { @@ -1098,15 +1134,12 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (!data || (remain <= 14)) return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_A); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(a_ttl); - udata += 2; - *udata++ = htons(4); // length - *(uint32_t*)udata = ipv4; // ipv4 address - udata += 2; - data = udata; + data = mdns_htons(data, MDNS_RECORDTYPE_A); + data = mdns_htons(data, rclass); + data = mdns_htonl(data, a_ttl); + data = mdns_htons(data, 4); // length + memcpy(data, &ipv4, 4); // ipv4 address + data = MDNS_POINTER_OFFSET(data, 4); remain = capacity - MDNS_POINTER_DIFF(data, buffer); } @@ -1116,14 +1149,12 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (!data || (remain <= 26)) return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_AAAA); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(a_ttl); - udata += 2; - *udata++ = htons(16); // length - memcpy(udata, ipv6, 16); // ipv6 address - data = MDNS_POINTER_OFFSET(udata, 16); + data = mdns_htons(data, MDNS_RECORDTYPE_AAAA); + data = mdns_htons(data, rclass); + data = mdns_htonl(data, a_ttl); + data = mdns_htons(data, 16); // length + memcpy(data, ipv6, 16); // ipv6 address + data = MDNS_POINTER_OFFSET(data, 16); remain = capacity - MDNS_POINTER_DIFF(data, buffer); } @@ -1133,13 +1164,11 @@ mdns_query_answer(int sock, const void* address, size_t address_size, void* buff remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (!data || (remain <= (11 + txt_length))) return -1; - udata = (uint16_t*)data; - *udata++ = htons(MDNS_RECORDTYPE_TXT); - *udata++ = htons(rclass); - *(uint32_t*)udata = htonl(ttl); - udata += 2; - *udata++ = htons((unsigned short)(txt_length + 1)); // length - char* txt_record = (char*)udata; + data = mdns_htons(data, MDNS_RECORDTYPE_TXT); + data = mdns_htons(data, rclass); + data = mdns_htonl(data, ttl); + data = mdns_htons(data, (unsigned short)(txt_length + 1)); // length + char* txt_record = (char*)data; *txt_record++ = (char)txt_length; memcpy(txt_record, txt, txt_length); // txt record data = MDNS_POINTER_OFFSET(txt_record, txt_length); @@ -1175,10 +1204,10 @@ mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t len // 2 bytes network-order unsigned port // string: discovery (domain) name, minimum 2 bytes when compressed if ((size >= offset + length) && (length >= 8)) { - const uint16_t* recorddata = (const uint16_t*)((const char*)buffer + offset); - srv.priority = ntohs(*recorddata++); - srv.weight = ntohs(*recorddata++); - srv.port = ntohs(*recorddata++); + const uint16_t* recorddata = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); + srv.priority = mdns_ntohs(recorddata++); + srv.weight = mdns_ntohs(recorddata++); + srv.port = mdns_ntohs(recorddata++); offset += 6; srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity); } @@ -1194,7 +1223,7 @@ mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t lengt addr->sin_len = sizeof(struct sockaddr_in); #endif if ((size >= offset + length) && (length == 4)) - addr->sin_addr.s_addr = *(const uint32_t*)((const char*)buffer + offset); + memcpy(&addr->sin_addr.s_addr, MDNS_POINTER_OFFSET(buffer, offset), 4); return addr; } @@ -1207,7 +1236,7 @@ mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t le addr->sin6_len = sizeof(struct sockaddr_in6); #endif if ((size >= offset + length) && (length == 16)) - addr->sin6_addr = *(const struct in6_addr*)((const char*)buffer + offset); + memcpy(&addr->sin6_addr, MDNS_POINTER_OFFSET(buffer, offset), 16); return addr; } @@ -1223,7 +1252,7 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len end = size; while ((offset < end) && (parsed < capacity)) { - strdata = (const char*)buffer + offset; + strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset); sublength = *(const unsigned char*)strdata; ++strdata; From e43065b1af6c3ab528b456809d424bb747161bd2 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 11:32:02 +0200 Subject: [PATCH 03/37] Refactored ServiceRecord to mdns.cpp it's not used elsewhere and changes to support newer mdns.h will be simpler that way. --- CMakeLists.txt | 1 - include/mdns_cpp/defs.hpp | 16 ---------------- include/mdns_cpp/mdns.hpp | 2 -- src/mdns.cpp | 9 +++++++++ 4 files changed, 9 insertions(+), 19 deletions(-) delete mode 100644 include/mdns_cpp/defs.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cf8360..6145af5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,6 @@ target_include_directories( target_sources( ${PROJECT_NAME} PRIVATE include/mdns_cpp/macros.hpp - include/mdns_cpp/defs.hpp include/mdns_cpp/logger.hpp src/logger.cpp src/mdns.h diff --git a/include/mdns_cpp/defs.hpp b/include/mdns_cpp/defs.hpp deleted file mode 100644 index fc8e326..0000000 --- a/include/mdns_cpp/defs.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -namespace mdns_cpp { - -class ServiceRecord { - public: - const char *service; - const char *hostname; - uint32_t address_ipv4; - uint8_t *address_ipv6; - uint16_t port; -}; - -} // namespace mdns_cpp diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index 552b17a..29f45a5 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -4,8 +4,6 @@ #include #include -#include "mdns_cpp/defs.hpp" - struct sockaddr; namespace mdns_cpp { diff --git a/src/mdns.cpp b/src/mdns.cpp index 4a908a1..842e037 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -22,6 +22,15 @@ namespace mdns_cpp { static mdns_record_txt_t txtbuffer[128]; +class ServiceRecord { + public: + const char *service; + const char *hostname; + uint32_t address_ipv4; + uint8_t *address_ipv6; + uint16_t port; +}; + int mDNS::openServiceSockets(int *sockets, int max_sockets) { // When receiving, each socket can receive data from all network interfaces // Thus we only need to open one socket for each address family From a33ff812e754be87a56eb0c98a276da565fbd695 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Thu, 31 Oct 2024 12:34:16 +0200 Subject: [PATCH 04/37] mdns.h - [Broken! not compiling] 4c64fbaa4a5dcac567c63f182cac2d7a4adc903c. --- src/mdns.h | 781 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 516 insertions(+), 265 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 2caa78a..c5b249c 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -37,6 +37,7 @@ extern "C" { #define MDNS_INVALID_POS ((size_t)-1) #define MDNS_STRING_CONST(s) (s), (sizeof((s)) - 1) +#define MDNS_STRING_ARGS(s) s.str, s.length #define MDNS_STRING_FORMAT(s) (int)((s).length), s.str #define MDNS_POINTER_OFFSET(p, ofs) ((void*)((char*)(p) + (ptrdiff_t)(ofs))) @@ -83,7 +84,13 @@ typedef int (*mdns_record_callback_fn)(int sock, const struct sockaddr* from, si typedef struct mdns_string_t mdns_string_t; typedef struct mdns_string_pair_t mdns_string_pair_t; +typedef struct mdns_string_table_item_t mdns_string_table_item_t; +typedef struct mdns_string_table_t mdns_string_table_t; +typedef struct mdns_record_t mdns_record_t; typedef struct mdns_record_srv_t mdns_record_srv_t; +typedef struct mdns_record_ptr_t mdns_record_ptr_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_txt_t mdns_record_txt_t; #ifdef _WIN32 @@ -105,6 +112,12 @@ struct mdns_string_pair_t { int ref; }; +struct mdns_string_table_t { + size_t offset[16]; + size_t count; + size_t next; +}; + struct mdns_record_srv_t { uint16_t priority; uint16_t weight; @@ -112,11 +125,35 @@ struct mdns_record_srv_t { mdns_string_t name; }; +struct mdns_record_ptr_t { + mdns_string_t name; +}; + +struct mdns_record_a_t { + struct sockaddr_in addr; +}; + +struct mdns_record_aaaa_t { + struct sockaddr_in6 addr; +}; + struct mdns_record_txt_t { mdns_string_t key; mdns_string_t value; }; +struct mdns_record_t { + mdns_string_t name; + mdns_record_type_t type; + union mdns_record_data { + mdns_record_ptr_t ptr; + mdns_record_srv_t srv; + mdns_record_a_t a; + mdns_record_aaaa_t aaaa; + mdns_record_txt_t txt; + } data; +}; + struct mdns_header_t { uint16_t query_id; uint16_t flags; @@ -128,35 +165,35 @@ struct mdns_header_t { // mDNS/DNS-SD public API -//! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, -// pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. -// To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign -// a random user level ephemeral port. To run discovery service listening for incoming -// discoveries and queries, you must set MDNS_PORT as port. +//! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass +//! in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. To +//! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. static int mdns_socket_open_ipv4(struct sockaddr_in* saddr); //! Setup an already opened IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, -// pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. -// To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign -// a random user level ephemeral port. To run discovery service listening for incoming -// discoveries and queries, you must set MDNS_PORT as port. +//! pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. +//! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. static int mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr); -//! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, -// pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. -// To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign -// a random user level ephemeral port. To run discovery service listening for incoming -// discoveries and queries, you must set MDNS_PORT as port. +//! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass +//! in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. To +//! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. static int mdns_socket_open_ipv6(struct sockaddr_in6* saddr); //! Setup an already opened IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, -// pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. -// To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign -// a random user level ephemeral port. To run discovery service listening for incoming -// discoveries and queries, you must set MDNS_PORT as port. +//! pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. +//! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. static int mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr); @@ -164,21 +201,22 @@ mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr); static void mdns_socket_close(int sock); -//! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been -// opened on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be -// 32 bit aligned. Returns the number of queries parsed. +//! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been opened +//! on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be 32 bit +//! aligned. Parsing is stopped when callback function returns non-zero. Returns the number of +//! queries parsed. static size_t mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); -//! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns -// 0 on success, or <0 if error. +//! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns 0 +//! on success, or <0 if error. static int mdns_discovery_send(int sock); //! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to -// the given callback for parsing. Buffer must be 32 bit aligned. Returns the number of -// responses parsed. +//! the given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback +//! function returns non-zero. Returns the number of responses parsed. static size_t mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); @@ -190,77 +228,104 @@ mdns_discovery_answer(int sock, const void* address, size_t address_size, void* size_t capacity, const char* record, size_t length); //! Send a multicast mDNS query on the given socket for the given service name. 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. 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. +//! 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. 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 int 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); //! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering -// 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 given callback for parsing. Buffer must be 32 bit aligned. Returns the number -// of responses parsed. +//! 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 +//! given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback +//! function returns non-zero. Returns the number of responses parsed. static size_t mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data, int query_id); -//! Send a unicast or multicast mDNS query answer with a single record to the given address. The -// answer will be sent multicast if address size is 0, otherwise it will be sent unicast to the -// given address. Use the top bit of the query class field (MDNS_UNICAST_RESPONSE) to determine -// if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be -// 32 bit aligned. Returns 0 if success, or <0 if error. +//! Send a variable unicast mDNS query answer to any question with variable number of records to the +//! given address. Use the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query +//! recieved to determine if the answer should be sent unicast (bit set) or multicast (bit not set). +//! Buffer must be 32 bit aligned. The record type and name should match the data from the query +//! recieved. Returns 0 if success, or <0 if error. static int -mdns_query_answer(int sock, const void* address, size_t address_size, void* buffer, size_t capacity, - uint16_t query_id, const char* service, size_t service_length, - const char* hostname, size_t hostname_length, uint32_t ipv4, const uint8_t* ipv6, - uint16_t port, const char* txt, size_t txt_length); - -// Internal functions - -static mdns_string_t -mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity); - +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, + const char* name, size_t name_length, mdns_record_t answer, + mdns_record_t* authority, size_t authority_count, + mdns_record_t* additional, size_t additional_count); + +//! 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 +//! if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be 32 bit +//! aligned. Returns 0 if success, or <0 if error. static int -mdns_string_skip(const void* buffer, size_t size, size_t* offset); +mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + mdns_record_t* authority, size_t authority_count, + mdns_record_t* additional, size_t additional_count); +//! 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 +//! startup to announce your instance to the local network. static int -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); - -static void* -mdns_string_make(void* data, size_t capacity, const char* name, size_t length); - -static void* -mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset); +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, + size_t additional_count); -static void* -mdns_string_make_with_ref(void* data, size_t capacity, const char* name, size_t length, - size_t ref_offset); +// Parse records functions +//! Parse a PTR record, returns the name in the record static mdns_string_t mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity); +//! Parse a SRV record, returns the priority, weight, port and name in the record static mdns_record_srv_t mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity); +//! Parse an A record, returns the IPv4 address in the record static struct sockaddr_in* mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in* addr); +//! Parse an AAAA record, returns the IPv6 address in the record static struct sockaddr_in6* mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in6* addr); +//! Parse a TXT record, returns the number of key=value records parsed and stores the key-value +//! pairs in the supplied buffer static size_t mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, mdns_record_txt_t* records, size_t capacity); +// Internal functions + +static mdns_string_t +mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity); + +static int +mdns_string_skip(const void* buffer, size_t size, size_t* offset); + +static size_t +mdns_string_find(const char* str, size_t length, char c, size_t offset); + +static int +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); + +static void* +mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, + mdns_string_table_t* string_table); + +static size_t +mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, + const char* str, size_t first_length, size_t total_length); + // Implementations static uint16_t @@ -443,7 +508,8 @@ mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { pair.offset = offset; return pair; } - if (mdns_is_string_ref(buffer[offset])) { + int recursion = 0; + while (mdns_is_string_ref(buffer[offset])) { if (size < offset + 2) return pair; @@ -452,6 +518,8 @@ mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { return pair; pair.ref = 1; + if (++recursion > 16) + return pair; } size_t length = (size_t)buffer[offset++]; @@ -563,6 +631,64 @@ mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, return result; } +static size_t +mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, + const char* str, size_t first_length, size_t total_length) { + if (!string_table) + return MDNS_INVALID_POS; + + for (size_t istr = 0; istr < string_table->count; ++istr) { + if (string_table->offset[istr] >= capacity) + continue; + size_t offset = 0; + mdns_string_pair_t sub_string = + mdns_get_next_substring(buffer, capacity, string_table->offset[istr]); + if (!sub_string.length || (sub_string.length != first_length)) + continue; + if (memcmp(str, MDNS_POINTER_OFFSET(buffer, sub_string.offset), sub_string.length)) + continue; + + // Initial substring matches, now match all remaining substrings + offset += first_length + 1; + while (offset < total_length) { + size_t dot_pos = mdns_string_find(str, total_length, '.', offset); + if (dot_pos == MDNS_INVALID_POS) + dot_pos = total_length; + size_t current_length = dot_pos - offset; + + sub_string = + mdns_get_next_substring(buffer, capacity, sub_string.offset + sub_string.length); + if (!sub_string.length || (sub_string.length != current_length)) + break; + if (memcmp(str + offset, MDNS_POINTER_OFFSET(buffer, sub_string.offset), + sub_string.length)) + break; + + offset = dot_pos + 1; + } + + // Return reference offset if entire string matches + if (offset >= total_length) + return string_table->offset[istr]; + } + + return MDNS_INVALID_POS; +} + +static void +mdns_string_table_add(mdns_string_table_t* string_table, size_t offset) { + if (!string_table) + return; + + string_table->offset[string_table->next] = offset; + + size_t table_capacity = sizeof(string_table->offset) / sizeof(string_table->offset[0]); + if (++string_table->count > table_capacity) + string_table->count = table_capacity; + if (++string_table->next >= table_capacity) + string_table->next = 0; +} + static size_t mdns_string_find(const char* str, size_t length, char c, size_t offset) { const void* found; @@ -574,42 +700,6 @@ mdns_string_find(const char* str, size_t length, char c, size_t offset) { return MDNS_INVALID_POS; } -static void* -mdns_string_make(void* data, size_t capacity, const char* name, size_t length) { - size_t pos = 0; - size_t last_pos = 0; - size_t remain = capacity; - unsigned char* dest = (unsigned char*)data; - while ((last_pos < length) && - ((pos = mdns_string_find(name, length, '.', last_pos)) != MDNS_INVALID_POS)) { - size_t sublength = pos - last_pos; - if (sublength < remain) { - *dest = (unsigned char)sublength; - memcpy(dest + 1, name + last_pos, sublength); - dest += sublength + 1; - remain -= sublength + 1; - } else { - return 0; - } - last_pos = pos + 1; - } - if (last_pos < length) { - size_t sublength = length - last_pos; - if (sublength < remain) { - *dest = (unsigned char)sublength; - memcpy(dest + 1, name + last_pos, sublength); - dest += sublength + 1; - remain -= sublength + 1; - } else { - return 0; - } - } - if (!remain) - return 0; - *dest++ = 0; - return dest; -} - static void* mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { if (capacity < 2) @@ -618,13 +708,41 @@ mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { } static void* -mdns_string_make_with_ref(void* data, size_t capacity, const char* name, size_t length, - size_t ref_offset) { - void* remaindata = mdns_string_make(data, capacity, name, length); - capacity -= MDNS_POINTER_DIFF(remaindata, data); - if (!data || !capacity) +mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, + mdns_string_table_t* string_table) { + size_t pos = 0; + size_t last_pos = 0; + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (name[length - 1] == '.') + --length; + while (last_pos < length) { + pos = mdns_string_find(name, length, '.', last_pos); + size_t sub_length = ((pos != MDNS_INVALID_POS) ? pos : length) - last_pos; + size_t total_length = length - last_pos; + + size_t ref_offset = + mdns_string_table_find(string_table, buffer, capacity, + MDNS_POINTER_OFFSET(name, last_pos), sub_length, total_length); + if (ref_offset != MDNS_INVALID_POS) + return mdns_string_make_ref(data, remain, ref_offset); + + if (remain <= (sub_length + 1)) + return 0; + + *(unsigned char*)data = (unsigned char)sub_length; + memcpy(MDNS_POINTER_OFFSET(data, 1), name + last_pos, sub_length); + mdns_string_table_add(string_table, MDNS_POINTER_DIFF(data, buffer)); + + data = MDNS_POINTER_OFFSET(data, sub_length + 1); + last_pos = ((pos != MDNS_INVALID_POS) ? pos + 1 : length); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + } + + if (!remain) return 0; - return mdns_string_make_ref(MDNS_POINTER_OFFSET(remaindata, -1), capacity + 1, ref_offset); + + *(unsigned char*)data = 0; + return MDNS_POINTER_OFFSET(data, 1); } static size_t @@ -632,7 +750,6 @@ mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const size_t size, size_t* offset, mdns_entry_type_t type, uint16_t query_id, size_t records, mdns_record_callback_fn callback, void* user_data) { size_t parsed = 0; - int do_callback = (callback ? 1 : 0); for (size_t i = 0; i < records; ++i) { size_t name_offset = *offset; mdns_string_skip(buffer, size, offset); @@ -649,11 +766,12 @@ mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const *offset += 10; - if (do_callback && (length <= (size - (*offset)))) { + if (length <= (size - (*offset))) { ++parsed; - if (callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size, + if (callback && + callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size, name_offset, name_length, *offset, length, user_data)) - do_callback = 0; + break; } *offset += length; @@ -784,7 +902,6 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac return 0; } - int do_callback = (callback ? 1 : 0); for (i = 0; i < answer_rrs; ++i) { size_t ofs = MDNS_POINTER_DIFF(data, buffer); size_t verify_ofs = 12; @@ -805,25 +922,34 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac if (length > (data_size - ofs)) return 0; - if (is_answer && do_callback) { + if (is_answer) { ++records; ofs = MDNS_POINTER_DIFF(data, buffer); - if (callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, + if (callback && + callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, buffer, data_size, name_offset, name_length, ofs, length, user_data)) - do_callback = 0; + return records; } data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length); } + size_t total_records = records; size_t offset = MDNS_POINTER_DIFF(data, buffer); - records += + records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); - records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, - user_data); - - return records; + 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); + total_records += records; + if (records != additional_rrs) + return total_records; + + return total_records; } static size_t @@ -878,12 +1004,11 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback if ((rclass & 0x7FFF) != MDNS_CLASS_IN) return 0; - if (callback) - callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, rclass, 0, - buffer, data_size, question_offset, length, question_offset, length, - user_data); - ++parsed; + if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, + rclass, 0, buffer, data_size, question_offset, length, + question_offset, length, user_data)) + break; } return parsed; @@ -916,13 +1041,11 @@ mdns_discovery_answer(int sock, const void* address, size_t address_size, void* // Record string length void* record_length = data; data = mdns_htons(data, 0); - uint8_t* record_data = (uint8_t*)data; - size_t remain = capacity - (sizeof(mdns_services_query) + 10); - record_data = (uint8_t*)mdns_string_make(record_data, remain, record, length); - mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(record_data, data)); - *record_data++ = 0; + uint8_t* record_start = (uint8_t*)data; + data = (uint8_t*)mdns_string_make(buffer, capacity, data, record, length, 0); + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_start)); - size_t tosend = MDNS_POINTER_DIFF(record_data, buffer); + size_t tosend = MDNS_POINTER_DIFF(data, buffer); return mdns_unicast_send(sock, address, address_size, buffer, tosend); } @@ -932,6 +1055,7 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng if (capacity < (17 + length)) return -1; + // Ask for a unicast response since it's a one-shot query uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE; struct sockaddr_storage addr_storage; @@ -960,7 +1084,7 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng // Fill in question // Name string void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - data = mdns_string_make(data, capacity - 17, name, length); + data = mdns_string_make(buffer, capacity, data, name, length, 0); if (!data) return -1; // Record type @@ -1019,169 +1143,296 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn } size_t records = 0; + size_t total_records = 0; 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); - records += + 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); - records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, - user_data); - return records; + 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); + total_records += records; + if (records != additional_rrs) + return total_records; + + return total_records; +} + +static void* +mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, + mdns_record_type_t record_type, const char* name, + size_t name_length, mdns_string_table_t* string_table) { + data = mdns_string_make(buffer, capacity, data, name, name_length, string_table); + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= 4)) + return 0; + + data = mdns_htons(data, record_type); + data = mdns_htons(data, MDNS_UNICAST_RESPONSE | MDNS_CLASS_IN); + + return data; +} + +static void* +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) { + data = + mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table); + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain < 10)) + return 0; + + data = mdns_htons(data, record.type); + data = mdns_htons(data, rclass); + data = mdns_htonl(data, ttl); + data = mdns_htons(data, 0); // Length, to be filled later + return data; +} + +static void* +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) { + // TXT records will be coalesced into one record later + if (!data || (record.type == MDNS_RECORDTYPE_TXT)) + return data; + + data = mdns_answer_add_record_header(buffer, capacity, data, record, rclass, ttl, string_table); + if (!data) + return 0; + + // Pointer to length of record to be filled at end + void* record_length = MDNS_POINTER_OFFSET(data, -2); + void* record_data = data; + + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + switch (record.type) { + case MDNS_RECORDTYPE_PTR: + data = mdns_string_make(buffer, capacity, data, record.data.ptr.name.str, + record.data.ptr.name.length, string_table); + break; + + case MDNS_RECORDTYPE_SRV: + if (remain <= 6) + return 0; + data = mdns_htons(data, record.data.srv.priority); + data = mdns_htons(data, record.data.srv.weight); + data = mdns_htons(data, record.data.srv.port); + data = mdns_string_make(buffer, capacity, data, record.data.srv.name.str, + record.data.srv.name.length, string_table); + break; + + case MDNS_RECORDTYPE_A: + if (remain < 4) + return 0; + memcpy(data, &record.data.a.addr.sin_addr.s_addr, 4); + data = MDNS_POINTER_OFFSET(data, 4); + break; + + case MDNS_RECORDTYPE_AAAA: + if (remain < 16) + return 0; + memcpy(data, &record.data.aaaa.addr.sin6_addr, 16); // ipv6 address + data = MDNS_POINTER_OFFSET(data, 16); + break; + + default: + break; + } + + if (!data) + return 0; + + // Fill record length + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); + return data; +} + +static void* +mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_record_t* records, + size_t record_count, uint16_t rclass, uint32_t ttl, + mdns_string_table_t* string_table) { + // Pointer to length of record to be filled at end + void* record_length = 0; + void* record_data = 0; + + size_t remain = 0; + for (size_t irec = 0; data && (irec < record_count); ++irec) { + if (records[irec].type != MDNS_RECORDTYPE_TXT) + continue; + + if (!record_data) { + data = mdns_answer_add_record_header(buffer, capacity, data, records[irec], rclass, ttl, + string_table); + record_length = MDNS_POINTER_OFFSET(data, -2); + record_data = data; + } + + // TXT strings are unlikely to be shared, just make then raw. Also need one byte for + // termination, thus the <= check + size_t string_length = + records[irec].data.txt.key.length + records[irec].data.txt.value.length + 1; + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (!data || (remain <= string_length) || (string_length > 0x3FFF)) + return 0; + + unsigned char* strdata = (unsigned char*)data; + *strdata++ = (unsigned char)string_length; + memcpy(strdata, records[irec].data.txt.key.str, records[irec].data.txt.key.length); + strdata += records[irec].data.txt.key.length; + *strdata++ = '='; + memcpy(strdata, records[irec].data.txt.value.str, records[irec].data.txt.value.length); + strdata += records[irec].data.txt.value.length; + + data = strdata; + } + + // Fill record length + if (record_data) + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); + + return data; +} + +static uint16_t +mdns_answer_get_record_count(mdns_record_t* records, size_t record_count) { + // TXT records will be coalesced into one record + uint16_t total_count = 0; + uint16_t txt_record = 0; + for (size_t irec = 0; irec < record_count; ++irec) { + if (records[irec].type == MDNS_RECORDTYPE_TXT) + txt_record = 1; + else + ++total_count; + } + return total_count + txt_record; } static int -mdns_query_answer(int sock, const void* address, size_t address_size, void* buffer, size_t capacity, - uint16_t query_id, const char* service, size_t service_length, - const char* hostname, size_t hostname_length, uint32_t ipv4, const uint8_t* ipv6, - uint16_t port, const char* txt, size_t txt_length) { - if (capacity < (sizeof(struct mdns_header_t) + 32 + service_length + hostname_length)) +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, + const char* name, size_t name_length, mdns_record_t answer, + mdns_record_t* authority, size_t authority_count, + mdns_record_t* additional, size_t additional_count) { + if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) return -1; - int unicast = (address_size ? 1 : 0); - int use_ipv4 = (ipv4 != 0); - int use_ipv6 = (ipv6 != 0); - int use_txt = (txt && txt_length && (txt_length <= 255)); - - uint16_t question_rclass = (unicast ? MDNS_UNICAST_RESPONSE : 0) | MDNS_CLASS_IN; - uint16_t rclass = (unicast ? MDNS_CACHE_FLUSH : 0) | MDNS_CLASS_IN; - uint32_t ttl = (unicast ? 10 : 60); - uint32_t a_ttl = ttl; + uint16_t question_rclass = MDNS_UNICAST_RESPONSE | MDNS_CLASS_IN; + uint16_t rclass = MDNS_CACHE_FLUSH | MDNS_CLASS_IN; + uint32_t ttl = 10; // Basic answer structure struct mdns_header_t* header = (struct mdns_header_t*)buffer; - header->query_id = (address_size ? htons(query_id) : 0); + header->query_id = htons(query_id); header->flags = htons(0x8400); - header->questions = htons(unicast ? 1 : 0); + header->questions = htons(1); header->answer_rrs = htons(1); - header->authority_rrs = 0; - header->additional_rrs = htons((unsigned short)(1 + use_ipv4 + use_ipv6 + use_txt)); + header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); + header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); + mdns_string_table_t string_table = {0}; void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - size_t remain, service_offset = 0, local_offset = 0, full_offset, host_offset; - - // Fill in question if unicast - if (unicast) { - service_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - service_offset; - data = mdns_string_make(data, remain, service, service_length); - local_offset = MDNS_POINTER_DIFF(data, buffer) - 7; - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 4)) - return -1; + size_t remain; - data = mdns_htons(data, MDNS_RECORDTYPE_PTR); - data = mdns_htons(data, question_rclass); - } - remain = capacity - MDNS_POINTER_DIFF(data, buffer); + // Fill in question + data = mdns_answer_add_question_unicast(buffer, capacity, data, record_type, name, name_length, + &string_table); - // Fill in answers - // PTR record for service - if (unicast) { - data = mdns_string_make_ref(data, remain, service_offset); - } else { - service_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - service_offset; - data = mdns_string_make(data, remain, service, service_length); - local_offset = MDNS_POINTER_DIFF(data, buffer) - 7; - } - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) - return -1; - data = mdns_htons(data, MDNS_RECORDTYPE_PTR); - data = mdns_htons(data, rclass); - data = mdns_htonl(data, ttl); - void* record_length = data; // length - data = mdns_htons(data, 0); - // Make a string ..local. - void* record_data = data; - full_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - full_offset; - data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, service_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) - return -1; - mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); + // Fill in answer + data = mdns_answer_add_record(buffer, capacity, data, answer, rclass, ttl, &string_table); + + // Fill in authority records + for (size_t irec = 0; data && (irec < authority_count); ++irec) + data = mdns_answer_add_record(buffer, capacity, data, authority[irec], rclass, ttl, + &string_table); + data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, + ttl, &string_table); // Fill in additional records - // SRV record for ..local. - data = mdns_string_make_ref(data, remain, full_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) + for (size_t irec = 0; data && (irec < additional_count); ++irec) + data = mdns_answer_add_record(buffer, capacity, data, additional[irec], rclass, ttl, + &string_table); + data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, + ttl, &string_table); + if (!data) return -1; - data = mdns_htons(data, MDNS_RECORDTYPE_SRV); - data = mdns_htons(data, rclass); - data = mdns_htonl(data, ttl); - record_length = data; - data = mdns_htons(data, 0); // length - record_data = data; - data = mdns_htons(data, 0); // priority - data = mdns_htons(data, 0); // weight - data = mdns_htons(data, port); // port - // Make a string .local. - host_offset = MDNS_POINTER_DIFF(data, buffer); - remain = capacity - host_offset; - data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, local_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 10)) + + size_t tosend = MDNS_POINTER_DIFF(data, buffer); + return mdns_unicast_send(sock, address, address_size, buffer, tosend); +} + +static 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) { + if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) return -1; - mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); - // A record for .local. - if (use_ipv4) { - data = mdns_string_make_ref(data, remain, host_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 14)) - return -1; - data = mdns_htons(data, MDNS_RECORDTYPE_A); - data = mdns_htons(data, rclass); - data = mdns_htonl(data, a_ttl); - data = mdns_htons(data, 4); // length - memcpy(data, &ipv4, 4); // ipv4 address - data = MDNS_POINTER_OFFSET(data, 4); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - } + uint32_t ttl = 60; - // AAAA record for .local. - if (use_ipv6) { - data = mdns_string_make_ref(data, remain, host_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 26)) - return -1; - data = mdns_htons(data, MDNS_RECORDTYPE_AAAA); - data = mdns_htons(data, rclass); - data = mdns_htonl(data, a_ttl); - data = mdns_htons(data, 16); // length - memcpy(data, ipv6, 16); // ipv6 address - data = MDNS_POINTER_OFFSET(data, 16); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - } + // Basic answer structure + struct mdns_header_t* header = (struct mdns_header_t*)buffer; + header->query_id = 0; + header->flags = htons(0x8400); + header->questions = 0; + header->answer_rrs = htons(1); + header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); + header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); - // TXT record for ..local. - if (use_txt) { - data = mdns_string_make_ref(data, remain, full_offset); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= (11 + txt_length))) - return -1; - data = mdns_htons(data, MDNS_RECORDTYPE_TXT); - data = mdns_htons(data, rclass); - data = mdns_htonl(data, ttl); - data = mdns_htons(data, (unsigned short)(txt_length + 1)); // length - char* txt_record = (char*)data; - *txt_record++ = (char)txt_length; - memcpy(txt_record, txt, txt_length); // txt record - data = MDNS_POINTER_OFFSET(txt_record, txt_length); - // Unused until multiple txt records are supported - // remain = capacity - MDNS_POINTER_DIFF(data, buffer); - } + mdns_string_table_t string_table = {0}; + void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); + size_t remain; + + // Fill in answer + data = mdns_answer_add_record(buffer, capacity, data, answer, rclass, ttl, &string_table); + + // Fill in authority records + for (size_t irec = 0; data && (irec < authority_count); ++irec) + data = mdns_answer_add_record(buffer, capacity, data, authority[irec], rclass, ttl, + &string_table); + data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, + ttl, &string_table); + + // Fill in additional records + for (size_t irec = 0; data && (irec < additional_count); ++irec) + data = mdns_answer_add_record(buffer, capacity, data, additional[irec], rclass, ttl, + &string_table); + data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, + ttl, &string_table); + if (!data) + return -1; size_t tosend = MDNS_POINTER_DIFF(data, buffer); - if (address_size) - return mdns_unicast_send(sock, address, address_size, buffer, tosend); return mdns_multicast_send(sock, buffer, tosend); } +static int +mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + mdns_record_t* authority, size_t authority_count, + mdns_record_t* additional, size_t additional_count) { + uint16_t rclass = MDNS_CLASS_IN; + return mdns_answer_multicast_rclass(sock, buffer, capacity, rclass, answer, authority, + authority_count, additional, additional_count); +} + +static int +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, + size_t additional_count) { + uint16_t rclass = MDNS_CLASS_IN | MDNS_CACHE_FLUSH; + return mdns_answer_multicast_rclass(sock, buffer, capacity, rclass, answer, authority, + authority_count, additional, additional_count); +} + static mdns_string_t mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { @@ -1197,7 +1448,7 @@ mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t len char* strbuffer, size_t capacity) { mdns_record_srv_t srv; memset(&srv, 0, sizeof(mdns_record_srv_t)); - // Read the priority, weight, port number and the discovery name + // Read the service priority, weight, port number and the discovery name // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): // 2 bytes network-order unsigned priority // 2 bytes network-order unsigned weight From a86c9f28b7f9981eb3be5ff5c33dd949d4904110 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Thu, 31 Oct 2024 12:36:41 +0200 Subject: [PATCH 05/37] mdns.h - [Still won't compile "cherry-pick" Explicit conversion required for C++. (#45) 0f28d8ba9049cf7d94c3868b28387d55f2c9a07f --- src/mdns.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mdns.h b/src/mdns.h index c5b249c..34f7d48 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -722,7 +722,8 @@ mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, si size_t ref_offset = mdns_string_table_find(string_table, buffer, capacity, - MDNS_POINTER_OFFSET(name, last_pos), sub_length, total_length); + (char*)MDNS_POINTER_OFFSET(name, last_pos), sub_length, + total_length); if (ref_offset != MDNS_INVALID_POS) return mdns_string_make_ref(data, remain, ref_offset); From 88e123713d2351ac1718e3b956d8aae3315305e9 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 12:02:47 +0200 Subject: [PATCH 06/37] Still broken! - adjusted ServiceRecord to reduce diff. --- include/mdns_cpp/mdns.hpp | 17 ++++++++----- src/mdns.cpp | 51 +++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index 29f45a5..2a7dd4c 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -4,7 +4,15 @@ #include #include -struct sockaddr; +#include "mdns_cpp/utils.hpp" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif namespace mdns_cpp { @@ -36,11 +44,8 @@ class mDNS { bool running_{false}; - bool has_ipv4_{false}; - bool has_ipv6_{false}; - - uint32_t service_address_ipv4_{0}; - uint8_t service_address_ipv6_[16]{0}; + struct sockaddr_in service_address_ipv4_; + struct sockaddr_in6 service_address_ipv6_; std::thread worker_thread_; }; diff --git a/src/mdns.cpp b/src/mdns.cpp index 842e037..84dcd00 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -7,15 +7,7 @@ #include "mdns.h" #include "mdns_cpp/logger.hpp" #include "mdns_cpp/macros.hpp" -#include "mdns_cpp/utils.hpp" -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif #include namespace mdns_cpp { @@ -24,11 +16,18 @@ static mdns_record_txt_t txtbuffer[128]; class ServiceRecord { public: - const char *service; - const char *hostname; - uint32_t address_ipv4; - uint8_t *address_ipv6; + std::string service; + std::string hostname; + std::string service_instance; + std::string hostname_qualified; + struct sockaddr_in address_ipv4; + struct sockaddr_in6 address_ipv6; uint16_t port; + mdns_record_t record_ptr; + mdns_record_t record_srv; + mdns_record_t record_a; + mdns_record_t record_aaaa; + mdns_record_t txt_record[2]; }; int mDNS::openServiceSockets(int *sockets, int max_sockets) { @@ -119,11 +118,11 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { (saddr->sin_addr.S_un.S_un_b.s_b3 != 0) || (saddr->sin_addr.S_un.S_un_b.s_b4 != 1)) { int log_addr = 0; if (first_ipv4) { - service_address_ipv4_ = saddr->sin_addr.S_un.S_addr; + service_address_ipv4_ = *saddr; first_ipv4 = 0; log_addr = 1; } - has_ipv4_ = 1; + if (num_sockets < max_sockets) { saddr->sin_port = htons((unsigned short)port); int sock = mdns_socket_open_ipv4(saddr); @@ -152,7 +151,7 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { first_ipv6 = 0; log_addr = 1; } - has_ipv6_ = 1; + if (num_sockets < max_sockets) { saddr->sin6_port = htons((unsigned short)port); int sock = mdns_socket_open_ipv6(saddr); @@ -196,11 +195,11 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { if (saddr->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { int log_addr = 0; if (first_ipv4) { - service_address_ipv4_ = saddr->sin_addr.s_addr; + service_address_ipv4_ = *saddr; first_ipv4 = 0; log_addr = 1; } - has_ipv4_ = 1; + if (num_sockets < max_sockets) { saddr->sin_port = htons(port); int sock = mdns_socket_open_ipv4(saddr); @@ -224,11 +223,11 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { if (memcmp(saddr->sin6_addr.s6_addr, localhost, 16) && memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) { int log_addr = 0; if (first_ipv6) { - memcpy(service_address_ipv6_, &saddr->sin6_addr, 16); + service_address_ipv6_ = *saddr; first_ipv6 = 0; log_addr = 1; } - has_ipv6_ = 1; + if (num_sockets < max_sockets) { saddr->sin6_port = htons(port); int sock = mdns_socket_open_ipv6(saddr); @@ -340,22 +339,22 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns const char dns_sd[] = "_services._dns-sd._udp.local."; const ServiceRecord *service_record = (const ServiceRecord *)user_data; - const size_t service_length = strlen(service_record->service); + const size_t service_length = service_record->service.length(); char sendbuffer[256] = {0}; if ((service.length == (sizeof(dns_sd) - 1)) && (strncmp(service.str, dns_sd, sizeof(dns_sd) - 1) == 0)) { MDNS_LOG << " --> answer " << service_record->service << " \n"; - mdns_discovery_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), service_record->service, + mdns_discovery_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), service_record->service.c_str(), service_length); } else if ((service.length == service_length) && - (strncmp(service.str, service_record->service, service_length) == 0)) { + (strncmp(service.str, service_record->service.c_str(), service_length) == 0)) { uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); - MDNS_LOG << " --> answer " << service_record->hostname << "." << service_record->service << " port " + MDNS_LOG << " --> answer " << service_record->hostname << "." << service_record->service.c_str() << " port " << service_record->port << " (" << (unicast ? "unicast" : "multicast") << ")\n"; if (!unicast) addrlen = 0; char txt_record[] = "asdf=1"; mdns_query_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, service_record->service, - service_length, service_record->hostname, strlen(service_record->hostname), + service_length, service_record->hostname, service_record->hostname.length(), service_record->address_ipv4, service_record->address_ipv6, (uint16_t)service_record->port, txt_record, sizeof(txt_record)); } @@ -432,8 +431,8 @@ void mDNS::runMainLoop() { ServiceRecord service_record{}; service_record.service = name_.data(); service_record.hostname = hostname_.data(); - service_record.address_ipv4 = has_ipv4_ ? service_address_ipv4_ : 0; - service_record.address_ipv6 = has_ipv6_ ? service_address_ipv6_ : 0; + service_record.address_ipv4 = service_address_ipv4_; + service_record.address_ipv6 = service_address_ipv6_; service_record.port = port_; // This is a crude implementation that checks for incoming queries From f5c09a05e89ce1941b8e72e8221646796c927f8a Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 14:06:38 +0200 Subject: [PATCH 07/37] Updated mDNS.cpp to support newer mdns.h - still broken (fix in next commits) 0b4e806bc825335dff79b992b3502ea96d2aafe6 --- include/mdns_cpp/mdns.hpp | 3 +- src/mdns.cpp | 320 ++++++++++++++++++++++++++++++-------- 2 files changed, 259 insertions(+), 64 deletions(-) diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index 2a7dd4c..82e42a1 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -8,6 +8,7 @@ #ifdef _WIN32 #include +#include #else #include #include @@ -29,7 +30,7 @@ class mDNS { void setServiceName(const std::string &name); void setServiceTxtRecord(const std::string &text_record); - void executeQuery(const std::string &service); + void executeQuery(const std::string &service, int record); void executeDiscovery(); private: diff --git a/src/mdns.cpp b/src/mdns.cpp index 84dcd00..dbae706 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -1,5 +1,7 @@ #include "mdns_cpp/mdns.hpp" +#include + #include #include #include @@ -8,8 +10,6 @@ #include "mdns_cpp/logger.hpp" #include "mdns_cpp/macros.hpp" -#include - namespace mdns_cpp { static mdns_record_txt_t txtbuffer[128]; @@ -30,6 +30,8 @@ class ServiceRecord { mdns_record_t txt_record[2]; }; +mdns_string_t to_mdns_str(const std::string &str) { return {str.c_str(), str.length()}; } + int mDNS::openServiceSockets(int *sockets, int max_sockets) { // When receiving, each socket can receive data from all network interfaces // Thus we only need to open one socket for each address family @@ -254,6 +256,7 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { return num_sockets; } +// Callback handling parsing answers to queries sent static int query_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void *data, size_t size, size_t name_offset, size_t name_length, size_t record_offset, @@ -273,7 +276,7 @@ static int query_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns_string_t entrystr = mdns_string_extract(data, size, &name_offset, entrybuffer, sizeof(entrybuffer)); const int str_capacity = 1000; - char str_buffer[str_capacity]={}; + char str_buffer[str_capacity] = {}; if (rtype == MDNS_RECORDTYPE_PTR) { mdns_string_t namestr = @@ -284,101 +287,219 @@ static int query_callback(int sock, const struct sockaddr *from, size_t addrlen, } else if (rtype == MDNS_RECORDTYPE_SRV) { mdns_record_srv_t srv = mdns_record_parse_srv(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); - snprintf(str_buffer, str_capacity,"%s : %s %.*s SRV %.*s priority %d weight %d port %d\n", fromaddrstr.data(), entrytype, - MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); + snprintf(str_buffer, str_capacity, "%s : %s %.*s SRV %.*s priority %d weight %d port %d\n", fromaddrstr.data(), + entrytype, MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); } else if (rtype == MDNS_RECORDTYPE_A) { struct sockaddr_in addr; mdns_record_parse_a(data, size, record_offset, record_length, &addr); const auto addrstr = ipv4AddressToString(namebuffer, sizeof(namebuffer), &addr, sizeof(addr)); - snprintf(str_buffer, str_capacity,"%s : %s %.*s A %s\n", fromaddrstr.data(), entrytype, MDNS_STRING_FORMAT(entrystr), addrstr.data()); + snprintf(str_buffer, str_capacity, "%s : %s %.*s A %s\n", fromaddrstr.data(), entrytype, + MDNS_STRING_FORMAT(entrystr), addrstr.data()); } else if (rtype == MDNS_RECORDTYPE_AAAA) { struct sockaddr_in6 addr; mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr); const auto addrstr = ipv6AddressToString(namebuffer, sizeof(namebuffer), &addr, sizeof(addr)); - snprintf(str_buffer, str_capacity,"%s : %s %.*s AAAA %s\n", fromaddrstr.data(), entrytype, MDNS_STRING_FORMAT(entrystr), addrstr.data()); + snprintf(str_buffer, str_capacity, "%s : %s %.*s AAAA %s\n", fromaddrstr.data(), entrytype, + MDNS_STRING_FORMAT(entrystr), addrstr.data()); } else if (rtype == MDNS_RECORDTYPE_TXT) { size_t parsed = mdns_record_parse_txt(data, size, record_offset, record_length, txtbuffer, sizeof(txtbuffer) / sizeof(mdns_record_txt_t)); for (size_t itxt = 0; itxt < parsed; ++itxt) { if (txtbuffer[itxt].value.length) { - snprintf(str_buffer, str_capacity,"%s : %s %.*s TXT %.*s = %.*s\n", fromaddrstr.data(), entrytype, MDNS_STRING_FORMAT(entrystr), - MDNS_STRING_FORMAT(txtbuffer[itxt].key), MDNS_STRING_FORMAT(txtbuffer[itxt].value)); + snprintf(str_buffer, str_capacity, "%s : %s %.*s TXT %.*s = %.*s\n", fromaddrstr.data(), entrytype, + MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(txtbuffer[itxt].key), + MDNS_STRING_FORMAT(txtbuffer[itxt].value)); } else { - snprintf(str_buffer, str_capacity,"%s : %s %.*s TXT %.*s\n", fromaddrstr.data(), entrytype, MDNS_STRING_FORMAT(entrystr), - MDNS_STRING_FORMAT(txtbuffer[itxt].key)); + snprintf(str_buffer, str_capacity, "%s : %s %.*s TXT %.*s\n", fromaddrstr.data(), entrytype, + MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(txtbuffer[itxt].key)); } } } else { - snprintf(str_buffer, str_capacity,"%s : %s %.*s type %u rclass 0x%x ttl %u length %d\n", fromaddrstr.data(), entrytype, - MDNS_STRING_FORMAT(entrystr), rtype, rclass, ttl, (int)record_length); + snprintf(str_buffer, str_capacity, "%s : %s %.*s type %u rclass 0x%x ttl %u length %d\n", fromaddrstr.data(), + entrytype, MDNS_STRING_FORMAT(entrystr), rtype, rclass, ttl, (int)record_length); } MDNS_LOG << std::string(str_buffer); return 0; } +// Callback handling questions incoming on service sockets int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns_entry_type entry, uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void *data, size_t size, size_t name_offset, size_t name_length, size_t record_offset, size_t record_length, void *user_data) { - (void)sizeof(name_offset); - (void)sizeof(name_length); (void)sizeof(ttl); if (static_cast(entry) != MDNS_ENTRYTYPE_QUESTION) { return 0; } + const char dns_sd[] = "_services._dns-sd._udp.local."; + const ServiceRecord *service_record = (const ServiceRecord *)user_data; + char addrbuffer[64] = {0}; char namebuffer[256] = {0}; const auto fromaddrstr = ipAddressToString(addrbuffer, sizeof(addrbuffer), from, addrlen); - if (rtype == static_cast(mdns_record_type::MDNS_RECORDTYPE_PTR)) { - const mdns_string_t service = - mdns_record_parse_ptr(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); - MDNS_LOG << fromaddrstr << " : question PTR " << std::string(service.str, service.length) << "\n"; - - const char dns_sd[] = "_services._dns-sd._udp.local."; - const ServiceRecord *service_record = (const ServiceRecord *)user_data; - const size_t service_length = service_record->service.length(); - char sendbuffer[256] = {0}; - - if ((service.length == (sizeof(dns_sd) - 1)) && (strncmp(service.str, dns_sd, sizeof(dns_sd) - 1) == 0)) { + const mdns_string_t service = + mdns_record_parse_ptr(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); + const size_t service_length = service_record->service.length(); + char sendbuffer[1024] = {0}; + + size_t offset = name_offset; + mdns_string_t name = mdns_string_extract(data, size, &offset, namebuffer, sizeof(namebuffer)); + + const char *record_name = 0; + if (rtype == MDNS_RECORDTYPE_PTR) + record_name = "PTR"; + else if (rtype == MDNS_RECORDTYPE_SRV) + record_name = "SRV"; + else if (rtype == MDNS_RECORDTYPE_A) + record_name = "A"; + else if (rtype == MDNS_RECORDTYPE_AAAA) + record_name = "AAAA"; + else + return 0; + MDNS_LOG << "Query " << record_name << MDNS_STRING_FORMAT(name); + if ((name.length == (sizeof(dns_sd) - 1)) && (strncmp(name.str, dns_sd, sizeof(dns_sd) - 1) == 0)) { + if (rtype == MDNS_RECORDTYPE_PTR) { + // The PTR query was for the DNS-SD domain, send answer with a PTR record for the + // service name we advertise, typically on the "<_service-name>._tcp.local." format MDNS_LOG << " --> answer " << service_record->service << " \n"; + mdns_discovery_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), service_record->service.c_str(), service_length); - } else if ((service.length == service_length) && - (strncmp(service.str, service_record->service.c_str(), service_length) == 0)) { + } + } else if ((service.length == service_length) && + (strncmp(service.str, service_record->service.c_str(), service_length) == 0)) { + if (rtype == MDNS_RECORDTYPE_PTR) { + // The PTR query was for our service (usually "<_service-name._tcp.local"), answer a PTR + // record reverse mapping the queried service name to our service instance name + // (typically on the ".<_service-name>._tcp.local." format), and add + // additional records containing the SRV record mapping the service instance name to our + // qualified hostname (typically ".local.") and port, as well as any IPv4/IPv6 + // address for the hostname as A/AAAA records, and two test TXT records + // Answer PTR record reverse mapping "<_service-name>._tcp.local." to + // ".<_service-name>._tcp.local." + mdns_record_t answer = service_record->record_ptr; + mdns_record_t additional[5] = {{}}; + size_t additional_count = 0; + // SRV record mapping ".<_service-name>._tcp.local." to + // ".local." with port. Set weight & priority to 0. + additional[additional_count++] = service_record->record_srv; + // A/AAAA records mapping ".local." to IPv4/IPv6 addresses + if (service_record->address_ipv4.sin_family == AF_INET) additional[additional_count++] = service_record->record_a; + if (service_record->address_ipv6.sin6_family == AF_INET6) + additional[additional_count++] = service_record->record_aaaa; + // Add two test TXT records for our service instance name, will be coalesced into + // one record with both key-value pair strings by the library + additional[additional_count++] = service_record->txt_record[0]; + additional[additional_count++] = service_record->txt_record[1]; + // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); - MDNS_LOG << " --> answer " << service_record->hostname << "." << service_record->service.c_str() << " port " - << service_record->port << " (" << (unicast ? "unicast" : "multicast") << ")\n"; - if (!unicast) addrlen = 0; - char txt_record[] = "asdf=1"; - mdns_query_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, service_record->service, - service_length, service_record->hostname, service_record->hostname.length(), - service_record->address_ipv4, service_record->address_ipv6, (uint16_t)service_record->port, - txt_record, sizeof(txt_record)); + printf(" --> answer %.*s port %d (%s)\n", MDNS_STRING_FORMAT(service_record->record_srv.data.srv.name), + service_record->port, (unicast ? "unicast" : "multicast")); + if (unicast) { + mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, + static_cast(rtype), name.str, name.length, answer, 0, 0, + additional, additional_count); + } else { + mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); + } } - } else if (rtype == static_cast(mdns_record_type::MDNS_RECORDTYPE_SRV)) { - mdns_record_srv_t service = - mdns_record_parse_srv(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); - MDNS_LOG << fromaddrstr << " : question SRV " << std::string(service.name.str, service.name.length) << "\n"; -#if 0 - if ((service.length == service_length) && - (strncmp(service.str, service_record->service, service_length) == 0)) { - uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); - printf(" --> answer %s.%s port %d (%s)\n", service_record->hostname, - service_record->service, service_record->port, - (unicast ? "unicast" : "multicast")); - if (!unicast) - addrlen = 0; - char txt_record[] = "test=1"; - mdns_query_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, - service_record->service, service_length, service_record->hostname, - strlen(service_record->hostname), service_record->address_ipv4, - service_record->address_ipv6, (uint16_t)service_record->port, - txt_record, sizeof(txt_record)); - } -#endif + } else if ((name.length == service_record->service_instance.length()) && + (strncmp(name.str, service_record->service_instance.c_str(), name.length) == 0)) { + if (rtype == MDNS_RECORDTYPE_SRV) { + // The SRV query was for our service instance (usually + // ".<_service-name._tcp.local"), answer a SRV record mapping the service + // instance name to our qualified hostname (typically ".local.") and port, as + // well as any IPv4/IPv6 address for the hostname as A/AAAA records, and two test TXT + // records + // Answer PTR record reverse mapping "<_service-name>._tcp.local." to + // ".<_service-name>._tcp.local." + mdns_record_t answer = service_record->record_srv; + mdns_record_t additional[5] = {{}}; + size_t additional_count = 0; + // A/AAAA records mapping ".local." to IPv4/IPv6 addresses + if (service_record->address_ipv4.sin_family == AF_INET) additional[additional_count++] = service_record->record_a; + if (service_record->address_ipv6.sin6_family == AF_INET6) + additional[additional_count++] = service_record->record_aaaa; + // Add two test TXT records for our service instance name, will be coalesced into + // one record with both key-value pair strings by the library + additional[additional_count++] = service_record->txt_record[0]; + additional[additional_count++] = service_record->txt_record[1]; + // Send the answer, unicast or multicast depending on flag in query + uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); + printf(" --> answer %.*s port %d (%s)\n", MDNS_STRING_FORMAT(service_record->record_srv.data.srv.name), + service_record->port, (unicast ? "unicast" : "multicast")); + if (unicast) { + mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, + static_cast(rtype), name.str, name.length, answer, 0, 0, + additional, additional_count); + } else { + mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); + } + } + } else if ((name.length == service_record->hostname_qualified.length()) && + (strncmp(name.str, service_record->hostname_qualified.c_str(), name.length) == 0)) { + if ((rtype == MDNS_RECORDTYPE_A) && (service_record->address_ipv4.sin_family == AF_INET)) { + // The A query was for our qualified hostname (typically ".local.") and we + // have an IPv4 address, answer with an A record mappiing the hostname to an IPv4 + // address, as well as any IPv6 address for the hostname, and two test TXT records + // Answer A records mapping ".local." to IPv4 address + mdns_record_t answer = service_record->record_a; + mdns_record_t additional[5] = {{}}; + size_t additional_count = 0; + // AAAA record mapping ".local." to IPv6 addresses + if (service_record->address_ipv6.sin6_family == AF_INET6) + additional[additional_count++] = service_record->record_aaaa; + // Add two test TXT records for our service instance name, will be coalesced into + // one record with both key-value pair strings by the library + additional[additional_count++] = service_record->txt_record[0]; + additional[additional_count++] = service_record->txt_record[1]; + // Send the answer, unicast or multicast depending on flag in query + uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); + const auto addrstr = + ipAddressToString(addrbuffer, sizeof(addrbuffer), (struct sockaddr *)&service_record->record_a.data.a.addr, + sizeof(service_record->record_a.data.a.addr)); + printf(" --> answer %.*s IPv4 %.*s (%s)\n", MDNS_STRING_FORMAT(service_record->record_a.name), (int)addrstr.length(), addrstr.c_str(), + (unicast ? "unicast" : "multicast")); + if (unicast) { + mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, + static_cast(rtype), name.str, name.length, answer, 0, 0, + additional, additional_count); + } else { + mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); + } + } else if ((rtype == MDNS_RECORDTYPE_AAAA) && (service_record->address_ipv6.sin6_family == AF_INET6)) { + // The AAAA query was for our qualified hostname (typically ".local.") and we + // have an IPv6 address, answer with an AAAA record mappiing the hostname to an IPv6 + // address, as well as any IPv4 address for the hostname, and two test TXT records + // Answer AAAA records mapping ".local." to IPv6 address + mdns_record_t answer = service_record->record_aaaa; + mdns_record_t additional[5] = {{}}; + size_t additional_count = 0; + // A record mapping ".local." to IPv4 addresses + if (service_record->address_ipv4.sin_family == AF_INET) additional[additional_count++] = service_record->record_a; + // Add two test TXT records for our service instance name, will be coalesced into + // one record with both key-value pair strings by the library + additional[additional_count++] = service_record->txt_record[0]; + additional[additional_count++] = service_record->txt_record[1]; + // Send the answer, unicast or multicast depending on flag in query + uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); + auto addrstr = ipAddressToString(addrbuffer, sizeof(addrbuffer), + (struct sockaddr *)&service_record->record_aaaa.data.aaaa.addr, + sizeof(service_record->record_aaaa.data.aaaa.addr)); + printf(" --> answer %.*s IPv6 %.*s (%s)\n", MDNS_STRING_FORMAT(service_record->record_a.name), (int)addrstr.length(), addrstr.c_str(), + (unicast ? "unicast" : "multicast")); + if (unicast) { + mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, + static_cast(rtype), name.str, name.length, answer, 0, 0, + additional, additional_count); + } else { + mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); + } + } + // #endif } return 0; } @@ -429,12 +550,75 @@ void mDNS::runMainLoop() { constexpr size_t capacity = 2048u; std::shared_ptr buffer(malloc(capacity), free); ServiceRecord service_record{}; - service_record.service = name_.data(); - service_record.hostname = hostname_.data(); + service_record.service = name_; + service_record.hostname = hostname_; + { + // Build the service instance ".<_service-name>._tcp.local." string + std::ostringstream oss; + oss << hostname_ << "." << name_; + service_record.service_instance = oss.str(); + } + { + // Build the ".local." string + std::ostringstream oss; + oss << hostname_ << ".local."; + service_record.hostname_qualified = oss.str(); + } service_record.address_ipv4 = service_address_ipv4_; service_record.address_ipv6 = service_address_ipv6_; service_record.port = port_; + // Setup our mDNS records + + // PTR record reverse mapping "<_service-name>._tcp.local." to + // ".<_service-name>._tcp.local." + service_record.record_ptr = {.name = to_mdns_str(service_record.service), + .type = MDNS_RECORDTYPE_PTR, + .data.ptr.name = to_mdns_str(service_record.service_instance)}; + + // SRV record mapping ".<_service-name>._tcp.local." to + // ".local." with port. Set weight & priority to 0. + service_record.record_srv = {.name = to_mdns_str(service_record.service_instance), + .type = MDNS_RECORDTYPE_SRV, + .data.srv.name = to_mdns_str(service_record.hostname_qualified), + .data.srv.port = service_record.port, + .data.srv.priority = 0, + .data.srv.weight = 0}; + + // A/AAAA records mapping ".local." to IPv4/IPv6 addresses + service_record.record_a = {.name = to_mdns_str(service_record.hostname_qualified), + .type = MDNS_RECORDTYPE_A, + .data.a.addr = service_record.address_ipv4}; + service_record.record_aaaa = {.name = to_mdns_str(service_record.hostname_qualified), + .type = MDNS_RECORDTYPE_AAAA, + .data.aaaa.addr = service_record.address_ipv6}; + + // Add two test TXT records for our service instance name, will be coalesced into + // one record with both key-value pair strings by the library + service_record.txt_record[0] = {.name = to_mdns_str(service_record.service_instance), + .type = MDNS_RECORDTYPE_TXT, + .data.txt.key = {MDNS_STRING_CONST("test")}, + .data.txt.value = {MDNS_STRING_CONST("1")}}; + service_record.txt_record[1] = {.name = to_mdns_str(service_record.service_instance), + .type = MDNS_RECORDTYPE_TXT, + .data.txt.key = {MDNS_STRING_CONST("other")}, + .data.txt.value = {MDNS_STRING_CONST("value")}}; + + // Send an announcement on startup of service + { + mdns_record_t additional[5] = {{}}; + size_t additional_count = 0; + additional[additional_count++] = service_record.record_srv; + if (service_record.address_ipv4.sin_family == AF_INET) additional[additional_count++] = service_record.record_a; + if (service_record.address_ipv6.sin6_family == AF_INET6) + additional[additional_count++] = service_record.record_aaaa; + additional[additional_count++] = service_record.txt_record[0]; + additional[additional_count++] = service_record.txt_record[1]; + for (int isock = 0; isock < num_sockets; ++isock) + mdns_announce_multicast(sockets[isock], buffer.get(), capacity, service_record.record_ptr, 0, 0, additional, + additional_count); + } + // This is a crude implementation that checks for incoming queries while (running_) { int nfds = 0; @@ -463,7 +647,7 @@ void mDNS::runMainLoop() { MDNS_LOG << "Closed socket " << (num_sockets ? "s" : "") << "\n"; } -void mDNS::executeQuery(const std::string &service) { +void mDNS::executeQuery(const std::string &service, int record) { int sockets[32]; int query_id[32]; int num_sockets = openClientSockets(sockets, sizeof(sockets) / sizeof(sockets[0]), 0); @@ -480,9 +664,19 @@ void mDNS::executeQuery(const std::string &service) { void *user_data = 0; size_t records; - MDNS_LOG << "Sending mDNS query: " << service << "\n"; + const char *record_name = "PTR"; + if (record == MDNS_RECORDTYPE_SRV) + record_name = "SRV"; + else if (record == MDNS_RECORDTYPE_A) + record_name = "A"; + else if (record == MDNS_RECORDTYPE_AAAA) + record_name = "AAAA"; + else + record = MDNS_RECORDTYPE_PTR; + + MDNS_LOG << "Sending mDNS query: " << service << " " << record_name << "\n"; for (int isock = 0; isock < num_sockets; ++isock) { - query_id[isock] = mdns_query_send(sockets[isock], MDNS_RECORDTYPE_PTR, service.data(), strlen(service.data()), + query_id[isock] = mdns_query_send(sockets[isock], (mdns_record_type)record, service.data(), strlen(service.data()), buffer, capacity, 0); if (query_id[isock] < 0) { MDNS_LOG << "Failed to send mDNS query: " << strerror(errno) << "\n"; @@ -495,7 +689,7 @@ void mDNS::executeQuery(const std::string &service) { MDNS_LOG << "Reading mDNS query replies\n"; do { struct timeval timeout; - timeout.tv_sec = 5; + timeout.tv_sec = 10; timeout.tv_usec = 0; int nfds = 0; From 138364d93febceb3b426391df187ab1cb36b69e5 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 14:18:30 +0200 Subject: [PATCH 08/37] mdns.h - af99e99a1c8f922ddfc32bed9c143aba215c2805 --- src/mdns.h | 53 ++++++++--------------------------------------------- 1 file changed, 8 insertions(+), 45 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 34f7d48..b01ae1f 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -60,7 +60,9 @@ enum mdns_record_type { // IP6 Address [Thomson] MDNS_RECORDTYPE_AAAA = 28, // Server Selection [RFC2782] - MDNS_RECORDTYPE_SRV = 33 + MDNS_RECORDTYPE_SRV = 33, + // Any available records + MDNS_RECORDTYPE_ANY = 255 }; enum mdns_entry_type { @@ -221,12 +223,6 @@ static size_t mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); -//! Send a unicast DNS-SD answer with a single record to the given address. Buffer must be 32 bit -// aligned. Returns 0 if success, or <0 if error. -static int -mdns_discovery_answer(int sock, const void* address, size_t address_size, void* buffer, - size_t capacity, const char* record, size_t length); - //! Send a multicast mDNS query on the given socket for the given service name. 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 @@ -986,10 +982,10 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback size_t question_offset = MDNS_POINTER_DIFF(data, buffer); size_t offset = question_offset; size_t verify_ofs = 12; + int dns_sd = 0; if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, sizeof(mdns_services_query), &verify_ofs)) { - if (flags || (questions != 1)) - return 0; + dns_sd = 1; } else { offset = question_offset; if (!mdns_string_skip(buffer, data_size, &offset)) @@ -1003,7 +999,9 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback // Make sure we get a question of class IN if ((rclass & 0x7FFF) != MDNS_CLASS_IN) - return 0; + break; + if (dns_sd && flags) + continue; ++parsed; if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, @@ -1015,41 +1013,6 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback return parsed; } -static int -mdns_discovery_answer(int sock, const void* address, size_t address_size, void* buffer, - size_t capacity, const char* record, size_t length) { - if (capacity < (sizeof(mdns_services_query) + 32 + length)) - return -1; - - void* data = buffer; - // Basic reply structure - memcpy(data, mdns_services_query, sizeof(mdns_services_query)); - // Flags - mdns_htons(MDNS_POINTER_OFFSET(data, 2), 0x8400U); - // One answer - mdns_htons(MDNS_POINTER_OFFSET(data, 6), 1); - - // Fill in answer PTR record - data = MDNS_POINTER_OFFSET(buffer, sizeof(mdns_services_query)); - // Reference _services._dns-sd._udp.local. string in question - data = mdns_htons(data, 0xC000U | 12U); - // Type - data = mdns_htons(data, MDNS_RECORDTYPE_PTR); - // Rclass - data = mdns_htons(data, MDNS_CLASS_IN); - // TTL - data = mdns_htonl(data, 10); - // Record string length - void* record_length = data; - data = mdns_htons(data, 0); - uint8_t* record_start = (uint8_t*)data; - data = (uint8_t*)mdns_string_make(buffer, capacity, data, record, length, 0); - mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_start)); - - size_t tosend = MDNS_POINTER_DIFF(data, buffer); - return mdns_unicast_send(sock, address, address_size, buffer, tosend); -} - static int 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) { From 186b6497e72d828be327a569d58d8f97d2aefae3 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 14:26:00 +0200 Subject: [PATCH 09/37] mdns.cpp fixes for prev commit --- src/mdns.cpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index dbae706..726693a 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -357,21 +357,32 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns record_name = "A"; else if (rtype == MDNS_RECORDTYPE_AAAA) record_name = "AAAA"; + else if (rtype == MDNS_RECORDTYPE_ANY) + record_name = "ANY"; else return 0; MDNS_LOG << "Query " << record_name << MDNS_STRING_FORMAT(name); if ((name.length == (sizeof(dns_sd) - 1)) && (strncmp(name.str, dns_sd, sizeof(dns_sd) - 1) == 0)) { - if (rtype == MDNS_RECORDTYPE_PTR) { + if ((rtype == MDNS_RECORDTYPE_PTR) || (rtype == MDNS_RECORDTYPE_ANY)) { // The PTR query was for the DNS-SD domain, send answer with a PTR record for the // service name we advertise, typically on the "<_service-name>._tcp.local." format - MDNS_LOG << " --> answer " << service_record->service << " \n"; - - mdns_discovery_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), service_record->service.c_str(), - service_length); + // Answer PTR record reverse mapping "<_service-name>._tcp.local." to + // ".<_service-name>._tcp.local." + mdns_record_t answer = { + .name = name, .type = MDNS_RECORDTYPE_PTR, .data.ptr.name = to_mdns_str(service_record->service)}; + // Send the answer, unicast or multicast depending on flag in query + uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); + printf(" --> answer %.*s (%s)\n", MDNS_STRING_FORMAT(answer.data.ptr.name), (unicast ? "unicast" : "multicast")); + if (unicast) { + mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, + static_cast(rtype), name.str, name.length, answer, 0, 0, 0, 0); + } else { + mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, 0, 0); + } } } else if ((service.length == service_length) && (strncmp(service.str, service_record->service.c_str(), service_length) == 0)) { - if (rtype == MDNS_RECORDTYPE_PTR) { + if ((rtype == MDNS_RECORDTYPE_PTR) || (rtype == MDNS_RECORDTYPE_ANY)) { // The PTR query was for our service (usually "<_service-name._tcp.local"), answer a PTR // record reverse mapping the queried service name to our service instance name // (typically on the ".<_service-name>._tcp.local." format), and add @@ -408,7 +419,7 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns } } else if ((name.length == service_record->service_instance.length()) && (strncmp(name.str, service_record->service_instance.c_str(), name.length) == 0)) { - if (rtype == MDNS_RECORDTYPE_SRV) { + if ((rtype == MDNS_RECORDTYPE_SRV) || (rtype == MDNS_RECORDTYPE_ANY)) { // The SRV query was for our service instance (usually // ".<_service-name._tcp.local"), answer a SRV record mapping the service // instance name to our qualified hostname (typically ".local.") and port, as @@ -441,7 +452,8 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns } } else if ((name.length == service_record->hostname_qualified.length()) && (strncmp(name.str, service_record->hostname_qualified.c_str(), name.length) == 0)) { - if ((rtype == MDNS_RECORDTYPE_A) && (service_record->address_ipv4.sin_family == AF_INET)) { + if (((rtype == MDNS_RECORDTYPE_A) || (rtype == MDNS_RECORDTYPE_ANY)) && + (service_record->address_ipv4.sin_family == AF_INET)) { // The A query was for our qualified hostname (typically ".local.") and we // have an IPv4 address, answer with an A record mappiing the hostname to an IPv4 // address, as well as any IPv6 address for the hostname, and two test TXT records @@ -470,7 +482,8 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns } else { mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); } - } else if ((rtype == MDNS_RECORDTYPE_AAAA) && (service_record->address_ipv6.sin6_family == AF_INET6)) { + } else if (((rtype == MDNS_RECORDTYPE_AAAA) || (rtype == MDNS_RECORDTYPE_ANY)) && + (service_record->address_ipv6.sin6_family == AF_INET6)) { // The AAAA query was for our qualified hostname (typically ".local.") and we // have an IPv6 address, answer with an AAAA record mappiing the hostname to an IPv6 // address, as well as any IPv4 address for the hostname, and two test TXT records From 542062cde010475aabb322784d7edb101d7931ff Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 20:17:35 +0200 Subject: [PATCH 10/37] mDNS.cpp - validate service_name (equivalent to mdns.h commit 9f76e58b88347087b7086772861698193a0a1584 --- src/mdns.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mdns.cpp b/src/mdns.cpp index 726693a..909b8b2 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -555,6 +555,13 @@ void mDNS::runMainLoop() { throw std::runtime_error(msg); } + if (name_.length() == 0) { + const auto msg = "Error: nvalid service name\n"; + MDNS_LOG << msg << "\n"; + throw std::runtime_error(msg); + } + if (!name_.ends_with(".")) name_ += "."; + MDNS_LOG << "Opened " << std::to_string(num_sockets) << " socket" << (num_sockets ? "s" : "") << " for mDNS service\n"; MDNS_LOG << "Service mDNS: " << name_ << ":" << port_ << "\n"; From 6476c152df83b1b3b0352fcc23d5f46d1caf60ea Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 20:20:55 +0200 Subject: [PATCH 11/37] mdns.h - minor cleanups 1f48fec798fb1d40165ea05b11ffecc1f7544dc5 --- src/mdns.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index b01ae1f..1f54b0a 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -1290,7 +1290,6 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) return -1; - uint16_t question_rclass = MDNS_UNICAST_RESPONSE | MDNS_CLASS_IN; uint16_t rclass = MDNS_CACHE_FLUSH | MDNS_CLASS_IN; uint32_t ttl = 10; @@ -1305,7 +1304,6 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo mdns_string_table_t string_table = {0}; void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - size_t remain; // Fill in question data = mdns_answer_add_question_unicast(buffer, capacity, data, record_type, name, name_length, @@ -1354,7 +1352,6 @@ mdns_answer_multicast_rclass(int sock, void* buffer, size_t capacity, uint16_t r mdns_string_table_t string_table = {0}; void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - size_t remain; // Fill in answer data = mdns_answer_add_record(buffer, capacity, data, answer, rclass, ttl, &string_table); From 974904da977f10f6f212a7039d1a8cf80e4f5bbd Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 20:22:04 +0200 Subject: [PATCH 12/37] mdns.h - tighter variable scoping - a91aefbf37e051fa0f614bc503073322dda5e086 --- src/mdns.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 1f54b0a..f6cad39 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -706,13 +706,12 @@ mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { static void* mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, mdns_string_table_t* string_table) { - size_t pos = 0; size_t last_pos = 0; size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); if (name[length - 1] == '.') --length; while (last_pos < length) { - pos = mdns_string_find(name, length, '.', last_pos); + size_t pos = mdns_string_find(name, length, '.', last_pos); size_t sub_length = ((pos != MDNS_INVALID_POS) ? pos : length) - last_pos; size_t total_length = length - last_pos; @@ -1457,7 +1456,6 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len mdns_record_txt_t* records, size_t capacity) { size_t parsed = 0; const char* strdata; - size_t separator, sublength; size_t end = offset + length; if (size < end) @@ -1465,12 +1463,12 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len while ((offset < end) && (parsed < capacity)) { strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset); - sublength = *(const unsigned char*)strdata; + size_t sublength = *(const unsigned char*)strdata; ++strdata; offset += sublength + 1; - separator = 0; + size_t separator = 0; for (size_t c = 0; c < sublength; ++c) { // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) From 3f594a04b3165c2f6e378c26dea11973e65a3a2f Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 20:24:33 +0200 Subject: [PATCH 13/37] mdns.h - (use const pointer in open and setup func) updated with aedb530287dbfc4f56e6d63e3409080686241bf9 --- src/mdns.cpp | 2 +- src/mdns.h | 63 ++++++++++++++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index 909b8b2..abd3207 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -91,7 +91,7 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { &address_size); if (ret == ERROR_BUFFER_OVERFLOW) { free(adapter_address); - adapter_address = 0; + address_size *= 2; } else { break; } diff --git a/src/mdns.h b/src/mdns.h index f6cad39..e7d1a1f 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -173,7 +173,7 @@ struct mdns_header_t { //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. static int -mdns_socket_open_ipv4(struct sockaddr_in* saddr); +mdns_socket_open_ipv4(const struct sockaddr_in* saddr); //! Setup an already opened IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, //! pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. @@ -181,7 +181,7 @@ mdns_socket_open_ipv4(struct sockaddr_in* saddr); //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. static int -mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr); +mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr); //! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass //! in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. To @@ -189,7 +189,7 @@ mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr); //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. static int -mdns_socket_open_ipv6(struct sockaddr_in6* saddr); +mdns_socket_open_ipv6(const struct sockaddr_in6* saddr); //! Setup an already opened IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, //! pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. @@ -197,7 +197,7 @@ mdns_socket_open_ipv6(struct sockaddr_in6* saddr); //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. static int -mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr); +mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr); //! Close a socket opened with mdns_socket_open_ipv4 and mdns_socket_open_ipv6. static void @@ -353,7 +353,7 @@ mdns_htonl(void* data, uint32_t val) { } static int -mdns_socket_open_ipv4(struct sockaddr_in* saddr) { +mdns_socket_open_ipv4(const struct sockaddr_in* saddr) { int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) return -1; @@ -365,7 +365,7 @@ mdns_socket_open_ipv4(struct sockaddr_in* saddr) { } static int -mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr) { +mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) { unsigned char ttl = 1; unsigned char loopback = 1; unsigned int reuseaddr = 1; @@ -387,22 +387,22 @@ mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr) { struct sockaddr_in sock_addr; if (!saddr) { - saddr = &sock_addr; - memset(saddr, 0, sizeof(struct sockaddr_in)); - saddr->sin_family = AF_INET; - saddr->sin_addr.s_addr = INADDR_ANY; + memset(&sock_addr, 0, sizeof(struct sockaddr_in)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = INADDR_ANY; #ifdef __APPLE__ - saddr->sin_len = sizeof(struct sockaddr_in); + sock_addr.sin_len = sizeof(struct sockaddr_in); #endif } else { - setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&saddr->sin_addr, - sizeof(saddr->sin_addr)); + memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in)); + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&sock_addr.sin_addr, + sizeof(sock_addr.sin_addr)); #ifndef _WIN32 - saddr->sin_addr.s_addr = INADDR_ANY; + sock_addr.sin_addr.s_addr = INADDR_ANY; #endif } - if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in))) + if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in))) return -1; #ifdef _WIN32 @@ -417,7 +417,7 @@ mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr) { } static int -mdns_socket_open_ipv6(struct sockaddr_in6* saddr) { +mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) { int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) return -1; @@ -429,7 +429,7 @@ mdns_socket_open_ipv6(struct sockaddr_in6* saddr) { } static int -mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr) { +mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) { int hops = 1; unsigned int loopback = 1; unsigned int reuseaddr = 1; @@ -451,22 +451,22 @@ mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr) { struct sockaddr_in6 sock_addr; if (!saddr) { - saddr = &sock_addr; - memset(saddr, 0, sizeof(struct sockaddr_in6)); - saddr->sin6_family = AF_INET6; - saddr->sin6_addr = in6addr_any; + memset(&sock_addr, 0, sizeof(struct sockaddr_in6)); + sock_addr.sin6_family = AF_INET6; + sock_addr.sin6_addr = in6addr_any; #ifdef __APPLE__ - saddr->sin6_len = sizeof(struct sockaddr_in6); + sock_addr.sin6_len = sizeof(struct sockaddr_in6); #endif } else { + memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in6)); unsigned int ifindex = 0; setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&ifindex, sizeof(ifindex)); #ifndef _WIN32 - saddr->sin6_addr = in6addr_any; + sock_addr.sin6_addr = in6addr_any; #endif } - if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in6))) + if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in6))) return -1; #ifdef _WIN32 @@ -1136,8 +1136,10 @@ mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, mdns_record_type_t record_type, const char* name, size_t name_length, mdns_string_table_t* string_table) { data = mdns_string_make(buffer, capacity, data, name, name_length, string_table); + if (!data) + return 0; size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= 4)) + if (remain < 4) return 0; data = mdns_htons(data, record_type); @@ -1149,10 +1151,11 @@ mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, static void* 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) { - 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) + return 0; size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain < 10)) + if (remain < 10) return 0; data = mdns_htons(data, record.type); @@ -1244,8 +1247,10 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor // termination, thus the <= check size_t string_length = records[irec].data.txt.key.length + records[irec].data.txt.value.length + 1; + if (!data) + return 0; remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (!data || (remain <= string_length) || (string_length > 0x3FFF)) + if ((remain <= string_length) || (string_length > 0x3FFF)) return 0; unsigned char* strdata = (unsigned char*)data; From dcc67bc45436a59f25938668659114856cb24620 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 20:28:51 +0200 Subject: [PATCH 14/37] mdns.h - equivalent with add possibility to stop service announcement - 5fdfc32e5b1b2c085012ca2c493e2bd9aa15590a --- src/mdns.cpp | 16 ++++++++++++++++ src/mdns.h | 31 +++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index abd3207..163b95b 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -661,6 +661,22 @@ void mDNS::runMainLoop() { } } + // Send a goodbye on end of service + { + mdns_record_t additional[5] = {{}}; + size_t additional_count = 0; + additional[additional_count++] = service_record.record_srv; + if (service_record.address_ipv4.sin_family == AF_INET) additional[additional_count++] = service_record.record_a; + if (service_record.address_ipv6.sin6_family == AF_INET6) + additional[additional_count++] = service_record.record_aaaa; + additional[additional_count++] = service_record.txt_record[0]; + additional[additional_count++] = service_record.txt_record[1]; + + for (int isock = 0; isock < num_sockets; ++isock) + mdns_goodbye_multicast(sockets[isock], buffer.get(), capacity, service_record.record_ptr, 0, 0, additional, + additional_count); + } + for (int isock = 0; isock < num_sockets; ++isock) { mdns_socket_close(sockets[isock]); } diff --git a/src/mdns.h b/src/mdns.h index e7d1a1f..bd005ed 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -271,6 +271,13 @@ mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t a mdns_record_t* authority, size_t authority_count, mdns_record_t* additional, size_t additional_count); +//! 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. +static int +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, + size_t additional_count); + // Parse records functions //! Parse a PTR record, returns the name in the record @@ -1336,15 +1343,14 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo return mdns_unicast_send(sock, address, address_size, buffer, tosend); } + static int -mdns_answer_multicast_rclass(int sock, void* buffer, size_t capacity, uint16_t rclass, +mdns_answer_multicast_rclass_ttl(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) { + mdns_record_t* additional, size_t additional_count, uint32_t ttl) { if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) return -1; - uint32_t ttl = 60; - // Basic answer structure struct mdns_header_t* header = (struct mdns_header_t*)buffer; header->query_id = 0; @@ -1380,6 +1386,14 @@ mdns_answer_multicast_rclass(int sock, void* buffer, size_t capacity, uint16_t r return mdns_multicast_send(sock, buffer, tosend); } +static 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 int mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_record_t* authority, size_t authority_count, @@ -1398,6 +1412,15 @@ mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t a authority_count, additional, additional_count); } +static int +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, + size_t additional_count) { + uint16_t rclass = MDNS_CLASS_IN | MDNS_CACHE_FLUSH; + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, rclass, answer, authority, + authority_count, additional, additional_count, 0); +} + static mdns_string_t mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { From 766ddde8074a54180d82cb79fc4b30f9abcce9d4 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 20:30:55 +0200 Subject: [PATCH 15/37] mdns.h - Fix clang warning: suggest braces around initialization of subobject 448f3e5fc9c1d139c4d1744481d0d3c41745fbba --- src/mdns.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index bd005ed..098e5a4 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -1313,7 +1313,7 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); - mdns_string_table_t string_table = {0}; + mdns_string_table_t string_table = {{0}, 0, 0}; void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); // Fill in question @@ -1360,7 +1360,7 @@ mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, uint16 header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); - mdns_string_table_t string_table = {0}; + mdns_string_table_t string_table = {{0}, 0, 0}; void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); // Fill in answer From 5699976b8c908438458db0fa7f6b4c6d23169540 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 21:34:06 +0200 Subject: [PATCH 16/37] mdns.cpp - sync with mdns.h 2d1563adb970e32c1aa5fb00f8efb0babee100e6 --- src/mdns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index 163b95b..665f641 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -765,7 +765,7 @@ void mDNS::executeDiscovery() { throw std::runtime_error(msg); } - MDNS_LOG << "Opened " << num_sockets << " socket" << (num_sockets ? "s" : "") << " for DNS-SD\n"; + MDNS_LOG << "Opened " << num_sockets << " socket" << (num_sockets > 1 ? "s" : "") << " for DNS-SD\n"; MDNS_LOG << "Sending DNS-SD discovery\n"; for (int isock = 0; isock < num_sockets; ++isock) { if (mdns_discovery_send(sockets[isock])) { From 5c7a4ede15223f2bc25b2d4630df402f1839b464 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 21:37:05 +0200 Subject: [PATCH 17/37] mdns.h Allow class type ANY in queries - 77fb33e0599e6f27970c9af53a887a133b654ab3 --- src/mdns.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 098e5a4..99f4d48 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -72,7 +72,7 @@ enum mdns_entry_type { MDNS_ENTRYTYPE_ADDITIONAL = 3 }; -enum mdns_class { MDNS_CLASS_IN = 1 }; +enum mdns_class { MDNS_CLASS_IN = 1, MDNS_CLASS_ANY = 255 }; typedef enum mdns_record_type mdns_record_type_t; typedef enum mdns_entry_type mdns_entry_type_t; @@ -1002,10 +1002,14 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback uint16_t rtype = mdns_ntohs(data++); uint16_t rclass = mdns_ntohs(data++); + uint16_t class_without_flushbit = rclass & ~MDNS_CACHE_FLUSH; // Make sure we get a question of class IN - if ((rclass & 0x7FFF) != MDNS_CLASS_IN) + if (!((class_without_flushbit == MDNS_CLASS_IN) || + (class_without_flushbit == MDNS_CLASS_ANY))) { break; + } + if (dns_sd && flags) continue; From 4929cbeb62fa52e60d9b329f2e61c3d7c5eece2d Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 21:38:43 +0200 Subject: [PATCH 18/37] mdns.h - Fix warnings about unused functions 22cc54d09efdd5f30c3607cac7154af744ac4a36 --- src/mdns.h | 138 ++++++++++++++++++++++++++--------------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 99f4d48..0d9c279 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -172,7 +172,7 @@ struct mdns_header_t { //! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. -static int +static inline int mdns_socket_open_ipv4(const struct sockaddr_in* saddr); //! Setup an already opened IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, @@ -180,7 +180,7 @@ mdns_socket_open_ipv4(const struct sockaddr_in* saddr); //! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. -static int +static inline int mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr); //! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass @@ -188,7 +188,7 @@ mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr); //! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. -static int +static inline int mdns_socket_open_ipv6(const struct sockaddr_in6* saddr); //! Setup an already opened IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, @@ -196,30 +196,30 @@ mdns_socket_open_ipv6(const struct sockaddr_in6* saddr); //! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a //! random user level ephemeral port. To run discovery service listening for incoming discoveries //! and queries, you must set MDNS_PORT as port. -static int +static inline int mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr); //! Close a socket opened with mdns_socket_open_ipv4 and mdns_socket_open_ipv6. -static void +static inline void mdns_socket_close(int sock); //! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been opened //! on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be 32 bit //! aligned. Parsing is stopped when callback function returns non-zero. Returns the number of //! queries parsed. -static size_t +static inline size_t mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); //! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns 0 //! on success, or <0 if error. -static int +static inline int mdns_discovery_send(int sock); //! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to //! the given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback //! function returns non-zero. Returns the number of responses parsed. -static size_t +static inline size_t mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); @@ -229,7 +229,7 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac //! multicast queries. 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 int +static inline int 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); @@ -238,7 +238,7 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng //! 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 //! function returns non-zero. Returns the number of responses parsed. -static size_t +static inline size_t mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data, int query_id); @@ -247,7 +247,7 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn //! recieved to determine if the answer should be sent unicast (bit set) or multicast (bit not set). //! Buffer must be 32 bit aligned. The record type and name should match the data from the query //! recieved. Returns 0 if success, or <0 if error. -static int +static inline int 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, const char* name, size_t name_length, mdns_record_t answer, @@ -258,7 +258,7 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo //! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine //! if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be 32 bit //! aligned. Returns 0 if success, or <0 if error. -static int +static inline int mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_record_t* authority, size_t authority_count, mdns_record_t* additional, size_t additional_count); @@ -266,14 +266,14 @@ mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record //! 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 //! startup to announce your instance to the local network. -static int +static inline int 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, size_t additional_count); //! 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. -static int +static inline int 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, size_t additional_count); @@ -281,85 +281,85 @@ mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t an // Parse records functions //! Parse a PTR record, returns the name in the record -static mdns_string_t +static inline mdns_string_t mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity); //! Parse a SRV record, returns the priority, weight, port and name in the record -static mdns_record_srv_t +static inline mdns_record_srv_t mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity); //! Parse an A record, returns the IPv4 address in the record -static struct sockaddr_in* +static inline struct sockaddr_in* mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in* addr); //! Parse an AAAA record, returns the IPv6 address in the record -static struct sockaddr_in6* +static inline struct sockaddr_in6* mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in6* addr); //! Parse a TXT record, returns the number of key=value records parsed and stores the key-value //! pairs in the supplied buffer -static size_t +static inline size_t mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, mdns_record_txt_t* records, size_t capacity); // Internal functions -static mdns_string_t +static inline mdns_string_t mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity); -static int +static inline int mdns_string_skip(const void* buffer, size_t size, size_t* offset); -static size_t +static inline size_t mdns_string_find(const char* str, size_t length, char c, size_t offset); -static int +static inline int 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); -static void* +static inline void* mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, mdns_string_table_t* string_table); -static size_t +static inline size_t mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, const char* str, size_t first_length, size_t total_length); // Implementations -static uint16_t +static inline uint16_t mdns_ntohs(const void* data) { uint16_t aligned; memcpy(&aligned, data, sizeof(uint16_t)); return ntohs(aligned); } -static uint32_t +static inline uint32_t mdns_ntohl(const void* data) { uint32_t aligned; memcpy(&aligned, data, sizeof(uint32_t)); return ntohl(aligned); } -static void* +static inline void* mdns_htons(void* data, uint16_t val) { val = htons(val); memcpy(data, &val, sizeof(uint16_t)); return MDNS_POINTER_OFFSET(data, sizeof(uint16_t)); } -static void* +static inline void* mdns_htonl(void* data, uint32_t val) { val = htonl(val); memcpy(data, &val, sizeof(uint32_t)); return MDNS_POINTER_OFFSET(data, sizeof(uint32_t)); } -static int +static inline int mdns_socket_open_ipv4(const struct sockaddr_in* saddr) { int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) @@ -371,7 +371,7 @@ mdns_socket_open_ipv4(const struct sockaddr_in* saddr) { return sock; } -static int +static inline int mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) { unsigned char ttl = 1; unsigned char loopback = 1; @@ -423,7 +423,7 @@ mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) { return 0; } -static int +static inline int mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) { int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) @@ -435,7 +435,7 @@ mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) { return sock; } -static int +static inline int mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) { int hops = 1; unsigned int loopback = 1; @@ -487,7 +487,7 @@ mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) { return 0; } -static void +static inline void mdns_socket_close(int sock) { #ifdef _WIN32 closesocket(sock); @@ -496,12 +496,12 @@ mdns_socket_close(int sock) { #endif } -static int +static inline int mdns_is_string_ref(uint8_t val) { return (0xC0 == (val & 0xC0)); } -static mdns_string_pair_t +static inline mdns_string_pair_t mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { const uint8_t* buffer = (const uint8_t*)rawdata; mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0}; @@ -535,7 +535,7 @@ mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { return pair; } -static int +static inline int mdns_string_skip(const void* buffer, size_t size, size_t* offset) { size_t cur = *offset; mdns_string_pair_t substr; @@ -555,7 +555,7 @@ mdns_string_skip(const void* buffer, size_t size, size_t* offset) { return 1; } -static int +static inline int 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 lhs_cur = *ofs_lhs; @@ -596,7 +596,7 @@ mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, cons return 1; } -static mdns_string_t +static inline mdns_string_t mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity) { size_t cur = *offset; size_t end = MDNS_INVALID_POS; @@ -634,7 +634,7 @@ mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, return result; } -static size_t +static inline size_t mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, const char* str, size_t first_length, size_t total_length) { if (!string_table) @@ -678,7 +678,7 @@ mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, si return MDNS_INVALID_POS; } -static void +static inline void mdns_string_table_add(mdns_string_table_t* string_table, size_t offset) { if (!string_table) return; @@ -692,7 +692,7 @@ mdns_string_table_add(mdns_string_table_t* string_table, size_t offset) { string_table->next = 0; } -static size_t +static inline size_t mdns_string_find(const char* str, size_t length, char c, size_t offset) { const void* found; if (offset >= length) @@ -703,14 +703,14 @@ mdns_string_find(const char* str, size_t length, char c, size_t offset) { return MDNS_INVALID_POS; } -static void* +static inline void* mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { if (capacity < 2) return 0; return mdns_htons(data, 0xC000 | (uint16_t)ref_offset); } -static void* +static inline void* mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, mdns_string_table_t* string_table) { size_t last_pos = 0; @@ -748,7 +748,7 @@ mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, si return MDNS_POINTER_OFFSET(data, 1); } -static size_t +static inline size_t mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const void* buffer, size_t size, size_t* offset, mdns_entry_type_t type, uint16_t query_id, size_t records, mdns_record_callback_fn callback, void* user_data) { @@ -782,7 +782,7 @@ mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const return parsed; } -static int +static inline int mdns_unicast_send(int sock, const void* address, size_t address_size, const void* buffer, size_t size) { if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address, @@ -791,7 +791,7 @@ mdns_unicast_send(int sock, const void* address, size_t address_size, const void return 0; } -static int +static inline int mdns_multicast_send(int sock, const void* buffer, size_t size) { struct sockaddr_storage addr_storage; struct sockaddr_in addr; @@ -829,7 +829,7 @@ mdns_multicast_send(int sock, const void* buffer, size_t size) { return 0; } -static const uint8_t mdns_services_query[] = { +static inline const uint8_t mdns_services_query[] = { // Query ID 0x00, 0x00, // Flags @@ -846,12 +846,12 @@ static const uint8_t mdns_services_query[] = { // QU (unicast response) and class IN 0x80, MDNS_CLASS_IN}; -static int +static inline int mdns_discovery_send(int sock) { return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query)); } -static size_t +static inline size_t mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data) { struct sockaddr_in6 addr; @@ -955,7 +955,7 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac return total_records; } -static size_t +static inline size_t mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data) { struct sockaddr_in6 addr; @@ -1023,7 +1023,7 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback return parsed; } -static int +static inline int 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) { if (capacity < (17 + length)) @@ -1072,7 +1072,7 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng return query_id; } -static size_t +static inline size_t mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data, int only_query_id) { struct sockaddr_in6 addr; @@ -1142,7 +1142,7 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn return total_records; } -static void* +static inline void* mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, mdns_record_type_t record_type, const char* name, size_t name_length, mdns_string_table_t* string_table) { @@ -1159,7 +1159,7 @@ mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, return data; } -static void* +static inline void* 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) { data = mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table); @@ -1176,7 +1176,7 @@ mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_re return data; } -static void* +static inline void* 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) { // TXT records will be coalesced into one record later @@ -1234,7 +1234,7 @@ mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t return data; } -static void* +static inline void* mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_record_t* records, size_t record_count, uint16_t rclass, uint32_t ttl, mdns_string_table_t* string_table) { @@ -1282,7 +1282,7 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor return data; } -static uint16_t +static inline uint16_t mdns_answer_get_record_count(mdns_record_t* records, size_t record_count) { // TXT records will be coalesced into one record uint16_t total_count = 0; @@ -1296,7 +1296,7 @@ mdns_answer_get_record_count(mdns_record_t* records, size_t record_count) { return total_count + txt_record; } -static int +static inline int 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, const char* name, size_t name_length, mdns_record_t answer, @@ -1348,7 +1348,7 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo } -static int +static inline int mdns_answer_multicast_rclass_ttl(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, uint32_t ttl) { @@ -1390,7 +1390,7 @@ mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, uint16 return mdns_multicast_send(sock, buffer, tosend); } -static int +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) { @@ -1398,7 +1398,7 @@ mdns_answer_multicast_rclass(int sock, void* buffer, size_t capacity, uint16_t r authority_count, additional, additional_count, 60); } -static int +static inline int mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, mdns_record_t* authority, size_t authority_count, mdns_record_t* additional, size_t additional_count) { @@ -1407,7 +1407,7 @@ mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record authority_count, additional, additional_count); } -static int +static inline int 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, size_t additional_count) { @@ -1416,7 +1416,7 @@ mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t a authority_count, additional, additional_count); } -static int +static inline int 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, size_t additional_count) { @@ -1425,7 +1425,7 @@ mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t an authority_count, additional, additional_count, 0); } -static mdns_string_t +static inline mdns_string_t mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { // PTR record is just a string @@ -1435,7 +1435,7 @@ mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t len return empty; } -static mdns_record_srv_t +static inline mdns_record_srv_t mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { mdns_record_srv_t srv; @@ -1457,7 +1457,7 @@ mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t len return srv; } -static struct sockaddr_in* +static inline struct sockaddr_in* mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in* addr) { memset(addr, 0, sizeof(struct sockaddr_in)); @@ -1470,7 +1470,7 @@ mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t lengt return addr; } -static struct sockaddr_in6* +static inline struct sockaddr_in6* mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, struct sockaddr_in6* addr) { memset(addr, 0, sizeof(struct sockaddr_in6)); @@ -1483,7 +1483,7 @@ mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t le return addr; } -static size_t +static inline size_t mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, mdns_record_txt_t* records, size_t capacity) { size_t parsed = 0; From 25cda9529191c55c1df61e1c500926dfbac615f2 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 21:39:42 +0200 Subject: [PATCH 19/37] mdns.h - fix bad inline decl 73a81e8907f511bbc3203202a8ed4617918bfc36 --- src/mdns.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdns.h b/src/mdns.h index 0d9c279..46d5124 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -829,7 +829,7 @@ mdns_multicast_send(int sock, const void* buffer, size_t size) { return 0; } -static inline const uint8_t mdns_services_query[] = { +static const uint8_t mdns_services_query[] = { // Query ID 0x00, 0x00, // Flags From fc6973c32c1ea8af6c8d3410829b4f8bde77958a Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 21:40:45 +0200 Subject: [PATCH 20/37] mdns.h - add missing string skip in discovery answer processing 87d43282900d2e4a1bcfb5b034dc44adc8bda269 --- src/mdns.h | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 46d5124..da2b009 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -317,6 +317,9 @@ mdns_string_skip(const void* buffer, size_t size, size_t* offset); static inline size_t 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 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); @@ -889,13 +892,13 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac int i; for (i = 0; i < questions; ++i) { - size_t ofs = MDNS_POINTER_DIFF(data, buffer); - size_t verify_ofs = 12; + size_t offset = MDNS_POINTER_DIFF(data, buffer); + size_t verify_offset = 12; // Verify it's our question, _services._dns-sd._udp.local. - if (!mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, - sizeof(mdns_services_query), &verify_ofs)) + if (!mdns_string_equal(buffer, data_size, &offset, mdns_services_query, + sizeof(mdns_services_query), &verify_offset)) 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 rclass = mdns_ntohs(data++); @@ -906,31 +909,33 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac } for (i = 0; i < answer_rrs; ++i) { - size_t ofs = MDNS_POINTER_DIFF(data, buffer); - size_t verify_ofs = 12; + size_t offset = MDNS_POINTER_DIFF(data, buffer); + size_t verify_offset = 12; // Verify it's an answer to our question, _services._dns-sd._udp.local. - size_t name_offset = ofs; - int is_answer = mdns_string_equal(buffer, data_size, &ofs, mdns_services_query, - sizeof(mdns_services_query), &verify_ofs); - size_t name_length = ofs - name_offset; - if ((ofs + 10) > data_size) + size_t name_offset = offset; + int is_answer = mdns_string_equal(buffer, data_size, &offset, mdns_services_query, + sizeof(mdns_services_query), &verify_offset); + if (!is_answer && !mdns_string_skip(buffer, data_size, &offset)) + break; + size_t name_length = offset - name_offset; + if ((offset + 10) > data_size) 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 rclass = mdns_ntohs(data++); uint32_t ttl = mdns_ntohl(data); data += 2; uint16_t length = mdns_ntohs(data++); - if (length > (data_size - ofs)) + if (length > (data_size - offset)) return 0; if (is_answer) { ++records; - ofs = MDNS_POINTER_DIFF(data, buffer); + offset = MDNS_POINTER_DIFF(data, buffer); if (callback && 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; } data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length); @@ -987,15 +992,13 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback for (int iquestion = 0; iquestion < questions; ++iquestion) { size_t question_offset = MDNS_POINTER_DIFF(data, buffer); size_t offset = question_offset; - size_t verify_ofs = 12; + size_t verify_offset = 12; int dns_sd = 0; 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; - } else { - offset = question_offset; - if (!mdns_string_skip(buffer, data_size, &offset)) - break; + } else if (!mdns_string_skip(buffer, data_size, &offset)) { + break; } size_t length = offset - question_offset; data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); @@ -1106,10 +1109,10 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn // Skip questions part int i; for (i = 0; i < questions; ++i) { - size_t ofs = MDNS_POINTER_DIFF(data, buffer); - if (!mdns_string_skip(buffer, data_size, &ofs)) + size_t offset = MDNS_POINTER_DIFF(data, buffer); + if (!mdns_string_skip(buffer, data_size, &offset)) 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 uint16_t rtype = mdns_ntohs(data++); uint16_t rclass = mdns_ntohs(data++);*/ From 06931b8caa23f1af41dcaa65c6258877a43faeff Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 21:42:29 +0200 Subject: [PATCH 21/37] mdns.h - add multiquery function a70b8a3dd6402559185673636b0626935888d6ce --- src/mdns.h | 55 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index da2b009..bee252e 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -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_aaaa_t mdns_record_aaaa_t; typedef struct mdns_record_txt_t mdns_record_txt_t; +typedef struct mdns_query_t mdns_query_t; #ifdef _WIN32 typedef int mdns_size_t; @@ -165,6 +166,12 @@ struct mdns_header_t { uint16_t additional_rrs; }; +struct mdns_query_t { + mdns_record_type_t type; + const char* name; + size_t length; +}; + // mDNS/DNS-SD public API //! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass @@ -233,6 +240,18 @@ static inline int 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); +//! 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_discovery_recv, optionally filtering //! 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 @@ -1029,7 +1048,17 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback static inline int 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) { - 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; // Ask for a unicast response since it's a one-shot query @@ -1049,25 +1078,27 @@ 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; // Query ID - header->query_id = htons(query_id); + header->query_id = htons((unsigned short)query_id); // Flags header->flags = 0; // Questions - header->questions = htons(1); + header->questions = htons((unsigned short)count); // No answer, authority or additional RRs header->answer_rrs = 0; header->authority_rrs = 0; header->additional_rrs = 0; - // Fill in question - // Name string + // Fill in questions void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - data = mdns_string_make(buffer, capacity, data, name, length, 0); - if (!data) - return -1; - // Record type - data = mdns_htons(data, type); - //! Optional unicast response based on local port, class IN - data = mdns_htons(data, rclass); + for (size_t iq = 0; iq < count; ++iq) { + // Name string + data = mdns_string_make(buffer, capacity, data, query[iq].name, query[iq].length, 0); + if (!data) + 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); if (mdns_multicast_send(sock, buffer, (size_t)tosend)) From 9be5021915fbc088d0dfa7c25eceb101e60a6a38 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:13:06 +0200 Subject: [PATCH 22/37] mdns.cpp - basic multi-query support --- include/mdns_cpp/mdns.hpp | 3 ++- src/mdns.cpp | 40 +++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index 82e42a1..7dffe1e 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -30,7 +30,8 @@ class mDNS { void setServiceName(const std::string &name); void setServiceTxtRecord(const std::string &text_record); - void executeQuery(const std::string &service, int record); + using ServiceQueries = std::vector>; + void executeQuery(ServiceQueries service); void executeDiscovery(); private: diff --git a/src/mdns.cpp b/src/mdns.cpp index 665f641..6c02999 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -683,7 +683,7 @@ void mDNS::runMainLoop() { MDNS_LOG << "Closed socket " << (num_sockets ? "s" : "") << "\n"; } -void mDNS::executeQuery(const std::string &service, int record) { +void mDNS::executeQuery(ServiceQueries serviceQueries) { int sockets[32]; int query_id[32]; int num_sockets = openClientSockets(sockets, sizeof(sockets) / sizeof(sockets[0]), 0); @@ -698,22 +698,28 @@ void mDNS::executeQuery(const std::string &service, int record) { size_t capacity = 2048; void *buffer = malloc(capacity); void *user_data = 0; - size_t records; - const char *record_name = "PTR"; - if (record == MDNS_RECORDTYPE_SRV) - record_name = "SRV"; - else if (record == MDNS_RECORDTYPE_A) - record_name = "A"; - else if (record == MDNS_RECORDTYPE_AAAA) - record_name = "AAAA"; - else - record = MDNS_RECORDTYPE_PTR; + std::vector queries; + + MDNS_LOG << "Sending mDNS query"; + for (auto &query : serviceQueries) { + auto &[name, type] = query; + const char *record_name = "PTR"; + if (type == MDNS_RECORDTYPE_SRV) + record_name = "SRV"; + else if (type == MDNS_RECORDTYPE_A) + record_name = "A"; + else if (type == MDNS_RECORDTYPE_AAAA) + record_name = "AAAA"; + else + type = MDNS_RECORDTYPE_PTR; + queries.push_back(mdns_query_t{static_cast(type), name.c_str(), name.length()}); + MDNS_LOG << " : " << name.c_str() << " " << record_name; + } + MDNS_LOG << ("\n"); - MDNS_LOG << "Sending mDNS query: " << service << " " << record_name << "\n"; for (int isock = 0; isock < num_sockets; ++isock) { - query_id[isock] = mdns_query_send(sockets[isock], (mdns_record_type)record, service.data(), strlen(service.data()), - buffer, capacity, 0); + query_id[isock] = mdns_multiquery_send(sockets[isock], queries.data(), queries.size(), buffer, capacity, 0); if (query_id[isock] < 0) { MDNS_LOG << "Failed to send mDNS query: " << strerror(errno) << "\n"; } @@ -723,6 +729,7 @@ void mDNS::executeQuery(const std::string &service, int record) { // get replies int res{}; MDNS_LOG << "Reading mDNS query replies\n"; + int records = 0; do { struct timeval timeout; timeout.tv_sec = 10; @@ -736,17 +743,18 @@ void mDNS::executeQuery(const std::string &service, int record) { FD_SET(sockets[isock], &readfs); } - records = 0; res = select(nfds, &readfs, 0, 0, &timeout); if (res > 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { - records += mdns_query_recv(sockets[isock], buffer, capacity, query_callback, user_data, query_id[isock]); + auto rec = mdns_query_recv(sockets[isock], buffer, capacity, query_callback, user_data, query_id[isock]); + if (rec > 0) records += rec; } FD_SET(sockets[isock], &readfs); } } } while (res > 0); + MDNS_LOG << "Read " << records << "records\n"; free(buffer); From 51b31639888693db0838ad02dbf2ecbe434dbe64 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:17:17 +0200 Subject: [PATCH 23/37] mdns.h - fix flush bit for shared record d62c6118f78a7107c92a3ea5f70208599b34816b mdns.c - includes dump_callback and dump_dns which are omitted (on this commit) from mdns.cpp updates. --- include/mdns_cpp/mdns.hpp | 1 + src/mdns.cpp | 30 ++++- src/mdns.h | 223 ++++++++++++++++++++++---------------- 3 files changed, 157 insertions(+), 97 deletions(-) diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index 7dffe1e..bff48b7 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -11,6 +11,7 @@ #include #else #include +#include #include #include #endif diff --git a/src/mdns.cpp b/src/mdns.cpp index 6c02999..72fb613 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -191,6 +191,8 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { if (!ifa->ifa_addr) { continue; } + if (!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_MULTICAST)) continue; + if ((ifa->ifa_flags & IFF_LOOPBACK) || (ifa->ifa_flags & IFF_POINTOPOINT)) continue; if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *saddr = (struct sockaddr_in *)ifa->ifa_addr; @@ -357,6 +359,8 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns record_name = "A"; else if (rtype == MDNS_RECORDTYPE_AAAA) record_name = "AAAA"; + else if (rtype == MDNS_RECORDTYPE_TXT) + record_name = "TXT"; else if (rtype == MDNS_RECORDTYPE_ANY) record_name = "ANY"; else @@ -594,7 +598,9 @@ void mDNS::runMainLoop() { // ".<_service-name>._tcp.local." service_record.record_ptr = {.name = to_mdns_str(service_record.service), .type = MDNS_RECORDTYPE_PTR, - .data.ptr.name = to_mdns_str(service_record.service_instance)}; + .data.ptr.name = to_mdns_str(service_record.service_instance), + .rclass = 0, + .ttl = 0}; // SRV record mapping ".<_service-name>._tcp.local." to // ".local." with port. Set weight & priority to 0. @@ -603,29 +609,40 @@ void mDNS::runMainLoop() { .data.srv.name = to_mdns_str(service_record.hostname_qualified), .data.srv.port = service_record.port, .data.srv.priority = 0, - .data.srv.weight = 0}; + .data.srv.weight = 0, + .rclass = 0, + .ttl = 0}; // A/AAAA records mapping ".local." to IPv4/IPv6 addresses service_record.record_a = {.name = to_mdns_str(service_record.hostname_qualified), .type = MDNS_RECORDTYPE_A, - .data.a.addr = service_record.address_ipv4}; + .data.a.addr = service_record.address_ipv4, + .rclass = 0, + .ttl = 0}; service_record.record_aaaa = {.name = to_mdns_str(service_record.hostname_qualified), .type = MDNS_RECORDTYPE_AAAA, - .data.aaaa.addr = service_record.address_ipv6}; + .data.aaaa.addr = service_record.address_ipv6, + .rclass = 0, + .ttl = 0}; // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library service_record.txt_record[0] = {.name = to_mdns_str(service_record.service_instance), .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("test")}, - .data.txt.value = {MDNS_STRING_CONST("1")}}; + .data.txt.value = {MDNS_STRING_CONST("1")}, + .rclass = 0, + .ttl = 0}; service_record.txt_record[1] = {.name = to_mdns_str(service_record.service_instance), .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("other")}, - .data.txt.value = {MDNS_STRING_CONST("value")}}; + .data.txt.value = {MDNS_STRING_CONST("value")}, + .rclass = 0, + .ttl = 0}; // Send an announcement on startup of service { + MDNS_LOG << "Sending announce\n"; mdns_record_t additional[5] = {{}}; size_t additional_count = 0; additional[additional_count++] = service_record.record_srv; @@ -663,6 +680,7 @@ void mDNS::runMainLoop() { // Send a goodbye on end of service { + MDNS_LOG << "Sending goodbye\n"; mdns_record_t additional[5] = {{}}; size_t additional_count = 0; additional[additional_count++] = service_record.record_srv; diff --git a/src/mdns.h b/src/mdns.h index bee252e..5f3d7ab 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -155,6 +155,8 @@ struct mdns_record_t { mdns_record_aaaa_t aaaa; mdns_record_txt_t txt; } data; + uint16_t rclass; + uint32_t ttl; }; struct mdns_header_t { @@ -270,8 +272,8 @@ static inline int 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, const char* name, size_t name_length, mdns_record_t answer, - mdns_record_t* authority, size_t authority_count, - mdns_record_t* additional, size_t additional_count); + const mdns_record_t* authority, size_t authority_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 //! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine @@ -279,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. static inline int mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - mdns_record_t* authority, size_t authority_count, - mdns_record_t* additional, size_t additional_count); + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); //! 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 //! startup to announce your instance to the local network. static inline int 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, - size_t additional_count); + const mdns_record_t* authority, size_t authority_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 //! from the local network. The records must be identical to the according announcement. static inline int 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, - size_t additional_count); + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); // Parse records functions @@ -904,10 +906,8 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac // It seems some implementations do not fill the correct questions field, // so ignore this check for now and only validate answer string - /* - if (questions != 1) - return 0; - */ + // if (questions != 1) + // return 0; int i; for (i = 0; i < questions; ++i) { @@ -999,15 +999,12 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback uint16_t query_id = mdns_ntohs(data++); uint16_t flags = 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 authority_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) { size_t question_offset = MDNS_POINTER_DIFF(data, buffer); size_t offset = question_offset; @@ -1026,7 +1023,7 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback uint16_t rclass = mdns_ntohs(data++); 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) || (class_without_flushbit == MDNS_CLASS_ANY))) { break; @@ -1035,14 +1032,32 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback if (dns_sd && flags) continue; - ++parsed; + ++total_records; if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, rclass, 0, buffer, data_size, question_offset, length, 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 @@ -1144,9 +1159,9 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn if (!mdns_string_skip(buffer, data_size, &offset)) return 0; data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); - /* Record type and class not used, skip - uint16_t rtype = mdns_ntohs(data++); - uint16_t rclass = mdns_ntohs(data++);*/ + // Record type and class not used, skip + // uint16_t rtype = mdns_ntohs(data++); + // uint16_t rclass = mdns_ntohs(data++); data += 2; } @@ -1195,7 +1210,7 @@ mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, static inline void* 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); if (!data) return 0; @@ -1204,20 +1219,20 @@ mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_re return 0; data = mdns_htons(data, record.type); - data = mdns_htons(data, rclass); - data = mdns_htonl(data, ttl); + data = mdns_htons(data, record.rclass); + data = mdns_htonl(data, record.ttl); data = mdns_htons(data, 0); // Length, to be filled later return data; } static inline void* 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 if (!data || (record.type == MDNS_RECORDTYPE_TXT)) 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) return 0; @@ -1268,8 +1283,20 @@ mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t 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* -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, mdns_string_table_t* string_table) { // Pointer to length of record to be filled at end @@ -1281,17 +1308,19 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor if (records[irec].type != MDNS_RECORDTYPE_TXT) continue; + mdns_record_t record = records[irec]; + mdns_record_update_rclass_ttl(&record, rclass, ttl); if (!record_data) { - data = mdns_answer_add_record_header(buffer, capacity, data, records[irec], rclass, ttl, - string_table); + data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table); + if (!data) + return data; record_length = MDNS_POINTER_OFFSET(data, -2); record_data = data; } // TXT strings are unlikely to be shared, just make then raw. Also need one byte for // termination, thus the <= check - size_t string_length = - records[irec].data.txt.key.length + records[irec].data.txt.value.length + 1; + size_t string_length = record.data.txt.key.length + record.data.txt.value.length + 1; if (!data) return 0; remain = capacity - MDNS_POINTER_DIFF(data, buffer); @@ -1300,11 +1329,11 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor unsigned char* strdata = (unsigned char*)data; *strdata++ = (unsigned char)string_length; - memcpy(strdata, records[irec].data.txt.key.str, records[irec].data.txt.key.length); - strdata += records[irec].data.txt.key.length; + memcpy(strdata, record.data.txt.key.str, record.data.txt.key.length); + strdata += record.data.txt.key.length; *strdata++ = '='; - memcpy(strdata, records[irec].data.txt.value.str, records[irec].data.txt.value.length); - strdata += records[irec].data.txt.value.length; + memcpy(strdata, record.data.txt.value.str, record.data.txt.value.length); + strdata += record.data.txt.value.length; data = strdata; } @@ -1317,7 +1346,7 @@ mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, mdns_recor } 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 uint16_t total_count = 0; uint16_t txt_record = 0; @@ -1334,12 +1363,15 @@ static inline int 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, const char* name, size_t name_length, mdns_record_t answer, - mdns_record_t* authority, size_t authority_count, - mdns_record_t* additional, size_t additional_count) { + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) 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; // Basic answer structure @@ -1359,21 +1391,31 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo &string_table); // 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 - for (size_t irec = 0; data && (irec < authority_count); ++irec) - data = mdns_answer_add_record(buffer, capacity, data, authority[irec], rclass, ttl, - &string_table); - data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, - ttl, &string_table); + for (size_t irec = 0; data && (irec < authority_count); ++irec) { + mdns_record_t record = authority[irec]; + record.rclass = rclass; + if (!record.ttl) + 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 - for (size_t irec = 0; data && (irec < additional_count); ++irec) - data = mdns_answer_add_record(buffer, capacity, data, additional[irec], rclass, ttl, - &string_table); - data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, - ttl, &string_table); + for (size_t irec = 0; data && (irec < additional_count); ++irec) { + mdns_record_t record = additional[irec]; + record.rclass = rclass; + if (!record.ttl) + 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) return -1; @@ -1381,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); } - static inline int -mdns_answer_multicast_rclass_ttl(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, uint32_t ttl) { +mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count, + uint16_t rclass, uint32_t ttl) { if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) return -1; @@ -1402,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)); // 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 - for (size_t irec = 0; data && (irec < authority_count); ++irec) - data = mdns_answer_add_record(buffer, capacity, data, authority[irec], rclass, ttl, - &string_table); - data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, - ttl, &string_table); + for (size_t irec = 0; data && (irec < authority_count); ++irec) { + record = authority[irec]; + mdns_record_update_rclass_ttl(&record, rclass, 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 - for (size_t irec = 0; data && (irec < additional_count); ++irec) - data = mdns_answer_add_record(buffer, capacity, data, additional[irec], rclass, ttl, - &string_table); - data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, - ttl, &string_table); + for (size_t irec = 0; data && (irec < additional_count); ++irec) { + record = additional[irec]; + mdns_record_update_rclass_ttl(&record, rclass, 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) return -1; @@ -1424,39 +1472,32 @@ mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, uint16 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 mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - mdns_record_t* authority, size_t authority_count, - mdns_record_t* additional, size_t additional_count) { - uint16_t rclass = MDNS_CLASS_IN; - return mdns_answer_multicast_rclass(sock, buffer, capacity, rclass, answer, authority, - authority_count, additional, additional_count); + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, + authority_count, additional, additional_count, + MDNS_CLASS_IN, 60); } static inline int 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, - size_t additional_count) { - uint16_t rclass = MDNS_CLASS_IN | MDNS_CACHE_FLUSH; - return mdns_answer_multicast_rclass(sock, buffer, capacity, rclass, answer, authority, - authority_count, additional, additional_count); + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, + authority_count, additional, additional_count, + MDNS_CLASS_IN | MDNS_CACHE_FLUSH, 60); } static inline int 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, - size_t additional_count) { - uint16_t rclass = MDNS_CLASS_IN | MDNS_CACHE_FLUSH; - return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, rclass, answer, authority, - authority_count, additional, additional_count, 0); + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + // Goodbye should have ttl of 0 + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, + authority_count, additional, additional_count, + MDNS_CLASS_IN, 0); } static inline mdns_string_t From 769af8276a588b5b15ad99ea71846477d570c11d Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Mon, 4 Nov 2024 10:34:32 +0200 Subject: [PATCH 24/37] mdns.cpp - add dump mode similar to mdns.c one. --- include/mdns_cpp/mdns.hpp | 4 ++- src/mdns.cpp | 76 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index bff48b7..ce3f038 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -22,7 +22,7 @@ class mDNS { public: ~mDNS(); - void startService(); + void startService(bool dumpMode = false); void stopService(); bool isServiceRunning(); @@ -37,6 +37,7 @@ class mDNS { private: void runMainLoop(); + void runDumpMode(int *sockets, int num_sockets); int openClientSockets(int *sockets, int max_sockets, int port); int openServiceSockets(int *sockets, int max_sockets); @@ -46,6 +47,7 @@ class mDNS { std::string txt_record_{}; bool running_{false}; + bool dumpMode_{false}; struct sockaddr_in service_address_ipv4_; struct sockaddr_in6 service_address_ipv6_; diff --git a/src/mdns.cpp b/src/mdns.cpp index 72fb613..8d38525 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -74,6 +74,44 @@ int mDNS::openServiceSockets(int *sockets, int max_sockets) { return num_sockets; } +// Callback handling questions and answers dump +static int dump_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns_entry_type_t entry, + uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void *data, + size_t size, size_t name_offset, size_t name_length, size_t record_offset, + size_t record_length, void *user_data) { + char addrbuffer[64]{}; + char namebuffer[256]{}; + + const auto fromaddrstr = ipAddressToString(addrbuffer, sizeof(addrbuffer), from, addrlen); + size_t offset = name_offset; + mdns_string_t name = mdns_string_extract(data, size, &offset, namebuffer, sizeof(namebuffer)); + const char *record_name = 0; + if (rtype == MDNS_RECORDTYPE_PTR) + record_name = "PTR"; + else if (rtype == MDNS_RECORDTYPE_SRV) + record_name = "SRV"; + else if (rtype == MDNS_RECORDTYPE_A) + record_name = "A"; + else if (rtype == MDNS_RECORDTYPE_AAAA) + record_name = "AAAA"; + else if (rtype == MDNS_RECORDTYPE_TXT) + record_name = "TXT"; + else if (rtype == MDNS_RECORDTYPE_ANY) + record_name = "ANY"; + else + record_name = ""; + const char *entry_type = "Question"; + if (entry == MDNS_ENTRYTYPE_ANSWER) + entry_type = "Answer"; + else if (entry == MDNS_ENTRYTYPE_AUTHORITY) + entry_type = "Authority"; + else if (entry == MDNS_ENTRYTYPE_ADDITIONAL) + entry_type = "Additional"; + printf("%.*s: %s %s %.*s rclass 0x%x ttl %u\n", (int)fromaddrstr.length(), fromaddrstr.c_str(), entry_type, + record_name, MDNS_STRING_FORMAT(name), (unsigned int)rclass, ttl); + return 0; +} + int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { // When sending, each socket can only send to one network interface // Thus we need to open one socket for each interface and address family @@ -523,7 +561,8 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns mDNS::~mDNS() { stopService(); } -void mDNS::startService() { +void mDNS::startService(const bool dumpMode) { + dumpMode_ = dumpMode; if (running_) { stopService(); } @@ -559,6 +598,12 @@ void mDNS::runMainLoop() { throw std::runtime_error(msg); } + if (dumpMode_) { + runDumpMode(sockets, num_sockets); + dumpMode_ = false; + return; + } + if (name_.length() == 0) { const auto msg = "Error: nvalid service name\n"; MDNS_LOG << msg << "\n"; @@ -840,4 +885,33 @@ void mDNS::executeDiscovery() { MDNS_LOG << "Closed socket" << (num_sockets ? "s" : "") << "\n"; } +void mDNS::runDumpMode(int *sockets, const int num_sockets) { + printf("Opened %d socket%s for mDNS dump\n", num_sockets, num_sockets > 1 ? "s" : ""); + size_t capacity = 2048; + void *buffer = malloc(capacity); + // This is a crude implementation that checks for incoming queries and answers + while (running_) { + int nfds = 0; + fd_set readfs; + FD_ZERO(&readfs); + for (int isock = 0; isock < num_sockets; ++isock) { + if (sockets[isock] >= nfds) nfds = sockets[isock] + 1; + FD_SET(sockets[isock], &readfs); + } + if (select(nfds, &readfs, 0, 0, 0) >= 0) { + for (int isock = 0; isock < num_sockets; ++isock) { + if (FD_ISSET(sockets[isock], &readfs)) { + mdns_socket_listen(sockets[isock], buffer, capacity, dump_callback, 0); + } + FD_SET(sockets[isock], &readfs); + } + } else { + break; + } + } + free(buffer); + for (int isock = 0; isock < num_sockets; ++isock) mdns_socket_close(sockets[isock]); + printf("Closed socket%s\n", num_sockets > 1 ? "s" : ""); +} + } // namespace mdns_cpp From 1586503e4d4e1ea5a71db83ea2b14530c4987d93 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:46:54 +0200 Subject: [PATCH 25/37] mdns.cpp - ignore link local addresses similar to mdns.h 40a8b4f77a618ac0a26130aae4a330815c755af7 --- src/mdns.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mdns.cpp b/src/mdns.cpp index 8d38525..8e2658a 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -181,6 +181,8 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { } } else if (unicast->Address.lpSockaddr->sa_family == AF_INET6) { struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)unicast->Address.lpSockaddr; + // Ignore link-local addresses + if (saddr->sin6_scope_id) continue; static constexpr unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; static constexpr unsigned char localhost_mapped[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1}; if ((unicast->DadState == NldsPreferred) && memcmp(saddr->sin6_addr.s6_addr, localhost, 16) && @@ -260,6 +262,8 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { } } else if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)ifa->ifa_addr; + // Ignore link-local addresses + if (saddr->sin6_scope_id) continue; static constexpr unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; static constexpr unsigned char localhost_mapped[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1}; if (memcmp(saddr->sin6_addr.s6_addr, localhost, 16) && memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) { From e4aaadbbe676a8a2421b7886a36fd4652fed732f Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:48:25 +0200 Subject: [PATCH 26/37] mdns.h - ignore questions in query receive e092f068b9cedccec06d040f3a0a950674060a7f --- src/mdns.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 5f3d7ab..08a13c4 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -1149,9 +1149,6 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn if ((only_query_id > 0) && (query_id != only_query_id)) return 0; // Not a reply to the wanted one-shot query - if (questions > 1) - return 0; - // Skip questions part int i; for (i = 0; i < questions; ++i) { From abc08a1e79c9157ca986fd711fec788dca9e7c9b Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:49:47 +0200 Subject: [PATCH 27/37] mdns.h - ignore questions in query receive e092f068b9cedccec06d040f3a0a950674060a7f --- src/mdns.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mdns.h b/src/mdns.h index 08a13c4..fc5a7f1 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -1572,11 +1572,13 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len ++strdata; offset += sublength + 1; - size_t separator = 0; + size_t separator = sublength; for (size_t c = 0; c < sublength; ++c) { // 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; + } if (strdata[c] == '=') { separator = c; break; From beb103d5eea9bbbe9bc2d84f43ab2bdaf426c062 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:50:47 +0200 Subject: [PATCH 28/37] mdns.h - Reset value parameters for boolean txt records 660b872dddcd870cd146fee392298bdfaf986ce1 --- src/mdns.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mdns.h b/src/mdns.h index fc5a7f1..ab0d326 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -1596,6 +1596,8 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len } else { records[parsed].key.str = strdata; records[parsed].key.length = sublength; + records[parsed].value.str = 0; + records[parsed].value.length = 0; } ++parsed; From af6a7f3b328bd01ed8c38ad04e5a71d17b430bce Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:51:29 +0200 Subject: [PATCH 29/37] mdns.h - Fix a possible heap overflow in txt record parse 166152bfbd84bb2e4d75963e700ab14e858ac626 --- src/mdns.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mdns.h b/src/mdns.h index ab0d326..fe79717 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -1569,6 +1569,9 @@ mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t len strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset); size_t sublength = *(const unsigned char*)strdata; + if (sublength >= (end - offset)) + break; + ++strdata; offset += sublength + 1; From c668e7035da56d0622a2d4c84103501d5fd6d726 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:54:01 +0200 Subject: [PATCH 30/37] mdns.h -Avoid buffer overflow in send path 8aabf221e78c62cd059416f81bde89cb02aca4f7 --- src/mdns.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mdns.h b/src/mdns.h index fe79717..56008b9 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -254,7 +254,7 @@ 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_discovery_recv, optionally filtering +//! 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, //! 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 @@ -1109,6 +1109,9 @@ mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* bu data = mdns_string_make(buffer, capacity, data, query[iq].name, query[iq].length, 0); if (!data) return -1; + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (remain < 4) + return -1; // Record type data = mdns_htons(data, query[iq].type); //! Optional unicast response based on local port, class IN From aa4bd60d6650ea0e7eb0734dbb467d6ba0933300 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 3 Nov 2024 22:57:09 +0200 Subject: [PATCH 31/37] mdns.cpp - Semantic print fixes similar to mdns.h 23c6f01bb8c68e0e9f6c1ee17b7ae721344925ab --- src/mdns.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index 8e2658a..457a480 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -615,7 +615,7 @@ void mDNS::runMainLoop() { } if (!name_.ends_with(".")) name_ += "."; - MDNS_LOG << "Opened " << std::to_string(num_sockets) << " socket" << (num_sockets ? "s" : "") + MDNS_LOG << "Opened " << std::to_string(num_sockets) << " socket" << (num_sockets > 1 ? "s" : "") << " for mDNS service\n"; MDNS_LOG << "Service mDNS: " << name_ << ":" << port_ << "\n"; MDNS_LOG << "Hostname: " << hostname_.data() << "\n"; @@ -747,7 +747,7 @@ void mDNS::runMainLoop() { for (int isock = 0; isock < num_sockets; ++isock) { mdns_socket_close(sockets[isock]); } - MDNS_LOG << "Closed socket " << (num_sockets ? "s" : "") << "\n"; + MDNS_LOG << "Closed socket " << (num_sockets > 1 ? "s" : "") << "\n"; } void mDNS::executeQuery(ServiceQueries serviceQueries) { @@ -760,7 +760,7 @@ void mDNS::executeQuery(ServiceQueries serviceQueries) { MDNS_LOG << msg << "\n"; throw std::runtime_error(msg); } - MDNS_LOG << "Opened " << num_sockets << " socket" << (num_sockets ? "s" : "") << " for mDNS query\n"; + MDNS_LOG << "Opened " << num_sockets << " socket" << (num_sockets > 1 ? "s" : "") << " for mDNS query\n"; size_t capacity = 2048; void *buffer = malloc(capacity); @@ -828,7 +828,7 @@ void mDNS::executeQuery(ServiceQueries serviceQueries) { for (int isock = 0; isock < num_sockets; ++isock) { mdns_socket_close(sockets[isock]); } - MDNS_LOG << "Closed socket" << (num_sockets ? "s" : "") << "\n"; + MDNS_LOG << "Closed socket" << (num_sockets > 1 ? "s" : "") << "\n"; } void mDNS::executeDiscovery() { @@ -886,7 +886,7 @@ void mDNS::executeDiscovery() { for (int isock = 0; isock < num_sockets; ++isock) { mdns_socket_close(sockets[isock]); } - MDNS_LOG << "Closed socket" << (num_sockets ? "s" : "") << "\n"; + MDNS_LOG << "Closed socket" << (num_sockets > 1 ? "s" : "") << "\n"; } void mDNS::runDumpMode(int *sockets, const int num_sockets) { From 8b5703645039fc603e7f131a83c8c5b3050765a5 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Mon, 4 Nov 2024 16:39:12 +0200 Subject: [PATCH 32/37] renamed to_mdns_str to hint it's a ref/ptr rather than by value. --- src/mdns.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index 457a480..bdf1d22 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -30,7 +30,7 @@ class ServiceRecord { mdns_record_t txt_record[2]; }; -mdns_string_t to_mdns_str(const std::string &str) { return {str.c_str(), str.length()}; } +mdns_string_t to_mdns_str_ref(const std::string &str_ref) { return {str_ref.c_str(), str_ref.length()}; } int mDNS::openServiceSockets(int *sockets, int max_sockets) { // When receiving, each socket can receive data from all network interfaces @@ -415,7 +415,7 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns // Answer PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." mdns_record_t answer = { - .name = name, .type = MDNS_RECORDTYPE_PTR, .data.ptr.name = to_mdns_str(service_record->service)}; + .name = name, .type = MDNS_RECORDTYPE_PTR, .data.ptr.name = to_mdns_str_ref(service_record->service)}; // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); printf(" --> answer %.*s (%s)\n", MDNS_STRING_FORMAT(answer.data.ptr.name), (unicast ? "unicast" : "multicast")); @@ -519,8 +519,8 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns const auto addrstr = ipAddressToString(addrbuffer, sizeof(addrbuffer), (struct sockaddr *)&service_record->record_a.data.a.addr, sizeof(service_record->record_a.data.a.addr)); - printf(" --> answer %.*s IPv4 %.*s (%s)\n", MDNS_STRING_FORMAT(service_record->record_a.name), (int)addrstr.length(), addrstr.c_str(), - (unicast ? "unicast" : "multicast")); + printf(" --> answer %.*s IPv4 %.*s (%s)\n", MDNS_STRING_FORMAT(service_record->record_a.name), + (int)addrstr.length(), addrstr.c_str(), (unicast ? "unicast" : "multicast")); if (unicast) { mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, static_cast(rtype), name.str, name.length, answer, 0, 0, @@ -548,8 +548,8 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns auto addrstr = ipAddressToString(addrbuffer, sizeof(addrbuffer), (struct sockaddr *)&service_record->record_aaaa.data.aaaa.addr, sizeof(service_record->record_aaaa.data.aaaa.addr)); - printf(" --> answer %.*s IPv6 %.*s (%s)\n", MDNS_STRING_FORMAT(service_record->record_a.name), (int)addrstr.length(), addrstr.c_str(), - (unicast ? "unicast" : "multicast")); + printf(" --> answer %.*s IPv6 %.*s (%s)\n", MDNS_STRING_FORMAT(service_record->record_a.name), + (int)addrstr.length(), addrstr.c_str(), (unicast ? "unicast" : "multicast")); if (unicast) { mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, static_cast(rtype), name.str, name.length, answer, 0, 0, @@ -645,17 +645,17 @@ void mDNS::runMainLoop() { // PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." - service_record.record_ptr = {.name = to_mdns_str(service_record.service), + service_record.record_ptr = {.name = to_mdns_str_ref(service_record.service), .type = MDNS_RECORDTYPE_PTR, - .data.ptr.name = to_mdns_str(service_record.service_instance), + .data.ptr.name = to_mdns_str_ref(service_record.service_instance), .rclass = 0, .ttl = 0}; // SRV record mapping ".<_service-name>._tcp.local." to // ".local." with port. Set weight & priority to 0. - service_record.record_srv = {.name = to_mdns_str(service_record.service_instance), + service_record.record_srv = {.name = to_mdns_str_ref(service_record.service_instance), .type = MDNS_RECORDTYPE_SRV, - .data.srv.name = to_mdns_str(service_record.hostname_qualified), + .data.srv.name = to_mdns_str_ref(service_record.hostname_qualified), .data.srv.port = service_record.port, .data.srv.priority = 0, .data.srv.weight = 0, @@ -663,12 +663,12 @@ void mDNS::runMainLoop() { .ttl = 0}; // A/AAAA records mapping ".local." to IPv4/IPv6 addresses - service_record.record_a = {.name = to_mdns_str(service_record.hostname_qualified), + service_record.record_a = {.name = to_mdns_str_ref(service_record.hostname_qualified), .type = MDNS_RECORDTYPE_A, .data.a.addr = service_record.address_ipv4, .rclass = 0, .ttl = 0}; - service_record.record_aaaa = {.name = to_mdns_str(service_record.hostname_qualified), + service_record.record_aaaa = {.name = to_mdns_str_ref(service_record.hostname_qualified), .type = MDNS_RECORDTYPE_AAAA, .data.aaaa.addr = service_record.address_ipv6, .rclass = 0, @@ -676,13 +676,13 @@ void mDNS::runMainLoop() { // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library - service_record.txt_record[0] = {.name = to_mdns_str(service_record.service_instance), + service_record.txt_record[0] = {.name = to_mdns_str_ref(service_record.service_instance), .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("test")}, .data.txt.value = {MDNS_STRING_CONST("1")}, .rclass = 0, .ttl = 0}; - service_record.txt_record[1] = {.name = to_mdns_str(service_record.service_instance), + service_record.txt_record[1] = {.name = to_mdns_str_ref(service_record.service_instance), .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("other")}, .data.txt.value = {MDNS_STRING_CONST("value")}, From c80217ba6768526c18b4b0bd537000454a23ac46 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Mon, 4 Nov 2024 17:33:25 +0200 Subject: [PATCH 33/37] Added flag for graceful exit of long-running operations (similar to 95a2b736780bbe22108034bc179da65480c76715 but not installing signaling which I guess should be managed by the app running the lib). --- src/mdns.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index bdf1d22..92041dd 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -715,7 +715,10 @@ void mDNS::runMainLoop() { FD_SET(sockets[isock], &readfs); } - if (select(nfds, &readfs, 0, 0, 0) >= 0) { + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + if (select(nfds, &readfs, 0, 0, &timeout) >= 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { mdns_socket_listen(sockets[isock], buffer.get(), capacity, service_callback, &service_record); @@ -902,7 +905,10 @@ void mDNS::runDumpMode(int *sockets, const int num_sockets) { if (sockets[isock] >= nfds) nfds = sockets[isock] + 1; FD_SET(sockets[isock], &readfs); } - if (select(nfds, &readfs, 0, 0, 0) >= 0) { + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + if (select(nfds, &readfs, 0, 0, &timeout) >= 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { mdns_socket_listen(sockets[isock], buffer, capacity, dump_callback, 0); From 775196c5152ccfbd8f4be17483ebb9fe0181196c Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Mon, 4 Nov 2024 17:33:53 +0200 Subject: [PATCH 34/37] tidy/nit - made TSAN happy on macOS. --- include/mdns_cpp/mdns.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index ce3f038..9ffd1c6 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -46,7 +46,7 @@ class mDNS { std::uint16_t port_{42424}; std::string txt_record_{}; - bool running_{false}; + std::atomic running_{false}; bool dumpMode_{false}; struct sockaddr_in service_address_ipv4_; From 00f7bba996f2c35fb3d410056f832ccb3cb1280c Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Tue, 5 Nov 2024 10:10:18 +0200 Subject: [PATCH 35/37] tidy - fixed missing whitespace. --- src/mdns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdns.cpp b/src/mdns.cpp index 92041dd..10be603 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -824,7 +824,7 @@ void mDNS::executeQuery(ServiceQueries serviceQueries) { } } } while (res > 0); - MDNS_LOG << "Read " << records << "records\n"; + MDNS_LOG << "Read " << records << " records\n"; free(buffer); From b9a61d4f30e4aa78df2e223af75d62afe2492c37 Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 10 Nov 2024 13:12:33 +0200 Subject: [PATCH 36/37] fixup for executeQuery changes --- example/query.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/query.cpp b/example/query.cpp index db69f4c..d543d8d 100644 --- a/example/query.cpp +++ b/example/query.cpp @@ -39,7 +39,7 @@ int main() { mdns_cpp::mDNS mdns; const std::string service = "_http._tcp.local."; - mdns.executeQuery(service); + mdns.executeQuery({{service, 0}}); while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); From 64316035cf0fc72d4bab3f11abdd06b04c3f9e8f Mon Sep 17 00:00:00 2001 From: Tal Aviram Date: Sun, 10 Nov 2024 14:32:14 +0200 Subject: [PATCH 37/37] Fix Windows (MSVC) compilation. --- include/mdns_cpp/mdns.hpp | 3 +- src/mdns.cpp | 79 ++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/include/mdns_cpp/mdns.hpp b/include/mdns_cpp/mdns.hpp index 9ffd1c6..3c88960 100644 --- a/include/mdns_cpp/mdns.hpp +++ b/include/mdns_cpp/mdns.hpp @@ -7,8 +7,9 @@ #include "mdns_cpp/utils.hpp" #ifdef _WIN32 -#include #include +#include +#include #else #include #include diff --git a/src/mdns.cpp b/src/mdns.cpp index 10be603..d4d9120 100644 --- a/src/mdns.cpp +++ b/src/mdns.cpp @@ -189,7 +189,7 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) { memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) { int log_addr = 0; if (first_ipv6) { - memcpy(service_address_ipv6_, &saddr->sin6_addr, 16); + memcpy(&service_address_ipv6_, &saddr->sin6_addr, sizeof(saddr->sin6_addr)); first_ipv6 = 0; log_addr = 1; } @@ -414,8 +414,9 @@ int service_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns // service name we advertise, typically on the "<_service-name>._tcp.local." format // Answer PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." - mdns_record_t answer = { - .name = name, .type = MDNS_RECORDTYPE_PTR, .data.ptr.name = to_mdns_str_ref(service_record->service)}; + mdns_record_t answer = {.name = name, + .type = MDNS_RECORDTYPE_PTR, + .data = {mdns_record_ptr_t{name = to_mdns_str_ref(service_record->service)}}}; // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); printf(" --> answer %.*s (%s)\n", MDNS_STRING_FORMAT(answer.data.ptr.name), (unicast ? "unicast" : "multicast")); @@ -645,49 +646,51 @@ void mDNS::runMainLoop() { // PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." - service_record.record_ptr = {.name = to_mdns_str_ref(service_record.service), - .type = MDNS_RECORDTYPE_PTR, - .data.ptr.name = to_mdns_str_ref(service_record.service_instance), - .rclass = 0, - .ttl = 0}; + service_record.record_ptr.name = to_mdns_str_ref(service_record.service); + service_record.record_ptr.type = MDNS_RECORDTYPE_PTR, + service_record.record_ptr.data.ptr = mdns_record_ptr_t{.name = to_mdns_str_ref(service_record.service_instance)}; + service_record.record_ptr.rclass = 0; + service_record.record_ptr.ttl = 0; // SRV record mapping ".<_service-name>._tcp.local." to // ".local." with port. Set weight & priority to 0. - service_record.record_srv = {.name = to_mdns_str_ref(service_record.service_instance), - .type = MDNS_RECORDTYPE_SRV, - .data.srv.name = to_mdns_str_ref(service_record.hostname_qualified), - .data.srv.port = service_record.port, - .data.srv.priority = 0, - .data.srv.weight = 0, - .rclass = 0, - .ttl = 0}; + service_record.record_srv.name = to_mdns_str_ref(service_record.service_instance); + service_record.record_srv.type = MDNS_RECORDTYPE_SRV; + service_record.record_srv.data.srv = mdns_record_srv_t{.priority = 0, + .weight = 0, + .port = service_record.port, + .name = to_mdns_str_ref(service_record.hostname_qualified)}; + service_record.record_srv.rclass = 0; + service_record.record_srv.ttl = 0; // A/AAAA records mapping ".local." to IPv4/IPv6 addresses - service_record.record_a = {.name = to_mdns_str_ref(service_record.hostname_qualified), - .type = MDNS_RECORDTYPE_A, - .data.a.addr = service_record.address_ipv4, - .rclass = 0, - .ttl = 0}; - service_record.record_aaaa = {.name = to_mdns_str_ref(service_record.hostname_qualified), - .type = MDNS_RECORDTYPE_AAAA, - .data.aaaa.addr = service_record.address_ipv6, - .rclass = 0, - .ttl = 0}; + service_record.record_a.name = to_mdns_str_ref(service_record.hostname_qualified); + service_record.record_a.type = MDNS_RECORDTYPE_A; + service_record.record_a.data.a = {mdns_record_a_t{.addr = service_record.address_ipv4}}; + service_record.record_a.rclass = 0; + service_record.record_a.ttl = 0; + + service_record.record_aaaa.name = to_mdns_str_ref(service_record.hostname_qualified); + service_record.record_aaaa.type = MDNS_RECORDTYPE_AAAA, + service_record.record_aaaa.data.aaaa.addr = service_record.address_ipv6; + service_record.record_aaaa.rclass = 0; + service_record.record_aaaa.ttl = 0; // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library - service_record.txt_record[0] = {.name = to_mdns_str_ref(service_record.service_instance), - .type = MDNS_RECORDTYPE_TXT, - .data.txt.key = {MDNS_STRING_CONST("test")}, - .data.txt.value = {MDNS_STRING_CONST("1")}, - .rclass = 0, - .ttl = 0}; - service_record.txt_record[1] = {.name = to_mdns_str_ref(service_record.service_instance), - .type = MDNS_RECORDTYPE_TXT, - .data.txt.key = {MDNS_STRING_CONST("other")}, - .data.txt.value = {MDNS_STRING_CONST("value")}, - .rclass = 0, - .ttl = 0}; + service_record.txt_record[0].name = to_mdns_str_ref(service_record.service_instance); + service_record.txt_record[0].type = MDNS_RECORDTYPE_TXT; + service_record.txt_record[0].data.txt = + mdns_record_txt_t{.key = {MDNS_STRING_CONST("test")}, .value = {MDNS_STRING_CONST("1")}}; + service_record.txt_record[0].rclass = 0; + service_record.txt_record[0].ttl = 0; + + service_record.txt_record[1].name = to_mdns_str_ref(service_record.service_instance); + service_record.txt_record[1].type = MDNS_RECORDTYPE_TXT; + service_record.txt_record[1].data.txt = + mdns_record_txt_t{.key = {MDNS_STRING_CONST("other")}, .value = {MDNS_STRING_CONST("value")}}; + service_record.txt_record[1].rclass = 0; + service_record.txt_record[1].ttl = 0; // Send an announcement on startup of service {