feat: add multicast_addresses to bind/multicast independently#1767
feat: add multicast_addresses to bind/multicast independently#1767bluetoothbot wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1767 +/- ##
==========================================
- Coverage 99.77% 99.71% -0.06%
==========================================
Files 33 33
Lines 3536 3560 +24
Branches 498 507 +9
==========================================
+ Hits 3528 3550 +22
- Misses 5 6 +1
- Partials 3 4 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds a multicast_addresses escape hatch to let callers join additional multicast groups on the listen socket without also binding per-interface respond sockets—targeting sandboxed environments (e.g. iOS) where binding :5353 on physical interfaces can be blocked while multicast membership is still allowed/needed.
Changes:
- Extend
create_sockets()withmulticast_addressesand rejectmulticast_addresseswithunicast=True. - Thread the new parameter through
ZeroconfandAsyncZeroconfconstructors (docs + typing included). - Add unit tests for IPv4/IPv6 membership behavior, the InterfaceChoice.Default fast path, unicast rejection, and Zeroconf-level wiring.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/zeroconf/_utils/net.py |
Adds multicast_addresses handling in create_sockets() to join extra multicast memberships on the listen socket. |
src/zeroconf/_core.py |
Exposes/forwards multicast_addresses from Zeroconf.__init__() into socket creation and documents behavior. |
src/zeroconf/asyncio.py |
Exposes/forwards multicast_addresses from AsyncZeroconf.__init__() into Zeroconf. |
tests/utils/test_net.py |
Adds focused socket-creation tests covering IPv4/IPv6, default fast path, and unicast incompatibility. |
tests/test_core.py |
Adds a wiring test that verifies multicast_addresses is forwarded to create_sockets(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| listen_socket, respond_sockets = create_sockets( | ||
| interfaces, | ||
| unicast, | ||
| ip_version, | ||
| apple_p2p=apple_p2p, | ||
| multicast_addresses=multicast_addresses, | ||
| ) |
| if not unicast and interfaces is InterfaceChoice.Default and ip_version != IPVersion.All: | ||
| for interface in normalized_interfaces: | ||
| add_multicast_member(cast(socket.socket, listen_socket), interface) | ||
| for interface in extra_multicast_members: | ||
| add_multicast_member(cast(socket.socket, listen_socket), interface) |
| zc = r.Zeroconf( | ||
| interfaces=["127.0.0.1"], | ||
| multicast_addresses=["192.168.1.5"], | ||
| unicast=True, |
| normalized_interfaces = normalize_interface_choice(interfaces, ip_version) | ||
| extra_multicast_members = ( | ||
| normalize_interface_choice(list(multicast_addresses), ip_version) if multicast_addresses else [] | ||
| ) |
PR Review — feat: add multicast_addresses to bind/multicast independentlyImplementation is sound and the design is right — separating multicast-group membership from per-interface bind sockets is the correct fix for the iOS sandbox case (issue #1520). Pre-validation rejects IP-version mismatches early, dedup against 🟢 Suggestions1. DRY the extra_multicast_members join loop (`src/zeroconf/_utils/net.py`, L510-513)The block Checklist
SummaryImplementation is sound and the design is right — separating multicast-group membership from per-interface bind sockets is the correct fix for the iOS sandbox case (issue #1520). Pre-validation rejects IP-version mismatches early, dedup against |
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
bdraco
left a comment
There was a problem hiding this comment.
Need to complete coverage
src/zeroconf/_utils/net.py 96.66% 0 Missing and 1 partia
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
371be53 to
4e4cf47
Compare
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
4e4cf47 to
73e0af3
Compare
iOS and other sandboxed platforms allow joining the mDNS multicast group on a physical interface but forbid binding port 5353 on it because the system mDNS daemon owns the port. The existing ``interfaces`` parameter conflates the two: every entry both joins the multicast group and gets a respond socket bound to that interface's address. Add an optional ``multicast_addresses`` parameter on ``Zeroconf`` / ``AsyncZeroconf`` / ``create_sockets`` for additional addresses that are joined to the listen socket's multicast group without creating a respond socket. The default behavior is unchanged. Fixes python-zeroconf#1520
…up against interfaces
… validation and autodetect
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
73e0af3 to
e7bc212
Compare
Summary
Adds a new
multicast_addressesparameter toZeroconf/AsyncZeroconf/create_socketsso multicast group membership can be configuredindependently of bind interfaces. On iOS (and other sandboxed
environments) the system mDNS daemon owns port 5353 on physical
interfaces so binding there is rejected, but joining the multicast
group on those interfaces is still permitted and necessary to receive
incoming queries.
Today the
interfacesparameter conflates the two roles: everyentry both joins the multicast group and gets a respond socket
bound on it.
multicast_addressesadds an additive escape hatchthat joins additional addresses to the listen socket's multicast
group without creating respond sockets for them. Default behavior is
unchanged.
Fixes #1520
Changes
create_socketsacceptsmulticast_addressesand joins eachnormalized address to the listen socket; rejects the combination
with
unicast=Truesince there is no listen socket.Zeroconf.__init__andAsyncZeroconf.__init__expose theparameter and forward it through.
InterfaceChoice.Defaultsingle-socket fastpath and the per-interface respond-socket path.
InterfaceChoice.Defaultpath, unicastrejection, and
Zeroconf-level wiring.Test plan
poetry run pytest tests/utils/test_net.py tests/test_core.pypoetry run pytest --timeout=60(full suite, 394 passed)poetry run ruff check+ruff format --checkon changedfiles
Quality Report
Changes: 5 files changed, 148 insertions(+), 3 deletions(-)
Code scan: clean
Tests: passed (4 PASSED)
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline