aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/daemon
diff options
context:
space:
mode:
authorErik de Castro Lopo <erikd@mega-nerd.com>2017-06-16 20:16:05 +1000
committerErik de Castro Lopo <erikd@mega-nerd.com>2017-06-17 23:04:00 +1000
commita85b5759f34c0c4110a479a8b5fa606f15ed9b23 (patch)
tree518cb8346249a42fd2aa8a78c09c3631e14db6aa /external/unbound/daemon
parentMerge pull request #2059 (diff)
downloadmonero-a85b5759f34c0c4110a479a8b5fa606f15ed9b23.tar.xz
Upgrade unbound library
These files were pulled from the 1.6.3 release tarball. This new version builds against OpenSSL version 1.1 which will be the default in the new Debian Stable which is due to be released RealSoonNow (tm).
Diffstat (limited to 'external/unbound/daemon')
-rw-r--r--external/unbound/daemon/acl_list.c325
-rw-r--r--external/unbound/daemon/acl_list.h38
-rw-r--r--external/unbound/daemon/cachedump.c2
-rw-r--r--external/unbound/daemon/daemon.c114
-rw-r--r--external/unbound/daemon/daemon.h19
-rw-r--r--external/unbound/daemon/remote.c581
-rw-r--r--external/unbound/daemon/remote.h4
-rw-r--r--external/unbound/daemon/stats.c16
-rw-r--r--external/unbound/daemon/stats.h16
-rw-r--r--external/unbound/daemon/unbound.c150
-rw-r--r--external/unbound/daemon/worker.c672
-rw-r--r--external/unbound/daemon/worker.h3
12 files changed, 1591 insertions, 349 deletions
diff --git a/external/unbound/daemon/acl_list.c b/external/unbound/daemon/acl_list.c
index 84d099ca5..f7d71b9fd 100644
--- a/external/unbound/daemon/acl_list.c
+++ b/external/unbound/daemon/acl_list.c
@@ -45,6 +45,8 @@
#include "util/log.h"
#include "util/config_file.h"
#include "util/net_help.h"
+#include "services/localzone.h"
+#include "sldns/str2wire.h"
struct acl_list*
acl_list_create(void)
@@ -71,21 +73,21 @@ acl_list_delete(struct acl_list* acl)
}
/** insert new address into acl_list structure */
-static int
+static struct acl_addr*
acl_list_insert(struct acl_list* acl, struct sockaddr_storage* addr,
socklen_t addrlen, int net, enum acl_access control,
int complain_duplicates)
{
- struct acl_addr* node = regional_alloc(acl->region,
+ struct acl_addr* node = regional_alloc_zero(acl->region,
sizeof(struct acl_addr));
if(!node)
- return 0;
+ return NULL;
node->control = control;
if(!addr_tree_insert(&acl->tree, &node->node, addr, addrlen, net)) {
if(complain_duplicates)
verbose(VERB_QUERY, "duplicate acl address ignored.");
}
- return 1;
+ return node;
}
/** apply acl_list string */
@@ -125,6 +127,205 @@ acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2,
return 1;
}
+/** find or create node (NULL on parse or error) */
+static struct acl_addr*
+acl_find_or_create(struct acl_list* acl, const char* str)
+{
+ struct acl_addr* node;
+ struct sockaddr_storage addr;
+ int net;
+ socklen_t addrlen;
+ if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
+ log_err("cannot parse netblock: %s", str);
+ return NULL;
+ }
+ /* find or create node */
+ if(!(node=(struct acl_addr*)addr_tree_find(&acl->tree, &addr,
+ addrlen, net))) {
+ /* create node, type 'allow' since otherwise tags are
+ * pointless, can override with specific access-control: cfg */
+ if(!(node=(struct acl_addr*)acl_list_insert(acl, &addr,
+ addrlen, net, acl_allow, 1))) {
+ log_err("out of memory");
+ return NULL;
+ }
+ }
+ return node;
+}
+
+/** apply acl_tag string */
+static int
+acl_list_tags_cfg(struct acl_list* acl, const char* str, uint8_t* bitmap,
+ size_t bitmaplen)
+{
+ struct acl_addr* node;
+ if(!(node=acl_find_or_create(acl, str)))
+ return 0;
+ node->taglen = bitmaplen;
+ node->taglist = regional_alloc_init(acl->region, bitmap, bitmaplen);
+ if(!node->taglist) {
+ log_err("out of memory");
+ return 0;
+ }
+ return 1;
+}
+
+/** apply acl_view string */
+static int
+acl_list_view_cfg(struct acl_list* acl, const char* str, const char* str2,
+ struct views* vs)
+{
+ struct acl_addr* node;
+ if(!(node=acl_find_or_create(acl, str)))
+ return 0;
+ node->view = views_find_view(vs, str2, 0 /* get read lock*/);
+ if(!node->view) {
+ log_err("no view with name: %s", str2);
+ return 0;
+ }
+ lock_rw_unlock(&node->view->lock);
+ return 1;
+}
+
+/** apply acl_tag_action string */
+static int
+acl_list_tag_action_cfg(struct acl_list* acl, struct config_file* cfg,
+ const char* str, const char* tag, const char* action)
+{
+ struct acl_addr* node;
+ int tagid;
+ enum localzone_type t;
+ if(!(node=acl_find_or_create(acl, str)))
+ return 0;
+ /* allocate array if not yet */
+ if(!node->tag_actions) {
+ node->tag_actions = (uint8_t*)regional_alloc_zero(acl->region,
+ sizeof(*node->tag_actions)*cfg->num_tags);
+ if(!node->tag_actions) {
+ log_err("out of memory");
+ return 0;
+ }
+ node->tag_actions_size = (size_t)cfg->num_tags;
+ }
+ /* parse tag */
+ if((tagid=find_tag_id(cfg, tag)) == -1) {
+ log_err("cannot parse tag (define-tag it): %s %s", str, tag);
+ return 0;
+ }
+ if((size_t)tagid >= node->tag_actions_size) {
+ log_err("tagid too large for array %s %s", str, tag);
+ return 0;
+ }
+ if(!local_zone_str2type(action, &t)) {
+ log_err("cannot parse access control action type: %s %s %s",
+ str, tag, action);
+ return 0;
+ }
+ node->tag_actions[tagid] = (uint8_t)t;
+ return 1;
+}
+
+/** check wire data parse */
+static int
+check_data(const char* data, const struct config_strlist* head)
+{
+ char buf[65536];
+ uint8_t rr[LDNS_RR_BUF_SIZE];
+ size_t len = sizeof(rr);
+ int res;
+ /* '.' is sufficient for validation, and it makes the call to
+ * sldns_wirerr_get_type() simpler below. */
+ snprintf(buf, sizeof(buf), "%s %s", ".", data);
+ res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL, 0,
+ NULL, 0);
+
+ /* Reject it if we would end up having CNAME and other data (including
+ * another CNAME) for the same tag. */
+ if(res == 0 && head) {
+ const char* err_data = NULL;
+
+ if(sldns_wirerr_get_type(rr, len, 1) == LDNS_RR_TYPE_CNAME) {
+ /* adding CNAME while other data already exists. */
+ err_data = data;
+ } else {
+ snprintf(buf, sizeof(buf), "%s %s", ".", head->str);
+ len = sizeof(rr);
+ res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600,
+ NULL, 0, NULL, 0);
+ if(res != 0) {
+ /* This should be impossible here as head->str
+ * has been validated, but we check it just in
+ * case. */
+ return 0;
+ }
+ if(sldns_wirerr_get_type(rr, len, 1) ==
+ LDNS_RR_TYPE_CNAME) /* already have CNAME */
+ err_data = head->str;
+ }
+ if(err_data) {
+ log_err("redirect tag data '%s' must not coexist with "
+ "other data.", err_data);
+ return 0;
+ }
+ }
+ if(res == 0)
+ return 1;
+ log_err("rr data [char %d] parse error %s",
+ (int)LDNS_WIREPARSE_OFFSET(res)-13,
+ sldns_get_errorstr_parse(res));
+ return 0;
+}
+
+/** apply acl_tag_data string */
+static int
+acl_list_tag_data_cfg(struct acl_list* acl, struct config_file* cfg,
+ const char* str, const char* tag, const char* data)
+{
+ struct acl_addr* node;
+ int tagid;
+ char* dupdata;
+ if(!(node=acl_find_or_create(acl, str)))
+ return 0;
+ /* allocate array if not yet */
+ if(!node->tag_datas) {
+ node->tag_datas = (struct config_strlist**)regional_alloc_zero(
+ acl->region, sizeof(*node->tag_datas)*cfg->num_tags);
+ if(!node->tag_datas) {
+ log_err("out of memory");
+ return 0;
+ }
+ node->tag_datas_size = (size_t)cfg->num_tags;
+ }
+ /* parse tag */
+ if((tagid=find_tag_id(cfg, tag)) == -1) {
+ log_err("cannot parse tag (define-tag it): %s %s", str, tag);
+ return 0;
+ }
+ if((size_t)tagid >= node->tag_datas_size) {
+ log_err("tagid too large for array %s %s", str, tag);
+ return 0;
+ }
+
+ /* check data? */
+ if(!check_data(data, node->tag_datas[tagid])) {
+ log_err("cannot parse access-control-tag data: %s %s '%s'",
+ str, tag, data);
+ return 0;
+ }
+
+ dupdata = regional_strdup(acl->region, data);
+ if(!dupdata) {
+ log_err("out of memory");
+ return 0;
+ }
+ if(!cfg_region_strlist_insert(acl->region,
+ &(node->tag_datas[tagid]), dupdata)) {
+ log_err("out of memory");
+ return 0;
+ }
+ return 1;
+}
+
/** read acl_list config */
static int
read_acl_list(struct acl_list* acl, struct config_file* cfg)
@@ -138,13 +339,114 @@ read_acl_list(struct acl_list* acl, struct config_file* cfg)
return 1;
}
+/** read acl tags config */
+static int
+read_acl_tags(struct acl_list* acl, struct config_file* cfg)
+{
+ struct config_strbytelist* np, *p = cfg->acl_tags;
+ cfg->acl_tags = NULL;
+ while(p) {
+ log_assert(p->str && p->str2);
+ if(!acl_list_tags_cfg(acl, p->str, p->str2, p->str2len)) {
+ config_del_strbytelist(p);
+ return 0;
+ }
+ /* free the items as we go to free up memory */
+ np = p->next;
+ free(p->str);
+ free(p->str2);
+ free(p);
+ p = np;
+ }
+ return 1;
+}
+
+/** read acl view config */
+static int
+read_acl_view(struct acl_list* acl, struct config_file* cfg, struct views* v)
+{
+ struct config_str2list* np, *p = cfg->acl_view;
+ cfg->acl_view = NULL;
+ while(p) {
+ log_assert(p->str && p->str2);
+ if(!acl_list_view_cfg(acl, p->str, p->str2, v)) {
+ return 0;
+ }
+ /* free the items as we go to free up memory */
+ np = p->next;
+ free(p->str);
+ free(p->str2);
+ free(p);
+ p = np;
+ }
+ return 1;
+}
+
+/** read acl tag actions config */
+static int
+read_acl_tag_actions(struct acl_list* acl, struct config_file* cfg)
+{
+ struct config_str3list* p, *np;
+ p = cfg->acl_tag_actions;
+ cfg->acl_tag_actions = NULL;
+ while(p) {
+ log_assert(p->str && p->str2 && p->str3);
+ if(!acl_list_tag_action_cfg(acl, cfg, p->str, p->str2,
+ p->str3)) {
+ config_deltrplstrlist(p);
+ return 0;
+ }
+ /* free the items as we go to free up memory */
+ np = p->next;
+ free(p->str);
+ free(p->str2);
+ free(p->str3);
+ free(p);
+ p = np;
+ }
+ return 1;
+}
+
+/** read acl tag datas config */
+static int
+read_acl_tag_datas(struct acl_list* acl, struct config_file* cfg)
+{
+ struct config_str3list* p, *np;
+ p = cfg->acl_tag_datas;
+ cfg->acl_tag_datas = NULL;
+ while(p) {
+ log_assert(p->str && p->str2 && p->str3);
+ if(!acl_list_tag_data_cfg(acl, cfg, p->str, p->str2, p->str3)) {
+ config_deltrplstrlist(p);
+ return 0;
+ }
+ /* free the items as we go to free up memory */
+ np = p->next;
+ free(p->str);
+ free(p->str2);
+ free(p->str3);
+ free(p);
+ p = np;
+ }
+ return 1;
+}
+
int
-acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg)
+acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg,
+ struct views* v)
{
regional_free_all(acl->region);
addr_tree_init(&acl->tree);
if(!read_acl_list(acl, cfg))
return 0;
+ if(!read_acl_view(acl, cfg, v))
+ return 0;
+ if(!read_acl_tags(acl, cfg))
+ return 0;
+ if(!read_acl_tag_actions(acl, cfg))
+ return 0;
+ if(!read_acl_tag_datas(acl, cfg))
+ return 0;
/* insert defaults, with '0' to ignore them if they are duplicates */
if(!acl_list_str_cfg(acl, "0.0.0.0/0", "refuse", 0))
return 0;
@@ -163,13 +465,18 @@ acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg)
}
enum acl_access
-acl_list_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
+acl_get_control(struct acl_addr* acl)
+{
+ if(acl) return acl->control;
+ return acl_deny;
+}
+
+struct acl_addr*
+acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
socklen_t addrlen)
{
- struct acl_addr* r = (struct acl_addr*)addr_tree_lookup(&acl->tree,
+ return (struct acl_addr*)addr_tree_lookup(&acl->tree,
addr, addrlen);
- if(r) return r->control;
- return acl_deny;
}
size_t
diff --git a/external/unbound/daemon/acl_list.h b/external/unbound/daemon/acl_list.h
index 2323697d5..d0d42bfae 100644
--- a/external/unbound/daemon/acl_list.h
+++ b/external/unbound/daemon/acl_list.h
@@ -43,6 +43,7 @@
#ifndef DAEMON_ACL_LIST_H
#define DAEMON_ACL_LIST_H
#include "util/storage/dnstree.h"
+#include "services/view.h"
struct config_file;
struct regional;
@@ -75,7 +76,7 @@ struct acl_list {
* Tree of the addresses that are allowed/blocked.
* contents of type acl_addr.
*/
- rbtree_t tree;
+ rbtree_type tree;
};
/**
@@ -87,6 +88,21 @@ struct acl_addr {
struct addr_tree_node node;
/** access control on this netblock */
enum acl_access control;
+ /** tag bitlist */
+ uint8_t* taglist;
+ /** length of the taglist (in bytes) */
+ size_t taglen;
+ /** array per tagnumber of localzonetype(in one byte). NULL if none. */
+ uint8_t* tag_actions;
+ /** size of the tag_actions_array */
+ size_t tag_actions_size;
+ /** array per tagnumber, with per tag a list of rdata strings.
+ * NULL if none. strings are like 'A 127.0.0.1' 'AAAA ::1' */
+ struct config_strlist** tag_datas;
+ /** size of the tag_datas array */
+ size_t tag_datas_size;
+ /* view element, NULL if none */
+ struct view* view;
};
/**
@@ -105,19 +121,29 @@ void acl_list_delete(struct acl_list* acl);
* Process access control config.
* @param acl: where to store.
* @param cfg: config options.
+ * @param v: views structure
* @return 0 on error.
*/
-int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg);
+int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg,
+ struct views* v);
/**
- * Lookup address to see its access control status.
+ * Lookup access control status for acl structure.
+ * @param acl: structure for acl storage.
+ * @return: what to do with message from this address.
+ */
+enum acl_access acl_get_control(struct acl_addr* acl);
+
+/**
+ * Lookup address to see its acl structure
* @param acl: structure for address storage.
* @param addr: address to check
* @param addrlen: length of addr.
- * @return: what to do with message from this address.
+ * @return: acl structure from this address.
*/
-enum acl_access acl_list_lookup(struct acl_list* acl,
- struct sockaddr_storage* addr, socklen_t addrlen);
+struct acl_addr*
+acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
+ socklen_t addrlen);
/**
* Get memory used by acl structure.
diff --git a/external/unbound/daemon/cachedump.c b/external/unbound/daemon/cachedump.c
index 4b0a583a6..8992e6cb8 100644
--- a/external/unbound/daemon/cachedump.c
+++ b/external/unbound/daemon/cachedump.c
@@ -563,6 +563,7 @@ load_qinfo(char* str, struct query_info* qinfo, struct regional* region)
qinfo->qclass = sldns_wirerr_get_class(rr, rr_len, dname_len);
qinfo->qname_len = dname_len;
qinfo->qname = (uint8_t*)regional_alloc_init(region, rr, dname_len);
+ qinfo->local_alias = NULL;
if(!qinfo->qname) {
log_warn("error out of memory");
return NULL;
@@ -826,6 +827,7 @@ int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm,
qinfo.qname_len = nmlen;
qinfo.qtype = LDNS_RR_TYPE_A;
qinfo.qclass = LDNS_RR_CLASS_IN;
+ qinfo.local_alias = NULL;
dname_str(nm, b);
if(!ssl_printf(ssl, "The following name servers are used for lookup "
diff --git a/external/unbound/daemon/daemon.c b/external/unbound/daemon/daemon.c
index e763f724e..dad9f86b3 100644
--- a/external/unbound/daemon/daemon.c
+++ b/external/unbound/daemon/daemon.c
@@ -73,20 +73,27 @@
#include "util/log.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
+#include "util/shm_side/shm_main.h"
#include "util/storage/lookup3.h"
#include "util/storage/slabhash.h"
#include "services/listen_dnsport.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "services/localzone.h"
+#include "services/view.h"
#include "services/modstack.h"
#include "util/module.h"
#include "util/random.h"
#include "util/tube.h"
#include "util/net_help.h"
#include "sldns/keyraw.h"
+#include "respip/respip.h"
#include <signal.h>
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
/** How many quit requests happened. */
static int sig_record_quit = 0;
/** How many reload requests happened. */
@@ -174,8 +181,15 @@ static void
signal_handling_playback(struct worker* wrk)
{
#ifdef SIGHUP
- if(sig_record_reload)
+ if(sig_record_reload) {
+# ifdef HAVE_SYSTEMD
+ sd_notify(0, "RELOADING=1");
+# endif
worker_sighandler(SIGHUP, wrk);
+# ifdef HAVE_SYSTEMD
+ sd_notify(0, "READY=1");
+# endif
+ }
#endif
if(sig_record_quit)
worker_sighandler(SIGTERM, wrk);
@@ -204,20 +218,29 @@ daemon_init(void)
signal_handling_record();
checklock_start();
#ifdef HAVE_SSL
+# ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
ERR_load_crypto_strings();
- ERR_load_SSL_strings();
-# ifdef HAVE_OPENSSL_CONFIG
- OPENSSL_config("unbound");
# endif
+ ERR_load_SSL_strings();
# ifdef USE_GOST
(void)sldns_key_EVP_load_gost_id();
# endif
+# if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
OpenSSL_add_all_algorithms();
+# else
+ OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
+ | OPENSSL_INIT_ADD_ALL_DIGESTS
+ | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
+# endif
# if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS
/* grab the COMP method ptr because openssl leaks it */
comp_meth = (void*)SSL_COMP_get_compression_methods();
# endif
+# if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
(void)SSL_library_init();
+# else
+ (void)OPENSSL_init_ssl(0, NULL);
+# endif
# if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED)
if(!ub_openssl_lock_init())
fatal_exit("could not init openssl locks");
@@ -239,9 +262,16 @@ daemon_init(void)
free(daemon);
return NULL;
}
+ /* init edns_known_options */
+ if(!edns_known_options_init(daemon->env)) {
+ free(daemon->env);
+ free(daemon);
+ return NULL;
+ }
alloc_init(&daemon->superalloc, NULL, 0);
daemon->acl = acl_list_create();
if(!daemon->acl) {
+ edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
return NULL;
@@ -338,6 +368,7 @@ static void daemon_setup_modules(struct daemon* daemon)
daemon->env)) {
fatal_exit("failed to setup modules");
}
+ log_edns_known_options(VERB_ALGO, daemon->env);
}
/**
@@ -407,6 +438,8 @@ daemon_create_workers(struct daemon* daemon)
}
daemon->workers = (struct worker**)calloc((size_t)daemon->num,
sizeof(struct worker*));
+ if(!daemon->workers)
+ fatal_exit("out of memory during daemon init");
if(daemon->cfg->dnstap) {
#ifdef USE_DNSTAP
daemon->dtenv = dt_create(daemon->cfg->dnstap_socket_path,
@@ -530,17 +563,55 @@ daemon_stop_others(struct daemon* daemon)
void
daemon_fork(struct daemon* daemon)
{
+ int have_view_respip_cfg = 0;
+
log_assert(daemon);
- if(!acl_list_apply_cfg(daemon->acl, daemon->cfg))
+ if(!(daemon->views = views_create()))
+ fatal_exit("Could not create views: out of memory");
+ /* create individual views and their localzone/data trees */
+ if(!views_apply_cfg(daemon->views, daemon->cfg))
+ fatal_exit("Could not set up views");
+
+ if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views))
fatal_exit("Could not setup access control list");
+ if(daemon->cfg->dnscrypt) {
+#ifdef USE_DNSCRYPT
+ daemon->dnscenv = dnsc_create();
+ if (!daemon->dnscenv)
+ fatal_exit("dnsc_create failed");
+ dnsc_apply_cfg(daemon->dnscenv, daemon->cfg);
+#else
+ fatal_exit("dnscrypt enabled in config but unbound was not built with "
+ "dnscrypt support");
+#endif
+ }
+ /* create global local_zones */
if(!(daemon->local_zones = local_zones_create()))
fatal_exit("Could not create local zones: out of memory");
if(!local_zones_apply_cfg(daemon->local_zones, daemon->cfg))
fatal_exit("Could not set up local zones");
+ /* process raw response-ip configuration data */
+ if(!(daemon->respip_set = respip_set_create()))
+ fatal_exit("Could not create response IP set");
+ if(!respip_global_apply_cfg(daemon->respip_set, daemon->cfg))
+ fatal_exit("Could not set up response IP set");
+ if(!respip_views_apply_cfg(daemon->views, daemon->cfg,
+ &have_view_respip_cfg))
+ fatal_exit("Could not set up per-view response IP sets");
+ daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) ||
+ have_view_respip_cfg;
+
/* setup modules */
daemon_setup_modules(daemon);
+ /* response-ip-xxx options don't work as expected without the respip
+ * module. To avoid run-time operational surprise we reject such
+ * configuration. */
+ if(daemon->use_response_ip &&
+ modstack_find(&daemon->mods, "respip") < 0)
+ fatal_exit("response-ip options require respip module");
+
/* first create all the worker structures, so we can pass
* them to the newly created threads.
*/
@@ -567,14 +638,26 @@ daemon_fork(struct daemon* daemon)
#endif
signal_handling_playback(daemon->workers[0]);
+ if (!shm_main_init(daemon))
+ log_warn("SHM has failed");
+
/* Start resolver service on main thread. */
+#ifdef HAVE_SYSTEMD
+ sd_notify(0, "READY=1");
+#endif
log_info("start of service (%s).", PACKAGE_STRING);
worker_work(daemon->workers[0]);
+#ifdef HAVE_SYSTEMD
+ sd_notify(0, "STOPPING=1");
+#endif
log_info("service stopped (%s).", PACKAGE_STRING);
/* we exited! a signal happened! Stop other threads */
daemon_stop_others(daemon);
+ /* Shutdown SHM */
+ shm_main_shutdown(daemon);
+
daemon->need_to_exit = daemon->workers[0]->need_to_exit;
}
@@ -589,13 +672,16 @@ daemon_cleanup(struct daemon* daemon)
log_thread_set(NULL);
/* clean up caches because
* a) RRset IDs will be recycled after a reload, causing collisions
- * b) validation config can change, thus rrset, msg, keycache clear
- * The infra cache is kept, the timing and edns info is still valid */
+ * b) validation config can change, thus rrset, msg, keycache clear */
slabhash_clear(&daemon->env->rrset_cache->table);
slabhash_clear(daemon->env->msg_cache);
local_zones_delete(daemon->local_zones);
daemon->local_zones = NULL;
- /* key cache is cleared by module desetup during next daemon_init() */
+ respip_set_delete(daemon->respip_set);
+ daemon->respip_set = NULL;
+ views_delete(daemon->views);
+ daemon->views = NULL;
+ /* key cache is cleared by module desetup during next daemon_fork() */
daemon_remote_clear(daemon->rc);
for(i=0; i<daemon->num; i++)
worker_delete(daemon->workers[i]);
@@ -624,6 +710,7 @@ daemon_delete(struct daemon* daemon)
slabhash_delete(daemon->env->msg_cache);
rrset_cache_delete(daemon->env->rrset_cache);
infra_delete(daemon->env->infra_cache);
+ edns_known_options_delete(daemon->env);
}
ub_randfree(daemon->rand);
alloc_clear(&daemon->superalloc);
@@ -647,18 +734,27 @@ daemon_delete(struct daemon* daemon)
# endif
# if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS && HAVE_DECL_SK_SSL_COMP_POP_FREE
# ifndef S_SPLINT_S
+# if OPENSSL_VERSION_NUMBER < 0x10100000
sk_SSL_COMP_pop_free(comp_meth, (void(*)())CRYPTO_free);
+# endif
# endif
# endif
# ifdef HAVE_OPENSSL_CONFIG
EVP_cleanup();
+# if OPENSSL_VERSION_NUMBER < 0x10100000
ENGINE_cleanup();
+# endif
CONF_modules_free();
# endif
+# ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
CRYPTO_cleanup_all_ex_data(); /* safe, no more threads right now */
- ERR_remove_state(0);
+# endif
+# ifdef HAVE_ERR_FREE_STRINGS
ERR_free_strings();
+# endif
+# if OPENSSL_VERSION_NUMBER < 0x10100000
RAND_cleanup();
+# endif
# if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED)
ub_openssl_lock_delete();
# endif
diff --git a/external/unbound/daemon/daemon.h b/external/unbound/daemon/daemon.h
index 86ddab1df..71e5bb96d 100644
--- a/external/unbound/daemon/daemon.h
+++ b/external/unbound/daemon/daemon.h
@@ -56,14 +56,22 @@ struct module_env;
struct rrset_cache;
struct acl_list;
struct local_zones;
+struct views;
struct ub_randstate;
struct daemon_remote;
+struct respip_set;
+struct shm_main_info;
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
struct dt_env;
#endif
+#include "dnscrypt/dnscrypt_config.h"
+#ifdef USE_DNSCRYPT
+struct dnsc_env;
+#endif
+
/**
* Structure holding worker list.
* Holds globally visible information.
@@ -114,10 +122,21 @@ struct daemon {
struct timeval time_last_stat;
/** time when daemon started */
struct timeval time_boot;
+ /** views structure containing view tree */
+ struct views* views;
#ifdef USE_DNSTAP
/** the dnstap environment master value, copied and changed by threads*/
struct dt_env* dtenv;
#endif
+ struct shm_main_info* shm_info;
+ /** response-ip set with associated actions and tags. */
+ struct respip_set* respip_set;
+ /** some response-ip tags or actions are configured if true */
+ int use_response_ip;
+#ifdef USE_DNSCRYPT
+ /** the dnscrypt environment */
+ struct dnsc_env* dnscenv;
+#endif
};
/**
diff --git a/external/unbound/daemon/remote.c b/external/unbound/daemon/remote.c
index d533e0867..c15967c20 100644
--- a/external/unbound/daemon/remote.c
+++ b/external/unbound/daemon/remote.c
@@ -46,9 +46,12 @@
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
-#ifndef HEADER_DH_H
+#ifdef HAVE_OPENSSL_DH_H
#include <openssl/dh.h>
#endif
+#ifdef HAVE_OPENSSL_BN_H
+#include <openssl/bn.h>
+#endif
#include <ctype.h>
#include "daemon/remote.h"
@@ -140,49 +143,68 @@ timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d)
/*
* The following function was generated using the openssl utility, using
- * the command : "openssl dhparam -dsaparam -C 1024"
+ * the command : "openssl dhparam -C 2048"
* (some openssl versions reject DH that is 'too small', eg. 512).
*/
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL)
#ifndef S_SPLINT_S
-DH *get_dh1024()
-{
- static unsigned char dh1024_p[]={
- 0xB3,0x67,0x2E,0x3B,0x68,0xC5,0xDA,0x58,0x46,0xD6,0x2B,0xD3,
- 0x41,0x78,0x97,0xE4,0xE1,0x61,0x71,0x68,0xE6,0x0F,0x1D,0x78,
- 0x05,0xAA,0xF0,0xFF,0x30,0xDF,0xAC,0x49,0x7F,0xE0,0x90,0xFE,
- 0xB9,0x56,0x4E,0x3F,0xE2,0x98,0x8A,0xED,0xF5,0x28,0x39,0xEF,
- 0x2E,0xA6,0xB7,0x67,0xB2,0x43,0xE4,0x53,0xF8,0xEB,0x2C,0x1F,
- 0x06,0x77,0x3A,0x6F,0x62,0x98,0xC1,0x3B,0xF7,0xBA,0x4D,0x93,
- 0xF7,0xEB,0x5A,0xAD,0xC5,0x5F,0xF0,0xB7,0x24,0x35,0x81,0xF7,
- 0x7F,0x1F,0x24,0xC0,0xDF,0xD3,0xD8,0x40,0x72,0x7E,0xF3,0x19,
- 0x2B,0x26,0x27,0xF4,0xB6,0xB3,0xD4,0x7D,0x08,0x23,0xBE,0x68,
- 0x2B,0xCA,0xB4,0x46,0xA8,0x9E,0xDD,0x6C,0x3D,0x75,0xA6,0x48,
- 0xF7,0x44,0x43,0xBF,0x91,0xC2,0xB4,0x49,
+static DH *get_dh2048(void)
+{
+ static unsigned char dh2048_p[]={
+ 0xE7,0x36,0x28,0x3B,0xE4,0xC3,0x32,0x1C,0x01,0xC3,0x67,0xD6,
+ 0xF5,0xF3,0xDA,0xDC,0x71,0xC0,0x42,0x8B,0xE6,0xEB,0x8D,0x80,
+ 0x35,0x7F,0x09,0x45,0x30,0xE5,0xB2,0x92,0x81,0x3F,0x08,0xCD,
+ 0x36,0x5E,0x19,0x83,0x62,0xCC,0xAE,0x9B,0x81,0x66,0x24,0xEE,
+ 0x16,0x6F,0xA9,0x9E,0xF4,0x82,0x1B,0xDD,0x46,0xC7,0x33,0x5D,
+ 0xF4,0xCA,0xE6,0x8F,0xFC,0xD4,0xD8,0x58,0x94,0x24,0x5D,0xFF,
+ 0x0A,0xE8,0xEF,0x3D,0xCE,0xBB,0x50,0x94,0xE0,0x5F,0xE8,0x41,
+ 0xC3,0x35,0x30,0x37,0xD5,0xCB,0x8F,0x3D,0x95,0x15,0x1A,0x77,
+ 0x42,0xB2,0x06,0x86,0xF6,0x09,0x66,0x0E,0x9A,0x25,0x94,0x3E,
+ 0xD2,0x04,0x25,0x25,0x1D,0x23,0xEB,0xDC,0x4D,0x0C,0x83,0x28,
+ 0x2E,0x15,0x81,0x2D,0xC1,0xAF,0x8D,0x36,0x64,0xE3,0x9A,0x83,
+ 0x78,0xC2,0x8D,0xC0,0x9D,0xD9,0x3A,0x1C,0xC5,0x2B,0x50,0x68,
+ 0x07,0xA9,0x4B,0x8C,0x07,0x57,0xD6,0x15,0x03,0x4E,0x9E,0x01,
+ 0xF2,0x6F,0x35,0xAC,0x26,0x9C,0x92,0x68,0x61,0x13,0xFB,0x01,
+ 0xBA,0x22,0x36,0x01,0x55,0xB6,0x62,0xD9,0xB2,0x98,0xCE,0x5D,
+ 0x4B,0xA5,0x41,0xD6,0xE5,0x70,0x78,0x12,0x1F,0x64,0xB6,0x6F,
+ 0xB0,0x91,0x51,0x91,0x92,0xC0,0x94,0x3A,0xD1,0x28,0x4D,0x30,
+ 0x84,0x3E,0xE4,0xE4,0x7F,0x47,0x89,0xB1,0xB6,0x8C,0x8E,0x0E,
+ 0x26,0xDB,0xCD,0x17,0x07,0x2A,0x21,0x7A,0xCC,0x68,0xE8,0x57,
+ 0x94,0x9E,0x59,0x61,0xEC,0x20,0x34,0x26,0x0D,0x66,0x44,0xEB,
+ 0x6F,0x02,0x58,0xE2,0xED,0xF6,0xF3,0x1B,0xBF,0x9E,0x45,0x52,
+ 0x5A,0x49,0xA1,0x5B,
};
- static unsigned char dh1024_g[]={
- 0x5F,0x37,0xB5,0x80,0x4D,0xB4,0xC4,0xB2,0x37,0x12,0xD5,0x2F,
- 0x56,0x81,0xB0,0xDF,0x3D,0x27,0xA2,0x54,0xE7,0x14,0x65,0x2D,
- 0x72,0xA8,0x97,0xE0,0xA9,0x4A,0x09,0x5E,0x89,0xBE,0x34,0x9A,
- 0x90,0x98,0xC1,0xE8,0xBB,0x01,0x2B,0xC2,0x74,0x74,0x90,0x59,
- 0x0B,0x72,0x62,0x5C,0xFD,0x49,0x63,0x4B,0x38,0x91,0xF1,0x7F,
- 0x13,0x25,0xEB,0x52,0x50,0x47,0xA2,0x8C,0x32,0x28,0x42,0xAC,
- 0xBD,0x7A,0xCC,0x58,0xBE,0x36,0xDA,0x6A,0x24,0x06,0xC7,0xF1,
- 0xDA,0x8D,0x8A,0x3B,0x03,0xFA,0x6F,0x25,0xE5,0x20,0xA7,0xD6,
- 0x6F,0x74,0x61,0x53,0x14,0x81,0x29,0x04,0xB5,0x61,0x12,0x53,
- 0xA3,0xD6,0x09,0x98,0x0C,0x8F,0x1C,0xBB,0xD7,0x1C,0x2C,0xEE,
- 0x56,0x4B,0x74,0x8F,0x4A,0xF8,0xA9,0xD5,
+ static unsigned char dh2048_g[]={
+ 0x02,
};
- DH *dh;
-
- if ((dh=DH_new()) == NULL) return(NULL);
- dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL);
- dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL);
- if ((dh->p == NULL) || (dh->g == NULL))
- { DH_free(dh); return(NULL); }
- dh->length = 160;
- return(dh);
+ DH *dh = NULL;
+ BIGNUM *p = NULL, *g = NULL;
+
+ dh = DH_new();
+ p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
+ g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
+ if (!dh || !p || !g)
+ goto err;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL)
+ dh->p = p;
+ dh->g = g;
+#else
+ if (!DH_set0_pqg(dh, p, NULL, g))
+ goto err;
+#endif
+ return dh;
+err:
+ if (p)
+ BN_free(p);
+ if (g)
+ BN_free(g);
+ if (dh)
+ DH_free(dh);
+ return NULL;
}
#endif /* SPLINT */
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
struct daemon_remote*
daemon_remote_create(struct config_file* cfg)
@@ -220,21 +242,53 @@ daemon_remote_create(struct config_file* cfg)
daemon_remote_delete(rc);
return NULL;
}
+#if defined(SSL_OP_NO_TLSv1) && defined(SSL_OP_NO_TLSv1_1)
+ /* if we have tls 1.1 disable 1.0 */
+ if((SSL_CTX_set_options(rc->ctx, SSL_OP_NO_TLSv1) & SSL_OP_NO_TLSv1)
+ != SSL_OP_NO_TLSv1){
+ log_crypto_err("could not set SSL_OP_NO_TLSv1");
+ daemon_remote_delete(rc);
+ return NULL;
+ }
+#endif
+#if defined(SSL_OP_NO_TLSv1_1) && defined(SSL_OP_NO_TLSv1_2)
+ /* if we have tls 1.2 disable 1.1 */
+ if((SSL_CTX_set_options(rc->ctx, SSL_OP_NO_TLSv1_1) & SSL_OP_NO_TLSv1_1)
+ != SSL_OP_NO_TLSv1_1){
+ log_crypto_err("could not set SSL_OP_NO_TLSv1_1");
+ daemon_remote_delete(rc);
+ return NULL;
+ }
+#endif
+#ifdef SHA256_DIGEST_LENGTH
+ /* if we have sha256, set the cipher list to have no known vulns */
+ if(!SSL_CTX_set_cipher_list(rc->ctx, "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"))
+ log_crypto_err("coult not set cipher list with SSL_CTX_set_cipher_list");
+#endif
if (cfg->remote_control_use_cert == 0) {
/* No certificates are requested */
- if(!SSL_CTX_set_cipher_list(rc->ctx, "aNULL")) {
+#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
+ SSL_CTX_set_security_level(rc->ctx, 0);
+#endif
+ if(!SSL_CTX_set_cipher_list(rc->ctx, "aNULL, eNULL")) {
log_crypto_err("Failed to set aNULL cipher list");
+ daemon_remote_delete(rc);
return NULL;
}
+ /* in openssl 1.1, the securitylevel 0 allows eNULL, that
+ * does not need the DH */
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL)
/* Since we have no certificates and hence no source of
* DH params, let's generate and set them
*/
- if(!SSL_CTX_set_tmp_dh(rc->ctx,get_dh1024())) {
+ if(!SSL_CTX_set_tmp_dh(rc->ctx,get_dh2048())) {
log_crypto_err("Wanted to set DH param, but failed");
+ daemon_remote_delete(rc);
return NULL;
}
+#endif
return rc;
}
rc->use_cert = 1;
@@ -350,7 +404,7 @@ add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err,
if(ip[0] == '/') {
/* This looks like a local socket */
- fd = create_local_accept_sock(ip, &noproto);
+ fd = create_local_accept_sock(ip, &noproto, cfg->use_systemd);
/*
* Change socket ownership and permissions so users other
* than root can access it provided they are in the same
@@ -359,8 +413,12 @@ add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err,
if(fd != -1) {
#ifdef HAVE_CHOWN
if (cfg->username && cfg->username[0] &&
- cfg_uid != (uid_t)-1)
- chown(ip, cfg_uid, cfg_gid);
+ cfg_uid != (uid_t)-1) {
+ if(chown(ip, cfg_uid, cfg_gid) == -1)
+ log_err("cannot chown %u.%u %s: %s",
+ (unsigned)cfg_uid, (unsigned)cfg_gid,
+ ip, strerror(errno));
+ }
chmod(ip, (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
#else
(void)cfg;
@@ -389,7 +447,7 @@ add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err,
/* open fd */
fd = create_tcp_accept_sock(res, 1, &noproto, 0,
- cfg->ip_transparent);
+ cfg->ip_transparent, 0, cfg->ip_freebind, cfg->use_systemd);
freeaddrinfo(res);
}
@@ -727,6 +785,8 @@ print_stats(SSL* ssl, const char* nm, struct stats_info* s)
struct timeval avg;
if(!ssl_printf(ssl, "%s.num.queries"SQ"%lu\n", nm,
(unsigned long)s->svr.num_queries)) return 0;
+ if(!ssl_printf(ssl, "%s.num.queries_ip_ratelimited"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_queries_ip_ratelimited)) return 0;
if(!ssl_printf(ssl, "%s.num.cachehits"SQ"%lu\n", nm,
(unsigned long)(s->svr.num_queries
- s->svr.num_queries_missed_cache))) return 0;
@@ -734,8 +794,20 @@ print_stats(SSL* ssl, const char* nm, struct stats_info* s)
(unsigned long)s->svr.num_queries_missed_cache)) return 0;
if(!ssl_printf(ssl, "%s.num.prefetch"SQ"%lu\n", nm,
(unsigned long)s->svr.num_queries_prefetch)) return 0;
+ if(!ssl_printf(ssl, "%s.num.zero_ttl"SQ"%lu\n", nm,
+ (unsigned long)s->svr.zero_ttl_responses)) return 0;
if(!ssl_printf(ssl, "%s.num.recursivereplies"SQ"%lu\n", nm,
(unsigned long)s->mesh_replies_sent)) return 0;
+#ifdef USE_DNSCRYPT
+ if(!ssl_printf(ssl, "%s.num.dnscrypt.crypted"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_query_dnscrypt_crypted)) return 0;
+ if(!ssl_printf(ssl, "%s.num.dnscrypt.cert"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_query_dnscrypt_cert)) return 0;
+ if(!ssl_printf(ssl, "%s.num.dnscrypt.cleartext"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_query_dnscrypt_cleartext)) return 0;
+ if(!ssl_printf(ssl, "%s.num.dnscrypt.malformed"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_query_dnscrypt_crypted_malformed)) return 0;
+#endif
if(!ssl_printf(ssl, "%s.requestlist.avg"SQ"%g\n", nm,
(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
(double)s->svr.sum_query_list_size/
@@ -791,17 +863,15 @@ static int
print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon)
{
int m;
- size_t msg, rrset, val, iter;
-#ifdef HAVE_SBRK
- extern void* unbound_start_brk;
- void* cur = sbrk(0);
- if(!print_longnum(ssl, "mem.total.sbrk"SQ,
- (size_t)((char*)cur - (char*)unbound_start_brk))) return 0;
-#endif /* HAVE_SBRK */
+ size_t msg, rrset, val, iter, respip;
+#ifdef CLIENT_SUBNET
+ size_t subnet = 0;
+#endif /* CLIENT_SUBNET */
msg = slabhash_get_mem(daemon->env->msg_cache);
rrset = slabhash_get_mem(&daemon->env->rrset_cache->table);
val=0;
iter=0;
+ respip=0;
m = modstack_find(&worker->env.mesh->mods, "validator");
if(m != -1) {
fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
@@ -816,6 +886,22 @@ print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon)
iter = (*worker->env.mesh->mods.mod[m]->get_mem)
(&worker->env, m);
}
+ m = modstack_find(&worker->env.mesh->mods, "respip");
+ if(m != -1) {
+ fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
+ mods.mod[m]->get_mem));
+ respip = (*worker->env.mesh->mods.mod[m]->get_mem)
+ (&worker->env, m);
+ }
+#ifdef CLIENT_SUBNET
+ m = modstack_find(&worker->env.mesh->mods, "subnet");
+ if(m != -1) {
+ fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
+ mods.mod[m]->get_mem));
+ subnet = (*worker->env.mesh->mods.mod[m]->get_mem)
+ (&worker->env, m);
+ }
+#endif /* CLIENT_SUBNET */
if(!print_longnum(ssl, "mem.cache.rrset"SQ, rrset))
return 0;
@@ -825,6 +911,12 @@ print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon)
return 0;
if(!print_longnum(ssl, "mem.mod.validator"SQ, val))
return 0;
+ if(!print_longnum(ssl, "mem.mod.respip"SQ, respip))
+ return 0;
+#ifdef CLIENT_SUBNET
+ if(!print_longnum(ssl, "mem.mod.subnet"SQ, subnet))
+ return 0;
+#endif /* CLIENT_SUBNET */
return 1;
}
@@ -1097,8 +1189,8 @@ find_arg2(SSL* ssl, char* arg, char** arg2)
}
/** Add a new zone */
-static void
-do_zone_add(SSL* ssl, struct worker* worker, char* arg)
+static int
+perform_zone_add(SSL* ssl, struct local_zones* zones, char* arg)
{
uint8_t* nm;
int nmlabs;
@@ -1107,83 +1199,290 @@ do_zone_add(SSL* ssl, struct worker* worker, char* arg)
enum localzone_type t;
struct local_zone* z;
if(!find_arg2(ssl, arg, &arg2))
- return;
+ return 0;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
- return;
+ return 0;
if(!local_zone_str2type(arg2, &t)) {
ssl_printf(ssl, "error not a zone type. %s\n", arg2);
free(nm);
- return;
+ return 0;
}
- lock_rw_wrlock(&worker->daemon->local_zones->lock);
- if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen,
+ lock_rw_wrlock(&zones->lock);
+ if((z=local_zones_find(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN))) {
/* already present in tree */
lock_rw_wrlock(&z->lock);
z->type = t; /* update type anyway */
lock_rw_unlock(&z->lock);
free(nm);
- lock_rw_unlock(&worker->daemon->local_zones->lock);
- send_ok(ssl);
- return;
+ lock_rw_unlock(&zones->lock);
+ return 1;
}
- if(!local_zones_add_zone(worker->daemon->local_zones, nm, nmlen,
+ if(!local_zones_add_zone(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN, t)) {
- lock_rw_unlock(&worker->daemon->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
ssl_printf(ssl, "error out of memory\n");
- return;
+ return 0;
}
- lock_rw_unlock(&worker->daemon->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
+ return 1;
+}
+
+/** Do the local_zone command */
+static void
+do_zone_add(SSL* ssl, struct local_zones* zones, char* arg)
+{
+ if(!perform_zone_add(ssl, zones, arg))
+ return;
send_ok(ssl);
}
-/** Remove a zone */
+/** Do the local_zones command */
static void
-do_zone_remove(SSL* ssl, struct worker* worker, char* arg)
+do_zones_add(SSL* ssl, struct local_zones* zones)
+{
+ char buf[2048];
+ int num = 0;
+ while(ssl_read_line(ssl, buf, sizeof(buf))) {
+ if(buf[0] == 0x04 && buf[1] == 0)
+ break; /* end of transmission */
+ if(!perform_zone_add(ssl, zones, buf)) {
+ if(!ssl_printf(ssl, "error for input line: %s\n", buf))
+ return;
+ }
+ else
+ num++;
+ }
+ (void)ssl_printf(ssl, "added %d zones\n", num);
+}
+
+/** Remove a zone */
+static int
+perform_zone_remove(SSL* ssl, struct local_zones* zones, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
struct local_zone* z;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
- return;
- lock_rw_wrlock(&worker->daemon->local_zones->lock);
- if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen,
+ return 0;
+ lock_rw_wrlock(&zones->lock);
+ if((z=local_zones_find(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN))) {
/* present in tree */
- local_zones_del_zone(worker->daemon->local_zones, z);
+ local_zones_del_zone(zones, z);
}
- lock_rw_unlock(&worker->daemon->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
free(nm);
+ return 1;
+}
+
+/** Do the local_zone_remove command */
+static void
+do_zone_remove(SSL* ssl, struct local_zones* zones, char* arg)
+{
+ if(!perform_zone_remove(ssl, zones, arg))
+ return;
send_ok(ssl);
}
-/** Add new RR data */
+/** Do the local_zones_remove command */
static void
-do_data_add(SSL* ssl, struct worker* worker, char* arg)
+do_zones_remove(SSL* ssl, struct local_zones* zones)
+{
+ char buf[2048];
+ int num = 0;
+ while(ssl_read_line(ssl, buf, sizeof(buf))) {
+ if(buf[0] == 0x04 && buf[1] == 0)
+ break; /* end of transmission */
+ if(!perform_zone_remove(ssl, zones, buf)) {
+ if(!ssl_printf(ssl, "error for input line: %s\n", buf))
+ return;
+ }
+ else
+ num++;
+ }
+ (void)ssl_printf(ssl, "removed %d zones\n", num);
+}
+
+/** Add new RR data */
+static int
+perform_data_add(SSL* ssl, struct local_zones* zones, char* arg)
{
- if(!local_zones_add_RR(worker->daemon->local_zones, arg)) {
+ if(!local_zones_add_RR(zones, arg)) {
ssl_printf(ssl,"error in syntax or out of memory, %s\n", arg);
- return;
+ return 0;
}
+ return 1;
+}
+
+/** Do the local_data command */
+static void
+do_data_add(SSL* ssl, struct local_zones* zones, char* arg)
+{
+ if(!perform_data_add(ssl, zones, arg))
+ return;
send_ok(ssl);
}
-/** Remove RR data */
+/** Do the local_datas command */
static void
-do_data_remove(SSL* ssl, struct worker* worker, char* arg)
+do_datas_add(SSL* ssl, struct local_zones* zones)
+{
+ char buf[2048];
+ int num = 0;
+ while(ssl_read_line(ssl, buf, sizeof(buf))) {
+ if(buf[0] == 0x04 && buf[1] == 0)
+ break; /* end of transmission */
+ if(!perform_data_add(ssl, zones, buf)) {
+ if(!ssl_printf(ssl, "error for input line: %s\n", buf))
+ return;
+ }
+ else
+ num++;
+ }
+ (void)ssl_printf(ssl, "added %d datas\n", num);
+}
+
+/** Remove RR data */
+static int
+perform_data_remove(SSL* ssl, struct local_zones* zones, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
- return;
- local_zones_del_data(worker->daemon->local_zones, nm,
+ return 0;
+ local_zones_del_data(zones, nm,
nmlen, nmlabs, LDNS_RR_CLASS_IN);
free(nm);
+ return 1;
+}
+
+/** Do the local_data_remove command */
+static void
+do_data_remove(SSL* ssl, struct local_zones* zones, char* arg)
+{
+ if(!perform_data_remove(ssl, zones, arg))
+ return;
send_ok(ssl);
}
+/** Do the local_datas_remove command */
+static void
+do_datas_remove(SSL* ssl, struct local_zones* zones)
+{
+ char buf[2048];
+ int num = 0;
+ while(ssl_read_line(ssl, buf, sizeof(buf))) {
+ if(buf[0] == 0x04 && buf[1] == 0)
+ break; /* end of transmission */
+ if(!perform_data_remove(ssl, zones, buf)) {
+ if(!ssl_printf(ssl, "error for input line: %s\n", buf))
+ return;
+ }
+ else
+ num++;
+ }
+ (void)ssl_printf(ssl, "removed %d datas\n", num);
+}
+
+/** Add a new zone to view */
+static void
+do_view_zone_add(SSL* ssl, struct worker* worker, char* arg)
+{
+ char* arg2;
+ struct view* v;
+ if(!find_arg2(ssl, arg, &arg2))
+ return;
+ v = views_find_view(worker->daemon->views,
+ arg, 1 /* get write lock*/);
+ if(!v) {
+ ssl_printf(ssl,"no view with name: %s\n", arg);
+ return;
+ }
+ if(!v->local_zones) {
+ if(!(v->local_zones = local_zones_create())){
+ lock_rw_unlock(&v->lock);
+ ssl_printf(ssl,"error out of memory\n");
+ return;
+ }
+ }
+ do_zone_add(ssl, v->local_zones, arg2);
+ lock_rw_unlock(&v->lock);
+}
+
+/** Remove a zone from view */
+static void
+do_view_zone_remove(SSL* ssl, struct worker* worker, char* arg)
+{
+ char* arg2;
+ struct view* v;
+ if(!find_arg2(ssl, arg, &arg2))
+ return;
+ v = views_find_view(worker->daemon->views,
+ arg, 1 /* get write lock*/);
+ if(!v) {
+ ssl_printf(ssl,"no view with name: %s\n", arg);
+ return;
+ }
+ if(!v->local_zones) {
+ lock_rw_unlock(&v->lock);
+ send_ok(ssl);
+ return;
+ }
+ do_zone_remove(ssl, v->local_zones, arg2);
+ lock_rw_unlock(&v->lock);
+}
+
+/** Add new RR data to view */
+static void
+do_view_data_add(SSL* ssl, struct worker* worker, char* arg)
+{
+ char* arg2;
+ struct view* v;
+ if(!find_arg2(ssl, arg, &arg2))
+ return;
+ v = views_find_view(worker->daemon->views,
+ arg, 1 /* get write lock*/);
+ if(!v) {
+ ssl_printf(ssl,"no view with name: %s\n", arg);
+ return;
+ }
+ if(!v->local_zones) {
+ if(!(v->local_zones = local_zones_create())){
+ lock_rw_unlock(&v->lock);
+ ssl_printf(ssl,"error out of memory\n");
+ return;
+ }
+ }
+ do_data_add(ssl, v->local_zones, arg2);
+ lock_rw_unlock(&v->lock);
+}
+
+/** Remove RR data from view */
+static void
+do_view_data_remove(SSL* ssl, struct worker* worker, char* arg)
+{
+ char* arg2;
+ struct view* v;
+ if(!find_arg2(ssl, arg, &arg2))
+ return;
+ v = views_find_view(worker->daemon->views,
+ arg, 1 /* get write lock*/);
+ if(!v) {
+ ssl_printf(ssl,"no view with name: %s\n", arg);
+ return;
+ }
+ if(!v->local_zones) {
+ lock_rw_unlock(&v->lock);
+ send_ok(ssl);
+ return;
+ }
+ do_data_remove(ssl, v->local_zones, arg2);
+ lock_rw_unlock(&v->lock);
+}
+
/** cache lookup of nameservers */
static void
do_lookup(SSL* ssl, struct worker* worker, char* arg)
@@ -1202,7 +1501,7 @@ static void
do_cache_remove(struct worker* worker, uint8_t* nm, size_t nmlen,
uint16_t t, uint16_t c)
{
- hashvalue_t h;
+ hashvalue_type h;
struct query_info k;
rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, t, c, 0);
if(t == LDNS_RR_TYPE_SOA)
@@ -1212,6 +1511,7 @@ do_cache_remove(struct worker* worker, uint8_t* nm, size_t nmlen,
k.qname_len = nmlen;
k.qtype = t;
k.qclass = c;
+ k.local_alias = NULL;
h = query_info_hash(&k, 0);
slabhash_remove(worker->env.msg_cache, h, &k);
if(t == LDNS_RR_TYPE_AAAA) {
@@ -2157,6 +2457,14 @@ do_set_option(SSL* ssl, struct worker* worker, char* arg)
(void)ssl_printf(ssl, "error setting option\n");
return;
}
+ /* effectuate some arguments */
+ if(strcmp(arg, "val-override-date:") == 0) {
+ int m = modstack_find(&worker->env.mesh->mods, "validator");
+ struct val_env* val_env = NULL;
+ if(m != -1) val_env = (struct val_env*)worker->env.modinfo[m];
+ if(val_env)
+ val_env->date_override = worker->env.cfg->val_date_override;
+ }
send_ok(ssl);
}
@@ -2237,9 +2545,8 @@ do_list_stubs(SSL* ssl, struct worker* worker)
/** do the list_local_zones command */
static void
-do_list_local_zones(SSL* ssl, struct worker* worker)
+do_list_local_zones(SSL* ssl, struct local_zones* zones)
{
- struct local_zones* zones = worker->daemon->local_zones;
struct local_zone* z;
char buf[257];
lock_rw_rdlock(&zones->lock);
@@ -2260,9 +2567,8 @@ do_list_local_zones(SSL* ssl, struct worker* worker)
/** do the list_local_data command */
static void
-do_list_local_data(SSL* ssl, struct worker* worker)
+do_list_local_data(SSL* ssl, struct worker* worker, struct local_zones* zones)
{
- struct local_zones* zones = worker->daemon->local_zones;
struct local_zone* z;
struct local_data* d;
struct local_rrset* p;
@@ -2298,6 +2604,38 @@ do_list_local_data(SSL* ssl, struct worker* worker)
lock_rw_unlock(&zones->lock);
}
+/** do the view_list_local_zones command */
+static void
+do_view_list_local_zones(SSL* ssl, struct worker* worker, char* arg)
+{
+ struct view* v = views_find_view(worker->daemon->views,
+ arg, 0 /* get read lock*/);
+ if(!v) {
+ ssl_printf(ssl,"no view with name: %s\n", arg);
+ return;
+ }
+ if(v->local_zones) {
+ do_list_local_zones(ssl, v->local_zones);
+ }
+ lock_rw_unlock(&v->lock);
+}
+
+/** do the view_list_local_data command */
+static void
+do_view_list_local_data(SSL* ssl, struct worker* worker, char* arg)
+{
+ struct view* v = views_find_view(worker->daemon->views,
+ arg, 0 /* get read lock*/);
+ if(!v) {
+ ssl_printf(ssl,"no view with name: %s\n", arg);
+ return;
+ }
+ if(v->local_zones) {
+ do_list_local_data(ssl, worker, v->local_zones);
+ }
+ lock_rw_unlock(&v->lock);
+}
+
/** struct for user arg ratelimit list */
struct ratelimit_list_arg {
/** the infra cache */
@@ -2310,6 +2648,8 @@ struct ratelimit_list_arg {
time_t now;
};
+#define ip_ratelimit_list_arg ratelimit_list_arg
+
/** list items in the ratelimit table */
static void
rate_list(struct lruhash_entry* e, void* arg)
@@ -2328,6 +2668,24 @@ rate_list(struct lruhash_entry* e, void* arg)
ssl_printf(a->ssl, "%s %d limit %d\n", buf, max, lim);
}
+/** list items in the ip_ratelimit table */
+static void
+ip_rate_list(struct lruhash_entry* e, void* arg)
+{
+ char ip[128];
+ struct ip_ratelimit_list_arg* a = (struct ip_ratelimit_list_arg*)arg;
+ struct ip_rate_key* k = (struct ip_rate_key*)e->key;
+ struct ip_rate_data* d = (struct ip_rate_data*)e->data;
+ int lim = infra_ip_ratelimit;
+ int max = infra_rate_max(d, a->now);
+ if(a->all == 0) {
+ if(max < lim)
+ return;
+ }
+ addr_to_str(&k->addr, k->addrlen, ip, sizeof(ip));
+ ssl_printf(a->ssl, "%s %d limit %d\n", ip, max, lim);
+}
+
/** do the ratelimit_list command */
static void
do_ratelimit_list(SSL* ssl, struct worker* worker, char* arg)
@@ -2346,6 +2704,24 @@ do_ratelimit_list(SSL* ssl, struct worker* worker, char* arg)
slabhash_traverse(a.infra->domain_rates, 0, rate_list, &a);
}
+/** do the ip_ratelimit_list command */
+static void
+do_ip_ratelimit_list(SSL* ssl, struct worker* worker, char* arg)
+{
+ struct ip_ratelimit_list_arg a;
+ a.all = 0;
+ a.infra = worker->env.infra_cache;
+ a.now = *worker->env.now;
+ a.ssl = ssl;
+ arg = skipwhite(arg);
+ if(strcmp(arg, "+a") == 0)
+ a.all = 1;
+ if(a.infra->client_ip_rates==NULL ||
+ (a.all == 0 && infra_ip_ratelimit == 0))
+ return;
+ slabhash_traverse(a.infra->client_ip_rates, 0, ip_rate_list, &a);
+}
+
/** tell other processes to execute the command */
static void
distribute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd)
@@ -2410,14 +2786,23 @@ execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd,
do_insecure_list(ssl, worker);
return;
} else if(cmdcmp(p, "list_local_zones", 16)) {
- do_list_local_zones(ssl, worker);
+ do_list_local_zones(ssl, worker->daemon->local_zones);
return;
} else if(cmdcmp(p, "list_local_data", 15)) {
- do_list_local_data(ssl, worker);
+ do_list_local_data(ssl, worker, worker->daemon->local_zones);
+ return;
+ } else if(cmdcmp(p, "view_list_local_zones", 21)) {
+ do_view_list_local_zones(ssl, worker, skipwhite(p+21));
+ return;
+ } else if(cmdcmp(p, "view_list_local_data", 20)) {
+ do_view_list_local_data(ssl, worker, skipwhite(p+20));
return;
} else if(cmdcmp(p, "ratelimit_list", 14)) {
do_ratelimit_list(ssl, worker, p+14);
return;
+ } else if(cmdcmp(p, "ip_ratelimit_list", 17)) {
+ do_ip_ratelimit_list(ssl, worker, p+17);
+ return;
} else if(cmdcmp(p, "stub_add", 8)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
@@ -2479,13 +2864,29 @@ execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd,
if(cmdcmp(p, "verbosity", 9)) {
do_verbosity(ssl, skipwhite(p+9));
} else if(cmdcmp(p, "local_zone_remove", 17)) {
- do_zone_remove(ssl, worker, skipwhite(p+17));
+ do_zone_remove(ssl, worker->daemon->local_zones, skipwhite(p+17));
+ } else if(cmdcmp(p, "local_zones_remove", 18)) {
+ do_zones_remove(ssl, worker->daemon->local_zones);
} else if(cmdcmp(p, "local_zone", 10)) {
- do_zone_add(ssl, worker, skipwhite(p+10));
+ do_zone_add(ssl, worker->daemon->local_zones, skipwhite(p+10));
+ } else if(cmdcmp(p, "local_zones", 11)) {
+ do_zones_add(ssl, worker->daemon->local_zones);
} else if(cmdcmp(p, "local_data_remove", 17)) {
- do_data_remove(ssl, worker, skipwhite(p+17));
+ do_data_remove(ssl, worker->daemon->local_zones, skipwhite(p+17));
+ } else if(cmdcmp(p, "local_datas_remove", 18)) {
+ do_datas_remove(ssl, worker->daemon->local_zones);
} else if(cmdcmp(p, "local_data", 10)) {
- do_data_add(ssl, worker, skipwhite(p+10));
+ do_data_add(ssl, worker->daemon->local_zones, skipwhite(p+10));
+ } else if(cmdcmp(p, "local_datas", 11)) {
+ do_datas_add(ssl, worker->daemon->local_zones);
+ } else if(cmdcmp(p, "view_local_zone_remove", 22)) {
+ do_view_zone_remove(ssl, worker, skipwhite(p+22));
+ } else if(cmdcmp(p, "view_local_zone", 15)) {
+ do_view_zone_add(ssl, worker, skipwhite(p+15));
+ } else if(cmdcmp(p, "view_local_data_remove", 22)) {
+ do_view_data_remove(ssl, worker, skipwhite(p+22));
+ } else if(cmdcmp(p, "view_local_data", 15)) {
+ do_view_data_add(ssl, worker, skipwhite(p+15));
} else if(cmdcmp(p, "flush_zone", 10)) {
do_flush_zone(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "flush_type", 10)) {
diff --git a/external/unbound/daemon/remote.h b/external/unbound/daemon/remote.h
index b25bfb1af..190286d47 100644
--- a/external/unbound/daemon/remote.h
+++ b/external/unbound/daemon/remote.h
@@ -56,8 +56,8 @@ struct comm_reply;
struct comm_point;
struct daemon_remote;
-/** number of seconds timeout on incoming remote control handshake */
-#define REMOTE_CONTROL_TCP_TIMEOUT 120
+/** number of milliseconds timeout on incoming remote control handshake */
+#define REMOTE_CONTROL_TCP_TIMEOUT 120000
/**
* a busy control command connection, SSL state
diff --git a/external/unbound/daemon/stats.c b/external/unbound/daemon/stats.c
index 838cf05ae..3665616be 100644
--- a/external/unbound/daemon/stats.c
+++ b/external/unbound/daemon/stats.c
@@ -102,12 +102,14 @@ void server_stats_log(struct server_stats* stats, struct worker* worker,
int threadnum)
{
log_info("server stats for thread %d: %u queries, "
- "%u answers from cache, %u recursions, %u prefetch",
+ "%u answers from cache, %u recursions, %u prefetch, %u rejected by "
+ "ip ratelimiting",
threadnum, (unsigned)stats->num_queries,
(unsigned)(stats->num_queries -
stats->num_queries_missed_cache),
(unsigned)stats->num_queries_missed_cache,
- (unsigned)stats->num_queries_prefetch);
+ (unsigned)stats->num_queries_prefetch,
+ (unsigned)stats->num_queries_ip_ratelimited);
log_info("server stats for thread %d: requestlist max %u avg %g "
"exceeded %u jostled %u", threadnum,
(unsigned)stats->max_query_list_size,
@@ -226,9 +228,18 @@ void server_stats_reply(struct worker* worker, int reset)
void server_stats_add(struct stats_info* total, struct stats_info* a)
{
total->svr.num_queries += a->svr.num_queries;
+ total->svr.num_queries_ip_ratelimited += a->svr.num_queries_ip_ratelimited;
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
total->svr.sum_query_list_size += a->svr.sum_query_list_size;
+#ifdef USE_DNSCRYPT
+ total->svr.num_query_dnscrypt_crypted += a->svr.num_query_dnscrypt_crypted;
+ total->svr.num_query_dnscrypt_cert += a->svr.num_query_dnscrypt_cert;
+ total->svr.num_query_dnscrypt_cleartext += \
+ a->svr.num_query_dnscrypt_cleartext;
+ total->svr.num_query_dnscrypt_crypted_malformed += \
+ a->svr.num_query_dnscrypt_crypted_malformed;
+#endif
/* the max size reached is upped to higher of both */
if(a->svr.max_query_list_size > total->svr.max_query_list_size)
total->svr.max_query_list_size = a->svr.max_query_list_size;
@@ -251,6 +262,7 @@ void server_stats_add(struct stats_info* total, struct stats_info* a)
total->svr.qEDNS += a->svr.qEDNS;
total->svr.qEDNS_DO += a->svr.qEDNS_DO;
total->svr.ans_rcode_nodata += a->svr.ans_rcode_nodata;
+ total->svr.zero_ttl_responses += a->svr.zero_ttl_responses;
total->svr.ans_secure += a->svr.ans_secure;
total->svr.ans_bogus += a->svr.ans_bogus;
total->svr.rrset_bogus += a->svr.rrset_bogus;
diff --git a/external/unbound/daemon/stats.h b/external/unbound/daemon/stats.h
index 6985446ce..39c4d21c5 100644
--- a/external/unbound/daemon/stats.h
+++ b/external/unbound/daemon/stats.h
@@ -43,6 +43,7 @@
#ifndef DAEMON_STATS_H
#define DAEMON_STATS_H
#include "util/timehist.h"
+#include "dnscrypt/dnscrypt_config.h"
struct worker;
struct config_file;
struct comm_point;
@@ -63,6 +64,8 @@ struct sldns_buffer;
struct server_stats {
/** number of queries from clients received. */
size_t num_queries;
+ /** number of queries that have been dropped/ratelimited by ip. */
+ size_t num_queries_ip_ratelimited;
/** number of queries that had a cache-miss. */
size_t num_queries_missed_cache;
/** number of prefetch queries - cachehits with prefetch */
@@ -131,7 +134,8 @@ struct server_stats {
size_t unwanted_queries;
/** usage of tcp accept list */
size_t tcp_accept_usage;
-
+ /** answers served from expired cache */
+ size_t zero_ttl_responses;
/** histogram data exported to array
* if the array is the same size, no data is lost, and
* if all histograms are same size (is so by default) then
@@ -146,6 +150,16 @@ struct server_stats {
size_t infra_cache_count;
/** number of key cache entries */
size_t key_cache_count;
+#ifdef USE_DNSCRYPT
+ /** number of queries that used dnscrypt */
+ size_t num_query_dnscrypt_crypted;
+ /** number of queries that queried dnscrypt certificates */
+ size_t num_query_dnscrypt_cert;
+ /** number of queries in clear text and not asking for the certificates */
+ size_t num_query_dnscrypt_cleartext;
+ /** number of malformed encrypted queries */
+ size_t num_query_dnscrypt_crypted_malformed;
+#endif
};
/**
diff --git a/external/unbound/daemon/unbound.c b/external/unbound/daemon/unbound.c
index 0ceee538c..ba7337d89 100644
--- a/external/unbound/daemon/unbound.c
+++ b/external/unbound/daemon/unbound.c
@@ -57,6 +57,7 @@
#include "util/data/msgreply.h"
#include "util/module.h"
#include "util/net_help.h"
+#include "util/ub_event.h"
#include <signal.h>
#include <fcntl.h>
#include <openssl/crypto.h>
@@ -77,22 +78,6 @@
#include <login_cap.h>
#endif
-#ifdef USE_MINI_EVENT
-# ifdef USE_WINSOCK
-# include "util/winsock_event.h"
-# else
-# include "util/mini_event.h"
-# endif
-#else
-# ifdef HAVE_EVENT_H
-# include <event.h>
-# else
-# include "event2/event.h"
-# include "event2/event_struct.h"
-# include "event2/event_compat.h"
-# endif
-#endif
-
#ifdef UB_ON_WINDOWS
# include "winrc/win_svc.h"
#endif
@@ -102,64 +87,14 @@
# include "nss.h"
#endif
-#ifdef HAVE_SBRK
-/** global debug value to keep track of heap memory allocation */
-void* unbound_start_brk = 0;
-#endif
-
-#if !defined(HAVE_EVENT_BASE_GET_METHOD) && (defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP))
-static const char* ev_backend2str(int b)
-{
- switch(b) {
- case EVBACKEND_SELECT: return "select";
- case EVBACKEND_POLL: return "poll";
- case EVBACKEND_EPOLL: return "epoll";
- case EVBACKEND_KQUEUE: return "kqueue";
- case EVBACKEND_DEVPOLL: return "devpoll";
- case EVBACKEND_PORT: return "evport";
- }
- return "unknown";
-}
-#endif
-
-/** get the event system in use */
-static void get_event_sys(const char** n, const char** s, const char** m)
-{
-#ifdef USE_WINSOCK
- *n = "event";
- *s = "winsock";
- *m = "WSAWaitForMultipleEvents";
-#elif defined(USE_MINI_EVENT)
- *n = "mini-event";
- *s = "internal";
- *m = "select";
-#else
- struct event_base* b;
- *s = event_get_version();
-# ifdef HAVE_EVENT_BASE_GET_METHOD
- *n = "libevent";
- b = event_base_new();
- *m = event_base_get_method(b);
-# elif defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)
- *n = "libev";
- b = (struct event_base*)ev_default_loop(EVFLAG_AUTO);
- *m = ev_backend2str(ev_backend((struct ev_loop*)b));
-# else
- *n = "unknown";
- *m = "not obtainable";
- b = NULL;
-# endif
-# ifdef HAVE_EVENT_BASE_FREE
- event_base_free(b);
-# endif
-#endif
-}
-
/** print usage. */
-static void usage()
+static void usage(void)
{
const char** m;
const char *evnm="event", *evsys="", *evmethod="";
+ time_t t;
+ struct timeval now;
+ struct ub_event_base* base;
printf("usage: unbound [options]\n");
printf(" start unbound daemon DNS resolver.\n");
printf("-h this help\n");
@@ -173,11 +108,16 @@ static void usage()
printf(" service - used to start from services control panel\n");
#endif
printf("Version %s\n", PACKAGE_VERSION);
- get_event_sys(&evnm, &evsys, &evmethod);
+ base = ub_default_event_base(0,&t,&now);
+ ub_get_event_sys(base, &evnm, &evsys, &evmethod);
printf("linked libs: %s %s (it uses %s), %s\n",
evnm, evsys, evmethod,
#ifdef HAVE_SSL
+# ifdef SSLEAY_VERSION
SSLeay_version(SSLEAY_VERSION)
+# else
+ OpenSSL_version(OPENSSL_VERSION)
+# endif
#elif defined(HAVE_NSS)
NSS_GetVersion()
#elif defined(HAVE_NETTLE)
@@ -190,6 +130,7 @@ static void usage()
printf("\n");
printf("BSD licensed, see LICENSE in source package for details.\n");
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
+ ub_event_base_free(base);
}
#ifndef unbound_testbound
@@ -230,7 +171,7 @@ checkrlimits(struct config_file* cfg)
struct rlimit rlim;
if(total > 1024 &&
- strncmp(event_get_version(), "mini-event", 10) == 0) {
+ strncmp(ub_event_get_version(), "mini-event", 10) == 0) {
log_warn("too many file descriptors requested. The builtin"
"mini-event cannot handle more than 1024. Config "
"for less fds or compile with libevent");
@@ -244,7 +185,7 @@ checkrlimits(struct config_file* cfg)
total = 1024;
}
if(perthread > 64 &&
- strncmp(event_get_version(), "winsock-event", 13) == 0) {
+ strncmp(ub_event_get_version(), "winsock-event", 13) == 0) {
log_err("too many file descriptors requested. The winsock"
" event handler cannot handle more than 64 per "
" thread. Config for less fds");
@@ -298,19 +239,37 @@ checkrlimits(struct config_file* cfg)
#endif /* S_SPLINT_S */
}
+/** set default logfile identity based on value from argv[0] at startup **/
+static void
+log_ident_set_fromdefault(struct config_file* cfg,
+ const char *log_default_identity)
+{
+ if(cfg->log_identity == NULL || cfg->log_identity[0] == 0)
+ log_ident_set(log_default_identity);
+ else
+ log_ident_set(cfg->log_identity);
+}
+
/** set verbosity, check rlimits, cache settings */
static void
apply_settings(struct daemon* daemon, struct config_file* cfg,
- int cmdline_verbose, int debug_mode)
+ int cmdline_verbose, int debug_mode, const char* log_default_identity)
{
/* apply if they have changed */
verbosity = cmdline_verbose + cfg->verbosity;
if (debug_mode > 1) {
cfg->use_syslog = 0;
+ free(cfg->logfile);
cfg->logfile = NULL;
}
daemon_apply_cfg(daemon, cfg);
checkrlimits(cfg);
+
+ if (cfg->use_systemd && cfg->do_daemonize) {
+ log_warn("use-systemd and do-daemonize should not be enabled at the same time");
+ }
+
+ log_ident_set_fromdefault(cfg, log_default_identity);
}
#ifdef HAVE_KILL
@@ -443,6 +402,9 @@ static void
perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
const char** cfgfile)
{
+#ifdef HAVE_KILL
+ int pidinchroot;
+#endif
#ifdef HAVE_GETPWNAM
struct passwd *pwd = NULL;
@@ -481,6 +443,12 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
#endif
#ifdef HAVE_KILL
+ /* true if pidfile is inside chrootdir, or nochroot */
+ pidinchroot = !(cfg->chrootdir && cfg->chrootdir[0]) ||
+ (cfg->chrootdir && cfg->chrootdir[0] &&
+ strncmp(cfg->pidfile, cfg->chrootdir,
+ strlen(cfg->chrootdir))==0);
+
/* check old pid file before forking */
if(cfg->pidfile && cfg->pidfile[0]) {
/* calculate position of pidfile */
@@ -490,12 +458,7 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
cfg, 1);
if(!daemon->pidfile)
fatal_exit("pidfile alloc: out of memory");
- checkoldpid(daemon->pidfile,
- /* true if pidfile is inside chrootdir, or nochroot */
- !(cfg->chrootdir && cfg->chrootdir[0]) ||
- (cfg->chrootdir && cfg->chrootdir[0] &&
- strncmp(daemon->pidfile, cfg->chrootdir,
- strlen(cfg->chrootdir))==0));
+ checkoldpid(daemon->pidfile, pidinchroot);
}
#endif
@@ -508,10 +471,11 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
#ifdef HAVE_KILL
if(cfg->pidfile && cfg->pidfile[0]) {
writepid(daemon->pidfile, getpid());
- if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) {
+ if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1 &&
+ pidinchroot) {
# ifdef HAVE_CHOWN
if(chown(daemon->pidfile, cfg_uid, cfg_gid) == -1) {
- log_err("cannot chown %u.%u %s: %s",
+ verbose(VERB_QUERY, "cannot chown %u.%u %s: %s",
(unsigned)cfg_uid, (unsigned)cfg_gid,
daemon->pidfile, strerror(errno));
}
@@ -597,7 +561,9 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
log_warn("unable to initgroups %s: %s",
cfg->username, strerror(errno));
# endif /* HAVE_INITGROUPS */
+# ifdef HAVE_ENDPWENT
endpwent();
+# endif
#ifdef HAVE_SETRESGID
if(setresgid(cfg_gid,cfg_gid,cfg_gid) != 0)
@@ -633,9 +599,10 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
* @param cmdline_verbose: verbosity resulting from commandline -v.
* These increase verbosity as specified in the config file.
* @param debug_mode: if set, do not daemonize.
+ * @param log_default_identity: Default identity to report in logs
*/
static void
-run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
+run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, const char* log_default_identity)
{
struct config_file* cfg = NULL;
struct daemon* daemon = NULL;
@@ -657,7 +624,7 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
cfgfile);
log_warn("Continuing with default config settings");
}
- apply_settings(daemon, cfg, cmdline_verbose, debug_mode);
+ apply_settings(daemon, cfg, cmdline_verbose, debug_mode, log_default_identity);
if(!done_setup)
config_lookup_uid(cfg);
@@ -665,7 +632,7 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
if(!daemon_open_shared_ports(daemon))
fatal_exit("could not open ports");
if(!done_setup) {
- perform_setup(daemon, cfg, debug_mode, &cfgfile);
+ perform_setup(daemon, cfg, debug_mode, &cfgfile);
done_setup = 1;
} else {
/* reopen log after HUP to facilitate log rotation */
@@ -712,19 +679,16 @@ main(int argc, char* argv[])
int c;
const char* cfgfile = CONFIGFILE;
const char* winopt = NULL;
+ const char* log_ident_default;
int cmdline_verbose = 0;
int debug_mode = 0;
#ifdef UB_ON_WINDOWS
int cmdline_cfg = 0;
#endif
-#ifdef HAVE_SBRK
- /* take debug snapshot of heap */
- unbound_start_brk = sbrk(0);
-#endif
-
log_init(NULL, 0, NULL);
- log_ident_set(strrchr(argv[0],'/')?strrchr(argv[0],'/')+1:argv[0]);
+ log_ident_default = strrchr(argv[0],'/')?strrchr(argv[0],'/')+1:argv[0];
+ log_ident_set(log_ident_default);
/* parse the options */
while( (c=getopt(argc, argv, "c:dhvw:")) != -1) {
switch(c) {
@@ -735,7 +699,7 @@ main(int argc, char* argv[])
#endif
break;
case 'v':
- cmdline_verbose ++;
+ cmdline_verbose++;
verbosity++;
break;
case 'd':
@@ -768,7 +732,7 @@ main(int argc, char* argv[])
return 1;
}
- run_daemon(cfgfile, cmdline_verbose, debug_mode);
+ run_daemon(cfgfile, cmdline_verbose, debug_mode, log_ident_default);
log_init(NULL, 0, NULL); /* close logfile */
return 0;
}
diff --git a/external/unbound/daemon/worker.c b/external/unbound/daemon/worker.c
index c90a65998..b1cc974aa 100644
--- a/external/unbound/daemon/worker.c
+++ b/external/unbound/daemon/worker.c
@@ -69,9 +69,13 @@
#include "iterator/iter_hints.h"
#include "validator/autotrust.h"
#include "validator/val_anchor.h"
+#include "respip/respip.h"
#include "libunbound/context.h"
#include "libunbound/libworker.h"
#include "sldns/sbuffer.h"
+#include "sldns/wire2str.h"
+#include "util/shm_side/shm_main.h"
+#include "dnscrypt/dnscrypt.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
@@ -101,61 +105,21 @@
*/
#define PREFETCH_EXPIRY_ADD 60
-#ifdef UNBOUND_ALLOC_STATS
-/** measure memory leakage */
-static void
-debug_memleak(size_t accounted, size_t heap,
- size_t total_alloc, size_t total_free)
-{
- static int init = 0;
- static size_t base_heap, base_accounted, base_alloc, base_free;
- size_t base_af, cur_af, grow_af, grow_acc;
- if(!init) {
- init = 1;
- base_heap = heap;
- base_accounted = accounted;
- base_alloc = total_alloc;
- base_free = total_free;
- }
- base_af = base_alloc - base_free;
- cur_af = total_alloc - total_free;
- grow_af = cur_af - base_af;
- grow_acc = accounted - base_accounted;
- log_info("Leakage: %d leaked. growth: %u use, %u acc, %u heap",
- (int)(grow_af - grow_acc), (unsigned)grow_af,
- (unsigned)grow_acc, (unsigned)(heap - base_heap));
-}
-
-/** give debug heap size indication */
-static void
-debug_total_mem(size_t calctotal)
-{
-#ifdef HAVE_SBRK
- extern void* unbound_start_brk;
- extern size_t unbound_mem_alloc, unbound_mem_freed;
- void* cur = sbrk(0);
- int total = cur-unbound_start_brk;
- log_info("Total heap memory estimate: %u total-alloc: %u "
- "total-free: %u", (unsigned)total,
- (unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed);
- debug_memleak(calctotal, (size_t)total,
- unbound_mem_alloc, unbound_mem_freed);
-#else
- (void)calctotal;
-#endif /* HAVE_SBRK */
-}
-#endif /* UNBOUND_ALLOC_STATS */
-
/** Report on memory usage by this thread and global */
static void
worker_mem_report(struct worker* ATTR_UNUSED(worker),
struct serviced_query* ATTR_UNUSED(cur_serv))
{
#ifdef UNBOUND_ALLOC_STATS
+ /* measure memory leakage */
+ extern size_t unbound_mem_alloc, unbound_mem_freed;
/* debug func in validator module */
size_t total, front, back, mesh, msg, rrset, infra, ac, superac;
size_t me, iter, val, anch;
int i;
+#ifdef CLIENT_SUBNET
+ size_t subnet = 0;
+#endif /* CLIENT_SUBNET */
if(verbosity < VERB_ALGO)
return;
front = listen_get_mem(worker->front);
@@ -175,6 +139,12 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
val += (*worker->env.mesh->mods.mod[i]->get_mem)
(&worker->env, i);
+#ifdef CLIENT_SUBNET
+ else if(strcmp(worker->env.mesh->mods.mod[i]->name,
+ "subnet")==0)
+ subnet += (*worker->env.mesh->mods.mod[i]->get_mem)
+ (&worker->env, i);
+#endif /* CLIENT_SUBNET */
else iter += (*worker->env.mesh->mods.mod[i]->get_mem)
(&worker->env, i);
}
@@ -192,6 +162,17 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
me += serviced_get_mem(cur_serv);
}
total = front+back+mesh+msg+rrset+infra+iter+val+ac+superac+me;
+#ifdef CLIENT_SUBNET
+ total += subnet;
+ log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
+ "rrset=%u infra=%u iter=%u val=%u subnet=%u anchors=%u "
+ "alloccache=%u globalalloccache=%u me=%u",
+ (unsigned)total, (unsigned)front, (unsigned)back,
+ (unsigned)mesh, (unsigned)msg, (unsigned)rrset, (unsigned)infra,
+ (unsigned)iter, (unsigned)val,
+ (unsigned)subnet, (unsigned)anch, (unsigned)ac,
+ (unsigned)superac, (unsigned)me);
+#else /* no CLIENT_SUBNET */
log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
"rrset=%u infra=%u iter=%u val=%u anchors=%u "
"alloccache=%u globalalloccache=%u me=%u",
@@ -199,9 +180,15 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
(unsigned)mesh, (unsigned)msg, (unsigned)rrset,
(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)anch,
(unsigned)ac, (unsigned)superac, (unsigned)me);
- debug_total_mem(total);
+#endif /* CLIENT_SUBNET */
+ log_info("Total heap memory estimate: %u total-alloc: %u "
+ "total-free: %u", (unsigned)total,
+ (unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed);
#else /* no UNBOUND_ALLOC_STATS */
size_t val = 0;
+#ifdef CLIENT_SUBNET
+ size_t subnet = 0;
+#endif /* CLIENT_SUBNET */
int i;
if(verbosity < VERB_QUERY)
return;
@@ -211,12 +198,27 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
val += (*worker->env.mesh->mods.mod[i]->get_mem)
(&worker->env, i);
+#ifdef CLIENT_SUBNET
+ else if(strcmp(worker->env.mesh->mods.mod[i]->name,
+ "subnet")==0)
+ subnet += (*worker->env.mesh->mods.mod[i]->get_mem)
+ (&worker->env, i);
+#endif /* CLIENT_SUBNET */
}
+#ifdef CLIENT_SUBNET
+ verbose(VERB_QUERY, "cache memory msg=%u rrset=%u infra=%u val=%u "
+ "subnet=%u",
+ (unsigned)slabhash_get_mem(worker->env.msg_cache),
+ (unsigned)slabhash_get_mem(&worker->env.rrset_cache->table),
+ (unsigned)infra_get_mem(worker->env.infra_cache),
+ (unsigned)val, (unsigned)subnet);
+#else /* no CLIENT_SUBNET */
verbose(VERB_QUERY, "cache memory msg=%u rrset=%u infra=%u val=%u",
(unsigned)slabhash_get_mem(worker->env.msg_cache),
(unsigned)slabhash_get_mem(&worker->env.rrset_cache->table),
(unsigned)infra_get_mem(worker->env.infra_cache),
(unsigned)val);
+#endif /* CLIENT_SUBNET */
#endif /* UNBOUND_ALLOC_STATS */
}
@@ -483,15 +485,17 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
qinfo->qname_len, qinfo->qtype, qinfo->qclass,
worker->scratchpad, &msg, timenow);
if(!dp) { /* no delegation, need to reprime */
- regional_free_all(worker->scratchpad);
return 0;
}
+ /* In case we have a local alias, copy it into the delegation message.
+ * Shallow copy should be fine, as we'll be done with msg in this
+ * function. */
+ msg->qinfo.local_alias = qinfo->local_alias;
if(must_validate) {
switch(check_delegation_secure(msg->rep)) {
case sec_status_unchecked:
/* some rrsets have not been verified yet, go and
* let validator do that */
- regional_free_all(worker->scratchpad);
return 0;
case sec_status_bogus:
/* some rrsets are bogus, reply servfail */
@@ -499,9 +503,11 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL,
+ msg->rep, LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
+ return 0;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
worker->stats.ans_bogus++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++;
@@ -527,14 +533,19 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, msg->rep,
+ (int)(flags&LDNS_RCODE_MASK), edns, worker->scratchpad))
+ return 0;
msg->rep->flags |= BIT_QR|BIT_RA;
if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags,
repinfo->c->buffer, 0, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
+ if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
+ LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
+ edns->opt_list = NULL;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
}
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
@@ -542,28 +553,93 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
return 1;
}
-/** answer query from the cache */
+/** Apply, if applicable, a response IP action to a cached answer.
+ * If the answer is rewritten as a result of an action, '*encode_repp' will
+ * point to the reply info containing the modified answer. '*encode_repp' will
+ * be intact otherwise.
+ * It returns 1 on success, 0 otherwise. */
+static int
+apply_respip_action(struct worker* worker, const struct query_info* qinfo,
+ struct respip_client_info* cinfo, struct reply_info* rep,
+ struct comm_reply* repinfo, struct ub_packed_rrset_key** alias_rrset,
+ struct reply_info** encode_repp)
+{
+ struct respip_action_info actinfo = {respip_none, NULL};
+
+ if(qinfo->qtype != LDNS_RR_TYPE_A &&
+ qinfo->qtype != LDNS_RR_TYPE_AAAA &&
+ qinfo->qtype != LDNS_RR_TYPE_ANY)
+ return 1;
+
+ if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
+ alias_rrset, 0, worker->scratchpad))
+ return 0;
+
+ /* xxx_deny actions mean dropping the reply, unless the original reply
+ * was redirected to response-ip data. */
+ if((actinfo.action == respip_deny ||
+ actinfo.action == respip_inform_deny) &&
+ *encode_repp == rep)
+ *encode_repp = NULL;
+
+ /* If address info is returned, it means the action should be an
+ * 'inform' variant and the information should be logged. */
+ if(actinfo.addrinfo) {
+ respip_inform_print(actinfo.addrinfo, qinfo->qname,
+ qinfo->qtype, qinfo->qclass, qinfo->local_alias,
+ repinfo);
+ }
+
+ return 1;
+}
+
+/** answer query from the cache.
+ * Normally, the answer message will be built in repinfo->c->buffer; if the
+ * answer is supposed to be suppressed or the answer is supposed to be an
+ * incomplete CNAME chain, the buffer is explicitly cleared to signal the
+ * caller as such. In the latter case *partial_rep will point to the incomplete
+ * reply, and this function is (possibly) supposed to be called again with that
+ * *partial_rep value to complete the chain. In addition, if the query should
+ * be completely dropped, '*need_drop' will be set to 1. */
static int
answer_from_cache(struct worker* worker, struct query_info* qinfo,
+ struct respip_client_info* cinfo, int* need_drop,
+ struct ub_packed_rrset_key** alias_rrset,
+ struct reply_info** partial_repp,
struct reply_info* rep, uint16_t id, uint16_t flags,
struct comm_reply* repinfo, struct edns_data* edns)
{
time_t timenow = *worker->env.now;
uint16_t udpsize = edns->udp_size;
+ struct reply_info* encode_rep = rep;
+ struct reply_info* partial_rep = *partial_repp;
int secure;
int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
&& worker->env.need_to_validate;
- /* see if it is possible */
- if(rep->ttl < timenow) {
- /* the rrsets may have been updated in the meantime.
- * we will refetch the message format from the
- * authoritative server
- */
- return 0;
+ *partial_repp = NULL; /* avoid accidental further pass */
+ if(worker->env.cfg->serve_expired) {
+ /* always lock rrsets, rep->ttl is ignored */
+ if(!rrset_array_lock(rep->ref, rep->rrset_count, 0))
+ return 0;
+ /* below, rrsets with ttl before timenow become TTL 0 in
+ * the response */
+ /* This response was served with zero TTL */
+ if (timenow >= rep->ttl) {
+ worker->stats.zero_ttl_responses++;
+ }
+ } else {
+ /* see if it is possible */
+ if(rep->ttl < timenow) {
+ /* the rrsets may have been updated in the meantime.
+ * we will refetch the message format from the
+ * authoritative server
+ */
+ return 0;
+ }
+ if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow))
+ return 0;
+ /* locked and ids and ttls are OK. */
}
- if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow))
- return 0;
- /* locked and ids and ttls are OK. */
/* check CNAME chain (if any) */
if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type ==
@@ -574,7 +650,6 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
bail_out:
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
- regional_free_all(worker->scratchpad);
return 0;
}
}
@@ -585,11 +660,13 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep,
+ LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
+ goto bail_out;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
worker->stats.ans_bogus ++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++;
@@ -616,9 +693,41 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
- if(!reply_info_answer_encode(qinfo, rep, id, flags,
+ if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, rep,
+ (int)(flags&LDNS_RCODE_MASK), edns, worker->scratchpad))
+ goto bail_out;
+ *alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */
+ if(worker->daemon->use_response_ip && !partial_rep &&
+ !apply_respip_action(worker, qinfo, cinfo, rep, repinfo, alias_rrset,
+ &encode_rep)) {
+ goto bail_out;
+ } else if(partial_rep &&
+ !respip_merge_cname(partial_rep, qinfo, rep, cinfo,
+ must_validate, &encode_rep, worker->scratchpad)) {
+ goto bail_out;
+ }
+ if(encode_rep != rep)
+ secure = 0; /* if rewritten, it can't be considered "secure" */
+ if(!encode_rep || *alias_rrset) {
+ sldns_buffer_clear(repinfo->c->buffer);
+ sldns_buffer_flip(repinfo->c->buffer);
+ if(!encode_rep)
+ *need_drop = 1;
+ else {
+ /* If a partial CNAME chain is found, we first need to
+ * make a copy of the reply in the scratchpad so we
+ * can release the locks and lookup the cache again. */
+ *partial_repp = reply_info_copy(encode_rep, NULL,
+ worker->scratchpad);
+ if(!*partial_repp)
+ goto bail_out;
+ }
+ } else if(!reply_info_answer_encode(qinfo, encode_rep, id, flags,
repinfo->c->buffer, timenow, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
+ if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
+ LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
+ edns->opt_list = NULL;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
}
@@ -626,7 +735,6 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
* is bad while holding locks. */
rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
rep->ref, rep->rrset_count);
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
@@ -635,14 +743,18 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
return 1;
}
-/** Reply to client and perform prefetch to keep cache up to date */
+/** Reply to client and perform prefetch to keep cache up to date.
+ * If the buffer for the reply is empty, it indicates that only prefetch is
+ * necessary and the reply should be suppressed (because it's dropped or
+ * being deferred). */
static void
reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
uint16_t flags, struct comm_reply* repinfo, time_t leeway)
{
/* first send answer to client to keep its latency
* as small as a cachereply */
- comm_point_send_reply(repinfo);
+ if(sldns_buffer_limit(repinfo->c->buffer) != 0)
+ comm_point_send_reply(repinfo);
server_stats_prefetch(&worker->stats, worker);
/* create the prefetch in the mesh as a normal lookup without
@@ -657,41 +769,115 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
* Fill CH class answer into buffer. Keeps query.
* @param pkt: buffer
* @param str: string to put into text record (<255).
+ * array of strings, every string becomes a text record.
+ * @param num: number of strings in array.
* @param edns: edns reply information.
+ * @param worker: worker with scratch region.
*/
static void
-chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns)
+chaos_replystr(sldns_buffer* pkt, char** str, int num, struct edns_data* edns,
+ struct worker* worker)
{
- size_t len = strlen(str);
+ int i;
unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt));
unsigned int cd = LDNS_CD_WIRE(sldns_buffer_begin(pkt));
- if(len>255) len=255; /* cap size of TXT record */
sldns_buffer_clear(pkt);
sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip id */
sldns_buffer_write_u16(pkt, (uint16_t)(BIT_QR|BIT_RA));
if(rd) LDNS_RD_SET(sldns_buffer_begin(pkt));
if(cd) LDNS_CD_SET(sldns_buffer_begin(pkt));
sldns_buffer_write_u16(pkt, 1); /* qdcount */
- sldns_buffer_write_u16(pkt, 1); /* ancount */
+ sldns_buffer_write_u16(pkt, (uint16_t)num); /* ancount */
sldns_buffer_write_u16(pkt, 0); /* nscount */
sldns_buffer_write_u16(pkt, 0); /* arcount */
(void)query_dname_len(pkt); /* skip qname */
sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qtype */
sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qclass */
- sldns_buffer_write_u16(pkt, 0xc00c); /* compr ptr to query */
- sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT);
- sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH);
- sldns_buffer_write_u32(pkt, 0); /* TTL */
- sldns_buffer_write_u16(pkt, sizeof(uint8_t) + len);
- sldns_buffer_write_u8(pkt, len);
- sldns_buffer_write(pkt, str, len);
+ for(i=0; i<num; i++) {
+ size_t len = strlen(str[i]);
+ if(len>255) len=255; /* cap size of TXT record */
+ sldns_buffer_write_u16(pkt, 0xc00c); /* compr ptr to query */
+ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT);
+ sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH);
+ sldns_buffer_write_u32(pkt, 0); /* TTL */
+ sldns_buffer_write_u16(pkt, sizeof(uint8_t) + len);
+ sldns_buffer_write_u8(pkt, len);
+ sldns_buffer_write(pkt, str[i], len);
+ }
sldns_buffer_flip(pkt);
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->bits &= EDNS_DO;
+ if(!inplace_cb_reply_local_call(&worker->env, NULL, NULL, NULL,
+ LDNS_RCODE_NOERROR, edns, worker->scratchpad))
+ edns->opt_list = NULL;
attach_edns_record(pkt, edns);
}
+/** Reply with one string */
+static void
+chaos_replyonestr(sldns_buffer* pkt, const char* str, struct edns_data* edns,
+ struct worker* worker)
+{
+ chaos_replystr(pkt, (char**)&str, 1, edns, worker);
+}
+
+/**
+ * Create CH class trustanchor answer.
+ * @param pkt: buffer
+ * @param edns: edns reply information.
+ * @param w: worker with scratch region.
+ */
+static void
+chaos_trustanchor(sldns_buffer* pkt, struct edns_data* edns, struct worker* w)
+{
+#define TA_RESPONSE_MAX_TXT 16 /* max number of TXT records */
+#define TA_RESPONSE_MAX_TAGS 32 /* max number of tags printed per zone */
+ char* str_array[TA_RESPONSE_MAX_TXT];
+ uint16_t tags[TA_RESPONSE_MAX_TAGS];
+ int num = 0;
+ struct trust_anchor* ta;
+
+ if(!w->env.need_to_validate) {
+ /* no validator module, reply no trustanchors */
+ chaos_replystr(pkt, NULL, 0, edns, w);
+ return;
+ }
+
+ /* fill the string with contents */
+ lock_basic_lock(&w->env.anchors->lock);
+ RBTREE_FOR(ta, struct trust_anchor*, w->env.anchors->tree) {
+ char* str;
+ size_t i, numtag, str_len = 255;
+ if(num == TA_RESPONSE_MAX_TXT) continue;
+ str = (char*)regional_alloc(w->scratchpad, str_len);
+ if(!str) continue;
+ lock_basic_lock(&ta->lock);
+ numtag = anchor_list_keytags(ta, tags, TA_RESPONSE_MAX_TAGS);
+ if(numtag == 0) {
+ /* empty, insecure point */
+ lock_basic_unlock(&ta->lock);
+ continue;
+ }
+ str_array[num] = str;
+ num++;
+
+ /* spool name of anchor */
+ (void)sldns_wire2str_dname_buf(ta->name, ta->namelen, str, str_len);
+ str_len -= strlen(str); str += strlen(str);
+ /* spool tags */
+ for(i=0; i<numtag; i++) {
+ snprintf(str, str_len, " %u", (unsigned)tags[i]);
+ str_len -= strlen(str); str += strlen(str);
+ }
+ lock_basic_unlock(&ta->lock);
+ }
+ lock_basic_unlock(&w->env.anchors->lock);
+
+ chaos_replystr(pkt, str_array, num, edns, w);
+ regional_free_all(w->scratchpad);
+}
+
/**
* Answer CH class queries.
* @param w: worker
@@ -718,13 +904,13 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
char buf[MAXHOSTNAMELEN+1];
if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
buf[MAXHOSTNAMELEN] = 0;
- chaos_replystr(pkt, buf, edns);
+ chaos_replyonestr(pkt, buf, edns, w);
} else {
log_err("gethostname: %s", strerror(errno));
- chaos_replystr(pkt, "no hostname", edns);
+ chaos_replyonestr(pkt, "no hostname", edns, w);
}
}
- else chaos_replystr(pkt, cfg->identity, edns);
+ else chaos_replyonestr(pkt, cfg->identity, edns, w);
return 1;
}
if(query_dname_compare(qinfo->qname,
@@ -735,10 +921,19 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
if(cfg->hide_version)
return 0;
if(cfg->version==NULL || cfg->version[0]==0)
- chaos_replystr(pkt, PACKAGE_STRING, edns);
- else chaos_replystr(pkt, cfg->version, edns);
+ chaos_replyonestr(pkt, PACKAGE_STRING, edns, w);
+ else chaos_replyonestr(pkt, cfg->version, edns, w);
return 1;
}
+ if(query_dname_compare(qinfo->qname,
+ (uint8_t*)"\013trustanchor\007unbound") == 0)
+ {
+ if(cfg->hide_trustanchor)
+ return 0;
+ chaos_trustanchor(pkt, edns, w);
+ return 1;
+ }
+
return 0;
}
@@ -768,6 +963,8 @@ deny_refuse(struct comm_point* c, enum acl_access acl,
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
+ sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
+ sldns_buffer_flip(c->buffer);
return 1;
}
@@ -794,25 +991,75 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
{
struct worker* worker = (struct worker*)arg;
int ret;
- hashvalue_t h;
+ hashvalue_type h;
struct lruhash_entry* e;
struct query_info qinfo;
struct edns_data edns;
enum acl_access acl;
+ struct acl_addr* acladdr;
int rc = 0;
+ int need_drop = 0;
+ /* We might have to chase a CNAME chain internally, in which case
+ * we'll have up to two replies and combine them to build a complete
+ * answer. These variables control this case. */
+ struct ub_packed_rrset_key* alias_rrset = NULL;
+ struct reply_info* partial_rep = NULL;
+ struct query_info* lookup_qinfo = &qinfo;
+ struct query_info qinfo_tmp; /* placeholdoer for lookup_qinfo */
+ struct respip_client_info* cinfo = NULL, cinfo_tmp;
if(error != NETEVENT_NOERROR) {
/* some bad tcp query DNS formats give these error calls */
verbose(VERB_ALGO, "handle request called with err=%d", error);
return 0;
}
+#ifdef USE_DNSCRYPT
+ repinfo->max_udp_size = worker->daemon->cfg->max_udp_size;
+ if(!dnsc_handle_curved_request(worker->daemon->dnscenv, repinfo)) {
+ worker->stats.num_query_dnscrypt_crypted_malformed++;
+ return 0;
+ }
+ if(c->dnscrypt && !repinfo->is_dnscrypted) {
+ char buf[LDNS_MAX_DOMAINLEN+1];
+ // Check if this is unencrypted and asking for certs
+ if(worker_check_request(c->buffer, worker) != 0) {
+ verbose(VERB_ALGO, "dnscrypt: worker check request: bad query.");
+ log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ if(!query_info_parse(&qinfo, c->buffer)) {
+ verbose(VERB_ALGO, "dnscrypt: worker parse request: formerror.");
+ log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ dname_str(qinfo.qname, buf);
+ if(!(qinfo.qtype == LDNS_RR_TYPE_TXT &&
+ strcasecmp(buf, worker->daemon->dnscenv->provider_name) == 0)) {
+ verbose(VERB_ALGO,
+ "dnscrypt: not TXT %s. Receive: %s %s",
+ worker->daemon->dnscenv->provider_name,
+ sldns_rr_descript(qinfo.qtype)->_name,
+ buf);
+ comm_point_drop_reply(repinfo);
+ worker->stats.num_query_dnscrypt_cleartext++;
+ return 0;
+ }
+ worker->stats.num_query_dnscrypt_cert++;
+ sldns_buffer_rewind(c->buffer);
+ } else if(c->dnscrypt && repinfo->is_dnscrypted) {
+ worker->stats.num_query_dnscrypt_crypted++;
+ }
+#endif
#ifdef USE_DNSTAP
if(worker->dtenv.log_client_query_messages)
dt_msg_send_client_query(&worker->dtenv, &repinfo->addr, c->type,
c->buffer);
#endif
- acl = acl_list_lookup(worker->daemon->acl, &repinfo->addr,
+ acladdr = acl_addr_lookup(worker->daemon->acl, &repinfo->addr,
repinfo->addrlen);
+ acl = acl_get_control(acladdr);
if((ret=deny_refuse_all(c, acl, worker, repinfo)) != -1)
{
if(ret == 1)
@@ -830,7 +1077,29 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
comm_point_drop_reply(repinfo);
return 0;
}
+
worker->stats.num_queries++;
+
+ /* check if this query should be dropped based on source ip rate limiting */
+ if(!infra_ip_ratelimit_inc(worker->env.infra_cache, repinfo,
+ *worker->env.now)) {
+ /* See if we are passed through with slip factor */
+ if(worker->env.cfg->ip_ratelimit_factor != 0 &&
+ ub_random_max(worker->env.rnd,
+ worker->env.cfg->ip_ratelimit_factor) == 1) {
+
+ char addrbuf[128];
+ addr_to_str(&repinfo->addr, repinfo->addrlen,
+ addrbuf, sizeof(addrbuf));
+ verbose(VERB_OPS, "ip_ratelimit allowed through for ip address %s ",
+ addrbuf);
+ } else {
+ worker->stats.num_queries_ip_ratelimited++;
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ }
+
/* see if query is in the cache */
if(!query_info_parse(&qinfo, c->buffer)) {
verbose(VERB_ALGO, "worker parse request: formerror.");
@@ -865,7 +1134,29 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
}
goto send_reply;
}
- if((ret=parse_edns_from_pkt(c->buffer, &edns)) != 0) {
+ if(qinfo.qtype == LDNS_RR_TYPE_OPT ||
+ qinfo.qtype == LDNS_RR_TYPE_TSIG ||
+ qinfo.qtype == LDNS_RR_TYPE_TKEY ||
+ qinfo.qtype == LDNS_RR_TYPE_MAILA ||
+ qinfo.qtype == LDNS_RR_TYPE_MAILB ||
+ (qinfo.qtype >= 128 && qinfo.qtype <= 248)) {
+ verbose(VERB_ALGO, "worker request: formerror for meta-type.");
+ log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ if(worker_err_ratelimit(worker, LDNS_RCODE_FORMERR) == -1) {
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ sldns_buffer_rewind(c->buffer);
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ if(worker->stats.extended) {
+ worker->stats.qtype[qinfo.qtype]++;
+ server_stats_insrcode(&worker->stats, c->buffer);
+ }
+ goto send_reply;
+ }
+ if((ret=parse_edns_from_pkt(c->buffer, &edns, worker->scratchpad)) != 0) {
struct edns_data reply_edns;
verbose(VERB_ALGO, "worker parse edns: formerror.");
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
@@ -876,6 +1167,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
error_encode(c->buffer, ret, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns);
+ regional_free_all(worker->scratchpad);
server_stats_insrcode(&worker->stats, c->buffer);
goto send_reply;
}
@@ -884,12 +1176,14 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits &= EDNS_DO;
+ edns.opt_list = NULL;
verbose(VERB_ALGO, "query with bad edns version.");
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), NULL);
attach_edns_record(c->buffer, &edns);
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE &&
@@ -918,6 +1212,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
sldns_buffer_write_at(c->buffer, 4,
(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
sldns_buffer_flip(c->buffer);
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
if(worker->stats.extended)
@@ -928,10 +1223,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
&edns, c->buffer)) {
server_stats_insrcode(&worker->stats, c->buffer);
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
- if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns,
- c->buffer, worker->scratchpad, repinfo)) {
+ if(local_zones_answer(worker->daemon->local_zones, &worker->env, &qinfo,
+ &edns, c->buffer, worker->scratchpad, repinfo, acladdr->taglist,
+ acladdr->taglen, acladdr->tag_actions,
+ acladdr->tag_actions_size, acladdr->tag_datas,
+ acladdr->tag_datas_size, worker->daemon->cfg->tagname,
+ worker->daemon->cfg->num_tags, acladdr->view)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
@@ -945,6 +1245,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
* might need to bail out based on ACLs now. */
if((ret=deny_refuse_non_local(c, acl, worker, repinfo)) != -1)
{
+ regional_free_all(worker->scratchpad);
if(ret == 1)
goto send_reply;
return ret;
@@ -961,46 +1262,125 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
sldns_buffer_flip(c->buffer);
+ regional_free_all(worker->scratchpad);
server_stats_insrcode(&worker->stats, c->buffer);
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
&repinfo->addr, repinfo->addrlen);
goto send_reply;
}
- h = query_info_hash(&qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
- if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) {
- /* answer from cache - we have acquired a readlock on it */
- if(answer_from_cache(worker, &qinfo,
- (struct reply_info*)e->data,
- *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
- sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
- &edns)) {
- /* prefetch it if the prefetch TTL expired */
- if(worker->env.cfg->prefetch && *worker->env.now >=
- ((struct reply_info*)e->data)->prefetch_ttl) {
- time_t leeway = ((struct reply_info*)e->
- data)->ttl - *worker->env.now;
+
+ /* If we've found a local alias, replace the qname with the alias
+ * target before resolving it. */
+ if(qinfo.local_alias) {
+ struct ub_packed_rrset_key* rrset = qinfo.local_alias->rrset;
+ struct packed_rrset_data* d = rrset->entry.data;
+
+ /* Sanity check: our current implementation only supports
+ * a single CNAME RRset as a local alias. */
+ if(qinfo.local_alias->next ||
+ rrset->rk.type != htons(LDNS_RR_TYPE_CNAME) ||
+ d->count != 1) {
+ log_err("assumption failure: unexpected local alias");
+ regional_free_all(worker->scratchpad);
+ return 0; /* drop it */
+ }
+ qinfo.qname = d->rr_data[0] + 2;
+ qinfo.qname_len = d->rr_len[0] - 2;
+ }
+
+ /* If we may apply IP-based actions to the answer, build the client
+ * information. As this can be expensive, skip it if there is
+ * absolutely no possibility of it. */
+ if(worker->daemon->use_response_ip &&
+ (qinfo.qtype == LDNS_RR_TYPE_A ||
+ qinfo.qtype == LDNS_RR_TYPE_AAAA ||
+ qinfo.qtype == LDNS_RR_TYPE_ANY)) {
+ cinfo_tmp.taglist = acladdr->taglist;
+ cinfo_tmp.taglen = acladdr->taglen;
+ cinfo_tmp.tag_actions = acladdr->tag_actions;
+ cinfo_tmp.tag_actions_size = acladdr->tag_actions_size;
+ cinfo_tmp.tag_datas = acladdr->tag_datas;
+ cinfo_tmp.tag_datas_size = acladdr->tag_datas_size;
+ cinfo_tmp.view = acladdr->view;
+ cinfo_tmp.respip_set = worker->daemon->respip_set;
+ cinfo = &cinfo_tmp;
+ }
+
+lookup_cache:
+ /* Lookup the cache. In case we chase an intermediate CNAME chain
+ * this is a two-pass operation, and lookup_qinfo is different for
+ * each pass. We should still pass the original qinfo to
+ * answer_from_cache(), however, since it's used to build the reply. */
+ if(!edns_bypass_cache_stage(edns.opt_list, &worker->env)) {
+ h = query_info_hash(lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
+ if((e=slabhash_lookup(worker->env.msg_cache, h, lookup_qinfo, 0))) {
+ /* answer from cache - we have acquired a readlock on it */
+ if(answer_from_cache(worker, &qinfo,
+ cinfo, &need_drop, &alias_rrset, &partial_rep,
+ (struct reply_info*)e->data,
+ *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
+ sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
+ &edns)) {
+ /* prefetch it if the prefetch TTL expired.
+ * Note that if there is more than one pass
+ * its qname must be that used for cache
+ * lookup. */
+ if((worker->env.cfg->prefetch || worker->env.cfg->serve_expired)
+ && *worker->env.now >=
+ ((struct reply_info*)e->data)->prefetch_ttl) {
+ time_t leeway = ((struct reply_info*)e->
+ data)->ttl - *worker->env.now;
+ if(((struct reply_info*)e->data)->ttl
+ < *worker->env.now)
+ leeway = 0;
+ lock_rw_unlock(&e->lock);
+ reply_and_prefetch(worker, lookup_qinfo,
+ sldns_buffer_read_u16_at(c->buffer, 2),
+ repinfo, leeway);
+ if(!partial_rep) {
+ rc = 0;
+ regional_free_all(worker->scratchpad);
+ goto send_reply_rc;
+ }
+ } else if(!partial_rep) {
+ lock_rw_unlock(&e->lock);
+ regional_free_all(worker->scratchpad);
+ goto send_reply;
+ }
+ /* We've found a partial reply ending with an
+ * alias. Replace the lookup qinfo for the
+ * alias target and lookup the cache again to
+ * (possibly) complete the reply. As we're
+ * passing the "base" reply, there will be no
+ * more alias chasing. */
lock_rw_unlock(&e->lock);
- reply_and_prefetch(worker, &qinfo,
- sldns_buffer_read_u16_at(c->buffer, 2),
- repinfo, leeway);
- rc = 0;
- goto send_reply_rc;
+ memset(&qinfo_tmp, 0, sizeof(qinfo_tmp));
+ get_cname_target(alias_rrset, &qinfo_tmp.qname,
+ &qinfo_tmp.qname_len);
+ if(!qinfo_tmp.qname) {
+ log_err("unexpected: invalid answer alias");
+ regional_free_all(worker->scratchpad);
+ return 0; /* drop query */
+ }
+ qinfo_tmp.qtype = qinfo.qtype;
+ qinfo_tmp.qclass = qinfo.qclass;
+ lookup_qinfo = &qinfo_tmp;
+ goto lookup_cache;
}
+ verbose(VERB_ALGO, "answer from the cache failed");
lock_rw_unlock(&e->lock);
- goto send_reply;
}
- verbose(VERB_ALGO, "answer from the cache failed");
- lock_rw_unlock(&e->lock);
- }
- if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
- if(answer_norec_from_cache(worker, &qinfo,
- *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
- sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
- &edns)) {
- goto send_reply;
+ if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
+ if(answer_norec_from_cache(worker, &qinfo,
+ *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
+ sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
+ &edns)) {
+ regional_free_all(worker->scratchpad);
+ goto send_reply;
+ }
+ verbose(VERB_ALGO, "answer norec from cache -- "
+ "need to validate or not primed");
}
- verbose(VERB_ALGO, "answer norec from cache -- "
- "need to validate or not primed");
}
sldns_buffer_rewind(c->buffer);
server_stats_querymiss(&worker->stats, worker);
@@ -1014,20 +1394,36 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
}
/* grab a work request structure for this new request */
- mesh_new_client(worker->env.mesh, &qinfo,
+ mesh_new_client(worker->env.mesh, &qinfo, cinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
&edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer));
+ regional_free_all(worker->scratchpad);
worker_mem_report(worker, NULL);
return 0;
send_reply:
rc = 1;
send_reply_rc:
+ if(need_drop) {
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
#ifdef USE_DNSTAP
if(worker->dtenv.log_client_response_messages)
dt_msg_send_client_response(&worker->dtenv, &repinfo->addr,
c->type, c->buffer);
#endif
+ if(worker->env.cfg->log_replies)
+ {
+ struct timeval tv = {0, 0};
+ log_reply_info(0, &qinfo, &repinfo->addr, repinfo->addrlen,
+ tv, 1, c->buffer);
+ }
+#ifdef USE_DNSCRYPT
+ if(!dnsc_handle_uncurved_request(repinfo)) {
+ return 0;
+ }
+#endif
return rc;
}
@@ -1083,6 +1479,10 @@ void worker_stat_timer_cb(void* arg)
server_stats_log(&worker->stats, worker, worker->thread_num);
mesh_stats(worker->env.mesh, "mesh has");
worker_mem_report(worker, NULL);
+ /* SHM is enabled, process data to SHM */
+ if (worker->daemon->cfg->shm_enable) {
+ shm_main_run(worker);
+ }
if(!worker->daemon->cfg->stat_cumulative) {
worker_stats_clear(worker);
}
@@ -1217,7 +1617,8 @@ worker_init(struct worker* worker, struct config_file *cfg,
cfg->do_tcp?cfg->outgoing_num_tcp:0,
worker->daemon->env->infra_cache, worker->rndstate,
cfg->use_caps_bits_for_id, worker->ports, worker->numports,
- cfg->unwanted_threshold, &worker_alloc_cleanup, worker,
+ cfg->unwanted_threshold, cfg->outgoing_tcp_mss,
+ &worker_alloc_cleanup, worker,
cfg->do_udp, worker->daemon->connect_sslctx, cfg->delay_close,
dtenv);
if(!worker->back) {
@@ -1352,10 +1753,10 @@ worker_delete(struct worker* worker)
}
struct outbound_entry*
-worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
- uint16_t qclass, uint16_t flags, int dnssec, int want_dnssec,
- int nocaps, struct sockaddr_storage* addr, socklen_t addrlen,
- uint8_t* zone, size_t zonelen, struct module_qstate* q)
+worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec,
+ int want_dnssec, int nocaps, struct sockaddr_storage* addr,
+ socklen_t addrlen, uint8_t* zone, size_t zonelen, int ssl_upstream,
+ struct module_qstate* q)
{
struct worker* worker = q->env->worker;
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
@@ -1363,11 +1764,10 @@ worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
if(!e)
return NULL;
e->qstate = q;
- e->qsent = outnet_serviced_query(worker->back, qname,
- qnamelen, qtype, qclass, flags, dnssec, want_dnssec, nocaps,
- q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, addr,
- addrlen, zone, zonelen, worker_handle_service_reply, e,
- worker->back->udp_buff);
+ e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec,
+ want_dnssec, nocaps, q->env->cfg->tcp_upstream,
+ ssl_upstream, addr, addrlen, zone, zonelen, q,
+ worker_handle_service_reply, e, worker->back->udp_buff, q->env);
if(!e->qsent) {
return NULL;
}
@@ -1407,13 +1807,13 @@ void worker_stop_accept(void* arg)
}
/* --- fake callbacks for fptr_wlist to work --- */
-struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname),
- size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
- uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
- int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
- int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
- socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
- size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q))
+struct outbound_entry* libworker_send_query(
+ struct query_info* ATTR_UNUSED(qinfo),
+ uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec),
+ int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
+ struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen),
+ uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
+ int ATTR_UNUSED(ssl_upstream), struct module_qstate* ATTR_UNUSED(q))
{
log_assert(0);
return 0;
diff --git a/external/unbound/daemon/worker.h b/external/unbound/daemon/worker.h
index 63613430b..0d7ce9521 100644
--- a/external/unbound/daemon/worker.h
+++ b/external/unbound/daemon/worker.h
@@ -61,6 +61,7 @@ struct ub_randstate;
struct regional;
struct tube;
struct daemon_remote;
+struct query_info;
/** worker commands */
enum worker_commands {
@@ -84,7 +85,7 @@ struct worker {
/** global shared daemon structure */
struct daemon* daemon;
/** thread id */
- ub_thread_t thr_id;
+ ub_thread_type thr_id;
/** pipe, for commands for this worker */
struct tube* cmd;
/** the event base this worker works with */