aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/daemon
diff options
context:
space:
mode:
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 */