diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index ac2c0c355bac0df3ef1161581e3b300fee22512f..068dac7d3f4a5230e0225d73035cf4c2b7429432 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -108,7 +108,7 @@ const knot_zone_t *ns_get_zone_for_qname(knot_zonedb_t *zdb, * \return The synthetized RRSet (this is a newly created RRSet, remember to * free it). */ -static knot_rrset_t *ns_synth_from_wildcard( +knot_rrset_t *ns_synth_from_wildcard( const knot_rrset_t *wildcard_rrset, const knot_dname_t *qname) { knot_rrset_t *rrset = NULL; @@ -184,7 +184,7 @@ dbg_ns_exec_verb( * \return KNOT_ENOMEM * \return KNOT_ESPACE */ -static int ns_add_rrsigs(knot_rrset_t *rrset, knot_pkt_t *resp, +int ns_add_rrsigs(knot_rrset_t *rrset, knot_pkt_t *resp, const knot_dname_t *name, uint32_t flags) { @@ -203,13 +203,16 @@ static int ns_add_rrsigs(knot_rrset_t *rrset, knot_pkt_t *resp, || knot_pkt_qtype(resp) == KNOT_RRTYPE_ANY) && (rrsigs = knot_rrset_get_rrsigs(rrset)) != NULL) { if (name != NULL) { + knot_rrset_t *rrsigs_orig = rrsigs; int ret = ns_check_wildcard(name, resp, &rrsigs); if (ret != KNOT_EOK) { dbg_ns("Failed to process wildcard: %s\n", knot_strerror(ret)); return ret; } - /* #10 will leak if synthetized new */ + if (rrsigs != rrsigs_orig) { + flags |= KNOT_PF_FREE; + } } return knot_pkt_put(resp, 0, rrsigs, flags|KNOT_PF_CHECKDUP); } @@ -476,14 +479,20 @@ dbg_ns_exec_verb( if (knot_rrset_rdata_rr_count(rrset) > 0 || knot_rrset_type(rrset) == KNOT_RRTYPE_APL) { + knot_rrset_t *rrset_orig = rrset; ret = ns_check_wildcard(name, resp, &rrset); if (ret != KNOT_EOK) { dbg_ns("Failed to process wildcard.\n"); break; } + unsigned flags = 0; + if (rrset != rrset_orig) { + flags |= KNOT_PF_FREE; + } + assert(KNOT_PKT_IN_AN(resp)); - ret = knot_pkt_put(resp, 0, rrset, 0); + ret = knot_pkt_put(resp, 0, rrset, flags); if (ret != KNOT_EOK) { dbg_ns("Failed add Answer RRSet: %s\n", knot_strerror(ret)); @@ -526,14 +535,20 @@ dbg_ns_exec_verb( continue; } + knot_rrset_t *rrset_orig = rrset; ret = ns_check_wildcard(name, resp, &rrset); if (ret != KNOT_EOK) { dbg_ns("Failed to process wildcard.\n"); break; } + unsigned flags = 0; + if (rrset != rrset_orig) { + flags |= KNOT_PF_FREE; + } + assert(KNOT_PKT_IN_AN(resp)); - ret = knot_pkt_put(resp, 0, rrset, 0); + ret = knot_pkt_put(resp, 0, rrset, flags); if (ret != KNOT_EOK) { dbg_ns("Failed add Answer RRSet: %s\n", knot_strerror(ret)); @@ -553,14 +568,20 @@ dbg_ns_exec_verb( if (rrset != NULL && knot_rrset_rdata_rr_count(rrset)) { dbg_ns_verb("Found RRSet of type %u\n", type); + knot_rrset_t *rrset2_orig = rrset2; ret = ns_check_wildcard(name, resp, &rrset2); if (ret != KNOT_EOK) { dbg_ns("Failed to process wildcard.\n"); break; } + unsigned flags = 0; + if (rrset2 != rrset2_orig) { + flags |= KNOT_PF_FREE; + } + assert(KNOT_PKT_IN_AN(resp)); - ret = knot_pkt_put(resp, 0, rrset2, 0); + ret = knot_pkt_put(resp, 0, rrset2, flags); if (ret != KNOT_EOK) { dbg_ns("Failed add Answer RRSet: %s\n", knot_strerror(ret)); @@ -674,9 +695,14 @@ dbg_ns_exec( return ret; } + unsigned flags = KNOT_PF_NOTRUNC|KNOT_PF_CHECKDUP; + if (rrset_add2 != rrset_add) { + flags |= KNOT_PF_FREE; + } + assert(KNOT_PKT_IN_AR(resp)); ret = knot_pkt_put( - resp, compr_hint, rrset_add2, KNOT_PF_NOTRUNC|KNOT_PF_CHECKDUP); + resp, compr_hint, rrset_add2, flags); if (ret != KNOT_EOK) { dbg_ns("Failed to add A RRSet to " @@ -711,9 +737,14 @@ dbg_ns_exec( return ret; } + unsigned flags = KNOT_PF_NOTRUNC|KNOT_PF_CHECKDUP; + if (rrset_add2 != rrset_add) { + flags |= KNOT_PF_FREE; + } + assert(KNOT_PKT_IN_AR(resp)); ret = knot_pkt_put( - resp, compr_hint, rrset_add2, KNOT_PF_NOTRUNC|KNOT_PF_CHECKDUP); + resp, compr_hint, rrset_add2, flags); if (ret != KNOT_EOK) { dbg_ns("Failed to add AAAA RRSet to " @@ -4117,6 +4148,10 @@ int ns_proc_out(uint8_t *wire, uint16_t *wire_len, ns_proc_context_t *ctx) } *wire_len = pkt->size; + + /* Free packet. */ + knot_pkt_free(&pkt); + return ctx->state; } diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h index d4595098b0219881a177fb1ce2a3c5de2cecb8f9..01457d20bba8e6bbb1d35d189a322764e0c8be90 100644 --- a/src/libknot/nameserver/name-server.h +++ b/src/libknot/nameserver/name-server.h @@ -418,6 +418,11 @@ int ns_referral(const knot_node_t *node, knot_pkt_t *resp, uint16_t qtype); +knot_rrset_t *ns_synth_from_wildcard(const knot_rrset_t *wildcard_rrset, const knot_dname_t *qname); + +int ns_add_rrsigs(knot_rrset_t *rrset, knot_pkt_t *resp, + const knot_dname_t *name, + uint32_t flags); /* #10 >>> Exposed API. */ /* #10 <<< Next-gen API. */ diff --git a/src/libknot/nameserver/ns_proc_query.c b/src/libknot/nameserver/ns_proc_query.c index a66dfaa49642288ec5ad01db132e61001810b692..078c744a8f2be090fec5e42b0b7f749b7c7d041a 100644 --- a/src/libknot/nameserver/ns_proc_query.c +++ b/src/libknot/nameserver/ns_proc_query.c @@ -6,15 +6,17 @@ #include "common/descriptor.h" #include "libknot/common.h" #include "libknot/consts.h" +#include "libknot/rdata.h" #include "libknot/util/debug.h" #include "libknot/nameserver/chaos.h" struct query_data { - int state; uint16_t rcode; uint16_t rcode_tsig; knot_pkt_t *pkt; const knot_node_t *node, *encloser, *previous; + list_t wildcards; + mm_ctx_t *mm; }; /* Forward decls. */ @@ -44,7 +46,13 @@ int ns_proc_query_begin(ns_proc_context_t *ctx) assert(ctx); ctx->type = NS_PROC_QUERY_ID; ctx->data = ctx->mm.alloc(ctx->mm.ctx, sizeof(struct query_data)); - memset(ctx->data, 0, sizeof(struct query_data)); + + struct query_data *data = QUERY_DATA(ctx); + memset(data, 0, sizeof(struct query_data)); + data->mm = &ctx->mm; + + /* Initialize list. */ + init_list(&data->wildcards); /* Await packet. */ return NS_PROC_MORE; @@ -56,7 +64,12 @@ int ns_proc_query_reset(ns_proc_context_t *ctx) assert(ctx); struct query_data *data = QUERY_DATA(ctx); knot_pkt_free(&data->pkt); - memset(data, 0, sizeof(struct query_data)); + data->rcode = KNOT_RCODE_NOERROR; + data->rcode_tsig = 0; + data->node = data->encloser = data->previous = NULL; + + /* Free wildcard list. */ + ptrlist_free(&data->wildcards, data->mm); /* Await packet. */ return NS_PROC_MORE; @@ -283,6 +296,90 @@ enum { ERROR }; +int in_zone_name_cname(knot_pkt_t *pkt, const knot_dname_t **name, struct query_data *qdata) +{ + dbg_ns("%s(%p, %p, %p)\n", __func__, name, pkt, qdata); + + const knot_node_t *cname_node = qdata->node; + knot_rrset_t *cname_rr = knot_node_get_rrset(qdata->node, KNOT_RRTYPE_CNAME); + knot_rrset_t *rr_to_add = cname_rr; + unsigned flags = 0; + int ret = KNOT_EOK; + + assert(cname_rr != NULL); + + /* Is node a wildcard? */ + if (knot_dname_is_wildcard(cname_node->owner)) { + + /* Check if is not in wildcard nodes (loop). */ + dbg_ns("%s: CNAME node %p is wildcard\n", __func__, cname_node); + if (ptrlist_contains(&qdata->wildcards, cname_node)) { + dbg_ns("%s: node %p already visited => CNAME loop\n", + __func__, cname_node); + return HIT; + } + + /* Put to wildcard node list. */ + if (ptrlist_add(&qdata->wildcards, cname_node, qdata->mm) == NULL) { + qdata->rcode = KNOT_RCODE_SERVFAIL; + return ERROR; + } + + /* Synthetic RRSet. */ + rr_to_add = ns_synth_from_wildcard(cname_rr, *name); + + /* Free RRSet with packet. */ + flags |= KNOT_PF_FREE; + + } else { + /* Normal CNAME name, check for duplicate. */ + flags |= KNOT_PF_CHECKDUP; + } + + /* Now, try to put CNAME to answer. */ + ret = knot_pkt_put(pkt, 0, rr_to_add, flags); + if (ret != KNOT_EOK) { + /* Free if synthetized. */ + if (rr_to_add != cname_rr) { + knot_rrset_deep_free(&rr_to_add, 1); + } + /* Duplicate found, end resolving chain. */ + if (ret == KNOT_ENORRSET) { + dbg_ns("%s: RR %p already inserted => CNAME loop\n", + __func__, rr_to_add); + return HIT; + } else { + qdata->rcode = KNOT_RCODE_SERVFAIL; + return ERROR; + } + } + + /* Add RR signatures (from original RR). */ + ret = ns_add_rrsigs(cname_rr, pkt, *name, 0); + if (ret != KNOT_EOK) { + dbg_ns("%s: couldn't add rrsigs for CNAME RRSet %p\n", + __func__, cname_rr); + qdata->rcode = KNOT_RCODE_SERVFAIL; + return ERROR; + } + + /* Now follow the next CNAME TARGET. */ + *name = knot_rdata_cname_name(cname_rr); + +#ifdef KNOT_NS_DEBUG + char *cname_str = knot_dname_to_str(cname_node->owner); + char *target_str = knot_dname_to_str(*name); + dbg_ns("%s: FOLLOW '%s' -> '%s'\n", __func__, cname_str, target_str); + free(cname_str); + free(target_str); +#endif /* KNOT_NS_DEBUG */ + + /* Invalidate current node. */ + qdata->node = qdata->encloser = qdata->previous = NULL; + + return FOLLOW; +} + static int in_zone_name_found(knot_pkt_t *pkt, const knot_dname_t **name, struct query_data *qdata) { @@ -292,9 +389,7 @@ static int in_zone_name_found(knot_pkt_t *pkt, const knot_dname_t **name, if (knot_node_rrset(qdata->node, KNOT_RRTYPE_CNAME) != NULL && qtype != KNOT_RRTYPE_CNAME && qtype != KNOT_RRTYPE_RRSIG) { dbg_ns("%s: solving CNAME\n", __func__); - qdata->rcode = KNOT_RCODE_NOTIMPL; - return ERROR; - assert(0); /*! \todo Implement CNAME solving. */ + return in_zone_name_cname(pkt, name, qdata); } // now we have the node for answering