Skip to content

Commit a039ce1

Browse files
committed
fix(dns): simplify response parsing and update examples
- Simplified `dns_response_t` to store `ip_addr_t` directly instead of a nested struct. - Renamed `esp_dns_extract_ip_addresses_from_response` to `esp_dns_get_ips_from_response` and optimized the answer scanning limits. - Migrated `esp_dns_basic` and `esp_dns_concurrent_test` examples from `protocol_examples_common` to `net_connect`. - Increased the `addr_info_task` stack size in `esp_dns_basic` to 8192 bytes to prevent stack overflows during DoT/DoH mbedTLS operations. - Enabled `CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_CROSS_SIGNED_VERIFY` in example `sdkconfig.defaults` to fix validation errors with cross-signed certificate chains (e.g., Google, Cloudflare). - Updated READMEs with troubleshooting steps for cross-signed certificate errors.
1 parent 1044afc commit a039ce1

15 files changed

Lines changed: 71 additions & 71 deletions

File tree

.github/workflows/esp_dns__build.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
name: Build
1414
strategy:
1515
matrix:
16-
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
16+
idf_ver: ["latest", "release-v5.4", "release-v5.5", "release-v6.0"]
1717
idf_target: ["esp32"]
1818
test: [ { app: esp_dns_basic, path: "components/esp_dns/examples"}]
1919
include:
@@ -33,8 +33,9 @@ jobs:
3333
shell: bash
3434
working-directory: ${{matrix.test.path}}
3535
run: |
36-
if [[ "${{ matrix.idf_ver }}" == "release-v5.3" || "${{ matrix.idf_ver }}" == "release-v5.4" ]]; then
37-
export EXPECTED_WARNING="unknown kconfig symbol 'LWIP_USE_ESP_GETADDRINFO'"
36+
if [[ "${{ matrix.idf_ver }}" == "release-v5.4" || "${{ matrix.idf_ver }}" == "release-v5.5" ]]; then
37+
export EXPECTED_WARNING="unknown kconfig symbol 'LWIP_USE_ESP_GETADDRINFO'
38+
unknown kconfig symbol 'MBEDTLS_CERTIFICATE_BUNDLE_CROSS_SIGNED_VERIFY'"
3839
fi
3940
. ${IDF_PATH}/export.sh
4041
python -m pip install idf-build-apps

components/esp_dns/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ Once you add this component to your project, it will replace the default LWIP DN
213213
- **Certificate Errors**:
214214
- Verify that the correct certificate is provided for secure protocols
215215
- For public DNS servers, use the certificate bundle approach
216+
- If you see `No matching trusted root certificate found` when using the certificate bundle (e.g. with `dns.google` or Cloudflare), the server likely uses a cross-signed chain. You must enable `CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_CROSS_SIGNED_VERIFY=y` in your `sdkconfig` (under Component config → mbedTLS → Certificate Bundle → Support cross-signed certificate verification).
216217

217218
- **Timeout Errors**:
218219
- Increase the timeout value for slow network connections

components/esp_dns/esp_dns_doh.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ err_t dns_resolve_doh(const esp_dns_handle_t handle, const char *name, ip_addr_t
295295
}
296296

297297
/* Extract IP addresses from DNS response */
298-
err = esp_dns_extract_ip_addresses_from_response(&response_buffer.dns_response, addr);
298+
err = esp_dns_get_ips_from_response(&response_buffer.dns_response, addr);
299299
} else {
300300
ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(ret));
301301
err = ERR_VAL;

components/esp_dns/esp_dns_dot.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ err_t dns_resolve_dot(const esp_dns_handle_t handle, const char *name, ip_addr_t
185185
&response_buffer.dns_response);
186186

