/** * Handle a cachedb module event with a query * @param qstate: query state (from the mesh), passed between modules. * contains qstate->env module environment with global caches and so on. * @param iq: query state specific for this module. per-query. * @param ie: environment specific for this module. global. * @param id: module id. */ static void cachedb_handle_query(struct module_qstate* qstate, struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id) { /* check if we are enabled, and skip if so */ if(!ie->enabled) { /* pass request to next module */ qstate->ext_state[id] = module_wait_module; return; } if(qstate->blacklist || qstate->no_cache_lookup) { /* cache is blacklisted or we are instructed from edns to not look */ /* pass request to next module */ qstate->ext_state[id] = module_wait_module; return; } /* lookup inside unbound's internal cache */ if(cachedb_intcache_lookup(qstate)) { if(verbosity >= VERB_ALGO) { if(qstate->return_msg->rep) log_dns_msg("cachedb internal cache lookup", &qstate->return_msg->qinfo, qstate->return_msg->rep); else log_info("cachedb internal cache lookup: rcode %s", sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)? sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name:"??"); } /* we are done with the query */ qstate->ext_state[id] = module_finished; return; } /* ask backend cache to see if we have data */ if(cachedb_extcache_lookup(qstate, ie)) { if(verbosity >= VERB_ALGO) log_dns_msg(ie->backend->name, &qstate->return_msg->qinfo, qstate->return_msg->rep); /* store this result in internal cache */ cachedb_intcache_store(qstate); /* we are done with the query */ qstate->ext_state[id] = module_finished; return; } /* no cache fetches */ /* pass request to next module */ qstate->ext_state[id] = module_wait_module; }
/* Create response according to the ldns packet content */ int createResponse(struct module_qstate* qstate, sldns_buffer* pkt) { struct msg_parse* prs; struct edns_data edns; /* parse message */ prs = (struct msg_parse*) regional_alloc(qstate->env->scratch, sizeof(struct msg_parse)); if (!prs) { log_err("storeResponse: out of memory on incoming message"); return 0; } memset(prs, 0, sizeof(*prs)); memset(&edns, 0, sizeof(edns)); sldns_buffer_set_position(pkt, 0); if (parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) { verbose(VERB_ALGO, "storeResponse: parse error on reply packet"); return 0; } /* edns is not examined, but removed from message to help cache */ if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR) return 0; /* remove CD-bit, we asked for in case we handle validation ourself */ prs->flags &= ~BIT_CD; /* allocate response dns_msg in region */ qstate->return_msg = (struct dns_msg*)regional_alloc(qstate->region, sizeof(struct dns_msg)); if (!qstate->return_msg) return 0; memset(qstate->return_msg, 0, sizeof(*qstate->return_msg)); if(!parse_create_msg(pkt, prs, NULL, &(qstate->return_msg)->qinfo, &(qstate->return_msg)->rep, qstate->region)) { log_err("storeResponse: malloc failure: allocating incoming dns_msg"); return 0; } /* Make sure that the RA flag is set (since the presence of * this module means that recursion is available) */ /* qstate->return_msg->rep->flags |= BIT_RA; */ /* Clear the AA flag */ /* FIXME: does this action go here or in some other module? */ /*qstate->return_msg->rep->flags &= ~BIT_AA; */ /* make sure QR flag is on */ /*qstate->return_msg->rep->flags |= BIT_QR; */ if(verbosity >= VERB_ALGO) log_dns_msg("storeResponse: packet:", &qstate->return_msg->qinfo, qstate->return_msg->rep); return 1; }
/** convert data from return_msg into the data buffer */ static int prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) { uint64_t timestamp, expiry; size_t oldlim; struct edns_data edns; memset(&edns, 0, sizeof(edns)); edns.edns_present = 1; edns.bits = EDNS_DO; edns.ext_rcode = 0; edns.edns_version = EDNS_ADVERTISED_VERSION; edns.udp_size = EDNS_ADVERTISED_SIZE; if(!qstate->return_msg || !qstate->return_msg->rep) return 0; /* We don't store the reply if its TTL is 0 unless serve-expired is * enabled. Such a reply won't be reusable and simply be a waste for * the backend. It's also compatible with the default behavior of * dns_cache_store_msg(). */ if(qstate->return_msg->rep->ttl == 0 && !qstate->env->cfg->serve_expired) return 0; if(verbosity >= VERB_ALGO) log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo, qstate->return_msg->rep); if(!reply_info_answer_encode(&qstate->return_msg->qinfo, qstate->return_msg->rep, 0, qstate->query_flags, buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0)) return 0; /* TTLs in the return_msg are relative to time(0) so we have to * store that, we also store the smallest ttl in the packet+time(0) * as the packet expiry time */ /* qstate->return_msg->rep->ttl contains that relative shortest ttl */ timestamp = (uint64_t)*qstate->env->now; expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl; timestamp = htobe64(timestamp); expiry = htobe64(expiry); oldlim = sldns_buffer_limit(buf); if(oldlim + sizeof(timestamp)+sizeof(expiry) >= sldns_buffer_capacity(buf)) return 0; /* doesn't fit. */ sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry)); sldns_buffer_write_at(buf, oldlim, ×tamp, sizeof(timestamp)); sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry, sizeof(expiry)); return 1; }
/** check RRSIGs in packet */ static void check_the_rrsigs(struct query_info* qinfo, struct reply_info* rep) { /* every RRSIG must be matched to an RRset */ size_t i; for(i=0; i<rep->rrset_count; i++) { struct ub_packed_rrset_key* s = rep->rrsets[i]; if(ntohs(s->rk.type) == LDNS_RR_TYPE_RRSIG) { /* see if really a problem, i.e. is there a data * element. */ if(no_data_for_rrsig(rep, rep->rrsets[i])) continue; log_dns_msg("rrsig failed for packet", qinfo, rep); print_packet_rrsets(qinfo, rep); printf("failed rrset is nr %d\n", (int)i); unit_assert(0); } } }
enum val_classification val_classify_response(uint16_t query_flags, struct query_info* origqinf, struct query_info* qinf, struct reply_info* rep, size_t skip) { int rcode = (int)FLAGS_GET_RCODE(rep->flags); size_t i; /* Normal Name Error's are easy to detect -- but don't mistake a CNAME * chain ending in NXDOMAIN. */ if(rcode == LDNS_RCODE_NXDOMAIN && rep->an_numrrsets == 0) return VAL_CLASS_NAMEERROR; /* check for referral: nonRD query and it looks like a nodata */ if(!(query_flags&BIT_RD) && rep->an_numrrsets == 0 && rcode == LDNS_RCODE_NOERROR) { /* SOA record in auth indicates it is NODATA instead. * All validation requiring NODATA messages have SOA in * authority section. */ /* uses fact that answer section is empty */ int saw_ns = 0; for(i=0; i<rep->ns_numrrsets; i++) { if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA) return VAL_CLASS_NODATA; if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DS) return VAL_CLASS_REFERRAL; if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS) saw_ns = 1; } return saw_ns?VAL_CLASS_REFERRAL:VAL_CLASS_NODATA; } /* root referral where NS set is in the answer section */ if(!(query_flags&BIT_RD) && rep->ns_numrrsets == 0 && rep->an_numrrsets == 1 && rcode == LDNS_RCODE_NOERROR && ntohs(rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_NS && query_dname_compare(rep->rrsets[0]->rk.dname, origqinf->qname) != 0) return VAL_CLASS_REFERRAL; /* dump bad messages */ if(rcode != LDNS_RCODE_NOERROR) return VAL_CLASS_UNKNOWN; log_assert(rcode == LDNS_RCODE_NOERROR); /* next check if the skip into the answer section shows no answer */ if(skip>0 && rep->an_numrrsets <= skip) return VAL_CLASS_CNAMENOANSWER; /* Next is NODATA */ if(rep->an_numrrsets == 0) return VAL_CLASS_NODATA; /* We distinguish between CNAME response and other positive/negative * responses because CNAME answers require extra processing. */ /* We distinguish between ANY and CNAME or POSITIVE because * ANY responses are validated differently. */ if(qinf->qtype == LDNS_RR_TYPE_ANY) return VAL_CLASS_ANY; /* Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless * qtype=CNAME, this will yield a CNAME response. */ for(i=skip; i<rep->an_numrrsets; i++) { if(ntohs(rep->rrsets[i]->rk.type) == qinf->qtype) return VAL_CLASS_POSITIVE; if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME) return VAL_CLASS_CNAME; } log_dns_msg("validator: error. failed to classify response message: ", qinf, rep); return VAL_CLASS_UNKNOWN; }