Skip to content

feat(mdns): Use internal cache for browser update, and support subtype browsing - #1121

Draft
Siflorite wants to merge 1 commit into
espressif:masterfrom
Siflorite:feat/mdns_browse_cache
Draft

feat(mdns): Use internal cache for browser update, and support subtype browsing#1121
Siflorite wants to merge 1 commit into
espressif:masterfrom
Siflorite:feat/mdns_browse_cache

Conversation

@Siflorite

@Siflorite Siflorite commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description

I'm currently implementing OpenThread DNS-SD interfaces for ESP Thread libraries. Those interfaces require a PTR browser and continuous resolvers for PTR, SRV, and ADDR records. Current mdns component cannot provide full functions, thus a series of PRs will be proposed to implement necessary contents.

As for browser, the existing browser in mdns component provides continuous browser through mdns_browse_*, but it lacks these functions to meet OpenThread interface requirements:

  • Public interfaces mdns_browse_new() and mdns_browse_delete() do not support browsers for subtypes.
  • The notifier type mdns_browse_notify_t only returns a mdns_result_t, where ttl is the minimum TTL of all records parsed from packet, not specific PTR TTL.

This PR introduces mdns_cache to store cached results that match running browses (and future continuous resolvers). If cache is updated, matching browses will generate temporary results from cache and notify. When a browse is deleted, the matching cache entry will be removed.

Cache structure

Cache is made up of two structures:

/**
 * @brief   mDNS cache service structure, contains PTR (with subtype list), SRV and TXT records
 */
typedef struct mdns_service_cache_s {
    char *instance_name;
    char *service;
    char *proto;
    // PTR
    bool ptr_present;   /*!< true if PTR record is present */
    uint32_t ptr_ttl;
    // Subtype list
    mdns_cache_subtype_t *subtype_list;
    // SRV
    bool srv_present;   /*!< true if SRV record is present */
    uint16_t priority;
    uint16_t weight;
    uint16_t port;
    uint32_t srv_ttl;
    // TXT
    bool txt_present;   /*!< true if TXT record is present */
    mdns_txt_linked_item_t *txt_list;
    uint32_t txt_ttl;
    // Dirty flag
    bool dirty;         /*!< true if the service cache is modified but not yet notified */
    struct mdns_service_cache_s *next;
} mdns_service_cache_t;

/**
 * @brief   mDNS cache entry structure, contains hostname, IP address list and service cache list
 */
typedef struct mdns_cache_entry_s {
    char *hostname;
    esp_netif_t *esp_netif;
    mdns_ip_protocol_t ip_protocol;

    mdns_cache_addr_t *addr_list;
    mdns_service_cache_t *service_cache_list;
    struct mdns_cache_entry_s *next;
} mdns_cache_entry_t;

mdns_cache.c holds a static mdns_cache_entry_t * s_cache as the head of cache entry linked list. Cache entry is identified with hostname, netif, and IP protocol. Every cache entry holds a list of addresses binded to hostname and a list of services. Every service cache entry is identified with instance name, service name, and protocol. A service entry contains all content for PTR, SRV, and TXT record, as well as flags to mark the presence of each record and whether the cache is dirty.

New routines

Browse registration routine is almost unchanged.

For notify events, the routine is as follow:

  • mdns_parse_packet() works the same until if (type == MDNS_TYPE_PTR)
  • If a running browse match the parsed PTR/SRV/TXT/A/AAAA record, call mdns_priv_cache_update_*() to update cache and mark the matching cache dirty.
  • After all data have been processed, call mdns_priv_cache_process_dirty(), this function will call mdns_priv_browse_update_from_service_cache() for every dirty service cache.
  • Find the matching browse to the dirty cache, generate a temporary result and invoke notifier. The result will be freed after the notifier returns.

When a browse is removed, the routine is as follow:

  • If no more browse with identical service name and protocol exists, remove all cache that match the service name and protocol of the browse.
  • If other browses with identical service name and protocol exist (only differ in subtype), and the browse to remove has subtype, just remove the subtype from the subtype list of the service cache.

The cache is updated in such a way:

