1.. SPDX-License-Identifier: GPL-3.0-or-later
2
3*********************
4Knot Resolver library
5*********************
6
7Requirements
8============
9
10* libknot_ 2.0 (Knot DNS high-performance DNS library.)
11
12For users
13=========
14
15The library as described provides basic services for name resolution, which should cover the usage,
16examples are in the :ref:`resolve API <lib_api_rplan>` documentation.
17
18.. tip:: If you're migrating from ``getaddrinfo()``, see *"synchronous"* API, but the library offers iterative API as well to plug it into your event loop for example.
19
20.. _lib-layers:
21
22For developers
23==============
24
25The resolution process starts with the functions in :ref:`resolve.c <lib_api_rplan>`, they are responsible for:
26
27* reacting to state machine state (i.e. calling consume layers if we have an answer ready)
28* interacting with the library user (i.e. asking caller for I/O, accepting queries)
29* fetching assets needed by layers (i.e. zone cut)
30
31This is the *driver*. The driver is not meant to know *"how"* the query resolves, but rather *"when"* to execute *"what"*.
32
33.. image:: ../doc/resolution.png
34   :align: center
35
36On the other side are *layers*. They are responsible for dissecting the packets and informing the driver about the results. For example, a *produce* layer generates query, a *consume* layer validates answer.
37
38.. tip:: Layers are executed asynchronously by the driver. If you need some asset beforehand, you can signalize the driver using returning state or current query flags. For example, setting a flag ``AWAIT_CUT`` forces driver to fetch zone cut information before the packet is consumed; setting a ``RESOLVED`` flag makes it pop a query after the current set of layers is finished; returning ``FAIL`` state makes it fail current query.
39
40Layers can also change course of resolution, for example by appending additional queries.
41
42.. code-block:: lua
43
44	consume = function (state, req, answer)
45		if answer:qtype() == kres.type.NS then
46			local qry = req:push(answer:qname(), kres.type.SOA, kres.class.IN)
47			qry.flags.AWAIT_CUT = true
48		end
49		return state
50	end
51
52This **doesn't** block currently processed query, and the newly created sub-request will start as soon as driver finishes processing current. In some cases you might need to issue sub-request and process it **before** continuing with the current, i.e. validator may need a DNSKEY before it can validate signatures. In this case, layers can yield and resume afterwards.
53
54.. code-block:: lua
55
56	consume = function (state, req, answer)
57		if state == kres.YIELD then
58			print('continuing yielded layer')
59			return kres.DONE
60		else
61			if answer:qtype() == kres.type.NS then
62				local qry = req:push(answer:qname(), kres.type.SOA, kres.class.IN)
63				qry.flags.AWAIT_CUT = true
64				print('planned SOA query, yielding')
65				return kres.YIELD
66			end
67			return state
68		end
69	end
70
71The ``YIELD`` state is a bit special. When a layer returns it, it interrupts current walk through the layers. When the layer receives it,
72it means that it yielded before and now it is resumed. This is useful in a situation where you need a sub-request to determine whether current answer is valid or not.
73
74Writing layers
75==============
76
77.. warning::  FIXME: this dev-docs section is outdated!  Better see comments in files instead, for now.
78
79The resolver :ref:`library <lib_index>` leverages the processing API from the libknot to separate packet processing code into layers.
80
81.. note:: This is only crash-course in the library internals, see the resolver :ref:`library <lib_index>` documentation for the complete overview of the services.
82
83The library offers following services:
84
85- :ref:`Cache <lib_api_cache>` - MVCC cache interface for retrieving/storing resource records.
86- :ref:`Resolution plan <lib_api_rplan>` - Query resolution plan, a list of partial queries (with hierarchy) sent in order to satisfy original query. This contains information about the queries, nameserver choice, timing information, answer and its class.
87- :ref:`Nameservers <lib_api_nameservers>` - Reputation database of nameservers, this serves as an aid for nameserver choice.
88
89A processing layer is going to be called by the query resolution driver for each query,
90so you're going to work with :ref:`struct kr_request <lib_api_rplan>` as your per-query context.
91This structure contains pointers to resolution context, resolution plan and also the final answer.
92
93.. code-block:: c
94
95	int consume(kr_layer_t *ctx, knot_pkt_t *pkt)
96	{
97		struct kr_request *req = ctx->req;
98		struct kr_query *qry = req->current_query;
99	}
100
101This is only passive processing of the incoming answer. If you want to change the course of resolution, say satisfy a query from a local cache before the library issues a query to the nameserver, you can use states (see the :ref:`Static hints <mod-hints>` for example).
102
103.. code-block:: c
104
105	int produce(kr_layer_t *ctx, knot_pkt_t *pkt)
106	{
107		struct kr_request *req = ctx->req;
108		struct kr_query *qry = req->current_query;
109
110		/* Query can be satisfied locally. */
111		if (can_satisfy(qry)) {
112			/* This flag makes the resolver move the query
113			 * to the "resolved" list. */
114			qry->flags.RESOLVED = true;
115			return KR_STATE_DONE;
116		}
117
118		/* Pass-through. */
119		return ctx->state;
120	}
121
122It is possible to not only act during the query resolution, but also to view the complete resolution plan afterwards. This is useful for analysis-type tasks, or *"per answer"* hooks.
123
124.. code-block:: c
125
126	int finish(kr_layer_t *ctx)
127	{
128		struct kr_request *req = ctx->req;
129		struct kr_rplan *rplan = req->rplan;
130
131		/* Print the query sequence with start time. */
132		char qname_str[KNOT_DNAME_MAXLEN];
133		struct kr_query *qry = NULL
134		WALK_LIST(qry, rplan->resolved) {
135			knot_dname_to_str(qname_str, qry->sname, sizeof(qname_str));
136			printf("%s at %u\n", qname_str, qry->timestamp);
137		}
138
139		return ctx->state;
140	}
141
142APIs in Lua
143===========
144
145The APIs in Lua world try to mirror the C APIs using LuaJIT FFI, with several differences and enhancements.
146There is not comprehensive guide on the API yet, but you can have a look at the bindings_ file.
147
148Elementary types and constants
149------------------------------
150
151* States are directly in ``kres`` table, e.g. ``kres.YIELD, kres.CONSUME, kres.PRODUCE, kres.DONE, kres.FAIL``.
152* DNS classes are in ``kres.class`` table, e.g. ``kres.class.IN`` for Internet class.
153* DNS types are in  ``kres.type`` table, e.g. ``kres.type.AAAA`` for AAAA type.
154* DNS rcodes types are in ``kres.rcode`` table, e.g. ``kres.rcode.NOERROR``.
155* Packet sections (QUESTION, ANSWER, AUTHORITY, ADDITIONAL) are in the ``kres.section`` table.
156
157Working with domain names
158-------------------------
159
160The internal API usually works with domain names in label format, you can convert between text and wire freely.
161
162.. code-block:: lua
163
164	local dname = kres.str2dname('business.se')
165	local strname = kres.dname2str(dname)
166
167Working with resource records
168-----------------------------
169
170Resource records are stored as tables.
171
172.. code-block:: lua
173
174	local rr = { owner = kres.str2dname('owner'),
175	             ttl = 0,
176	             class = kres.class.IN,
177	             type = kres.type.CNAME,
178	             rdata = kres.str2dname('someplace') }
179	print(kres.rr2str(rr))
180
181RRSets in packet can be accessed using FFI, you can easily fetch single records.
182
183.. code-block:: lua
184
185	local rrset = { ... }
186	local rr = rrset:get(0) -- Return first RR
187	print(kres.dname2str(rr:owner()))
188	print(rr:ttl())
189	print(kres.rr2str(rr))
190
191Working with packets
192--------------------
193
194Packet is the data structure that you're going to see in layers very often. They consists of a header, and four sections: QUESTION, ANSWER, AUTHORITY, ADDITIONAL. The first section is special, as it contains the query name, type, and class; the rest of the sections contain RRSets.
195
196First you need to convert it to a type known to FFI and check basic properties. Let's start with a snippet of a *consume* layer.
197
198.. code-block:: lua
199
200	consume = function (state, req, pkt)
201		print('rcode:', pkt:rcode())
202		print('query:', kres.dname2str(pkt:qname()), pkt:qclass(), pkt:qtype())
203		if pkt:rcode() ~= kres.rcode.NOERROR then
204			print('error response')
205		end
206	end
207
208You can enumerate records in the sections.
209
210.. code-block:: lua
211
212	local records = pkt:section(kres.section.ANSWER)
213	for i = 1, #records do
214		local rr = records[i]
215		if rr.type == kres.type.AAAA then
216			print(kres.rr2str(rr))
217		end
218	end
219
220During *produce* or *begin*, you might want to want to write to packet. Keep in mind that you have to write packet sections in sequence,
221e.g. you can't write to ANSWER after writing AUTHORITY, it's like stages where you can't go back.
222
223.. code-block:: lua
224
225		pkt:rcode(kres.rcode.NXDOMAIN)
226		-- Clear answer and write QUESTION
227		pkt:recycle()
228		pkt:question('\7blocked', kres.class.IN, kres.type.SOA)
229		-- Start writing data
230		pkt:begin(kres.section.ANSWER)
231		-- Nothing in answer
232		pkt:begin(kres.section.AUTHORITY)
233		local soa = { owner = '\7blocked', ttl = 900, class = kres.class.IN, type = kres.type.SOA, rdata = '...' }
234		pkt:put(soa.owner, soa.ttl, soa.class, soa.type, soa.rdata)
235
236Working with requests
237---------------------
238
239The request holds information about currently processed query, enabled options, cache, and other extra data.
240You primarily need to retrieve currently processed query.
241
242.. code-block:: lua
243
244	consume = function (state, req, pkt)
245		print(req.options)
246		print(req.state)
247
248		-- Print information about current query
249		local current = req:current()
250		print(kres.dname2str(current.owner))
251		print(current.stype, current.sclass, current.id, current.flags)
252	end
253
254In layers that either begin or finalize, you can walk the list of resolved queries.
255
256.. code-block:: lua
257
258	local last = req:resolved()
259	print(last.stype)
260
261As described in the layers, you can not only retrieve information about current query, but also push new ones or pop old ones.
262
263.. code-block:: lua
264
265		-- Push new query
266		local qry = req:push(pkt:qname(), kres.type.SOA, kres.class.IN)
267		qry.flags.AWAIT_CUT = true
268
269		-- Pop the query, this will erase it from resolution plan
270		req:pop(qry)
271
272
273.. _libknot:  https://gitlab.nic.cz/knot/knot-dns/tree/master/src/libknot
274.. _bindings: https://gitlab.nic.cz/knot/knot-resolver/blob/master/daemon/lua/kres.lua
275
276
277.. _significant-lua-changes:
278
279Significant Lua API changes
280---------------------------
281
282Incompatible changes since 3.0.0
283~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284
285In the main ``kres.*`` lua binding, there was only change in struct knot_rrset_t:
286
287- constructor now accepts TTL as additional parameter (defaulting to zero)
288- add_rdata() doesn't accept TTL anymore (and will throw an error if passed)
289
290In case you used knot_* functions and structures bound to lua:
291
292- knot_dname_is_sub(a, b): knot_dname_in_bailiwick(a, b) > 0
293- knot_rdata_rdlen(): knot_rdataset_at().len
294- knot_rdata_data(): knot_rdataset_at().data
295- knot_rdata_array_size(): offsetof(struct knot_data_t, data) + knot_rdataset_at().len
296- struct knot_rdataset: field names were renamed to .count and .rdata
297- some functions got inlined from headers, but you can use their kr_* clones:
298  kr_rrsig_sig_inception(), kr_rrsig_sig_expiration(), kr_rrsig_type_covered().
299  Note that these functions now accept knot_rdata_t* instead of a pair
300  knot_rdataset_t* and size_t - you can use knot_rdataset_at() for that.
301
302- knot_rrset_add_rdata() doesn't take TTL parameter anymore
303- knot_rrset_init_empty() was inlined, but in lua you can use the constructor
304- knot_rrset_ttl() was inlined, but in lua you can use :ttl() method instead
305
306- knot_pkt_qname(), _qtype(), _qclass(), _rr(), _section() were inlined,
307  but in lua you can use methods instead, e.g. myPacket:qname()
308- knot_pkt_free() takes knot_pkt_t* instead of knot_pkt_t**, but from lua
309  you probably didn't want to use that; constructor ensures garbage collection.
310
311
312.. |---| unicode:: U+02014 .. em dash
313