/* Invalidate the message associated with query_info stored in message cache */ void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo) { hashvalue_t h; struct lruhash_entry* e; struct reply_info *r; size_t i, j; h = query_info_hash(qinfo, qstate->query_flags); if ((e=slabhash_lookup(qstate->env->msg_cache, h, qinfo, 0))) { r = (struct reply_info*)(e->data); if (r) { r->ttl = 0; if(rrset_array_lock(r->ref, r->rrset_count, *qstate->env->now)) { for(i=0; i< r->rrset_count; i++) { struct packed_rrset_data* data = (struct packed_rrset_data*) r->ref[i].key->entry.data; if(i>0 && r->ref[i].key == r->ref[i-1].key) continue; data->ttl = r->ttl; for(j=0; j<data->count + data->rrsig_count; j++) data->rr_ttl[j] = r->ttl; } rrset_array_unlock(r->ref, r->rrset_count); } } lock_rw_unlock(&e->lock); } else { log_info("invalidateQueryInCache: qinfo is not in cache"); } }
/** find data item in array, for write access, caller unlocks */ static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra, uint8_t* name, size_t namelen, int wr) { struct rate_key key; hashvalue_t h = dname_query_hash(name, 0xab); memset(&key, 0, sizeof(key)); key.name = name; key.namelen = namelen; key.entry.hash = h; return slabhash_lookup(infra->domain_rates, h, &key, wr); }
/** test hashtable using short sequence */ static void test_short_table(struct slabhash* table) { testkey_type* k = newkey(12); testkey_type* k2 = newkey(14); testdata_type* d = newdata(128); testdata_type* d2 = newdata(129); k->entry.data = d; k2->entry.data = d2; slabhash_insert(table, myhash(12), &k->entry, d, NULL); slabhash_insert(table, myhash(14), &k2->entry, d2, NULL); unit_assert( slabhash_lookup(table, myhash(12), k, 0) == &k->entry); lock_rw_unlock( &k->entry.lock ); unit_assert( slabhash_lookup(table, myhash(14), k2, 0) == &k2->entry); lock_rw_unlock( &k2->entry.lock ); slabhash_remove(table, myhash(12), k); slabhash_remove(table, myhash(14), k2); }
/** lookup version that does not check host ttl (you check it) */ struct lruhash_entry* infra_lookup_nottl(struct infra_cache* infra, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name, size_t namelen, int wr) { struct infra_key k; k.addrlen = addrlen; memcpy(&k.addr, addr, addrlen); k.namelen = namelen; k.zonename = name; k.entry.hash = hash_infra(addr, addrlen, name); k.entry.key = (void*)&k; k.entry.data = NULL; return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr); }
/** * Lookup exactly in the key cache. Returns pointer to locked entry. * Caller must unlock it after use. * @param kcache: the key cache. * @param name: for what name to look; uncompressed wireformat * @param namelen: length of the name. * @param key_class: class of the key. * @param wr: set true to get a writelock. * @return key entry, locked, or NULL if not found. No TTL checking is * performed. */ static struct key_entry_key* key_cache_search(struct key_cache* kcache, uint8_t* name, size_t namelen, uint16_t key_class, int wr) { struct lruhash_entry* e; struct key_entry_key lookfor; lookfor.entry.key = &lookfor; lookfor.name = name; lookfor.namelen = namelen; lookfor.key_class = key_class; key_entry_hash(&lookfor); e = slabhash_lookup(kcache->slab, lookfor.entry.hash, &lookfor, wr); if(!e) return NULL; return (struct key_entry_key*)e->key; }
/** test adding a random element */ static void testlookup(struct slabhash* table, testdata_type* ref[]) { int num = random() % HASHTESTMAX; testkey_type* key = newkey(num); struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0); testdata_type* data = en? (testdata_type*)en->data : NULL; if(en) { unit_assert(en->key); unit_assert(en->data); } if(0) log_info("lookup %d got %d, expect %d", num, en? data->data :-1, ref[num]? ref[num]->data : -1); unit_assert( data == ref[num] ); if(en) { lock_rw_unlock(&en->lock); } delkey(key); }
/** test adding a random element (unlimited range) */ static void testlookup_unlim(struct slabhash* table, testdata_type** ref) { int num = random() % (HASHTESTMAX*10); testkey_type* key = newkey(num); struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0); testdata_type* data = en? (testdata_type*)en->data : NULL; if(en) { unit_assert(en->key); unit_assert(en->data); } if(0 && ref) log_info("lookup unlim %d got %d, expect %d", num, en ? data->data :-1, ref[num] ? ref[num]->data : -1); if(data && ref) { /* its okay for !data, it fell off the lru */ unit_assert( data == ref[num] ); } if(en) { lock_rw_unlock(&en->lock); } delkey(key); }
/** lookup message in message cache */ static struct msgreply_entry* msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, time_t now, int wr) { struct lruhash_entry* e; struct query_info k; hashvalue_t h; k.qname = qname; k.qname_len = qnamelen; k.qtype = qtype; k.qclass = qclass; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, wr); if(!e) return NULL; if( now > ((struct reply_info*)e->data)->ttl ) { lock_rw_unlock(&e->lock); return NULL; } return (struct msgreply_entry*)e->key; }
struct dns_msg* dns_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, struct regional* region, struct regional* scratch, int no_partial) { struct lruhash_entry* e; struct query_info k; hashvalue_type h; time_t now = *env->now; struct ub_packed_rrset_key* rrset; /* lookup first, this has both NXdomains and ANSWER responses */ k.qname = qname; k.qname_len = qnamelen; k.qtype = qtype; k.qclass = qclass; k.local_alias = NULL; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); if(e) { struct msgreply_entry* key = (struct msgreply_entry*)e->key; struct reply_info* data = (struct reply_info*)e->data; struct dns_msg* msg = tomsg(env, &key->key, data, region, now, scratch); if(msg) { lock_rw_unlock(&e->lock); return msg; } /* could be msg==NULL; due to TTL or not all rrsets available */ lock_rw_unlock(&e->lock); } /* see if a DNAME exists. Checked for first, to enforce that DNAMEs * are more important, the CNAME is resynthesized and thus * consistent with the DNAME */ if(!no_partial && (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, LDNS_RR_TYPE_DNAME, 1))) { /* synthesize a DNAME+CNAME message based on this */ enum sec_status sec_status = sec_status_unchecked; struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k, &sec_status); if(msg) { struct ub_packed_rrset_key* cname_rrset; lock_rw_unlock(&rrset->entry.lock); /* now, after unlocking the DNAME rrset lock, * check the sec_status, and see if we need to look * up the CNAME record associated before it can * be used */ /* normally, only secure DNAMEs allowed from cache*/ if(sec_status == sec_status_secure) return msg; /* but if we have a CNAME cached with this name, then we * have previously already allowed this name to pass. * the next cache lookup is going to fetch that CNAME itself, * but it is better to have the (unsigned)DNAME + CNAME in * that case */ cname_rrset = rrset_cache_lookup( env->rrset_cache, qname, qnamelen, LDNS_RR_TYPE_CNAME, qclass, 0, now, 0); if(cname_rrset) { /* CNAME already synthesized by * synth_dname_msg routine, so we can * straight up return the msg */ lock_rw_unlock(&cname_rrset->entry.lock); return msg; } } else { lock_rw_unlock(&rrset->entry.lock); } } /* see if we have CNAME for this domain, * but not for DS records (which are part of the parent) */ if(!no_partial && qtype != LDNS_RR_TYPE_DS && (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) { uint8_t* wc = NULL; size_t wl; /* if the rrset is not a wildcard expansion, with wcname */ /* because, if we return that CNAME rrset on its own, it is * missing the NSEC or NSEC3 proof */ if(!(val_rrset_wildcard(rrset, &wc, &wl) && wc != NULL)) { struct dns_msg* msg = rrset_msg(rrset, region, now, &k); if(msg) { lock_rw_unlock(&rrset->entry.lock); return msg; } } lock_rw_unlock(&rrset->entry.lock); } /* construct DS, DNSKEY, DLV messages from rrset cache. */ if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY || qtype == LDNS_RR_TYPE_DLV) && (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, qtype, qclass, 0, now, 0))) { /* if the rrset is from the additional section, and the * signatures have fallen off, then do not synthesize a msg * instead, allow a full query for signed results to happen. * Forego all rrset data from additional section, because * some signatures may not be present and cause validation * failure. */ struct packed_rrset_data *d = (struct packed_rrset_data*) rrset->entry.data; if(d->trust != rrset_trust_add_noAA && d->trust != rrset_trust_add_AA && (qtype == LDNS_RR_TYPE_DS || (d->trust != rrset_trust_auth_noAA && d->trust != rrset_trust_auth_AA) )) { struct dns_msg* msg = rrset_msg(rrset, region, now, &k); if(msg) { lock_rw_unlock(&rrset->entry.lock); return msg; } } lock_rw_unlock(&rrset->entry.lock); } /* stop downwards cache search on NXDOMAIN. * Empty nonterminals are NOERROR, so an NXDOMAIN for foo * means bla.foo also does not exist. The DNSSEC proofs are * the same. We search upwards for NXDOMAINs. */ if(env->cfg->harden_below_nxdomain) while(!dname_is_root(k.qname)) { dname_remove_label(&k.qname, &k.qname_len); h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); if(!e && k.qtype != LDNS_RR_TYPE_A && env->cfg->qname_minimisation) { k.qtype = LDNS_RR_TYPE_A; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); } if(e) { struct reply_info* data = (struct reply_info*)e->data; struct dns_msg* msg; if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN && data->security == sec_status_secure && (msg=tomsg(env, &k, data, region, now, scratch))){ lock_rw_unlock(&e->lock); msg->qinfo.qname=qname; msg->qinfo.qname_len=qnamelen; /* check that DNSSEC really works out */ msg->rep->security = sec_status_unchecked; return msg; } lock_rw_unlock(&e->lock); } k.qtype = qtype; } /* fill common RR types for ANY response to avoid requery */ if(qtype == LDNS_RR_TYPE_ANY) { return fill_any(env, qname, qnamelen, qtype, qclass, region); } return NULL; }
struct dns_msg* dns_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, struct regional* region, struct regional* scratch) { struct lruhash_entry* e; struct query_info k; hashvalue_t h; time_t now = *env->now; struct ub_packed_rrset_key* rrset; /* lookup first, this has both NXdomains and ANSWER responses */ k.qname = qname; k.qname_len = qnamelen; k.qtype = qtype; k.qclass = qclass; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); if(e) { struct msgreply_entry* key = (struct msgreply_entry*)e->key; struct reply_info* data = (struct reply_info*)e->data; struct dns_msg* msg = tomsg(env, &key->key, data, region, now, scratch); if(msg) { lock_rw_unlock(&e->lock); return msg; } /* could be msg==NULL; due to TTL or not all rrsets available */ lock_rw_unlock(&e->lock); } /* see if a DNAME exists. Checked for first, to enforce that DNAMEs * are more important, the CNAME is resynthesized and thus * consistent with the DNAME */ if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, LDNS_RR_TYPE_DNAME, 1))) { /* synthesize a DNAME+CNAME message based on this */ struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k); if(msg) { lock_rw_unlock(&rrset->entry.lock); return msg; } lock_rw_unlock(&rrset->entry.lock); } /* see if we have CNAME for this domain, * but not for DS records (which are part of the parent) */ if( qtype != LDNS_RR_TYPE_DS && (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) { struct dns_msg* msg = rrset_msg(rrset, region, now, &k); if(msg) { lock_rw_unlock(&rrset->entry.lock); return msg; } lock_rw_unlock(&rrset->entry.lock); } /* construct DS, DNSKEY, DLV messages from rrset cache. */ if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY || qtype == LDNS_RR_TYPE_DLV) && (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, qtype, qclass, 0, now, 0))) { /* if the rrset is from the additional section, and the * signatures have fallen off, then do not synthesize a msg * instead, allow a full query for signed results to happen. * Forego all rrset data from additional section, because * some signatures may not be present and cause validation * failure. */ struct packed_rrset_data *d = (struct packed_rrset_data*) rrset->entry.data; if(d->trust != rrset_trust_add_noAA && d->trust != rrset_trust_add_AA && (qtype == LDNS_RR_TYPE_DS || (d->trust != rrset_trust_auth_noAA && d->trust != rrset_trust_auth_AA) )) { struct dns_msg* msg = rrset_msg(rrset, region, now, &k); if(msg) { lock_rw_unlock(&rrset->entry.lock); return msg; } } lock_rw_unlock(&rrset->entry.lock); } /* stop downwards cache search on NXDOMAIN. * Empty nonterminals are NOERROR, so an NXDOMAIN for foo * means bla.foo also does not exist. The DNSSEC proofs are * the same. We search upwards for NXDOMAINs. */ if(env->cfg->harden_below_nxdomain) while(!dname_is_root(k.qname)) { dname_remove_label(&k.qname, &k.qname_len); h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); if(e) { struct reply_info* data = (struct reply_info*)e->data; struct dns_msg* msg; if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN && data->security == sec_status_secure && (msg=tomsg(env, &k, data, region, now, scratch))) { lock_rw_unlock(&e->lock); msg->qinfo.qname=qname; msg->qinfo.qname_len=qnamelen; /* check that DNSSEC really works out */ msg->rep->security = sec_status_unchecked; return msg; } lock_rw_unlock(&e->lock); } } return NULL; }