diff options
Diffstat (limited to 'services')
| -rw-r--r-- | services/authzone.c | 150 | ||||
| -rw-r--r-- | services/cache/dns.c | 110 | ||||
| -rw-r--r-- | services/cache/dns.h | 2 | ||||
| -rw-r--r-- | services/cache/infra.c | 3 | ||||
| -rw-r--r-- | services/cache/rrset.c | 37 | ||||
| -rw-r--r-- | services/listen_dnsport.c | 40 | ||||
| -rw-r--r-- | services/localzone.c | 179 | ||||
| -rw-r--r-- | services/localzone.h | 12 | ||||
| -rw-r--r-- | services/mesh.c | 130 | ||||
| -rw-r--r-- | services/mesh.h | 13 | ||||
| -rw-r--r-- | services/modstack.c | 7 | ||||
| -rw-r--r-- | services/outside_network.c | 65 | ||||
| -rw-r--r-- | services/outside_network.h | 7 | ||||
| -rw-r--r-- | services/rpz.c | 1 |
14 files changed, 511 insertions, 245 deletions
diff --git a/services/authzone.c b/services/authzone.c index 60ccc8698748..ebcdc7e43643 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -171,7 +171,7 @@ get_rrset_ttl(struct ub_packed_rrset_key* k) /** Copy rrset into region from domain-datanode and packet rrset */ static struct ub_packed_rrset_key* auth_packed_rrset_copy_region(struct auth_zone* z, struct auth_data* node, - struct auth_rrset* rrset, struct regional* region, time_t adjust) + struct auth_rrset* rrset, struct regional* region) { struct ub_packed_rrset_key key; memset(&key, 0, sizeof(key)); @@ -182,7 +182,7 @@ auth_packed_rrset_copy_region(struct auth_zone* z, struct auth_data* node, key.rk.type = htons(rrset->type); key.rk.rrset_class = htons(z->dclass); key.entry.hash = rrset_key_hash(&key.rk); - return packed_rrset_copy_region(&key, region, adjust); + return packed_rrset_copy_region(&key, region, 0); } /** fix up msg->rep TTL and prefetch ttl */ @@ -236,7 +236,7 @@ msg_add_rrset_an(struct auth_zone* z, struct regional* region, return 0; /* copy it */ if(!(msg->rep->rrsets[msg->rep->rrset_count] = - auth_packed_rrset_copy_region(z, node, rrset, region, 0))) + auth_packed_rrset_copy_region(z, node, rrset, region))) return 0; msg->rep->rrset_count++; msg->rep->an_numrrsets++; @@ -260,7 +260,7 @@ msg_add_rrset_ns(struct auth_zone* z, struct regional* region, return 0; /* copy it */ if(!(msg->rep->rrsets[msg->rep->rrset_count] = - auth_packed_rrset_copy_region(z, node, rrset, region, 0))) + auth_packed_rrset_copy_region(z, node, rrset, region))) return 0; msg->rep->rrset_count++; msg->rep->ns_numrrsets++; @@ -283,7 +283,7 @@ msg_add_rrset_ar(struct auth_zone* z, struct regional* region, return 0; /* copy it */ if(!(msg->rep->rrsets[msg->rep->rrset_count] = - auth_packed_rrset_copy_region(z, node, rrset, region, 0))) + auth_packed_rrset_copy_region(z, node, rrset, region))) return 0; msg->rep->rrset_count++; msg->rep->ar_numrrsets++; @@ -1369,6 +1369,10 @@ decompress_rr_into_buffer(struct sldns_buffer* buf, uint8_t* pkt, uncompressed_len = pkt_dname_len(&pktbuf); if(!uncompressed_len) return 0; /* parse error in dname */ + compressed_len = sldns_buffer_position( + &pktbuf) - oldpos; + if(compressed_len > rdlen) + return 0; /* dname exceeds rdata */ if(!sldns_buffer_available(buf, uncompressed_len)) /* dname too long for buffer */ @@ -1376,14 +1380,15 @@ decompress_rr_into_buffer(struct sldns_buffer* buf, uint8_t* pkt, dname_pkt_copy(&pktbuf, sldns_buffer_current(buf), rd); sldns_buffer_skip(buf, (ssize_t)uncompressed_len); - compressed_len = sldns_buffer_position( - &pktbuf) - oldpos; rd += compressed_len; rdlen -= compressed_len; count--; len = 0; break; case LDNS_RDF_TYPE_STR: + /* Check rdlen for resilience, because it is + * checked above, that rdlen > 0 */ + if(rdlen < 1) return 0; /* malformed */ len = rd[0] + 1; break; default: @@ -1391,6 +1396,8 @@ decompress_rr_into_buffer(struct sldns_buffer* buf, uint8_t* pkt, break; } if(len) { + if(len > rdlen) + return 0; /* malformed */ if(!sldns_buffer_available(buf, len)) return 0; /* too long for buffer */ sldns_buffer_write(buf, rd, len); @@ -1998,12 +2005,21 @@ auth_zone_get_serial(struct auth_zone* z, uint32_t* serial) struct auth_data* apex; struct auth_rrset* soa; struct packed_rrset_data* d; + size_t primlen, mboxlen; apex = az_find_name(z, z->name, z->namelen); if(!apex) return 0; soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA); if(!soa || soa->data->count==0) return 0; /* no RRset or no RRs in rrset */ if(soa->data->rr_len[0] < 2+4*5) return 0; /* SOA too short */ + if((primlen = dname_valid(soa->data->rr_data[0]+2, + soa->data->rr_len[0]-2)) == 0) + return 0; /* primary dname malformed */ + if((mboxlen = dname_valid(soa->data->rr_data[0]+2+primlen, + soa->data->rr_len[0]-2-primlen)) == 0) + return 0; /* mailbox dname malformed */ + if(2+primlen+mboxlen+4*5 != soa->data->rr_len[0]) + return 0; /* rdata malformed */ d = soa->data; *serial = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-20)); return 1; @@ -2016,12 +2032,21 @@ xfr_find_soa(struct auth_zone* z, struct auth_xfer* xfr) struct auth_data* apex; struct auth_rrset* soa; struct packed_rrset_data* d; + size_t primlen, mboxlen; apex = az_find_name(z, z->name, z->namelen); if(!apex) return 0; soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA); if(!soa || soa->data->count==0) return 0; /* no RRset or no RRs in rrset */ if(soa->data->rr_len[0] < 2+4*5) return 0; /* SOA too short */ + if((primlen = dname_valid(soa->data->rr_data[0]+2, + soa->data->rr_len[0]-2)) == 0) + return 0; /* primary dname malformed */ + if((mboxlen = dname_valid(soa->data->rr_data[0]+2+primlen, + soa->data->rr_len[0]-2-primlen)) == 0) + return 0; /* mailbox dname malformed */ + if(2+primlen+mboxlen+4*5 != soa->data->rr_len[0]) + return 0; /* rdata malformed */ /* SOA record ends with serial, refresh, retry, expiry, minimum, * as 4 byte fields */ d = soa->data; @@ -3990,6 +4015,22 @@ auth_master_copy(struct auth_master* o) return m; } +/** append the master to the copied list. */ +static int +auth_master_copy_and_append(struct auth_master* p, struct auth_master** list, + struct auth_master** last) +{ + struct auth_master* m = auth_master_copy(p); + if(!m) { + return 0; + } + m->next = NULL; + if(*last) (*last)->next = m; + if(!*list) *list = m; + *last = m; + return 1; +} + /** copy the master addresses from the task_probe lookups to the allow_notify * list of masters */ static void @@ -3998,17 +4039,27 @@ probe_copy_masters_for_allow_notify(struct auth_xfer* xfr) struct auth_master* list = NULL, *last = NULL; struct auth_master* p; /* build up new list with copies */ + /* The list in task probe has been looked up before the list in + * task transfer. */ + for(p = xfr->task_probe->masters; p; p=p->next) { + if(!auth_master_copy_and_append(p, &list, &last)) { + auth_free_masters(list); + /* failed because of malloc failure, use old list */ + return; + } + } + /* The list in task transfer also contains the http entries. */ for(p = xfr->task_transfer->masters; p; p=p->next) { - struct auth_master* m = auth_master_copy(p); - if(!m) { + /* Copy the http entries from this lookup. The allow_notify + * entries are not looked up from this list. The other + * ones are already in from the probe lookups. */ + if(!p->http) + continue; + if(!auth_master_copy_and_append(p, &list, &last)) { auth_free_masters(list); /* failed because of malloc failure, use old list */ return; } - m->next = NULL; - if(last) last->next = m; - if(!list) list = m; - last = m; } /* success, replace list */ auth_free_masters(xfr->allow_notify_list); @@ -4556,6 +4607,23 @@ http_parse_ttl(sldns_buffer* buf, struct sldns_file_parse_state* pstate) return 0; } +/** remove newlines from collated line */ +static void +chunkline_newline_removal(sldns_buffer* buf) +{ + size_t i, end=sldns_buffer_limit(buf); + for(i=0; i<end; i++) { + char c = (char)sldns_buffer_read_u8_at(buf, i); + if(c == '\n' && i==end-1) { + sldns_buffer_write_u8_at(buf, i, 0); + sldns_buffer_set_limit(buf, end-1); + return; + } + if(c == '\n') + sldns_buffer_write_u8_at(buf, i, (uint8_t)' '); + } +} + /** find noncomment RR line in chunks, collates lines if ( ) format */ static int chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos, @@ -4563,6 +4631,7 @@ chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos, { int ret; while(chunkline_get_line_collated(chunk, chunk_pos, buf)) { + chunkline_newline_removal(buf); if(chunkline_is_comment_line_or_empty(buf)) { /* a comment, go to next line */ continue; @@ -4638,23 +4707,6 @@ chunklist_sum(struct auth_chunk* list) return s; } -/** remove newlines from collated line */ -static void -chunkline_newline_removal(sldns_buffer* buf) -{ - size_t i, end=sldns_buffer_limit(buf); - for(i=0; i<end; i++) { - char c = (char)sldns_buffer_read_u8_at(buf, i); - if(c == '\n' && i==end-1) { - sldns_buffer_write_u8_at(buf, i, 0); - sldns_buffer_set_limit(buf, end-1); - return; - } - if(c == '\n') - sldns_buffer_write_u8_at(buf, i, (uint8_t)' '); - } -} - /** for http download, parse and add RR to zone */ static int http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z, @@ -6668,6 +6720,18 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) return 1; } +/** return true if there are probe (SOA UDP query) targets in the master list*/ +static int +have_probe_targets(struct auth_master* list) +{ + struct auth_master* p; + for(p=list; p; p = p->next) { + if(!p->allow_notify && p->host) + return 1; + } + return 0; +} + /** move to sending the probe packets, next if fails. task_probe */ static void xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env) @@ -6707,6 +6771,16 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env) verbose(VERB_ALGO, "auth zone %s probe: finished only_lookup", zname); } xfr_probe_disown(xfr); + if(!have_probe_targets(xfr->task_probe->masters)) { + /* If there are no masters to probe, go to transfer. */ + if(xfr->task_transfer->worker == NULL) { + xfr_start_transfer(xfr, env, NULL); + return; + } + /* The transfer is already in progress. */ + lock_basic_unlock(&xfr->lock); + return; + } if(xfr->task_nextprobe->worker == NULL) xfr_set_timeout(xfr, env, 0, 0); lock_basic_unlock(&xfr->lock); @@ -6863,18 +6937,6 @@ auth_xfer_timer(void* arg) } } -/** return true if there are probe (SOA UDP query) targets in the master list*/ -static int -have_probe_targets(struct auth_master* list) -{ - struct auth_master* p; - for(p=list; p; p = p->next) { - if(!p->allow_notify && p->host) - return 1; - } - return 0; -} - /** start task_probe if possible, if no masters for probe start task_transfer * returns true if task has been started, and false if the task is already * in progress. */ @@ -6886,7 +6948,9 @@ xfr_start_probe(struct auth_xfer* xfr, struct module_env* env, * progress (due to notify)) */ if(xfr->task_probe->worker == NULL) { if(!have_probe_targets(xfr->task_probe->masters) && - !(xfr->task_probe->only_lookup && + xfr->task_probe->masters != NULL) + xfr->task_probe->only_lookup = 1; + if(!(xfr->task_probe->only_lookup && xfr->task_probe->masters != NULL)) { /* useless to pick up task_probe, no masters to * probe. Instead attempt to pick up task transfer */ diff --git a/services/cache/dns.c b/services/cache/dns.c index 351b3568c80b..c593dcd72ceb 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -60,10 +60,10 @@ * @param rep: contains list of rrsets to store. * @param now: current time. * @param leeway: during prefetch how much leeway to update TTLs. - * This makes rrsets (other than type NS) timeout sooner so they get - * updated with a new full TTL. - * Type NS does not get this, because it must not be refreshed from the - * child domain, but keep counting down properly. + * This makes rrsets expire sooner so they get updated with a new full + * TTL. + * Child side type NS does get this but TTL checks are done using the time + * the query was created rather than the time the answer was received. * @param pside: if from parentside discovered NS, so that its NS is okay * in a prefetch situation to be updated (without becoming sticky). * @param qrep: update rrsets here if cache is better @@ -100,11 +100,20 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, rep->ref[i].id != rep->ref[i].key->id) ck = NULL; else ck = packed_rrset_copy_region( - rep->ref[i].key, region, now); + rep->ref[i].key, region, + ((ntohs(rep->ref[i].key->rk.type)== + LDNS_RR_TYPE_NS && !pside)?qstarttime:now)); lock_rw_unlock(&rep->ref[i].key->entry.lock); if(ck) { /* use cached copy if memory allows */ qrep->rrsets[i] = ck; + ttl = ((struct packed_rrset_data*) + ck->entry.data)->ttl; + if(ttl < qrep->ttl) { + qrep->ttl = ttl; + qrep->prefetch_ttl = PREFETCH_TTL_CALC(qrep->ttl); + qrep->serve_expired_ttl = qrep->ttl + SERVE_EXPIRED_TTL; + } } } /* no break: also copy key item */ @@ -169,10 +178,12 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, /* there was a reply_info_sortref(rep) here but it seems to be * unnecessary, because the cache gets locked per rrset. */ - reply_info_set_ttls(rep, *env->now); + if((flags & DNSCACHE_STORE_EXPIRED_MSG_CACHEDB)) { + reply_info_absolute_ttls(rep, *env->now, *env->now - ttl); + } else reply_info_set_ttls(rep, *env->now); store_rrsets(env, rep, *env->now, leeway, pside, qrep, region, qstarttime); - if(ttl == 0 && !(flags & DNSCACHE_STORE_ZEROTTL)) { + if(ttl == 0) { /* we do not store the message, but we did store the RRs, * which could be useful for delegation information */ verbose(VERB_ALGO, "TTL 0: dropped msg from cache"); @@ -221,8 +232,15 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, /* snip off front part of qname until the type is found */ while(qnamelen > 0) { - if((rrset = rrset_cache_lookup(env->rrset_cache, qname, - qnamelen, searchtype, qclass, 0, now, 0))) { + rrset = rrset_cache_lookup(env->rrset_cache, qname, + qnamelen, searchtype, qclass, 0, now, 0); + if(!rrset && searchtype == LDNS_RR_TYPE_DNAME) + /* If not found, for type DNAME, try 0TTL stored, + * for its grace period. */ + rrset = rrset_cache_lookup(env->rrset_cache, qname, + qnamelen, searchtype, qclass, + PACKED_RRSET_UPSTREAM_0TTL, now, 0); + if(rrset) { uint8_t* origqname = qname; size_t origqnamelen = qnamelen; if(!noexpiredabove) @@ -272,8 +290,10 @@ addr_to_additional(struct ub_packed_rrset_key* rrset, struct regional* region, { if((msg->rep->rrsets[msg->rep->rrset_count] = packed_rrset_copy_region(rrset, region, now))) { + struct packed_rrset_data* d = rrset->entry.data; msg->rep->ar_numrrsets++; msg->rep->rrset_count++; + UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl); } } @@ -456,8 +476,10 @@ find_add_ds(struct module_env* env, struct regional* region, /* add it to auth section. This is the second rrset. */ if((msg->rep->rrsets[msg->rep->rrset_count] = packed_rrset_copy_region(rrset, region, now))) { + struct packed_rrset_data* d = rrset->entry.data; msg->rep->ns_numrrsets++; msg->rep->rrset_count++; + UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl); } lock_rw_unlock(&rrset->entry.lock); } @@ -487,6 +509,8 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, return NULL; /* integer overflow protection */ msg->rep->flags = BIT_QR; /* with QR, no AA */ msg->rep->qdcount = 1; + msg->rep->ttl = MAX_TTL; /* will be updated (brought down) while we add + * rrsets to the message */ msg->rep->reason_bogus = LDNS_EDE_NONE; msg->rep->rrsets = (struct ub_packed_rrset_key**) regional_alloc(region, @@ -497,24 +521,28 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, } int -dns_msg_authadd(struct dns_msg* msg, struct regional* region, +dns_msg_authadd(struct dns_msg* msg, struct regional* region, struct ub_packed_rrset_key* rrset, time_t now) { - if(!(msg->rep->rrsets[msg->rep->rrset_count++] = + struct packed_rrset_data* d = rrset->entry.data; + if(!(msg->rep->rrsets[msg->rep->rrset_count++] = packed_rrset_copy_region(rrset, region, now))) return 0; msg->rep->ns_numrrsets++; + UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl); return 1; } int -dns_msg_ansadd(struct dns_msg* msg, struct regional* region, +dns_msg_ansadd(struct dns_msg* msg, struct regional* region, struct ub_packed_rrset_key* rrset, time_t now) { - if(!(msg->rep->rrsets[msg->rep->rrset_count++] = + struct packed_rrset_data* d = rrset->entry.data; + if(!(msg->rep->rrsets[msg->rep->rrset_count++] = packed_rrset_copy_region(rrset, region, now))) return 0; msg->rep->an_numrrsets++; + UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl); return 1; } @@ -585,6 +613,7 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num) sizeof(struct reply_info) - sizeof(struct rrset_ref)); if(!msg->rep) return NULL; + msg->rep->ttl = MAX_TTL; msg->rep->reason_bogus = LDNS_EDE_NONE; msg->rep->reason_bogus_str = NULL; if(num > RR_COUNT_MAX) @@ -606,13 +635,13 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, size_t i; int is_expired = 0; time_t now_control = now; - if(now > r->ttl) { + if(TTL_IS_EXPIRED(r->ttl, now)) { /* Check if we are allowed to serve expired */ if(!allow_expired || !reply_info_can_answer_expired(r, now)) return NULL; - /* Change the current time so we can pass the below TTL checks when - * serving expired data. */ - now_control = r->ttl - env->cfg->serve_expired_reply_ttl; + /* Change the current time so we can pass the below TTL checks + * when serving expired data. */ + now_control = 0; is_expired = 1; } @@ -620,15 +649,6 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, if(!msg) return NULL; msg->rep->flags = r->flags; msg->rep->qdcount = r->qdcount; - msg->rep->ttl = is_expired - ?SERVE_EXPIRED_REPLY_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->serve_expired_norec_ttl = 0; msg->rep->security = r->security; msg->rep->an_numrrsets = r->an_numrrsets; msg->rep->ns_numrrsets = r->ns_numrrsets; @@ -656,13 +676,30 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, return NULL; } for(i=0; i<msg->rep->rrset_count; i++) { + struct packed_rrset_data* d; 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; } + d = msg->rep->rrsets[i]->entry.data; + UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl); + } + if(msg->rep->rrset_count < 1) { + msg->rep->ttl = is_expired + ?SERVE_EXPIRED_REPLY_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); + } else { + /* msg->rep->ttl has been updated through the RRSets above */ + msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); } + msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; + msg->rep->serve_expired_norec_ttl = 0; if(env) rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, r->rrset_count); @@ -701,7 +738,7 @@ rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, struct dns_msg* msg; struct packed_rrset_data* d = (struct packed_rrset_data*) rrset->entry.data; - if(now > d->ttl) + if(TTL_IS_EXPIRED(d->ttl, now)) return NULL; msg = gen_dns_msg(region, q, 1); /* only the CNAME (or other) RRset */ if(!msg) @@ -736,8 +773,15 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, rrset->entry.data; uint8_t* newname, *dtarg = NULL; size_t newlen, dtarglen; - if(now > d->ttl) - return NULL; + time_t rr_ttl; + if(TTL_IS_EXPIRED(d->ttl, now)) { + /* Allow TTL=0 DNAME from upstream within grace period */ + if(!(rrset->rk.flags & PACKED_RRSET_UPSTREAM_0TTL)) + return NULL; + rr_ttl = 0; + } else { + rr_ttl = d->ttl - now; + } /* only allow validated (with DNSSEC) DNAMEs used from cache * for insecure DNAMEs, query again. */ *sec_status = d->security; @@ -749,7 +793,7 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, 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->ttl = rr_ttl; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; msg->rep->serve_expired_norec_ttl = 0; @@ -801,7 +845,7 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, if(!newd) return NULL; ck->entry.data = newd; - newd->ttl = d->ttl - now; /* RFC6672: synth CNAME TTL == DNAME TTL */ + newd->ttl = rr_ttl; /* RFC6672: synth CNAME TTL == DNAME TTL */ newd->count = 1; newd->rrsig_count = 0; newd->trust = rrset_trust_ans_noAA; @@ -844,6 +888,8 @@ fill_any(struct module_env* env, /* set NOTIMPL for RFC 8482 */ msg->rep->flags |= LDNS_RCODE_NOTIMPL; msg->rep->security = sec_status_indeterminate; + msg->rep->ttl = 1; /* empty NOTIMPL response will never be + * updated with rrsets, set TTL to 1 */ return msg; } @@ -1069,7 +1115,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf, msgqinf->qclass, flags, 0, 1); if(e) { struct reply_info* cached = e->entry.data; - if(cached->ttl < *env->now + if(TTL_IS_EXPIRED(cached->ttl, *env->now) && reply_info_could_use_expired(cached, *env->now) /* If we are validating make sure only * validating modules can update such messages. diff --git a/services/cache/dns.h b/services/cache/dns.h index 8aa6b44bc341..41d42e60d070 100644 --- a/services/cache/dns.h +++ b/services/cache/dns.h @@ -53,7 +53,7 @@ struct delegpt; * Must be an unsigned 32-bit value larger than 0xffff */ /** Allow caching a DNS message with a zero TTL. */ -#define DNSCACHE_STORE_ZEROTTL 0x100000 +#define DNSCACHE_STORE_EXPIRED_MSG_CACHEDB 0x100000 /** * Region allocated message reply diff --git a/services/cache/infra.c b/services/cache/infra.c index cf999422d002..4d2cd8d491f0 100644 --- a/services/cache/infra.c +++ b/services/cache/infra.c @@ -1269,7 +1269,8 @@ int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, int cookie_valid, struct config_file* cfg) { struct lruhash_entry* entry; - if(cfg->wait_limit == 0) + if(cfg->wait_limit == 0 || + (cookie_valid && cfg->wait_limit_cookie == 0)) return 1; entry = infra_find_ip_ratedata(infra, &rep->client_addr, diff --git a/services/cache/rrset.c b/services/cache/rrset.c index 6d5c24f8053e..c1716a565783 100644 --- a/services/cache/rrset.c +++ b/services/cache/rrset.c @@ -131,7 +131,7 @@ need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns) struct packed_rrset_data* newd = (struct packed_rrset_data*)nd; struct packed_rrset_data* cached = (struct packed_rrset_data*)cd; /* o if new data is expired, cached data is better */ - if( newd->ttl < timenow && timenow <= cached->ttl) + if( TTL_IS_EXPIRED(newd->ttl, timenow) && !TTL_IS_EXPIRED(cached->ttl, timenow)) return 0; /* o store if rrset has been validated * everything better than bogus data @@ -146,13 +146,13 @@ need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns) if( newd->trust > cached->trust ) { /* if the cached rrset is bogus, and new is equal, * do not update the TTL - let it expire. */ - if(equal && cached->ttl >= timenow && + if(equal && !TTL_IS_EXPIRED(cached->ttl, timenow) && cached->security == sec_status_bogus) return 0; return 1; } /* o item in cache has expired */ - if( cached->ttl < timenow ) + if( TTL_IS_EXPIRED(cached->ttl, timenow) ) return 1; /* o same trust, but different in data - insert it */ if( newd->trust == cached->trust && !equal ) { @@ -278,6 +278,10 @@ void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache, (void)rrset_cache_update(rrset_cache, &ref, alloc, timenow); } +/** Grace period in seconds for TTL=0 DNAME rrsets (RFC 2308: do not cache). + * Allows synthesis from cache within this window to reduce recursion load. */ +#define DNAME_TTL0_GRACE_SECONDS 1 + struct ub_packed_rrset_key* rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint32_t flags, time_t timenow, @@ -300,27 +304,36 @@ rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen, /* check TTL */ struct packed_rrset_data* data = (struct packed_rrset_data*)e->data; - if(timenow > data->ttl) { - lock_rw_unlock(&e->lock); - return NULL; + struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key; + if(TTL_IS_EXPIRED(data->ttl, timenow)) { + /* Allow TTL=0 DNAME within grace period for synthesis */ + if(qtype == LDNS_RR_TYPE_DNAME && + (k->rk.flags & PACKED_RRSET_UPSTREAM_0TTL) && + (timenow - data->ttl_add) <= DNAME_TTL0_GRACE_SECONDS) { + /* within grace: allow for synthesis */ + } else { + lock_rw_unlock(&e->lock); + return NULL; + } } /* we're done */ - return (struct ub_packed_rrset_key*)e->key; + return k; } return NULL; } -int +int rrset_array_lock(struct rrset_ref* ref, size_t count, time_t timenow) { size_t i; + struct packed_rrset_data* d; for(i=0; i<count; i++) { if(i>0 && ref[i].key == ref[i-1].key) continue; /* only lock items once */ lock_rw_rdlock(&ref[i].key->entry.lock); - if(ref[i].id != ref[i].key->id || timenow > - ((struct packed_rrset_data*)(ref[i].key->entry.data)) - ->ttl) { + d = ref[i].key->entry.data; + if(ref[i].id != ref[i].key->id || + TTL_IS_EXPIRED(d->ttl, timenow)) { /* failure! rollback our readlocks */ rrset_array_unlock(ref, i+1); return 0; @@ -511,7 +524,7 @@ rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, size_t* *qnamelen, searchtype, qclass, 0, 0, 0))) { struct packed_rrset_data* data = (struct packed_rrset_data*)rrset->entry.data; - if(now > data->ttl) { + if(TTL_IS_EXPIRED(data->ttl, now)) { /* it is expired, this is not wanted */ lock_rw_unlock(&rrset->entry.lock); log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass); diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index f7fcca194b40..5db2b940bc04 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -1564,7 +1564,7 @@ listen_create(struct comm_base* base, struct listen_port* ports, cp = comm_point_create_udp(base, ports->fd, front->udp_buff, ports->pp2_enabled, cb, cb_arg, ports->socket); - } else if(ports->ftype == listen_type_doq) { + } else if(ports->ftype == listen_type_doq && doq_table) { #ifndef HAVE_NGTCP2 log_warn("Unbound is not compiled with " "ngtcp2. This is required to use DNS " @@ -2300,21 +2300,8 @@ int tcp_req_info_handle_read_close(struct tcp_req_info* req) { verbose(VERB_ALGO, "tcp channel read side closed %d", req->cp->fd); - /* reset byte count for (potential) partial read */ - req->cp->tcp_byte_count = 0; - /* if we still have results to write, pick up next and write it */ - if(req->num_done_req != 0) { - tcp_req_pickup_next_result(req); - tcp_req_info_setup_listen(req); - return 1; - } - /* if nothing to do, this closes the connection */ - if(req->num_open_req == 0 && req->num_done_req == 0) - return 0; - /* otherwise, we must be waiting for dns resolve, wait with timeout */ - req->read_is_closed = 1; - tcp_req_info_setup_listen(req); - return 1; + /* RFC 7766 6.2.4 says to drop pending replies when client closes. */ + return 0; /* drop connection */ } void @@ -2884,6 +2871,7 @@ submit_http_error: sldns_buffer_flip(h2_stream->qbuffer); h2_session->postpone_drop = 1; query_read_done = http2_query_read_done(h2_session, h2_stream); + h2_session->postpone_drop = 0; if(query_read_done < 0) return NGHTTP2_ERR_CALLBACK_FAILURE; else if(!query_read_done) { @@ -2893,11 +2881,9 @@ submit_http_error: * failure will result in reclaiming (and closing) * of comm point. */ verbose(VERB_QUERY, "http2 query dropped in worker cb"); - h2_session->postpone_drop = 0; return NGHTTP2_ERR_CALLBACK_FAILURE; } /* nothing to submit right now, query added to mesh. */ - h2_session->postpone_drop = 0; return 0; } if(!http2_submit_dns_response(h2_session)) { @@ -3275,14 +3261,18 @@ nghttp2_session_callbacks* http2_req_callbacks_create(void) struct doq_table* doq_table_create(struct config_file* cfg, struct ub_randstate* rnd) { - struct doq_table* table = calloc(1, sizeof(*table)); + struct doq_table* table; + + if (!cfg->quic_port) + return NULL; + table = calloc(1, sizeof(*table)); if(!table) return NULL; #ifdef USE_NGTCP2_CRYPTO_OSSL /* Initialize the ossl crypto, it is harmless to call twice, * and this is before use of doq connections. */ if(ngtcp2_crypto_ossl_init() != 0) { - log_err("ngtcp2_crypto_oss_init failed"); + log_err("ngtcp2_crypto_ossl_init failed"); free(table); return NULL; } @@ -3354,7 +3344,7 @@ conn_tree_del(rbnode_type* node, void* arg) { struct doq_table* table = (struct doq_table*)arg; struct doq_conn* conn; - if(!node) + if(!node || !table) return; conn = (struct doq_conn*)node->key; if(conn->timer.timer_in_list) { @@ -3413,6 +3403,7 @@ doq_timer_find_time(struct doq_table* table, struct timeval* tv) { struct doq_timer key; struct rbnode_type* node; + log_assert(table != NULL); memset(&key, 0, sizeof(key)); key.time.tv_sec = tv->tv_sec; key.time.tv_usec = tv->tv_usec; @@ -3776,7 +3767,7 @@ doq_repinfo_retrieve_localaddr(struct comm_reply* repinfo, memset(sa6, 0, *localaddrlen); sa6->sin6_family = AF_INET6; memmove(&sa6->sin6_addr, &repinfo->pktinfo.v6info.ipi6_addr, - *localaddrlen); + sizeof(struct in6_addr)); sa6->sin6_port = repinfo->doq_srcport; #endif } else { @@ -3786,7 +3777,7 @@ doq_repinfo_retrieve_localaddr(struct comm_reply* repinfo, memset(sa, 0, *localaddrlen); sa->sin_family = AF_INET; memmove(&sa->sin_addr, &repinfo->pktinfo.v4info.ipi_addr, - *localaddrlen); + sizeof(struct in_addr)); sa->sin_port = repinfo->doq_srcport; #elif defined(IP_RECVDSTADDR) struct sockaddr_in* sa = (struct sockaddr_in*)localaddr; @@ -4922,6 +4913,7 @@ doq_conid_find(struct doq_table* table, const uint8_t* data, size_t datalen) key.node.key = &key; key.cid = (void*)data; key.cidlen = datalen; + log_assert(table != NULL); node = rbtree_search(table->conid_tree, &key); if(node) return (struct doq_conid*)node->key; @@ -5662,6 +5654,8 @@ doq_table_quic_size_available(struct doq_table* table, struct config_file* cfg, size_t mem) { size_t cur; + if (!table) + return 0; lock_basic_lock(&table->size_lock); cur = table->current_size; lock_basic_unlock(&table->size_lock); diff --git a/services/localzone.c b/services/localzone.c index 9ea98c250907..52166ae2d2bd 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -56,6 +56,24 @@ * with 16 bytes for an A record, a 64K packet has about 4000 max */ #define LOCALZONE_RRSET_COUNT_MAX 4096 +static const char* default_zones_reverse_array[] = { + "127.in-addr.arpa.", /* reverse ip4 zone */ + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", /* reverse ip6 zone */ + 0 +}; +const char** local_zones_default_reverse = default_zones_reverse_array; + +static const char* default_zones_special_array[] = { + "test.", /* RFC 6761 */ + "invalid.", /* RFC 6761 */ + "onion.", /* RFC 7686 */ + "home.arpa.", /* RFC 8375 */ + "resolver.arpa.", /* RFC 9462 */ + "service.arpa.", /* RFC 9665 */ + 0 +}; +const char** local_zones_default_special = default_zones_special_array; + /** print all RRsets in local zone */ static void local_zone_out(struct local_zone* z) @@ -650,7 +668,7 @@ lz_enter_rr_str(struct local_zones* zones, const char* rr) } labs = dname_count_size_labels(rr_name, &len); lock_rw_rdlock(&zones->lock); - z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type); + z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type, 1); if(!z) { lock_rw_unlock(&zones->lock); fatal_exit("internal error: no zone for rr %s", rr); @@ -834,7 +852,7 @@ lz_nodefault(struct config_file* cfg, const char* name) for(p = cfg->local_zones_nodefault; p; p = p->next) { /* compare zone name, lowercase, compare without ending . */ - if(strncasecmp(p->str, name, len) == 0 && + if(strncasecmp(p->str, name, len) == 0 && (strlen(p->str) == len || (strlen(p->str)==len+1 && p->str[len] == '.'))) return 1; @@ -842,6 +860,45 @@ lz_nodefault(struct config_file* cfg, const char* name) return 0; } +/** enter reverse default zone */ +static int +add_reverse_default(struct local_zones* zones, struct config_file* cfg, + const char* name) +{ + struct local_zone* z; + char str[1024]; /* known long enough */ + if(lz_exists(zones, name) || lz_nodefault(cfg, name)) + return 1; /* do not enter default content */ + if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN))) + return 0; + snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. " + "nobody.invalid. 1 3600 1200 604800 10800", name); + if(!lz_enter_rr_into_zone(z, str)) { + lock_rw_unlock(&z->lock); + return 0; + } + snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name); + if(!lz_enter_rr_into_zone(z, str)) { + lock_rw_unlock(&z->lock); + return 0; + } + if(strncasecmp("127.in-addr.arpa.", name, 17) == 0) { + if(!lz_enter_rr_into_zone(z, + "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) { + lock_rw_unlock(&z->lock); + return 0; + } + } else if(strncasecmp("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", name, 73) == 0) { + snprintf(str, sizeof(str), "%s 10800 IN PTR localhost.", name); + if(!lz_enter_rr_into_zone(z, str)) { + lock_rw_unlock(&z->lock); + return 0; + } + } + lock_rw_unlock(&z->lock); + return 1; +} + /** enter (AS112) empty default zone */ static int add_empty_default(struct local_zones* zones, struct config_file* cfg, @@ -902,72 +959,23 @@ int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg } lock_rw_unlock(&z->lock); } - /* reverse ip4 zone */ - if(!lz_exists(zones, "127.in-addr.arpa.") && - !lz_nodefault(cfg, "127.in-addr.arpa.")) { - if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static", - LDNS_RR_CLASS_IN)) || - !lz_enter_rr_into_zone(z, - "127.in-addr.arpa. 10800 IN NS localhost.") || - !lz_enter_rr_into_zone(z, - "127.in-addr.arpa. 10800 IN SOA localhost. " - "nobody.invalid. 1 3600 1200 604800 10800") || - !lz_enter_rr_into_zone(z, - "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) { + + /* ip4 and ip6 reverse */ + for(zstr = local_zones_default_reverse; *zstr; zstr++) { + if(!add_reverse_default(zones, cfg, *zstr)) { log_err("out of memory adding default zone"); - if(z) { lock_rw_unlock(&z->lock); } return 0; } - lock_rw_unlock(&z->lock); } - /* reverse ip6 zone */ - if(!lz_exists(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") && - !lz_nodefault(cfg, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.")) { - if(!(z=lz_enter_zone(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", "static", - LDNS_RR_CLASS_IN)) || - !lz_enter_rr_into_zone(z, - "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN NS localhost.") || - !lz_enter_rr_into_zone(z, - "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN SOA localhost. " - "nobody.invalid. 1 3600 1200 604800 10800") || - !lz_enter_rr_into_zone(z, - "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost.")) { + + /* special-use zones */ + for(zstr = local_zones_default_special; *zstr; zstr++) { + if(!add_empty_default(zones, cfg, *zstr)) { log_err("out of memory adding default zone"); - if(z) { lock_rw_unlock(&z->lock); } return 0; } - lock_rw_unlock(&z->lock); - } - /* home.arpa. zone (RFC 8375) */ - if(!add_empty_default(zones, cfg, "home.arpa.")) { - log_err("out of memory adding default zone"); - return 0; - } - /* resolver.arpa. zone (RFC 9462) */ - if(!add_empty_default(zones, cfg, "resolver.arpa.")) { - log_err("out of memory adding default zone"); - return 0; - } - /* service.arpa. zone (draft-ietf-dnssd-srp-25) */ - if(!add_empty_default(zones, cfg, "service.arpa.")) { - log_err("out of memory adding default zone"); - return 0; - } - /* onion. zone (RFC 7686) */ - if(!add_empty_default(zones, cfg, "onion.")) { - log_err("out of memory adding default zone"); - return 0; - } - /* test. zone (RFC 6761) */ - if(!add_empty_default(zones, cfg, "test.")) { - log_err("out of memory adding default zone"); - return 0; - } - /* invalid. zone (RFC 6761) */ - if(!add_empty_default(zones, cfg, "invalid.")) { - log_err("out of memory adding default zone"); - return 0; } + /* block AS112 zones, unless asked not to */ if(!cfg->unblock_lan_zones) { for(zstr = as112_zones; *zstr; zstr++) { @@ -1062,14 +1070,15 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg) labs = dname_count_size_labels(rr_name, &len); lock_rw_rdlock(&zones->lock); if(!local_zones_lookup(zones, rr_name, len, labs, rr_class, - rr_type)) { + rr_type, 1)) { /* Check if there is a zone that this could go * under but for different class; created zones are * always for LDNS_RR_CLASS_IN. Create the zone with * a different class but the same configured * local_zone_type. */ struct local_zone* z = local_zones_lookup(zones, - rr_name, len, labs, LDNS_RR_CLASS_IN, rr_type); + rr_name, len, labs, LDNS_RR_CLASS_IN, rr_type, + 1); if(z) { uint8_t* name = memdup(z->name, z->namelen); size_t znamelen = z->namelen; @@ -1231,28 +1240,48 @@ local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg) struct local_zone* local_zones_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype) + uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype, + int foradd) { return local_zones_tags_lookup(zones, name, len, labs, - dclass, dtype, NULL, 0, 1); + dclass, dtype, NULL, 0, 1, foradd); } struct local_zone* local_zones_tags_lookup(struct local_zones* zones, uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype, - uint8_t* taglist, size_t taglen, int ignoretags) + uint8_t* taglist, size_t taglen, int ignoretags, int foradd) { rbnode_type* res = NULL; struct local_zone *result; struct local_zone key; int m; + key.node.key = &key; + key.dclass = dclass; /* for type DS use a zone higher when on a zonecut */ if(dtype == LDNS_RR_TYPE_DS && !dname_is_root(name)) { - dname_remove_label(&name, &len); - labs--; + /* If this is at a zone cut, of a local-zone, and it is + * of type always_refuse. Then also refuse the type DS + * for it. That could make it DNSSEC bogus, but it is + * REFUSED anyway. It stops CNAME type answers in the + * type DS lookup. */ + key.name = name; + key.namelen = len; + key.namelabs = labs; + /* For additions and removals, use the ordinary rule, + * to remove a label for type DS to locate the parent zone. + * That is where the DS RR needs to be put. */ + if(!foradd && + (result=(struct local_zone*)rbtree_search( + &zones->ztree, &key)) != NULL && + result->type == local_zone_always_refuse) { + /* The type DS does not go up one label. */ + return result; + } else { + dname_remove_label(&name, &len); + labs--; + } } - key.node.key = &key; - key.dclass = dclass; key.name = name; key.namelen = len; key.namelabs = labs; @@ -1863,7 +1892,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env, if(view->local_zones && (z = local_zones_lookup(view->local_zones, qinfo->qname, qinfo->qname_len, labs, - qinfo->qclass, qinfo->qtype))) { + qinfo->qclass, qinfo->qtype, 0))) { lock_rw_rdlock(&z->lock); lzt = z->type; } @@ -1897,7 +1926,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env, lock_rw_rdlock(&zones->lock); if(!(z = local_zones_tags_lookup(zones, qinfo->qname, qinfo->qname_len, labs, qinfo->qclass, qinfo->qtype, - taglist, taglen, 0))) { + taglist, taglen, 0, 0))) { lock_rw_unlock(&zones->lock); return 0; } @@ -2102,7 +2131,8 @@ local_zones_add_RR(struct local_zones* zones, const char* rr) /* could first try readlock then get writelock if zone does not exist, * but we do not add enough RRs (from multiple threads) to optimize */ lock_rw_wrlock(&zones->lock); - z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type); + z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type, + 1); if(!z) { z = local_zones_add_zone(zones, rr_name, len, labs, rr_class, local_zone_transparent); @@ -2180,7 +2210,8 @@ void local_zones_del_data(struct local_zones* zones, /* remove DS */ lock_rw_rdlock(&zones->lock); - z = local_zones_lookup(zones, name, len, labs, dclass, LDNS_RR_TYPE_DS); + z = local_zones_lookup(zones, name, len, labs, dclass, LDNS_RR_TYPE_DS, + 1); if(z) { lock_rw_wrlock(&z->lock); d = local_zone_find_data(z, name, len, labs); @@ -2194,7 +2225,7 @@ void local_zones_del_data(struct local_zones* zones, /* remove other types */ lock_rw_rdlock(&zones->lock); - z = local_zones_lookup(zones, name, len, labs, dclass, 0); + z = local_zones_lookup(zones, name, len, labs, dclass, 0, 1); if(!z) { /* no such zone, we're done */ lock_rw_unlock(&zones->lock); diff --git a/services/localzone.h b/services/localzone.h index 66102fd98f7e..76c011836030 100644 --- a/services/localzone.h +++ b/services/localzone.h @@ -57,6 +57,9 @@ struct sldns_buffer; struct comm_reply; struct config_strlist; +extern const char** local_zones_default_special; +extern const char** local_zones_default_reverse; + /** * Local zone type * This type determines processing for queries that did not match @@ -262,11 +265,13 @@ void local_zone_delete(struct local_zone* z); * @param taglen: length of taglist. * @param ignoretags: lookup zone by name and class, regardless the * local-zone's tags. + * @param foradd: if the lookup is for addition or removal of the type. + * Used for type DS. The lookup for answers turns this off. * @return closest local_zone or NULL if no covering zone is found. */ struct local_zone* local_zones_tags_lookup(struct local_zones* zones, uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype, - uint8_t* taglist, size_t taglen, int ignoretags); + uint8_t* taglist, size_t taglen, int ignoretags, int foradd); /** * Lookup zone that contains the given name, class. @@ -278,10 +283,13 @@ struct local_zone* local_zones_tags_lookup(struct local_zones* zones, * @param dclass: class to lookup. * @param dtype: type of the record, if type DS then a zone higher up is found * pass 0 to just plain find a zone for a name. + * @param foradd: if the lookup is for addition or removal of the type. + * Used for type DS. The lookup for answers turns this off. * @return closest local_zone or NULL if no covering zone is found. */ struct local_zone* local_zones_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype); + uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype, + int foradd); /** * Debug helper. Print all zones diff --git a/services/mesh.c b/services/mesh.c index 3212a6abf4c6..433aabc9cb19 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -231,6 +231,7 @@ mesh_create(struct module_stack* stack, struct module_env* env) mesh->ans_expired = 0; mesh->ans_cachedb = 0; mesh->num_queries_discard_timeout = 0; + mesh->num_queries_replyaddr_limit = 0; mesh->num_queries_wait_limit = 0; mesh->num_dns_error_reports = 0; mesh->max_reply_states = env->cfg->num_queries_per_thread; @@ -348,7 +349,7 @@ mesh_serve_expired_lookup(struct module_qstate* qstate, key = (struct msgreply_entry*)e->key; data = (struct reply_info*)e->data; - if(data->ttl < timenow) *is_expired = 1; + if(TTL_IS_EXPIRED(data->ttl, timenow)) *is_expired = 1; msg = tomsg(qstate->env, &key->key, data, qstate->region, timenow, qstate->env->cfg->serve_expired, qstate->env->scratch); if(!msg) @@ -441,9 +442,18 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, if(!infra_wait_limit_allowed(mesh->env->infra_cache, rep, edns->cookie_valid, mesh->env->cfg)) { verbose(VERB_ALGO, "Too many queries waiting from the IP. " - "dropping incoming query."); - comm_point_drop_reply(rep); + "servfail incoming query."); mesh->num_queries_wait_limit++; + edns_opt_list_append_ede(&edns->opt_list_out, + mesh->env->scratch, LDNS_EDE_OTHER, + "Too many queries queued up and waiting from the IP"); + if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL, + LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv)) + edns->opt_list_inplace_cb_out = NULL; + error_encode(r_buffer, LDNS_RCODE_SERVFAIL, + qinfo, qid, qflags, edns); + regional_free_all(mesh->env->scratch); + comm_point_send_reply(rep); return; } if(!unique) @@ -453,6 +463,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, if(!mesh_make_new_space(mesh, rep->c->buffer)) { verbose(VERB_ALGO, "Too many queries. dropping " "incoming query."); + if(rep->c->use_h2) + http2_stream_remove_mesh_state(rep->c->h2_stream); comm_point_drop_reply(rep); mesh->stats_dropped++; return; @@ -464,8 +476,10 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, if(mesh->num_reply_addrs > mesh->max_reply_states*16) { verbose(VERB_ALGO, "Too many requests queued. " "dropping incoming query."); + if(rep->c->use_h2) + http2_stream_remove_mesh_state(rep->c->h2_stream); comm_point_drop_reply(rep); - mesh->stats_dropped++; + mesh->num_queries_replyaddr_limit++; return; } } @@ -1022,6 +1036,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo, mstate->s.no_cache_store = 0; mstate->s.need_refetch = 0; mstate->s.was_ratelimited = 0; + mstate->s.error_response_cache = 0; mstate->s.qstarttime = *env->now; /* init modules */ @@ -1061,6 +1076,14 @@ mesh_state_cleanup(struct mesh_state* mstate) if(!mstate->replies_sent) { struct mesh_reply* rep = mstate->reply_list; struct mesh_cb* cb; + /* One http2 stream could bring down its comm_point along with + * the other streams which could share the same query. Do all + * the http2 stream bookkeeping upfront. */ + for(; rep; rep=rep->next) { + if(rep->query_reply.c->use_h2) + http2_stream_remove_mesh_state(rep->h2_stream); + } + rep = mstate->reply_list; /* in tcp_req_info, the mstates linked are removed, but * the reply_list is now NULL, so the remove-from-empty-list * takes no time and also it does not do the mesh accounting */ @@ -1068,8 +1091,6 @@ mesh_state_cleanup(struct mesh_state* mstate) for(; rep; rep=rep->next) { infra_wait_limit_dec(mesh->env->infra_cache, &rep->query_reply, mesh->env->cfg); - if(rep->query_reply.c->use_h2) - http2_stream_remove_mesh_state(rep->h2_stream); comm_point_drop_reply(&rep->query_reply); log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; @@ -1152,8 +1173,7 @@ mesh_detect_cycle_found(struct module_qstate* qstate, struct mesh_state* dep_m) { struct mesh_state* cyc_m = qstate->mesh_info; size_t counter = 0; - if(!dep_m) - return 0; + log_assert(dep_m); if(dep_m == cyc_m || find_in_subsub(dep_m, cyc_m, &counter)) { if(counter > MESH_MAX_SUBSUB) return 2; @@ -1190,24 +1210,19 @@ void mesh_detach_subs(struct module_qstate* qstate) } int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec, struct module_qstate** newq, - struct mesh_state** sub) + struct respip_client_info* cinfo, uint16_t qflags, int prime, + int valrec, struct module_qstate** newq, struct mesh_state** sub) { /* find it, if not, create it */ struct mesh_area* mesh = qstate->env->mesh; - *sub = mesh_area_find(mesh, NULL, qinfo, qflags, - prime, valrec); - if(mesh_detect_cycle_found(qstate, *sub)) { - verbose(VERB_ALGO, "attach failed, cycle detected"); - return 0; - } + *sub = mesh_area_find(mesh, cinfo, qinfo, qflags, prime, valrec); if(!*sub) { #ifdef UNBOUND_DEBUG struct rbnode_type* n; #endif /* create a new one */ - *sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, - valrec); + *sub = mesh_state_create(qstate->env, qinfo, cinfo, qflags, + prime, valrec); if(!*sub) { log_err("mesh_attach_sub: out of memory"); return 0; @@ -1230,18 +1245,25 @@ int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo, rbtree_insert(&mesh->run, &(*sub)->run_node); log_assert(n != NULL); *newq = &(*sub)->s; - } else + } else { *newq = NULL; + if(mesh_detect_cycle_found(qstate, *sub)) { + verbose(VERB_ALGO, "attach failed, cycle detected"); + return 0; + } + } return 1; } int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec, struct module_qstate** newq) + struct respip_client_info* cinfo, uint16_t qflags, int prime, + int valrec, struct module_qstate** newq) { struct mesh_area* mesh = qstate->env->mesh; struct mesh_state* sub = NULL; int was_detached; - if(!mesh_add_sub(qstate, qinfo, qflags, prime, valrec, newq, &sub)) + if(!mesh_add_sub(qstate, qinfo, cinfo, qflags, prime, valrec, newq, + &sub)) return 0; was_detached = (sub->super_set.count == 0); if(!mesh_state_attachment(qstate->mesh_info, sub)) @@ -1684,7 +1706,7 @@ static void dns_error_reporting(struct module_qstate* qstate, log_query_info(VERB_ALGO, "DNS Error Reporting: generating report " "query for", &qinfo); - if(mesh_add_sub(qstate, &qinfo, BIT_RD, 0, 0, &newq, &sub)) { + if(mesh_add_sub(qstate, &qinfo, NULL, BIT_RD, 0, 0, &newq, &sub)) { qstate->env->mesh->num_dns_error_reports++; } return; @@ -1727,28 +1749,39 @@ void mesh_query_done(struct mesh_state* mstate) dns_error_reporting(&mstate->s, rep); for(r = mstate->reply_list; r; r = r->next) { - struct timeval old; - timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); - if(mstate->s.env->cfg->discard_timeout != 0 && - ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > - mstate->s.env->cfg->discard_timeout) { - /* Drop the reply, it is too old */ - /* briefly set the reply_list to NULL, so that the - * tcp req info cleanup routine that calls the mesh - * to deregister the meshstate for it is not done - * because the list is NULL and also accounting is not - * done there, but instead we do that here. */ - struct mesh_reply* reply_list = mstate->reply_list; - verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); - infra_wait_limit_dec(mstate->s.env->infra_cache, - &r->query_reply, mstate->s.env->cfg); - mstate->reply_list = NULL; - if(r->query_reply.c->use_h2) - http2_stream_remove_mesh_state(r->h2_stream); - comm_point_drop_reply(&r->query_reply); - mstate->reply_list = reply_list; - mstate->s.env->mesh->num_queries_discard_timeout++; - continue; + if(mesh_is_udp(r)) { + /* For UDP queries, the old replies are discarded. + * This stops a large volume of old replies from + * building up. + * The stream replies, are not discarded. The + * stream is open, the other side is waiting. + * Some answer is needed, even if servfail, but the + * real reply is ready to go, so that is given. */ + struct timeval old; + timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); + if(mstate->s.env->cfg->discard_timeout != 0 && + ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > + mstate->s.env->cfg->discard_timeout) { + /* Drop the reply, it is too old */ + /* briefly set the reply_list to NULL, so that the + * tcp req info cleanup routine that calls the mesh + * to deregister the meshstate for it is not done + * because the list is NULL and also accounting is not + * done there, but instead we do that here. */ + struct mesh_reply* reply_list = mstate->reply_list; + verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); + mstate->reply_list = NULL; + if(r->query_reply.c->use_h2) + http2_stream_remove_mesh_state(r->h2_stream); + comm_point_drop_reply(&r->query_reply); + mstate->reply_list = reply_list; + log_assert(mstate->s.env->mesh->num_reply_addrs > 0); + mstate->s.env->mesh->num_reply_addrs--; + mstate->s.env->mesh->num_queries_discard_timeout++; + continue; + } } i++; @@ -1782,6 +1815,8 @@ void mesh_query_done(struct mesh_state* mstate) } comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; + log_assert(mstate->s.env->mesh->num_reply_addrs > 0); + mstate->s.env->mesh->num_reply_addrs--; } else { struct sldns_buffer* r_buffer = r->query_reply.c->buffer; if(r->query_reply.c->tcp_req_info) { @@ -2272,6 +2307,7 @@ mesh_stats_clear(struct mesh_area* mesh) memset(&mesh->rpz_action[0], 0, sizeof(size_t)*UB_STATS_RPZ_ACTION_NUM); mesh->ans_nodata = 0; mesh->num_queries_discard_timeout = 0; + mesh->num_queries_replyaddr_limit = 0; mesh->num_queries_wait_limit = 0; mesh->num_dns_error_reports = 0; } @@ -2297,7 +2333,7 @@ mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo, struct mesh_area* mesh = qstate->env->mesh; struct mesh_state* dep_m = NULL; dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec); - return mesh_detect_cycle_found(qstate, dep_m); + return dep_m?mesh_detect_cycle_found(qstate, dep_m):0; } void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp, @@ -2341,6 +2377,10 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m, mesh->num_reply_addrs--; infra_wait_limit_dec(mesh->env->infra_cache, &n->query_reply, mesh->env->cfg); + /* We may be removing more than one http2 stream (they + * share the same comm_point); make sure the streams + * don't point back. */ + if(n->h2_stream) n->h2_stream->mesh_state = NULL; /* prev = prev; */ n = n->next; diff --git a/services/mesh.h b/services/mesh.h index f19f423a8cd3..d2fac9d3c918 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -141,6 +141,8 @@ struct mesh_area { size_t rpz_action[UB_STATS_RPZ_ACTION_NUM]; /** stats, number of queries removed due to discard-timeout */ size_t num_queries_discard_timeout; + /** stats, number of queries removed due to replyaddr limit */ + size_t num_queries_replyaddr_limit; /** stats, number of queries removed due to wait-limit */ size_t num_queries_wait_limit; /** stats, number of dns error reports generated */ @@ -399,6 +401,8 @@ void mesh_detach_subs(struct module_qstate* qstate); * @param qstate: the state to find mesh state, and that wants to receive * the results from the new subquery. * @param qinfo: what to query for (copied). + * @param cinfo: if non-NULL client specific info that may affect IP-based + * actions that apply to the query result. It is copied. * @param qflags: what flags to use (RD / CD flag or not). * @param prime: if it is a (stub) priming query. * @param valrec: if it is a validation recursion query (lookup of key, DS). @@ -407,7 +411,8 @@ void mesh_detach_subs(struct module_qstate* qstate); * @return: false on error, true if success (and init may be needed). */ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec, struct module_qstate** newq); + struct respip_client_info* cinfo, uint16_t qflags, int prime, + int valrec, struct module_qstate** newq); /** * Add detached query. @@ -426,6 +431,8 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, * @param qstate: the state to find mesh state, and that wants to receive * the results from the new subquery. * @param qinfo: what to query for (copied). + * @param cinfo: if non-NULL client specific info that may affect IP-based + * actions that apply to the query result. It is copied. * @param qflags: what flags to use (RD / CD flag or not). * @param prime: if it is a (stub) priming query. * @param valrec: if it is a validation recursion query (lookup of key, DS). @@ -435,8 +442,8 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, * @return: false on error, true if success (and init may be needed). */ int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec, struct module_qstate** newq, - struct mesh_state** sub); + struct respip_client_info* cinfo, uint16_t qflags, int prime, + int valrec, struct module_qstate** newq, struct mesh_state** sub); /** * Query state is done, send messages to reply entries. diff --git a/services/modstack.c b/services/modstack.c index 2bc79c4adfd7..f5913dc387c9 100644 --- a/services/modstack.c +++ b/services/modstack.c @@ -262,6 +262,7 @@ int modstack_call_init(struct module_stack* stack, const char* module_conf, struct module_env* env) { + const char* orig_module_conf = module_conf; int i, changed = 0; env->need_to_validate = 0; /* set by module init below */ for(i=0; i<stack->num; i++) { @@ -276,11 +277,13 @@ modstack_call_init(struct module_stack* stack, const char* module_conf, changed = 1; } } - module_conf += strlen(stack->mod[i]->name); + /* Skip this module name in module_conf. */ + while(*module_conf && !isspace((unsigned char)*module_conf)) + module_conf++; } if(changed) { modstack_free(stack); - if(!modstack_config(stack, module_conf)) { + if(!modstack_config(stack, orig_module_conf)) { return 0; } } diff --git a/services/outside_network.c b/services/outside_network.c index 2b7f7d0a2f21..8034ff60ba10 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -160,6 +160,19 @@ reuse_cmp_addrportssl(const void* key1, const void* key2) return 1; if(!r1->is_ssl && r2->is_ssl) return -1; + + /* compare tls_auth_name if SSL-enabled */ + if(r1->is_ssl) { + if(r1->tls_auth_name && !r2->tls_auth_name) + return 1; + if(!r1->tls_auth_name && r2->tls_auth_name) + return -1; + if(r1->tls_auth_name && r2->tls_auth_name) { + r = strcmp(r1->tls_auth_name, r2->tls_auth_name); + if(r != 0) + return r; + } + } return 0; } @@ -531,7 +544,7 @@ reuse_tcp_insert(struct outside_network* outnet, struct pending_tcp* pend_tcp) /** find reuse tcp stream to destination for query, or NULL if none */ static struct reuse_tcp* reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr, - socklen_t addrlen, int use_ssl) + socklen_t addrlen, int use_ssl, char* tls_auth_name) { struct waiting_tcp key_w; struct pending_tcp key_p; @@ -545,8 +558,10 @@ reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr, key_p.c = &c; key_p.reuse.pending = &key_p; key_p.reuse.node.key = &key_p.reuse; - if(use_ssl) + if(use_ssl) { key_p.reuse.is_ssl = 1; + key_p.reuse.tls_auth_name = tls_auth_name; + } if(addrlen > (socklen_t)sizeof(key_p.reuse.addr)) return NULL; memmove(&key_p.reuse.addr, addr, addrlen); @@ -646,6 +661,7 @@ static int outnet_tcp_take_into_use(struct waiting_tcp* w) { struct pending_tcp* pend = w->outnet->tcp_free; + char* tls_auth_name = NULL; int s; log_assert(pend); log_assert(w->pkt); @@ -746,7 +762,22 @@ outnet_tcp_take_into_use(struct waiting_tcp* w) comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl); #endif pend->c->ssl_shake_state = comm_ssl_shake_write; - if(!set_auth_name_on_ssl(pend->c->ssl, w->tls_auth_name, + if(w->tls_auth_name) { + /* strdup the auth name, while not linked the list yet, + * in case of failure, easy cleanup. */ + tls_auth_name = strdup(w->tls_auth_name); + if(!tls_auth_name) { + log_err("out of memory: alloc tls auth name"); + pend->c->fd = s; +#ifdef HAVE_SSL + SSL_free(pend->c->ssl); +#endif + pend->c->ssl = NULL; + comm_point_close(pend->c); + return 0; + } + } + if(!set_auth_name_on_ssl(pend->c->ssl, tls_auth_name, w->outnet->tls_use_sni)) { pend->c->fd = s; #ifdef HAVE_SSL @@ -754,6 +785,7 @@ outnet_tcp_take_into_use(struct waiting_tcp* w) #endif pend->c->ssl = NULL; comm_point_close(pend->c); + free(tls_auth_name); return 0; } } @@ -778,9 +810,20 @@ outnet_tcp_take_into_use(struct waiting_tcp* w) if(pend->reuse.node.key) reuse_tcp_remove_tree_list(w->outnet, &pend->reuse); - if(pend->c->ssl) + if(pend->c->ssl) { pend->reuse.is_ssl = 1; - else pend->reuse.is_ssl = 0; + if(pend->reuse.tls_auth_name) + free(pend->reuse.tls_auth_name); + pend->reuse.tls_auth_name = tls_auth_name; + tls_auth_name = NULL; + } else { + pend->reuse.is_ssl = 0; + if(pend->reuse.tls_auth_name) + free(pend->reuse.tls_auth_name); + pend->reuse.tls_auth_name = NULL; + } + /* free tls auth name if nonNULL */ + free(tls_auth_name); /* insert in reuse by address tree if not already inserted there */ (void)reuse_tcp_insert(w->outnet, pend); reuse_tree_by_id_insert(&pend->reuse, w); @@ -969,7 +1012,7 @@ use_free_buffer(struct outside_network* outnet) (!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) || (outnet->tcp_reuse_first && outnet->tcp_reuse_last)); reuse = reuse_tcp_find(outnet, &w->addr, w->addrlen, - w->ssl_upstream); + w->ssl_upstream, w->tls_auth_name); /* re-select an ID when moving to a new TCP buffer */ w->id = tcp_select_id(outnet, reuse); LDNS_ID_SET(w->pkt, w->id); @@ -1198,6 +1241,10 @@ decommission_pending_tcp(struct outside_network* outnet, /* needs unlink from the reuse tree to get deleted */ reuse_tcp_remove_tree_list(outnet, &pend->reuse); } + if(pend->reuse.tls_auth_name) { + free(pend->reuse.tls_auth_name); + pend->reuse.tls_auth_name = NULL; + } /* free SSL structure after remove from outnet tcp reuse tree, * because the c->ssl null or not is used for sorting in the tree */ if(pend->c->ssl) { @@ -1922,6 +1969,10 @@ outside_network_delete(struct outside_network* outnet) * the tcp conn is working on */ decommission_pending_tcp(outnet, pend); } + if(pend->reuse.tls_auth_name) { + free(pend->reuse.tls_auth_name); + pend->reuse.tls_auth_name = NULL; + } comm_point_delete(outnet->tcp_conns[i]->c); free(outnet->tcp_conns[i]); outnet->tcp_conns[i] = NULL; @@ -2447,7 +2498,7 @@ pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet, /* find out if a reused stream to the target exists */ /* if so, take it into use */ reuse = reuse_tcp_find(sq->outnet, &sq->addr, sq->addrlen, - sq->ssl_upstream); + sq->ssl_upstream, sq->tls_auth_name); if(reuse) { log_reuse_tcp(VERB_CLIENT, "pending_tcp_query: found reuse", reuse); log_assert(reuse->pending); diff --git a/services/outside_network.h b/services/outside_network.h index 0a77e3388ddc..e30ce92eb3fc 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -48,6 +48,10 @@ #include "util/regional.h" #include "util/netevent.h" #include "dnstap/dnstap_config.h" +#ifdef __QNX__ +/* For struct timeval */ +#include <sys/time.h> +#endif /* __QNX__ */ struct pending; struct pending_timeout; struct ub_randstate; @@ -260,6 +264,9 @@ struct reuse_tcp { socklen_t addrlen; /** also key for tcp_reuse tree, if ssl is used */ int is_ssl; + /** If is_ssl is enabled, tls_auth_name is part of the key for + * tcp_reuse tree. If the string is NULL, it without a tls_auth_name */ + char* tls_auth_name; /** lru chain, so that the oldest can be removed to get a new * connection when all are in (re)use. oldest is last in list. * The lru only contains empty connections waiting for reuse, diff --git a/services/rpz.c b/services/rpz.c index f45cf65420d7..d83acbfb08e0 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -153,6 +153,7 @@ rpz_type_ignored(uint16_t rr_type) case LDNS_RR_TYPE_SOA: case LDNS_RR_TYPE_NS: case LDNS_RR_TYPE_DNAME: + case LDNS_RR_TYPE_ZONEMD: /* all DNSSEC-related RRs must be ignored */ case LDNS_RR_TYPE_DNSKEY: case LDNS_RR_TYPE_DS: |
