aboutsummaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/authzone.c10
-rw-r--r--services/cache/dns.c52
-rw-r--r--services/cache/infra.c170
-rw-r--r--services/cache/infra.h28
-rw-r--r--services/cache/rrset.c87
-rw-r--r--services/cache/rrset.h31
-rw-r--r--services/listen_dnsport.c51
-rw-r--r--services/listen_dnsport.h8
-rw-r--r--services/localzone.c6
-rw-r--r--services/mesh.c106
-rw-r--r--services/mesh.h6
-rw-r--r--services/rpz.c342
-rw-r--r--services/rpz.h8
-rw-r--r--services/view.h3
14 files changed, 734 insertions, 174 deletions
diff --git a/services/authzone.c b/services/authzone.c
index 93fef8ef1c29..f01a6d9e0e0e 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -2152,6 +2152,16 @@ auth_zones_cfg(struct auth_zones* az, struct config_auth* c)
if(az->rpz_first)
az->rpz_first->rpz_az_prev = z;
az->rpz_first = z;
+ } else if(c->isrpz && z->rpz) {
+ if(!rpz_config(z->rpz, c)) {
+ log_err("Could not change rpz config");
+ if(x) {
+ lock_basic_unlock(&x->lock);
+ }
+ lock_rw_unlock(&z->lock);
+ lock_rw_unlock(&az->rpz_lock);
+ return 0;
+ }
}
if(c->isrpz) {
lock_rw_unlock(&az->rpz_lock);
diff --git a/services/cache/dns.c b/services/cache/dns.c
index 6a980548d72f..632ed79ace49 100644
--- a/services/cache/dns.c
+++ b/services/cache/dns.c
@@ -193,46 +193,6 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
}
-/** see if an rrset is expired above the qname, return upper qname. */
-static int
-rrset_expired_above(struct module_env* env, uint8_t** qname, size_t* qnamelen,
- uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* expiretop,
- size_t expiretoplen)
-{
- struct ub_packed_rrset_key *rrset;
- uint8_t lablen;
-
- while(*qnamelen > 0) {
- /* look one label higher */
- lablen = **qname;
- *qname += lablen + 1;
- *qnamelen -= lablen + 1;
- if(*qnamelen <= 0)
- break;
-
- /* looks up with a time of 0, to see expired entries */
- if((rrset = rrset_cache_lookup(env->rrset_cache, *qname,
- *qnamelen, searchtype, qclass, 0, 0, 0))) {
- struct packed_rrset_data* data =
- (struct packed_rrset_data*)rrset->entry.data;
- if(now > data->ttl) {
- /* it is expired, this is not wanted */
- lock_rw_unlock(&rrset->entry.lock);
- log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
- return 1;
- }
- /* it is not expired, continue looking */
- lock_rw_unlock(&rrset->entry.lock);
- }
-
- /* do not look above the expiretop. */
- if(expiretop && *qnamelen == expiretoplen &&
- query_dname_compare(*qname, expiretop)==0)
- break;
- }
- return 0;
-}
-
/** find closest NS or DNAME and returns the rrset (locked) */
static struct ub_packed_rrset_key*
find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
@@ -266,12 +226,12 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
/* check for expiry, but we have to let go of the rrset
* for the lock ordering */
lock_rw_unlock(&rrset->entry.lock);
- /* the expired_above function always takes off one
- * label (if qnamelen>0) and returns the final qname
- * where it searched, so we can continue from there
- * turning the O N*N search into O N. */
- if(!rrset_expired_above(env, &qname, &qnamelen,
- searchtype, qclass, now, expiretop,
+ /* the rrset_cache_expired_above function always takes
+ * off one label (if qnamelen>0) and returns the final
+ * qname where it searched, so we can continue from
+ * there turning the O N*N search into O N. */
+ if(!rrset_cache_expired_above(env->rrset_cache, &qname,
+ &qnamelen, searchtype, qclass, now, expiretop,
expiretoplen)) {
/* we want to return rrset, but it may be
* gone from cache, if so, just loop like
diff --git a/services/cache/infra.c b/services/cache/infra.c
index 31462d13ae0a..457685ab5985 100644
--- a/services/cache/infra.c
+++ b/services/cache/infra.c
@@ -234,6 +234,81 @@ setup_domain_limits(struct infra_cache* infra, struct config_file* cfg)
return 1;
}
+/** find or create element in wait limit netblock tree */
+static struct wait_limit_netblock_info*
+wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
+ int cookie)
+{
+ rbtree_type* tree;
+ struct sockaddr_storage addr;
+ int net;
+ socklen_t addrlen;
+ struct wait_limit_netblock_info* d;
+
+ if(!netblockstrtoaddr(str, 0, &addr, &addrlen, &net)) {
+ log_err("cannot parse wait limit netblock '%s'", str);
+ return 0;
+ }
+
+ /* can we find it? */
+ if(cookie)
+ tree = &infra->wait_limits_cookie_netblock;
+ else
+ tree = &infra->wait_limits_netblock;
+ d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr,
+ addrlen, net);
+ if(d)
+ return d;
+
+ /* create it */
+ d = (struct wait_limit_netblock_info*)calloc(1, sizeof(*d));
+ if(!d)
+ return NULL;
+ d->limit = -1;
+ if(!addr_tree_insert(tree, &d->node, &addr, addrlen, net)) {
+ log_err("duplicate element in domainlimit tree");
+ free(d);
+ return NULL;
+ }
+ return d;
+}
+
+
+/** insert wait limit information into lookup tree */
+static int
+infra_wait_limit_netblock_insert(struct infra_cache* infra,
+ struct config_file* cfg)
+{
+ struct config_str2list* p;
+ struct wait_limit_netblock_info* d;
+ for(p = cfg->wait_limit_netblock; p; p = p->next) {
+ d = wait_limit_netblock_findcreate(infra, p->str, 0);
+ if(!d)
+ return 0;
+ d->limit = atoi(p->str2);
+ }
+ for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) {
+ d = wait_limit_netblock_findcreate(infra, p->str, 1);
+ if(!d)
+ return 0;
+ d->limit = atoi(p->str2);
+ }
+ return 1;
+}
+
+/** setup wait limits tree (0 on failure) */
+static int
+setup_wait_limits(struct infra_cache* infra, struct config_file* cfg)
+{
+ addr_tree_init(&infra->wait_limits_netblock);
+ addr_tree_init(&infra->wait_limits_cookie_netblock);
+ if(!infra_wait_limit_netblock_insert(infra, cfg))
+ return 0;
+ addr_tree_init_parents(&infra->wait_limits_netblock);
+ addr_tree_init_parents(&infra->wait_limits_cookie_netblock);
+ return 1;
+}
+
struct infra_cache*
infra_create(struct config_file* cfg)
{
@@ -267,6 +342,10 @@ infra_create(struct config_file* cfg)
infra_delete(infra);
return NULL;
}
+ if(!setup_wait_limits(infra, cfg)) {
+ infra_delete(infra);
+ return NULL;
+ }
infra_ip_ratelimit = cfg->ip_ratelimit;
infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs,
INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc,
@@ -287,6 +366,12 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg))
}
}
+/** delete wait_limit_netblock_info entries */
+static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg))
+{
+ free(n);
+}
+
void
infra_delete(struct infra_cache* infra)
{
@@ -296,6 +381,10 @@ infra_delete(struct infra_cache* infra)
slabhash_delete(infra->domain_rates);
traverse_postorder(&infra->domain_limits, domain_limit_free, NULL);
slabhash_delete(infra->client_ip_rates);
+ traverse_postorder(&infra->wait_limits_netblock,
+ wait_limit_netblock_del, NULL);
+ traverse_postorder(&infra->wait_limits_cookie_netblock,
+ wait_limit_netblock_del, NULL);
free(infra);
}
@@ -880,7 +969,8 @@ static void infra_create_ratedata(struct infra_cache* infra,
/** create rate data item for ip address */
static void infra_ip_create_ratedata(struct infra_cache* infra,
- struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow)
+ struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow,
+ int mesh_wait)
{
hashvalue_type h = hash_addr(addr, addrlen, 0);
struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k));
@@ -898,6 +988,7 @@ static void infra_ip_create_ratedata(struct infra_cache* infra,
k->entry.data = d;
d->qps[0] = 1;
d->timestamp[0] = timenow;
+ d->mesh_wait = mesh_wait;
slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL);
}
@@ -1121,6 +1212,81 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra,
}
/* create */
- infra_ip_create_ratedata(infra, addr, addrlen, timenow);
+ infra_ip_create_ratedata(infra, addr, addrlen, timenow, 0);
return 1;
}
+
+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)
+ return 1;
+
+ entry = infra_find_ip_ratedata(infra, &rep->client_addr,
+ rep->client_addrlen, 0);
+ if(entry) {
+ rbtree_type* tree;
+ struct wait_limit_netblock_info* w;
+ struct rate_data* d = (struct rate_data*)entry->data;
+ int mesh_wait = d->mesh_wait;
+ lock_rw_unlock(&entry->lock);
+
+ /* have the wait amount, check how much is allowed */
+ if(cookie_valid)
+ tree = &infra->wait_limits_cookie_netblock;
+ else tree = &infra->wait_limits_netblock;
+ w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree,
+ &rep->client_addr, rep->client_addrlen);
+ if(w) {
+ if(w->limit != -1 && mesh_wait > w->limit)
+ return 0;
+ } else {
+ /* if there is no IP netblock specific information,
+ * use the configured value. */
+ if(mesh_wait > (cookie_valid?cfg->wait_limit_cookie:
+ cfg->wait_limit))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep,
+ time_t timenow, struct config_file* cfg)
+{
+ struct lruhash_entry* entry;
+ if(cfg->wait_limit == 0)
+ return;
+
+ /* Find it */
+ entry = infra_find_ip_ratedata(infra, &rep->client_addr,
+ rep->client_addrlen, 1);
+ if(entry) {
+ struct rate_data* d = (struct rate_data*)entry->data;
+ d->mesh_wait++;
+ lock_rw_unlock(&entry->lock);
+ return;
+ }
+
+ /* Create it */
+ infra_ip_create_ratedata(infra, &rep->client_addr,
+ rep->client_addrlen, timenow, 1);
+}
+
+void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep,
+ struct config_file* cfg)
+{
+ struct lruhash_entry* entry;
+ if(cfg->wait_limit == 0)
+ return;
+
+ entry = infra_find_ip_ratedata(infra, &rep->client_addr,
+ rep->client_addrlen, 1);
+ if(entry) {
+ struct rate_data* d = (struct rate_data*)entry->data;
+ if(d->mesh_wait > 0)
+ d->mesh_wait--;
+ lock_rw_unlock(&entry->lock);
+ }
+}
diff --git a/services/cache/infra.h b/services/cache/infra.h
index 525073bf35bb..ee6f384de345 100644
--- a/services/cache/infra.h
+++ b/services/cache/infra.h
@@ -122,6 +122,10 @@ struct infra_cache {
rbtree_type domain_limits;
/** hash table with query rates per client ip: ip_rate_key, ip_rate_data */
struct slabhash* client_ip_rates;
+ /** tree of addr_tree_node, with wait_limit_netblock_info information */
+ rbtree_type wait_limits_netblock;
+ /** tree of addr_tree_node, with wait_limit_netblock_info information */
+ rbtree_type wait_limits_cookie_netblock;
};
/** ratelimit, unless overridden by domain_limits, 0 is off */
@@ -184,10 +188,22 @@ struct rate_data {
/** what the timestamp is of the qps array members, counter is
* valid for that timestamp. Usually now and now-1. */
time_t timestamp[RATE_WINDOW];
+ /** the number of queries waiting in the mesh */
+ int mesh_wait;
};
#define ip_rate_data rate_data
+/**
+ * Data to store the configuration per netblock for the wait limit
+ */
+struct wait_limit_netblock_info {
+ /** The addr tree node, this must be first. */
+ struct addr_tree_node node;
+ /** the limit on the amount */
+ int limit;
+};
+
/** infra host cache default hash lookup size */
#define INFRA_HOST_STARTSIZE 32
/** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */
@@ -474,4 +490,16 @@ void ip_rate_delkeyfunc(void* d, void* arg);
/* delete data */
#define ip_rate_deldatafunc rate_deldatafunc
+/** See if the IP address can have another reply in the wait limit */
+int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep,
+ int cookie_valid, struct config_file* cfg);
+
+/** Increment number of waiting replies for IP */
+void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep,
+ time_t timenow, struct config_file* cfg);
+
+/** Decrement number of waiting replies for IP */
+void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep,
+ struct config_file* cfg);
+
#endif /* SERVICES_CACHE_INFRA_H */
diff --git a/services/cache/rrset.c b/services/cache/rrset.c
index 4e3d08bdaaf5..2c03214c8fe2 100644
--- a/services/cache/rrset.c
+++ b/services/cache/rrset.c
@@ -46,6 +46,7 @@
#include "util/data/packed_rrset.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
+#include "util/data/dname.h"
#include "util/regional.h"
#include "util/alloc.h"
#include "util/net_help.h"
@@ -127,6 +128,9 @@ 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, current data is better */
+ if( newd->ttl < timenow && cached->ttl >= timenow)
+ return 0;
/* o store if rrset has been validated
* everything better than bogus data
* secure is preferred */
@@ -440,6 +444,89 @@ rrset_check_sec_status(struct rrset_cache* r,
lock_rw_unlock(&e->lock);
}
+void
+rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname, size_t*
+ qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t*
+ qnametop, size_t qnametoplen)
+{
+ struct ub_packed_rrset_key *rrset;
+ uint8_t lablen;
+
+ while(*qnamelen > 0) {
+ /* look one label higher */
+ lablen = **qname;
+ *qname += lablen + 1;
+ *qnamelen -= lablen + 1;
+ if(*qnamelen <= 0)
+ return;
+
+ /* stop at qnametop */
+ if(qnametop && *qnamelen == qnametoplen &&
+ query_dname_compare(*qname, qnametop)==0)
+ return;
+
+ if(verbosity >= VERB_ALGO) {
+ /* looks up with a time of 0, to see expired entries */
+ if((rrset = rrset_cache_lookup(r, *qname,
+ *qnamelen, searchtype, qclass, 0, 0, 0))) {
+ struct packed_rrset_data* data =
+ (struct packed_rrset_data*)rrset->entry.data;
+ int expired = (now > data->ttl);
+ lock_rw_unlock(&rrset->entry.lock);
+ if(expired)
+ log_nametypeclass(verbosity, "this "
+ "(grand)parent rrset will be "
+ "removed (expired)",
+ *qname, searchtype, qclass);
+ else log_nametypeclass(verbosity, "this "
+ "(grand)parent rrset will be "
+ "removed",
+ *qname, searchtype, qclass);
+ }
+ }
+ rrset_cache_remove(r, *qname, *qnamelen, searchtype, qclass, 0);
+ }
+}
+
+int
+rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, size_t*
+ qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t*
+ qnametop, size_t qnametoplen)
+{
+ struct ub_packed_rrset_key *rrset;
+ uint8_t lablen;
+
+ while(*qnamelen > 0) {
+ /* look one label higher */
+ lablen = **qname;
+ *qname += lablen + 1;
+ *qnamelen -= lablen + 1;
+ if(*qnamelen <= 0)
+ break;
+
+ /* looks up with a time of 0, to see expired entries */
+ if((rrset = rrset_cache_lookup(r, *qname,
+ *qnamelen, searchtype, qclass, 0, 0, 0))) {
+ struct packed_rrset_data* data =
+ (struct packed_rrset_data*)rrset->entry.data;
+ if(now > data->ttl) {
+ /* it is expired, this is not wanted */
+ lock_rw_unlock(&rrset->entry.lock);
+ log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
+ return 1;
+ }
+ /* it is not expired, continue looking */
+ lock_rw_unlock(&rrset->entry.lock);
+ }
+
+ /* do not look above the qnametop. */
+ if(qnametop && *qnamelen == qnametoplen &&
+ query_dname_compare(*qname, qnametop)==0)
+ break;
+ }
+ return 0;
+}
+
void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen,
uint16_t type, uint16_t dclass, uint32_t flags)
{
diff --git a/services/cache/rrset.h b/services/cache/rrset.h
index 7c36d4032ebc..6db79d90f532 100644
--- a/services/cache/rrset.h
+++ b/services/cache/rrset.h
@@ -232,6 +232,37 @@ void rrset_check_sec_status(struct rrset_cache* r,
struct ub_packed_rrset_key* rrset, time_t now);
/**
+ * Removes rrsets above the qname, returns upper qname.
+ * @param r: the rrset cache.
+ * @param qname: the start qname, also used as the output.
+ * @param qnamelen: length of qname, updated when it returns.
+ * @param searchtype: qtype to search for.
+ * @param qclass: qclass to search for.
+ * @param now: current time.
+ * @param qnametop: the top qname to stop removal (it is not removed).
+ * @param qnametoplen: length of qnametop.
+ */
+void rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname,
+ size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now,
+ uint8_t* qnametop, size_t qnametoplen);
+
+/**
+ * Sees if an rrset is expired above the qname, returns upper qname.
+ * @param r: the rrset cache.
+ * @param qname: the start qname, also used as the output.
+ * @param qnamelen: length of qname, updated when it returns.
+ * @param searchtype: qtype to search for.
+ * @param qclass: qclass to search for.
+ * @param now: current time.
+ * @param qnametop: the top qname, don't look farther than that.
+ * @param qnametoplen: length of qnametop.
+ * @return true if there is an expired rrset above, false otherwise.
+ */
+int rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname,
+ size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now,
+ uint8_t* qnametop, size_t qnametoplen);
+
+/**
* Remove an rrset from the cache, by name and type and flags
* @param r: rrset cache
* @param nm: name of rrset
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index 753550978a07..7eb59a1618a1 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.c
@@ -140,9 +140,11 @@ void
verbose_print_unbound_socket(struct unbound_socket* ub_sock)
{
if(verbosity >= VERB_ALGO) {
+ char buf[256];
log_info("listing of unbound_socket structure:");
- verbose_print_addr(ub_sock->addr);
- log_info("s is: %d, fam is: %s, acl: %s", ub_sock->s,
+ addr_to_str((void*)ub_sock->addr, ub_sock->addrlen, buf,
+ sizeof(buf));
+ log_info("%s s is: %d, fam is: %s, acl: %s", buf, ub_sock->s,
ub_sock->fam == AF_INET?"AF_INET":"AF_INET6",
ub_sock->acl?"yes":"no");
}
@@ -610,7 +612,9 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
# elif defined(IP_DONTFRAG) && !defined(__APPLE__)
/* the IP_DONTFRAG option if defined in the 11.0 OSX headers,
* but does not work on that version, so we exclude it */
- int off = 0;
+ /* a nonzero value disables fragmentation, according to
+ * docs.oracle.com for ip(4). */
+ int off = 1;
if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
&off, (socklen_t)sizeof(off)) < 0) {
log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s",
@@ -1047,7 +1051,22 @@ make_sock(int stype, const char* ifname, const char* port,
}
}
- ub_sock->addr = res;
+ if(!res->ai_addr) {
+ log_err("getaddrinfo returned no address");
+ freeaddrinfo(res);
+ sock_close(s);
+ return -1;
+ }
+ ub_sock->addr = memdup(res->ai_addr, res->ai_addrlen);
+ ub_sock->addrlen = res->ai_addrlen;
+ if(!ub_sock->addr) {
+ log_err("out of memory: allocate listening address");
+ freeaddrinfo(res);
+ sock_close(s);
+ return -1;
+ }
+ freeaddrinfo(res);
+
ub_sock->s = s;
ub_sock->fam = hints->ai_family;
ub_sock->acl = NULL;
@@ -1277,8 +1296,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) {
- if(ub_sock->addr)
- freeaddrinfo(ub_sock->addr);
+ free(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
@@ -1289,8 +1307,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
/* getting source addr packet info is highly non-portable */
if(!set_recvpktinfo(s, hints->ai_family)) {
sock_close(s);
- if(ub_sock->addr)
- freeaddrinfo(ub_sock->addr);
+ free(ub_sock->addr);
free(ub_sock);
return 0;
}
@@ -1301,8 +1318,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
?listen_type_udpancil_dnscrypt:listen_type_udpancil,
is_pp2, ub_sock)) {
sock_close(s);
- if(ub_sock->addr)
- freeaddrinfo(ub_sock->addr);
+ free(ub_sock->addr);
free(ub_sock);
return 0;
}
@@ -1314,8 +1330,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) {
- if(ub_sock->addr)
- freeaddrinfo(ub_sock->addr);
+ free(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
@@ -1332,8 +1347,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
listen_type_udpancil:listen_type_udp),
is_pp2, ub_sock)) {
sock_close(s);
- if(ub_sock->addr)
- freeaddrinfo(ub_sock->addr);
+ free(ub_sock->addr);
free(ub_sock);
return 0;
}
@@ -1356,8 +1370,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1,
&noip6, 0, 0, reuseport, transparent, tcp_mss, nodelay,
freebind, use_systemd, dscp, ub_sock)) == -1) {
- if(ub_sock->addr)
- freeaddrinfo(ub_sock->addr);
+ free(ub_sock->addr);
free(ub_sock);
if(noip6) {
/*log_warn("IPv6 protocol not available");*/
@@ -1369,8 +1382,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
verbose(VERB_ALGO, "setup TCP for SSL service");
if(!port_insert(list, s, port_type, is_pp2, ub_sock)) {
sock_close(s);
- if(ub_sock->addr)
- freeaddrinfo(ub_sock->addr);
+ free(ub_sock->addr);
free(ub_sock);
return 0;
}
@@ -1952,8 +1964,7 @@ void listening_ports_free(struct listen_port* list)
}
/* rc_ports don't have ub_socket */
if(list->socket) {
- if(list->socket->addr)
- freeaddrinfo(list->socket->addr);
+ free(list->socket->addr);
free(list->socket);
}
free(list);
diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h
index 816d79aea61b..84ac4b068b1b 100644
--- a/services/listen_dnsport.h
+++ b/services/listen_dnsport.h
@@ -107,11 +107,13 @@ enum listen_type {
* socket properties (just like NSD nsd_socket structure definition)
*/
struct unbound_socket {
- /** socket-address structure */
- struct addrinfo* addr;
+ /** the address of the socket */
+ struct sockaddr* addr;
+ /** length of the address */
+ socklen_t addrlen;
/** socket descriptor returned by socket() syscall */
int s;
- /** address family (AF_INET/IF_INET6) */
+ /** address family (AF_INET/AF_INET6) */
int fam;
/** ACL on the socket (listening interface) */
struct acl_addr* acl;
diff --git a/services/localzone.c b/services/localzone.c
index 9c8e3c7acb16..51056c8ffef4 100644
--- a/services/localzone.c
+++ b/services/localzone.c
@@ -330,14 +330,16 @@ get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass,
static struct local_rrset*
local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
{
- struct local_rrset* p;
+ struct local_rrset* p, *cname = NULL;
type = htons(type);
for(p = data->rrsets; p; p = p->next) {
if(p->rrset->rk.type == type)
return p;
if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME))
- return p;
+ cname = p;
}
+ if(alias_ok)
+ return cname;
return NULL;
}
diff --git a/services/mesh.c b/services/mesh.c
index 47cfb04249b5..e886c4b92c84 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -47,6 +47,7 @@
#include "services/outbound_list.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
+#include "services/cache/infra.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/module.h"
@@ -385,7 +386,7 @@ mesh_serve_expired_init(struct mesh_state* mstate, int timeout)
&mesh_serve_expired_lookup;
/* In case this timer already popped, start it again */
- if(!mstate->s.serve_expired_data->timer) {
+ if(!mstate->s.serve_expired_data->timer && timeout != -1) {
mstate->s.serve_expired_data->timer = comm_timer_create(
mstate->s.env->worker_base, mesh_serve_expired_callback, mstate);
if(!mstate->s.serve_expired_data->timer)
@@ -415,6 +416,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
if(rep->c->tcp_req_info) {
r_buffer = rep->c->tcp_req_info->spool_buffer;
}
+ 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);
+ mesh->stats_dropped++;
+ return;
+ }
if(!unique)
s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
/* does this create a new reply state? */
@@ -511,6 +520,19 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
log_err("mesh_new_client: out of memory initializing serve expired");
goto servfail_mem;
}
+#ifdef USE_CACHEDB
+ if(!timeout && mesh->env->cfg->serve_expired &&
+ !mesh->env->cfg->serve_expired_client_timeout &&
+ (mesh->env->cachedb_enabled &&
+ mesh->env->cfg->cachedb_check_when_serve_expired)) {
+ if(!mesh_serve_expired_init(s, -1)) {
+ log_err("mesh_new_client: out of memory initializing serve expired");
+ goto servfail_mem;
+ }
+ }
+#endif
+ infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now,
+ mesh->env->cfg);
/* update statistics */
if(was_detached) {
log_assert(mesh->num_detached_states > 0);
@@ -616,6 +638,18 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
mesh_state_delete(&s->s);
return 0;
}
+#ifdef USE_CACHEDB
+ if(!timeout && mesh->env->cfg->serve_expired &&
+ !mesh->env->cfg->serve_expired_client_timeout &&
+ (mesh->env->cachedb_enabled &&
+ mesh->env->cfg->cachedb_check_when_serve_expired)) {
+ if(!mesh_serve_expired_init(s, -1)) {
+ if(added)
+ mesh_state_delete(&s->s);
+ return 0;
+ }
+ }
+#endif
/* update statistics */
if(was_detached) {
log_assert(mesh->num_detached_states > 0);
@@ -930,6 +964,8 @@ mesh_state_cleanup(struct mesh_state* mstate)
* takes no time and also it does not do the mesh accounting */
mstate->reply_list = NULL;
for(; rep; rep=rep->next) {
+ infra_wait_limit_dec(mesh->env->infra_cache,
+ &rep->query_reply, mesh->env->cfg);
comm_point_drop_reply(&rep->query_reply);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
@@ -1179,7 +1215,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
rcode = LDNS_RCODE_SERVFAIL;
if(!rcode && rep && (rep->security == sec_status_bogus ||
rep->security == sec_status_secure_sentinel_fail)) {
- if(!(reason = errinf_to_str_bogus(&m->s)))
+ if(!(reason = errinf_to_str_bogus(&m->s, NULL)))
rcode = LDNS_RCODE_SERVFAIL;
}
/* send the reply */
@@ -1413,6 +1449,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
}
+ infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply,
+ m->s.env->cfg);
/* account */
log_assert(m->s.env->mesh->num_reply_addrs > 0);
m->s.env->mesh->num_reply_addrs--;
@@ -1436,7 +1474,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
log_reply_info(NO_VERBOSE, &m->s.qinfo,
&r->query_reply.client_addr,
r->query_reply.client_addrlen, duration, 0, r_buffer,
- (m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr->ai_addr:NULL),
+ (m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr:NULL),
r->query_reply.c->type);
}
}
@@ -1464,12 +1502,32 @@ void mesh_query_done(struct mesh_state* mstate)
&& mstate->s.env->cfg->log_servfail
&& !mstate->s.env->cfg->val_log_squelch) {
char* err = errinf_to_str_servfail(&mstate->s);
- if(err)
- log_err("%s", err);
- free(err);
+ if(err) { log_err("%s", err); }
}
}
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;
+ comm_point_drop_reply(&r->query_reply);
+ mstate->reply_list = reply_list;
+ mstate->s.env->mesh->stats_dropped++;
+ continue;
+ }
+
i++;
tv = r->start_time;
@@ -1493,6 +1551,8 @@ void mesh_query_done(struct mesh_state* mstate)
* 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;
+ infra_wait_limit_dec(mstate->s.env->infra_cache,
+ &r->query_reply, mstate->s.env->cfg);
mstate->reply_list = NULL;
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
@@ -2025,6 +2085,8 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
/* delete it, but allocated in m region */
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
+ infra_wait_limit_dec(mesh->env->infra_cache,
+ &n->query_reply, mesh->env->cfg);
/* prev = prev; */
n = n->next;
@@ -2165,6 +2227,28 @@ mesh_serve_expired_callback(void* arg)
log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->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;
+ comm_point_drop_reply(&r->query_reply);
+ mstate->reply_list = reply_list;
+ mstate->s.env->mesh->stats_dropped++;
+ continue;
+ }
+
i++;
tv = r->start_time;
@@ -2192,6 +2276,8 @@ mesh_serve_expired_callback(void* arg)
r, r_buffer, prev, prev_buffer);
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
+ infra_wait_limit_dec(mstate->s.env->infra_cache,
+ &r->query_reply, mstate->s.env->cfg);
prev = r;
prev_buffer = r_buffer;
}
@@ -2238,6 +2324,14 @@ mesh_serve_expired_callback(void* arg)
}
}
+void
+mesh_respond_serve_expired(struct mesh_state* mstate)
+{
+ if(!mstate->s.serve_expired_data)
+ mesh_serve_expired_init(mstate, -1);
+ mesh_serve_expired_callback(mstate);
+}
+
int mesh_jostle_exceeded(struct mesh_area* mesh)
{
if(mesh->all.count < mesh->max_reply_states)
diff --git a/services/mesh.h b/services/mesh.h
index d926cfc9dec3..5bd53e065e8f 100644
--- a/services/mesh.h
+++ b/services/mesh.h
@@ -690,4 +690,10 @@ mesh_serve_expired_lookup(struct module_qstate* qstate,
*/
int mesh_jostle_exceeded(struct mesh_area* mesh);
+/**
+ * Give the serve expired responses.
+ * @param mstate: mesh state for query that has serve_expired_data.
+ */
+void mesh_respond_serve_expired(struct mesh_state* mstate);
+
#endif /* SERVICES_MESH_H */
diff --git a/services/rpz.c b/services/rpz.c
index 18d76c07bff3..f036cc5fd649 100644
--- a/services/rpz.c
+++ b/services/rpz.c
@@ -478,43 +478,30 @@ new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen)
return rrset;
}
-struct rpz*
-rpz_create(struct config_auth* p)
+/** delete the cname override */
+static void
+delete_cname_override(struct rpz* r)
{
- struct rpz* r = calloc(1, sizeof(*r));
- if(!r)
- goto err;
-
- r->region = regional_create_custom(sizeof(struct regional));
- if(!r->region) {
- goto err;
- }
-
- if(!(r->local_zones = local_zones_create())){
- goto err;
- }
-
- r->nsdname_zones = local_zones_create();
- if(r->local_zones == NULL){
- goto err;
- }
-
- if(!(r->respip_set = respip_set_create())) {
- goto err;
- }
-
- r->client_set = rpz_clientip_synthesized_set_create();
- if(r->client_set == NULL) {
- goto err;
+ if(r->cname_override) {
+ /* The cname override is what is allocated in the region. */
+ regional_free_all(r->region);
+ r->cname_override = NULL;
}
+}
- r->ns_set = rpz_clientip_synthesized_set_create();
- if(r->ns_set == NULL) {
- goto err;
+/** Apply rpz config elements to the rpz structure, false on failure. */
+static int
+rpz_apply_cfg_elements(struct rpz* r, struct config_auth* p)
+{
+ if(p->rpz_taglist && p->rpz_taglistlen) {
+ r->taglistlen = p->rpz_taglistlen;
+ r->taglist = memdup(p->rpz_taglist, r->taglistlen);
+ if(!r->taglist) {
+ log_err("malloc failure on RPZ taglist alloc");
+ return 0;
+ }
}
- r->taglistlen = p->rpz_taglistlen;
- r->taglist = memdup(p->rpz_taglist, r->taglistlen);
if(p->rpz_action_override) {
r->action_override = rpz_config_to_action(p->rpz_action_override);
}
@@ -528,17 +515,17 @@ rpz_create(struct config_auth* p)
if(!p->rpz_cname) {
log_err("rpz: override with cname action found, but no "
"rpz-cname-override configured");
- goto err;
+ return 0;
}
if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) {
log_err("rpz: cannot parse cname override: %s",
p->rpz_cname);
- goto err;
+ return 0;
}
r->cname_override = new_cname_override(r->region, nm, nmlen);
if(!r->cname_override) {
- goto err;
+ return 0;
}
}
r->log = p->rpz_log;
@@ -546,9 +533,49 @@ rpz_create(struct config_auth* p)
if(p->rpz_log_name) {
if(!(r->log_name = strdup(p->rpz_log_name))) {
log_err("malloc failure on RPZ log_name strdup");
- goto err;
+ return 0;
}
}
+ return 1;
+}
+
+struct rpz*
+rpz_create(struct config_auth* p)
+{
+ struct rpz* r = calloc(1, sizeof(*r));
+ if(!r)
+ goto err;
+
+ r->region = regional_create_custom(sizeof(struct regional));
+ if(!r->region) {
+ goto err;
+ }
+
+ if(!(r->local_zones = local_zones_create())){
+ goto err;
+ }
+
+ r->nsdname_zones = local_zones_create();
+ if(r->local_zones == NULL){
+ goto err;
+ }
+
+ if(!(r->respip_set = respip_set_create())) {
+ goto err;
+ }
+
+ r->client_set = rpz_clientip_synthesized_set_create();
+ if(r->client_set == NULL) {
+ goto err;
+ }
+
+ r->ns_set = rpz_clientip_synthesized_set_create();
+ if(r->ns_set == NULL) {
+ goto err;
+ }
+
+ if(!rpz_apply_cfg_elements(r, p))
+ goto err;
return r;
err:
if(r) {
@@ -571,6 +598,32 @@ err:
return NULL;
}
+int
+rpz_config(struct rpz* r, struct config_auth* p)
+{
+ /* If the zonefile changes, it is read later, after which
+ * rpz_clear and rpz_finish_config is called. */
+
+ /* free taglist, if any */
+ if(r->taglist) {
+ free(r->taglist);
+ r->taglist = NULL;
+ r->taglistlen = 0;
+ }
+
+ /* free logname, if any */
+ if(r->log_name) {
+ free(r->log_name);
+ r->log_name = NULL;
+ }
+
+ delete_cname_override(r);
+
+ if(!rpz_apply_cfg_elements(r, p))
+ return 0;
+ return 1;
+}
+
/**
* Remove RPZ zone name from dname
* Copy dname to newdname, without the originlen number of trailing bytes
@@ -1191,16 +1244,20 @@ rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint1
/** Find entry for RR type in the list of rrsets for the clientip. */
static struct local_rrset*
rpz_find_synthesized_rrset(uint16_t qtype,
- struct clientip_synthesized_rr* data)
+ struct clientip_synthesized_rr* data, int alias_ok)
{
- struct local_rrset* cursor = data->data;
+ struct local_rrset* cursor = data->data, *cname = NULL;
while( cursor != NULL) {
struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
if(htons(qtype) == packed_rrset->type) {
return cursor;
}
+ if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok)
+ cname = cursor;
cursor = cursor->next;
}
+ if(alias_ok)
+ return cname;
return NULL;
}
@@ -1386,7 +1443,7 @@ static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node,
struct local_rrset* rrset;
struct packed_rrset_data* d;
size_t index;
- rrset = rpz_find_synthesized_rrset(rr_type, node);
+ rrset = rpz_find_synthesized_rrset(rr_type, node, 0);
if(rrset == NULL)
return 0; /* type not found, ignore */
d = (struct packed_rrset_data*)rrset->rrset->entry.data;
@@ -1789,7 +1846,7 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr,
}
/* check query type / rr type */
- rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr);
+ rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: unable to find local-data for query");
rrset_count = 0;
@@ -1823,6 +1880,28 @@ nodata:
rrset_count, rcode, rsoa);
}
+/** Apply the cname override action, during worker request callback.
+ * false on failure. */
+static int
+rpz_apply_cname_override_action(struct rpz* r,
+ struct query_info* qinfo, struct regional* temp)
+{
+ if(!r)
+ return 0;
+ qinfo->local_alias = regional_alloc_zero(temp,
+ sizeof(struct local_rrset));
+ if(qinfo->local_alias == NULL)
+ return 0; /* out of memory */
+ qinfo->local_alias->rrset = respip_copy_rrset(r->cname_override, temp);
+ if(qinfo->local_alias->rrset == NULL) {
+ qinfo->local_alias = NULL;
+ return 0; /* out of memory */
+ }
+ qinfo->local_alias->rrset->rk.dname = qinfo->qname;
+ qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
+ return 1;
+}
+
/** add additional section SOA record to the reply.
* Since this gets fed into the normal iterator answer creation, it
* gets minimal-responses applied to it, that can remove the additional SOA
@@ -1933,6 +2012,7 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs
msg = rpz_dns_msg_new(ms->region);
if(msg == NULL) { return NULL; }
+ msg->qinfo = *qi;
new_reply_info = construct_reply_info_base(ms->region,
LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
1, /* qd */
@@ -1975,40 +2055,42 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs
static inline struct dns_msg*
rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms,
- struct clientip_synthesized_rr* data, struct auth_zone* az)
+ struct query_info* qi, struct clientip_synthesized_rr* data,
+ struct auth_zone* az)
{
- struct query_info* qi = &ms->qinfo;
struct local_rrset* rrset;
- rrset = rpz_find_synthesized_rrset(qi->qtype, data);
+ rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: nsip: no matching local data found");
return NULL;
}
- return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az);
+ return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az);
}
/* copy'n'paste from localzone.c */
static struct local_rrset*
local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
{
- struct local_rrset* p;
+ struct local_rrset* p, *cname = NULL;
type = htons(type);
for(p = data->rrsets; p; p = p->next) {
if(p->rrset->rk.type == type)
return p;
if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME))
- return p;
+ cname = p;
}
+ if(alias_ok)
+ return cname;
return NULL;
}
/* based on localzone.c:local_data_answer() */
static inline struct dns_msg*
rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms,
- struct local_zone* z, struct matched_delegation_point const* match,
- struct auth_zone* az)
+ struct query_info* qi, struct local_zone* z,
+ struct matched_delegation_point const* match, struct auth_zone* az)
{
struct local_data key;
struct local_data* ld;
@@ -2029,13 +2111,13 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms,
return NULL;
}
- rrset = local_data_find_type(ld, ms->qinfo.qtype, 1);
+ rrset = local_data_find_type(ld, qi->qtype, 1);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: nsdname: no matching local data found");
return NULL;
}
- return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az);
+ return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az);
}
/* like local_data_answer for qname triggers after a cname */
@@ -2052,17 +2134,70 @@ rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms,
key.namelabs = dname_count_labels(qinfo->qname);
ld = (struct local_data*)rbtree_search(&z->data, &key.node);
if(ld == NULL) {
- verbose(VERB_ALGO, "rpz: qname after cname: name not found");
+ verbose(VERB_ALGO, "rpz: qname: name not found");
return NULL;
}
rrset = local_data_find_type(ld, qinfo->qtype, 1);
if(rrset == NULL) {
- verbose(VERB_ALGO, "rpz: qname after cname: type not found");
+ verbose(VERB_ALGO, "rpz: qname: type not found");
return NULL;
}
return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az);
}
+/** Synthesize a CNAME message for RPZ action override */
+static struct dns_msg*
+rpz_synthesize_cname_override_msg(struct rpz* r, struct module_qstate* ms,
+ struct query_info* qinfo)
+{
+ struct dns_msg* msg = NULL;
+ struct reply_info* new_reply_info;
+ struct ub_packed_rrset_key* rp;
+
+ msg = rpz_dns_msg_new(ms->region);
+ if(msg == NULL) { return NULL; }
+
+ msg->qinfo = *qinfo;
+ new_reply_info = construct_reply_info_base(ms->region,
+ LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
+ 1, /* qd */
+ 0, /* ttl */
+ 0, /* prettl */
+ 0, /* expttl */
+ 1, /* an */
+ 0, /* ns */
+ 0, /* ar */
+ 1, /* total */
+ sec_status_insecure,
+ LDNS_EDE_NONE);
+ if(new_reply_info == NULL) {
+ log_err("out of memory");
+ return NULL;
+ }
+ new_reply_info->authoritative = 1;
+
+ rp = respip_copy_rrset(r->cname_override, ms->region);
+ if(rp == NULL) {
+ log_err("out of memory");
+ return NULL;
+ }
+ rp->rk.dname = qinfo->qname;
+ rp->rk.dname_len = qinfo->qname_len;
+ /* this rrset is from the rpz data, or synthesized.
+ * It is not actually from the network, so we flag it with this
+ * flags as a fake RRset. If later the cache is used to look up
+ * rrsets, then the fake ones are not returned (if you look without
+ * the flag). For like CNAME lookups from the iterator or A, AAAA
+ * lookups for nameserver targets, it would use the without flag
+ * actual data. So that the actual network data and fake data
+ * are kept track of separately. */
+ rp->rk.flags |= PACKED_RRSET_RPZ;
+ new_reply_info->rrsets[0] = rp;
+
+ msg->rep = new_reply_info;
+ return msg;
+}
+
static int
rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo,
@@ -2072,17 +2207,8 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
struct local_data* ld = NULL;
int ret = 0;
if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
- qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset));
- if(qinfo->local_alias == NULL) {
- return 0; /* out of memory */
- }
- qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override,
- sizeof(*r->cname_override));
- if(qinfo->local_alias->rrset == NULL) {
- return 0; /* out of memory */
- }
- qinfo->local_alias->rrset->rk.dname = qinfo->qname;
- qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
+ if(!rpz_apply_cname_override_action(r, qinfo, temp))
+ return 0;
if(r->log) {
log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION,
qinfo, repinfo, NULL, r->log_name);
@@ -2134,8 +2260,9 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate*
}
static struct dns_msg*
-rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r,
- struct clientip_synthesized_rr* raddr, struct auth_zone* az)
+rpz_apply_nsip_trigger(struct module_qstate* ms, struct query_info* qchase,
+ struct rpz* r, struct clientip_synthesized_rr* raddr,
+ struct auth_zone* az)
{
enum rpz_action action = raddr->action;
struct dns_msg* ret = NULL;
@@ -2148,16 +2275,16 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r,
if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) {
verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data");
- ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ ret = rpz_synthesize_nodata(r, ms, qchase, az);
goto done;
}
switch(action) {
case RPZ_NXDOMAIN_ACTION:
- ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az);
+ ret = rpz_synthesize_nxdomain(r, ms, qchase, az);
break;
case RPZ_NODATA_ACTION:
- ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ ret = rpz_synthesize_nodata(r, ms, qchase, az);
break;
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
@@ -2166,17 +2293,20 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r,
ret = NULL;
break;
case RPZ_DROP_ACTION:
- ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ ret = rpz_synthesize_nodata(r, ms, qchase, az);
ms->is_drop = 1;
break;
case RPZ_LOCAL_DATA_ACTION:
- ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az);
- if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); }
+ ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az);
+ if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); }
break;
case RPZ_PASSTHRU_ACTION:
ret = NULL;
ms->rpz_passthru = 1;
break;
+ case RPZ_CNAME_OVERRIDE_ACTION:
+ ret = rpz_synthesize_cname_override_msg(r, ms, qchase);
+ break;
default:
verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
rpz_action_to_string(action));
@@ -2194,9 +2324,9 @@ done:
}
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)
+rpz_apply_nsdname_trigger(struct module_qstate* ms, struct query_info* qchase,
+ struct rpz* r, struct local_zone* z,
+ struct matched_delegation_point const* match, struct auth_zone* az)
{
struct dns_msg* ret = NULL;
enum rpz_action action = localzone_type_to_rpz_action(z->type);
@@ -2209,10 +2339,10 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r,
switch(action) {
case RPZ_NXDOMAIN_ACTION:
- ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az);
+ ret = rpz_synthesize_nxdomain(r, ms, qchase, az);
break;
case RPZ_NODATA_ACTION:
- ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ ret = rpz_synthesize_nodata(r, ms, qchase, az);
break;
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
@@ -2221,19 +2351,22 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r,
ret = NULL;
break;
case RPZ_DROP_ACTION:
- ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ ret = rpz_synthesize_nodata(r, ms, qchase, az);
ms->is_drop = 1;
break;
case RPZ_LOCAL_DATA_ACTION:
- ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az);
- if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); }
+ ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az);
+ if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); }
break;
case RPZ_PASSTHRU_ACTION:
ret = NULL;
ms->rpz_passthru = 1;
break;
+ case RPZ_CNAME_OVERRIDE_ACTION:
+ ret = rpz_synthesize_cname_override_msg(r, ms, qchase);
+ break;
default:
- verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
+ verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'",
rpz_action_to_string(action));
ret = NULL;
}
@@ -2324,7 +2457,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate*
/* the nsdname has precedence over the nsip triggers */
z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones,
- ms->qinfo.qclass, &match);
+ is->qchase.qclass, &match);
if(z != NULL) {
lock_rw_unlock(&a->lock);
break;
@@ -2347,9 +2480,9 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate*
if(z) {
lock_rw_unlock(&z->lock);
}
- return rpz_apply_nsip_trigger(ms, r, raddr, a);
+ return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a);
}
- return rpz_apply_nsdname_trigger(ms, r, z, &match, a);
+ return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a);
}
struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
@@ -2412,10 +2545,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
dname_str(is->qchase.qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
- verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s",
+ verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s",
zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
else
- verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s",
+ verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s",
nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
}
switch(localzone_type_to_rpz_action(lzt)) {
@@ -2444,7 +2577,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
ms->rpz_passthru = 1;
break;
default:
- verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'",
+ verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'",
rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
ret = NULL;
}
@@ -2472,8 +2605,21 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env,
az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out);
client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action);
+ if(node != NULL && *r_out &&
+ (*r_out)->action_override != RPZ_NO_OVERRIDE_ACTION) {
+ client_action = (*r_out)->action_override;
+ }
if(client_action == RPZ_PASSTHRU_ACTION) {
+ if(*r_out && (*r_out)->log)
+ log_rpz_apply(
+ (node?"clientip":"qname"),
+ ((*z_out)?(*z_out)->name:NULL),
+ (node?&node->node:NULL),
+ client_action, qinfo, repinfo, NULL,
+ (*r_out)->log_name);
*passthru = 1;
+ ret = 0;
+ goto done;
}
if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION &&
client_action != RPZ_PASSTHRU_ACTION)) {
@@ -2488,14 +2634,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env,
if(client_action == RPZ_LOCAL_DATA_ACTION) {
rpz_apply_clientip_localdata_action(node, env, qinfo,
edns, repinfo, buf, temp, *a_out);
+ ret = 1;
+ } else if(client_action == RPZ_CNAME_OVERRIDE_ACTION) {
+ if(!rpz_apply_cname_override_action(*r_out, qinfo,
+ temp)) {
+ ret = 0;
+ goto done;
+ }
+ ret = 0;
} else {
- if(*r_out && (*r_out)->log)
- log_rpz_apply(
- (node?"clientip":"qname"),
- ((*z_out)?(*z_out)->name:NULL),
- (node?&node->node:NULL),
- client_action, qinfo, repinfo, NULL,
- (*r_out)->log_name);
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));
@@ -2503,8 +2650,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env,
LDNS_RCODE_WIRE(sldns_buffer_begin(buf))
== LDNS_RCODE_NXDOMAIN)
LDNS_RA_CLR(sldns_buffer_begin(buf));
+ ret = 1;
}
- ret = 1;
+ if(*r_out && (*r_out)->log)
+ log_rpz_apply(
+ (node?"clientip":"qname"),
+ ((*z_out)?(*z_out)->name:NULL),
+ (node?&node->node:NULL),
+ client_action, qinfo, repinfo, NULL,
+ (*r_out)->log_name);
goto done;
}
ret = -1;
diff --git a/services/rpz.h b/services/rpz.h
index e6d8bf566e16..7f409087f772 100644
--- a/services/rpz.h
+++ b/services/rpz.h
@@ -226,6 +226,14 @@ int rpz_clear(struct rpz* r);
struct rpz* rpz_create(struct config_auth* p);
/**
+ * Change config on rpz, after reload.
+ * @param r: the rpz structure.
+ * @param p: the config that was read.
+ * @return false on failure.
+ */
+int rpz_config(struct rpz* r, struct config_auth* p);
+
+/**
* String for RPZ action enum
* @param a: RPZ action to get string for
* @return: string for RPZ action
diff --git a/services/view.h b/services/view.h
index 17778100474b..12f7a64e7171 100644
--- a/services/view.h
+++ b/services/view.h
@@ -126,7 +126,8 @@ void view_delete(struct view* v);
*/
void views_print(struct views* v);
-/* Find a view by name.
+/**
+ * Find a view by name.
* @param vs: views
* @param name: name of the view we are looking for
* @param write: 1 for obtaining write lock on found view, 0 for read lock