Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 78 additions & 3 deletions components/mdns/mdns_querier.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,23 @@ mdns_search_once_t *mdns_priv_query_find_from(mdns_search_once_t *s, mdns_name_t
return s;
}

if (type == MDNS_TYPE_PTR && type == s->type && !strcasecmp(name->service, s->service) && !strcasecmp(name->proto, s->proto)) {
return s;
if (type == MDNS_TYPE_PTR && type == s->type) {
// Special handling for _services._dns-sd._udp queries
// When querying _services._dns-sd._udp, the search has service="_services._dns-sd" and proto="_udp"
// But the parsed PTR name has host="_services", service="_dns-sd", proto="_udp"
if ((name->host[0] && !strcasecmp(name->host, "_services"))
&& (name->service[0] && !strcasecmp(name->service, "_dns-sd"))
&& (name->proto[0] && !strcasecmp(name->proto, "_udp"))
&& (name->domain[0] && !strcasecmp(name->domain, MDNS_UTILS_DEFAULT_DOMAIN))) {
// Check if this search is for _services._dns-sd._udp
if (s->service && s->proto &&
!strcasecmp(s->service, "_services._dns-sd") &&
!strcasecmp(s->proto, "_udp")) {
return s;
}
} else if (!strcasecmp(name->service, s->service) && !strcasecmp(name->proto, s->proto)) {
return s;
}
}

s = s->next;
Expand Down Expand Up @@ -288,6 +303,64 @@ static mdns_tx_packet_t *create_search_packet(mdns_search_once_t *search, mdns_i
q->proto = search->proto;
q->domain = MDNS_UTILS_DEFAULT_DOMAIN;
q->own_dynamic_memory = false;

// Special handling for _services._dns-sd._udp queries
// The service parameter is "_services._dns-sd" which needs to be split into two labels:
// host="_services" and service="_dns-sd"
if (search->service && strchr(search->service, '.')) {
char *dot = strchr(search->service, '.');
if (dot && dot > search->service) {
// Split: first part becomes host, second part becomes service
size_t host_len = dot - search->service;
size_t service_len = strlen(dot + 1);
if (host_len < MDNS_NAME_BUF_LEN && service_len < MDNS_NAME_BUF_LEN && host_len > 0 && service_len > 0) {
// Allocate temporary buffers for all fields that will be freed
size_t proto_len = search->proto ? strlen(search->proto) : 0;
size_t domain_len = strlen(MDNS_UTILS_DEFAULT_DOMAIN);
char *host_part = mdns_mem_malloc(host_len + 1);
char *service_part = mdns_mem_malloc(service_len + 1);
char *proto_part = NULL;
char *domain_part = mdns_mem_malloc(domain_len + 1);

// Only allocate proto_part if proto exists
if (proto_len > 0) {
proto_part = mdns_mem_malloc(proto_len + 1);
}

// Check if all required allocations succeeded
bool alloc_ok = host_part && service_part && domain_part && (!proto_len || proto_part);

if (alloc_ok) {
memcpy(host_part, search->service, host_len);
host_part[host_len] = '\0';
memcpy(service_part, dot + 1, service_len);
service_part[service_len] = '\0';
if (proto_part && search->proto) {
memcpy(proto_part, search->proto, proto_len);
proto_part[proto_len] = '\0';
}
memcpy(domain_part, MDNS_UTILS_DEFAULT_DOMAIN, domain_len);
domain_part[domain_len] = '\0';

q->host = host_part;
q->service = service_part;
// If proto exists, we must have allocated proto_part (checked in alloc_ok)
// Set to NULL if proto doesn't exist so cleanup can safely free NULL
q->proto = proto_part ? proto_part : NULL;
q->domain = domain_part;
q->own_dynamic_memory = true; // Mark that we need to free these
} else {
// If allocation fails, free what we got and fall back to original strings
mdns_mem_free(host_part);
mdns_mem_free(service_part);
mdns_mem_free(proto_part);
mdns_mem_free(domain_part);
// q->host, q->service, q->proto, q->domain remain pointing to original values
}
}
}
}

queueToEnd(mdns_out_question_t, packet->questions, q);

if (search->type == MDNS_TYPE_PTR) {
Expand Down Expand Up @@ -763,7 +836,9 @@ esp_err_t mdns_query_generic(const char *name, const char *service, const char *

esp_err_t mdns_query(const char *name, const char *service_type, const char *proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t **results)
{
return mdns_query_generic(name, service_type, proto, type, type != MDNS_TYPE_PTR, timeout, max_results, results);
// PTR queries should be multicast, all other types should be unicast
mdns_query_transmission_type_t transmission_type = (type == MDNS_TYPE_PTR) ? MDNS_QUERY_MULTICAST : MDNS_QUERY_UNICAST;
return mdns_query_generic(name, service_type, proto, type, transmission_type, timeout, max_results, results);
}

esp_err_t mdns_query_ptr(const char *service, const char *proto, uint32_t timeout, size_t max_results, mdns_result_t **results)
Expand Down
11 changes: 10 additions & 1 deletion components/mdns/tests/host_test/main/main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
Expand Down Expand Up @@ -93,6 +93,15 @@ static void mdns_test_app(esp_netif_t *interface)
ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_TEST_HOSTNAME);
ESP_ERROR_CHECK(mdns_register_netif(interface));
ESP_ERROR_CHECK(mdns_netif_action(interface, MDNS_EVENT_ENABLE_IP4 /*| MDNS_EVENT_ENABLE_IP6 */ | MDNS_EVENT_IP4_REVERSE_LOOKUP | MDNS_EVENT_IP6_REVERSE_LOOKUP));
/// here we query for _services._dns-sd._udp
mdns_result_t *results = NULL;
esp_err_t err = mdns_query_ptr("_services._dns-sd", "_udp", 2000, 1, &results);
if (err) {
ESP_LOGE(TAG, "Query Failed: %x", (err));
// return;
}
ESP_LOGI(TAG, "Query PTR: _services._dns-sd._udp results: %p", results);
mdns_query_results_free(results);

#ifdef CONFIG_TEST_CONSOLE
esp_console_repl_t *repl = NULL;
Expand Down
Loading