FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: correct nsec bitmap semantics and resolve txt denials by bdraco · Pull Request #1825 · python-zeroconf/python-zeroconf · GitHub

fix: correct nsec bitmap semantics and resolve txt denials - #1825

Merged
bdraco merged 2 commits into
masterfrom
nsec_support
Aug 28, 2026
Merged

fix: correct nsec bitmap semantics and resolve txt denials#1825
bdraco merged 2 commits into
masterfrom
nsec_support

Conversation

bdraco commented Aug 28, 2026
edited
Loading

Copy link
Copy Markdown
Member

Summary

Follow up to #1821. Outgoing NSEC records now follow RFC 6762 §6.1, and ServiceInfo consumes an NSEC that denies the TXT record so async_request resolves promptly instead of burning the full timeout.

Details

  • The type bitmap was inverted; we listed the missing address types, but RFC 4034 §4.1.2 and RFC 6762 §6.1 require the types that exist at the name. A real Bonjour capture in test_protocol.py confirms this encoding.
  • The NSEC was owned by the service instance name; address denials now use the server name, matching the A/AAAA question they answer, with next_name set to the record's own name per RFC 6762 §6.1.
  • An NSEC at the service name with the SRV bit set and the TXT bit absent now counts as a TXT denial; the service completes with empty properties, since RFC 6763 §6 requires a TXT record yet some devices deny it instead. Requiring the SRV bit keeps NSECs from older python-zeroconf releases, which carried the inverted bitmap, from being misread as denials.
  • dns_address_nsec() replaces the public dns_nsec(), which produced protocol invalid records; dns_nsec() stays for one release as a deprecated shim that warns, delegates, and ignores missing_types.
  • A service registered without any addresses no longer emits an NSEC; an empty bitmap is not encodable.
  • Out of scope: letting AddressResolver bail early on an address denial, left as a follow up.

Test plan

  • poetry run pytest tests passes, 466 passed
  • new tests in tests/services/test_info.py and tests/test_handlers.py cover denial, the legacy bitmap guard, cache loading, and responder output
  • extension builds with REQUIRE_CYTHON=1 under Cython 3.3.0
  • verified on a live network; a v4 only responder answers an AAAA question with an NSEC owned by the host name listing only A, a crafted no TXT responder resolves immediately with empty properties, and 15 of 15 real LAN services still resolve with their TXT data

codspeed-hq Bot commented Aug 28, 2026
edited
Loading

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 24 untouched benchmarks


Comparing nsec_support (a46dea6) with master (ea490c0)

codecov Bot commented Aug 28, 2026
edited
Loading

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.81%. Comparing base (ea490c0) to head (a46dea6).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1825   +/-   ##
=======================================
  Coverage   99.81%   99.81%           
=======================================
  Files          33       33           
  Lines        3772     3799   +27     
  Branches      536      543    +7     
=======================================
+ Hits         3765     3792   +27     
  Misses          5        5           
  Partials        2        2           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

bluetoothbot commented Aug 28, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Previous review — superseded by a newer review below.

bluetoothbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Warning

Important issues found.

  • Stale @cython.locals decorator left stacked on _add_address_answers

bdraco force-pushed the nsec_support branch 2 times, most recently from 9c4a39e to 7d711a4 Compare August 28, 2026 17:52

Copy link
Copy Markdown
Contributor

PR Review — fix: correct nsec bitmap semantics and resolve txt denials

All four findings from the previous round are resolved; nothing blocking remains. Merge-ready with two non-blocking notes.

Re-verified against the current head (7d711a4):

  • Warning #1 fixed — query_handler.pxd:86-91 now carries a single @cython.locals block; the stale one-line decorator is gone, so type_seen=cython.bint / nsec=DNSNsec actually take effect.
  • Suggestion #2 fixed — both get_address_and_nsec_records docstrings (info.py:816, 820) now read "plus an NSEC asserting which address types exist", matching the corrected semantics.
  • Suggestion #3 fixed — dns_nsec() is back as a one-release shim that warns, delegates to _dns_address_nsec, and documents that missing_types is ignored; test_service_info_dns_nsec_deprecated pins the warning and the delegation. No AttributeError on a patch upgrade.
  • Suggestion #4 fixed — test_service_info_address_nsec_records now exercises the memoized (override_ttl=None) path, asserts identity across calls, and pins that mutating addresses drops _dns_address_nsec_cache in both directions (v4-only → empty → dual-stack).

