Skip to content

Commit 1e6379b

Browse files
committed
async_dns: DNSSEC
p
1 parent 0bf903a commit 1e6379b

20 files changed

Lines changed: 996 additions & 113 deletions

File tree

CMakeLists-implied-options.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,11 @@ if (LWS_WITH_AUTHORITATIVE_DNS)
426426
set(LWS_WITH_JOSE 1)
427427
endif()
428428

429+
if (LWS_WITH_SYS_ASYNC_DNS_DNSSEC)
430+
set(LWS_WITH_SYS_ASYNC_DNS 1)
431+
set(LWS_WITH_GENCRYPTO 1)
432+
endif()
433+
429434
# using any abstract protocol enables LWS_WITH_ABSTRACT
430435

431436
#if (LWS_WITH_SMTP)

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ option(LWS_WITH_ALEXA "Enable Alexa example" OFF)
176176
option(LWS_WITH_GTK "Enable gtk example" OFF)
177177
option(LWS_WITH_FTS "Full Text Search support" OFF)
178178
option(LWS_WITH_SYS_ASYNC_DNS "Nonblocking internal IPv4 + IPv6 DNS resolver" OFF)
179+
option(LWS_WITH_SYS_ASYNC_DNS_DNSSEC "Include DNSSEC parsing/validation in async-dns (requires crypto)" OFF)
179180
option(LWS_WITH_AUTHORITATIVE_DNS "Authoritative DNS zone signer" OFF)
180181
option(LWS_WITH_SYS_NTPCLIENT "Build in tiny ntpclient good for tls date validation and run via lws_system" OFF)
181182
option(LWS_WITH_SYS_DHCP_CLIENT "Build in tiny DHCP client" OFF)

READMEs/README.async-dns.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,17 @@ and restarts it. It allows this to happen for 3 CNAME deep.
9797
At the end, either way, the cached result is set using the original
9898
query name and the results from the last CNAME in the chain.
9999

100+
## DNSSEC Support
101+
102+
Async DNS supports DNSSEC validation of responses. This is optional and provides robust protection against DNS spoofing or injection of forged results.
103+
104+
To enable DNSSEC features, build with `-DLWS_WITH_SYS_ASYNC_DNS_DNSSEC=1` in CMake. This will automatically enable the `LWS_WITH_GENCRYPTO` required dependency.
105+
106+
Once enabled, clients can strictly enforce DNSSEC validation globally via the config struct or explicitly over context:
107+
```c
108+
lws_async_dns_dnssec_set_mode(context, LWS_ADNS_DNSSEC_REQUIRE);
109+
```
110+
111+
When validation is set to `LWS_ADNS_DNSSEC_REQUIRE`, queries failing to authenticate computationally with upstream Trust Anchors (or those lacking RRSIG/DNSKEY records entirely) will be explicitly rejected by the resolver and not propagate to callbacks or connections. But some domains inherently lack DNSSEC. For situations where strict DNSSEC is globally mandated, but a small handful of known-unsigned destinations must be reached, clients can explicitly set the `LWS_ADNS_INDICATE_LACKS_DNSSEC` bitflag natively on integer `qtype` lookups. This allows the resolver to tolerate missing records explicitly for that singular lookup, while strictly required globally.
112+
100113

cmake/lws_config.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
#cmakedefine LWS_WITH_ALLOC_METADATA_LWS
155155
#cmakedefine LWS_WITH_ALSA
156156
#cmakedefine LWS_WITH_SYS_ASYNC_DNS
157+
#cmakedefine LWS_WITH_SYS_ASYNC_DNS_DNSSEC
157158
#cmakedefine LWS_WITH_AUTHORITATIVE_DNS
158159
#cmakedefine LWS_WITH_BORINGSSL
159160
#cmakedefine LWS_WITH_AWSLC

include/libwebsockets/lws-async-dns.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ typedef enum dns_query_type {
2828
LWS_ADNS_RECORD_A = 0x01,
2929
LWS_ADNS_RECORD_CNAME = 0x05,
3030
LWS_ADNS_RECORD_MX = 0x0f,
31+
LWS_ADNS_RECORD_TXT = 0x10,
3132
LWS_ADNS_RECORD_AAAA = 0x1c,
33+
LWS_ADNS_RECORD_DS = 0x2b,
34+
LWS_ADNS_RECORD_RRSIG = 0x2e,
35+
LWS_ADNS_RECORD_NSEC = 0x2f,
36+
LWS_ADNS_RECORD_DNSKEY = 0x30,
37+
LWS_ADNS_RECORD_NSEC3 = 0x32,
3238
} adns_query_type_t;
3339

