mdns_example_test.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import os
  2. import re
  3. import select
  4. import socket
  5. import struct
  6. import subprocess
  7. import time
  8. from threading import Event, Thread
  9. import dpkt
  10. import dpkt.dns
  11. import ttfw_idf
  12. from tiny_test_fw import DUT
  13. from tiny_test_fw.Utility import console_log
  14. stop_mdns_server = Event()
  15. esp_answered = Event()
  16. esp_delegated_answered = Event()
  17. def get_dns_query_for_esp(esp_host):
  18. dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
  19. dns.qd[0].name = esp_host + u'.local'
  20. console_log('Created query for esp host: {} '.format(dns.__repr__()))
  21. return dns.pack()
  22. def get_dns_answer_to_mdns(tester_host):
  23. dns = dpkt.dns.DNS(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
  24. dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA
  25. dns.rcode = dpkt.dns.DNS_RCODE_NOERR
  26. arr = dpkt.dns.DNS.RR()
  27. arr.cls = dpkt.dns.DNS_IN
  28. arr.type = dpkt.dns.DNS_A
  29. arr.name = tester_host
  30. arr.ip = socket.inet_aton('127.0.0.1')
  31. dns. an.append(arr)
  32. console_log('Created answer to mdns query: {} '.format(dns.__repr__()))
  33. return dns.pack()
  34. def get_dns_answer_to_mdns_lwip(tester_host, id):
  35. dns = dpkt.dns.DNS(b'\x5e\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x0a\x64\x61\x76\x69\x64'
  36. b'\x2d\x63\x6f\x6d\x70\x05\x6c\x6f\x63\x61\x6c\x00\x00\x01\x00\x01\xc0\x0c'
  37. b'\x00\x01\x00\x01\x00\x00\x00\x0a\x00\x04\xc0\xa8\x0a\x6c')
  38. dns.qd[0].name = tester_host
  39. dns.an[0].name = tester_host
  40. dns.an[0].ip = socket.inet_aton('127.0.0.1')
  41. dns.an[0].rdata = socket.inet_aton('127.0.0.1')
  42. dns.id = id
  43. print('Created answer to mdns (lwip) query: {} '.format(dns.__repr__()))
  44. return dns.pack()
  45. def mdns_server(esp_host):
  46. global esp_answered
  47. UDP_IP = '0.0.0.0'
  48. UDP_PORT = 5353
  49. MCAST_GRP = '224.0.0.251'
  50. TESTER_NAME = u'tinytester.local'
  51. TESTER_NAME_LWIP = u'tinytester-lwip.local'
  52. QUERY_TIMEOUT = 0.2
  53. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  54. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  55. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
  56. sock.setblocking(False)
  57. sock.bind((UDP_IP,UDP_PORT))
  58. mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
  59. sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
  60. last_query_timepoint = time.time()
  61. while not stop_mdns_server.is_set():
  62. try:
  63. current_time = time.time()
  64. if current_time - last_query_timepoint > QUERY_TIMEOUT:
  65. last_query_timepoint = current_time
  66. if not esp_answered.is_set():
  67. sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP,UDP_PORT))
  68. if not esp_delegated_answered.is_set():
  69. sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'), (MCAST_GRP,UDP_PORT))
  70. timeout = max(0, QUERY_TIMEOUT - (current_time - last_query_timepoint))
  71. read_socks, _, _ = select.select([sock], [], [], timeout)
  72. if not read_socks:
  73. continue
  74. data, addr = sock.recvfrom(1024)
  75. dns = dpkt.dns.DNS(data)
  76. if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A:
  77. if dns.qd[0].name == TESTER_NAME:
  78. console_log('Received query: {} '.format(dns.__repr__()))
  79. sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), (MCAST_GRP,UDP_PORT))
  80. elif dns.qd[0].name == TESTER_NAME_LWIP:
  81. console_log('Received query: {} '.format(dns.__repr__()))
  82. sock.sendto(get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), addr)
  83. if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A:
  84. console_log('Received answer from {}'.format(dns.an[0].name))
  85. if dns.an[0].name == esp_host + u'.local':
  86. console_log('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
  87. esp_answered.set()
  88. if dns.an[0].name == esp_host + u'-delegated.local':
  89. console_log('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__()))
  90. esp_delegated_answered.set()
  91. except socket.timeout:
  92. break
  93. except dpkt.UnpackError:
  94. continue
  95. @ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
  96. def test_examples_protocol_mdns(env, extra_data):
  97. global stop_mdns_server
  98. """
  99. steps: |
  100. 1. obtain IP address + init mdns example
  101. 2. get the dut host name (and IP address)
  102. 3. check the mdns name is accessible
  103. 4. check DUT output if mdns advertized host is resolved
  104. """
  105. dut1 = env.get_dut('mdns-test', 'examples/protocols/mdns', dut_class=ttfw_idf.ESP32DUT, app_config_name='eth_kit')
  106. # check and log bin size
  107. binary_file = os.path.join(dut1.app.binary_path, 'mdns_test.bin')
  108. bin_size = os.path.getsize(binary_file)
  109. ttfw_idf.log_performance('mdns-test_bin_size', '{}KB'.format(bin_size // 1024))
  110. # 1. start mdns application
  111. dut1.start_app()
  112. # 2. get the dut host name (and IP address)
  113. specific_host = dut1.expect(re.compile(r'mdns hostname set to: \[([^\]]+)\]'), timeout=30)[0]
  114. mdns_responder = Thread(target=mdns_server, args=(str(specific_host),))
  115. try:
  116. ip_address = dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30)[0]
  117. console_log('Connected to AP with IP: {}'.format(ip_address))
  118. except DUT.ExpectTimeout:
  119. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  120. try:
  121. # 3. check the mdns name is accessible
  122. mdns_responder.start()
  123. if not esp_answered.wait(timeout=30):
  124. raise ValueError('Test has failed: did not receive mdns answer within timeout')
  125. if not esp_delegated_answered.wait(timeout=30):
  126. raise ValueError('Test has failed: did not receive mdns answer for delegated host within timeout')
  127. # 4. check DUT output if mdns advertized host is resolved
  128. dut1.expect(re.compile(r'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1'), timeout=30)
  129. dut1.expect(re.compile(r'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
  130. dut1.expect(re.compile(r'mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
  131. # 5. check the DUT answers to `dig` command
  132. dig_output = subprocess.check_output(['dig', '+short', '-p', '5353', '@224.0.0.251',
  133. '{}.local'.format(specific_host)])
  134. console_log('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output))
  135. if not ip_address.encode('utf-8') in dig_output:
  136. raise ValueError('Test has failed: Incorrectly resolved DUT hostname using dig'
  137. "Output should've contained DUT's IP address:{}".format(ip_address))
  138. finally:
  139. stop_mdns_server.set()
  140. mdns_responder.join()
  141. if __name__ == '__main__':
  142. test_examples_protocol_mdns()