Skip to content

Commit

Permalink
handle dns-sd properly
Browse files Browse the repository at this point in the history
  • Loading branch information
mjansson committed Apr 14, 2021
1 parent 0b4e806 commit af99e99
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 53 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -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
37 changes: 29 additions & 8 deletions mdns.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
// "<hostname>.<_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 "<hostname>.<_service-name>._tcp.local." format), and add
Expand Down Expand Up @@ -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
// "<hostname>.<_service-name._tcp.local"), answer a SRV record mapping the service
// instance name to our qualified hostname (typically "<hostname>.local.") and port, as
Expand Down Expand Up @@ -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 "<hostname>.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
Expand Down Expand Up @@ -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 "<hostname>.local.") and we
// have an IPv6 address, answer with an AAAA record mappiing the hostname to an IPv6
Expand Down
53 changes: 8 additions & 45 deletions mdns.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand All @@ -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,
Expand All @@ -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) {
Expand Down

0 comments on commit af99e99

Please sign in to comment.