aboutsummaryrefslogtreecommitdiff
path: root/contrib/unbound/services
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2022-02-18 00:05:15 +0000
committerCy Schubert <cy@FreeBSD.org>2022-02-18 00:05:15 +0000
commit9cf5bc93f6ba1711ae7bf96a982a2b3c8b073a18 (patch)
treeac738d3541e70099db4c216689c30f0758de951d /contrib/unbound/services
parent11c4d4b9668c3092838beb168e0bbb3187433748 (diff)
parent3574dc0bd83e731bba79edc130c0569bf05f7af5 (diff)
downloadsrc-9cf5bc93f6ba1711ae7bf96a982a2b3c8b073a18.tar.gz
src-9cf5bc93f6ba1711ae7bf96a982a2b3c8b073a18.zip
unbound: Vendor import 1.15.0
Vendor import GA release of unbound 1.15.0. MFC after: 2 weeks
Diffstat (limited to 'contrib/unbound/services')
-rw-r--r--contrib/unbound/services/cache/infra.c66
-rw-r--r--contrib/unbound/services/cache/infra.h14
-rw-r--r--contrib/unbound/services/listen_dnsport.c37
-rw-r--r--contrib/unbound/services/localzone.c102
-rw-r--r--contrib/unbound/services/outside_network.c307
-rw-r--r--contrib/unbound/services/outside_network.h27
-rw-r--r--contrib/unbound/services/rpz.c34
-rw-r--r--contrib/unbound/services/rpz.h2
8 files changed, 415 insertions, 174 deletions
diff --git a/contrib/unbound/services/cache/infra.c b/contrib/unbound/services/cache/infra.c
index 518e69622f83..252e1e288b35 100644
--- a/contrib/unbound/services/cache/infra.c
+++ b/contrib/unbound/services/cache/infra.c
@@ -898,8 +898,9 @@ static void infra_ip_create_ratedata(struct infra_cache* infra,
slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL);
}
-/** find the second and return its rate counter, if none, remove oldest */
-static int* infra_rate_find_second(void* data, time_t t)
+/** Find the second and return its rate counter. If none and should_add, remove
+ * oldest to accommodate. Else return none. */
+static int* infra_rate_find_second_or_none(void* data, time_t t, int should_add)
{
struct rate_data* d = (struct rate_data*)data;
int i, oldest;
@@ -907,6 +908,7 @@ static int* infra_rate_find_second(void* data, time_t t)
if(d->timestamp[i] == t)
return &(d->qps[i]);
}
+ if(!should_add) return NULL;
/* remove oldest timestamp, and insert it at t with 0 qps */
oldest = 0;
for(i=0; i<RATE_WINDOW; i++) {
@@ -918,21 +920,41 @@ static int* infra_rate_find_second(void* data, time_t t)
return &(d->qps[oldest]);
}
-int infra_rate_max(void* data, time_t now)
+/** find the second and return its rate counter, if none, remove oldest to
+ * accommodate */
+static int* infra_rate_give_second(void* data, time_t t)
+{
+ return infra_rate_find_second_or_none(data, t, 1);
+}
+
+/** find the second and return its rate counter only if it exists. Caller
+ * should check for NULL return value */
+static int* infra_rate_get_second(void* data, time_t t)
+{
+ return infra_rate_find_second_or_none(data, t, 0);
+}
+
+int infra_rate_max(void* data, time_t now, int backoff)
{
struct rate_data* d = (struct rate_data*)data;
int i, max = 0;
for(i=0; i<RATE_WINDOW; i++) {
- if(now-d->timestamp[i] <= RATE_WINDOW) {
- if(d->qps[i] > max)
+ if(backoff) {
+ if(now-d->timestamp[i] <= RATE_WINDOW &&
+ d->qps[i] > max) {
max = d->qps[i];
+ }
+ } else {
+ if(now == d->timestamp[i]) {
+ return d->qps[i];
+ }
}
}
return max;
}
int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
- size_t namelen, time_t timenow, struct query_info* qinfo,
+ size_t namelen, time_t timenow, int backoff, struct query_info* qinfo,
struct comm_reply* replylist)
{
int lim, max;
@@ -949,13 +971,13 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
/* find or insert ratedata */
entry = infra_find_ratedata(infra, name, namelen, 1);
if(entry) {
- int premax = infra_rate_max(entry->data, timenow);
- int* cur = infra_rate_find_second(entry->data, timenow);
+ int premax = infra_rate_max(entry->data, timenow, backoff);
+ int* cur = infra_rate_give_second(entry->data, timenow);
(*cur)++;
- max = infra_rate_max(entry->data, timenow);
+ max = infra_rate_max(entry->data, timenow, backoff);
lock_rw_unlock(&entry->lock);
- if(premax < lim && max >= lim) {
+ if(premax <= lim && max > lim) {
char buf[257], qnm[257], ts[12], cs[12], ip[128];
dname_str(name, buf);
dname_str(qinfo->qname, qnm);
@@ -970,12 +992,12 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s", buf, lim, qnm, cs, ts);
}
}
- return (max < lim);
+ return (max <= lim);
}
/* create */
infra_create_ratedata(infra, name, namelen, timenow);
- return (1 < lim);
+ return (1 <= lim);
}
void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name,
@@ -987,14 +1009,19 @@ void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name,
return; /* not enabled */
entry = infra_find_ratedata(infra, name, namelen, 1);
if(!entry) return; /* not cached */
- cur = infra_rate_find_second(entry->data, timenow);
+ cur = infra_rate_get_second(entry->data, timenow);
+ if(cur == NULL) {
+ /* our timenow is not available anymore; nothing to decrease */
+ lock_rw_unlock(&entry->lock);
+ return;
+ }
if((*cur) > 0)
(*cur)--;
lock_rw_unlock(&entry->lock);
}
int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name,
- size_t namelen, time_t timenow)
+ size_t namelen, time_t timenow, int backoff)
{
struct lruhash_entry* entry;
int lim, max;
@@ -1010,7 +1037,7 @@ int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name,
entry = infra_find_ratedata(infra, name, namelen, 0);
if(!entry)
return 0; /* not cached */
- max = infra_rate_max(entry->data, timenow);
+ max = infra_rate_max(entry->data, timenow, backoff);
lock_rw_unlock(&entry->lock);
return (max >= lim);
@@ -1027,7 +1054,8 @@ infra_get_mem(struct infra_cache* infra)
}
int infra_ip_ratelimit_inc(struct infra_cache* infra,
- struct comm_reply* repinfo, time_t timenow, struct sldns_buffer* buffer)
+ struct comm_reply* repinfo, time_t timenow, int backoff,
+ struct sldns_buffer* buffer)
{
int max;
struct lruhash_entry* entry;
@@ -1039,10 +1067,10 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra,
/* find or insert ratedata */
entry = infra_find_ip_ratedata(infra, repinfo, 1);
if(entry) {
- int premax = infra_rate_max(entry->data, timenow);
- int* cur = infra_rate_find_second(entry->data, timenow);
+ int premax = infra_rate_max(entry->data, timenow, backoff);
+ int* cur = infra_rate_give_second(entry->data, timenow);
(*cur)++;
- max = infra_rate_max(entry->data, timenow);
+ max = infra_rate_max(entry->data, timenow, backoff);
lock_rw_unlock(&entry->lock);
if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) {
diff --git a/contrib/unbound/services/cache/infra.h b/contrib/unbound/services/cache/infra.h
index 14f97c4c64d3..6a2371aca477 100644
--- a/contrib/unbound/services/cache/infra.h
+++ b/contrib/unbound/services/cache/infra.h
@@ -368,6 +368,7 @@ long long infra_get_host_rto(struct infra_cache* infra,
* @param name: zone name
* @param namelen: zone name length
* @param timenow: what time it is now.
+ * @param backoff: if backoff is enabled.
* @param qinfo: for logging, query name.
* @param replylist: for logging, querier's address (if any).
* @return 1 if it could be incremented. 0 if the increment overshot the
@@ -375,7 +376,7 @@ long long infra_get_host_rto(struct infra_cache* infra,
* Failures like alloc failures are not returned (probably as 1).
*/
int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
- size_t namelen, time_t timenow, struct query_info* qinfo,
+ size_t namelen, time_t timenow, int backoff, struct query_info* qinfo,
struct comm_reply* replylist);
/**
@@ -398,13 +399,15 @@ void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name,
* @param name: zone name
* @param namelen: zone name length
* @param timenow: what time it is now.
+ * @param backoff: if backoff is enabled.
* @return true if exceeded.
*/
int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name,
- size_t namelen, time_t timenow);
+ size_t namelen, time_t timenow, int backoff);
-/** find the maximum rate stored, not too old. 0 if no information. */
-int infra_rate_max(void* data, time_t now);
+/** find the maximum rate stored. 0 if no information.
+ * When backoff is enabled look for the maximum in the whole RATE_WINDOW. */
+int infra_rate_max(void* data, time_t now, int backoff);
/** find the ratelimit in qps for a domain. 0 if no limit for domain. */
int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name,
@@ -415,11 +418,12 @@ int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name,
* @param infra: infra cache
* @param repinfo: information about client
* @param timenow: what time it is now.
+ * @param backoff: if backoff is enabled.
* @param buffer: with query for logging.
* @return 1 if it could be incremented. 0 if the increment overshot the
* ratelimit and the query should be dropped. */
int infra_ip_ratelimit_inc(struct infra_cache* infra,
- struct comm_reply* repinfo, time_t timenow,
+ struct comm_reply* repinfo, time_t timenow, int backoff,
struct sldns_buffer* buffer);
/**
diff --git a/contrib/unbound/services/listen_dnsport.c b/contrib/unbound/services/listen_dnsport.c
index 6a33fbcdaf7e..983b96f09eec 100644
--- a/contrib/unbound/services/listen_dnsport.c
+++ b/contrib/unbound/services/listen_dnsport.c
@@ -1370,39 +1370,38 @@ listen_create(struct comm_base* base, struct listen_port* ports,
struct comm_point* cp = NULL;
if(ports->ftype == listen_type_udp ||
ports->ftype == listen_type_udp_dnscrypt)
- cp = comm_point_create_udp(base, ports->fd,
+ cp = comm_point_create_udp(base, ports->fd,
front->udp_buff, cb, cb_arg, ports->socket);
else if(ports->ftype == listen_type_tcp ||
ports->ftype == listen_type_tcp_dnscrypt)
- cp = comm_point_create_tcp(base, ports->fd,
+ cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, tcp_idle_timeout,
harden_large_queries, 0, NULL,
tcp_conn_limit, bufsize, front->udp_buff,
ports->ftype, cb, cb_arg, ports->socket);
else if(ports->ftype == listen_type_ssl ||
ports->ftype == listen_type_http) {
- cp = comm_point_create_tcp(base, ports->fd,
+ cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, tcp_idle_timeout,
harden_large_queries,
http_max_streams, http_endpoint,
tcp_conn_limit, bufsize, front->udp_buff,
ports->ftype, cb, cb_arg, ports->socket);
- if(http_notls && ports->ftype == listen_type_http)
- cp->ssl = NULL;
- else
- cp->ssl = sslctx;
if(ports->ftype == listen_type_http) {
if(!sslctx && !http_notls) {
- log_warn("HTTPS port configured, but no TLS "
- "tls-service-key or tls-service-pem "
- "set");
+ log_warn("HTTPS port configured, but "
+ "no TLS tls-service-key or "
+ "tls-service-pem set");
}
#ifndef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
- if(!http_notls)
- log_warn("Unbound is not compiled with an "
- "OpenSSL version supporting ALPN "
- " (OpenSSL >= 1.0.2). This is required "
- "to use DNS-over-HTTPS");
+ if(!http_notls) {
+ log_warn("Unbound is not compiled "
+ "with an OpenSSL version "
+ "supporting ALPN "
+ "(OpenSSL >= 1.0.2). This "
+ "is required to use "
+ "DNS-over-HTTPS");
+ }
#endif
#ifndef HAVE_NGHTTP2_NGHTTP2_H
log_warn("Unbound is not compiled with "
@@ -1412,13 +1411,17 @@ listen_create(struct comm_base* base, struct listen_port* ports,
}
} else if(ports->ftype == listen_type_udpancil ||
ports->ftype == listen_type_udpancil_dnscrypt)
- cp = comm_point_create_udp_ancil(base, ports->fd,
+ cp = comm_point_create_udp_ancil(base, ports->fd,
front->udp_buff, cb, cb_arg, ports->socket);
if(!cp) {
- log_err("can't create commpoint");
+ log_err("can't create commpoint");
listen_delete(front);
return NULL;
}
+ if(http_notls && ports->ftype == listen_type_http)
+ cp->ssl = NULL;
+ else
+ cp->ssl = sslctx;
cp->dtenv = dtenv;
cp->do_not_close = 1;
#ifdef USE_DNSCRYPT
diff --git a/contrib/unbound/services/localzone.c b/contrib/unbound/services/localzone.c
index 77d0107f9f6f..3e3a71aea3c5 100644
--- a/contrib/unbound/services/localzone.c
+++ b/contrib/unbound/services/localzone.c
@@ -56,6 +56,44 @@
* with 16 bytes for an A record, a 64K packet has about 4000 max */
#define LOCALZONE_RRSET_COUNT_MAX 4096
+/** print all RRsets in local zone */
+static void
+local_zone_out(struct local_zone* z)
+{
+ struct local_data* d;
+ struct local_rrset* p;
+ RBTREE_FOR(d, struct local_data*, &z->data) {
+ for(p = d->rrsets; p; p = p->next) {
+ log_nametypeclass(NO_VERBOSE, "rrset", d->name,
+ ntohs(p->rrset->rk.type),
+ ntohs(p->rrset->rk.rrset_class));
+ }
+ }
+}
+
+static void
+local_zone_print(struct local_zone* z)
+{
+ char buf[64];
+ lock_rw_rdlock(&z->lock);
+ snprintf(buf, sizeof(buf), "%s zone",
+ local_zone_type2str(z->type));
+ log_nametypeclass(NO_VERBOSE, buf, z->name, 0, z->dclass);
+ local_zone_out(z);
+ lock_rw_unlock(&z->lock);
+}
+
+void local_zones_print(struct local_zones* zones)
+{
+ struct local_zone* z;
+ lock_rw_rdlock(&zones->lock);
+ log_info("number of auth zones %u", (unsigned)zones->ztree.count);
+ RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
+ local_zone_print(z);
+ }
+ lock_rw_unlock(&zones->lock);
+}
+
struct local_zones*
local_zones_create(void)
{
@@ -1010,6 +1048,38 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
lock_rw_rdlock(&zones->lock);
if(!local_zones_lookup(zones, rr_name, len, labs, rr_class,
rr_type)) {
+ /* 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);
+ if(z) {
+ uint8_t* name = memdup(z->name, z->namelen);
+ size_t znamelen = z->namelen;
+ int znamelabs = z->namelabs;
+ enum localzone_type ztype = z->type;
+ lock_rw_unlock(&zones->lock);
+ if(!name) {
+ log_err("out of memory");
+ free(rr_name);
+ return 0;
+ }
+ if(!(
+#ifndef THREADS_DISABLED
+ z =
+#endif
+ lz_enter_zone_dname(zones, name,
+ znamelen, znamelabs,
+ ztype, rr_class))) {
+ free(rr_name);
+ return 0;
+ }
+ lock_rw_unlock(&z->lock);
+ free(rr_name);
+ continue;
+ }
if(!have_name) {
dclass = rr_class;
nm = rr_name;
@@ -1220,38 +1290,6 @@ local_zones_find_le(struct local_zones* zones,
return (struct local_zone*)node;
}
-/** print all RRsets in local zone */
-static void
-local_zone_out(struct local_zone* z)
-{
- struct local_data* d;
- struct local_rrset* p;
- RBTREE_FOR(d, struct local_data*, &z->data) {
- for(p = d->rrsets; p; p = p->next) {
- log_nametypeclass(NO_VERBOSE, "rrset", d->name,
- ntohs(p->rrset->rk.type),
- ntohs(p->rrset->rk.rrset_class));
- }
- }
-}
-
-void local_zones_print(struct local_zones* zones)
-{
- struct local_zone* z;
- lock_rw_rdlock(&zones->lock);
- log_info("number of auth zones %u", (unsigned)zones->ztree.count);
- RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
- char buf[64];
- lock_rw_rdlock(&z->lock);
- snprintf(buf, sizeof(buf), "%s zone",
- local_zone_type2str(z->type));
- log_nametypeclass(NO_VERBOSE, buf, z->name, 0, z->dclass);
- local_zone_out(z);
- lock_rw_unlock(&z->lock);
- }
- lock_rw_unlock(&zones->lock);
-}
-
/** encode answer consisting of 1 rrset */
static int
local_encode(struct query_info* qinfo, struct module_env* env,
diff --git a/contrib/unbound/services/outside_network.c b/contrib/unbound/services/outside_network.c
index f4a5d0707845..a7e5fa3ad583 100644
--- a/contrib/unbound/services/outside_network.c
+++ b/contrib/unbound/services/outside_network.c
@@ -94,6 +94,16 @@ static void waiting_list_remove(struct outside_network* outnet,
static uint16_t tcp_select_id(struct outside_network* outnet,
struct reuse_tcp* reuse);
+/** Perform serviced query UDP sending operation */
+static int serviced_udp_send(struct serviced_query* sq, sldns_buffer* buff);
+
+/** Send serviced query over TCP return false on initial failure */
+static int serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff);
+
+/** call the callbacks for a serviced query */
+static void serviced_callbacks(struct serviced_query* sq, int error,
+ struct comm_point* c, struct comm_reply* rep);
+
int
pending_cmp(const void* key1, const void* key2)
{
@@ -836,6 +846,7 @@ outnet_add_tcp_waiting_first(struct outside_network* outnet,
if(w->on_tcp_waiting_list)
return;
w->next_waiting = outnet->tcp_wait_first;
+ log_assert(w->next_waiting != w);
if(!outnet->tcp_wait_last)
outnet->tcp_wait_last = w;
outnet->tcp_wait_first = w;
@@ -1136,6 +1147,22 @@ static void reuse_cb_readwait_for_failure(rbtree_type* tree_by_id, int err)
}
}
+/** mark the entry for being in the cb_and_decommission stage */
+static void mark_for_cb_and_decommission(rbnode_type* node,
+ void* ATTR_UNUSED(arg))
+{
+ struct waiting_tcp* w = (struct waiting_tcp*)node->key;
+ /* Mark the waiting_tcp to signal later code (serviced_delete) that
+ * this item is part of the backed up tree_by_id and will be deleted
+ * later. */
+ w->in_cb_and_decommission = 1;
+ /* Mark the serviced_query for deletion so that later code through
+ * callbacks (iter_clear .. outnet_serviced_query_stop) won't
+ * prematurely delete it. */
+ if(w->cb)
+ ((struct serviced_query*)w->cb_arg)->to_be_deleted = 1;
+}
+
/** perform callbacks for failure and also decommission pending tcp.
* the callbacks remove references in sq->pending to the waiting_tcp
* members of the tree_by_id in the pending tcp. The pending_tcp is
@@ -1151,6 +1178,9 @@ static void reuse_cb_and_decommission(struct outside_network* outnet,
pend->reuse.write_wait_first = NULL;
pend->reuse.write_wait_last = NULL;
decommission_pending_tcp(outnet, pend);
+ if(store.root != NULL && store.root != RBTREE_NULL) {
+ traverse_postorder(&store, &mark_for_cb_and_decommission, NULL);
+ }
reuse_cb_readwait_for_failure(&store, error);
reuse_del_readwait(&store);
}
@@ -1248,6 +1278,12 @@ outnet_tcp_cb(struct comm_point* c, void* arg, int error,
c->buffer));
/* find the query the reply is for */
w = reuse_tcp_by_id_find(&pend->reuse, id);
+ /* Make sure that the reply we got is at least for a
+ * sent query with the same ID; the waiting_tcp that
+ * gets a reply is assumed to not be waiting to be
+ * sent. */
+ if(w && (w->on_tcp_waiting_list || w->write_wait_queued))
+ w = NULL;
}
}
if(error == NETEVENT_NOERROR && !w) {
@@ -1265,6 +1301,8 @@ outnet_tcp_cb(struct comm_point* c, void* arg, int error,
}
}
if(w) {
+ log_assert(!w->on_tcp_waiting_list);
+ log_assert(!w->write_wait_queued);
reuse_tree_by_id_delete(&pend->reuse, w);
verbose(VERB_CLIENT, "outnet tcp callback query err %d buflen %d",
error, (int)sldns_buffer_limit(c->buffer));
@@ -1324,7 +1362,7 @@ outnet_send_wait_udp(struct outside_network* outnet)
{
struct pending* pend;
/* process waiting queries */
- while(outnet->udp_wait_first && outnet->unused_fds
+ while(outnet->udp_wait_first && outnet->unused_fds
&& !outnet->want_to_quit) {
pend = outnet->udp_wait_first;
outnet->udp_wait_first = pend->next_waiting;
@@ -1333,8 +1371,10 @@ outnet_send_wait_udp(struct outside_network* outnet)
sldns_buffer_write(outnet->udp_buff, pend->pkt, pend->pkt_len);
sldns_buffer_flip(outnet->udp_buff);
free(pend->pkt); /* freeing now makes get_mem correct */
- pend->pkt = NULL;
+ pend->pkt = NULL;
pend->pkt_len = 0;
+ log_assert(!pend->sq->busy);
+ pend->sq->busy = 1;
if(!randomize_and_send_udp(pend, outnet->udp_buff,
pend->timeout)) {
/* callback error on pending */
@@ -1344,6 +1384,8 @@ outnet_send_wait_udp(struct outside_network* outnet)
NETEVENT_CLOSED, NULL);
}
pending_delete(outnet, pend);
+ } else {
+ pend->sq->busy = 0;
}
}
}
@@ -1454,7 +1496,6 @@ calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6,
(*num_ip4)++;
}
}
-
}
void
@@ -1708,16 +1749,9 @@ static void
serviced_node_del(rbnode_type* node, void* ATTR_UNUSED(arg))
{
struct serviced_query* sq = (struct serviced_query*)node;
- struct service_callback* p = sq->cblist, *np;
- free(sq->qbuf);
- free(sq->zone);
- free(sq->tls_auth_name);
- edns_opt_list_free(sq->opt_list);
- while(p) {
- np = p->next;
- free(p);
- p = np;
- }
+ alloc_reg_release(sq->alloc, sq->region);
+ if(sq->timer)
+ comm_timer_delete(sq->timer);
free(sq);
}
@@ -2174,10 +2208,13 @@ pending_udp_query(struct serviced_query* sq, struct sldns_buffer* packet,
sq->outnet->udp_wait_last = pend;
return pend;
}
+ log_assert(!sq->busy);
+ sq->busy = 1;
if(!randomize_and_send_udp(pend, packet, timeout)) {
pending_delete(sq->outnet, pend);
return NULL;
}
+ sq->busy = 0;
return pend;
}
@@ -2247,7 +2284,7 @@ reuse_tcp_select_id(struct reuse_tcp* reuse, struct outside_network* outnet)
}
/* equally pick a random unused element from the tree that is
- * not in use. Pick a the n-th index of an ununused number,
+ * not in use. Pick a the n-th index of an unused number,
* then loop over the empty spaces in the tree and find it */
log_assert(reuse->tree_by_id.count < 0xffff);
select = ub_random_max(outnet->rnd, 0xffff - reuse->tree_by_id.count);
@@ -2360,6 +2397,7 @@ pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet,
#ifdef USE_DNSTAP
w->sq = NULL;
#endif
+ w->in_cb_and_decommission = 0;
if(pend) {
/* we have a buffer available right now */
if(reuse) {
@@ -2456,30 +2494,62 @@ lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
return (struct serviced_query*)rbtree_search(outnet->serviced, &key);
}
+void
+serviced_timer_cb(void* arg)
+{
+ struct serviced_query* sq = (struct serviced_query*)arg;
+ struct outside_network* outnet = sq->outnet;
+ verbose(VERB_ALGO, "serviced send timer");
+ /* By the time this cb is called, if we don't have any registered
+ * callbacks for this serviced_query anymore; do not send. */
+ if(!sq->cblist)
+ goto delete;
+ /* perform first network action */
+ if(outnet->do_udp && !(sq->tcp_upstream || sq->ssl_upstream)) {
+ if(!serviced_udp_send(sq, outnet->udp_buff))
+ goto delete;
+ } else {
+ if(!serviced_tcp_send(sq, outnet->udp_buff))
+ goto delete;
+ }
+ /* Maybe by this time we don't have callbacks attached anymore. Don't
+ * proactively try to delete; let it run and maybe another callback
+ * will get attached by the time we get an answer. */
+ return;
+delete:
+ serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
+}
+
/** Create new serviced entry */
static struct serviced_query*
serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
int want_dnssec, int nocaps, int tcp_upstream, int ssl_upstream,
char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* zone, size_t zonelen, int qtype, struct edns_option* opt_list,
- size_t pad_queries_block_size)
+ size_t pad_queries_block_size, struct alloc_cache* alloc,
+ struct regional* region)
{
struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
+ struct timeval t;
#ifdef UNBOUND_DEBUG
rbnode_type* ins;
#endif
if(!sq)
return NULL;
sq->node.key = sq;
- sq->qbuf = memdup(sldns_buffer_begin(buff), sldns_buffer_limit(buff));
+ sq->alloc = alloc;
+ sq->region = region;
+ sq->qbuf = regional_alloc_init(region, sldns_buffer_begin(buff),
+ sldns_buffer_limit(buff));
if(!sq->qbuf) {
+ alloc_reg_release(alloc, region);
free(sq);
return NULL;
}
sq->qbuflen = sldns_buffer_limit(buff);
- sq->zone = memdup(zone, zonelen);
+ sq->zone = regional_alloc_init(region, zone, zonelen);
if(!sq->zone) {
- free(sq->qbuf);
+ alloc_reg_release(alloc, region);
free(sq);
return NULL;
}
@@ -2491,10 +2561,9 @@ serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
sq->tcp_upstream = tcp_upstream;
sq->ssl_upstream = ssl_upstream;
if(tls_auth_name) {
- sq->tls_auth_name = strdup(tls_auth_name);
+ sq->tls_auth_name = regional_strdup(region, tls_auth_name);
if(!sq->tls_auth_name) {
- free(sq->zone);
- free(sq->qbuf);
+ alloc_reg_release(alloc, region);
free(sq);
return NULL;
}
@@ -2503,17 +2572,16 @@ serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
}
memcpy(&sq->addr, addr, addrlen);
sq->addrlen = addrlen;
- sq->opt_list = NULL;
- if(opt_list) {
- sq->opt_list = edns_opt_copy_alloc(opt_list);
- if(!sq->opt_list) {
- free(sq->tls_auth_name);
- free(sq->zone);
- free(sq->qbuf);
- free(sq);
- return NULL;
- }
+ sq->opt_list = opt_list;
+ sq->busy = 0;
+ sq->timer = comm_timer_create(outnet->base, serviced_timer_cb, sq);
+ if(!sq->timer) {
+ alloc_reg_release(alloc, region);
+ free(sq);
+ return NULL;
}
+ memset(&t, 0, sizeof(t));
+ comm_timer_set(sq->timer, &t);
sq->outnet = outnet;
sq->cblist = NULL;
sq->pending = NULL;
@@ -2522,7 +2590,7 @@ serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
sq->to_be_deleted = 0;
sq->padding_block_size = pad_queries_block_size;
#ifdef UNBOUND_DEBUG
- ins =
+ ins =
#else
(void)
#endif
@@ -2620,29 +2688,38 @@ serviced_delete(struct serviced_query* sq)
struct waiting_tcp* w = (struct waiting_tcp*)
sq->pending;
verbose(VERB_CLIENT, "serviced_delete: TCP");
+ log_assert(!(w->write_wait_queued && w->on_tcp_waiting_list));
/* if on stream-write-waiting list then
* remove from waiting list and waiting_tcp_delete */
if(w->write_wait_queued) {
struct pending_tcp* pend =
(struct pending_tcp*)w->next_waiting;
verbose(VERB_CLIENT, "serviced_delete: writewait");
- reuse_tree_by_id_delete(&pend->reuse, w);
+ if(!w->in_cb_and_decommission)
+ reuse_tree_by_id_delete(&pend->reuse, w);
reuse_write_wait_remove(&pend->reuse, w);
- waiting_tcp_delete(w);
+ if(!w->in_cb_and_decommission)
+ waiting_tcp_delete(w);
} else if(!w->on_tcp_waiting_list) {
struct pending_tcp* pend =
(struct pending_tcp*)w->next_waiting;
verbose(VERB_CLIENT, "serviced_delete: tcpreusekeep");
+ /* w needs to stay on tree_by_id to not assign
+ * the same ID; remove the callback since its
+ * serviced_query will be gone. */
+ w->cb = NULL;
if(!reuse_tcp_remove_serviced_keep(w, sq)) {
- reuse_cb_and_decommission(sq->outnet,
- pend, NETEVENT_CLOSED);
+ if(!w->in_cb_and_decommission)
+ reuse_cb_and_decommission(sq->outnet,
+ pend, NETEVENT_CLOSED);
use_free_buffer(sq->outnet);
}
sq->pending = NULL;
} else {
verbose(VERB_CLIENT, "serviced_delete: tcpwait");
waiting_list_remove(sq->outnet, w);
- waiting_tcp_delete(w);
+ if(!w->in_cb_and_decommission)
+ waiting_tcp_delete(w);
}
}
}
@@ -2892,7 +2969,8 @@ serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
* use secondary buffer to store the query.
* This is a data copy, but faster than packet to server */
backlen = sldns_buffer_limit(c->buffer);
- backup_p = memdup(sldns_buffer_begin(c->buffer), backlen);
+ backup_p = regional_alloc_init(sq->region,
+ sldns_buffer_begin(c->buffer), backlen);
if(!backup_p) {
log_err("malloc failure in serviced query callbacks");
error = NETEVENT_CLOSED;
@@ -2910,10 +2988,8 @@ serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
}
fptr_ok(fptr_whitelist_serviced_query(p->cb));
(void)(*p->cb)(c, p->cb_arg, error, rep);
- free(p);
}
if(backup_p) {
- free(backup_p);
sq->outnet->svcd_overhead = 0;
}
verbose(VERB_ALGO, "svcd callbacks end");
@@ -2931,7 +3007,7 @@ serviced_tcp_callback(struct comm_point* c, void* arg, int error,
struct waiting_tcp* w = (struct waiting_tcp*)sq->pending;
struct pending_tcp* pend_tcp = NULL;
struct port_if* pi = NULL;
- if(!w->on_tcp_waiting_list && w->next_waiting) {
+ if(w && !w->on_tcp_waiting_list && w->next_waiting) {
pend_tcp = (struct pending_tcp*)w->next_waiting;
pi = pend_tcp->pi;
}
@@ -3027,8 +3103,11 @@ serviced_tcp_initiate(struct serviced_query* sq, sldns_buffer* buff)
sq->status==serviced_query_TCP_EDNS?"EDNS":"");
serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
sq->last_sent_time = *sq->outnet->now_tv;
+ log_assert(!sq->busy);
+ sq->busy = 1;
sq->pending = pending_tcp_query(sq, buff, sq->outnet->tcp_auth_query_timeout,
serviced_tcp_callback, sq);
+ sq->busy = 0;
if(!sq->pending) {
/* delete from tree so that a retry by above layer does not
* clash with this entry */
@@ -3060,8 +3139,11 @@ serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff)
} else {
timeout = sq->outnet->tcp_auth_query_timeout;
}
+ log_assert(!sq->busy);
+ sq->busy = 1;
sq->pending = pending_tcp_query(sq, buff, timeout,
serviced_tcp_callback, sq);
+ sq->busy = 0;
return sq->pending != NULL;
}
@@ -3112,7 +3194,6 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
struct timeval now = *sq->outnet->now_tv;
#ifdef USE_DNSTAP
struct pending* p = (struct pending*)sq->pending;
- struct port_if* pi = p->pc->pif;
#endif
sq->pending = NULL; /* removed after callback */
@@ -3154,14 +3235,16 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
/*
* sending src (local service)/dst (upstream) addresses over DNSTAP
*/
- if(error == NETEVENT_NOERROR && outnet->dtenv &&
- (outnet->dtenv->log_resolver_response_messages ||
- outnet->dtenv->log_forwarder_response_messages)) {
+ if(error == NETEVENT_NOERROR && outnet->dtenv && p->pc &&
+ (outnet->dtenv->log_resolver_response_messages ||
+ outnet->dtenv->log_forwarder_response_messages)) {
log_addr(VERB_ALGO, "response from upstream", &sq->addr, sq->addrlen);
- log_addr(VERB_ALGO, "to local addr", &pi->addr, pi->addrlen);
- dt_msg_send_outside_response(outnet->dtenv, &sq->addr, &pi->addr, c->type,
- sq->zone, sq->zonelen, sq->qbuf, sq->qbuflen,
- &sq->last_sent_time, sq->outnet->now_tv, c->buffer);
+ log_addr(VERB_ALGO, "to local addr", &p->pc->pif->addr,
+ p->pc->pif->addrlen);
+ dt_msg_send_outside_response(outnet->dtenv, &sq->addr,
+ &p->pc->pif->addr, c->type, sq->zone, sq->zonelen,
+ sq->qbuf, sq->qbuflen, &sq->last_sent_time,
+ sq->outnet->now_tv, c->buffer);
}
#endif
if( (sq->status == serviced_query_UDP_EDNS
@@ -3251,64 +3334,117 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
struct serviced_query*
outnet_serviced_query(struct outside_network* outnet,
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
- int nocaps, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
- struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
- size_t zonelen, struct module_qstate* qstate,
- comm_point_callback_type* callback, void* callback_arg, sldns_buffer* buff,
- struct module_env* env)
+ int nocaps, int check_ratelimit, int tcp_upstream, int ssl_upstream,
+ char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
+ uint8_t* zone, size_t zonelen, struct module_qstate* qstate,
+ comm_point_callback_type* callback, void* callback_arg,
+ sldns_buffer* buff, struct module_env* env, int* was_ratelimited)
{
struct serviced_query* sq;
struct service_callback* cb;
struct edns_string_addr* client_string_addr;
-
- if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone, zonelen,
- qstate, qstate->region))
+ struct regional* region;
+ struct edns_option* backed_up_opt_list = qstate->edns_opts_back_out;
+ struct edns_option* per_upstream_opt_list = NULL;
+ time_t timenow = 0;
+
+ /* If we have an already populated EDNS option list make a copy since
+ * we may now add upstream specific EDNS options. */
+ /* Use a region that could be attached to a serviced_query, if it needs
+ * to be created. If an existing one is found then this region will be
+ * destroyed here. */
+ region = alloc_reg_obtain(env->alloc);
+ if(!region) return NULL;
+ if(qstate->edns_opts_back_out) {
+ per_upstream_opt_list = edns_opt_copy_region(
+ qstate->edns_opts_back_out, region);
+ if(!per_upstream_opt_list) {
+ alloc_reg_release(env->alloc, region);
return NULL;
+ }
+ qstate->edns_opts_back_out = per_upstream_opt_list;
+ }
+
+ if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone,
+ zonelen, qstate, region)) {
+ alloc_reg_release(env->alloc, region);
+ return NULL;
+ }
+ /* Restore the option list; we can explicitly use the copied one from
+ * now on. */
+ per_upstream_opt_list = qstate->edns_opts_back_out;
+ qstate->edns_opts_back_out = backed_up_opt_list;
if((client_string_addr = edns_string_addr_lookup(
&env->edns_strings->client_strings, addr, addrlen))) {
- edns_opt_list_append(&qstate->edns_opts_back_out,
+ edns_opt_list_append(&per_upstream_opt_list,
env->edns_strings->client_string_opcode,
client_string_addr->string_len,
- client_string_addr->string, qstate->region);
+ client_string_addr->string, region);
}
serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype,
qinfo->qclass, flags);
sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen,
- qstate->edns_opts_back_out);
- /* duplicate entries are included in the callback list, because
- * there is a counterpart registration by our caller that needs to
- * be doubly-removed (with callbacks perhaps). */
- if(!(cb = (struct service_callback*)malloc(sizeof(*cb))))
- return NULL;
+ per_upstream_opt_list);
if(!sq) {
+ /* Check ratelimit only for new serviced_query */
+ if(check_ratelimit) {
+ timenow = *env->now;
+ if(!infra_ratelimit_inc(env->infra_cache, zone,
+ zonelen, timenow, env->cfg->ratelimit_backoff,
+ &qstate->qinfo, qstate->reply)) {
+ /* Can we pass through with slip factor? */
+ if(env->cfg->ratelimit_factor == 0 ||
+ ub_random_max(env->rnd,
+ env->cfg->ratelimit_factor) != 1) {
+ *was_ratelimited = 1;
+ alloc_reg_release(env->alloc, region);
+ return NULL;
+ }
+ log_nametypeclass(VERB_ALGO,
+ "ratelimit allowed through for "
+ "delegation point", zone,
+ LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
+ }
+ }
/* make new serviced query entry */
sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps,
tcp_upstream, ssl_upstream, tls_auth_name, addr,
addrlen, zone, zonelen, (int)qinfo->qtype,
- qstate->edns_opts_back_out,
+ per_upstream_opt_list,
( ssl_upstream && env->cfg->pad_queries
- ? env->cfg->pad_queries_block_size : 0 ));
+ ? env->cfg->pad_queries_block_size : 0 ),
+ env->alloc, region);
if(!sq) {
- free(cb);
+ if(check_ratelimit) {
+ infra_ratelimit_dec(env->infra_cache,
+ zone, zonelen, timenow);
+ }
+ alloc_reg_release(env->alloc, region);
return NULL;
}
- /* perform first network action */
- if(outnet->do_udp && !(tcp_upstream || ssl_upstream)) {
- if(!serviced_udp_send(sq, buff)) {
- (void)rbtree_delete(outnet->serviced, sq);
- serviced_node_del(&sq->node, NULL);
- free(cb);
- return NULL;
- }
- } else {
- if(!serviced_tcp_send(sq, buff)) {
- (void)rbtree_delete(outnet->serviced, sq);
- serviced_node_del(&sq->node, NULL);
- free(cb);
- return NULL;
+ if(!(cb = (struct service_callback*)regional_alloc(
+ sq->region, sizeof(*cb)))) {
+ if(check_ratelimit) {
+ infra_ratelimit_dec(env->infra_cache,
+ zone, zonelen, timenow);
}
+ (void)rbtree_delete(outnet->serviced, sq);
+ serviced_node_del(&sq->node, NULL);
+ return NULL;
+ }
+ /* No network action at this point; it will be invoked with the
+ * serviced_query timer instead to run outside of the mesh. */
+ } else {
+ /* We don't need this region anymore. */
+ alloc_reg_release(env->alloc, region);
+ /* duplicate entries are included in the callback list, because
+ * there is a counterpart registration by our caller that needs
+ * to be doubly-removed (with callbacks perhaps). */
+ if(!(cb = (struct service_callback*)regional_alloc(
+ sq->region, sizeof(*cb)))) {
+ return NULL;
}
}
/* add callback to list of callbacks */
@@ -3328,7 +3464,6 @@ callback_list_remove(struct serviced_query* sq, void* cb_arg)
if((*pp)->cb_arg == cb_arg) {
struct service_callback* del = *pp;
*pp = del->next;
- free(del);
return;
}
pp = &(*pp)->next;
@@ -3337,13 +3472,13 @@ callback_list_remove(struct serviced_query* sq, void* cb_arg)
void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
{
- if(!sq)
+ if(!sq)
return;
callback_list_remove(sq, cb_arg);
/* if callbacks() routine scheduled deletion, let it do that */
- if(!sq->cblist && !sq->to_be_deleted) {
+ if(!sq->cblist && !sq->busy && !sq->to_be_deleted) {
(void)rbtree_delete(sq->outnet->serviced, sq);
- serviced_delete(sq);
+ serviced_delete(sq);
}
}
diff --git a/contrib/unbound/services/outside_network.h b/contrib/unbound/services/outside_network.h
index d0d532e6425f..4c5b96f83424 100644
--- a/contrib/unbound/services/outside_network.h
+++ b/contrib/unbound/services/outside_network.h
@@ -43,7 +43,9 @@
#ifndef OUTSIDE_NETWORK_H
#define OUTSIDE_NETWORK_H
+#include "util/alloc.h"
#include "util/rbtree.h"
+#include "util/regional.h"
#include "util/netevent.h"
#include "dnstap/dnstap_config.h"
struct pending;
@@ -412,6 +414,8 @@ struct waiting_tcp {
char* tls_auth_name;
/** the packet was involved in an error, to stop looping errors */
int error_count;
+ /** if true, the item is at the cb_and_decommission stage */
+ int in_cb_and_decommission;
#ifdef USE_DNSTAP
/** serviced query pointer for dnstap to get logging info, if nonNULL*/
struct serviced_query* sq;
@@ -512,6 +516,15 @@ struct serviced_query {
void* pending;
/** block size with which to pad encrypted queries (default: 128) */
size_t padding_block_size;
+ /** region for this serviced query. Will be cleared when this
+ * serviced_query will be deleted */
+ struct regional* region;
+ /** allocation service for the region */
+ struct alloc_cache* alloc;
+ /** flash timer to start the net I/O as a separate event */
+ struct comm_timer* timer;
+ /** true if serviced_query is currently doing net I/O and may block */
+ int busy;
};
/**
@@ -619,6 +632,7 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
* @param want_dnssec: signatures are needed, without EDNS the answer is
* likely to be useless.
* @param nocaps: ignore use_caps_for_id and use unperturbed qname.
+ * @param check_ratelimit: if set, will check ratelimit before sending out.
* @param tcp_upstream: use TCP for upstream queries.
* @param ssl_upstream: use SSL for upstream queries.
* @param tls_auth_name: when ssl_upstream is true, use this name to check
@@ -635,16 +649,18 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
* @param callback_arg: user argument to callback function.
* @param buff: scratch buffer to create query contents in. Empty on exit.
* @param env: the module environment.
+ * @param was_ratelimited: it will signal back if the query failed to pass the
+ * ratelimit check.
* @return 0 on error, or pointer to serviced query that is used to answer
* this serviced query may be shared with other callbacks as well.
*/
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
- int nocaps, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
- struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
- size_t zonelen, struct module_qstate* qstate,
+ int nocaps, int check_ratelimit, int tcp_upstream, int ssl_upstream,
+ char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
+ uint8_t* zone, size_t zonelen, struct module_qstate* qstate,
comm_point_callback_type* callback, void* callback_arg,
- struct sldns_buffer* buff, struct module_env* env);
+ struct sldns_buffer* buff, struct module_env* env, int* was_ratelimited);
/**
* Remove service query callback.
@@ -785,6 +801,9 @@ void pending_udp_timer_delay_cb(void *arg);
/** callback for outgoing TCP timer event */
void outnet_tcptimer(void* arg);
+/** callback to send serviced queries */
+void serviced_timer_cb(void *arg);
+
/** callback for serviced query UDP answers */
int serviced_udp_callback(struct comm_point* c, void* arg, int error,
struct comm_reply* rep);
diff --git a/contrib/unbound/services/rpz.c b/contrib/unbound/services/rpz.c
index d408f9383d24..322e9d1393c4 100644
--- a/contrib/unbound/services/rpz.c
+++ b/contrib/unbound/services/rpz.c
@@ -542,6 +542,7 @@ rpz_create(struct config_auth* p)
}
}
r->log = p->rpz_log;
+ r->signal_nxdomain_ra = p->rpz_signal_nxdomain_ra;
if(p->rpz_log_name) {
if(!(r->log_name = strdup(p->rpz_log_name))) {
log_err("malloc failure on RPZ log_name strdup");
@@ -836,7 +837,7 @@ rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) {
}
/* from localzone.c; difference is we don't have a dname */
-struct local_rrset*
+static struct local_rrset*
rpz_clientip_new_rrset(struct regional* region,
struct clientip_synthesized_rr* raddr, uint16_t rrtype, uint16_t rrclass)
{
@@ -1384,9 +1385,9 @@ log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode,
if(dname) {
dname_str(dname, dnamestr);
} else if(addrnode) {
- char a[128];
- addr_to_str(&addrnode->addr, addrnode->addrlen, a, sizeof(a));
- snprintf(dnamestr, sizeof(dnamestr), "%s/%d", a, addrnode->net);
+ char addrbuf[128];
+ addr_to_str(&addrnode->addr, addrnode->addrlen, addrbuf, sizeof(addrbuf));
+ snprintf(dnamestr, sizeof(dnamestr), "%s/%d", addrbuf, addrnode->net);
} else {
dnamestr[0]=0;
}
@@ -1697,7 +1698,7 @@ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
if(msg == NULL) { return msg; }
msg->qinfo = *qinfo;
msg->rep = construct_reply_info_base(ms->region,
- LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+ LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
1, /* qd */
0, /* ttl */
0, /* prettl */
@@ -1715,14 +1716,18 @@ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
}
static inline struct dns_msg*
-rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
+rpz_synthesize_nxdomain(struct rpz* r, struct module_qstate* ms,
struct query_info* qinfo, struct auth_zone* az)
{
struct dns_msg* msg = rpz_dns_msg_new(ms->region);
+ uint16_t flags;
if(msg == NULL) { return msg; }
msg->qinfo = *qinfo;
+ flags = LDNS_RCODE_NXDOMAIN | BIT_QR | BIT_AA | BIT_RA;
+ if(r->signal_nxdomain_ra)
+ flags &= ~BIT_RA;
msg->rep = construct_reply_info_base(ms->region,
- LDNS_RCODE_NXDOMAIN | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+ flags,
1, /* qd */
0, /* ttl */
0, /* prettl */
@@ -1752,7 +1757,7 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs
if(msg == NULL) { return NULL; }
new_reply_info = construct_reply_info_base(ms->region,
- LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+ LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
1, /* qd */
0, /* ttl */
0, /* prettl */
@@ -1922,6 +1927,9 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
0 /* no local data used */, lzt);
+ if(r->signal_nxdomain_ra && LDNS_RCODE_WIRE(sldns_buffer_begin(buf))
+ == LDNS_RCODE_NXDOMAIN)
+ LDNS_RA_CLR(sldns_buffer_begin(buf));
if(r->log) {
log_rpz_apply("qname", z->name, NULL, localzone_type_to_rpz_action(lzt),
qinfo, repinfo, NULL, r->log_name);
@@ -1930,7 +1938,7 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
return ret;
}
-struct clientip_synthesized_rr*
+static struct clientip_synthesized_rr*
rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* is)
{
struct delegpt_addr* cursor;
@@ -1947,7 +1955,7 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate*
return NULL;
}
-struct dns_msg*
+static struct dns_msg*
rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r,
struct clientip_synthesized_rr* raddr, struct auth_zone* az)
{
@@ -2006,7 +2014,7 @@ done:
return ret;
}
-struct dns_msg*
+static struct dns_msg*
rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r,
struct local_zone* z, struct matched_delegation_point const* match,
struct auth_zone* az)
@@ -2295,6 +2303,10 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env,
local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns,
repinfo, buf, temp, 0 /* no local data used */,
rpz_action_to_localzone_type(client_action));
+ if(*r_out && (*r_out)->signal_nxdomain_ra &&
+ LDNS_RCODE_WIRE(sldns_buffer_begin(buf))
+ == LDNS_RCODE_NXDOMAIN)
+ LDNS_RA_CLR(sldns_buffer_begin(buf));
}
ret = 1;
goto done;
diff --git a/contrib/unbound/services/rpz.h b/contrib/unbound/services/rpz.h
index 691475743606..c29d30dff506 100644
--- a/contrib/unbound/services/rpz.h
+++ b/contrib/unbound/services/rpz.h
@@ -123,6 +123,8 @@ struct rpz {
struct ub_packed_rrset_key* cname_override;
int log;
char* log_name;
+ /** signal NXDOMAIN blocked with unset RA flag */
+ int signal_nxdomain_ra;
struct regional* region;
int disabled;
};