diff options
Diffstat (limited to 'src/knot/query')
-rw-r--r-- | src/knot/query/capture.c | 63 | ||||
-rw-r--r-- | src/knot/query/capture.h | 32 | ||||
-rw-r--r-- | src/knot/query/layer.h | 136 | ||||
-rw-r--r-- | src/knot/query/query.c | 85 | ||||
-rw-r--r-- | src/knot/query/query.h | 66 | ||||
-rw-r--r-- | src/knot/query/requestor.c | 378 | ||||
-rw-r--r-- | src/knot/query/requestor.h | 119 |
7 files changed, 879 insertions, 0 deletions
diff --git a/src/knot/query/capture.c b/src/knot/query/capture.c new file mode 100644 index 0000000..43f3e54 --- /dev/null +++ b/src/knot/query/capture.c @@ -0,0 +1,63 @@ +/* Copyright (C) 2016 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/>. + */ + +#include <assert.h> + +#include "knot/query/capture.h" + +static int reset(knot_layer_t *ctx) +{ + return KNOT_STATE_PRODUCE; +} + +static int finish(knot_layer_t *ctx) +{ + return KNOT_STATE_NOOP; +} + +static int begin(knot_layer_t *ctx, void *module_param) +{ + ctx->data = module_param; /* struct capture_param */ + return reset(ctx); +} + +static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + return KNOT_STATE_CONSUME; +} + +static int capture(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + assert(pkt && ctx && ctx->data); + struct capture_param *param = ctx->data; + + knot_pkt_copy(param->sink, pkt); + + return KNOT_STATE_DONE; +} + +const knot_layer_api_t *query_capture_api(void) +{ + static const knot_layer_api_t API = { + .begin = begin, + .reset = reset, + .finish = finish, + .consume = capture, + .produce = prepare_query, + }; + + return &API; +} diff --git a/src/knot/query/capture.h b/src/knot/query/capture.h new file mode 100644 index 0000000..41f8270 --- /dev/null +++ b/src/knot/query/capture.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2016 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 "knot/query/layer.h" +#include "libknot/packet/pkt.h" + +/*! + * \brief Processing module for packet capture. + */ +const knot_layer_api_t *query_capture_api(void); + +/*! + * \brief Processing module parameters. + */ +struct capture_param { + knot_pkt_t *sink; /*!< Container for captured response. */ +}; diff --git a/src/knot/query/layer.h b/src/knot/query/layer.h new file mode 100644 index 0000000..119ae5d --- /dev/null +++ b/src/knot/query/layer.h @@ -0,0 +1,136 @@ +/* Copyright (C) 2018 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 "libknot/packet/pkt.h" +#include "libknot/mm_ctx.h" +#include "knot/nameserver/tsig_ctx.h" + +/*! + * \brief Layer processing states. + * + * Each state represents the state machine transition, + * and determines readiness for the next action. + */ +typedef enum { + KNOT_STATE_NOOP = 0, //!< Invalid. + KNOT_STATE_CONSUME, //!< Consume data. + KNOT_STATE_PRODUCE, //!< Produce data. + KNOT_STATE_RESET, //!< Restart processing. + KNOT_STATE_DONE, //!< Finished. + KNOT_STATE_FAIL, //!< Error. + KNOT_STATE_FINAL, //!< Finished and finalized. + KNOT_STATE_IGNORE, //!< Data has been ignored. +} knot_layer_state_t; + +typedef struct knot_layer_api knot_layer_api_t; + +/*! \brief Packet processing context. */ +typedef struct { + const knot_layer_api_t *api; //!< Layer API. + knot_mm_t *mm; //!< Processing memory context. + knot_layer_state_t state; //!< Processing state. + void *data; //!< Module specific. + tsig_ctx_t *tsig; //!< TODO: remove + unsigned flags; //!< Custom flags. +} knot_layer_t; + +/*! \brief Packet processing module API. */ +struct knot_layer_api { + int (*begin)(knot_layer_t *ctx, void *params); + int (*reset)(knot_layer_t *ctx); + int (*finish)(knot_layer_t *ctx); + int (*consume)(knot_layer_t *ctx, knot_pkt_t *pkt); + int (*produce)(knot_layer_t *ctx, knot_pkt_t *pkt); +}; + +/*! \brief Helper for conditional layer call. */ +#define LAYER_CALL(layer, func, ...) \ + assert(layer->api); \ + if (layer->api->func) { \ + layer->state = layer->api->func(layer, ##__VA_ARGS__); \ + } + +/*! + * \brief Initialize packet processing context. + * + * \param ctx Layer context. + * \param mm Memory context. + * \param api Layer API. + */ +inline static void knot_layer_init(knot_layer_t *ctx, knot_mm_t *mm, + const knot_layer_api_t *api) +{ + memset(ctx, 0, sizeof(*ctx)); + + ctx->mm = mm; + ctx->api = api; + ctx->state = KNOT_STATE_NOOP; +} + +/*! + * \brief Prepare packet processing. + * + * \param ctx Layer context. + * \param params Initialization params. + */ +inline static void knot_layer_begin(knot_layer_t *ctx, void *params) +{ + LAYER_CALL(ctx, begin, params); +} + +/*! + * \brief Reset current packet processing context. + * + * \param ctx Layer context. + */ +inline static void knot_layer_reset(knot_layer_t *ctx) +{ + LAYER_CALL(ctx, reset); +} + +/*! + * \brief Finish and close packet processing context. + * + * \param ctx Layer context. + */ +inline static void knot_layer_finish(knot_layer_t *ctx) +{ + LAYER_CALL(ctx, finish); +} + +/*! + * \brief Add more data to layer processing. + * + * \param ctx Layer context. + * \param pkt Data packet. + */ +inline static void knot_layer_consume(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + LAYER_CALL(ctx, consume, pkt); +} + +/*! + * \brief Generate output from layer. + * + * \param ctx Layer context. + * \param pkt Data packet. + */ +inline static void knot_layer_produce(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + LAYER_CALL(ctx, produce, pkt); +} diff --git a/src/knot/query/query.c b/src/knot/query/query.c new file mode 100644 index 0000000..877851a --- /dev/null +++ b/src/knot/query/query.c @@ -0,0 +1,85 @@ +/* Copyright (C) 2022 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/>. + */ + +#include "knot/query/query.h" + +#include "contrib/wire_ctx.h" +#include "libdnssec/random.h" + +void query_init_pkt(knot_pkt_t *pkt) +{ + if (pkt == NULL) { + return; + } + + knot_pkt_clear(pkt); + knot_wire_set_id(pkt->wire, dnssec_random_uint16_t()); +} + +query_edns_data_t query_edns_data_init(conf_t *conf, int remote_family, + query_edns_opt_t opts) +{ + assert(conf); + + query_edns_data_t edns = { + .max_payload = remote_family == AF_INET ? + conf->cache.srv_udp_max_payload_ipv4 : + conf->cache.srv_udp_max_payload_ipv6, + .do_flag = (opts & QUERY_EDNS_OPT_DO), + .expire_option = (opts & QUERY_EDNS_OPT_EXPIRE) + }; + + return edns; +} + +int query_put_edns(knot_pkt_t *pkt, const query_edns_data_t *edns) +{ + if (!pkt || !edns) { + return KNOT_EINVAL; + } + + // Construct EDNS RR + + knot_rrset_t opt_rr = { 0 }; + int ret = knot_edns_init(&opt_rr, edns->max_payload, 0, KNOT_EDNS_VERSION, &pkt->mm); + if (ret != KNOT_EOK) { + return ret; + } + + if (edns->do_flag) { + knot_edns_set_do(&opt_rr); + } + + if (edns->expire_option) { + ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_EXPIRE, 0, NULL, &pkt->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &pkt->mm); + return ret; + } + } + + // Add result into the packet + + knot_pkt_begin(pkt, KNOT_ADDITIONAL); + + ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NOCOMP, &opt_rr, KNOT_PF_FREE); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &pkt->mm); + return ret; + } + + return KNOT_EOK; +} diff --git a/src/knot/query/query.h b/src/knot/query/query.h new file mode 100644 index 0000000..fbf437d --- /dev/null +++ b/src/knot/query/query.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2022 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 "knot/conf/conf.h" +#include "knot/nameserver/log.h" +#include "libknot/packet/pkt.h" + +/*! + * \brief EDNS data. + */ +typedef struct { + uint16_t max_payload; + bool do_flag; + bool expire_option; +} query_edns_data_t; + +typedef enum { + QUERY_EDNS_OPT_DO = 1 << 0, + QUERY_EDNS_OPT_EXPIRE = 1 << 1, +} query_edns_opt_t; + +/*! + * \brief Initialize new packet. + * + * Clear the packet and generate random transaction ID. + * + * \param pkt Packet to initialize. + */ +void query_init_pkt(knot_pkt_t *pkt); + +/*! + * \brief Initialize EDNS parameters from server configuration. + * + * \param[in] conf Server configuration. + * \param[in] remote_family Address family for remote host. + * \param[in] opts EDNS options. + * + * \return EDNS parameters. + */ +query_edns_data_t query_edns_data_init(conf_t *conf, int remote_family, + query_edns_opt_t opts); + +/*! + * \brief Append EDNS into the packet. + * + * \param pkt Packet to add EDNS into. + * \param edns EDNS data. + * + * \return KNOT_E* + */ +int query_put_edns(knot_pkt_t *pkt, const query_edns_data_t *edns); diff --git a/src/knot/query/requestor.c b/src/knot/query/requestor.c new file mode 100644 index 0000000..8643f74 --- /dev/null +++ b/src/knot/query/requestor.c @@ -0,0 +1,378 @@ +/* Copyright (C) 2022 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/>. + */ + +#include <assert.h> + +#include "libknot/attribute.h" +#include "knot/common/unreachable.h" +#include "knot/query/requestor.h" +#include "libknot/errcode.h" +#include "contrib/conn_pool.h" +#include "contrib/mempattern.h" +#include "contrib/net.h" +#include "contrib/sockaddr.h" + +static bool use_tcp(knot_request_t *request) +{ + return (request->flags & KNOT_REQUEST_UDP) == 0; +} + +static bool is_answer_to_query(const knot_pkt_t *query, const knot_pkt_t *answer) +{ + return knot_wire_get_id(query->wire) == knot_wire_get_id(answer->wire); +} + +/*! \brief Ensure a socket is connected. */ +static int request_ensure_connected(knot_request_t *request, bool *reused_fd) +{ + if (request->fd >= 0) { + return KNOT_EOK; + } + + int sock_type = use_tcp(request) ? SOCK_STREAM : SOCK_DGRAM; + + if (sock_type == SOCK_STREAM) { + request->fd = conn_pool_get(global_conn_pool, + &request->source, + &request->remote); + if (request->fd >= 0) { + if (reused_fd != NULL) { + *reused_fd = true; + } + return KNOT_EOK; + } + + if (knot_unreachable_is(global_unreachables, &request->remote, + &request->source)) { + return KNOT_EUNREACH; + } + } + + request->fd = net_connected_socket(sock_type, + &request->remote, + &request->source, + request->flags & KNOT_REQUEST_TFO); + if (request->fd < 0) { + if (request->fd == KNOT_ETIMEOUT) { + knot_unreachable_add(global_unreachables, &request->remote, + &request->source); + } + return request->fd; + } + + return KNOT_EOK; +} + +static int request_send(knot_request_t *request, int timeout_ms, bool *reused_fd) +{ + /* Initiate non-blocking connect if not connected. */ + *reused_fd = false; + int ret = request_ensure_connected(request, reused_fd); + if (ret != KNOT_EOK) { + return ret; + } + + /* Send query, construct if not exists. */ + knot_pkt_t *query = request->query; + uint8_t *wire = query->wire; + size_t wire_len = query->size; + struct sockaddr_storage *tfo_addr = (request->flags & KNOT_REQUEST_TFO) ? + &request->remote : NULL; + + /* Send query. */ + if (use_tcp(request)) { + ret = net_dns_tcp_send(request->fd, wire, wire_len, timeout_ms, + tfo_addr); + if (ret == KNOT_ETIMEOUT) { // Includes establishing conn which times out. + knot_unreachable_add(global_unreachables, &request->remote, + &request->source); + } + } else { + ret = net_dgram_send(request->fd, wire, wire_len, NULL); + } + if (ret < 0) { + return ret; + } else if (ret != wire_len) { + return KNOT_ECONN; + } + + return KNOT_EOK; +} + +static int request_recv(knot_request_t *request, int timeout_ms) +{ + knot_pkt_t *resp = request->resp; + knot_pkt_clear(resp); + + /* Wait for readability */ + int ret = request_ensure_connected(request, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + /* Receive it */ + if (use_tcp(request)) { + ret = net_dns_tcp_recv(request->fd, resp->wire, resp->max_size, timeout_ms); + } else { + ret = net_dgram_recv(request->fd, resp->wire, resp->max_size, timeout_ms); + } + if (ret <= 0) { + resp->size = 0; + if (ret == 0) { + return KNOT_ECONN; + } + return ret; + } + + resp->size = ret; + return ret; +} + +knot_request_t *knot_request_make(knot_mm_t *mm, + const struct sockaddr_storage *remote, + const struct sockaddr_storage *source, + knot_pkt_t *query, + const knot_tsig_key_t *tsig_key, + knot_request_flag_t flags) +{ + if (remote == NULL || query == NULL) { + return NULL; + } + + knot_request_t *request = mm_calloc(mm, 1, sizeof(*request)); + if (request == NULL) { + return NULL; + } + + request->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, mm); + if (request->resp == NULL) { + mm_free(mm, request); + return NULL; + } + + request->query = query; + request->fd = -1; + request->flags = flags; + memcpy(&request->remote, remote, sockaddr_len(remote)); + if (source) { + memcpy(&request->source, source, sockaddr_len(source)); + } else { + request->source.ss_family = AF_UNSPEC; + } + + if (tsig_key && tsig_key->algorithm == DNSSEC_TSIG_UNKNOWN) { + tsig_key = NULL; + } + tsig_init(&request->tsig, tsig_key); + + return request; +} + +void knot_request_free(knot_request_t *request, knot_mm_t *mm) +{ + if (request == NULL) { + return; + } + + if (request->fd >= 0 && use_tcp(request) && + (request->flags & KNOT_REQUEST_KEEP)) { + request->fd = conn_pool_put(global_conn_pool, + &request->source, + &request->remote, + request->fd); + } + if (request->fd >= 0) { + close(request->fd); + } + knot_pkt_free(request->query); + knot_pkt_free(request->resp); + tsig_cleanup(&request->tsig); + + mm_free(mm, request); +} + +int knot_requestor_init(knot_requestor_t *requestor, + const knot_layer_api_t *proc, void *proc_param, + knot_mm_t *mm) +{ + if (requestor == NULL || proc == NULL) { + return KNOT_EINVAL; + } + + memset(requestor, 0, sizeof(*requestor)); + + requestor->mm = mm; + knot_layer_init(&requestor->layer, mm, proc); + knot_layer_begin(&requestor->layer, proc_param); + + return KNOT_EOK; +} + +void knot_requestor_clear(knot_requestor_t *requestor) +{ + if (requestor == NULL) { + return; + } + + knot_layer_finish(&requestor->layer); + + memset(requestor, 0, sizeof(*requestor)); +} + +static int request_reset(knot_requestor_t *req, knot_request_t *last) +{ + knot_layer_reset(&req->layer); + tsig_reset(&last->tsig); + + if (req->layer.flags & KNOT_REQUESTOR_CLOSE) { + req->layer.flags &= ~KNOT_REQUESTOR_CLOSE; + if (last->fd >= 0) { + close(last->fd); + last->fd = -1; + } + } + + if (req->layer.state == KNOT_STATE_RESET) { + return KNOT_EPROCESSING; + } + + return KNOT_EOK; +} + +static int request_produce(knot_requestor_t *req, knot_request_t *last, + int timeout_ms) +{ + knot_layer_produce(&req->layer, last->query); + + int ret = tsig_sign_packet(&last->tsig, last->query); + if (ret != KNOT_EOK) { + return ret; + } + + // TODO: verify condition + if (req->layer.state == KNOT_STATE_CONSUME) { + bool reused_fd = false; + ret = request_send(last, timeout_ms, &reused_fd); + if (reused_fd) { + req->layer.flags |= KNOT_REQUESTOR_REUSED; + } else { + req->layer.flags &= ~KNOT_REQUESTOR_REUSED; + } + } + + return ret; +} + +static int request_consume(knot_requestor_t *req, knot_request_t *last, + int timeout_ms) +{ + int ret = request_recv(last, timeout_ms); + if (ret < 0) { + return ret; + } + + ret = knot_pkt_parse(last->resp, 0); + if (ret != KNOT_EOK) { + return ret; + } + + if (!is_answer_to_query(last->query, last->resp)) { + return KNOT_EMALF; + } + + ret = tsig_verify_packet(&last->tsig, last->resp); + if (ret != KNOT_EOK) { + return ret; + } + + if (tsig_unsigned_count(&last->tsig) >= 100) { + return KNOT_TSIG_EBADSIG; + } + + knot_layer_consume(&req->layer, last->resp); + + return KNOT_EOK; +} + +static bool layer_active(knot_layer_state_t state) +{ + switch (state) { + case KNOT_STATE_CONSUME: + case KNOT_STATE_PRODUCE: + case KNOT_STATE_RESET: + return true; + default: + return false; + } +} + +static int request_io(knot_requestor_t *req, knot_request_t *last, + int timeout_ms) +{ + switch (req->layer.state) { + case KNOT_STATE_CONSUME: + return request_consume(req, last, timeout_ms); + case KNOT_STATE_PRODUCE: + return request_produce(req, last, timeout_ms); + case KNOT_STATE_RESET: + return request_reset(req, last); + default: + return KNOT_EINVAL; + } +} + +int knot_requestor_exec(knot_requestor_t *requestor, knot_request_t *request, + int timeout_ms) +{ + if (requestor == NULL || request == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + + requestor->layer.tsig = &request->tsig; + + /* Do I/O until the processing is satisfied or fails. */ + while (layer_active(requestor->layer.state)) { + ret = request_io(requestor, request, timeout_ms); + if (ret != KNOT_EOK) { + knot_layer_finish(&requestor->layer); + return ret; + } + } + + /* Expect complete request. */ + switch (requestor->layer.state) { + case KNOT_STATE_DONE: + request->flags |= KNOT_REQUEST_KEEP; + break; + case KNOT_STATE_IGNORE: + ret = KNOT_ERROR; + break; + default: + ret = KNOT_EPROCESSING; + } + + /* Verify last TSIG */ + if (tsig_unsigned_count(&request->tsig) != 0) { + ret = KNOT_TSIG_EBADSIG; + } + + /* Finish current query processing. */ + knot_layer_finish(&requestor->layer); + + return ret; +} diff --git a/src/knot/query/requestor.h b/src/knot/query/requestor.h new file mode 100644 index 0000000..aa90cd5 --- /dev/null +++ b/src/knot/query/requestor.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2022 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 <sys/socket.h> +#include <sys/time.h> + +#include "knot/nameserver/tsig_ctx.h" +#include "knot/query/layer.h" +#include "libknot/mm_ctx.h" +#include "libknot/rrtype/tsig.h" + +typedef enum { + KNOT_REQUEST_NONE = 0, /*!< Empty flag. */ + KNOT_REQUEST_UDP = 1 << 0, /*!< Use UDP for requests. */ + KNOT_REQUEST_TFO = 1 << 1, /*!< Enable TCP Fast Open for requests. */ + KNOT_REQUEST_KEEP = 1 << 2, /*!< Keep upstream TCP connection in pool for later reuse. */ +} knot_request_flag_t; + +typedef enum { + KNOT_REQUESTOR_CLOSE = 1 << 0, /*!< Close the connection indication. */ + KNOT_REQUESTOR_REUSED = 1 << 1, /*!< Reused FD indication. */ +} knot_requestor_flag_t; + +/*! \brief Requestor structure. + * + * Requestor holds a FIFO of pending queries. + */ +typedef struct { + knot_mm_t *mm; /*!< Memory context. */ + knot_layer_t layer; /*!< Response processing layer. */ +} knot_requestor_t; + +/*! \brief Request data (socket, payload, response, TSIG and endpoints). */ +typedef struct { + int fd; + knot_request_flag_t flags; + struct sockaddr_storage remote, source; + knot_pkt_t *query; + knot_pkt_t *resp; + tsig_ctx_t tsig; + + knot_sign_context_t sign; /*!< Required for async. DDNS processing. */ +} knot_request_t; + +/*! + * \brief Make request out of endpoints and query. + * + * \param mm Memory context. + * \param remote Remote endpoint address. + * \param source Source address (or NULL). + * \param query Query message. + * \param tsig_key TSIG key for authentication. + * \param flags Request flags. + * + * \return Prepared request or NULL in case of error. + */ +knot_request_t *knot_request_make(knot_mm_t *mm, + const struct sockaddr_storage *remote, + const struct sockaddr_storage *source, + knot_pkt_t *query, + const knot_tsig_key_t *tsig_key, + knot_request_flag_t flags); + +/*! + * \brief Free request and associated data. + * + * \param request Freed request. + * \param mm Memory context. + */ +void knot_request_free(knot_request_t *request, knot_mm_t *mm); + +/*! + * \brief Initialize requestor structure. + * + * \param requestor Requestor instance. + * \param proc Response processing module. + * \param proc_param Processing module context. + * \param mm Memory context. + * + * \return KNOT_EOK or error + */ +int knot_requestor_init(knot_requestor_t *requestor, + const knot_layer_api_t *proc, void *proc_param, + knot_mm_t *mm); + +/*! + * \brief Clear the requestor structure and close pending queries. + * + * \param requestor Requestor instance. + */ +void knot_requestor_clear(knot_requestor_t *requestor); + +/*! + * \brief Execute a request. + * + * \param requestor Requestor instance. + * \param request Request instance. + * \param timeout_ms Timeout of each operation in milliseconds (-1 for infinity). + * + * \return KNOT_EOK or error + */ +int knot_requestor_exec(knot_requestor_t *requestor, + knot_request_t *request, + int timeout_ms); |