From 6c2c2cd22b183534f18e0b5ec0cbadf59abb5d59 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 27 Nov 2025 11:50:57 +0100 Subject: [PATCH 1/2] fix(mdns): Create a test to check answer section for PTR/ANY to differentiate between discoveries and actual queries fix(mdns): Revert the fix to check if CI fails --- components/mdns/tests/host_test/pytest_mdns.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/components/mdns/tests/host_test/pytest_mdns.py b/components/mdns/tests/host_test/pytest_mdns.py index 95fefc2eb..f492ef63e 100644 --- a/components/mdns/tests/host_test/pytest_mdns.py +++ b/components/mdns/tests/host_test/pytest_mdns.py @@ -76,6 +76,21 @@ def test_ptr_additional_records_for_service(dig_app): dig_app.check_additional(resp, 'TXT', 'test_service._http._tcp.local', expected=True) +def test_instance_any_answer_records(dig_app): + """Query ANY for the service instance and ensure SRV/TXT are in Answer (Q/A path).""" + resp = dig_app.run_query('test_service._http._tcp.local', query_type='ANY') + + # Answer section should contain SRV and TXT records for the instance + srv_answers = dig_app.parse_section(resp, 'answer', 'SRV') + txt_answers = dig_app.parse_section(resp, 'answer', 'TXT') + assert any('test_service._http._tcp.local' in a for a in srv_answers) + assert any('test_service._http._tcp.local' in a for a in txt_answers) + + # We should not see a PTR for the generic service name in the Answer section + ptr_answers = dig_app.parse_section(resp, 'answer', 'PTR') + assert not any('_http._tcp.local' in a for a in ptr_answers) + + def test_remove_service(mdns_console, dig_app): mdns_console.send_input('mdns_service_remove _http _tcp') mdns_console.send_input('mdns_service_lookup _http _tcp') From 0f6235f13e6dd6bf1bd7ccd1fd4064324c5ac352 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 3 Dec 2025 11:57:31 +0100 Subject: [PATCH 2/2] fix(mdns): Fix to keep TXT/SRV in answers to queries unlike discoveries, where they need to stay in additional section Quick fix of regression b7b8c5dbd7433497d806d40c53d0cfcea8dfaebb --- components/mdns/mdns_send.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/components/mdns/mdns_send.c b/components/mdns/mdns_send.c index f007ea079..0ea8185a3 100644 --- a/components/mdns/mdns_send.c +++ b/components/mdns/mdns_send.c @@ -257,9 +257,20 @@ static bool create_answer_from_service(mdns_tx_packet_t *packet, mdns_service_t { mdns_host_item_t *host = get_host_item(service->hostname); bool is_delegated = (host != mdns_priv_get_self_host()); - if (question->type == MDNS_TYPE_PTR || question->type == MDNS_TYPE_ANY) { - // According to RFC6763-section12.1, for DNS-SD, SRV, TXT and all address records - // should be included in additional records. + bool is_instance_question = !mdns_utils_str_null_or_empty(question->host); + if ((question->type == MDNS_TYPE_ANY) && is_instance_question) { + // Instance-scoped ANY queries expect the resolved data (SRV/TXT) to be + // part of the answer section so that queriers can stop searching as soon + // as they receive this packet (RFC6762 section 6, RFC6763 section 12.1). + if (!mdns_priv_create_answer(&packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) || + !mdns_priv_create_answer(&packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false) || + !mdns_priv_create_answer(&packet->additional, MDNS_TYPE_A, service, host, send_flush, false) || + !mdns_priv_create_answer(&packet->additional, MDNS_TYPE_AAAA, service, host, send_flush, false)) { + return false; + } + } else if (question->type == MDNS_TYPE_PTR || (question->type == MDNS_TYPE_ANY && !is_instance_question)) { + // For service discovery (PTR or wildcard ANY) RFC6763 section 12.1 + // requires SRV/TXT/address records to stay in the additional section. if (!mdns_priv_create_answer(&packet->answers, MDNS_TYPE_PTR, service, NULL, false, false) || !mdns_priv_create_answer(&packet->additional, MDNS_TYPE_SRV, service, NULL, send_flush, false) || !mdns_priv_create_answer(&packet->additional, MDNS_TYPE_TXT, service, NULL, send_flush, false) ||