aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/services
diff options
context:
space:
mode:
authorErik de Castro Lopo <erikd@mega-nerd.com>2017-06-16 20:16:05 +1000
committerErik de Castro Lopo <erikd@mega-nerd.com>2017-06-17 23:04:00 +1000
commita85b5759f34c0c4110a479a8b5fa606f15ed9b23 (patch)
tree518cb8346249a42fd2aa8a78c09c3631e14db6aa /external/unbound/services
parentMerge pull request #2059 (diff)
downloadmonero-a85b5759f34c0c4110a479a8b5fa606f15ed9b23.tar.xz
Upgrade unbound library
These files were pulled from the 1.6.3 release tarball. This new version builds against OpenSSL version 1.1 which will be the default in the new Debian Stable which is due to be released RealSoonNow (tm).
Diffstat (limited to '')
-rw-r--r--external/unbound/services/cache/dns.c28
-rw-r--r--external/unbound/services/cache/dns.h16
-rw-r--r--external/unbound/services/cache/infra.c139
-rw-r--r--external/unbound/services/cache/infra.h50
-rw-r--r--external/unbound/services/cache/rrset.c10
-rw-r--r--external/unbound/services/cache/rrset.h2
-rw-r--r--external/unbound/services/listen_dnsport.c348
-rw-r--r--external/unbound/services/listen_dnsport.h27
-rw-r--r--external/unbound/services/localzone.c797
-rw-r--r--external/unbound/services/localzone.h198
-rw-r--r--external/unbound/services/mesh.c364
-rw-r--r--external/unbound/services/mesh.h72
-rw-r--r--external/unbound/services/modstack.c23
-rw-r--r--external/unbound/services/outside_network.c210
-rw-r--r--external/unbound/services/outside_network.h57
-rw-r--r--external/unbound/services/view.c212
-rw-r--r--external/unbound/services/view.h137
17 files changed, 2288 insertions, 402 deletions
diff --git a/external/unbound/services/cache/dns.c b/external/unbound/services/cache/dns.c
index e14e636db..a8fde9f28 100644
--- a/external/unbound/services/cache/dns.c
+++ b/external/unbound/services/cache/dns.c
@@ -106,7 +106,7 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
void
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
- hashvalue_t hash, struct reply_info* rep, time_t leeway, int pside,
+ hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
struct reply_info* qrep, struct regional* region)
{
struct msgreply_entry* e;
@@ -188,12 +188,13 @@ msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen,
{
struct lruhash_entry* e;
struct query_info k;
- hashvalue_t h;
+ hashvalue_type h;
k.qname = qname;
k.qname_len = qnamelen;
k.qtype = qtype;
k.qclass = qclass;
+ k.local_alias = NULL;
h = query_info_hash(&k, flags);
e = slabhash_lookup(env->msg_cache, h, &k, wr);
@@ -361,6 +362,7 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
msg->qinfo.qname_len = qnamelen;
msg->qinfo.qtype = qtype;
msg->qinfo.qclass = qclass;
+ msg->qinfo.local_alias = NULL;
/* non-packed reply_info, because it needs to grow the array */
msg->rep = (struct reply_info*)regional_alloc_zero(region,
sizeof(struct reply_info)-sizeof(struct rrset_ref));
@@ -477,8 +479,7 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num)
return msg;
}
-/** generate dns_msg from cached message */
-static struct dns_msg*
+struct dns_msg*
tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
struct regional* region, time_t now, struct regional* scratch)
{
@@ -523,8 +524,11 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
return NULL;
}
}
- rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref,
+ if(env)
+ rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref,
r->rrset_count);
+ else
+ rrset_array_unlock(r->ref, r->rrset_count);
return msg;
}
@@ -707,7 +711,7 @@ dns_cache_lookup(struct module_env* env,
{
struct lruhash_entry* e;
struct query_info k;
- hashvalue_t h;
+ hashvalue_type h;
time_t now = *env->now;
struct ub_packed_rrset_key* rrset;
@@ -716,6 +720,7 @@ dns_cache_lookup(struct module_env* env,
k.qname_len = qnamelen;
k.qtype = qtype;
k.qclass = qclass;
+ k.local_alias = NULL;
h = query_info_hash(&k, flags);
e = slabhash_lookup(env->msg_cache, h, &k, 0);
if(e) {
@@ -795,6 +800,12 @@ dns_cache_lookup(struct module_env* env,
dname_remove_label(&k.qname, &k.qname_len);
h = query_info_hash(&k, flags);
e = slabhash_lookup(env->msg_cache, h, &k, 0);
+ if(!e && k.qtype != LDNS_RR_TYPE_A &&
+ env->cfg->qname_minimisation) {
+ k.qtype = LDNS_RR_TYPE_A;
+ h = query_info_hash(&k, flags);
+ e = slabhash_lookup(env->msg_cache, h, &k, 0);
+ }
if(e) {
struct reply_info* data = (struct reply_info*)e->data;
struct dns_msg* msg;
@@ -810,7 +821,8 @@ dns_cache_lookup(struct module_env* env,
}
lock_rw_unlock(&e->lock);
}
- }
+ k.qtype = qtype;
+ }
/* fill common RR types for ANY response to avoid requery */
if(qtype == LDNS_RR_TYPE_ANY) {
@@ -855,7 +867,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
} else {
/* store msg, and rrsets */
struct query_info qinf;
- hashvalue_t h;
+ hashvalue_type h;
qinf = *msgqinf;
qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
diff --git a/external/unbound/services/cache/dns.h b/external/unbound/services/cache/dns.h
index 69796c2eb..0dfb68874 100644
--- a/external/unbound/services/cache/dns.h
+++ b/external/unbound/services/cache/dns.h
@@ -106,7 +106,7 @@ int dns_cache_store(struct module_env* env, struct query_info* qinf,
* @param region: to allocate into for qmsg.
*/
void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
- hashvalue_t hash, struct reply_info* rep, time_t leeway, int pside,
+ hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
struct reply_info* qrep, struct regional* region);
/**
@@ -126,6 +126,20 @@ struct delegpt* dns_cache_find_delegation(struct module_env* env,
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
struct regional* region, struct dns_msg** msg, time_t timenow);
+/**
+ * generate dns_msg from cached message
+ * @param env: module environment with the DNS cache. NULL if the LRU from cache
+ * does not need to be touched.
+ * @param q: query info, contains qname that will make up the dns message.
+ * @param r: reply info that, together with qname, will make up the dns message.
+ * @param region: where to allocate dns message.
+ * @param now: the time now, for check if TTL on cache entry is ok.
+ * @param scratch: where to allocate temporary data.
+ * */
+struct dns_msg* tomsg(struct module_env* env, struct query_info* q,
+ struct reply_info* r, struct regional* region, time_t now,
+ struct regional* scratch);
+
/**
* Find cached message
* @param env: module environment with the DNS cache.
diff --git a/external/unbound/services/cache/infra.c b/external/unbound/services/cache/infra.c
index c0049d8b6..314c85ef5 100644
--- a/external/unbound/services/cache/infra.c
+++ b/external/unbound/services/cache/infra.c
@@ -61,6 +61,10 @@
/** ratelimit value for delegation point */
int infra_dp_ratelimit = 0;
+/** ratelimit value for client ip addresses,
+ * in queries per second. */
+int infra_ip_ratelimit = 0;
+
size_t
infra_sizefunc(void* k, void* ATTR_UNUSED(d))
{
@@ -244,11 +248,19 @@ infra_create(struct config_file* cfg)
}
name_tree_init_parents(&infra->domain_limits);
}
+ infra_ip_ratelimit = cfg->ip_ratelimit;
+ infra->client_ip_rates = slabhash_create(cfg->ratelimit_slabs,
+ INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc,
+ &ip_rate_compfunc, &ip_rate_delkeyfunc, &ip_rate_deldatafunc, NULL);
+ if(!infra->client_ip_rates) {
+ infra_delete(infra);
+ return NULL;
+ }
return infra;
}
/** delete domain_limit entries */
-static void domain_limit_free(rbnode_t* n, void* ATTR_UNUSED(arg))
+static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg))
{
if(n) {
free(((struct domain_limit_data*)n)->node.name);
@@ -264,6 +276,7 @@ infra_delete(struct infra_cache* infra)
slabhash_delete(infra->hosts);
slabhash_delete(infra->domain_rates);
traverse_postorder(&infra->domain_limits, domain_limit_free, NULL);
+ slabhash_delete(infra->client_ip_rates);
free(infra);
}
@@ -284,31 +297,38 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg)
return infra;
}
-/** calculate the hash value for a host key */
-static hashvalue_t
-hash_addr(struct sockaddr_storage* addr, socklen_t addrlen)
+/** calculate the hash value for a host key
+ * set use_port to a non-0 number to use the port in
+ * the hash calculation; 0 to ignore the port.*/
+static hashvalue_type
+hash_addr(struct sockaddr_storage* addr, socklen_t addrlen,
+ int use_port)
{
- hashvalue_t h = 0xab;
+ hashvalue_type h = 0xab;
/* select the pieces to hash, some OS have changing data inside */
if(addr_is_ip6(addr, addrlen)) {
struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr;
h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h);
- h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h);
+ if(use_port){
+ h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h);
+ }
h = hashlittle(&in6->sin6_addr, INET6_SIZE, h);
} else {
struct sockaddr_in* in = (struct sockaddr_in*)addr;
h = hashlittle(&in->sin_family, sizeof(in->sin_family), h);
- h = hashlittle(&in->sin_port, sizeof(in->sin_port), h);
+ if(use_port){
+ h = hashlittle(&in->sin_port, sizeof(in->sin_port), h);
+ }
h = hashlittle(&in->sin_addr, INET_SIZE, h);
}
return h;
}
/** calculate infra hash for a key */
-static hashvalue_t
+static hashvalue_type
hash_infra(struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name)
{
- return dname_query_hash(name, hash_addr(addr, addrlen));
+ return dname_query_hash(name, hash_addr(addr, addrlen, 1));
}
/** lookup version that does not check host ttl (you check it) */
@@ -726,12 +746,36 @@ int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name,
return infra_dp_ratelimit;
}
+size_t ip_rate_sizefunc(void* k, void* ATTR_UNUSED(d))
+{
+ struct ip_rate_key* key = (struct ip_rate_key*)k;
+ return sizeof(*key) + sizeof(struct ip_rate_data)
+ + lock_get_mem(&key->entry.lock);
+}
+
+int ip_rate_compfunc(void* key1, void* key2)
+{
+ struct ip_rate_key* k1 = (struct ip_rate_key*)key1;
+ struct ip_rate_key* k2 = (struct ip_rate_key*)key2;
+ return sockaddr_cmp_addr(&k1->addr, k1->addrlen,
+ &k2->addr, k2->addrlen);
+}
+
+void ip_rate_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
+{
+ struct ip_rate_key* key = (struct ip_rate_key*)k;
+ if(!key)
+ return;
+ lock_rw_destroy(&key->entry.lock);
+ free(key);
+}
+
/** find data item in array, for write access, caller unlocks */
static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra,
uint8_t* name, size_t namelen, int wr)
{
struct rate_key key;
- hashvalue_t h = dname_query_hash(name, 0xab);
+ hashvalue_type h = dname_query_hash(name, 0xab);
memset(&key, 0, sizeof(key));
key.name = name;
key.namelen = namelen;
@@ -739,11 +783,25 @@ static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra,
return slabhash_lookup(infra->domain_rates, h, &key, wr);
}
+/** find data item in array for ip addresses */
+struct lruhash_entry* infra_find_ip_ratedata(struct infra_cache* infra,
+ struct comm_reply* repinfo, int wr)
+{
+ struct ip_rate_key key;
+ hashvalue_type h = hash_addr(&(repinfo->addr),
+ repinfo->addrlen, 0);
+ memset(&key, 0, sizeof(key));
+ key.addr = repinfo->addr;
+ key.addrlen = repinfo->addrlen;
+ key.entry.hash = h;
+ return slabhash_lookup(infra->client_ip_rates, h, &key, wr);
+}
+
/** create rate data item for name, number 1 in now */
static void infra_create_ratedata(struct infra_cache* infra,
uint8_t* name, size_t namelen, time_t timenow)
{
- hashvalue_t h = dname_query_hash(name, 0xab);
+ hashvalue_type h = dname_query_hash(name, 0xab);
struct rate_key* k = (struct rate_key*)calloc(1, sizeof(*k));
struct rate_data* d = (struct rate_data*)calloc(1, sizeof(*d));
if(!k || !d) {
@@ -767,6 +825,30 @@ static void infra_create_ratedata(struct infra_cache* infra,
slabhash_insert(infra->domain_rates, h, &k->entry, d, NULL);
}
+/** create rate data item for ip address */
+static void infra_ip_create_ratedata(struct infra_cache* infra,
+ struct comm_reply* repinfo, time_t timenow)
+{
+ hashvalue_type h = hash_addr(&(repinfo->addr),
+ repinfo->addrlen, 0);
+ struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k));
+ struct ip_rate_data* d = (struct ip_rate_data*)calloc(1, sizeof(*d));
+ if(!k || !d) {
+ free(k);
+ free(d);
+ return; /* alloc failure */
+ }
+ k->addr = repinfo->addr;
+ k->addrlen = repinfo->addrlen;
+ lock_rw_init(&k->entry.lock);
+ k->entry.hash = h;
+ k->entry.key = k;
+ k->entry.data = d;
+ d->qps[0] = 1;
+ d->timestamp[0] = timenow;
+ 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)
{
@@ -875,6 +957,41 @@ infra_get_mem(struct infra_cache* infra)
{
size_t s = sizeof(*infra) + slabhash_get_mem(infra->hosts);
if(infra->domain_rates) s += slabhash_get_mem(infra->domain_rates);
+ if(infra->client_ip_rates) s += slabhash_get_mem(infra->client_ip_rates);
/* ignore domain_limits because walk through tree is big */
return s;
}
+
+int infra_ip_ratelimit_inc(struct infra_cache* infra,
+ struct comm_reply* repinfo, time_t timenow)
+{
+ int max;
+ struct lruhash_entry* entry;
+
+ /* not enabled */
+ if(!infra_ip_ratelimit) {
+ return 1;
+ }
+ /* 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);
+ (*cur)++;
+ max = infra_rate_max(entry->data, timenow);
+ lock_rw_unlock(&entry->lock);
+
+ if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) {
+ char client_ip[128];
+ addr_to_str((struct sockaddr_storage *)&repinfo->addr,
+ repinfo->addrlen, client_ip, sizeof(client_ip));
+ verbose(VERB_OPS, "ratelimit exceeded %s %d", client_ip,
+ infra_ip_ratelimit);
+ }
+ return (max <= infra_ip_ratelimit);
+ }
+
+ /* create */
+ infra_ip_create_ratedata(infra, repinfo, timenow);
+ return 1;
+}
diff --git a/external/unbound/services/cache/infra.h b/external/unbound/services/cache/infra.h
index fc7abb7c4..6f9471a39 100644
--- a/external/unbound/services/cache/infra.h
+++ b/external/unbound/services/cache/infra.h
@@ -36,7 +36,10 @@
/**
* \file
*
- * This file contains the infrastructure cache.
+ * This file contains the infrastructure cache, as well as rate limiting.
+ * Note that there are two sorts of rate-limiting here:
+ * - Pre-cache, per-query rate limiting (query ratelimits)
+ * - Post-cache, per-domain name rate limiting (infra-ratelimits)
*/
#ifndef SERVICES_CACHE_INFRA_H
@@ -44,6 +47,8 @@
#include "util/storage/lruhash.h"
#include "util/storage/dnstree.h"
#include "util/rtt.h"
+#include "util/netevent.h"
+#include "util/data/msgreply.h"
struct slabhash;
struct config_file;
@@ -112,7 +117,9 @@ struct infra_cache {
/** hash table with query rates per name: rate_key, rate_data */
struct slabhash* domain_rates;
/** ratelimit settings for domains, struct domain_limit_data */
- rbtree_t domain_limits;
+ rbtree_type domain_limits;
+ /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */
+ struct slabhash* client_ip_rates;
};
/** ratelimit, unless overridden by domain_limits, 0 is off */
@@ -142,6 +149,21 @@ struct rate_key {
size_t namelen;
};
+/** ip ratelimit, 0 is off */
+extern int infra_ip_ratelimit;
+
+/**
+ * key for ip_ratelimit lookups, a source IP.
+ */
+struct ip_rate_key {
+ /** lruhash key entry */
+ struct lruhash_entry entry;
+ /** client ip information */
+ struct sockaddr_storage addr;
+ /** length of address */
+ socklen_t addrlen;
+};
+
/** number of seconds to track qps rate */
#define RATE_WINDOW 2
@@ -160,6 +182,8 @@ struct rate_data {
time_t timestamp[RATE_WINDOW];
};
+#define ip_rate_data rate_data
+
/** infra host cache default hash lookup size */
#define INFRA_HOST_STARTSIZE 32
/** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */
@@ -381,6 +405,16 @@ int infra_rate_max(void* data, time_t now);
int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name,
size_t namelen);
+/** Update query ratelimit hash and decide
+ * whether or not a query should be dropped.
+ * @param infra: infra cache
+ * @param repinfo: information about client
+ * @param timenow: what time it is now.
+ * @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);
+
/**
* Get memory used by the infra cache.
* @param infra: infrastructure cache.
@@ -413,4 +447,16 @@ void rate_delkeyfunc(void* k, void* arg);
/** delete data */
void rate_deldatafunc(void* d, void* arg);
+/* calculate size for the client ip hashtable */
+size_t ip_rate_sizefunc(void* k, void* d);
+
+/* compare two addresses */
+int ip_rate_compfunc(void* key1, void* key2);
+
+/* delete key, and destroy the lock */
+void ip_rate_delkeyfunc(void* d, void* arg);
+
+/* delete data */
+#define ip_rate_deldatafunc rate_deldatafunc
+
#endif /* SERVICES_CACHE_INFRA_H */
diff --git a/external/unbound/services/cache/rrset.c b/external/unbound/services/cache/rrset.c
index 2f6a1b506..7e5732b76 100644
--- a/external/unbound/services/cache/rrset.c
+++ b/external/unbound/services/cache/rrset.c
@@ -91,7 +91,7 @@ struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r,
void
rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
- hashvalue_t hash, rrset_id_t id)
+ hashvalue_type hash, rrset_id_type id)
{
struct lruhash* table = slabhash_gettable(&r->table, hash);
/*
@@ -186,7 +186,7 @@ rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
{
struct lruhash_entry* e;
struct ub_packed_rrset_key* k = ref->key;
- hashvalue_t h = k->entry.hash;
+ hashvalue_type h = k->entry.hash;
uint16_t rrset_type = ntohs(k->rk.type);
int equal = 0;
log_assert(ref->id != 0 && k->id != 0);
@@ -303,10 +303,10 @@ void
rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
struct rrset_ref* ref, size_t count)
{
- hashvalue_t* h;
+ hashvalue_type* h;
size_t i;
- if(count > RR_COUNT_MAX || !(h = (hashvalue_t*)regional_alloc(scratch,
- sizeof(hashvalue_t)*count))) {
+ if(count > RR_COUNT_MAX || !(h = (hashvalue_type*)regional_alloc(
+ scratch, sizeof(hashvalue_type)*count))) {
log_warn("rrset LRU: memory allocation failed");
h = NULL;
} else /* store hash values */
diff --git a/external/unbound/services/cache/rrset.h b/external/unbound/services/cache/rrset.h
index 98e44a4e5..d5439ef08 100644
--- a/external/unbound/services/cache/rrset.h
+++ b/external/unbound/services/cache/rrset.h
@@ -102,7 +102,7 @@ struct rrset_cache* rrset_cache_adjust(struct rrset_cache* r,
* @param id: used to check that the item is unchanged and not deleted.
*/
void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
- hashvalue_t hash, rrset_id_t id);
+ hashvalue_type hash, rrset_id_type id);
/**
* Update an rrset in the rrset cache. Stores the information for later use.
diff --git a/external/unbound/services/listen_dnsport.c b/external/unbound/services/listen_dnsport.c
index 276c0fb32..37ee9a6b9 100644
--- a/external/unbound/services/listen_dnsport.c
+++ b/external/unbound/services/listen_dnsport.c
@@ -43,6 +43,9 @@
# include <sys/types.h>
#endif
#include <sys/time.h>
+#ifdef USE_TCP_FASTOPEN
+#include <netinet/tcp.h>
+#endif
#include "services/listen_dnsport.h"
#include "services/outside_network.h"
#include "util/netevent.h"
@@ -60,6 +63,10 @@
#include <sys/un.h>
#endif
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
/** number of queued TCP connections for listen() */
#define TCP_BACKLOG 256
@@ -93,13 +100,74 @@ verbose_print_addr(struct addrinfo *addr)
}
}
+#ifdef HAVE_SYSTEMD
+static int
+systemd_get_activated(int family, int socktype, int listen,
+ struct sockaddr *addr, socklen_t addrlen,
+ const char *path)
+{
+ int i = 0;
+ int r = 0;
+ int s = -1;
+ const char* listen_pid, *listen_fds;
+
+ /* We should use "listen" option only for stream protocols. For UDP it should be -1 */
+
+ if((r = sd_booted()) < 1) {
+ if(r == 0)
+ log_warn("systemd is not running");
+ else
+ log_err("systemd sd_booted(): %s", strerror(-r));
+ return -1;
+ }
+
+ listen_pid = getenv("LISTEN_PID");
+ listen_fds = getenv("LISTEN_FDS");
+
+ if (!listen_pid) {
+ log_warn("Systemd mandatory ENV variable is not defined: LISTEN_PID");
+ return -1;
+ }
+
+ if (!listen_fds) {
+ log_warn("Systemd mandatory ENV variable is not defined: LISTEN_FDS");
+ return -1;
+ }
+
+ if((r = sd_listen_fds(0)) < 1) {
+ if(r == 0)
+ log_warn("systemd: did not return socket, check unit configuration");
+ else
+ log_err("systemd sd_listen_fds(): %s", strerror(-r));
+ return -1;
+ }
+
+ for(i = 0; i < r; i++) {
+ if(sd_is_socket(SD_LISTEN_FDS_START + i, family, socktype, listen)) {
+ s = SD_LISTEN_FDS_START + i;
+ break;
+ }
+ }
+ if (s == -1) {
+ if (addr)
+ log_err_addr("systemd sd_listen_fds()",
+ "no such socket",
+ (struct sockaddr_storage *)addr, addrlen);
+ else
+ log_err("systemd sd_listen_fds(): %s", path);
+ }
+ return s;
+}
+#endif
+
int
create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t addrlen, int v6only, int* inuse, int* noproto,
- int rcv, int snd, int listen, int* reuseport, int transparent)
+ int rcv, int snd, int listen, int* reuseport, int transparent,
+ int freebind, int use_systemd)
{
int s;
-#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU) || defined(IP_TRANSPARENT)
+#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND)
int on=1;
#endif
#ifdef IPV6_MTU
@@ -114,9 +182,22 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
#ifndef IPV6_V6ONLY
(void)v6only;
#endif
-#ifndef IP_TRANSPARENT
+#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY)
(void)transparent;
#endif
+#if !defined(IP_FREEBIND)
+ (void)freebind;
+#endif
+#ifdef HAVE_SYSTEMD
+ int got_fd_from_systemd = 0;
+
+ if (!use_systemd
+ || (use_systemd
+ && (s = systemd_get_activated(family, socktype, -1, addr,
+ addrlen, NULL)) == -1)) {
+#else
+ (void)use_systemd;
+#endif
if((s = socket(family, socktype, 0)) == -1) {
*inuse = 0;
#ifndef USE_WINSOCK
@@ -137,6 +218,11 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
*noproto = 0;
return -1;
}
+#ifdef HAVE_SYSTEMD
+ } else {
+ got_fd_from_systemd = 1;
+ }
+#endif
if(listen) {
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
@@ -187,8 +273,24 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s",
strerror(errno));
}
-#endif /* IP_TRANSPARENT */
+#elif defined(IP_BINDANY)
+ if (transparent &&
+ setsockopt(s, (family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP),
+ (family == AF_INET6? IPV6_BINDANY:IP_BINDANY),
+ (void*)&on, (socklen_t)sizeof(on)) < 0) {
+ log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s",
+ (family==AF_INET6?"V6":""), strerror(errno));
+ }
+#endif /* IP_TRANSPARENT || IP_BINDANY */
+ }
+#ifdef IP_FREEBIND
+ if(freebind &&
+ setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
+ (socklen_t)sizeof(on)) < 0) {
+ log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
+ strerror(errno));
}
+#endif /* IP_FREEBIND */
if(rcv) {
#ifdef SO_RCVBUF
int got;
@@ -442,7 +544,11 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
}
# endif /* IPv4 MTU */
}
- if(bind(s, (struct sockaddr*)addr, addrlen) != 0) {
+ if(
+#ifdef HAVE_SYSTEMD
+ !got_fd_from_systemd &&
+#endif
+ bind(s, (struct sockaddr*)addr, addrlen) != 0) {
*noproto = 0;
*inuse = 0;
#ifndef USE_WINSOCK
@@ -465,7 +571,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
(struct sockaddr_storage*)addr, addrlen);
}
closesocket(s);
-#endif
+#endif /* USE_WINSOCK */
return -1;
}
if(!fd_set_nonblock(s)) {
@@ -483,17 +589,35 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
int
create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
- int* reuseport, int transparent)
+ int* reuseport, int transparent, int mss, int freebind, int use_systemd)
{
int s;
-#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT)
+#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND)
int on = 1;
#endif
-#ifndef IP_TRANSPARENT
+#ifdef HAVE_SYSTEMD
+ int got_fd_from_systemd = 0;
+#endif
+#ifdef USE_TCP_FASTOPEN
+ int qlen;
+#endif
+#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY)
(void)transparent;
#endif
+#if !defined(IP_FREEBIND)
+ (void)freebind;
+#endif
verbose_print_addr(addr);
*noproto = 0;
+#ifdef HAVE_SYSTEMD
+ if (!use_systemd ||
+ (use_systemd
+ && (s = systemd_get_activated(addr->ai_family, addr->ai_socktype, 1,
+ addr->ai_addr, addr->ai_addrlen,
+ NULL)) == -1)) {
+#else
+ (void)use_systemd;
+#endif
if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
#ifndef USE_WINSOCK
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
@@ -512,6 +636,30 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
#endif
return -1;
}
+ if (mss > 0) {
+#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG)
+ if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, (void*)&mss,
+ (socklen_t)sizeof(mss)) < 0) {
+ #ifndef USE_WINSOCK
+ log_err(" setsockopt(.. TCP_MAXSEG ..) failed: %s",
+ strerror(errno));
+ #else
+ log_err(" setsockopt(.. TCP_MAXSEG ..) failed: %s",
+ wsa_strerror(WSAGetLastError()));
+ #endif
+ } else {
+ verbose(VERB_ALGO,
+ " tcp socket mss set to %d", mss);
+ }
+#else
+ log_warn(" setsockopt(TCP_MAXSEG) unsupported");
+#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */
+ }
+#ifdef HAVE_SYSTEMD
+ } else {
+ got_fd_from_systemd = 1;
+ }
+#endif
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
@@ -527,6 +675,13 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
return -1;
}
#endif /* SO_REUSEADDR */
+#ifdef IP_FREEBIND
+ if (freebind && setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
+ (socklen_t)sizeof(on)) < 0) {
+ log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
+ strerror(errno));
+ }
+#endif /* IP_FREEBIND */
#ifdef SO_REUSEPORT
/* try to set SO_REUSEPORT so that incoming
* connections are distributed evenly among the receiving threads.
@@ -573,8 +728,20 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s",
strerror(errno));
}
-#endif /* IP_TRANSPARENT */
- if(bind(s, addr->ai_addr, addr->ai_addrlen) != 0) {
+#elif defined(IP_BINDANY)
+ if (transparent &&
+ setsockopt(s, (addr->ai_family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP),
+ (addr->ai_family == AF_INET6? IPV6_BINDANY:IP_BINDANY),
+ (void*)&on, (socklen_t)sizeof(on)) < 0) {
+ log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s",
+ (addr->ai_family==AF_INET6?"V6":""), strerror(errno));
+ }
+#endif /* IP_TRANSPARENT || IP_BINDANY */
+ if(
+#ifdef HAVE_SYSTEMD
+ !got_fd_from_systemd &&
+#endif
+ bind(s, addr->ai_addr, addr->ai_addrlen) != 0) {
#ifndef USE_WINSOCK
/* detect freebsd jail with no ipv6 permission */
if(addr->ai_family==AF_INET6 && errno==EINVAL)
@@ -612,20 +779,46 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
#endif
return -1;
}
+#ifdef USE_TCP_FASTOPEN
+ /* qlen specifies how many outstanding TFO requests to allow. Limit is a defense
+ against IP spoofing attacks as suggested in RFC7413 */
+#ifdef __APPLE__
+ /* OS X implementation only supports qlen of 1 via this call. Actual
+ value is configured by the net.inet.tcp.fastopen_backlog kernel parm. */
+ qlen = 1;
+#else
+ /* 5 is recommended on linux */
+ qlen = 5;
+#endif
+ if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen,
+ sizeof(qlen))) == -1 ) {
+ log_err("Setting TCP Fast Open as server failed: %s", strerror(errno));
+ }
+#endif
return s;
}
int
-create_local_accept_sock(const char *path, int* noproto)
+create_local_accept_sock(const char *path, int* noproto, int use_systemd)
{
+#ifdef HAVE_SYSTEMD
+ int ret;
+
+ if (use_systemd && (ret = systemd_get_activated(AF_LOCAL, SOCK_STREAM, 1, NULL, 0, path)) != -1)
+ return ret;
+ else {
+#endif
#ifdef HAVE_SYS_UN_H
int s;
struct sockaddr_un usock;
+#ifndef HAVE_SYSTEMD
+ (void)use_systemd;
+#endif
verbose(VERB_ALGO, "creating unix socket %s", path);
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
/* this member exists on BSDs, not Linux */
- usock.sun_len = (socklen_t)sizeof(usock);
+ usock.sun_len = (unsigned)sizeof(usock);
#endif
usock.sun_family = AF_LOCAL;
/* length is 92-108, 104 on FreeBSD */
@@ -641,29 +834,42 @@ create_local_accept_sock(const char *path, int* noproto)
/* The socket already exists and cannot be removed */
log_err("Cannot remove old local socket %s (%s)",
path, strerror(errno));
- return -1;
+ goto err;
}
if (bind(s, (struct sockaddr *)&usock,
(socklen_t)sizeof(struct sockaddr_un)) == -1) {
log_err("Cannot bind local socket %s (%s)",
path, strerror(errno));
- return -1;
+ goto err;
}
if (!fd_set_nonblock(s)) {
log_err("Cannot set non-blocking mode");
- return -1;
+ goto err;
}
if (listen(s, TCP_BACKLOG) == -1) {
log_err("can't listen: %s", strerror(errno));
- return -1;
+ goto err;
}
(void)noproto; /*unused*/
return s;
+
+err:
+#ifndef USE_WINSOCK
+ close(s);
+#else
+ closesocket(s);
+#endif
+ return -1;
+
+#ifdef HAVE_SYSTEMD
+ }
+#endif
#else
+ (void)use_systemd;
(void)path;
log_err("Local sockets are not supported");
*noproto = 1;
@@ -678,7 +884,7 @@ create_local_accept_sock(const char *path, int* noproto)
static int
make_sock(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
- int* reuseport, int transparent)
+ int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd)
{
struct addrinfo *res = NULL;
int r, s, inuse, noproto;
@@ -706,7 +912,7 @@ make_sock(int stype, const char* ifname, const char* port,
s = create_udp_sock(res->ai_family, res->ai_socktype,
(struct sockaddr*)res->ai_addr, res->ai_addrlen,
v6only, &inuse, &noproto, (int)rcv, (int)snd, 1,
- reuseport, transparent);
+ reuseport, transparent, freebind, use_systemd);
if(s == -1 && inuse) {
log_err("bind: address already in use");
} else if(s == -1 && noproto && hints->ai_family == AF_INET6){
@@ -714,7 +920,7 @@ make_sock(int stype, const char* ifname, const char* port,
}
} else {
s = create_tcp_accept_sock(res, v6only, &noproto, reuseport,
- transparent);
+ transparent, tcp_mss, freebind, use_systemd);
if(s == -1 && noproto && hints->ai_family == AF_INET6){
*noip6 = 1;
}
@@ -727,7 +933,7 @@ make_sock(int stype, const char* ifname, const char* port,
static int
make_sock_port(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
- int* reuseport, int transparent)
+ int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd)
{
char* s = strchr(ifname, '@');
if(s) {
@@ -749,10 +955,10 @@ make_sock_port(int stype, const char* ifname, const char* port,
(void)strlcpy(p, s+1, sizeof(p));
p[strlen(s+1)]=0;
return make_sock(stype, newif, p, hints, v6only, noip6,
- rcv, snd, reuseport, transparent);
+ rcv, snd, reuseport, transparent, tcp_mss, freebind, use_systemd);
}
return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd,
- reuseport, transparent);
+ reuseport, transparent, tcp_mss, freebind, use_systemd);
}
/**
@@ -802,7 +1008,7 @@ set_recvpktinfo(int s, int family)
}
# else
log_err("no IPV6_RECVPKTINFO and no IPV6_PKTINFO option, please "
- "disable interface-automatic in config");
+ "disable interface-automatic or do-ip6 in config");
return 0;
# endif /* defined IPV6_RECVPKTINFO */
@@ -823,7 +1029,7 @@ set_recvpktinfo(int s, int family)
}
# else
log_err("no IP_SENDSRCADDR or IP_PKTINFO option, please disable "
- "interface-automatic in config");
+ "interface-automatic or do-ip4 in config");
return 0;
# endif /* IP_PKTINFO */
@@ -847,19 +1053,34 @@ set_recvpktinfo(int s, int family)
* @param reuseport: try to set SO_REUSEPORT if nonNULL and true.
* set to false on exit if reuseport failed due to no kernel support.
* @param transparent: set IP_TRANSPARENT socket option.
+ * @param tcp_mss: maximum segment size of tcp socket. default if zero.
+ * @param freebind: set IP_FREEBIND socket option.
+ * @param use_systemd: if true, fetch sockets from systemd.
+ * @param dnscrypt_port: dnscrypt service port number
* @return: returns false on error.
*/
static int
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct addrinfo *hints, const char* port, struct listen_port** list,
- size_t rcv, size_t snd, int ssl_port, int* reuseport, int transparent)
+ size_t rcv, size_t snd, int ssl_port, int* reuseport, int transparent,
+ int tcp_mss, int freebind, int use_systemd, int dnscrypt_port)
{
int s, noip6=0;
+#ifdef USE_DNSCRYPT
+ int is_dnscrypt = ((strchr(ifname, '@') &&
+ atoi(strchr(ifname, '@')+1) == dnscrypt_port) ||
+ (!strchr(ifname, '@') && atoi(port) == dnscrypt_port));
+#else
+ int is_dnscrypt = 0;
+ (void)dnscrypt_port;
+#endif
+
if(!do_udp && !do_tcp)
return 0;
if(do_auto) {
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
- &noip6, rcv, snd, reuseport, transparent)) == -1) {
+ &noip6, rcv, snd, reuseport, transparent,
+ tcp_mss, freebind, use_systemd)) == -1) {
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
@@ -875,7 +1096,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
#endif
return 0;
}
- if(!port_insert(list, s, listen_type_udpancil)) {
+ if(!port_insert(list, s,
+ is_dnscrypt?listen_type_udpancil_dnscrypt:listen_type_udpancil)) {
#ifndef USE_WINSOCK
close(s);
#else
@@ -886,14 +1108,16 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
} else if(do_udp) {
/* regular udp socket */
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
- &noip6, rcv, snd, reuseport, transparent)) == -1) {
+ &noip6, rcv, snd, reuseport, transparent,
+ tcp_mss, freebind, use_systemd)) == -1) {
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
}
return 0;
}
- if(!port_insert(list, s, listen_type_udp)) {
+ if(!port_insert(list, s,
+ is_dnscrypt?listen_type_udp_dnscrypt:listen_type_udp)) {
#ifndef USE_WINSOCK
close(s);
#else
@@ -907,7 +1131,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
atoi(strchr(ifname, '@')+1) == ssl_port) ||
(!strchr(ifname, '@') && atoi(port) == ssl_port));
if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1,
- &noip6, 0, 0, reuseport, transparent)) == -1) {
+ &noip6, 0, 0, reuseport, transparent, tcp_mss,
+ freebind, use_systemd)) == -1) {
if(noip6) {
/*log_warn("IPv6 protocol not available");*/
return 1;
@@ -917,7 +1142,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if(is_ssl)
verbose(VERB_ALGO, "setup TCP for SSL service");
if(!port_insert(list, s, is_ssl?listen_type_ssl:
- listen_type_tcp)) {
+ (is_dnscrypt?listen_type_tcp_dnscrypt:listen_type_tcp))) {
#ifndef USE_WINSOCK
close(s);
#else
@@ -951,7 +1176,7 @@ listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
struct listen_dnsport*
listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, void* sslctx,
- struct dt_env* dtenv, comm_point_callback_t* cb, void *cb_arg)
+ struct dt_env* dtenv, comm_point_callback_type* cb, void *cb_arg)
{
struct listen_dnsport* front = (struct listen_dnsport*)
malloc(sizeof(struct listen_dnsport));
@@ -959,6 +1184,9 @@ listen_create(struct comm_base* base, struct listen_port* ports,
return NULL;
front->cps = NULL;
front->udp_buff = sldns_buffer_new(bufsize);
+#ifdef USE_DNSCRYPT
+ front->dnscrypt_udp_buff = NULL;
+#endif
if(!front->udp_buff) {
free(front);
return NULL;
@@ -967,17 +1195,20 @@ listen_create(struct comm_base* base, struct listen_port* ports,
/* create comm points as needed */
while(ports) {
struct comm_point* cp = NULL;
- if(ports->ftype == listen_type_udp)
+ if(ports->ftype == listen_type_udp ||
+ ports->ftype == listen_type_udp_dnscrypt)
cp = comm_point_create_udp(base, ports->fd,
front->udp_buff, cb, cb_arg);
- else if(ports->ftype == listen_type_tcp)
+ else if(ports->ftype == listen_type_tcp ||
+ ports->ftype == listen_type_tcp_dnscrypt)
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, bufsize, cb, cb_arg);
else if(ports->ftype == listen_type_ssl) {
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, bufsize, cb, cb_arg);
cp->ssl = sslctx;
- } else if(ports->ftype == listen_type_udpancil)
+ } else if(ports->ftype == listen_type_udpancil ||
+ ports->ftype == listen_type_udpancil_dnscrypt)
cp = comm_point_create_udp_ancil(base, ports->fd,
front->udp_buff, cb, cb_arg);
if(!cp) {
@@ -987,6 +1218,21 @@ listen_create(struct comm_base* base, struct listen_port* ports,
}
cp->dtenv = dtenv;
cp->do_not_close = 1;
+#ifdef USE_DNSCRYPT
+ if (ports->ftype == listen_type_udp_dnscrypt ||
+ ports->ftype == listen_type_tcp_dnscrypt ||
+ ports->ftype == listen_type_udpancil_dnscrypt) {
+ cp->dnscrypt = 1;
+ cp->dnscrypt_buffer = sldns_buffer_new(bufsize);
+ if(!cp->dnscrypt_buffer) {
+ log_err("can't alloc dnscrypt_buffer");
+ comm_point_delete(cp);
+ listen_delete(front);
+ return NULL;
+ }
+ front->dnscrypt_udp_buff = cp->dnscrypt_buffer;
+ }
+#endif
if(!listen_cp_insert(cp, front)) {
log_err("malloc failed");
comm_point_delete(cp);
@@ -1022,6 +1268,12 @@ listen_delete(struct listen_dnsport* front)
if(!front)
return;
listen_list_delete(front->cps);
+#ifdef USE_DNSCRYPT
+ if(front->dnscrypt_udp_buff &&
+ front->udp_buff != front->dnscrypt_udp_buff) {
+ sldns_buffer_free(front->dnscrypt_udp_buff);
+ }
+#endif
sldns_buffer_free(front->udp_buff);
free(front);
}
@@ -1064,7 +1316,9 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
- cfg->ip_transparent)) {
+ cfg->ip_transparent,
+ cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+ cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
@@ -1076,7 +1330,9 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
- cfg->ip_transparent)) {
+ cfg->ip_transparent,
+ cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+ cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
@@ -1090,7 +1346,9 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
- cfg->ip_transparent)) {
+ cfg->ip_transparent,
+ cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+ cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
@@ -1102,7 +1360,9 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
- cfg->ip_transparent)) {
+ cfg->ip_transparent,
+ cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+ cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
@@ -1130,10 +1390,16 @@ void listening_ports_free(struct listen_port* list)
size_t listen_get_mem(struct listen_dnsport* listen)
{
+ struct listen_list* p;
size_t s = sizeof(*listen) + sizeof(*listen->base) +
sizeof(*listen->udp_buff) +
sldns_buffer_capacity(listen->udp_buff);
- struct listen_list* p;
+#ifdef USE_DNSCRYPT
+ s += sizeof(*listen->dnscrypt_udp_buff);
+ if(listen->udp_buff != listen->dnscrypt_udp_buff){
+ s += sldns_buffer_capacity(listen->dnscrypt_udp_buff);
+ }
+#endif
for(p = listen->cps; p; p = p->next) {
s += sizeof(*p);
s += comm_point_get_mem(p->com);
diff --git a/external/unbound/services/listen_dnsport.h b/external/unbound/services/listen_dnsport.h
index 676f0c638..fac0f7970 100644
--- a/external/unbound/services/listen_dnsport.h
+++ b/external/unbound/services/listen_dnsport.h
@@ -59,7 +59,9 @@ struct listen_dnsport {
/** buffer shared by UDP connections, since there is only one
datagram at any time. */
struct sldns_buffer* udp_buff;
-
+#ifdef USE_DNSCRYPT
+ struct sldns_buffer* dnscrypt_udp_buff;
+#endif
/** list of comm points used to get incoming events */
struct listen_list* cps;
};
@@ -85,7 +87,14 @@ enum listen_type {
/** udp ipv6 (v4mapped) for use with ancillary data */
listen_type_udpancil,
/** ssl over tcp type */
- listen_type_ssl
+ listen_type_ssl,
+ /** udp type + dnscrypt*/
+ listen_type_udp_dnscrypt,
+ /** tcp type + dnscrypt */
+ listen_type_tcp_dnscrypt,
+ /** udp ipv6 (v4mapped) for use with ancillary data + dnscrypt*/
+ listen_type_udpancil_dnscrypt
+
};
/**
@@ -137,7 +146,7 @@ void listening_ports_free(struct listen_port* list);
*/
struct listen_dnsport* listen_create(struct comm_base* base,
struct listen_port* ports, size_t bufsize, int tcp_accept_count,
- void* sslctx, struct dt_env *dtenv, comm_point_callback_t* cb,
+ void* sslctx, struct dt_env *dtenv, comm_point_callback_type* cb,
void* cb_arg);
/**
@@ -190,11 +199,13 @@ void listen_start_accept(struct listen_dnsport* listen);
* @param reuseport: if nonNULL and true, try to set SO_REUSEPORT on
* listening UDP port. Set to false on return if it failed to do so.
* @param transparent: set IP_TRANSPARENT socket option.
+ * @param freebind: set IP_FREEBIND socket option.
+ * @param use_systemd: if true, fetch sockets from systemd.
* @return: the socket. -1 on error.
*/
int create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t addrlen, int v6only, int* inuse, int* noproto, int rcv,
- int snd, int listen, int* reuseport, int transparent);
+ int snd, int listen, int* reuseport, int transparent, int freebind, int use_systemd);
/**
* Create and bind TCP listening socket
@@ -204,18 +215,22 @@ int create_udp_sock(int family, int socktype, struct sockaddr* addr,
* @param reuseport: if nonNULL and true, try to set SO_REUSEPORT on
* listening UDP port. Set to false on return if it failed to do so.
* @param transparent: set IP_TRANSPARENT socket option.
+ * @param mss: maximum segment size of the socket. if zero, leaves the default.
+ * @param freebind: set IP_FREEBIND socket option.
+ * @param use_systemd: if true, fetch sockets from systemd.
* @return: the socket. -1 on error.
*/
int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
- int* reuseport, int transparent);
+ int* reuseport, int transparent, int mss, int freebind, int use_systemd);
/**
* Create and bind local listening socket
* @param path: path to the socket.
* @param noproto: on error, this is set true if cause is that local sockets
* are not supported.
+ * @param use_systemd: if true, fetch sockets from systemd.
* @return: the socket. -1 on error.
*/
-int create_local_accept_sock(const char* path, int* noproto);
+int create_local_accept_sock(const char* path, int* noproto, int use_systemd);
#endif /* LISTEN_DNSPORT_H */
diff --git a/external/unbound/services/localzone.c b/external/unbound/services/localzone.c
index c50ad0f15..dcce46e86 100644
--- a/external/unbound/services/localzone.c
+++ b/external/unbound/services/localzone.c
@@ -51,6 +51,12 @@
#include "util/netevent.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
+#include "util/as112.h"
+#include "util/config_file.h"
+
+/* maximum RRs in an RRset, to cap possible 'endless' list RRs.
+ * with 16 bytes for an A record, a 64K packet has about 4000 max */
+#define LOCALZONE_RRSET_COUNT_MAX 4096
struct local_zones*
local_zones_create(void)
@@ -68,7 +74,7 @@ local_zones_create(void)
/** helper traverse to delete zones */
static void
-lzdel(rbnode_t* n, void* ATTR_UNUSED(arg))
+lzdel(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct local_zone* z = (struct local_zone*)n->key;
local_zone_delete(z);
@@ -93,6 +99,7 @@ local_zone_delete(struct local_zone* z)
lock_rw_destroy(&z->lock);
regional_destroy(z->region);
free(z->name);
+ free(z->taglist);
free(z);
}
@@ -152,13 +159,13 @@ local_zone_create(uint8_t* nm, size_t len, int labs,
z->namelen = len;
z->namelabs = labs;
lock_rw_init(&z->lock);
- z->region = regional_create();
+ z->region = regional_create_custom(sizeof(struct regional));
if(!z->region) {
free(z);
return NULL;
}
rbtree_init(&z->data, &local_data_cmp);
- lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_t));
+ lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_type));
/* also the zones->lock protects node, parent, name*, class */
return z;
}
@@ -170,6 +177,7 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
{
struct local_zone* z = local_zone_create(nm, len, labs, t, c);
if(!z) {
+ free(nm);
log_err("out of memory");
return NULL;
}
@@ -178,11 +186,18 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
lock_rw_wrlock(&zones->lock);
lock_rw_wrlock(&z->lock);
if(!rbtree_insert(&zones->ztree, &z->node)) {
+ struct local_zone* oldz;
log_warn("duplicate local-zone");
lock_rw_unlock(&z->lock);
- local_zone_delete(z);
+ /* save zone name locally before deallocation,
+ * otherwise, nm is gone if we zone_delete now. */
+ oldz = z;
+ /* find the correct zone, so not an error for duplicate */
+ z = local_zones_find(zones, nm, len, labs, c);
+ lock_rw_wrlock(&z->lock);
lock_rw_unlock(&zones->lock);
- return NULL;
+ local_zone_delete(oldz);
+ return z;
}
lock_rw_unlock(&zones->lock);
return z;
@@ -214,9 +229,8 @@ lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
return z;
}
-/** return name and class and rdata of rr; parses string */
-static int
-get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
+int
+rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len,
uint8_t** rdata, size_t* rdata_len)
{
@@ -269,16 +283,20 @@ get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass)
* Find an rrset in local data structure.
* @param data: local data domain name structure.
* @param type: type to look for (host order).
+ * @param alias_ok: 1 if matching a non-exact, alias type such as CNAME is
+ * allowed. otherwise 0.
* @return rrset pointer or NULL if not found.
*/
static struct local_rrset*
-local_data_find_type(struct local_data* data, uint16_t type)
+local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
{
struct local_rrset* p;
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;
}
return NULL;
}
@@ -334,15 +352,20 @@ new_local_rrset(struct regional* region, struct local_data* node,
}
/** insert RR into RRset data structure; Wastes a couple of bytes */
-static int
-insert_rr(struct regional* region, struct packed_rrset_data* pd,
- uint8_t* rdata, size_t rdata_len, time_t ttl)
+int
+rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd,
+ uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr)
{
size_t* oldlen = pd->rr_len;
time_t* oldttl = pd->rr_ttl;
uint8_t** olddata = pd->rr_data;
/* add RR to rrset */
+ if(pd->count > LOCALZONE_RRSET_COUNT_MAX) {
+ log_warn("RRset '%s' has more than %d records, record ignored",
+ rrstr, LOCALZONE_RRSET_COUNT_MAX);
+ return 1;
+ }
pd->count++;
pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count);
pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count);
@@ -432,8 +455,8 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
uint8_t rr[LDNS_RR_BUF_SIZE];
uint8_t* rdata;
size_t rdata_len;
- if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr),
- &rdata, &rdata_len)) {
+ if(!rrstr_get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr,
+ sizeof(rr), &rdata, &rdata_len)) {
log_err("bad local-data: %s", rrstr);
return 0;
}
@@ -453,7 +476,23 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
log_assert(node);
free(nm);
- rrset = local_data_find_type(node, rrtype);
+ /* Reject it if we would end up having CNAME and other data (including
+ * another CNAME) for a redirect zone. */
+ if(z->type == local_zone_redirect && node->rrsets) {
+ const char* othertype = NULL;
+ if (rrtype == LDNS_RR_TYPE_CNAME)
+ othertype = "other";
+ else if (node->rrsets->rrset->rk.type ==
+ htons(LDNS_RR_TYPE_CNAME)) {
+ othertype = "CNAME";
+ }
+ if(othertype) {
+ log_err("local-data '%s' in redirect zone must not "
+ "coexist with %s local-data", rrstr, othertype);
+ return 0;
+ }
+ }
+ rrset = local_data_find_type(node, rrtype, 0);
if(!rrset) {
rrset = new_local_rrset(z->region, node, rrtype, rrclass);
if(!rrset)
@@ -473,7 +512,7 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr);
return 1;
}
- return insert_rr(z->region, pd, rdata, rdata_len, ttl);
+ return rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr);
}
/** enter a data RR into auth data; a zone for it must exist */
@@ -505,6 +544,123 @@ lz_enter_rr_str(struct local_zones* zones, const char* rr)
return r;
}
+/** enter tagstring into zone */
+static int
+lz_enter_zone_tag(struct local_zones* zones, char* zname, uint8_t* list,
+ size_t len, uint16_t rr_class)
+{
+ uint8_t dname[LDNS_MAX_DOMAINLEN+1];
+ size_t dname_len = sizeof(dname);
+ int dname_labs, r = 0;
+ struct local_zone* z;
+
+ if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) {
+ log_err("cannot parse zone name in local-zone-tag: %s", zname);
+ return 0;
+ }
+ dname_labs = dname_count_labels(dname);
+
+ lock_rw_rdlock(&zones->lock);
+ z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class);
+ if(!z) {
+ lock_rw_unlock(&zones->lock);
+ log_err("no local-zone for tag %s", zname);
+ return 0;
+ }
+ lock_rw_wrlock(&z->lock);
+ lock_rw_unlock(&zones->lock);
+ free(z->taglist);
+ z->taglist = memdup(list, len);
+ z->taglen = len;
+ if(z->taglist)
+ r = 1;
+ lock_rw_unlock(&z->lock);
+ return r;
+}
+
+/** enter override into zone */
+static int
+lz_enter_override(struct local_zones* zones, char* zname, char* netblock,
+ char* type, uint16_t rr_class)
+{
+ uint8_t dname[LDNS_MAX_DOMAINLEN+1];
+ size_t dname_len = sizeof(dname);
+ int dname_labs;
+ struct sockaddr_storage addr;
+ int net;
+ socklen_t addrlen;
+ struct local_zone* z;
+ enum localzone_type t;
+
+ /* parse zone name */
+ if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) {
+ log_err("cannot parse zone name in local-zone-override: %s %s",
+ zname, netblock);
+ return 0;
+ }
+ dname_labs = dname_count_labels(dname);
+
+ /* parse netblock */
+ if(!netblockstrtoaddr(netblock, UNBOUND_DNS_PORT, &addr, &addrlen,
+ &net)) {
+ log_err("cannot parse netblock in local-zone-override: %s %s",
+ zname, netblock);
+ return 0;
+ }
+
+ /* parse zone type */
+ if(!local_zone_str2type(type, &t)) {
+ log_err("cannot parse type in local-zone-override: %s %s %s",
+ zname, netblock, type);
+ return 0;
+ }
+
+ /* find localzone entry */
+ lock_rw_rdlock(&zones->lock);
+ z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class);
+ if(!z) {
+ lock_rw_unlock(&zones->lock);
+ log_err("no local-zone for local-zone-override %s", zname);
+ return 0;
+ }
+ lock_rw_wrlock(&z->lock);
+ lock_rw_unlock(&zones->lock);
+
+ /* create netblock addr_tree if not present yet */
+ if(!z->override_tree) {
+ z->override_tree = (struct rbtree_type*)regional_alloc_zero(
+ z->region, sizeof(*z->override_tree));
+ if(!z->override_tree) {
+ lock_rw_unlock(&z->lock);
+ log_err("out of memory");
+ return 0;
+ }
+ addr_tree_init(z->override_tree);
+ }
+ /* add new elem to tree */
+ if(z->override_tree) {
+ struct local_zone_override* n;
+ n = (struct local_zone_override*)regional_alloc_zero(
+ z->region, sizeof(*n));
+ if(!n) {
+ lock_rw_unlock(&z->lock);
+ log_err("out of memory");
+ return 0;
+ }
+ n->type = t;
+ if(!addr_tree_insert(z->override_tree,
+ (struct addr_tree_node*)n, &addr, addrlen, net)) {
+ lock_rw_unlock(&z->lock);
+ log_err("duplicate local-zone-override %s %s",
+ zname, netblock);
+ return 1;
+ }
+ }
+
+ lock_rw_unlock(&z->lock);
+ return 1;
+}
+
/** parse local-zone: statements */
static int
lz_enter_zones(struct local_zones* zones, struct config_file* cfg)
@@ -592,10 +748,11 @@ static int
lz_enter_defaults(struct local_zones* zones, struct config_file* cfg)
{
struct local_zone* z;
+ const char** zstr;
- /* this list of zones is from RFC 6303 */
+ /* this list of zones is from RFC 6303 and RFC 7686 */
- /* block localhost level zones, first, later the LAN zones */
+ /* block localhost level zones first, then onion and later the LAN zones */
/* localhost. zone */
if(!lz_exists(zones, "localhost.") &&
@@ -653,111 +810,44 @@ lz_enter_defaults(struct local_zones* zones, struct config_file* cfg)
}
lock_rw_unlock(&z->lock);
}
+ /* onion. zone (RFC 7686) */
+ if(!lz_exists(zones, "onion.") &&
+ !lz_nodefault(cfg, "onion.")) {
+ if(!(z=lz_enter_zone(zones, "onion.", "static",
+ LDNS_RR_CLASS_IN)) ||
+ !lz_enter_rr_into_zone(z,
+ "onion. 10800 IN NS localhost.") ||
+ !lz_enter_rr_into_zone(z,
+ "onion. 10800 IN SOA localhost. nobody.invalid. "
+ "1 3600 1200 604800 10800")) {
+ log_err("out of memory adding default zone");
+ if(z) { lock_rw_unlock(&z->lock); }
+ return 0;
+ }
+ lock_rw_unlock(&z->lock);
+ }
- /* if unblock lan-zones, then do not add the zones below.
- * we do add the zones above, about 127.0.0.1, because localhost is
- * not on the lan. */
- if(cfg->unblock_lan_zones)
- return 1;
+ /* block AS112 zones, unless asked not to */
+ if(!cfg->unblock_lan_zones) {
+ for(zstr = as112_zones; *zstr; zstr++) {
+ if(!add_as112_default(zones, cfg, *zstr)) {
+ log_err("out of memory adding default zone");
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
- /* block LAN level zones */
- if ( !add_as112_default(zones, cfg, "10.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "16.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "17.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "18.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "19.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "20.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "21.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "22.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "23.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "24.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "25.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "26.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "27.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "28.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "29.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "30.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "31.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "168.192.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "0.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "64.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "65.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "66.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "67.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "68.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "69.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "70.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "71.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "72.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "73.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "74.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "75.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "76.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "77.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "78.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "79.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "80.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "81.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "82.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "83.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "84.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "85.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "86.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "87.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "88.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "89.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "90.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "91.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "92.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "93.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "94.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "95.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "96.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "97.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "98.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "99.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "100.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "101.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "102.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "103.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "104.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "105.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "106.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "107.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "108.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "109.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "110.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "111.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "112.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "113.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "114.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "115.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "116.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "117.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "118.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "119.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "120.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "121.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "122.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "123.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "124.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "125.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "126.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "127.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "254.169.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "2.0.192.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "100.51.198.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "113.0.203.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "255.255.255.255.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "d.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "8.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "9.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "a.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "b.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "8.b.d.0.1.0.0.2.ip6.arpa.")) {
- log_err("out of memory adding default zone");
- return 0;
+/** parse local-zone-override: statements */
+static int
+lz_enter_overrides(struct local_zones* zones, struct config_file* cfg)
+{
+ struct config_str3list* p;
+ for(p = cfg->local_zone_overrides; p; p = p->next) {
+ if(!lz_enter_override(zones, p->str, p->str2, p->str3,
+ LDNS_RR_CLASS_IN))
+ return 0;
}
return 1;
}
@@ -791,6 +881,9 @@ init_parents(struct local_zones* zones)
break;
}
prev = node;
+
+ if(node->override_tree)
+ addr_tree_init_parents(node->override_tree);
lock_rw_unlock(&node->lock);
}
lock_rw_unlock(&zones->lock);
@@ -878,6 +971,22 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
return 1;
}
+/** enter local-zone-tag info */
+static int
+lz_enter_zone_tags(struct local_zones* zones, struct config_file* cfg)
+{
+ struct config_strbytelist* p;
+ int c = 0;
+ for(p = cfg->local_zone_tags; p; p = p->next) {
+ if(!lz_enter_zone_tag(zones, p->str, p->str2, p->str2len,
+ LDNS_RR_CLASS_IN))
+ return 0;
+ c++;
+ }
+ if(c) verbose(VERB_ALGO, "applied tags to %d local zones", c);
+ return 1;
+}
+
/** enter auth data */
static int
lz_enter_data(struct local_zones* zones, struct config_file* cfg)
@@ -913,6 +1022,10 @@ local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
if(!lz_enter_defaults(zones, cfg)) {
return 0;
}
+ /* enter local zone overrides */
+ if(!lz_enter_overrides(zones, cfg)) {
+ return 0;
+ }
/* create implicit transparent zone from data. */
if(!lz_setup_implicit(zones, cfg)) {
return 0;
@@ -920,6 +1033,10 @@ local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
/* setup parent ptrs for lookup during data entry */
init_parents(zones);
+ /* insert local zone tags */
+ if(!lz_enter_zone_tags(zones, cfg)) {
+ return 0;
+ }
/* insert local data */
if(!lz_enter_data(zones, cfg)) {
return 0;
@@ -933,33 +1050,41 @@ struct local_zone*
local_zones_lookup(struct local_zones* zones,
uint8_t* name, size_t len, int labs, uint16_t dclass)
{
- rbnode_t* res = NULL;
+ return local_zones_tags_lookup(zones, name, len, labs,
+ dclass, NULL, 0, 1);
+}
+
+struct local_zone*
+local_zones_tags_lookup(struct local_zones* zones,
+ uint8_t* name, size_t len, int labs, uint16_t dclass,
+ uint8_t* taglist, size_t taglen, int ignoretags)
+{
+ rbnode_type* res = NULL;
struct local_zone *result;
struct local_zone key;
+ int m;
key.node.key = &key;
key.dclass = dclass;
key.name = name;
key.namelen = len;
key.namelabs = labs;
- if(rbtree_find_less_equal(&zones->ztree, &key, &res)) {
- /* exact */
- return (struct local_zone*)res;
- } else {
- /* smaller element (or no element) */
- int m;
- result = (struct local_zone*)res;
- if(!result || result->dclass != dclass)
- return NULL;
- /* count number of labels matched */
- (void)dname_lab_cmp(result->name, result->namelabs, key.name,
- key.namelabs, &m);
- while(result) { /* go up until qname is subdomain of zone */
- if(result->namelabs <= m)
- break;
- result = result->parent;
- }
- return result;
+ rbtree_find_less_equal(&zones->ztree, &key, &res);
+ result = (struct local_zone*)res;
+ /* exact or smaller element (or no element) */
+ if(!result || result->dclass != dclass)
+ return NULL;
+ /* count number of labels matched */
+ (void)dname_lab_cmp(result->name, result->namelabs, key.name,
+ key.namelabs, &m);
+ while(result) { /* go up until qname is zone or subdomain of zone */
+ if(result->namelabs <= m)
+ if(ignoretags || !result->taglist ||
+ taglist_intersect(result->taglist,
+ result->taglen, taglist, taglen))
+ break;
+ result = result->parent;
}
+ return result;
}
struct local_zone*
@@ -1031,6 +1156,18 @@ void local_zones_print(struct local_zones* zones)
log_nametypeclass(0, "inform_deny zone",
z->name, 0, z->dclass);
break;
+ case local_zone_always_transparent:
+ log_nametypeclass(0, "always_transparent zone",
+ z->name, 0, z->dclass);
+ break;
+ case local_zone_always_refuse:
+ log_nametypeclass(0, "always_refuse zone",
+ z->name, 0, z->dclass);
+ break;
+ case local_zone_always_nxdomain:
+ log_nametypeclass(0, "always_nxdomain zone",
+ z->name, 0, z->dclass);
+ break;
default:
log_nametypeclass(0, "badtyped zone",
z->name, 0, z->dclass);
@@ -1044,8 +1181,8 @@ void local_zones_print(struct local_zones* zones)
/** encode answer consisting of 1 rrset */
static int
-local_encode(struct query_info* qinfo, struct edns_data* edns,
- sldns_buffer* buf, struct regional* temp,
+local_encode(struct query_info* qinfo, struct module_env* env,
+ struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
struct ub_packed_rrset_key* rrset, int ansec, int rcode)
{
struct reply_info rep;
@@ -1064,22 +1201,155 @@ local_encode(struct query_info* qinfo, struct edns_data* edns,
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
- if(!reply_info_answer_encode(qinfo, &rep,
+ if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, temp)
+ || !reply_info_answer_encode(qinfo, &rep,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2),
- buf, 0, 0, temp, udpsize, edns,
+ buf, 0, 0, temp, udpsize, edns,
(int)(edns->bits&EDNS_DO), 0))
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
- sldns_buffer_read_u16_at(buf, 2), edns);
+ sldns_buffer_read_u16_at(buf, 2), edns);
return 1;
}
+/** encode local error answer */
+static void
+local_error_encode(struct query_info* qinfo, struct module_env* env,
+ struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
+ int rcode, int r)
+{
+ edns->edns_version = EDNS_ADVERTISED_VERSION;
+ edns->udp_size = EDNS_ADVERTISED_SIZE;
+ edns->ext_rcode = 0;
+ edns->bits &= EDNS_DO;
+
+ if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
+ rcode, edns, temp))
+ edns->opt_list = NULL;
+ error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf),
+ sldns_buffer_read_u16_at(buf, 2), edns);
+}
+
+/** find local data tag string match for the given type in the list */
+int
+local_data_find_tag_datas(const struct query_info* qinfo,
+ struct config_strlist* list, struct ub_packed_rrset_key* r,
+ struct regional* temp)
+{
+ struct config_strlist* p;
+ char buf[65536];
+ uint8_t rr[LDNS_RR_BUF_SIZE];
+ size_t len;
+ int res;
+ struct packed_rrset_data* d;
+ for(p=list; p; p=p->next) {
+ uint16_t rdr_type;
+
+ len = sizeof(rr);
+ /* does this element match the type? */
+ snprintf(buf, sizeof(buf), ". %s", p->str);
+ res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600,
+ NULL, 0, NULL, 0);
+ if(res != 0)
+ /* parse errors are already checked before, in
+ * acllist check_data, skip this for robustness */
+ continue;
+ if(len < 1 /* . */ + 8 /* typeclassttl*/ + 2 /*rdatalen*/)
+ continue;
+ rdr_type = sldns_wirerr_get_type(rr, len, 1);
+ if(rdr_type != qinfo->qtype && rdr_type != LDNS_RR_TYPE_CNAME)
+ continue;
+
+ /* do we have entries already? if not setup key */
+ if(r->rk.dname == NULL) {
+ r->entry.key = r;
+ r->rk.dname = qinfo->qname;
+ r->rk.dname_len = qinfo->qname_len;
+ r->rk.type = htons(rdr_type);
+ r->rk.rrset_class = htons(qinfo->qclass);
+ r->rk.flags = 0;
+ d = (struct packed_rrset_data*)regional_alloc_zero(
+ temp, sizeof(struct packed_rrset_data)
+ + sizeof(size_t) + sizeof(uint8_t*) +
+ sizeof(time_t));
+ if(!d) return 0; /* out of memory */
+ r->entry.data = d;
+ d->ttl = sldns_wirerr_get_ttl(rr, len, 1);
+ d->rr_len = (size_t*)((uint8_t*)d +
+ sizeof(struct packed_rrset_data));
+ d->rr_data = (uint8_t**)&(d->rr_len[1]);
+ d->rr_ttl = (time_t*)&(d->rr_data[1]);
+ }
+ d = (struct packed_rrset_data*)r->entry.data;
+ /* add entry to the data */
+ if(d->count != 0) {
+ size_t* oldlen = d->rr_len;
+ uint8_t** olddata = d->rr_data;
+ time_t* oldttl = d->rr_ttl;
+ /* increase arrays for lookup */
+ /* this is of course slow for very many records,
+ * but most redirects are expected with few records */
+ d->rr_len = (size_t*)regional_alloc_zero(temp,
+ (d->count+1)*sizeof(size_t));
+ d->rr_data = (uint8_t**)regional_alloc_zero(temp,
+ (d->count+1)*sizeof(uint8_t*));
+ d->rr_ttl = (time_t*)regional_alloc_zero(temp,
+ (d->count+1)*sizeof(time_t));
+ if(!d->rr_len || !d->rr_data || !d->rr_ttl)
+ return 0; /* out of memory */
+ /* first one was allocated after struct d, but new
+ * ones get their own array increment alloc, so
+ * copy old content */
+ memmove(d->rr_len, oldlen, d->count*sizeof(size_t));
+ memmove(d->rr_data, olddata, d->count*sizeof(uint8_t*));
+ memmove(d->rr_ttl, oldttl, d->count*sizeof(time_t));
+ }
+
+ d->rr_len[d->count] = sldns_wirerr_get_rdatalen(rr, len, 1)+2;
+ d->rr_ttl[d->count] = sldns_wirerr_get_ttl(rr, len, 1);
+ d->rr_data[d->count] = regional_alloc_init(temp,
+ sldns_wirerr_get_rdatawl(rr, len, 1),
+ d->rr_len[d->count]);
+ if(!d->rr_data[d->count])
+ return 0; /* out of memory */
+ d->count++;
+ }
+ if(r->rk.dname)
+ return 1;
+ return 0;
+}
+
+static int
+find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
+ struct ub_packed_rrset_key* r, struct regional* temp)
+{
+ int result = local_data_find_tag_datas(qinfo, list, r, temp);
+
+ /* If we've found a non-exact alias type of local data, make a shallow
+ * copy of the RRset and remember it in qinfo to complete the alias
+ * chain later. */
+ if(result && qinfo->qtype != LDNS_RR_TYPE_CNAME &&
+ r->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
+ qinfo->local_alias =
+ regional_alloc_zero(temp, sizeof(struct local_rrset));
+ if(!qinfo->local_alias)
+ return 0; /* out of memory */
+ qinfo->local_alias->rrset =
+ regional_alloc_init(temp, r, sizeof(*r));
+ if(!qinfo->local_alias->rrset)
+ return 0; /* out of memory */
+ }
+ return result;
+}
+
/** answer local data match */
static int
-local_data_answer(struct local_zone* z, struct query_info* qinfo,
- struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
- int labs, struct local_data** ldp)
+local_data_answer(struct local_zone* z, struct module_env* env,
+ struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
+ struct regional* temp, int labs, struct local_data** ldp,
+ enum localzone_type lz_type, int tag, struct config_strlist** tag_datas,
+ size_t tag_datas_size, char** tagname, int num_tags)
{
struct local_data key;
struct local_data* ld;
@@ -1088,58 +1358,95 @@ local_data_answer(struct local_zone* z, struct query_info* qinfo,
key.name = qinfo->qname;
key.namelen = qinfo->qname_len;
key.namelabs = labs;
- if(z->type == local_zone_redirect) {
+ if(lz_type == local_zone_redirect) {
key.name = z->name;
key.namelen = z->namelen;
key.namelabs = z->namelabs;
+ if(tag != -1 && (size_t)tag<tag_datas_size && tag_datas[tag]) {
+ struct ub_packed_rrset_key r;
+ memset(&r, 0, sizeof(r));
+ if(find_tag_datas(qinfo, tag_datas[tag], &r, temp)) {
+ verbose(VERB_ALGO, "redirect with tag data [%d] %s",
+ tag, (tag<num_tags?tagname[tag]:"null"));
+
+ /* If we found a matching alias, we should
+ * use it as part of the answer, but we can't
+ * encode it until we complete the alias
+ * chain. */
+ if(qinfo->local_alias)
+ return 1;
+ return local_encode(qinfo, env, edns, buf, temp,
+ &r, 1, LDNS_RCODE_NOERROR);
+ }
+ }
}
ld = (struct local_data*)rbtree_search(&z->data, &key.node);
*ldp = ld;
if(!ld) {
return 0;
}
- lr = local_data_find_type(ld, qinfo->qtype);
+ lr = local_data_find_type(ld, qinfo->qtype, 1);
if(!lr)
return 0;
- if(z->type == local_zone_redirect) {
+
+ /* Special case for alias matching. See local_data_answer(). */
+ if(lz_type == local_zone_redirect &&
+ qinfo->qtype != LDNS_RR_TYPE_CNAME &&
+ lr->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
+ qinfo->local_alias =
+ regional_alloc_zero(temp, sizeof(struct local_rrset));
+ if(!qinfo->local_alias)
+ return 0; /* out of memory */
+ qinfo->local_alias->rrset =
+ regional_alloc_init(temp, lr->rrset, sizeof(*lr->rrset));
+ if(!qinfo->local_alias->rrset)
+ 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;
+ }
+ if(lz_type == local_zone_redirect) {
/* convert rrset name to query name; like a wildcard */
struct ub_packed_rrset_key r = *lr->rrset;
r.rk.dname = qinfo->qname;
r.rk.dname_len = qinfo->qname_len;
- return local_encode(qinfo, edns, buf, temp, &r, 1,
+ return local_encode(qinfo, env, edns, buf, temp, &r, 1,
LDNS_RCODE_NOERROR);
}
- return local_encode(qinfo, edns, buf, temp, lr->rrset, 1,
+ return local_encode(qinfo, env, edns, buf, temp, lr->rrset, 1,
LDNS_RCODE_NOERROR);
}
/**
* answer in case where no exact match is found
* @param z: zone for query
+ * @param env: module environment
* @param qinfo: query
* @param edns: edns from query
* @param buf: buffer for answer.
* @param temp: temp region for encoding
* @param ld: local data, if NULL, no such name exists in localdata.
+ * @param lz_type: type of the local zone
* @return 1 if a reply is to be sent, 0 if not.
*/
static int
-lz_zone_answer(struct local_zone* z, struct query_info* qinfo,
- struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
- struct local_data* ld)
+lz_zone_answer(struct local_zone* z, struct module_env* env,
+ struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
+ struct regional* temp, struct local_data* ld, enum localzone_type lz_type)
{
- if(z->type == local_zone_deny || z->type == local_zone_inform_deny) {
+ if(lz_type == local_zone_deny || lz_type == local_zone_inform_deny) {
/** no reply at all, signal caller by clearing buffer. */
sldns_buffer_clear(buf);
sldns_buffer_flip(buf);
return 1;
- } else if(z->type == local_zone_refuse) {
- error_encode(buf, (LDNS_RCODE_REFUSED|BIT_AA), qinfo,
- *(uint16_t*)sldns_buffer_begin(buf),
- sldns_buffer_read_u16_at(buf, 2), edns);
+ } else if(lz_type == local_zone_refuse
+ || lz_type == local_zone_always_refuse) {
+ local_error_encode(qinfo, env, edns, buf, temp,
+ LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA));
return 1;
- } else if(z->type == local_zone_static ||
- z->type == local_zone_redirect) {
+ } else if(lz_type == local_zone_static ||
+ lz_type == local_zone_redirect ||
+ lz_type == local_zone_always_nxdomain) {
/* for static, reply nodata or nxdomain
* for redirect, reply nodata */
/* no additional section processing,
@@ -1147,30 +1454,30 @@ lz_zone_answer(struct local_zone* z, struct query_info* qinfo,
* or using closest match for NSEC.
* or using closest match for returning delegation downwards
*/
- int rcode = ld?LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
+ int rcode = (ld || lz_type == local_zone_redirect)?
+ LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
if(z->soa)
- return local_encode(qinfo, edns, buf, temp,
+ return local_encode(qinfo, env, edns, buf, temp,
z->soa, 0, rcode);
- error_encode(buf, (rcode|BIT_AA), qinfo,
- *(uint16_t*)sldns_buffer_begin(buf),
- sldns_buffer_read_u16_at(buf, 2), edns);
+ local_error_encode(qinfo, env, edns, buf, temp, rcode,
+ (rcode|BIT_AA));
return 1;
- } else if(z->type == local_zone_typetransparent) {
+ } else if(lz_type == local_zone_typetransparent
+ || lz_type == local_zone_always_transparent) {
/* no NODATA or NXDOMAINS for this zone type */
return 0;
}
- /* else z->type == local_zone_transparent */
+ /* else lz_type == local_zone_transparent */
/* if the zone is transparent and the name exists, but the type
* does not, then we should make this noerror/nodata */
if(ld && ld->rrsets) {
int rcode = LDNS_RCODE_NOERROR;
if(z->soa)
- return local_encode(qinfo, edns, buf, temp,
+ return local_encode(qinfo, env, edns, buf, temp,
z->soa, 0, rcode);
- error_encode(buf, (rcode|BIT_AA), qinfo,
- *(uint16_t*)sldns_buffer_begin(buf),
- sldns_buffer_read_u16_at(buf, 2), edns);
+ local_error_encode(qinfo, env, edns, buf, temp, rcode,
+ (rcode|BIT_AA));
return 1;
}
@@ -1193,44 +1500,136 @@ lz_inform_print(struct local_zone* z, struct query_info* qinfo,
log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
}
+static enum localzone_type
+lz_type(uint8_t *taglist, size_t taglen, uint8_t *taglist2, size_t taglen2,
+ uint8_t *tagactions, size_t tagactionssize, enum localzone_type lzt,
+ struct comm_reply* repinfo, struct rbtree_type* override_tree,
+ int* tag, char** tagname, int num_tags)
+{
+ struct local_zone_override* lzo;
+ if(repinfo && override_tree) {
+ lzo = (struct local_zone_override*)addr_tree_lookup(
+ override_tree, &repinfo->addr, repinfo->addrlen);
+ if(lzo && lzo->type) {
+ verbose(VERB_ALGO, "local zone override to type %s",
+ local_zone_type2str(lzo->type));
+ return lzo->type;
+ }
+ }
+ if(!taglist || !taglist2)
+ return lzt;
+ return local_data_find_tag_action(taglist, taglen, taglist2, taglen2,
+ tagactions, tagactionssize, lzt, tag, tagname, num_tags);
+}
+
+enum localzone_type
+local_data_find_tag_action(const uint8_t* taglist, size_t taglen,
+ const uint8_t* taglist2, size_t taglen2, const uint8_t* tagactions,
+ size_t tagactionssize, enum localzone_type lzt, int* tag,
+ char* const* tagname, int num_tags)
+{
+ size_t i, j;
+ uint8_t tagmatch;
+
+ for(i=0; i<taglen && i<taglen2; i++) {
+ tagmatch = (taglist[i] & taglist2[i]);
+ for(j=0; j<8 && tagmatch>0; j++) {
+ if((tagmatch & 0x1)) {
+ *tag = (int)(i*8+j);
+ verbose(VERB_ALGO, "matched tag [%d] %s",
+ *tag, (*tag<num_tags?tagname[*tag]:"null"));
+ /* does this tag have a tag action? */
+ if(i*8+j < tagactionssize && tagactions
+ && tagactions[i*8+j] != 0) {
+ verbose(VERB_ALGO, "tag action [%d] %s to type %s",
+ *tag, (*tag<num_tags?tagname[*tag]:"null"),
+ local_zone_type2str(
+ (enum localzone_type)
+ tagactions[i*8+j]));
+ return (enum localzone_type)tagactions[i*8+j];
+ }
+ return lzt;
+ }
+ tagmatch >>= 1;
+ }
+ }
+ return lzt;
+}
+
int
-local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
- struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
- struct comm_reply* repinfo)
+local_zones_answer(struct local_zones* zones, struct module_env* env,
+ struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
+ struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist,
+ size_t taglen, uint8_t* tagactions, size_t tagactionssize,
+ struct config_strlist** tag_datas, size_t tag_datas_size,
+ char** tagname, int num_tags, struct view* view)
{
/* see if query is covered by a zone,
* if so: - try to match (exact) local data
* - look at zone type for negative response. */
int labs = dname_count_labels(qinfo->qname);
- struct local_data* ld;
- struct local_zone* z;
- int r;
- lock_rw_rdlock(&zones->lock);
- z = local_zones_lookup(zones, qinfo->qname,
- qinfo->qname_len, labs, qinfo->qclass);
+ struct local_data* ld = NULL;
+ struct local_zone* z = NULL;
+ enum localzone_type lzt = local_zone_transparent;
+ int r, tag = -1;
+
+ if(view) {
+ lock_rw_rdlock(&view->lock);
+ if(view->local_zones &&
+ (z = local_zones_lookup(view->local_zones,
+ qinfo->qname, qinfo->qname_len, labs,
+ qinfo->qclass))) {
+ verbose(VERB_ALGO,
+ "using localzone from view: %s",
+ view->name);
+ lock_rw_rdlock(&z->lock);
+ lzt = z->type;
+ }
+ if(!z && !view->isfirst){
+ lock_rw_unlock(&view->lock);
+ return 0;
+ }
+ lock_rw_unlock(&view->lock);
+ }
if(!z) {
+ /* try global local_zones tree */
+ lock_rw_rdlock(&zones->lock);
+ if(!(z = local_zones_tags_lookup(zones, qinfo->qname,
+ qinfo->qname_len, labs, qinfo->qclass, taglist,
+ taglen, 0))) {
+ lock_rw_unlock(&zones->lock);
+ return 0;
+ }
+ lock_rw_rdlock(&z->lock);
+
+ lzt = lz_type(taglist, taglen, z->taglist, z->taglen,
+ tagactions, tagactionssize, z->type, repinfo,
+ z->override_tree, &tag, tagname, num_tags);
lock_rw_unlock(&zones->lock);
- return 0;
}
- lock_rw_rdlock(&z->lock);
- lock_rw_unlock(&zones->lock);
-
- if((z->type == local_zone_inform || z->type == local_zone_inform_deny)
+ if((lzt == local_zone_inform || lzt == local_zone_inform_deny)
&& repinfo)
lz_inform_print(z, qinfo, repinfo);
- if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld)) {
+ if(lzt != local_zone_always_refuse
+ && lzt != local_zone_always_transparent
+ && lzt != local_zone_always_nxdomain
+ && local_data_answer(z, env, qinfo, edns, buf, temp, labs, &ld, lzt,
+ tag, tag_datas, tag_datas_size, tagname, num_tags)) {
lock_rw_unlock(&z->lock);
- return 1;
+ /* We should tell the caller that encode is deferred if we found
+ * a local alias. */
+ return !qinfo->local_alias;
}
- r = lz_zone_answer(z, qinfo, edns, buf, temp, ld);
+ r = lz_zone_answer(z, env, qinfo, edns, buf, temp, ld, lzt);
lock_rw_unlock(&z->lock);
- return r;
+ return r && !qinfo->local_alias; /* see above */
}
const char* local_zone_type2str(enum localzone_type t)
{
switch(t) {
+ case local_zone_unset: return "unset";
case local_zone_deny: return "deny";
case local_zone_refuse: return "refuse";
case local_zone_redirect: return "redirect";
@@ -1240,6 +1639,9 @@ const char* local_zone_type2str(enum localzone_type t)
case local_zone_nodefault: return "nodefault";
case local_zone_inform: return "inform";
case local_zone_inform_deny: return "inform_deny";
+ case local_zone_always_transparent: return "always_transparent";
+ case local_zone_always_refuse: return "always_refuse";
+ case local_zone_always_nxdomain: return "always_nxdomain";
}
return "badtyped";
}
@@ -1262,6 +1664,12 @@ int local_zone_str2type(const char* type, enum localzone_type* t)
*t = local_zone_inform;
else if(strcmp(type, "inform_deny") == 0)
*t = local_zone_inform_deny;
+ else if(strcmp(type, "always_transparent") == 0)
+ *t = local_zone_always_transparent;
+ else if(strcmp(type, "always_refuse") == 0)
+ *t = local_zone_always_refuse;
+ else if(strcmp(type, "always_nxdomain") == 0)
+ *t = local_zone_always_nxdomain;
else return 0;
return 1;
}
@@ -1298,7 +1706,10 @@ struct local_zone* local_zones_add_zone(struct local_zones* zones,
{
/* create */
struct local_zone* z = local_zone_create(name, len, labs, tp, dclass);
- if(!z) return NULL;
+ if(!z) {
+ free(name);
+ return NULL;
+ }
lock_rw_wrlock(&z->lock);
/* find the closest parent */
diff --git a/external/unbound/services/localzone.h b/external/unbound/services/localzone.h
index 3d62a69d1..658f28024 100644
--- a/external/unbound/services/localzone.h
+++ b/external/unbound/services/localzone.h
@@ -43,6 +43,10 @@
#define SERVICES_LOCALZONE_H
#include "util/rbtree.h"
#include "util/locks.h"
+#include "util/storage/dnstree.h"
+#include "util/module.h"
+#include "services/view.h"
+struct packed_rrset_data;
struct ub_packed_rrset_key;
struct regional;
struct config_file;
@@ -50,6 +54,7 @@ struct edns_data;
struct query_info;
struct sldns_buffer;
struct comm_reply;
+struct config_strlist;
/**
* Local zone type
@@ -57,8 +62,10 @@ struct comm_reply;
* local-data directly.
*/
enum localzone_type {
+ /** unset type, used for unset tag_action elements */
+ local_zone_unset = 0,
/** drop query */
- local_zone_deny = 0,
+ local_zone_deny,
/** answer with error */
local_zone_refuse,
/** answer nxdomain or nodata */
@@ -75,7 +82,13 @@ enum localzone_type {
/** log client address, but no block (transparent) */
local_zone_inform,
/** log client address, and block (drop) */
- local_zone_inform_deny
+ local_zone_inform_deny,
+ /** resolve normally, even when there is local data */
+ local_zone_always_transparent,
+ /** answer with error, even when there is local data */
+ local_zone_always_refuse,
+ /** answer with nxdomain, even when there is local data */
+ local_zone_always_nxdomain
};
/**
@@ -83,9 +96,9 @@ enum localzone_type {
*/
struct local_zones {
/** lock on the localzone tree */
- lock_rw_t lock;
+ lock_rw_type lock;
/** rbtree of struct local_zone */
- rbtree_t ztree;
+ rbtree_type ztree;
};
/**
@@ -93,7 +106,7 @@ struct local_zones {
*/
struct local_zone {
/** rbtree node, key is name and class */
- rbnode_t node;
+ rbnode_type node;
/** parent zone, if any. */
struct local_zone* parent;
@@ -111,17 +124,24 @@ struct local_zone {
* For the node, parent, name, namelen, namelabs, dclass, you
* need to also hold the zones_tree lock to change them (or to
* delete this zone) */
- lock_rw_t lock;
+ lock_rw_type lock;
/** how to process zone */
enum localzone_type type;
+ /** tag bitlist */
+ uint8_t* taglist;
+ /** length of the taglist (in bytes) */
+ size_t taglen;
+ /** netblock addr_tree with struct local_zone_override information
+ * or NULL if there are no override elements */
+ struct rbtree_type* override_tree;
/** in this region the zone's data is allocated.
* the struct local_zone itself is malloced. */
struct regional* region;
/** local data for this zone
* rbtree of struct local_data */
- rbtree_t data;
+ rbtree_type data;
/** if data contains zone apex SOA data, this is a ptr to it. */
struct ub_packed_rrset_key* soa;
};
@@ -131,7 +151,7 @@ struct local_zone {
*/
struct local_data {
/** rbtree node, key is name only */
- rbnode_t node;
+ rbnode_type node;
/** domain name */
uint8_t* name;
/** length of name */
@@ -154,6 +174,16 @@ struct local_rrset {
};
/**
+ * Local zone override information
+ */
+struct local_zone_override {
+ /** node in addrtree */
+ struct addr_tree_node node;
+ /** override for local zone type */
+ enum localzone_type type;
+};
+
+/**
* Create local zones storage
* @return new struct or NULL on error.
*/
@@ -198,6 +228,24 @@ int local_data_cmp(const void* d1, const void* d2);
void local_zone_delete(struct local_zone* z);
/**
+ * Lookup zone that contains the given name, class and taglist.
+ * User must lock the tree or result zone.
+ * @param zones: the zones tree
+ * @param name: dname to lookup
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to lookup.
+ * @param taglist: taglist to lookup.
+ * @param taglen: lenth of taglist.
+ * @param ignoretags: lookup zone by name and class, regardless the
+ * local-zone's tags.
+ * @return closest local_zone or NULL if no covering zone is found.
+ */
+struct local_zone* local_zones_tags_lookup(struct local_zones* zones,
+ uint8_t* name, size_t len, int labs, uint16_t dclass,
+ uint8_t* taglist, size_t taglen, int ignoretags);
+
+/**
* Lookup zone that contains the given name, class.
* User must lock the tree or result zone.
* @param zones: the zones tree
@@ -221,18 +269,39 @@ void local_zones_print(struct local_zones* zones);
* Answer authoritatively for local zones.
* Takes care of locking.
* @param zones: the stored zones (shared, read only).
+ * @param env: the module environment.
* @param qinfo: query info (parsed).
* @param edns: edns info (parsed).
* @param buf: buffer with query ID and flags, also for reply.
* @param temp: temporary storage region.
* @param repinfo: source address for checks. may be NULL.
+ * @param taglist: taglist for checks. May be NULL.
+ * @param taglen: length of the taglist.
+ * @param tagactions: local zone actions for tags. May be NULL.
+ * @param tagactionssize: length of the tagactions.
+ * @param tag_datas: array per tag of strlist with rdata strings. or NULL.
+ * @param tag_datas_size: size of tag_datas array.
+ * @param tagname: array of tag name strings (for debug output).
+ * @param num_tags: number of items in tagname array.
+ * @param view: answer using this view. May be NULL.
* @return true if answer is in buffer. false if query is not answered
* by authority data. If the reply should be dropped altogether, the return
* value is true, but the buffer is cleared (empty).
+ * It can also return true if a non-exact alias answer is found. In this
+ * case qinfo->local_alias points to the corresponding alias RRset but the
+ * answer is NOT encoded in buffer. It's the caller's responsibility to
+ * complete the alias chain (if needed) and encode the final set of answer.
+ * Data pointed to by qinfo->local_alias is allocated in 'temp' or refers to
+ * configuration data. So the caller will need to make a deep copy of it
+ * if it needs to keep it beyond the lifetime of 'temp' or a dynamic update
+ * to local zone data.
*/
-int local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
- struct edns_data* edns, struct sldns_buffer* buf, struct regional* temp,
- struct comm_reply* repinfo);
+int local_zones_answer(struct local_zones* zones, struct module_env* env,
+ struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf,
+ struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist,
+ size_t taglen, uint8_t* tagactions, size_t tagactionssize,
+ struct config_strlist** tag_datas, size_t tag_datas_size,
+ char** tagname, int num_tags, struct view* view);
/**
* Parse the string into localzone type.
@@ -321,4 +390,111 @@ void local_zones_del_data(struct local_zones* zones,
*/
int parse_dname(const char* str, uint8_t** res, size_t* len, int* labs);
+/**
+ * Find local data tag string match for the given type (in qinfo) in the list.
+ * If found, 'r' will be filled with corresponding rrset information.
+ * @param qinfo: contains name, type, and class for the data
+ * @param list: stores local tag data to be searched
+ * @param r: rrset key to be filled for matched data
+ * @param temp: region to allocate rrset in 'r'
+ * @return 1 if a match is found and rrset is built; otherwise 0 including
+ * errors.
+ */
+int local_data_find_tag_datas(const struct query_info* qinfo,
+ struct config_strlist* list, struct ub_packed_rrset_key* r,
+ struct regional* temp);
+
+/**
+ * See if two sets of tag lists (in the form of bitmap) have the same tag that
+ * has an action. If so, '*tag' will be set to the found tag index, and the
+ * corresponding action will be returned in the form of local zone type.
+ * Otherwise the passed type (lzt) will be returned as the default action.
+ * Pointers except tagactions must not be NULL.
+ * @param taglist: 1st list of tags
+ * @param taglen: size of taglist in bytes
+ * @param taglist2: 2nd list of tags
+ * @param taglen2: size of taglist2 in bytes
+ * @param tagactions: local data actions for tags. May be NULL.
+ * @param tagactionssize: length of the tagactions.
+ * @param lzt: default action (local zone type) if no tag action is found.
+ * @param tag: see above.
+ * @param tagname: array of tag name strings (for debug output).
+ * @param num_tags: number of items in tagname array.
+ * @return found tag action or the default action.
+ */
+enum localzone_type local_data_find_tag_action(const uint8_t* taglist,
+ size_t taglen, const uint8_t* taglist2, size_t taglen2,
+ const uint8_t* tagactions, size_t tagactionssize,
+ enum localzone_type lzt, int* tag, char* const* tagname, int num_tags);
+
+/**
+ * Parses resource record string into wire format, also returning its field values.
+ * @param str: input resource record
+ * @param nm: domain name field
+ * @param type: record type field
+ * @param dclass: record class field
+ * @param ttl: ttl field
+ * @param rr: buffer for the parsed rr in wire format
+ * @param len: buffer length
+ * @param rdata: rdata field
+ * @param rdata_len: rdata field length
+ * @return 1 on success; 0 otherwise.
+ */
+int rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
+ uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len,
+ uint8_t** rdata, size_t* rdata_len);
+
+/**
+ * Insert specified rdata into the specified resource record.
+ * @param region: allocator
+ * @param pd: data portion of the destination resource record
+ * @param rdata: source rdata
+ * @param rdata_len: source rdata length
+ * @param ttl: time to live
+ * @param rrstr: resource record in text form (for logging)
+ * @return 1 on success; 0 otherwise.
+ */
+int rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd,
+ uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr);
+
+/**
+ * Valid response ip actions for the IP-response-driven-action feature;
+ * defined here instead of in the respip module to enable sharing of enum
+ * values with the localzone_type enum.
+ * Note that these values except 'none' are the same as localzone types of
+ * the 'same semantics'. It's intentional as we use these values via
+ * access-control-tags, which can be shared for both response ip actions and
+ * local zones.
+ */
+enum respip_action {
+ /** no respip action */
+ respip_none = local_zone_unset,
+ /** don't answer */
+ respip_deny = local_zone_deny,
+ /** redirect as per provided data */
+ respip_redirect = local_zone_redirect,
+ /** log query source and answer query */
+ respip_inform = local_zone_inform,
+ /** log query source and don't answer query */
+ respip_inform_deny = local_zone_inform_deny,
+ /** resolve normally, even when there is response-ip data */
+ respip_always_transparent = local_zone_always_transparent,
+ /** answer with 'refused' response */
+ respip_always_refuse = local_zone_always_refuse,
+ /** answer with 'no such domain' response */
+ respip_always_nxdomain = local_zone_always_nxdomain,
+
+ /* The rest of the values are only possible as
+ * access-control-tag-action */
+
+ /** serves response data (if any), else, drops queries. */
+ respip_refuse = local_zone_refuse,
+ /** serves response data, else, nodata answer. */
+ respip_static = local_zone_static,
+ /** gives response data (if any), else nodata answer. */
+ respip_transparent = local_zone_transparent,
+ /** gives response data (if any), else nodata answer. */
+ respip_typetransparent = local_zone_typetransparent,
+};
+
#endif /* SERVICES_LOCALZONE_H */
diff --git a/external/unbound/services/mesh.c b/external/unbound/services/mesh.c
index 8076874ae..0cb134ade 100644
--- a/external/unbound/services/mesh.c
+++ b/external/unbound/services/mesh.c
@@ -56,6 +56,10 @@
#include "util/alloc.h"
#include "util/config_file.h"
#include "sldns/sbuffer.h"
+#include "sldns/wire2str.h"
+#include "services/localzone.h"
+#include "util/data/dname.h"
+#include "respip/respip.h"
/** subtract timers and the values do not overflow or become negative */
static void
@@ -121,11 +125,69 @@ timeval_smaller(const struct timeval* x, const struct timeval* y)
#endif
}
+/*
+ * Compare two response-ip client info entries for the purpose of mesh state
+ * compare. It returns 0 if ci_a and ci_b are considered equal; otherwise
+ * 1 or -1 (they mean 'ci_a is larger/smaller than ci_b', respectively, but
+ * in practice it should be only used to mean they are different).
+ * We cannot share the mesh state for two queries if different response-ip
+ * actions can apply in the end, even if those queries are otherwise identical.
+ * For this purpose we compare tag lists and tag action lists; they should be
+ * identical to share the same state.
+ * For tag data, we don't look into the data content, as it can be
+ * expensive; unless tag data are not defined for both or they point to the
+ * exact same data in memory (i.e., they come from the same ACL entry), we
+ * consider these data different.
+ * Likewise, if the client info is associated with views, we don't look into
+ * the views. They are considered different unless they are exactly the same
+ * even if the views only differ in the names.
+ */
+static int
+client_info_compare(const struct respip_client_info* ci_a,
+ const struct respip_client_info* ci_b)
+{
+ int cmp;
+
+ if(!ci_a && !ci_b)
+ return 0;
+ if(ci_a && !ci_b)
+ return -1;
+ if(!ci_a && ci_b)
+ return 1;
+ if(ci_a->taglen != ci_b->taglen)
+ return (ci_a->taglen < ci_b->taglen) ? -1 : 1;
+ cmp = memcmp(ci_a->taglist, ci_b->taglist, ci_a->taglen);
+ if(cmp != 0)
+ return cmp;
+ if(ci_a->tag_actions_size != ci_b->tag_actions_size)
+ return (ci_a->tag_actions_size < ci_b->tag_actions_size) ?
+ -1 : 1;
+ cmp = memcmp(ci_a->tag_actions, ci_b->tag_actions,
+ ci_a->tag_actions_size);
+ if(cmp != 0)
+ return cmp;
+ if(ci_a->tag_datas != ci_b->tag_datas)
+ return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1;
+ if(ci_a->view != ci_b->view)
+ return ci_a->view < ci_b->view ? -1 : 1;
+ /* For the unbound daemon these should be non-NULL and identical,
+ * but we check that just in case. */
+ if(ci_a->respip_set != ci_b->respip_set)
+ return ci_a->respip_set < ci_b->respip_set ? -1 : 1;
+ return 0;
+}
+
int
mesh_state_compare(const void* ap, const void* bp)
{
struct mesh_state* a = (struct mesh_state*)ap;
struct mesh_state* b = (struct mesh_state*)bp;
+ int cmp;
+
+ if(a->unique < b->unique)
+ return -1;
+ if(a->unique > b->unique)
+ return 1;
if(a->s.is_priming && !b->s.is_priming)
return -1;
@@ -147,7 +209,10 @@ mesh_state_compare(const void* ap, const void* bp)
if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD))
return 1;
- return query_info_compare(&a->s.qinfo, &b->s.qinfo);
+ cmp = query_info_compare(&a->s.qinfo, &b->s.qinfo);
+ if(cmp != 0)
+ return cmp;
+ return client_info_compare(a->s.client_info, b->s.client_info);
}
int
@@ -195,7 +260,7 @@ mesh_create(struct module_stack* stack, struct module_env* env)
/** help mesh delete delete mesh states */
static void
-mesh_delete_helper(rbnode_t* n)
+mesh_delete_helper(rbnode_type* n)
{
struct mesh_state* mstate = (struct mesh_state*)n->key;
/* perform a full delete, not only 'cleanup' routine,
@@ -279,13 +344,16 @@ int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf)
}
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
- uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
- uint16_t qid)
+ struct respip_client_info* cinfo, uint16_t qflags,
+ struct edns_data* edns, struct comm_reply* rep, uint16_t qid)
{
- struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+ struct mesh_state* s = NULL;
+ int unique = unique_mesh_state(edns->opt_list, mesh->env);
int was_detached = 0;
int was_noreply = 0;
int added = 0;
+ if(!unique)
+ s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
/* does this create a new reply state? */
if(!s || s->list_select == mesh_no_list) {
if(!mesh_make_new_space(mesh, rep->c->buffer)) {
@@ -310,16 +378,38 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
/* see if it already exists, if not, create one */
if(!s) {
#ifdef UNBOUND_DEBUG
- struct rbnode_t* n;
+ struct rbnode_type* n;
#endif
- s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+ s = mesh_state_create(mesh->env, qinfo, cinfo,
+ qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) {
log_err("mesh_state_create: out of memory; SERVFAIL");
+ if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL,
+ LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch))
+ edns->opt_list = NULL;
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
return;
}
+ if(unique)
+ mesh_state_make_unique(s);
+ /* copy the edns options we got from the front */
+ if(edns->opt_list) {
+ s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list,
+ s->s.region);
+ if(!s->s.edns_opts_front_in) {
+ log_err("mesh_state_create: out of memory; SERVFAIL");
+ if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL,
+ NULL, LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch))
+ edns->opt_list = NULL;
+ error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
+ qinfo, qid, qflags, edns);
+ comm_point_send_reply(rep);
+ return;
+ }
+ }
+
#ifdef UNBOUND_DEBUG
n =
#else
@@ -336,8 +426,11 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
if(!s->reply_list && !s->cb_list)
was_noreply = 1;
/* add reply to s */
- if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) {
+ if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo)) {
log_err("mesh_new_client: out of memory; SERVFAIL");
+ if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s,
+ NULL, LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch))
+ edns->opt_list = NULL;
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
@@ -374,23 +467,37 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
int
mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
- uint16_t qid, mesh_cb_func_t cb, void* cb_arg)
+ uint16_t qid, mesh_cb_func_type cb, void* cb_arg)
{
- struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+ struct mesh_state* s = NULL;
+ int unique = unique_mesh_state(edns->opt_list, mesh->env);
int was_detached = 0;
int was_noreply = 0;
int added = 0;
+ if(!unique)
+ s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+
/* there are no limits on the number of callbacks */
/* see if it already exists, if not, create one */
if(!s) {
#ifdef UNBOUND_DEBUG
- struct rbnode_t* n;
+ struct rbnode_type* n;
#endif
- s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+ s = mesh_state_create(mesh->env, qinfo, NULL,
+ qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) {
return 0;
}
+ if(unique)
+ mesh_state_make_unique(s);
+ if(edns->opt_list) {
+ s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list,
+ s->s.region);
+ if(!s->s.edns_opts_front_in) {
+ return 0;
+ }
+ }
#ifdef UNBOUND_DEBUG
n =
#else
@@ -429,9 +536,10 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway)
{
- struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+ struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo,
+ qflags&(BIT_RD|BIT_CD), 0, 0);
#ifdef UNBOUND_DEBUG
- struct rbnode_t* n;
+ struct rbnode_type* n;
#endif
/* already exists, and for a different purpose perhaps.
* if mesh_no_list, keep it that way. */
@@ -448,7 +556,9 @@ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
mesh->stats_dropped ++;
return;
}
- s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+
+ s = mesh_state_create(mesh->env, qinfo, NULL,
+ qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) {
log_err("prefetch mesh_state_create: out of memory");
return;
@@ -497,7 +607,8 @@ void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
struct mesh_state*
mesh_state_create(struct module_env* env, struct query_info* qinfo,
- uint16_t qflags, int prime, int valrec)
+ struct respip_client_info* cinfo, uint16_t qflags, int prime,
+ int valrec)
{
struct regional* region = alloc_reg_obtain(env->alloc);
struct mesh_state* mstate;
@@ -521,9 +632,11 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
mstate->num_activated = 0;
+ mstate->unique = NULL;
/* init module qstate */
mstate->s.qinfo.qtype = qinfo->qtype;
mstate->s.qinfo.qclass = qinfo->qclass;
+ mstate->s.qinfo.local_alias = NULL;
mstate->s.qinfo.qname_len = qinfo->qname_len;
mstate->s.qinfo.qname = regional_alloc_init(region, qinfo->qname,
qinfo->qname_len);
@@ -531,6 +644,14 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
alloc_reg_release(env->alloc, region);
return NULL;
}
+ if(cinfo) {
+ mstate->s.client_info = regional_alloc_init(region, cinfo,
+ sizeof(*cinfo));
+ if(!mstate->s.client_info) {
+ alloc_reg_release(env->alloc, region);
+ return NULL;
+ }
+ }
/* remove all weird bits from qflags */
mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD));
mstate->s.is_priming = prime;
@@ -543,14 +664,34 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
mstate->s.env = env;
mstate->s.mesh_info = mstate;
mstate->s.prefetch_leeway = 0;
+ mstate->s.no_cache_lookup = 0;
+ mstate->s.no_cache_store = 0;
/* init modules */
for(i=0; i<env->mesh->mods.num; i++) {
mstate->s.minfo[i] = NULL;
mstate->s.ext_state[i] = module_state_initial;
}
+ /* init edns option lists */
+ mstate->s.edns_opts_front_in = NULL;
+ mstate->s.edns_opts_back_out = NULL;
+ mstate->s.edns_opts_back_in = NULL;
+ mstate->s.edns_opts_front_out = NULL;
+
return mstate;
}
+int
+mesh_state_is_unique(struct mesh_state* mstate)
+{
+ return mstate->unique != NULL;
+}
+
+void
+mesh_state_make_unique(struct mesh_state* mstate)
+{
+ mstate->unique = mstate;
+}
+
void
mesh_state_cleanup(struct mesh_state* mstate)
{
@@ -658,7 +799,7 @@ void mesh_detach_subs(struct module_qstate* qstate)
struct mesh_area* mesh = qstate->env->mesh;
struct mesh_state_ref* ref, lookup;
#ifdef UNBOUND_DEBUG
- struct rbnode_t* n;
+ struct rbnode_type* n;
#endif
lookup.node.key = &lookup;
lookup.s = qstate->mesh_info;
@@ -685,8 +826,8 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
{
/* find it, if not, create it */
struct mesh_area* mesh = qstate->env->mesh;
- struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime,
- valrec);
+ struct mesh_state* sub = mesh_area_find(mesh, NULL, qinfo, qflags,
+ prime, valrec);
int was_detached;
if(mesh_detect_cycle_found(qstate, sub)) {
verbose(VERB_ALGO, "attach failed, cycle detected");
@@ -694,10 +835,10 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
}
if(!sub) {
#ifdef UNBOUND_DEBUG
- struct rbnode_t* n;
+ struct rbnode_type* n;
#endif
/* create a new one */
- sub = mesh_state_create(qstate->env, qinfo, qflags, prime,
+ sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime,
valrec);
if(!sub) {
log_err("mesh_attach_sub: out of memory");
@@ -740,7 +881,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub)
{
#ifdef UNBOUND_DEBUG
- struct rbnode_t* n;
+ struct rbnode_type* n;
#endif
struct mesh_state_ref* subref; /* points to sub, inserted in super */
struct mesh_state_ref* superref; /* points to super, inserted in sub */
@@ -800,6 +941,15 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
}
/* send the reply */
if(rcode) {
+ if(rcode == LDNS_RCODE_SERVFAIL) {
+ if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
+ rep, rcode, &r->edns, m->s.region))
+ r->edns.opt_list = NULL;
+ } else {
+ if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
+ &r->edns, m->s.region))
+ r->edns.opt_list = NULL;
+ }
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL);
} else {
@@ -809,7 +959,10 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO;
- if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+
+ if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
+ LDNS_RCODE_NOERROR, &r->edns, m->s.region) ||
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r->buf, 0, 1,
m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure))
@@ -842,6 +995,9 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
struct timeval end_time;
struct timeval duration;
int secure;
+ /* Copy the client's EDNS for later restore, to make sure the edns
+ * compare is with the correct edns options. */
+ struct edns_data edns_bak = r->edns;
/* examine security status */
if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
m->s.env->cfg->ignore_cd) && rep &&
@@ -856,10 +1012,18 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
/* send the reply */
+ /* We don't reuse the encoded answer if either the previous or current
+ * response has a local alias. We could compare the alias records
+ * and still reuse the previous answer if they are the same, but that
+ * would be complicated and error prone for the relatively minor case.
+ * So we err on the side of safety. */
if(prev && prev->qflags == r->qflags &&
+ !prev->local_alias && !r->local_alias &&
prev->edns.edns_present == r->edns.edns_present &&
prev->edns.bits == r->edns.bits &&
- prev->edns.udp_size == r->edns.udp_size) {
+ prev->edns.udp_size == r->edns.udp_size &&
+ edns_opt_list_compare(prev->edns.opt_list, r->edns.opt_list)
+ == 0) {
/* if the previous reply is identical to this one, fix ID */
if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
sldns_buffer_copy(r->query_reply.c->buffer,
@@ -871,6 +1035,16 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
comm_point_send_reply(&r->query_reply);
} else if(rcode) {
m->s.qinfo.qname = r->qname;
+ m->s.qinfo.local_alias = r->local_alias;
+ if(rcode == LDNS_RCODE_SERVFAIL) {
+ if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
+ rep, rcode, &r->edns, m->s.region))
+ r->edns.opt_list = NULL;
+ } else {
+ if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
+ &r->edns, m->s.region))
+ r->edns.opt_list = NULL;
+ }
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
r->qid, r->qflags, &r->edns);
comm_point_send_reply(&r->query_reply);
@@ -881,15 +1055,22 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO;
m->s.qinfo.qname = r->qname;
- if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+ m->s.qinfo.local_alias = r->local_alias;
+ if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
+ LDNS_RCODE_NOERROR, &r->edns, m->s.region) ||
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r->query_reply.c->buffer, 0, 1,
m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure))
{
+ if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
+ rep, LDNS_RCODE_SERVFAIL, &r->edns, m->s.region))
+ r->edns.opt_list = NULL;
error_encode(r->query_reply.c->buffer,
LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
}
+ r->edns = edns_bak;
comm_point_send_reply(&r->query_reply);
}
/* account */
@@ -910,6 +1091,12 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
query_reply.c->buffer)) == 0)
m->s.env->mesh->ans_nodata++;
}
+ /* Log reply sent */
+ if(m->s.env->cfg->log_replies) {
+ log_reply_info(0, &m->s.qinfo, &r->query_reply.addr,
+ r->query_reply.addrlen, duration, 0,
+ r->query_reply.c->buffer);
+ }
}
void mesh_query_done(struct mesh_state* mstate)
@@ -920,8 +1107,25 @@ void mesh_query_done(struct mesh_state* mstate)
struct reply_info* rep = (mstate->s.return_msg?
mstate->s.return_msg->rep:NULL);
for(r = mstate->reply_list; r; r = r->next) {
- mesh_send_reply(mstate, mstate->s.return_rcode, rep, r, prev);
- prev = r;
+ /* if a response-ip address block has been stored the
+ * information should be logged for each client. */
+ if(mstate->s.respip_action_info &&
+ mstate->s.respip_action_info->addrinfo) {
+ respip_inform_print(mstate->s.respip_action_info->addrinfo,
+ r->qname, mstate->s.qinfo.qtype,
+ mstate->s.qinfo.qclass, r->local_alias,
+ &r->query_reply);
+ }
+
+ /* if this query is determined to be dropped during the
+ * mesh processing, this is the point to take that action. */
+ if(mstate->s.is_drop)
+ comm_point_drop_reply(&r->query_reply);
+ else {
+ mesh_send_reply(mstate, mstate->s.return_rcode, rep,
+ r, prev);
+ prev = r;
+ }
}
mstate->replies_sent = 1;
for(c = mstate->cb_list; c; c = c->next) {
@@ -945,7 +1149,8 @@ void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
}
struct mesh_state* mesh_area_find(struct mesh_area* mesh,
- struct query_info* qinfo, uint16_t qflags, int prime, int valrec)
+ struct respip_client_info* cinfo, struct query_info* qinfo,
+ uint16_t qflags, int prime, int valrec)
{
struct mesh_state key;
struct mesh_state* result;
@@ -955,13 +1160,18 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
key.s.is_valrec = valrec;
key.s.qinfo = *qinfo;
key.s.query_flags = qflags;
+ /* We are searching for a similar mesh state when we DO want to
+ * aggregate the state. Thus unique is set to NULL. (default when we
+ * desire aggregation).*/
+ key.unique = NULL;
+ key.s.client_info = cinfo;
result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
return result;
}
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
- sldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg,
+ sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
uint16_t qid, uint16_t qflags)
{
struct mesh_cb* r = regional_alloc(s->s.region,
@@ -973,6 +1183,12 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
r->cb = cb;
r->cb_arg = cb_arg;
r->edns = *edns;
+ if(edns->opt_list) {
+ r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
+ s->s.region);
+ if(!r->edns.opt_list)
+ return 0;
+ }
r->qid = qid;
r->qflags = qflags;
r->next = s->cb_list;
@@ -982,7 +1198,8 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
}
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
- struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname)
+ struct comm_reply* rep, uint16_t qid, uint16_t qflags,
+ const struct query_info* qinfo)
{
struct mesh_reply* r = regional_alloc(s->s.region,
sizeof(struct mesh_reply));
@@ -990,17 +1207,74 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
return 0;
r->query_reply = *rep;
r->edns = *edns;
+ if(edns->opt_list) {
+ r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
+ s->s.region);
+ if(!r->edns.opt_list)
+ return 0;
+ }
r->qid = qid;
r->qflags = qflags;
r->start_time = *s->s.env->now_tv;
r->next = s->reply_list;
- r->qname = regional_alloc_init(s->s.region, qname,
+ r->qname = regional_alloc_init(s->s.region, qinfo->qname,
s->s.qinfo.qname_len);
if(!r->qname)
return 0;
+
+ /* Data related to local alias stored in 'qinfo' (if any) is ephemeral
+ * and can be different for different original queries (even if the
+ * replaced query name is the same). So we need to make a deep copy
+ * and store the copy for each reply info. */
+ if(qinfo->local_alias) {
+ struct packed_rrset_data* d;
+ struct packed_rrset_data* dsrc;
+ r->local_alias = regional_alloc_zero(s->s.region,
+ sizeof(*qinfo->local_alias));
+ if(!r->local_alias)
+ return 0;
+ r->local_alias->rrset = regional_alloc_init(s->s.region,
+ qinfo->local_alias->rrset,
+ sizeof(*qinfo->local_alias->rrset));
+ if(!r->local_alias->rrset)
+ return 0;
+ dsrc = qinfo->local_alias->rrset->entry.data;
+
+ /* In the current implementation, a local alias must be
+ * a single CNAME RR (see worker_handle_request()). */
+ log_assert(!qinfo->local_alias->next && dsrc->count == 1 &&
+ qinfo->local_alias->rrset->rk.type ==
+ htons(LDNS_RR_TYPE_CNAME));
+ /* Technically, we should make a local copy for the owner
+ * name of the RRset, but in the case of the first (and
+ * currently only) local alias RRset, the owner name should
+ * point to the qname of the corresponding query, which should
+ * be valid throughout the lifetime of this mesh_reply. So
+ * we can skip copying. */
+ log_assert(qinfo->local_alias->rrset->rk.dname ==
+ sldns_buffer_at(rep->c->buffer, LDNS_HEADER_SIZE));
+
+ d = regional_alloc_init(s->s.region, dsrc,
+ sizeof(struct packed_rrset_data)
+ + sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t));
+ if(!d)
+ return 0;
+ r->local_alias->rrset->entry.data = d;
+ d->rr_len = (size_t*)((uint8_t*)d +
+ sizeof(struct packed_rrset_data));
+ d->rr_data = (uint8_t**)&(d->rr_len[1]);
+ d->rr_ttl = (time_t*)&(d->rr_data[1]);
+ d->rr_len[0] = dsrc->rr_len[0];
+ d->rr_ttl[0] = dsrc->rr_ttl[0];
+ d->rr_data[0] = regional_alloc_init(s->s.region,
+ dsrc->rr_data[0], d->rr_len[0]);
+ if(!d->rr_data[0])
+ return 0;
+ } else
+ r->local_alias = NULL;
+
s->reply_list = r;
return 1;
-
}
/**
@@ -1041,15 +1315,26 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
return mesh_continue(mesh, mstate, module_error, ev);
}
if(s == module_restart_next) {
- fptr_ok(fptr_whitelist_mod_clear(
- mesh->mods.mod[mstate->s.curmod]->clear));
- (*mesh->mods.mod[mstate->s.curmod]->clear)
- (&mstate->s, mstate->s.curmod);
- mstate->s.minfo[mstate->s.curmod] = NULL;
+ int curmod = mstate->s.curmod;
+ for(; mstate->s.curmod < mesh->mods.num;
+ mstate->s.curmod++) {
+ fptr_ok(fptr_whitelist_mod_clear(
+ mesh->mods.mod[mstate->s.curmod]->clear));
+ (*mesh->mods.mod[mstate->s.curmod]->clear)
+ (&mstate->s, mstate->s.curmod);
+ mstate->s.minfo[mstate->s.curmod] = NULL;
+ }
+ mstate->s.curmod = curmod;
}
*ev = module_event_pass;
return 1;
}
+ if(s == module_wait_subquery && mstate->sub_set.count == 0) {
+ log_err("module cannot wait for subquery, subquery list empty");
+ log_query_info(VERB_QUERY, "pass error for qstate",
+ &mstate->s.qinfo);
+ s = module_error;
+ }
if(s == module_error && mstate->s.return_rcode == LDNS_RCODE_NOERROR) {
/* error is bad, handle pass back up below */
mstate->s.return_rcode = LDNS_RCODE_SERVFAIL;
@@ -1187,8 +1472,9 @@ mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
uint16_t flags, int prime, int valrec)
{
struct mesh_area* mesh = qstate->env->mesh;
- struct mesh_state* dep_m = mesh_area_find(mesh, qinfo, flags, prime,
- valrec);
+ struct mesh_state* dep_m = NULL;
+ if(!mesh_state_is_unique(qstate->mesh_info))
+ dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec);
return mesh_detect_cycle_found(qstate, dep_m);
}
diff --git a/external/unbound/services/mesh.h b/external/unbound/services/mesh.h
index 086e39094..1c7794532 100644
--- a/external/unbound/services/mesh.h
+++ b/external/unbound/services/mesh.h
@@ -59,6 +59,7 @@ struct query_info;
struct reply_info;
struct outbound_entry;
struct timehist;
+struct respip_client_info;
/**
* Maximum number of mesh state activations. Any more is likely an
@@ -83,9 +84,9 @@ struct mesh_area {
struct module_env* env;
/** set of runnable queries (mesh_state.run_node) */
- rbtree_t run;
+ rbtree_type run;
/** rbtree of all current queries (mesh_state.node)*/
- rbtree_t all;
+ rbtree_type all;
/** count of the total number of mesh_reply entries */
size_t num_reply_addrs;
@@ -154,9 +155,9 @@ struct mesh_area {
*/
struct mesh_state {
/** node in mesh_area all tree, key is this struct. Must be first. */
- rbnode_t node;
+ rbnode_type node;
/** node in mesh_area runnable tree, key is this struct */
- rbnode_t run_node;
+ rbnode_type run_node;
/** the query state. Note that the qinfo and query_flags
* may not change. */
struct module_qstate s;
@@ -166,10 +167,10 @@ struct mesh_state {
struct mesh_cb* cb_list;
/** set of superstates (that want this state's result)
* contains struct mesh_state_ref* */
- rbtree_t super_set;
+ rbtree_type super_set;
/** set of substates (that this state needs to continue)
* contains struct mesh_state_ref* */
- rbtree_t sub_set;
+ rbtree_type sub_set;
/** number of activations for the mesh state */
size_t num_activated;
@@ -180,6 +181,8 @@ struct mesh_state {
/** if this state is in the forever list, jostle list, or neither */
enum mesh_list_select { mesh_no_list, mesh_forever_list,
mesh_jostle_list } list_select;
+ /** pointer to this state for uniqueness or NULL */
+ struct mesh_state* unique;
/** true if replies have been sent out (at end for alignment) */
uint8_t replies_sent;
@@ -191,7 +194,7 @@ struct mesh_state {
*/
struct mesh_state_ref {
/** node in rbtree for set, key is this structure */
- rbnode_t node;
+ rbnode_type node;
/** the mesh state */
struct mesh_state* s;
};
@@ -214,13 +217,15 @@ struct mesh_reply {
uint16_t qflags;
/** qname from this query. len same as mesh qinfo. */
uint8_t* qname;
+ /** same as that in query_info. */
+ struct local_rrset* local_alias;
};
/**
* Mesh result callback func.
* called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus);
*/
-typedef void (*mesh_cb_func_t)(void*, int, struct sldns_buffer*, enum sec_status,
+typedef void (*mesh_cb_func_type)(void*, int, struct sldns_buffer*, enum sec_status,
char*);
/**
@@ -241,7 +246,7 @@ struct mesh_cb {
/** callback routine for results. if rcode != 0 buf has message.
* called as cb(cb_arg, rcode, buf, sec_state);
*/
- mesh_cb_func_t cb;
+ mesh_cb_func_type cb;
/** user arg for callback */
void* cb_arg;
};
@@ -270,14 +275,18 @@ void mesh_delete(struct mesh_area* mesh);
*
* @param mesh: the mesh.
* @param qinfo: query from client.
+ * @param cinfo: additional information associated with the query client.
+ * 'cinfo' itself is ephemeral but data pointed to by its members
+ * can be assumed to be valid and unchanged until the query processing is
+ * completed.
* @param qflags: flags from client query.
* @param edns: edns data from client query.
* @param rep: where to reply to.
* @param qid: query id to reply with.
*/
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
- uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
- uint16_t qid);
+ struct respip_client_info* cinfo, uint16_t qflags,
+ struct edns_data* edns, struct comm_reply* rep, uint16_t qid);
/**
* New query with callback. Create new query state if needed, and
@@ -296,7 +305,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
*/
int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, struct edns_data* edns, struct sldns_buffer* buf,
- uint16_t qid, mesh_cb_func_t cb, void* cb_arg);
+ uint16_t qid, mesh_cb_func_type cb, void* cb_arg);
/**
* New prefetch message. Create new query state if needed.
@@ -405,14 +414,31 @@ void mesh_state_delete(struct module_qstate* qstate);
* Does not put the mesh state into rbtrees and so on.
* @param env: module environment to set.
* @param qinfo: query info that the mesh is for.
+ * @param cinfo: control info for the query client (can be NULL).
* @param qflags: flags for query (RD / CD flag).
* @param prime: if true, it is a priming query, set is_priming on mesh state.
* @param valrec: if true, it is a validation recursion query, and sets
* is_valrec on the mesh state.
* @return: new mesh state or NULL on allocation error.
*/
-struct mesh_state* mesh_state_create(struct module_env* env,
- struct query_info* qinfo, uint16_t qflags, int prime, int valrec);
+struct mesh_state* mesh_state_create(struct module_env* env,
+ struct query_info* qinfo, struct respip_client_info* cinfo,
+ uint16_t qflags, int prime, int valrec);
+
+/**
+ * Check if the mesh state is unique.
+ * A unique mesh state uses it's unique member to point to itself, else NULL.
+ * @param mstate: mesh state to check.
+ * @return true if the mesh state is unique, false otherwise.
+ */
+int mesh_state_is_unique(struct mesh_state* mstate);
+
+/**
+ * Make a mesh state unique.
+ * A unique mesh state uses it's unique member to point to itself.
+ * @param mstate: mesh state to check.
+ */
+void mesh_state_make_unique(struct mesh_state* mstate);
/**
* Cleanup a mesh state and its query state. Does not do rbtree or
@@ -432,14 +458,17 @@ void mesh_delete_all(struct mesh_area* mesh);
* Find a mesh state in the mesh area. Pass relevant flags.
*
* @param mesh: the mesh area to look in.
+ * @param cinfo: if non-NULL client specific info that may affect IP-based
+ * actions that apply to the query result.
* @param qinfo: what query
* @param qflags: if RD / CD bit is set or not.
* @param prime: if it is a priming query.
* @param valrec: if it is a validation-recursion query.
* @return: mesh state or NULL if not found.
*/
-struct mesh_state* mesh_area_find(struct mesh_area* mesh,
- struct query_info* qinfo, uint16_t qflags, int prime, int valrec);
+struct mesh_state* mesh_area_find(struct mesh_area* mesh,
+ struct respip_client_info* cinfo, struct query_info* qinfo,
+ uint16_t qflags, int prime, int valrec);
/**
* Setup attachment super/sub relation between super and sub mesh state.
@@ -459,11 +488,12 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
* @param rep: comm point reply info.
* @param qid: ID of reply.
* @param qflags: original query flags.
- * @param qname: original query name.
+ * @param qinfo: original query info.
* @return: 0 on alloc error.
*/
-int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
- struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname);
+int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
+ struct comm_reply* rep, uint16_t qid, uint16_t qflags,
+ const struct query_info* qinfo);
/**
* Create new callback structure and attach it to a mesh state.
@@ -478,8 +508,8 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
* @return: 0 on alloc error.
*/
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
- struct sldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg, uint16_t qid,
- uint16_t qflags);
+ struct sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
+ uint16_t qid, uint16_t qflags);
/**
* Run the mesh. Run all runnable mesh states. Which can create new
diff --git a/external/unbound/services/modstack.c b/external/unbound/services/modstack.c
index 49bb2fd15..9bebd3a56 100644
--- a/external/unbound/services/modstack.c
+++ b/external/unbound/services/modstack.c
@@ -46,10 +46,17 @@
#include "dns64/dns64.h"
#include "iterator/iterator.h"
#include "validator/validator.h"
+#include "respip/respip.h"
#ifdef WITH_PYTHONMODULE
#include "pythonmod/pythonmod.h"
#endif
+#ifdef USE_CACHEDB
+#include "cachedb/cachedb.h"
+#endif
+#ifdef CLIENT_SUBNET
+#include "edns-subnet/subnetmod.h"
+#endif
/** count number of modules (words) in the string */
static int
@@ -121,6 +128,13 @@ module_list_avail(void)
#ifdef WITH_PYTHONMODULE
"python",
#endif
+#ifdef USE_CACHEDB
+ "cachedb",
+#endif
+#ifdef CLIENT_SUBNET
+ "subnetcache",
+#endif
+ "respip",
"validator",
"iterator",
NULL};
@@ -139,6 +153,13 @@ module_funcs_avail(void)
#ifdef WITH_PYTHONMODULE
&pythonmod_get_funcblock,
#endif
+#ifdef USE_CACHEDB
+ &cachedb_get_funcblock,
+#endif
+#ifdef CLIENT_SUBNET
+ &subnetmod_get_funcblock,
+#endif
+ &respip_get_funcblock,
&val_get_funcblock,
&iter_get_funcblock,
NULL};
@@ -207,7 +228,7 @@ int
modstack_find(struct module_stack* stack, const char* name)
{
int i;
- for(i=0; i<stack->num; i++) {
+ for(i=0; i<stack->num; i++) {
if(strcmp(stack->mod[i]->name, name) == 0)
return i;
}
diff --git a/external/unbound/services/outside_network.c b/external/unbound/services/outside_network.c
index f105bc0d4..426e87b3e 100644
--- a/external/unbound/services/outside_network.c
+++ b/external/unbound/services/outside_network.c
@@ -122,6 +122,8 @@ serviced_cmp(const void* key1, const void* key2)
}
if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0)
return r;
+ if((r = edns_opt_list_compare(q1->opt_list, q2->opt_list)) != 0)
+ return r;
return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen);
}
@@ -222,11 +224,52 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len)
#endif
return 0;
}
+
+ if (w->outnet->tcp_mss > 0) {
+#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG)
+ if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG,
+ (void*)&w->outnet->tcp_mss,
+ (socklen_t)sizeof(w->outnet->tcp_mss)) < 0) {
+ verbose(VERB_ALGO, "outgoing tcp:"
+ " setsockopt(.. SO_REUSEADDR ..) failed");
+ }
+#else
+ verbose(VERB_ALGO, "outgoing tcp:"
+ " setsockopt(TCP_MAXSEG) unsupported");
+#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */
+ }
+
if(!pick_outgoing_tcp(w, s))
return 0;
fd_set_nonblock(s);
+#ifdef USE_OSX_MSG_FASTOPEN
+ /* API for fast open is different here. We use a connectx() function and
+ then writes can happen as normal even using SSL.*/
+ /* connectx requires that the len be set in the sockaddr struct*/
+ struct sockaddr_in *addr_in = (struct sockaddr_in *)&w->addr;
+ addr_in->sin_len = w->addrlen;
+ sa_endpoints_t endpoints;
+ endpoints.sae_srcif = 0;
+ endpoints.sae_srcaddr = NULL;
+ endpoints.sae_srcaddrlen = 0;
+ endpoints.sae_dstaddr = (struct sockaddr *)&w->addr;
+ endpoints.sae_dstaddrlen = w->addrlen;
+ if (connectx(s, &endpoints, SAE_ASSOCID_ANY,
+ CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
+ NULL, 0, NULL, NULL) == -1) {
+#else /* USE_OSX_MSG_FASTOPEN*/
+#ifdef USE_MSG_FASTOPEN
+ pend->c->tcp_do_fastopen = 1;
+ /* Only do TFO for TCP in which case no connect() is required here.
+ Don't combine client TFO with SSL, since OpenSSL can't
+ currently support doing a handshake on fd that already isn't connected*/
+ if (w->outnet->sslctx && w->ssl_upstream) {
+ if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
+#else /* USE_MSG_FASTOPEN*/
if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
+#endif /* USE_MSG_FASTOPEN*/
+#endif /* USE_OSX_MSG_FASTOPEN*/
#ifndef USE_WINSOCK
#ifdef EINPROGRESS
if(errno != EINPROGRESS) {
@@ -246,6 +289,9 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len)
return 0;
}
}
+#ifdef USE_MSG_FASTOPEN
+ }
+#endif /* USE_MSG_FASTOPEN */
if(w->outnet->sslctx && w->ssl_upstream) {
pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s);
if(!pend->c->ssl) {
@@ -288,7 +334,7 @@ use_free_buffer(struct outside_network* outnet)
if(outnet->tcp_wait_last == w)
outnet->tcp_wait_last = NULL;
if(!outnet_tcp_take_into_use(w, w->pkt, w->pkt_len)) {
- comm_point_callback_t* cb = w->cb;
+ comm_point_callback_type* cb = w->cb;
void* cb_arg = w->cb_arg;
waiting_tcp_delete(w);
fptr_ok(fptr_whitelist_pending_tcp(cb));
@@ -574,7 +620,9 @@ static int setup_if(struct port_if* pif, const char* addrstr,
pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int));
if(!pif->avail_ports)
return 0;
- if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen))
+ if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) &&
+ !netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT,
+ &pif->addr, &pif->addrlen, &pif->pfxlen))
return 0;
pif->maxout = (int)numfd;
pif->inuse = 0;
@@ -590,7 +638,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
size_t num_ports, char** ifs, int num_ifs, int do_ip4,
int do_ip6, size_t num_tcp, struct infra_cache* infra,
struct ub_randstate* rnd, int use_caps_for_id, int* availports,
- int numavailports, size_t unwanted_threshold,
+ int numavailports, size_t unwanted_threshold, int tcp_mss,
void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
void* sslctx, int delayclose, struct dt_env* dtenv)
{
@@ -620,6 +668,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
outnet->unwanted_param = unwanted_param;
outnet->use_caps_for_id = use_caps_for_id;
outnet->do_udp = do_udp;
+ outnet->tcp_mss = tcp_mss;
#ifndef S_SPLINT_S
if(delayclose) {
outnet->delayclose = 1;
@@ -726,7 +775,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
/** helper pending delete */
static void
-pending_node_del(rbnode_t* node, void* arg)
+pending_node_del(rbnode_type* node, void* arg)
{
struct pending* pend = (struct pending*)node;
struct outside_network* outnet = (struct outside_network*)arg;
@@ -735,12 +784,13 @@ pending_node_del(rbnode_t* node, void* arg)
/** helper serviced delete */
static void
-serviced_node_del(rbnode_t* node, void* ATTR_UNUSED(arg))
+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);
+ edns_opt_list_free(sq->opt_list);
while(p) {
np = p->next;
free(p);
@@ -874,32 +924,55 @@ pending_delete(struct outside_network* outnet, struct pending* p)
free(p);
}
+static void
+sai6_putrandom(struct sockaddr_in6 *sa, int pfxlen, struct ub_randstate *rnd)
+{
+ int i, last;
+ if(!(pfxlen > 0 && pfxlen < 128))
+ return;
+ for(i = 0; i < (128 - pfxlen) / 8; i++) {
+ sa->sin6_addr.s6_addr[15-i] = (uint8_t)ub_random_max(rnd, 256);
+ }
+ last = pfxlen & 7;
+ if(last != 0) {
+ sa->sin6_addr.s6_addr[15-i] |=
+ ((0xFF >> last) & ub_random_max(rnd, 256));
+ }
+}
+
/**
* Try to open a UDP socket for outgoing communication.
* Sets sockets options as needed.
* @param addr: socket address.
* @param addrlen: length of address.
+ * @param pfxlen: length of network prefix (for address randomisation).
* @param port: port override for addr.
* @param inuse: if -1 is returned, this bool means the port was in use.
+ * @param rnd: random state (for address randomisation).
* @return fd or -1
*/
static int
-udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int port,
- int* inuse)
+udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int pfxlen,
+ int port, int* inuse, struct ub_randstate* rnd)
{
int fd, noproto;
if(addr_is_ip6(addr, addrlen)) {
- struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
- sa->sin6_port = (in_port_t)htons((uint16_t)port);
+ int freebind = 0;
+ struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr;
+ sa.sin6_port = (in_port_t)htons((uint16_t)port);
+ if(pfxlen != 0) {
+ freebind = 1;
+ sai6_putrandom(&sa, pfxlen, rnd);
+ }
fd = create_udp_sock(AF_INET6, SOCK_DGRAM,
- (struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
- 0, 0, 0, NULL, 0);
+ (struct sockaddr*)&sa, addrlen, 1, inuse, &noproto,
+ 0, 0, 0, NULL, 0, freebind, 0);
} else {
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
sa->sin_port = (in_port_t)htons((uint16_t)port);
fd = create_udp_sock(AF_INET, SOCK_DGRAM,
(struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
- 0, 0, 0, NULL, 0);
+ 0, 0, 0, NULL, 0, 0, 0);
}
return fd;
}
@@ -959,7 +1032,8 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
/* try to open new port, if fails, loop to try again */
log_assert(pif->inuse < pif->maxout);
portno = pif->avail_ports[my_port - pif->inuse];
- fd = udp_sockport(&pif->addr, pif->addrlen, portno, &inuse);
+ fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen,
+ portno, &inuse, outnet->rnd);
if(fd == -1 && !inuse) {
/* nonrecoverable error making socket */
return 0;
@@ -1050,7 +1124,7 @@ randomize_and_send_udp(struct pending* pend, sldns_buffer* packet, int timeout)
struct pending*
pending_udp_query(struct serviced_query* sq, struct sldns_buffer* packet,
- int timeout, comm_point_callback_t* cb, void* cb_arg)
+ int timeout, comm_point_callback_type* cb, void* cb_arg)
{
struct pending* pend = (struct pending*)calloc(1, sizeof(*pend));
if(!pend) return NULL;
@@ -1100,7 +1174,7 @@ outnet_tcptimer(void* arg)
{
struct waiting_tcp* w = (struct waiting_tcp*)arg;
struct outside_network* outnet = w->outnet;
- comm_point_callback_t* cb;
+ comm_point_callback_type* cb;
void* cb_arg;
if(w->pkt) {
/* it is on the waiting list */
@@ -1123,7 +1197,7 @@ outnet_tcptimer(void* arg)
struct waiting_tcp*
pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet,
- int timeout, comm_point_callback_t* callback, void* callback_arg)
+ int timeout, comm_point_callback_type* callback, void* callback_arg)
{
struct pending_tcp* pend = sq->outnet->tcp_free;
struct waiting_tcp* w;
@@ -1203,7 +1277,8 @@ serviced_gen_query(sldns_buffer* buff, uint8_t* qname, size_t qnamelen,
/** lookup serviced query in serviced query rbtree */
static struct serviced_query*
lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
- struct sockaddr_storage* addr, socklen_t addrlen)
+ struct sockaddr_storage* addr, socklen_t addrlen,
+ struct edns_option* opt_list)
{
struct serviced_query key;
key.node.key = &key;
@@ -1213,6 +1288,7 @@ lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
memcpy(&key.addr, addr, addrlen);
key.addrlen = addrlen;
key.outnet = outnet;
+ key.opt_list = opt_list;
return (struct serviced_query*)rbtree_search(outnet->serviced, &key);
}
@@ -1221,11 +1297,11 @@ 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,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
- size_t zonelen, int qtype)
+ size_t zonelen, int qtype, struct edns_option* opt_list)
{
struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
#ifdef UNBOUND_DEBUG
- rbnode_t* ins;
+ rbnode_type* ins;
#endif
if(!sq)
return NULL;
@@ -1251,6 +1327,16 @@ serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
sq->ssl_upstream = ssl_upstream;
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->zone);
+ free(sq->qbuf);
+ free(sq);
+ return NULL;
+ }
+ }
sq->outnet = outnet;
sq->cblist = NULL;
sq->pending = NULL;
@@ -1330,6 +1416,7 @@ serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
long int random = 0;
int bits = 0;
log_assert(len >= 10 + 5 /* offset qname, root, qtype, qclass */);
+ (void)len;
lablen = *d++;
while(lablen) {
while(lablen--) {
@@ -1378,6 +1465,7 @@ serviced_encode(struct serviced_query* sq, sldns_buffer* buff, int with_edns)
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = EDNS_ADVERTISED_VERSION;
+ edns.opt_list = sq->opt_list;
if(sq->status == serviced_query_UDP_EDNS_FRAG) {
if(addr_is_ip6(&sq->addr, sq->addrlen)) {
if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE)
@@ -1461,7 +1549,7 @@ serviced_check_qname(sldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen)
return 0;
while(len1 != 0 || len2 != 0) {
if(LABEL_IS_PTR(len1)) {
- d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
+ d1 = sldns_buffer_begin(pkt)+PTR_OFFSET(len1, *d1);
if(d1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt)))
return 0;
len1 = *d1++;
@@ -1499,7 +1587,7 @@ serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
uint8_t *backup_p = NULL;
size_t backlen = 0;
#ifdef UNBOUND_DEBUG
- rbnode_t* rem =
+ rbnode_type* rem =
#else
(void)
#endif
@@ -1511,7 +1599,10 @@ serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
sq->to_be_deleted = 1;
verbose(VERB_ALGO, "svcd callbacks start");
if(sq->outnet->use_caps_for_id && error == NETEVENT_NOERROR && c &&
- !sq->nocaps) {
+ !sq->nocaps && sq->qtype != LDNS_RR_TYPE_PTR) {
+ /* for type PTR do not check perturbed name in answer,
+ * compatibility with cisco dns guard boxes that mess up
+ * reverse queries 0x20 contents */
/* noerror and nxdomain must have a qname in reply */
if(sldns_buffer_read_u16_at(c->buffer, 4) == 0 &&
(LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer))
@@ -1692,6 +1783,44 @@ serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff)
return sq->pending != NULL;
}
+/* see if packet is edns malformed; got zeroes at start.
+ * This is from servers that return malformed packets to EDNS0 queries,
+ * but they return good packets for nonEDNS0 queries.
+ * We try to detect their output; without resorting to a full parse or
+ * check for too many bytes after the end of the packet. */
+static int
+packet_edns_malformed(struct sldns_buffer* buf, int qtype)
+{
+ size_t len;
+ if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE)
+ return 1; /* malformed */
+ /* they have NOERROR rcode, 1 answer. */
+ if(LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) != LDNS_RCODE_NOERROR)
+ return 0;
+ /* one query (to skip) and answer records */
+ if(LDNS_QDCOUNT(sldns_buffer_begin(buf)) != 1 ||
+ LDNS_ANCOUNT(sldns_buffer_begin(buf)) == 0)
+ return 0;
+ /* skip qname */
+ len = dname_valid(sldns_buffer_at(buf, LDNS_HEADER_SIZE),
+ sldns_buffer_limit(buf)-LDNS_HEADER_SIZE);
+ if(len == 0)
+ return 0;
+ if(len == 1 && qtype == 0)
+ return 0; /* we asked for '.' and type 0 */
+ /* and then 4 bytes (type and class of query) */
+ if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE + len + 4 + 3)
+ return 0;
+
+ /* and start with 11 zeroes as the answer RR */
+ /* so check the qtype of the answer record, qname=0, type=0 */
+ if(sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[0] == 0 &&
+ sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[1] == 0 &&
+ sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[2] == 0)
+ return 1;
+ return 0;
+}
+
int
serviced_udp_callback(struct comm_point* c, void* arg, int error,
struct comm_reply* rep)
@@ -1750,7 +1879,7 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
return 0;
}
#ifdef USE_DNSTAP
- if(outnet->dtenv &&
+ if(error == NETEVENT_NOERROR && outnet->dtenv &&
(outnet->dtenv->log_resolver_response_messages ||
outnet->dtenv->log_forwarder_response_messages))
dt_msg_send_outside_response(outnet->dtenv, &sq->addr, c->type,
@@ -1762,7 +1891,9 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
||sq->status == serviced_query_UDP_EDNS_FRAG)
&& (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer))
== LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(
- sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL)) {
+ sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL
+ || packet_edns_malformed(c->buffer, sq->qtype)
+ )) {
/* try to get an answer by falling back without EDNS */
verbose(VERB_ALGO, "serviced query: attempt without EDNS");
sq->status = serviced_query_UDP_EDNS_fallback;
@@ -1855,17 +1986,22 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
struct serviced_query*
outnet_serviced_query(struct outside_network* outnet,
- uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
- uint16_t flags, int dnssec, int want_dnssec, int nocaps,
- int tcp_upstream, int ssl_upstream, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- comm_point_callback_t* callback, void* callback_arg,
- sldns_buffer* buff)
+ struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
+ int nocaps, int tcp_upstream, int ssl_upstream,
+ 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)
{
struct serviced_query* sq;
struct service_callback* cb;
- serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags);
- sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen);
+ if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone, zonelen,
+ qstate, qstate->region))
+ return NULL;
+ 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). */
@@ -1875,7 +2011,7 @@ outnet_serviced_query(struct outside_network* outnet,
/* make new serviced query entry */
sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps,
tcp_upstream, ssl_upstream, addr, addrlen, zone,
- zonelen, (int)qtype);
+ zonelen, (int)qinfo->qtype, qstate->edns_opts_back_out);
if(!sq) {
free(cb);
return NULL;
@@ -1932,13 +2068,7 @@ void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
callback_list_remove(sq, cb_arg);
/* if callbacks() routine scheduled deletion, let it do that */
if(!sq->cblist && !sq->to_be_deleted) {
-#ifdef UNBOUND_DEBUG
- rbnode_t* rem =
-#else
- (void)
-#endif
- rbtree_delete(sq->outnet->serviced, sq);
- log_assert(rem); /* should be present */
+ (void)rbtree_delete(sq->outnet->serviced, sq);
serviced_delete(sq);
}
}
diff --git a/external/unbound/services/outside_network.h b/external/unbound/services/outside_network.h
index 9959676d3..befd512f0 100644
--- a/external/unbound/services/outside_network.h
+++ b/external/unbound/services/outside_network.h
@@ -58,6 +58,10 @@ struct port_if;
struct sldns_buffer;
struct serviced_query;
struct dt_env;
+struct edns_option;
+struct module_env;
+struct module_qstate;
+struct query_info;
/**
* Send queries to outside servers and wait for answers from servers.
@@ -119,9 +123,9 @@ struct outside_network {
struct pending* udp_wait_last;
/** pending udp answers. sorted by id, addr */
- rbtree_t* pending;
+ rbtree_type* pending;
/** serviced queries, sorted by qbuf, addr, dnssec */
- rbtree_t* serviced;
+ rbtree_type* serviced;
/** host cache, pointer but not owned by outnet. */
struct infra_cache* infra;
/** where to get random numbers */
@@ -132,6 +136,8 @@ struct outside_network {
/** dnstap environment */
struct dt_env* dtenv;
#endif
+ /** maximum segment size of tcp socket */
+ int tcp_mss;
/**
* Array of tcp pending used for outgoing TCP connections.
@@ -162,6 +168,10 @@ struct port_if {
/** length of addr field */
socklen_t addrlen;
+ /** prefix length of network address (in bits), for randomisation.
+ * if 0, no randomisation. */
+ int pfxlen;
+
/** the available ports array. These are unused.
* Only the first total-inuse part is filled. */
int* avail_ports;
@@ -200,7 +210,7 @@ struct port_comm {
*/
struct pending {
/** redblacktree entry, key is the pending struct(id, addr). */
- rbnode_t node;
+ rbnode_type node;
/** the ID for the query. int so that a value out of range can
* be used to signify a pending that is for certain not present in
* the rbtree. (and for which deletion is safe). */
@@ -214,7 +224,7 @@ struct pending {
/** timeout event */
struct comm_timer* timer;
/** callback for the timeout, error or reply to the message */
- comm_point_callback_t* cb;
+ comm_point_callback_type* cb;
/** callback user argument */
void* cb_arg;
/** the outside network it is part of */
@@ -275,7 +285,7 @@ struct waiting_tcp {
/** length of query packet. */
size_t pkt_len;
/** callback for the timeout, error or reply to the message */
- comm_point_callback_t* cb;
+ comm_point_callback_type* cb;
/** callback user argument */
void* cb_arg;
/** if it uses ssl upstream */
@@ -289,7 +299,7 @@ struct service_callback {
/** next in callback list */
struct service_callback* next;
/** callback function */
- comm_point_callback_t* cb;
+ comm_point_callback_type* cb;
/** user argument for callback function */
void* cb_arg;
};
@@ -307,7 +317,7 @@ struct service_callback {
*/
struct serviced_query {
/** The rbtree node, key is this record */
- rbnode_t node;
+ rbnode_type node;
/** The query that needs to be answered. Starts with flags u16,
* then qdcount, ..., including qname, qtype, qclass. Does not include
* EDNS record. */
@@ -365,6 +375,8 @@ struct serviced_query {
int last_rtt;
/** do we know edns probe status already, for UDP_EDNS queries */
int edns_lame_known;
+ /** edns options to use for sending upstream packet */
+ struct edns_option* opt_list;
/** outside network this is part of */
struct outside_network* outnet;
/** list of interested parties that need callback on results. */
@@ -392,6 +404,7 @@ struct serviced_query {
* @param unwanted_threshold: when to take defensive action.
* @param unwanted_action: the action to take.
* @param unwanted_param: user parameter to action.
+ * @param tcp_mss: maximum segment size of tcp socket.
* @param do_udp: if udp is done.
* @param sslctx: context to create outgoing connections with (if enabled).
* @param delayclose: if not 0, udp sockets are delayed before timeout closure.
@@ -403,7 +416,7 @@ struct outside_network* outside_network_create(struct comm_base* base,
size_t bufsize, size_t num_ports, char** ifs, int num_ifs,
int do_ip4, int do_ip6, size_t num_tcp, struct infra_cache* infra,
struct ub_randstate* rnd, int use_caps_for_id, int* availports,
- int numavailports, size_t unwanted_threshold,
+ int numavailports, size_t unwanted_threshold, int tcp_mss,
void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
void* sslctx, int delayclose, struct dt_env *dtenv);
@@ -430,7 +443,7 @@ void outside_network_quit_prepare(struct outside_network* outnet);
* @return: NULL on error for malloc or socket. Else the pending query object.
*/
struct pending* pending_udp_query(struct serviced_query* sq,
- struct sldns_buffer* packet, int timeout, comm_point_callback_t* callback,
+ struct sldns_buffer* packet, int timeout, comm_point_callback_type* callback,
void* callback_arg);
/**
@@ -446,7 +459,7 @@ struct pending* pending_udp_query(struct serviced_query* sq,
* @return: false on error for malloc or socket. Else the pending TCP object.
*/
struct waiting_tcp* pending_tcp_query(struct serviced_query* sq,
- struct sldns_buffer* packet, int timeout, comm_point_callback_t* callback,
+ struct sldns_buffer* packet, int timeout, comm_point_callback_type* callback,
void* callback_arg);
/**
@@ -461,10 +474,7 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
* Perform a serviced query to the authoritative servers.
* Duplicate efforts are detected, and EDNS, TCP and UDP retry is performed.
* @param outnet: outside network, with rbtree of serviced queries.
- * @param qname: what qname to query.
- * @param qnamelen: length of qname in octets including 0 root label.
- * @param qtype: rrset type to query (host format)
- * @param qclass: query class. (host format)
+ * @param qinfo: query info.
* @param flags: flags u16 (host format), includes opcode, CD bit.
* @param dnssec: if set, DO bit is set in EDNS queries.
* If the value includes BIT_CD, CD bit is set when in EDNS queries.
@@ -474,25 +484,28 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
* @param nocaps: ignore use_caps_for_id and use unperturbed qname.
* @param tcp_upstream: use TCP for upstream queries.
* @param ssl_upstream: use SSL for upstream queries.
- * @param callback: callback function.
- * @param callback_arg: user argument to callback function.
* @param addr: to which server to send the query.
* @param addrlen: length of addr.
* @param zone: name of the zone of the delegation point. wireformat dname.
This is the delegation point name for which the server is deemed
authoritative.
* @param zonelen: length of zone.
+ * @param qstate: module qstate. Mainly for inspecting the available
+ * edns_opts_lists.
+ * @param callback: callback function.
+ * @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.
* @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,
- uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
- uint16_t flags, int dnssec, int want_dnssec, int nocaps,
- int tcp_upstream, int ssl_upstream, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- comm_point_callback_t* callback, void* callback_arg,
- struct sldns_buffer* buff);
+ struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
+ int nocaps, int tcp_upstream, int ssl_upstream,
+ 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);
/**
* Remove service query callback.
diff --git a/external/unbound/services/view.c b/external/unbound/services/view.c
new file mode 100644
index 000000000..33f4f4986
--- /dev/null
+++ b/external/unbound/services/view.c
@@ -0,0 +1,212 @@
+/*
+ * services/view.c - named views containing local zones authority service.
+ *
+ * Copyright (c) 2016, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to enable named views that can hold local zone
+ * authority service.
+ */
+#include "config.h"
+#include "services/view.h"
+#include "services/localzone.h"
+#include "util/config_file.h"
+
+int
+view_cmp(const void* v1, const void* v2)
+{
+ struct view* a = (struct view*)v1;
+ struct view* b = (struct view*)v2;
+
+ return strcmp(a->name, b->name);
+}
+
+struct views*
+views_create(void)
+{
+ struct views* v = (struct views*)calloc(1,
+ sizeof(*v));
+ if(!v)
+ return NULL;
+ rbtree_init(&v->vtree, &view_cmp);
+ lock_rw_init(&v->lock);
+ lock_protect(&v->lock, &v->vtree, sizeof(v->vtree));
+ return v;
+}
+
+/** This prototype is defined in in respip.h, but we want to avoid
+ * unnecessary dependencies */
+void respip_set_delete(struct respip_set *set);
+
+void
+view_delete(struct view* v)
+{
+ if(!v)
+ return;
+ lock_rw_destroy(&v->lock);
+ local_zones_delete(v->local_zones);
+ respip_set_delete(v->respip_set);
+ free(v->name);
+ free(v);
+}
+
+static void
+delviewnode(rbnode_type* n, void* ATTR_UNUSED(arg))
+{
+ struct view* v = (struct view*)n;
+ view_delete(v);
+}
+
+void
+views_delete(struct views* v)
+{
+ if(!v)
+ return;
+ lock_rw_destroy(&v->lock);
+ traverse_postorder(&v->vtree, delviewnode, NULL);
+ free(v);
+}
+
+/** create a new view */
+static struct view*
+view_create(char* name)
+{
+ struct view* v = (struct view*)calloc(1, sizeof(*v));
+ if(!v)
+ return NULL;
+ v->node.key = v;
+ if(!(v->name = strdup(name))) {
+ free(v);
+ return NULL;
+ }
+ lock_rw_init(&v->lock);
+ lock_protect(&v->lock, &v->name, sizeof(*v)-sizeof(rbnode_type));
+ return v;
+}
+
+/** enter a new view returns with WRlock */
+static struct view*
+views_enter_view_name(struct views* vs, char* name)
+{
+ struct view* v = view_create(name);
+ if(!v) {
+ log_err("out of memory");
+ return NULL;
+ }
+
+ /* add to rbtree */
+ lock_rw_wrlock(&vs->lock);
+ lock_rw_wrlock(&v->lock);
+ if(!rbtree_insert(&vs->vtree, &v->node)) {
+ log_warn("duplicate view: %s", name);
+ lock_rw_unlock(&v->lock);
+ view_delete(v);
+ lock_rw_unlock(&vs->lock);
+ return NULL;
+ }
+ lock_rw_unlock(&vs->lock);
+ return v;
+}
+
+int
+views_apply_cfg(struct views* vs, struct config_file* cfg)
+{
+ struct config_view* cv;
+ struct view* v;
+ struct config_file lz_cfg;
+ /* Check existence of name in first view (last in config). Rest of
+ * views are already checked when parsing config. */
+ if(cfg->views && !cfg->views->name) {
+ log_err("view without a name");
+ return 0;
+ }
+ for(cv = cfg->views; cv; cv = cv->next) {
+ /* create and enter view */
+ if(!(v = views_enter_view_name(vs, cv->name)))
+ return 0;
+ v->isfirst = cv->isfirst;
+ if(cv->local_zones || cv->local_data) {
+ if(!(v->local_zones = local_zones_create())){
+ lock_rw_unlock(&v->lock);
+ return 0;
+ }
+ memset(&lz_cfg, 0, sizeof(lz_cfg));
+ lz_cfg.local_zones = cv->local_zones;
+ lz_cfg.local_data = cv->local_data;
+ lz_cfg.local_zones_nodefault =
+ cv->local_zones_nodefault;
+ if(!local_zones_apply_cfg(v->local_zones, &lz_cfg)){
+ lock_rw_unlock(&v->lock);
+ return 0;
+ }
+ /* local_zones, local_zones_nodefault and local_data
+ * are free'd from config_view by local_zones_apply_cfg.
+ * Set pointers to NULL. */
+ cv->local_zones = NULL;
+ cv->local_data = NULL;
+ cv->local_zones_nodefault = NULL;
+ }
+ lock_rw_unlock(&v->lock);
+ }
+ return 1;
+}
+
+/** find a view by name */
+struct view*
+views_find_view(struct views* vs, const char* name, int write)
+{
+ struct view* v;
+ struct view key;
+ key.node.key = &v;
+ key.name = (char *)name;
+ lock_rw_rdlock(&vs->lock);
+ if(!(v = (struct view*)rbtree_search(&vs->vtree, &key.node))) {
+ lock_rw_unlock(&vs->lock);
+ return 0;
+ }
+ if(write) {
+ lock_rw_wrlock(&v->lock);
+ } else {
+ lock_rw_rdlock(&v->lock);
+ }
+ lock_rw_unlock(&vs->lock);
+ return v;
+}
+
+void views_print(struct views* v)
+{
+ /* TODO implement print */
+ (void)v;
+}
diff --git a/external/unbound/services/view.h b/external/unbound/services/view.h
new file mode 100644
index 000000000..e0b346419
--- /dev/null
+++ b/external/unbound/services/view.h
@@ -0,0 +1,137 @@
+/*
+ * services/view.h - named views containing local zones authority service.
+ *
+ * Copyright (c) 2016, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to enable named views that can hold local zone
+ * authority service.
+ */
+
+#ifndef SERVICES_VIEW_H
+#define SERVICES_VIEW_H
+#include "util/rbtree.h"
+#include "util/locks.h"
+struct regional;
+struct config_file;
+struct config_view;
+struct respip_set;
+
+
+/**
+ * Views storage, shared.
+ */
+struct views {
+ /** lock on the view tree */
+ lock_rw_type lock;
+ /** rbtree of struct view */
+ rbtree_type vtree;
+};
+
+/**
+ * View. Named structure holding local authority zones.
+ */
+struct view {
+ /** rbtree node, key is name */
+ rbnode_type node;
+ /** view name.
+ * Has to be right after rbnode_t due to pointer arithmatic in
+ * view_create's lock protect */
+ char* name;
+ /** view specific local authority zones */
+ struct local_zones* local_zones;
+ /** response-ip configuration data for this view */
+ struct respip_set* respip_set;
+ /** Fallback to global local_zones when there is no match in the view
+ * specific tree. 1 for yes, 0 for no */
+ int isfirst;
+ /** lock on the data in the structure
+ * For the node and name you need to also hold the views_tree lock to
+ * change them. */
+ lock_rw_type lock;
+};
+
+
+/**
+ * Create views storage
+ * @return new struct or NULL on error.
+ */
+struct views* views_create(void);
+
+/**
+ * Delete views storage
+ * @param v: views to delete.
+ */
+void views_delete(struct views* v);
+
+/**
+ * Apply config settings;
+ * Takes care of locking.
+ * @param v: view is set up.
+ * @param cfg: config data.
+ * @return false on error.
+ */
+int views_apply_cfg(struct views* v, struct config_file* cfg);
+
+/**
+ * Compare two view entries in rbtree. Sort canonical.
+ * @param v1: view 1
+ * @param v2: view 2
+ * @return: negative, positive or 0 comparison value.
+ */
+int view_cmp(const void* v1, const void* v2);
+
+/**
+ * Delete one view
+ * @param v: view to delete.
+ */
+void view_delete(struct view* v);
+
+/**
+ * Debug helper. Print all views
+ * Takes care of locking.
+ * @param v: the views tree
+ */
+void views_print(struct views* v);
+
+/* 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
+ * @return: locked view or NULL.
+ */
+struct view* views_find_view(struct views* vs, const char* name, int write);
+
+#endif /* SERVICES_VIEW_H */