mirror of
https://github.com/espressif/openthread.git
synced 2026-08-14 14:47:46 +00:00
[dns-server] handle multiple questions (#6155)
This commit enhances the DNS-SD server to handle multiple questions.
Specifically for DNS service resolving query with SRV+TXT questions,
this commit would reply with SRV+TXT+AAAA RRs, so that the service
instance can be fully resolved with one query.
More implementation details:
- This commit enhances NameCompressInfo so that question names can be
compressed together with RR names.
- Full compression is limited to only one instance / service / host
name.
- For any RR type in the question, this commit will exclude it in
additional RRs. For example, if the query contains a AAAA question,
the response won't include any additional AAAA RR, but will only
have AAAA answer(s). The purpose is to avoid duplicate RRs in answer
section and additional section.
This commit is contained in:
@@ -2429,6 +2429,132 @@ class NodeImpl:
|
||||
|
||||
return list(zip(ip, ttl))
|
||||
|
||||
def dns_resolve_service(self, instance, service, server=None, port=53):
|
||||
"""
|
||||
Resolves the service instance and returns the instance information as a dict.
|
||||
|
||||
Example return value:
|
||||
{
|
||||
'port': 12345,
|
||||
'priority': 0,
|
||||
'weight': 0,
|
||||
'host': 'ins1._ipps._tcp.default.service.arpa.',
|
||||
'address': '2001::1',
|
||||
'txt_data': b'\x00',
|
||||
'srv_ttl': 7100,
|
||||
'txt_ttl': 7100,
|
||||
'aaaa_ttl': 7100,
|
||||
}
|
||||
"""
|
||||
cmd = f'dns service {instance} {service}'
|
||||
if server is not None:
|
||||
cmd += f' {server} {port}'
|
||||
|
||||
self.send_command(cmd)
|
||||
self.simulator.go(10)
|
||||
output = self._expect_command_output(cmd)
|
||||
|
||||
# Example output:
|
||||
# DNS service resolution response for ins2 for service _ipps._tcp.default.service.arpa.
|
||||
# Port:22222, Priority:2, Weight:2, TTL:7155
|
||||
# Host:host2.default.service.arpa.
|
||||
# HostAddress:0:0:0:0:0:0:0:0 TTL:0
|
||||
# TXT-Data:(len:1) [00] TTL:7155
|
||||
# Done
|
||||
|
||||
m = re.match(
|
||||
r'.*Port:(\d+), Priority:(\d+), Weight:(\d+), TTL:(\d+)\s+Host:(.*?)\s+HostAddress:(\S+) TTL:(\d+)\s+TXT-Data:\(len:\d+\) \[(.*?)\] TTL:(\d+)',
|
||||
'\r'.join(output))
|
||||
if m:
|
||||
port, priority, weight, srv_ttl, hostname, address, aaaa_ttl, txt_data, txt_ttl = m.groups()
|
||||
return {
|
||||
'port': int(port),
|
||||
'priority': int(priority),
|
||||
'weight': int(weight),
|
||||
'host': hostname,
|
||||
'address': address,
|
||||
'txt_data': self.__parse_hex_string(txt_data),
|
||||
'srv_ttl': int(srv_ttl),
|
||||
'txt_ttl': int(txt_ttl),
|
||||
'aaaa_ttl': int(aaaa_ttl),
|
||||
}
|
||||
else:
|
||||
raise Exception('dns resolve service failed: %s.%s' % (instance, service))
|
||||
|
||||
@staticmethod
|
||||
def __parse_hex_string(hexstr: str) -> bytes:
|
||||
assert (len(hexstr) % 2 == 0)
|
||||
return bytes(int(hexstr[i:i + 2], 16) for i in range(0, len(hexstr), 2))
|
||||
|
||||
def dns_browse(self, service_name, server=None, port=53):
|
||||
"""
|
||||
Browse the service and returns the instances.
|
||||
|
||||
Example return value:
|
||||
{
|
||||
'ins1': {
|
||||
'port': 12345,
|
||||
'priority': 1,
|
||||
'weight': 1,
|
||||
'host': 'ins1._ipps._tcp.default.service.arpa.',
|
||||
'address': '2001::1',
|
||||
'txt_data': b'\x00',
|
||||
'srv_ttl': 7100,
|
||||
'txt_ttl': 7100,
|
||||
'aaaa_ttl': 7100,
|
||||
},
|
||||
'ins2': {
|
||||
'port': 12345,
|
||||
'priority': 2,
|
||||
'weight': 2,
|
||||
'host': 'ins2._ipps._tcp.default.service.arpa.',
|
||||
'address': '2001::2',
|
||||
'txt_data': b'\x00',
|
||||
'srv_ttl': 7100,
|
||||
'txt_ttl': 7100,
|
||||
'aaaa_ttl': 7100,
|
||||
}
|
||||
}
|
||||
"""
|
||||
cmd = f'dns browse {service_name}'
|
||||
if server is not None:
|
||||
cmd += f' {server} {port}'
|
||||
|
||||
self.send_command(cmd)
|
||||
self.simulator.go(10)
|
||||
output = '\n'.join(self._expect_command_output(cmd))
|
||||
|
||||
# Example output:
|
||||
# ins2
|
||||
# Port:22222, Priority:2, Weight:2, TTL:7175
|
||||
# Host:host2.default.service.arpa.
|
||||
# HostAddress:fd00:db8:0:0:3205:28dd:5b87:6a63 TTL:7175
|
||||
# TXT-Data:(len:1) [00] TTL:7175
|
||||
# ins1
|
||||
# Port:11111, Priority:1, Weight:1, TTL:7170
|
||||
# Host:host1.default.service.arpa.
|
||||
# HostAddress:fd00:db8:0:0:39f4:d9:eb4f:778 TTL:7170
|
||||
# TXT-Data:(len:1) [00] TTL:7170
|
||||
# Done
|
||||
|
||||
result = {}
|
||||
for ins, port, priority, weight, srv_ttl, hostname, address, aaaa_ttl, txt_data, txt_ttl in re.findall(
|
||||
r'(.*?)\s+Port:(\d+), Priority:(\d+), Weight:(\d+), TTL:(\d+)\s*Host:(\S+)\s+HostAddress:(\S+) TTL:(\d+)\s+TXT-Data:\(len:\d+\) \[(.*?)\] TTL:(\d+)',
|
||||
output):
|
||||
result[ins] = {
|
||||
'port': int(port),
|
||||
'priority': int(priority),
|
||||
'weight': int(weight),
|
||||
'host': hostname,
|
||||
'address': address,
|
||||
'txt_data': self.__parse_hex_string(txt_data),
|
||||
'srv_ttl': int(srv_ttl),
|
||||
'txt_ttl': int(txt_ttl),
|
||||
'aaaa_ttl': int(aaaa_ttl),
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Node(NodeImpl, OtCli):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user