3440
typedef enum {
@@ -40,8 +46,18 @@ typedef enum {
4046
LADNS_RET_CONTINUING
4147
} lws_async_dns_retcode_t;
4248

49+
typedef enum {
50+
LWS_ADNS_DNSSEC_OFF = 0,
51+
LWS_ADNS_DNSSEC_TOLERATE,
52+
LWS_ADNS_DNSSEC_REQUIRE,
53+
} lws_async_dns_dnssec_mode_t;
54+
55+
#define LWS_ADNS_DNSSEC_VALID (1 << 8)
56+
#define LWS_ADNS_DNSSEC_INVALID (1 << 9)
57+
4358
#define LWS_ADNS_SYNTHETIC 0x10000 /* don't send, synthetic response will
4459
* be injected for testing */
60+
#define LWS_ADNS_INDICATE_LACKS_DNSSEC 0x20000 /* tolerate missing DNSSEC on this specific lookup */
4561

4662
struct addrinfo;
4763

@@ -127,4 +143,31 @@ lws_adns_get_async_dns(struct lws_adns_q *q);
127143
LWS_VISIBLE LWS_EXTERN void
128144
lws_adns_parse_udp(struct lws_async_dns *dns, const uint8_t *pkt, size_t len);
129145

146+
/**
147+
* lws_plat_asyncdns_get_server() - Get system DNS server address
148+
*
149+
* \param context: the lws_context
150+
* \param n: the zero-based index of the server to get
151+
* \param sa46: pointer to lws_sockaddr46 to receive the server address
152+
*
153+
* This platform-specific primitive allows retrieving the system's DNS
154+
* configuration. It returns 0 if the `n`th nameserver is written to `sa46`,
155+
* or < 0 if there is no `n`th nameserver available.
156+
*/
157+
LWS_VISIBLE LWS_EXTERN int
158+
lws_plat_asyncdns_get_server(struct lws_context *context, int n,
159+
lws_sockaddr46 *sa46);
160+
161+
162+
/**
163+
* lws_async_dns_dnssec_set_mode() - Set the system-wide DNSSEC mode
164+
*
165+
* \param context: the lws_context
166+
* \param mode: the requested DNSSEC mode (off, tolerate, or require)
167+
*
168+
* Configures how the asynchronous DNS handles DNSSEC validation.
169+
*/
170+
LWS_VISIBLE LWS_EXTERN void
171+
lws_async_dns_dnssec_set_mode(struct lws_context *context, lws_async_dns_dnssec_mode_t mode);
172+
130173
#endif

include/libwebsockets/lws-context-vhost.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,7 @@ struct lws_context_creation_info {
965965
* server to forcibly add. If given, the list of strings must be
966966
* terminated with a NULL.
967967
*/
968+
968969
#endif
969970

970971
#if defined(WIN32)

include/libwebsockets/lws-system.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,14 @@ typedef struct lws_system_ops {
232232
uint32_t wake_latency_us;
233233
/**< time taken for this device to wake from suspend, in us
234234
*/
235+
236+
#if defined(LWS_WITH_SYS_ASYNC_DNS)
237+
uint8_t async_dns_dnssec_mode;
238+
/**< 0: OFF, 1: REQUIRE, 2: TOLERATE */
239+
240+
const char *async_dns_dnssec_trust_anchor;
241+
/**< base64 DS record string serving as the root trust anchor */
242+
#endif
235243
} lws_system_ops_t;
236244

237245
#if defined(LWS_WITH_SYS_STATE)

lib/core-net/private-lib-core-net.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ typedef struct lws_async_dns {
266266
lws_dll2_owner_t cached;
267267

268268
struct lws_context *cx;
269+
270+
uint8_t dnssec_mode; /* lws_async_dns_dnssec_mode_t */
269271
} lws_async_dns_t;
270272

271273
#define lws_async_dns_from_server(_s) ((lws_async_dns_t *)_s->list.owner)

lib/core/context.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,9 @@ lws_create_context(const struct lws_context_creation_info *info)
14101410
context->count_caps = info->count_caps;
14111411
#endif
14121412

1413+
#if defined(LWS_WITH_SYS_ASYNC_DNS)
1414+
#endif
1415+
14131416

14141417
#if defined(LWS_WITH_NETWORK)
14151418

lib/plat/freertos/freertos-resolv.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,38 @@
2626
#include "private-lib-async-dns.h"
2727

2828
#if defined(LWS_WITH_SYS_ASYNC_DNS)
29-
lws_async_dns_server_check_t
30-
lws_plat_asyncdns_init(struct lws_context *context, lws_async_dns_t *dns)
29+
int
30+
lws_plat_asyncdns_get_server(struct lws_context *context, int index,
31+
lws_sockaddr46 *sa46)
3132
{
32-
lws_sockaddr46 sa46t;
3333
uint32_t ipv4;
34-
lws_async_dns_server_check_t s = LADNS_CONF_SERVER_SAME;
35-
lws_async_dns_server_t *dsrv;
34+
35+
if (index > 0)
36+
return -1;
3637

3738
FreeRTOS_GetAddressConfiguration(NULL, NULL, NULL, &ipv4);
3839

39-
memset(&sa46t, 0, sizeof(sa46t));
40+
memset(sa46, 0, sizeof(*sa46));
41+
sa46->sa4.sin_family = AF_INET;
42+
sa46->sa4.sin_addr.s_addr = ipv4;
43+
44+
return 0;
45+
}
4046

41-
sa46t.sa4.sin_family = AF_INET;
42-
sa46t.sa4.sin_addr.s_addr = ipv4;
47+
lws_async_dns_server_check_t
48+
lws_plat_asyncdns_init(struct lws_context *context, lws_async_dns_t *dns)
49+
{
50+
lws_sockaddr46 sa46t;
51+
lws_async_dns_server_check_t s = LADNS_CONF_SERVER_SAME;
52+
lws_async_dns_server_t *dsrv;
53+
int n = 0;
4354

44-
dsrv = __lws_async_dns_server_find(dns, &sa46t);
45-
if (!dsrv) {
46-
__lws_async_dns_server_add(dns, &sa46t);
47-
s = LADNS_CONF_SERVER_CHANGED;
55+
while (lws_plat_asyncdns_get_server(context, n++, &sa46t) == 0) {
56+
dsrv = __lws_async_dns_server_find(dns, &sa46t);
57+
if (!dsrv) {
58+
__lws_async_dns_server_add(dns, &sa46t);
59+
s = LADNS_CONF_SERVER_CHANGED;
60+
}
4861
}
4962

5063
return s;

0 commit comments

Comments
 (0)