diff --git a/src/libknot/nameserver/internet.c b/src/libknot/nameserver/internet.c index bdbefad74241fe3985d53bcd18f24700cd32fb92..b91c13bac68104698f3c248afc453e83757dd790 100644 --- a/src/libknot/nameserver/internet.c +++ b/src/libknot/nameserver/internet.c @@ -30,7 +30,13 @@ enum { MISS, /* Negative result. */ DELEG, /* Result is delegation. */ FOLLOW, /* Resolution not complete (CNAME/DNAME chain). */ - ERROR /* Resolution failed. */ + ERROR, /* Resolution failed. */ + TRUNC /* Finished, but truncated. */ +}; + +/*! \brief Features. */ +enum { + HAVE_DNSSEC = 1 << 0 /* DNSSEC both requested and supported. */ }; /*! \brief Check if given node was already visited. */ @@ -80,6 +86,94 @@ static int wildcard_list_cover(knot_pkt_t *pkt, struct query_data *qdata) return ret; } +/*! \brief DNSSEC both requested & available. */ +static bool have_dnssec(struct query_data *qdata) +{ + return qdata->flags & HAVE_DNSSEC; +} + +/*! \brief Put RRSet and its RRSIG (if applicable) to packet. */ +static int put_answer_rr(knot_pkt_t *pkt, const knot_rrset_t *rr, + const knot_dname_t *qname, struct query_data *qdata) +{ + /* RFC3123 s.6 - empty APL is valid, ignore other empty RRs. */ + if (knot_rrset_rdata_rr_count(rr) < 1 && + knot_rrset_type(rr) != KNOT_RRTYPE_APL) { + return KNOT_EMALF; + } + + uint16_t flags = 0; + uint16_t compr_hint = COMPR_HINT_NONE; + + /* Wildcard expansion or exact match, either way RRSet owner is + * is QNAME. We can fake name synthesis by setting compression hint to + * QNAME position. Just need to check if we're answering QNAME and not + * a CNAME target. + */ + if (pkt->rrset_count == 0) { /* Guaranteed first answer. */ + compr_hint = COMPR_HINT_QNAME; + } else { + if (knot_dname_is_wildcard(rr->owner)) { + rr = ns_synth_from_wildcard(rr, qname); + flags |= KNOT_PF_FREE; + } + } + + /*! \todo I don't like this much, RRSIGS could be added to whole section + * after processing I think. */ + int ret = knot_pkt_put(pkt, compr_hint, rr, flags); + if (ret == KNOT_EOK && rr->rrsigs && have_dnssec(qdata)) { + ret = put_answer_rr(pkt, rr->rrsigs, qname, qdata); + } + + return ret; +} + +/*! \brief This is a wildcard-covered or any other terminal node for QNAME. + * e.g. positive answer. + */ +static int put_answer_node(knot_pkt_t *pkt, uint16_t type, + const knot_dname_t *qname, struct query_data *qdata) +{ + const knot_rrset_t *rrset = NULL; + knot_rrset_t **rrsets = knot_node_get_rrsets_no_copy(qdata->node); + + int ret = KNOT_EOK; + switch (type) { + case KNOT_RRTYPE_ANY: /* Append all RRSets. */ + /* If ANY not allowed, set TC bit. */ + if (knot_zone_contents_any_disabled(qdata->zone->contents)) { + knot_wire_set_tc(pkt->wire); + return KNOT_EOK; + } + for (unsigned i = 0; i < knot_node_rrset_count(qdata->node); ++i) { + ret = put_answer_rr(pkt, rrsets[i], qname, qdata); + if (ret != KNOT_EOK) { + break; + } + } + break; + case KNOT_RRTYPE_RRSIG: /* Append all RRSIGs. */ + for (unsigned i = 0; i < knot_node_rrset_count(qdata->node); ++i) { + if (rrsets[i]->rrsigs) { + ret = put_answer_rr(pkt, rrsets[i]->rrsigs, qname, qdata); + if (ret != KNOT_EOK) { + break; + } + } + } + break; + default: /* Single RRSet of given type. */ + rrset = knot_node_get_rrset(qdata->node, type); + if (rrset) { + ret = put_answer_rr(pkt, rrset, qname, qdata); + } + break; + } + + return ret; +} + static int follow_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); @@ -127,7 +221,11 @@ static int follow_cname(knot_pkt_t *pkt, const knot_dname_t **name, struct query if (rr_to_add != cname_rr) { knot_rrset_deep_free(&rr_to_add, 1); } - return ERROR; + if (ret == KNOT_ESPACE) { + return TRUNC; + } else { + return ERROR; + } } else { /* Check if RR count increased. */ if (pkt->rrset_count <= rr_count_before) { @@ -135,7 +233,6 @@ static int follow_cname(knot_pkt_t *pkt, const knot_dname_t **name, struct query __func__, rr_to_add); return HIT; } - } /* Add RR signatures (from original RR). */ @@ -143,7 +240,11 @@ static int follow_cname(knot_pkt_t *pkt, const knot_dname_t **name, struct query if (ret != KNOT_EOK) { dbg_ns("%s: couldn't add rrsigs for CNAME RRSet %p\n", __func__, cname_rr); - return ERROR; + if (ret == KNOT_ESPACE) { + return TRUNC; + } else { + return ERROR; + } } /* Now follow the next CNAME TARGET. */ @@ -181,19 +282,20 @@ static int name_found(knot_pkt_t *pkt, const knot_dname_t **name, return DELEG; } - int added = 0; /*! \todo useless */ - int ret = ns_put_answer(qdata->node, qdata->zone->contents, *name, qtype, pkt, &added, 0 /*! \todo check from pkt */); - + uint16_t old_rrcount = pkt->rrset_count; + int ret = put_answer_node(pkt, qtype, *name, qdata); if (ret != KNOT_EOK) { - dbg_ns("%s: failed answer from node %p (%d)\n", __func__, qdata->node, ret); - /*! \todo set rcode */ - return ERROR; - } else { - dbg_ns("%s: answered, %d added\n", __func__, added); + dbg_ns("%s: failed answer from node %p (%s)\n", + __func__, qdata->node, knot_strerror(ret)); + if (ret == KNOT_ESPACE) { + return TRUNC; + } else { + return ERROR; + } } - /* Check for NODATA. */ - if (added == 0) { + /* Check for NODATA (=0 RRs added). */ + if (old_rrcount == pkt->rrset_count) { return NODATA; } else { return HIT; @@ -228,7 +330,11 @@ static int name_not_found(knot_pkt_t *pkt, const knot_dname_t **name, dbg_ns("%s: solving DNAME for name %p\n", __func__, *name); int ret = ns_process_dname(dname_rrset, name, pkt); if (ret != KNOT_EOK) { - return ERROR; + if (ret == KNOT_ESPACE) { + return TRUNC; + } else { + return ERROR; + } } return FOLLOW; @@ -295,6 +401,7 @@ static int solve_authority(int state, const knot_dname_t **qname, knot_pkt_t *pkt, struct query_data *qdata) { int ret = KNOT_ERROR; + uint16_t qtype = knot_pkt_type(pkt); const knot_zone_contents_t *zone_contents = qdata->zone->contents; switch (state) { @@ -304,8 +411,7 @@ static int solve_authority(int state, const knot_dname_t **qname, * But taking response size into consideration, DS/DNSKEY RRs * are rather large and may trigger fragmentation or even TCP * recovery. */ - if (knot_pkt_qtype(qdata->pkt) != KNOT_RRTYPE_DS && - knot_pkt_qtype(qdata->pkt) != KNOT_RRTYPE_DNSKEY) { + if (qtype != KNOT_RRTYPE_DS && qtype != KNOT_RRTYPE_DNSKEY) { ret = ns_put_authority_ns(zone_contents, pkt); if (ret == KNOT_ESPACE) { /* Optional. */ ret = KNOT_EOK; @@ -314,7 +420,7 @@ static int solve_authority(int state, const knot_dname_t **qname, ret = KNOT_EOK; } /* Put NSEC/NSEC3 Wildcard proof if answered from wildcard. */ - if (ret == KNOT_EOK && knot_pkt_have_dnssec(qdata->pkt)) { + if (ret == KNOT_EOK && have_dnssec(qdata)) { ret = wildcard_list_cover(pkt, qdata); } break; @@ -322,7 +428,7 @@ static int solve_authority(int state, const knot_dname_t **qname, dbg_ns("%s: answer is NXDOMAIN\n", __func__); qdata->rcode = KNOT_RCODE_NXDOMAIN; ret = ns_put_authority_soa(zone_contents, pkt); - if (ret == KNOT_EOK && knot_pkt_have_dnssec(qdata->pkt)) { + if (ret == KNOT_EOK && have_dnssec(qdata)) { ret = ns_put_nsec_nsec3_nxdomain(zone_contents, qdata->previous, qdata->encloser, @@ -332,7 +438,7 @@ static int solve_authority(int state, const knot_dname_t **qname, case NODATA: /* NODATA append AUTHORITY SOA + NSEC/NSEC3. */ dbg_ns("%s: answer is NODATA\n", __func__); ret = ns_put_authority_soa(zone_contents, pkt); - if (ret == KNOT_EOK && knot_pkt_have_dnssec(qdata->pkt)) { + if (ret == KNOT_EOK && have_dnssec(qdata)) { if (knot_dname_is_wildcard(qdata->node->owner)) { ret = ns_put_nsec_nsec3_wildcard_nodata(qdata->node, qdata->encloser, @@ -346,11 +452,13 @@ static int solve_authority(int state, const knot_dname_t **qname, } } break; - case DELEG: /* Referral response. */ /*! \todo DS + NS */ - ret = ns_referral(qdata->node, zone_contents, *qname, pkt, knot_pkt_qtype(pkt)); + case DELEG: /* Referral response. */ + ret = ns_referral(qdata->node, zone_contents, *qname, pkt, qtype); break; - case ERROR: - dbg_ns("%s: failed to resolve qname\n", __func__); + case TRUNC: /* Truncated ANSWER. */ + ret = KNOT_ESPACE; + break; + case ERROR: /* Error resolving ANSWER. */ break; default: dbg_ns("%s: invalid state after qname processing = %d\n", @@ -371,6 +479,12 @@ int internet_answer(knot_pkt_t *response, struct query_data *qdata) NS_NEED_VALID_ZONE(qdata, KNOT_RCODE_REFUSED); + /* Check features - DNSSEC. */ + if (knot_pkt_have_dnssec(qdata->pkt) && + knot_zone_contents_is_signed(qdata->zone->contents)) { + qdata->flags |= HAVE_DNSSEC; + } + /* Write answer RRs for QNAME. */ dbg_ns("%s: writing %p ANSWER\n", __func__, response); knot_pkt_begin(response, KNOT_ANSWER); @@ -385,7 +499,12 @@ int internet_answer(knot_pkt_t *response, struct query_data *qdata) knot_pkt_begin(response, KNOT_AUTHORITY); int ret = solve_authority(state, &qname, response, qdata); if (ret != KNOT_EOK) { - return NS_PROC_FAIL; + /* Truncated. */ + if (ret == KNOT_ESPACE) { + return NS_PROC_FINISH; + } else { + return NS_PROC_FAIL; + } } @@ -393,7 +512,8 @@ int internet_answer(knot_pkt_t *response, struct query_data *qdata) dbg_ns("%s: writing %p ADDITIONAL\n", __func__, response); knot_pkt_begin(response, KNOT_ADDITIONAL); ret = ns_put_additional(response); - if (ret != KNOT_EOK) { + /* Optional section. */ + if (ret != KNOT_EOK && ret != KNOT_ESPACE) { return NS_PROC_FAIL; } diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index 8e9cdd06d0daa8f0e1d091817d375c626fd15c30..5fd8c55d5139789a82bf59e7e83fc1db27b3ac35 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -220,187 +220,6 @@ int ns_add_rrsigs(knot_rrset_t *rrset, knot_pkt_t *resp, return KNOT_EOK; } -/*----------------------------------------------------------------------------*/ -/*! - * \brief Retrieves RRSet(s) of given type from the given node and adds them to - * the response's Answer section. - * - * \param node Node where to take the RRSet from. - * \param name Actual searched name (used in case of wildcard RRSet(s)). - * \param type Type of the RRSet(s). If set to KNOT_RRTYPE_ANY, all RRSets - * from the node will be added to the answer. - * \param resp Response where to add the RRSets. - * - * \return Number of RRSets added. - */ -int ns_put_answer(const knot_node_t *node, - const knot_zone_contents_t *zone, - const knot_dname_t *name, - uint16_t type, knot_pkt_t *resp, int *added, - int check_any) -{ - *added = 0; -dbg_ns_exec_verb( - char *name_str = knot_dname_to_str(node->owner); - dbg_ns_verb("Putting answers from node %s.\n", name_str); - free(name_str); -); - - int ret = KNOT_EOK; - - switch (type) { - case KNOT_RRTYPE_ANY: { - dbg_ns_verb("Returning all RRTYPES.\n"); - - // if ANY not allowed, set TC bit - if (check_any && knot_zone_contents_any_disabled(zone)) { - knot_wire_set_tc(resp->wire); - break; - } - - knot_rrset_t **rrsets = knot_node_get_rrsets(node); - if (rrsets == NULL) { - break; - } - int i = 0; - knot_rrset_t *rrset; - while (i < knot_node_rrset_count(node)) { - assert(rrsets[i] != NULL); - rrset = rrsets[i]; - - dbg_ns_detail(" Type: %u\n", knot_rrset_type(rrset)); - - 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, flags); - if (ret != KNOT_EOK) { - dbg_ns("Failed add Answer RRSet: %s\n", - knot_strerror(ret)); - break; - } - - *added += 1; - } - - assert(KNOT_PKT_IN_AN(resp)); - ret = ns_add_rrsigs(rrset, resp, name, 1); - if (ret != KNOT_EOK) { - dbg_ns("Failed add RRSIGs for Answer RRSet: %s" - "\n", knot_strerror(ret)); - break; - } - - *added += 1; - - ++i; - } - free(rrsets); - break; - } - case KNOT_RRTYPE_RRSIG: { - dbg_ns_verb("Returning all RRSIGs.\n"); - knot_rrset_t **rrsets = knot_node_get_rrsets(node); - if (rrsets == NULL) { - break; - } - int i = 0; - int ret = 0; - knot_rrset_t *rrset; - while (i < knot_node_rrset_count(node)) { - assert(rrsets[i] != NULL); - rrset = knot_rrset_get_rrsigs(rrsets[i]); - - if (rrset == NULL) { - ++i; - 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, flags); - if (ret != KNOT_EOK) { - dbg_ns("Failed add Answer RRSet: %s\n", - knot_strerror(ret)); - break; - } - - *added += 1; - ++i; - } - free(rrsets); - break; - } - default: { - int ret = 0; - knot_rrset_t *rrset = knot_node_get_rrset(node, type); - knot_rrset_t *rrset2 = rrset; - 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, flags); - if (ret != KNOT_EOK) { - dbg_ns("Failed add Answer RRSet: %s\n", - knot_strerror(ret)); - break; - } - - *added += 1; - - assert(KNOT_PKT_IN_AN(resp)); - ret = ns_add_rrsigs(rrset, resp, name, 1); - - if (ret != KNOT_EOK) { - dbg_ns("Failed add RRSIGs for Answer RRSet: %s" - "\n", knot_strerror(ret)); - break; - } - - *added += 1; - } - } - } - - return ret; -} - /*----------------------------------------------------------------------------*/ static int ns_put_additional_rrset(knot_pkt_t *pkt, uint16_t compr_hint, diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h index 4ef4f2f3d5a8e69797047c527e497ac383a3049f..ac58e172a8ddbb4fc57172a3fcbea1f9fa03a7dc 100644 --- a/src/libknot/nameserver/name-server.h +++ b/src/libknot/nameserver/name-server.h @@ -400,11 +400,7 @@ int ns_put_additional(knot_pkt_t *resp); int ns_put_authority_soa(const knot_zone_contents_t *zone, knot_pkt_t *resp); -int ns_put_answer(const knot_node_t *node, - const knot_zone_contents_t *zone, - const knot_dname_t *name, - uint16_t type, knot_pkt_t *resp, int *added, - int check_any); + int ns_put_authority_ns(const knot_zone_contents_t *zone, knot_pkt_t *resp); int ns_referral(const knot_node_t *node, diff --git a/src/libknot/nameserver/ns_proc_query.c b/src/libknot/nameserver/ns_proc_query.c index fbe0bd374a6850eaec39154705f20a099548edfe..bc99aaf7467ebfd05869563c5a21ef0b5a3baa17 100644 --- a/src/libknot/nameserver/ns_proc_query.c +++ b/src/libknot/nameserver/ns_proc_query.c @@ -60,6 +60,7 @@ int ns_proc_query_reset(ns_proc_context_t *ctx) knot_pkt_free(&data->pkt); data->rcode = KNOT_RCODE_NOERROR; data->rcode_tsig = 0; + data->flags = 0; data->node = data->encloser = data->previous = NULL; /* Free wildcard list. */ @@ -82,8 +83,8 @@ int ns_proc_query_in(knot_pkt_t *pkt, ns_proc_context_t *ctx) struct query_data *data = QUERY_DATA(ctx); /* Check query type. */ - uint16_t query_type = knot_pkt_type(pkt); - switch(query_type) { + uint16_t pkt_type = knot_pkt_type(pkt); + switch(pkt_type) { case KNOT_QUERY_NORMAL: case KNOT_QUERY_NOTIFY: case KNOT_QUERY_AXFR: @@ -91,7 +92,7 @@ int ns_proc_query_in(knot_pkt_t *pkt, ns_proc_context_t *ctx) case KNOT_QUERY_UPDATE: break; /* Supported. */ default: - dbg_ns("%s: query_type(%hu) NOT SUPPORTED\n", __func__, query_type); + dbg_ns("%s: query_type(%hu) NOT SUPPORTED\n", __func__, pkt_type); knot_pkt_free(&pkt); return NS_PROC_NOOP; /* Refuse to process. */ } diff --git a/src/libknot/nameserver/ns_proc_query.h b/src/libknot/nameserver/ns_proc_query.h index 184ba1e13ff1b1648f98288086fc170af864ff42..ee7842086695cae7280c4677849d056f1354eaeb 100644 --- a/src/libknot/nameserver/ns_proc_query.h +++ b/src/libknot/nameserver/ns_proc_query.h @@ -43,6 +43,7 @@ enum ns_proc_query_flag { struct query_data { uint16_t rcode; uint16_t rcode_tsig; + uint16_t flags; knot_pkt_t *pkt; const knot_zone_t *zone; /*!< Associated zone. */ const knot_node_t *node, *encloser, *previous;