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)
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);
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);
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);
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));
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,
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);
-- 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
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)
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.
+
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];
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) {
* 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);
}
}
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);
}
if (ret) {
del_pair(data, args_copy, addr);
} else {
- ret = add_pair(&data->hints, args_copy, addr);
+ ret = add_pair(data, args_copy, addr);
}
}
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.", },