187187
/* Extract IP addresses from DNS response */
188-
err = esp_dns_extract_ip_addresses_from_response(&response_buffer.dns_response, addr);
188+
err = esp_dns_get_ips_from_response(&response_buffer.dns_response, addr);
189189
if (err != ERR_OK) {
190190
ESP_LOGE(TAG, "Failed to extract IP address from DNS response");
191191
goto cleanup;

components/esp_dns/esp_dns_tcp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ err_t dns_resolve_tcp(const esp_dns_handle_t handle, const char *name, ip_addr_t
168168
&response_buffer.dns_response);
169169

170170
/* Extract IP addresses from DNS response */
171-
err = esp_dns_extract_ip_addresses_from_response(&response_buffer.dns_response, addr);
171+
err = esp_dns_get_ips_from_response(&response_buffer.dns_response, addr);
172172
if (err != ERR_OK) {
173173
ESP_LOGE(TAG, "Failed to extract IP address from DNS response");
174174
goto cleanup;

components/esp_dns/esp_dns_utils.c

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_
137137
return;
138138
}
139139

140-
/* Ensure only MAX_ANSWERS are processed */
141-
dns_response->num_answers = (answer_count < MAX_ANSWERS ? answer_count : MAX_ANSWERS);
140+
/* Ensure we scan up to ESP_DNS_MAX_ANSWER_SCAN answers */
141+
dns_response->num_answers = 0;
142+
int scan_count = (answer_count < ESP_DNS_MAX_ANSWER_SCAN ? answer_count : ESP_DNS_MAX_ANSWER_SCAN);
142143

143144
/* Skip the header and question section */
144145
uint8_t *ptr = buffer + sizeof(dns_header_t);
@@ -154,7 +155,10 @@ void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_
154155
ptr += sizeof(dns_question_t);
155156

156157
/* Parse each answer record */
157-
for (int i = 0; i < dns_response->num_answers; i++) {
158+
for (int i = 0; i < scan_count; i++) {
159+
if (dns_response->num_answers >= MAX_ANSWERS) {
160+
break; /* We have collected enough IP addresses */
161+
}
158162

159163
/* Answer fields */
160164
ptr = skip_dns_name(ptr, response_size - (ptr - buffer));
@@ -174,24 +178,20 @@ void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_
174178

175179
/* Validate RR class and ttl */
176180
if ((class != DNS_RRCLASS_IN) || (ttl > DNS_MAX_TTL)) {
177-
dns_response->answers[i].status = ERR_VAL;
178181
goto next_answer;
179182
}
180183

181-
/* Initialize status for this answer */
182-
dns_response->answers[i].status = ERR_OK;
183-
184184
/* Check the type of answer */
185185
if (type == DNS_RRTYPE_A && data_len == 4) {
186186
/* IPv4 Address (A record) */
187-
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in_addr));
188-
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V4);
187+
memcpy(&dns_response->answers[dns_response->num_answers], ptr, sizeof(struct in_addr));
188+
IP_SET_TYPE(&dns_response->answers[dns_response->num_answers], IPADDR_TYPE_V4);
189+
dns_response->num_answers++;
189190
} else if (type == DNS_RRTYPE_AAAA && data_len == 16) {
190191
/* IPv6 Address (AAAA record) */
191-
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in6_addr));
192-
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V6);
193-
} else {
194-
dns_response->answers[i].status = ERR_VAL;
192+
memcpy(&dns_response->answers[dns_response->num_answers], ptr, sizeof(struct in6_addr));
193+
IP_SET_TYPE(&dns_response->answers[dns_response->num_answers], IPADDR_TYPE_V6);
194+
dns_response->num_answers++;
195195
}
196196

197197
next_answer:
@@ -201,42 +201,33 @@ void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_
201201
}
202202

