summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/authzone.c150
-rw-r--r--services/cache/dns.c110
-rw-r--r--services/cache/dns.h2
-rw-r--r--services/cache/infra.c3
-rw-r--r--services/cache/rrset.c37
-rw-r--r--services/listen_dnsport.c40
-rw-r--r--services/localzone.c179
-rw-r--r--services/localzone.h12
-rw-r--r--services/mesh.c130
-rw-r--r--services/mesh.h13
-rw-r--r--services/modstack.c7
-rw-r--r--services/outside_network.c65
-rw-r--r--services/outside_network.h7
-rw-r--r--services/rpz.c1
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: