423 lines
16 KiB
C
423 lines
16 KiB
C
/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
#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/array.h"
|
|
#include "lib/selection.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);
|
|
* 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
|
|
*/
|
|
|
|
|
|
struct kr_request;
|
|
/** Allocate buffer for answer's wire (*maxlen may get lowered).
|
|
*
|
|
* Motivation: XDP wire allocation is an overlap of library and daemon:
|
|
* - it needs to be called from the library
|
|
* - it needs to rely on some daemon's internals
|
|
* - the library (currently) isn't allowed to directly use symbols from daemon
|
|
* (contrary to modules), e.g. some of our lib-using tests run without daemon
|
|
*
|
|
* Note: after we obtain the wire, we're obliged to send it out.
|
|
* (So far there's no use case to allow cancelling at that point.)
|
|
*/
|
|
typedef uint8_t * (*alloc_wire_f)(struct kr_request *req, uint16_t *maxlen);
|
|
|
|
/**
|
|
* 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, /**< No RRSIG found for that owner+type combination. */
|
|
|
|
/** 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.
|
|
* Or it may be covered by a closer negative TA. */
|
|
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. */
|
|
bool kr_rank_test(uint8_t rank, uint8_t kr_flag) KR_PURE KR_EXPORT;
|
|
|
|
/** 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)
|
|
{
|
|
if (kr_fails_assert(rank && kr_rank_check(*rank)))
|
|
return;
|
|
if (kr_fails_assert(kr_rank_check(kr_flag) && !(kr_flag & KR_RANK_AUTH)))
|
|
return;
|
|
*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
|
|
{
|
|
/** Default kr_request flags. For startup defaults see init_resolver() */
|
|
struct kr_qflags options;
|
|
|
|
/** Default EDNS towards *both* clients and upstream.
|
|
* LATER: consider splitting the two, e.g. allow separately
|
|
* configured limits for UDP packet size (say, LAN is under control). */
|
|
knot_rrset_t *downstream_opt_rr;
|
|
knot_rrset_t *upstream_opt_rr;
|
|
|
|
trie_t *trust_anchors;
|
|
trie_t *negative_anchors;
|
|
/** Validator's limit on the number of cryptographic steps for a single upstream packet. */
|
|
int32_t vld_limit_crypto;
|
|
|
|
struct kr_zonecut root_hints;
|
|
struct kr_cache cache;
|
|
unsigned cache_rtt_tout_retry_interval;
|
|
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 not on UDP; only meaningful if (dst_addr). */
|
|
bool tls:1; /**< true if the request is encrypted; only meaningful if (dst_addr). */
|
|
bool http:1; /**< true if the request is on HTTP; only meaningful if (dst_addr). */
|
|
bool xdp:1; /**< true if the request is on AF_XDP; only meaningful if (dst_addr). */
|
|
};
|
|
|
|
/* Extended DNS Errors, RFC 8914 */
|
|
struct kr_extended_error {
|
|
int32_t info_code; /**< May contain -1 (KNOT_EDNS_EDE_NONE); filter before converting to uint16_t. */
|
|
const char *extra_text; /**< Can be NULL. Allocated on the kr_request::pool or static. */
|
|
};
|
|
|
|
|
|
typedef bool (*addr_info_f)(struct sockaddr*);
|
|
typedef void (*async_resolution_f)(knot_dname_t*, enum knot_rr_type);
|
|
typedef array_t(union kr_sockaddr) kr_sockaddr_array_t;
|
|
|
|
/**
|
|
* 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; /**< See kr_request_ensure_answer() */
|
|
struct kr_query *current_query; /**< Current evaluated query. */
|
|
struct {
|
|
/** Address that originated the request. May be that of a client
|
|
* behind a proxy, if PROXYv2 is used. Otherwise, it will be
|
|
* the same as `comm_addr`. `NULL` for internal origin. */
|
|
const struct sockaddr *addr;
|
|
/** Address that communicated the request. This may be the address
|
|
* of a proxy. It is the same as `addr` if no proxy is used.
|
|
* `NULL` for internal origin. */
|
|
const struct sockaddr *comm_addr;
|
|
/** Address that accepted the request. `NULL` for internal origin.
|
|
* Beware: in case of UDP on wildcard address it will be wildcard;
|
|
* closely related: issue #173. */
|
|
const struct sockaddr *dst_addr;
|
|
const knot_pkt_t *packet;
|
|
/** Request flags from the point of view of the original client.
|
|
* This client may be behind a proxy. */
|
|
struct kr_request_qsource_flags flags;
|
|
/** Request flags from the point of view of the client actually
|
|
* communicating with the resolver. When PROXYv2 protocol is used,
|
|
* this describes the request from the proxy. When there is no
|
|
* proxy, this will have exactly the same value as `flags`. */
|
|
struct kr_request_qsource_flags comm_flags;
|
|
size_t size; /**< query packet size */
|
|
int32_t stream_id; /**< HTTP/2 stream ID for DoH requests */
|
|
kr_http_header_array_t headers; /**< HTTP/2 headers for DoH requests */
|
|
} qsource;
|
|
struct {
|
|
unsigned rtt; /**< Current upstream RTT */
|
|
const struct kr_transport *transport; /**< Current upstream transport */
|
|
} 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;
|
|
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 */
|
|
struct {
|
|
addr_info_f is_tls_capable;
|
|
addr_info_f is_tcp_connected;
|
|
addr_info_f is_tcp_waiting;
|
|
kr_sockaddr_array_t forwarding_targets; /**< When forwarding, possible targets are put here */
|
|
} selection_context;
|
|
unsigned int count_no_nsaddr;
|
|
unsigned int count_fail_row;
|
|
alloc_wire_f alloc_wire_cb; /**< CB to allocate answer wire (can be NULL). */
|
|
struct kr_extended_error extended_error; /**< EDE info; don't modify directly, use kr_request_set_extended_error() */
|
|
};
|
|
|
|
/** 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.
|
|
*
|
|
* @param request request state with initialized mempool
|
|
* @param ctx resolution context
|
|
* @return CONSUME (expecting query)
|
|
*/
|
|
KR_EXPORT
|
|
int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx);
|
|
|
|
/**
|
|
* Ensure that request->answer->opt_rr is present if query has EDNS.
|
|
*
|
|
* This function should be used after clearing a response packet to ensure its
|
|
* opt_rr is properly set. Returns the opt_rr (for convenience) or NULL.
|
|
*/
|
|
KR_EXPORT
|
|
knot_rrset_t * kr_request_ensure_edns(struct kr_request *request);
|
|
|
|
/**
|
|
* Ensure that request->answer is usable, and return it (for convenience).
|
|
*
|
|
* It may return NULL, in which case it marks ->state with _FAIL and no answer will be sent.
|
|
* Only use this when it's guaranteed that there will be no delay before sending it.
|
|
* You don't need to call this in places where "resolver knows" that there will be no delay,
|
|
* but even there you need to check if the ->answer is NULL (unless you check for _FAIL anyway).
|
|
*/
|
|
KR_EXPORT
|
|
knot_pkt_t * kr_request_ensure_answer(struct kr_request *request);
|
|
|
|
/**
|
|
* 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, struct kr_transport **transport, 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 kr_transport **transport, 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 kr_transport *transport, 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 (to be assigned to request->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);
|
|
|
|
/**
|
|
* Set the extended DNS error for request.
|
|
*
|
|
* The error is set only if it has a higher or the same priority as the one
|
|
* already assigned. The provided extra_text may be NULL, or a string that is
|
|
* allocated either statically, or on the request's mempool. To clear any
|
|
* error, call it with KNOT_EDNS_EDE_NONE and NULL as extra_text.
|
|
*
|
|
* To facilitate debugging, we include a unique base32 identifier at the start
|
|
* of the extra_text field for every call of this function. To generate such an
|
|
* identifier, you can use the command:
|
|
* $ base32 /dev/random | head -c 4
|
|
*
|
|
* @param request request state
|
|
* @param info_code extended DNS error code
|
|
* @param extra_text optional string with additional information
|
|
* @return info_code that is set after the call
|
|
*/
|
|
KR_EXPORT
|
|
int kr_request_set_extended_error(struct kr_request *request, int info_code, const char *extra_text);
|
|
|
|
static inline void kr_query_inform_timeout(struct kr_request *req, const struct kr_query *qry)
|
|
{
|
|
kr_request_set_extended_error(req, KNOT_EDNS_EDE_NREACH_AUTH, "RRPF");
|
|
|
|
unsigned ind = 0;
|
|
for (const struct kr_query *q = qry; q; q = q->parent)
|
|
ind += 2;
|
|
const uint32_t qid = qry ? qry->uid : 0;
|
|
|
|
kr_log_req(req, qid, ind, WORKER, "internal timeout for resolving the request has expired\n");
|
|
}
|