203203
/**
204-
* @brief Converts a dns_response_t to an array of IP addresses.
204+
* @brief Copies parsed IP addresses from a DNS response to an array.
205205
*
206-
* This function iterates over the DNS response and extracts valid
207-
* IPv4 and IPv6 addresses, storing them in the provided array.
206+
* This function retrieves the valid IPv4 and IPv6 addresses that were
207+
* previously parsed and stored in the DNS response structure, copying
208+
* them into the provided array.
208209
*
209-
* @param response The DNS response to process
210-
* @param ipaddr Array to store the extracted IP addresses
210+
* @param response The parsed DNS response
211+
* @param ipaddr Array to store the copied IP addresses
211212
*
212-
* @return err_t Status of DNS response parsing
213+
* @return err_t ERR_OK on success, or an error code from the response
213214
*/
214-
err_t esp_dns_extract_ip_addresses_from_response(const dns_response_t *response, ip_addr_t ipaddr[])
215+
err_t esp_dns_get_ips_from_response(const dns_response_t *response, ip_addr_t ipaddr[])
215216
{
216-
int count = 0;
217217
memset(ipaddr, 0, DNS_MAX_HOST_IP * sizeof(ip_addr_t));
218218

219219
if (response->status_code != ERR_OK) {
220220
return response->status_code;
221221
}
222222

223-
/* Iterate over the DNS answers */
224-
for (int i = 0; i < response->num_answers && count < DNS_MAX_HOST_IP; i++) {
225-
const dns_answer_storage_t *answer = &response->answers[i];
226-
227-
/* Check if the answer is valid */
228-
if (answer->status != ERR_OK) {
229-
continue;
230-
}
231-
232-
ipaddr[count] = answer->ip;
233-
count++;
223+
if (response->num_answers == 0) {
224+
return ERR_VAL;
234225
}
235226

236-
if (count == 0) {
237-
return ERR_VAL;
227+
/* Copy the valid IP addresses */
228+
for (int i = 0; i < response->num_answers; i++) {
229+
ipaddr[i] = response->answers[i];
238230
}
239231

240-
/* Store the number of valid IP addresses */
241232
return ERR_OK;
242233
}

components/esp_dns/esp_dns_utils.h

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,12 @@ typedef struct {
7070
/** Maximum number of answers that can be stored */
7171
#define MAX_ANSWERS (CONFIG_LWIP_DNS_MAX_HOST_IP)
7272

73-
#define ESP_DNS_BUFFER_SIZE 512
73+
/** Maximum number of resource records to scan in a response */
74+
#ifndef ESP_DNS_MAX_ANSWER_SCAN
75+
#define ESP_DNS_MAX_ANSWER_SCAN 16
76+
#endif
7477

75-
/**
76-
* @brief Structure to store a single DNS answer
77-
*/
78-
typedef struct {
79-
err_t status; /* Status of the answer */
80-
ip_addr_t ip; /* IP address from the answer */
81-
} dns_answer_storage_t;
78+
#define ESP_DNS_BUFFER_SIZE 512
8279

8380
/**
8481
* @brief Structure to store a complete DNS response
@@ -87,7 +84,7 @@ typedef struct {
8784
err_t status_code; /* Overall status of the DNS response */
8885
uint16_t id; /* Transaction ID */
8986
int num_answers; /* Number of valid answers */
90-
dns_answer_storage_t answers[MAX_ANSWERS]; /* Array of answers */
87+
ip_addr_t answers[MAX_ANSWERS]; /* Array of IP addresses */
9188
} dns_response_t;
9289

9390
/**
@@ -122,17 +119,18 @@ size_t esp_dns_create_query(uint8_t *buffer, size_t buffer_size, const char *hos
122119
void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_t *dns_response);
123120

124121
/**
125-
* @brief Converts a dns_response_t to an array of IP addresses.
122+
* @brief Copies parsed IP addresses from a DNS response to an array.
126123
*
127-
* This function iterates over the DNS response and extracts valid
128-
* IPv4 and IPv6 addresses, storing them in the provided array.
124+
* This function retrieves the valid IPv4 and IPv6 addresses that were
125+
* previously parsed and stored in the DNS response structure, copying
126+
* them into the provided array.
129127
*
130-
* @param response The DNS response to process.
131-
* @param ipaddr An array to store the extracted IP addresses.
128+
* @param response The parsed DNS response
129+
* @param ipaddr Array to store the copied IP addresses
132130
*
133-
* @return err Status of dns response parsing
131+
* @return err_t ERR_OK on success, or an error code from the response
134132
*/
135-
err_t esp_dns_extract_ip_addresses_from_response(const dns_response_t *response, ip_addr_t ipaddr[]);
133+
err_t esp_dns_get_ips_from_response(const dns_response_t *response, ip_addr_t ipaddr[]);
136134

137135
#ifdef __cplusplus
138136
}

components/esp_dns/examples/esp_dns_basic/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
6767

6868
* **Certificate Issues**:
6969
For DoT and DoH protocols, ensure that the certificates are valid for the DNS server you're using. The example includes Google DNS certificates, but these may need to be updated if they expire.
70+
If you are using the Certificate Bundle and see a `No matching trusted root certificate found` error, it is likely due to cross-signed chains (e.g., from Google). Make sure that `CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_CROSS_SIGNED_VERIFY=y` is enabled in your configuration. This can be enabled via `idf.py menuconfig` -> `Component config` -> `mbedTLS` -> `Certificate Bundle` -> `Support cross-signed certificate verification`.
7071

7172
## Example Output
7273

components/esp_dns/examples/esp_dns_basic/main/esp_dns_example.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include "esp_event.h"
1919
#include "esp_timer.h"
2020
#include "lwip/opt.h"
21-
#include "protocol_examples_common.h"
21+
#include "net_connect.h"
2222
#include "esp_dns.h"
2323
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
2424
#include "esp_crt_bundle.h"
@@ -31,6 +31,10 @@
3131

3232
#define TAG "example_esp_dns"
3333

34+
/* ESP-IDF xTaskCreate() stack size is in bytes. DoT/DoH paths pull in mbedTLS inside
35+
* getaddrinfo(); 4 KiB is too small and triggers a stack overflow on typical builds. */
36+
#define DNS_ADDRINFO_TASK_STACK 8192
37+
3438
extern const char server_root_cert_pem_start[] asm("_binary_cert_google_root_pem_start");
3539
extern const char server_root_cert_pem_end[] asm("_binary_cert_google_root_pem_end");
3640

@@ -137,7 +141,7 @@ static void run_dns_query_task(void)
137141
{
138142
TaskHandle_t task_handle = NULL;
139143
TaskHandle_t parent_handle = xTaskGetCurrentTaskHandle();
140-
xTaskCreate(addr_info_task, "AddressInfo", 4 * 1024, parent_handle, 5, &task_handle);
144+
xTaskCreate(addr_info_task, "AddressInfo", DNS_ADDRINFO_TASK_STACK, parent_handle, 5, &task_handle);
141145

142146
/* Wait for task to complete */
143147
if (task_handle != NULL) {
@@ -298,7 +302,7 @@ void app_main(void)
298302
* Read "Establishing Wi-Fi or Ethernet Connection" section in
299303
* examples/protocols/README.md for more information about this function.
300304
*/
301-
ESP_ERROR_CHECK(example_connect());
305+
ESP_ERROR_CHECK(net_connect());
302306

303307
/* Test Without ESP_DNS module */
304308
ESP_LOGI(TAG, "Executing DNS without initializing ESP_DNS module");
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
dependencies:
22
idf:
3-
version: ">=5.1"
4-
protocol_examples_common:
5-
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
3+
version: ">=5.4.3"
4+
espressif/net_connect: "^0.1.1"
65
esp_dns:
76
version: "*"
87
override_path: '../../../'

0 commit comments

Comments
 (0)