Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions src/zeroconf/_services/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
_DNS_OTHER_TTL,
_FLAGS_QR_QUERY,
_LISTENER_TIME,
_MDNS_PORT,
_TYPE_A,
_TYPE_AAAA,
_TYPE_NSEC,
Expand Down Expand Up @@ -616,7 +617,12 @@ def _is_complete(self) -> bool:
return bool(self.text is not None and (self._ipv4_addresses or self._ipv6_addresses))

def request(
self, zc: 'Zeroconf', timeout: float, question_type: Optional[DNSQuestionType] = None
self,
zc: 'Zeroconf',
timeout: float,
question_type: Optional[DNSQuestionType] = None,
addr: Optional[str] = None,
port: int = _MDNS_PORT,
) -> bool:
"""Returns true if the service could be discovered on the
network, and updates this object with details discovered.
Expand All @@ -628,13 +634,29 @@ def request(
assert zc.loop is not None and zc.loop.is_running()
if zc.loop == get_running_loop():
raise RuntimeError("Use AsyncServiceInfo.async_request from the event loop")
return bool(run_coro_with_timeout(self.async_request(zc, timeout, question_type), zc.loop, timeout))
return bool(
run_coro_with_timeout(
self.async_request(zc, timeout, question_type, addr, port), zc.loop, timeout
)
)

async def async_request(
self, zc: 'Zeroconf', timeout: float, question_type: Optional[DNSQuestionType] = None
self,
zc: 'Zeroconf',
timeout: float,
question_type: Optional[DNSQuestionType] = None,
addr: Optional[str] = None,
port: int = _MDNS_PORT,
) -> bool:
"""Returns true if the service could be discovered on the
network, and updates this object with details discovered.

This method will be run in the event loop.

Passing addr and port is optional, and will default to the
mDNS multicast address and port. This is useful for directing
requests to a specific host that may be able to respond across
subnets.
"""
if not zc.started:
await zc.async_wait_for_start()
Expand All @@ -658,7 +680,7 @@ async def async_request(
first_request = False
if not out.questions:
return self.load_from_cache(zc)
zc.async_send(out)
zc.async_send(out, addr, port)
next_ = now + delay
delay *= 2
next_ += random.randint(*_AVOID_SYNC_DELAY_RANDOM_INTERVAL)
Expand Down
83 changes: 83 additions & 0 deletions tests/services/test_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,89 @@ async def test_port_changes_are_seen():
await aiozc.async_close()


@pytest.mark.asyncio
async def test_port_changes_are_seen_with_directed_request():
"""Test that port changes are seen by async_request with a directed request."""
type_ = "_http._tcp.local."
registration_name = "multiarec.%s" % type_
desc = {'path': '/~paulsm/'}
aiozc = AsyncZeroconf(interfaces=['127.0.0.1'])
host = "multahost.local."

# New kwarg way
generated = r.DNSOutgoing(const._FLAGS_QR_RESPONSE)
generated.add_answer_at_time(
r.DNSNsec(
registration_name,
const._TYPE_NSEC,
const._CLASS_IN | const._CLASS_UNIQUE,
const._DNS_OTHER_TTL,
registration_name,
[const._TYPE_AAAA],
),
0,
)
generated.add_answer_at_time(
r.DNSService(
registration_name,
const._TYPE_SRV,
const._CLASS_IN | const._CLASS_UNIQUE,
10000,
0,
0,
80,
host,
),
0,
)
generated.add_answer_at_time(
r.DNSAddress(
host,
const._TYPE_A,
const._CLASS_IN,
10000,
b'\x7f\x00\x00\x01',
),
0,
)
generated.add_answer_at_time(
r.DNSText(
registration_name,
const._TYPE_TXT,
const._CLASS_IN | const._CLASS_UNIQUE,
10000,
b'\x04ff=0\x04ci=2\x04sf=0\x0bsh=6fLM5A==',
),
0,
)
await aiozc.zeroconf.async_wait_for_start()
await asyncio.sleep(0)
aiozc.zeroconf.handle_response(r.DNSIncoming(generated.packets()[0]))

generated = r.DNSOutgoing(const._FLAGS_QR_RESPONSE)
generated.add_answer_at_time(
r.DNSService(
registration_name,
const._TYPE_SRV,
const._CLASS_IN | const._CLASS_UNIQUE,
10000,
90,
90,
81,
host,
),
0,
)
aiozc.zeroconf.handle_response(r.DNSIncoming(generated.packets()[0]))

info = ServiceInfo(type_, registration_name, 80, 10, 10, desc, host)
await info.async_request(aiozc.zeroconf, timeout=200, addr="127.0.0.1", port=5353)
assert info.port == 81
assert info.priority == 90
assert info.weight == 90
await aiozc.async_close()


@pytest.mark.asyncio
async def test_ipv4_changes_are_seen():
"""Test that ipv4 changes are seen by async_request."""
Expand Down