Newer
Older
/* 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/>.
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <contrib/cleanup.h>
#include <ccan/isaac/isaac.h>
#include <libknot/descriptor.h>
#include <libknot/dname.h>
#include <libknot/rrtype/rrsig.h>
#include "lib/defines.h"
#include "lib/utils.h"
#include "lib/generic/array.h"
#include "lib/nsrep.h"
#include "lib/module.h"
#include "lib/resolve.h"
/* Always compile-in log symbols, even if disabled. */
Ondřej Surý
committed
#undef kr_verbose_set
#undef kr_log_verbose
/* Logging & debugging */
bool kr_verbose_status = false;
/** @internal CSPRNG context */
static isaac_ctx ISAAC;
static bool isaac_seeded = false;
#define SEED_SIZE 256
/*
* Macros.
*/
#define strlen_safe(x) ((x) ? strlen(x) : 0)
/**
* @internal Convert 16bit unsigned to string, keeps leading spaces.
* @note Always fills dst length = 5
* Credit: http://computer-programming-forum.com/46-asm/7aa4b50bce8dd985.htm
*/
static inline int u16tostr(uint8_t *dst, uint16_t num)
{
uint32_t tmp = num * (((1 << 28) / 10000) + 1) - (num / 4);
for(size_t i = 0; i < 5; i++) {
dst[i] = '0' + (char) (tmp >> 28);
tmp = (tmp & 0x0fffffff) * 10;
}
return 5;
}
/*
* Cleanup callbacks.
*/
Ondřej Surý
committed
bool kr_verbose_set(bool status)
Ondřej Surý
committed
#ifndef NOVERBOSELOG
kr_verbose_status = status;
return kr_verbose_status;
Ondřej Surý
committed
void kr_log_verbose(const char *fmt, ...)
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
fflush(stdout);
}
char* kr_strcatdup(unsigned n, ...)
{
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* Calculate total length */
size_t total_len = 0;
va_list vl;
va_start(vl, n);
for (unsigned i = 0; i < n; ++i) {
char *item = va_arg(vl, char *);
total_len += strlen_safe(item);
}
va_end(vl);
/* Allocate result and fill */
char *result = NULL;
if (total_len > 0) {
result = malloc(total_len + 1);
}
if (result) {
char *stream = result;
va_start(vl, n);
for (unsigned i = 0; i < n; ++i) {
char *item = va_arg(vl, char *);
if (item) {
size_t len = strlen(item);
memcpy(stream, item, len + 1);
stream += len;
}
}
va_end(vl);
}
return result;
}
static int seed_file(FILE *fp, char *buf, size_t buflen)
{
if (!fp) {
return -1;
}
/* Read whole buffer even if interrupted */
ssize_t readb = 0;
while (!ferror(fp) && readb < buflen) {
readb += fread(buf, 1, buflen - readb, fp);
}
return 0;
}
static int randseed(char *buf, size_t buflen)
{
/* This is adapted from Tor's crypto_seed_rng() */
static const char *filenames[] = {
"/dev/srandom", "/dev/urandom", "/dev/random", NULL
};
for (unsigned i = 0; filenames[i]; ++i) {
auto_fclose FILE *fp = fopen(filenames[i], "r");
if (seed_file(fp, buf, buflen) == 0) {
return 0;
}
}
/* Seed from time, this is not going to be secure. */
struct timeval tv;
gettimeofday(&tv, NULL);
memcpy(buf, &tv, buflen < sizeof(tv) ? buflen : sizeof(tv));
return 0;
int kr_rand_reseed(void)
{
uint8_t seed[SEED_SIZE];
randseed((char *)seed, sizeof(seed));
isaac_reseed(&ISAAC, seed, sizeof(seed));
return kr_ok();
}
unsigned kr_rand_uint(unsigned max)
{
if (!isaac_seeded) {
kr_rand_reseed();
isaac_seeded = true;
}
return isaac_next_uint(&ISAAC, max);
}
int kr_memreserve(void *baton, char **mem, size_t elm_size, size_t want, size_t *have)
{
if (*have >= want) {
return 0;
} else {
size_t next_size = array_next_count(want);
void *mem_new = mm_alloc(pool, next_size * elm_size);
if (mem_new != NULL) {
memcpy(mem_new, *mem, (*have)*(elm_size));
mm_free(pool, *mem);
*mem = mem_new;
*have = next_size;
return 0;
}
}
return -1;
}
int kr_pkt_recycle(knot_pkt_t *pkt)
{
pkt->rrset_count = 0;
pkt->size = KNOT_WIRE_HEADER_SIZE;
pkt->current = KNOT_ANSWER;
knot_wire_set_qdcount(pkt->wire, 0);
knot_wire_set_ancount(pkt->wire, 0);
knot_wire_set_nscount(pkt->wire, 0);
knot_wire_set_arcount(pkt->wire, 0);
memset(pkt->sections, 0, sizeof(pkt->sections));
knot_pkt_begin(pkt, KNOT_ANSWER);
return knot_pkt_parse_question(pkt);
}
int kr_pkt_clear_payload(knot_pkt_t *pkt)
{
pkt->rrset_count = 0;
pkt->size = KNOT_WIRE_HEADER_SIZE + pkt->qname_size +
2 * sizeof(uint16_t); /* QTYPE + QCLASS */
pkt->parsed = KNOT_WIRE_HEADER_SIZE;
pkt->current = KNOT_ANSWER;
knot_wire_set_ancount(pkt->wire, 0);
knot_wire_set_nscount(pkt->wire, 0);
knot_wire_set_arcount(pkt->wire, 0);
memset(&pkt->sections[KNOT_ANSWER], 0, sizeof(knot_pktsection_t) *
(KNOT_PKT_SECTIONS - (KNOT_ANSWER + 1)));
knot_pkt_begin(pkt, KNOT_ANSWER);
return knot_pkt_parse_question(pkt);
}
int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl,
uint16_t rclass, uint16_t rtype, const uint8_t *rdata, uint16_t rdlen)
{
if (!pkt || !name) {
return kr_error(EINVAL);
}
/* Create empty RR */
knot_rrset_t rr;
knot_rrset_init(&rr, knot_dname_copy(name, &pkt->mm), rtype, rclass);
/* Create RDATA
* @warning _NOT_ thread safe.
*/
static knot_rdata_t rdata_arr[RDATA_ARR_MAX];
knot_rdata_init(rdata_arr, rdlen, rdata, ttl);
knot_rdataset_add(&rr.rrs, rdata_arr, &pkt->mm);
/* Append RR */
return knot_pkt_put(pkt, 0, &rr, KNOT_PF_FREE);
}
const char *kr_inaddr(const struct sockaddr *addr)
{
if (!addr) {
return NULL;
}
switch (addr->sa_family) {
case AF_INET: return (const char *)&(((const struct sockaddr_in *)addr)->sin_addr);
case AF_INET6: return (const char *)&(((const struct sockaddr_in6 *)addr)->sin6_addr);
default: return NULL;
}
}
int kr_inaddr_family(const struct sockaddr *addr)
{
if (!addr)
return AF_UNSPEC;
return addr->sa_family;
}
int kr_inaddr_len(const struct sockaddr *addr)
{
if (!addr) {
return kr_error(EINVAL);
}
return kr_family_len(addr->sa_family);
int kr_straddr_family(const char *addr)
{
if (!addr) {
return kr_error(EINVAL);
}
if (strchr(addr, ':')) {
return AF_INET6;
}
return AF_INET;
int kr_family_len(int family)
{
switch (family) {
case AF_INET: return sizeof(struct in_addr);
case AF_INET6: return sizeof(struct in6_addr);
default: return kr_error(EINVAL);
}
int kr_straddr_subnet(void *dst, const char *addr)
{
if (!dst || !addr) {
return kr_error(EINVAL);
}
/* Parse subnet */
int bit_len = 0;
int family = kr_straddr_family(addr);
auto_free char *addr_str = strdup(addr);
char *subnet = strchr(addr_str, '/');
if (subnet) {
*subnet = '\0';
subnet += 1;
bit_len = atoi(subnet);
/* Check client subnet length */
const int max_len = (family == AF_INET6) ? 128 : 32;
if (bit_len < 0 || bit_len > max_len) {
return kr_error(ERANGE);
}
} else {
/* No subnet, use maximal subnet length. */
bit_len = (family == AF_INET6) ? 128 : 32;
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
}
/* Parse address */
int ret = inet_pton(family, addr_str, dst);
if (ret < 0) {
return kr_error(EILSEQ);
}
return bit_len;
}
int kr_bitcmp(const char *a, const char *b, int bits)
{
if (!a || !b || bits == 0) {
return kr_error(ENOMEM);
}
/* Compare part byte-divisible part. */
const size_t chunk = bits / 8;
int ret = memcmp(a, b, chunk);
if (ret != 0) {
return ret;
}
a += chunk;
b += chunk;
bits -= chunk * 8;
/* Compare last partial byte address block. */
if (bits > 0) {
const size_t shift = (8 - bits);
ret = ((uint8_t)(*a >> shift) - (uint8_t)(*b >> shift));
}
return ret;
int kr_rrkey(char *key, const knot_dname_t *owner, uint16_t type, uint8_t rank)
{
if (!key || !owner) {
return kr_error(EINVAL);
}
key[0] = (rank << 2) | 0x01; /* Must be non-zero */
uint8_t *key_buf = (uint8_t *)key + 1;
int ret = knot_dname_to_wire(key_buf, owner, KNOT_DNAME_MAXLEN);
if (ret <= 0) {
return ret;
}
knot_dname_to_lower(key_buf);
key_buf += ret - 1;
/* Must convert to string, as the key must not contain 0x00 */
ret = u16tostr(key_buf, type);
key_buf[ret] = '\0';
return (char *)&key_buf[ret] - key;
}
int kr_rrmap_add(map_t *stash, const knot_rrset_t *rr, uint8_t rank, knot_mm_t *pool)
{
if (!stash || !rr) {
return kr_error(EINVAL);
}
/* Stash key = {[1] flags, [1-255] owner, [5] type, [1] \x00 } */
char key[KR_RRKEY_LEN];
uint8_t extra_flags = 0;
uint16_t rrtype = rr->type;
/* Stash RRSIGs in a special cache, flag them and set type to its covering RR.
* This way it the stash won't merge RRSIGs together. */
if (rr->type == KNOT_RRTYPE_RRSIG) {
rrtype = knot_rrsig_type_covered(&rr->rrs, 0);
extra_flags |= KEY_FLAG_RRSIG;
int ret = kr_rrkey(key, rr->owner, rrtype, rank);
return kr_error(EILSEQ);
key[0] |= extra_flags;
/* Check if already exists */
knot_rrset_t *stashed = map_get(stash, key);
if (!stashed) {
stashed = knot_rrset_copy(rr, pool);
if (!stashed) {
return kr_error(ENOMEM);
}
return map_set(stash, key, stashed);
}
/* Merge rdataset */
return knot_rdataset_merge(&stashed->rrs, &rr->rrs, pool);
}
int kr_rrarray_add(rr_array_t *array, const knot_rrset_t *rr, knot_mm_t *pool)
int ret = array_reserve_mm(*array, array->len + 1, kr_memreserve, pool);
if (ret != 0) {
return kr_error(ENOMEM);
}
knot_rrset_t *copy = knot_rrset_copy(rr, pool);
if (!copy) {
return kr_error(ENOMEM);
}
array_push(*array, copy);
return kr_ok();
}
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr,
uint8_t rank, bool to_wire, uint32_t qry_uid, knot_mm_t *pool)
{
/* rr always has one record per rrset
* check if another rrset with the same
* rclass/type/owner combination exists within current query
* and merge if needed */
for (ssize_t i = array->len - 1; i >= 0; --i) {
ranked_rr_array_entry_t *stashed = array->at[i];
if (stashed->yielded) {
break;
}
if (stashed->qry_uid != qry_uid) {
break;
}
if (stashed->rr->rclass == rr->rclass &&
stashed->rr->type == rr->type &&
knot_dname_is_equal(stashed->rr->owner, rr->owner)) {
assert(stashed->rank == rank &&
stashed->cached == false &&
stashed->to_wire == to_wire);
/* Merge */
return knot_rdataset_merge(&stashed->rr->rrs, &rr->rrs, pool);
}
}
/* No stashed rrset found, add */
int ret = array_reserve_mm(*array, array->len + 1, kr_memreserve, pool);
if (ret != 0) {
return kr_error(ENOMEM);
}
ranked_rr_array_entry_t *entry = mm_alloc(pool, sizeof(ranked_rr_array_entry_t));
if (!entry) {
return kr_error(ENOMEM);
}
knot_rrset_t *copy = knot_rrset_copy(rr, pool);
if (!copy) {
return kr_error(ENOMEM);
}
entry->qry_uid = qry_uid;
entry->rr = copy;
entry->rank = rank;
entry->revalidation_cnt = 0;
entry->cached = false;
entry->yielded = false;
entry->to_wire = to_wire;
array_push(*array, entry);
return kr_ok();
}

Grigorii Demidov
committed
int kr_ranked_rrarray_set_wire(ranked_rr_array_t *array, bool to_wire,
uint32_t qry_uid, bool check_dups)
{
for (size_t i = 0; i < array->len; ++i) {
ranked_rr_array_entry_t *entry = array->at[i];

Grigorii Demidov
committed
if (entry->qry_uid != qry_uid) {
continue;
}
entry->to_wire = to_wire;
if (!check_dups) {
continue;
}
knot_rrset_t *rr = entry->rr;
for (size_t j = 0; j < array->len; ++j) {
ranked_rr_array_entry_t *stashed = array->at[j];
if (stashed->qry_uid == qry_uid) {
continue;
}
if (stashed->rr->rclass != rr->rclass ||
stashed->rr->type != rr->type) {
continue;
}
bool is_equal = knot_rrset_equal(rr, stashed->rr,
KNOT_RRSET_COMPARE_WHOLE);
if (is_equal && to_wire) {
stashed->to_wire = false;
}
}
}
return kr_ok();
}
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
static char *callprop(struct kr_module *module, const char *prop, const char *input, void *env)
{
if (!module || !prop) {
return NULL;
}
for (struct kr_prop *p = module->props; p && p->name; ++p) {
if (p->cb != NULL && strcmp(p->name, prop) == 0) {
return p->cb(env, module, input);
}
}
return NULL;
}
char *kr_module_call(struct kr_context *ctx, const char *module, const char *prop, const char *input)
{
if (!ctx || !ctx->modules || !module || !prop) {
return NULL;
}
module_array_t *mod_list = ctx->modules;
for (size_t i = 0; i < mod_list->len; ++i) {
struct kr_module *mod = mod_list->at[i];
if (strcmp(mod->name, module) == 0) {
return callprop(mod, prop, input, ctx);
}
}
return NULL;
void kr_rrset_print(const knot_rrset_t *rr, const char *prefix)
char rrtext[KNOT_DNAME_MAXLEN * 2] = {0};
knot_rrset_txt_dump(rr, rrtext, sizeof(rrtext), &KNOT_DUMP_STYLE_DEFAULT);
kr_log_verbose("%s%s", prefix, rrtext);
#else
size_t size = 4000;
char *rrtext = malloc(size);
knot_rrset_txt_dump(rr, &rrtext, &size, &KNOT_DUMP_STYLE_DEFAULT);
kr_log_verbose("%s%s", prefix, rrtext);
static void flags_to_str(char *dst, const knot_pkt_t *pkt, size_t maxlen)
{
int offset = 0;
int ret = 0;
struct {
uint8_t (*get) (const uint8_t *packet);
char name[3];
} flag[7] = {
{knot_wire_get_qr, "qr"},
{knot_wire_get_aa, "aa"},
{knot_wire_get_rd, "rd"},
{knot_wire_get_ra, "ra"},
{knot_wire_get_tc, "tc"},
{knot_wire_get_cd, "cd"},
{knot_wire_get_ad, "ad"}
};
for (int i = 0; i < 7; ++i) {
if (!flag[i].get(pkt->wire)) {
continue;
}
ret = snprintf(dst + offset, maxlen, "%s ", flag[i].name);
if (ret <= 0 || ret >= maxlen) {
dst[0] = 0;
return;
}
offset += ret;
maxlen -= offset;
}
dst[offset] = 0;
}
static void print_section_opt(const knot_rrset_t *rr, const uint8_t rcode)
{
uint8_t ercode = knot_edns_get_ext_rcode(rr);
uint16_t ext_rcode_id = knot_edns_whole_rcode(ercode, rcode);
const char *ext_rcode_str = "Unused";
const knot_lookup_t *ext_rcode;
if (ercode > 0) {
ext_rcode = knot_lookup_by_id(knot_rcode_names, ext_rcode_id);
if (ext_rcode != NULL) {
ext_rcode_str = ext_rcode->name;
} else {
ext_rcode_str = "Unknown";
}
}
kr_log_verbose(";; EDNS PSEUDOSECTION:\n;; "
"Version: %u; flags: %s; UDP size: %u B; ext-rcode: %s\n\n",
knot_edns_get_version(rr),
(knot_edns_do(rr) != 0) ? "do" : "",
knot_edns_get_payload(rr),
ext_rcode_str);
}
char *snames[] = {";; ANSWER SECTION",";; AUTHORITY SECTION",";; ADDITIONAL SECTION"};
char flags[32];
char qname[KNOT_DNAME_MAXLEN];
uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire);
uint8_t pkt_opcode = knot_wire_get_opcode(pkt->wire);
const char *rcode_str = "Unknown";
const char *opcode_str = "Unknown";
const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, pkt_rcode);
const knot_lookup_t *opcode = knot_lookup_by_id(knot_opcode_names, pkt_opcode);
uint16_t qry_id = knot_wire_get_id(pkt->wire);
if (rcode != NULL) {
rcode_str = rcode->name;
}
if (opcode != NULL) {
opcode_str = opcode->name;
}
flags_to_str(flags, pkt, sizeof(flags));
knot_dname_to_str(qname, knot_pkt_qname(pkt), KNOT_DNAME_MAXLEN);
knot_rrtype_to_string(knot_pkt_qtype(pkt), rrtype, sizeof(rrtype));
kr_log_verbose(";; ->>HEADER<<- opcode: %s; status: %s; id: %hu\n",
opcode_str, rcode_str, qry_id);
kr_log_verbose(";; Flags: %s QUERY: %hu; ANSWER: %hu; "
"AUTHORITY: %hu; ADDITIONAL: %hu\n\n",
flags,
knot_wire_get_qdcount(pkt->wire),
knot_wire_get_ancount(pkt->wire),
knot_wire_get_nscount(pkt->wire),
knot_wire_get_arcount(pkt->wire));
if (knot_pkt_has_edns(pkt)) {
print_section_opt(pkt->opt_rr,
knot_wire_get_rcode(pkt->wire));
}
kr_log_verbose(";; QUESTION SECTION\n%s\t\t%s\n\n", qname, rrtype);
for (knot_section_t i = KNOT_ANSWER; i <= KNOT_AUTHORITY; ++i) {
const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
if (sec->count == 0) {
continue;
}
kr_log_verbose("%s\n", snames[i - KNOT_ANSWER]);
for (unsigned k = 0; k < sec->count; ++k) {
const knot_rrset_t *rr = knot_pkt_rr(sec, k);
kr_rrset_print(rr, "");
const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_ADDITIONAL);
bool header_was_printed = false;
for (unsigned k = 0; k < sec->count; ++k) {
const knot_rrset_t *rr = knot_pkt_rr(sec, k);
if (rr->type == KNOT_RRTYPE_OPT) {
continue;
}
if (!header_was_printed) {
header_was_printed = true;
kr_log_verbose("%s\n", snames[KNOT_ADDITIONAL - KNOT_ANSWER]);
}
kr_rrset_print(rr, "");
}
kr_log_verbose("\n");
}
void kr_dname_print(const knot_dname_t *name, const char *prefix, const char *postfix)
{
char str[KNOT_DNAME_MAXLEN];
knot_dname_to_str(str, name, KNOT_DNAME_MAXLEN);
kr_log_verbose ("%s%s%s", prefix, str, postfix);
}
void kr_rrtype_print(const uint16_t rrtype, const char *prefix, const char *postfix)
{
char str[32];
knot_rrtype_to_string(rrtype, str, 32);
kr_log_verbose ("%s%s%s", prefix, str, postfix);