struct ub_packed_rrset_key* reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep) { uint8_t* sname = qinfo->qname; size_t snamelen = qinfo->qname_len; size_t i; for(i=0; i<rep->an_numrrsets; i++) { struct ub_packed_rrset_key* s = rep->rrsets[i]; /* first match type, for query of qtype cname */ if(ntohs(s->rk.type) == qinfo->qtype && ntohs(s->rk.rrset_class) == qinfo->qclass && snamelen == s->rk.dname_len && query_dname_compare(sname, s->rk.dname) == 0) { return s; } /* follow CNAME chain (if any) */ if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && ntohs(s->rk.rrset_class) == qinfo->qclass && snamelen == s->rk.dname_len && query_dname_compare(sname, s->rk.dname) == 0) { get_cname_target(s, &sname, &snamelen); } } return NULL; }
static int generate_cname_request(struct module_qstate* qstate, struct ub_packed_rrset_key* alias_rrset) { struct module_qstate* subq = NULL; struct query_info subqi; memset(&subqi, 0, sizeof(subqi)); get_cname_target(alias_rrset, &subqi.qname, &subqi.qname_len); if(!subqi.qname) return 0; /* unexpected: not a valid CNAME RDATA */ subqi.qtype = qstate->qinfo.qtype; subqi.qclass = qstate->qinfo.qclass; fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); return (*qstate->env->attach_sub)(qstate, &subqi, BIT_RD, 0, 0, &subq); }
int val_chase_cname(struct query_info* qchase, struct reply_info* rep, size_t* cname_skip) { size_t i; /* skip any DNAMEs, go to the CNAME for next part */ for(i = *cname_skip; i < rep->an_numrrsets; i++) { if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME && query_dname_compare(qchase->qname, rep->rrsets[i]-> rk.dname) == 0) { qchase->qname = NULL; get_cname_target(rep->rrsets[i], &qchase->qname, &qchase->qname_len); if(!qchase->qname) return 0; /* bad CNAME rdata */ (*cname_skip) = i+1; return 1; } } return 0; /* CNAME classified but no matching CNAME ?! */ }
int reply_check_cname_chain(struct reply_info* rep) { /* check only answer section rrs for matching cname chain. * the cache may return changed rdata, but owner names are untouched.*/ size_t i; uint8_t* sname = rep->rrsets[0]->rk.dname; size_t snamelen = rep->rrsets[0]->rk.dname_len; for(i=0; i<rep->an_numrrsets; i++) { uint16_t t = ntohs(rep->rrsets[i]->rk.type); if(t == LDNS_RR_TYPE_DNAME) continue; /* skip dnames; note TTL 0 not cached */ /* verify that owner matches current sname */ if(query_dname_compare(sname, rep->rrsets[i]->rk.dname) != 0){ /* cname chain broken */ return 0; } /* if this is a cname; move on */ if(t == LDNS_RR_TYPE_CNAME) { get_cname_target(rep->rrsets[i], &sname, &snamelen); } } return 1; }
/** synthesize DNAME+CNAME response from cached DNAME item */ static struct dns_msg* synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, time_t now, struct query_info* q, enum sec_status* sec_status) { struct dns_msg* msg; struct ub_packed_rrset_key* ck; struct packed_rrset_data* newd, *d = (struct packed_rrset_data*) rrset->entry.data; uint8_t* newname, *dtarg = NULL; size_t newlen, dtarglen; if(now > d->ttl) return NULL; /* only allow validated (with DNSSEC) DNAMEs used from cache * for insecure DNAMEs, query again. */ *sec_status = d->security; /* return sec status, so the status of the CNAME can be checked * by the calling routine. */ msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */ if(!msg) return NULL; msg->rep->flags = BIT_QR; /* reply, no AA, no error */ msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */ msg->rep->qdcount = 1; msg->rep->ttl = d->ttl - now; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); msg->rep->security = sec_status_unchecked; msg->rep->an_numrrsets = 1; msg->rep->ns_numrrsets = 0; msg->rep->ar_numrrsets = 0; msg->rep->rrset_count = 1; msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); if(!msg->rep->rrsets[0]) /* copy DNAME */ return NULL; /* synth CNAME rrset */ get_cname_target(rrset, &dtarg, &dtarglen); if(!dtarg) return NULL; newlen = q->qname_len + dtarglen - rrset->rk.dname_len; if(newlen > LDNS_MAX_DOMAINLEN) { msg->rep->flags |= LDNS_RCODE_YXDOMAIN; return msg; } newname = (uint8_t*)regional_alloc(region, newlen); if(!newname) return NULL; /* new name is concatenation of qname front (without DNAME owner) * and DNAME target name */ memcpy(newname, q->qname, q->qname_len-rrset->rk.dname_len); memmove(newname+(q->qname_len-rrset->rk.dname_len), dtarg, dtarglen); /* create rest of CNAME rrset */ ck = (struct ub_packed_rrset_key*)regional_alloc(region, sizeof(struct ub_packed_rrset_key)); if(!ck) return NULL; memset(&ck->entry, 0, sizeof(ck->entry)); msg->rep->rrsets[1] = ck; ck->entry.key = ck; ck->rk.type = htons(LDNS_RR_TYPE_CNAME); ck->rk.rrset_class = rrset->rk.rrset_class; ck->rk.flags = 0; ck->rk.dname = regional_alloc_init(region, q->qname, q->qname_len); if(!ck->rk.dname) return NULL; ck->rk.dname_len = q->qname_len; ck->entry.hash = rrset_key_hash(&ck->rk); newd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(struct packed_rrset_data) + sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t) + sizeof(uint16_t) + newlen); if(!newd) return NULL; ck->entry.data = newd; newd->ttl = 0; /* 0 for synthesized CNAME TTL */ newd->count = 1; newd->rrsig_count = 0; newd->trust = rrset_trust_ans_noAA; newd->rr_len = (size_t*)((uint8_t*)newd + sizeof(struct packed_rrset_data)); newd->rr_len[0] = newlen + sizeof(uint16_t); packed_rrset_ptr_fixup(newd); newd->rr_ttl[0] = newd->ttl; msg->rep->ttl = newd->ttl; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(newd->ttl); sldns_write_uint16(newd->rr_data[0], newlen); memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen); msg->rep->an_numrrsets ++; msg->rep->rrset_count ++; return msg; }