mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-05 00:29:22 +01:00
The MDNS component has been refactored from a single monolithic file mdns.c into a set of focused modules with clear responsibilities. This restructuring maintains the same functionality while improving code organization, maintainability, and testability. In the stage#2 we will focus on module based tests In the stage#3 we will focus on small scale refators and optimizations
1282 lines
51 KiB
C
1282 lines
51 KiB
C
|
|
/*
|
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <string.h>
|
|
#include "sdkconfig.h"
|
|
#include "esp_log.h"
|
|
#include "mdns_private.h"
|
|
#include "mdns_networking.h"
|
|
#include "mdns.h"
|
|
#include "mdns_mem_caps.h"
|
|
#include "mdns_utils.h"
|
|
#include "mdns_debug.h"
|
|
#include "mdns_netif.h"
|
|
#include "mdns_send.h"
|
|
#include "mdns_browser.h"
|
|
#include "mdns_querier.h"
|
|
#include "mdns_pcb.h"
|
|
#include "mdns_responder.h"
|
|
|
|
static const char *TAG = "mdns_receive";
|
|
|
|
/**
|
|
* @brief Check if parsed name is discovery
|
|
*/
|
|
static bool is_discovery(mdns_name_t *name, uint16_t type)
|
|
{
|
|
return (
|
|
(name->host[0] && !strcasecmp(name->host, "_services"))
|
|
&& (name->service[0] && !strcasecmp(name->service, "_dns-sd"))
|
|
&& (name->proto[0] && !strcasecmp(name->proto, "_udp"))
|
|
&& (name->domain[0] && !strcasecmp(name->domain, MDNS_UTILS_DEFAULT_DOMAIN))
|
|
&& type == MDNS_TYPE_PTR
|
|
);
|
|
}
|
|
|
|
static mdns_srv_item_t *get_service_item_subtype(const char *subtype, const char *service, const char *proto)
|
|
{
|
|
mdns_srv_item_t *s = mdns_priv_get_services();
|
|
while (s) {
|
|
if (mdns_utils_service_match(s->service, service, proto, NULL)) {
|
|
mdns_subtype_t *subtype_item = s->service->subtype;
|
|
while (subtype_item) {
|
|
if (!strcasecmp(subtype_item->subtype, subtype)) {
|
|
return s;
|
|
}
|
|
subtype_item = subtype_item->next;
|
|
}
|
|
}
|
|
s = s->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the parsed name is ours (matches service or host name)
|
|
*/
|
|
static bool is_ours(mdns_name_t *name)
|
|
{
|
|
//domain have to be "local"
|
|
if (mdns_utils_str_null_or_empty(name->domain) || (strcasecmp(name->domain, MDNS_UTILS_DEFAULT_DOMAIN)
|
|
#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
|
|
&& strcasecmp(name->domain, "arpa")
|
|
#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */
|
|
)) {
|
|
return false;
|
|
}
|
|
|
|
//if service and proto are empty, host must match out hostname
|
|
if (mdns_utils_str_null_or_empty(name->service) && mdns_utils_str_null_or_empty(name->proto)) {
|
|
if (!mdns_utils_str_null_or_empty(name->host)
|
|
&& !mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname())
|
|
&& mdns_utils_hostname_is_ours(name->host)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//if service or proto is empty, name is invalid
|
|
if (mdns_utils_str_null_or_empty(name->service) || mdns_utils_str_null_or_empty(name->proto)) {
|
|
return false;
|
|
}
|
|
|
|
|
|
//find the service
|
|
mdns_srv_item_t *service;
|
|
if (name->sub) {
|
|
service = get_service_item_subtype(name->host, name->service, name->proto);
|
|
} else if (mdns_utils_str_null_or_empty(name->host)) {
|
|
service = mdns_utils_get_service_item(name->service, name->proto, NULL);
|
|
} else {
|
|
service = mdns_utils_get_service_item_instance(name->host, name->service, name->proto, NULL);
|
|
}
|
|
if (!service) {
|
|
return false;
|
|
}
|
|
|
|
//if query is PTR query and we have service, we have success
|
|
if (name->sub || mdns_utils_str_null_or_empty(name->host)) {
|
|
return true;
|
|
}
|
|
|
|
//OK we have host in the name. find what is the instance of the service
|
|
const char *instance = mdns_utils_get_service_instance_name(service->service);
|
|
if (instance == NULL) {
|
|
return false;
|
|
}
|
|
|
|
//compare the instance against the name
|
|
if (strcasecmp(name->host, instance) == 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Duplicate string or return error
|
|
*/
|
|
static esp_err_t strdup_check(char **out, char *in)
|
|
{
|
|
if (in && in[0]) {
|
|
*out = mdns_mem_strdup(in);
|
|
if (!*out) {
|
|
return ESP_FAIL;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
*out = NULL;
|
|
return ESP_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief Appends/increments a number to name/instance in case of collision
|
|
* */
|
|
static char *mangle_name(char *in)
|
|
{
|
|
char *p = strrchr(in, '-');
|
|
int suffix = 0;
|
|
if (p == NULL) {
|
|
//No - in ``in``
|
|
suffix = 2;
|
|
} else {
|
|
char *endp = NULL;
|
|
suffix = strtol(p + 1, &endp, 10);
|
|
if (*endp != 0) {
|
|
//suffix is not numerical
|
|
suffix = 2;
|
|
p = NULL; //so we append -suffix to the entire string
|
|
}
|
|
}
|
|
char *ret;
|
|
if (p == NULL) {
|
|
//need to add -2 to string
|
|
ret = mdns_mem_malloc(strlen(in) + 3);
|
|
if (ret == NULL) {
|
|
HOOK_MALLOC_FAILED;
|
|
return NULL;
|
|
}
|
|
sprintf(ret, "%s-2", in);
|
|
} else {
|
|
size_t in_len = strlen(in);
|
|
ret = mdns_mem_malloc(in_len + 2); //one extra byte in case 9-10 or 99-100 etc
|
|
if (ret == NULL) {
|
|
HOOK_MALLOC_FAILED;
|
|
return NULL;
|
|
}
|
|
memcpy(ret, in, in_len);
|
|
int baseLen = p - in; //length of 'bla' in 'bla-123'
|
|
//overwrite suffix with new suffix
|
|
sprintf(ret + baseLen, "-%d", suffix + 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get number of items in TXT parsed data
|
|
*/
|
|
static int get_txt_items_count(const uint8_t *data, size_t len)
|
|
{
|
|
if (len == 1) {
|
|
return 0;
|
|
}
|
|
|
|
int num_items = 0;
|
|
uint16_t i = 0;
|
|
size_t partLen = 0;
|
|
|
|
while (i < len) {
|
|
partLen = data[i++];
|
|
if (!partLen) {
|
|
break;
|
|
}
|
|
if ((i + partLen) > len) {
|
|
return -1;//error
|
|
}
|
|
i += partLen;
|
|
num_items++;
|
|
}
|
|
return num_items;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the length of TXT item's key name
|
|
*/
|
|
static int get_txt_item_len(const uint8_t *data, size_t len)
|
|
{
|
|
if (*data == '=') {
|
|
return -1;
|
|
}
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (data[i] == '=') {
|
|
return i;
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* @brief Create TXT result array from parsed TXT data
|
|
*/
|
|
static void result_txt_create(const uint8_t *data, size_t len, mdns_txt_item_t **out_txt, uint8_t **out_value_len,
|
|
size_t *out_count)
|
|
{
|
|
*out_txt = NULL;
|
|
*out_count = 0;
|
|
uint16_t i = 0, y;
|
|
size_t part_len = 0;
|
|
int num_items = get_txt_items_count(data, len);
|
|
if (num_items < 0 || num_items > SIZE_MAX / sizeof(mdns_txt_item_t)) {
|
|
// Error: num_items is incorrect (or too large to allocate)
|
|
return;
|
|
}
|
|
|
|
if (!num_items) {
|
|
return;
|
|
}
|
|
|
|
mdns_txt_item_t *txt = (mdns_txt_item_t *)mdns_mem_malloc(sizeof(mdns_txt_item_t) * num_items);
|
|
if (!txt) {
|
|
HOOK_MALLOC_FAILED;
|
|
return;
|
|
}
|
|
uint8_t *txt_value_len = (uint8_t *)mdns_mem_malloc(num_items);
|
|
if (!txt_value_len) {
|
|
mdns_mem_free(txt);
|
|
HOOK_MALLOC_FAILED;
|
|
return;
|
|
}
|
|
memset(txt, 0, sizeof(mdns_txt_item_t) * num_items);
|
|
memset(txt_value_len, 0, num_items);
|
|
size_t txt_num = 0;
|
|
|
|
while (i < len && txt_num < num_items) {
|
|
part_len = data[i++];
|
|
if (!part_len) {
|
|
break;
|
|
}
|
|
|
|
if ((i + part_len) > len) {
|
|
goto handle_error;//error
|
|
}
|
|
|
|
int name_len = get_txt_item_len(data + i, part_len);
|
|
if (name_len < 0) { //invalid item (no name)
|
|
i += part_len;
|
|
continue;
|
|
}
|
|
char *key = (char *) mdns_mem_malloc(name_len + 1);
|
|
if (!key) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto handle_error;//error
|
|
}
|
|
|
|
mdns_txt_item_t *t = &txt[txt_num];
|
|
uint8_t *value_len = &txt_value_len[txt_num];
|
|
txt_num++;
|
|
|
|
memcpy(key, data + i, name_len);
|
|
key[name_len] = 0;
|
|
i += name_len + 1;
|
|
t->key = key;
|
|
|
|
int new_value_len = part_len - name_len - 1;
|
|
if (new_value_len > 0) {
|
|
char *value = (char *) mdns_mem_malloc(new_value_len + 1);
|
|
if (!value) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto handle_error;//error
|
|
}
|
|
memcpy(value, data + i, new_value_len);
|
|
value[new_value_len] = 0;
|
|
*value_len = new_value_len;
|
|
i += new_value_len;
|
|
t->value = value;
|
|
} else {
|
|
t->value = NULL;
|
|
}
|
|
}
|
|
|
|
*out_txt = txt;
|
|
*out_count = txt_num;
|
|
*out_value_len = txt_value_len;
|
|
return;
|
|
|
|
handle_error :
|
|
for (y = 0; y < txt_num; y++) {
|
|
mdns_txt_item_t *t = &txt[y];
|
|
mdns_mem_free((char *)t->key);
|
|
mdns_mem_free((char *)t->value);
|
|
}
|
|
mdns_mem_free(txt_value_len);
|
|
mdns_mem_free(txt);
|
|
}
|
|
|
|
#ifdef CONFIG_LWIP_IPV4
|
|
/**
|
|
* @brief Detect IPv4 address collision
|
|
*/
|
|
static int check_a_collision(esp_ip4_addr_t *ip, mdns_if_t tcpip_if)
|
|
{
|
|
esp_netif_ip_info_t if_ip_info;
|
|
esp_netif_ip_info_t other_ip_info;
|
|
if (!ip->addr) {
|
|
return 1;//denial! they win
|
|
}
|
|
if (esp_netif_get_ip_info(mdns_priv_get_esp_netif(tcpip_if), &if_ip_info)) {
|
|
return 1;//they win
|
|
}
|
|
int ret = memcmp((uint8_t *)&if_ip_info.ip.addr, (uint8_t *)&ip->addr, sizeof(esp_ip4_addr_t));
|
|
if (ret > 0) {
|
|
return -1;//we win
|
|
} else if (ret < 0) {
|
|
//is it the other interface?
|
|
mdns_if_t other_if = mdns_priv_netif_get_other_interface(tcpip_if);
|
|
if (other_if == MDNS_MAX_INTERFACES) {
|
|
return 1;//AP interface! They win
|
|
}
|
|
if (esp_netif_get_ip_info(mdns_priv_get_esp_netif(other_if), &other_ip_info)) {
|
|
return 1;//IPv4 not active! They win
|
|
}
|
|
if (ip->addr != other_ip_info.ip.addr) {
|
|
return 1;//IPv4 not ours! They win
|
|
}
|
|
mdns_priv_pcb_set_duplicate(tcpip_if);
|
|
return 2;//they win
|
|
}
|
|
return 0;//same
|
|
}
|
|
#endif /* CONFIG_LWIP_IPV4 */
|
|
|
|
#ifdef CONFIG_LWIP_IPV6
|
|
/**
|
|
* @brief Detect IPv6 address collision
|
|
*/
|
|
static int check_aaaa_collision(esp_ip6_addr_t *ip, mdns_if_t tcpip_if)
|
|
{
|
|
struct esp_ip6_addr if_ip6;
|
|
struct esp_ip6_addr other_ip6;
|
|
if (mdns_utils_ipv6_address_is_zero(*ip)) {
|
|
return 1;//denial! they win
|
|
}
|
|
if (esp_netif_get_ip6_linklocal(mdns_priv_get_esp_netif(tcpip_if), &if_ip6)) {
|
|
return 1;//they win
|
|
}
|
|
int ret = memcmp((uint8_t *)&if_ip6.addr, (uint8_t *)ip->addr, MDNS_UTILS_SIZEOF_IP6_ADDR);
|
|
if (ret > 0) {
|
|
return -1;//we win
|
|
} else if (ret < 0) {
|
|
//is it the other interface?
|
|
mdns_if_t other_if = mdns_priv_netif_get_other_interface(tcpip_if);
|
|
if (other_if == MDNS_MAX_INTERFACES) {
|
|
return 1;//AP interface! They win
|
|
}
|
|
if (esp_netif_get_ip6_linklocal(mdns_priv_get_esp_netif(other_if), &other_ip6)) {
|
|
return 1;//IPv6 not active! They win
|
|
}
|
|
if (memcmp((uint8_t *)&other_ip6.addr, (uint8_t *)ip->addr, MDNS_UTILS_SIZEOF_IP6_ADDR)) {
|
|
return 1;//IPv6 not ours! They win
|
|
}
|
|
mdns_priv_pcb_set_duplicate(tcpip_if);
|
|
return 2;//they win
|
|
}
|
|
return 0;//same
|
|
}
|
|
#endif /* CONFIG_LWIP_IPV6 */
|
|
|
|
/**
|
|
* @brief Detect TXT collision
|
|
*/
|
|
static int check_txt_collision(mdns_service_t *service, const uint8_t *data, size_t len)
|
|
{
|
|
size_t data_len = 0;
|
|
if (len <= 1 && service->txt) { // len==0 means incorrect packet (and handled by the packet parser)
|
|
// but handled here again to fix clang-tidy warning on VLA "uint8_t our[0];"
|
|
return -1;//we win
|
|
} else if (len > 1 && !service->txt) {
|
|
return 1;//they win
|
|
} else if (len <= 1 && !service->txt) {
|
|
return 0;//same
|
|
}
|
|
|
|
mdns_txt_linked_item_t *txt = service->txt;
|
|
while (txt) {
|
|
data_len += 1 /* record-len */ + strlen(txt->key) + txt->value_len + (txt->value ? 1 : 0 /* "=" */);
|
|
txt = txt->next;
|
|
}
|
|
|
|
if (len > data_len) {
|
|
return 1;//they win
|
|
} else if (len < data_len) {
|
|
return -1;//we win
|
|
}
|
|
|
|
uint8_t ours[len];
|
|
uint16_t index = 0;
|
|
|
|
txt = service->txt;
|
|
while (txt) {
|
|
mdns_priv_append_one_txt_record_entry(ours, &index, txt);
|
|
txt = txt->next;
|
|
}
|
|
|
|
int ret = memcmp(ours, data, len);
|
|
if (ret > 0) {
|
|
return -1;//we win
|
|
} else if (ret < 0) {
|
|
return 1;//they win
|
|
}
|
|
return 0;//same
|
|
}
|
|
|
|
/**
|
|
* @brief Detect SRV collision
|
|
*/
|
|
static int check_srv_collision(mdns_service_t *service, uint16_t priority, uint16_t weight, uint16_t port, const char *host, const char *domain)
|
|
{
|
|
if (mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname())) {
|
|
return 0;
|
|
}
|
|
|
|
size_t our_host_len = strlen(mdns_priv_get_global_hostname());
|
|
size_t our_len = 14 + our_host_len;
|
|
|
|
size_t their_host_len = strlen(host);
|
|
size_t their_domain_len = strlen(domain);
|
|
size_t their_len = 9 + their_host_len + their_domain_len;
|
|
|
|
if (their_len > our_len) {
|
|
return 1;//they win
|
|
} else if (their_len < our_len) {
|
|
return -1;//we win
|
|
}
|
|
|
|
uint16_t our_index = 0;
|
|
uint8_t our_data[our_len];
|
|
mdns_utils_append_u16(our_data, &our_index, service->priority);
|
|
mdns_utils_append_u16(our_data, &our_index, service->weight);
|
|
mdns_utils_append_u16(our_data, &our_index, service->port);
|
|
our_data[our_index++] = our_host_len;
|
|
memcpy(our_data + our_index, mdns_priv_get_global_hostname(), our_host_len);
|
|
our_index += our_host_len;
|
|
our_data[our_index++] = 5;
|
|
memcpy(our_data + our_index, MDNS_UTILS_DEFAULT_DOMAIN, 5);
|
|
our_index += 5;
|
|
our_data[our_index++] = 0;
|
|
|
|
uint16_t their_index = 0;
|
|
uint8_t their_data[their_len];
|
|
mdns_utils_append_u16(their_data, &their_index, priority);
|
|
mdns_utils_append_u16(their_data, &their_index, weight);
|
|
mdns_utils_append_u16(their_data, &their_index, port);
|
|
their_data[their_index++] = their_host_len;
|
|
memcpy(their_data + their_index, host, their_host_len);
|
|
their_index += their_host_len;
|
|
their_data[their_index++] = their_domain_len;
|
|
memcpy(their_data + their_index, domain, their_domain_len);
|
|
their_index += their_domain_len;
|
|
their_data[their_index++] = 0;
|
|
|
|
int ret = memcmp(our_data, their_data, our_len);
|
|
if (ret > 0) {
|
|
return -1;//we win
|
|
} else if (ret < 0) {
|
|
return 1;//they win
|
|
}
|
|
return 0;//same
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the parsed name is self-hosted, i.e. we should resolve conflicts
|
|
*/
|
|
static bool is_name_selfhosted(mdns_name_t *name)
|
|
{
|
|
if (mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname())) { // self-hostname needs to be defined
|
|
return false;
|
|
}
|
|
|
|
// hostname only -- check if selfhosted name
|
|
if (mdns_utils_str_null_or_empty(name->service) && mdns_utils_str_null_or_empty(name->proto) &&
|
|
strcasecmp(name->host, mdns_priv_get_global_hostname()) == 0) {
|
|
return true;
|
|
}
|
|
|
|
// service -- check if selfhosted service
|
|
mdns_srv_item_t *srv = mdns_utils_get_service_item(name->service, name->proto, NULL);
|
|
if (srv && strcasecmp(mdns_priv_get_global_hostname(), srv->service->hostname) == 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Called from parser to check if question matches particular service
|
|
*/
|
|
static bool question_matches(mdns_parsed_question_t *question, uint16_t type, mdns_srv_item_t *service)
|
|
{
|
|
if (question->type != type) {
|
|
return false;
|
|
}
|
|
if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) {
|
|
return true;
|
|
} else if (type == MDNS_TYPE_PTR || type == MDNS_TYPE_SDPTR) {
|
|
if (question->service && question->proto && question->domain
|
|
&& !strcasecmp(service->service->service, question->service)
|
|
&& !strcasecmp(service->service->proto, question->proto)
|
|
&& !strcasecmp(MDNS_UTILS_DEFAULT_DOMAIN, question->domain)) {
|
|
if (!service->service->instance) {
|
|
return true;
|
|
} else if (service->service->instance && question->host && !strcasecmp(service->service->instance, question->host)) {
|
|
return true;
|
|
}
|
|
}
|
|
} else if (service && (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT)) {
|
|
const char *name = mdns_utils_get_service_instance_name(service->service);
|
|
if (name && question->host && question->service && question->proto && question->domain
|
|
&& !strcasecmp(name, question->host)
|
|
&& !strcasecmp(service->service->service, question->service)
|
|
&& !strcasecmp(service->service->proto, question->proto)
|
|
&& !strcasecmp(MDNS_UTILS_DEFAULT_DOMAIN, question->domain)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Removes saved question from parsed data
|
|
*/
|
|
static void remove_parsed_question(mdns_parsed_packet_t *parsed_packet, uint16_t type, mdns_srv_item_t *service)
|
|
{
|
|
mdns_parsed_question_t *q = parsed_packet->questions;
|
|
|
|
if (question_matches(q, type, service)) {
|
|
parsed_packet->questions = q->next;
|
|
mdns_mem_free(q->host);
|
|
mdns_mem_free(q->service);
|
|
mdns_mem_free(q->proto);
|
|
mdns_mem_free(q->domain);
|
|
mdns_mem_free(q);
|
|
return;
|
|
}
|
|
|
|
while (q->next) {
|
|
mdns_parsed_question_t *p = q->next;
|
|
if (question_matches(p, type, service)) {
|
|
q->next = p->next;
|
|
mdns_mem_free(p->host);
|
|
mdns_mem_free(p->service);
|
|
mdns_mem_free(p->proto);
|
|
mdns_mem_free(p->domain);
|
|
mdns_mem_free(p);
|
|
return;
|
|
}
|
|
q = q->next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief main packet parser
|
|
*
|
|
* @param packet the packet
|
|
*/
|
|
static void mdns_parse_packet(mdns_rx_packet_t *packet)
|
|
{
|
|
static mdns_name_t n;
|
|
mdns_header_t header;
|
|
const uint8_t *data = mdns_priv_get_packet_data(packet);
|
|
size_t len = mdns_priv_get_packet_len(packet);
|
|
const uint8_t *content = data + MDNS_HEAD_LEN;
|
|
bool do_not_reply = false;
|
|
mdns_search_once_t *search_result = NULL;
|
|
mdns_browse_t *browse_result = NULL;
|
|
char *browse_result_instance = NULL;
|
|
char *browse_result_service = NULL;
|
|
char *browse_result_proto = NULL;
|
|
mdns_browse_sync_t *out_sync_browse = NULL;
|
|
|
|
DBG_RX_PACKET(packet, data, len);
|
|
|
|
#ifndef CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES
|
|
// Check if the packet wasn't sent by us
|
|
#ifdef CONFIG_LWIP_IPV4
|
|
if (packet->ip_protocol == MDNS_IP_PROTOCOL_V4) {
|
|
esp_netif_ip_info_t if_ip_info;
|
|
if (esp_netif_get_ip_info(mdns_priv_get_esp_netif(packet->tcpip_if), &if_ip_info) == ESP_OK &&
|
|
memcmp(&if_ip_info.ip.addr, &packet->src.u_addr.ip4.addr, sizeof(esp_ip4_addr_t)) == 0) {
|
|
return;
|
|
}
|
|
}
|
|
#endif /* CONFIG_LWIP_IPV4 */
|
|
#ifdef CONFIG_LWIP_IPV6
|
|
if (packet->ip_protocol == MDNS_IP_PROTOCOL_V6) {
|
|
struct esp_ip6_addr if_ip6;
|
|
if (esp_netif_get_ip6_linklocal(mdns_priv_get_esp_netif(packet->tcpip_if), &if_ip6) == ESP_OK &&
|
|
memcmp(&if_ip6, &packet->src.u_addr.ip6, sizeof(esp_ip6_addr_t)) == 0) {
|
|
return;
|
|
}
|
|
}
|
|
#endif /* CONFIG_LWIP_IPV6 */
|
|
#endif // CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES
|
|
|
|
// Check for the minimum size of mdns packet
|
|
if (len <= MDNS_HEAD_ADDITIONAL_OFFSET) {
|
|
return;
|
|
}
|
|
|
|
mdns_parsed_packet_t *parsed_packet = (mdns_parsed_packet_t *)mdns_mem_malloc(sizeof(mdns_parsed_packet_t));
|
|
if (!parsed_packet) {
|
|
HOOK_MALLOC_FAILED;
|
|
return;
|
|
}
|
|
memset(parsed_packet, 0, sizeof(mdns_parsed_packet_t));
|
|
|
|
mdns_name_t *name = &n;
|
|
memset(name, 0, sizeof(mdns_name_t));
|
|
|
|
header.id = mdns_utils_read_u16(data, MDNS_HEAD_ID_OFFSET);
|
|
header.flags = mdns_utils_read_u16(data, MDNS_HEAD_FLAGS_OFFSET);
|
|
header.questions = mdns_utils_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET);
|
|
header.answers = mdns_utils_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET);
|
|
header.servers = mdns_utils_read_u16(data, MDNS_HEAD_SERVERS_OFFSET);
|
|
header.additional = mdns_utils_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET);
|
|
|
|
if (header.flags == MDNS_FLAGS_QR_AUTHORITATIVE && packet->src_port != MDNS_SERVICE_PORT) {
|
|
mdns_mem_free(parsed_packet);
|
|
return;
|
|
}
|
|
|
|
//if we have not set the hostname, we can not answer questions
|
|
if (header.questions && !header.answers && mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname())) {
|
|
mdns_mem_free(parsed_packet);
|
|
return;
|
|
}
|
|
|
|
parsed_packet->tcpip_if = packet->tcpip_if;
|
|
parsed_packet->ip_protocol = packet->ip_protocol;
|
|
parsed_packet->multicast = packet->multicast;
|
|
parsed_packet->authoritative = (header.flags == MDNS_FLAGS_QR_AUTHORITATIVE);
|
|
parsed_packet->distributed = header.flags == MDNS_FLAGS_DISTRIBUTED;
|
|
parsed_packet->id = header.id;
|
|
esp_netif_ip_addr_copy(&parsed_packet->src, &packet->src);
|
|
parsed_packet->src_port = packet->src_port;
|
|
parsed_packet->records = NULL;
|
|
|
|
if (header.questions) {
|
|
uint8_t qs = header.questions;
|
|
|
|
while (qs--) {
|
|
content = mdns_utils_parse_fqdn(data, content, name, len);
|
|
if (!content) {
|
|
header.answers = 0;
|
|
header.additional = 0;
|
|
header.servers = 0;
|
|
goto clear_rx_packet; // error
|
|
}
|
|
|
|
if (content + MDNS_CLASS_OFFSET + 1 >= data + len) {
|
|
goto clear_rx_packet; // malformed packet, won't read behind it
|
|
}
|
|
uint16_t type = mdns_utils_read_u16(content, MDNS_TYPE_OFFSET);
|
|
uint16_t mdns_class = mdns_utils_read_u16(content, MDNS_CLASS_OFFSET);
|
|
bool unicast = !!(mdns_class & 0x8000);
|
|
mdns_class &= 0x7FFF;
|
|
content = content + 4;
|
|
|
|
if (mdns_class != 0x0001 || name->invalid) { // bad class or invalid name for this question entry
|
|
continue;
|
|
}
|
|
|
|
if (is_discovery(name, type)) {
|
|
//service discovery
|
|
parsed_packet->discovery = true;
|
|
mdns_srv_item_t *a = mdns_priv_get_services();
|
|
while (a) {
|
|
mdns_parsed_question_t *question = (mdns_parsed_question_t *)mdns_mem_calloc(1, sizeof(mdns_parsed_question_t));
|
|
if (!question) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
question->next = parsed_packet->questions;
|
|
parsed_packet->questions = question;
|
|
|
|
question->unicast = unicast;
|
|
question->type = MDNS_TYPE_SDPTR;
|
|
question->host = NULL;
|
|
question->service = mdns_mem_strdup(a->service->service);
|
|
question->proto = mdns_mem_strdup(a->service->proto);
|
|
question->domain = mdns_mem_strdup(MDNS_UTILS_DEFAULT_DOMAIN);
|
|
if (!question->service || !question->proto || !question->domain) {
|
|
goto clear_rx_packet;
|
|
}
|
|
a = a->next;
|
|
}
|
|
continue;
|
|
}
|
|
if (!is_ours(name)) {
|
|
continue;
|
|
}
|
|
|
|
if (type == MDNS_TYPE_ANY && !mdns_utils_str_null_or_empty(name->host)) {
|
|
parsed_packet->probe = true;
|
|
}
|
|
|
|
mdns_parsed_question_t *question = (mdns_parsed_question_t *)mdns_mem_calloc(1, sizeof(mdns_parsed_question_t));
|
|
if (!question) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
question->next = parsed_packet->questions;
|
|
parsed_packet->questions = question;
|
|
|
|
question->unicast = unicast;
|
|
question->type = type;
|
|
question->sub = name->sub;
|
|
if (strdup_check(&(question->host), name->host)
|
|
|| strdup_check(&(question->service), name->service)
|
|
|| strdup_check(&(question->proto), name->proto)
|
|
|| strdup_check(&(question->domain), name->domain)) {
|
|
goto clear_rx_packet;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (header.questions && !parsed_packet->questions && !parsed_packet->discovery && !header.answers) {
|
|
goto clear_rx_packet;
|
|
} else if (header.answers || header.servers || header.additional) {
|
|
uint16_t recordIndex = 0;
|
|
|
|
while (content < (data + len)) {
|
|
|
|
content = mdns_utils_parse_fqdn(data, content, name, len);
|
|
if (!content) {
|
|
goto clear_rx_packet;
|
|
}
|
|
|
|
if (content + MDNS_LEN_OFFSET + 1 >= data + len) {
|
|
goto clear_rx_packet; // malformed packet, won't read behind it
|
|
}
|
|
uint16_t type = mdns_utils_read_u16(content, MDNS_TYPE_OFFSET);
|
|
uint16_t mdns_class = mdns_utils_read_u16(content, MDNS_CLASS_OFFSET);
|
|
uint32_t ttl = mdns_utils_read_u32(content, MDNS_TTL_OFFSET);
|
|
uint16_t data_len = mdns_utils_read_u16(content, MDNS_LEN_OFFSET);
|
|
const uint8_t *data_ptr = content + MDNS_DATA_OFFSET;
|
|
mdns_class &= 0x7FFF;
|
|
|
|
content = data_ptr + data_len;
|
|
if (content > (data + len) || data_len == 0) {
|
|
goto clear_rx_packet;
|
|
}
|
|
|
|
bool discovery = false;
|
|
bool ours = false;
|
|
mdns_srv_item_t *service = NULL;
|
|
mdns_parsed_record_type_t record_type = MDNS_ANSWER;
|
|
|
|
if (recordIndex >= (header.answers + header.servers)) {
|
|
record_type = MDNS_EXTRA;
|
|
} else if (recordIndex >= (header.answers)) {
|
|
record_type = MDNS_NS;
|
|
}
|
|
recordIndex++;
|
|
|
|
if (type == MDNS_TYPE_NSEC || type == MDNS_TYPE_OPT) {
|
|
//skip NSEC and OPT
|
|
continue;
|
|
}
|
|
|
|
if (parsed_packet->discovery && is_discovery(name, type)) {
|
|
discovery = true;
|
|
} else if (!name->sub && is_ours(name)) {
|
|
ours = true;
|
|
if (name->service[0] && name->proto[0]) {
|
|
service = mdns_utils_get_service_item(name->service, name->proto, NULL);
|
|
}
|
|
} else {
|
|
if ((header.flags & MDNS_FLAGS_QUERY_REPSONSE) == 0 || record_type == MDNS_NS) {
|
|
//skip this record
|
|
continue;
|
|
}
|
|
search_result = mdns_priv_query_find(name, type, packet->tcpip_if, packet->ip_protocol);
|
|
browse_result = mdns_priv_browse_find(name, type, packet->tcpip_if, packet->ip_protocol);
|
|
if (browse_result) {
|
|
if (!out_sync_browse) {
|
|
// will be freed in function `browse_sync`
|
|
out_sync_browse = (mdns_browse_sync_t *)mdns_mem_malloc(sizeof(mdns_browse_sync_t));
|
|
if (!out_sync_browse) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
out_sync_browse->browse = browse_result;
|
|
out_sync_browse->sync_result = NULL;
|
|
}
|
|
if (!browse_result_service) {
|
|
browse_result_service = (char *)mdns_mem_malloc(MDNS_NAME_BUF_LEN);
|
|
if (!browse_result_service) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
}
|
|
memcpy(browse_result_service, browse_result->service, MDNS_NAME_BUF_LEN);
|
|
if (!browse_result_proto) {
|
|
browse_result_proto = (char *)mdns_mem_malloc(MDNS_NAME_BUF_LEN);
|
|
if (!browse_result_proto) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
}
|
|
memcpy(browse_result_proto, browse_result->proto, MDNS_NAME_BUF_LEN);
|
|
if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) {
|
|
if (!browse_result_instance) {
|
|
browse_result_instance = (char *)mdns_mem_malloc(MDNS_NAME_BUF_LEN);
|
|
if (!browse_result_instance) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
}
|
|
memcpy(browse_result_instance, name->host, MDNS_NAME_BUF_LEN);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == MDNS_TYPE_PTR) {
|
|
if (!mdns_utils_parse_fqdn(data, data_ptr, name, len)) {
|
|
continue;//error
|
|
}
|
|
if (search_result) {
|
|
mdns_priv_query_result_add_ptr(search_result, name->host, name->service, name->proto,
|
|
packet->tcpip_if, packet->ip_protocol, ttl);
|
|
} else if ((discovery || ours) && !name->sub && is_ours(name)) {
|
|
if (name->host[0]) {
|
|
service = mdns_utils_get_service_item_instance(name->host, name->service, name->proto, NULL);
|
|
} else {
|
|
service = mdns_utils_get_service_item(name->service, name->proto, NULL);
|
|
}
|
|
if (discovery && service) {
|
|
remove_parsed_question(parsed_packet, MDNS_TYPE_SDPTR, service);
|
|
} else if (service && parsed_packet->questions && !parsed_packet->probe) {
|
|
remove_parsed_question(parsed_packet, type, service);
|
|
} else if (service) {
|
|
//check if TTL is more than half of the full TTL value (4500)
|
|
if (ttl > (MDNS_ANSWER_PTR_TTL / 2)) {
|
|
mdns_priv_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
|
|
}
|
|
}
|
|
if (service) {
|
|
mdns_parsed_record_t *record = mdns_mem_malloc(sizeof(mdns_parsed_record_t));
|
|
if (!record) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
record->next = parsed_packet->records;
|
|
parsed_packet->records = record;
|
|
record->type = MDNS_TYPE_PTR;
|
|
record->record_type = MDNS_ANSWER;
|
|
record->ttl = ttl;
|
|
record->host = NULL;
|
|
record->service = NULL;
|
|
record->proto = NULL;
|
|
if (name->host[0]) {
|
|
record->host = mdns_mem_malloc(MDNS_NAME_BUF_LEN);
|
|
if (!record->host) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
memcpy(record->host, name->host, MDNS_NAME_BUF_LEN);
|
|
}
|
|
if (name->service[0]) {
|
|
record->service = mdns_mem_malloc(MDNS_NAME_BUF_LEN);
|
|
if (!record->service) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
memcpy(record->service, name->service, MDNS_NAME_BUF_LEN);
|
|
}
|
|
if (name->proto[0]) {
|
|
record->proto = mdns_mem_malloc(MDNS_NAME_BUF_LEN);
|
|
if (!record->proto) {
|
|
HOOK_MALLOC_FAILED;
|
|
goto clear_rx_packet;
|
|
}
|
|
memcpy(record->proto, name->proto, MDNS_NAME_BUF_LEN);
|
|
}
|
|
}
|
|
}
|
|
} else if (type == MDNS_TYPE_SRV) {
|
|
mdns_result_t *result = NULL;
|
|
if (search_result && search_result->type == MDNS_TYPE_PTR) {
|
|
result = search_result->result;
|
|
while (result) {
|
|
if (mdns_priv_get_esp_netif(packet->tcpip_if) == result->esp_netif
|
|
&& packet->ip_protocol == result->ip_protocol
|
|
&& result->instance_name && !strcmp(name->host, result->instance_name)) {
|
|
break;
|
|
}
|
|
result = result->next;
|
|
}
|
|
if (!result) {
|
|
result = mdns_priv_query_result_add_ptr(search_result, name->host, name->service, name->proto,
|
|
packet->tcpip_if, packet->ip_protocol, ttl);
|
|
if (!result) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
bool is_selfhosted = is_name_selfhosted(name);
|
|
if (!mdns_utils_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name, len)) {
|
|
continue;
|
|
}
|
|
if (data_ptr + MDNS_SRV_PORT_OFFSET + 1 >= data + len) {
|
|
goto clear_rx_packet; // malformed packet, won't read behind it
|
|
}
|
|
uint16_t priority = mdns_utils_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET);
|
|
uint16_t weight = mdns_utils_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET);
|
|
uint16_t port = mdns_utils_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET);
|
|
|
|
if (browse_result) {
|
|
mdns_priv_browse_result_add_srv(browse_result, name->host, browse_result_instance,
|
|
browse_result_service,
|
|
browse_result_proto, port, packet->tcpip_if, packet->ip_protocol,
|
|
ttl,
|
|
out_sync_browse);
|
|
}
|
|
if (search_result) {
|
|
if (search_result->type == MDNS_TYPE_PTR) {
|
|
if (!result->hostname) { // assign host/port for this entry only if not previously set
|
|
result->port = port;
|
|
result->hostname = mdns_mem_strdup(name->host);
|
|
}
|
|
} else {
|
|
mdns_priv_query_result_add_srv(search_result, name->host, port, packet->tcpip_if,
|
|
packet->ip_protocol, ttl);
|
|
}
|
|
} else if (ours) {
|
|
if (parsed_packet->questions && !parsed_packet->probe) {
|
|
remove_parsed_question(parsed_packet, type, service);
|
|
continue;
|
|
} else if (parsed_packet->distributed) {
|
|
mdns_priv_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
|
|
continue;
|
|
}
|
|
if (!is_selfhosted) {
|
|
continue;
|
|
}
|
|
//detect collision (-1=won, 0=none, 1=lost)
|
|
int col = 0;
|
|
if (mdns_class > 1) {
|
|
col = 1;
|
|
} else if (!mdns_class) {
|
|
col = -1;
|
|
} else if (service) { // only detect srv collision if service existed
|
|
col = check_srv_collision(service->service, priority, weight, port, name->host, name->domain);
|
|
}
|
|
if (service && col && (parsed_packet->probe || parsed_packet->authoritative)) {
|
|
if (col > 0 || !port) {
|
|
do_not_reply = true;
|
|
if (mdns_priv_pcb_is_probing(packet)) {
|
|
mdns_priv_pcb_set_probe_failed(packet);
|
|
if (!mdns_utils_str_null_or_empty(service->service->instance)) {
|
|
char *new_instance = mangle_name((char *) service->service->instance);
|
|
if (new_instance) {
|
|
mdns_mem_free((char *)service->service->instance);
|
|
service->service->instance = new_instance;
|
|
}
|
|
mdns_priv_probe_all_pcbs(&service, 1, false, false);
|
|
} else if (!mdns_utils_str_null_or_empty(mdns_priv_get_instance())) {
|
|
char *new_instance = mangle_name((char *) mdns_priv_get_instance());
|
|
if (new_instance) {
|
|
mdns_priv_set_instance(new_instance);
|
|
}
|
|
mdns_priv_restart_all_pcbs_no_instance();
|
|
} else {
|
|
char *new_host = mangle_name((char *) mdns_priv_get_global_hostname());
|
|
if (new_host) {
|
|
mdns_priv_remap_self_service_hostname(mdns_priv_get_global_hostname(), new_host);
|
|
mdns_priv_set_global_hostname(new_host);
|
|
}
|
|
mdns_priv_restart_all_pcbs();
|
|
}
|
|
} else if (service) {
|
|
mdns_priv_send_bye(packet->tcpip_if, packet->ip_protocol, &service, 1, false);
|
|
mdns_priv_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, &service, 1, false);
|
|
}
|
|
}
|
|
} else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions) {
|
|
mdns_priv_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
|
|
}
|
|
}
|
|
} else if (type == MDNS_TYPE_TXT) {
|
|
mdns_txt_item_t *txt = NULL;
|
|
uint8_t *txt_value_len = NULL;
|
|
size_t txt_count = 0;
|
|
|
|
mdns_result_t *result = NULL;
|
|
if (browse_result) {
|
|
result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count);
|
|
mdns_priv_browse_result_add_txt(browse_result, browse_result_instance, browse_result_service,
|
|
browse_result_proto,
|
|
txt, txt_value_len, txt_count, packet->tcpip_if,
|
|
packet->ip_protocol,
|
|
ttl, out_sync_browse);
|
|
}
|
|
if (search_result) {
|
|
if (search_result->type == MDNS_TYPE_PTR) {
|
|
result = search_result->result;
|
|
while (result) {
|
|
if (mdns_priv_get_esp_netif(packet->tcpip_if) == result->esp_netif
|
|
&& packet->ip_protocol == result->ip_protocol
|
|
&& result->instance_name && !strcmp(name->host, result->instance_name)) {
|
|
break;
|
|
}
|
|
result = result->next;
|
|
}
|
|
if (!result) {
|
|
result = mdns_priv_query_result_add_ptr(search_result, name->host, name->service,
|
|
name->proto,
|
|
packet->tcpip_if, packet->ip_protocol, ttl);
|
|
if (!result) {
|
|
continue;
|
|
}
|
|
}
|
|
if (!result->txt) {
|
|
result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count);
|
|
if (txt_count) {
|
|
result->txt = txt;
|
|
result->txt_count = txt_count;
|
|
result->txt_value_len = txt_value_len;
|
|
}
|
|
}
|
|
} else {
|
|
result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count);
|
|
if (txt_count) {
|
|
mdns_priv_query_result_add_txt(search_result, txt, txt_value_len, txt_count,
|
|
packet->tcpip_if, packet->ip_protocol, ttl);
|
|
}
|
|
}
|
|
} else if (ours) {
|
|
if (parsed_packet->questions && !parsed_packet->probe && service) {
|
|
remove_parsed_question(parsed_packet, type, service);
|
|
continue;
|
|
}
|
|
if (!is_name_selfhosted(name)) {
|
|
continue;
|
|
}
|
|
//detect collision (-1=won, 0=none, 1=lost)
|
|
int col = 0;
|
|
if (mdns_class > 1) {
|
|
col = 1;
|
|
} else if (!mdns_class) {
|
|
col = -1;
|
|
} else if (service) { // only detect txt collision if service existed
|
|
col = check_txt_collision(service->service, data_ptr, data_len);
|
|
}
|
|
if (col && !mdns_priv_pcb_is_probing(packet) && service) {
|
|
do_not_reply = true;
|
|
mdns_priv_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, &service, 1, true);
|
|
} else if (ttl > (MDNS_ANSWER_TXT_TTL / 2) && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !mdns_priv_pcb_is_probing(
|
|
packet)) {
|
|
mdns_priv_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
|
|
}
|
|
}
|
|
|
|
}
|
|
#ifdef CONFIG_LWIP_IPV6
|
|
else if (type == MDNS_TYPE_AAAA) { // ipv6
|
|
esp_ip_addr_t ip6;
|
|
ip6.type = ESP_IPADDR_TYPE_V6;
|
|
memcpy(ip6.u_addr.ip6.addr, data_ptr, MDNS_ANSWER_AAAA_SIZE);
|
|
if (browse_result) {
|
|
mdns_priv_browse_result_add_ip(browse_result, name->host, &ip6, packet->tcpip_if,
|
|
packet->ip_protocol,
|
|
ttl, out_sync_browse);
|
|
}
|
|
if (search_result) {
|
|
//check for more applicable searches (PTR & A/AAAA at the same time)
|
|
while (search_result) {
|
|
mdns_priv_query_result_add_ip(search_result, name->host, &ip6, packet->tcpip_if,
|
|
packet->ip_protocol, ttl);
|
|
search_result = mdns_priv_query_find_from(search_result->next, name, type, packet->tcpip_if,
|
|
packet->ip_protocol);
|
|
}
|
|
} else if (ours) {
|
|
if (parsed_packet->questions && !parsed_packet->probe) {
|
|
remove_parsed_question(parsed_packet, type, NULL);
|
|
continue;
|
|
}
|
|
if (!is_name_selfhosted(name)) {
|
|
continue;
|
|
}
|
|
//detect collision (-1=won, 0=none, 1=lost)
|
|
int col = 0;
|
|
if (mdns_class > 1) {
|
|
col = 1;
|
|
} else if (!mdns_class) {
|
|
col = -1;
|
|
} else {
|
|
col = check_aaaa_collision(&(ip6.u_addr.ip6), packet->tcpip_if);
|
|
}
|
|
if (col == 2) {
|
|
goto clear_rx_packet;
|
|
} else if (col == 1) {
|
|
do_not_reply = true;
|
|
if (mdns_priv_pcb_is_probing(packet)) {
|
|
if (col && (parsed_packet->probe || parsed_packet->authoritative)) {
|
|
mdns_priv_pcb_set_probe_failed(packet);
|
|
char *new_host = mangle_name((char *) mdns_priv_get_global_hostname());
|
|
if (new_host) {
|
|
mdns_priv_remap_self_service_hostname(mdns_priv_get_global_hostname(), new_host);
|
|
mdns_priv_set_global_hostname(new_host);
|
|
}
|
|
mdns_priv_restart_all_pcbs();
|
|
}
|
|
} else {
|
|
mdns_priv_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, NULL, 0, true);
|
|
}
|
|
} else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !mdns_priv_pcb_is_probing(
|
|
packet)) {
|
|
mdns_priv_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, NULL);
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif /* CONFIG_LWIP_IPV6 */
|
|
#ifdef CONFIG_LWIP_IPV4
|
|
else if (type == MDNS_TYPE_A) {
|
|
esp_ip_addr_t ip;
|
|
ip.type = ESP_IPADDR_TYPE_V4;
|
|
memcpy(&(ip.u_addr.ip4.addr), data_ptr, 4);
|
|
if (browse_result) {
|
|
mdns_priv_browse_result_add_ip(browse_result, name->host, &ip, packet->tcpip_if,
|
|
packet->ip_protocol,
|
|
ttl, out_sync_browse);
|
|
}
|
|
if (search_result) {
|
|
//check for more applicable searches (PTR & A/AAAA at the same time)
|
|
while (search_result) {
|
|
mdns_priv_query_result_add_ip(search_result, name->host, &ip, packet->tcpip_if,
|
|
packet->ip_protocol, ttl);
|
|
search_result = mdns_priv_query_find_from(search_result->next, name, type, packet->tcpip_if,
|
|
packet->ip_protocol);
|
|
}
|
|
} else if (ours) {
|
|
if (parsed_packet->questions && !parsed_packet->probe) {
|
|
remove_parsed_question(parsed_packet, type, NULL);
|
|
continue;
|
|
}
|
|
if (!is_name_selfhosted(name)) {
|
|
continue;
|
|
}
|
|
//detect collision (-1=won, 0=none, 1=lost)
|
|
int col = 0;
|
|
if (mdns_class > 1) {
|
|
col = 1;
|
|
} else if (!mdns_class) {
|
|
col = -1;
|
|
} else {
|
|
col = check_a_collision(&(ip.u_addr.ip4), packet->tcpip_if);
|
|
}
|
|
if (col == 2) {
|
|
goto clear_rx_packet;
|
|
} else if (col == 1) {
|
|
do_not_reply = true;
|
|
if (mdns_priv_pcb_is_probing(packet)) {
|
|
if (col && (parsed_packet->probe || parsed_packet->authoritative)) {
|
|
mdns_priv_pcb_set_probe_failed(packet);
|
|
char *new_host = mangle_name((char *) mdns_priv_get_global_hostname());
|
|
if (new_host) {
|
|
mdns_priv_remap_self_service_hostname(mdns_priv_get_global_hostname(), new_host);
|
|
mdns_priv_set_global_hostname(new_host);
|
|
}
|
|
mdns_priv_restart_all_pcbs();
|
|
}
|
|
} else {
|
|
mdns_priv_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, NULL, 0, true);
|
|
}
|
|
} else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !mdns_priv_pcb_is_probing(
|
|
packet)) {
|
|
mdns_priv_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, NULL);
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif /* CONFIG_LWIP_IPV4 */
|
|
}
|
|
//end while
|
|
if (parsed_packet->authoritative) {
|
|
mdns_priv_query_done();
|
|
}
|
|
}
|
|
|
|
if (!do_not_reply && mdns_priv_pcb_is_after_probing(packet) && (parsed_packet->questions || parsed_packet->discovery)) {
|
|
mdns_priv_create_answer_from_parsed_packet(parsed_packet);
|
|
}
|
|
if (out_sync_browse) {
|
|
DBG_BROWSE_RESULTS_WITH_MSG(out_sync_browse->browse->result,
|
|
"Browse %s%s total result:", out_sync_browse->browse->service, out_sync_browse->browse->proto);
|
|
if (out_sync_browse->sync_result) {
|
|
DBG_BROWSE_RESULTS_WITH_MSG(out_sync_browse->sync_result->result,
|
|
"Changed result:");
|
|
mdns_priv_browse_sync(out_sync_browse);
|
|
} else {
|
|
mdns_mem_free(out_sync_browse);
|
|
}
|
|
out_sync_browse = NULL;
|
|
}
|
|
|
|
clear_rx_packet:
|
|
while (parsed_packet->questions) {
|
|
mdns_parsed_question_t *question = parsed_packet->questions;
|
|
parsed_packet->questions = parsed_packet->questions->next;
|
|
if (question->host) {
|
|
mdns_mem_free(question->host);
|
|
}
|
|
if (question->service) {
|
|
mdns_mem_free(question->service);
|
|
}
|
|
if (question->proto) {
|
|
mdns_mem_free(question->proto);
|
|
}
|
|
if (question->domain) {
|
|
mdns_mem_free(question->domain);
|
|
}
|
|
mdns_mem_free(question);
|
|
}
|
|
while (parsed_packet->records) {
|
|
mdns_parsed_record_t *record = parsed_packet->records;
|
|
parsed_packet->records = parsed_packet->records->next;
|
|
if (record->host) {
|
|
mdns_mem_free(record->host);
|
|
}
|
|
if (record->service) {
|
|
mdns_mem_free(record->service);
|
|
}
|
|
if (record->proto) {
|
|
mdns_mem_free(record->proto);
|
|
}
|
|
record->next = NULL;
|
|
mdns_mem_free(record);
|
|
}
|
|
mdns_mem_free(parsed_packet);
|
|
mdns_mem_free(browse_result_instance);
|
|
mdns_mem_free(browse_result_service);
|
|
mdns_mem_free(browse_result_proto);
|
|
mdns_mem_free(out_sync_browse);
|
|
}
|
|
|
|
void mdns_priv_receive_action(mdns_action_t *action, mdns_action_subtype_t type)
|
|
{
|
|
if (action->type != ACTION_RX_HANDLE) {
|
|
abort();
|
|
}
|
|
if (type == ACTION_RUN) {
|
|
mdns_parse_packet(action->data.rx_handle.packet);
|
|
mdns_priv_packet_free(action->data.rx_handle.packet);
|
|
} else if (type == ACTION_CLEANUP) {
|
|
mdns_priv_packet_free(action->data.rx_handle.packet);
|
|
}
|
|
}
|