For regular events (TTL>0):

  • PTR record: If no matching service cache is found, create a new cache entry with hostname=NULL, and insert the new service under this entry. If subtype is not NULL, just add subtype to subtype list, otherwise update ptr_present and ttl.
  • SRV record: If no matching service cache is found, create a new cache entry with hostname. If previously a new PTR record with the same service has created a hostname=NULL entry, move the service to this entry. Then update SRV record data.
  • TXT record: Create new cache entry or service cache if not found, then update TXT record data.
  • A/AAAA record: Create new cache entry if not found, insert address into address list of the cacthe entry or update TTL, then mark all service cache under this entry as dirty.

When a TTL=0 record is sent in, PTR will notify a goodbye event, then remove subtype from subtype list if subtype is not NULL, otherwise set ptr_present = false. SRV and TXT will mark present = false and clear record. A/AAAA will remove the address from address list of the cache entry and mark all its services as dirty. If all records under a service cache are absent, it will be removed; if an entry has no services, the entry will be removed.

All update functions return a mdns_cache_update_result_t, MDNS_CACHE_ADDED and MDNS_CACHE_REMOVED refer to whether a cache entry or a service cache is created or freed. MDNS_CACHE_UPDATED and MDNS_CACHE_NO_CHANGE refer to whether cache content is changed. MDNS_CACHE_ERROR means error occurred while handling cache.

Changed behaviors

These behaviors are different from previous versions and need careful consideration:

  • The mdns_result_t *result under mdns_browse_t is now removed, as the result of browse is only generated when notifies. As a result, the next pointer of a result obtained from notifier will always be NULL.
  • Previously mdns_parse_packet assume that a packet only contains data of one service, thus only one browse will be notified. Now browses are notified based on dirty cache, therefore multiple browses can be notified, including batch TTL=0 events previously mentioned in mdns_notifier_t. Users should no longer traverse the results obtained from browses.
  • The ttl in mdns_result_t used to represent the minimum TTL of all records, which may cause ambiguity. Now ttl only presents PTR TTL, it is recommended to use one-shot queries, or continuous resolvers which may be proposed in the future, to obtain TTL of SRV/TXT/A/AAAA records.
  • A new member char *subtype has been appended to mdns_result_t, to distinguish subtype information for browse notifications. This may not cause ABI breaking change as subtype is appended to the end and offsets of other members remain unchanged. But all binaries using sizeof(mdns_result_t) need to be re-compiled.

Related

Previously PR #1028 has proposed browse and query with subtypes, but it has not been pushed forward since March 2026. This PR uses some of the code in #1028 for subtype browse, the query part is not used. #1028 or a new PR should be pushed to implement one-shot query with subtypes, and support subtype for mdns_lookup_selfhosted_service() and mdns_lookup_delegated_service().

Testing

This PR has only been tested under mdns browse with one Thread BR and one Thread router, where browse and cache are created and removed in expected routine. A new PR should be made to implement unit tests for mdns_cache and mdns_browse.


Checklist

Before submitting a Pull Request, please ensure the following:

  • 🚨 This PR does not introduce breaking changes.
  • All CI checks (GH Actions) pass.
  • Documentation is updated as needed.
  • Tests are updated or added as necessary.
  • Code is well-commented, especially in complex areas.
  • Git history is clean — commits are squashed to the minimum necessary.

Comment thread components/mdns/private_include/mdns_private.h Outdated
Comment thread components/mdns/private_include/mdns_cache.h Outdated
Comment thread components/mdns/private_include/mdns_cache.h Outdated
@Siflorite
Siflorite force-pushed the feat/mdns_browse_cache branch 3 times, most recently from fa7c6c7 to 0f1fd81 Compare August 11, 2026 07:49
Comment thread components/mdns/mdns_cache.c Fixed
@Siflorite
Siflorite force-pushed the feat/mdns_browse_cache branch from 0f1fd81 to d5642b6 Compare August 11, 2026 08:16
@Siflorite

Copy link
Copy Markdown
Contributor Author

I have modified the commit a bit. Some names and annotations changed based on reviews of @zwx1995esp and some new types are added to support future resolvers.

@espressif-bot espressif-bot added the Status: Opened Issue is new label Aug 11, 2026
@Siflorite
Siflorite force-pushed the feat/mdns_browse_cache branch from d5642b6 to 78163fd Compare August 11, 2026 09:52
@Siflorite
Siflorite force-pushed the feat/mdns_browse_cache branch from 78163fd to 5ebf488 Compare August 11, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Status: Opened Issue is new

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants