/* 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"); } }
/** dump message entry */ static int dump_msg(SSL* ssl, struct query_info* k, struct reply_info* d, time_t now) { size_t i; char* nm, *tp, *cl; if(!k || !d) return 1; if(d->ttl < now) return 1; /* expired */ nm = sldns_wire2str_dname(k->qname, k->qname_len); tp = sldns_wire2str_type(k->qtype); cl = sldns_wire2str_class(k->qclass); if(!nm || !tp || !cl) { free(nm); free(tp); free(cl); return 1; /* skip this entry */ } if(!rrset_array_lock(d->ref, d->rrset_count, now)) { /* rrsets have timed out or do not exist */ free(nm); free(tp); free(cl); return 1; /* skip this entry */ } /* meta line */ if(!ssl_printf(ssl, "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u\n", nm, cl, tp, (int)d->flags, (int)d->qdcount, (long long)(d->ttl-now), (int)d->security, (unsigned)d->an_numrrsets, (unsigned)d->ns_numrrsets, (unsigned)d->ar_numrrsets)) { free(nm); free(tp); free(cl); rrset_array_unlock(d->ref, d->rrset_count); return 0; } free(nm); free(tp); free(cl); for(i=0; i<d->rrset_count; i++) { if(!dump_msg_ref(ssl, d->rrsets[i])) { rrset_array_unlock(d->ref, d->rrset_count); return 0; } } rrset_array_unlock(d->ref, d->rrset_count); return 1; }
struct dns_msg* tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, struct regional* region, time_t now, struct regional* scratch) { struct dns_msg* msg; size_t i; if(now > r->ttl) return NULL; msg = gen_dns_msg(region, q, r->rrset_count); if(!msg) return NULL; msg->rep->flags = r->flags; msg->rep->qdcount = r->qdcount; msg->rep->ttl = r->ttl - now; if(r->prefetch_ttl > now) msg->rep->prefetch_ttl = r->prefetch_ttl - now; else msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; msg->rep->security = r->security; msg->rep->an_numrrsets = r->an_numrrsets; msg->rep->ns_numrrsets = r->ns_numrrsets; msg->rep->ar_numrrsets = r->ar_numrrsets; msg->rep->rrset_count = r->rrset_count; msg->rep->authoritative = r->authoritative; if(!rrset_array_lock(r->ref, r->rrset_count, now)) return NULL; if(r->an_numrrsets > 0 && (r->rrsets[0]->rk.type == htons( LDNS_RR_TYPE_CNAME) || r->rrsets[0]->rk.type == htons( LDNS_RR_TYPE_DNAME)) && !reply_check_cname_chain(q, r)) { /* cname chain is now invalid, reconstruct msg */ rrset_array_unlock(r->ref, r->rrset_count); return NULL; } if(r->security == sec_status_secure && !reply_all_rrsets_secure(r)) { /* message rrsets have changed status, revalidate */ rrset_array_unlock(r->ref, r->rrset_count); return NULL; } for(i=0; i<msg->rep->rrset_count; i++) { msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i], region, now); if(!msg->rep->rrsets[i]) { rrset_array_unlock(r->ref, r->rrset_count); return NULL; } } if(env) rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, r->rrset_count); else rrset_array_unlock(r->ref, r->rrset_count); return msg; }
/** answer query from the cache */ static int answer_from_cache(struct worker* worker, struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, struct comm_reply* repinfo, struct edns_data* edns) { time_t timenow = *worker->env.now; uint16_t udpsize = edns->udp_size; int secure; int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd) && worker->env.need_to_validate; /* see if it is possible */ if(rep->ttl < timenow) { /* the rrsets may have been updated in the meantime. * we will refetch the message format from the * authoritative server */ return 0; } if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow)) return 0; /* locked and ids and ttls are OK. */ /* check CNAME chain (if any) */ if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type == htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type == htons(LDNS_RR_TYPE_DNAME))) { if(!reply_check_cname_chain(qinfo, rep)) { /* cname chain invalid, redo iterator steps */ verbose(VERB_ALGO, "Cache reply: cname chain broken"); bail_out: rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); return 0; } } /* check security status of the cached answer */ if( rep->security == sec_status_bogus && must_validate) { /* BAD cached */ edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) return 0; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); if(worker->stats.extended) { worker->stats.ans_bogus ++; worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++; } return 1; } else if( rep->security == sec_status_unchecked && must_validate) { verbose(VERB_ALGO, "Cache reply: unchecked entry needs " "validation"); goto bail_out; /* need to validate cache entry first */ } else if(rep->security == sec_status_secure) { if(reply_all_rrsets_secure(rep)) secure = 1; else { if(must_validate) { verbose(VERB_ALGO, "Cache reply: secure entry" " changed status"); goto bail_out; /* rrset changed, re-verify */ } secure = 0; } } else secure = 0; edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) return 0; if(!reply_info_answer_encode(qinfo, rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); } /* cannot send the reply right now, because blocking network syscall * is bad while holding locks. */ rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); if(worker->stats.extended) { if(secure) worker->stats.ans_secure++; server_stats_insrcode(&worker->stats, repinfo->c->buffer); } /* go and return this buffer to the client */ return 1; }
/** dump message entry */ static int dump_msg(SSL* ssl, struct query_info* k, struct reply_info* d, uint32_t now) { size_t i; char* nm, *tp, *cl; ldns_rdf* rdf; ldns_status status; size_t pos; if(!k || !d) return 1; if(d->ttl < now) return 1; /* expired */ pos = 0; status = ldns_wire2dname(&rdf, k->qname, k->qname_len, &pos); if(status != LDNS_STATUS_OK) { return 1; /* skip this entry */ } nm = ldns_rdf2str(rdf); ldns_rdf_deep_free(rdf); tp = ldns_rr_type2str(k->qtype); cl = ldns_rr_class2str(k->qclass); if(!nm || !tp || !cl) { free(nm); free(tp); free(cl); return 1; /* skip this entry */ } if(!rrset_array_lock(d->ref, d->rrset_count, now)) { /* rrsets have timed out or do not exist */ free(nm); free(tp); free(cl); return 1; /* skip this entry */ } /* meta line */ if(!ssl_printf(ssl, "msg %s %s %s %d %d %u %d %u %u %u\n", nm, cl, tp, (int)d->flags, (int)d->qdcount, (unsigned)(d->ttl-now), (int)d->security, (unsigned)d->an_numrrsets, (unsigned)d->ns_numrrsets, (unsigned)d->ar_numrrsets)) { free(nm); free(tp); free(cl); rrset_array_unlock(d->ref, d->rrset_count); return 0; } free(nm); free(tp); free(cl); for(i=0; i<d->rrset_count; i++) { if(!dump_msg_ref(ssl, d->rrsets[i])) { rrset_array_unlock(d->ref, d->rrset_count); return 0; } } rrset_array_unlock(d->ref, d->rrset_count); return 1; }