mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-23 00:00:06 +01:00
77 lines
3.2 KiB
Markdown
77 lines
3.2 KiB
Markdown
![]() |
# Asynchronous DNS
|
||
|
|
||
|
## Introduction
|
||
|
|
||
|
Lws now features optional asynchronous, ie, nonblocking DNS
|
||
|
resolution done on the event loop, enable `-DLWS_WITH_SYS_ASYNC_DNS=1`
|
||
|
at cmake to build it in.
|
||
|
|
||
|
## Description
|
||
|
|
||
|
The default libc name resolution is via libc `getaddrinfo()`, which is
|
||
|
blocking, possibly for quite long periods (seconds). If you are
|
||
|
taking care about latency, but want to create outgoing connections,
|
||
|
you can't tolerate this exception from the rule that everything in
|
||
|
lws is nonblocking.
|
||
|
|
||
|
Lws' asynchronous DNS resolver creates a caching name resolver
|
||
|
that directly queries the configured nameserver itself over UDP,
|
||
|
from the event loop.
|
||
|
|
||
|
It supports both ipv4 / A records and ipv6 / AAAA records (see later
|
||
|
for a description about how). One server supported over UDP :53,
|
||
|
and the nameserver is autodicovered on linux, windows, and freertos.
|
||
|
|
||
|
Other features
|
||
|
|
||
|
- lws-style paranoid response parsing
|
||
|
- random unique tid generation to increase difficulty of poisoning
|
||
|
- it's really integrated with the lws event loop, it does not spawn
|
||
|
threads or use the libc resolver, and of course no blocking at all
|
||
|
- platform-specific server address capturing (from /etc/resolv.conf
|
||
|
on linux, windows apis on windows)
|
||
|
- LRU caching
|
||
|
- piggybacking (multiple requests before the first completes go on
|
||
|
a list on the first request, not spawn multiple requests)
|
||
|
- observes TTL in cache
|
||
|
- TTL and timeout use `lws_sul` timers on the event loop
|
||
|
- ipv6 pieces only built if cmake `LWS_IPV6` enabled
|
||
|
|
||
|
## Api
|
||
|
|
||
|
If enabled at cmake, the async DNS implementation is used automatically
|
||
|
for lws client connections. It's also possible to call it directly, see
|
||
|
the api-test-async-dns example for how.
|
||
|
|
||
|
The Api follows that of `getaddrinfo()` but results are not created on
|
||
|
the heap. Instead a single, const cached copy of the addrinfo struct
|
||
|
chain is reference-counted, with `lws_async_dns_freeaddrinfo()` provided
|
||
|
to deduct from the reference count. Cached items with a nonzero
|
||
|
reference count can't be destroyed from the cache, so it's safe to keep
|
||
|
a pointer to the results and iterate through them.
|
||
|
|
||
|
## Dealing with IPv4 and IPv6
|
||
|
|
||
|
DNS is a very old standard that has some quirks... one of them is that
|
||
|
multiple queries are not supported in one packet, even though the protocol
|
||
|
suggests it is. This creates problems on ipv6 enabled systems, where
|
||
|
it may prefer to have AAAA results, but the server may only have A records.
|
||
|
|
||
|
To square the circle, for ipv4 only systems (`LWS_IPV6=0`) the resolver
|
||
|
requests only A records. For ipv6-capable systems, it always requests
|
||
|
first A and then immediately afterwards AAAA records.
|
||
|
|
||
|
To simplify the implementation, the tid b0 is used to differentiate
|
||
|
between A (b0 = 0) and AAAA (b0 = 1) requests and responses using the
|
||
|
same query body.
|
||
|
|
||
|
The first response to come back is parsed, and a cache entry made...
|
||
|
it leaves a note in the query about the address of the last `struct addrinfo`
|
||
|
record. When the second response comes, a second allocation is made,
|
||
|
but not added to the logical cache... instead it's chained on to the
|
||
|
first cache entry and the `struct addrinfo` linked-list from the
|
||
|
first cache entry is extended into the second one. At the time the
|
||
|
second result arrives, the query is destroyed and the cached results
|
||
|
provided on the result callback.
|
||
|
|