From: Grigorii Demidov Date: Thu, 11 May 2017 08:28:17 +0000 (+0200) Subject: lib/resove: WIP some improvements in zone cut detection in forward mode X-Git-Tag: 1.3.0-rc1~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2c5887039d744d483d549fab6257d8d600346bf2;p=thirdparty%2Fknot-resolver.git lib/resove: WIP some improvements in zone cut detection in forward mode --- diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c index d3c79ae66..8f4e08f77 100644 --- a/lib/layer/iterate.c +++ b/lib/layer/iterate.c @@ -821,6 +821,33 @@ int kr_make_query(struct kr_query *query, knot_pkt_t *pkt) return kr_ok(); } +int kr_make_query2(struct kr_query *query, knot_pkt_t *pkt, uint16_t qtype_minimized) +{ + /* Minimize QNAME (if possible). */ + uint16_t qtype = qtype_minimized; + const knot_dname_t *qname = minimized_qname(query, &qtype); + + /* Form a query for the authoritative. */ + knot_pkt_clear(pkt); + int ret = knot_pkt_put_question(pkt, qname, query->sclass, qtype); + if (ret != KNOT_EOK) { + return ret; + } + + /* Query built, expect answer. */ + query->id = kr_rand_uint(UINT16_MAX); + knot_wire_set_id(pkt->wire, query->id); + pkt->parsed = pkt->size; + WITH_VERBOSE { + char name_str[KNOT_DNAME_MAXLEN], type_str[16]; + knot_dname_to_str(name_str, query->sname, sizeof(name_str)); + knot_rrtype_to_string(query->stype, type_str, sizeof(type_str)); + QVERBOSE_MSG(query, "'%s' type '%s' id was assigned, parent id %hu\n", + name_str, type_str, query->parent ? query->parent->id : 0); + } + return kr_ok(); +} + static int prepare_query(kr_layer_t *ctx, knot_pkt_t *pkt) { assert(pkt && ctx); diff --git a/lib/layer/iterate.h b/lib/layer/iterate.h index 189aaf19b..b211a594c 100644 --- a/lib/layer/iterate.h +++ b/lib/layer/iterate.h @@ -33,3 +33,7 @@ int kr_response_classify(knot_pkt_t *pkt); /** Make next iterative query. */ int kr_make_query(struct kr_query *query, knot_pkt_t *pkt); + +/** Make next iterative query. If qname is minimized, + * qtype is set to qtype_minimized */ +int kr_make_query2(struct kr_query *query, knot_pkt_t *pkt, uint16_t qtype_minimized); diff --git a/lib/layer/validate.c b/lib/layer/validate.c index 52b7fa810..798d7a03f 100644 --- a/lib/layer/validate.c +++ b/lib/layer/validate.c @@ -303,7 +303,9 @@ static int update_parent_keys(struct kr_query *qry, uint16_t answer_type) break; case KNOT_RRTYPE_DS: VERBOSE_MSG(qry, "<= parent: updating DS\n"); - if (qry->flags & (QUERY_DNSSEC_NODS | QUERY_DNSSEC_INSECURE)) { /* DS non-existence proven. */ + if (qry->flags & (QUERY_DNSSEC_INSECURE)) { /* DS non-existence proven. */ + mark_insecure_parents(qry); + } if ((qry->flags & (QUERY_DNSSEC_NODS | QUERY_FORWARD)) == QUERY_DNSSEC_NODS) { mark_insecure_parents(qry); } else { /* DS existence proven. */ parent->zone_cut.trust_anchor = knot_rrset_copy(qry->zone_cut.trust_anchor, parent->zone_cut.pool); @@ -548,12 +550,15 @@ static bool check_empty_answer(kr_layer_t *ctx, knot_pkt_t *pkt) return ((an->count != 0) && (num_entries == 0)) ? false : true; } -static void unsigned_forward(kr_layer_t *ctx, knot_pkt_t *pkt) +static int unsigned_forward(kr_layer_t *ctx, knot_pkt_t *pkt) { struct kr_request *req = ctx->req; struct kr_query *qry = req->current_query; const uint16_t qtype = knot_pkt_qtype(pkt); + printf("unsigned forward\n"); + +/* if (qtype != KNOT_RRTYPE_DS) { struct kr_rplan *rplan = &req->rplan; struct kr_query *next = kr_rplan_push(rplan, qry, qry->sname, qry->sclass, KNOT_RRTYPE_DS); @@ -567,83 +572,53 @@ static void unsigned_forward(kr_layer_t *ctx, knot_pkt_t *pkt) return; } return; - - - /* We are in forwarding mode and have got unsigned answer. - * Check if answer is empty or not */ - if ((qtype == KNOT_RRTYPE_DS) || (qtype == KNOT_RRTYPE_RRSIG)) { - /* We are already trying to detect zone security status. - * Answer for DS \ RRSIG query can contains - * CNAME instead of target qtype. - * Ask for DS for parent zone in this case */ - if ((knot_pkt_section(pkt, KNOT_ANSWER)->count != 0) && - !(qry->flags & QUERY_CNAME)) { - /* Answer isn't empty and doesn't contain CNAME. - * Do nothing */ - return; +*/ +// if (qtype == KNOT_RRTYPE_NS) { + printf("KNOT_RRTYPE_NS\n"); + bool nods = false; + bool ds_req = false; + for (int i = 0; i < req->rplan.resolved.len; ++i) { + struct kr_query *q = req->rplan.resolved.at[i]; + kr_dname_print(q->sname, "q: ", " "); + kr_dname_print(qry->sname, "qry: ", " "); + kr_rrtype_print(q->stype, "type: ", "\n"); + if (/* q->parent == qry && */ + q->sclass == qry->sclass && + q->stype == KNOT_RRTYPE_DS && + knot_dname_is_equal(q->sname, qry->sname)) { + ds_req = true; + printf("DSREQ\n"); + if (q->flags & QUERY_DNSSEC_NODS) { + printf("NODS\n"); + nods = true; + } + } } - } else if ((knot_pkt_section(pkt, KNOT_ANSWER)->count != 0) && - !(qry->flags & QUERY_CNAME)) { - /* Answer isn't empty. */ - return; - } - - /* AUTHORITY can contain SOA */ - if (knot_pkt_section(pkt, KNOT_AUTHORITY)->count > 1) { - return; - } - const knot_dname_t *qname = NULL; - bool has_soa = false; - if (knot_pkt_section(pkt, KNOT_AUTHORITY)->count == 1) { - const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_AUTHORITY); - const knot_rrset_t *rr = knot_pkt_rr(sec, 0); - if (rr->type != KNOT_RRTYPE_SOA) { - return; + if (nods) { + printf("NODS return\n"); + qry->flags &= ~QUERY_DNSSEC_WANT; + qry->flags |= QUERY_DNSSEC_INSECURE; + if (qry->parent) { + qry->parent->flags &= ~QUERY_DNSSEC_WANT; + qry->parent->flags |= QUERY_DNSSEC_INSECURE; + } + return KR_STATE_DONE; } - qname = rr->owner; - has_soa = true; - } +// } - /* check_signer() is going to return KR_STATE_YIELD. - * This will cause refetching DS for current zone name by check_trust_chain() - * (i.e. - we do not receive RRSIG at all and - * want to figure out with zone security status). - * Here we set current zone name. When qtype is : - * 1) RRSIG + SOA - use SOA owner - * 2) RRSIG - use qname - * 3) DS + SOA - next-label(SOA owner) - * 4) DS - use next-label(qname) */ if (qtype != KNOT_RRTYPE_DS) { - /* We have got empty answer. - * It is necessary to figure out with zone security status. - * If there is SOA. use SOA owner since it is exact zone name. - * If no, try the owner of packet qname. */ - if (!has_soa) { - qname = knot_pkt_qname(pkt); - } - } else { - /* We already trying to figure out with security status. - * In previous steps DS query was spawned. - * Here we have got empty answer for DS query. - * If there is SOA. use next-label (SOA owner), - * otherwise use next-label (packet qname) */ - if (has_soa) { - if (knot_dname_is_equal(qname, knot_pkt_qname(pkt)) && qname[0] != '\0') { - qname = knot_wire_next_label(qname, NULL); - } - } else { - qname = knot_pkt_qname(pkt); - if (qname[0] != '\0') { - qname = knot_wire_next_label(qname, NULL); - } + struct kr_rplan *rplan = &req->rplan; + struct kr_query *next = kr_rplan_push(rplan, qry, qry->sname, qry->sclass, KNOT_RRTYPE_DS); + int state = kr_nsrep_copy_set(&next->ns, &qry->ns); + if (state != kr_ok()) { + return KR_STATE_FAIL; } + kr_zonecut_set(&next->zone_cut, qry->zone_cut.name); + kr_zonecut_copy_trust(&next->zone_cut, &qry->zone_cut); + next->flags |= QUERY_DNSSEC_WANT; } - if (qname[0] != '\0') { - qry->zone_cut.name = knot_dname_copy(qname, &req->pool); - } - - return; + return KR_STATE_YIELD; } static int check_signer(kr_layer_t *ctx, knot_pkt_t *pkt) @@ -655,12 +630,16 @@ static int check_signer(kr_layer_t *ctx, knot_pkt_t *pkt) if (ta_name && (!signer || !knot_dname_is_equal(ta_name, signer))) { /* check all newly added RRSIGs */ if (!signer) { + if (qry->flags & QUERY_FORWARD) { + return unsigned_forward(ctx, pkt); + } /* Not a DNSSEC-signed response. */ if (ctx->state == KR_STATE_YIELD) { /* Already yielded for revalidation. * It means that trust chain is OK and * transition to INSECURE hasn't occured. * Let the validation logic ask about RRSIG. */ + printf("already yielded\n"); return KR_STATE_DONE; } /* Ask parent for DS @@ -673,13 +652,30 @@ static int check_signer(kr_layer_t *ctx, knot_pkt_t *pkt) * for both parent and child, * and child zone is not signed. */ qry->zone_cut.name = knot_dname_copy(qname, &req->pool); - } else if (qry->flags & QUERY_FORWARD) { - unsigned_forward(ctx, pkt); - return KR_STATE_YIELD; } } else if (knot_dname_is_sub(signer, qry->zone_cut.name)) { /* Key signer is below current cut, advance and refetch keys. */ - qry->zone_cut.name = knot_dname_copy(signer, &req->pool); + if (!(qry->flags & QUERY_FORWARD)) { + qry->zone_cut.name = knot_dname_copy(signer, &req->pool); + } else { + for (int i = 0; i < req->rplan.resolved.len; ++i) { + struct kr_query *q = req->rplan.resolved.at[i]; + if (/* q->parent == qry && */ + q->sclass == qry->sclass && + q->stype == KNOT_RRTYPE_DS && + knot_dname_is_equal(q->sname, signer)) { + printf("DSREQQQQ\n"); + if (q->flags & QUERY_DNSSEC_NODS) { + qry->flags &= ~QUERY_DNSSEC_WANT; + qry->flags |= QUERY_DNSSEC_INSECURE; + if (qry->parent) { + qry->parent->flags &= ~QUERY_DNSSEC_WANT; + qry->parent->flags |= QUERY_DNSSEC_INSECURE; + } + } + } + } + } } else if (!knot_dname_is_equal(signer, qry->zone_cut.name)) { /* Key signer is above the current cut, so we can't validate it. This happens when a server is authoritative for both grandparent, parent and child zone. @@ -693,6 +689,7 @@ static int check_signer(kr_layer_t *ctx, knot_pkt_t *pkt) } /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */ if (qry->stype != KNOT_RRTYPE_DS) { /* zone cut matches, but DS/DNSKEY doesn't => refetch. */ + printf("sheck_signer\n"); VERBOSE_MSG(qry, ">< cut changed, needs revalidation\n"); return KR_STATE_YIELD; } @@ -796,11 +793,16 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt) if (ret != KR_STATE_DONE) { return ret; } + if ((qry->flags & (QUERY_FORWARD | QUERY_DNSSEC_INSECURE)) == + (QUERY_FORWARD | QUERY_DNSSEC_INSECURE)) { + return KR_STATE_DONE; + } } if (knot_wire_get_aa(pkt->wire) && qtype == KNOT_RRTYPE_DNSKEY) { ret = validate_keyset(req, pkt, has_nsec3); if (ret == kr_error(EAGAIN)) { + printf("validate\n"); VERBOSE_MSG(qry, ">< cut changed, needs revalidation\n"); return KR_STATE_YIELD; } else if (ret != 0) { @@ -922,6 +924,21 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt) return KR_STATE_FAIL; } } + + if (qry->flags & QUERY_FORWARD) { + if (qry->parent && + qtype == KNOT_RRTYPE_NS && + !no_data && + pkt_rcode == KNOT_RCODE_NOERROR) { + const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_ANSWER); + const knot_rrset_t *rr = knot_pkt_rr(sec, 0); + if (rr->type == KNOT_RRTYPE_NS) { + qry->parent->zone_cut.name = knot_dname_copy(rr->owner, &req->pool); + qry->parent->flags &= ~QUERY_DNSSEC_WANT; + qry->parent->flags |= QUERY_DNSSEC_INSECURE; + } + } + } VERBOSE_MSG(qry, "<= answer valid, OK\n"); return KR_STATE_DONE; } diff --git a/lib/resolve.c b/lib/resolve.c index 4163d4366..042ca5454 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -922,7 +922,7 @@ static struct kr_query *zone_cut_subreq(struct kr_rplan *rplan, struct kr_query return next; } -static int forward_trust_chain_check(struct kr_request *request, struct kr_query *qry, bool resume) +static int forward_trust_chain_check(struct kr_request *request, struct kr_query *qry, bool resume, knot_pkt_t *packet) { struct kr_rplan *rplan = &request->rplan; map_t *trust_anchors = &request->ctx->trust_anchors; @@ -934,35 +934,96 @@ static int forward_trust_chain_check(struct kr_request *request, struct kr_query return KR_STATE_PRODUCE; } - const knot_dname_t* wanted_name = qry->zone_cut.name; +// if (qry->parent != NULL) { +// return KR_STATE_PRODUCE; +// } + bool nods = false; - if (qry->parent == NULL && !resume) { - wanted_name = qry->sname; + bool ds_req = false; + bool ns_req = false; + bool minimized = false; +// const knot_dname_t* wanted_name = qry->zone_cut.name; + const knot_dname_t* wanted_name = NULL; + int name_offset = 1; + while (1) { + wanted_name = qry->sname; + nods = false; + ds_req = false; + ns_req = false; + minimized = false; + kr_dname_print(qry->zone_cut.name, "cut_name: ", " "); + kr_dname_print(qry->sname, "sname: ", " "); + kr_rrtype_print(qry->stype, "type: ", "\n"); + if (qry->parent == NULL /* && !resume */) { +// wanted_name = qry->sname; int cut_labels = knot_dname_labels(qry->zone_cut.name, NULL); int wanted_name_labels = knot_dname_labels(wanted_name, NULL); - while(wanted_name[0] && wanted_name_labels > cut_labels + 1) { + while(wanted_name[0] && wanted_name_labels > cut_labels + name_offset) { wanted_name = knot_wire_next_label(wanted_name, NULL); wanted_name_labels -= 1; } - nods = (wanted_name == qry->sname); - } + minimized = (wanted_name != qry->sname); + } + + for (int i = 0; i < request->rplan.resolved.len; ++i) { + struct kr_query *q = request->rplan.resolved.at[i]; + if (q->parent == qry && + q->sclass == qry->sclass && + (q->stype == KNOT_RRTYPE_DS || q->stype == KNOT_RRTYPE_NS) && + knot_dname_is_equal(q->sname, wanted_name)) { + if (q->stype == KNOT_RRTYPE_DS) { + ds_req = true; + if (qry->flags & QUERY_DNSSEC_NODS) { + nods = true; + } + } else { + ns_req = true; + } + } + } - for (int i = 0; i < request->rplan.resolved.len; ++i) { - struct kr_query *q = request->rplan.resolved.at[i]; - if (q->parent == qry && - q->sclass == qry->sclass && - q->stype == KNOT_RRTYPE_DS && - (q->flags & QUERY_DNSSEC_NODS) && - knot_dname_is_equal(q->sname, wanted_name)) { - nods = true; - VERBOSE_MSG(qry, "stop chasing trust chain\n"); + if (qry->parent == NULL /* && !resume */) { + printf("initial request ds_req %i ns_req %i\n", ds_req, ns_req); + + if (ds_req && !ns_req && minimized) { + struct kr_query *next = kr_rplan_push(rplan, qry, wanted_name, qry->sclass, KNOT_RRTYPE_NS); + if (!next) { + return KR_STATE_FAIL; + } + int state = kr_nsrep_copy_set(&next->ns, &qry->ns); + if (state != kr_ok()) { + return KR_STATE_FAIL; + } + kr_zonecut_set(&next->zone_cut, qry->zone_cut.name); + kr_zonecut_copy_trust(&next->zone_cut, &qry->zone_cut); + next->flags |= QUERY_DNSSEC_WANT; + return KR_STATE_DONE; + } + } + kr_dname_print(wanted_name, "wanted_name: ", " "); + printf("resume? %i\n", resume); + + if ((qry->stype == KNOT_RRTYPE_DS) && + knot_dname_is_equal(wanted_name, qry->sname)) { + printf("if1\n"); + nods = true; + } else if (resume && !ds_req) { + printf("if2\n"); + nods = false; + } else if (!minimized) { + printf("if3\n"); + nods = true; + } else { + printf("if4\n"); + nods = ds_req; + } + if (ds_req && ns_req) { + name_offset += 1; + } else { break; } } - - nods = nods || - ((qry->stype == KNOT_RRTYPE_DS) && - knot_dname_is_equal(wanted_name, qry->sname)); + printf("ds_req %i ns_req %i nods? %i\n", ds_req, ns_req, nods); /* Disable DNSSEC if it enters NTA. */ if (kr_ta_get(negative_anchors, wanted_name)){ @@ -1120,7 +1181,7 @@ static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot * Since forwarding targets already are in qry->ns - * cut fetching is not needed. */ if (qry->flags & QUERY_FORWARD) { - return forward_trust_chain_check(request, qry, false); + return forward_trust_chain_check(request, qry, false, packet); } if (!(qry->flags & QUERY_AWAIT_CUT)) { /* The query was resolved from cache. @@ -1191,7 +1252,7 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t if (qry->deferred != NULL) { /* @todo: Refactoring validator, check trust chain before resuming. */ int state = (qry->flags & QUERY_FORWARD) ? - forward_trust_chain_check(request, qry, true) : + forward_trust_chain_check(request, qry, true, packet) : trust_chain_check(request, qry); switch(state) { case KR_STATE_FAIL: return KR_STATE_FAIL;