• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Asynchronous DNS
2
3## Introduction
4
5Lws now features optional asynchronous, ie, nonblocking recursive DNS
6resolution done on the event loop, enable `-DLWS_WITH_SYS_ASYNC_DNS=1`
7at cmake to build it in.
8
9## Description
10
11The default libc name resolution is via libc `getaddrinfo()`, which is
12blocking, possibly for quite long periods (seconds).  If you are
13taking care about latency, but want to create outgoing connections,
14you can't tolerate this exception from the rule that everything in
15lws is nonblocking.
16
17Lws' asynchronous DNS resolver creates a caching name resolver
18that directly queries the configured nameserver itself over UDP,
19from the event loop.
20
21It supports both ipv4 / A records and ipv6 / AAAA records (see later
22for a description about how).  One server supported over UDP :53,
23and the nameserver is autodicovered on linux, windows, and freertos.
24
25Other features
26
27 - lws-style paranoid response parsing
28 - random unique tid generation to increase difficulty of poisoning
29 - it's really integrated with the lws event loop, it does not spawn
30   threads or use the libc resolver, and of course no blocking at all
31 - platform-specific server address capturing (from /etc/resolv.conf
32   on linux, windows apis on windows)
33 - LRU caching
34 - piggybacking (multiple requests before the first completes go on
35    a list on the first request, not spawn multiple requests)
36 - observes TTL in cache
37 - TTL and timeout use `lws_sul` timers on the event loop
38 - Uses CNAME resolution inside the same response if present, otherwise
39   recurses to resolve the CNAME (up to 3 deep)
40 - ipv6 pieces only built if cmake `LWS_IPV6` enabled
41
42## Api
43
44If enabled at cmake, the async DNS implementation is used automatically
45for lws client connections.  It's also possible to call it directly, see
46the api-test-async-dns example for how.
47
48The Api follows that of `getaddrinfo()` but results are not created on
49the heap.  Instead a single, const cached copy of the addrinfo struct
50chain is reference-counted, with `lws_async_dns_freeaddrinfo()` provided
51to deduct from the reference count.  Cached items with a nonzero
52reference count can't be destroyed from the cache, so it's safe to keep
53a pointer to the results and iterate through them.
54
55## Dealing with IPv4 and IPv6
56
57DNS is a very old standard that has some quirks... one of them is that
58multiple queries are not supported in one packet, even though the protocol
59suggests it is.  This creates problems on ipv6 enabled systems, where
60it may prefer to have AAAA results, but the server may only have A records.
61
62To square the circle, for ipv4 only systems (`LWS_IPV6=0`) the resolver
63requests only A records.  For ipv6-capable systems, it always requests
64first A and then immediately afterwards AAAA records.
65
66To simplify the implementation, the tid b0 is used to differentiate
67between A (b0 = 0) and AAAA (b0 = 1) requests and responses using the
68same query body.
69
70The first response to come back is parsed, and a cache entry made...
71it leaves a note in the query about the address of the last `struct addrinfo`
72record.  When the second response comes, a second allocation is made,
73but not added to the logical cache... instead it's chained on to the
74first cache entry and the `struct addrinfo` linked-list from the
75first cache entry is extended into the second one.  At the time the
76second result arrives, the query is destroyed and the cached results
77provided on the result callback.
78
79## Recursion
80
81Where CNAMEs are returned, DNS servers may take two approaches... if the
82CNAME is also resolved by the same server and so it knows what it should
83resolve to, it may provide the CNAME resolution in the same response
84packet.
85
86In the case the CNAME is actually resolved by a different name server,
87the server with the CNAME does not have the information to hand to also
88resolve the CNAME in the same response.  So it just leaves it for the
89client to sort out.
90
91The lws implementation can deal with both of these, first it "recurses"
92(it does not recurse on the process stack but uses its own manual stack)
93to look for results in the same packet that told it about the CNAME.  If
94there are no results, it resets the query to look instead for the CNAME,
95and restarts it.  It allows this to happen for 3 CNAME deep.
96
97At the end, either way, the cached result is set using the original
98query name and the results from the last CNAME in the chain.
99
100
101