diff --git a/lib/resolve.c b/lib/resolve.c index f7f6f0aa86ae8a359996644ac3f74043ee211109..ae623568de2947767b15208390abad847d4369b1 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -322,46 +322,36 @@ int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet) return kr_rplan_empty(&request->rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE; } -int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet) +/** @internal Spawn subrequest in current zone cut (no minimization or lookup). */ +static int zone_cut_subreq(struct kr_rplan *rplan, struct kr_query *parent, + const knot_dname_t *qname, uint16_t qtype) { - struct kr_rplan *rplan = &request->rplan; - struct kr_query *qry = kr_rplan_current(rplan); - unsigned ns_election_iter = 0; - - /* No query left for resolution */ - if (kr_rplan_empty(rplan)) { - return KNOT_STATE_FAIL; - } - - /* Resolve current query and produce dependent or finish */ - ITERATE_LAYERS(request, produce, packet); - if (request->state != KNOT_STATE_FAIL && knot_wire_get_qr(packet->wire)) { - /* Produced an answer, consume it. */ - qry->secret = 0; - request->state = KNOT_STATE_CONSUME; - ITERATE_LAYERS(request, consume, packet); + struct kr_query *next = kr_rplan_push(rplan, parent, qname, parent->sclass, qtype); + if (!next) { + return kr_error(ENOMEM); } - switch(request->state) { - case KNOT_STATE_FAIL: return request->state; break; - case KNOT_STATE_CONSUME: break; - case KNOT_STATE_DONE: - default: /* Current query is done */ - if (qry->flags & QUERY_RESOLVED) { - kr_rplan_pop(rplan, qry); - } - ITERATE_LAYERS(request, reset); - return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE; + kr_zonecut_set(&next->zone_cut, parent->zone_cut.name); + if (kr_zonecut_copy(&next->zone_cut, &parent->zone_cut) != 0 || + kr_zonecut_copy_trust(&next->zone_cut, &parent->zone_cut) != 0) { + return kr_error(ENOMEM); } + next->flags |= QUERY_NO_MINIMIZE; + return kr_ok(); +} - /* The query wasn't resolved from cache, - * now it's the time to look up closest zone cut from cache. - */ - /* Always try with DNSSEC if it finds island of trust. */ - /* @todo this interface is going to change */ - if (kr_ta_contains(&global_trust_anchors, qry->zone_cut.name)) { +/** @internal Check current zone cut status and credibility, spawn subrequests if needed. */ +static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot_pkt_t *packet) +{ + struct kr_rplan *rplan = &request->rplan; + + /* Always try with DNSSEC if it finds an island of trust. */ + if (!(request->options & QUERY_DNSSEC_WANT) && + kr_ta_contains(&global_trust_anchors, qry->zone_cut.name)) { request->options |= QUERY_DNSSEC_WANT; DEBUG_MSG(">< entered island of trust\n"); } + /* The query wasn't resolved from cache, + * now it's the time to look up closest zone cut from cache. */ bool want_secured = (request->options & QUERY_DNSSEC_WANT); if (qry->flags & QUERY_AWAIT_CUT) { int ret = ns_fetch_cut(qry, request, want_secured); @@ -377,18 +367,9 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t qry->flags &= ~QUERY_AWAIT_CUT; } - /* fetch missing DS record. */ + /* Missing delegation signature, fetch it first. */ if ((qry->flags & QUERY_AWAIT_DS) && (qry->zone_cut.missing_name)) { - struct kr_query *next = kr_rplan_push(rplan, qry, qry->zone_cut.missing_name, KNOT_CLASS_IN, KNOT_RRTYPE_DS); - if (!next) { - return KNOT_STATE_FAIL; - } - kr_zonecut_set(&next->zone_cut, qry->zone_cut.parent_name); - int ret = kr_zonecut_copy(&next->zone_cut, &qry->zone_cut); - if (ret != 0) { - return KNOT_STATE_FAIL; - } - ret = kr_zonecut_copy_trust(&next->zone_cut, &qry->zone_cut); + int ret = zone_cut_subreq(rplan, qry, qry->zone_cut.missing_name, KNOT_RRTYPE_DS); if (ret != 0) { return KNOT_STATE_FAIL; } @@ -396,33 +377,67 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool); knot_rrset_free(&qry->zone_cut.trust_anchor, qry->zone_cut.pool); qry->flags &= ~QUERY_AWAIT_DS; - return KNOT_STATE_PRODUCE; + return KNOT_STATE_DONE; } /* Try to fetch missing DNSKEY. */ if (want_secured && !qry->zone_cut.key && qry->stype != KNOT_RRTYPE_DNSKEY) { - struct kr_query *next = kr_rplan_push(rplan, qry, qry->zone_cut.name, KNOT_CLASS_IN, KNOT_RRTYPE_DNSKEY); - if (!next) { - return KNOT_STATE_FAIL; - } - kr_zonecut_set(&next->zone_cut, qry->zone_cut.name); - int ret = kr_zonecut_copy(&next->zone_cut, &qry->zone_cut); + int ret = zone_cut_subreq(rplan, qry, qry->zone_cut.name, KNOT_RRTYPE_DNSKEY); if (ret != 0) { return KNOT_STATE_FAIL; } - ret = kr_zonecut_copy_trust(&next->zone_cut, &qry->zone_cut); - if (ret != 0) { - return KNOT_STATE_FAIL; + return KNOT_STATE_DONE; + } + + return KNOT_STATE_PRODUCE; +} + +int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet) +{ + struct kr_rplan *rplan = &request->rplan; + struct kr_query *qry = kr_rplan_current(rplan); + unsigned ns_election_iter = 0; + + /* No query left for resolution */ + if (kr_rplan_empty(rplan)) { + return KNOT_STATE_FAIL; + } + + /* Resolve current query and produce dependent or finish */ + ITERATE_LAYERS(request, produce, packet); + if (request->state != KNOT_STATE_FAIL && knot_wire_get_qr(packet->wire)) { + /* Produced an answer, consume it. */ + qry->secret = 0; + request->state = KNOT_STATE_CONSUME; + ITERATE_LAYERS(request, consume, packet); + } + switch(request->state) { + case KNOT_STATE_FAIL: return request->state; + case KNOT_STATE_CONSUME: break; + case KNOT_STATE_DONE: + default: /* Current query is done */ + if (qry->flags & QUERY_RESOLVED) { + kr_rplan_pop(rplan, qry); } - return KNOT_STATE_PRODUCE; + ITERATE_LAYERS(request, reset); + return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE; } + + /* Update zone cut, spawn new subrequests. */ + int state = zone_cut_check(request, qry, packet); + switch(state) { + case KNOT_STATE_FAIL: return KNOT_STATE_FAIL; + case KNOT_STATE_DONE: return KNOT_STATE_PRODUCE; + default: break; + } + ns_election: /* If the query has already selected a NS and is waiting for IPv4/IPv6 record, * elect best address only, otherwise elect a completely new NS. */ if(++ns_election_iter >= KR_ITER_LIMIT) { - DEBUG_MSG("=> couldn't agree NS decision, report this\n"); + DEBUG_MSG("=> couldn't converge NS selection, bail out\n"); return KNOT_STATE_FAIL; } if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {