diff options
Diffstat (limited to 'modules/hints/hints.c')
-rw-r--r-- | modules/hints/hints.c | 361 |
1 files changed, 117 insertions, 244 deletions
diff --git a/modules/hints/hints.c b/modules/hints/hints.c index 25d41c90..ccbc9880 100644 --- a/modules/hints/hints.c +++ b/modules/hints/hints.c @@ -20,6 +20,7 @@ #include "lib/zonecut.h" #include "lib/module.h" #include "lib/layer.h" +#include "lib/rules/api.h" #include <inttypes.h> #include <math.h> @@ -29,8 +30,6 @@ #define ERR_MSG(...) kr_log_error(HINT, "[ ]" __VA_ARGS__) struct hints_data { - struct kr_zonecut hints; - struct kr_zonecut reverse_hints; bool use_nodata; /**< See hint_use_nodata() description, exposed via lua. */ uint32_t ttl; /**< TTL used for the hints, exposed via lua. */ }; @@ -45,130 +44,6 @@ static char * bool2jsonstr(bool val) return result; } -static int put_answer(knot_pkt_t *pkt, struct kr_query *qry, knot_rrset_t *rr, bool use_nodata) -{ - int ret = 0; - if (!knot_rrset_empty(rr) || use_nodata) { - /* Update packet question */ - if (!knot_dname_is_equal(knot_pkt_qname(pkt), rr->owner)) { - kr_pkt_recycle(pkt); - knot_pkt_put_question(pkt, qry->sname, qry->sclass, qry->stype); - } - if (!knot_rrset_empty(rr)) { - /* Append to packet */ - ret = knot_pkt_put_rotate(pkt, KNOT_COMPR_HINT_QNAME, rr, - qry->reorder, KNOT_PF_FREE); - } else { - /* Return empty answer if name exists, but type doesn't match */ - knot_wire_set_aa(pkt->wire); - } - } else { - ret = kr_error(ENOENT); - } - /* Clear RR if failed */ - if (ret != 0) { - knot_rrset_clear(rr, &pkt->mm); - } - return ret; -} - -static int satisfy_reverse(/*const*/ struct hints_data *data, - knot_pkt_t *pkt, struct kr_query *qry) -{ - /* Find a matching name */ - pack_t *addr_set = kr_zonecut_find(&data->reverse_hints, qry->sname); - if (!addr_set || addr_set->len == 0) { - return kr_error(ENOENT); - } - knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm); - knot_rrset_t rr; - knot_rrset_init(&rr, qname, KNOT_RRTYPE_PTR, KNOT_CLASS_IN, data->ttl); - - /* Append address records from hints */ - uint8_t *addr = pack_last(*addr_set); - if (addr != NULL) { - size_t len = pack_obj_len(addr); - void *addr_val = pack_obj_val(addr); - knot_rrset_add_rdata(&rr, addr_val, len, &pkt->mm); - } - - return put_answer(pkt, qry, &rr, data->use_nodata); -} - -static int satisfy_forward(/*const*/ struct hints_data *data, - knot_pkt_t *pkt, struct kr_query *qry) -{ - /* Find a matching name */ - pack_t *addr_set = kr_zonecut_find(&data->hints, qry->sname); - if (!addr_set || addr_set->len == 0) { - return kr_error(ENOENT); - } - knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm); - knot_rrset_t rr; - knot_rrset_init(&rr, qname, qry->stype, qry->sclass, data->ttl); - - size_t family_len; - switch (rr.type) { - case KNOT_RRTYPE_A: - family_len = sizeof(struct in_addr); - break; - case KNOT_RRTYPE_AAAA: - family_len = sizeof(struct in6_addr); - break; - default: - goto finish; - }; - - /* Append address records from hints */ - uint8_t *addr = pack_head(*addr_set); - while (addr != pack_tail(*addr_set)) { - size_t len = pack_obj_len(addr); - void *addr_val = pack_obj_val(addr); - if (len == family_len) { - knot_rrset_add_rdata(&rr, addr_val, len, &pkt->mm); - } - addr = pack_obj_next(addr); - } -finish: - return put_answer(pkt, qry, &rr, data->use_nodata); -} - -static int query(kr_layer_t *ctx, knot_pkt_t *pkt) -{ - struct kr_query *qry = ctx->req->current_query; - if (!qry || (ctx->state & KR_STATE_FAIL)) { - return ctx->state; - } - - struct kr_module *module = ctx->api->data; - struct hints_data *data = module->data; - if (!data) { /* No valid file. */ - return ctx->state; - } - /* We can optimize for early return like this: */ - if (!data->use_nodata && qry->stype != KNOT_RRTYPE_A - && qry->stype != KNOT_RRTYPE_AAAA && qry->stype != KNOT_RRTYPE_PTR) { - return ctx->state; - } - /* FIXME: putting directly into packet breaks ordering in case the hint - * is applied after a CNAME jump. */ - if (knot_dname_in_bailiwick(qry->sname, (const uint8_t *)"\4arpa\0") >= 0) { - if (satisfy_reverse(data, pkt, qry) != 0) - return ctx->state; - } else { - if (satisfy_forward(data, pkt, qry) != 0) - return ctx->state; - } - - VERBOSE_MSG(qry, "<= answered from hints\n"); - qry->flags.DNSSEC_WANT = false; /* Never authenticated */ - qry->flags.CACHED = true; - qry->flags.NO_MINIMIZE = true; - pkt->parsed = pkt->size; - knot_wire_set_qr(pkt->wire); - return KR_STATE_DONE; -} - static int parse_addr_str(union kr_sockaddr *sa, const char *addr) { int family = strchr(addr, ':') ? AF_INET6 : AF_INET; @@ -228,7 +103,7 @@ static const knot_dname_t * addr2reverse(const char *addr) kr_inaddr_family(&ia.ip)); } -static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr) +static int add_pair_root(struct kr_zonecut *hints, const char *name, const char *addr) { /* Build key */ knot_dname_t key[KNOT_DNAME_MAXLEN]; @@ -241,73 +116,107 @@ static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr if (parse_addr_str(&ia, addr) != 0) { return kr_error(EINVAL); } - return kr_zonecut_add(hints, key, kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip)); } -static int add_reverse_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) { - const knot_dname_t *key = addr2reverse(addr); + /* Build key */ + knot_dname_t key[KNOT_DNAME_MAXLEN]; + if (!knot_dname_from_str(key, name, sizeof(key))) { + return kr_error(EINVAL); + } + knot_dname_to_lower(key); - if (key == NULL) { + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) { return kr_error(EINVAL); } + 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, 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) 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(const struct hints_data *data, const char *name, const char *addr) +{ + const knot_dname_t *key = addr2reverse(addr); + if (!key) + return kr_error(EINVAL); + knot_rrset_t rrs; + knot_rrset_init(&rrs, /*const-cast*/(knot_dname_t *)key, + KNOT_RRTYPE_PTR, KNOT_CLASS_IN, data->ttl); knot_dname_t ptr_name[KNOT_DNAME_MAXLEN]; - if (!knot_dname_from_str(ptr_name, name, sizeof(ptr_name))) { + if (!knot_dname_from_str(ptr_name, name, sizeof(ptr_name))) return kr_error(EINVAL); + int ret = knot_rrset_add_rdata(&rrs, ptr_name, knot_dname_size(ptr_name), NULL); + if (!ret) { + ret = kr_rule_local_data_ins(&rrs, NULL, KR_RULE_TAGS_ALL); + knot_rdataset_clear(&rrs.rrs, NULL); } - - return kr_zonecut_add(hints, key, ptr_name, knot_dname_size(ptr_name)); + return ret; } -/** For a given name, remove either one address or all of them (if == NULL). +/** For a given name, remove either one address ##or all of them (if == NULL). * * Also remove the corresponding reverse records. */ static int del_pair(struct hints_data *data, const char *name, const char *addr) { - /* Build key */ - knot_dname_t key[KNOT_DNAME_MAXLEN]; - if (!knot_dname_from_str(key, name, sizeof(key))) { + // Parse addr + if (!addr) + return kr_error(ENOSYS); + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) return kr_error(EINVAL); - } - int key_len = knot_dname_size(key); - - if (addr) { - /* Remove the pair. */ - union kr_sockaddr ia; - if (parse_addr_str(&ia, addr) != 0) { - return kr_error(EINVAL); - } - - const knot_dname_t *reverse_key = addr2reverse(addr); - kr_zonecut_del(&data->reverse_hints, reverse_key, key, key_len); - return kr_zonecut_del(&data->hints, key, - kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip)); - } - /* We're removing everything for the name; - * first find the name's pack */ - pack_t *addr_set = kr_zonecut_find(&data->hints, key); - if (!addr_set || addr_set->len == 0) { - return kr_error(ENOENT); - } - - /* Remove address records in hints from reverse_hints. */ - - for (uint8_t *a = pack_head(*addr_set); a != pack_tail(*addr_set); - a = pack_obj_next(a)) { - void *addr_val = pack_obj_val(a); - int family = pack_obj_len(a) == kr_family_len(AF_INET) - ? AF_INET : AF_INET6; - const knot_dname_t *reverse_key = raw_addr2reverse(addr_val, family); - if (reverse_key != NULL) { - kr_zonecut_del(&data->reverse_hints, reverse_key, key, key_len); - } - } - /* Remove the whole name. */ - return kr_zonecut_del_all(&data->hints, key); + // Remove the PTR + const knot_dname_t *reverse_key = addr2reverse(addr); + knot_rrset_t rrs; + knot_rrset_init(&rrs, /*const-cast*/(knot_dname_t *)reverse_key, + KNOT_RRTYPE_PTR, KNOT_CLASS_IN, data->ttl); + int ret = kr_rule_local_data_del(&rrs, KR_RULE_TAGS_ALL); + if (ret != 1) + VERBOSE_MSG(NULL, "del_pair PTR for %s; error: %s\n", addr, kr_strerror(ret)); + if (ret != 1 && ret != kr_error(ENOENT)) // ignore ENOENT for PTR (duplicities) + return ret; + + // Remove the forward entry + knot_dname_t key_buf[KNOT_DNAME_MAXLEN]; + rrs.owner = knot_dname_from_str(key_buf, name, sizeof(key_buf)); + if (!rrs.owner) + return kr_error(EINVAL); + rrs.type = ia.ip.sa_family == AF_INET6 ? KNOT_RRTYPE_AAAA : KNOT_RRTYPE_A; + ret = kr_rule_local_data_del(&rrs, KR_RULE_TAGS_ALL); + if (ret != 1) + VERBOSE_MSG(NULL, "del_pair for %s; error: %s\n", name, kr_strerror(ret)); + + // Remove the NODATA entry; again, not perfect matching, + // but we don't care much about this dynamic hints API. + if (ret == 1 && data->use_nodata) { + rrs.type = KNOT_RRTYPE_CNAME; + ret = kr_rule_local_data_del(&rrs, KR_RULE_TAGS_ALL); + if (ret != 1) + VERBOSE_MSG(NULL, "del_pair for NODATA %s; error: %s\n", + name, kr_strerror(ret)); + } + return ret < 0 ? ret : kr_ok(); } static int load_file(struct kr_module *module, const char *path) @@ -343,31 +252,21 @@ static int load_file(struct kr_module *module, const char *path) } const char *canonical_name = strtok_r(NULL, " \t\n", &saveptr); if (canonical_name == NULL) { - ret = -1; + ret = kr_error(EINVAL); goto error; } - /* Since the last added PTR records takes preference, - * 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); - if (!ret) { - ret = add_reverse_pair(&data->reverse_hints, name_tok, addr); - } - if (ret) { - ret = -1; + ret = add_pair(data, name_tok, addr); + if (ret) goto error; - } count += 1; } - ret = add_pair(&data->hints, canonical_name, addr); - if (!ret) { - ret = add_reverse_pair(&data->reverse_hints, canonical_name, addr); - } - if (ret) { - ret = -1; + ret = add_pair(data, canonical_name, addr); + if (!ret) // PTR only to the canonical name + ret = add_reverse_pair(data, canonical_name, addr); + if (ret) goto error; - } count += 1; } error: @@ -408,12 +307,9 @@ static char* hint_set(void *env, struct kr_module *module, const char *args) if (addr) { *addr = '\0'; ++addr; - ret = add_reverse_pair(&data->reverse_hints, args_copy, addr); - if (ret) { - del_pair(data, args_copy, addr); - } else { - ret = add_pair(&data->hints, args_copy, addr); - } + ret = add_reverse_pair(data, args_copy, addr); + if (!ret) + ret = add_pair(data, args_copy, addr); } return bool2jsonstr(ret == 0); @@ -435,6 +331,8 @@ static char* hint_del(void *env, struct kr_module *module, const char *args) ++addr; } ret = del_pair(data, args_copy, addr); + if (ret) + VERBOSE_MSG(NULL, "hints.del(%s) error: %s\n", args, kr_strerror(ret)); return bool2jsonstr(ret == 0); } @@ -457,7 +355,6 @@ static JsonNode *pack_addrs(pack_t *pack) return root; } -static char* pack_hints(struct kr_zonecut *hints); /** * Retrieve address hints, either for given name or for all names. * @@ -466,30 +363,7 @@ static char* pack_hints(struct kr_zonecut *hints); */ static char* hint_get(void *env, struct kr_module *module, const char *args) { - struct kr_zonecut *hints = &((struct hints_data *) module->data)->hints; - if (kr_fails_assert(hints)) - return NULL; - - if (!args) { - return pack_hints(hints); - } - - knot_dname_t key[KNOT_DNAME_MAXLEN]; - pack_t *pack = NULL; - if (knot_dname_from_str(key, args, sizeof(key))) { - pack = kr_zonecut_find(hints, key); - } - if (!pack || pack->len == 0) { - return NULL; - } - - char *result = NULL; - JsonNode *root = pack_addrs(pack); - if (root) { - result = json_encode(root); - json_delete(root); - } - return result; + return NULL; } /** @internal Pack all hints into serialized JSON. */ @@ -515,8 +389,12 @@ static void unpack_hint(struct kr_zonecut *root_hints, JsonNode *table, const ch JsonNode *node = NULL; json_foreach(node, table) { switch(node->tag) { - case JSON_STRING: add_pair(root_hints, name ? name : node->key, node->string_); break; - case JSON_ARRAY: unpack_hint(root_hints, node, name ? name : node->key); break; + case JSON_STRING: + add_pair_root(root_hints, name ? name : node->key, node->string_); + break; + case JSON_ARRAY: + unpack_hint(root_hints, node, name ? name : node->key); + break; default: continue; } } @@ -597,14 +475,22 @@ static char* hint_ttl(void *env, struct kr_module *module, const char *args) KR_EXPORT int hints_init(struct kr_module *module) { - static kr_layer_api_t layer = { - .produce = &query, - }; + static kr_layer_api_t layer = { 0 }; /* Store module reference */ layer.data = module; module->layer = &layer; static const struct kr_prop props[] = { + /* FIXME(decide): .set() and .del() used to work on individual RRs; + * now they overwrite or delete whole RRsets. + * Also, .get() doesn't work at all. + * + * It really depends what kind of config/API we'll be exposing to user. + * - Manipulating whole RRsets generally makes more sense to me. + * (But hints.set() currently can't even insert larger sets.) + * - We'll probably be deprecating access through these non-declarative + * commands (set, get, del) which are also usable dynamically. + */ { &hint_set, "set", "Set {name, address} hint.", }, { &hint_del, "del", "Delete one {name, address} hint or all addresses for the name.", }, { &hint_get, "get", "Retrieve hint for given name.", }, @@ -617,17 +503,9 @@ int hints_init(struct kr_module *module) }; module->props = props; - knot_mm_t *pool = mm_ctx_mempool2(MM_DEFAULT_BLKSIZE); - if (!pool) { - return kr_error(ENOMEM); - } - struct hints_data *data = mm_alloc(pool, sizeof(struct hints_data)); - if (!data) { - mp_delete(pool->ctx); + struct hints_data *data = malloc(sizeof(*data)); + if (!data) return kr_error(ENOMEM); - } - kr_zonecut_init(&data->hints, (const uint8_t *)(""), pool); - kr_zonecut_init(&data->reverse_hints, (const uint8_t *)(""), pool); data->use_nodata = true; data->ttl = HINTS_TTL_DEFAULT; module->data = data; @@ -639,13 +517,8 @@ int hints_init(struct kr_module *module) KR_EXPORT int hints_deinit(struct kr_module *module) { - struct hints_data *data = module->data; - if (data) { - kr_zonecut_deinit(&data->hints); - kr_zonecut_deinit(&data->reverse_hints); - mp_delete(data->hints.pool->ctx); - module->data = NULL; - } + free(module->data); + module->data = NULL; return kr_ok(); } |