1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 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 128 129 130 131 132 133 134 135
|
/* Copyright (C) Knot Resolver contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*
* This module logs (query name, type) pairs which failed DNSSEC validation. */
#include <libknot/packet/pkt.h>
#include <libknot/dname.h>
#include <ccan/json/json.h>
#include <contrib/cleanup.h>
#include "daemon/engine.h"
#include "lib/layer.h"
#include "lib/generic/lru.h"
#ifdef LRU_REP_SIZE
#define FREQUENT_COUNT LRU_REP_SIZE /* Size of frequent tables */
#else
#define FREQUENT_COUNT 5000 /* Size of frequent tables */
#endif
/** @internal LRU hash of most frequent names. */
typedef lru_t(unsigned) namehash_t;
/** @internal Stats data structure. */
struct stat_data {
namehash_t *frequent;
};
static int consume(kr_layer_t *ctx, knot_pkt_t *pkt)
{
if (!(ctx->state & KR_STATE_FAIL)
|| !ctx->req
|| !ctx->req->current_query
|| !ctx->req->current_query->flags.DNSSEC_BOGUS
|| knot_wire_get_qdcount(pkt->wire) != 1)
return ctx->state;
auto_free char *qname_text = kr_dname_text(knot_pkt_qname(pkt));
auto_free char *qtype_text = kr_rrtype_text(knot_pkt_qtype(pkt));
kr_log_notice(DNSSEC, "validation failure: %s %s\n", qname_text, qtype_text);
/* log of most frequent bogus queries */
uint16_t type = knot_pkt_qtype(pkt);
char key[sizeof(type) + KNOT_DNAME_MAXLEN];
memcpy(key, &type, sizeof(type));
int key_len = knot_dname_to_wire((uint8_t *)key + sizeof(type), knot_pkt_qname(pkt), KNOT_DNAME_MAXLEN);
if (key_len >= 0) {
struct kr_module *module = ctx->api->data;
struct stat_data *data = module->data;
unsigned *count = lru_get_new(data->frequent, key, key_len+sizeof(type), NULL);
if (count)
*count += 1;
}
return ctx->state;
}
/** @internal Helper for dump_list: add a single namehash_t item to JSON. */
static enum lru_apply_do dump_value(const char *key, uint len, unsigned *val, void *baton)
{
uint16_t key_type = 0;
/* Extract query name, type and counter */
memcpy(&key_type, key, sizeof(key_type));
KR_DNAME_GET_STR(key_name, (uint8_t *)key + sizeof(key_type));
KR_RRTYPE_GET_STR(type_str, key_type);
/* Convert to JSON object */
JsonNode *json_val = json_mkobject();
json_append_member(json_val, "count", json_mknumber(*val));
json_append_member(json_val, "name", json_mkstring(key_name));
json_append_member(json_val, "type", json_mkstring(type_str));
json_append_element((JsonNode *)baton, json_val);
return LRU_APPLY_DO_NOTHING; // keep the item
}
/**
* List frequent names.
*
* Output: [{ count: <counter>, name: <qname>, type: <qtype>}, ... ]
*/
static char* dump_list(void *env, struct kr_module *module, const char *args, namehash_t *table)
{
if (!table) {
return NULL;
}
JsonNode *root = json_mkarray();
lru_apply(table, dump_value, root);
char *ret = json_encode(root);
json_delete(root);
return ret;
}
static char* dump_frequent(void *env, struct kr_module *module, const char *args)
{
struct stat_data *data = module->data;
return dump_list(env, module, args, data->frequent);
}
KR_EXPORT
int bogus_log_init(struct kr_module *module)
{
static kr_layer_api_t layer = {
.consume = &consume,
};
layer.data = module;
module->layer = &layer;
static const struct kr_prop props[] = {
{ &dump_frequent, "frequent", "List most frequent queries.", },
{ NULL, NULL, NULL }
};
module->props = props;
struct stat_data *data = calloc(1, sizeof(*data));
if (!data) {
return kr_error(ENOMEM);
}
module->data = data;
lru_create(&data->frequent, FREQUENT_COUNT, NULL, NULL);
return kr_ok();
}
KR_EXPORT
int bogus_log_deinit(struct kr_module *module)
{
struct stat_data *data = module->data;
if (data) {
lru_free(data->frequent);
free(data);
}
return kr_ok();
}
KR_MODULE_EXPORT(bogus_log)
|