]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/resove: WIP some improvements in zone cut detection in forward mode
authorGrigorii Demidov <grigorii.demidov@nic.cz>
Thu, 11 May 2017 08:28:17 +0000 (10:28 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Thu, 1 Jun 2017 14:27:16 +0000 (16:27 +0200)
lib/layer/iterate.c
lib/layer/iterate.h
lib/layer/validate.c
lib/resolve.c

index d3c79ae66316cb5c0ec0596c3e50e09ea5391f5e..8f4e08f77ed55e78cc6ee3163c2e23f169f67dc1 100644 (file)
@@ -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);
index 189aaf19b5652676b161dc70f16d39c37c919b4e..b211a594ca6ab9a6226381a38a7fc637ad63905c 100644 (file)
@@ -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);
index 52b7fa810c2341fb6c4b0f782927eb86ea8dc07b..798d7a03fe2bcf4b4b6c2bad731af9f4b83769df 100644 (file)
@@ -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;
 }
index 4163d43662c5c8b72d80d4cc9042d8ab4e8b444a..042ca5454dcde4e6994135bad14a722e74a2137e 100644 (file)
@@ -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;