| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  | # SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD | 
					
						
							|  |  |  | # SPDX-License-Identifier: Unlicense OR CC0-1.0 | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2021-05-11 13:47:03 +08:00
										 |  |  | import select | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  | import socket | 
					
						
							|  |  |  | import struct | 
					
						
							| 
									
										
										
										
											2020-07-30 16:12:37 +02:00
										 |  |  | import subprocess | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  | import time | 
					
						
							|  |  |  | from threading import Event, Thread | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  | import dpkt | 
					
						
							|  |  |  | import dpkt.dns | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def get_dns_query_for_esp(esp_host): | 
					
						
							|  |  |  |     dns = dpkt.dns.DNS( | 
					
						
							|  |  |  |         b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     dns.qd[0].name = esp_host + u'.local' | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |     print('Created query for esp host: {} '.format(dns.__repr__())) | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |     return dns.pack() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_dns_answer_to_mdns(tester_host): | 
					
						
							|  |  |  |     dns = dpkt.dns.DNS( | 
					
						
							|  |  |  |         b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA | 
					
						
							|  |  |  |     dns.rcode = dpkt.dns.DNS_RCODE_NOERR | 
					
						
							|  |  |  |     arr = dpkt.dns.DNS.RR() | 
					
						
							|  |  |  |     arr.cls = dpkt.dns.DNS_IN | 
					
						
							|  |  |  |     arr.type = dpkt.dns.DNS_A | 
					
						
							|  |  |  |     arr.name = tester_host | 
					
						
							|  |  |  |     arr.ip = socket.inet_aton('127.0.0.1') | 
					
						
							| 
									
										
										
										
											2021-12-14 18:34:04 +01:00
										 |  |  |     dns.an.append(arr) | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |     print('Created answer to mdns query: {} '.format(dns.__repr__())) | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |     return dns.pack() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_dns_answer_to_mdns_lwip(tester_host, id): | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  |     dns = dpkt.dns.DNS( | 
					
						
							|  |  |  |         b'\x5e\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x0a\x64\x61\x76\x69\x64' | 
					
						
							|  |  |  |         b'\x2d\x63\x6f\x6d\x70\x05\x6c\x6f\x63\x61\x6c\x00\x00\x01\x00\x01\xc0\x0c' | 
					
						
							|  |  |  |         b'\x00\x01\x00\x01\x00\x00\x00\x0a\x00\x04\xc0\xa8\x0a\x6c') | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |     dns.qd[0].name = tester_host | 
					
						
							|  |  |  |     dns.an[0].name = tester_host | 
					
						
							|  |  |  |     dns.an[0].ip = socket.inet_aton('127.0.0.1') | 
					
						
							|  |  |  |     dns.an[0].rdata = socket.inet_aton('127.0.0.1') | 
					
						
							|  |  |  |     dns.id = id | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  |     print('Created answer to mdns (lwip) query: {} '.format(dns.__repr__())) | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |     return dns.pack() | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-04 08:32:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  | def mdns_server(esp_host, events): | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  |     UDP_IP = '0.0.0.0' | 
					
						
							| 
									
										
										
										
											2018-12-04 08:32:48 +01:00
										 |  |  |     UDP_PORT = 5353 | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |     MCAST_GRP = '224.0.0.251' | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |     TESTER_NAME = u'tinytester.local' | 
					
						
							|  |  |  |     TESTER_NAME_LWIP = u'tinytester-lwip.local' | 
					
						
							| 
									
										
										
										
											2021-05-11 13:47:03 +08:00
										 |  |  |     QUERY_TIMEOUT = 0.2 | 
					
						
							| 
									
										
										
										
											2018-12-04 08:32:48 +01:00
										 |  |  |     sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |     sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | 
					
						
							|  |  |  |     sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | 
					
						
							| 
									
										
										
										
											2021-05-11 13:47:03 +08:00
										 |  |  |     sock.setblocking(False) | 
					
						
							| 
									
										
										
										
											2021-12-14 18:34:04 +01:00
										 |  |  |     sock.bind((UDP_IP, UDP_PORT)) | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  |     mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |     sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) | 
					
						
							| 
									
										
										
										
											2021-05-11 13:47:03 +08:00
										 |  |  |     last_query_timepoint = time.time() | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |     while not events['stop'].is_set(): | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2021-05-11 13:47:03 +08:00
										 |  |  |             current_time = time.time() | 
					
						
							| 
									
										
										
										
											2021-06-30 08:42:09 +02:00
										 |  |  |             if current_time - last_query_timepoint > QUERY_TIMEOUT: | 
					
						
							| 
									
										
										
										
											2021-05-11 13:47:03 +08:00
										 |  |  |                 last_query_timepoint = current_time | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |                 if not events['esp_answered'].is_set(): | 
					
						
							| 
									
										
										
										
											2021-12-14 18:34:04 +01:00
										 |  |  |                     sock.sendto(get_dns_query_for_esp(esp_host), | 
					
						
							|  |  |  |                                 (MCAST_GRP, UDP_PORT)) | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |                 if not events['esp_delegated_answered'].is_set(): | 
					
						
							| 
									
										
										
										
											2021-12-14 18:34:04 +01:00
										 |  |  |                     sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'), | 
					
						
							|  |  |  |                                 (MCAST_GRP, UDP_PORT)) | 
					
						
							| 
									
										
										
										
											2021-05-11 13:47:03 +08:00
										 |  |  |             timeout = max( | 
					
						
							|  |  |  |                 0, QUERY_TIMEOUT - (current_time - last_query_timepoint)) | 
					
						
							|  |  |  |             read_socks, _, _ = select.select([sock], [], [], timeout) | 
					
						
							|  |  |  |             if not read_socks: | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |             data, addr = sock.recvfrom(1024) | 
					
						
							|  |  |  |             dns = dpkt.dns.DNS(data) | 
					
						
							| 
									
										
										
										
											2018-12-04 08:32:48 +01:00
										 |  |  |             if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A: | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |                 if dns.qd[0].name == TESTER_NAME: | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |                     print('Received query: {} '.format(dns.__repr__())) | 
					
						
							| 
									
										
										
										
											2021-12-14 18:34:04 +01:00
										 |  |  |                     sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), | 
					
						
							|  |  |  |                                 (MCAST_GRP, UDP_PORT)) | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |                 elif dns.qd[0].name == TESTER_NAME_LWIP: | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |                     print('Received query: {} '.format(dns.__repr__())) | 
					
						
							| 
									
										
										
										
											2019-06-19 13:20:58 +02:00
										 |  |  |                     sock.sendto( | 
					
						
							|  |  |  |                         get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), | 
					
						
							|  |  |  |                         addr) | 
					
						
							| 
									
										
										
										
											2018-12-04 08:32:48 +01:00
										 |  |  |             if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A: | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |                 print('Received answer from {}'.format(dns.an[0].name)) | 
					
						
							| 
									
										
										
										
											2018-12-04 08:32:48 +01:00
										 |  |  |                 if dns.an[0].name == esp_host + u'.local': | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |                     print('Received answer to esp32-mdns query: {}'.format( | 
					
						
							|  |  |  |                         dns.__repr__())) | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |                     events['esp_answered'].set() | 
					
						
							| 
									
										
										
										
											2021-04-15 20:03:53 +08:00
										 |  |  |                 if dns.an[0].name == esp_host + u'-delegated.local': | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |                     print('Received answer to esp32-mdns-delegate query: {}'. | 
					
						
							|  |  |  |                           format(dns.__repr__())) | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |                     events['esp_delegated_answered'].set() | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |         except socket.timeout: | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2019-01-25 20:24:30 +01:00
										 |  |  |         except dpkt.UnpackError: | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-04 08:32:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  | def test_examples_protocol_mdns(dut): | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     steps: | | 
					
						
							| 
									
										
										
										
											2022-01-14 14:33:13 +01:00
										 |  |  |       1. obtain IP address + init mdns example | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |       2. get the dut host name (and IP address) | 
					
						
							|  |  |  |       3. check the mdns name is accessible | 
					
						
							|  |  |  |       4. check DUT output if mdns advertized host is resolved | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     specific_host = dut.expect(re.compile( | 
					
						
							|  |  |  |         b'mdns hostname set to: \[(.*?)\]')).group(1).decode()  # noqa: W605 | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     mdns_server_events = { | 
					
						
							|  |  |  |         'stop': Event(), | 
					
						
							|  |  |  |         'esp_answered': Event(), | 
					
						
							|  |  |  |         'esp_delegated_answered': Event() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     mdns_responder = Thread(target=mdns_server, | 
					
						
							|  |  |  |                             args=(str(specific_host), mdns_server_events)) | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |     ip_address = dut.expect( | 
					
						
							|  |  |  |         re.compile(b'IPv4 address:([a-zA-Z0-9]*).*')).group(1).decode() | 
					
						
							|  |  |  |     print('Connected to AP with IP: {}'.format(ip_address)) | 
					
						
							| 
									
										
										
										
											2018-10-12 16:45:52 +02:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |         # 3. check the mdns name is accessible. | 
					
						
							| 
									
										
										
										
											2021-06-30 08:42:09 +02:00
										 |  |  |         mdns_responder.start() | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |         if not mdns_server_events['esp_answered'].wait(timeout=30): | 
					
						
							| 
									
										
										
										
											2020-05-15 10:41:14 +02:00
										 |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 'Test has failed: did not receive mdns answer within timeout') | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |         if not mdns_server_events['esp_delegated_answered'].wait(timeout=30): | 
					
						
							| 
									
										
										
										
											2021-04-15 20:03:53 +08:00
										 |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 'Test has failed: did not receive mdns answer for delegated host within timeout' | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2020-05-15 10:41:14 +02:00
										 |  |  |         # 4. check DUT output if mdns advertized host is resolved | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |         dut.expect( | 
					
						
							|  |  |  |             re.compile( | 
					
						
							|  |  |  |                 b'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1') | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         dut.expect( | 
					
						
							|  |  |  |             re.compile( | 
					
						
							|  |  |  |                 b'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1' | 
					
						
							|  |  |  |             )) | 
					
						
							|  |  |  |         dut.expect( | 
					
						
							|  |  |  |             re.compile( | 
					
						
							|  |  |  |                 b'mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1' | 
					
						
							| 
									
										
										
										
											2022-10-11 16:31:57 +02:00
										 |  |  |             )) | 
					
						
							| 
									
										
										
										
											2020-07-30 16:12:37 +02:00
										 |  |  |         # 5. check the DUT answers to `dig` command | 
					
						
							|  |  |  |         dig_output = subprocess.check_output([ | 
					
						
							|  |  |  |             'dig', '+short', '-p', '5353', '@224.0.0.251', | 
					
						
							|  |  |  |             '{}.local'.format(specific_host) | 
					
						
							|  |  |  |         ]) | 
					
						
							| 
									
										
										
										
											2022-08-19 13:49:32 +04:00
										 |  |  |         print('Resolving {} using "dig" succeeded with:\n{}'.format( | 
					
						
							|  |  |  |             specific_host, dig_output)) | 
					
						
							| 
									
										
										
										
											2020-07-30 16:12:37 +02:00
										 |  |  |         if not ip_address.encode('utf-8') in dig_output: | 
					
						
							| 
									
										
										
										
											2021-01-26 10:49:01 +08:00
										 |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 'Test has failed: Incorrectly resolved DUT hostname using dig' | 
					
						
							| 
									
										
										
										
											2020-07-30 16:12:37 +02:00
										 |  |  |                 "Output should've contained DUT's IP address:{}".format( | 
					
						
							|  |  |  |                     ip_address)) | 
					
						
							| 
									
										
										
										
											2019-01-25 20:24:30 +01:00
										 |  |  |     finally: | 
					
						
							| 
									
										
										
										
											2021-12-15 15:48:59 +01:00
										 |  |  |         mdns_server_events['stop'].set() | 
					
						
							| 
									
										
										
										
											2021-06-30 08:42:09 +02:00
										 |  |  |         mdns_responder.join() |