feat(example): Added DNS over HTTPS (DoH) example

This commit is contained in:
Abhik Roy
2024-09-30 22:03:37 +10:00
parent 5462240135
commit 967603b5aa
22 changed files with 1392 additions and 0 deletions
@@ -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})
@@ -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
@@ -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-----
@@ -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);
}
@@ -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