def create_response( request: bytes, services: Dict[str, FakeDnsService], ip_filter: Optional[str] = None, sleep_proxy: bool = False, ): msg = dns.DnsMessage().unpack(request) resp = dns.DnsMessage() resp.flags = 0x0840 resp.questions = msg.questions for question in resp.questions: service, full_name = _lookup_service(question, services) if service is None or (ip_filter and ip_filter not in service.addresses): continue # For typing purposes, because service is not None, then full_name is not None # either. full_name = cast(str, full_name) # Add answer if full_name: resp.answers.append(dns_utils.answer(question.qname, full_name)) # If acting as sleep proxy, just return a PTR if sleep_proxy and question.qname.startswith("_"): continue # Add service (SRV) resource if service.port: local_name = dns.qname_encode(service.name + ".local") rd = struct.pack(">3H", 0, 0, service.port) + local_name resp.resources.append( dns_utils.resource(full_name, dns.QueryType.SRV, rd)) # Add IP address(es) for address in service.addresses: ipaddr = IPv4Address(address).packed resp.resources.append( dns_utils.resource(service.name + ".local", dns.QueryType.A, ipaddr)) # Add properties if service.properties: rd = dns_utils.properties(service.properties) resp.resources.append( dns_utils.resource(full_name, dns.QueryType.TXT, rd)) # Add model if present if service.model: rd = dns_utils.properties({"model": service.model.encode("utf-8")}) resp.resources.append( dns_utils.resource(service.name + "._device-info._tcp.local", dns.QueryType.TXT, rd)) return resp
def test_authority(): msg = dns.DnsMessage() msg.authorities.append( dns_utils.resource("test.local", dns.QueryType.A, b"\x01\x02\x03\x04")) unpacked = dns.DnsMessage().unpack(msg.pack()) assert len(unpacked.authorities) == 1 record = unpacked.authorities[0] assert record.qname == "test.local" assert record.qtype == dns.QueryType.A assert record.qclass == dns_utils.DEFAULT_QCLASS assert record.ttl == dns_utils.DEFAULT_TTL assert record.rd == "1.2.3.4"
def test_parse_single_service(): service_params = ("_abc._tcp.local", "service", ["10.0.10.1"], 123, {"foo": "bar"}) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 1 dns_utils.assert_service(parsed[0], *service_params)
def test_parse_with_port_and_address(): service_params = ("_abc._tcp.local", "service", ["10.0.0.1"], 123, {}) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 1 dns_utils.assert_service(parsed[0], *service_params)
def test_parse_with_name_and_type(): service_params = ("_abc._tcp.local", "service", [], 0, {}) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 1 dns_utils.assert_service(parsed[0], *service_params)
def test_parse_properties_converts_keys_to_lower_case(): service_params = ("_abc._tcp.local", "service", [], 0, {"FOO": "bar", "Bar": "FOO"}) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 1 assert parsed[0].properties["foo"] == "bar" assert parsed[0].properties["Bar"] == "FOO"
def test_parse_ignore_link_local_address(): service_params = ( "_abc._tcp.local", "service", ["169.254.1.1"], 123, {"foo": "bar"}, ) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 1 assert parsed[0].address is None
def datagram_received(self, data: bytes, addr): msg = dns.DnsMessage().unpack(data) _LOGGER.debug("Received DNS request %s: %s", addr, msg) if self.skip_count > 0: _LOGGER.debug("Not sending DNS response (%d)", self.skip_count) self.skip_count -= 1 return resp = create_response(data, self.services, self.ip_filter, self.sleep_proxy) self.transport.sendto(resp.pack(), addr) self.request_count += 1
def test_parse_pick_one_available_address(): addresses = ["10.0.10.1", "10.0.10.2"] service_params = ( "_abc._tcp.local", "service", addresses, 123, {"foo": "bar"}, ) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 1 assert str(parsed[0].address) in addresses
def test_parse_double_service(): service1_params = ("_abc._tcp.local", "service1", "10.0.10.1", 123, { "foo": "bar" }) service2_params = ( "_def._tcp.local", "service2", "10.0.10.2", 456, { "fizz": "buzz" }, ) message = dns_utils.add_service(dns.DnsMessage(), *service1_params) message = dns_utils.add_service(message, *service2_params) parsed = mdns.parse_services(message) assert len(parsed) == 2 dns_utils.assert_service(parsed[0], *service1_params) dns_utils.assert_service(parsed[1], *service2_params)
def get_response_for_service( service: str, ) -> Tuple[dns.DnsMessage, Optional[fake_udns.FakeDnsService]]: req = mdns.create_request([service]) resp = fake_udns.create_response(req, TEST_SERVICES) return dns.DnsMessage().unpack(resp.pack()), TEST_SERVICES.get(service)
def test_parse_no_service_name(): service_params = ("_abc._tcp.local", None, [], 0, {}) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 0
def test_parse_no_service_type(): service_params = (None, "service", [], 0, {}) message = dns_utils.add_service(dns.DnsMessage(), *service_params) parsed = mdns.parse_services(message) assert len(parsed) == 0
def test_parse_empty_service(): assert mdns.parse_services(dns.DnsMessage()) == []