]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/rules: add initial kr_rule_opts_t
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 28 May 2025 12:35:46 +0000 (14:35 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 28 May 2025 12:35:46 +0000 (14:35 +0200)
For now this relates to logging/dry-run of local-data rules.

14 files changed:
daemon/lua/kres-gen-33.lua
daemon/lua/kres-gen.sh
daemon/main.c
daemon/worker.c
lib/cache/impl.h
lib/resolve.h
lib/rules/api.c
lib/rules/api.h
lib/rules/defaults.c
lib/rules/impl.h
lib/rules/local-addr.c
lib/rules/zonefile.c
modules/hints/hints.c
python/knot_resolver/datamodel/templates/macros/local_data_macros.lua.j2

index 3e7313c4b4dd02e46c60c782c5f0df33982c8362..efe0d68564fb0ecece2cc7231b7deced696f55b6 100644 (file)
@@ -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;
index 69011adf155620bb27facf07e1cc804ee451bf5a..a5bc6f9a4d18cd4c23b960a28600c4e885a2ea9d 100755 (executable)
@@ -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 "
index 613977580fc5f0c5817d5a07431df8d5d86a76b4..3925a15c18c80b82b0c319d7951a418d6038f40b 100644 (file)
@@ -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. */
index 6f6cda584196c74bce1308242bd72347c063b5d1..16191a8e0e973558279160cf6a89a929c82caec4 100644 (file)
@@ -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;
index 9b5cb2f453f43e9cc6a931303d8c9f3218fa9e27..87ff6cd4b8d58cfb8ab6fafe654bbfe9a306fb91 100644 (file)
@@ -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. */
index 51d47925adafadb7034b1edeb56843295ba0faba..8aad3bbae349b94c2196eaa691585b25d0c232de 100644 (file)
@@ -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 */
index 53ebbf7e83a50b5d4088c25410113eda216f7974..8078de715e6c54073097651c95bf4bf0984d6918 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <stdlib.h>
 
+
 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);
index c7d1dd2972ed409a54ebe480287c8252c18e0a00..b2e11dd11b032e91cecf5470a349d3bf7fb3d144 100644 (file)
@@ -9,6 +9,8 @@ struct kr_query;
 struct kr_request;
 struct knot_pkt;
 struct sockaddr;
+#include <syslog.h>
+#include <lib/utils.h>
 #include <libknot/db/db.h>
 
 /// 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
index bd21fa1093b3a4c63a08bc422ece8bfd907dd6fe..3cf5eb8ee6f8241c65c6c3b28c942c320cd45fe2 100644 (file)
@@ -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);
index 1a2ee4dda4300d0b7237f8c2b1682e9faedffd24..bf82b581a947f2d60da985478e4658705eb878b0 100644 (file)
@@ -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]);
 
index cd5d456b4da7e07973d84cfae2d8e5dc46bad3d5..63680bbf5c1cba3b87ae0fe9f4da469ed6cd2f52 100644 (file)
@@ -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;
index 773ca937d5c41c2cb14088f26c6b5fdfef972f44..44df2a819f3363d34e9853f251099f5e09d030dd 100644 (file)
@@ -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)
index eaefaae4cc538cab0dc72a613b5bcf42bda5e127..c7278e86a5c2feb75ed043193767daad9b0b6988 100644 (file)
@@ -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();
 }
index 58861f93fd5460be23d700d5d45931af17e59be0..5bfb45afd5ba7b3921a1ef61b1246bd6ca4d8980 100644 (file)
@@ -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 %}