From: Vladimír Čunát Date: Mon, 7 Feb 2022 12:29:50 +0000 (+0100) Subject: hints: implement .use_nodata(true) and .ttl X-Git-Tag: v6.0.1~9^2~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3c39cbe9b4dbd7affa54711b9a56a1f82e273ab1;p=thirdparty%2Fknot-resolver.git hints: implement .use_nodata(true) and .ttl fixes modules/dns64 test --- diff --git a/lib/rules/api.c b/lib/rules/api.c index 1650e6e7a..010751d5a 100644 --- a/lib/rules/api.c +++ b/lib/rules/api.c @@ -307,6 +307,14 @@ int kr_rule_local_data_answer(struct kr_query *qry, knot_pkt_t *pkt) return kr_error(ENOENT); } +/** SOA RDATA content, used as default in negative answers. + * + * It's as recommended except for using a fixed mname (for simplicity): + https://tools.ietf.org/html/rfc6303#section-3 + */ +static const uint8_t soa_rdata[] = "\x09localhost\0\6nobody\7invalid\0" + "\0\0\0\1\0\0\x0e\x10\0\0\4\xb0\0\x09\x3a\x80\0\0\x2a\x30"; + #define CHECK_RET(ret) do { \ if ((ret) < 0) { kr_assert(false); return kr_error((ret)); } \ } while (false) @@ -334,22 +342,37 @@ static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t ty return kr_error(ENOMEM); ret = rdataset_materialize(&arrset.set.rr->rrs, data, data_bound, &pkt->mm); CHECK_RET(ret); - const size_t data_off = 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_off, data_bound, &pkt->mm); + ret = rdataset_materialize(&arrset.sig_rds, data, data_bound, &pkt->mm); CHECK_RET(ret); + data += ret; /* Sanity check: we consumed exactly all data. */ - const int unused_bytes = data_bound - data - data_off - ret; + 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); return kr_error(EILSEQ); } + /* Special NODATA sub-case. */ + knot_rrset_t *rr = arrset.set.rr; + const int is_nodata = rr->rrs.count == 0; + if (is_nodata) { + if (kr_fails_assert(type == KNOT_RRTYPE_CNAME && arrset.sig_rds.count == 0)) + return kr_error(EILSEQ); + rr->type = KNOT_RRTYPE_SOA; + ret = knot_rrset_add_rdata(rr, soa_rdata, sizeof(soa_rdata) - 1, &pkt->mm); + CHECK_RET(ret); + ret = knot_pkt_begin(pkt, KNOT_AUTHORITY); + CHECK_RET(ret); + } + /* Put links to the materialized data into the pkt. */ + knot_wire_set_rcode(pkt->wire, KNOT_RCODE_NOERROR); ret = pkt_append(pkt, &arrset); CHECK_RET(ret); @@ -358,13 +381,15 @@ static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t ty qry->flags.CACHED = true; qry->flags.NO_MINIMIZE = true; - VERBOSE_MSG(qry, "=> satisfied by local data (positive)\n"); + VERBOSE_MSG(qry, "=> satisfied by local data (%s)\n", + is_nodata ? "no data" : "positive"); return kr_ok(); } int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds, kr_rule_tags_t tags) { + // Construct the DB key. uint8_t key_data[KEY_MAXLEN]; knot_db_val_t key; key.data = key_dname_lf(rrs->owner, key_data); @@ -381,6 +406,7 @@ int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_r key.len = key_data + KEY_DNAME_END_OFFSET + 2 + sizeof(rrs->type) - (uint8_t *)key.data; + // Allocate the data in DB. const int rr_ssize = rdataset_dematerialize_size(&rrs->rrs); const int to_alloc = sizeof(tags) + sizeof(rrs->ttl) + rr_ssize + rdataset_dematerialize_size(sig_rds); @@ -388,6 +414,7 @@ int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_r int ret = ruledb_op(write, &key, &val, 1); CHECK_RET(ret); + // Write all the data. memcpy(val.data, &tags, sizeof(tags)); val.data += sizeof(tags); memcpy(val.data, &rrs->ttl, sizeof(rrs->ttl)); @@ -418,12 +445,7 @@ static int answer_zla_empty(struct kr_query *qry, knot_pkt_t *pkt, struct answer_rrset arrset; memset(&arrset, 0, sizeof(arrset)); - /* Construct SOA or NS data (hardcoded content). The SOA content is - * as recommended except for using a fixed mname (for simplicity): - https://tools.ietf.org/html/rfc6303#section-3 - */ - static const uint8_t soa_rdata[] = "\x09localhost\0\6nobody\7invalid\0" - "\0\0\0\1\0\0\x0e\x10\0\0\4\xb0\0\x09\x3a\x80\0\0\x2a\x30"; + /* Construct SOA or NS data (hardcoded content). */ const bool name_matches = knot_dname_is_equal(qry->sname, apex_name); const bool want_NS = name_matches && qry->stype == KNOT_RRTYPE_NS; arrset.set.rr = knot_rrset_new(apex_name, want_NS ? KNOT_RRTYPE_NS : KNOT_RRTYPE_SOA, diff --git a/lib/rules/api.h b/lib/rules/api.h index 4173d7508..1f9a25f41 100644 --- a/lib/rules/api.h +++ b/lib/rules/api.h @@ -25,7 +25,9 @@ void kr_rules_deinit(void); int kr_rule_local_data_answer(struct kr_query *qry, struct knot_pkt *pkt); /** Insert/overwrite a local data rule. - * Into the default rule-set ATM. */ + * + * Into the default rule-set ATM. + * 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); diff --git a/modules/dns64/dns64.test.lua b/modules/dns64/dns64.test.lua index 45956a4c1..0686ecc0d 100644 --- a/modules/dns64/dns64.test.lua +++ b/modules/dns64/dns64.test.lua @@ -3,9 +3,9 @@ local condition = require('cqueues.condition') -- setup resolver modules = { 'hints', 'dns64' } -hints['dns64.example'] = '192.168.1.1' hints.use_nodata(true) -- Respond NODATA to AAAA query hints.ttl(60) +hints['dns64.example'] = '192.168.1.1' dns64.config('fe80::21b:77ff:0:0') -- helper to wait for query resolution diff --git a/modules/hints/README.rst b/modules/hints/README.rst index c2deb5a93..7d7751882 100644 --- a/modules/hints/README.rst +++ b/modules/hints/README.rst @@ -136,6 +136,8 @@ Properties If set to true (the default), NODATA will be synthesised for matching hint name, but mismatching type (e.g. AAAA query when only A hint exists). + The setting is (now) per-entry, so you want to set it before any address-name pairs. + .. function:: hints.ttl([new_ttl]) :param int new_ttl: new TTL to set (optional) @@ -143,3 +145,5 @@ Properties This function allows to read and write the TTL value used for records generated by the hints module. + The setting is (now) per-entry, so you want to set it before any address-name pairs. + diff --git a/modules/hints/hints.c b/modules/hints/hints.c index a8746a141..03569bde2 100644 --- a/modules/hints/hints.c +++ b/modules/hints/hints.c @@ -245,7 +245,7 @@ static int add_pair_root(struct kr_zonecut *hints, const char *name, const char return kr_zonecut_add(hints, key, kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip)); } -static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr) +static int add_pair(const struct hints_data *data, const char *name, const char *addr) { /* Build key */ knot_dname_t key[KNOT_DNAME_MAXLEN]; @@ -261,22 +261,28 @@ static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr uint16_t rrtype = ia.ip.sa_family == AF_INET6 ? KNOT_RRTYPE_AAAA : KNOT_RRTYPE_A; knot_rrset_t rrs; - knot_rrset_init(&rrs, key, rrtype, KNOT_CLASS_IN, HINTS_TTL_DEFAULT/*FIXME*/); + knot_rrset_init(&rrs, key, rrtype, KNOT_CLASS_IN, data->ttl); int ret; if (ia.ip.sa_family == AF_INET6) { ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip6.sin6_addr, 16, NULL); } else { ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip4.sin_addr, 4, NULL); } - if (ret == KNOT_EOK) { + if (!ret) ret = kr_rule_local_data_ins(&rrs, NULL, KR_RULE_TAGS_ALL); + if (!ret && data->use_nodata) { + rrs.type = KNOT_RRTYPE_CNAME; + rrs.rrs.count = 0; + rrs.rrs.size = 0; ret = kr_rule_local_data_ins(&rrs, NULL, KR_RULE_TAGS_ALL); } + knot_rdataset_clear(&rrs.rrs, NULL); return ret; } static int add_reverse_pair(struct kr_zonecut *hints, const char *name, const char *addr) { + // FIXME: implement via new policy? const knot_dname_t *key = addr2reverse(addr); if (key == NULL) { @@ -380,7 +386,7 @@ static int load_file(struct kr_module *module, const char *path) * we add canonical name as the last one. */ const char *name_tok; while ((name_tok = strtok_r(NULL, " \t\n", &saveptr)) != NULL) { - ret = add_pair(&data->hints, name_tok, addr); + ret = add_pair(data, name_tok, addr); if (!ret) { ret = add_reverse_pair(&data->reverse_hints, name_tok, addr); } @@ -390,7 +396,7 @@ static int load_file(struct kr_module *module, const char *path) } count += 1; } - ret = add_pair(&data->hints, canonical_name, addr); + ret = add_pair(data, canonical_name, addr); if (!ret) { ret = add_reverse_pair(&data->reverse_hints, canonical_name, addr); } @@ -442,7 +448,7 @@ static char* hint_set(void *env, struct kr_module *module, const char *args) if (ret) { del_pair(data, args_copy, addr); } else { - ret = add_pair(&data->hints, args_copy, addr); + ret = add_pair(data, args_copy, addr); } } @@ -644,6 +650,7 @@ int hints_init(struct kr_module *module) static const struct kr_prop props[] = { { &hint_set, "set", "Set {name, address} hint.", }, + // TODO: _del, _get etc. with new policy { &hint_del, "del", "Delete one {name, address} hint or all addresses for the name.", }, { &hint_get, "get", "Retrieve hint for given name.", }, { &hint_ttl, "ttl", "Set/get TTL used for the hints.", },