#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wsign-compare"
#include "lib/resolve.h"
#include "lib/dnssec/ta.h"
#include "lib/cache/cdb_lmdb.h"
#include "contrib/ucw/mempool.c"
#pragma GCC diagnostic pop
#include "resolve.h"
struct lkr_context {
struct kr_context resolver;
module_array_t modules;
knot_mm_t pool;
};
struct lkr_request {
struct kr_request req;
};
int lkr_module_load(struct lkr_context *ctx, const char *name)
{
struct kr_module *module = mm_alloc(&ctx->pool, sizeof(*module));
if (!module) {
return kr_error(ENOMEM);
}
module->data = ctx;
int ret = kr_module_load(module, name, "");
if (ret == 0) {
array_push_mm(ctx->modules, module, kr_memreserve, &ctx->pool);
}
return ret;
}
int lkr_module_unload(struct lkr_context *ctx, const char *name)
{
for (size_t i = 0; i < ctx->modules.len; ++i) {
struct kr_module *module = ctx->modules.at[i];
if (strcmp(module->name, name) == 0) {
array_del(ctx->modules, i);
kr_module_unload(module);
return 0;
}
}
return kr_error(ENOENT);
}
int lkr_root_hint(struct lkr_context *ctx, const uint8_t *data, size_t len)
{
knot_rdata_t rdata[RDATA_ARR_MAX];
knot_rdata_init(rdata, len, data);
kr_zonecut_add(&ctx->resolver.root_hints, (const knot_dname_t *)"", rdata);
return 0;
}
int lkr_trust_anchor(struct lkr_context *ctx, const uint8_t *data, size_t len)
{
return kr_ta_add(&ctx->resolver.trust_anchors, (const knot_dname_t *)"", KNOT_RRTYPE_DS, 172800, data, len);
}
void lkr_verbose(struct lkr_context *ctx, bool val)
{
if (ctx != NULL) {
kr_verbose_set(val);
}
}
struct lkr_context *lkr_context_new()
{
knot_mm_t pool = {
.ctx = mp_new (4096),
.alloc = (knot_mm_alloc_t) mp_alloc
};
struct lkr_context *ctx = mm_alloc(&pool, sizeof(*ctx));
memset(ctx, 0, sizeof(*ctx));
ctx->pool = pool;
struct kr_context *resolver = &ctx->resolver;
resolver->trust_anchors = map_make(NULL);
resolver->negative_anchors = map_make(NULL);
resolver->pool = &ctx->pool;
resolver->modules = &ctx->modules;
resolver->cache_rtt_tout_retry_interval = 10;
resolver->opt_rr = mm_alloc(&ctx->pool, sizeof(knot_rrset_t));
if (!resolver->opt_rr) {
return NULL;
}
knot_edns_init(resolver->opt_rr, 1452, 0, 0, &ctx->pool);
resolver->tls_padding = -1;
kr_zonecut_init(&resolver->root_hints, (const uint8_t *)"", &ctx->pool);
lru_create(&resolver->cache_rtt, 65535, &ctx->pool, NULL);
lru_create(&resolver->cache_rep, 65535, &ctx->pool, NULL);
lkr_module_load(ctx, "iterate");
lkr_module_load(ctx, "validate");
lkr_root_hint(ctx, (const uint8_t *) "\xc0\xcb\xe6\x0a", 4);
resolver->options.NO_0X20 = true;
resolver->options.NO_IPV6 = true;
return ctx;
}
int lkr_cache_open(struct lkr_context *ctx, const char *path, size_t max_bytes)
{
struct kr_cdb_opts opts = { path, max_bytes };
return kr_cache_open(&ctx->resolver.cache, kr_cdb_lmdb(), &opts, &ctx->pool);
}
void lkr_context_free(struct lkr_context *ctx)
{
for (size_t i = 0; i < ctx->modules.len; ++i) {
struct kr_module *module = ctx->modules.at[i];
kr_module_unload(module);
}
mp_delete((struct mempool *)ctx->pool.ctx);
}
void lkr_request_free(struct lkr_request *req)
{
mp_delete((struct mempool *)req->req.pool.ctx);
}
struct lkr_request *lkr_request_new(struct lkr_context *ctx)
{
knot_mm_t pool = {
.ctx = mp_new (4096),
.alloc = (knot_mm_alloc_t) mp_alloc
};
struct lkr_request *req = mm_alloc(&pool, sizeof(*req));
memset(req, 0, sizeof(*req));
req->req.pool = pool;
req->req.answer = knot_pkt_new(NULL, 4096, &req->req.pool);
if (!req->req.answer) {
return NULL;
}
int ret = kr_resolve_begin(&req->req, &ctx->resolver, req->req.answer);
if (ret == KR_STATE_FAIL) {
lkr_request_free(req);
return NULL;
}
return req;
}
enum lkr_state lkr_consume(struct lkr_request *req, const struct sockaddr *addr, const uint8_t *data, size_t len)
{
knot_pkt_t *packet = knot_pkt_new((uint8_t *)data, len, &req->req.pool);
int ret = knot_pkt_parse(packet, 0);
if (ret != 0) {
return FAIL;
}
return (enum lkr_state) kr_resolve_consume(&req->req, addr, packet);
}
enum lkr_state lkr_produce(struct lkr_request *req, struct sockaddr *addrs[], size_t addrs_len, uint8_t *data, size_t *len, _Bool is_stream)
{
knot_pkt_t *packet = knot_pkt_new(data, *len, &req->req.pool);
struct sockaddr *addr_list = NULL;
int sock_type = is_stream ? SOCK_STREAM : SOCK_DGRAM;
int res = kr_resolve_produce(&req->req, &addr_list, &sock_type, packet);
if (!addr_list) {
*len = packet->size;
return (enum lkr_state) res;
}
int ret = kr_resolve_checkout(&req->req, NULL, addr_list, sock_type, packet);
if (ret != 0) {
*len = packet->size;
return FAIL;
}
struct sockaddr_in6 *addr_list_entries = (struct sockaddr_in6 *)addr_list;
for (uint16_t i = 0; i < MIN(addrs_len, KR_NSREP_MAXADDR); ++i) {
struct sockaddr *choice = (struct sockaddr *)(&addr_list_entries[i]);
if (choice->sa_family == AF_UNSPEC) {
break;
}
addrs[i] = choice;
}
*len = packet->size;
return (enum lkr_state) res;
}
size_t lkr_finish(struct lkr_request *req, enum lkr_state state)
{
(void) kr_resolve_finish(&req->req, state);
return req->req.answer->size;
}
size_t lkr_write_answer(struct lkr_request *req, uint8_t *dst, size_t max_bytes)
{
knot_pkt_t *answer = req->req.answer;
if (answer->size > max_bytes) {
return 0;
}
memmove(dst, answer->wire, answer->size);
return answer->size;
}
int lkr_sockaddr_len(struct sockaddr *sa)
{
return kr_sockaddr_len(sa);
}