From: Vladimír Čunát Date: Wed, 28 May 2025 12:35:46 +0000 (+0200) Subject: lib/rules: add initial kr_rule_opts_t X-Git-Tag: v6.0.13~2^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a782e9c3;p=thirdparty%2Fknot-resolver.git lib/rules: add initial kr_rule_opts_t For now this relates to logging/dry-run of local-data rules. --- diff --git a/daemon/lua/kres-gen-33.lua b/daemon/lua/kres-gen-33.lua index 3e7313c4b..efe0d6856 100644 --- a/daemon/lua/kres-gen-33.lua +++ b/daemon/lua/kres-gen-33.lua @@ -203,6 +203,15 @@ struct kr_request_qsource_flags { _Bool xdp : 1; }; typedef unsigned long kr_rule_tags_t; +struct kr_rule_opts { + uint8_t score : 4; + _Bool log_ip : 1; + _Bool log_name : 1; + uint8_t log_level : 2; + uint8_t ede_code : 2; + uint8_t ede_sub : 3; +}; +typedef struct kr_rule_opts kr_rule_opts_t; struct kr_rule_zonefile_config { const char *filename; const char *input_str; @@ -212,6 +221,7 @@ struct kr_rule_zonefile_config { kr_rule_tags_t tags; const char *origin; uint32_t ttl; + kr_rule_opts_t opts; }; struct kr_rule_fwd_flags { _Bool is_auth : 1; @@ -253,6 +263,8 @@ struct kr_request { _Bool stale_accounted; _Bool ratelimited; uint8_t rank; + uint8_t rule_score_apply; + uint8_t rule_score_log; struct kr_rplan rplan; trace_log_f trace_log; trace_callback_f trace_finish; @@ -360,6 +372,7 @@ kr_layer_t kr_layer_t_static; _Bool kr_dbg_assertion_abort; int kr_dbg_assertion_fork; const uint32_t KR_RULE_TTL_DEFAULT; +const kr_rule_opts_t KR_RULE_OPTS_DEFAULT; typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type, const struct kr_query *qry); @@ -510,11 +523,11 @@ int kr_rules_reset(void); int kr_view_insert_action(const char *, const char *, kr_proto_set, const char *); int kr_view_select_action(const struct kr_request *, knot_db_val_t *); int kr_rule_tag_add(const char *, kr_rule_tags_t *); -int kr_rule_local_subtree(const knot_dname_t *, enum kr_rule_sub_t, uint32_t, kr_rule_tags_t); +int kr_rule_local_subtree(const knot_dname_t *, enum kr_rule_sub_t, uint32_t, kr_rule_tags_t, kr_rule_opts_t); int kr_rule_zonefile(const struct kr_rule_zonefile_config *); int kr_rule_forward(const knot_dname_t *, kr_rule_fwd_flags_t, const struct sockaddr **); -int kr_rule_local_address(const char *, const char *, _Bool, uint32_t, kr_rule_tags_t); -int kr_rule_local_hosts(const char *, _Bool, uint32_t, kr_rule_tags_t); +int kr_rule_local_address(const char *, const char *, _Bool, uint32_t, kr_rule_tags_t, kr_rule_opts_t); +int kr_rule_local_hosts(const char *, _Bool, uint32_t, kr_rule_tags_t, kr_rule_opts_t); struct tls_credentials; typedef struct { int sock_type; diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index 69011adf1..a5bc6f9a4 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -137,6 +137,8 @@ ${CDEFS} ${LIBKRES} types <<-EOF struct kr_rplan struct kr_request_qsource_flags kr_rule_tags_t + struct kr_rule_opts + typedef kr_rule_opts_t struct kr_rule_zonefile_config struct kr_rule_fwd_flags typedef kr_rule_fwd_flags_t @@ -169,6 +171,7 @@ ${CDEFS} ${LIBKRES} variables <<-EOF kr_dbg_assertion_abort kr_dbg_assertion_fork KR_RULE_TTL_DEFAULT + KR_RULE_OPTS_DEFAULT EOF printf " diff --git a/daemon/main.c b/daemon/main.c index 613977580..3925a15c1 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -76,7 +76,7 @@ KR_EXPORT const char *malloc_conf = "narenas:1"; KR_EXPORT void kr_misc_unused(void) { kr_rule_zonefile(NULL); - kr_rule_local_address(NULL, NULL, false, 0, 0); + kr_rule_local_address(NULL, NULL, false, 0, 0, KR_RULE_OPTS_DEFAULT); } struct args the_args_value; /** Static allocation for the_args singleton. */ diff --git a/daemon/worker.c b/daemon/worker.c index 6f6cda584..16191a8e0 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -385,6 +385,9 @@ static struct request_ctx *request_create(struct session2 *session, array_init(req->selection_context.forwarding_targets); array_reserve_mm(req->selection_context.forwarding_targets, 1, kr_memreserve, &req->pool); + req->rule_score_log = KR_RULE_SCORE_LOG; + req->rule_score_apply = KR_RULE_SCORE_APPLY; + the_worker->stats.rconcurrent += 1; return ctx; diff --git a/lib/cache/impl.h b/lib/cache/impl.h index 9b5cb2f45..87ff6cd4b 100644 --- a/lib/cache/impl.h +++ b/lib/cache/impl.h @@ -332,6 +332,18 @@ void rdataset_dematerialize(const knot_rdataset_t *rds, uint8_t * restrict data) * Return the number of bytes consumed or an error code. */ int rdataset_materialize(knot_rdataset_t * restrict rds, const uint8_t * const data, const uint8_t *data_bound, knot_mm_t *pool); +static inline int rdataset_materialize_val(knot_rdataset_t * restrict rds, + knot_db_val_t *val, knot_mm_t *pool) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-arith" + int ret = rdataset_materialize(rds, val->data, val->data + val->len, pool); + if (unlikely(ret < 0)) return ret; + val->data += ret; + val->len -= ret; + return ret; +#pragma GCC diagnostic pop +} /** Partially constructed answer when gathering RRsets from cache. */ diff --git a/lib/resolve.h b/lib/resolve.h index 51d47925a..8aad3bbae 100644 --- a/lib/resolve.h +++ b/lib/resolve.h @@ -280,6 +280,9 @@ struct kr_request { */ uint8_t rank; + uint8_t rule_score_apply; + uint8_t rule_score_log; + struct kr_rplan rplan; trace_log_f trace_log; /**< Logging tracepoint */ trace_callback_f trace_finish; /**< Request finish tracepoint */ diff --git a/lib/rules/api.c b/lib/rules/api.c index 53ebbf7e8..8078de715 100644 --- a/lib/rules/api.c +++ b/lib/rules/api.c @@ -9,6 +9,7 @@ #include + struct kr_rules *the_rules = NULL; /* The default TTL value is a compromise and probably of little practical impact. @@ -18,6 +19,8 @@ struct kr_rules *the_rules = NULL; */ const uint32_t KR_RULE_TTL_DEFAULT = 300; +const kr_rule_opts_t KR_RULE_OPTS_DEFAULT = { .score = KR_RULE_SCORE_DEFAULT, /*and zeros*/ }; + /* DB key-space summary - "\0" starts special keys like "\0rulesets" or "\0stamp" @@ -42,8 +45,17 @@ static const uint8_t KEY_ZONELIKE_A [1] = "a"; static const uint8_t KEY_VIEW_SRC4[1] = "4"; static const uint8_t KEY_VIEW_SRC6[1] = "6"; + +/// Returns for functions below: RET_ANSWERED, RET_CONTINUE, negative error codes for bugs +// FIXME: doc-comment for kr_rule_local_data_answer(), etc. +enum ret_codes_ { + RET_CONT_CACHE = 0, + RET_ANSWERED = 1, + RET_CONTINUE, +}; + static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t type, - const uint8_t *data, const uint8_t *data_bound); + knot_db_val_t *val); static int answer_zla_empty(val_zla_type_t type, struct kr_query *qry, knot_pkt_t *pkt, knot_db_val_t zla_lf, uint32_t ttl); static int answer_zla_dname(val_zla_type_t type, struct kr_query *qry, knot_pkt_t *pkt, @@ -51,7 +63,8 @@ static int answer_zla_dname(val_zla_type_t type, struct kr_query *qry, knot_pkt_ static int answer_zla_redirect(struct kr_query *qry, knot_pkt_t *pkt, const char *ruleset_name, knot_db_val_t zla_lf, uint32_t ttl); static int rule_local_subtree(const knot_dname_t *apex, enum kr_rule_sub_t type, - const knot_dname_t *target, uint32_t ttl, kr_rule_tags_t tags); + const knot_dname_t *target, uint32_t ttl, + kr_rule_tags_t tags, kr_rule_opts_t opts); // LATER: doing tag_names_default() and kr_rule_tag_add() inside a RW transaction would be better. static int tag_names_default(void) @@ -230,6 +243,48 @@ static bool kr_rule_consume_tags(knot_db_val_t *val, const struct kr_request *re +/// Log that we apply a local-data rule (if desired) +// TODO: we might parametrize by some log string that expresses e.g. the type of rule +static void log_rule(kr_rule_opts_t opts, const struct kr_query *qry) +{ + const struct kr_request *req = qry->request; + const int level = map_log_level(opts.log_level); + bool do_log = opts.score >= req->rule_score_log + && (kr_log_is_debug(RULES, req) || KR_LOG_LEVEL_IS(level)); + if (!do_log) + return; + bool applied = opts.score >= req->rule_score_apply; + + //// Let's construct the log message, piece by piece in `s**` variables. + const char * s1a = "=> local data ", + *s1b = applied ? "applied" : "dry-run"; + + const char *s2a = ""; + char s2b[INET6_ADDRSTRLEN + 1] = ""; + if (opts.log_ip) { + s2a = ", user: "; + const struct sockaddr *addr = req->qsource.addr; + if (addr) { + bool ok = inet_ntop(addr->sa_family, kr_inaddr(addr), s2b, sizeof(s2b)); + kr_assert(ok); + } else { + strcpy(s2b, "internal"); + } + } + + const char *s3a = ""; + char s3b[KR_DNAME_STR_MAXLEN] = ""; + if (opts.log_name) { + s3a = ", name: "; + knot_dname_to_str(s3b, qry->sname, sizeof(s3b)); + s3b[sizeof(s3b) - 1] = 0; + } + + kr_log_fmt(LOG_GRP_RULES, level, SD_JOURNAL_METADATA, + "[%-6s] %s%s%s%s%s%s\n", + LOG_GRP_RULES_TAG, s1a, s1b, s2a, s2b, s3a, s3b); +} + /** Add name lookup format on the fixed end-position inside key_data. * * Note: key_data[KEY_DNAME_END_OFFSET] = '\0' even though @@ -283,10 +338,6 @@ static size_t key_common_subtree(knot_db_val_t k1, knot_db_val_t k2, size_t lf_s int rule_local_data_answer(struct kr_query *qry, knot_pkt_t *pkt) { - // return shorthands; see doc-comment for kr_rule_local_data_answer() - static const int RET_CONT_CACHE = 0; - static const int RET_ANSWERED = 1; - kr_require(the_rules); // TODO: implement EDE codes somehow @@ -348,9 +399,9 @@ int rule_local_data_answer(struct kr_query *qry, knot_pkt_t *pkt) continue; // We found a rule that applies to the dname+rrtype+req. - ret = answer_exact_match(qry, pkt, types[i], - val.data, val.data + val.len); - return ret ? kr_error(ret) : RET_ANSWERED; + ret = answer_exact_match(qry, pkt, types[i], &val); + if (ret != RET_CONTINUE) + return ret; } if (kr_fails_assert(ret == 0 || ret == -ENOENT)) return kr_error(ret); @@ -418,7 +469,16 @@ int rule_local_data_answer(struct kr_query *qry, knot_pkt_t *pkt) qry->data_src.rule_depth = knot_dname_labels(apex_name, NULL); return RET_CONT_CACHE; } - // The other types optionally specify TTL. + + // Process opts. + kr_rule_opts_t opts; + if (deserialize_fails_assert(&val, &opts)) + return kr_error(EILSEQ); + log_rule(opts, qry); + if (opts.score < qry->request->rule_score_apply) + goto shorten; // continue looking for rules + + // The non-forward types optionally specify TTL. uint32_t ttl = KR_RULE_TTL_DEFAULT; if (val.len >= sizeof(ttl)) // allow omitting -> can't kr_assert deserialize_fails_assert(&val, &ttl); @@ -465,14 +525,19 @@ static const uint8_t soa_rdata[] = "\x09localhost\0\6nobody\7invalid\0" } while (false) static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t type, - const uint8_t *data, const uint8_t *data_bound) + knot_db_val_t *val) { - /* Extract ttl from data. */ + /* Process opts. */ + kr_rule_opts_t opts; + if (deserialize_fails_assert(val, &opts)) + return kr_error(EILSEQ); + log_rule(opts, qry); + if (opts.score < qry->request->rule_score_apply) + return RET_CONTINUE; + uint32_t ttl; - if (kr_fails_assert(data + sizeof(ttl) <= data_bound)) + if (deserialize_fails_assert(val, &ttl)) return kr_error(EILSEQ); - memcpy(&ttl, data, sizeof(ttl)); - data += sizeof(ttl); /* Start constructing the (pseudo-)packet. */ int ret = pkt_renew(pkt, qry->sname, qry->stype); @@ -485,21 +550,18 @@ static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t ty arrset.set.rr = knot_rrset_new(qry->sname, type, KNOT_CLASS_IN, ttl, &pkt->mm); if (kr_fails_assert(arrset.set.rr)) return kr_error(ENOMEM); - ret = rdataset_materialize(&arrset.set.rr->rrs, data, data_bound, &pkt->mm); + ret = rdataset_materialize_val(&arrset.set.rr->rrs, val, &pkt->mm); CHECK_RET(ret); - data += ret; arrset.set.rank = KR_RANK_SECURE | KR_RANK_AUTH; // local data has high trust arrset.set.expiring = false; /* Materialize the RRSIG RRset for the answer in (pseudo-)packet. * (There will almost never be any RRSIG.) */ - ret = rdataset_materialize(&arrset.sig_rds, data, data_bound, &pkt->mm); + ret = rdataset_materialize_val(&arrset.sig_rds, val, &pkt->mm); CHECK_RET(ret); - data += ret; /* Sanity check: we consumed exactly all data. */ - const int unused_bytes = data_bound - data; - if (kr_fails_assert(unused_bytes == 0)) { - kr_log_error(RULES, "ERROR: unused bytes: %d\n", unused_bytes); + if (kr_fails_assert(val->len == 0)) { + kr_log_error(RULES, "ERROR: unused bytes: %zu\n", val->len); return kr_error(EILSEQ); } @@ -525,10 +587,7 @@ static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t ty qry->flags.EXPIRING = false; qry->flags.CACHED = true; qry->flags.NO_MINIMIZE = true; - - VERBOSE_MSG(qry, "=> satisfied by local data (%s)\n", - is_nodata ? "no data" : "positive"); - return kr_ok(); + return RET_ANSWERED; } knot_db_val_t local_data_key(const knot_rrset_t *rrs, uint8_t key_data[KEY_MAXLEN], @@ -551,24 +610,26 @@ knot_db_val_t local_data_key(const knot_rrset_t *rrs, uint8_t key_data[KEY_MAXLE return key; } int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds, - kr_rule_tags_t tags) + kr_rule_tags_t tags, kr_rule_opts_t opts) { ENSURE_the_rules; // Construct the DB key. uint8_t key_data[KEY_MAXLEN]; knot_db_val_t key = local_data_key(rrs, key_data, RULESET_DEFAULT); - return local_data_ins(key, rrs, sig_rds, tags); + return local_data_ins(key, rrs, sig_rds, tags, opts); } -int local_data_ins(knot_db_val_t key, const knot_rrset_t *rrs, - const knot_rdataset_t *sig_rds, kr_rule_tags_t tags) +int local_data_ins(knot_db_val_t key, const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds, + kr_rule_tags_t tags, kr_rule_opts_t opts) { // Prepare the data into a temporary buffer. const int rr_ssize = rdataset_dematerialize_size(&rrs->rrs); - const int val_len = sizeof(tags) + sizeof(rrs->ttl) + rr_ssize + const int val_len = sizeof(tags) + sizeof(opts) + sizeof(rrs->ttl) + rr_ssize + rdataset_dematerialize_size(sig_rds); uint8_t buf[val_len], *data = buf; memcpy(data, &tags, sizeof(tags)); data += sizeof(tags); + memcpy(data, &opts, sizeof(opts)); + data += sizeof(opts); memcpy(data, &rrs->ttl, sizeof(rrs->ttl)); data += sizeof(rrs->ttl); rdataset_dematerialize(&rrs->rrs, data); @@ -589,7 +650,7 @@ int local_data_ins(knot_db_val_t key, const knot_rrset_t *rrs, if (kr_fails_assert(rrs->rrs.count)) return kr_error(EINVAL); return rule_local_subtree(rrs->owner, KR_RULE_SUB_DNAME, - knot_dname_target(rrs->rrs.rdata), rrs->ttl, tags); + knot_dname_target(rrs->rrs.rdata), rrs->ttl, tags, opts); } int kr_rule_local_data_del(const knot_rrset_t *rrs, kr_rule_tags_t tags) { @@ -598,7 +659,7 @@ int kr_rule_local_data_del(const knot_rrset_t *rrs, kr_rule_tags_t tags) knot_db_val_t key = local_data_key(rrs, key_data, RULESET_DEFAULT); return ruledb_op(remove, &key, 1); } -int kr_rule_local_data_merge(const knot_rrset_t *rrs, const kr_rule_tags_t tags) +int kr_rule_local_data_merge(const knot_rrset_t *rrs, const kr_rule_tags_t tags, kr_rule_opts_t opts) { ENSURE_the_rules; // Construct the DB key. @@ -617,6 +678,9 @@ int kr_rule_local_data_merge(const knot_rrset_t *rrs, const kr_rule_tags_t tags) kr_rule_tags_t tags_old; if (deserialize_fails_assert(&val, &tags_old) || tags_old != tags) goto fallback; + kr_rule_opts_t opts_old; + if (deserialize_fails_assert(&val, &opts_old)) + goto fallback; // merge TTLs uint32_t ttl; if (deserialize_fails_assert(&val, &ttl)) @@ -640,11 +704,11 @@ int kr_rule_local_data_merge(const knot_rrset_t *rrs, const kr_rule_tags_t tags) return kr_error(ret); } // everything is ready to insert the merged RRset - ret = local_data_ins(key, &rrs_new, NULL, tags); + ret = local_data_ins(key, &rrs_new, NULL, tags, opts); mm_ctx_delete(mm); return ret; fallback: - return local_data_ins(key, rrs, NULL, tags); + return local_data_ins(key, rrs, NULL, tags, opts); } /** Empty or NXDOMAIN or NODATA. Returning kr_error(EAGAIN) means the rule didn't match. */ @@ -809,9 +873,11 @@ static int answer_zla_redirect(struct kr_query *qry, knot_pkt_t *pkt, const char knot_db_val_t val; // Multiple variants are possible, with different tags. for (ret = ruledb_op(it_first, &key, &val); ret == 0; ret = ruledb_op(it_next, &val)) { - if (kr_rule_consume_tags(&val, qry->request)) - return answer_exact_match(qry, pkt, qry->stype, - val.data, val.data + val.len); + if (kr_rule_consume_tags(&val, qry->request)) { + int ret2 = answer_exact_match(qry, pkt, qry->stype, &val); + if (ret2 != RET_CONTINUE) + return ret2; + } } if (ret && ret != -ENOENT) return ret; @@ -852,9 +918,9 @@ nodata: // Want NODATA answer (or NOERROR if it hits apex SOA). } int kr_rule_local_subtree(const knot_dname_t *apex, enum kr_rule_sub_t type, - uint32_t ttl, kr_rule_tags_t tags) + uint32_t ttl, kr_rule_tags_t tags, kr_rule_opts_t opts) { - return rule_local_subtree(apex, type, NULL, ttl, tags); + return rule_local_subtree(apex, type, NULL, ttl, tags, opts); } knot_db_val_t zla_key(const knot_dname_t *apex, uint8_t key_data[KEY_MAXLEN]) { @@ -872,7 +938,8 @@ knot_db_val_t zla_key(const knot_dname_t *apex, uint8_t key_data[KEY_MAXLEN]) return key; } static int rule_local_subtree(const knot_dname_t *apex, enum kr_rule_sub_t type, - const knot_dname_t *target, uint32_t ttl, kr_rule_tags_t tags) + const knot_dname_t *target, uint32_t ttl, + kr_rule_tags_t tags, kr_rule_opts_t opts) { // type-check const bool has_target = (type == KR_RULE_SUB_DNAME); @@ -900,13 +967,15 @@ static int rule_local_subtree(const knot_dname_t *apex, enum kr_rule_sub_t type, // Prepare the data into a temporary buffer. const int target_len = has_target ? knot_dname_size(target) : 0; const bool has_ttl = ttl != KR_RULE_TTL_DEFAULT || has_target; - const int val_len = sizeof(tags) + sizeof(ztype) + (has_ttl ? sizeof(ttl) : 0) - + target_len; + const int val_len = sizeof(tags) + sizeof(ztype) + sizeof(opts) + + (has_ttl ? sizeof(ttl) : 0) + target_len; uint8_t buf[val_len], *data = buf; memcpy(data, &tags, sizeof(tags)); data += sizeof(tags); memcpy(data, &ztype, sizeof(ztype)); data += sizeof(ztype); + memcpy(data, &opts, sizeof(opts)); + data += sizeof(opts); if (has_ttl) { memcpy(data, &ttl, sizeof(ttl)); data += sizeof(ttl); diff --git a/lib/rules/api.h b/lib/rules/api.h index c7d1dd297..b2e11dd11 100644 --- a/lib/rules/api.h +++ b/lib/rules/api.h @@ -9,6 +9,8 @@ struct kr_query; struct kr_request; struct knot_pkt; struct sockaddr; +#include +#include #include /// Storage for a tag-set. It's a bitmap, so 64 tags are supported now. @@ -17,6 +19,46 @@ typedef uint64_t kr_rule_tags_t; /// Tags "capacity", i.e. numbered from 0 to _CAP - 1. #define KR_RULE_TAGS_CAP (sizeof(kr_rule_tags_t) * 8) +/// Extra options for a rule (not for forwarding) +struct kr_rule_opts { + /// Degree of severity for the rule; FIXME: granularity, defaults, etc. + uint8_t score : 4; + + bool log_ip : 1, log_name : 1; + // +maybe log rule/QNAME/something + /// Log level: 0 = debug, 1 = info, ... + uint8_t log_level : 2; + + /** Maybe 2 bits: (unset), blocked, censored, filtered + https://www.rfc-editor.org/rfc/rfc8914.html#name-extended-dns-error-code-15- + */ + uint8_t ede_code : 2; + /** Maybe 3 bits: (unset), Malware, Phishing, ... from + https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-structured-dns-error#name-new-registry-for-dns-sub-er + */ + uint8_t ede_sub : 3; +}; +typedef struct kr_rule_opts kr_rule_opts_t; +static_assert(sizeof(kr_rule_opts_t) == 2, "kr_rule_opts_t size changed unexpectedly"); +/// Default opts; in particular used for the RFC-mandated special-use names +KR_EXPORT extern const kr_rule_opts_t KR_RULE_OPTS_DEFAULT; +enum { // Default minimal score of a rule to log/apply it. + KR_RULE_SCORE_LOG = 3, + KR_RULE_SCORE_APPLY = 6, + KR_RULE_SCORE_DEFAULT = 10, +}; + +static inline int map_log_level(uint8_t ll) +{ + switch (ll) { + case 0: return LOG_DEBUG; + case 1: return LOG_INFO; + case 2: return LOG_NOTICE; + case 3: return LOG_WARNING; + } + return LOG_DEBUG; // shouldn't happen +} + /** Open the rule DB. * * You can call this to override the path or size (NULL/0 -> default) @@ -56,7 +98,7 @@ int kr_rules_reset(void); /** Try answering the query from local data; WIP: otherwise determine data source overrides. * - * \return kr_error() on errors, >0 if answered, 0 otherwise (also when forwarding) + * \return kr_error() on errors, >0 if answered FIXME, 0 otherwise (also when forwarding) * * FIXME: we probably want to ensure AA flags in answer as appropriate. * Perhaps approach it like AD? Tweak flags in ranked_rr_array_entry @@ -98,7 +140,7 @@ const uint32_t KR_RULE_TTL_DEFAULT; * Special NODATA case: use a CNAME type with zero records (TTL matters). */ KR_EXPORT int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds, - kr_rule_tags_t tags); + kr_rule_tags_t tags, kr_rule_opts_t opts); /** Merge RRs into a local data rule. * * - FIXME: with multiple tags variants for the same name-type pair, @@ -106,9 +148,10 @@ int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_r * - RRSIGs get dropped, if any were attached. * - We assume that this is called with a RW transaction open already, * which is always true in normal usage (long RW txn covering whole config). + * - TODO: what if opts don't match? */ KR_EXPORT -int kr_rule_local_data_merge(const knot_rrset_t *rrs, kr_rule_tags_t tags); +int kr_rule_local_data_merge(const knot_rrset_t *rrs, kr_rule_tags_t tags, kr_rule_opts_t opts); /** Add a name-address pair into rules. * @@ -117,8 +160,8 @@ int kr_rule_local_data_merge(const knot_rrset_t *rrs, kr_rule_tags_t tags); * - NODATA is optionally inserted */ KR_EXPORT -int kr_rule_local_address(const char *name, const char *addr, - bool use_nodata, uint32_t ttl, kr_rule_tags_t tags); +int kr_rule_local_address(const char *name, const char *addr, bool use_nodata, + uint32_t ttl, kr_rule_tags_t tags, kr_rule_opts_t opts); /** For a given name, remove one address ##or all of them (if == NULL). * @@ -134,7 +177,8 @@ int kr_rule_local_address_del(const char *name, const char *addr, * Same as kr_rule_data_address() but from a file. */ KR_EXPORT -int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, kr_rule_tags_t tags); +int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, + kr_rule_tags_t tags, kr_rule_opts_t opts); /** Remove a local data rule. * @@ -167,7 +211,7 @@ enum kr_rule_sub_t { */ KR_EXPORT int kr_rule_local_subtree(const knot_dname_t *apex, enum kr_rule_sub_t type, - uint32_t ttl, kr_rule_tags_t tags); + uint32_t ttl, kr_rule_tags_t tags, kr_rule_opts_t opts); /** Insert a view action into the default ruleset. * @@ -209,6 +253,7 @@ struct kr_rule_zonefile_config { kr_rule_tags_t tags; /// tag-set for the generated rule const char *origin; /// NULL or zone origin if known uint32_t ttl; /// default TTL + kr_rule_opts_t opts; /// options for these rules }; /** Load rules from some zonefile format, e.g. RPZ. Code in ./zonefile.c */ KR_EXPORT diff --git a/lib/rules/defaults.c b/lib/rules/defaults.c index bd21fa109..3cf5eb8ee 100644 --- a/lib/rules/defaults.c +++ b/lib/rules/defaults.c @@ -141,7 +141,7 @@ int rules_defaults_insert(void) const knot_dname_t *dname = knot_dname_from_str(name_buf, names[i], sizeof(name_buf)); int ret = kr_rule_local_subtree(dname, KR_RULE_SUB_EMPTY, - TTL, KR_RULE_TAGS_ALL); + TTL, KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); CHECK_RET(ret); /* The double conversion is perhaps a bit wasteful, but it should be rare. */ /* LATER: add extra info with explanation? policy module had an ADDITIONAL @@ -153,7 +153,7 @@ int rules_defaults_insert(void) knot_dname_t localhost_dname[] = "\x09localhost\0"; { // forward localhost int ret = kr_rule_local_subtree(localhost_dname, KR_RULE_SUB_REDIRECT, - TTL, KR_RULE_TAGS_ALL); + TTL, KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); CHECK_RET(ret); knot_rrset_t rr = { @@ -165,7 +165,8 @@ int rules_defaults_insert(void) }; rr.type = KNOT_RRTYPE_A; ret = knot_rrset_add_rdata(&rr, (const uint8_t *)"\x7f\0\0\1", 4, NULL); - if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL); + if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, + KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); knot_rdataset_clear(&rr.rrs, NULL); CHECK_RET(ret); @@ -173,13 +174,15 @@ int rules_defaults_insert(void) ret = knot_rrset_add_rdata(&rr, (const uint8_t *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 16, NULL); - if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL); + if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, + KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); knot_rdataset_clear(&rr.rrs, NULL); CHECK_RET(ret); rr.type = KNOT_RRTYPE_NS; ret = knot_rrset_add_rdata(&rr, localhost_dname, 1+9+1, NULL); - if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL); + if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, + KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); knot_rdataset_clear(&rr.rrs, NULL); CHECK_RET(ret); } @@ -194,18 +197,21 @@ int rules_defaults_insert(void) .additional = NULL, }; int ret = knot_rrset_add_rdata(&rr, localhost_dname, 1+9+1, NULL); - if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL); + if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, + KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); knot_dname_t name_buf[KNOT_DNAME_MAXLEN]; rr.owner = knot_dname_from_str(name_buf, "1.0.0.127.in-addr.arpa.", sizeof(name_buf)); - if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL); + if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, + KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); rr.owner = knot_dname_from_str(name_buf, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", sizeof(name_buf)); - if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL); + if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, + KR_RULE_TAGS_ALL, KR_RULE_OPTS_DEFAULT); knot_rdataset_clear(&rr.rrs, NULL); CHECK_RET(ret); diff --git a/lib/rules/impl.h b/lib/rules/impl.h index 1a2ee4dda..bf82b581a 100644 --- a/lib/rules/impl.h +++ b/lib/rules/impl.h @@ -34,8 +34,8 @@ extern struct kr_rules *the_rules; knot_db_val_t local_data_key(const knot_rrset_t *rrs, uint8_t key_data[KEY_MAXLEN], const char *ruleset_name); /** Same as kr_rule_local_data_ins() but with precomputed `key`. */ -int local_data_ins(knot_db_val_t key, const knot_rrset_t *rrs, - const knot_rdataset_t *sig_rds, kr_rule_tags_t tags); +int local_data_ins(knot_db_val_t key, const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds, + kr_rule_tags_t tags, kr_rule_opts_t opts); /** Construct key for a zone-like-apex entry. It's stored in `key_data`. */ knot_db_val_t zla_key(const knot_dname_t *apex, uint8_t key_data[KEY_MAXLEN]); diff --git a/lib/rules/local-addr.c b/lib/rules/local-addr.c index cd5d456b4..63680bbf5 100644 --- a/lib/rules/local-addr.c +++ b/lib/rules/local-addr.c @@ -22,7 +22,7 @@ static int parse_addr_str(union kr_sockaddr *sa, const char *addr) } static int add_pair(const char *name, const char *addr, - bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) + bool use_nodata, uint32_t ttl, kr_rule_tags_t tags, kr_rule_opts_t opts) { /* Build key */ knot_dname_t key[KNOT_DNAME_MAXLEN]; @@ -45,13 +45,13 @@ static int add_pair(const char *name, const char *addr, } else { ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip4.sin_addr, 4, NULL); } - if (!ret) ret = kr_rule_local_data_merge(&rrs, tags); + if (!ret) ret = kr_rule_local_data_merge(&rrs, tags, opts); if (!ret && use_nodata) { rrs.type = KNOT_RRTYPE_CNAME; rrs.rrs.count = 0; rrs.rrs.size = 0; // no point in the _merge() variant here - ret = kr_rule_local_data_ins(&rrs, NULL, tags); + ret = kr_rule_local_data_ins(&rrs, NULL, tags, opts); } knot_rdataset_clear(&rrs.rrs, NULL); @@ -105,7 +105,7 @@ static const knot_dname_t * addr2reverse(const char *addr) } static int add_reverse_pair(const char *name, const char *addr, - bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) + bool use_nodata, uint32_t ttl, kr_rule_tags_t tags, kr_rule_opts_t opts) { const knot_dname_t *key = addr2reverse(addr); if (!key) @@ -120,18 +120,18 @@ static int add_reverse_pair(const char *name, const char *addr, if (!ret) { // We use _merge(). Using multiple PTR RRs is not recommended generally, // but here it seems better than choosing any "arbitrarily". - ret = kr_rule_local_data_merge(&rrs, tags); + ret = kr_rule_local_data_merge(&rrs, tags, opts); knot_rdataset_clear(&rrs.rrs, NULL); } return ret; } -int kr_rule_local_address(const char *name, const char *addr, - bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) +int kr_rule_local_address(const char *name, const char *addr, bool use_nodata, + uint32_t ttl, kr_rule_tags_t tags, kr_rule_opts_t opts) { - int ret = add_reverse_pair(name, addr, use_nodata, ttl, tags); + int ret = add_reverse_pair(name, addr, use_nodata, ttl, tags, opts); if (ret) return ret; - return add_pair(name, addr, use_nodata, ttl, tags); + return add_pair(name, addr, use_nodata, ttl, tags, opts); } int kr_rule_local_address_del(const char *name, const char *addr, @@ -177,7 +177,8 @@ int kr_rule_local_address_del(const char *name, const char *addr, return ret < 0 ? ret : kr_ok(); } -int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) +int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, + kr_rule_tags_t tags, kr_rule_opts_t opts) { auto_fclose FILE *fp = fopen(path, "r"); if (fp == NULL) { @@ -214,14 +215,14 @@ int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, kr_rule } const char *name_tok; while ((name_tok = strtok_r(NULL, " \t\n", &saveptr)) != NULL) { - ret = add_pair(name_tok, addr, use_nodata, ttl, tags); + ret = add_pair(name_tok, addr, use_nodata, ttl, tags, opts); if (ret) goto error; count += 1; } - ret = add_pair(canonical_name, addr, use_nodata, ttl, tags); + ret = add_pair(canonical_name, addr, use_nodata, ttl, tags, opts); if (!ret) // PTR only to the canonical name - ret = add_reverse_pair(canonical_name, addr, use_nodata, ttl, tags); + ret = add_reverse_pair(canonical_name, addr, use_nodata, ttl, tags, opts); if (ret) goto error; count += 1; diff --git a/lib/rules/zonefile.c b/lib/rules/zonefile.c index 773ca937d..44df2a819 100644 --- a/lib/rules/zonefile.c +++ b/lib/rules/zonefile.c @@ -61,7 +61,7 @@ static int rr_trie2rule(const char *key_data, uint32_t key_len, trie_val_t *rr_p const knot_db_val_t key = { .data = (void *)key_data, .len = key_len }; const knot_rrset_t *rr = *rr_p; const struct kr_rule_zonefile_config *c = config; - return local_data_ins(key, rr, NULL, c->tags); + return local_data_ins(key, rr, NULL, c->tags, c->opts); //TODO: check error logging path here (LMDB) } @@ -87,19 +87,20 @@ static void cname_scan2rule(zs_scanner_t *s) // Exact RPZ semantics would be hard here, it makes more sense // to apply also to a subtree, and corresponding wildcard rule // usually accompanies this rule anyway. - ret = kr_rule_local_subtree(apex, KR_RULE_SUB_NXDOMAIN, s->r_ttl, c->tags); + ret = kr_rule_local_subtree(apex, KR_RULE_SUB_NXDOMAIN, + s->r_ttl, c->tags, c->opts); } else if (knot_dname_is_wildcard(s->r_data) && s->r_data[2] == 0) { // "CNAME *." -> NODATA knot_dname_t *apex = s->r_owner; if (knot_dname_is_wildcard(apex)) { apex += 2; ret = kr_rule_local_subtree(apex, KR_RULE_SUB_NODATA, - s->r_ttl, c->tags); + s->r_ttl, c->tags, c->opts); } else { // using special kr_rule_ semantics of empty CNAME RRset knot_rrset_t rrs; knot_rrset_init(&rrs, apex, KNOT_RRTYPE_CNAME, KNOT_CLASS_IN, s->r_ttl); - ret = kr_rule_local_data_ins(&rrs, NULL, c->tags); + ret = kr_rule_local_data_ins(&rrs, NULL, c->tags, c->opts); } } else { knot_dname_t *target = s->r_owner; @@ -107,7 +108,7 @@ static void cname_scan2rule(zs_scanner_t *s) knot_rrset_init(&rrs, target, KNOT_RRTYPE_CNAME, KNOT_CLASS_IN, s->r_ttl); // TODO: implement wildcard expansion for target ret = knot_rrset_add_rdata(&rrs, s->r_data, s->r_data_length, NULL); - if (!ret) ret = kr_rule_local_data_ins(&rrs, NULL, c->tags); + if (!ret) ret = kr_rule_local_data_ins(&rrs, NULL, c->tags, c->opts); knot_rdataset_clear(&rrs.rrs, NULL); } if (ret) diff --git a/modules/hints/hints.c b/modules/hints/hints.c index eaefaae4c..c7278e86a 100644 --- a/modules/hints/hints.c +++ b/modules/hints/hints.c @@ -32,6 +32,7 @@ struct hints_data { bool use_nodata; /**< See hint_use_nodata() description, exposed via lua. */ uint32_t ttl; /**< TTL used for the hints, exposed via lua. */ + kr_rule_opts_t opts; /**< Options used for local-data rules. Not overridable yet. */ }; /** Useful for returning from module properties. */ @@ -76,7 +77,8 @@ static char* hint_add_hosts(void *env, struct kr_module *module, const char *arg if (!args) args = "/etc/hosts"; const struct hints_data *data = module->data; - int err = kr_rule_local_hosts(args, data->use_nodata, data->ttl, KR_RULE_TAGS_ALL); + int err = kr_rule_local_hosts(args, data->use_nodata, data->ttl, + KR_RULE_TAGS_ALL, data->opts); return bool2jsonstr(err == kr_ok()); } @@ -102,7 +104,7 @@ static char* hint_set(void *env, struct kr_module *module, const char *args) *addr = '\0'; ++addr; ret = kr_rule_local_address(args_copy, addr, - data->use_nodata, data->ttl, KR_RULE_TAGS_ALL); + data->use_nodata, data->ttl, KR_RULE_TAGS_ALL, data->opts); } return bool2jsonstr(ret == 0); @@ -299,6 +301,7 @@ int hints_init(struct kr_module *module) return kr_error(ENOMEM); data->use_nodata = true; data->ttl = KR_RULE_TTL_DEFAULT; + data->opts = KR_RULE_OPTS_DEFAULT; module->data = data; return kr_ok(); @@ -329,7 +332,7 @@ int hints_config(struct kr_module *module, const char *conf) if (conf && conf[0]) { const struct hints_data *data = module->data; return kr_rule_local_hosts(conf, - data->use_nodata, data->ttl, KR_RULE_TAGS_ALL); + data->use_nodata, data->ttl, KR_RULE_TAGS_ALL, data->opts); } return kr_ok(); } diff --git a/python/knot_resolver/datamodel/templates/macros/local_data_macros.lua.j2 b/python/knot_resolver/datamodel/templates/macros/local_data_macros.lua.j2 index 58861f93f..5bfb45afd 100644 --- a/python/knot_resolver/datamodel/templates/macros/local_data_macros.lua.j2 +++ b/python/knot_resolver/datamodel/templates/macros/local_data_macros.lua.j2 @@ -28,7 +28,8 @@ hints.root_file('{{ file }}') {% macro kr_rule_local_address(name, address, nodata, ttl, tags=none) -%} assert(C.kr_rule_local_address('{{ name }}', '{{ address }}', - {{ boolean(nodata) }}, {{ local_data_ttl(ttl)}}, {{ policy_get_tagset(tags) }}) == 0) + {{ boolean(nodata) }}, {{ local_data_ttl(ttl)}}, {{ policy_get_tagset(tags) }}) == 0, + C.KR_RULE_OPTS_DEFAULT) {%- endmacro -%} @@ -43,7 +44,7 @@ assert(C.kr_rule_local_address('{{ name }}', '{{ address }}', {% macro kr_rule_local_hosts(file, nodata, ttl, tags=none) -%} assert(C.kr_rule_local_hosts('{{ file }}', {{ boolean(nodata) }}, - {{ local_data_ttl(ttl)}}, {{ policy_get_tagset(tags) }}) == 0) + {{ local_data_ttl(ttl)}}, {{ policy_get_tagset(tags) }}) == 0, C.KR_RULE_OPTS_DEFAULT) {%- endmacro %} @@ -60,6 +61,7 @@ assert(C.kr_rule_local_hosts('{{ file }}', {{ boolean(nodata) }}, {{ id }}.tags = {{ policy_get_tagset(tags) }} {{ id }}.nodata = {{ boolean(nodata) }} {{ id }}.is_rpz = {{ boolean(is_rpz) }} +{{ id }}.opts = C.KR_RULE_OPTS_DEFAULT {% if is_rpz -%} {{ id }}.filename = '{{ input_str }}' {% else %} @@ -73,7 +75,8 @@ assert(C.kr_rule_zonefile({{ id }})==0) {% macro kr_rule_local_subtree(name, type, ttl, tags=none) -%} assert(C.kr_rule_local_subtree(todname('{{ name }}'), - C.KR_RULE_SUB_{{ type.upper() }}, {{ local_data_ttl(ttl) }}, {{ policy_get_tagset(tags) }}) == 0) + C.KR_RULE_SUB_{{ type.upper() }}, {{ local_data_ttl(ttl) }}, {{ policy_get_tagset(tags) }}) == 0, + C.KR_RULE_OPTS_DEFAULT) {%- endmacro %}