net: dns_sd, mdns: support service type enumeration
Support DNS-SD Service Type Enumeration in the dns_sd library
and mdns_responder sample application.
For more information, please see Section 9, "Service Type
Enumeration" in RFC 6763.
https://datatracker.ietf.org/doc/html/rfc6763
Fixes #38673
Signed-off-by: Christopher Friedt <chrisfriedt@gmail.com>
diff --git a/include/net/dns_sd.h b/include/net/dns_sd.h
index ae2fe2a..e5f0b09 100644
--- a/include/net/dns_sd.h
+++ b/include/net/dns_sd.h
@@ -51,6 +51,38 @@
#define DNS_SD_DOMAIN_MAX_SIZE 63
/**
+ * Minimum number of segments in a fully-qualified name
+ *
+ * This reqpresents FQN's of the form below
+ * ```
+ * <sn>._tcp.<domain>.
+ * ```
+ * Currently sub-types and service domains are unsupported and only the
+ * "local" domain is supported. Specifically, that excludes the following:
+ * ```
+ * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
+ * ```
+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc6763">RFC 6763</a>, Section 7.2.
+ */
+#define DNS_SD_MIN_LABELS 3
+/**
+ * Maximum number of segments in a fully-qualified name
+ *
+ * This reqpresents FQN's of the form below
+ * ```
+ * <instance>.<sn>._tcp.<domain>.
+ * ```
+ *
+ * Currently sub-types and service domains are unsupported and only the
+ * "local" domain is supported. Specifically, that excludes the following:
+ * ```
+ * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
+ * ```
+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc6763">RFC 6763</a>, Section 7.2.
+ */
+#define DNS_SD_MAX_LABELS 4
+
+/**
* @brief Register a service for DNS Service Discovery
*
* This macro should be used for advanced use cases. Two simple use cases are
@@ -209,6 +241,12 @@
* @internal
*/
extern const char dns_sd_empty_txt[1];
+/**
+ * @brief Wildcard Port specifier for DNS-SD
+ *
+ * @internal
+ */
+extern const uint16_t dns_sd_port_zero;
/** @endcond */
@@ -224,6 +262,32 @@
}
/**
+ * @brief Check if @a rec is a DNS-SD Service Type Enumeration
+ *
+ * DNS-SD Service Type Enumeration is used by network tooling to
+ * acquire a list of all mDNS-advertised services belonging to a
+ * particular host on a particular domain.
+ *
+ * For example, for the domain '.local', the equivalent query
+ * would be '_services._dns-sd._udp.local'.
+ *
+ * Currently, only the '.local' domain is supported.
+ *
+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc6763#section-9">Service Type Enumeration, RFC 6763</a>.
+ *
+ * @param rec the record to in question
+ * @return true if @a rec is a DNS-SD Service Type Enumeration
+ */
+bool dns_sd_is_service_type_enumeration(const struct dns_sd_rec *rec);
+
+/**
+ * @brief Create a wildcard filter for DNS-SD records
+ *
+ * @param filter a pointer to the filter to use
+ */
+void dns_sd_create_wildcard_filter(struct dns_sd_rec *filter);
+
+/**
* @}
*/
diff --git a/subsys/net/lib/dns/Kconfig b/subsys/net/lib/dns/Kconfig
index 1ed027e..9c52253 100644
--- a/subsys/net/lib/dns/Kconfig
+++ b/subsys/net/lib/dns/Kconfig
@@ -159,6 +159,17 @@
By doing so, Zephyr network services are discoverable
using e.g. 'avahi-browse -t -r _greybus._tcp'.
+if MDNS_RESPONDER_DNS_SD
+config MDNS_RESPONDER_DNS_SD_SERVICE_TYPE_ENUMERATION
+ bool "Enable DNS SD Service Type Enumeration"
+ default y
+ help
+ Selecting this option ensures that the MDNS Responder
+ performs DNS-SD Service Type Enumeration according to RFC 6763,
+ Chapter 9. By doing so, Zephyr network services are discoverable
+ using e.g. 'avahi-browse -t -r _services._dns-sd._udp.local'.
+endif # MDNS_RESPONDER_DNS_SD
+
module = MDNS_RESPONDER
module-dep = NET_LOG
module-str = Log level for mDNS responder
diff --git a/subsys/net/lib/dns/dns_sd.c b/subsys/net/lib/dns/dns_sd.c
index a0d0b11..8853f98 100644
--- a/subsys/net/lib/dns/dns_sd.c
+++ b/subsys/net/lib/dns/dns_sd.c
@@ -25,6 +25,7 @@
LOG_MODULE_REGISTER(net_dns_sd, CONFIG_DNS_SD_LOG_LEVEL);
const char dns_sd_empty_txt[1];
+const uint16_t dns_sd_port_zero;
#ifndef CONFIG_NET_TEST
@@ -699,9 +700,9 @@
}
#endif /* CONFIG_NET_TEST */
-int dns_sd_handle_ptr_query(const struct dns_sd_rec *inst,
- const struct in_addr *addr4, const struct in6_addr *addr6,
- uint8_t *buf, uint16_t buf_size)
+
+int dns_sd_handle_ptr_query(const struct dns_sd_rec *inst, const struct in_addr *addr4,
+ const struct in6_addr *addr6, uint8_t *buf, uint16_t buf_size)
{
/*
* RFC 6763 Section 12.1
@@ -734,9 +735,8 @@
}
if (*(inst->port) == 0) {
- NET_DBG("Ephemeral port %u for %s.%s.%s.%s "
- "not initialized", ntohs(*(inst->port)),
- inst->instance, inst->service, inst->proto,
+ NET_DBG("Ephemeral port %u for %s.%s.%s.%s not initialized",
+ ntohs(*(inst->port)), inst->instance, inst->service, inst->proto,
inst->domain);
return -EHOSTDOWN;
}
@@ -756,10 +756,8 @@
}
/* first add the answer record */
- r = add_ptr_record(inst, DNS_SD_PTR_TTL, buf, offset,
- buf_size - offset,
- &service_offset, &instance_offset,
- &domain_offset);
+ r = add_ptr_record(inst, DNS_SD_PTR_TTL, buf, offset, buf_size - offset, &service_offset,
+ &instance_offset, &domain_offset);
if (r < 0) {
return r; /* LCOV_EXCL_LINE */
}
@@ -768,9 +766,7 @@
offset += r;
/* then add the additional records */
- r = add_txt_record(inst, DNS_SD_TXT_TTL, instance_offset, buf,
- offset,
- buf_size - offset);
+ r = add_txt_record(inst, DNS_SD_TXT_TTL, instance_offset, buf, offset, buf_size - offset);
if (r < 0) {
return r; /* LCOV_EXCL_LINE */
}
@@ -778,9 +774,8 @@
rsp->arcount++;
offset += r;
- r = add_srv_record(inst, DNS_SD_SRV_TTL, instance_offset,
- domain_offset,
- buf, offset, buf_size - offset, &host_offset);
+ r = add_srv_record(inst, DNS_SD_SRV_TTL, instance_offset, domain_offset, buf, offset,
+ buf_size - offset, &host_offset);
if (r < 0) {
return r; /* LCOV_EXCL_LINE */
}
@@ -789,12 +784,10 @@
offset += r;
if (addr6 != NULL) {
- r = add_aaaa_record(inst, DNS_SD_AAAA_TTL, host_offset,
- addr6->s6_addr,
- buf, offset,
+ r = add_aaaa_record(inst, DNS_SD_AAAA_TTL, host_offset, addr6->s6_addr, buf, offset,
buf_size - offset); /* LCOV_EXCL_LINE */
if (r < 0) {
- return r; /* LCOV_EXCL_LINE */
+ return r; /* LCOV_EXCL_LINE */
}
rsp->arcount++;
@@ -803,8 +796,7 @@
if (addr4 != NULL) {
tmp = htonl(*(addr4->s4_addr32));
- r = add_a_record(inst, DNS_SD_A_TTL, host_offset,
- tmp, buf, offset,
+ r = add_a_record(inst, DNS_SD_A_TTL, host_offset, tmp, buf, offset,
buf_size - offset);
if (r < 0) {
return r; /* LCOV_EXCL_LINE */
@@ -822,6 +814,93 @@
return offset;
}
+int dns_sd_handle_service_type_enum(const struct dns_sd_rec *inst,
+ const struct in_addr *addr4, const struct in6_addr *addr6,
+ uint8_t *buf, uint16_t buf_size)
+{
+ static const char query[] = { "\x09_services\x07_dns-sd\x04_udp\x05local" };
+ /* offset of '.local' in the above */
+ uint16_t domain_offset = DNS_SD_PTR_MASK | 35;
+ uint16_t proto;
+ int name_size;
+ uint16_t service_size;
+ uint16_t offset = sizeof(struct dns_header);
+ struct dns_rr *rr;
+ struct dns_header *const rsp = (struct dns_header *)buf;
+
+ if (!rec_is_valid(inst)) {
+ return -EINVAL;
+ }
+
+ if (*(inst->port) == 0) {
+ NET_DBG("Ephemeral port %u for %s.%s.%s.%s "
+ "not initialized",
+ ntohs(*(inst->port)), inst->instance, inst->service, inst->proto,
+ inst->domain);
+ return -EHOSTDOWN;
+ }
+
+ if (strncmp("_tcp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
+ proto = IPPROTO_TCP;
+ } else if (strncmp("_udp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
+ proto = IPPROTO_UDP;
+ } else {
+ NET_DBG("invalid protocol %s", inst->proto);
+ return -EINVAL;
+ }
+
+ if (!port_in_use(proto, ntohs(*(inst->port)), addr4, addr6)) {
+ /* Service is not yet bound, so do not advertise */
+ return -EHOSTDOWN;
+ }
+
+ service_size = strlen(inst->service);
+ name_size =
+ /* uncompressed. e.g. "._foo._tcp.local." */
+ sizeof(query)
+ + sizeof(*rr)
+ /* compressed e.g. ._googlecast._tcp" followed by (DNS_SD_PTR_MASK | 0x0abc) */
+ + DNS_LABEL_LEN_SIZE + service_size
+ + DNS_LABEL_LEN_SIZE + DNS_SD_PROTO_SIZE
+ + DNS_POINTER_SIZE;
+
+ if (offset > buf_size || name_size >= buf_size - offset) {
+ NET_DBG("Buffer too small. required: %u available: %d", name_size,
+ (int)buf_size - (int)offset);
+ return -ENOSPC;
+ }
+
+ memset(rsp, 0, sizeof(*rsp));
+ memcpy(&buf[offset], query, sizeof(query));
+ offset += sizeof(query);
+
+ rr = (struct dns_rr *)&buf[offset];
+ rr->type = htons(DNS_RR_TYPE_PTR);
+ rr->class_ = htons(DNS_CLASS_IN);
+ rr->ttl = htonl(DNS_SD_PTR_TTL);
+ rr->rdlength = htons(0
+ + DNS_LABEL_LEN_SIZE + service_size
+ + DNS_LABEL_LEN_SIZE + DNS_SD_PROTO_SIZE
+ + DNS_POINTER_SIZE);
+ offset += sizeof(*rr);
+
+ buf[offset++] = service_size;
+ memcpy(&buf[offset], inst->service, service_size);
+ offset += service_size;
+ buf[offset++] = DNS_SD_PROTO_SIZE;
+ memcpy(&buf[offset], inst->proto, DNS_SD_PROTO_SIZE);
+ offset += DNS_SD_PROTO_SIZE;
+ domain_offset = htons(domain_offset);
+ memcpy(&buf[offset], &domain_offset, sizeof(domain_offset));
+ offset += sizeof(domain_offset);
+
+ /* Set the Response and AA bits */
+ rsp->flags = htons(BIT(15) | BIT(10));
+ rsp->ancount = htons(1);
+
+ return offset;
+}
+
/* TODO: dns_sd_handle_srv_query() */
/* TODO: dns_sd_handle_txt_query() */
@@ -893,82 +972,202 @@
return true;
}
-int dns_sd_extract_service_proto_domain(const uint8_t *query,
- size_t query_size, struct dns_sd_rec *record, char *service,
- size_t service_size, char *proto, size_t proto_size, char *domain,
- size_t domain_size)
+int dns_sd_query_extract(const uint8_t *query, size_t query_size, struct dns_sd_rec *record,
+ char **label, size_t *size, size_t *n)
{
- uint16_t offs;
- uint8_t label_size;
+ size_t i;
+ size_t offset;
+ size_t qlabels;
+ size_t qsize;
+ const size_t N = (n) ? (*n) : 0;
- if (query == NULL || record == NULL || service == NULL
- || proto == NULL || domain == NULL) {
- NET_DBG("one or more arguments are NULL");
+ /*
+ * See RFC 6763, 7.2. Service Name Length Limits
+ *
+ * <sn>._tcp.<servicedomain>.<parentdomain>.
+ * <Instance>.<sn>._tcp.<servicedomain>.<parentdomain>.
+ * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
+ */
+ __ASSERT(DNS_SD_MIN_LABELS <= N, "invalid number of labels %zu", N);
+ __ASSERT(!(query == NULL || label == NULL || size == NULL || n == NULL),
+ "one or more required arguments are NULL");
+ __ASSERT(query + query_size >= query, "query %p + query_size %zu wraps NULL", query,
+ query_size);
+ __ASSERT(label + N >= label, "label %p + n %zu wraps NULL", label, N);
+ __ASSERT(size + N >= size, "size %p + n %zu wraps NULL", size, N);
+ for (i = 0; i < N; ++i) {
+ if (label[i] == NULL) {
+ __ASSERT(label[i] != NULL, "label[%zu] is NULL", i);
+ }
+ }
+
+ if (query_size <= DNS_MSG_HEADER_SIZE) {
+ NET_DBG("query size %zu is less than DNS_MSG_HEADER_SIZE %d", query_size,
+ DNS_MSG_HEADER_SIZE);
return -EINVAL;
}
- if (query_size <= DNS_MSG_HEADER_SIZE
- || service_size < DNS_SD_SERVICE_MAX_SIZE + 1
- || proto_size < DNS_SD_PROTO_SIZE + 1
- || domain_size < DNS_SD_DOMAIN_MAX_SIZE + 1
- ) {
- NET_DBG("one or more size arguments are too small");
- return -EINVAL;
+ query += DNS_MSG_HEADER_SIZE;
+ query_size -= DNS_MSG_HEADER_SIZE;
+ offset = DNS_MSG_HEADER_SIZE;
+ dns_sd_create_wildcard_filter(record);
+ /* valid record must have non-NULL port */
+ record->port = &dns_sd_port_zero;
+
+ /* also counts labels */
+ for (i = 0, qlabels = 0; query_size > 0;) {
+ qsize = *query;
+ ++offset;
+ ++query;
+ --query_size;
+
+ if (qsize == 0) {
+ break;
+ }
+
+ ++qlabels;
+ if (qsize >= query_size) {
+ NET_DBG("claimed query size %zu > query buffer size %zu", qsize,
+ query_size);
+ return -EINVAL;
+ }
+
+ if (qsize > size[i] - 1) {
+ NET_DBG("qsize %zu > size[%zu] - 1 %zu", qsize, i, size[i] - 1);
+ return -ENOBUFS;
+ }
+
+ if (i < N) {
+ /* only extract the label if there is storage for it */
+ memcpy(label[i], query, qsize);
+ label[i][qsize] = '\0';
+ size[i] = qsize;
+ ++i;
+ }
+
+ offset += qsize;
+ query += qsize;
+ query_size -= qsize;
}
- memset(record, 0, sizeof(*record));
- offs = DNS_MSG_HEADER_SIZE;
-
- /* Copy service label to '\0'-terminated buffer */
- label_size = query[offs];
- if (label_size == 0 || label_size > service_size - 1
- || offs + label_size > query_size) {
- NET_DBG("could not get service");
- return -EINVAL;
- } else {
- strncpy(service, &query[offs + 1], label_size);
- service[label_size] = '\0';
- offs += label_size + 1;
+ /* write-out the actual number of labels in 'n' */
+ for (*n = i; i < N; ++i) {
+ label[i] = NULL;
+ size[i] = 0;
}
- /* Copy proto label to '\0'-terminated buffer */
- label_size = query[offs];
- if (label_size == 0 || label_size > proto_size - 1
- || offs + label_size > query_size) {
- NET_DBG("could not get proto for '%s...'", log_strdup(service));
+ if (qlabels < DNS_SD_MIN_LABELS) {
+ NET_DBG("too few labels in query %zu, DNS_SD_MIN_LABELS: %d", qlabels,
+ DNS_SD_MIN_LABELS);
return -EINVAL;
- } else {
- strncpy(proto, &query[offs + 1], label_size);
- proto[label_size] = '\0';
- offs += label_size + 1;
+ } else if (qlabels == DNS_SD_MIN_LABELS) {
+ /* e.g. _zephyr._tcp.local */
+ record->service = label[0];
+ record->proto = label[1];
+ record->domain = label[2];
+
+ if (!service_is_valid(record->service)) {
+ NET_DBG("service '%s' is invalid", record->service);
+ return -EINVAL;
+ }
+
+ if (!proto_is_valid(record->proto)) {
+ NET_DBG("proto '%s' is invalid", record->proto);
+ return -EINVAL;
+ }
+
+ if (!domain_is_valid(record->domain)) {
+ NET_DBG("domain '%s' is invalid", record->domain);
+ return -EINVAL;
+ }
+ } else if (qlabels > DNS_SD_MIN_LABELS && qlabels < DNS_SD_MAX_LABELS) {
+ NET_DBG("unsupported number of labels %zu", qlabels);
+ return -EINVAL;
+ } else if (qlabels >= DNS_SD_MAX_LABELS) {
+ /* e.g.
+ * "Zephyr 42"._zephyr._tcp.local, or
+ * _domains._dns-sd._udp.local
+ */
+ record->instance = label[0];
+ record->service = label[1];
+ record->proto = label[2];
+ record->domain = label[3];
+
+ if (!instance_is_valid(record->instance)) {
+ NET_DBG("service '%s' is invalid", record->instance);
+ return -EINVAL;
+ }
+
+ if (!service_is_valid(record->service)) {
+ NET_DBG("service '%s' is invalid", record->service);
+ return -EINVAL;
+ }
+
+ if (!proto_is_valid(record->proto)) {
+ NET_DBG("proto '%s' is invalid", record->proto);
+ return -EINVAL;
+ }
+
+ if (!domain_is_valid(record->domain)) {
+ NET_DBG("domain '%s' is invalid", record->domain);
+ return -EINVAL;
+ }
+ } else if (qlabels > N) {
+ NET_DBG("too few buffers to extract query: qlabels: %zu, N: %zu",
+ qlabels, N);
+ return -ENOBUFS;
}
- /* Copy domain label to '\0'-terminated buffer */
- label_size = query[offs];
- if (label_size == 0 || label_size > domain_size - 1
- || offs + label_size > query_size) {
- NET_DBG("could not get domain for '%s.%s...'",
- log_strdup(service), log_strdup(proto));
- return -EINVAL;
- } else {
- strncpy(domain, &query[offs + 1], label_size);
- domain[label_size] = '\0';
- offs += label_size + 1;
+ return offset;
+}
+
+int dns_sd_extract_service_proto_domain(const uint8_t *query, size_t query_size,
+ struct dns_sd_rec *record, char *service,
+ size_t service_size, char *proto, size_t proto_size,
+ char *domain, size_t domain_size)
+{
+ char instance[DNS_SD_INSTANCE_MAX_SIZE + 1];
+ char *label[4];
+ size_t size[] = {
+ ARRAY_SIZE(instance),
+ service_size,
+ proto_size,
+ domain_size,
+ };
+ size_t n = ARRAY_SIZE(label);
+
+ BUILD_ASSERT(ARRAY_SIZE(label) == ARRAY_SIZE(size),
+ "label and size arrays are different size");
+
+ /*
+ * work around for bug in compliance scripts which say that the array
+ * should be static const (incorrect)
+ */
+ label[0] = instance;
+ label[1] = service;
+ label[2] = proto;
+ label[3] = domain;
+
+ return dns_sd_query_extract(query, query_size, record, label, size, &n);
+}
+
+bool dns_sd_is_service_type_enumeration(const struct dns_sd_rec *rec)
+{
+ static const struct dns_sd_rec filter = {
+ .instance = "_services",
+ .service = "_dns-sd",
+ .proto = "_udp",
+ .domain = "local",
+ };
+
+ return dns_sd_rec_match(rec, &filter);
+}
+
+void dns_sd_create_wildcard_filter(struct dns_sd_rec *filter)
+{
+ if (filter != NULL) {
+ memset(filter, 0, sizeof(*filter));
+ filter->text = dns_sd_empty_txt;
+ filter->text_size = sizeof(dns_sd_empty_txt);
}
-
- /* Check that we have reached the DNS terminator */
- if (query[offs] != 0) {
- NET_DBG("ignoring request for '%s.%s.%s...'",
- log_strdup(service),
- log_strdup(proto),
- log_strdup(domain));
- return -EINVAL;
- }
-
- offs++;
- record->service = service;
- record->proto = proto;
- record->domain = domain;
-
- return offs;
}
diff --git a/subsys/net/lib/dns/dns_sd.h b/subsys/net/lib/dns/dns_sd.h
index d73b31a..1a10935 100644
--- a/subsys/net/lib/dns/dns_sd.h
+++ b/subsys/net/lib/dns/dns_sd.h
@@ -33,6 +33,35 @@
STRUCT_SECTION_FOREACH(dns_sd_rec, it)
/**
+ * @brief Extract labels from a DNS-SD PTR query
+ *
+ * ```
+ * <sn>._tcp.<domain>.
+ * <instance>.<sn>._tcp.<domain>.
+ * ```
+ *
+ * Currently sub-types and service domains are unsupported and only the
+ * "local" domain is supported. Specifically, that excludes the following:
+ * ```
+ * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
+ * ```
+ *
+ * @param query a pointer to the start of the query
+ * @param query_size the number of bytes contained in the query
+ * @param[out] record the DNS-SD record to initialize and populate
+ * @param label array of pointers to suitably sized buffers
+ * @param size array of sizes for each buffer in @p label
+ * @param[inout] n number of elements in @p label and @p size
+ *
+ * @return on success, number of bytes read from @p query
+ * @return on failure, a negative errno value
+ *
+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc6763">RFC 6763</a>, Section 7.2.
+ */
+int dns_sd_query_extract(const uint8_t *query, size_t query_size, struct dns_sd_rec *record,
+ char **label, size_t *size, size_t *n);
+
+/**
* @brief Extract the Service, Protocol, and Domain from a DNS-SD PTR query
*
* This function zero-initializes @p record and populates the appropriate
@@ -57,6 +86,7 @@
* @return on success, a positive number representing length of the query
* @return on failure, a negative errno value
*/
+__deprecated
int dns_sd_extract_service_proto_domain(const uint8_t *query,
size_t query_size, struct dns_sd_rec *record, char *service,
size_t service_size, char *proto, size_t proto_size,
@@ -121,7 +151,7 @@
* If there is no IPv6 address to advertise, then @p addr6 should be
* NULL.
*
- * @param inst the DNS-SD record for to advertise
+ * @param inst the DNS-SD record to advertise
* @param addr4 pointer to the IPv4 address
* @param addr6 pointer to the IPv6 address
* @param buf output buffer
@@ -134,6 +164,24 @@
const struct in_addr *addr4, const struct in6_addr *addr6,
uint8_t *buf, uint16_t buf_size);
+/**
+ * @brief Handle a Service Type Enumeration with DNS Service Discovery
+ *
+ * This function should be called once for each type of advertised service.
+ *
+ * @param service the DNS-SD service to advertise
+ * @param addr4 pointer to the IPv4 address
+ * @param addr6 pointer to the IPv6 address
+ * @param buf output buffer
+ * @param buf_size size of the output buffer
+ *
+ * @return on success, number of bytes written to @p buf
+ * @return on failure, a negative errno value
+ */
+int dns_sd_handle_service_type_enum(const struct dns_sd_rec *service,
+ const struct in_addr *addr4, const struct in6_addr *addr6,
+ uint8_t *buf, uint16_t buf_size);
+
#ifdef __cplusplus
};
#endif
diff --git a/subsys/net/lib/dns/mdns_responder.c b/subsys/net/lib/dns/mdns_responder.c
index b794cf8..0e2ce11 100644
--- a/subsys/net/lib/dns/mdns_responder.c
+++ b/subsys/net/lib/dns/mdns_responder.c
@@ -306,11 +306,32 @@
struct dns_sd_rec filter;
struct sockaddr dst;
socklen_t dst_len;
+ bool service_type_enum = false;
const struct in6_addr *addr6 = NULL;
const struct in_addr *addr4 = NULL;
+ char instance_buf[DNS_SD_SERVICE_MAX_SIZE + 1];
char service_buf[DNS_SD_SERVICE_MAX_SIZE + 1];
char proto_buf[DNS_SD_PROTO_SIZE + 1];
char domain_buf[DNS_SD_DOMAIN_MAX_SIZE + 1];
+ char *label[4];
+ size_t size[] = {
+ ARRAY_SIZE(instance_buf),
+ ARRAY_SIZE(service_buf),
+ ARRAY_SIZE(proto_buf),
+ ARRAY_SIZE(domain_buf),
+ };
+ size_t n = ARRAY_SIZE(label);
+
+ BUILD_ASSERT(ARRAY_SIZE(label) == ARRAY_SIZE(size), "");
+
+ /*
+ * work around for bug in compliance scripts which say that the array
+ * should be static const (incorrect)
+ */
+ label[0] = instance_buf;
+ label[1] = service_buf;
+ label[2] = proto_buf;
+ label[3] = domain_buf;
/* This actually is used but the compiler doesn't see that */
ARG_UNUSED(record);
@@ -333,14 +354,28 @@
&ip_hdr->ipv6->src);
}
- ret = dns_sd_extract_service_proto_domain(dns_msg->msg,
- dns_msg->msg_size, &filter, service_buf, sizeof(service_buf),
- proto_buf, sizeof(proto_buf), domain_buf, sizeof(domain_buf));
+ ret = dns_sd_query_extract(dns_msg->msg,
+ dns_msg->msg_size, &filter, label, size, &n);
if (ret < 0) {
- NET_DBG("unable to extract service.proto.domain (%d)", ret);
+ NET_DBG("unable to extract query (%d)", ret);
return;
}
+ if (IS_ENABLED(CONFIG_MDNS_RESPONDER_DNS_SD_SERVICE_TYPE_ENUMERATION)
+ && dns_sd_is_service_type_enumeration(&filter)) {
+
+ /*
+ * RFC 6763, Section 9
+ *
+ * A DNS query for PTR records with the name
+ * "_services._dns-sd._udp.<Domain>" yields a set of PTR records,
+ * where the rdata of each PTR record is the two-label <Service> name,
+ * plus the same domain, e.g., "_http._tcp.<Domain>".
+ */
+ dns_sd_create_wildcard_filter(&filter);
+ service_type_enum = true;
+ }
+
DNS_SD_FOREACH(record) {
/* Checks validity and then compare */
if (dns_sd_rec_match(record, &filter)) {
@@ -350,13 +385,21 @@
ntohs(*(record->port)));
/* Construct the response */
- ret = dns_sd_handle_ptr_query(record,
- addr4, addr6,
- result->data, result->size);
- if (ret < 0) {
- NET_DBG("dns_sd_handle_ptr_query() failed (%d)",
- ret);
- continue;
+ if (service_type_enum) {
+ ret = dns_sd_handle_service_type_enum(record, addr4, addr6,
+ result->data, result->size);
+ if (ret < 0) {
+ NET_DBG("dns_sd_handle_service_type_enum() failed (%d)",
+ ret);
+ continue;
+ }
+ } else {
+ ret = dns_sd_handle_ptr_query(record, addr4, addr6,
+ result->data, result->size);
+ if (ret < 0) {
+ NET_DBG("dns_sd_handle_ptr_query() failed (%d)", ret);
+ continue;
+ }
}
result->len = ret;