Commit 28206f75 authored by Marek Vavruša's avatar Marek Vavruša

lib: cleanup

parent cfe1a9f9
......@@ -20,24 +20,8 @@
* @{
*/
#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. */
......
......@@ -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. */
......
......@@ -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,7 +29,7 @@
#include "lib/layer/itercache.h"
#include "lib/layer/iterate.h"
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), "resl", fmt)
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), "resl", fmt)
/* Defines */
#define ITER_LIMIT 50
......@@ -42,160 +44,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 = &param->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(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;
(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 int iterate(struct knot_requestor *requestor, struct kr_layer_param *param)
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(&param->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);
}
/* 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 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->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 *proto, 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(&param, &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;
*proto = (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;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment