mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-04 16:19:20 +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
417 lines
16 KiB
C
417 lines
16 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
#include "sdkconfig.h"
|
|
#include "mdns_private.h"
|
|
#include "mdns_utils.h"
|
|
|
|
#ifdef CONFIG_MDNS_DEBUG_USE_ESP_LOG
|
|
|
|
#include <stdarg.h>
|
|
#include "esp_log.h"
|
|
|
|
#define MDNS_DBG_MAX_LINE CONFIG_MDNS_DEBUG_BUFFER_SIZE
|
|
|
|
static char s_mdns_dbg_buf[MDNS_DBG_MAX_LINE];
|
|
static size_t s_mdns_dbg_pos = 0;
|
|
|
|
static void mdns_dbg_puts(const char *str)
|
|
{
|
|
ESP_LOGI("mdns", "%s", str);
|
|
}
|
|
|
|
static inline void mdns_dbg_flush(void)
|
|
{
|
|
if (s_mdns_dbg_pos > 0) {
|
|
s_mdns_dbg_buf[s_mdns_dbg_pos] = '\0';
|
|
mdns_dbg_puts(s_mdns_dbg_buf);
|
|
s_mdns_dbg_pos = 0;
|
|
}
|
|
}
|
|
|
|
static void mdns_dbg_vprintf(const char *fmt, va_list args)
|
|
{
|
|
// Try to format directly into the buffer
|
|
int len = vsnprintf(s_mdns_dbg_buf + s_mdns_dbg_pos,
|
|
MDNS_DBG_MAX_LINE - s_mdns_dbg_pos,
|
|
fmt, args);
|
|
|
|
if (len < 0) {
|
|
return; // Error in formatting
|
|
}
|
|
|
|
// Check if the entire formatted string fit in the buffer
|
|
if (len < (MDNS_DBG_MAX_LINE - s_mdns_dbg_pos)) {
|
|
// If it fit, just update the position
|
|
s_mdns_dbg_pos += len;
|
|
} else {
|
|
// The formatted string was truncated because it didn't fit
|
|
// First, flush what we have (the partial string)
|
|
mdns_dbg_flush();
|
|
|
|
// Create a new va_list copy and try again with the full buffer
|
|
va_list args_copy;
|
|
va_copy(args_copy, args);
|
|
|
|
// Format again with the entire buffer available
|
|
len = vsnprintf(s_mdns_dbg_buf, MDNS_DBG_MAX_LINE - 1, fmt, args_copy);
|
|
va_end(args_copy);
|
|
|
|
if (len < 0) {
|
|
return; // Error
|
|
}
|
|
|
|
// Check if content will be lost (true truncation)
|
|
if (len >= MDNS_DBG_MAX_LINE - 1) {
|
|
// This is when actual content will be lost - log a warning
|
|
ESP_LOGW("mdns", "Message truncated: length (%d) exceeds buffer size (%d). Consider increasing CONFIG_MDNS_DEBUG_BUFFER_SIZE.",
|
|
len, MDNS_DBG_MAX_LINE - 1);
|
|
|
|
// Display what we could fit, then flush and return
|
|
s_mdns_dbg_pos = MDNS_DBG_MAX_LINE - 1;
|
|
mdns_dbg_flush();
|
|
return;
|
|
}
|
|
|
|
// If we get here, the whole message fit this time
|
|
s_mdns_dbg_pos = len;
|
|
}
|
|
|
|
// If buffer is nearly full after this operation, flush it
|
|
if (s_mdns_dbg_pos >= MDNS_DBG_MAX_LINE - 1) {
|
|
mdns_dbg_flush();
|
|
}
|
|
}
|
|
|
|
static void mdns_dbg_printf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
mdns_dbg_vprintf(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
#define dbg_printf(...) mdns_dbg_printf(__VA_ARGS__)
|
|
#else
|
|
#define dbg_printf(...) printf(__VA_ARGS__)
|
|
#define mdns_dbg_flush()
|
|
#endif
|
|
|
|
void static dbg_packet(const uint8_t *data, size_t len)
|
|
{
|
|
static mdns_name_t n;
|
|
mdns_header_t header;
|
|
const uint8_t *content = data + MDNS_HEAD_LEN;
|
|
uint64_t t = xTaskGetTickCount() * portTICK_PERIOD_MS;
|
|
mdns_name_t *name = &n;
|
|
memset(name, 0, sizeof(mdns_name_t));
|
|
|
|
dbg_printf("Packet[%" PRIu64 "]: ", 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);
|
|
|
|
dbg_printf("%s",
|
|
(header.flags == MDNS_FLAGS_QR_AUTHORITATIVE) ? "AUTHORITATIVE\n" :
|
|
(header.flags == MDNS_FLAGS_DISTRIBUTED) ? "DISTRIBUTED\n" :
|
|
(header.flags == 0) ? "\n" : " "
|
|
);
|
|
if (header.flags && header.flags != MDNS_FLAGS_QR_AUTHORITATIVE) {
|
|
dbg_printf("0x%04X\n", header.flags);
|
|
}
|
|
|
|
if (header.questions) {
|
|
uint8_t qs = header.questions;
|
|
|
|
while (qs--) {
|
|
content = mdns_utils_parse_fqdn(data, content, name, len);
|
|
if (!content || content + MDNS_CLASS_OFFSET + 1 >= data + len) {
|
|
header.answers = 0;
|
|
header.additional = 0;
|
|
header.servers = 0;
|
|
dbg_printf("ERROR: parse header questions\n");
|
|
break;
|
|
}
|
|
|
|
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;
|
|
|
|
dbg_printf(" Q: ");
|
|
if (unicast) {
|
|
dbg_printf("*U* ");
|
|
}
|
|
if (type == MDNS_TYPE_PTR) {
|
|
dbg_printf("%s.%s%s.%s.%s. PTR ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_SRV) {
|
|
dbg_printf("%s.%s%s.%s.%s. SRV ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_TXT) {
|
|
dbg_printf("%s.%s%s.%s.%s. TXT ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_A) {
|
|
dbg_printf("%s.%s. A ", name->host, name->domain);
|
|
} else if (type == MDNS_TYPE_AAAA) {
|
|
dbg_printf("%s.%s. AAAA ", name->host, name->domain);
|
|
} else if (type == MDNS_TYPE_NSEC) {
|
|
dbg_printf("%s.%s%s.%s.%s. NSEC ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_ANY) {
|
|
dbg_printf("%s.%s%s.%s.%s. ANY ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
|
} else {
|
|
dbg_printf("%s.%s%s.%s.%s. %04X ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain, type);
|
|
}
|
|
|
|
if (mdns_class == 0x0001) {
|
|
dbg_printf("IN");
|
|
} else {
|
|
dbg_printf("%04X", mdns_class);
|
|
}
|
|
dbg_printf("\n");
|
|
}
|
|
}
|
|
|
|
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) {
|
|
dbg_printf("ERROR: parse mdns records\n");
|
|
break;
|
|
}
|
|
|
|
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;
|
|
bool flush = !!(mdns_class & 0x8000);
|
|
mdns_class &= 0x7FFF;
|
|
|
|
content = data_ptr + data_len;
|
|
if (content > (data + len)) {
|
|
dbg_printf("ERROR: content length overflow\n");
|
|
break;
|
|
}
|
|
|
|
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 (record_type == MDNS_EXTRA) {
|
|
dbg_printf(" X");
|
|
} else if (record_type == MDNS_NS) {
|
|
dbg_printf(" S");
|
|
} else {
|
|
dbg_printf(" A");
|
|
}
|
|
|
|
if (type == MDNS_TYPE_PTR) {
|
|
dbg_printf(": %s%s%s.%s.%s. PTR ", name->host, name->host[0] ? "." : "", name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_SRV) {
|
|
dbg_printf(": %s.%s.%s.%s. SRV ", name->host, name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_TXT) {
|
|
dbg_printf(": %s.%s.%s.%s. TXT ", name->host, name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_A) {
|
|
dbg_printf(": %s.%s. A ", name->host, name->domain);
|
|
} else if (type == MDNS_TYPE_AAAA) {
|
|
dbg_printf(": %s.%s. AAAA ", name->host, name->domain);
|
|
} else if (type == MDNS_TYPE_NSEC) {
|
|
dbg_printf(": %s.%s.%s.%s. NSEC ", name->host, name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_ANY) {
|
|
dbg_printf(": %s.%s.%s.%s. ANY ", name->host, name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_OPT) {
|
|
dbg_printf(": . OPT ");
|
|
} else {
|
|
dbg_printf(": %s.%s.%s.%s. %04X ", name->host, name->service, name->proto, name->domain, type);
|
|
}
|
|
|
|
if (mdns_class == 0x0001) {
|
|
dbg_printf("IN ");
|
|
} else {
|
|
dbg_printf("%04X ", mdns_class);
|
|
}
|
|
if (flush) {
|
|
dbg_printf("FLUSH ");
|
|
}
|
|
dbg_printf("%" PRIu32, ttl);
|
|
dbg_printf("[%u] ", data_len);
|
|
if (type == MDNS_TYPE_PTR) {
|
|
if (!mdns_utils_parse_fqdn(data, data_ptr, name, len)) {
|
|
dbg_printf("ERROR: parse PTR\n");
|
|
continue;
|
|
}
|
|
dbg_printf("%s.%s.%s.%s.\n", name->host, name->service, name->proto, name->domain);
|
|
} else if (type == MDNS_TYPE_SRV) {
|
|
if (!mdns_utils_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name, len)) {
|
|
dbg_printf("ERROR: parse SRV\n");
|
|
continue;
|
|
}
|
|
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);
|
|
dbg_printf("%u %u %u %s.%s.\n", priority, weight, port, name->host, name->domain);
|
|
} else if (type == MDNS_TYPE_TXT) {
|
|
uint16_t i = 0, y;
|
|
while (i < data_len) {
|
|
uint8_t partLen = data_ptr[i++];
|
|
if ((i + partLen) > data_len) {
|
|
dbg_printf("ERROR: parse TXT\n");
|
|
break;
|
|
}
|
|
char txt[partLen + 1];
|
|
for (y = 0; y < partLen; y++) {
|
|
char d = data_ptr[i++];
|
|
txt[y] = d;
|
|
}
|
|
txt[partLen] = 0;
|
|
dbg_printf("%s", txt);
|
|
if (i < data_len) {
|
|
dbg_printf("; ");
|
|
}
|
|
}
|
|
dbg_printf("\n");
|
|
} else if (type == MDNS_TYPE_AAAA) {
|
|
esp_ip6_addr_t ip6;
|
|
memcpy(&ip6, data_ptr, sizeof(esp_ip6_addr_t));
|
|
dbg_printf(IPV6STR "\n", IPV62STR(ip6));
|
|
} else if (type == MDNS_TYPE_A) {
|
|
esp_ip4_addr_t ip;
|
|
memcpy(&ip, data_ptr, sizeof(esp_ip4_addr_t));
|
|
dbg_printf(IPSTR "\n", IP2STR(&ip));
|
|
} else if (type == MDNS_TYPE_NSEC) {
|
|
const uint8_t *old_ptr = data_ptr;
|
|
const uint8_t *new_ptr = mdns_utils_parse_fqdn(data, data_ptr, name, len);
|
|
if (new_ptr) {
|
|
dbg_printf("%s.%s.%s.%s. ", name->host, name->service, name->proto, name->domain);
|
|
size_t diff = new_ptr - old_ptr;
|
|
data_len -= diff;
|
|
data_ptr = new_ptr;
|
|
}
|
|
size_t i;
|
|
for (i = 0; i < data_len; i++) {
|
|
dbg_printf(" %02x", data_ptr[i]);
|
|
}
|
|
dbg_printf("\n");
|
|
} else if (type == MDNS_TYPE_OPT) {
|
|
uint16_t opCode = mdns_utils_read_u16(data_ptr, 0);
|
|
uint16_t opLen = mdns_utils_read_u16(data_ptr, 2);
|
|
dbg_printf(" Code: %04x Data[%u]:", opCode, opLen);
|
|
size_t i;
|
|
for (i = 4; i < data_len; i++) {
|
|
dbg_printf(" %02x", data_ptr[i]);
|
|
}
|
|
dbg_printf("\n");
|
|
} else {
|
|
size_t i;
|
|
for (i = 0; i < data_len; i++) {
|
|
dbg_printf(" %02x", data_ptr[i]);
|
|
}
|
|
dbg_printf("\n");
|
|
}
|
|
}
|
|
}
|
|
mdns_dbg_flush();
|
|
}
|
|
|
|
void mdns_debug_tx_packet(mdns_tx_packet_t *p, uint8_t packet[MDNS_MAX_PACKET_SIZE], uint16_t index)
|
|
{
|
|
dbg_printf("\nTX[%lu][%lu]: ", (unsigned long)p->tcpip_if, (unsigned long)p->ip_protocol);
|
|
#ifdef CONFIG_LWIP_IPV4
|
|
if (p->dst.type == ESP_IPADDR_TYPE_V4) {
|
|
dbg_printf("To: " IPSTR ":%u, ", IP2STR(&p->dst.u_addr.ip4), p->port);
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_LWIP_IPV6
|
|
if (p->dst.type == ESP_IPADDR_TYPE_V6) {
|
|
dbg_printf("To: " IPV6STR ":%u, ", IPV62STR(p->dst.u_addr.ip6), p->port);
|
|
}
|
|
#endif
|
|
dbg_packet(packet, index);
|
|
mdns_dbg_flush();
|
|
}
|
|
|
|
void mdns_debug_rx_packet(mdns_rx_packet_t *packet, const uint8_t* data, uint16_t len)
|
|
{
|
|
dbg_printf("\nRX[%lu][%lu]: ", (unsigned long)packet->tcpip_if, (unsigned long)packet->ip_protocol);
|
|
#ifdef CONFIG_LWIP_IPV4
|
|
if (packet->src.type == ESP_IPADDR_TYPE_V4) {
|
|
dbg_printf("From: " IPSTR ":%u, To: " IPSTR ", ", IP2STR(&packet->src.u_addr.ip4), packet->src_port, IP2STR(&packet->dest.u_addr.ip4));
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_LWIP_IPV6
|
|
if (packet->src.type == ESP_IPADDR_TYPE_V6) {
|
|
dbg_printf("From: " IPV6STR ":%u, To: " IPV6STR ", ", IPV62STR(packet->src.u_addr.ip6), packet->src_port, IPV62STR(packet->dest.u_addr.ip6));
|
|
}
|
|
#endif
|
|
dbg_packet(data, len);
|
|
mdns_dbg_flush();
|
|
}
|
|
|
|
static void dbg_printf_result(mdns_result_t *r_t)
|
|
{
|
|
mdns_ip_addr_t *r_a = NULL;
|
|
int addr_count = 0;
|
|
dbg_printf("result esp_netif: %p\n", r_t->esp_netif);
|
|
dbg_printf("result ip_protocol: %d\n", r_t->ip_protocol);
|
|
dbg_printf("result hostname: %s\n", mdns_utils_str_null_or_empty(r_t->hostname) ? "NULL" : r_t->hostname);
|
|
dbg_printf("result instance_name: %s\n", mdns_utils_str_null_or_empty(r_t->instance_name) ? "NULL" : r_t->instance_name);
|
|
dbg_printf("result service_type: %s\n", mdns_utils_str_null_or_empty(r_t->service_type) ? "NULL" : r_t->service_type);
|
|
dbg_printf("result proto: %s\n", mdns_utils_str_null_or_empty(r_t->proto) ? "NULL" : r_t->proto);
|
|
dbg_printf("result port: %d\n", r_t->port);
|
|
dbg_printf("result ttl: %" PRIu32 "\n", r_t->ttl);
|
|
for (int i = 0; i < r_t->txt_count; i++) {
|
|
dbg_printf("result txt item%d, key: %s, value: %s\n", i, r_t->txt[i].key, r_t->txt[i].value);
|
|
}
|
|
r_a = r_t->addr;
|
|
while (r_a) {
|
|
#ifdef CONFIG_LWIP_IPV4
|
|
if (r_a->addr.type == ESP_IPADDR_TYPE_V4) {
|
|
dbg_printf("Addr%d: " IPSTR "\n", addr_count++, IP2STR(&r_a->addr.u_addr.ip4));
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_LWIP_IPV6
|
|
if (r_a->addr.type == ESP_IPADDR_TYPE_V6) {
|
|
dbg_printf("Addr%d: " IPV6STR "\n", addr_count++, IPV62STR(r_a->addr.u_addr.ip6));
|
|
}
|
|
#endif
|
|
r_a = r_a->next;
|
|
}
|
|
mdns_dbg_flush();
|
|
}
|
|
|
|
void mdns_debug_printf_browse_result(mdns_result_t *r_t, mdns_browse_t *b_t)
|
|
{
|
|
dbg_printf("----------------sync browse %s.%s result---------------\n", b_t->service, b_t->proto);
|
|
dbg_printf("browse pointer: %p\n", b_t);
|
|
dbg_printf_result(r_t);
|
|
mdns_dbg_flush();
|
|
}
|
|
|
|
void mdns_debug_printf_browse_result_all(mdns_result_t *r_t)
|
|
{
|
|
int count = 0;
|
|
while (r_t) {
|
|
dbg_printf("----------------result %d---------------\n", count++);
|
|
dbg_printf_result(r_t);
|
|
r_t = r_t->next;
|
|
}
|
|
mdns_dbg_flush();
|
|
}
|