diff --git a/daemon/bindings.c b/daemon/bindings.c index b009814f84bf9b91c9344da498309d410643cbba..dc677b3e4421786ebf9b11e17d4973043f568c40 100644 --- a/daemon/bindings.c +++ b/daemon/bindings.c @@ -19,6 +19,23 @@ #include "lib/cache.h" #include "daemon/bindings.h" +/** @internal Prefix error with file:line */ +static int format_error(lua_State* L, const char *err) +{ + lua_Debug d; + lua_getstack(L, 1, &d); + /* error message prefix */ + lua_getinfo(L, "Sln", &d); + lua_pushstring(L, d.short_src); + lua_pushstring(L, ":"); + lua_pushnumber(L, d.currentline); + lua_pushstring(L, ": error: "); + /* error message */ + lua_pushstring(L, err); + lua_concat(L, 5); + return 1; +} + /** @internal Compatibility wrapper for Lua 5.0 - 5.2 */ #if LUA_VERSION_NUM >= 502 #define register_lib(L, name, lib) \ @@ -49,7 +66,7 @@ static int mod_load(lua_State *L) /* Check parameters */ int n = lua_gettop(L); if (n != 1 || !lua_isstring(L, 1)) { - lua_pushstring(L, "expected module name"); + lua_pushstring(L, "expected load(string name)"); lua_error(L); } /* Load engine module */ @@ -70,7 +87,7 @@ static int mod_unload(lua_State *L) /* Check parameters */ int n = lua_gettop(L); if (n != 1 || !lua_isstring(L, 1)) { - lua_pushstring(L, "expected module name"); + format_error(L, "expected unload(string name)"); lua_error(L); } /* Unload engine module */ @@ -168,7 +185,7 @@ static int net_listen(lua_State *L) if (lua_istable(L, 1)) { return net_listen_iface(L, port); } else if (n < 1 || !lua_isstring(L, 1)) { - lua_pushstring(L, "expected (string addr, int port = 53)"); + format_error(L, "expected listen(string addr, int port = 53)"); lua_error(L); } @@ -190,7 +207,7 @@ static int net_close(lua_State *L) /* Check parameters */ int n = lua_gettop(L); if (n < 2) { - lua_pushstring(L, "expected (string addr, int port)"); + format_error(L, "expected close(string addr, int port)"); lua_error(L); } @@ -291,8 +308,8 @@ static int cache_open(lua_State *L) { /* Check parameters */ int n = lua_gettop(L); - if (n < 1) { - lua_pushstring(L, "expected (number max_size)"); + if (n < 1 || !lua_isnumber(L, 1)) { + format_error(L, "expected open(number max_size)"); lua_error(L); } @@ -305,7 +322,7 @@ static int cache_open(lua_State *L) /* Open resolution context cache */ engine->resolver.cache = kr_cache_open(".", engine->pool, lua_tointeger(L, 1)); if (engine->resolver.cache == NULL) { - lua_pushstring(L, "can't open cache in rundir"); + format_error(L, "can't open cache in rundir"); lua_error(L); } diff --git a/doc/lib.rst b/doc/lib.rst index c89712bd4548a74d7015d6154d9eb93b5ae7b7ba..5aec08f256609657313a6619d68e7e6fa1fe09fd 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -14,10 +14,9 @@ API reference Name resolution --------------- -.. doxygengroup:: resolution +.. doxygenfile:: resolve.h :project: libkresolve - -.. doxygengroup:: rplan +.. doxygenfile:: rplan.h :project: libkresolve .. _lib_api_cache: @@ -25,7 +24,7 @@ Name resolution Cache ----- -.. doxygengroup:: cache +.. doxygenfile:: cache.h :project: libkresolve .. _lib_api_nameservers: @@ -33,7 +32,9 @@ Cache Nameservers ----------- -.. doxygengroup:: nameservers +.. doxygenfile:: nsrep.h + :project: libkresolve +.. doxygenfile:: zonecut.h :project: libkresolve .. _lib_api_modules: @@ -41,13 +42,15 @@ Nameservers Modules ------- -.. doxygengroup:: modules +.. doxygenfile:: module.h :project: libkresolve Utilities --------- -.. doxygengroup:: utils +.. doxygenfile:: utils.h + :project: libkresolve +.. doxygenfile:: defines.h :project: libkresolve .. _lib_generics: diff --git a/lib/README.rst b/lib/README.rst index 4b8bf299355b02ff450afdf0c12d1ffcbcc892bc..1e3fe89d28a58ba633f9be5bd561f808abffaadb 100644 --- a/lib/README.rst +++ b/lib/README.rst @@ -44,15 +44,15 @@ The library offers following services: - :ref:`Nameservers <lib_api_nameservers>` - Reputation database of nameservers, this serves as an aid for nameserver choice. A processing layer is going to be called by the query resolution driver for each query, -so you're going to work with :ref:`struct kr_layer_param <lib_api_rplan>` as your per-query context. This structure contains pointers to +so you're going to work with :ref:`struct kr_request <lib_api_rplan>` as your per-query context. This structure contains pointers to resolution context, resolution plan and also the final answer. You're likely to retrieve currently solved query from the query plan: .. code-block:: c int consume(knot_layer_t *ctx, knot_pkt_t *pkt) { - struct kr_layer_param *param = ctx->data; - struct kr_query *query = kr_rplan_current(param->rplan); + struct kr_request *request = ctx->data; + struct kr_query *query = kr_rplan_current(request->rplan); } This 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). @@ -61,8 +61,8 @@ This is only passive processing of the incoming answer. If you want to change th int produce(knot_layer_t *ctx, knot_pkt_t *pkt) { - struct kr_layer_param *param = ctx->data; - struct kr_query *cur = kr_rplan_current(param->rplan); + struct kr_request *request = ctx->data; + struct kr_query *cur = kr_rplan_current(request->rplan); /* Query can be satisfied locally. */ if (can_satisfy(cur)) { @@ -83,8 +83,8 @@ This is useful for analysis-type tasks, or *"on-resolution"* hooks. int finish(knot_layer_t *ctx) { - struct kr_layer_param *param = ctx->data; - struct kr_rplan *rplan = param->rplan; + struct kr_request *request = ctx->data; + struct kr_rplan *rplan = request->rplan; /* Print the query sequence with start time. */ char qname_str[KNOT_DNAME_MAXLEN]; diff --git a/lib/cache.h b/lib/cache.h index 371a48f2e71ca6d20039d556081c18c87c137af1..ba658d78cd8efcca115ee4502d0e0a54aaefeea8 100644 --- a/lib/cache.h +++ b/lib/cache.h @@ -14,9 +14,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup cache - * @{ - */ #pragma once #include <libknot/rrset.h> @@ -117,5 +114,3 @@ int kr_cache_remove(namedb_txn_t *txn, const knot_rrset_t *rr); * @return KNOT_E* */ int kr_cache_clear(namedb_txn_t *txn); - -/** @} */ diff --git a/lib/defines.h b/lib/defines.h index cde6352f242839215de03c12e39de9949f2584ed..141f219557df72af09e1b7b5f38297ec84595925 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -14,10 +14,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup utils - * @{ - */ - #pragma once #include <errno.h> @@ -36,7 +32,8 @@ * Connection limits. * @cond internal */ -#define KR_CONN_RTT_MAX 5000 +#define KR_CONN_RTT_MAX 5000 /* Timeout for network activity */ +#define ITER_LIMIT 50 /* Built-in iterator limit */ /* * Timers. @@ -50,5 +47,3 @@ #define KR_EDNS_VERSION 0 #define KR_EDNS_PAYLOAD 4096 /* @endcond */ - -/** @} */ diff --git a/lib/generic/array.h b/lib/generic/array.h index eb844fff94851b4dd0f41b137bc7c82313c65bb3..5655396f15a19110790b1e0ec6102acdd98790bd 100644 --- a/lib/generic/array.h +++ b/lib/generic/array.h @@ -23,14 +23,16 @@ * Be aware of that, as direct usage of the macros in the evaluating macros * may lead to different expectations: * - * # Undefined behaviour + * @code{.c} * MIN(array_push(arr, val), other) + * @endcode * * May evaluate the code twice, leading to unexpected behaviour. * This is a price to pay for the absence of proper generics. * - * Example usage: + * # Example usage: * + * @code{.c} * array_t(const char*) arr; * array_init(arr); * @@ -55,7 +57,7 @@ * * // Random delete * array_del(arr, 0); - * + * @endcode * \addtogroup generics * @{ */ diff --git a/lib/generic/map.h b/lib/generic/map.h index 7b04af51095de2b5d9675ba492ce6fae45b29dc3..7d40f9b528bb4f19718fc0a19c14a5d491fa3027 100644 --- a/lib/generic/map.h +++ b/lib/generic/map.h @@ -9,8 +9,9 @@ * * @warning If the user provides a custom allocator, it must return addresses aligned to 2B boundary. * - * Example usage: + * # Example usage: * + * @code{.c} * map_t map = map_make(); * * // Custom allocator (optional) @@ -43,6 +44,7 @@ * * // Clear the map * map_clear(&map); + * @endcode * * \addtogroup generics * @{ diff --git a/lib/generic/pack.h b/lib/generic/pack.h index bd3a8a774444b53016c11cf24a813e1beb1e442b..1fcdc616ce74d4d6f21f8b09ec8e2fe9b7958e4d 100644 --- a/lib/generic/pack.h +++ b/lib/generic/pack.h @@ -24,8 +24,9 @@ * * @note Maximum object size is 2^16 bytes, see ::pack_objlen_t * - * Example usage: + * # Example usage: * + * @code{.c} * pack_t pack; * pack_init(pack); * @@ -47,6 +48,7 @@ * pack_obj_del(pack, U8("jedi"), 4); * * pack_clear(pack); + * @endcode * * \addtogroup generics * @{ diff --git a/lib/generic/set.h b/lib/generic/set.h index 26a3d36a08056494fdf8f3553159738ac692e9f8..be5be1602a7b54656014208fb37a63c4bc17d45a 100644 --- a/lib/generic/set.h +++ b/lib/generic/set.h @@ -20,8 +20,9 @@ * * @note The API is based on map.h, see it for more examples. * - * Example usage: + * # Example usage: * + * @code{.c} * set_t set = set_make(); * * // Insert keys @@ -50,6 +51,7 @@ * * // Clear the set * set_clear(&set); + * @endcode * * \addtogroup generics * @{ diff --git a/lib/layer.h b/lib/layer.h index 2189581a8fe12748bcc6e7726dd538a7c9ebfda1..0f7f314abf072181ee0888a16a8105fcf3aa98de 100644 --- a/lib/layer.h +++ b/lib/layer.h @@ -16,28 +16,8 @@ #pragma once -/** \addtogroup rplan - * @{ - */ - -#include <libknot/processing/layer.h> -#include <libknot/packet/pkt.h> - #include "lib/defines.h" - -struct kr_context; -struct kr_rplan; - -/** - * Processing module parameters. - * - * @note These parameters are passed to each processing layer. - */ -struct kr_layer_param { - struct kr_context *ctx; - struct kr_rplan *rplan; - knot_pkt_t *answer; -}; +#include "lib/resolve.h" #ifndef NDEBUG /** @internal Print a debug message related to resolution. */ @@ -48,6 +28,4 @@ struct kr_layer_param { } while (0) #else #define QRDEBUG(query, cls, fmt, ...) -#endif - -/** @} */ +#endif \ No newline at end of file diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c index 3deac9c7507073e1ebd31ef49a257ce517d021f1..f7673cce31fe47cbdf2cf6a4da5434acc56d74d9 100644 --- a/lib/layer/iterate.c +++ b/lib/layer/iterate.c @@ -28,7 +28,7 @@ #include "lib/nsrep.h" #include "lib/module.h" -#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), "iter", fmt) +#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(&req->rplan), "iter", fmt) /* Packet classification. */ enum { @@ -39,7 +39,7 @@ enum { }; /* Iterator often walks through packet section, this is an abstraction. */ -typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_layer_param *); +typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_request *); /** Return minimized QNAME/QTYPE for current zone cut. */ static const knot_dname_t *minimized_qname(struct kr_query *query, uint16_t *qtype) @@ -137,20 +137,20 @@ static int update_nsaddr(const knot_rrset_t *rr, struct kr_query *query, uint16_ return KNOT_STATE_CONSUME; } -static int update_glue(const knot_rrset_t *rr, unsigned hint, struct kr_layer_param *param) +static int update_glue(const knot_rrset_t *rr, unsigned hint, struct kr_request *req) { - return update_nsaddr(rr, kr_rplan_current(param->rplan), hint); + return update_nsaddr(rr, kr_rplan_current(&req->rplan), hint); } -int rr_update_parent(const knot_rrset_t *rr, unsigned hint, struct kr_layer_param *param) +int rr_update_parent(const knot_rrset_t *rr, unsigned hint, struct kr_request *req) { - struct kr_query *query = kr_rplan_current(param->rplan); - return update_nsaddr(rr, query->parent, hint); + struct kr_query *qry = kr_rplan_current(&req->rplan); + return update_nsaddr(rr, qry->parent, hint); } -int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_layer_param *param) +int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_request *req) { - knot_pkt_t *answer = param->answer; + knot_pkt_t *answer = req->answer; /* Write copied RR to the result packet. */ int ret = knot_pkt_put(answer, KNOT_COMPR_HINT_NONE, rr, hint); @@ -166,23 +166,23 @@ int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_layer_para } /** Attempt to find glue for given nameserver name (best effort). */ -static int fetch_glue(knot_pkt_t *pkt, const knot_dname_t *ns, struct kr_layer_param *param) +static int fetch_glue(knot_pkt_t *pkt, const knot_dname_t *ns, struct kr_request *req) { int result = 0; const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL); for (unsigned i = 0; i < ar->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(ar, i); if (knot_dname_is_equal(ns, rr->owner)) { - (void) update_glue(rr, 0, param); + (void) update_glue(rr, 0, req); result += 1; } } return result; } -static int update_cut(knot_pkt_t *pkt, const knot_rrset_t *rr, struct kr_layer_param *param) +static int update_cut(knot_pkt_t *pkt, const knot_rrset_t *rr, struct kr_request *req) { - struct kr_query *query = kr_rplan_current(param->rplan); + struct kr_query *query = kr_rplan_current(&req->rplan); struct kr_zonecut *cut = &query->zone_cut; int state = KNOT_STATE_CONSUME; @@ -203,7 +203,7 @@ static int update_cut(knot_pkt_t *pkt, const knot_rrset_t *rr, struct kr_layer_p kr_zonecut_add(cut, knot_ns_name(&rr->rrs, 0), NULL); for (unsigned i = 0; i < rr->rrs.rr_count; ++i) { const knot_dname_t *ns_name = knot_ns_name(&rr->rrs, i); - int glue_records = fetch_glue(pkt, ns_name, param); + int glue_records = fetch_glue(pkt, ns_name, req); /* Glue is mandatory for NS below zone */ if (knot_dname_in(ns_name, rr->owner) ) { if (glue_records == 0) { @@ -216,7 +216,7 @@ static int update_cut(knot_pkt_t *pkt, const knot_rrset_t *rr, struct kr_layer_p return state; } -static int process_authority(knot_pkt_t *pkt, struct kr_layer_param *param) +static int process_authority(knot_pkt_t *pkt, struct kr_request *req) { int result = KNOT_STATE_CONSUME; const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY); @@ -230,7 +230,7 @@ static int process_authority(knot_pkt_t *pkt, struct kr_layer_param *param) for (unsigned i = 0; i < ns->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(ns, i); if (rr->type == KNOT_RRTYPE_NS) { - int state = update_cut(pkt, rr, param); + int state = update_cut(pkt, rr, req); switch(state) { case KNOT_STATE_DONE: result = state; break; case KNOT_STATE_FAIL: return state; break; @@ -244,10 +244,10 @@ static int process_authority(knot_pkt_t *pkt, struct kr_layer_param *param) return result; } -static void finalize_answer(knot_pkt_t *pkt, struct kr_layer_param *param) +static void finalize_answer(knot_pkt_t *pkt, struct kr_request *req) { /* Finalize header */ - knot_pkt_t *answer = param->answer; + knot_pkt_t *answer = req->answer; knot_wire_set_rcode(answer->wire, knot_wire_get_rcode(pkt->wire)); /* Fill in SOA if negative response */ @@ -258,16 +258,16 @@ static void finalize_answer(knot_pkt_t *pkt, struct kr_layer_param *param) for (unsigned i = 0; i < ns->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(ns, i); if (rr->type == KNOT_RRTYPE_SOA) { - rr_update_answer(rr, 0, param); + rr_update_answer(rr, 0, req); break; } } } } -static int process_answer(knot_pkt_t *pkt, struct kr_layer_param *param) +static int process_answer(knot_pkt_t *pkt, struct kr_request *req) { - struct kr_query *query = kr_rplan_current(param->rplan); + struct kr_query *query = kr_rplan_current(&req->rplan); /* Response for minimized QNAME. * NODATA => may be empty non-terminal, retry (found zone cut) @@ -292,7 +292,7 @@ static int process_answer(knot_pkt_t *pkt, struct kr_layer_param *param) const knot_dname_t *cname = query->sname; for (unsigned i = 0; i < an->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(an, i); - int state = is_final ? rr_update_answer(rr, 0, param) : rr_update_parent(rr, 0, param); + int state = is_final ? rr_update_answer(rr, 0, req) : rr_update_parent(rr, 0, req); if (state == KNOT_STATE_FAIL) { return state; } @@ -301,10 +301,10 @@ static int process_answer(knot_pkt_t *pkt, struct kr_layer_param *param) /* Follow canonical name as next SNAME. */ if (cname != query->sname) { - (void) kr_rplan_push(param->rplan, query->parent, cname, query->sclass, query->stype); + (void) kr_rplan_push(&req->rplan, query->parent, cname, query->sclass, query->stype); } else { if (query->parent == NULL) { - finalize_answer(pkt, param); + finalize_answer(pkt, req); } } @@ -314,7 +314,7 @@ static int process_answer(knot_pkt_t *pkt, struct kr_layer_param *param) } /** Error handling, RFC1034 5.3.3, 4d. */ -static int resolve_error(knot_pkt_t *pkt, struct kr_layer_param *param) +static int resolve_error(knot_pkt_t *pkt, struct kr_request *req) { return KNOT_STATE_FAIL; } @@ -333,9 +333,10 @@ static int begin(knot_layer_t *ctx, void *module_param) static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt) { assert(pkt && ctx); - struct kr_layer_param *param = ctx->data; - struct kr_query *query = kr_rplan_current(param->rplan); + struct kr_request *req = ctx->data; + struct kr_query *query = kr_rplan_current(&req->rplan); if (query == NULL || ctx->state == KNOT_STATE_DONE) { + assert(0); return ctx->state; } @@ -367,15 +368,6 @@ static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt) return KNOT_STATE_FAIL; } -#ifndef NDEBUG - char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN]; - knot_dname_to_str(qname_str, qname, sizeof(qname_str)); - struct sockaddr *addr = &query->ns.addr.ip; - inet_ntop(addr->sa_family, kr_nsrep_inaddr(query->ns.addr), ns_str, sizeof(ns_str)); - knot_dname_to_str(zonecut_str, query->zone_cut.name, sizeof(zonecut_str)); - DEBUG_MSG("=> querying: '%s' zone cut: '%s' m12n: '%s'\n", ns_str, zonecut_str, qname_str); -#endif - /* Query built, expect answer. */ return KNOT_STATE_CONSUME; } @@ -387,8 +379,8 @@ static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt) static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt) { assert(pkt && ctx); - struct kr_layer_param *param = ctx->data; - struct kr_query *query = kr_rplan_current(param->rplan); + struct kr_request *req = ctx->data; + struct kr_query *query = kr_rplan_current(&req->rplan); if (query == NULL || (query->flags & QUERY_RESOLVED)) { return ctx->state; } @@ -396,20 +388,19 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt) /* Check for packet processing errors first. */ if (pkt->parsed < pkt->size) { DEBUG_MSG("<= malformed response\n"); - return resolve_error(pkt, param); + return resolve_error(pkt, req); } else if (!is_paired_to_query(pkt, query)) { DEBUG_MSG("<= ignoring mismatching response\n"); return KNOT_STATE_CONSUME; } else if (knot_wire_get_tc(pkt->wire)) { DEBUG_MSG("<= truncated response, failover to TCP\n"); - struct kr_query *cur = kr_rplan_current(param->rplan); - if (cur) { + if (query) { /* Fail if already on TCP. */ - if (cur->flags & QUERY_TCP) { + if (query->flags & QUERY_TCP) { DEBUG_MSG("<= TC=1 with TCP, bailing out\n"); - return resolve_error(pkt, param); + return resolve_error(pkt, req); } - cur->flags |= QUERY_TCP; + query->flags |= QUERY_TCP; } return KNOT_STATE_DONE; } @@ -424,16 +415,16 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt) break; /* OK */ default: DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??"); - return resolve_error(pkt, param); + return resolve_error(pkt, req); } /* Resolve authority to see if it's referral or authoritative. */ int state = KNOT_STATE_CONSUME; - state = process_authority(pkt, param); + state = process_authority(pkt, req); switch(state) { case KNOT_STATE_CONSUME: /* Not referral, process answer. */ DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??"); - state = process_answer(pkt, param); + state = process_answer(pkt, req); break; case KNOT_STATE_DONE: /* Referral */ DEBUG_MSG("<= referral response, follow\n"); diff --git a/lib/layer/iterate.h b/lib/layer/iterate.h index 3716e791161bc554a92b3bdf7fd38fff50fe0beb..db7de8bc1df5da3341042352de2ca569ff6092ef 100644 --- a/lib/layer/iterate.h +++ b/lib/layer/iterate.h @@ -26,13 +26,13 @@ extern const knot_layer_api_t *iterate_layer(void); * Result updates the query parent. * @note Hint is an index of chosen RR in the set. */ -int rr_update_parent(const knot_rrset_t *rr, unsigned hint, struct kr_layer_param *param); +int rr_update_parent(const knot_rrset_t *rr, unsigned hint, struct kr_request *param); /** * Result updates the original query response. * @note When \a hint is KNOT_PF_FREE, RR is treated as a copy and answer takes its ownership. */ -int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_layer_param *param); +int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_request *param); /* Processing module implementation. */ const knot_layer_api_t *iterate_layer(void); \ No newline at end of file diff --git a/lib/layer/itercache.c b/lib/layer/itercache.c index 3abd8180302d53183ac5c5d2c89503531d8eeb28..30ef1e06f585886474f04bd1f88e72d8a353c3ad 100644 --- a/lib/layer/itercache.c +++ b/lib/layer/itercache.c @@ -24,11 +24,11 @@ #include "lib/cache.h" #include "lib/module.h" -#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), " cc ", fmt) +#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), " cc ", fmt) -typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_layer_param *); +typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_request *); -static int update_parent(const knot_rrset_t *rr, unsigned drift, struct kr_layer_param *param) +static int update_parent(const knot_rrset_t *rr, unsigned drift, struct kr_request *req) { /* Find a first non-expired record. */ uint16_t i = 0; @@ -39,12 +39,12 @@ static int update_parent(const knot_rrset_t *rr, unsigned drift, struct kr_layer } } - return rr_update_parent(rr, i, param); + return rr_update_parent(rr, i, req); } -static int update_answer(const knot_rrset_t *rr, unsigned drift, struct kr_layer_param *param) +static int update_answer(const knot_rrset_t *rr, unsigned drift, struct kr_request *req) { - knot_pkt_t *answer = param->answer; + knot_pkt_t *answer = req->answer; /* Materialize RR set */ knot_rrset_t rr_copy = kr_cache_materialize(rr, drift, &answer->mm); @@ -52,18 +52,18 @@ static int update_answer(const knot_rrset_t *rr, unsigned drift, struct kr_layer return KNOT_STATE_FAIL; } - return rr_update_answer(&rr_copy, 0, param); + return rr_update_answer(&rr_copy, 0, req); } static int read_cache_rr(namedb_txn_t *txn, knot_rrset_t *cache_rr, uint32_t timestamp, - rr_callback_t cb, struct kr_layer_param *param) + rr_callback_t cb, struct kr_request *req) { /* Query cache for requested record */ if (kr_cache_peek(txn, cache_rr, ×tamp) != KNOT_EOK) { return KNOT_STATE_NOOP; } - return cb(cache_rr, timestamp, param); + return cb(cache_rr, timestamp, req); } static int begin(knot_layer_t *ctx, void *module_param) @@ -75,13 +75,14 @@ static int begin(knot_layer_t *ctx, void *module_param) static int read_cache(knot_layer_t *ctx, knot_pkt_t *pkt) { assert(pkt && ctx); - struct kr_layer_param *param = ctx->data; - struct kr_query *cur = kr_rplan_current(param->rplan); + struct kr_request *req = ctx->data; + struct kr_rplan *rplan = &req->rplan; + struct kr_query *cur = kr_rplan_current(rplan); if (cur == NULL) { return ctx->state; } - namedb_txn_t *txn = kr_rplan_txn_acquire(param->rplan, NAMEDB_RDONLY); + namedb_txn_t *txn = kr_rplan_txn_acquire(rplan, NAMEDB_RDONLY); uint32_t timestamp = cur->timestamp.tv_sec; knot_rrset_t cache_rr; knot_rrset_init(&cache_rr, cur->sname, cur->stype, cur->sclass); @@ -93,7 +94,7 @@ static int read_cache(knot_layer_t *ctx, knot_pkt_t *pkt) } /* Try to find expected record first. */ - int state = read_cache_rr(txn, &cache_rr, timestamp, callback, param); + int state = read_cache_rr(txn, &cache_rr, timestamp, callback, req); if (state == KNOT_STATE_DONE) { DEBUG_MSG("=> satisfied from cache\n"); cur->flags |= QUERY_RESOLVED; @@ -102,11 +103,11 @@ static int read_cache(knot_layer_t *ctx, knot_pkt_t *pkt) /* Check if CNAME chain exists. */ cache_rr.type = KNOT_RRTYPE_CNAME; - state = read_cache_rr(txn, &cache_rr, timestamp, callback, param); + state = read_cache_rr(txn, &cache_rr, timestamp, callback, req); if (state != KNOT_STATE_NOOP) { if (cur->stype != KNOT_RRTYPE_CNAME) { const knot_dname_t *cname = knot_cname_name(&cache_rr.rrs); - if (kr_rplan_push(param->rplan, cur->parent, cname, cur->sclass, cur->stype) == NULL) { + if (kr_rplan_push(rplan, cur->parent, cname, cur->sclass, cur->stype) == NULL) { return KNOT_STATE_FAIL; } } @@ -229,8 +230,9 @@ static int write_cache_authority(knot_pkt_t *pkt, namedb_txn_t *txn, mm_ctx_t *p static int write_cache(knot_layer_t *ctx, knot_pkt_t *pkt) { - struct kr_layer_param *param = ctx->data; - struct kr_query *query = kr_rplan_current(param->rplan); + struct kr_request *req = ctx->data; + struct kr_rplan *rplan = &req->rplan; + struct kr_query *query = kr_rplan_current(rplan); /* Don't cache anything if failed. */ if (query == NULL || ctx->state == KNOT_STATE_FAIL) { @@ -238,9 +240,9 @@ static int write_cache(knot_layer_t *ctx, knot_pkt_t *pkt) } /* Open write transaction */ - mm_ctx_t *pool = param->rplan->pool; + mm_ctx_t *pool = rplan->pool; uint32_t timestamp = query->timestamp.tv_sec; - namedb_txn_t *txn = kr_rplan_txn_acquire(param->rplan, 0); + namedb_txn_t *txn = kr_rplan_txn_acquire(rplan, 0); if (txn == NULL) { return ctx->state; /* Couldn't acquire cache, ignore. */ } diff --git a/lib/module.h b/lib/module.h index bca4d3c626bab755a22bff2eef1d1106deda1afc..2b8c9c7eff5d97e6f02e20895db4b0a258491fc6 100644 --- a/lib/module.h +++ b/lib/module.h @@ -14,10 +14,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup modules - * @{ - */ - #pragma once #include <libknot/processing/layer.h> @@ -92,5 +88,3 @@ void kr_module_unload(struct kr_module *module); */ #define KR_MODULE_EXPORT(module) \ uint32_t module ## _api() { return KR_MODULE_API; } - -/** @} */ diff --git a/lib/nsrep.c b/lib/nsrep.c index 02f9bf9af5aa45b4dc58125662d93a6819a18552..9b8fb37ed3534196d8baa8d009ebfd97d7656d21 100644 --- a/lib/nsrep.c +++ b/lib/nsrep.c @@ -14,7 +14,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <assert.h> + #include "lib/nsrep.h" +#include "lib/defines.h" #include "lib/generic/pack.h" /** @internal Macro to set address structure. */ diff --git a/lib/nsrep.h b/lib/nsrep.h index e61f063f2897545e13c5b834e315685b03fcc8a4..13cdef59ecb1fd9bdd051c2c1bcf21e542ab6bbf 100644 --- a/lib/nsrep.h +++ b/lib/nsrep.h @@ -14,31 +14,36 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup nameservers - * @{ - */ - #pragma once #include <netinet/in.h> +#include <libknot/dname.h> #include "lib/generic/map.h" -#include "lib/layer.h" +/** + * Special values for nameserver score. + * All positive values mean valid nameserver. + */ enum kr_ns_score { KR_NS_INVALID = 0, KR_NS_VALID = 1 }; +/** + * Name server representation. + * Contains extra information about the name server, e.g. score + * or other metadata. + */ struct kr_nsrep { - unsigned score; - const knot_dname_t *name; + unsigned score; /**< Server score */ + const knot_dname_t *name; /**< Server name */ union { struct sockaddr ip; struct sockaddr_in ip4; struct sockaddr_in6 ip6; - } addr; + } addr; /**< Server address */ }; /** @internal Address bytes for given family. */ @@ -52,8 +57,6 @@ struct kr_nsrep * Elect best nameserver/address pair from the nsset. * @param ns updated NS representation * @param nsset NS set to choose from - * @return 0 if success (ns is updated), error otherwise + * @return score, see enum kr_ns_score */ int kr_nsrep_elect(struct kr_nsrep *ns, map_t *nsset); - -/** @} */ diff --git a/lib/resolve.c b/lib/resolve.c index 014a5a180edef6a54f74d0d4289f114788bb633c..85650c2753a68514267692c612b057da35b74914 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -15,11 +15,13 @@ */ #include <stdio.h> +#include <sys/fcntl.h> #include <libknot/internal/mempool.h> #include <libknot/processing/requestor.h> #include <libknot/rrtype/rdname.h> #include <libknot/descriptor.h> +#include <libknot/internal/net.h> #include <dnssec/random.h> #include "lib/rplan.h" @@ -27,10 +29,7 @@ #include "lib/layer/itercache.h" #include "lib/layer/iterate.h" -#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), "resl", fmt) - -/* Defines */ -#define ITER_LIMIT 50 +#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), "resl", fmt) /** Invalidate current NS/addr pair. */ static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry) @@ -42,160 +41,314 @@ static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry) return kr_zonecut_del(&qry->zone_cut, qry->ns.name, rdata); } -static int ns_resolve_addr(struct kr_query *cur, struct kr_layer_param *param) +static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param) { - if (kr_rplan_satisfies(cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A) || - kr_rplan_satisfies(cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA) || - cur->flags & QUERY_AWAIT_ADDR) { + struct kr_rplan *rplan = ¶m->rplan; + if (kr_rplan_satisfies(qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A) || + kr_rplan_satisfies(qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA) || + qry->flags & QUERY_AWAIT_ADDR) { DEBUG_MSG("=> dependency loop, bailing out\n"); - kr_rplan_pop(param->rplan, cur); - return KNOT_EOK; + kr_rplan_pop(rplan, qry); + return KNOT_STATE_PRODUCE; + } + + (void) kr_rplan_push(rplan, qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA); + (void) kr_rplan_push(rplan, qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A); + qry->flags |= QUERY_AWAIT_ADDR; + return KNOT_STATE_PRODUCE; +} + +static void prepare_layers(struct kr_request *param) +{ + struct kr_context *ctx = param->ctx; + for (size_t i = 0; i < ctx->modules->len; ++i) { + struct kr_module *mod = &ctx->modules->at[i]; + if (mod->layer) { + knot_overlay_add(¶m->overlay, mod->layer(), param); + } + } +} + +static int connected(struct sockaddr *addr, int proto, struct timeval *timeout) +{ + unsigned flags = (proto == SOCK_STREAM) ? O_NONBLOCK : 0; + int fd = net_connected_socket(proto, (struct sockaddr_storage *)addr, NULL, flags); + if (fd < 0) { + return kr_error(ECONNREFUSED); } - (void) kr_rplan_push(param->rplan, cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA); - (void) kr_rplan_push(param->rplan, cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A); - cur->flags |= QUERY_AWAIT_ADDR; - return KNOT_EOK; + /* Workaround for timeout, as we have no control over + * connect() time limit in blocking mode. */ + if (proto == SOCK_STREAM) { + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + int ret = select(fd + 1, NULL, &set, NULL, timeout); + if (ret == 0) { + close(fd); + return kr_error(ETIMEDOUT); + } + if (ret < 0) { + close(fd); + return kr_error(ECONNREFUSED); + } + fcntl(fd, F_SETFL, 0); + } + + return fd; } -static int iterate(struct knot_requestor *requestor, struct kr_layer_param *param) +static int sendrecv(struct sockaddr *addr, int proto, const knot_pkt_t *query, knot_pkt_t *resp) { - int ret = KNOT_EOK; struct timeval timeout = { KR_CONN_RTT_MAX / 1000, 0 }; - struct kr_rplan *rplan = param->rplan; - struct kr_query *cur = kr_rplan_current(rplan); + auto_close int fd = connected(addr, proto, &timeout); + if (fd < 0) { + return fd; + } -#ifndef NDEBUG - char name_str[KNOT_DNAME_MAXLEN], type_str[16]; - knot_dname_to_str(name_str, cur->sname, sizeof(name_str)); - knot_rrtype_to_string(cur->stype, type_str, sizeof(type_str)); - DEBUG_MSG("query '%s %s'\n", name_str, type_str); -#endif + /* Send packet */ + int ret = 0; + if (proto == SOCK_STREAM) { + ret = tcp_send_msg(fd, query->wire, query->size, &timeout); + } else { + ret = udp_send_msg(fd, query->wire, query->size, NULL); + } + if (ret != query->size) { + return kr_error(EIO); + } - /* Elect best nameserver candidate. */ - kr_nsrep_elect(&cur->ns, &cur->zone_cut.nsset); - if (cur->ns.score < KR_NS_VALID) { - DEBUG_MSG("=> no valid NS left\n"); - kr_rplan_pop(param->rplan, cur); - return KNOT_EOK; + /* Receive it */ + if (proto == SOCK_STREAM) { + ret = tcp_recv_msg(fd, resp->wire, resp->max_size, &timeout); } else { - if (cur->ns.addr.ip.sa_family == AF_UNSPEC) { - DEBUG_MSG("=> ns missing A/AAAA, fetching\n"); - return ns_resolve_addr(cur, param); - } + ret = udp_recv_msg(fd, resp->wire, resp->max_size, &timeout); + } + if (ret <= 0) { + return kr_error(ETIMEDOUT); } - /* Prepare query resolution. */ - int mode = (cur->flags & QUERY_TCP) ? 0 : KNOT_RQ_UDP; - knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MIN_PKTSIZE, requestor->mm); - struct knot_request *tx = knot_request_make(requestor->mm, &cur->ns.addr.ip, NULL, query, mode); - knot_requestor_enqueue(requestor, tx); + /* Parse and return */ + resp->size = ret; + if (knot_pkt_parse(resp, 0) != 0) { + return kr_error(EBADMSG); + } - /* Resolve and check status. */ - ret = knot_requestor_exec(requestor, &timeout); - if (ret != KNOT_EOK) { - /* Network error, retry over TCP. */ - if (ret != KNOT_LAYER_ERROR && !(cur->flags & QUERY_TCP)) { - DEBUG_MSG("=> ns unreachable, retrying over TCP\n"); - cur->flags |= QUERY_TCP; - return iterate(requestor, param); + return kr_ok(); +} + +int kr_resolve(struct kr_context* ctx, knot_pkt_t *answer, + const knot_dname_t *qname, uint16_t qclass, uint16_t qtype) +{ + if (ctx == NULL || answer == NULL || qname == NULL) { + return kr_error(EINVAL); + } + + /* Create memory pool */ + mm_ctx_t pool; + mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE); + knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MIN_PKTSIZE, &pool); + knot_pkt_t *resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &pool); + if (!query || !resp) { + mp_delete(pool.ctx); + return kr_error(ENOMEM); + } + + /* Initialize context. */ + struct kr_request request; + request.pool = pool; + kr_resolve_begin(&request, ctx, answer); +#ifndef NDEBUG + struct kr_rplan *rplan = &request.rplan; /* for DEBUG_MSG */ +#endif + + /* Resolve query, iteratively */ + int proto = 0; + struct sockaddr *addr = NULL; + unsigned iter_count = 0; + int state = kr_resolve_query(&request, qname, qclass, qtype); + while (state == KNOT_STATE_PRODUCE) { + /* Hardlimit on iterative queries */ + if (++iter_count > ITER_LIMIT) { + DEBUG_MSG("iteration limit %d reached\n", ITER_LIMIT); + state = KNOT_STATE_FAIL; + break; } - /* Resolution failed, invalidate current NS and reset to UDP. */ - DEBUG_MSG("=> resolution failed: '%s', invalidating\n", knot_strerror(ret)); - if (invalidate_ns(rplan, cur) == 0) { - cur->flags &= ~QUERY_TCP; + /* Produce next query or finish */ + state = kr_resolve_produce(&request, &addr, &proto, query); + while (state == KNOT_STATE_CONSUME) { + /* Get answer from nameserver and consume it */ + int ret = sendrecv(addr, proto, query, resp); + if (ret != 0) { + DEBUG_MSG("sendrecv: %s\n", kr_strerror(ret)); + resp->size = 0; + } + state = kr_resolve_consume(&request, resp); + knot_pkt_clear(resp); } - return KNOT_EOK; + knot_pkt_clear(query); } - /* Pop query if resolved. */ - if (cur->flags & QUERY_RESOLVED) { - kr_rplan_pop(rplan, cur); - } + /* Cleanup */ + kr_resolve_finish(&request, state); + mp_delete(pool.ctx); + return state == KNOT_STATE_DONE ? 0 : kr_error(EIO); +} + + +int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pkt_t *answer) +{ + /* Initialize request */ + kr_rplan_init(&request->rplan, ctx, &request->pool); + knot_overlay_init(&request->overlay, &request->pool); + request->ctx = ctx; + request->answer = answer; + prepare_layers(request); - return ret; + /* Expect first query */ + return KNOT_STATE_CONSUME; } -static void prepare_layers(struct knot_requestor *req, struct kr_layer_param *param) +int kr_resolve_query(struct kr_request *request, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype) { - struct kr_context *ctx = param->ctx; - for (size_t i = 0; i < ctx->modules->len; ++i) { - struct kr_module *mod = &ctx->modules->at[i]; - if (mod->layer) { - knot_requestor_overlay(req, mod->layer(), param); - } + struct kr_rplan *rplan = &request->rplan; + struct kr_query *qry = kr_rplan_push(rplan, NULL, qname, qclass, qtype); + if (!qry) { + return KNOT_STATE_FAIL; } + + /* Create answer packet */ + knot_pkt_t *answer = request->answer; + knot_wire_set_qr(answer->wire); + knot_wire_clear_aa(answer->wire); + knot_wire_set_ra(answer->wire); + knot_wire_set_rcode(answer->wire, KNOT_RCODE_NOERROR); + + /* Expect answer */ + return KNOT_STATE_PRODUCE; } -static int resolve_iterative(struct kr_layer_param *param, mm_ctx_t *pool) +int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet) { -/* Initialize requestor. */ - struct knot_requestor requestor; - knot_requestor_init(&requestor, pool); - prepare_layers(&requestor, param); + struct kr_rplan *rplan = &request->rplan; + struct kr_query *qry = kr_rplan_current(rplan); - /* Iteratively solve the query. */ - int ret = KNOT_EOK; - unsigned iter_count = 0; - while((ret == KNOT_EOK) && !kr_rplan_empty(param->rplan)) { - ret = iterate(&requestor, param); - if (++iter_count > ITER_LIMIT) { - DEBUG_MSG("iteration limit %d reached\n", ITER_LIMIT); - ret = KNOT_ELIMIT; + /* Empty resolution plan, push packet as the new query */ + if (kr_rplan_empty(&request->rplan)) { + const knot_dname_t *qname = knot_pkt_qname(packet); + uint16_t qclass = knot_pkt_qclass(packet); + uint16_t qtype = knot_pkt_qtype(packet); + return kr_resolve_query(request, qname, qclass, qtype); + } + + /* Different processing for network error */ + int state = KNOT_STATE_FAIL; + if (!packet || packet->size == 0) { + /* Network error, retry over TCP. */ + if (!(qry->flags & QUERY_TCP)) { + /** @todo This should just penalize UDP and elect next best. */ + DEBUG_MSG("=> ns unreachable, retrying over TCP\n"); + qry->flags |= QUERY_TCP; + return KNOT_STATE_CONSUME; /* Try again */ } + } else { + state = knot_overlay_consume(&request->overlay, packet); } - /* Set RCODE on internal failure. */ - if (ret != KNOT_EOK) { - if (knot_wire_get_rcode(param->answer->wire) == KNOT_RCODE_NOERROR) { - knot_wire_set_rcode(param->answer->wire, KNOT_RCODE_SERVFAIL); + /* Resolution failed, invalidate current NS and reset to UDP. */ + if (state == KNOT_STATE_FAIL) { + DEBUG_MSG("=> resolution failed, invalidating\n"); + if (invalidate_ns(rplan, qry) == 0) { + qry->flags &= ~QUERY_TCP; } } - DEBUG_MSG("finished: %s, mempool: %zu B\n", knot_strerror(ret), (size_t) mp_total_size(pool->ctx)); - knot_requestor_clear(&requestor); - return ret; + /* Pop query if resolved. */ + if (qry->flags & QUERY_RESOLVED) { + kr_rplan_pop(rplan, qry); + + } + + knot_overlay_reset(&request->overlay); + return kr_rplan_empty(&request->rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE; } -int kr_resolve(struct kr_context* ctx, knot_pkt_t *answer, - const knot_dname_t *qname, uint16_t qclass, uint16_t qtype) +int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet) { - if (ctx == NULL || answer == NULL || qname == NULL) { - return KNOT_EINVAL; + struct kr_rplan *rplan = &request->rplan; + struct kr_query *qry = kr_rplan_current(rplan); + + /* No query left for resolution */ + if (kr_rplan_empty(rplan)) { + return KNOT_STATE_FAIL; } - /* Initialize context. */ - int ret = KNOT_EOK; - mm_ctx_t rplan_pool; - mm_ctx_mempool(&rplan_pool, MM_DEFAULT_BLKSIZE); - struct kr_rplan rplan; - kr_rplan_init(&rplan, ctx, &rplan_pool); - struct kr_layer_param param; - param.ctx = ctx; - param.rplan = &rplan; - param.answer = answer; - - /* Push query to resolution plan. */ - struct kr_query *qry = kr_rplan_push(&rplan, NULL, qname, qclass, qtype); - if (qry != NULL) { - ret = resolve_iterative(¶m, &rplan_pool); +#ifndef NDEBUG + char name_str[KNOT_DNAME_MAXLEN], type_str[16]; + knot_dname_to_str(name_str, qry->sname, sizeof(name_str)); + knot_rrtype_to_string(qry->stype, type_str, sizeof(type_str)); + DEBUG_MSG("query '%s %s'\n", type_str, name_str); +#endif + + /* Resolve current query and produce dependent or finish */ + int state = knot_overlay_produce(&request->overlay, packet); + switch(state) { + case KNOT_STATE_FAIL: return state; break; + case KNOT_STATE_CONSUME: break; + default: /* Current query is done */ + knot_overlay_reset(&request->overlay); + kr_rplan_pop(rplan, qry); + return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE; + } + + /* Elect best nameserver candidate */ + kr_nsrep_elect(&qry->ns, &qry->zone_cut.nsset); + if (qry->ns.score < KR_NS_VALID) { + DEBUG_MSG("=> no valid NS left\n"); + knot_overlay_reset(&request->overlay); + kr_rplan_pop(rplan, qry); + return KNOT_STATE_PRODUCE; } else { - ret = KNOT_ENOMEM; + if (qry->ns.addr.ip.sa_family == AF_UNSPEC) { + DEBUG_MSG("=> ns missing A/AAAA, fetching\n"); + knot_overlay_reset(&request->overlay); + return ns_resolve_addr(qry, request); + } } - /* Check flags. */ - knot_wire_set_qr(answer->wire); - knot_wire_clear_aa(answer->wire); - knot_wire_set_ra(answer->wire); +#ifndef NDEBUG + char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN]; + knot_dname_to_str(qname_str, knot_pkt_qname(packet), sizeof(qname_str)); + struct sockaddr *addr = &qry->ns.addr.ip; + inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr), ns_str, sizeof(ns_str)); + knot_dname_to_str(zonecut_str, qry->zone_cut.name, sizeof(zonecut_str)); + DEBUG_MSG("=> querying: '%s' zone cut: '%s' m12n: '%s'\n", ns_str, zonecut_str, qname_str); +#endif + + /* Issue dependent query to this address */ + *dst = &qry->ns.addr.ip; + *type = (qry->flags & QUERY_TCP) ? SOCK_STREAM : SOCK_DGRAM; + return state; +} + +int kr_resolve_finish(struct kr_request *request, int state) +{ + struct kr_rplan *rplan = &request->rplan; + DEBUG_MSG("finished: %d, mempool: %zu B\n", state, (size_t) mp_total_size(request->pool.ctx)); /* Resolution success, commit cache transaction. */ - if (ret == KNOT_EOK) { - kr_rplan_txn_commit(&rplan); + if (state == KNOT_STATE_DONE) { + kr_rplan_txn_commit(rplan); + } else { + /* Error during procesing, internal failure */ + knot_pkt_t *answer = request->answer; + if (knot_wire_get_rcode(answer->wire) == KNOT_RCODE_NOERROR) { + knot_wire_set_rcode(answer->wire, KNOT_RCODE_SERVFAIL); + } } /* Clean up. */ - kr_rplan_deinit(&rplan); - mp_delete(rplan_pool.ctx); - - return ret; + knot_overlay_reset(&request->overlay); + knot_overlay_deinit(&request->overlay); + kr_rplan_deinit(&request->rplan); + return KNOT_STATE_DONE; } diff --git a/lib/resolve.h b/lib/resolve.h index 7319c246ebd25ec67ff4872b0b5be7f48dc5ac99..ec8db797a4864a6316c70d4ddea1639ba89d0a63 100644 --- a/lib/resolve.h +++ b/lib/resolve.h @@ -14,19 +14,78 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup resolution - * @{ - */ - #pragma once +#include <netinet/in.h> +#include <libknot/processing/overlay.h> #include <libknot/packet/pkt.h> #include "lib/generic/array.h" +#include "lib/rplan.h" #include "lib/module.h" -/** Array of modules. */ +/** + * @file resolve.h + * @brief The API provides a high-level API for simple name resolution, + * and an API providing a "consumer-producer"-like interface to enable + * you write custom I/O or special iterative resolution driver. + * + * # Example usage of the high-level API: + * + * @code{.c} + * + * struct kr_context ctx = { + * .pool = NULL, // for persistent data + * .cache = ..., // open cache instance (or NULL) + * .layers = {} // loaded layers + * }; + * + * // Push basic layers + * array_push(ctx.layers, iterate_layer); + * array_push(ctx.layers, itercache_layer); + * + * // Resolve "IN A cz." + * knot_pkt_t *answer = knot_pkt_new(NULL, 65535, ctx.pool); + * int ret = kr_resolve(&ctx, answer, (uint8_t*)"\x02cz", 1, 1); + * printf("rcode: %d, ancount: %u\n", + * knot_wire_get_rcode(answer->wire), + * knot_wire_get_ancount(answer->wire)); + * @endcode + * + * # Example usage of the iterative API: + * + * @code{.c} + * + * // Create request and its memory pool + * struct kr_request req; + * mm_ctx_mempool(&req.pool, 4096); + * kr_resolve_begin(&req, ctx, answer); + * int state = kr_resolve_query(&req, qname, qclass, qtype); + * + * // Generate answer + * while (state == KNOT_STATE_PRODUCE) { + * + * // Additional query generate, do the I/O and pass back answer + * state = kr_resolve_produce(&req, &addr, &type, query); + * while (state == KNOT_STATE_CONSUME) { + * int ret = sendrecv(addr, proto, query, resp); + * + * // If I/O fails, make "resp" empty + * state = kr_resolve_consume(&request, resp); + * knot_pkt_clear(resp); + * } + * knot_pkt_clear(query); + * } + * + * // "state" is either DONE or FAIL + * kr_resolve_finish(&request, state); + * + * @endcode + */ + +/* @cond internal Array of modules. */ typedef array_t(struct kr_module) module_array_t; +/* @endcond */ /** * Name resolution context. @@ -44,17 +103,96 @@ struct kr_context uint32_t options; }; +/** + * Name resolution request. + * + * Keeps information about current query processing between calls to + * processing APIs, i.e. current resolved query, resolution plan, ... + * Use this instead of the simple interface if you want to implement + * multiplexing or custom I/O. + * + * @note All data for this request must be allocated from the given pool. + */ +struct kr_request { + struct kr_context *ctx; + struct kr_rplan rplan; + struct knot_overlay overlay; + knot_pkt_t *answer; + mm_ctx_t pool; +}; + /** * Resolve an input query and produce a packet with an answer. + * * @note The function doesn't change the packet question or message ID. - * @param ctx resolution context + * + * @param ctx resolution context * @param answer answer packet to be written - * @param qname resolved query name + * @param qname resolved query name * @param qclass resolved query class - * @param qtype resolved query type - * @return KNOT_E* + * @param qtype resolved query type + * @return 0 or an error code */ int kr_resolve(struct kr_context* ctx, knot_pkt_t *answer, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype); -/** @} */ +/** + * Begin name resolution. + * + * @note Expects a request to have an initialized mempool, the "answer" packet will + * be kept during the resolution and will contain the final answer at the end. + * + * @param request request state with initialized mempool + * @param ctx resolution context + * @param answer allocated packet for final answer + * @return CONSUME (expecting query) + */ +int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pkt_t *answer); + +/** + * Push new query for resolution to the state. + * @param request request state (if already has a question, this will be resolved first) + * @param qname + * @param qclass + * @param qtype + * @return PRODUCE|FAIL + */ +int kr_resolve_query(struct kr_request *request, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype); + +/** + * Consume input packet (may be either first query or answer to query originated from kr_resolve_produce()) + * + * @note If the I/O fails, provide an empty or NULL packet, this will make iterator recognize nameserver failure. + * + * @param request request state (awaiting input) + * @param packet [in] input packet + * @return any state + */ +int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet); + +/** + * Produce either next additional query or finish. + * + * If the CONSUME is returned then dst, type and packet will be filled with + * appropriate values and caller is responsible to send them and receive answer. + * If it returns any other state, then content of the variables is undefined. + * + * @param request request state (in PRODUCE state) + * @param dst [out] possible address of the next nameserver + * @param type [out] possible used socket type (SOCK_STREAM, SOCK_DGRAM) + * @param packet [out] packet to be filled with additional query + * @return any state + */ +int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet); + +/** + * Finish resolution and commit results if the state is DONE. + * + * @note The structures will be deinitialized, but the assigned memory pool is not going to + * be destroyed, as it's owned by caller. + * + * @param request request state + * @param state either DONE or FAIL state + * @return DONE + */ +int kr_resolve_finish(struct kr_request *request, int state); diff --git a/lib/rplan.h b/lib/rplan.h index 534280d3c2204faaaa0aefa7b6b2e87ccdd5f5bd..07aa951fa1d74eca53268afc2643898e984dbd49 100644 --- a/lib/rplan.h +++ b/lib/rplan.h @@ -14,14 +14,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup rplan - * @{ - */ - #pragma once #include <sys/time.h> - #include <libknot/dname.h> #include <libknot/internal/lists.h> #include <libknot/internal/namedb/namedb.h> @@ -142,5 +137,3 @@ struct kr_query *kr_rplan_current(struct kr_rplan *rplan); * Return true if resolution chain satisfies given query. */ bool kr_rplan_satisfies(struct kr_query *closure, const knot_dname_t *name, uint16_t cls, uint16_t type); - -/** @} */ diff --git a/lib/utils.h b/lib/utils.h index 24f8c4af2ce0c4751655dcf321dc0e17e00a9292..219b662a770bf352909b7cdbd6eba94073409ab4 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -14,10 +14,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup utils - * @{ - */ - #pragma once #include <stdio.h> @@ -40,5 +36,3 @@ extern void _cleanup_fclose(FILE **p); /** Concatenate N strings. */ char* kr_strcatdup(unsigned n, ...); - -/** @} */ diff --git a/lib/zonecut.h b/lib/zonecut.h index ed2cf3765a515d3bc55669702ef6698e5db0f7e3..fd708c1b0577fa6a577a0df4233945751c12b3ac 100644 --- a/lib/zonecut.h +++ b/lib/zonecut.h @@ -14,15 +14,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \addtogroup rplan - * @{ - */ - #pragma once -#include "lib/cache.h" #include "lib/generic/map.h" #include "lib/generic/pack.h" +#include "lib/cache.h" struct kr_rplan; @@ -31,8 +27,8 @@ struct kr_rplan; */ struct kr_zonecut { knot_dname_t *name; /**< Zone cut name. */ - mm_ctx_t *pool; /**< Memory pool. */ - map_t nsset; /**< Map of nameserver => address_set. */ + mm_ctx_t *pool; /**< Memory pool. */ + map_t nsset; /**< Map of nameserver => address_set. */ }; /** @@ -109,5 +105,3 @@ int kr_zonecut_set_sbelt(struct kr_zonecut *cut); * @return 0 or error code */ int kr_zonecut_find_cached(struct kr_zonecut *cut, namedb_txn_t *txn, uint32_t timestamp); - -/** @} */ diff --git a/modules/README.rst b/modules/README.rst index bd753439097fa792c02ae45487fc49b2617a58ac..66072265488ea2717d359a4466d8256bdd9fb5a5 100644 --- a/modules/README.rst +++ b/modules/README.rst @@ -164,7 +164,7 @@ Now we can add the implementations for the ``Begin`` and ``Finish`` functions, a func Finish(ctx *C.knot_layer_t) C.int { // Since the context is unsafe.Pointer, we need to cast it - var param *C.struct_kr_layer_param = (*C.struct_kr_layer_param)(ctx.data) + var param *C.struct_kr_request = (*C.struct_kr_request)(ctx.data) // Now we can use the C API as well fmt.Printf("[go] resolved %d queries", C.list_size(¶m.rplan.resolved)) return 0 diff --git a/modules/gostats/gostats.go b/modules/gostats/gostats.go index 6a4d89b219f01600c3373afbaddafff5d52e1cf9..0c3b4ee61f0c0375889430cd79f7e2c9e87e4a6c 100644 --- a/modules/gostats/gostats.go +++ b/modules/gostats/gostats.go @@ -36,7 +36,7 @@ func Begin(ctx *C.knot_layer_t, param unsafe.Pointer) C.int { } func Finish(ctx *C.knot_layer_t) C.int { - var param *C.struct_kr_layer_param = (*C.struct_kr_layer_param)(ctx.data) + var param *C.struct_kr_request = (*C.struct_kr_request)(ctx.data) fmt.Printf("[gostats] resolved %d queries", C.list_size(¶m.rplan.resolved)) return 0 } diff --git a/modules/hints/hints.c b/modules/hints/hints.c index 2fa3ef0da55ca0ea2926947417097525208a83da..a35356c128ffcdbe4fce50a5c58348c1bb94cce7 100644 --- a/modules/hints/hints.c +++ b/modules/hints/hints.c @@ -34,8 +34,8 @@ /* Defaults */ #define DEFAULT_FILE "/etc/hosts" -#define DEBUG_MSG(fmt...) QRDEBUG(NULL, "hint", fmt) -typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_layer_param *); +#define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "hint", fmt) +typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_request *); /** @todo Hack until layers can store userdata. */ static struct kr_zonecut *g_map = NULL; @@ -46,9 +46,11 @@ static int begin(knot_layer_t *ctx, void *module_param) return ctx->state; } -static int answer_query(pack_t *addr_set, struct kr_layer_param *param) +static int answer_query(pack_t *addr_set, struct kr_request *param) { - struct kr_query *qry = kr_rplan_current(param->rplan); + struct kr_query *qry = kr_rplan_current(¶m->rplan); + assert(qry); + knot_rrset_t rr; knot_rrset_init(&rr, qry->sname, qry->stype, KNOT_CLASS_IN); int family_len = sizeof(struct in_addr); @@ -75,7 +77,7 @@ static int answer_query(pack_t *addr_set, struct kr_layer_param *param) callback(&rr, 0, param); /* Finalize */ - DEBUG_MSG("<= answered from hints\n"); + DEBUG_MSG(qry, "<= answered from hints\n"); knot_rdataset_clear(&rr.rrs, NULL); qry->flags |= QUERY_RESOLVED; return KNOT_STATE_DONE; @@ -84,8 +86,8 @@ static int answer_query(pack_t *addr_set, struct kr_layer_param *param) static int query(knot_layer_t *ctx, knot_pkt_t *pkt) { assert(pkt && ctx); - struct kr_layer_param *param = ctx->data; - struct kr_query *qry = kr_rplan_current(param->rplan); + struct kr_request *param = ctx->data; + struct kr_query *qry = kr_rplan_current(¶m->rplan); if (qry->stype != KNOT_RRTYPE_A && qry->stype != KNOT_RRTYPE_AAAA) { return ctx->state; } @@ -149,7 +151,7 @@ static int load_map(struct kr_zonecut *hints, FILE *fp) } } - DEBUG_MSG("loaded %zu hints\n", count); + DEBUG_MSG(NULL, "loaded %zu hints\n", count); return kr_ok(); } @@ -157,10 +159,10 @@ static int load(struct kr_module *module, const char *path) { auto_fclose FILE *fp = fopen(path, "r"); if (fp == NULL) { - DEBUG_MSG("reading '%s' failed: %s\n", path, strerror(errno)); + DEBUG_MSG(NULL, "reading '%s' failed: %s\n", path, strerror(errno)); return kr_error(errno); } else { - DEBUG_MSG("reading '%s'\n", path); + DEBUG_MSG(NULL, "reading '%s'\n", path); } /* Create pool and copy itself */ diff --git a/tests/test_array.c b/tests/test_array.c index 5151a545fa76473daf87480fa8e8be7913c0885a..8003700abf179eda13b3aee0036d8d5b51bcc504 100644 --- a/tests/test_array.c +++ b/tests/test_array.c @@ -46,6 +46,12 @@ static void test_array(void **state) assert_true(ret == 0); } + /* Overfill. */ + for (unsigned i = 0; i < 4096; ++i) { + ret = array_push(arr, i); + assert_true(ret >= 0); + } + array_clear(arr); } diff --git a/tests/test_cache.c b/tests/test_cache.c index 86e79267521b3fdc1c4d3528f6698563d0060dbf..aaab55a4f2fad974b17cfb56db3382bf9e4f807e 100644 --- a/tests/test_cache.c +++ b/tests/test_cache.c @@ -26,7 +26,8 @@ knot_rrset_t global_rr; const char *global_env; #define CACHE_SIZE 10 * 4096 -#define CACHE_TTL 2 +#define CACHE_TTL 10 +#define CACHE_TIME 0 /* Test invalid parameters. */ static void test_invalid(void **state) @@ -82,7 +83,7 @@ static void test_insert(void **state) test_random_rr(&global_rr, CACHE_TTL); namedb_txn_t *txn = test_txn_write(state); - int ret = kr_cache_insert(txn, &global_rr, 0); + int ret = kr_cache_insert(txn, &global_rr, CACHE_TIME); if (ret == KNOT_EOK) { ret = kr_cache_txn_commit(txn); } else { @@ -101,8 +102,9 @@ static void test_query(void **state) namedb_txn_t *txn = test_txn_rdonly(state); - for (uint32_t timestamp = 0; timestamp < CACHE_TTL; ++timestamp) { - int query_ret = kr_cache_peek(txn, &cache_rr, ×tamp); + for (uint32_t timestamp = CACHE_TIME; timestamp < CACHE_TIME + CACHE_TTL; ++timestamp) { + uint32_t drift = timestamp; + int query_ret = kr_cache_peek(txn, &cache_rr, &drift); bool rr_equal = knot_rrset_equal(&global_rr, &cache_rr, KNOT_RRSET_COMPARE_WHOLE); assert_int_equal(query_ret, KNOT_EOK); assert_true(rr_equal); @@ -114,7 +116,7 @@ static void test_query(void **state) /* Test cache read (simulate aged entry) */ static void test_query_aged(void **state) { - uint32_t timestamp = CACHE_TTL; + uint32_t timestamp = CACHE_TIME + CACHE_TTL; knot_rrset_t cache_rr; knot_rrset_init(&cache_rr, global_rr.owner, global_rr.type, global_rr.rclass); @@ -124,6 +126,21 @@ static void test_query_aged(void **state) kr_cache_txn_abort(txn); } +/* Test cache removal */ +static void test_remove(void **state) +{ + uint32_t timestamp = CACHE_TIME; + knot_rrset_t cache_rr; + knot_rrset_init(&cache_rr, global_rr.owner, global_rr.type, global_rr.rclass); + + namedb_txn_t *txn = test_txn_write(state); + int ret = kr_cache_remove(txn, &cache_rr); + assert_int_equal(ret, KNOT_EOK); + ret = kr_cache_peek(txn, &cache_rr, ×tamp); + assert_int_equal(ret, KNOT_ENOENT); + kr_cache_txn_commit(txn); +} + /* Test cache fill */ static void test_fill(void **state) { @@ -181,6 +198,8 @@ int main(void) unit_test(test_query), /* Cache aging */ unit_test(test_query_aged), + /* Removal */ + unit_test(test_remove), /* Cache fill */ unit_test(test_fill), unit_test(test_clear),