Files
esp-protocols/components/mdns/mdns_debug.c
David Cermak bed116d98b feat(mdns): Refactor mdns library (stage #1)
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
2025-11-25 17:30:27 +01:00

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();
}