Import mDNS changes

This commit is contained in:
me-no-dev 2017-12-07 14:21:40 +01:00
parent f2e51bc08a
commit 4bddbc031c
11 changed files with 5954 additions and 1359 deletions

View File

@ -0,0 +1,2 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include

View File

@ -20,35 +20,69 @@ extern "C" {
#ifndef MDNS_TEST_MODE
#include <tcpip_adapter.h>
#include "esp_event.h"
#else
#include "esp32_compat.h"
#endif
struct mdns_server_s;
typedef struct mdns_server_s mdns_server_t;
#define MDNS_TYPE_A 0x0001
#define MDNS_TYPE_PTR 0x000C
#define MDNS_TYPE_TXT 0x0010
#define MDNS_TYPE_AAAA 0x001C
#define MDNS_TYPE_SRV 0x0021
#define MDNS_TYPE_OPT 0x0029
#define MDNS_TYPE_NSEC 0x002F
#define MDNS_TYPE_ANY 0x00FF
/**
* @brief mDNS query result structure
*
* @brief mDNS enum to specify the ip_protocol type
*/
typedef enum {
MDNS_IP_PROTOCOL_V4,
MDNS_IP_PROTOCOL_V6,
MDNS_IP_PROTOCOL_MAX
} mdns_ip_protocol_t;
/**
* @brief mDNS basic text item structure
* Used in mdns_service_add()
*/
typedef struct {
char * key; /*!< item key name */
char * value; /*!< item value string */
} mdns_txt_item_t;
/**
* @brief mDNS query linked list IP item
*/
typedef struct mdns_ip_addr_s {
ip_addr_t addr; /*!< IP address */
struct mdns_ip_addr_s * next; /*!< next IP, or NULL for the last IP in the list */
} mdns_ip_addr_t;
/**
* @brief mDNS query result structure
*/
typedef struct mdns_result_s {
const char * host; /*!< hostname */
const char * instance; /*!< instance */
const char * txt; /*!< txt data */
uint16_t priority; /*!< service priority */
uint16_t weight; /*!< service weight */
uint16_t port; /*!< service port */
struct ip4_addr addr; /*!< ip4 address */
struct ip6_addr addrv6; /*!< ip6 address */
const struct mdns_result_s * next; /*!< next result, or NULL for the last result in the list */
struct mdns_result_s * next; /*!< next result, or NULL for the last result in the list */
tcpip_adapter_if_t tcpip_if; /*!< interface on which the result came (AP/STA/ETH) */
mdns_ip_protocol_t ip_protocol; /*!< ip_protocol type of the interface (v4/v6) */
// PTR
char * instance_name; /*!< instance name */
// SRV
char * hostname; /*!< hostname */
uint16_t port; /*!< service port */
// TXT
mdns_txt_item_t * txt; /*!< txt record */
size_t txt_count; /*!< number of txt items */
// A and AAAA
mdns_ip_addr_t * addr; /*!< linked list of IP addreses found */
} mdns_result_t;
/**
* @brief Initialize mDNS on given interface
*
* @param tcpip_if Interface that the server will listen on
* @param server Server pointer to populate on success
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG when bad tcpip_if is given
@ -56,20 +90,18 @@ typedef struct mdns_result_s {
* - ESP_ERR_NO_MEM on memory error
* - ESP_ERR_WIFI_NOT_INIT when WiFi is not initialized by eps_wifi_init
*/
esp_err_t mdns_init(tcpip_adapter_if_t tcpip_if, mdns_server_t ** server);
esp_err_t mdns_init();
/**
* @brief Stop and free mDNS server
*
* @param server mDNS Server to free
*
*/
void mdns_free(mdns_server_t * server);
void mdns_free();
/**
* @brief Set the hostname for mDNS server
* required if you want to advertise services
*
* @param server mDNS Server
* @param hostname Hostname to set
*
* @return
@ -77,41 +109,42 @@ void mdns_free(mdns_server_t * server);
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_set_hostname(mdns_server_t * server, const char * hostname);
esp_err_t mdns_hostname_set(const char * hostname);
/**
* @brief Set the default instance name for mDNS server
*
* @param server mDNS Server
* @param instance Instance name to set
* @param instance_name Instance name to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_set_instance(mdns_server_t * server, const char * instance);
esp_err_t mdns_instance_name_set(const char * instance_name);
/**
* @brief Add service to mDNS server
*
* @param server mDNS Server
* @param service service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param port service port
* @param instance_name instance name to set. If NULL,
* global instance name or hostname will be used
* @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"}})
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_add(mdns_server_t * server, const char * service, const char * proto, uint16_t port);
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);
/**
* @brief Remove service from mDNS server
*
* @param server mDNS Server
* @param service service type (_http, _ftp, etc)
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
*
* @return
@ -120,15 +153,14 @@ esp_err_t mdns_service_add(mdns_server_t * server, const char * service, const c
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_FAIL unknown error
*/
esp_err_t mdns_service_remove(mdns_server_t * server, const char * service, const char * proto);
esp_err_t mdns_service_remove(const char * service_type, const char * proto);
/**
* @brief Set instance name for service
*
* @param server mDNS Server
* @param service service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param instance instance name to set
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param instance_name instance name to set
*
* @return
* - ESP_OK success
@ -136,30 +168,12 @@ esp_err_t mdns_service_remove(mdns_server_t * server, const char * service, cons
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_instance_set(mdns_server_t * server, const char * service, const char * proto, const char * instance);
/**
* @brief Set TXT data for service
*
* @param server mDNS Server
* @param service service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param num_items number of items in TXT data
* @param txt string array of TXT data (eg. {"var=val","other=2"})
*
* @return
* - 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_txt_set(mdns_server_t * server, const char * service, const char * proto, uint8_t num_items, const char ** txt);
esp_err_t mdns_service_instance_name_set(const char * service_type, const char * proto, const char * instance_name);
/**
* @brief Set service port
*
* @param server mDNS Server
* @param service service type (_http, _ftp, etc)
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param port service port
*
@ -168,69 +182,182 @@ esp_err_t mdns_service_txt_set(mdns_server_t * server, const char * service, con
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
*/
esp_err_t mdns_service_port_set(mdns_server_t * server, const char * service, const char * proto, uint16_t port);
esp_err_t mdns_service_port_set(const char * service_type, const char * proto, uint16_t port);
/**
* @brief Replace all TXT items for service
*
* @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"}})
*
* @return
* - 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_txt_set(const char * service_type, const char * proto, mdns_txt_item_t txt[], uint8_t num_items);
/**
* @brief Set/Add TXT item for service TXT record
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to add/update
* @param value the new value of the key
*
* @return
* - 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_txt_item_set(const char * service_type, const char * proto, const char * key, const char * value);
/**
* @brief Remove TXT item for service TXT record
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to remove
*
* @return
* - 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_txt_item_remove(const char * service_type, const char * proto, const char * key);
/**
* @brief Remove and free all services from mDNS server
*
* @param server mDNS Server
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t mdns_service_remove_all(mdns_server_t * server);
esp_err_t mdns_service_remove_all();
/**
* @brief Query mDNS for host or service
* All following query methods are derived from this one
*
* @param server mDNS Server
* @param service service type or host name
* @param proto service protocol or NULL if searching for host
* @param timeout time to wait for answers. If 0, mdns_query_end MUST be called to end the search
*
* @return the number of results found
*/
size_t mdns_query(mdns_server_t * server, const char * service, const char * proto, uint32_t timeout);
/**
* @brief Stop mDNS Query started with timeout = 0
*
* @param server mDNS Server
*
* @return the number of results found
*/
size_t mdns_query_end(mdns_server_t * server);
/**
* @brief get the number of results currently in memoty
*
* @param server mDNS Server
*
* @return the number of results
*/
size_t mdns_result_get_count(mdns_server_t * server);
/**
* @brief Get mDNS Search result with given index
*
* @param server mDNS Server
* @param num the index of the result
*
* @return the result or NULL if error
*/
const mdns_result_t * mdns_result_get(mdns_server_t * server, size_t num);
/**
* @brief Remove and free all search results from mDNS server
*
* @param server mDNS Server
* @param name service instance or host name (NULL for PTR queries)
* @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries)
* @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries)
* @param type type of query (MDNS_TYPE_*)
* @param timeout time in milliseconds to wait for answers.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
* results must be freed using mdns_query_results_free below
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG timeout was not given
*/
esp_err_t mdns_result_free(mdns_server_t * server);
esp_err_t mdns_query(const char * name, const char * service_type, const char * proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t ** results);
/**
* @brief Free query results
*
* @param results linked list of results to be freed
*/
void mdns_query_results_free(mdns_result_t * results);
/**
* @brief Query mDNS for service
*
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_ptr(const char * service_type, const char * proto, uint32_t timeout, size_t max_results, mdns_result_t ** results);
/**
* @brief Query mDNS for SRV record
*
* @param instance_name service instance name
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param result pointer to the result of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_srv(const char * instance_name, const char * service_type, const char * proto, uint32_t timeout, mdns_result_t ** result);
/**
* @brief Query mDNS for TXT record
*
* @param instance_name service instance name
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param result pointer to the result of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_txt(const char * instance_name, const char * service_type, const char * proto, uint32_t timeout, mdns_result_t ** result);
/**
* @brief Query mDNS for A record
*
* @param host_name host name to look for
* @param timeout time in milliseconds to wait for answer.
* @param addr pointer to the resulting IP4 address
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_a(const char * host_name, uint32_t timeout, ip4_addr_t * addr);
/**
* @brief Query mDNS for A record
*
* @param host_name host name to look for
* @param timeout time in milliseconds to wait for answer. If 0, max_results needs to be defined
* @param addr pointer to the resulting IP6 address
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_aaaa(const char * host_name, uint32_t timeout, ip6_addr_t * addr);
/**
* @brief System event handler
* This method controls the service state on all active interfaces and applications are required
* to call it from the system event handler for normal operation of mDNS service.
*
* @param ctx The system event context
* @param event The system event
*/
esp_err_t mdns_handle_system_event(void *ctx, system_event_t *event);
#ifdef __cplusplus
}

