aboutsummaryrefslogtreecommitdiff
path: root/contrib/unbound/daemon/worker.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/unbound/daemon/worker.c')
-rw-r--r--contrib/unbound/daemon/worker.c868
1 files changed, 640 insertions, 228 deletions
diff --git a/contrib/unbound/daemon/worker.c b/contrib/unbound/daemon/worker.c
index 862affb24e9a..176abf57d56e 100644
--- a/contrib/unbound/daemon/worker.c
+++ b/contrib/unbound/daemon/worker.c
@@ -4,22 +4,22 @@
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -66,8 +66,10 @@
#include "util/data/msgencode.h"
#include "util/data/dname.h"
#include "util/fptr_wlist.h"
+#include "util/proxy_protocol.h"
#include "util/tube.h"
#include "util/edns.h"
+#include "util/timeval_func.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_hints.h"
#include "iterator/iter_utils.h"
@@ -98,7 +100,7 @@
/** ratelimit for error responses */
#define ERROR_RATELIMIT 100 /* qps */
-/**
+/**
* seconds to add to prefetch leeway. This is a TTL that expires old rrsets
* earlier than they should in order to put the new update into the cache.
* This additional value is to make sure that if not all TTLs are equal in
@@ -112,7 +114,7 @@
/** Report on memory usage by this thread and global */
static void
-worker_mem_report(struct worker* ATTR_UNUSED(worker),
+worker_mem_report(struct worker* ATTR_UNUSED(worker),
struct serviced_query* ATTR_UNUSED(cur_serv))
{
#ifdef UNBOUND_ALLOC_STATS
@@ -125,7 +127,7 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
#ifdef CLIENT_SUBNET
size_t subnet = 0;
#endif /* CLIENT_SUBNET */
- if(verbosity < VERB_ALGO)
+ if(verbosity < VERB_ALGO)
return;
front = listen_get_mem(worker->front);
back = outnet_get_mem(worker->back);
@@ -133,7 +135,7 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
rrset = slabhash_get_mem(&worker->env.rrset_cache->table);
infra = infra_get_mem(worker->env.infra_cache);
mesh = mesh_get_mem(worker->env.mesh);
- ac = alloc_get_mem(&worker->alloc);
+ ac = alloc_get_mem(worker->alloc);
superac = alloc_get_mem(&worker->daemon->superalloc);
anch = anchors_get_mem(worker->env.anchors);
iter = 0;
@@ -154,10 +156,10 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
(&worker->env, i);
}
me = sizeof(*worker) + sizeof(*worker->base) + sizeof(*worker->comsig)
- + comm_point_get_mem(worker->cmd_com)
- + sizeof(worker->rndstate)
- + regional_get_mem(worker->scratchpad)
- + sizeof(*worker->env.scratch_buffer)
+ + comm_point_get_mem(worker->cmd_com)
+ + sizeof(worker->rndstate)
+ + regional_get_mem(worker->scratchpad)
+ + sizeof(*worker->env.scratch_buffer)
+ sldns_buffer_capacity(worker->env.scratch_buffer)
+ forwards_get_mem(worker->env.fwds)
+ hints_get_mem(worker->env.hints);
@@ -172,7 +174,7 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
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)total, (unsigned)front, (unsigned)back,
(unsigned)mesh, (unsigned)msg, (unsigned)rrset, (unsigned)infra,
(unsigned)iter, (unsigned)val,
(unsigned)subnet, (unsigned)anch, (unsigned)ac,
@@ -181,13 +183,13 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
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",
- (unsigned)total, (unsigned)front, (unsigned)back,
- (unsigned)mesh, (unsigned)msg, (unsigned)rrset,
+ (unsigned)total, (unsigned)front, (unsigned)back,
+ (unsigned)mesh, (unsigned)msg, (unsigned)rrset,
(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)anch,
(unsigned)ac, (unsigned)superac, (unsigned)me);
#endif /* CLIENT_SUBNET */
log_info("Total heap memory estimate: %u total-alloc: %u "
- "total-free: %u", (unsigned)total,
+ "total-free: %u", (unsigned)total,
(unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed);
#else /* no UNBOUND_ALLOC_STATS */
size_t val = 0;
@@ -227,7 +229,7 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
#endif /* UNBOUND_ALLOC_STATS */
}
-void
+void
worker_send_cmd(struct worker* worker, enum worker_commands cmd)
{
uint32_t c = (uint32_t)htonl(cmd);
@@ -236,8 +238,8 @@ worker_send_cmd(struct worker* worker, enum worker_commands cmd)
}
}
-int
-worker_handle_service_reply(struct comm_point* c, void* arg, int error,
+int
+worker_handle_service_reply(struct comm_point* c, void* arg, int error,
struct comm_reply* reply_info)
{
struct outbound_entry* e = (struct outbound_entry*)arg;
@@ -252,13 +254,13 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error,
}
/* sanity check. */
if(!LDNS_QR_WIRE(sldns_buffer_begin(c->buffer))
- || LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) !=
+ || LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) !=
LDNS_PACKET_QUERY
|| LDNS_QDCOUNT(sldns_buffer_begin(c->buffer)) > 1) {
/* error becomes timeout for the module as if this reply
* never arrived. */
verbose(VERB_ALGO, "worker: bad reply handled as timeout");
- mesh_report_reply(worker->env.mesh, e, reply_info,
+ mesh_report_reply(worker->env.mesh, e, reply_info,
NETEVENT_TIMEOUT);
worker_mem_report(worker, sq);
return 0;
@@ -288,64 +290,86 @@ worker_err_ratelimit(struct worker* worker, int err)
return err;
}
+/**
+ * Structure holding the result of the worker_check_request function.
+ * Based on configuration it could be called up to four times; ideally should
+ * be called once.
+ */
+struct check_request_result {
+ int checked;
+ int value;
+};
/** check request sanity.
* @param pkt: the wire packet to examine for sanity.
* @param worker: parameters for checking.
- * @return error code, 0 OK, or -1 discard.
+ * @param out: struct to update with the result.
*/
-static int
-worker_check_request(sldns_buffer* pkt, struct worker* worker)
+static void
+worker_check_request(sldns_buffer* pkt, struct worker* worker,
+ struct check_request_result* out)
{
+ if(out->checked) return;
+ out->checked = 1;
if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "request too short, discarded");
- return -1;
+ out->value = -1;
+ return;
}
- if(sldns_buffer_limit(pkt) > NORMAL_UDP_SIZE &&
+ if(sldns_buffer_limit(pkt) > NORMAL_UDP_SIZE &&
worker->daemon->cfg->harden_large_queries) {
verbose(VERB_QUERY, "request too large, discarded");
- return -1;
+ out->value = -1;
+ return;
}
if(LDNS_QR_WIRE(sldns_buffer_begin(pkt))) {
verbose(VERB_QUERY, "request has QR bit on, discarded");
- return -1;
+ out->value = -1;
+ return;
}
if(LDNS_TC_WIRE(sldns_buffer_begin(pkt))) {
LDNS_TC_CLR(sldns_buffer_begin(pkt));
verbose(VERB_QUERY, "request bad, has TC bit on");
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY &&
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_NOTIFY) {
- verbose(VERB_QUERY, "request unknown opcode %d",
+ verbose(VERB_QUERY, "request unknown opcode %d",
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL);
+ return;
}
if(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) != 1) {
- verbose(VERB_QUERY, "request wrong nr qd=%d",
+ verbose(VERB_QUERY, "request wrong nr qd=%d",
LDNS_QDCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
- if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 &&
+ if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 &&
(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 1 ||
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_NOTIFY)) {
- verbose(VERB_QUERY, "request wrong nr an=%d",
+ verbose(VERB_QUERY, "request wrong nr an=%d",
LDNS_ANCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
if(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) {
- verbose(VERB_QUERY, "request wrong nr ns=%d",
+ verbose(VERB_QUERY, "request wrong nr ns=%d",
LDNS_NSCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) {
- verbose(VERB_QUERY, "request wrong nr ar=%d",
+ verbose(VERB_QUERY, "request wrong nr ar=%d",
LDNS_ARCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
- return 0;
+ out->value = 0;
+ return;
}
-void
+void
worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
size_t len, int error, void* arg)
{
@@ -388,7 +412,7 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
/** check if a delegation is secure */
static enum sec_status
-check_delegation_secure(struct reply_info *rep)
+check_delegation_secure(struct reply_info *rep)
{
/* return smallest security status */
size_t i;
@@ -424,10 +448,10 @@ deleg_remove_nonsecure_additional(struct reply_info* rep)
s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
->security;
if(s != sec_status_secure) {
- memmove(rep->rrsets+i, rep->rrsets+i+1,
- sizeof(struct ub_packed_rrset_key*)*
+ memmove(rep->rrsets+i, rep->rrsets+i+1,
+ sizeof(struct ub_packed_rrset_key*)*
(rep->rrset_count - i - 1));
- rep->ar_numrrsets--;
+ rep->ar_numrrsets--;
rep->rrset_count--;
i--;
}
@@ -437,29 +461,30 @@ deleg_remove_nonsecure_additional(struct reply_info* rep)
/** answer nonrecursive query from the cache */
static int
answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
- uint16_t id, uint16_t flags, struct comm_reply* repinfo,
+ uint16_t id, uint16_t flags, struct comm_reply* repinfo,
struct edns_data* edns)
{
/* for a nonrecursive query return either:
* o an error (servfail; we try to avoid this)
* o a delegation (closest we have; this routine tries that)
- * o the answer (checked by answer_from_cache)
+ * o the answer (checked by answer_from_cache)
*
- * So, grab a delegation from the rrset cache.
+ * So, grab a delegation from the rrset cache.
* Then check if it needs validation, if so, this routine fails,
* so that iterator can prime and validator can verify rrsets.
*/
uint16_t udpsize = edns->udp_size;
int secure = 0;
time_t timenow = *worker->env.now;
- int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
+ int has_cd_bit = (flags&BIT_CD);
+ int must_validate = (!has_cd_bit || worker->env.cfg->ignore_cd)
&& worker->env.need_to_validate;
struct dns_msg *msg = NULL;
struct delegpt *dp;
- dp = dns_cache_find_delegation(&worker->env, qinfo->qname,
+ dp = dns_cache_find_delegation(&worker->env, qinfo->qname,
qinfo->qname_len, qinfo->qtype, qinfo->qclass,
- worker->scratchpad, &msg, timenow);
+ worker->scratchpad, &msg, timenow, 0, NULL, 0);
if(!dp) { /* no delegation, need to reprime */
return 0;
}
@@ -470,7 +495,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
if(must_validate) {
switch(check_delegation_secure(msg->rep)) {
case sec_status_unchecked:
- /* some rrsets have not been verified yet, go and
+ /* some rrsets have not been verified yet, go and
* let validator do that */
return 0;
case sec_status_bogus:
@@ -484,7 +509,14 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
msg->rep, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
return 0;
- error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
+ /* Attach the cached EDE (RFC8914) */
+ if(worker->env.cfg->ede &&
+ msg->rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, msg->rep->reason_bogus,
+ msg->rep->reason_bogus_str);
+ }
+ error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
if(worker->stats.extended) {
worker->stats.ans_bogus++;
@@ -511,11 +543,23 @@ 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(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO))
+ edns->edns_present = 0;
if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, msg->rep,
(int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad,
worker->env.now_tv))
return 0;
msg->rep->flags |= BIT_QR|BIT_RA;
+ /* Attach the cached EDE (RFC8914) if CD bit is set and the answer is
+ * bogus. */
+ if(worker->env.cfg->ede && has_cd_bit &&
+ (check_delegation_secure(msg->rep) == sec_status_bogus ||
+ check_delegation_secure(msg->rep) == sec_status_secure_sentinel_fail) &&
+ msg->rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, msg->rep->reason_bogus,
+ msg->rep->reason_bogus_str);
+ }
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)) {
@@ -523,7 +567,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
edns->opt_list_inplace_cb_out = NULL;
- error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
+ error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
}
if(worker->stats.extended) {
@@ -541,7 +585,8 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
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 sockaddr_storage* addr, socklen_t addrlen,
+ struct ub_packed_rrset_key** alias_rrset,
struct reply_info** encode_repp, struct auth_zones* az)
{
struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL};
@@ -553,14 +598,15 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
- alias_rrset, 0, worker->scratchpad, az))
+ alias_rrset, 0, worker->scratchpad, az, NULL))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
* was redirected to response-ip data. */
- if((actinfo.action == respip_deny ||
+ if(actinfo.action == respip_always_deny ||
+ ((actinfo.action == respip_deny ||
actinfo.action == respip_inform_deny) &&
- *encode_repp == rep)
+ *encode_repp == rep))
*encode_repp = NULL;
/* If address info is returned, it means the action should be an
@@ -568,7 +614,7 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
if(actinfo.addrinfo) {
respip_inform_print(&actinfo, qinfo->qname,
qinfo->qtype, qinfo->qclass, qinfo->local_alias,
- repinfo);
+ addr, addrlen);
if(worker->stats.extended && actinfo.rpz_used) {
if(actinfo.rpz_disabled)
@@ -604,7 +650,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
uint16_t udpsize = edns->udp_size;
struct reply_info* encode_rep = rep;
struct reply_info* partial_rep = *partial_repp;
- int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
+ int has_cd_bit = (flags&BIT_CD);
+ int must_validate = (!has_cd_bit || worker->env.cfg->ignore_cd)
&& worker->env.need_to_validate;
*partial_repp = NULL; /* avoid accidental further pass */
@@ -616,6 +663,14 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
if(worker->env.cfg->serve_expired_ttl &&
rep->serve_expired_ttl < timenow)
return 0;
+ /* Ignore expired failure answers */
+ if(FLAGS_GET_RCODE(rep->flags) !=
+ LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(rep->flags) !=
+ LDNS_RCODE_NXDOMAIN &&
+ FLAGS_GET_RCODE(rep->flags) !=
+ LDNS_RCODE_YXDOMAIN)
+ return 0;
if(!rrset_array_lock(rep->ref, rep->rrset_count, 0))
return 0;
*is_expired_answer = 1;
@@ -650,10 +705,18 @@ 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(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO))
+ edns->edns_present = 0;
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep,
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
goto bail_out;
+ /* Attach the cached EDE (RFC8914) */
+ if(worker->env.cfg->ede && rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, rep->reason_bogus,
+ rep->reason_bogus_str);
+ }
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
rrset_array_unlock_touch(worker->env.rrset_cache,
@@ -684,14 +747,12 @@ 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_cache_call(&worker->env, qinfo, NULL, rep,
- (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad,
- worker->env.now_tv))
- goto bail_out;
+ if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO))
+ edns->edns_present = 0;
*alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */
if((worker->daemon->use_response_ip || worker->daemon->use_rpz) &&
!partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep,
- repinfo, alias_rrset,
+ &repinfo->client_addr, repinfo->client_addrlen, alias_rrset,
&encode_rep, worker->env.auth_zones)) {
goto bail_out;
} else if(partial_rep &&
@@ -716,15 +777,36 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
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), *is_secure_answer)) {
- if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
- LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
+ } else {
+ if(*is_expired_answer == 1 &&
+ worker->env.cfg->ede_serve_expired && worker->env.cfg->ede) {
+ EDNS_OPT_LIST_APPEND_EDE(&edns->opt_list_out,
+ worker->scratchpad, LDNS_EDE_STALE_ANSWER, "");
+ }
+ /* Attach the cached EDE (RFC8914) if CD bit is set and the
+ * answer is bogus. */
+ if(*is_secure_answer == 0 &&
+ worker->env.cfg->ede && has_cd_bit &&
+ encode_rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, encode_rep->reason_bogus,
+ encode_rep->reason_bogus_str);
+ }
+ if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, encode_rep,
+ (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad,
worker->env.now_tv))
- edns->opt_list_inplace_cb_out = NULL;
- error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
- qinfo, id, flags, edns);
+ goto bail_out;
+ if(!reply_info_answer_encode(qinfo, encode_rep, id, flags,
+ repinfo->c->buffer, timenow, 1, worker->scratchpad,
+ udpsize, edns, (int)(edns->bits & EDNS_DO),
+ *is_secure_answer)) {
+ if(!inplace_cb_reply_servfail_call(&worker->env, qinfo,
+ NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo,
+ worker->scratchpad, worker->env.now_tv))
+ edns->opt_list_inplace_cb_out = NULL;
+ error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
+ qinfo, id, flags, edns);
+ }
}
/* cannot send the reply right now, because blocking network syscall
* is bad while holding locks. */
@@ -734,17 +816,19 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
return 1;
bail_out:
- rrset_array_unlock_touch(worker->env.rrset_cache,
+ rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
return 0;
}
/** Reply to client and perform prefetch to keep cache up to date. */
static void
-reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
- uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply)
+reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
+ uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply,
+ int rpz_passthru, struct edns_option* opt_list)
{
- /* first send answer to client to keep its latency
+ (void)opt_list;
+ /* first send answer to client to keep its latency
* as small as a cachereply */
if(!noreply) {
if(repinfo->c->tcp_req_info) {
@@ -755,13 +839,24 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
comm_point_send_reply(repinfo);
}
server_stats_prefetch(&worker->stats, worker);
-
+#ifdef CLIENT_SUBNET
+ /* Check if the subnet module is enabled. In that case pass over the
+ * comm_reply information for ECS generation later. The mesh states are
+ * unique when subnet is enabled. */
+ if(modstack_find(&worker->env.mesh->mods, "subnetcache") != -1
+ && worker->env.unique_mesh) {
+ mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
+ PREFETCH_EXPIRY_ADD, rpz_passthru,
+ &repinfo->client_addr, opt_list);
+ return;
+ }
+#endif
/* create the prefetch in the mesh as a normal lookup without
* client addrs waiting, which has the cache blacklisted (to bypass
* the cache and go to the network for the data). */
/* this (potentially) runs the mesh for the new query */
- mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
- PREFETCH_EXPIRY_ADD);
+ mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
+ PREFETCH_EXPIRY_ADD, rpz_passthru, NULL, NULL);
}
/**
@@ -906,12 +1001,12 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
struct config_file* cfg = w->env.cfg;
if(qinfo->qtype != LDNS_RR_TYPE_ANY && qinfo->qtype != LDNS_RR_TYPE_TXT)
return 0;
- if(query_dname_compare(qinfo->qname,
+ if(query_dname_compare(qinfo->qname,
(uint8_t*)"\002id\006server") == 0 ||
- query_dname_compare(qinfo->qname,
+ query_dname_compare(qinfo->qname,
(uint8_t*)"\010hostname\004bind") == 0)
{
- if(cfg->hide_identity)
+ if(cfg->hide_identity)
return 0;
if(cfg->identity==NULL || cfg->identity[0]==0) {
char buf[MAXHOSTNAMELEN+1];
@@ -926,12 +1021,12 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
else chaos_replyonestr(pkt, cfg->identity, edns, w, repinfo);
return 1;
}
- if(query_dname_compare(qinfo->qname,
+ if(query_dname_compare(qinfo->qname,
(uint8_t*)"\007version\006server") == 0 ||
- query_dname_compare(qinfo->qname,
+ query_dname_compare(qinfo->qname,
(uint8_t*)"\007version\004bind") == 0)
{
- if(cfg->hide_version)
+ if(cfg->hide_version)
return 0;
if(cfg->version==NULL || cfg->version[0]==0)
chaos_replyonestr(pkt, PACKAGE_STRING, edns, w, repinfo);
@@ -957,12 +1052,14 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
* @param w: worker
* @param qinfo: query info. Pointer into packet buffer.
* @param edns: edns info from query.
- * @param repinfo: reply info with source address.
+ * @param addr: client address.
+ * @param addrlen: client address length.
* @param pkt: packet buffer.
*/
static void
-answer_notify(struct worker* w, struct query_info* qinfo,
- struct edns_data* edns, sldns_buffer* pkt, struct comm_reply* repinfo)
+answer_notify(struct worker* w, struct query_info* qinfo,
+ struct edns_data* edns, sldns_buffer* pkt,
+ struct sockaddr_storage* addr, socklen_t addrlen)
{
int refused = 0;
int rcode = LDNS_RCODE_NOERROR;
@@ -971,8 +1068,8 @@ answer_notify(struct worker* w, struct query_info* qinfo,
if(!w->env.auth_zones) return;
has_serial = auth_zone_parse_notify_serial(pkt, &serial);
if(auth_zones_notify(w->env.auth_zones, &w->env, qinfo->qname,
- qinfo->qname_len, qinfo->qclass, &repinfo->addr,
- repinfo->addrlen, has_serial, serial, &refused)) {
+ qinfo->qname_len, qinfo->qclass, addr,
+ addrlen, has_serial, serial, &refused)) {
rcode = LDNS_RCODE_NOERROR;
} else {
if(refused)
@@ -997,7 +1094,7 @@ answer_notify(struct worker* w, struct query_info* qinfo,
"servfail for NOTIFY %sfor %s from", sr, zname);
else snprintf(buf, sizeof(buf),
"received NOTIFY %sfor %s from", sr, zname);
- log_addr(VERB_DETAIL, buf, &repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_DETAIL, buf, addr, addrlen);
}
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
@@ -1012,52 +1109,250 @@ answer_notify(struct worker* w, struct query_info* qinfo,
static int
deny_refuse(struct comm_point* c, enum acl_access acl,
enum acl_access deny, enum acl_access refuse,
- struct worker* worker, struct comm_reply* repinfo)
+ struct worker* worker, struct comm_reply* repinfo,
+ struct acl_addr* acladdr, int ede,
+ struct check_request_result* check_result)
{
if(acl == deny) {
+ if(verbosity >= VERB_ALGO) {
+ log_acl_action("dropped", &repinfo->client_addr,
+ repinfo->client_addrlen, acl, acladdr);
+ log_buf(VERB_ALGO, "dropped", c->buffer);
+ }
comm_point_drop_reply(repinfo);
if(worker->stats.extended)
worker->stats.unwanted_queries++;
return 0;
} else if(acl == refuse) {
- log_addr(VERB_ALGO, "refused query from",
- &repinfo->addr, repinfo->addrlen);
- log_buf(VERB_ALGO, "refuse", c->buffer);
+ size_t opt_rr_mark;
+
+ if(verbosity >= VERB_ALGO) {
+ log_acl_action("refused", &repinfo->client_addr,
+ repinfo->client_addrlen, acl, acladdr);
+ log_buf(VERB_ALGO, "refuse", c->buffer);
+ }
+
if(worker->stats.extended)
worker->stats.unwanted_queries++;
- if(worker_check_request(c->buffer, worker) == -1) {
+ worker_check_request(c->buffer, worker, check_result);
+ if(check_result->value != 0) {
+ if(check_result->value != -1) {
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ check_result->value);
+ return 1;
+ }
comm_point_drop_reply(repinfo);
- return 0; /* discard this */
+ return 0;
+ }
+ /* worker_check_request() above guarantees that the buffer contains at
+ * least a header and that qdcount == 1
+ */
+ log_assert(sldns_buffer_limit(c->buffer) >= LDNS_HEADER_SIZE
+ && LDNS_QDCOUNT(sldns_buffer_begin(c->buffer)) == 1);
+
+ sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE); /* skip header */
+
+ /* check additional section is present and that we respond with EDEs */
+ if(LDNS_ARCOUNT(sldns_buffer_begin(c->buffer)) != 1
+ || !ede) {
+ LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ 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;
+ }
+
+ if (!query_dname_len(c->buffer)) {
+ LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+ /* space available for query type and class? */
+ if (sldns_buffer_remaining(c->buffer) < 2 * sizeof(uint16_t)) {
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
+ sldns_buffer_flip(c->buffer);
+ return 1;
}
- sldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE);
- sldns_buffer_write_at(c->buffer, 4,
- (uint8_t*)"\0\0\0\0\0\0\0\0", 8);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_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_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qtype */
+
+ sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qclass */
+
+ /* The OPT RR to be returned should come directly after
+ * the query, so mark this spot.
+ */
+ opt_rr_mark = sldns_buffer_position(c->buffer);
+
+ /* Skip through the RR records */
+ if(LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)) != 0 ||
+ LDNS_NSCOUNT(sldns_buffer_begin(c->buffer)) != 0) {
+ if(!skip_pkt_rrs(c->buffer,
+ ((int)LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)))+
+ ((int)LDNS_NSCOUNT(sldns_buffer_begin(c->buffer))))) {
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+ }
+ /* Do we have a valid OPT RR here? If not return REFUSED (could be a valid TSIG or something so no FORMERR) */
+ /* domain name must be the root of length 1. */
+ if(sldns_buffer_remaining(c->buffer) < 1 || *sldns_buffer_current(c->buffer) != 0) {
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ } else {
+ sldns_buffer_skip(c->buffer, 1); /* skip root label */
+ }
+ if(sldns_buffer_remaining(c->buffer) < 2 ||
+ sldns_buffer_read_u16(c->buffer) != LDNS_RR_TYPE_OPT) {
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+ /* Write OPT RR directly after the query,
+ * so without the (possibly skipped) Answer and NS RRs
+ */
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_clear(c->buffer); /* reset write limit */
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+
+ /* Check if OPT record can be written
+ * 17 == root label (1) + RR type (2) + UDP Size (2)
+ * + Fields (4) + rdata len (2) + EDE Option code (2)
+ * + EDE Option length (2) + EDE info-code (2)
+ */
+ if (sldns_buffer_available(c->buffer, 17) == 0) {
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 1);
+
+ /* root label */
+ sldns_buffer_write_u8(c->buffer, 0);
+ sldns_buffer_write_u16(c->buffer, LDNS_RR_TYPE_OPT);
+ sldns_buffer_write_u16(c->buffer, EDNS_ADVERTISED_SIZE);
+
+ /* write OPT Record TTL Field */
+ sldns_buffer_write_u32(c->buffer, 0);
+
+ /* write rdata len: EDE option + length + info-code */
+ sldns_buffer_write_u16(c->buffer, 6);
+
+ /* write OPTIONS; add EDE option code */
+ sldns_buffer_write_u16(c->buffer, LDNS_EDNS_EDE);
+
+ /* write single EDE option length (for just 1 info-code) */
+ sldns_buffer_write_u16(c->buffer, 2);
+
+ /* write single EDE info-code */
+ sldns_buffer_write_u16(c->buffer, LDNS_EDE_PROHIBITED);
+
sldns_buffer_flip(c->buffer);
+
+ verbose(VERB_ALGO, "attached EDE code: %d", LDNS_EDE_PROHIBITED);
+
return 1;
+
}
return -1;
}
static int
-deny_refuse_all(struct comm_point* c, enum acl_access acl,
- struct worker* worker, struct comm_reply* repinfo)
+deny_refuse_all(struct comm_point* c, enum acl_access* acl,
+ struct worker* worker, struct comm_reply* repinfo,
+ struct acl_addr** acladdr, int ede, int check_proxy,
+ struct check_request_result* check_result)
{
- return deny_refuse(c, acl, acl_deny, acl_refuse, worker, repinfo);
+ if(check_proxy) {
+ *acladdr = acl_addr_lookup(worker->daemon->acl,
+ &repinfo->remote_addr, repinfo->remote_addrlen);
+ } else {
+ *acladdr = acl_addr_lookup(worker->daemon->acl,
+ &repinfo->client_addr, repinfo->client_addrlen);
+ }
+ /* If there is no ACL based on client IP use the interface ACL. */
+ if(!(*acladdr) && c->socket) {
+ *acladdr = c->socket->acl;
+ }
+ *acl = acl_get_control(*acladdr);
+ return deny_refuse(c, *acl, acl_deny, acl_refuse, worker, repinfo,
+ *acladdr, ede, check_result);
}
static int
deny_refuse_non_local(struct comm_point* c, enum acl_access acl,
- struct worker* worker, struct comm_reply* repinfo)
+ struct worker* worker, struct comm_reply* repinfo,
+ struct acl_addr* acladdr, int ede,
+ struct check_request_result* check_result)
+{
+ return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local,
+ worker, repinfo, acladdr, ede, check_result);
+}
+
+/* Check if the query is blocked by source IP rate limiting.
+ * Returns 1 if it passes the check, 0 otherwise. */
+static int
+check_ip_ratelimit(struct worker* worker, struct sockaddr_storage* addr,
+ socklen_t addrlen, int has_cookie, sldns_buffer* pkt)
{
- return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local, worker, repinfo);
+ if(!infra_ip_ratelimit_inc(worker->env.infra_cache, addr, addrlen,
+ *worker->env.now, has_cookie,
+ worker->env.cfg->ip_ratelimit_backoff, pkt)) {
+ /* See if we can pass through with slip factor */
+ if(!has_cookie && worker->env.cfg->ip_ratelimit_factor != 0 &&
+ ub_random_max(worker->env.rnd,
+ worker->env.cfg->ip_ratelimit_factor) == 0) {
+ char addrbuf[128];
+ addr_to_str(addr, addrlen, addrbuf, sizeof(addrbuf));
+ verbose(VERB_QUERY, "ip_ratelimit allowed through for "
+ "ip address %s because of slip in "
+ "ip_ratelimit_factor", addrbuf);
+ return 1;
+ }
+ return 0;
+ }
+ return 1;
}
-int
+int
worker_handle_request(struct comm_point* c, void* arg, int error,
struct comm_reply* repinfo)
{
@@ -1067,12 +1362,16 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
struct lruhash_entry* e;
struct query_info qinfo;
struct edns_data edns;
+ struct edns_option* original_edns_list = NULL;
enum acl_access acl;
struct acl_addr* acladdr;
+ int pre_edns_ip_ratelimit = 1;
int rc = 0;
int need_drop = 0;
int is_expired_answer = 0;
int is_secure_answer = 0;
+ int rpz_passthru = 0;
+ long long wait_queue_time = 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. */
@@ -1081,6 +1380,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
struct query_info* lookup_qinfo = &qinfo;
struct query_info qinfo_tmp; /* placeholder for lookup_qinfo */
struct respip_client_info* cinfo = NULL, cinfo_tmp;
+ struct timeval wait_time;
+ struct check_request_result check_result = {0,0};
memset(&qinfo, 0, sizeof(qinfo));
if((error != NETEVENT_NOERROR && error != NETEVENT_DONE)|| !repinfo) {
@@ -1088,6 +1389,20 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
verbose(VERB_ALGO, "handle request called with err=%d", error);
return 0;
}
+
+ if (worker->env.cfg->sock_queue_timeout && timeval_isset(&c->recv_tv)) {
+ timeval_subtract(&wait_time, worker->env.now_tv, &c->recv_tv);
+ wait_queue_time = wait_time.tv_sec * 1000000 + wait_time.tv_usec;
+ if (worker->stats.max_query_time_us < wait_queue_time)
+ worker->stats.max_query_time_us = wait_queue_time;
+ if(wait_queue_time >
+ (long long)(worker->env.cfg->sock_queue_timeout * 1000000)) {
+ /* count and drop queries that were sitting in the socket queue too long */
+ worker->stats.num_queries_timed_out++;
+ return 0;
+ }
+ }
+
#ifdef USE_DNSCRYPT
repinfo->max_udp_size = worker->daemon->cfg->max_udp_size;
if(!dnsc_handle_curved_request(worker->daemon->dnscenv, repinfo)) {
@@ -1097,19 +1412,20 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
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) {
+ worker_check_request(c->buffer, worker, &check_result);
+ if(check_result.value != 0) {
verbose(VERB_ALGO,
"dnscrypt: worker check request: bad query.");
- log_addr(VERB_CLIENT,"from",&repinfo->addr,
- repinfo->addrlen);
+ log_addr(VERB_CLIENT,"from",&repinfo->client_addr,
+ repinfo->client_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);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
return 0;
}
@@ -1137,26 +1453,36 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
* sending src (client)/dst (local service) addresses over DNSTAP from incoming request handler
*/
if(worker->dtenv.log_client_query_messages) {
- log_addr(VERB_ALGO, "request from client", &repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_ALGO, "request from client", &repinfo->client_addr, repinfo->client_addrlen);
log_addr(VERB_ALGO, "to local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen);
- dt_msg_send_client_query(&worker->dtenv, &repinfo->addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->buffer);
+ dt_msg_send_client_query(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->ssl, c->buffer,
+ ((worker->env.cfg->sock_queue_timeout && timeval_isset(&c->recv_tv))?&c->recv_tv:NULL));
}
#endif
- 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)
- {
+ /* Check deny/refuse ACLs */
+ if(repinfo->is_proxied) {
+ if((ret=deny_refuse_all(c, &acl, worker, repinfo, &acladdr,
+ worker->env.cfg->ede, 1, &check_result)) != -1) {
+ if(ret == 1)
+ goto send_reply;
+ return ret;
+ }
+ }
+ if((ret=deny_refuse_all(c, &acl, worker, repinfo, &acladdr,
+ worker->env.cfg->ede, 0, &check_result)) != -1) {
if(ret == 1)
goto send_reply;
return ret;
}
- if((ret=worker_check_request(c->buffer, worker)) != 0) {
+
+ worker_check_request(c->buffer, worker, &check_result);
+ if(check_result.value != 0) {
verbose(VERB_ALGO, "worker check request: bad query.");
- log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
- if(ret != -1) {
+ log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
+ if(check_result.value != -1) {
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), ret);
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ check_result.value);
return 1;
}
comm_point_drop_reply(repinfo);
@@ -1164,32 +1490,27 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
}
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,
- worker->env.cfg->ip_ratelimit_backoff, c->buffer)) {
- /* 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) == 0) {
-
- char addrbuf[128];
- addr_to_str(&repinfo->addr, repinfo->addrlen,
- addrbuf, sizeof(addrbuf));
- verbose(VERB_QUERY, "ip_ratelimit allowed through for ip address %s because of slip in ip_ratelimit_factor",
- addrbuf);
- } else {
+ pre_edns_ip_ratelimit = !worker->env.cfg->do_answer_cookie
+ || sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE
+ || LDNS_ARCOUNT(sldns_buffer_begin(c->buffer)) == 0;
+
+ /* If the IP rate limiting check needs extra EDNS information (e.g.,
+ * DNS Cookies) postpone the check until after EDNS is parsed. */
+ if(pre_edns_ip_ratelimit) {
+ /* NOTE: we always check the repinfo->client_address.
+ * IP ratelimiting is implicitly disabled for proxies. */
+ if(!check_ip_ratelimit(worker, &repinfo->client_addr,
+ repinfo->client_addrlen, 0, c->buffer)) {
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.");
- log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
memset(&qinfo, 0, sizeof(qinfo)); /* zero qinfo.qname */
if(worker_err_ratelimit(worker, LDNS_RCODE_FORMERR) == -1) {
comm_point_drop_reply(repinfo);
@@ -1197,58 +1518,61 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
}
sldns_buffer_rewind(c->buffer);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
goto send_reply;
}
if(worker->env.cfg->log_queries) {
char ip[128];
- addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
+ addr_to_str(&repinfo->client_addr, repinfo->client_addrlen, ip, sizeof(ip));
log_query_in(ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
}
- if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
+ if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
qinfo.qtype == LDNS_RR_TYPE_IXFR) {
verbose(VERB_ALGO, "worker request: refused zone transfer.");
- log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
sldns_buffer_rewind(c->buffer);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
if(worker->stats.extended) {
worker->stats.qtype[qinfo.qtype]++;
}
goto send_reply;
}
- if(qinfo.qtype == LDNS_RR_TYPE_OPT ||
+ 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);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_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_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
if(worker->stats.extended) {
worker->stats.qtype[qinfo.qtype]++;
}
goto send_reply;
}
- if((ret=parse_edns_from_query_pkt(c->buffer, &edns, worker->env.cfg, c,
- worker->scratchpad)) != 0) {
+ if((ret=parse_edns_from_query_pkt(
+ c->buffer, &edns, worker->env.cfg, c, repinfo,
+ (worker->env.now ? *worker->env.now : time(NULL)),
+ worker->scratchpad)) != 0) {
struct edns_data reply_edns;
verbose(VERB_ALGO, "worker parse edns: formerror.");
- log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
memset(&reply_edns, 0, sizeof(reply_edns));
reply_edns.edns_present = 1;
- reply_edns.udp_size = EDNS_ADVERTISED_SIZE;
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), ret);
error_encode(c->buffer, ret, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns);
@@ -1257,22 +1581,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
}
if(edns.edns_present) {
if(edns.edns_version != 0) {
- edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4);
- edns.edns_version = EDNS_ADVERTISED_VERSION;
- edns.udp_size = EDNS_ADVERTISED_SIZE;
- edns.bits &= EDNS_DO;
edns.opt_list_in = NULL;
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
- edns.padding_block_size = 0;
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,
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
+ extended_error_encode(c->buffer, EDNS_RCODE_BADVERS, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
- sldns_buffer_read_u16_at(c->buffer, 2), NULL);
- if(sldns_buffer_capacity(c->buffer) >=
- sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns))
- attach_edns_record(c->buffer, &edns);
+ sldns_buffer_read_u16_at(c->buffer, 2), 0, &edns);
regional_free_all(worker->scratchpad);
goto send_reply;
}
@@ -1280,27 +1597,86 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
worker->daemon->cfg->harden_short_bufsize) {
verbose(VERB_QUERY, "worker request: EDNS bufsize %d ignored",
(int)edns.udp_size);
- log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
edns.udp_size = NORMAL_UDP_SIZE;
}
}
+
+ /* Get stats for cookies */
+ server_stats_downstream_cookie(&worker->stats, &edns);
+
+ /* If the IP rate limiting check was postponed, check now. */
+ if(!pre_edns_ip_ratelimit) {
+ /* NOTE: we always check the repinfo->client_address.
+ * IP ratelimiting is implicitly disabled for proxies. */
+ if(!check_ip_ratelimit(worker, &repinfo->client_addr,
+ repinfo->client_addrlen, edns.cookie_valid,
+ c->buffer)) {
+ worker->stats.num_queries_ip_ratelimited++;
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ }
+
+ /* "if, else if" sequence below deals with downstream DNS Cookies */
+ if(acl != acl_allow_cookie)
+ ; /* pass; No cookie downstream processing whatsoever */
+
+ else if(edns.cookie_valid)
+ ; /* pass; Valid cookie is good! */
+
+ else if(c->type != comm_udp)
+ ; /* pass; Stateful transport */
+
+ else if(edns.cookie_present) {
+ /* Cookie present, but not valid: Cookie was bad! */
+ extended_error_encode(c->buffer,
+ LDNS_EXT_RCODE_BADCOOKIE, &qinfo,
+ *(uint16_t*)(void *)
+ sldns_buffer_begin(c->buffer),
+ sldns_buffer_read_u16_at(c->buffer, 2),
+ 0, &edns);
+ regional_free_all(worker->scratchpad);
+ goto send_reply;
+ } else {
+ /* Cookie required, but no cookie present on UDP */
+ verbose(VERB_ALGO, "worker request: "
+ "need cookie or stateful transport");
+ log_addr(VERB_ALGO, "from",&repinfo->remote_addr
+ , repinfo->remote_addrlen);
+ EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
+ worker->scratchpad, LDNS_EDE_OTHER,
+ "DNS Cookie needed for UDP replies");
+ error_encode(c->buffer,
+ (LDNS_RCODE_REFUSED|BIT_TC), &qinfo,
+ *(uint16_t*)(void *)
+ sldns_buffer_begin(c->buffer),
+ sldns_buffer_read_u16_at(c->buffer, 2),
+ &edns);
+ regional_free_all(worker->scratchpad);
+ goto send_reply;
+ }
+
if(edns.udp_size > worker->daemon->cfg->max_udp_size &&
c->type == comm_udp) {
verbose(VERB_QUERY,
"worker request: max UDP reply size modified"
" (%d to max-udp-size)", (int)edns.udp_size);
- log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
edns.udp_size = worker->daemon->cfg->max_udp_size;
}
if(edns.udp_size < LDNS_HEADER_SIZE) {
verbose(VERB_ALGO, "worker request: edns is too small.");
- log_addr(VERB_CLIENT, "from", &repinfo->addr, repinfo->addrlen);
+ log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
+ repinfo->client_addrlen);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_TC_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_SERVFAIL);
sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
- sldns_buffer_write_at(c->buffer, 4,
+ 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);
@@ -1318,7 +1694,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
}
if(LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) ==
LDNS_PACKET_NOTIFY) {
- answer_notify(worker, &qinfo, &edns, c->buffer, repinfo);
+ answer_notify(worker, &qinfo, &edns, c->buffer,
+ &repinfo->client_addr, repinfo->client_addrlen);
regional_free_all(worker->scratchpad);
goto send_reply;
}
@@ -1338,7 +1715,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
if(worker->env.auth_zones &&
rpz_callback_from_worker_request(worker->env.auth_zones,
&worker->env, &qinfo, &edns, c->buffer, worker->scratchpad,
- repinfo, acladdr->taglist, acladdr->taglen, &worker->stats)) {
+ repinfo, acladdr->taglist, acladdr->taglen, &worker->stats,
+ &rpz_passthru)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
@@ -1364,7 +1742,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
/* We've looked in our local zones. If the answer isn't there, we
* might need to bail out based on ACLs now. */
- if((ret=deny_refuse_non_local(c, acl, worker, repinfo)) != -1)
+ if((ret=deny_refuse_non_local(c, acl, worker, repinfo, acladdr,
+ worker->env.cfg->ede, &check_result)) != -1)
{
regional_free_all(worker->scratchpad);
if(ret == 1)
@@ -1383,12 +1762,17 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
* ACLs allow the snooping. */
if(!(LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) &&
acl != acl_allow_snoop ) {
+ if(worker->env.cfg->ede) {
+ EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
+ worker->scratchpad, LDNS_EDE_NOT_AUTHORITATIVE, "");
+ }
error_encode(c->buffer, LDNS_RCODE_REFUSED, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
- sldns_buffer_read_u16_at(c->buffer, 2), NULL);
+ sldns_buffer_read_u16_at(c->buffer, 2), &edns);
regional_free_all(worker->scratchpad);
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
- &repinfo->addr, repinfo->addrlen);
+ &repinfo->client_addr, repinfo->client_addrlen);
+
goto send_reply;
}
@@ -1429,6 +1813,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
cinfo = &cinfo_tmp;
}
+ /* Keep the original edns list around. The pointer could change if there is
+ * a cached answer (through the inplace callback function there).
+ * No need to actually copy the contents as they shouldn't change.
+ * Used while prefetching and subnet is enabled. */
+ original_edns_list = edns.opt_list_in;
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
@@ -1439,10 +1828,11 @@ lookup_cache:
is_secure_answer = 0;
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))) {
+ struct reply_info* rep = (struct reply_info*)e->data;
/* answer from cache - we have acquired a readlock on it */
- if(answer_from_cache(worker, &qinfo,
- cinfo, &need_drop, &is_expired_answer, &is_secure_answer,
- &alias_rrset, &partial_rep, (struct reply_info*)e->data,
+ if(answer_from_cache(worker, &qinfo, cinfo, &need_drop,
+ &is_expired_answer, &is_secure_answer,
+ &alias_rrset, &partial_rep, rep,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) {
@@ -1450,21 +1840,22 @@ lookup_cache:
* 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.now >=
- ((struct reply_info*)e->data)->prefetch_ttl) ||
- (worker->env.cfg->serve_expired &&
- *worker->env.now >= ((struct reply_info*)e->data)->ttl)) {
-
- time_t leeway = ((struct reply_info*)e->
- data)->ttl - *worker->env.now;
- if(((struct reply_info*)e->data)->ttl
- < *worker->env.now)
+ if((worker->env.cfg->prefetch &&
+ *worker->env.now >= rep->prefetch_ttl) ||
+ (worker->env.cfg->serve_expired &&
+ *worker->env.now > rep->ttl)) {
+
+ time_t leeway = rep->ttl - *worker->env.now;
+ if(rep->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,
- (partial_rep || need_drop));
+ (partial_rep || need_drop),
+ rpz_passthru,
+ original_edns_list);
if(!partial_rep) {
rc = 0;
regional_free_all(worker->scratchpad);
@@ -1501,10 +1892,11 @@ lookup_cache:
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,
+ *(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;
@@ -1519,15 +1911,16 @@ lookup_cache:
if(verbosity >= VERB_CLIENT) {
if(c->type == comm_udp)
log_addr(VERB_CLIENT, "udp request from",
- &repinfo->addr, repinfo->addrlen);
+ &repinfo->client_addr, repinfo->client_addrlen);
else log_addr(VERB_CLIENT, "tcp request from",
- &repinfo->addr, repinfo->addrlen);
+ &repinfo->client_addr, repinfo->client_addrlen);
}
/* grab a work request structure for this new request */
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));
+ &edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
+ rpz_passthru);
regional_free_all(worker->scratchpad);
worker_mem_report(worker, NULL);
return 0;
@@ -1552,8 +1945,8 @@ send_reply_rc:
*/
if(worker->dtenv.log_client_response_messages) {
log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen);
- log_addr(VERB_ALGO, "response to client", &repinfo->addr, repinfo->addrlen);
- dt_msg_send_client_response(&worker->dtenv, &repinfo->addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->buffer);
+ log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen);
+ dt_msg_send_client_response(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->ssl, c->buffer);
}
#endif
if(worker->env.cfg->log_replies)
@@ -1565,11 +1958,17 @@ send_reply_rc:
/* log original qname, before the local alias was
* used to resolve that CNAME to something else */
qinfo.qname = qinfo.local_alias->rrset->rk.dname;
- log_reply_info(NO_VERBOSE, &qinfo, &repinfo->addr, repinfo->addrlen,
- tv, 1, c->buffer);
+ log_reply_info(NO_VERBOSE, &qinfo,
+ &repinfo->client_addr, repinfo->client_addrlen,
+ tv, 1, c->buffer,
+ (worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr->ai_addr:NULL),
+ c->type);
} else {
- log_reply_info(NO_VERBOSE, &qinfo, &repinfo->addr, repinfo->addrlen,
- tv, 1, c->buffer);
+ log_reply_info(NO_VERBOSE, &qinfo,
+ &repinfo->client_addr, repinfo->client_addrlen,
+ tv, 1, c->buffer,
+ (worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr->ai_addr:NULL),
+ c->type);
}
}
#ifdef USE_DNSCRYPT
@@ -1580,10 +1979,10 @@ send_reply_rc:
return rc;
}
-void
+void
worker_sighandler(int sig, void* arg)
{
- /* note that log, print, syscalls here give race conditions.
+ /* note that log, print, syscalls here give race conditions.
* And cause hangups if the log-lock is held by the application. */
struct worker* worker = (struct worker*)arg;
switch(sig) {
@@ -1592,6 +1991,9 @@ worker_sighandler(int sig, void* arg)
comm_base_exit(worker->base);
break;
#endif
+#ifdef SIGBREAK
+ case SIGBREAK:
+#endif
case SIGINT:
worker->need_to_exit = 1;
comm_base_exit(worker->base);
@@ -1655,13 +2057,13 @@ void worker_probe_timer_cb(void* arg)
comm_timer_set(worker->env.probe_timer, &tv);
}
-struct worker*
+struct worker*
worker_create(struct daemon* daemon, int id, int* ports, int n)
{
unsigned int seed;
- struct worker* worker = (struct worker*)calloc(1,
+ struct worker* worker = (struct worker*)calloc(1,
sizeof(struct worker));
- if(!worker)
+ if(!worker)
return NULL;
worker->numports = n;
worker->ports = (int*)memdup(ports, sizeof(int)*n);
@@ -1689,7 +2091,7 @@ worker_create(struct daemon* daemon, int id, int* ports, int n)
}
int
-worker_init(struct worker* worker, struct config_file *cfg,
+worker_init(struct worker* worker, struct config_file *cfg,
struct listen_port* ports, int do_sigs)
{
#ifdef USE_DNSTAP
@@ -1697,6 +2099,9 @@ worker_init(struct worker* worker, struct config_file *cfg,
#else
void* dtenv = NULL;
#endif
+#ifdef HAVE_GETTID
+ worker->thread_tid = gettid();
+#endif
worker->need_to_exit = 0;
worker->base = comm_base_create(do_sigs);
if(!worker->base) {
@@ -1710,15 +2115,18 @@ worker_init(struct worker* worker, struct config_file *cfg,
#ifdef SIGHUP
ub_thread_sig_unblock(SIGHUP);
#endif
+#ifdef SIGBREAK
+ ub_thread_sig_unblock(SIGBREAK);
+#endif
ub_thread_sig_unblock(SIGINT);
#ifdef SIGQUIT
ub_thread_sig_unblock(SIGQUIT);
#endif
ub_thread_sig_unblock(SIGTERM);
#ifndef LIBEVENT_SIGNAL_PROBLEM
- worker->comsig = comm_signal_create(worker->base,
+ worker->comsig = comm_signal_create(worker->base,
worker_sighandler, worker);
- if(!worker->comsig
+ if(!worker->comsig
#ifdef SIGHUP
|| !comm_signal_bind(worker->comsig, SIGHUP)
#endif
@@ -1726,13 +2134,16 @@ worker_init(struct worker* worker, struct config_file *cfg,
|| !comm_signal_bind(worker->comsig, SIGQUIT)
#endif
|| !comm_signal_bind(worker->comsig, SIGTERM)
+#ifdef SIGBREAK
+ || !comm_signal_bind(worker->comsig, SIGBREAK)
+#endif
|| !comm_signal_bind(worker->comsig, SIGINT)) {
log_err("could not create signal handlers");
worker_delete(worker);
return 0;
}
#endif /* LIBEVENT_SIGNAL_PROBLEM */
- if(!daemon_remote_open_accept(worker->daemon->rc,
+ if(!daemon_remote_open_accept(worker->daemon->rc,
worker->daemon->rc_ports, worker)) {
worker_delete(worker);
return 0;
@@ -1766,8 +2177,8 @@ worker_init(struct worker* worker, struct config_file *cfg,
return 0;
}
worker->back = outside_network_create(worker->base,
- cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports,
- cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
+ cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports,
+ cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
cfg->do_tcp?cfg->outgoing_num_tcp:0, cfg->ip_dscp,
worker->daemon->env->infra_cache, worker->rndstate,
cfg->use_caps_bits_for_id, worker->ports, worker->numports,
@@ -1792,13 +2203,13 @@ worker_init(struct worker* worker, struct config_file *cfg,
worker_delete(worker);
return 0;
}
- worker->stat_timer = comm_timer_create(worker->base,
+ worker->stat_timer = comm_timer_create(worker->base,
worker_stat_timer_cb, worker);
if(!worker->stat_timer) {
log_err("could not create statistics timer");
}
- /* we use the msg_buffer_size as a good estimate for what the
+ /* we use the msg_buffer_size as a good estimate for what the
* user wants for memory usage sizes */
worker->scratchpad = regional_create_custom(cfg->msg_buffer_size);
if(!worker->scratchpad) {
@@ -1808,15 +2219,14 @@ worker_init(struct worker* worker, struct config_file *cfg,
}
server_stats_init(&worker->stats, cfg);
- alloc_init(&worker->alloc, &worker->daemon->superalloc,
- worker->thread_num);
- alloc_set_id_cleanup(&worker->alloc, &worker_alloc_cleanup, worker);
+ worker->alloc = worker->daemon->worker_allocs[worker->thread_num];
+ alloc_set_id_cleanup(worker->alloc, &worker_alloc_cleanup, worker);
worker->env = *worker->daemon->env;
comm_base_timept(worker->base, &worker->env.now, &worker->env.now_tv);
worker->env.worker = worker;
worker->env.worker_base = worker->base;
worker->env.send_query = &worker_send_query;
- worker->env.alloc = &worker->alloc;
+ worker->env.alloc = worker->alloc;
worker->env.outnet = worker->back;
worker->env.rnd = worker->rndstate;
/* If case prefetch is triggered, the corresponding mesh will clear
@@ -1908,23 +2318,24 @@ worker_init(struct worker* worker, struct config_file *cfg,
worker_mem_report(worker, NULL);
/* if statistics enabled start timer */
if(worker->env.cfg->stat_interval > 0) {
- verbose(VERB_ALGO, "set statistics interval %d secs",
+ verbose(VERB_ALGO, "set statistics interval %d secs",
worker->env.cfg->stat_interval);
worker_restart_timer(worker);
}
+ pp_init(&sldns_write_uint16, &sldns_write_uint32);
return 1;
}
-void
+void
worker_work(struct worker* worker)
{
comm_base_dispatch(worker->base);
}
-void
+void
worker_delete(struct worker* worker)
{
- if(!worker)
+ if(!worker)
return;
if(worker->env.mesh && verbosity >= VERB_OPS) {
server_stats_log(&worker->stats, worker, worker->thread_num);
@@ -1960,7 +2371,7 @@ worker_delete(struct worker* worker)
#endif /* USE_DNSTAP */
comm_base_delete(worker->base);
ub_randfree(worker->rndstate);
- alloc_clear(&worker->alloc);
+ /* don't touch worker->alloc, as it's maintained in daemon */
regional_destroy(worker->env.scratch);
regional_destroy(worker->scratchpad);
free(worker);
@@ -1976,7 +2387,7 @@ worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec,
struct worker* worker = q->env->worker;
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
q->region, sizeof(*e));
- if(!e)
+ if(!e)
return NULL;
e->qstate = q;
e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec,
@@ -1990,7 +2401,7 @@ worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec,
return e;
}
-void
+void
worker_alloc_cleanup(void* arg)
{
struct worker* worker = (struct worker*)arg;
@@ -2004,6 +2415,7 @@ void worker_stats_clear(struct worker* worker)
mesh_stats_clear(worker->env.mesh);
worker->back->unwanted_replies = 0;
worker->back->num_tcp_outgoing = 0;
+ worker->back->num_udp_outgoing = 0;
}
void worker_start_accept(void* arg)
@@ -2037,7 +2449,7 @@ struct outbound_entry* libworker_send_query(
return 0;
}
-int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
+int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(reply_info))
{