forked from espressif/esp-protocols
mDNS: Initial version based on IDF 5.0
This commit is contained in:
1 parent
8863ed944d
commit
b6b20ad399
105 files changed
+312
-18
No files matched your search
@@ -0,0 +1,11 @@
|
||||
# mDNS Service
|
||||
|
||||
mDNS is a multicast UDP service that is used to provide local network service and host discovery.
|
||||
|
||||
## Examples
|
||||
|
||||
Get started with example test [Example](examples/README.md):
|
||||
|
||||
## Documentation
|
||||
|
||||
* View the full [html documentation](https://espressif.github.io/esp-protocols/mdns/index.html)
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
# This is Doxygen configuration file
|
||||
#
|
||||
# Doxygen provides over 260 configuration statements
|
||||
# To make this file easier to follow,
|
||||
# it contains only statements that are non-default
|
||||
#
|
||||
# NOTE:
|
||||
# It is recommended not to change defaults unless specifically required
|
||||
# Test any changes how they affect generated documentation
|
||||
# Make sure that correct warnings are generated to flag issues with documented code
|
||||
#
|
||||
# For the complete list of configuration statements see:
|
||||
# http://doxygen.nl/manual/config.html
|
||||
|
||||
|
||||
PROJECT_NAME = "ESP Protocols Programming Guide"
|
||||
|
||||
## The 'INPUT' statement below is used as input by script 'gen-df-input.py'
|
||||
## to automatically generate API reference list files heder_file.inc
|
||||
## These files are placed in '_inc' directory
|
||||
## and used to include in API reference documentation
|
||||
|
||||
INPUT = \
|
||||
$(PROJECT_PATH)/include/mdns.h
|
||||
|
||||
## Get warnings for functions that have no documentation for their parameters or return value
|
||||
##
|
||||
WARN_NO_PARAMDOC = YES
|
||||
|
||||
## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files
|
||||
##
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = \
|
||||
$(ENV_DOXYGEN_DEFINES) \
|
||||
__DOXYGEN__=1 \
|
||||
__attribute__(x)= \
|
||||
_Static_assert()= \
|
||||
IDF_DEPRECATED(X)= \
|
||||
IRAM_ATTR= \
|
||||
configSUPPORT_DYNAMIC_ALLOCATION=1 \
|
||||
configSUPPORT_STATIC_ALLOCATION=1 \
|
||||
configQUEUE_REGISTRY_SIZE=1 \
|
||||
configUSE_RECURSIVE_MUTEXES=1 \
|
||||
configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \
|
||||
configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
|
||||
configUSE_APPLICATION_TASK_TAG=1 \
|
||||
configTASKLIST_INCLUDE_COREID=1 \
|
||||
"ESP_EVENT_DECLARE_BASE(x)=extern esp_event_base_t x"
|
||||
|
||||
## Do not complain about not having dot
|
||||
##
|
||||
HAVE_DOT = NO
|
||||
|
||||
## Generate XML that is required for Breathe
|
||||
##
|
||||
GENERATE_XML = YES
|
||||
XML_OUTPUT = xml
|
||||
|
||||
GENERATE_HTML = NO
|
||||
HAVE_DOT = NO
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_MAN = YES
|
||||
GENERATE_RTF = NO
|
||||
|
||||
## Skip distracting progress messages
|
||||
##
|
||||
QUIET = YES
|
||||
|
||||
## Enable Section Tags for conditional documentation
|
||||
##
|
||||
ENABLED_SECTIONS += \
|
||||
DOC_EXCLUDE_HEADER_SECTION \ ## To conditionally remove doc sections from IDF source files without affecting documentation in upstream files.
|
||||
DOC_SINGLE_GROUP ## To conditionally remove groups from the documentation and create a 'flat' document without affecting documentation in upstream files.
|
||||
@@ -0,0 +1,21 @@
|
||||
from esp_docs.conf_docs import * # noqa: F403,F401
|
||||
|
||||
extensions += ['sphinx_copybutton',
|
||||
# Needed as a trigger for running doxygen
|
||||
'esp_docs.esp_extensions.dummy_build_system',
|
||||
'esp_docs.esp_extensions.run_doxygen',
|
||||
]
|
||||
|
||||
# link roles config
|
||||
github_repo = 'espressif/esp-protocols'
|
||||
|
||||
# context used by sphinx_idf_theme
|
||||
html_context['github_user'] = 'espressif'
|
||||
html_context['github_repo'] = 'esp-protocols'
|
||||
|
||||
# Extra options required by sphinx_idf_theme
|
||||
project_slug = 'esp-idf' # >=5.0
|
||||
versions_url = 'https://github.com/espressif/esp-protocols/docs/docs_versions.js'
|
||||
|
||||
idf_targets = ['esp32']
|
||||
languages = ['en', 'zh_CN']
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# English Language RTD & Sphinx config file
|
||||
#
|
||||
# Uses ../conf_common.py for most non-language-specific settings.
|
||||
|
||||
# Importing conf_common adds all the non-language-specific
|
||||
# parts to this conf module
|
||||
|
||||
try:
|
||||
from conf_common import * # noqa: F403,F401
|
||||
except ImportError:
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../'))
|
||||
from conf_common import * # noqa: F403,F401
|
||||
|
||||
# General information about the project.
|
||||
project = u'ESP-Protocols'
|
||||
copyright = u'2016 - 2022, Espressif Systems (Shanghai) Co., Ltd'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'en'
|
||||
@@ -0,0 +1,205 @@
|
||||
mDNS Service
|
||||
============
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
mDNS is a multicast UDP service that is used to provide local network service and host discovery.
|
||||
|
||||
mDNS is installed by default on most operating systems or is available as separate package. On ``Mac OS`` it is installed by default and is called ``Bonjour``. Apple releases an installer for ``Windows`` that can be found `on Apple's support page <https://support.apple.com/downloads/bonjour%2520for%2520windows>`_. On ``Linux``, mDNS is provided by `avahi <https://github.com/lathiat/avahi>`_ and is usually installed by default.
|
||||
|
||||
mDNS Properties
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
* ``hostname``: the hostname that the device will respond to. If not set, the ``hostname`` will be read from the interface. Example: ``my-{IDF_TARGET_PATH_NAME}`` will resolve to ``my-{IDF_TARGET_PATH_NAME}.local``
|
||||
* ``default_instance``: friendly name for your device, like ``Jhon's {IDF_TARGET_NAME} Thing``. If not set, ``hostname`` will be used.
|
||||
|
||||
Example method to start mDNS for the STA interface and set ``hostname`` and ``default_instance``:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
void start_mdns_service()
|
||||
{
|
||||
//initialize mDNS service
|
||||
esp_err_t err = mdns_init();
|
||||
if (err) {
|
||||
printf("MDNS Init failed: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
//set hostname
|
||||
mdns_hostname_set("my-{IDF_TARGET_PATH_NAME}");
|
||||
//set default instance
|
||||
mdns_instance_name_set("Jhon's {IDF_TARGET_NAME} Thing");
|
||||
}
|
||||
|
||||
mDNS Services
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
mDNS can advertise information about network services that your device offers. Each service is defined by a few properties.
|
||||
|
||||
* ``instance_name``: friendly name for your service, like ``Jhon's E{IDF_TARGET_NAME} Web Server``. If not defined, ``default_instance`` will be used.
|
||||
* ``service_type``: (required) service type, prepended with underscore. Some common types can be found `here <http://www.dns-sd.org/serviceTypes.html>`_.
|
||||
* ``proto``: (required) protocol that the service runs on, prepended with underscore. Example: ``_tcp`` or ``_udp``
|
||||
* ``port``: (required) network port that the service runs on
|
||||
* ``txt``: ``{var, val}`` array of strings, used to define properties for your service
|
||||
|
||||
Example method to add a few services and different properties::
|
||||
|
||||
void add_mdns_services()
|
||||
{
|
||||
//add our services
|
||||
mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0);
|
||||
mdns_service_add(NULL, "_arduino", "_tcp", 3232, NULL, 0);
|
||||
mdns_service_add(NULL, "_myservice", "_udp", 1234, NULL, 0);
|
||||
|
||||
//NOTE: services must be added before their properties can be set
|
||||
//use custom instance for the web server
|
||||
mdns_service_instance_name_set("_http", "_tcp", "Jhon's {IDF_TARGET_NAME} Web Server");
|
||||
|
||||
mdns_txt_item_t serviceTxtData[3] = {
|
||||
{"board","{{IDF_TARGET_PATH_NAME}}"},
|
||||
{"u","user"},
|
||||
{"p","password"}
|
||||
};
|
||||
//set txt data for service (will free and replace current data)
|
||||
mdns_service_txt_set("_http", "_tcp", serviceTxtData, 3);
|
||||
|
||||
//change service port
|
||||
mdns_service_port_set("_myservice", "_udp", 4321);
|
||||
}
|
||||
|
||||
mDNS Query
|
||||
^^^^^^^^^^
|
||||
|
||||
mDNS provides methods for browsing for services and resolving host's IP/IPv6 addresses.
|
||||
|
||||
Results for services are returned as a linked list of ``mdns_result_t`` objects.
|
||||
|
||||
Example method to resolve host IPs::
|
||||
|
||||
void resolve_mdns_host(const char * host_name)
|
||||
{
|
||||
printf("Query A: %s.local", host_name);
|
||||
|
||||
struct ip4_addr addr;
|
||||
addr.addr = 0;
|
||||
|
||||
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
|
||||
if(err){
|
||||
if(err == ESP_ERR_NOT_FOUND){
|
||||
printf("Host was not found!");
|
||||
return;
|
||||
}
|
||||
printf("Query Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
printf(IPSTR, IP2STR(&addr));
|
||||
}
|
||||
|
||||
Example method to resolve local services::
|
||||
|
||||
static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
|
||||
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
|
||||
|
||||
void mdns_print_results(mdns_result_t * results){
|
||||
mdns_result_t * r = results;
|
||||
mdns_ip_addr_t * a = NULL;
|
||||
int i = 1, t;
|
||||
while(r){
|
||||
printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol]);
|
||||
if(r->instance_name){
|
||||
printf(" PTR : %s\n", r->instance_name);
|
||||
}
|
||||
if(r->hostname){
|
||||
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
|
||||
}
|
||||
if(r->txt_count){
|
||||
printf(" TXT : [%u] ", r->txt_count);
|
||||
for(t=0; t<r->txt_count; t++){
|
||||
printf("%s=%s; ", r->txt[t].key, r->txt[t].value);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
a = r->addr;
|
||||
while(a){
|
||||
if(a->addr.type == IPADDR_TYPE_V6){
|
||||
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void find_mdns_service(const char * service_name, const char * proto)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
|
||||
|
||||
mdns_result_t * results = NULL;
|
||||
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
|
||||
if(err){
|
||||
ESP_LOGE(TAG, "Query Failed");
|
||||
return;
|
||||
}
|
||||
if(!results){
|
||||
ESP_LOGW(TAG, "No results found!");
|
||||
return;
|
||||
}
|
||||
|
||||
mdns_print_results(results);
|
||||
mdns_query_results_free(results);
|
||||
}
|
||||
|
||||
Example of using the methods above::
|
||||
|
||||
void my_app_some_method(){
|
||||
//search for {IDF_TARGET_PATH_NAME}-mdns.local
|
||||
resolve_mdns_host("{IDF_TARGET_PATH_NAME}-mdns");
|
||||
|
||||
//search for HTTP servers
|
||||
find_mdns_service("_http", "_tcp");
|
||||
//or file servers
|
||||
find_mdns_service("_smb", "_tcp"); //windows sharing
|
||||
find_mdns_service("_afpovertcp", "_tcp"); //apple sharing
|
||||
find_mdns_service("_nfs", "_tcp"); //NFS server
|
||||
find_mdns_service("_ftp", "_tcp"); //FTP server
|
||||
//or networked printer
|
||||
find_mdns_service("_printer", "_tcp");
|
||||
find_mdns_service("_ipp", "_tcp");
|
||||
}
|
||||
|
||||
|
||||
Performance Optimization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Execution Speed
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- mDNS creates a task with default low priority 1 ``CONFIG_MDNS_TASK_PRIORITY`` (If ``CONFIG_FREERTOS_UNICORE`` enabeled it pinned to CPU0 (``CONFIG_MDNS_TASK_AFFINITY``).
|
||||
Please check `Maximizing Execution Speed <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/performance/speed.html>`_ for more details.
|
||||
|
||||
Minimizing RAM Usage
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- mDNS creates a tasks with stack sizes configured by ``CONFIG_MDNS_TASK_STACK_SIZE``.
|
||||
Please check `Minimizing RAM Usage <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/performance/ram-usage.html>`_ for more details.
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
mDNS server/scanner example: :example:`<../examples>`.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/mdns.inc
|
||||
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
build-docs --target esp32 --language en
|
||||
build-docs --target esp32 --language zh_CN
|
||||
|
||||
cp -rf _build/en/esp32/html html_en
|
||||
cp -rf _build/zh_CN/esp32/html html_zh_CN
|
||||
rm -rf _build __pycache__ tee
|
||||
|
||||
# Modifes some version and target fields of index.html
|
||||
echo "<script type="text/javascript">
|
||||
window.onload =(function() {
|
||||
var myAnchor = document.getElementById('version-select');
|
||||
var mySpan = document.createElement('input');
|
||||
mySpan.setAttribute('type', 'text');
|
||||
mySpan.setAttribute('maxLength', '10');
|
||||
mySpan.value = 'latest';
|
||||
mySpan.setAttribute('disabled', true);
|
||||
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
|
||||
|
||||
var myAnchor = document.getElementById('target-select');
|
||||
var mySpan = document.createElement('input');
|
||||
mySpan.setAttribute('type', 'text');
|
||||
mySpan.setAttribute('maxLength', '10');
|
||||
mySpan.value = 'all targets';
|
||||
mySpan.setAttribute('disabled', true);
|
||||
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
|
||||
|
||||
})();
|
||||
</script>" | tee -a html_en/index.html html_zh_CN/index.html > /dev/null
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# English Language RTD & Sphinx config file
|
||||
#
|
||||
# Uses ../conf_common.py for most non-language-specific settings.
|
||||
|
||||
# Importing conf_common adds all the non-language-specific
|
||||
# parts to this conf module
|
||||
try:
|
||||
from conf_common import * # noqa: F403,F401
|
||||
except ImportError:
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
from conf_common import * # noqa: F403,F401
|
||||
|
||||
import datetime
|
||||
|
||||
current_year = datetime.datetime.now().year
|
||||
|
||||
# General information about the project.
|
||||
project = u'ESP-IDF 编程指南'
|
||||
copyright = u'2016 - {} 乐鑫信息科技(上海)股份有限公司'.format(current_year)
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'zh_CN'
|
||||
@@ -0,0 +1,189 @@
|
||||
mDNS 服务
|
||||
=========
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
概述
|
||||
----
|
||||
|
||||
mDNS 是一种组播 UDP 服务,用来提供本地网络服务和主机发现。
|
||||
|
||||
绝大多数的操作系统默认都会安装 mDNS 服务,或者提供单独的安装包。``Mac OS`` 默认会安装名为 ``Bonjour`` 的服务(该服务基于 mDNS),此外 Apple 还发布了适用于 Windows 系统的安装程序,可以在 `官方支持 <https://support.apple.com/downloads/bonjour%2520for%2520windows>`_ 找到。在 ``Linux`` 上,mDNS 服务由 `avahi <https://github.com/lathiat/avahi>`_ 提供,通常也会被默认安装。
|
||||
|
||||
mDNS 属性
|
||||
^^^^^^^^^
|
||||
|
||||
* ``hostname``:设备会去响应的主机名,如果没有设置,会根据设备的网络接口名定义 ``hostname`` 。例如,``my-{IDF_TARGET_PATH_NAME}`` 会被解析为 ``my-{IDF_TARGET_PATH_NAME}.local``。
|
||||
* ``default_instance``:默认实例名(即易记的设备名),例如 ``Jhon's {IDF_TARGET_NAME} Thing``。如果没有设置,将会使用 ``hostname``。
|
||||
|
||||
以下为 STA 接口启动 mDNS 服务并设置 ``hostname`` 和 ``default_instance`` 的示例方法:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
void start_mdns_service()
|
||||
{
|
||||
// 初始化 mDNS 服务
|
||||
esp_err_t err = mdns_init();
|
||||
if (err) {
|
||||
printf("MDNS Init failed: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置 hostname
|
||||
mdns_hostname_set("my-{IDF_TARGET_PATH_NAME}");
|
||||
// 设置默认实例
|
||||
mdns_instance_name_set("Jhon's {IDF_TARGET_NAME} Thing");
|
||||
}
|
||||
|
||||
mDNS 服务
|
||||
^^^^^^^^^
|
||||
|
||||
mDNS 可以广播设备能够提供的网络服务的相关信息,每个服务会由以下属性构成。
|
||||
|
||||
* ``instance_name``:实例名(即易记的服务名),例如 ``Jhon's {IDF_TARGET_NAME} Web Server``。如果没有定义,会使用 ``default_instance``。
|
||||
* ``service_type``:(必需)服务类型,以下划线为前缀,`这里 <http://www.dns-sd.org/serviceTypes.html>`_ 列出了常见的类型。
|
||||
* ``proto``:(必需)服务运行所依赖的协议,以下划线为前缀,例如 ``_tcp`` 或者 ``_udp``。
|
||||
* ``port``:(必需)服务运行所用的端口号。
|
||||
* ``txt``:形如 ``{var, val}`` 的字符串数组,用于定义服务的属性。
|
||||
|
||||
添加一些服务和不同属性的示例方法::
|
||||
|
||||
void add_mdns_services()
|
||||
{
|
||||
// 添加服务
|
||||
mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0);
|
||||
mdns_service_add(NULL, "_arduino", "_tcp", 3232, NULL, 0);
|
||||
mdns_service_add(NULL, "_myservice", "_udp", 1234, NULL, 0);
|
||||
|
||||
// 注意:必须先添加服务,然后才能设置其属性
|
||||
// web 服务器使用自定义的实例名
|
||||
mdns_service_instance_name_set("_http", "_tcp", "Jhon's {IDF_TARGET_NAME} Web Server");
|
||||
|
||||
mdns_txt_item_t serviceTxtData[3] = {
|
||||
{"board","{IDF_TARGET_PATH_NAME}"},
|
||||
{"u","user"},
|
||||
{"p","password"}
|
||||
};
|
||||
// 设置服务的文本数据(会释放并替换当前数据)
|
||||
mdns_service_txt_set("_http", "_tcp", serviceTxtData, 3);
|
||||
|
||||
// 修改服务端口号
|
||||
mdns_service_port_set("_myservice", "_udp", 4321);
|
||||
}
|
||||
|
||||
mDNS 查询
|
||||
^^^^^^^^^
|
||||
|
||||
mDNS 提供查询服务和解析主机 IP/IPv6 地址的方法。
|
||||
|
||||
服务查询的结果会作为 ``mdns_result_t`` 类型对象的链表返回。
|
||||
|
||||
解析主机 IP 地址的示例方法::
|
||||
|
||||
void resolve_mdns_host(const char * host_name)
|
||||
{
|
||||
printf("Query A: %s.local", host_name);
|
||||
|
||||
struct ip4_addr addr;
|
||||
addr.addr = 0;
|
||||
|
||||
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
|
||||
if(err){
|
||||
if(err == ESP_ERR_NOT_FOUND){
|
||||
printf("Host was not found!");
|
||||
return;
|
||||
}
|
||||
printf("Query Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
printf(IPSTR, IP2STR(&addr));
|
||||
}
|
||||
|
||||
解析本地服务的示例方法::
|
||||
|
||||
static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
|
||||
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
|
||||
|
||||
void mdns_print_results(mdns_result_t * results){
|
||||
mdns_result_t * r = results;
|
||||
mdns_ip_addr_t * a = NULL;
|
||||
int i = 1, t;
|
||||
while(r){
|
||||
printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol]);
|
||||
if(r->instance_name){
|
||||
printf(" PTR : %s\n", r->instance_name);
|
||||
}
|
||||
if(r->hostname){
|
||||
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
|
||||
}
|
||||
if(r->txt_count){
|
||||
printf(" TXT : [%u] ", r->txt_count);
|
||||
for(t=0; t<r->txt_count; t++){
|
||||
printf("%s=%s; ", r->txt[t].key, r->txt[t].value);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
a = r->addr;
|
||||
while(a){
|
||||
if(a->addr.type == IPADDR_TYPE_V6){
|
||||
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void find_mdns_service(const char * service_name, const char * proto)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
|
||||
|
||||
mdns_result_t * results = NULL;
|
||||
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
|
||||
if(err){
|
||||
ESP_LOGE(TAG, "Query Failed");
|
||||
return;
|
||||
}
|
||||
if(!results){
|
||||
ESP_LOGW(TAG, "No results found!");
|
||||
return;
|
||||
}
|
||||
|
||||
mdns_print_results(results);
|
||||
mdns_query_results_free(results);
|
||||
}
|
||||
|
||||
使用上述方法的示例::
|
||||
|
||||
void my_app_some_method(){
|
||||
// 搜索 {IDF_TARGET_PATH_NAME}-mdns.local
|
||||
resolve_mdns_host("{IDF_TARGET_PATH_NAME}-mdns");
|
||||
|
||||
// 搜索 HTTP 服务器
|
||||
find_mdns_service("_http", "_tcp");
|
||||
// 或者搜索文件服务器
|
||||
find_mdns_service("_smb", "_tcp"); // Windows 系统的共享服务
|
||||
find_mdns_service("_afpovertcp", "_tcp"); // Apple AFP 文件共享服务
|
||||
find_mdns_service("_nfs", "_tcp"); // NFS 服务器
|
||||
find_mdns_service("_ftp", "_tcp"); // FTP 服务器
|
||||
// 或者网络打印机
|
||||
find_mdns_service("_printer", "_tcp");
|
||||
find_mdns_service("_ipp", "_tcp");
|
||||
}
|
||||
|
||||
应用示例
|
||||
--------
|
||||
|
||||
有关 mDNS 服务器和查询器的应用示例请参考 :example:`<../examples>`。
|
||||
|
||||
API 参考
|
||||
--------
|
||||
|
||||
.. include-build-file:: inc/mdns.inc
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../..")
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "../../../common_components/protocol_examples_common")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mdns_test)
|
||||
@@ -0,0 +1,91 @@
|
||||
# mDNS example
|
||||
|
||||
Shows how to use mDNS to advertise lookup services and hosts
|
||||
|
||||
## Example workflow
|
||||
|
||||
- mDNS is initialized with host name and instance name defined through the project configuration and `_http._tcp` service is added to be advertised
|
||||
- A delegated host `esp32-delegated._local` is added and another `_http._tcp` service is added for this host.
|
||||
- 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
|
||||
|
||||
### Configure the project
|
||||
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu
|
||||
* 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)
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mdns_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,55 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||
|
||||
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_PUBLISH_DELEGATE_HOST
|
||||
bool "Publish a delegated host"
|
||||
help
|
||||
Enable publishing a delegated host other than ESP32.
|
||||
The example will also add a mock service for this host.
|
||||
|
||||
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
|
||||
|
||||
config MDNS_BUTTON_GPIO
|
||||
int "Button GPIO to trigger querries"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||
default 0
|
||||
help
|
||||
Set the GPIO number used as mDNS test button
|
||||
|
||||
config MDNS_ADD_CUSTOM_NETIF
|
||||
bool "Add user netif to mdns service"
|
||||
default n
|
||||
help
|
||||
If enabled, we try to add a custom netif to mdns service.
|
||||
Note that for using with common connection example code, we have to disable
|
||||
all predefined interfaces in mdns component setup (since we're adding one
|
||||
of the default interfaces)
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,343 @@
|
||||
/* MDNS-SD Query and advertise Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "mdns.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "netdb.h"
|
||||
|
||||
|
||||
#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE
|
||||
#define EXAMPLE_BUTTON_GPIO CONFIG_MDNS_BUTTON_GPIO
|
||||
|
||||
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();
|
||||
|
||||
//initialize mDNS
|
||||
ESP_ERROR_CHECK( mdns_init() );
|
||||
//set mDNS hostname (required if you want to advertise services)
|
||||
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) );
|
||||
|
||||
//structure with TXT records
|
||||
mdns_txt_item_t serviceTxtData[3] = {
|
||||
{"board", "esp32"},
|
||||
{"u", "user"},
|
||||
{"p", "password"}
|
||||
};
|
||||
|
||||
//initialize service
|
||||
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) );
|
||||
ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") );
|
||||
#if CONFIG_MDNS_MULTIPLE_INSTANCE
|
||||
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer1", "_http", "_tcp", 80, NULL, 0) );
|
||||
#endif
|
||||
|
||||
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
|
||||
char *delegated_hostname;
|
||||
if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
mdns_ip_addr_t addr4, addr6;
|
||||
esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4);
|
||||
addr4.addr.type = ESP_IPADDR_TYPE_V4;
|
||||
esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6);
|
||||
addr6.addr.type = ESP_IPADDR_TYPE_V6;
|
||||
addr4.next = &addr6;
|
||||
addr6.next = NULL;
|
||||
ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) );
|
||||
ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3) );
|
||||
free(delegated_hostname);
|
||||
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
|
||||
|
||||
//add another TXT item
|
||||
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") );
|
||||
//change TXT item value
|
||||
ESP_ERROR_CHECK( mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")) );
|
||||
free(hostname);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
mdns_result_t *r = results;
|
||||
mdns_ip_addr_t *a = NULL;
|
||||
int i = 1, t;
|
||||
while (r) {
|
||||
printf("%d: Interface: %s, Type: %s, TTL: %u\n", i++, esp_netif_get_ifkey(r->esp_netif), ip_protocol_str[r->ip_protocol],
|
||||
r->ttl);
|
||||
if (r->instance_name) {
|
||||
printf(" PTR : %s.%s.%s\n", r->instance_name, r->service_type, r->proto);
|
||||
}
|
||||
if (r->hostname) {
|
||||
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
|
||||
}
|
||||
if (r->txt_count) {
|
||||
printf(" TXT : [%zu] ", r->txt_count);
|
||||
for (t = 0; t < r->txt_count; t++) {
|
||||
printf("%s=%s(%d); ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL", r->txt_value_len[t]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
a = r->addr;
|
||||
while (a) {
|
||||
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
|
||||
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void query_mdns_service(const char * service_name, const char * proto)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
|
||||
|
||||
mdns_result_t * results = NULL;
|
||||
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
|
||||
if(err){
|
||||
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
if(!results){
|
||||
ESP_LOGW(TAG, "No results found!");
|
||||
return;
|
||||
}
|
||||
|
||||
mdns_print_results(results);
|
||||
mdns_query_results_free(results);
|
||||
}
|
||||
|
||||
static bool check_and_print_result(mdns_search_once_t *search)
|
||||
{
|
||||
// Check if any result is available
|
||||
mdns_result_t * result = NULL;
|
||||
if (!mdns_query_async_get_results(search, 0, &result, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!result) { // search timeout, but no result
|
||||
return true;
|
||||
}
|
||||
|
||||
// If yes, print the result
|
||||
mdns_ip_addr_t * a = result->addr;
|
||||
while (a) {
|
||||
if(a->addr.type == ESP_IPADDR_TYPE_V6){
|
||||
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
// and free the result
|
||||
mdns_query_results_free(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void query_mdns_hosts_async(const char * host_name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name);
|
||||
|
||||
mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1, NULL);
|
||||
mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1, NULL);
|
||||
while (s_a || s_aaaa) {
|
||||
if (s_a && check_and_print_result(s_a)) {
|
||||
ESP_LOGI(TAG, "Query A %s.local finished", host_name);
|
||||
mdns_query_async_delete(s_a);
|
||||
s_a = NULL;
|
||||
}
|
||||
if (s_aaaa && check_and_print_result(s_aaaa)) {
|
||||
ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name);
|
||||
mdns_query_async_delete(s_aaaa);
|
||||
s_aaaa = NULL;
|
||||
}
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
static void query_mdns_host(const char * host_name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query A: %s.local", host_name);
|
||||
|
||||
struct esp_ip4_addr addr;
|
||||
addr.addr = 0;
|
||||
|
||||
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
|
||||
if(err){
|
||||
if(err == ESP_ERR_NOT_FOUND){
|
||||
ESP_LOGW(TAG, "%s: Host was not found!", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
|
||||
}
|
||||
|
||||
static void initialise_button(void)
|
||||
{
|
||||
gpio_config_t io_conf = {0};
|
||||
io_conf.intr_type = GPIO_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;
|
||||
gpio_config(&io_conf);
|
||||
}
|
||||
|
||||
static void check_button(void)
|
||||
{
|
||||
static bool old_level = true;
|
||||
bool new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO);
|
||||
if (!new_level && old_level) {
|
||||
query_mdns_hosts_async("esp32-mdns");
|
||||
query_mdns_host("esp32");
|
||||
query_mdns_service("_arduino", "_tcp");
|
||||
query_mdns_service("_http", "_tcp");
|
||||
query_mdns_service("_printer", "_tcp");
|
||||
query_mdns_service("_ipp", "_tcp");
|
||||
query_mdns_service("_afpovertcp", "_tcp");
|
||||
query_mdns_service("_smb", "_tcp");
|
||||
query_mdns_service("_ftp", "_tcp");
|
||||
query_mdns_service("_nfs", "_tcp");
|
||||
}
|
||||
old_level = new_level;
|
||||
}
|
||||
|
||||
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) {
|
||||
check_button();
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_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());
|
||||
|
||||
#if defined(CONFIG_MDNS_ADD_CUSTOM_NETIF) && !defined(CONFIG_MDNS_PREDEF_NETIF_STA) && !defined(CONFIG_MDNS_PREDEF_NETIF_ETH)
|
||||
/* Demonstration of adding a custom netif to mdns service, but we're adding the default example one,
|
||||
* so we must disable all predefined interfaces (PREDEF_NETIF_STA, AP and ETH) first
|
||||
*/
|
||||
ESP_ERROR_CHECK(mdns_register_netif(EXAMPLE_INTERFACE));
|
||||
/* It is not enough to just register the interface, we have to enable is manually.
|
||||
* This is typically performed in "GOT_IP" event handler, but we call it here directly
|
||||
* since the `EXAMPLE_INTERFACE` netif is connected already, to keep the example simple.
|
||||
*/
|
||||
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ENABLE_IP4));
|
||||
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ANNOUNCE_IP4));
|
||||
#endif
|
||||
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(void)
|
||||
{
|
||||
#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
|
||||
}
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,166 @@
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import socket
|
||||
import struct
|
||||
import subprocess
|
||||
import time
|
||||
from threading import Event, Thread
|
||||
|
||||
import dpkt
|
||||
import dpkt.dns
|
||||
import ttfw_idf
|
||||
from tiny_test_fw import DUT
|
||||
from tiny_test_fw.Utility import console_log
|
||||
|
||||
|
||||
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'
|
||||
console_log('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)
|
||||
console_log('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, events):
|
||||
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'
|
||||
QUERY_TIMEOUT = 0.2
|
||||
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.setblocking(False)
|
||||
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)
|
||||
last_query_timepoint = time.time()
|
||||
while not events['stop'].is_set():
|
||||
try:
|
||||
current_time = time.time()
|
||||
if current_time - last_query_timepoint > QUERY_TIMEOUT:
|
||||
last_query_timepoint = current_time
|
||||
if not events['esp_answered'].is_set():
|
||||
sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP, UDP_PORT))
|
||||
if not events['esp_delegated_answered'].is_set():
|
||||
sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'), (MCAST_GRP, UDP_PORT))
|
||||
timeout = max(0, QUERY_TIMEOUT - (current_time - last_query_timepoint))
|
||||
read_socks, _, _ = select.select([sock], [], [], timeout)
|
||||
if not read_socks:
|
||||
continue
|
||||
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 == TESTER_NAME:
|
||||
console_log('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:
|
||||
console_log('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:
|
||||
console_log('Received answer from {}'.format(dns.an[0].name))
|
||||
if dns.an[0].name == esp_host + u'.local':
|
||||
console_log('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
|
||||
events['esp_answered'].set()
|
||||
if dns.an[0].name == esp_host + u'-delegated.local':
|
||||
console_log('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__()))
|
||||
events['esp_delegated_answered'].set()
|
||||
except socket.timeout:
|
||||
break
|
||||
except dpkt.UnpackError:
|
||||
continue
|
||||
|
||||
|
||||
def test_examples_protocol_mdns(env, config):
|
||||
"""
|
||||
steps: |
|
||||
1. obtain IP address + 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', dut_class=ttfw_idf.ESP32DUT, app_config_name=config)
|
||||
# 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))
|
||||
# 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)[0]
|
||||
|
||||
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))
|
||||
try:
|
||||
ip_address = dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30)[0]
|
||||
console_log('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
try:
|
||||
# 3. check the mdns name is accessible
|
||||
mdns_responder.start()
|
||||
if not mdns_server_events['esp_answered'].wait(timeout=30):
|
||||
raise ValueError('Test has failed: did not receive mdns answer within timeout')
|
||||
if not mdns_server_events['esp_delegated_answered'].wait(timeout=30):
|
||||
raise ValueError('Test has failed: did not receive mdns answer for delegated host within timeout')
|
||||
# 4. check DUT output if mdns advertized host is resolved
|
||||
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)
|
||||
# 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)])
|
||||
console_log('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output))
|
||||
if not ip_address.encode('utf-8') in dig_output:
|
||||
raise ValueError('Test has failed: Incorrectly resolved DUT hostname using dig'
|
||||
"Output should've contained DUT's IP address:{}".format(ip_address))
|
||||
finally:
|
||||
mdns_server_events['stop'].set()
|
||||
mdns_responder.join()
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
|
||||
def test_examples_protocol_mdns_default(env, _):
|
||||
test_examples_protocol_mdns(env, 'eth_def')
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
|
||||
def test_examples_protocol_mdns_socket(env, _):
|
||||
test_examples_protocol_mdns(env, 'eth_socket')
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
|
||||
def test_examples_protocol_mdns_custom_netif(env, _):
|
||||
test_examples_protocol_mdns(env, 'eth_custom_netif')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_mdns_default()
|
||||
@@ -0,0 +1,19 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
|
||||
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
|
||||
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
|
||||
CONFIG_MDNS_PREDEF_NETIF_STA=n
|
||||
CONFIG_MDNS_PREDEF_NETIF_AP=n
|
||||
CONFIG_MDNS_PREDEF_NETIF_ETH=n
|
||||
CONFIG_MDNS_ADD_CUSTOM_NETIF=y
|
||||
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
CONFIG_MDNS_BUTTON_GPIO=32
|
||||
@@ -0,0 +1,15 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
|
||||
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
|
||||
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
|
||||
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
CONFIG_MDNS_BUTTON_GPIO=32
|
||||
@@ -0,0 +1,16 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
|
||||
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
|
||||
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
|
||||
CONFIG_MDNS_NETWORKING_SOCKET=y
|
||||
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
CONFIG_MDNS_BUTTON_GPIO=32
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+3
-3
@@ -1,10 +1,10 @@
|
||||
TEST_NAME=test
|
||||
FUZZ=afl-fuzz
|
||||
COMPONENTS_DIR=../..
|
||||
COMPONENTS_DIR=$(IDF_PATH)/components
|
||||
COMPILER_ICLUDE_DIR=$(shell echo `which xtensa-esp32-elf-gcc | xargs dirname | xargs dirname`/xtensa-esp32-elf)
|
||||
|
||||
CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversion -Wno-macro-redefined -Wno-int-to-void-pointer-cast -DHOOK_MALLOC_FAILED -DESP_EVENT_H_ -D__ESP_LOG_H__ \
|
||||
-I. -I.. -I../include -I../private_include -I ./build/config \
|
||||
-I. -I../.. -I../../include -I../../private_include -I ./build/config \
|
||||
-I$(COMPONENTS_DIR) \
|
||||
-I$(COMPONENTS_DIR)/driver/include \
|
||||
-I$(COMPONENTS_DIR)/esp_common/include \
|
||||
@@ -68,7 +68,7 @@ all: $(TEST_NAME)
|
||||
@echo "[CC] $<"
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
mdns.o: ../mdns.c
|
||||
mdns.o: ../../mdns.c
|
||||
@echo "[CC] $<"
|
||||
@$(CC) $(CFLAGS) -include mdns_mock.h $(MDNS_C_DEPENDENCY_INJECTION) -c $< -o $@
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,11 @@
|
||||
# The following four lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS "../../../../common_components/protocol_examples_common")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(mdns)
|
||||
@@ -0,0 +1,49 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- |
|
||||
|
||||
# mDNS test project
|
||||
|
||||
Main purpose of this application is to test the mDNS library to correctly advertise, lookup services and hosts.
|
||||
The "app_test.py" logically separated from two sets of test cases 1. "ESP32 board sends queries -> Host sends answers" and 2. "Host sends queries" -> "ESP32 board sends answers".
|
||||
Two first sets of test cases are starting by 'test_query_' and the second ones by 'start_case'.
|
||||
|
||||
## Runtime settings
|
||||
|
||||
1. For first sets of test cases python creates and sends dns queries using "dpkt" library
|
||||
2. For the second sets of test cases this app waits for user input to provide test case(e.g. CONFIG_TEST_QUERY_HOST, CONFIG_TEST_QUERY_HOST_ASYNC or CONFIG_TEST_QUERY_SERVICE)
|
||||
In order to run both of them just needed to set up the project and run by 'python app_test.py'
|
||||
|
||||
## Test app workflow
|
||||
|
||||
- mDNS is initialized with hostname and instance name defined through the project configuration and `_http._tcp` service is added to be advertised
|
||||
- A delegated host `esp32-delegated._local` is added and another `_http._tcp` service is added for this host.
|
||||
- WiFi STA is started and tries to connect to the access point defined through the project configuration
|
||||
|
||||
### Configure the project
|
||||
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu.
|
||||
|
||||
* Set `mDNS Hostname` as host name prefix for the device and its instance name in `mDNS Instance Name`
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run the monitor tool to view the 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
|
||||
- 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.
|
||||
|
||||
## Hardware Required
|
||||
This test-app can be executed on any ESP32 board, the only required interface is WiFi and connection to a local network and tls server.
|
||||
@@ -0,0 +1,290 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import re
|
||||
import select
|
||||
import socket
|
||||
import struct
|
||||
import time
|
||||
from threading import Event, Thread
|
||||
|
||||
import dpkt
|
||||
import dpkt.dns
|
||||
import ttfw_idf
|
||||
from tiny_test_fw.Utility import console_log
|
||||
|
||||
UDP_PORT = 5353
|
||||
MCAST_GRP = '224.0.0.251'
|
||||
|
||||
# This service is created from esp board startup
|
||||
SERVICE_NAME = u'ESP32-WebServer._http._tcp.local'
|
||||
SUB_SERVICE_NAME = u'_server._sub._http._tcp.local'
|
||||
|
||||
# This host name answer sent by host, when there is query from board
|
||||
HOST_NAME = u'tinytester.local'
|
||||
|
||||
# This servce answer sent by host, when there is query from board
|
||||
MDNS_HOST_SERVICE = u'ESP32._http._tcp.local'
|
||||
|
||||
# Number of retries to receive mdns answear
|
||||
RETRY_COUNT = 10
|
||||
|
||||
stop_mdns_listener = Event()
|
||||
start_mdns_listener = Event()
|
||||
esp_service_answered = Event()
|
||||
esp_sub_service_answered = Event()
|
||||
esp_host_answered = Event()
|
||||
esp_delegated_host_answered = Event()
|
||||
|
||||
|
||||
# Get query of ESP32-WebServer._http._tcp.local service
|
||||
def get_mdns_service_query(service): # type:(str) -> dpkt.dns.Msg
|
||||
dns = dpkt.dns.DNS()
|
||||
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_SRV
|
||||
arr.name = service
|
||||
arr.target = socket.inet_aton('127.0.0.1')
|
||||
arr.srvname = service
|
||||
dns.qd.append(arr)
|
||||
console_log('Created mdns service query: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
# Get query of _server_.sub._http._tcp.local sub service
|
||||
def get_mdns_sub_service_query(sub_service): # type:(str) -> dpkt.dns.Msg
|
||||
dns = dpkt.dns.DNS()
|
||||
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_PTR
|
||||
arr.name = sub_service
|
||||
arr.target = socket.inet_aton('127.0.0.1')
|
||||
arr.ptrname = sub_service
|
||||
dns.qd.append(arr)
|
||||
console_log('Created mdns subtype service query: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
# Get query for host resolution
|
||||
def get_dns_query_for_esp(esp_host): # type:(str) -> dpkt.dns.Msg
|
||||
dns = dpkt.dns.DNS()
|
||||
arr = dpkt.dns.DNS.RR()
|
||||
arr.cls = dpkt.dns.DNS_IN
|
||||
arr.name = esp_host + u'.local'
|
||||
dns.qd.append(arr)
|
||||
console_log('Created query for esp host: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
# Get mdns answer for host resoloution
|
||||
def get_dns_answer_to_mdns(tester_host): # type:(str) -> dpkt.dns.Msg
|
||||
dns = dpkt.dns.DNS()
|
||||
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)
|
||||
console_log('Created answer to mdns query: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
# Get mdns answer for service query
|
||||
def get_dns_answer_to_service_query(mdns_service): # type:(str) -> dpkt.dns.Msg
|
||||
dns = dpkt.dns.DNS()
|
||||
dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA
|
||||
dns.rcode = dpkt.dns.DNS_RCODE_NOERR
|
||||
arr = dpkt.dns.DNS.RR()
|
||||
arr.name = mdns_service
|
||||
arr.cls = dpkt.dns.DNS_IN
|
||||
arr.type = dpkt.dns.DNS_SRV
|
||||
arr.priority = 0
|
||||
arr.weight = 0
|
||||
arr.port = 100
|
||||
arr.srvname = mdns_service
|
||||
arr.ip = socket.inet_aton('127.0.0.1')
|
||||
dns. an.append(arr)
|
||||
console_log('Created answer to mdns query: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
def mdns_listener(esp_host): # type:(str) -> None
|
||||
print('mdns_listener thread started')
|
||||
|
||||
UDP_IP = '0.0.0.0'
|
||||
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.setblocking(False)
|
||||
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)
|
||||
last_query_timepoint = time.time()
|
||||
QUERY_TIMEOUT = 0.2
|
||||
while not stop_mdns_listener.is_set():
|
||||
try:
|
||||
start_mdns_listener.set()
|
||||
current_time = time.time()
|
||||
if current_time - last_query_timepoint > QUERY_TIMEOUT:
|
||||
last_query_timepoint = current_time
|
||||
timeout = max(0, QUERY_TIMEOUT - (current_time - last_query_timepoint))
|
||||
read_socks, _, _ = select.select([sock], [], [], timeout)
|
||||
if not read_socks:
|
||||
continue
|
||||
data, _ = sock.recvfrom(1024)
|
||||
dns = dpkt.dns.DNS(data)
|
||||
# Receives queries from esp board and sends answers
|
||||
if len(dns.qd) > 0:
|
||||
if dns.qd[0].name == HOST_NAME:
|
||||
console_log('Received query: {} '.format(dns.__repr__()))
|
||||
sock.sendto(get_dns_answer_to_mdns(HOST_NAME), (MCAST_GRP,UDP_PORT))
|
||||
if dns.qd[0].name == HOST_NAME:
|
||||
console_log('Received query: {} '.format(dns.__repr__()))
|
||||
sock.sendto(get_dns_answer_to_mdns(HOST_NAME), (MCAST_GRP,UDP_PORT))
|
||||
if dns.qd[0].name == MDNS_HOST_SERVICE:
|
||||
print(dns.qd[0].name)
|
||||
console_log('Received query: {} '.format(dns.__repr__()))
|
||||
sock.sendto(get_dns_answer_to_service_query(MDNS_HOST_SERVICE), (MCAST_GRP,UDP_PORT))
|
||||
# Receives answers from esp board and sets event flags for python test cases
|
||||
if len(dns.an) == 1:
|
||||
if dns.an[0].name.startswith(SERVICE_NAME):
|
||||
console_log('Received answer to service query: {}'.format(dns.__repr__()))
|
||||
esp_service_answered.set()
|
||||
if len(dns.an) > 1:
|
||||
if dns.an[1].name.startswith(SUB_SERVICE_NAME):
|
||||
console_log('Received answer for sub service query: {}'.format(dns.__repr__()))
|
||||
esp_sub_service_answered.set()
|
||||
if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A:
|
||||
if dns.an[0].name == esp_host + u'.local':
|
||||
console_log('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
|
||||
esp_host_answered.set()
|
||||
if dns.an[0].name == esp_host + u'-delegated.local':
|
||||
console_log('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__()))
|
||||
esp_delegated_host_answered.set()
|
||||
except socket.timeout:
|
||||
break
|
||||
except dpkt.UnpackError:
|
||||
continue
|
||||
|
||||
|
||||
def create_socket(): # type:() -> socket.socket
|
||||
UDP_IP = '0.0.0.0'
|
||||
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.setblocking(False)
|
||||
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)
|
||||
return sock
|
||||
|
||||
|
||||
def test_query_dns_http_service(service): # type: (str) -> None
|
||||
print('SRV: Query {}'.format(service))
|
||||
sock = create_socket()
|
||||
packet = get_mdns_service_query(service)
|
||||
for _ in range(RETRY_COUNT):
|
||||
if esp_service_answered.wait(timeout=25):
|
||||
break
|
||||
sock.sendto(packet, (MCAST_GRP,UDP_PORT))
|
||||
else:
|
||||
raise RuntimeError('Test has failed: did not receive mdns answer within timeout')
|
||||
|
||||
|
||||
def test_query_dns_sub_service(sub_service): # type: (str) -> None
|
||||
print('PTR: Query {}'.format(sub_service))
|
||||
sock = create_socket()
|
||||
packet = get_mdns_sub_service_query(sub_service)
|
||||
for _ in range(RETRY_COUNT):
|
||||
if esp_sub_service_answered.wait(timeout=25):
|
||||
break
|
||||
sock.sendto(packet, (MCAST_GRP,UDP_PORT))
|
||||
else:
|
||||
raise RuntimeError('Test has failed: did not receive mdns answer within timeout')
|
||||
|
||||
|
||||
def test_query_dns_host(esp_host): # type: (str) -> None
|
||||
print('A: {}'.format(esp_host))
|
||||
sock = create_socket()
|
||||
packet = get_dns_query_for_esp(esp_host)
|
||||
for _ in range(RETRY_COUNT):
|
||||
if esp_host_answered.wait(timeout=25):
|
||||
break
|
||||
sock.sendto(packet, (MCAST_GRP,UDP_PORT))
|
||||
else:
|
||||
raise RuntimeError('Test has failed: did not receive mdns answer within timeout')
|
||||
|
||||
|
||||
def test_query_dns_host_delegated(esp_host): # type: (str) -> None
|
||||
print('A: {}'.format(esp_host))
|
||||
sock = create_socket()
|
||||
packet = get_dns_query_for_esp(esp_host + '-delegated')
|
||||
for _ in range(RETRY_COUNT):
|
||||
if esp_delegated_host_answered.wait(timeout=25):
|
||||
break
|
||||
sock.sendto(packet, (MCAST_GRP,UDP_PORT))
|
||||
else:
|
||||
raise RuntimeError('Test has failed: did not receive mdns answer within timeout')
|
||||
|
||||
|
||||
@ttfw_idf.idf_custom_test(env_tag='Example_WIFI', group='test-apps')
|
||||
def test_app_esp_mdns(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
|
||||
dut1 = env.get_dut('mdns', 'tools/test_apps/protocols/mdns', dut_class=ttfw_idf.ESP32DUT)
|
||||
|
||||
# 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)[0]
|
||||
|
||||
esp_ip = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
print('Got IP={}'.format(esp_ip[0]))
|
||||
|
||||
mdns_responder = Thread(target=mdns_listener, args=(str(specific_host),))
|
||||
|
||||
def start_case(case, desc, result): # type: (str, str, str) -> None
|
||||
print('Starting {}: {}'.format(case, desc))
|
||||
dut1.write(case)
|
||||
dut1.expect(re.compile(result), timeout=30)
|
||||
|
||||
try:
|
||||
# start dns listener thread
|
||||
mdns_responder.start()
|
||||
|
||||
# wait untill mdns listener thred started
|
||||
if not start_mdns_listener.wait(timeout=5):
|
||||
raise ValueError('Test has failed: mdns listener thread did not start')
|
||||
|
||||
# query dns service from host, answer should be received from esp board
|
||||
test_query_dns_http_service(SERVICE_NAME)
|
||||
|
||||
# query dns sub-service from host, answer should be received from esp board
|
||||
test_query_dns_sub_service(SUB_SERVICE_NAME)
|
||||
|
||||
# query dns host name, answer should be received from esp board
|
||||
test_query_dns_host(specific_host)
|
||||
|
||||
# query dns host name delegated, answer should be received from esp board
|
||||
test_query_dns_host_delegated(specific_host)
|
||||
|
||||
# query service from esp board, answer should be received from host
|
||||
start_case('CONFIG_TEST_QUERY_SERVICE', 'Query SRV ESP32._http._tcp.local', 'SRV:ESP32')
|
||||
|
||||
# query dns-host from esp board, answer should be received from host
|
||||
start_case('CONFIG_TEST_QUERY_HOST', 'Query tinytester.local', 'tinytester.local resolved to: 127.0.0.1')
|
||||
|
||||
# query dns-host aynchrounusely from esp board, answer should be received from host
|
||||
start_case('CONFIG_TEST_QUERY_HOST_ASYNC', 'Query tinytester.local async', 'Async query resolved to A:127.0.0.1')
|
||||
|
||||
finally:
|
||||
stop_mdns_listener.set()
|
||||
mdns_responder.join()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_app_esp_mdns()
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.c" "mdns_test.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,28 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config TEST_MDNS_HOSTNAME
|
||||
string "mDNS Hostname"
|
||||
default "esp32-mdns"
|
||||
help
|
||||
mDNS Hostname for example to use
|
||||
|
||||
config TEST_MDNS_INSTANCE
|
||||
string "mDNS Instance Name"
|
||||
default "ESP32 with mDNS"
|
||||
help
|
||||
mDNS Instance Name for example to use
|
||||
|
||||
config TEST_MDNS_PUBLISH_DELEGATE_HOST
|
||||
bool "Publish a delegated host"
|
||||
help
|
||||
Enable publishing a delegated host other than ESP32.
|
||||
The example will also add a mock service for this host.
|
||||
|
||||
config TEST_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
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_mac.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_log.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "mdns.h"
|
||||
|
||||
static const char *TAG = "MDNS_TEST";
|
||||
void mdns_test(char *line);
|
||||
|
||||
static void get_string(char *line, size_t size)
|
||||
{
|
||||
int count = 0;
|
||||
while (count < size) {
|
||||
int c = fgetc(stdin);
|
||||
if (c == '\n') {
|
||||
line[count] = '\0';
|
||||
break;
|
||||
} else if (c > 0 && c < 127) {
|
||||
line[count] = c;
|
||||
++count;
|
||||
}
|
||||
vTaskDelay(20 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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(void)
|
||||
{
|
||||
#ifndef CONFIG_TEST_MDNS_ADD_MAC_TO_HOSTNAME
|
||||
return strdup(CONFIG_TEST_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_TEST_MDNS_HOSTNAME, mac[3], mac[4], mac[5])) {
|
||||
abort();
|
||||
}
|
||||
return hostname;
|
||||
#endif
|
||||
}
|
||||
|
||||
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(hostname) );
|
||||
|
||||
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
|
||||
//set default mDNS instance name
|
||||
ESP_ERROR_CHECK( mdns_instance_name_set(CONFIG_TEST_MDNS_INSTANCE) );
|
||||
|
||||
//initialize service
|
||||
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, NULL, 0) );
|
||||
|
||||
#if CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST
|
||||
char *delegated_hostname;
|
||||
if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
mdns_ip_addr_t addr4, addr6;
|
||||
esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4);
|
||||
addr4.addr.type = ESP_IPADDR_TYPE_V4;
|
||||
esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6);
|
||||
addr6.addr.type = ESP_IPADDR_TYPE_V6;
|
||||
addr4.next = &addr6;
|
||||
addr6.next = NULL;
|
||||
ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) );
|
||||
ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, NULL, 0) );
|
||||
free(delegated_hostname);
|
||||
#endif // CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST
|
||||
|
||||
ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") );
|
||||
|
||||
free(hostname);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
|
||||
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
|
||||
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_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());
|
||||
|
||||
while (1) {
|
||||
char line[256];
|
||||
|
||||
get_string(line, sizeof(line));
|
||||
mdns_test(line);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "mdns.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
static const char * TAG = "MDNS_TEST_APP";
|
||||
static const int RETRY_COUNT = 10;
|
||||
|
||||
static void mdns_print_results(mdns_result_t *results)
|
||||
{
|
||||
mdns_result_t *r = results;
|
||||
mdns_ip_addr_t *a = NULL;
|
||||
int t;
|
||||
while (r) {
|
||||
if (r->instance_name) {
|
||||
printf("PTR:%s.%s.%s\n", r->instance_name, r->service_type, r->proto);
|
||||
}
|
||||
if (r->hostname) {
|
||||
printf("SRV:%s.local:%u\n", r->hostname, r->port);
|
||||
}
|
||||
if (r->txt_count) {
|
||||
printf("TXT:[%zu] ", r->txt_count);
|
||||
for (t = 0; t < r->txt_count; t++) {
|
||||
printf("%s=%s(%d); ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL", r->txt_value_len[t]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
a = r->addr;
|
||||
while (a) {
|
||||
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
|
||||
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_and_print_result(mdns_search_once_t *search)
|
||||
{
|
||||
// Check if any result is available
|
||||
mdns_result_t * result = NULL;
|
||||
if (!mdns_query_async_get_results(search, 0, &result, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!result) { // search timeout, but no result
|
||||
return false;
|
||||
}
|
||||
|
||||
// If yes, print the result
|
||||
mdns_ip_addr_t * a = result->addr;
|
||||
while (a) {
|
||||
if(a->addr.type == ESP_IPADDR_TYPE_V6){
|
||||
printf("Async query resolved to AAAA:" IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf("Async query resolved to A:" IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
// and free the result
|
||||
mdns_query_results_free(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool query_mdns_hosts_async(const char * host_name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name);
|
||||
bool res = false;
|
||||
|
||||
mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1, NULL);
|
||||
mdns_query_async_delete(s_a);
|
||||
mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1, NULL);
|
||||
while (s_a || s_aaaa) {
|
||||
if (s_a && check_and_print_result(s_a)) {
|
||||
ESP_LOGI(TAG, "Query A %s.local finished", host_name);
|
||||
mdns_query_async_delete(s_a);
|
||||
s_a = NULL;
|
||||
res = true;
|
||||
}
|
||||
if (s_aaaa && check_and_print_result(s_aaaa)) {
|
||||
ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name);
|
||||
mdns_query_async_delete(s_aaaa);
|
||||
s_aaaa = NULL;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static esp_err_t query_mdns_host(const char * host_name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query A: %s.local", host_name);
|
||||
|
||||
struct esp_ip4_addr addr;
|
||||
addr.addr = 0;
|
||||
|
||||
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
|
||||
if(err){
|
||||
if(err == ESP_ERR_NOT_FOUND){
|
||||
ESP_LOGW(TAG, "%s: Host was not found!", esp_err_to_name(err));
|
||||
}
|
||||
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t query_mdns_service(const char * instance, const char * service_name, const char * proto)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query SRV: %s.%s.local", service_name, proto);
|
||||
|
||||
mdns_result_t * results = NULL;
|
||||
esp_err_t err = mdns_query_srv(instance, service_name, proto, 3000, &results);
|
||||
if(err){
|
||||
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
if(!results){
|
||||
ESP_LOGW(TAG, "No results found!");
|
||||
}
|
||||
|
||||
mdns_print_results(results);
|
||||
mdns_query_results_free(results);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void query_mdns_service_sub_type(const char * subtype, const char * service_name, const char * proto) {
|
||||
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
|
||||
|
||||
mdns_result_t * results = NULL;
|
||||
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
|
||||
if(err){
|
||||
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
if(!results){
|
||||
ESP_LOGW(TAG, "No results found!");
|
||||
}
|
||||
|
||||
mdns_print_results(results);
|
||||
mdns_query_results_free(results);
|
||||
}
|
||||
|
||||
void mdns_test(const char *line)
|
||||
{
|
||||
char test_case[32];
|
||||
int i = 0;
|
||||
const TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
|
||||
|
||||
sscanf(line, "%s", test_case);
|
||||
ESP_LOGI(TAG, "test case = %s", test_case);
|
||||
|
||||
if (strcmp(test_case, "CONFIG_TEST_QUERY_HOST") == 0) {
|
||||
i = 0;
|
||||
while (query_mdns_host("tinytester") != ESP_OK && i != RETRY_COUNT) {
|
||||
query_mdns_host("tinytester");
|
||||
i++;
|
||||
vTaskDelay(xDelay);
|
||||
}
|
||||
} else if (strcmp(test_case, "CONFIG_TEST_QUERY_HOST_ASYNC") == 0) {
|
||||
i = 0;
|
||||
while (query_mdns_hosts_async("tinytester") == false && i != RETRY_COUNT) {
|
||||
query_mdns_hosts_async("tinytester");
|
||||
i++;
|
||||
vTaskDelay(xDelay);
|
||||
}
|
||||
} else if (strcmp(test_case, "CONFIG_TEST_QUERY_SERVICE") == 0) {
|
||||
i = 0;
|
||||
while (query_mdns_service("ESP32", "_http", "_tcp") != ESP_OK && i != RETRY_COUNT) {
|
||||
query_mdns_service("ESP32", "_http", "_tcp");
|
||||
i++;
|
||||
vTaskDelay(xDelay);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: No such test case", test_case);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_TEST_MDNS_ADD_MAC_TO_HOSTNAME=y
|
||||
CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST=y
|
||||
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user