mdns: Allow TXT entries larger than 63 bytes; add configurable maximum TXT RDATA size

This can be used to fix device adoption in ESPHome, which uses a long
"package_import_url" string to advertise the location of the sourcecode
for the device's configuration.
This commit is contained in:
2026-01-01 21:05:26 -07:00
parent dfc2efe117
commit 61202f715a
5 changed files with 41 additions and 10 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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.
*/

View File

@@ -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' */