feat(mdns): Bring mdns component and example from idf

Commit ID: 463a9d8b
This commit is contained in:
yuanjm
2020-02-27 15:37:11 +08:00
parent c4f549dbc1
commit 1cf9c9c33a
23 changed files with 635 additions and 243 deletions

View File

@ -1,19 +1,7 @@
if(CONFIG_ENABLE_MDNS)
set(COMPONENT_SRCS "src/mdns.c"
"src/mdns_networking.c")
set(COMPONENT_PRIV_INCLUDEDIRS "private_include")
endif()
set(COMPONENT_ADD_INCLUDEDIRS "include")
set(COMPONENT_REQUIRES "lwip" "mbedtls" "tcpip_adapter")
if(CONFIG_ENABLE_MDNS_CONSOLE)
set(COMPONENT_SRCS "${COMPONENT_SRCS}"
"src/mdns_console.c")
endif()
set(COMPONENT_REQUIRES "console" "tcpip_adapter" "newlib")
register_component()
idf_component_register(SRCS "mdns.c"
"mdns_console.c"
"mdns_networking.c"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include"
REQUIRES lwip mbedtls console tcpip_adapter)

View File

@ -1,38 +1,13 @@
menu "mDNS"
config ENABLE_MDNS
bool "Enable mDNS"
default n
select LWIP_IPV6
help
Enable this option and then mDNS is to be used.
config MDNS_MAX_SERVICES
int "Max number of services"
range 1 64
default 10
help
Services take up a certain amount of memory, and allowing fewer
services to be open at the same time conserves memory. Specify
the maximum amount of services here. The valid value is from 1
to 64.
config ENABLE_MDNS_CONSOLE
bool "Enable mDNS console"
default n
depends on ENABLE_MDNS
select USING_ESP_CONSOLE
help
Enable this option and then mDNS console is to be used.
config MDNS_MAX_SERVICES
int "Max number of services"
range 1 64
default 10
depends on ENABLE_MDNS
help
Services take up a certain amount of memory, and allowing fewer
services to be open at the same time conserves memory. Specify
the maximum amount of services here. The valid value is from 1
to 64.
config MDNS_STACKSIZE
int "Max stack size of MDNS"
range 2048 4096
default 4096
depends on ENABLE_MDNS
help
The stacksize of the MDNS task that will be responsible for transmitting
and receiving MDNS packets.
endmenu

View File

@ -1,10 +1,2 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include
ifdef CONFIG_ENABLE_MDNS
COMPONENT_SRCDIRS := src
endif
ifndef CONFIG_ENABLE_MDNS_CONSOLE
COMPONENT_OBJEXCLUDE := src/mdns_console.o
endif

View File