The protocol substance still holds up on re-read. _process_nsec_record is only reachable after the record_key != self.key guard, so the host-name NSEC our own responder emits can never be read as a TXT denial — test_own_nsec_response_does_not_complete_service pins exactly that. get_by_details(..., _CLASS_IN) matches the cached unique-bit NSEC because DNSEntry._fast_init_entry masks class_ through _CLASS_MASK. The goodbye path (_core.py:578, override_ttl=0) now retracts the NSEC at the same host name it announced it at, which is self-consistent. Test fixtures (zc_loopback, aiozc_loopback, quick_request_timing, _clear_cache, RecordUpdate) all exist and handle teardown.

Remaining, both non-blocking:

  • 🟢 warnings.warn(stacklevel=2) in a cythonized module — compiled defs do not push a Python frame, so the deprecation warning likely reports the caller's caller rather than the caller. Flagged as unverified: I could not build the extension or run Python in this read-only shell.
  • 🟢 [Pre-Existing] ANY questions at a host name produce no address answers (RFC 6762 §6) — unchanged by this PR, but it lives in the block that was rewritten and is untested.

Note: I could not execute the test suite (no Python in the review shell), so the author's "466 passed" test plan is taken as a claim, not verified here.


✅ Resolved since last review (4)

Previously-flagged issues verified fixed
  • src/zeroconf/_handlers/query_handler.pxd:86 Stale @cython.locals decorator left stacked on _add_address_answers
  • src/zeroconf/_services/info.py:804 get_address_and_nsec_records docstrings still describe the old inverted semantics
  • src/zeroconf/_services/info.py:775 Public ServiceInfo.dns_nsec() removed without a deprecation shim under a fix: title
  • tests/services/test_info.py:2044 New _dns_address_nsec_cache invalidation is untested

🟢 Suggestions

1. `stacklevel=2` will point at the wrong frame in the Cython build
src/zeroconf/_services/info.py:778-783

src/zeroconf/_services/info.py is in TO_CYTHONIZE (build_ext.py:29), so in shipped wheels dns_nsec is a compiled def on a cdef class.

Cython does not push a Python frame for a compiled function during normal execution (frames are materialised lazily, only by __Pyx_AddTraceback on the exception path). _warnings.warn resolves stacklevel against the topmost Python frame, which from inside compiled code is already the caller. So stacklevel=2 skips one frame too far and attributes the DeprecationWarning to the caller's caller.

Why it matters: the whole point of the shim is to tell a downstream user where their dns_nsec() call is. In the pure-Python build (and in test_service_info_dns_nsec_deprecated, which only matches on the message) the line is right; in the compiled wheel — the only build most users get — it points at the wrong file:line, which is worse than no location at all.

Caveat: unverified against an actual compiled extension — I could not build or run Python in this review shell. If you want to confirm before changing anything, a REQUIRE_CYTHON=1 build plus python -W always::DeprecationWarning -c "..." will show which line gets reported. If it does misattribute, stacklevel=1 is the correct value for a compiled def; there is no single value correct for both builds, so a short comment naming the trade-off is probably the honest fix.

        warnings.warn(
            "dns_nsec is deprecated, and will be removed in a future version. "
            "Use dns_address_nsec instead; missing_types is ignored.",
            DeprecationWarning,
            stacklevel=2,
        )

Checklist

  • Prior warning #1 (stacked @cython.locals) resolved
  • Prior suggestions #2-#4 (docstrings, deprecation shim, cache-invalidation test) resolved
  • NSEC type bitmap encodes present types per RFC 4034 §4.1.2 / RFC 6762 §6.1
  • Own-responder NSEC cannot be misread as a TXT denial
  • NSEC owner/next_name non-None for all registered services
  • _dns_address_nsec_cache invalidated on every address mutation path, and pinned by a test
  • .pxd declarations consistent with the .py signatures
  • No backward-incompatible public API change without a migration path
  • Cython build correctness (frames, cdef return nullability, module globals) — suggestion #1
  • New branches covered by tests (denial, legacy bitmap, cache load, responder output, no-address service)
  • Tests assert observable behavior, not source inspection
  • Test resources cleaned up (loopback fixtures handle teardown)
  • Diff matches PR description; no unexplained scope creep
  • No new unbounded growth, resource leaks, or swallowed errors

Important

The branch moved during review. This review was performed against HEAD=7d711a4, but the PR branch now points at 7fb0c4f. Commits pushed after the review started are not reflected below — re-run /review to cover them.


Automated review by Kōan (Claude) HEAD=7d711a4 5 min 18s

bluetoothbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Tip

No blocking issues found — ready to merge.

bdraco marked this pull request as ready for review August 28, 2026 18:02
bdraco merged commit b37155e into master Aug 28, 2026
38 checks passed
bdraco deleted the nsec_support branch August 28, 2026 18:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL