diff options
Diffstat (limited to '')
-rw-r--r-- | lib/resolve.h | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/lib/resolve.h b/lib/resolve.h new file mode 100644 index 0000000..60d80bb --- /dev/null +++ b/lib/resolve.h @@ -0,0 +1,332 @@ +/* Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <netinet/in.h> +#include <sys/socket.h> +#include <libknot/packet/pkt.h> + +#include "lib/cookies/control.h" +#include "lib/cookies/lru_cache.h" +#include "lib/layer.h" +#include "lib/generic/map.h" +#include "lib/generic/array.h" +#include "lib/nsrep.h" +#include "lib/rplan.h" +#include "lib/module.h" +#include "lib/cache/api.h" + +/** + * @file resolve.h + * @brief The API provides an API providing a "consumer-producer"-like interface to enable + * user to plug it into existing event loop or I/O code. + * + * # Example usage of the iterative API: + * + * @code{.c} + * + * // Create request and its memory pool + * struct kr_request req = { + * .pool = { + * .ctx = mp_new (4096), + * .alloc = (mm_alloc_t) mp_alloc + * } + * }; + * + * // Setup and provide input query + * int state = kr_resolve_begin(&req, ctx, final_answer); + * state = kr_resolve_consume(&req, query); + * + * // Generate answer + * while (state == KR_STATE_PRODUCE) { + * + * // Additional query generate, do the I/O and pass back answer + * state = kr_resolve_produce(&req, &addr, &type, query); + * while (state == KR_STATE_CONSUME) { + * int ret = sendrecv(addr, proto, query, resp); + * + * // If I/O fails, make "resp" empty + * state = kr_resolve_consume(&request, addr, resp); + * knot_pkt_clear(resp); + * } + * knot_pkt_clear(query); + * } + * + * // "state" is either DONE or FAIL + * kr_resolve_finish(&request, state); + * + * @endcode + */ + + +/** + * RRset rank - for cache and ranked_rr_*. + * + * The rank meaning consists of one independent flag - KR_RANK_AUTH, + * and the rest have meaning of values where only one can hold at any time. + * You can use one of the enums as a safe initial value, optionally | KR_RANK_AUTH; + * otherwise it's best to manipulate ranks via the kr_rank_* functions. + * + * @note The representation is complicated by restrictions on integer comparison: + * - AUTH must be > than !AUTH + * - AUTH INSECURE must be > than AUTH (because it attempted validation) + * - !AUTH SECURE must be > than AUTH (because it's valid) + * + * See also: + * https://tools.ietf.org/html/rfc2181#section-5.4.1 + * https://tools.ietf.org/html/rfc4035#section-4.3 + */ +enum kr_rank { + /* Initial-like states. No validation has been attempted (yet). */ + KR_RANK_INITIAL = 0, /**< Did not attempt to validate. It's assumed + compulsory to validate (or prove insecure). */ + KR_RANK_OMIT, /**< Do not attempt to validate. + (And don't consider it a validation failure.) */ + KR_RANK_TRY, /**< Attempt to validate, but failures are non-fatal. */ + + /* Failure states. These have higher value because they have more information. */ + KR_RANK_INDET = 4, /**< Unable to determine whether it should be secure. */ + KR_RANK_BOGUS, /**< Ought to be secure but isn't. */ + KR_RANK_MISMATCH, + KR_RANK_MISSING, /**< Unable to obtain a good signature. */ + + /** Proven to be insecure, i.e. we have a chain of trust from TAs + * that cryptographically denies the possibility of existence + * of a positive chain of trust from the TAs to the record. */ + KR_RANK_INSECURE = 8, + + /** Authoritative data flag; the chain of authority was "verified". + * Even if not set, only in-bailiwick stuff is acceptable, + * i.e. almost authoritative (example: mandatory glue and its NS RR). */ + KR_RANK_AUTH = 16, + + KR_RANK_SECURE = 32, /**< Verified whole chain of trust from the closest TA. */ + /* @note Rank must not exceed 6 bits */ +}; + +/** Check that a rank value is valid. Meant for assertions. */ +bool kr_rank_check(uint8_t rank) KR_PURE; + +/** Test the presence of any flag/state in a rank, i.e. including KR_RANK_AUTH. */ +static inline bool kr_rank_test(uint8_t rank, uint8_t kr_flag) +{ + assert(kr_rank_check(rank) && kr_rank_check(kr_flag)); + if (kr_flag == KR_RANK_AUTH) { + return rank & KR_RANK_AUTH; + } + assert(!(kr_flag & KR_RANK_AUTH)); + /* The rest are exclusive values - exactly one has to be set. */ + return (rank & ~KR_RANK_AUTH) == kr_flag; +} + +/** Set the rank state. The _AUTH flag is kept as it was. */ +static inline void kr_rank_set(uint8_t *rank, uint8_t kr_flag) +{ + assert(rank && kr_rank_check(*rank)); + assert(kr_rank_check(kr_flag) && !(kr_flag & KR_RANK_AUTH)); + *rank = kr_flag | (*rank & KR_RANK_AUTH); +} + + +/** @cond internal Array of modules. */ +typedef array_t(struct kr_module *) module_array_t; +/* @endcond */ + +/** + * Name resolution context. + * + * Resolution context provides basic services like cache, configuration and options. + * + * @note This structure is persistent between name resolutions and may + * be shared between threads. + */ +struct kr_context +{ + struct kr_qflags options; + knot_rrset_t *opt_rr; + map_t trust_anchors; + map_t negative_anchors; + struct kr_zonecut root_hints; + struct kr_cache cache; + kr_nsrep_rtt_lru_t *cache_rtt; + unsigned cache_rtt_tout_retry_interval; + kr_nsrep_lru_t *cache_rep; + module_array_t *modules; + /* The cookie context structure should not be held within the cookies + * module because of better access. */ + struct kr_cookie_ctx cookie_ctx; + kr_cookie_lru_t *cache_cookie; + int32_t tls_padding; /**< See net.tls_padding in ../daemon/README.rst -- -1 is "true" (default policy), 0 is "false" (no padding) */ + knot_mm_t *pool; +}; + +/* Kept outside, because kres-gen.lua can't handle this depth + * (and lines here were too long anyway). */ +struct kr_request_qsource_flags { + bool tcp:1; /**< true if the request is on TCP (or TLS); only meaningful if (dst_addr). */ + bool tls:1; /**< true if the request is on TLS; only meaningful if (dst_addr). */ +}; + +/** + * 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; + knot_pkt_t *answer; + struct kr_query *current_query; /**< Current evaluated query. */ + struct { + /** Address that originated the request. NULL for internal origin. */ + const struct sockaddr *addr; + /** Address that accepted the request. NULL for internal origin. */ + const struct sockaddr *dst_addr; + const knot_pkt_t *packet; + struct kr_request_qsource_flags flags; /**< See definition above. */ + size_t size; /**< query packet size */ + } qsource; + struct { + unsigned rtt; /**< Current upstream RTT */ + const struct sockaddr *addr; /**< Current upstream address */ + } upstream; /**< Upstream information, valid only in consume() phase */ + struct kr_qflags options; + int state; + ranked_rr_array_t answ_selected; + ranked_rr_array_t auth_selected; + ranked_rr_array_t add_selected; + rr_array_t additional; + bool answ_validated; /**< internal to validator; beware of caching, etc. */ + bool auth_validated; /**< see answ_validated ^^ ; TODO */ + + /** Overall rank for the request. + * + * Values from kr_rank, currently just KR_RANK_SECURE and _INITIAL. + * Only read this in finish phase and after validator, please. + * Meaning of _SECURE: all RRs in answer+authority are _SECURE, + * including any negative results implied (NXDOMAIN, NODATA). + */ + uint8_t rank; + + struct kr_rplan rplan; + trace_log_f trace_log; /**< Logging tracepoint */ + trace_callback_f trace_finish; /**< Request finish tracepoint */ + int vars_ref; /**< Reference to per-request variable table. LUA_NOREF if not set. */ + knot_mm_t pool; + unsigned int uid; /** for logging purposes only */ + void *daemon_context; /** pointer to worker from daemon. Can be used in modules. */ +}; + +/** Initializer for an array of *_selected. */ +#define kr_request_selected(req) { \ + [KNOT_ANSWER] = &(req)->answ_selected, \ + [KNOT_AUTHORITY] = &(req)->auth_selected, \ + [KNOT_ADDITIONAL] = &(req)->add_selected, \ + } + +/** + * 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) + */ +KR_EXPORT +int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pkt_t *answer); + +/** + * 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 src [in] packet source address + * @param packet [in] input packet + * @return any state + */ +KR_EXPORT +int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, 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 + */ +KR_EXPORT +int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet); + +/** + * Finalises the outbound query packet with the knowledge of the IP addresses. + * + * @note The function must be called before actual sending of the request packet. + * + * @param request request state (in PRODUCE state) + * @param src address from which the query is going to be sent + * @param dst address of the name server + * @param type used socket type (SOCK_STREAM, SOCK_DGRAM) + * @param packet [in,out] query packet to be finalised + * @return kr_ok() or error code + */ +KR_EXPORT +int kr_resolve_checkout(struct kr_request *request, const struct sockaddr *src, + 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 + */ +KR_EXPORT +int kr_resolve_finish(struct kr_request *request, int state); + +/** + * Return resolution plan. + * @param request request state + * @return pointer to rplan + */ +KR_EXPORT KR_PURE +struct kr_rplan *kr_resolve_plan(struct kr_request *request); + +/** + * Return memory pool associated with request. + * @param request request state + * @return mempool + */ +KR_EXPORT KR_PURE +knot_mm_t *kr_resolve_pool(struct kr_request *request); + |