View File

@ -0,0 +1,22 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _MDNS_CONSOLE_H_
#define _MDNS_CONSOLE_H_
/**
* @brief Register MDNS functions with the console component
*/
void mdns_console_register();
#endif /* _MDNS_CONSOLE_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,388 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MDNS_PRIVATE_H_
#define MDNS_PRIVATE_H_
//#define MDNS_ENABLE_DEBUG
#ifdef MDNS_ENABLE_DEBUG
#define _mdns_dbg_printf(...) printf(__VA_ARGS__)
#endif
#define MDNS_ANSWER_PTR_TTL 4500
#define MDNS_ANSWER_TXT_TTL 4500
#define MDNS_ANSWER_SRV_TTL 120
#define MDNS_ANSWER_A_TTL 120
#define MDNS_ANSWER_AAAA_TTL 120
#define MDNS_FLAGS_AUTHORITATIVE 0x8400
#define MDNS_FLAGS_DISTRIBUTED 0x0200
#define MDNS_NAME_REF 0xC000
//custom type! only used by this implementation
//to help manage service discovery handling
#define MDNS_TYPE_SDPTR 0x0032
#define MDNS_CLASS_IN 0x0001
#define MDNS_CLASS_ANY 0x00FF
#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001
#define MDNS_ANSWER_ALL 0x3F
#define MDNS_ANSWER_PTR 0x08
#define MDNS_ANSWER_TXT 0x04
#define MDNS_ANSWER_SRV 0x02
#define MDNS_ANSWER_A 0x01
#define MDNS_ANSWER_AAAA 0x10
#define MDNS_ANSWER_NSEC 0x20
#define MDNS_ANSWER_SDPTR 0x80
#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on
#define MDNS_SERVICE_STACK_DEPTH 4096 // Stack size for the service thread
#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing
#define MDNS_ACTION_QUEUE_LEN 16 // Maximum actions pending to the server
#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record
#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto
#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto
#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet
#define MDNS_HEAD_LEN 12
#define MDNS_HEAD_ID_OFFSET 0
#define MDNS_HEAD_FLAGS_OFFSET 2
#define MDNS_HEAD_QUESTIONS_OFFSET 4
#define MDNS_HEAD_ANSWERS_OFFSET 6
#define MDNS_HEAD_SERVERS_OFFSET 8
#define MDNS_HEAD_ADDITIONAL_OFFSET 10
#define MDNS_TYPE_OFFSET 0
#define MDNS_CLASS_OFFSET 2
#define MDNS_TTL_OFFSET 4
#define MDNS_LEN_OFFSET 8
#define MDNS_DATA_OFFSET 10
#define MDNS_SRV_PRIORITY_OFFSET 0
#define MDNS_SRV_WEIGHT_OFFSET 2
#define MDNS_SRV_PORT_OFFSET 4
#define MDNS_SRV_FQDN_OFFSET 6
#define MDNS_TIMER_PERIOD_US 100000
#define MDNS_SERVICE_LOCK() xSemaphoreTake(_mdns_service_semaphore, portMAX_DELAY)
#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(_mdns_service_semaphore)
#define queueToEnd(type, queue, item) \
if (!queue) { \
queue = item; \
} else { \
type * _q = queue; \
while (_q->next) { _q = _q->next; } \
_q->next = item; \
}
#define queueDetach(type, queue, item) \
if (queue) { \
if (queue == item) { \
queue = queue->next; \
} else { \
type * _q = queue; \
while (_q->next && _q->next != item) { \
_q = _q->next; \
} \
if (_q->next == item) { \
_q->next = item->next; \
item->next = NULL; \
} \
} \
}
#define queueFree(type, queue) while (queue) { type * _q = queue; queue = queue->next; free(_q); }
#define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1)
#define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING)
#define PCB_STATE_IS_RUNNING(s) (s->state == PCB_RUNNING)
#define MDNS_SEARCH_LOCK() xSemaphoreTake(_mdns_server->search.lock, portMAX_DELAY)
#define MDNS_SEARCH_UNLOCK() xSemaphoreGive(_mdns_server->search.lock)
typedef enum {
PCB_OFF, PCB_DUP, PCB_INIT,
PCB_PROBE_1, PCB_PROBE_2, PCB_PROBE_3,
PCB_ANNOUNCE_1, PCB_ANNOUNCE_2, PCB_ANNOUNCE_3,
PCB_RUNNING
} mdns_pcb_state_t;
typedef enum {
MDNS_ANSWER, MDNS_NS, MDNS_EXTRA
} mdns_parsed_recort_type_t;
typedef enum {
ACTION_SYSTEM_EVENT,
ACTION_HOSTNAME_SET,
ACTION_INSTANCE_SET,
ACTION_SERVICE_ADD,
ACTION_SERVICE_DEL,
ACTION_SERVICE_INSTANCE_SET,
ACTION_SERVICE_PORT_SET,
ACTION_SERVICE_TXT_REPLACE,
ACTION_SERVICE_TXT_SET,
ACTION_SERVICE_TXT_DEL,
ACTION_SERVICES_CLEAR,
ACTION_SEARCH_ADD,
ACTION_SEARCH_SEND,
ACTION_SEARCH_END,
ACTION_TX_HANDLE,
ACTION_RX_HANDLE,
ACTION_TASK_STOP,
ACTION_MAX
} mdns_action_type_t;
typedef struct {
uint16_t id;
union {
struct {
uint16_t qr :1;
uint16_t opCode :4;
uint16_t aa :1;
uint16_t tc :1;
uint16_t rd :1;
uint16_t ra :1;
uint16_t z :1;
uint16_t ad :1;
uint16_t cd :1;
uint16_t rCode :4;//response/error code
};
uint16_t value;
} flags;
uint16_t questions; //QDCOUNT
uint16_t answers; //ANCOUNT
uint16_t servers; //NSCOUNT
uint16_t additional;//ARCOUNT
} mdns_header_t;
typedef struct {
char host[MDNS_NAME_BUF_LEN];
char service[MDNS_NAME_BUF_LEN];
char proto[MDNS_NAME_BUF_LEN];
char domain[MDNS_NAME_BUF_LEN];
uint8_t parts;
uint8_t sub;
} mdns_name_t;
typedef struct mdns_parsed_question_s {
struct mdns_parsed_question_s * next;
uint16_t type;
bool unicast;
char * host;
char * service;
char * proto;
char * domain;
} mdns_parsed_question_t;
typedef struct mdns_parsed_record_s {
struct mdns_parsed_record_s * next;
mdns_parsed_recort_type_t record_type;
uint16_t type;
uint16_t clas;
uint8_t flush;
uint32_t ttl;
char * host;
char * service;
char * proto;
char * domain;
uint16_t data_len;
uint8_t *data;
} mdns_parsed_record_t;
typedef struct {
tcpip_adapter_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
//struct udp_pcb *pcb;
ip_addr_t src;
uint16_t src_port;
uint8_t multicast;
uint8_t authoritative;
uint8_t probe;
uint8_t discovery;
uint8_t distributed;
mdns_parsed_question_t * questions;
mdns_parsed_record_t * records;
} mdns_parsed_packet_t;
typedef struct {
tcpip_adapter_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
struct pbuf *pb;
ip_addr_t src;
ip_addr_t dest;
uint16_t src_port;
uint8_t multicast;
} mdns_rx_packet_t;
typedef struct mdns_txt_linked_item_s {
const char * key; /*!< item key name */
const char * value; /*!< item value string */
struct mdns_txt_linked_item_s * next; /*!< next result, or NULL for the last result in the list */
} mdns_txt_linked_item_t;
typedef struct {
const char * instance;
const char * service;
const char * proto;
uint16_t priority;
uint16_t weight;
uint16_t port;
mdns_txt_linked_item_t * txt;
} mdns_service_t;
typedef struct mdns_srv_item_s {
struct mdns_srv_item_s * next;
mdns_service_t * service;
} mdns_srv_item_t;
typedef struct mdns_out_question_s {
struct mdns_out_question_s * next;
uint16_t type;
uint8_t unicast;
const char * host;
const char * service;
const char * proto;
const char * domain;
} mdns_out_question_t;
typedef struct mdns_out_answer_s {
struct mdns_out_answer_s * next;
uint16_t type;
uint8_t bye;
uint8_t flush;
mdns_service_t * service;
const char * custom_instance;
const char * custom_service;
const char * custom_proto;
} mdns_out_answer_t;
typedef struct mdns_tx_packet_s {
struct mdns_tx_packet_s * next;
uint32_t send_at;
tcpip_adapter_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
ip_addr_t dst;
uint16_t port;
uint16_t flags;
uint8_t distributed;
mdns_out_question_t * questions;
mdns_out_answer_t * answers;
mdns_out_answer_t * servers;
mdns_out_answer_t * additional;
} mdns_tx_packet_t;
typedef struct {
mdns_pcb_state_t state;
struct udp_pcb * pcb;
mdns_srv_item_t ** probe_services;
uint8_t probe_services_len;
uint8_t probe_ip;
uint8_t probe_running;
uint16_t failed_probes;
} mdns_pcb_t;
typedef enum {
SEARCH_OFF,
SEARCH_INIT,
SEARCH_RUNNING,
SEARCH_MAX
} mdns_search_once_state_t;
typedef struct mdns_search_once_s {
struct mdns_search_once_s * next;
mdns_search_once_state_t state;
uint32_t started_at;
uint32_t sent_at;
uint32_t timeout;
SemaphoreHandle_t lock;
uint16_t type;
uint8_t max_results;
uint8_t num_results;
char * instance;
char * service;
char * proto;
mdns_result_t * result;
} mdns_search_once_t;
typedef struct mdns_server_s {
struct {
mdns_pcb_t pcbs[MDNS_IP_PROTOCOL_MAX];
} interfaces[TCPIP_ADAPTER_IF_MAX];
const char * hostname;
const char * instance;
mdns_srv_item_t * services;
SemaphoreHandle_t lock;
QueueHandle_t action_queue;
mdns_tx_packet_t * tx_queue_head;
mdns_search_once_t * search_once;
esp_timer_handle_t timer_handle;
} mdns_server_t;
typedef struct {
mdns_action_type_t type;
union {
char * hostname;
char * instance;
struct {
system_event_id_t event_id;
tcpip_adapter_if_t interface;
} sys_event;
struct {
mdns_srv_item_t * service;
} srv_add;
struct {
mdns_srv_item_t * service;
} srv_del;
struct {
mdns_srv_item_t * service;
char * instance;
} srv_instance;
struct {
mdns_srv_item_t * service;
uint16_t port;
} srv_port;
struct {
mdns_srv_item_t * service;
uint8_t num_items;
mdns_txt_item_t * txt;
} srv_txt_replace;
struct {
mdns_srv_item_t * service;
char * key;
char * value;
} srv_txt_set;
struct {
mdns_srv_item_t * service;
char * key;
} srv_txt_del;
struct {
mdns_search_once_t * search;
} search_add;
struct {
mdns_tx_packet_t * packet;
} tx_handle;
struct {
mdns_rx_packet_t * packet;
} rx_handle;
} data;
} mdns_action_t;
#endif /* MDNS_PRIVATE_H_ */

