aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/services
diff options
context:
space:
mode:
Diffstat (limited to 'external/unbound/services')
-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 */