From af99e99a1c8f922ddfc32bed9c143aba215c2805 Mon Sep 17 00:00:00 2001 From: Mattias Jansson Date: Wed, 14 Apr 2021 13:01:53 +0200 Subject: [PATCH] handle dns-sd properly --- CHANGELOG | 4 ++++ mdns.c | 37 +++++++++++++++++++++++++++++-------- mdns.h | 53 ++++++++--------------------------------------------- 3 files changed, 41 insertions(+), 53 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index baa5734..05679ca 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,3 +7,7 @@ The function to send a query answer has been split in two, one for unicast answe The functions to send query answers have been generalized to send any number of records. Added new function to do multicast announce on start/wake-up (unsolicited answer). + +Added parsing of ANY question records and DNS-SD queries with multiple questions + +Removed mdns_discovery_answer in favour of the new generalized answer functions, to handle both unicast and multicast response diff --git a/mdns.c b/mdns.c index 3ae1e9c..63660f6 100644 --- a/mdns.c +++ b/mdns.c @@ -198,22 +198,42 @@ service_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_ent record_name = "A"; else if (rtype == MDNS_RECORDTYPE_AAAA) record_name = "AAAA"; + else if (rtype == MDNS_RECORDTYPE_ANY) + record_name = "ANY"; else return 0; printf("Query %s %.*s\n", 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 - printf(" --> answer %.*s\n", MDNS_STRING_FORMAT(service->service)); - mdns_discovery_answer(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), - service->service.str, service->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 = service->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, 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 ((name.length == service->service.length) && (strncmp(name.str, service->service.str, name.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 @@ -260,7 +280,7 @@ service_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_ent } } else if ((name.length == service->service_instance.length) && (strncmp(name.str, service->service_instance.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 @@ -302,7 +322,8 @@ service_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_ent } } else if ((name.length == service->hostname_qualified.length) && (strncmp(name.str, service->hostname_qualified.str, name.length) == 0)) { - if ((rtype == MDNS_RECORDTYPE_A) && (service->address_ipv4.sin_family == AF_INET)) { + if (((rtype == MDNS_RECORDTYPE_A) || (rtype == MDNS_RECORDTYPE_ANY)) && + (service->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 @@ -338,7 +359,7 @@ service_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_ent mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); } - } else if ((rtype == MDNS_RECORDTYPE_AAAA) && + } else if (((rtype == MDNS_RECORDTYPE_AAAA)|| (rtype == MDNS_RECORDTYPE_ANY)) && (service->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 diff --git a/mdns.h b/mdns.h index eb64f3d..849314f 100644 --- a/mdns.h +++ b/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 @@ -985,10 +981,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)) @@ -1002,7 +998,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, @@ -1014,41 +1012,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) {