summaryrefslogtreecommitdiffstats
path: root/modules/bogus_log/bogus_log.c
blob: 7b36187ef0fb438d75189569e523926341867f24 (plain)
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)