mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 09:09:10 -04:00
feat(example): Added DNS over HTTPS (DoH) example
This commit is contained in:
parent
5462240135
commit
967603b5aa
6
examples/protocols/dns_over_https/CMakeLists.txt
Normal file
6
examples/protocols/dns_over_https/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(dns_over_https)
|
109
examples/protocols/dns_over_https/README.md
Normal file
109
examples/protocols/dns_over_https/README.md
Normal file
@ -0,0 +1,109 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# DNS over HTTPS Example
|
||||
|
||||
This example demonstrates how to use the DNS over HTTPS (DoH) component in an ESP32 application. The example resolves domain names securely via HTTPS using the configured DNS over HTTPS provider (Google(default), Cloudflare, or a custom server).
|
||||
|
||||
## Features
|
||||
|
||||
- **DNS over HTTPS**: Resolves domain names securely using HTTPS.
|
||||
|
||||
## Certificate Options
|
||||
This example provides two certificate options for DNS over HTTPS:
|
||||
|
||||
1. Internal Certificate Bundle (Default): By default, the example uses an internal certificate bundle, making it easy to get started with popular DoH providers like Google and Cloudflare.
|
||||
2. Custom Certificate: If you prefer to use your own DoH server and certificate, you can configure the server name in menuconfig and place your custom certificate in the `cert_custom_root.pem` file. This option provides flexibility if you have specific security or server requirements.
|
||||
|
||||
To configure the certificate option in menuconfig, navigate to `Example DNS-over-HTTPS Configuration → Use internal certificate bundle` to enable or disable the internal bundle.
|
||||
|
||||
## Configuration
|
||||
|
||||
Before building and running the example, you need to configure the DNS over HTTPS provider in `menuconfig`:
|
||||
|
||||
1. Run `idf.py menuconfig`.
|
||||
2. Look for the **Example DNS-over-HTTPS Configuration** section.
|
||||
3. Choose your preferred DNS server:
|
||||
* Google DNS (default: dns.google)
|
||||
* Cloudflare DNS (cloudflare-dns.com)
|
||||
* Custom DNS-over-HTTPS Server
|
||||
4. For custom DNS configuration enter the custom DNS-over-HTTPS server URL.
|
||||
5. Specify a custom DNS-over-HTTPS query path (default: dns-query).
|
||||
6. Toggle whether to use the internal certificate bundle:
|
||||
* If disabled, specify the DNS certificate (cert_custom_root.pem).
|
||||
7. Save your changes and exit the configuration menu.
|
||||
8. Configure Wi-Fi or Ethernet to join a network. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Network Initialization**: The application initializes the network interfaces (Wi-Fi or Ethernet) and establishes a connection.
|
||||
2. **NVS Initialization**: Non-Volatile Storage (NVS) is initialized to store and retrieve system time across reboots.
|
||||
3. **DNS over HTTPS Initialization**: The `init_dns_over_https()` function initializes the DNS over HTTPS resolver, which securely handles DNS queries using HTTPS.
|
||||
4. **Performing getaddrinfo**: The application executes the getaddrinfo operation for several domain names.
|
||||
|
||||
## How to use example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
|
||||
## Troubleshooting Tips
|
||||
|
||||
* **Connectivity**:
|
||||
Ensure that the network connection details are accurate. For example, verify the Wi-Fi SSID and password or check that the Ethernet connection is secure and not faulty.
|
||||
|
||||
* **Stack Overflow Errors**:
|
||||
If you encounter a stack overflow, it might indicate that the DNS server is returning a lengthy error message. To diagnose the issue, try increasing the stack size to retrieve more detailed information.
|
||||
* **Incorrect Path Issues**:
|
||||
Occasionally, stack overflow error is caused by an incorrect server path. Verify the server details and ensure the server path is correctly configured.
|
||||
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (4652) esp_netif_handlers: example_netif_sta ip: 192.168.50.136, mask: 255.255.255.0, gw: 192.168.50.1
|
||||
I (4652) example_connect: Got IPv4 event: Interface "example_netif_sta" address: 192.168.50.136
|
||||
I (5552) example_connect: Got IPv6 event: Interface "example_netif_sta" address: fe80:0000:0000:0000:5abf:25ff:fee0:4100, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5552) example_common: Connected to example_netif_sta
|
||||
I (5562) example_common: - IPv4 address: 192.168.50.136,
|
||||
I (5562) example_common: - IPv6 address: fe80:0000:0000:0000:5abf:25ff:fee0:4100, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5572) time_sync: Updating time from NVS
|
||||
I (5582) main_task: Returned from app_main()
|
||||
I (5592) wifi:<ba-add>idx:1 (ifx:0, a0:36:bc:0e:c4:f0), tid:7, ssn:3, winSize:64
|
||||
I (5622) wifi:<ba-del>idx:0, tid:6
|
||||
I (5622) wifi:<ba-add>idx:0 (ifx:0, a0:36:bc:0e:c4:f0), tid:0, ssn:1, winSize:64
|
||||
I (5962) esp-x509-crt-bundle: Certificate validated
|
||||
I (6772) example_dns_over_https: Using DNS Over HTTPS server: dns.google
|
||||
I (6772) example_dns_over_https: Resolving IP addresses for yahoo.com:
|
||||
I (6772) example_dns_over_https: IPv4: 74.6.143.26
|
||||
I (6782) example_dns_over_https: IPv4: 74.6.231.21
|
||||
I (6782) example_dns_over_https: IPv4: 98.137.11.163
|
||||
I (6792) example_dns_over_https: IPv4: 74.6.231.20
|
||||
|
||||
I (7192) esp-x509-crt-bundle: Certificate validated
|
||||
I (8382) example_dns_over_https: Using DNS Over HTTPS server: dns.google
|
||||
I (8382) example_dns_over_https: Resolving IP addresses for www.google.com:
|
||||
I (8382) example_dns_over_https: IPv6: 2404:6800:4015:803::2004
|
||||
|
||||
I (9032) esp-x509-crt-bundle: Certificate validated
|
||||
I (9862) example_dns_over_https: Using DNS Over HTTPS server: dns.google
|
||||
I (9862) example_dns_over_https: Resolving IP addresses for www.google.com:
|
||||
I (9862) example_dns_over_https: IPv4: 142.250.70.228
|
||||
```
|
@ -0,0 +1,7 @@
|
||||
idf_component_register(SRCS "dns_over_https.c" "dns_utils.c"
|
||||
INCLUDE_DIRS "include" "."
|
||||
PRIV_REQUIRES nvs_flash lwip esp_event esp-tls esp_http_client)
|
||||
|
||||
if(CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM)
|
||||
target_link_libraries(${COMPONENT_LIB} "-u lwip_hook_netconn_external_resolve")
|
||||
endif()
|
@ -0,0 +1,10 @@
|
||||
menu "DNS-over-HTTPS Configuration"
|
||||
|
||||
config HTTPS_DNS_HTTP_VERSION
|
||||
string
|
||||
prompt "DNS-over-HTTPS Server HTTP version"
|
||||
default "1.1"
|
||||
help
|
||||
HTTP version of DNS-over-HTTPS server.
|
||||
|
||||
endmenu
|
@ -0,0 +1,87 @@
|
||||
# DNS Over HTTPS (DoH) Component for ESP-IDF
|
||||
|
||||
## Overview
|
||||
This component allows your ESP-IDF device to use DNS over HTTPS (DoH) for DNS resolution. By using DoH, your DNS queries are encrypted, which helps protect your privacy and prevents third parties from spying on or altering your DNS requests.
|
||||
|
||||
## Features
|
||||
- **Secure DNS Queries:** Encrypts DNS requests using TLS for better privacy.
|
||||
- **Runtime DNS Server Configuration Support:** Configure popular DNS providers like Google or Cloudflare, or set up a custom DNS server directly from your application.
|
||||
- **Certificate Bundle Support (Default):** Uses an internal certificate bundle by default, simplifying the setup for common DoH servers.
|
||||
- **Dynamic DNS Request Generation:** Creates DNS queries on-the-fly to handle different hostnames and types.
|
||||
- **Note on Future Support for DNS over TLS (DoT):** While this component currently supports DNS over HTTPS (DoH), future updates may include support for DNS over TLS (DoT), enabling another secure and privacy-focused method for DNS resolution.
|
||||
|
||||
## How It Works
|
||||
This component utilizes the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` hook to override the core DNS functionality of LWIP and implement custom DNS over HTTPS resolution. To enable this, ensure that the configuration option `Component config → LWIP → Hooks → Netconn external resolve Hook` is set to `Custom implementation`.
|
||||
|
||||
Once you add this component to your project, it will replace the default LWIP DNS resolution automatically.
|
||||
|
||||
The component must be initialized using the `dns_over_https_init()` function. Security details necessary for server communication must be included in the configuration passed to this function.
|
||||
|
||||
**⚠️ Warning:** This component cannot work alongside the OpenThread component, as both components use the CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM hook.
|
||||
|
||||
## DNS Resolution Support
|
||||
This component improves DNS resolution by combining the LWIP DNS module and DoH.
|
||||
|
||||
It supports:
|
||||
### Standard DNS Queries:
|
||||
Uses the LWIP DNS module to resolve DNS server URLs, localhost, and IP addresses.
|
||||
|
||||
Note: Localhost and IP addresses are resolved locally and no actual queries are sent to the DNS server.
|
||||
|
||||
### DNS over HTTPS:
|
||||
For any other DNS queries, it uses mbedTLS to securely send requests over HTTPS, ensuring your queries are encrypted for added privacy.
|
||||
|
||||
## Configuration
|
||||
To set up the DNS over HTTPS component, follow these steps:
|
||||
|
||||
1. Go to your project directory.
|
||||
2. Run `idf.py menuconfig`.
|
||||
3. Look for the **Component config -> DNS-over-HTTPS Configuration** section.
|
||||
4. Set the HTTP version for DNS-over-HTTPS (default: 1.1).
|
||||
|
||||
### Important Configuration Parameters
|
||||
To enable custom DNS resolution, configure the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` setting either through menuconfig by navigating to `Component config → LWIP → Hooks → Netconn external resolve Hook` and enabling it, or by adding `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM=y` to your `sdkconfig.defaults` file to pre-set the configuration during the build process.
|
||||
|
||||
## Initialization Requirements
|
||||
To initialize this module, make sure to call the `init_dns_over_https()` function after these initializations in your main application:
|
||||
|
||||
- `nvs_flash_init()`
|
||||
- `esp_event_loop_create_default()`
|
||||
- `esp_netif_init()`
|
||||
|
||||
## Example Usage
|
||||
Here’s a quick example of how to use this component in your application:
|
||||
|
||||
```c
|
||||
#include "dns_over_https.h" // Include the DoH component header
|
||||
|
||||
void app_main()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init()); /* Initialize the network interface */
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default()); /* Create default event loop */
|
||||
esp_err_t ret = nvs_flash_init(); /* Initialize NVS */
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
/* Sets up periodic time updates */
|
||||
setup_periodic_time_updates();
|
||||
|
||||
/* Initialize the DOH config */
|
||||
dns_over_https_config_t config = {
|
||||
.dns_server = "dns.google",
|
||||
.dns_service_path = "dns-query",
|
||||
/* Supply function to attach certificate bundle */
|
||||
.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
/* or Supplying certificate */
|
||||
.cert_pem = server_root_cert_pem_start,
|
||||
};
|
||||
ESP_ERROR_CHECK(dns_over_https_init(&config));
|
||||
|
||||
// Your application logic here...
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ Warning:** The default stack size may not be sufficient for the thread performing DNS resolution. To avoid potential issues, ensure that the stack size allocated to the task handling DNS resolution is increased.
|
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_tls.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "lwip/prot/dns.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip_default_hooks.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "dns_utils.h"
|
||||
#include "dns_over_https.h"
|
||||
|
||||
#define SERVER_URL_MAX_SZ 256
|
||||
#define BUFFER_SIZE 512
|
||||
|
||||
static const char *TAG = "dns_over_https";
|
||||
static uint8_t buffer_qry_g[BUFFER_SIZE];
|
||||
|
||||
/* Buffer structure to store incoming chunks */
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
int length;
|
||||
} response_buffer_t;
|
||||
|
||||
/* Initialize a global or context-specific buffer */
|
||||
static response_buffer_t dns_response_buffer = { .buffer = NULL, .length = 0 };
|
||||
static dns_response_t dns_response_g;
|
||||
|
||||
/* Global variable to store the configuration */
|
||||
static dns_over_https_config_t server_config_g;
|
||||
|
||||
/* Initializes the DNS over HTTPS (DoH) component with the given configuration. */
|
||||
esp_err_t dns_over_https_init(const dns_over_https_config_t *config)
|
||||
{
|
||||
/* Validate configuration: Either a certificate bundle or a PEM certificate must be provided */
|
||||
if ((!config->crt_bundle_attach) && (!config->cert_pem)) {
|
||||
ESP_LOGE(TAG, "Error: Root certificates must be provided when not using internal certificate bundle.");
|
||||
return ESP_FAIL; /* Return failure code */
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the values from the provided configuration structure
|
||||
* to the global server configuration structure.
|
||||
*/
|
||||
server_config_g.dns_server = config->dns_server; /* Set the DNS server address */
|
||||
server_config_g.dns_service_path = config->dns_service_path; /* Set the DNS service path */
|
||||
server_config_g.cert_pem = config->cert_pem; /* Set the PEM certificate pointer */
|
||||
server_config_g.crt_bundle_attach = config->crt_bundle_attach; /* Set the certificate bundle attach function */
|
||||
|
||||
return ESP_OK; /* Return success code */
|
||||
}
|
||||
|
||||
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
||||
{
|
||||
switch (evt->event_id) {
|
||||
case HTTP_EVENT_ERROR:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_HEADER_SENT:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
|
||||
/* Check if buffer is null, if yes, initialize it */
|
||||
if (dns_response_buffer.buffer == NULL) {
|
||||
dns_response_buffer.buffer = malloc(evt->data_len);
|
||||
dns_response_buffer.length = evt->data_len;
|
||||
memcpy(dns_response_buffer.buffer, evt->data, evt->data_len);
|
||||
} else {
|
||||
/* Reallocate buffer to hold the new data chunk */
|
||||
int new_len = dns_response_buffer.length + evt->data_len;
|
||||
dns_response_buffer.buffer = realloc(dns_response_buffer.buffer, new_len);
|
||||
memcpy(dns_response_buffer.buffer + dns_response_buffer.length, evt->data, evt->data_len);
|
||||
dns_response_buffer.length = new_len;
|
||||
}
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
|
||||
/* Entire response received, process it here */
|
||||
ESP_LOGD(TAG, "Received full response, length: %d", dns_response_buffer.length);
|
||||
|
||||
/* Check if the buffer indicates an HTTP error response */
|
||||
if (HttpStatus_Ok == esp_http_client_get_status_code(evt->client)) {
|
||||
/* Parse the DNS response */
|
||||
parse_dns_response((uint8_t *)dns_response_buffer.buffer, dns_response_buffer.length, &dns_response_g);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "HTTP Error: %d", esp_http_client_get_status_code(evt->client));
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, dns_response_buffer.buffer, dns_response_buffer.length, ESP_LOG_ERROR);
|
||||
dns_response_g.status_code = ERR_VAL;
|
||||
}
|
||||
|
||||
free(dns_response_buffer.buffer);
|
||||
dns_response_buffer.buffer = NULL;
|
||||
dns_response_buffer.length = 0;
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_REDIRECT:
|
||||
ESP_LOGE(TAG, "HTTP_EVENT_REDIRECT: Not supported(%d)", esp_http_client_get_status_code(evt->client));
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a dns_response_t to an array of IP addresses.
|
||||
*
|
||||
* This function iterates over the DNS response and extracts valid
|
||||
* IPv4 and IPv6 addresses, storing them in the provided array.
|
||||
*
|
||||
* @param response The DNS response to process.
|
||||
* @param ipaddr An array to store the extracted IP addresses.
|
||||
*
|
||||
* @return err Status of dns response parsing
|
||||
*/
|
||||
static err_t write_ip_addresses_from_dns_response(const dns_response_t *response, ip_addr_t ipaddr[])
|
||||
{
|
||||
int count = 0;
|
||||
memset(ipaddr, 0, DNS_MAX_HOST_IP * sizeof(ip_addr_t));
|
||||
|
||||
if (response->status_code != ERR_OK) {
|
||||
return response->status_code;
|
||||
}
|
||||
|
||||
/* Iterate over the DNS answers */
|
||||
for (int i = 0; i < response->num_answers && count < DNS_MAX_HOST_IP; i++) {
|
||||
const dns_answer_storage_t *answer = &response->answers[i];
|
||||
|
||||
/* Check if the answer is valid */
|
||||
if (answer->status != ERR_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ipaddr[count] = answer->ip;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/* Store the number of valid IP addresses */
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to handle the HTTPS request for DNS resolution.
|
||||
*
|
||||
* This function generates a DNS request, sends it via HTTPS, and processes
|
||||
* the response to extract IP addresses.
|
||||
*
|
||||
* @param name The name to resolve.
|
||||
* @param addr A pointer to an array to store the resolved IP addresses.
|
||||
* @param addrtypestr The address RR type (A or AAAA).
|
||||
*/
|
||||
static err_t do_https_request(const char *name, ip_addr_t *addr, int addrtype)
|
||||
{
|
||||
err_t err = ERR_OK;
|
||||
const char *prefix = "https://";
|
||||
|
||||
size_t url_len = strlen(prefix) + \
|
||||
strlen(server_config_g.dns_server) + 1 + \
|
||||
strlen(server_config_g.dns_service_path) + 1; /* 1 for '/' and 1 for '\0' */
|
||||
|
||||
char *dns_server_url = malloc(url_len);
|
||||
if (dns_server_url == NULL) {
|
||||
ESP_LOGE(TAG, "Memory allocation failed");
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
snprintf(dns_server_url, url_len, "%s%s/%s", prefix,
|
||||
server_config_g.dns_server,
|
||||
server_config_g.dns_service_path);
|
||||
|
||||
esp_http_client_config_t config = {
|
||||
.url = dns_server_url,
|
||||
.event_handler = _http_event_handler,
|
||||
.method = HTTP_METHOD_POST,
|
||||
};
|
||||
|
||||
if (server_config_g.crt_bundle_attach) {
|
||||
config.crt_bundle_attach = server_config_g.crt_bundle_attach;
|
||||
} else {
|
||||
config.cert_pem = server_config_g.cert_pem; /* Use the root certificate for dns.google if needed */
|
||||
}
|
||||
|
||||
/* Generate DNS request string */
|
||||
size_t query_size = create_dns_query(buffer_qry_g, sizeof(buffer_qry_g), name, addrtype, &dns_response_g.id);
|
||||
if (query_size == -1) {
|
||||
ESP_LOGE(TAG, "Error: Hostname too big");
|
||||
err = ERR_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
esp_http_client_handle_t client = esp_http_client_init(&config);
|
||||
if (client == NULL) {
|
||||
ESP_LOGE(TAG, "Error initializing HTTP client");
|
||||
err = ERR_VAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Set headers for DoH request */
|
||||
esp_err_t ret = esp_http_client_set_header(client, "Content-Type", "application/dns-message");
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error setting HTTP header: %s", esp_err_to_name(ret));
|
||||
err = ERR_VAL;
|
||||
goto client_cleanup;
|
||||
}
|
||||
|
||||
/* Set DNS query payload */
|
||||
ret = esp_http_client_set_post_field(client, (const char *)buffer_qry_g, query_size);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error setting POST field: %s", esp_err_to_name(ret));
|
||||
err = ERR_VAL;
|
||||
goto client_cleanup;
|
||||
}
|
||||
|
||||
/* Perform the request */
|
||||
ret = esp_http_client_perform(client);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGD(TAG, "HTTP POST Status = %d, content_length = %lld",
|
||||
esp_http_client_get_status_code(client),
|
||||
esp_http_client_get_content_length(client));
|
||||
|
||||
/* Check if the response status or buffer indicates an error response */
|
||||
if ((HttpStatus_Ok != esp_http_client_get_status_code(client)) ||
|
||||
(dns_response_g.status_code != ERR_OK)) {
|
||||
err = ERR_ARG;
|
||||
goto client_cleanup;
|
||||
}
|
||||
|
||||
err = write_ip_addresses_from_dns_response(&dns_response_g, addr);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(ret));
|
||||
err = ERR_VAL;
|
||||
}
|
||||
|
||||
client_cleanup:
|
||||
esp_http_client_cleanup(client);
|
||||
cleanup:
|
||||
free(dns_server_url);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM)
|
||||
int lwip_hook_netconn_external_resolve(const char *name, ip_addr_t *addr, u8_t addrtype, err_t *err)
|
||||
{
|
||||
LWIP_UNUSED_ARG(name);
|
||||
LWIP_UNUSED_ARG(addr);
|
||||
LWIP_UNUSED_ARG(addrtype);
|
||||
LWIP_UNUSED_ARG(err);
|
||||
|
||||
if (server_config_g.dns_server == NULL) {
|
||||
ESP_LOGE(TAG, "DNS Over HTTPS Server not set. Ensure DNS Over HTTPS module was initialized");
|
||||
*err = ERR_VAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if HTTPS_DNS_SERVER is in the dns cache */
|
||||
if ((strcmp(name, server_config_g.dns_server) == 0) ||
|
||||
#if LWIP_HAVE_LOOPIF
|
||||
(strcmp(name, "localhost") == 0) ||
|
||||
#endif
|
||||
ipaddr_aton(name, addr)) { /* host name already in octet notation */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((addrtype == NETCONN_DNS_IPV4) || (addrtype == NETCONN_DNS_IPV4_IPV6)) {
|
||||
*err = do_https_request(name, addr, DNS_RRTYPE_A);
|
||||
} else if ((addrtype == NETCONN_DNS_IPV6) || (addrtype == NETCONN_DNS_IPV6_IPV4)) {
|
||||
*err = do_https_request(name, addr, DNS_RRTYPE_AAAA);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Error: Invalid address type");
|
||||
*err = ERR_VAL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE... */
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <arpa/inet.h>
|
||||
#include "esp_random.h"
|
||||
#include "dns_utils.h"
|
||||
|
||||
/* Function to create a DNS query */
|
||||
size_t create_dns_query(uint8_t *buffer, size_t buffer_size, const char *hostname, int addrtype, uint16_t *id_o)
|
||||
{
|
||||
/*
|
||||
DNS Query for example.com (Type A)
|
||||
0x00, 0x00, // Transaction ID
|
||||
0x01, 0x00, // Flags: Standard query
|
||||
0x00, 0x01, // Questions: 1
|
||||
0x00, 0x00, // Answer RRs: 0
|
||||
0x00, 0x00, // Authority RRs: 0
|
||||
0x00, 0x00, // Additional RRs: 0
|
||||
0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', // QNAME: example.com
|
||||
0x03, 'c', 'o', 'm',
|
||||
0x00, // End of QNAME
|
||||
0x00, 0x01, // QTYPE: A (host address)
|
||||
0x00, 0x01 // QCLASS: IN (internet)
|
||||
*/
|
||||
|
||||
dns_header_t *header = (dns_header_t *)buffer;
|
||||
memset(buffer, 0, buffer_size);
|
||||
|
||||
/* Set header fields */
|
||||
*id_o = (uint16_t)(esp_random() & 0xFFFF); /* Return the id for response validation */
|
||||
header->id = htons(*id_o); /* Random transaction ID */
|
||||
header->flags = htons(0x0100); /* Standard query with recursion */
|
||||
header->qdcount = htons(1); /* One question */
|
||||
|
||||
/* Add the question name */
|
||||
uint8_t *qname = buffer + sizeof(dns_header_t);
|
||||
const char *dot = hostname;
|
||||
while (*dot) {
|
||||
const char *next_dot = strchr(dot, '.');
|
||||
if (!next_dot) {
|
||||
next_dot = dot + strlen(dot);
|
||||
}
|
||||
uint8_t len = next_dot - dot;
|
||||
*qname++ = len;
|
||||
/* Check for overflow */
|
||||
if ((qname - buffer) > buffer_size) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(qname, dot, len);
|
||||
qname += len;
|
||||
dot = (*next_dot) ? next_dot + 1 : next_dot;
|
||||
}
|
||||
*qname++ = 0; /* Null-terminate the question name */
|
||||
|
||||
/* Set question fields */
|
||||
dns_question_t *question = (dns_question_t *)qname;
|
||||
question->qtype = htons(addrtype);
|
||||
question->qclass = htons(DNS_RRCLASS_IN);
|
||||
|
||||
/* Return the total query size */
|
||||
return (qname + sizeof(dns_question_t)) - buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips over a DNS name in a DNS reply message and returns the offset to the end of the name.
|
||||
*
|
||||
* This function handles both uncompressed labels and compression pointers according to RFC 1035.
|
||||
* Reference: RFC 1035, sections 3.1 (Name Space Definitions) and 4.1.4 (Message Compression).
|
||||
*
|
||||
* @param ptr Pointer to the start of the DNS name in the DNS message.
|
||||
* @return The ptr to the end of the DNS name.
|
||||
*/
|
||||
static uint8_t *skip_dns_name(uint8_t *ptr)
|
||||
{
|
||||
uint8_t offset = 0;
|
||||
|
||||
/* Loop through each part of the name, handling labels and compression pointers */
|
||||
while (ptr[offset] != 0) {
|
||||
/* Check if this part is a compression pointer, indicated by the two high bits set to 1 (0xC0) */
|
||||
/* RFC 1035, Section 4.1.4: Compression pointers */
|
||||
if ((ptr[offset] & 0xC0) == 0xC0) {
|
||||
/* Compression pointer is 2 bytes; move offset by 2 and stop */
|
||||
offset += 2;
|
||||
return ptr + offset; /* End of name processing due to pointer */
|
||||
} else {
|
||||
/* Otherwise, it's a label
|
||||
RFC 1035, Section 3.1: Labels
|
||||
- The first byte is the length of this label
|
||||
- Followed by 'length' bytes of label content */
|
||||
offset += ptr[offset] + 1; /* Move past this label (1 byte for length + label content) */
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC 1035, Section 3.1: End of a name is indicated by a zero-length byte (0x00) */
|
||||
offset += 1; /* Move past the terminating zero byte */
|
||||
return ptr + offset;
|
||||
}
|
||||
|
||||
void parse_dns_response(uint8_t *buffer, size_t response_size, dns_response_t *dns_response)
|
||||
{
|
||||
dns_header_t *header = (dns_header_t *)buffer;
|
||||
|
||||
dns_response->status_code = ERR_OK; /* Initialize DNS response code */
|
||||
|
||||
/* Check if there are answers and Transaction id matches */
|
||||
int answer_count = ntohs(header->ancount);
|
||||
if ((ntohs(header->id) != dns_response->id) || (answer_count == 0)) {
|
||||
dns_response->status_code = ERR_VAL; /* DNS response code */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure only MAX_ANSWERS are processed */
|
||||
dns_response->num_answers = (answer_count < MAX_ANSWERS ? answer_count : MAX_ANSWERS);
|
||||
|
||||
/* Skip the header and question section */
|
||||
uint8_t *ptr = buffer + sizeof(dns_header_t);
|
||||
|
||||
/* Skip the question name */
|
||||
ptr = skip_dns_name(ptr);
|
||||
|
||||
/* Skip the question type and class */
|
||||
ptr += sizeof(dns_question_t);
|
||||
|
||||
/* Parse each answer record */
|
||||
for (int i = 0; i < dns_response->num_answers; i++) {
|
||||
|
||||
/* Answer fields */
|
||||
ptr = skip_dns_name(ptr);
|
||||
|
||||
dns_answer_t *answer = (dns_answer_t *)ptr;
|
||||
uint16_t type = ntohs(answer->type);
|
||||
uint16_t class = ntohs(answer->class);
|
||||
uint32_t ttl = ntohl(answer->ttl);
|
||||
uint16_t data_len = ntohs(answer->data_len);
|
||||
|
||||
/* Skip fixed parts of answer (type, class, ttl, data_len) */
|
||||
ptr += SIZEOF_DNS_ANSWER;
|
||||
|
||||
/* Validate RR class and ttl */
|
||||
if ((class != DNS_RRCLASS_IN) || (ttl > DNS_MAX_TTL)) {
|
||||
dns_response->answers[i].status = ERR_VAL;
|
||||
goto next_answer;
|
||||
}
|
||||
|
||||
/* Initialize status for this answer */
|
||||
dns_response->answers[i].status = ERR_OK;
|
||||
|
||||
/* Check the type of answer */
|
||||
if (type == DNS_RRTYPE_A && data_len == 4) {
|
||||
/* IPv4 Address (A record) */
|
||||
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in_addr));
|
||||
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V4);
|
||||
} else if (type == DNS_RRTYPE_AAAA && data_len == 16) {
|
||||
/* IPv6 Address (AAAA record) */
|
||||
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in6_addr));
|
||||
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V6);
|
||||
} else {
|
||||
dns_response->answers[i].status = ERR_VAL;
|
||||
}
|
||||
|
||||
next_answer:
|
||||
/* Move pointer to next answer */
|
||||
ptr += data_len;
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_tls.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "lwip/prot/dns.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* DNS header structure */
|
||||
typedef struct {
|
||||
uint16_t id; /* Identification */
|
||||
uint16_t flags; /* Flags */
|
||||
uint16_t qdcount; /* Number of questions */
|
||||
uint16_t ancount; /* Number of answers */
|
||||
uint16_t nscount; /* Number of authority records */
|
||||
uint16_t arcount; /* Number of additional records */
|
||||
} dns_header_t;
|
||||
|
||||
/* DNS question structure */
|
||||
typedef struct {
|
||||
/* DNS query record starts with either a domain name or a pointer
|
||||
to a name already present somewhere in the packet. */
|
||||
uint16_t qtype; /* Question type (e.g., A, MX) */
|
||||
uint16_t qclass; /* Question class (e.g., IN for internet) */
|
||||
} dns_question_t;
|
||||
|
||||
/** DNS answer message structure.
|
||||
No packing needed: only used locally on the stack. */
|
||||
typedef struct {
|
||||
/* DNS query record starts with either a domain name or a pointer
|
||||
to a name already present somewhere in the packet. */
|
||||
uint16_t type; /* Type (e.g., A, MX) */
|
||||
uint16_t class; /* Class (e.g., IN for internet) */
|
||||
uint32_t ttl; /* Time-to-live */
|
||||
uint16_t data_len; /* Length of data */
|
||||
} dns_answer_t;
|
||||
#define SIZEOF_DNS_ANSWER 10
|
||||
|
||||
/** DNS resource record max. TTL (one week as default) */
|
||||
#define DNS_MAX_TTL 604800
|
||||
|
||||
#define MAX_ANSWERS (CONFIG_LWIP_DNS_MAX_HOST_IP)
|
||||
|
||||
typedef struct {
|
||||
err_t status;
|
||||
ip_addr_t ip;
|
||||
} dns_answer_storage_t;
|
||||
|
||||
typedef struct {
|
||||
err_t status_code;
|
||||
uint16_t id;
|
||||
int num_answers;
|
||||
dns_answer_storage_t answers[MAX_ANSWERS];
|
||||
} dns_response_t;
|
||||
|
||||
/* Function to create a DNS query for an A and AAAA records */
|
||||
size_t create_dns_query(uint8_t *buffer, size_t buffer_size, const char *hostname, int addrtype, uint16_t *id_o);
|
||||
|
||||
/* Function to parse a DNS answer for an A and AAAA records */
|
||||
void parse_dns_response(uint8_t *buffer, size_t response_size, dns_response_t *dns_response);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
version: 0.1.0
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.1"
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Define the configuration structure */
|
||||
typedef struct {
|
||||
const char *dns_server; /*!< DNS server url or IP address */
|
||||
const char *dns_service_path; /*!< Path to server DNS-over-HTTPS Service */
|
||||
const char *cert_pem; /*!< SSL server certification, PEM format as string, if the client requires to verify server */
|
||||
esp_err_t (*crt_bundle_attach)(void *conf); /*!< Function pointer to esp_crt_bundle_attach. Enables the use of certification
|
||||
bundle for server verification, must be enabled in menuconfig */
|
||||
} dns_over_https_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes the DNS over HTTPS (DoH) component with the given configuration.
|
||||
*
|
||||
* This function sets up the DNS over HTTPS component by configuring the DNS server,
|
||||
* service path, and root certificates required for secure communication. It validates
|
||||
* the provided configuration and ensures necessary parameters are set.
|
||||
*
|
||||
* @param[in] config Pointer to the configuration structure containing DNS server details,
|
||||
* service path, and root certificate options.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise \see esp_err_t
|
||||
*
|
||||
* @warning This function must be called at least once before making any DNS-over-HTTPS requests
|
||||
*/
|
||||
esp_err_t dns_over_https_init(const dns_over_https_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "time_sync.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES nvs_flash esp_event esp_netif esp_timer)
|
@ -0,0 +1,18 @@
|
||||
menu "Time Sync Configuration"
|
||||
|
||||
config TIME_SYNC_NTP_SERVER
|
||||
string "NTP Server"
|
||||
default "pool.ntp.org"
|
||||
help
|
||||
Configure the NTP server for time synchronization.
|
||||
The default is set to "pool.ntp.org".
|
||||
|
||||
config TIME_SYNC_NTP_UPDATE_PERIOD
|
||||
int "NTP Time Update Interval (in hours)"
|
||||
default 24
|
||||
range 1 24
|
||||
help
|
||||
The time interval in hours for updating the system time using the NTP server.
|
||||
For example, if set to 12, the system will update the time from the NTP server every 12 hours.
|
||||
|
||||
endmenu
|
@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
version: 0.1.0
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.1"
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Update system time from NVS or SNTP if not available.
|
||||
*
|
||||
* This function retrieves the stored system time from NVS. If the time is not found,
|
||||
* it synchronizes with the SNTP server and updates the time in NVS.
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on failure.
|
||||
*/
|
||||
esp_err_t update_time_from_nvs(void);
|
||||
|
||||
/**
|
||||
* @brief Fetch the current system time and store it in NVS.
|
||||
*
|
||||
* This function initializes SNTP, retrieves the current system time, and stores
|
||||
* it in Non-Volatile Storage (NVS). In case of failure, an error message is logged.
|
||||
*
|
||||
* @param[in] args Unused argument placeholder.
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on failure.
|
||||
*/
|
||||
esp_err_t fetch_and_store_time_in_nvs(void*);
|
||||
|
||||
/**
|
||||
* @brief Sets up periodic time updates.
|
||||
*
|
||||
* If the reset reason is power-on, this function updates the system time from NVS.
|
||||
* It also creates a periodic timer to regularly fetch and store the time in NVS
|
||||
* using `fetch_and_store_time_in_nvs` as the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void setup_periodic_time_updates(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_netif_sntp.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "time_sync.h"
|
||||
|
||||
static const char *TAG = "time_sync";
|
||||
|
||||
#define STORAGE_NAMESPACE "storage"
|
||||
|
||||
/* Timer interval once every day (24 Hours) */
|
||||
#define SECONDS_PER_HOUR 3600ULL
|
||||
#define MICROSECONDS_PER_SECOND 1000000ULL
|
||||
#define TIME_PERIOD ((uint64_t)CONFIG_TIME_SYNC_NTP_UPDATE_PERIOD * SECONDS_PER_HOUR * MICROSECONDS_PER_SECOND) // Convert hours to microseconds
|
||||
|
||||
/**
|
||||
* @brief Initialize SNTP service with predefined servers.
|
||||
*
|
||||
* This function sets up the SNTP service to sync time.
|
||||
*/
|
||||
void initialize_sntp(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing SNTP");
|
||||
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(1,
|
||||
ESP_SNTP_SERVER_LIST(CONFIG_TIME_SYNC_NTP_SERVER));
|
||||
esp_netif_sntp_init(&config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain system time from SNTP service.
|
||||
*
|
||||
* This function attempts to synchronize the system time using SNTP, retrying
|
||||
* up to 10 times if necessary. If the synchronization is successful, it returns
|
||||
* ESP_OK, otherwise ESP_FAIL.
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on failure.
|
||||
*/
|
||||
static esp_err_t obtain_time(void)
|
||||
{
|
||||
/* wait for time to be set */
|
||||
int retry = 0;
|
||||
const int retry_count = 10;
|
||||
while (esp_netif_sntp_sync_wait(pdMS_TO_TICKS(2000)) != ESP_OK && ++retry < retry_count) {
|
||||
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
|
||||
}
|
||||
if (retry == retry_count) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fetch the current system time and store it in NVS.
|
||||
*
|
||||
* This function initializes SNTP, retrieves the current system time, and stores
|
||||
* it in Non-Volatile Storage (NVS). In case of failure, an error message is logged.
|
||||
*
|
||||
* @param[in] args Unused argument placeholder.
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on failure.
|
||||
*/
|
||||
esp_err_t fetch_and_store_time_in_nvs(void *args)
|
||||
{
|
||||
nvs_handle_t my_handle = 0;
|
||||
esp_err_t err;
|
||||
|
||||
initialize_sntp();
|
||||
if (obtain_time() != ESP_OK) {
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
/* Open */
|
||||
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
|
||||
if (err != ESP_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Write */
|
||||
err = nvs_set_i64(my_handle, "timestamp", now);
|
||||
if (err != ESP_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = nvs_commit(my_handle);
|
||||
if (err != ESP_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (my_handle != 0) {
|
||||
nvs_close(my_handle);
|
||||
}
|
||||
esp_netif_sntp_deinit();
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error updating time in nvs");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Updated time in NVS");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update system time from NVS or SNTP if not available.
|
||||
*
|
||||
* This function retrieves the stored system time from NVS. If the time is not found,
|
||||
* it synchronizes with the SNTP server and updates the time in NVS.
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on failure.
|
||||
*/
|
||||
esp_err_t update_time_from_nvs(void)
|
||||
{
|
||||
nvs_handle_t my_handle = 0;
|
||||
esp_err_t err;
|
||||
|
||||
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error opening NVS");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
int64_t timestamp = 0;
|
||||
|
||||
err = nvs_get_i64(my_handle, "timestamp", ×tamp);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
ESP_LOGI(TAG, "Time not found in NVS. Syncing time from SNTP server.");
|
||||
if (fetch_and_store_time_in_nvs(NULL) != ESP_OK) {
|
||||
err = ESP_FAIL;
|
||||
} else {
|
||||
err = ESP_OK;
|
||||
}
|
||||
} else if (err == ESP_OK) {
|
||||
struct timeval get_nvs_time;
|
||||
get_nvs_time.tv_sec = timestamp;
|
||||
settimeofday(&get_nvs_time, NULL);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (my_handle != 0) {
|
||||
nvs_close(my_handle);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets up periodic time updates.
|
||||
*
|
||||
* If the reset reason is power-on, this function updates the system time from NVS.
|
||||
* It also creates a periodic timer to regularly fetch and store the time in NVS
|
||||
* using `fetch_and_store_time_in_nvs` as the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void setup_periodic_time_updates(void)
|
||||
{
|
||||
/* Check if the reset reason is power-on, and update time from NVS */
|
||||
if (esp_reset_reason() == ESP_RST_POWERON) {
|
||||
ESP_LOGI(TAG, "Updating time from NVS");
|
||||
ESP_ERROR_CHECK(update_time_from_nvs());
|
||||
}
|
||||
|
||||
/* Set up a periodic timer to fetch and store the time in NVS */
|
||||
const esp_timer_create_args_t nvs_update_timer_args = {
|
||||
.callback = (void *) &fetch_and_store_time_in_nvs,
|
||||
};
|
||||
|
||||
esp_timer_handle_t nvs_update_timer;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&nvs_update_timer_args, &nvs_update_timer));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(nvs_update_timer, TIME_PERIOD));
|
||||
}
|
10
examples/protocols/dns_over_https/main/CMakeLists.txt
Normal file
10
examples/protocols/dns_over_https/main/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# Check for configuration options and set the EMBED_TXTFILES accordingly
|
||||
if(DEFINED CONFIG_HTTPS_DNS_CERT_GOOGLE_HIDDEN)
|
||||
set(cert_file "${CONFIG_HTTPS_DNS_CERT_GOOGLE_HIDDEN}")
|
||||
elseif(DEFINED CONFIG_HTTPS_DNS_CERT_CUSTOM_HIDDEN)
|
||||
set(cert_file "${CONFIG_HTTPS_DNS_CERT_CUSTOM_HIDDEN}")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "example_dns_over_https.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES ${cert_file})
|
102
examples/protocols/dns_over_https/main/Kconfig.projbuild
Normal file
102
examples/protocols/dns_over_https/main/Kconfig.projbuild
Normal file
@ -0,0 +1,102 @@
|
||||
menu "Example DNS-over-HTTPS Configuration"
|
||||
|
||||
choice HTTPS_DNS_SERVER
|
||||
prompt "Choose DNS-over-HTTPS Server"
|
||||
default HTTPS_DNS_SERVER_GOOGLE
|
||||
|
||||
config HTTPS_DNS_SERVER_GOOGLE
|
||||
bool "Google DNS (dns.google)"
|
||||
help
|
||||
Use Google's DNS-over-HTTPS server (dns.google) with its corresponding root certificate.
|
||||
|
||||
config HTTPS_DNS_SERVER_CLOUDFLARE
|
||||
bool "Cloudflare DNS (cloudflare-dns.com)"
|
||||
help
|
||||
Use Cloudflare's DNS-over-HTTPS server (cloudflare-dns.com) with its corresponding root certificate.
|
||||
|
||||
config HTTPS_DNS_SERVER_CUSTOM
|
||||
bool "Custom DNS-over-HTTPS Server"
|
||||
help
|
||||
Use a custom DNS-over-HTTPS server. You must specify both the server URL and certificate manually.
|
||||
endchoice
|
||||
|
||||
config HTTPS_DNS_SERVER_URL_GOOGLE
|
||||
string
|
||||
prompt "Google DNS-over-HTTPS Server URL"
|
||||
default "dns.google"
|
||||
depends on HTTPS_DNS_SERVER_GOOGLE
|
||||
help
|
||||
Google DNS-over-HTTPS server URL.
|
||||
|
||||
config HTTPS_DNS_SERVICE_PATH_GOOGLE
|
||||
string
|
||||
prompt "Path to Google DNS-over-HTTPS Service"
|
||||
default "dns-query"
|
||||
depends on HTTPS_DNS_SERVER_GOOGLE
|
||||
help
|
||||
Path to Google DNS-over-HTTPS Service.
|
||||
|
||||
config HTTPS_DNS_SERVER_URL_CLOUDFLARE
|
||||
string
|
||||
prompt "Cloudflare DNS-over-HTTPS Server URL"
|
||||
default "cloudflare-dns.com"
|
||||
depends on HTTPS_DNS_SERVER_CLOUDFLARE
|
||||
help
|
||||
Cloudflare DNS-over-HTTPS server URL.
|
||||
|
||||
config HTTPS_DNS_SERVICE_PATH_CLOUDFLARE
|
||||
string
|
||||
prompt "Path to Cloudflare DNS-over-HTTPS Service"
|
||||
default "dns-query"
|
||||
depends on HTTPS_DNS_SERVER_CLOUDFLARE
|
||||
help
|
||||
Path to Cloudflare DNS-over-HTTPS Service.
|
||||
|
||||
config HTTPS_DNS_SERVER_URL_CUSTOM
|
||||
string
|
||||
prompt "Custom DNS-over-HTTPS Server URL"
|
||||
depends on HTTPS_DNS_SERVER_CUSTOM
|
||||
help
|
||||
Specify your custom DNS-over-HTTPS server URL here.
|
||||
|
||||
config HTTPS_DNS_SERVICE_PATH_CUSTOM
|
||||
string
|
||||
prompt "Path to the Custom DNS-over-HTTPS Service"
|
||||
default "dns-query"
|
||||
depends on HTTPS_DNS_SERVER_CUSTOM
|
||||
help
|
||||
Path to the Custom DNS-over-HTTPS Service.
|
||||
|
||||
config HTTPS_DNS_ESP_CERT_BUNDLE
|
||||
bool "Use internal certificate bundle"
|
||||
default y
|
||||
help
|
||||
Enable this option to use the internal certificate bundle for DNS-over-HTTPS.
|
||||
|
||||
config HTTPS_DNS_CERT_GOOGLE_HIDDEN
|
||||
string
|
||||
default "cert_google_root.pem"
|
||||
depends on HTTPS_DNS_SERVER_GOOGLE && !HTTPS_DNS_ESP_CERT_BUNDLE
|
||||
|
||||
config HTTPS_DNS_CERT_GOOGLE
|
||||
string
|
||||
prompt "Google DNS Certificate (readonly)"
|
||||
default HTTPS_DNS_CERT_GOOGLE_HIDDEN
|
||||
depends on HTTPS_DNS_SERVER_GOOGLE && !HTTPS_DNS_ESP_CERT_BUNDLE
|
||||
help
|
||||
Google DNS root certificate in PEM format. This option is read-only.
|
||||
|
||||
config HTTPS_DNS_CERT_CUSTOM_HIDDEN
|
||||
string
|
||||
default "cert_custom_root.pem"
|
||||
depends on HTTPS_DNS_SERVER_CUSTOM && !HTTPS_DNS_ESP_CERT_BUNDLE
|
||||
|
||||
config HTTPS_DNS_CERT_CUSTOM
|
||||
string
|
||||
prompt "Custom DNS Certificate (readonly)"
|
||||
default HTTPS_DNS_CERT_CUSTOM_HIDDEN
|
||||
depends on HTTPS_DNS_SERVER_CUSTOM && !HTTPS_DNS_ESP_CERT_BUNDLE
|
||||
help
|
||||
Specify the certificate file for the custom DNS server in PEM format. This option is read-only.
|
||||
|
||||
endmenu
|
31
examples/protocols/dns_over_https/main/cert_google_root.pem
Normal file
31
examples/protocols/dns_over_https/main/cert_google_root.pem
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
|
||||
27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
|
||||
Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
|
||||
TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
|
||||
qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
|
||||
szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
|
||||
Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
|
||||
MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
|
||||
wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
|
||||
aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
|
||||
VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
|
||||
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
|
||||
C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
|
||||
QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
|
||||
h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
|
||||
7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
|
||||
ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
|
||||
MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
|
||||
Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
|
||||
6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
|
||||
0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
|
||||
2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
|
||||
bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
|
||||
-----END CERTIFICATE-----
|
160
examples/protocols/dns_over_https/main/example_dns_over_https.c
Normal file
160
examples/protocols/dns_over_https/main/example_dns_over_https.c
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_timer.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "time_sync.h"
|
||||
#include "dns_over_https.h"
|
||||
#if defined(CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE) && defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
#include "esp_crt_bundle.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE) && !defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
#error "CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE is enabled, but CONFIG_MBEDTLS_CERTIFICATE_BUNDLE is not enabled. Please enable CONFIG_MBEDTLS_CERTIFICATE_BUNDLE."
|
||||
#endif
|
||||
|
||||
#ifndef INET6_ADDRSTRLEN
|
||||
#define INET6_ADDRSTRLEN INET_ADDRSTRLEN
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HTTPS_DNS_SERVER_GOOGLE
|
||||
#define HTTPS_DNS_SERVER CONFIG_HTTPS_DNS_SERVER_URL_GOOGLE
|
||||
#define HTTPS_DNS_SERVICE_PATH CONFIG_HTTPS_DNS_SERVICE_PATH_GOOGLE
|
||||
#if !defined(CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE)
|
||||
extern const char server_root_cert_pem_start[] asm("_binary_cert_google_root_pem_start");
|
||||
extern const char server_root_cert_pem_end[] asm("_binary_cert_google_root_pem_end");
|
||||
#endif /* CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE */
|
||||
|
||||
#elif CONFIG_HTTPS_DNS_SERVER_CLOUDFLARE
|
||||
#define HTTPS_DNS_SERVER CONFIG_HTTPS_DNS_SERVER_URL_CLOUDFLARE
|
||||
#define HTTPS_DNS_SERVICE_PATH CONFIG_HTTPS_DNS_SERVICE_PATH_CLOUDFLARE
|
||||
const char *server_root_cert_pem_start = NULL;
|
||||
const char *server_root_cert_pem_end = NULL;
|
||||
|
||||
#elif CONFIG_HTTPS_DNS_SERVER_CUSTOM
|
||||
#define HTTPS_DNS_SERVER CONFIG_HTTPS_DNS_SERVER_URL_CUSTOM
|
||||
#define HTTPS_DNS_SERVICE_PATH CONFIG_HTTPS_DNS_SERVICE_PATH_CUSTOM
|
||||
#if !defined(CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE)
|
||||
extern const char server_root_cert_pem_start[] asm("_binary_cert_custom_root_pem_start");
|
||||
extern const char server_root_cert_pem_end[] asm("_binary_cert_custom_root_pem_end");
|
||||
#endif /* CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE */
|
||||
#endif
|
||||
|
||||
static const char *TAG = "example_dns_over_https";
|
||||
|
||||
static void do_getaddrinfo(char *hostname, int family)
|
||||
{
|
||||
struct addrinfo hints, *res, *p;
|
||||
int status;
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
void *addr = NULL;
|
||||
char *ipver = NULL;
|
||||
|
||||
/* Initialize the hints structure */
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = family;
|
||||
hints.ai_socktype = SOCK_STREAM; /* TCP stream sockets */
|
||||
|
||||
/* Get address information */
|
||||
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
|
||||
ESP_LOGE(TAG, "getaddrinfo error: %d", status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Using DNS Over HTTPS server: %s", HTTPS_DNS_SERVER);
|
||||
ESP_LOGI(TAG, "Resolving IP addresses for %s:", hostname);
|
||||
|
||||
/* Loop through all the results */
|
||||
for (p = res; p != NULL; p = p->ai_next) {
|
||||
|
||||
/* Get pointer to the address itself */
|
||||
#if defined(CONFIG_LWIP_IPV4)
|
||||
if (p->ai_family == AF_INET) { /* IPv4 */
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
||||
addr = &(ipv4->sin_addr);
|
||||
ipver = "IPv4";
|
||||
|
||||
/* Convert the IP to a string and print it */
|
||||
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
|
||||
ESP_LOGI(TAG, "%s: %s", ipver, ipstr);
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_LWIP_IPV6)
|
||||
if (p->ai_family == AF_INET6) { /* IPv6 */
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
|
||||
addr = &(ipv6->sin6_addr);
|
||||
ipver = "IPv6";
|
||||
|
||||
/* Convert the IP to a string and print it */
|
||||
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
|
||||
ESP_LOGI(TAG, "%s: %s", ipver, ipstr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
cleanup:
|
||||
freeaddrinfo(res); /* Free the linked list */
|
||||
}
|
||||
|
||||
static void addr_info_task(void *pvParameters)
|
||||
{
|
||||
do_getaddrinfo("yahoo.com", AF_INET);
|
||||
do_getaddrinfo("yahoo.com", AF_UNSPEC);
|
||||
do_getaddrinfo("www.google.com", AF_INET6);
|
||||
do_getaddrinfo("www.google.com", AF_UNSPEC);
|
||||
do_getaddrinfo("0.0.0.0", AF_UNSPEC);
|
||||
do_getaddrinfo("fe80:0000:0000:0000:5abf:25ff:fee0:4100", AF_UNSPEC);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_err_t ret = nvs_flash_init(); /* Initialize NVS */
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
/* Enables periodic time synchronization required for certificate expiry validation */
|
||||
#ifdef CONFIG_MBEDTLS_HAVE_TIME_DATE
|
||||
setup_periodic_time_updates();
|
||||
#endif
|
||||
|
||||
/* Initialize the DOH config */
|
||||
dns_over_https_config_t config = {
|
||||
.dns_server = HTTPS_DNS_SERVER,
|
||||
.dns_service_path = HTTPS_DNS_SERVICE_PATH,
|
||||
#if defined(CONFIG_HTTPS_DNS_ESP_CERT_BUNDLE)
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
#else
|
||||
.cert_pem = server_root_cert_pem_start,
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(dns_over_https_init(&config));
|
||||
|
||||
xTaskCreate(addr_info_task, "AddressInfo", 4 * 1024, NULL, 5, NULL);
|
||||
}
|
10
examples/protocols/dns_over_https/main/idf_component.yml
Normal file
10
examples/protocols/dns_over_https/main/idf_component.yml
Normal file
@ -0,0 +1,10 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.1"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
dns_over_https:
|
||||
path: ${IDF_PATH}/examples/protocols/dns_over_https/components/dns_over_https
|
||||
time_sync:
|
||||
path: ${IDF_PATH}/examples/protocols/dns_over_https/components/time_sync
|
8
examples/protocols/dns_over_https/sdkconfig.defaults
Normal file
8
examples/protocols/dns_over_https/sdkconfig.defaults
Normal file
@ -0,0 +1,8 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
CONFIG_LWIP_DNS_MAX_HOST_IP=4
|
||||
CONFIG_LWIP_USE_ESP_GETADDRINFO=y
|
||||
CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM=y
|
||||
CONFIG_MBEDTLS_HAVE_TIME_DATE=y
|
Loading…
x
Reference in New Issue
Block a user