From 0bb37f342e402855c2fedc67ffb2b8d4e42a69f0 Mon Sep 17 00:00:00 2001 From: Darsey Litzenberger Date: Thu, 1 Jan 2026 21:05:26 -0700 Subject: [PATCH] mdns: Allow TXT entries larger than 63 bytes; add configurable maximum TXT RDATA length --- doc/mdns.txt | 6 +++--- src/apps/mdns/mdns.c | 27 +++++++++++++++++++++++---- src/apps/mdns/mdns_out.c | 4 ++-- src/include/lwip/apps/mdns_opts.h | 6 ++++++ src/include/lwip/apps/mdns_priv.h | 8 +++++++- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/doc/mdns.txt b/doc/mdns.txt index 12ac04b1..1ff64fed 100644 --- a/doc/mdns.txt +++ b/doc/mdns.txt @@ -92,9 +92,9 @@ If this call returns successfully, the following queries will be answered: LWIP_ERROR("mdns add service txt failed\n", (res == ERR_OK), return); } - Since a hostname struct is used for TXT storage each single item can be max - 63 bytes long, and the total max length (including length bytes for each - item) is 255 bytes. + Each item is encoded as a length byte followed by the data, so each single + item can be max 255 bytes long, and the total max length (including length + bytes for each item) is defined by MDNS_TXT_RDATA_SIZE (default 255). If your device runs a webserver on port 80, an example call might be: diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c index 84f267a6..054467c5 100644 --- a/src/apps/mdns/mdns.c +++ b/src/apps/mdns/mdns.c @@ -1272,7 +1272,7 @@ mdns_parse_pkt_known_answers(struct netif *netif, struct mdns_packet *pkt, } else if (match & REPLY_SERVICE_TXT) { mdns_prepare_txtdata(service); if (service->txtdata.length == ans.rd_length && - pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) { + pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.rdata, ans.rd_length) == 0) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n")); reply->serv_replies[i] &= ~REPLY_SERVICE_TXT; } @@ -2050,7 +2050,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif) } else if (ans.info.type == DNS_RRTYPE_TXT) { mdns_prepare_txtdata(service); if (service->txtdata.length == ans.rd_length && - pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) { + pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.rdata, ans.rd_length) == 0) { LWIP_DEBUGF(MDNS_DEBUG, ("mDNS: response equals our own TXT record -> no conflict\n")); conflict = 0; } @@ -2613,10 +2613,29 @@ mdns_resp_rename_service(struct netif *netif, u8_t slot, const char *name) return ERR_OK; } +/* Adds an RFC 1035 character-string to TXT RDATA. */ +static err_t +mdns_txt_add_charstr(struct mdns_txtdata *txtdata, const char *value, u8_t len) +{ + if (1 + len + txtdata->length > MDNS_TXT_RDATA_SIZE) { + LWIP_DEBUGF(MDNS_DEBUG, ("mdns_txt_add_charstr: adding string would exceed buffer (1+%d+%d > %d). Consider increasing MDNS_TXT_RDATA_SIZE.\n", + len, txtdata->length, MDNS_TXT_RDATA_SIZE)); + return ERR_MEM; + } + txtdata->rdata[txtdata->length] = len; + txtdata->length++; + if (len) { + MEMCPY(&txtdata->rdata[txtdata->length], value, len); + txtdata->length += len; + } + return ERR_OK; +} + /** * @ingroup mdns * Call this function from inside the service_get_txt_fn_t callback to add text data. - * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte. + * Buffer for TXT data is MDNS_TXT_RDATA_SIZE (default 256) bytes, and each + * field is prefixed with a length byte. * @param service The service provided to the get_txt callback * @param txt String to add to the TXT field. * @param txt_len Length of string @@ -2629,7 +2648,7 @@ mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_ LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service); /* Use a mdns_domain struct to store txt chunks since it is the same encoding */ - return mdns_domain_add_label(&service->txtdata, txt, txt_len); + return mdns_txt_add_charstr(&service->txtdata, txt, txt_len); } #if LWIP_MDNS_SEARCH diff --git a/src/apps/mdns/mdns_out.c b/src/apps/mdns/mdns_out.c index 5c6d26b7..7dfbcba2 100644 --- a/src/apps/mdns/mdns_out.c +++ b/src/apps/mdns/mdns_out.c @@ -62,7 +62,7 @@ static void mdns_clear_outmsg(struct mdns_outmsg *outmsg); void mdns_prepare_txtdata(struct mdns_service *service) { - memset(&service->txtdata, 0, sizeof(struct mdns_domain)); + memset(&service->txtdata, 0, sizeof(struct mdns_txtdata)); if (service->txt_fn) { service->txt_fn(service, service->txt_userdata); } @@ -508,7 +508,7 @@ mdns_add_txt_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg, } LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n")); return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, - msg->cache_flush, ttl, (u8_t *) &service->txtdata.name, + msg->cache_flush, ttl, service->txtdata.rdata, service->txtdata.length, NULL); } diff --git a/src/include/lwip/apps/mdns_opts.h b/src/include/lwip/apps/mdns_opts.h index 1eee3e38..7a0ef4eb 100644 --- a/src/include/lwip/apps/mdns_opts.h +++ b/src/include/lwip/apps/mdns_opts.h @@ -88,6 +88,12 @@ #define MDNS_OUTPUT_PACKET_SIZE ((MDNS_MAX_SERVICES == 1) ? 512 : 1450) #endif +/** The maximum size of TXT RDATA allocated for each service. + */ +#ifndef MDNS_TXT_RDATA_SIZE +# define MDNS_TXT_RDATA_SIZE 256 +#endif + /** MDNS_RESP_USENETIF_EXTCALLBACK==1: register an ext_callback on the netif * to automatically restart probing/announcing on status or address change. */ diff --git a/src/include/lwip/apps/mdns_priv.h b/src/include/lwip/apps/mdns_priv.h index 5209ba00..8a08e681 100644 --- a/src/include/lwip/apps/mdns_priv.h +++ b/src/include/lwip/apps/mdns_priv.h @@ -90,10 +90,16 @@ struct mdns_request { }; #endif +/** TXT record data */ +struct mdns_txtdata { + u8_t rdata[MDNS_TXT_RDATA_SIZE]; + u16_t length; +}; + /** Description of a service */ struct mdns_service { /** TXT record to answer with */ - struct mdns_domain txtdata; + struct mdns_txtdata txtdata; /** Name of service, like 'myweb' */ char name[MDNS_LABEL_MAXLEN + 1]; /** Type of service, like '_http' */