mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-04 16:19:20 +01:00
657 lines
21 KiB
C
657 lines
21 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_browser.h"
|
||
|
|
#include "mdns_mem_caps.h"
|
||
|
|
#include "mdns_debug.h"
|
||
|
|
#include "mdns_utils.h"
|
||
|
|
#include "mdns_querier.h"
|
||
|
|
#include "mdns_responder.h"
|
||
|
|
#include "mdns_netif.h"
|
||
|
|
#include "mdns_service.h"
|
||
|
|
#include "esp_log.h"
|
||
|
|
|
||
|
|
static const char *TAG = "mdns_browser";
|
||
|
|
|
||
|
|
static mdns_browse_t *s_browse;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Browse action
|
||
|
|
*/
|
||
|
|
static esp_err_t send_browse_action(mdns_action_type_t type, mdns_browse_t *browse)
|
||
|
|
{
|
||
|
|
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.browse_add.browse = browse;
|
||
|
|
if (!mdns_priv_queue_action(action)) {
|
||
|
|
mdns_mem_free(action);
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Free a browse item (Not free the list).
|
||
|
|
*/
|
||
|
|
static void browse_item_free(mdns_browse_t *browse)
|
||
|
|
{
|
||
|
|
mdns_mem_free(browse->service);
|
||
|
|
mdns_mem_free(browse->proto);
|
||
|
|
if (browse->result) {
|
||
|
|
mdns_priv_query_results_free(browse->result);
|
||
|
|
}
|
||
|
|
mdns_mem_free(browse);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void browse_sync(mdns_browse_sync_t *browse_sync)
|
||
|
|
{
|
||
|
|
mdns_browse_t *browse = browse_sync->browse;
|
||
|
|
mdns_browse_result_sync_t *sync_result = browse_sync->sync_result;
|
||
|
|
while (sync_result) {
|
||
|
|
mdns_result_t *result = sync_result->result;
|
||
|
|
DBG_BROWSE_RESULTS(result, browse_sync->browse);
|
||
|
|
browse->notifier(result);
|
||
|
|
if (result->ttl == 0) {
|
||
|
|
queueDetach(mdns_result_t, browse->result, result);
|
||
|
|
// Just free current result
|
||
|
|
result->next = NULL;
|
||
|
|
mdns_query_results_free(result);
|
||
|
|
}
|
||
|
|
sync_result = sync_result->next;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Send PTR query packet to all available interfaces for browsing.
|
||
|
|
*/
|
||
|
|
static void browse_send(mdns_browse_t *browse, mdns_if_t interface)
|
||
|
|
{
|
||
|
|
// Using search once for sending the PTR query
|
||
|
|
mdns_search_once_t search = {0};
|
||
|
|
|
||
|
|
search.instance = NULL;
|
||
|
|
search.service = browse->service;
|
||
|
|
search.proto = browse->proto;
|
||
|
|
search.type = MDNS_TYPE_PTR;
|
||
|
|
search.unicast = false;
|
||
|
|
search.result = NULL;
|
||
|
|
search.next = NULL;
|
||
|
|
|
||
|
|
for (uint8_t protocol_idx = 0; protocol_idx < MDNS_IP_PROTOCOL_MAX; protocol_idx++) {
|
||
|
|
mdns_priv_query_send(&search, interface, (mdns_ip_protocol_t) protocol_idx);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdns_priv_browse_send_all(mdns_if_t mdns_if)
|
||
|
|
{
|
||
|
|
mdns_browse_t *browse = s_browse;
|
||
|
|
while (browse) {
|
||
|
|
browse_send(browse, mdns_if);
|
||
|
|
browse = browse->next;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdns_priv_browse_free(void)
|
||
|
|
{
|
||
|
|
while (s_browse) {
|
||
|
|
mdns_browse_t *b = s_browse;
|
||
|
|
s_browse = s_browse->next;
|
||
|
|
browse_item_free(b);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Mark browse as finished, remove and free it from browse chain
|
||
|
|
*/
|
||
|
|
static void browse_finish(mdns_browse_t *browse)
|
||
|
|
{
|
||
|
|
browse->state = BROWSE_OFF;
|
||
|
|
mdns_browse_t *b = s_browse;
|
||
|
|
mdns_browse_t *target_free = NULL;
|
||
|
|
while (b) {
|
||
|
|
if (strlen(b->service) == strlen(browse->service) && memcmp(b->service, browse->service, strlen(b->service)) == 0 &&
|
||
|
|
strlen(b->proto) == strlen(browse->proto) && memcmp(b->proto, browse->proto, strlen(b->proto)) == 0) {
|
||
|
|
target_free = b;
|
||
|
|
b = b->next;
|
||
|
|
queueDetach(mdns_browse_t, s_browse, target_free);
|
||
|
|
browse_item_free(target_free);
|
||
|
|
} else {
|
||
|
|
b = b->next;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
browse_item_free(browse);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Allocate new browse structure
|
||
|
|
*/
|
||
|
|
static mdns_browse_t *browse_init(const char *service, const char *proto, mdns_browse_notify_t notifier)
|
||
|
|
{
|
||
|
|
mdns_browse_t *browse = (mdns_browse_t *)mdns_mem_malloc(sizeof(mdns_browse_t));
|
||
|
|
|
||
|
|
if (!browse) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
memset(browse, 0, sizeof(mdns_browse_t));
|
||
|
|
|
||
|
|
browse->state = BROWSE_INIT;
|
||
|
|
if (!mdns_utils_str_null_or_empty(service)) {
|
||
|
|
browse->service = mdns_mem_strndup(service, MDNS_NAME_BUF_LEN - 1);
|
||
|
|
if (!browse->service) {
|
||
|
|
browse_item_free(browse);
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
browse->proto = mdns_mem_strndup(proto, MDNS_NAME_BUF_LEN - 1);
|
||
|
|
if (!browse->proto) {
|
||
|
|
browse_item_free(browse);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
browse->notifier = notifier;
|
||
|
|
return browse;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Add new browse to the browse chain
|
||
|
|
*/
|
||
|
|
static void browse_add(mdns_browse_t *browse)
|
||
|
|
{
|
||
|
|
browse->state = BROWSE_RUNNING;
|
||
|
|
mdns_browse_t *queue = s_browse;
|
||
|
|
bool found = false;
|
||
|
|
// looking for this browse in active browses
|
||
|
|
while (queue) {
|
||
|
|
if (strlen(queue->service) == strlen(browse->service) && memcmp(queue->service, browse->service, strlen(queue->service)) == 0 &&
|
||
|
|
strlen(queue->proto) == strlen(browse->proto) && memcmp(queue->proto, browse->proto, strlen(queue->proto)) == 0) {
|
||
|
|
found = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
queue = queue->next;
|
||
|
|
}
|
||
|
|
if (!found) {
|
||
|
|
browse->next = s_browse;
|
||
|
|
s_browse = browse;
|
||
|
|
}
|
||
|
|
for (uint8_t interface_idx = 0; interface_idx < MDNS_MAX_INTERFACES; interface_idx++) {
|
||
|
|
browse_send(browse, (mdns_if_t) interface_idx);
|
||
|
|
}
|
||
|
|
if (found) {
|
||
|
|
browse_item_free(browse);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from packet parser to find matching running search
|
||
|
|
*/
|
||
|
|
mdns_browse_t *mdns_priv_browse_find(mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||
|
|
{
|
||
|
|
mdns_browse_t *b = s_browse;
|
||
|
|
// For browse, we only care about the SRV, TXT, A and AAAA
|
||
|
|
if (type != MDNS_TYPE_SRV && type != MDNS_TYPE_A && type != MDNS_TYPE_AAAA && type != MDNS_TYPE_TXT) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
mdns_result_t *r = NULL;
|
||
|
|
while (b) {
|
||
|
|
if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) {
|
||
|
|
if (strcasecmp(name->service, b->service)
|
||
|
|
|| strcasecmp(name->proto, b->proto)) {
|
||
|
|
b = b->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
return b;
|
||
|
|
} else if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) {
|
||
|
|
r = b->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 b;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
b = b->next;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return b;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void sync_browse_result_link_free(mdns_browse_sync_t *browse_sync)
|
||
|
|
{
|
||
|
|
mdns_browse_result_sync_t *current = browse_sync->sync_result;
|
||
|
|
mdns_browse_result_sync_t *need_free;
|
||
|
|
while (current) {
|
||
|
|
need_free = current;
|
||
|
|
current = current->next;
|
||
|
|
mdns_mem_free(need_free);
|
||
|
|
}
|
||
|
|
mdns_mem_free(browse_sync);
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdns_priv_browse_action(mdns_action_t *action, mdns_action_subtype_t type)
|
||
|
|
{
|
||
|
|
if (type == ACTION_RUN) {
|
||
|
|
switch (action->type) {
|
||
|
|
case ACTION_BROWSE_ADD:
|
||
|
|
browse_add(action->data.browse_add.browse);
|
||
|
|
break;
|
||
|
|
case ACTION_BROWSE_SYNC:
|
||
|
|
browse_sync(action->data.browse_sync.browse_sync);
|
||
|
|
sync_browse_result_link_free(action->data.browse_sync.browse_sync);
|
||
|
|
break;
|
||
|
|
case ACTION_BROWSE_END:
|
||
|
|
browse_finish(action->data.browse_add.browse);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
abort();
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (type == ACTION_CLEANUP) {
|
||
|
|
switch (action->type) {
|
||
|
|
case ACTION_BROWSE_ADD:
|
||
|
|
//fallthrough
|
||
|
|
case ACTION_BROWSE_END:
|
||
|
|
browse_item_free(action->data.browse_add.browse);
|
||
|
|
break;
|
||
|
|
case ACTION_BROWSE_SYNC:
|
||
|
|
sync_browse_result_link_free(action->data.browse_sync.browse_sync);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
abort();
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Add result to browse, only add when the result is a new one.
|
||
|
|
*/
|
||
|
|
static esp_err_t add_browse_result(mdns_browse_sync_t *sync_browse, mdns_result_t *r)
|
||
|
|
{
|
||
|
|
mdns_browse_result_sync_t *sync_r = sync_browse->sync_result;
|
||
|
|
while (sync_r) {
|
||
|
|
if (sync_r->result == r) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
sync_r = sync_r->next;
|
||
|
|
}
|
||
|
|
if (!sync_r) {
|
||
|
|
// Do not find, need to add the result to the list
|
||
|
|
mdns_browse_result_sync_t *new = (mdns_browse_result_sync_t *)mdns_mem_malloc(sizeof(mdns_browse_result_sync_t));
|
||
|
|
|
||
|
|
if (!new) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
new->result = r;
|
||
|
|
new->next = sync_browse->sync_result;
|
||
|
|
sync_browse->sync_result = new;
|
||
|
|
}
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to add A/AAAA data to search result
|
||
|
|
*/
|
||
|
|
void mdns_priv_browse_result_add_ip(mdns_browse_t *browse, const char *hostname, esp_ip_addr_t *ip,
|
||
|
|
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse)
|
||
|
|
{
|
||
|
|
if (out_sync_browse->browse == NULL) {
|
||
|
|
return;
|
||
|
|
} else {
|
||
|
|
if (out_sync_browse->browse != browse) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mdns_result_t *r = NULL;
|
||
|
|
mdns_ip_addr_t *r_a = NULL;
|
||
|
|
if (browse) {
|
||
|
|
r = browse->result;
|
||
|
|
while (r) {
|
||
|
|
if (r->ip_protocol == ip_protocol) {
|
||
|
|
// Find the target result in browse result.
|
||
|
|
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
|
||
|
|
r_a = r->addr;
|
||
|
|
// Check if the address has already added in result.
|
||
|
|
while (r_a) {
|
||
|
|
#ifdef CONFIG_LWIP_IPV4
|
||
|
|
if (r_a->addr.type == ip->type && r_a->addr.type == ESP_IPADDR_TYPE_V4 && r_a->addr.u_addr.ip4.addr == ip->u_addr.ip4.addr) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
#ifdef CONFIG_LWIP_IPV6
|
||
|
|
if (r_a->addr.type == ip->type && r_a->addr.type == ESP_IPADDR_TYPE_V6 && !memcmp(r_a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
r_a = r_a->next;
|
||
|
|
}
|
||
|
|
if (!r_a) {
|
||
|
|
// The current IP is a new one, add it to the link list.
|
||
|
|
mdns_ip_addr_t *a = NULL;
|
||
|
|
a = mdns_priv_result_addr_create_ip(ip);
|
||
|
|
if (!a) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
a->next = r->addr;
|
||
|
|
r->addr = a;
|
||
|
|
if (r->ttl != ttl) {
|
||
|
|
if (r->ttl == 0) {
|
||
|
|
r->ttl = ttl;
|
||
|
|
} else {
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool is_txt_item_in_list(mdns_txt_item_t txt, uint8_t txt_value_len, mdns_txt_item_t *txt_list, uint8_t *txt_value_len_list, size_t txt_count)
|
||
|
|
{
|
||
|
|
for (size_t i = 0; i < txt_count; i++) {
|
||
|
|
if (strcmp(txt.key, txt_list[i].key) == 0) {
|
||
|
|
if (txt_value_len == txt_value_len_list[i] && memcmp(txt.value, txt_list[i].value, txt_value_len) == 0) {
|
||
|
|
return true;
|
||
|
|
} else {
|
||
|
|
// The key value is unique, so there is no need to continue searching.
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to add TXT data to search result
|
||
|
|
*/
|
||
|
|
void mdns_priv_browse_result_add_txt(mdns_browse_t *browse, const char *instance, const char *service, const char *proto,
|
||
|
|
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_browse_sync_t *out_sync_browse)
|
||
|
|
{
|
||
|
|
if (out_sync_browse->browse == NULL) {
|
||
|
|
return;
|
||
|
|
} else {
|
||
|
|
if (out_sync_browse->browse != browse) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mdns_result_t *r = browse->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_utils_str_null_or_empty(r->service_type) && !strcasecmp(service, r->service_type) &&
|
||
|
|
!mdns_utils_str_null_or_empty(r->proto) && !strcasecmp(proto, r->proto)) {
|
||
|
|
bool should_update = false;
|
||
|
|
if (r->txt) {
|
||
|
|
// Check if txt changed
|
||
|
|
if (txt_count != r->txt_count) {
|
||
|
|
should_update = true;
|
||
|
|
} else {
|
||
|
|
for (size_t txt_index = 0; txt_index < txt_count; txt_index++) {
|
||
|
|
if (!is_txt_item_in_list(txt[txt_index], txt_value_len[txt_index], r->txt, r->txt_value_len, r->txt_count)) {
|
||
|
|
should_update = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// If the result has a previous txt entry, we delete it and re-add.
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
r->txt = txt;
|
||
|
|
r->txt_value_len = txt_value_len;
|
||
|
|
r->txt_count = txt_count;
|
||
|
|
if (r->ttl != ttl) {
|
||
|
|
uint32_t previous_ttl = r->ttl;
|
||
|
|
if (r->ttl == 0) {
|
||
|
|
r->ttl = ttl;
|
||
|
|
} else {
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
}
|
||
|
|
if (previous_ttl != r->ttl) {
|
||
|
|
should_update = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (should_update) {
|
||
|
|
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
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->instance_name = mdns_mem_strdup(instance);
|
||
|
|
r->service_type = mdns_mem_strdup(service);
|
||
|
|
r->proto = mdns_mem_strdup(proto);
|
||
|
|
if (!r->instance_name || !r->service_type || !r->proto) {
|
||
|
|
mdns_mem_free(r->instance_name);
|
||
|
|
mdns_mem_free(r->service_type);
|
||
|
|
mdns_mem_free(r->proto);
|
||
|
|
mdns_mem_free(r);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
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 = browse->result;
|
||
|
|
browse->result = r;
|
||
|
|
add_browse_result(out_sync_browse, r);
|
||
|
|
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);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
static esp_err_t copy_address_in_previous_result(mdns_result_t *result_list, mdns_result_t *r)
|
||
|
|
{
|
||
|
|
while (result_list) {
|
||
|
|
if (!mdns_utils_str_null_or_empty(result_list->hostname) && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(result_list->hostname, r->hostname) &&
|
||
|
|
result_list->ip_protocol == r->ip_protocol && result_list->addr && !r->addr) {
|
||
|
|
// If there is a same hostname in previous result, we need to copy the address here.
|
||
|
|
r->addr = mdns_utils_copy_address_list(result_list->addr);
|
||
|
|
if (!r->addr) {
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
} else {
|
||
|
|
result_list = result_list->next;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Called from parser to add SRV data to search result
|
||
|
|
*/
|
||
|
|
void mdns_priv_browse_result_add_srv(mdns_browse_t *browse, const char *hostname, const char *instance, const char *service, const char *proto,
|
||
|
|
uint16_t port, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse)
|
||
|
|
{
|
||
|
|
if (out_sync_browse->browse == NULL) {
|
||
|
|
return;
|
||
|
|
} else {
|
||
|
|
if (out_sync_browse->browse != browse) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mdns_result_t *r = browse->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_utils_str_null_or_empty(r->service_type) && !strcasecmp(service, r->service_type) &&
|
||
|
|
!mdns_utils_str_null_or_empty(r->proto) && !strcasecmp(proto, r->proto)) {
|
||
|
|
if (mdns_utils_str_null_or_empty(r->hostname) || strcasecmp(hostname, r->hostname)) {
|
||
|
|
r->hostname = mdns_mem_strdup(hostname);
|
||
|
|
r->port = port;
|
||
|
|
if (!r->hostname) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!r->addr) {
|
||
|
|
esp_err_t err = copy_address_in_previous_result(browse->result, r);
|
||
|
|
if (err == ESP_ERR_NO_MEM) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (r->ttl != ttl) {
|
||
|
|
uint32_t previous_ttl = r->ttl;
|
||
|
|
if (r->ttl == 0) {
|
||
|
|
r->ttl = ttl;
|
||
|
|
} else {
|
||
|
|
mdns_priv_query_update_result_ttl(r, ttl);
|
||
|
|
}
|
||
|
|
if (previous_ttl != r->ttl) {
|
||
|
|
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
r = r->next;
|
||
|
|
}
|
||
|
|
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);
|
||
|
|
r->instance_name = mdns_mem_strdup(instance);
|
||
|
|
r->service_type = mdns_mem_strdup(service);
|
||
|
|
r->proto = mdns_mem_strdup(proto);
|
||
|
|
if (!r->hostname || !r->instance_name || !r->service_type || !r->proto) {
|
||
|
|
HOOK_MALLOC_FAILED;
|
||
|
|
mdns_mem_free(r->hostname);
|
||
|
|
mdns_mem_free(r->instance_name);
|
||
|
|
mdns_mem_free(r->service_type);
|
||
|
|
mdns_mem_free(r->proto);
|
||
|
|
mdns_mem_free(r);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
r->port = port;
|
||
|
|
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||
|
|
r->ip_protocol = ip_protocol;
|
||
|
|
r->ttl = ttl;
|
||
|
|
r->next = browse->result;
|
||
|
|
browse->result = r;
|
||
|
|
add_browse_result(out_sync_browse, r);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Browse sync result
|
||
|
|
*/
|
||
|
|
esp_err_t mdns_priv_browse_sync(mdns_browse_sync_t *browse_sync)
|
||
|
|
{
|
||
|
|
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 = ACTION_BROWSE_SYNC;
|
||
|
|
action->data.browse_sync.browse_sync = browse_sync;
|
||
|
|
if (!mdns_priv_queue_action(action)) {
|
||
|
|
mdns_mem_free(action);
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
return ESP_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @defgroup MDNS_PUBCLIC_API
|
||
|
|
*/
|
||
|
|
mdns_browse_t *mdns_browse_new(const char *service, const char *proto, mdns_browse_notify_t notifier)
|
||
|
|
{
|
||
|
|
mdns_browse_t *browse = NULL;
|
||
|
|
|
||
|
|
if (mdns_priv_is_server_init() || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
browse = browse_init(service, proto, notifier);
|
||
|
|
if (!browse) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (send_browse_action(ACTION_BROWSE_ADD, browse)) {
|
||
|
|
browse_item_free(browse);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return browse;
|
||
|
|
}
|
||
|
|
|
||
|
|
esp_err_t mdns_browse_delete(const char *service, const char *proto)
|
||
|
|
{
|
||
|
|
mdns_browse_t *browse = NULL;
|
||
|
|
|
||
|
|
if (!mdns_priv_is_server_init() || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||
|
|
return ESP_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
browse = browse_init(service, proto, NULL);
|
||
|
|
if (!browse) {
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (send_browse_action(ACTION_BROWSE_END, browse)) {
|
||
|
|
browse_item_free(browse);
|
||
|
|
return ESP_ERR_NO_MEM;
|
||
|
|
}
|
||
|
|
return ESP_OK;
|
||
|
|
}
|