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