mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-04 16:19:20 +01:00
873 lines
26 KiB
C
873 lines
26 KiB
C
|
|
/*
|
||
|
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||
|
|
*
|
||
|
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
|
*/
|
||
|
|
#include <string.h>
|
||
|
|
#include "sdkconfig.h"
|
||
|
|
#include "mdns_private.h"
|
||
|
|
#include "mdns_querier.h"
|
||
|
|
#include "mdns_mem_caps.h"
|
||
|
|
#include "mdns_utils.h"
|
||
|
|
#include "mdns_send.h"
|
||
|
|
#include "esp_log.h"
|
||
|
|
#include "mdns_pcb.h"
|
||
|
|
#include "mdns_netif.h"
|
||
|
|
#include "mdns_responder.h"
|
||
|
|
#include "mdns_service.h"
|
||
|
|
|
||
|
|
static const char *TAG = "mdns_querier";
|
||
|
|
static mdns_search_once_t *s_search_once;
|
||
|
|
|
||
|
|
static esp_err_t send_search_action(mdns_action_type_t type, mdns_search_once_t *search);
|
||
|
|
static void search_free(mdns_search_once_t *search);
|
||
|
|
|
||
|
|
void mdns_priv_query_results_free(mdns_result_t *results)
|
||
|
|
{
|
||
|
|
mdns_result_t *r;
|
||
|
|
mdns_ip_addr_t *a;
|
||
|
|
|
||
|
|
while (results) {
|
||
|
|
r = results;
|
||
|
|
|
||
|
|
mdns_mem_free((char *)(r->hostname));
|
||
|
|
mdns_mem_free((char *)(r->instance_name));
|
||
|
|
mdns_mem_free((char *)(r->service_type));
|
||
|
|
mdns_mem_free((char *)(r->proto));
|
||
|
|
|
||
|
|
for (size_t i = 0; i < r->txt_count; i++) {
|
||
|
|
mdns_mem_free((char *)(r->txt[i].key));
|
||
|
|
mdns_mem_free((char *)(r->txt[i].value));
|
||
|
|
}
|
||
|
|
mdns_mem_free(r->txt);
|
||
|
|
mdns_mem_free(r->txt_value_len);
|
||
|
|
|
||
|
|
while (r->addr) {
|
||
|
|
a = r->addr;
|
||
|
|
r->addr = r->addr->next;
|
||
|
|
mdns_mem_free(a);
|
||
|
|
}
|
||
|
|
|
||
|
|
results = results->next;
|
||
|
|
mdns_mem_free(r);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Mark search as finished and remove it from search chain
|
||
|
|
*/
|
||
|
|
static void search_finish(mdns_search_once_t *search)
|
||
|
|
{
|
||
|
|
search->state = SEARCH_OFF;
|
||
|
|
queueDetach(mdns_search_once_t, s_search_once, search);
|
||
|
|
if (search->notifier) {
|
||
|
|
search->notifier(search);
|
||
|
|
}
|
||
|
|
xSemaphoreGive(search->done_semaphore);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Add new search to the search chain
|
||
|
|
*/
|
||
|
|
void search_add(mdns_search_once_t *search)
|
||
|
|
{
|
||
|
|
search->next = s_search_once;
|
||
|
|
s_search_once = search;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Send search packet to all available interfaces
|
||
|
|
*/
|
||
|
|
static void search_send(mdns_search_once_t *search)
|
||
|
|
{
|
||
|
|
mdns_search_once_t *queue = s_search_once;
|
||
|
|
bool found = false;
|
||
|
|
// looking for this search in active searches
|
||
|
|
while (queue) {
|
||
|
|
if (queue == search) {
|
||
|
|
found = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
queue = queue->next;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!found) {
|
||
|
|
// no longer active -> skip sending this search
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint8_t i, j;
|
||
|
|
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||
|
|
for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
|
||
|
|
mdns_priv_query_send(search, (mdns_if_t) i, (mdns_ip_protocol_t) j);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdns_priv_query_action(mdns_action_t *action, mdns_action_subtype_t type)
|
||
|
|
{
|
||
|
|
if (type == ACTION_RUN) {
|
||
|
|
switch (action->type) {
|
||
|
|
case ACTION_SEARCH_ADD:
|
||
|
|
search_add(action->data.search_add.search);
|
||
|
|
break;
|
||
|
|
case ACTION_SEARCH_SEND:
|
||
|
|
search_send(action->data.search_add.search);
|
||
|
|
break;
|
||
|
|
case ACTION_SEARCH_END:
|
||
|
|
search_finish(action->data.search_add.search);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
abort();
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (type == ACTION_CLEANUP) {
|
||
|
|
search_free(action->data.search_add.search);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from timer task to run active searches
|
||
|
|
*/
|
||
|
|
void mdns_priv_query_start_stop(void)
|
||
|
|
{
|
||
|
|
mdns_priv_service_lock();
|
||
|
|
mdns_search_once_t *s = s_search_once;
|
||
|
|
uint32_t now = xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||
|
|
if (!s) {
|
||
|
|
mdns_priv_service_unlock();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
while (s) {
|
||
|
|
if (s->state != SEARCH_OFF) {
|
||
|
|
if (now > (s->started_at + s->timeout)) {
|
||
|
|
s->state = SEARCH_OFF;
|
||
|
|
if (send_search_action(ACTION_SEARCH_END, s) != ESP_OK) {
|
||
|
|
s->state = SEARCH_RUNNING;
|
||
|
|
}
|
||
|
|
} else if (s->state == SEARCH_INIT || (now - s->sent_at) > 1000) {
|
||
|
|
s->state = SEARCH_RUNNING;
|
||
|
|
s->sent_at = now;
|
||
|
|
if (send_search_action(ACTION_SEARCH_SEND, s) != ESP_OK) {
|
||
|
|
s->sent_at -= 1000;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
s = s->next;
|
||
|
|
}
|
||
|
|
mdns_priv_service_unlock();
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdns_priv_query_free(void)
|
||
|
|
{
|
||
|
|
while (s_search_once) {
|
||
|
|
mdns_search_once_t *h = s_search_once;
|
||
|
|
s_search_once = h->next;
|
||
|
|
mdns_mem_free(h->instance);
|
||
|
|
mdns_mem_free(h->service);
|
||
|
|
mdns_mem_free(h->proto);
|
||
|
|
vSemaphoreDelete(h->done_semaphore);
|
||
|
|
if (h->result) {
|
||
|
|
mdns_priv_query_results_free(h->result);
|
||
|
|
}
|
||
|
|
mdns_mem_free(h);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to finish any searches that have reached maximum results
|
||
|
|
*/
|
||
|
|
void mdns_priv_query_done(void)
|
||
|
|
{
|
||
|
|
mdns_search_once_t *search = s_search_once;
|
||
|
|
mdns_search_once_t *s = NULL;
|
||
|
|
while (search) {
|
||
|
|
s = search;
|
||
|
|
search = search->next;
|
||
|
|
if (s->max_results && s->num_results >= s->max_results) {
|
||
|
|
search_finish(s);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from packet parser to find matching running search
|
||
|
|
*/
|
||
|
|
mdns_search_once_t *mdns_priv_query_find_from(mdns_search_once_t *s, mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||
|
|
{
|
||
|
|
mdns_result_t *r = NULL;
|
||
|
|
while (s) {
|
||
|
|
if (s->state == SEARCH_OFF) {
|
||
|
|
s = s->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) {
|
||
|
|
if ((s->type == MDNS_TYPE_ANY && s->service != NULL)
|
||
|
|
|| (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV)) {
|
||
|
|
s = s->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV) {
|
||
|
|
if (!strcasecmp(name->host, s->instance)) {
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
s = s->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
r = s->result;
|
||
|
|
while (r) {
|
||
|
|
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(name->host, r->hostname)) {
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
s = s->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) {
|
||
|
|
if ((s->type == MDNS_TYPE_ANY && s->service == NULL)
|
||
|
|
|| (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR)) {
|
||
|
|
s = s->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (strcasecmp(name->service, s->service)
|
||
|
|
|| strcasecmp(name->proto, s->proto)) {
|
||
|
|
s = s->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (s->type != MDNS_TYPE_PTR) {
|
||
|
|
if (s->instance && strcasecmp(name->host, s->instance) == 0) {
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
s = s->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (type == MDNS_TYPE_PTR && type == s->type && !strcasecmp(name->service, s->service) && !strcasecmp(name->proto, s->proto)) {
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
s = s->next;
|
||
|
|
}
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_search_once_t *mdns_priv_query_find(mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||
|
|
{
|
||
|
|
return mdns_priv_query_find_from(s_search_once, name, type, tcpip_if, ip_protocol);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Create search packet for particular interface
|
||
|
|
*/
|
||
|
|
static mdns_tx_packet_t *create_search_packet(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||
|
|
{
|
||
|
|
mdns_result_t *r = NULL;
|
||
|
|
mdns_tx_packet_t *packet = mdns_priv_alloc_packet(tcpip_if, ip_protocol);
|
||
|
|
if (!packet) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_out_question_t *q = (mdns_out_question_t *)mdns_mem_malloc(sizeof(mdns_out_question_t));
|
||
|
|
if (!q) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
mdns_priv_free_tx_packet(packet);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
q->next = NULL;
|
||
|
|
q->unicast = search->unicast;
|
||
|
|
q->type = search->type;
|
||
|
|
q->host = search->instance;
|
||
|
|
q->service = search->service;
|
||
|
|
q->proto = search->proto;
|
||
|
|
q->domain = MDNS_UTILS_DEFAULT_DOMAIN;
|
||
|
|
q->own_dynamic_memory = false;
|
||
|
|
queueToEnd(mdns_out_question_t, packet->questions, q);
|
||
|
|
|
||
|
|
if (search->type == MDNS_TYPE_PTR) {
|
||
|
|
r = search->result;
|
||
|
|
while (r) {
|
||
|
|
//full record on the same interface is available
|
||
|
|
if (r->esp_netif != mdns_priv_get_esp_netif(tcpip_if) || r->ip_protocol != ip_protocol || r->instance_name == NULL || r->hostname == NULL || r->addr == NULL) {
|
||
|
|
r = r->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
mdns_out_answer_t *a = (mdns_out_answer_t *)mdns_mem_malloc(sizeof(mdns_out_answer_t));
|
||
|
|
if (!a) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
mdns_priv_free_tx_packet(packet);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
a->type = MDNS_TYPE_PTR;
|
||
|
|
a->service = NULL;
|
||
|
|
a->custom_instance = r->instance_name;
|
||
|
|
a->custom_service = search->service;
|
||
|
|
a->custom_proto = search->proto;
|
||
|
|
a->bye = false;
|
||
|
|
a->flush = false;
|
||
|
|
a->next = NULL;
|
||
|
|
queueToEnd(mdns_out_answer_t, packet->answers, a);
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return packet;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Send search packet to particular interface
|
||
|
|
*/
|
||
|
|
void mdns_priv_query_send(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||
|
|
{
|
||
|
|
mdns_tx_packet_t *packet = NULL;
|
||
|
|
if (mdsn_priv_pcb_is_inited(tcpip_if, ip_protocol)) {
|
||
|
|
packet = create_search_packet(search, tcpip_if, ip_protocol);
|
||
|
|
if (!packet) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
mdns_priv_dispatch_tx_packet(packet);
|
||
|
|
mdns_priv_free_tx_packet(packet);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Free search structure (except the results)
|
||
|
|
*/
|
||
|
|
static void search_free(mdns_search_once_t *search)
|
||
|
|
{
|
||
|
|
mdns_mem_free(search->instance);
|
||
|
|
mdns_mem_free(search->service);
|
||
|
|
mdns_mem_free(search->proto);
|
||
|
|
vSemaphoreDelete(search->done_semaphore);
|
||
|
|
mdns_mem_free(search);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Allocate new search structure
|
||
|
|
*/
|
||
|
|
static mdns_search_once_t *search_init(const char *name, const char *service, const char *proto, uint16_t type, bool unicast,
|
||
|
|
uint32_t timeout, uint8_t max_results, mdns_query_notify_t notifier)
|
||
|
|
{
|
||
|
|
mdns_search_once_t *search = (mdns_search_once_t *)mdns_mem_malloc(sizeof(mdns_search_once_t));
|
||
|
|
if (!search) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
memset(search, 0, sizeof(mdns_search_once_t));
|
||
|
|
|
||
|
|
search->done_semaphore = xSemaphoreCreateBinary();
|
||
|
|
if (!search->done_semaphore) {
|
||
|
|
mdns_mem_free(search);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!mdns_utils_str_null_or_empty(name)) {
|
||
|
|
search->instance = mdns_mem_strndup(name, MDNS_NAME_BUF_LEN - 1);
|
||
|
|
if (!search->instance) {
|
||
|
|
search_free(search);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!mdns_utils_str_null_or_empty(service)) {
|
||
|
|
search->service = mdns_mem_strndup(service, MDNS_NAME_BUF_LEN - 1);
|
||
|
|
if (!search->service) {
|
||
|
|
search_free(search);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
search->proto = mdns_mem_strndup(proto, MDNS_NAME_BUF_LEN - 1);
|
||
|
|
if (!search->proto) {
|
||
|
|
search_free(search);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
search->type = type;
|
||
|
|
search->unicast = unicast;
|
||
|
|
search->timeout = timeout;
|
||
|
|
search->num_results = 0;
|
||
|
|
search->max_results = max_results;
|
||
|
|
search->result = NULL;
|
||
|
|
search->state = SEARCH_INIT;
|
||
|
|
search->sent_at = 0;
|
||
|
|
search->started_at = xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||
|
|
search->notifier = notifier;
|
||
|
|
search->next = NULL;
|
||
|
|
|
||
|
|
return search;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Queue search action
|
||
|
|
*/
|
||
|
|
static esp_err_t send_search_action(mdns_action_type_t type, mdns_search_once_t *search)
|
||
|
|
{
|
||
|
|
mdns_action_t *action = NULL;
|
||
|
|
|
||
|
|
action = (mdns_action_t *)mdns_mem_malloc(sizeof(mdns_action_t));
|
||
|
|
if (!action) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
action->type = type;
|
||
|
|
action->data.search_add.search = search;
|
||
|
|
if (!mdns_priv_queue_action(action)) {
|
||
|
|
mdns_mem_free(action);
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to add TXT data to search result
|
||
|
|
*/
|
||
|
|
void mdns_priv_query_result_add_txt(mdns_search_once_t *search, mdns_txt_item_t *txt, uint8_t *txt_value_len,
|
||
|
|
size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol,
|
||
|
|
uint32_t ttl)
|
||
|
|
{
|
||
|
|
mdns_result_t *r = search->result;
|
||
|
|
while (r) {
|
||
|
|
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol) {
|
||
|
|
if (r->txt) {
|
||
|
|
goto free_txt;
|
||
|
|
}
|
||
|
|
r->txt = txt;
|
||
|
|
r->txt_value_len = txt_value_len;
|
||
|
|
r->txt_count = txt_count;
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
if (!search->max_results || search->num_results < search->max_results) {
|
||
|
|
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||
|
|
if (!r) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
goto free_txt;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(r, 0, sizeof(mdns_result_t));
|
||
|
|
r->txt = txt;
|
||
|
|
r->txt_value_len = txt_value_len;
|
||
|
|
r->txt_count = txt_count;
|
||
|
|
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||
|
|
r->ip_protocol = ip_protocol;
|
||
|
|
r->ttl = ttl;
|
||
|
|
r->next = search->result;
|
||
|
|
search->result = r;
|
||
|
|
search->num_results++;
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
|
||
|
|
free_txt:
|
||
|
|
for (size_t i = 0; i < txt_count; i++) {
|
||
|
|
mdns_mem_free((char *)(txt[i].key));
|
||
|
|
mdns_mem_free((char *)(txt[i].value));
|
||
|
|
}
|
||
|
|
mdns_mem_free(txt);
|
||
|
|
mdns_mem_free(txt_value_len);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Chain new IP to search result
|
||
|
|
*/
|
||
|
|
static void result_add_ip(mdns_result_t *r, esp_ip_addr_t *ip)
|
||
|
|
{
|
||
|
|
mdns_ip_addr_t *a = r->addr;
|
||
|
|
while (a) {
|
||
|
|
if (a->addr.type == ip->type) {
|
||
|
|
#ifdef CONFIG_LWIP_IPV4
|
||
|
|
if (a->addr.type == ESP_IPADDR_TYPE_V4 && a->addr.u_addr.ip4.addr == ip->u_addr.ip4.addr) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
#ifdef CONFIG_LWIP_IPV6
|
||
|
|
if (a->addr.type == ESP_IPADDR_TYPE_V6 && !memcmp(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
a = a->next;
|
||
|
|
}
|
||
|
|
a = mdns_priv_result_addr_create_ip(ip);
|
||
|
|
if (!a) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
a->next = r->addr;
|
||
|
|
r->addr = a;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to add A/AAAA data to search result
|
||
|
|
*/
|
||
|
|
void mdns_priv_query_result_add_ip(mdns_search_once_t *search, const char *hostname, esp_ip_addr_t *ip,
|
||
|
|
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl)
|
||
|
|
{
|
||
|
|
mdns_result_t *r = NULL;
|
||
|
|
mdns_ip_addr_t *a = NULL;
|
||
|
|
|
||
|
|
if ((search->type == MDNS_TYPE_A && ip->type == ESP_IPADDR_TYPE_V4)
|
||
|
|
|| (search->type == MDNS_TYPE_AAAA && ip->type == ESP_IPADDR_TYPE_V6)
|
||
|
|
|| search->type == MDNS_TYPE_ANY) {
|
||
|
|
r = search->result;
|
||
|
|
while (r) {
|
||
|
|
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol) {
|
||
|
|
result_add_ip(r, ip);
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
if (!search->max_results || search->num_results < search->max_results) {
|
||
|
|
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||
|
|
if (!r) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(r, 0, sizeof(mdns_result_t));
|
||
|
|
|
||
|
|
a = mdns_priv_result_addr_create_ip(ip);
|
||
|
|
if (!a) {
|
||
|
|
mdns_mem_free(r);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
a->next = r->addr;
|
||
|
|
r->hostname = mdns_mem_strdup(hostname);
|
||
|
|
r->addr = a;
|
||
|
|
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||
|
|
r->ip_protocol = ip_protocol;
|
||
|
|
r->next = search->result;
|
||
|
|
r->ttl = ttl;
|
||
|
|
search->result = r;
|
||
|
|
search->num_results++;
|
||
|
|
}
|
||
|
|
} else if (search->type == MDNS_TYPE_PTR || search->type == MDNS_TYPE_SRV) {
|
||
|
|
r = search->result;
|
||
|
|
while (r) {
|
||
|
|
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
|
||
|
|
result_add_ip(r, ip);
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to add SRV data to search result
|
||
|
|
*/
|
||
|
|
void mdns_priv_query_result_add_srv(mdns_search_once_t *search, const char *hostname, uint16_t port,
|
||
|
|
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl)
|
||
|
|
{
|
||
|
|
mdns_result_t *r = search->result;
|
||
|
|
while (r) {
|
||
|
|
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
if (!search->max_results || search->num_results < search->max_results) {
|
||
|
|
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||
|
|
if (!r) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(r, 0, sizeof(mdns_result_t));
|
||
|
|
r->hostname = mdns_mem_strdup(hostname);
|
||
|
|
if (!r->hostname) {
|
||
|
|
mdns_mem_free(r);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (search->instance) {
|
||
|
|
r->instance_name = mdns_mem_strdup(search->instance);
|
||
|
|
}
|
||
|
|
r->service_type = mdns_mem_strdup(search->service);
|
||
|
|
r->proto = mdns_mem_strdup(search->proto);
|
||
|
|
r->port = port;
|
||
|
|
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||
|
|
r->ip_protocol = ip_protocol;
|
||
|
|
r->ttl = ttl;
|
||
|
|
r->next = search->result;
|
||
|
|
search->result = r;
|
||
|
|
search->num_results++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to add PTR data to search result
|
||
|
|
*/
|
||
|
|
mdns_result_t *mdns_priv_query_result_add_ptr(mdns_search_once_t *search, const char *instance,
|
||
|
|
const char *service_type, const char *proto, mdns_if_t tcpip_if,
|
||
|
|
mdns_ip_protocol_t ip_protocol, uint32_t ttl)
|
||
|
|
{
|
||
|
|
mdns_result_t *r = search->result;
|
||
|
|
while (r) {
|
||
|
|
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name)) {
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
if (!search->max_results || search->num_results < search->max_results) {
|
||
|
|
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||
|
|
if (!r) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(r, 0, sizeof(mdns_result_t));
|
||
|
|
r->instance_name = mdns_mem_strdup(instance);
|
||
|
|
r->service_type = mdns_mem_strdup(service_type);
|
||
|
|
r->proto = mdns_mem_strdup(proto);
|
||
|
|
if (!r->instance_name) {
|
||
|
|
mdns_mem_free(r);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||
|
|
r->ip_protocol = ip_protocol;
|
||
|
|
r->ttl = ttl;
|
||
|
|
r->next = search->result;
|
||
|
|
search->result = r;
|
||
|
|
search->num_results++;
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_ip_addr_t *mdns_priv_result_addr_create_ip(esp_ip_addr_t *ip)
|
||
|
|
{
|
||
|
|
mdns_ip_addr_t *a = (mdns_ip_addr_t *)mdns_mem_malloc(sizeof(mdns_ip_addr_t));
|
||
|
|
if (!a) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
memset(a, 0, sizeof(mdns_ip_addr_t));
|
||
|
|
a->addr.type = ip->type;
|
||
|
|
if (ip->type == ESP_IPADDR_TYPE_V6) {
|
||
|
|
memcpy(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16);
|
||
|
|
} else {
|
||
|
|
a->addr.u_addr.ip4.addr = ip->u_addr.ip4.addr;
|
||
|
|
}
|
||
|
|
return a;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief MDNS_PUBLIC_API
|
||
|
|
* */
|
||
|
|
void mdns_query_results_free(mdns_result_t *results)
|
||
|
|
{
|
||
|
|
mdns_priv_service_lock();
|
||
|
|
mdns_priv_query_results_free(results);
|
||
|
|
mdns_priv_service_unlock();
|
||
|
|
}
|
||
|
|
|
||
|
|
esp_err_t mdns_query_async_delete(mdns_search_once_t *search)
|
||
|
|
{
|
||
|
|
if (!search) {
|
||
|
|
return ESP_ERR_INVALID_ARG;
|
||
|
|
}
|
||
|
|
if (search->state != SEARCH_OFF) {
|
||
|
|
return ESP_ERR_INVALID_STATE;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_priv_service_lock();
|
||
|
|
search_free(search);
|
||
|
|
mdns_priv_service_unlock();
|
||
|
|
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool mdns_query_async_get_results(mdns_search_once_t *search, uint32_t timeout, mdns_result_t **results, uint8_t *num_results)
|
||
|
|
{
|
||
|
|
if (xSemaphoreTake(search->done_semaphore, pdMS_TO_TICKS(timeout)) == pdTRUE) {
|
||
|
|
if (results) {
|
||
|
|
*results = search->result;
|
||
|
|
}
|
||
|
|
if (num_results) {
|
||
|
|
*num_results = search->num_results;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_search_once_t *mdns_query_async_new(const char *name, const char *service, const char *proto, uint16_t type,
|
||
|
|
uint32_t timeout, size_t max_results, mdns_query_notify_t notifier)
|
||
|
|
{
|
||
|
|
mdns_search_once_t *search = NULL;
|
||
|
|
|
||
|
|
if (!mdns_priv_is_server_init() || !timeout || mdns_utils_str_null_or_empty(service) != mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
search = search_init(name, service, proto, type, type != MDNS_TYPE_PTR, timeout, max_results, notifier);
|
||
|
|
if (!search) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (send_search_action(ACTION_SEARCH_ADD, search)) {
|
||
|
|
search_free(search);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return search;
|
||
|
|
}
|
||
|
|
|
||
|
|
esp_err_t mdns_query_generic(const char *name, const char *service, const char *proto, uint16_t type, mdns_query_transmission_type_t transmission_type, uint32_t timeout, size_t max_results, mdns_result_t **results)
|
||
|
|
{
|
||
|
|
mdns_search_once_t *search = NULL;
|
||
|
|
|
||
|
|
*results = NULL;
|
||
|
|
|
||
|
|
if (!mdns_priv_is_server_init()) {
|
||
|
|
return ESP_ERR_INVALID_STATE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!timeout || mdns_utils_str_null_or_empty(service) != mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
return ESP_ERR_INVALID_ARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
search = search_init(name, service, proto, type, transmission_type == MDNS_QUERY_UNICAST, timeout, max_results,
|
||
|
|
NULL);
|
||
|
|
if (!search) {
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (send_search_action(ACTION_SEARCH_ADD, search)) {
|
||
|
|
search_free(search);
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
xSemaphoreTake(search->done_semaphore, portMAX_DELAY);
|
||
|
|
|
||
|
|
*results = search->result;
|
||
|
|
search_free(search);
|
||
|
|
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
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)
|
||
|
|
{
|
||
|
|
return mdns_query_generic(name, service_type, proto, type, type != MDNS_TYPE_PTR, timeout, max_results, results);
|
||
|
|
}
|
||
|
|
|
||
|
|
esp_err_t mdns_query_ptr(const char *service, const char *proto, uint32_t timeout, size_t max_results, mdns_result_t **results)
|
||
|
|
{
|
||
|
|
if (mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
return ESP_ERR_INVALID_ARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
return mdns_query(NULL, service, proto, MDNS_TYPE_PTR, timeout, max_results, results);
|
||
|
|
}
|
||
|
|
|
||
|
|
esp_err_t mdns_query_srv(const char *instance, const char *service, const char *proto, uint32_t timeout, mdns_result_t **result)
|
||
|
|
{
|
||
|
|
if (mdns_utils_str_null_or_empty(instance) || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
return ESP_ERR_INVALID_ARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
return mdns_query(instance, service, proto, MDNS_TYPE_SRV, timeout, 1, result);
|
||
|
|
}
|
||
|
|
|
||
|
|
esp_err_t mdns_query_txt(const char *instance, const char *service, const char *proto, uint32_t timeout, mdns_result_t **result)
|
||
|
|
{
|
||
|
|
if (mdns_utils_str_null_or_empty(instance) || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
return ESP_ERR_INVALID_ARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
return mdns_query(instance, service, proto, MDNS_TYPE_TXT, timeout, 1, result);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef CONFIG_LWIP_IPV4
|
||
|
|
esp_err_t mdns_query_a(const char *name, uint32_t timeout, esp_ip4_addr_t *addr)
|
||
|
|
{
|
||
|
|
mdns_result_t *result = NULL;
|
||
|
|
esp_err_t err;
|
||
|
|
|
||
|
|
if (mdns_utils_str_null_or_empty(name)) {
|
||
|
|
return ESP_ERR_INVALID_ARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (strstr(name, ".local")) {
|
||
|
|
ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain");
|
||
|
|
}
|
||
|
|
|
||
|
|
err = mdns_query(name, NULL, NULL, MDNS_TYPE_A, timeout, 1, &result);
|
||
|
|
|
||
|
|
if (err) {
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!result) {
|
||
|
|
return ESP_ERR_NOT_FOUND;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_ip_addr_t *a = result->addr;
|
||
|
|
while (a) {
|
||
|
|
if (a->addr.type == ESP_IPADDR_TYPE_V4) {
|
||
|
|
addr->addr = a->addr.u_addr.ip4.addr;
|
||
|
|
mdns_query_results_free(result);
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
a = a->next;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_query_results_free(result);
|
||
|
|
return ESP_ERR_NOT_FOUND;
|
||
|
|
}
|
||
|
|
#endif /* CONFIG_LWIP_IPV4 */
|
||
|
|
|
||
|
|
#ifdef CONFIG_LWIP_IPV6
|
||
|
|
esp_err_t mdns_query_aaaa(const char *name, uint32_t timeout, esp_ip6_addr_t *addr)
|
||
|
|
{
|
||
|
|
mdns_result_t *result = NULL;
|
||
|
|
esp_err_t err;
|
||
|
|
|
||
|
|
if (mdns_utils_str_null_or_empty(name)) {
|
||
|
|
return ESP_ERR_INVALID_ARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (strstr(name, ".local")) {
|
||
|
|
ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain");
|
||
|
|
}
|
||
|
|
|
||
|
|
err = mdns_query(name, NULL, NULL, MDNS_TYPE_AAAA, timeout, 1, &result);
|
||
|
|
|
||
|
|
if (err) {
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!result) {
|
||
|
|
return ESP_ERR_NOT_FOUND;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_ip_addr_t *a = result->addr;
|
||
|
|
while (a) {
|
||
|
|
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
|
||
|
|
memcpy(addr->addr, a->addr.u_addr.ip6.addr, 16);
|
||
|
|
mdns_query_results_free(result);
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
a = a->next;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdns_query_results_free(result);
|
||
|
|
return ESP_ERR_NOT_FOUND;
|
||
|
|
}
|
||
|
|
#endif /* CONFIG_LWIP_IPV6 */
|