diff --git a/src/dnssec.c b/src/dnssec.c index 26d14cca..95c8cbbf 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -814,13 +814,7 @@ static void add_question2val_chain(struct mem_funcs *mf, const uint8_t *qname, uint16_t qtype, uint16_t qclass, getdns_network_req *netreq) { - _getdns_rrset q_rrset; - uint8_t cname_spc[256]; - size_t cname_len = sizeof(cname_spc); - size_t anti_loop; - _getdns_rdf_iter rdf_spc, *rdf; - _getdns_rrtype_iter *rr, rr_spc; - + _getdns_rrset_spc q_rrset; chain_head *head; assert(pkt); @@ -828,43 +822,26 @@ static void add_question2val_chain(struct mem_funcs *mf, assert(qname); /* First find the canonical name for the question */ - q_rrset.name = qname; - q_rrset.rr_type = GETDNS_RRTYPE_CNAME; - q_rrset.rr_class = qclass; - q_rrset.pkt = pkt; - q_rrset.pkt_len = pkt_len; - q_rrset.sections = SECTION_ANSWER; + q_rrset.rrset.name = qname; + q_rrset.rrset.rr_type = qtype; + q_rrset.rrset.rr_class = qclass; + q_rrset.rrset.pkt = pkt; + q_rrset.rrset.pkt_len = pkt_len; + q_rrset.rrset.sections = SECTION_ANSWER; - for (anti_loop = MAX_CNAME_REFERRALS; anti_loop; anti_loop--) { - if (!(rr = _getdns_rrtype_iter_init(&rr_spc, &q_rrset))) - break; - if (!(rdf = _getdns_rdf_iter_init(&rdf_spc, &rr->rr_i))) - break; - q_rrset.name = _getdns_rdf_if_or_as_decompressed( - rdf, cname_spc, &cname_len); - } - - /* If the qtype was a CNAME, and we got one, we'r done. - * We asked for it directly, so no redirection applies. - * Otherwise we have to check the referred to name/qtype. - */ - if (qtype == GETDNS_RRTYPE_CNAME && q_rrset.name != qname) + if (_getdns_initialized_rrset_answer(&q_rrset)) return; - q_rrset.rr_type = qtype; - if (!(rr = _getdns_rrtype_iter_init(&rr_spc, &q_rrset))) { - /* No answer for the question. Add a head for this rrset - * anyway, to validate proof of non-existance, or to find - * proof that the packet is insecure. - */ - debug_sec_print_rrset("Adding NX rrset: ", &q_rrset); - head = add_rrset2val_chain(mf, chain_p, &q_rrset, netreq); + /* No answer for the question. Add a head for this rrset + * anyway, to validate proof of non-existance, or to find + * proof that the packet is insecure. + */ + debug_sec_print_rrset("Adding NX rrset: ", &q_rrset.rrset); + head = add_rrset2val_chain(mf, chain_p, &q_rrset.rrset, netreq); - /* On empty packet, find SOA (zonecut) for the qname */ - if (head && GLDNS_ANCOUNT(pkt) == 0 && GLDNS_NSCOUNT(pkt) == 0) - - val_chain_sched_soa(head, q_rrset.name); - } + /* On empty packet, find SOA (zonecut) for the qname */ + if (head && GLDNS_ANCOUNT(pkt) == 0 && GLDNS_NSCOUNT(pkt) == 0) + val_chain_sched_soa(head, q_rrset.rrset.name); } diff --git a/src/general.c b/src/general.c index a497fda3..4b80dbdd 100644 --- a/src/general.c +++ b/src/general.c @@ -84,76 +84,15 @@ static int no_answer(getdns_dns_req *dns_req) { getdns_network_req **netreq_p, *netreq; - int new_canonical = 0, cnames_followed; - uint8_t canon_spc[256]; - const uint8_t *canon; - size_t canon_len; - uint8_t owner_spc[256]; - const uint8_t *owner; - size_t owner_len; - - _getdns_rr_iter rr_spc, *rr; - _getdns_rdf_iter rdf_spc, *rdf; for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) { - if (netreq->response_len == 0 || - GLDNS_ANCOUNT(netreq->response) == 0) - continue; - canon = netreq->owner->name; - canon_len = netreq->owner->name_len; - if (netreq->request_type != GETDNS_RRTYPE_CNAME - && GLDNS_ANCOUNT(netreq->response) > 1) do { - new_canonical = 0, cnames_followed = 0; - for ( rr = _getdns_rr_iter_init(&rr_spc - , netreq->response - , netreq->response_len) - ; rr && _getdns_rr_iter_section(rr) - <= SECTION_ANSWER - ; rr = _getdns_rr_iter_next(rr)) { + _getdns_rrset_spc answer; - if (_getdns_rr_iter_section(rr) != - SECTION_ANSWER) - continue; - - if (gldns_read_uint16(rr->rr_type) != - GETDNS_RRTYPE_CNAME) - continue; - - owner = _getdns_owner_if_or_as_decompressed( - rr, owner_spc, &owner_len); - if (!_getdns_dname_equal(canon, owner)) - continue; - - if (!(rdf = _getdns_rdf_iter_init( - &rdf_spc, rr))) - continue; - - canon = _getdns_rdf_if_or_as_decompressed( - rdf, canon_spc, &canon_len); - new_canonical = 1; - cnames_followed++; - } - } while (new_canonical && cnames_followed<MAX_CNAME_REFERRALS); - for ( rr = _getdns_rr_iter_init(&rr_spc - , netreq->response - , netreq->response_len) - ; rr && _getdns_rr_iter_section(rr) - <= SECTION_ANSWER - ; rr = _getdns_rr_iter_next(rr)) { - - if (_getdns_rr_iter_section(rr) != - SECTION_ANSWER) - continue; - - if (gldns_read_uint16(rr->rr_type) != - netreq->request_type) - continue; - - owner = _getdns_owner_if_or_as_decompressed( - rr, owner_spc, &owner_len); - if (_getdns_dname_equal(canon, owner)) - return 0; - } + if (netreq->response_len > 0 && + GLDNS_ANCOUNT(netreq->response) > 0 && + _getdns_rrset_answer(&answer, netreq->response + , netreq->response_len)) + return 0; } return 1; } diff --git a/src/rr-iter.c b/src/rr-iter.c index 999d9607..e6a711de 100644 --- a/src/rr-iter.c +++ b/src/rr-iter.c @@ -240,6 +240,62 @@ _getdns_owner_if_or_as_decompressed(_getdns_rr_iter *i, ff_bytes, len, 0); } +_getdns_rrset * +_getdns_initialized_rrset_answer(_getdns_rrset_spc *query_rrset) +{ + _getdns_rrset *rrset = &query_rrset->rrset; + uint16_t qtype = rrset->rr_type; + size_t cname_loop_protection; + + assert(query_rrset); + + /* Follow CNAMEs */ + rrset->rr_type = GETDNS_RRTYPE_CNAME; + for ( cname_loop_protection = MAX_CNAME_REFERRALS + ; cname_loop_protection > 0 + ; cname_loop_protection-- ) { + _getdns_rrtype_iter rr_spc, *rr; + _getdns_rdf_iter rdf_spc, *rdf; + + if (!(rr = _getdns_rrtype_iter_init(&rr_spc, rrset))) + break; + if (!(rdf = _getdns_rdf_iter_init(&rdf_spc, &rr->rr_i))) + break; + query_rrset->name_len = sizeof(query_rrset->name_spc); + rrset->name = _getdns_rdf_if_or_as_decompressed( + rdf, query_rrset->name_spc, &query_rrset->name_len); + } + rrset->rr_type = qtype; + if (qtype == GETDNS_RRTYPE_CNAME && + cname_loop_protection < MAX_CNAME_REFERRALS) + return rrset; /* The CNAME was the answer */ + + return _getdns_rrset_has_rrs(rrset) ? rrset : NULL; +} + +_getdns_rrset * +_getdns_rrset_answer(_getdns_rrset_spc *spc, const uint8_t *pkt, size_t len) +{ + _getdns_rr_iter rr_spc, *rr; + + assert(spc); + + spc->rrset.name = NULL; + spc->name_len = sizeof(spc->name_spc); + if ( !(rr = _getdns_rr_iter_init(&rr_spc, pkt, len)) + || _getdns_rr_iter_section(rr) != SECTION_QUESTION + || !(spc->rrset.name = _getdns_owner_if_or_as_decompressed( + rr, spc->name_spc, &spc->name_len)) + || rr->nxt < rr->rr_type + 4) + return NULL; + + spc->rrset.rr_class = rr_iter_class(rr); + spc->rrset.rr_type = rr_iter_type(rr); + spc->rrset.pkt = pkt; + spc->rrset.pkt_len = len; + spc->rrset.sections = SECTION_ANSWER; + return _getdns_initialized_rrset_answer(spc); +} /* Utility function to compare owner name of rr with name */ static int rr_owner_equal(_getdns_rr_iter *rr, const uint8_t *name) diff --git a/src/rr-iter.h b/src/rr-iter.h index d9221df2..d657d484 100644 --- a/src/rr-iter.h +++ b/src/rr-iter.h @@ -108,7 +108,6 @@ static inline uint16_t rr_iter_type(_getdns_rr_iter *rr) static inline uint16_t rr_iter_class(_getdns_rr_iter *rr) { return rr->rr_type + 4 <= rr->nxt ? gldns_read_uint16(rr->rr_type + 2) : 0; } - typedef struct _getdns_rrset { const uint8_t *name; uint16_t rr_class; @@ -118,6 +117,18 @@ typedef struct _getdns_rrset { _getdns_section sections; } _getdns_rrset; +typedef struct _getdns_rrset_spc { + _getdns_rrset rrset; + uint8_t name_spc[256]; + size_t name_len; +} _getdns_rrset_spc; + +_getdns_rrset *_getdns_rrset_answer( + _getdns_rrset_spc *rrset2init, const uint8_t *pkt, size_t pkt_len); + +_getdns_rrset *_getdns_initialized_rrset_answer( + _getdns_rrset_spc *query_rrset); + typedef struct _getdns_rrtype_iter { _getdns_rr_iter rr_i; _getdns_rrset *rrset; @@ -164,6 +175,8 @@ _getdns_rrset_iter *_getdns_rrset_iter_init(_getdns_rrset_iter *i, const uint8_t *pkt, size_t pkt_len, _getdns_section sections); _getdns_rrset_iter *_getdns_rrset_iter_next(_getdns_rrset_iter *i); + + static inline _getdns_rrset *_getdns_rrset_iter_value(_getdns_rrset_iter *i) { return i && i->rr_i.pos ? &i->rrset : NULL; } diff --git a/src/util-internal.c b/src/util-internal.c index 508ff1f4..22a5e0a8 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -553,17 +553,16 @@ _getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, size_t bin_size; const uint8_t *bin_data; _getdns_section section; - uint8_t canonical_name_space[256], owner_name_space[256], - query_name_space[256]; - const uint8_t *canonical_name, *owner_name, *query_name; - size_t canonical_name_len = sizeof(canonical_name_space), - owner_name_len = sizeof(owner_name_space), + uint8_t owner_name_space[256], query_name_space[256]; + const uint8_t *owner_name, *query_name; + size_t owner_name_len = sizeof(owner_name_space), query_name_len = sizeof(query_name_space); - int new_canonical = 0, cnames_followed, - request_answered, all_numeric_label; + int all_numeric_label; uint16_t rr_type; getdns_dict *header = NULL; getdns_list *bad_dns = NULL; + _getdns_rrset_spc answer_spc; + _getdns_rrset *answer; if (!result) goto error; @@ -593,9 +592,6 @@ _getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, goto error; header = NULL; - canonical_name = req->owner->name; - canonical_name_len = req->owner->name_len; - if (req->query && (rr_iter = _getdns_rr_iter_init(&rr_iter_storage, req->query , req->response - req->query))) @@ -652,23 +648,6 @@ _getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, goto error; else rr_dict = NULL; - if (rr_type == GETDNS_RRTYPE_CNAME) { - - owner_name_len = sizeof(owner_name_space); - owner_name = _getdns_owner_if_or_as_decompressed( - rr_iter, owner_name_space, &owner_name_len); - if (!_getdns_dname_equal(canonical_name, owner_name)) - continue; - - if (!(rdf_iter = _getdns_rdf_iter_init( - &rdf_iter_storage, rr_iter))) - continue; - - new_canonical = 1; - canonical_name = _getdns_rdf_if_or_as_decompressed( - rdf_iter,canonical_name_space,&canonical_name_len); - continue; - } if (srvs->capacity && rr_type == GETDNS_RRTYPE_SRV) { if (srvs->count >= srvs->capacity && !_grow_srvs(&context->mf, srvs)) @@ -724,42 +703,14 @@ _getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, if (getdns_dict_set_int(result, "answer_type", GETDNS_NAMETYPE_DNS)) goto error; - cnames_followed = new_canonical; - while (cnames_followed < MAX_CNAME_REFERRALS && new_canonical) { - new_canonical = 0; + + answer = _getdns_rrset_answer(&answer_spc, req->response + , req->response_len); - for ( rr_iter = _getdns_rr_iter_init(&rr_iter_storage - , req->response - , req->response_len) - ; rr_iter && _getdns_rr_iter_section(rr_iter) - <= SECTION_ANSWER - ; rr_iter = _getdns_rr_iter_next(rr_iter)) { - - if (_getdns_rr_iter_section(rr_iter) != - SECTION_ANSWER) - continue; - - if (gldns_read_uint16(rr_iter->rr_type) != - GETDNS_RRTYPE_CNAME) - continue; - - owner_name = _getdns_owner_if_or_as_decompressed( - rr_iter, owner_name_space, &owner_name_len); - if (!_getdns_dname_equal(canonical_name, owner_name)) - continue; - - if (!(rdf_iter = _getdns_rdf_iter_init( - &rdf_iter_storage, rr_iter))) - continue; - - canonical_name = _getdns_rdf_if_or_as_decompressed( - rdf_iter,canonical_name_space,&canonical_name_len); - new_canonical = 1; - cnames_followed++; - } - } - if (_getdns_dict_set_const_bindata( - result, "canonical_name", canonical_name_len, canonical_name)) + if (answer_spc.rrset.name && + _getdns_dict_set_const_bindata(result, "canonical_name" + , answer_spc.name_len + , answer_spc.rrset.name)) goto error; if (!req->owner->add_warning_for_bad_dns) @@ -768,35 +719,15 @@ _getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, if (!(bad_dns = getdns_list_create_with_context(context))) goto error; - if (cnames_followed && req->request_type != GETDNS_RRTYPE_CNAME) { - request_answered = 0; - for ( rr_iter = _getdns_rr_iter_init(&rr_iter_storage - , req->response - , req->response_len) - ; rr_iter && _getdns_rr_iter_section(rr_iter) - <= SECTION_ANSWER - ; rr_iter = _getdns_rr_iter_next(rr_iter)) { + if ( !answer + && req->request_type != GETDNS_RRTYPE_CNAME + && query_name + && answer_spc.rrset.name + && !_getdns_dname_equal(query_name, answer_spc.rrset.name) + && _getdns_list_append_int(bad_dns + , GETDNS_BAD_DNS_CNAME_RETURNED_FOR_OTHER_TYPE)) + goto error; - if (_getdns_rr_iter_section(rr_iter) != - SECTION_ANSWER) - continue; - if (gldns_read_uint16(rr_iter->rr_type) != - req->request_type) - continue; - - owner_name=_getdns_owner_if_or_as_decompressed( - rr_iter, owner_name_space,&owner_name_len); - if (_getdns_dname_equal( - canonical_name, owner_name)) { - request_answered = 1; - break; - } - } - if (!request_answered && - _getdns_list_append_int(bad_dns, - GETDNS_BAD_DNS_CNAME_RETURNED_FOR_OTHER_TYPE)) - goto error; - } all_numeric_label = 0; for ( rr_iter = _getdns_rr_iter_init(&rr_iter_storage , req->response