View File

@ -603,6 +603,18 @@ esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *ho
*/
esp_err_t tcpip_adapter_get_hostname(tcpip_adapter_if_t tcpip_if, const char **hostname);
/**
* @brief Get the LwIP netif* that is assigned to the interface
*
* @param[in] tcpip_if: the interface which we will get the hostname
* @param[out] void ** netif: pointer to fill the resulting interface
*
* @return ESP_OK:success
* ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY:interface status error
* ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS:parameter error
*/
esp_err_t tcpip_adapter_get_netif(tcpip_adapter_if_t tcpip_if, void ** netif);
#ifdef __cplusplus
}
#endif

View File

@ -1181,4 +1181,18 @@ static esp_err_t tcpip_adapter_reset_ip_info(tcpip_adapter_if_t tcpip_if)
return ESP_OK;
}
esp_err_t tcpip_adapter_get_netif(tcpip_adapter_if_t tcpip_if, void ** netif)
{
if (tcpip_if >= TCPIP_ADAPTER_IF_MAX) {
return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS;
}
*netif = esp_netif[tcpip_if];
if (*netif == NULL) {
return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY;
}
return ESP_OK;
}
#endif /* CONFIG_TCPIP_LWIP */

View File

@ -20,21 +20,19 @@ Example method to start mDNS for the STA interface and set ``hostname`` and ``de
::
mdns_server_t * mdns = NULL;
void start_mdns_service()
{
//initialize mDNS service on STA interface
esp_err_t err = mdns_init(TCPIP_ADAPTER_IF_STA, &mdns);
//initialize mDNS service
esp_err_t err = mdns_init();
if (err) {
printf("MDNS Init failed: %d\n", err);
return;
}
//set hostname
mdns_set_hostname(mdns, "my-esp32");
mdns_hostname_set("my-esp32");
//set default instance
mdns_set_instance(mdns, "Jhon's ESP32 Thing");
mdns_instance_name_set("Jhon's ESP32 Thing");
}
mDNS Services
@ -42,36 +40,35 @@ mDNS Services
mDNS can advertise information about network services that your device offers. Each service is defined by a few properties.
* ``service``: (required) service type, prepended with underscore. Some common types can be found `here <http://www.dns-sd.org/serviceTypes.html>`_.
* ``instance_name``: friendly name for your service, like ``Jhon's ESP32 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
* ``instance``: friendly name for your service, like ``Jhon's ESP32 Web Server``. If not defined, ``default_instance`` will be used.
* ``txt``: ``var=val`` array of strings, used to define properties for your service
* ``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(mdns, "_http", "_tcp", 80);
mdns_service_add(mdns, "_arduino", "_tcp", 3232);
mdns_service_add(mdns, "_myservice", "_udp", 1234);
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_set(mdns, "_http", "_tcp", "Jhon's ESP32 Web Server");
mdns_service_instance_name_set("_http", "_tcp", "Jhon's ESP32 Web Server");
const char * arduTxtData[4] = {
"board=esp32",
"tcp_check=no",
"ssh_upload=no",
"auth_upload=no"
mdns_txt_item_t serviceTxtData[3] = {
{"board","esp32"},
{"u","user"},
{"p","password"}
};
//set txt data for service (will free and replace current data)
mdns_service_txt_set(mdns, "_arduino", "_tcp", 4, arduTxtData);
mdns_service_txt_set("_http", "_tcp", serviceTxtData, 3);
//change service port
mdns_service_port_set(mdns, "_myservice", "_udp", 4321);
mdns_service_port_set("_myservice", "_udp", 4321);
}
mDNS Query
@ -79,60 +76,85 @@ mDNS Query
mDNS provides methods for browsing for services and resolving host's IP/IPv6 addresses.
Results are returned as a linked list of ``mdns_result_t`` objects. If the result is from host query, it will contain only ``addr`` and ``addrv6`` if found. Service queries will populate all fields in a result that were found.
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 * hostname)
void resolve_mdns_host(const char * host_name)
{
printf("mDNS Host Lookup: %s.local\n", hostname);
//run search for 1000 ms
if (mdns_query(mdns, hostname, NULL, 1000)) {
//results were found
const mdns_result_t * results = mdns_result_get(mdns, 0);
//itterate through all results
size_t i = 1;
while(results) {
//print result information
printf(" %u: IP:" IPSTR ", IPv6:" IPV6STR "\n", i++
IP2STR(&results->addr), IPV62STR(results->addrv6));
//load next result. Will be NULL if this was the last one
results = results->next;
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;
}
//free the results from memory
mdns_result_free(mdns);
} else {
//host was not found
printf(" Host Not Found\n");
printf("Query Failed");
return;
}
printf(IPSTR, IP2STR(&addr));
}
Example method to resolve local services::
void find_mdns_service(const char * service, const char * proto)
{
printf("mDNS Service Lookup: %s.%s\n", service, proto);
//run search for 1000 ms
if (mdns_query(mdns, service, proto, 1000)) {
//results were found
const mdns_result_t * results = mdns_result_get(mdns, 0);
//itterate through all results
size_t i = 1;
while(results) {
//print result information
printf(" %u: hostname:%s, instance:\"%s\", IP:" IPSTR ", IPv6:" IPV6STR ", port:%u, txt:%s\n", i++,
(results->host)?results->host:"NULL", (results->instance)?results->instance:"NULL",
IP2STR(&results->addr), IPV62STR(results->addrv6),
results->port, (results->txt)?results->txt:"\r");
//load next result. Will be NULL if this was the last one
results = results->next;
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);
}
//free the results from memory
mdns_result_free(mdns);
} else {
//service was not found
printf(" Service Not Found\n");
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 == MDNS_IP_PROTOCOL_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::

View File

@ -2,4 +2,20 @@
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`
- 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
- 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
See the README.md file in the upper level 'examples' directory for more information about examples.

228
examples/protocols/mdns/main/mdns_example_main.c Normal file → Executable file
View File

@ -16,6 +16,9 @@
#include "esp_log.h"
#include "nvs_flash.h"
#include "mdns.h"
#include "driver/gpio.h"
#include <sys/socket.h>
#include <netdb.h>
/* The examples use simple WiFi configuration that you can set via
'make menuconfig'.
@ -35,9 +38,11 @@ static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
const int IP4_CONNECTED_BIT = BIT0;
const int IP6_CONNECTED_BIT = BIT1;
static const char *TAG = "mdns-test";
static bool auto_reconnect = true;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
@ -50,17 +55,23 @@ static esp_err_t event_handler(void *ctx, system_event_t *event)
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
xEventGroupSetBits(wifi_event_group, IP4_CONNECTED_BIT);
break;
case SYSTEM_EVENT_AP_STA_GOT_IP6:
xEventGroupSetBits(wifi_event_group, IP6_CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
if (auto_reconnect) {
esp_wifi_connect();
}
xEventGroupClearBits(wifi_event_group, IP4_CONNECTED_BIT | IP6_CONNECTED_BIT);
break;
default:
break;
}
mdns_handle_system_event(ctx, event);
return ESP_OK;
}
@ -84,101 +95,150 @@ static void initialise_wifi(void)
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void query_mdns_service(mdns_server_t * mdns, const char * service, const char * proto)
static void initialise_mdns(void)
{
if(!mdns) {
//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) );
//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) );
//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("_http", "_tcp", "u", "admin") );
}
static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
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\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 == MDNS_IP_PROTOCOL_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");
return;
}
uint32_t res;
if (!proto) {
ESP_LOGI(TAG, "Host Lookup: %s", service);
res = mdns_query(mdns, service, 0, 1000);
if (res) {
size_t i;
for(i=0; i<res; i++) {
const mdns_result_t * r = mdns_result_get(mdns, i);
if (r) {
ESP_LOGI(TAG, " %u: " IPSTR " " IPV6STR, i+1,
IP2STR(&r->addr), IPV62STR(r->addrv6));
}
}
mdns_result_free(mdns);
} else {
ESP_LOGI(TAG, " Not Found");
}
} else {
ESP_LOGI(TAG, "Service Lookup: %s.%s ", service, proto);
res = mdns_query(mdns, service, proto, 1000);
if (res) {
size_t i;
for(i=0; i<res; i++) {
const mdns_result_t * r = mdns_result_get(mdns, i);
if (r) {
ESP_LOGI(TAG, " %u: %s \"%s\" " IPSTR " " IPV6STR " %u %s", i+1,
(r->host)?r->host:"", (r->instance)?r->instance:"",
IP2STR(&r->addr), IPV62STR(r->addrv6),
r->port, (r->txt)?r->txt:"");
}
}
mdns_result_free(mdns);
}
if(!results){
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
static void query_mdns_host(const char * host_name)
{
ESP_LOGI(TAG, "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){
ESP_LOGW(TAG, "Host was not found!");
return;
}
ESP_LOGE(TAG, "Query Failed");
return;
}
ESP_LOGI(TAG, IPSTR, IP2STR(&addr));
}
static void initialise_button(void)
{
gpio_config_t io_conf;
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.pin_bit_mask = 1;
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(GPIO_NUM_0);
if (!new_level && old_level) {
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)
{
mdns_server_t * mdns = NULL;
/* Wait for the callback to set the CONNECTED_BIT in the event group. */
xEventGroupWaitBits(wifi_event_group, IP4_CONNECTED_BIT | IP6_CONNECTED_BIT,
false, true, portMAX_DELAY);
while(1) {
/* Wait for the callback to set the CONNECTED_BIT in the
event group.
*/
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to AP");
if (!mdns) {
esp_err_t err = mdns_init(TCPIP_ADAPTER_IF_STA, &mdns);
if (err) {
ESP_LOGE(TAG, "Failed starting MDNS: %u", err);
continue;
}
ESP_ERROR_CHECK( mdns_set_hostname(mdns, EXAMPLE_MDNS_HOSTNAME) );
ESP_ERROR_CHECK( mdns_set_instance(mdns, EXAMPLE_MDNS_INSTANCE) );
const char * arduTxtData[4] = {
"board=esp32",
"tcp_check=no",
"ssh_upload=no",
"auth_upload=no"
};
ESP_ERROR_CHECK( mdns_service_add(mdns, "_arduino", "_tcp", 3232) );
ESP_ERROR_CHECK( mdns_service_txt_set(mdns, "_arduino", "_tcp", 4, arduTxtData) );
ESP_ERROR_CHECK( mdns_service_add(mdns, "_http", "_tcp", 80) );
ESP_ERROR_CHECK( mdns_service_instance_set(mdns, "_http", "_tcp", "ESP32 WebServer") );
ESP_ERROR_CHECK( mdns_service_add(mdns, "_smb", "_tcp", 445) );
} else {
query_mdns_service(mdns, "esp32", NULL);
query_mdns_service(mdns, "_arduino", "_tcp");
query_mdns_service(mdns, "_http", "_tcp");
query_mdns_service(mdns, "_printer", "_tcp");
query_mdns_service(mdns, "_ipp", "_tcp");
query_mdns_service(mdns, "_afpovertcp", "_tcp");
query_mdns_service(mdns, "_smb", "_tcp");
query_mdns_service(mdns, "_ftp", "_tcp");
query_mdns_service(mdns, "_nfs", "_tcp");
}
ESP_LOGI(TAG, "Restarting in 10 seconds!");
vTaskDelay(10000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Starting again!");
check_button();
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
void app_main()
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_mdns();
initialise_wifi();
initialise_button();
xTaskCreate(&mdns_example_task, "mdns_example_task", 2048, NULL, 5, NULL);
}