diff options
Diffstat (limited to 'contrib/unbound/daemon/worker.c')
-rw-r--r-- | contrib/unbound/daemon/worker.c | 868 |
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)) { |