@ -44,8 +44,8 @@ typedef enum {
* Used in mdns_service_add()
*/
typedef struct {
char * key; /*!< item key name */
char * value; /*!< item value string */
const char * key; /*!< item key name */
const char * value; /*!< item value string */
} mdns_txt_item_t;
/**
@ -81,10 +81,9 @@ typedef struct mdns_result_s {
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG when bad tcpip_if is given
* - ESP_ERR_INVALID_STATE when the network returned error
* - ESP_ERR_INVALID_STATE when failed to register event handler
* - ESP_ERR_NO_MEM on memory error
* - ESP_ERR_WIFI_NOT_INIT when WiFi is not initialized by eps_wifi_init
* - ESP_FAIL when failed to start mdns task
*/
esp_err_t mdns_init();
@ -127,13 +126,14 @@ esp_err_t mdns_instance_name_set(const char * instance_name);
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param port service port
* @param num_items number of items in TXT data
* @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
* - ESP_FAIL failed to add serivce
*/
esp_err_t mdns_service_add(const char * instance_name, const char * service_type, const char * proto, uint16_t port, mdns_txt_item_t txt[], size_t num_items);
@ -147,7 +147,7 @@ esp_err_t mdns_service_add(const char * instance_name, const char * service_type
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_FAIL unknown error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_remove(const char * service_type, const char * proto);
@ -177,6 +177,7 @@ esp_err_t mdns_service_instance_name_set(const char * service_type, const char *
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_port_set(const char * service_type, const char * proto, uint16_t port);
@ -185,8 +186,8 @@ esp_err_t mdns_service_port_set(const char * service_type, const char * proto, u
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param num_items number of items in TXT data
* @param txt array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success

View File

@ -22,6 +22,10 @@
void mdns_debug_packet(const uint8_t * data, size_t len);
#endif
// Internal size of IPv6 address is defined here as size of AAAA record in mdns packet
// since the ip6_addr_t is defined in lwip and depends on using IPv6 zones
#define _MDNS_SIZEOF_IP6_ADDR (MDNS_ANSWER_AAAA_SIZE)
static const char * MDNS_DEFAULT_DOMAIN = "local";
static const char * MDNS_SUB_STR = "_sub";
@ -174,7 +178,7 @@ static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * s
size_t index = 0;
while (start[index]) {
if (name->parts == 4) {
return NULL;
name->invalid = true;
}
uint8_t len = start[index++];
if (len < 0xC0) {
@ -195,7 +199,7 @@ static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * s
strlcat(name->host, buf, sizeof(name->host));
} else if (strcasecmp(buf, MDNS_SUB_STR) == 0) {
name->sub = 1;
} else {
} else if (!name->invalid) {
char* mdns_name_ptrs[]={name->host, name->service, name->proto, name->domain};
memcpy(mdns_name_ptrs[name->parts++], buf, len+1);
}
@ -441,6 +445,10 @@ static uint16_t _mdns_append_ptr_record(uint8_t * packet, uint16_t * index, cons
uint16_t record_length = 0;
uint8_t part_length;
if (service == NULL) {
return 0;
}
str[0] = instance;
str[1] = service;
str[2] = proto;
@ -485,6 +493,10 @@ static uint16_t _mdns_append_sdptr_record(uint8_t * packet, uint16_t * index, md
uint16_t record_length = 0;
uint8_t part_length;
if (service == NULL) {
return 0;
}
sd_str[0] = (char*)"_services";
sd_str[1] = (char*)"_dns-sd";
sd_str[2] = (char*)"_udp";
@ -530,6 +542,10 @@ static uint16_t _mdns_append_txt_record(uint8_t * packet, uint16_t * index, mdns
uint16_t record_length = 0;
uint8_t part_length;
if (service == NULL) {
return 0;
}
str[0] = _mdns_get_service_instance_name(service);
str[1] = service->service;
str[2] = service->proto;
@ -598,6 +614,10 @@ static uint16_t _mdns_append_srv_record(uint8_t * packet, uint16_t * index, mdns
uint16_t record_length = 0;
uint8_t part_length;
if (service == NULL) {
return 0;
}
str[0] = _mdns_get_service_instance_name(service);
str[1] = service->service;
str[2] = service->proto;
@ -734,11 +754,11 @@ static uint16_t _mdns_append_aaaa_record(uint8_t * packet, uint16_t * index, uin
uint16_t data_len_location = *index - 2;
if ((*index + 15) >= MDNS_MAX_PACKET_SIZE) {
if ((*index + MDNS_ANSWER_AAAA_SIZE) > MDNS_MAX_PACKET_SIZE) {
return 0;
}
part_length = sizeof(ip6_addr_t);
part_length = MDNS_ANSWER_AAAA_SIZE;
memcpy(packet + *index, ipv6, part_length);
*index += part_length;
_mdns_set_u16(packet, data_len_location, part_length);
@ -817,7 +837,7 @@ static bool _ipv6_address_is_zero(ip6_addr_t ip6)
{
uint8_t i;
uint8_t * data = (uint8_t *)ip6.addr;
for (i=0; i<16; i++) {
for (i=0; i<_MDNS_SIZEOF_IP6_ADDR; i++) {
if (data[i]) {
return false;
}
@ -1147,6 +1167,7 @@ static bool _mdns_alloc_answer(mdns_out_answer_t ** destnation, uint16_t type, m
}
a->type = type;
a->service = service;
a->custom_service = NULL;
a->bye = bye;
a->flush = flush;
a->next = NULL;
@ -1234,7 +1255,7 @@ static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t * parsed
}
} else if (q->type == MDNS_TYPE_SDPTR) {
shared = true;
if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service->service, false, false)) {
if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, service->service, false, false)) {
_mdns_free_tx_packet(packet);
return;
}
@ -1312,6 +1333,7 @@ static mdns_tx_packet_t * _mdns_create_probe_packet(tcpip_adapter_if_t tcpip_if,
q->domain = MDNS_DEFAULT_DOMAIN;
if (!q->host || _mdns_question_exists(q, packet->questions)) {
free(q);
continue;
} else {
queueToEnd(mdns_out_question_t, packet->questions, q);
}
@ -2188,7 +2210,7 @@ static int _mdns_check_aaaa_collision(ip6_addr_t * ip, tcpip_adapter_if_t tcpip_
if (tcpip_adapter_get_ip6_linklocal(tcpip_if, &if_ip6)) {
return 1;//they win
}
int ret = memcmp((uint8_t*)&if_ip6.addr, (uint8_t*)ip->addr, sizeof(ip6_addr_t));
int ret = memcmp((uint8_t*)&if_ip6.addr, (uint8_t*)ip->addr, _MDNS_SIZEOF_IP6_ADDR);
if (ret > 0) {
return -1;//we win
} else if (ret < 0) {
@ -2200,7 +2222,7 @@ static int _mdns_check_aaaa_collision(ip6_addr_t * ip, tcpip_adapter_if_t tcpip_
if (tcpip_adapter_get_ip6_linklocal(other_if, &other_ip6)) {
return 1;//IPv6 not active! They win
}
if (memcmp((uint8_t*)&other_ip6.addr, (uint8_t*)ip->addr, sizeof(ip6_addr_t))) {
if (memcmp((uint8_t*)&other_ip6.addr, (uint8_t*)ip->addr, _MDNS_SIZEOF_IP6_ADDR)) {
return 1;//IPv6 not ours! They win
}
_mdns_dup_interface(tcpip_if);
@ -2315,6 +2337,7 @@ static const uint8_t * _mdns_parse_fqdn(const uint8_t * packet, const uint8_t *
name->service[0] = 0;
name->proto[0] = 0;
name->domain[0] = 0;
name->invalid = false;
static char buf[MDNS_NAME_BUF_LEN];
@ -2322,7 +2345,7 @@ static const uint8_t * _mdns_parse_fqdn(const uint8_t * packet, const uint8_t *
if (!next_data) {
return 0;
}
if (!name->parts) {
if (!name->parts || name->invalid) {
return next_data;
}
if (name->parts == 3) {
@ -2620,7 +2643,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
clas &= 0x7FFF;
content = content + 4;
if (clas != 0x0001) {//bad class
if (clas != 0x0001 || name->invalid) {//bad class or invalid name for this question entry
continue;
}
@ -2801,7 +2824,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
col = 1;
} else if (!clas) {
col = -1;
} else {
} else if (service) { // only detect srv collision if service existed
col = _mdns_check_srv_collision(service->service, priority, weight, port, name->host, name->domain);
}
if (col && (parsed_packet->probe || parsed_packet->authoritative)) {
@ -2886,7 +2909,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
col = 1;
} else if (!clas) {
col = -1;
} else {
} else if (service) { // only detect txt collision if service existed
col = _mdns_check_txt_collision(service->service, data_ptr, data_len);
}
if (col && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
@ -2900,7 +2923,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
} else if (type == MDNS_TYPE_AAAA) {//ipv6
ip_addr_t ip6;
ip6.type = IPADDR_TYPE_V6;
memcpy(ip6.u_addr.ip6.addr, data_ptr, 16);
memcpy(ip6.u_addr.ip6.addr, data_ptr, MDNS_ANSWER_AAAA_SIZE);
if (search_result) {
//check for more applicable searches (PTR & A/AAAA at the same time)
while (search_result) {
@ -3048,56 +3071,69 @@ void _mdns_disable_pcb(tcpip_adapter_if_t tcpip_if, mdns_ip_protocol_t ip_protoc
/**
* @brief Dispatch interface changes based on system events
*/
void _mdns_handle_system_event(system_event_id_t event, tcpip_adapter_if_t interface)
static void _mdns_handle_system_event(esp_event_base_t event_base,
int32_t event_id, tcpip_adapter_if_t interface)
{
if (!_mdns_server) {
return;
}
tcpip_adapter_dhcp_status_t dcst;
switch(event) {
case SYSTEM_EVENT_STA_CONNECTED:
if (!tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dcst)) {
if (dcst != TCPIP_ADAPTER_DHCP_STARTED) {
if (event_base == WIFI_EVENT) {
switch(event_id) {
case WIFI_EVENT_STA_CONNECTED:
if (!tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dcst)) {
if (dcst == TCPIP_ADAPTER_DHCP_STOPPED) {
_mdns_enable_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V4);
}
}
break;
case WIFI_EVENT_STA_DISCONNECTED:
_mdns_disable_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V4);
_mdns_disable_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V6);
break;
case WIFI_EVENT_AP_START:
_mdns_enable_pcb(TCPIP_ADAPTER_IF_AP, MDNS_IP_PROTOCOL_V4);
break;
case WIFI_EVENT_AP_STOP:
_mdns_disable_pcb(TCPIP_ADAPTER_IF_AP, MDNS_IP_PROTOCOL_V4);
_mdns_disable_pcb(TCPIP_ADAPTER_IF_AP, MDNS_IP_PROTOCOL_V6);
break;
default:
break;
}
} else if (event_base == ETH_EVENT) {
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
if (!tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_ETH, &dcst)) {
if (dcst == TCPIP_ADAPTER_DHCP_STOPPED) {
_mdns_enable_pcb(TCPIP_ADAPTER_IF_ETH, MDNS_IP_PROTOCOL_V4);
}
}
break;
case ETHERNET_EVENT_DISCONNECTED:
_mdns_disable_pcb(TCPIP_ADAPTER_IF_ETH, MDNS_IP_PROTOCOL_V4);
_mdns_disable_pcb(TCPIP_ADAPTER_IF_ETH, MDNS_IP_PROTOCOL_V6);
break;
default:
break;
}
} else if (event_base == IP_EVENT) {
switch (event_id) {
case IP_EVENT_STA_GOT_IP:
_mdns_enable_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V4);
}
}
break;
case SYSTEM_EVENT_STA_GOT_IP:
_mdns_enable_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V4);
_mdns_announce_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V6, NULL, 0, true);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
_mdns_disable_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V4);
_mdns_disable_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V6);
break;
case SYSTEM_EVENT_AP_START:
_mdns_enable_pcb(TCPIP_ADAPTER_IF_AP, MDNS_IP_PROTOCOL_V4);
break;
case SYSTEM_EVENT_AP_STOP:
_mdns_disable_pcb(TCPIP_ADAPTER_IF_AP, MDNS_IP_PROTOCOL_V4);
_mdns_disable_pcb(TCPIP_ADAPTER_IF_AP, MDNS_IP_PROTOCOL_V6);
break;
case SYSTEM_EVENT_GOT_IP6:
_mdns_enable_pcb(interface, MDNS_IP_PROTOCOL_V6);
_mdns_announce_pcb(interface, MDNS_IP_PROTOCOL_V4, NULL, 0, true);
break;
case SYSTEM_EVENT_ETH_CONNECTED:
if (!tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_ETH, &dcst)) {
if (dcst != TCPIP_ADAPTER_DHCP_STARTED) {
_mdns_announce_pcb(TCPIP_ADAPTER_IF_STA, MDNS_IP_PROTOCOL_V6, NULL, 0, true);
break;
case IP_EVENT_ETH_GOT_IP:
_mdns_enable_pcb(TCPIP_ADAPTER_IF_ETH, MDNS_IP_PROTOCOL_V4);
}
break;
case IP_EVENT_GOT_IP6:
_mdns_enable_pcb(interface, MDNS_IP_PROTOCOL_V6);
_mdns_announce_pcb(interface, MDNS_IP_PROTOCOL_V4, NULL, 0, true);
break;
default:
break;
}
break;
case SYSTEM_EVENT_ETH_GOT_IP:
_mdns_enable_pcb(TCPIP_ADAPTER_IF_ETH, MDNS_IP_PROTOCOL_V4);
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
_mdns_disable_pcb(TCPIP_ADAPTER_IF_ETH, MDNS_IP_PROTOCOL_V4);
_mdns_disable_pcb(TCPIP_ADAPTER_IF_ETH, MDNS_IP_PROTOCOL_V6);
break;
default:
break;
}
}
@ -3709,7 +3745,8 @@ static void _mdns_execute_action(mdns_action_t * action)
switch(action->type) {
case ACTION_SYSTEM_EVENT:
_mdns_handle_system_event(action->data.sys_event.event_id, action->data.sys_event.interface);
_mdns_handle_system_event(action->data.sys_event.event_base,
action->data.sys_event.event_id, action->data.sys_event.interface);
break;
case ACTION_HOSTNAME_SET:
_mdns_send_final_bye(true);
@ -3817,23 +3854,25 @@ static void _mdns_execute_action(mdns_action_t * action)
break;
case ACTION_SERVICE_DEL:
a = _mdns_server->services;
if (_mdns_server->services == action->data.srv_del.service) {
_mdns_server->services = a->next;
_mdns_send_bye(&a, 1, false);
_mdns_remove_scheduled_service_packets(a->service);
_mdns_free_service(a->service);
free(a);
} else {
while (a->next && a->next != action->data.srv_del.service) {
a = a->next;
}
if (a->next == action->data.srv_del.service) {
mdns_srv_item_t * b = a->next;
a->next = a->next->next;
_mdns_send_bye(&b, 1, false);
_mdns_remove_scheduled_service_packets(b->service);
_mdns_free_service(b->service);
free(b);
if (action->data.srv_del.service) {
if (_mdns_server->services == action->data.srv_del.service) {
_mdns_server->services = a->next;
_mdns_send_bye(&a, 1, false);
_mdns_remove_scheduled_service_packets(a->service);
_mdns_free_service(a->service);
free(a);
} else {
while (a->next && a->next != action->data.srv_del.service) {
a = a->next;
}
if (a->next == action->data.srv_del.service) {
mdns_srv_item_t * b = a->next;
a->next = a->next->next;
_mdns_send_bye(&b, 1, false);
_mdns_remove_scheduled_service_packets(b->service);
_mdns_free_service(b->service);
free(b);
}
}
}
@ -4098,23 +4137,34 @@ static esp_err_t _mdns_service_task_stop()
* */
esp_err_t mdns_handle_system_event(void *ctx, system_event_t *event)
{
/* no-op, kept for compatibility */
return ESP_OK;
}
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (!_mdns_server) {
return ESP_OK;
return;
}
mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
mdns_action_t * action = (mdns_action_t *)calloc(1, sizeof(mdns_action_t));
if (!action) {
HOOK_MALLOC_FAILED;
return ESP_OK;
return;
}
action->type = ACTION_SYSTEM_EVENT;
action->data.sys_event.event_id = event->event_id;
action->data.sys_event.interface = (event->event_id == SYSTEM_EVENT_GOT_IP6)?event->event_info.got_ip6.if_index:0;
action->data.sys_event.event_base = event_base;
action->data.sys_event.event_id = event_id;
if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) {
ip_event_got_ip6_t* event = (ip_event_got_ip6_t*) event_data;
action->data.sys_event.interface = event->if_index;
}
if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
free(action);
}
return ESP_OK;
}
esp_err_t mdns_init()
@ -4144,6 +4194,16 @@ esp_err_t mdns_init()
goto free_lock;
}
if ((err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)) != ESP_OK) {
goto free_event_handlers;
}
if ((err = esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)) != ESP_OK) {
goto free_event_handlers;
}
if ((err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)) != ESP_OK) {
goto free_event_handlers;
}
uint8_t i;
ip6_addr_t tmp_addr6;
tcpip_adapter_ip_info_t if_ip_info;
@ -4170,6 +4230,10 @@ free_all_and_disable_pcbs:
_mdns_disable_pcb(i, MDNS_IP_PROTOCOL_V6);
_mdns_disable_pcb(i, MDNS_IP_PROTOCOL_V4);
}
free_event_handlers:
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler);
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler);
esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, &event_handler);
vQueueDelete(_mdns_server->action_queue);
free_lock:
vSemaphoreDelete(_mdns_server->lock);
@ -4185,6 +4249,11 @@ void mdns_free()
if (!_mdns_server) {
return;
}
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler);
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler);
esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, &event_handler);
mdns_service_remove_all(_mdns_server);
_mdns_service_task_stop();
for (i=0; i<TCPIP_ADAPTER_IF_MAX; i++) {
@ -4892,7 +4961,7 @@ void mdns_debug_packet(const uint8_t * data, size_t len)
_mdns_dbg_printf("\n");
} else if (type == MDNS_TYPE_AAAA) {
ip6_addr_t ip6;
memcpy(&ip6, data_ptr, sizeof(ip6_addr_t));
memcpy(&ip6, data_ptr, MDNS_ANSWER_AAAA_SIZE);
_mdns_dbg_printf(IPV6STR "\n", IPV62STR(ip6));
} else if (type == MDNS_TYPE_A) {
ip4_addr_t ip;

View File

@ -4,7 +4,6 @@
*
*/
#include <string.h>
#include "mdns_private.h"
#include "mdns_networking.h"
#include "esp_log.h"

View File

@ -6,7 +6,7 @@
*
*/
#include "mdns.h"
#include "mdns_private.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
@ -20,7 +20,8 @@
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "esp_event_loop.h"
#include "esp_event.h"
#include "esp_eth.h"
/**

View File

@ -14,11 +14,7 @@
#ifndef MDNS_PRIVATE_H_
#define MDNS_PRIVATE_H_
#include <stdint.h>
#include <stdbool.h>
#include "tcpip_adapter.h"
#include "esp_timer.h"
#include "mdns.h"
#include "esp_event_base.h"
//#define MDNS_ENABLE_DEBUG
@ -56,6 +52,7 @@
#define MDNS_ANSWER_AAAA 0x10
#define MDNS_ANSWER_NSEC 0x20
#define MDNS_ANSWER_SDPTR 0x80
#define MDNS_ANSWER_AAAA_SIZE 16
#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on
#define MDNS_SERVICE_STACK_DEPTH 4096 // Stack size for the service thread
@ -188,6 +185,7 @@ typedef struct {
char domain[MDNS_NAME_BUF_LEN];
uint8_t parts;
uint8_t sub;
bool invalid;
} mdns_name_t;
typedef struct mdns_parsed_question_s {
@ -264,7 +262,7 @@ typedef struct mdns_srv_item_s {
typedef struct mdns_out_question_s {
struct mdns_out_question_s * next;
uint16_t type;
uint8_t unicast;
bool unicast;
const char * host;
const char * service;
const char * proto;
@ -352,7 +350,8 @@ typedef struct {
char * hostname;
char * instance;
struct {
system_event_id_t event_id;
esp_event_base_t event_base;
int32_t event_id;
tcpip_adapter_if_t interface;
} sys_event;
struct {

View File

@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
PRIV_REQUIRES unity test_utils mdns)

View File

@ -0,0 +1,4 @@
#
#Component Makefile
#
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View File

@ -0,0 +1,99 @@
#include "test_utils.h"
#include "mdns.h"
#include "unity.h"
#define MDNS_HOSTNAME "test-hostname"
#define MDNS_INSTANCE "test-instance"
#define MDNS_SERVICE_NAME "_http"
#define MDNS_SERVICE_PROTO "_tcp"
#define MDNS_SERVICE_PORT 80
static void yield_to_all_priorities(void)
{
// Lower the test-task priority before testing to ensure other tasks got executed on forced context switch
size_t test_task_prio_before = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, tskIDLE_PRIORITY);
taskYIELD(); // Let the RTOS to switch context
vTaskPrioritySet(NULL, test_task_prio_before);
}
TEST_CASE("mdns api to fail in invalid state", "[mdns][leaks=64]")
{
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME) );
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE) );
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0) );
}
TEST_CASE("mdns init and deinit", "[mdns][leaks=64]")
{
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
yield_to_all_priorities(); // Make sure that mdns task has executed to complete initialization
mdns_free();
esp_event_loop_delete_default();
}
TEST_CASE("mdns api return expected err-code and do not leak memory", "[mdns][leaks=64]")
{
mdns_txt_item_t serviceTxtData[CONFIG_MDNS_MAX_SERVICES] = { {NULL, NULL},
};
for (int i=0; i<CONFIG_MDNS_MAX_SERVICES; ++i) {
serviceTxtData[i].key = "Key";
serviceTxtData[i].value = "Value";
}
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1", "value1") );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1") );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO) );
yield_to_all_priorities(); // Make sure that mdns task has executed to remove the service
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove_all() );
yield_to_all_priorities(); // Make sure that mdns task has executed to remove all services
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080) );
mdns_free();
esp_event_loop_delete_default();
}
TEST_CASE("mdns query api return expected err-code and do not leak memory", "[leaks=64]")
{
mdns_result_t * results = NULL;
ip6_addr_t addr6;
ip4_addr_t addr4;
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_ptr(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, CONFIG_MDNS_MAX_SERVICES, &results) );
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_srv(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results) );
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_txt(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results) );
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_a(MDNS_HOSTNAME, 10, &addr4) );
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_aaaa(MDNS_HOSTNAME, 10, &addr6) );
mdns_query_results_free(results);
mdns_free();
esp_event_loop_delete_default();
}

View File

@ -1,7 +1,9 @@
TEST_NAME=test
FUZZ=afl-fuzz
COMPONENTS_DIR=../..
CFLAGS=-g -DMDNS_TEST_MODE -I. -I.. -I../include -I../private_include -I$(COMPONENTS_DIR)/tcpip_adapter/include -I$(COMPONENTS_DIR)/esp32/include -include esp32_compat.h
CFLAGS=-g -DHOOK_MALLOC_FAILED -DESP_EVENT_H_ -D__ESP_LOG_H__ -DMDNS_TEST_MODE \
-I. -I.. -I../include -I../private_include -include esp32_compat.h \
-I$(COMPONENTS_DIR)/tcpip_adapter/include -I$(COMPONENTS_DIR)/esp_event/include -I$(COMPONENTS_DIR)/log/include
MDNS_C_DEPENDENCY_INJECTION=-include mdns_di.h
ifeq ($(INSTR),off)
CC=gcc

View File

@ -20,7 +20,6 @@
// Not to include
#define ESP_MDNS_NETWORKING_H_
#define _TCPIP_ADAPTER_H_
#define __ESP_EVENT_H__
#ifdef USE_BSD_STRING
@ -57,6 +56,7 @@
#define portMAX_DELAY 0xFFFFFFFF
#define portTICK_PERIOD_MS 1
#define ESP_LOGD(a,b)
#define xSemaphoreTake(s,d)
#define xTaskDelete(a)
@ -65,6 +65,7 @@
#define _mdns_pcb_init(a,b) true
#define _mdns_pcb_deinit(a,b) true
#define xSemaphoreCreateMutex() malloc(1)
#define xSemaphoreCreateBinary() malloc(1)
#define vSemaphoreDelete(s) free(s)
#define xTaskCreatePinnedToCore(a,b,c,d,e,f,g) *(f) = malloc(1)
#define vTaskDelay(m) usleep((m)*0)
@ -114,6 +115,47 @@ typedef enum {
WIFI_MODE_MAX
} wifi_mode_t;
extern const char * WIFI_EVENT;
extern const char * IP_EVENT;
extern const char * ETH_EVENT;
typedef enum {
WIFI_EVENT_WIFI_READY = 0, /**< ESP32 WiFi ready */
WIFI_EVENT_SCAN_DONE, /**< ESP32 finish scanning AP */
WIFI_EVENT_STA_START, /**< ESP32 station start */
WIFI_EVENT_STA_STOP, /**< ESP32 station stop */
WIFI_EVENT_STA_CONNECTED, /**< ESP32 station connected to AP */
WIFI_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */
WIFI_EVENT_STA_AUTHMODE_CHANGE, /**< the auth mode of AP connected by ESP32 station changed */
WIFI_EVENT_STA_WPS_ER_SUCCESS, /**< ESP32 station wps succeeds in enrollee mode */
WIFI_EVENT_STA_WPS_ER_FAILED, /**< ESP32 station wps fails in enrollee mode */
WIFI_EVENT_STA_WPS_ER_TIMEOUT, /**< ESP32 station wps timeout in enrollee mode */
WIFI_EVENT_STA_WPS_ER_PIN, /**< ESP32 station wps pin code in enrollee mode */
WIFI_EVENT_AP_START, /**< ESP32 soft-AP start */
WIFI_EVENT_AP_STOP, /**< ESP32 soft-AP stop */
WIFI_EVENT_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */
WIFI_EVENT_AP_STADISCONNECTED, /**< a station disconnected from ESP32 soft-AP */
WIFI_EVENT_AP_PROBEREQRECVED, /**< Receive probe request packet in soft-AP interface */
} wifi_event_t;
typedef enum {
ETHERNET_EVENT_START, /**< ESP32 ethernet start */
ETHERNET_EVENT_STOP, /**< ESP32 ethernet stop */
ETHERNET_EVENT_CONNECTED, /**< ESP32 ethernet phy link up */
ETHERNET_EVENT_DISCONNECTED, /**< ESP32 ethernet phy link down */
} eth_event_t;
typedef enum {
IP_EVENT_STA_GOT_IP, /*!< ESP32 station got IP from connected AP */
IP_EVENT_STA_LOST_IP, /*!< ESP32 station lost IP and the IP is reset to 0 */
IP_EVENT_AP_STAIPASSIGNED, /*!< ESP32 soft-AP assign an IP to a connected station */
IP_EVENT_GOT_IP6, /*!< ESP32 station or ap or ethernet interface v6IP addr is preferred */
IP_EVENT_ETH_GOT_IP, /*!< ESP32 ethernet got IP from connected AP */
} ip_event_t;
/* status of DHCP client or DHCP server */
typedef enum {
TCPIP_ADAPTER_DHCP_INIT = 0, /**< DHCP client/server in initial state */
@ -122,34 +164,6 @@ typedef enum {
TCPIP_ADAPTER_DHCP_STATUS_MAX
} tcpip_adapter_dhcp_status_t;
typedef enum {
SYSTEM_EVENT_WIFI_READY = 0, /**< ESP32 WiFi ready */
SYSTEM_EVENT_SCAN_DONE, /**< ESP32 finish scanning AP */
SYSTEM_EVENT_STA_START, /**< ESP32 station start */
SYSTEM_EVENT_STA_STOP, /**< ESP32 station stop */
SYSTEM_EVENT_STA_CONNECTED, /**< ESP32 station connected to AP */
SYSTEM_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */
SYSTEM_EVENT_STA_AUTHMODE_CHANGE, /**< the auth mode of AP connected by ESP32 station changed */
SYSTEM_EVENT_STA_GOT_IP, /**< ESP32 station got IP from connected AP */
SYSTEM_EVENT_STA_LOST_IP, /**< ESP32 station lost IP and the IP is reset to 0 */
SYSTEM_EVENT_STA_WPS_ER_SUCCESS, /**< ESP32 station wps succeeds in enrollee mode */
SYSTEM_EVENT_STA_WPS_ER_FAILED, /**< ESP32 station wps fails in enrollee mode */
SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, /**< ESP32 station wps timeout in enrollee mode */
SYSTEM_EVENT_STA_WPS_ER_PIN, /**< ESP32 station wps pin code in enrollee mode */
SYSTEM_EVENT_AP_START, /**< ESP32 soft-AP start */
SYSTEM_EVENT_AP_STOP, /**< ESP32 soft-AP stop */
SYSTEM_EVENT_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */
SYSTEM_EVENT_AP_STADISCONNECTED, /**< a station disconnected from ESP32 soft-AP */
SYSTEM_EVENT_AP_PROBEREQRECVED, /**< Receive probe request packet in soft-AP interface */
SYSTEM_EVENT_GOT_IP6, /**< ESP32 station or ap or ethernet interface v6IP addr is preferred */
SYSTEM_EVENT_ETH_START, /**< ESP32 ethernet start */
SYSTEM_EVENT_ETH_STOP, /**< ESP32 ethernet stop */
SYSTEM_EVENT_ETH_CONNECTED, /**< ESP32 ethernet phy link up */
SYSTEM_EVENT_ETH_DISCONNECTED, /**< ESP32 ethernet phy link down */
SYSTEM_EVENT_ETH_GOT_IP, /**< ESP32 ethernet got IP from connected AP */
SYSTEM_EVENT_MAX
} system_event_id_t;
struct udp_pcb {
uint8_t dummy;
};
@ -178,7 +192,6 @@ typedef struct {
ip4_addr_t gw;
} tcpip_adapter_ip_info_t;
// typedef int32_t system_event_id_t;
typedef enum {
TCPIP_ADAPTER_IF_STA = 0, /**< ESP32 station interface */
TCPIP_ADAPTER_IF_AP, /**< ESP32 soft-AP interface */
@ -189,18 +202,13 @@ typedef enum {
typedef struct {
ip6_addr_t ip;
} tcpip_adapter_ip6_info_t;
typedef struct {
tcpip_adapter_if_t if_index;
tcpip_adapter_ip6_info_t ip6_info;
} system_event_got_ip6_t;
typedef union {
system_event_got_ip6_t got_ip6; /**< ESP32 station or ap or ethernet ipv6 addr state change to preferred */
} system_event_info_t;
typedef struct {
system_event_id_t event_id; /**< event ID */
system_event_info_t event_info; /**< event information */
} system_event_t;
tcpip_adapter_if_t if_index; /*!< Interface for which the event is received */
tcpip_adapter_ip6_info_t ip6_info; /*!< IPv6 address of the interface */
} ip_event_got_ip6_t;
typedef void* system_event_t;
inline esp_err_t esp_wifi_get_mode(wifi_mode_t * mode)
{

View File

@ -9,6 +9,23 @@ void* g_queue;
int g_queue_send_shall_fail = 0;
int g_size = 0;
const char * WIFI_EVENT = "wifi_event";
const char * IP_EVENT = "ip_event";
const char * ETH_EVENT = "eth_event";
esp_err_t esp_event_handler_register(const char * event_base,
int32_t event_id,
void* event_handler,
void* event_handler_arg)
{
return ESP_OK;
}
esp_err_t esp_event_handler_unregister(const char * event_base, int32_t event_id, void* event_handler)
{
return ESP_OK;
}
esp_err_t esp_timer_delete(esp_timer_handle_t timer)
{
return ESP_OK;

View File

@ -40,6 +40,10 @@ void GetLastItem(void *pvBuffer);
void ForceTaskDelete();
esp_err_t esp_event_handler_register(const char * event_base, int32_t event_id, void* event_handler, void* event_handler_arg);
esp_err_t esp_event_handler_unregister(const char * event_base, int32_t event_id, void* event_handler);
#define _mdns_udp_pcb_write(tcpip_if, ip_protocol, ip, port, data, len) len
#endif /* ESP32_MOCK_H_ */

View File

@ -4,18 +4,90 @@ Shows how to use mDNS to advertise lookup services and hosts
## Example workflow
- mDNS is initialized with host name and instance name defined through `make menuconfig` and `_http._tcp` service is added to be advertised
- WiFi STA is started and trying to connect to the access point defined through `make menuconfig`
- mDNS is initialized with host name and instance name defined through the project configuration and `_http._tcp` service is added to be advertised
- WiFi STA is started and trying to connect to the access point defined through the project configuration
- The system event handler is used to pass the network events to mDNS so the service is aware when the interface comes up or down
- GPIO0 (BOOT Button) is initialized as pulled-up input that can be monitored for button press
- Example task is started to check if the button is pressed so it can execute the mDNS queries defined
## Running the example
### Configure the project
- Run `make menuconfig` to configure the access point's SSID and Password and the default device mDNS host name and instance name
- Run `make flash monitor` to build and upload the example to your board and connect to it's serial terminal
- Wait for WiFi to connec to your access point
- You can now ping the device at `[hostname].local` and browse for `_http._tcp` on the same network to find the advertised service
- Pressing the BOOT button will start quring the local network for the predefined in `check_button` hosts and services
* Open the project configuration menu (`idf.py menuconfig`)
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../README.md) for more details.
* When using Make build system, set `Default serial port` under `Serial flasher config`.
* Set `mDNS Hostname` as host name prefix for the device and its instance name in `mDNS Instance Name`
* Disable `Resolve test services` to prevent the example from querying defined names/services on startup (cause warnings in example logs, as illustrated below)
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
- Wait for WiFi to connect to your access point
- You can now ping the device at `[board-hostname].local`, where `[board-hostname]` is preconfigured hostname, `esp32-mdns` by default.
- You can also browse for `_http._tcp` on the same network to find the advertised service
- Pressing the BOOT button will start querying the local network for the predefined in `check_button` hosts and services
- Note that for purpose of CI tests, configuration options of `MDNS_RESOLVE_TEST_SERVICES` and `MDNS_ADD_MAC_TO_HOSTNAME` are available, but disabled by default. If enabled, then the hostname suffix of last 3 bytes from device MAC address is added, e.g. `esp32-mdns-80FFFF`, and a query for test service is issued.
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (0) cpu_start: Starting scheduler on APP CPU.
I (276) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (276) mdns-test: mdns hostname set to: [esp32-mdns]
I (286) wifi: wifi driver task: 3ffc2fa4, prio:23, stack:3584, core=0
I (286) wifi: wifi firmware version: a3be639
I (286) wifi: config NVS flash: enabled
I (296) wifi: config nano formating: disabled
I (296) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (306) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (336) wifi: Init dynamic tx buffer num: 32
I (336) wifi: Init data frame dynamic rx buffer num: 32
I (336) wifi: Init management frame dynamic rx buffer num: 32
I (346) wifi: Init static rx buffer size: 1600
I (346) wifi: Init static rx buffer num: 10
I (346) wifi: Init dynamic rx buffer num: 32
I (356) mdns-test: Setting WiFi configuration SSID myssid...
I (426) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0
I (426) wifi: mode : sta (30:ae:a4:80:FF:FF)
I (426) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (1756) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1
I (2736) wifi: state: init -> auth (b0)
I (2756) wifi: state: auth -> assoc (0)
I (2766) wifi: state: assoc -> run (10)
I (2786) wifi: connected with myssid, channel 11
I (2786) wifi: pm start, type: 1
I (4786) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2
I (21126) mdns-test: Query A: esp32.local
W (23176) mdns-test: ESP_ERR_NOT_FOUND: Host was not found!
I (23176) mdns-test: Query PTR: _arduino._tcp.local
W (26276) mdns-test: No results found!
I (26276) mdns-test: Query PTR: _http._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:80
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : switch4e4919
SRV : switch4e4919.local:80
TXT : [1] path=/config/authentication_page.htm;
A : 192.168.0.118
I (29396) mdns-test: Query PTR: _printer._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:515
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : HP Color LaserJet MFP M277dw (7C2E10)
```
See the README.md file in the upper level 'examples' directory for more information about examples.

View File

@ -1,4 +1,2 @@
set(COMPONENT_SRCS "mdns_example_main.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()
idf_component_register(SRCS "mdns_example_main.c"
INCLUDE_DIRS ".")

View File

@ -1,15 +1,31 @@
menu "Example Configuration"
config MDNS_HOSTNAME
string "mDNS Hostname"
default "esp32-mdns"
help
mDNS Hostname for example to use
config MDNS_HOSTNAME
string "mDNS Hostname"
default "esp32-mdns"
help
mDNS Hostname for example to use
config MDNS_INSTANCE
string "mDNS Instance Name"
default "ESP32 with mDNS"
help
mDNS Instance Name for example to use
config MDNS_INSTANCE
string "mDNS Instance Name"
default "ESP32 with mDNS"
help
mDNS Instance Name for example to use
config MDNS_RESOLVE_TEST_SERVICES
bool "Resolve test services"
default n
help
Enable resolving test services on startup.
These services are advertized and evaluated in automated tests.
When executed locally, these will not be resolved and warnings appear in the log.
Please set to false to disable initial querying to avoid warnings.
config MDNS_ADD_MAC_TO_HOSTNAME
bool "Add mac suffix to hostname"
default n
help
If enabled, a portion of MAC address is added to the hostname, this is used
for evaluation of tests in CI
endmenu

View File

@ -7,33 +7,36 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "tcpip_adapter.h"
#include "protocol_examples_common.h"
#include "mdns.h"
#include "driver/gpio.h"
#include <sys/socket.h>
#include <netdb.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "mdns.h"
#define EXAMPLE_MDNS_HOSTNAME CONFIG_MDNS_HOSTNAME
#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE
#define EXAMPLE_BUTTON_GPIO 0
static const char *TAG = "mdns-test";
static char* generate_hostname();
static void initialise_mdns(void)
{
char* hostname = generate_hostname();
//initialize mDNS
ESP_ERROR_CHECK( mdns_init() );
//set mDNS hostname (required if you want to advertise services)
ESP_ERROR_CHECK( mdns_hostname_set(EXAMPLE_MDNS_HOSTNAME) );
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
//set default mDNS instance name
ESP_ERROR_CHECK( mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE) );
@ -50,9 +53,13 @@ static void initialise_mdns(void)
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") );
//change TXT item value
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "u", "admin") );
free(hostname);
}
/* these strings match tcpip_adapter_if_t enumeration */
static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
/* these strings match mdns_ip_protocol_t enumeration */
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
static void mdns_print_results(mdns_result_t * results){
@ -124,14 +131,14 @@ static void query_mdns_host(const char * host_name)
return;
}
ESP_LOGI(TAG, IPSTR, IP2STR(&addr));
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
}
static void initialise_button(void)
{
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = 1;
gpio_config_t io_conf = {0};
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.pin_bit_mask = BIT64(EXAMPLE_BUTTON_GPIO);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
io_conf.pull_down_en = 0;
@ -141,7 +148,7 @@ static void initialise_button(void)
static void check_button(void)
{
static bool old_level = true;
bool new_level = gpio_get_level(GPIO_NUM_0);
bool new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO);
if (!new_level && old_level) {
query_mdns_host("esp32");
query_mdns_service("_arduino", "_tcp");
@ -158,6 +165,11 @@ static void check_button(void)
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");
#endif
while(1) {
check_button();
vTaskDelay(50 / portTICK_PERIOD_MS);
@ -167,13 +179,35 @@ static void mdns_example_task(void *pvParameters)
void app_main()
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
initialise_mdns();
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
initialise_mdns();
initialise_button();
xTaskCreate(&mdns_example_task, "mdns_example_task", 2048, NULL, 5, NULL);
}
/** Generate host name based on sdkconfig, optionally adding a portion of MAC address to it.
* @return host name string allocated from the heap
*/
static char* generate_hostname()
{
#ifndef CONFIG_MDNS_ADD_MAC_TO_HOSTNAME
return strdup(CONFIG_MDNS_HOSTNAME);
#else
uint8_t mac[6];
char *hostname;
esp_read_mac(mac, ESP_MAC_WIFI_STA);
if (-1 == asprintf(&hostname, "%s-%02X%02X%02X", CONFIG_MDNS_HOSTNAME, mac[3], mac[4], mac[5])) {
abort();
}
return hostname;
#endif
}

View File

@ -0,0 +1,111 @@
import re
import os
import socket
import time
import struct
import dpkt
import dpkt.dns
from threading import Thread
from tiny_test_fw import DUT
import ttfw_idf
g_run_server = True
g_done = False
def mdns_server(esp_host):
global g_done
UDP_IP = "0.0.0.0"
UDP_PORT = 5353
MCAST_GRP = '224.0.0.251'
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:
try:
m = sock.recvfrom(1024)
dns = dpkt.dns.DNS(m[0])
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 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))
except socket.timeout:
break
except dpkt.UnpackError:
continue
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_mdns(env, extra_data):
global g_run_server
"""
steps: |
1. join AP + init mdns example
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
"""
dut1 = env.get_dut("mdns-test", "examples/protocols/mdns")
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "mdns-test.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("mdns-test_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("mdns-test_bin_size", bin_size // 1024)
# 1. start mdns application
dut1.start_app()
# 2. get the dut host name (and IP address)
specific_host = dut1.expect(re.compile(r"mdns hostname set to: \[([^\]]+)\]"), timeout=30)
specific_host = str(specific_host[0])
thread1 = Thread(target=mdns_server, args=(specific_host,))
thread1.start()
try:
dut1.expect(re.compile(r" sta ip: ([^,]+),"), timeout=30)
except DUT.ExpectTimeout:
g_run_server = False
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:
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)
finally:
g_run_server = False
thread1.join()
if __name__ == '__main__':
test_examples_protocol_mdns()

View File

@ -0,0 +1,2 @@
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y

View File

@ -1 +0,0 @@
CONFIG_ENABLE_MDNS=y