diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index ec6787b80..6a5c9dd27 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -6,6 +6,14 @@ menu "LWIP" help The name this device will report to other devices on the network + config LWIP_DNS_SUPPORT_MDNS_QUERIES + bool "Enable mDNS queries in resolving host name" + default y + help + If this feature is enabled, standard API such as gethostbyname + support .local addresses by sending one shot multicast mDNS + query + config LWIP_L2_TO_L3_COPY bool "Enable copy between Layer2 and Layer3 packets" default n diff --git a/components/lwip/port/esp32/include/lwipopts.h b/components/lwip/port/esp32/include/lwipopts.h index 884ffb880..5ad6c9cc5 100644 --- a/components/lwip/port/esp32/include/lwipopts.h +++ b/components/lwip/port/esp32/include/lwipopts.h @@ -549,6 +549,12 @@ */ #define SO_REUSE CONFIG_LWIP_SO_REUSE + +/** + * LWIP_DNS_SUPPORT_MDNS_QUERIES==1: Enable mDNS queries in hostname resolution. + * This option is set via menuconfig. + */ +#define LWIP_DNS_SUPPORT_MDNS_QUERIES CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES /** * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets * to all local matches if SO_REUSEADDR is turned on. diff --git a/examples/protocols/mdns/main/mdns_example_main.c b/examples/protocols/mdns/main/mdns_example_main.c index a406b74d1..003c8e784 100644 --- a/examples/protocols/mdns/main/mdns_example_main.c +++ b/examples/protocols/mdns/main/mdns_example_main.c @@ -29,6 +29,11 @@ static const char *TAG = "mdns-test"; static char* generate_hostname(void); +#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1 +static void query_mdns_host_with_gethostbyname(char * host); +static void query_mdns_host_with_getaddrinfo(char * host); +#endif + static void initialise_mdns(void) { char* hostname = generate_hostname(); @@ -168,6 +173,8 @@ static void mdns_example_task(void *pvParameters) #if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1 /* Send initial queries that are started by CI tester */ query_mdns_host("tinytester"); + query_mdns_host_with_gethostbyname("tinytester-lwip.local"); + query_mdns_host_with_getaddrinfo("tinytester-lwip.local"); #endif while(1) { @@ -211,3 +218,45 @@ static char* generate_hostname(void) return hostname; #endif } + +#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1 +/** + * @brief Executes gethostbyname and displays list of resolved addresses. + * Note: This function is used only to test advertised mdns hostnames resolution + */ +static void query_mdns_host_with_gethostbyname(char * host) +{ + struct hostent *res = gethostbyname(host); + if (res) { + unsigned int i = 0; + while (res->h_addr_list[i] != NULL) { + ESP_LOGI(TAG, "gethostbyname: %s resolved to: %s", host, inet_ntoa(*(struct in_addr *) (res->h_addr_list[i]))); + i++; + } + } +} + +/** + * @brief Executes getaddrinfo and displays list of resolved addresses. + * Note: This function is used only to test advertised mdns hostnames resolution + */ +static void query_mdns_host_with_getaddrinfo(char * host) +{ + struct addrinfo hints; + struct addrinfo * res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (!getaddrinfo(host, NULL, &hints, &res)) { + while (res) { + ESP_LOGI(TAG, "getaddrinfo: %s resolved to: %s", host, + res->ai_family == AF_INET? + inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr): + inet_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr)); + res = res->ai_next; + } + } +} +#endif \ No newline at end of file diff --git a/examples/protocols/mdns/mdns_example_test.py b/examples/protocols/mdns/mdns_example_test.py index cb527989a..a06623fcc 100644 --- a/examples/protocols/mdns/mdns_example_test.py +++ b/examples/protocols/mdns/mdns_example_test.py @@ -6,7 +6,7 @@ import time import struct import dpkt import dpkt.dns -from threading import Thread +from threading import Thread, Event # this is a test case write with tiny-test-fw. @@ -24,52 +24,78 @@ except ImportError: import DUT -g_run_server = True -g_done = False +# g_run_server = True +# g_done = False +stop_mdns_server = Event() +esp_answered = Event() + + +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' + print("Created query for esp host: {} ".format(dns.__repr__())) + 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') + dns. an.append(arr) + print("Created answer to mdns query: {} ".format(dns.__repr__())) + return dns.pack() + + +def get_dns_answer_to_mdns_lwip(tester_host, id): + 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") + 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 + print("Created answer to mdns (lwip) query: {} ".format(dns.__repr__())) + return dns.pack() def mdns_server(esp_host): - global g_done + global esp_answered UDP_IP = "0.0.0.0" UDP_PORT = 5353 MCAST_GRP = '224.0.0.251' + TESTER_NAME = u'tinytester.local' + TESTER_NAME_LWIP = u'tinytester-lwip.local' sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) sock.bind((UDP_IP,UDP_PORT)) mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) - dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01') sock.settimeout(30) - resp_dns = dpkt.dns.DNS(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') - resp_dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA - resp_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 = u'tinytester.local' - arr.ip = socket.inet_aton('127.0.0.1') - resp_dns. an.append(arr) - sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT)) - while g_run_server: + while not stop_mdns_server.is_set(): try: - m = sock.recvfrom(1024) - dns = dpkt.dns.DNS(m[0]) + if not esp_answered.is_set(): + sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP,UDP_PORT)) + time.sleep(0.2) + data, addr = sock.recvfrom(1024) + dns = dpkt.dns.DNS(data) if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A: - if dns.qd[0].name == u'tinytester.local': - print(dns.__repr__(),dns.qd[0].name) - sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT)) + if dns.qd[0].name == TESTER_NAME: + print("Received query: {} ".format(dns.__repr__())) + sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), (MCAST_GRP,UDP_PORT)) + elif dns.qd[0].name == TESTER_NAME_LWIP: + print("Received query: {} ".format(dns.__repr__())) + sock.sendto(get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), addr) if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A: if dns.an[0].name == esp_host + u'.local': - print("Received answer esp32-mdns query") - g_done = True - print(dns.an[0].name) - 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' - sock.sendto(dns.pack(),(MCAST_GRP,UDP_PORT)) - print("Sending esp32-mdns query") - time.sleep(0.5) - sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT)) + print("Received answer to esp32-mdns query: {}".format(dns.__repr__())) + esp_answered.set() except socket.timeout: break except dpkt.UnpackError: @@ -78,7 +104,7 @@ def mdns_server(esp_host): @IDF.idf_example_test(env_tag="Example_WIFI") def test_examples_protocol_mdns(env, extra_data): - global g_run_server + global stop_mdns_server """ steps: | 1. join AP + init mdns example @@ -102,22 +128,19 @@ def test_examples_protocol_mdns(env, extra_data): try: dut1.expect(re.compile(r" sta ip: ([^,]+),"), timeout=30) except DUT.ExpectTimeout: - g_run_server = False + stop_mdns_server.set() thread1.join() raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP') # 3. check the mdns name is accessible - start = time.time() - while (time.time() - start) <= 60: - if g_done: - break - time.sleep(0.5) - if g_done is False: + if not esp_answered.wait(timeout=30): raise ValueError('Test has failed: did not receive mdns answer within timeout') # 4. check DUT output if mdns advertized host is resolved try: dut1.expect(re.compile(r"mdns-test: Query A: tinytester.local resolved to: 127.0.0.1"), timeout=30) + dut1.expect(re.compile(r"mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1"), timeout=30) + dut1.expect(re.compile(r"mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1"), timeout=30) finally: - g_run_server = False + stop_mdns_server.set() thread1.join() diff --git a/examples/protocols/mdns/sdkconfig.ci b/examples/protocols/mdns/sdkconfig.ci index 13b56273e..60158c45c 100644 --- a/examples/protocols/mdns/sdkconfig.ci +++ b/examples/protocols/mdns/sdkconfig.ci @@ -1,2 +1,3 @@ CONFIG_MDNS_RESOLVE_TEST_SERVICES=y CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y \ No newline at end of file