summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2023-09-18 19:59:52 +0000
committerCy Schubert <cy@FreeBSD.org>2023-09-18 19:59:52 +0000
commit401770e05c71ecb5ae61a59d316069b4b78bf622 (patch)
treea66e3d57ff5dde81aaa0fdc4c2d86c8b7a525ae0 /services
parent7699e1386a16236002b26107ffd2dcbde375e197 (diff)
unbound: Vendor import 1.18.0vendor/unbound/1.18.0
Diffstat (limited to 'services')
-rw-r--r--services/authzone.c10
-rw-r--r--services/cache/dns.c50
-rw-r--r--services/cache/infra.c75
-rw-r--r--services/cache/infra.h5
-rw-r--r--services/listen_dnsport.c153
-rw-r--r--services/localzone.c20
-rw-r--r--services/localzone.h2
-rw-r--r--services/mesh.c385
-rw-r--r--services/mesh.h14
-rw-r--r--services/modstack.c10
-rw-r--r--services/outside_network.c28
-rw-r--r--services/rpz.c230
-rw-r--r--services/rpz.h16
13 files changed, 624 insertions, 374 deletions
diff --git a/services/authzone.c b/services/authzone.c
index 3898767c7e05..cd3ef8dbbf3d 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -1306,8 +1306,8 @@ az_remove_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len,
auth_data_delete(node);
}
if(z->rpz) {
- rpz_remove_rr(z->rpz, z->namelen, dname, dname_len, rr_type,
- rr_class, rdata, rdatalen);
+ rpz_remove_rr(z->rpz, z->name, z->namelen, dname, dname_len,
+ rr_type, rr_class, rdata, rdatalen);
}
return 1;
}
@@ -5420,6 +5420,8 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
edns.padding_block_size = 0;
+ edns.cookie_present = 0;
+ edns.cookie_valid = 0;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;
@@ -6613,6 +6615,8 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
edns.padding_block_size = 0;
+ edns.cookie_present = 0;
+ edns.cookie_valid = 0;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;
@@ -7510,7 +7514,7 @@ static void add_rrlist_rrsigs_into_data(struct packed_rrset_data* data,
size_t j;
if(!rrlist[i])
continue;
- if(rrlist[i] && rrlist[i]->type == LDNS_RR_TYPE_ZONEMD &&
+ if(rrlist[i]->type == LDNS_RR_TYPE_ZONEMD &&
query_dname_compare(z->name, node->name)==0) {
/* omit RRSIGs over type ZONEMD at apex */
continue;
diff --git a/services/cache/dns.c b/services/cache/dns.c
index 6fc9919ef4c0..9b4ad5888721 100644
--- a/services/cache/dns.c
+++ b/services/cache/dns.c
@@ -132,31 +132,6 @@ msg_cache_remove(struct module_env* env, uint8_t* qname, size_t qnamelen,
slabhash_remove(env->msg_cache, h, &k);
}
-/** remove servfail msg cache entry */
-static void
-msg_del_servfail(struct module_env* env, struct query_info* qinfo,
- uint32_t flags)
-{
- struct msgreply_entry* e;
- /* see if the entry is servfail, and then remove it, so that
- * lookups move from the cacheresponse stage to the recursionresponse
- * stage */
- e = msg_cache_lookup(env, qinfo->qname, qinfo->qname_len,
- qinfo->qtype, qinfo->qclass, flags, 0, 0);
- if(!e) return;
- /* we don't check for the ttl here, also expired servfail entries
- * are removed. If the user uses serve-expired, they would still be
- * used to answer from cache */
- if(FLAGS_GET_RCODE(((struct reply_info*)e->entry.data)->flags)
- != LDNS_RCODE_SERVFAIL) {
- lock_rw_unlock(&e->entry.lock);
- return;
- }
- lock_rw_unlock(&e->entry.lock);
- msg_cache_remove(env, qinfo->qname, qinfo->qname_len, qinfo->qtype,
- qinfo->qclass, flags);
-}
-
void
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
@@ -182,13 +157,20 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
/* we do not store the message, but we did store the RRs,
* which could be useful for delegation information */
verbose(VERB_ALGO, "TTL 0: dropped msg from cache");
- free(rep);
- /* if the message is SERVFAIL in cache, remove that SERVFAIL,
+ reply_info_delete(rep, NULL);
+ /* if the message is in the cache, remove that msg,
* so that the TTL 0 response can be returned for future
- * responses (i.e. don't get answered by the servfail from
+ * responses (i.e. don't get answered from
* cache, but instead go to recursion to get this TTL0
- * response). */
- msg_del_servfail(env, qinfo, flags);
+ * response).
+ * Possible messages that could be in the cache:
+ * - SERVFAIL
+ * - NXDOMAIN
+ * - NODATA
+ * - an older record that is expired
+ * - an older record that did not yet expire */
+ msg_cache_remove(env, qinfo->qname, qinfo->qname_len,
+ qinfo->qtype, qinfo->qclass, flags);
return;
}
@@ -610,6 +592,7 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num)
if(!msg->rep)
return NULL;
msg->rep->reason_bogus = LDNS_EDE_NONE;
+ msg->rep->reason_bogus_str = NULL;
if(num > RR_COUNT_MAX)
return NULL; /* integer overflow protection */
msg->rep->rrsets = (struct ub_packed_rrset_key**)
@@ -672,6 +655,10 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
msg->rep->rrset_count = r->rrset_count;
msg->rep->authoritative = r->authoritative;
msg->rep->reason_bogus = r->reason_bogus;
+ if(r->reason_bogus_str) {
+ msg->rep->reason_bogus_str = regional_strdup(region, r->reason_bogus_str);
+ }
+
if(!rrset_array_lock(r->ref, r->rrset_count, now_control)) {
return NULL;
}
@@ -1075,7 +1062,6 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
/* ttl must be relative ;i.e. 0..86400 not time(0)+86400.
* the env->now is added to message and RRsets in this routine. */
/* the leeway is used to invalidate other rrsets earlier */
-
if(is_referral) {
/* store rrsets */
struct rrset_ref ref;
@@ -1092,7 +1078,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS
&& !pside) ? qstarttime:*env->now + leeway));
}
- free(rep);
+ reply_info_delete(rep, NULL);
return 1;
} else {
/* store msg, and rrsets */
diff --git a/services/cache/infra.c b/services/cache/infra.c
index 537cb949cf88..31462d13ae0a 100644
--- a/services/cache/infra.c
+++ b/services/cache/infra.c
@@ -67,6 +67,11 @@ int infra_dp_ratelimit = 0;
* in queries per second. */
int infra_ip_ratelimit = 0;
+/** ratelimit value for client ip addresses,
+ * in queries per second.
+ * For clients with a valid DNS Cookie. */
+int infra_ip_ratelimit_cookie = 0;
+
size_t
infra_sizefunc(void* k, void* ATTR_UNUSED(d))
{
@@ -1051,9 +1056,50 @@ infra_get_mem(struct infra_cache* infra)
return s;
}
+/* Returns 1 if the limit has not been exceeded, 0 otherwise. */
+static int
+check_ip_ratelimit(struct sockaddr_storage* addr, socklen_t addrlen,
+ struct sldns_buffer* buffer, int premax, int max, int has_cookie)
+{
+ int limit;
+
+ if(has_cookie) limit = infra_ip_ratelimit_cookie;
+ else limit = infra_ip_ratelimit;
+
+ /* Disabled */
+ if(limit == 0) return 1;
+
+ if(premax <= limit && max > limit) {
+ char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12];
+ addr_to_str(addr, addrlen, client_ip, sizeof(client_ip));
+ qnm[0]=0;
+ if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE &&
+ LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) {
+ (void)sldns_wire2str_rrquestion_buf(
+ sldns_buffer_at(buffer, LDNS_HEADER_SIZE),
+ sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE,
+ qnm, sizeof(qnm));
+ if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n')
+ qnm[strlen(qnm)-1] = 0; /*remove newline*/
+ if(strchr(qnm, '\t'))
+ *strchr(qnm, '\t') = ' ';
+ if(strchr(qnm, '\t'))
+ *strchr(qnm, '\t') = ' ';
+ verbose(VERB_OPS, "ip_ratelimit exceeded %s %d%s %s",
+ client_ip, limit,
+ has_cookie?"(cookie)":"", qnm);
+ } else {
+ verbose(VERB_OPS, "ip_ratelimit exceeded %s %d%s (no query name)",
+ client_ip, limit,
+ has_cookie?"(cookie)":"");
+ }
+ }
+ return (max <= limit);
+}
+
int infra_ip_ratelimit_inc(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow,
- int backoff, struct sldns_buffer* buffer)
+ int has_cookie, int backoff, struct sldns_buffer* buffer)
{
int max;
struct lruhash_entry* entry;
@@ -1070,31 +1116,8 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra,
(*cur)++;
max = infra_rate_max(entry->data, timenow, backoff);
lock_rw_unlock(&entry->lock);
-
- if(premax <= infra_ip_ratelimit && max > infra_ip_ratelimit) {
- char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12];
- addr_to_str(addr, addrlen, client_ip, sizeof(client_ip));
- qnm[0]=0;
- if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE &&
- LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) {
- (void)sldns_wire2str_rrquestion_buf(
- sldns_buffer_at(buffer, LDNS_HEADER_SIZE),
- sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE,
- qnm, sizeof(qnm));
- if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n')
- qnm[strlen(qnm)-1] = 0; /*remove newline*/
- if(strchr(qnm, '\t'))
- *strchr(qnm, '\t') = ' ';
- if(strchr(qnm, '\t'))
- *strchr(qnm, '\t') = ' ';
- verbose(VERB_OPS, "ip_ratelimit exceeded %s %d %s",
- client_ip, infra_ip_ratelimit, qnm);
- } else {
- verbose(VERB_OPS, "ip_ratelimit exceeded %s %d (no query name)",
- client_ip, infra_ip_ratelimit);
- }
- }
- return (max <= infra_ip_ratelimit);
+ return check_ip_ratelimit(addr, addrlen, buffer, premax, max,
+ has_cookie);
}
/* create */
diff --git a/services/cache/infra.h b/services/cache/infra.h
index faf7fd2f30e1..525073bf35bb 100644
--- a/services/cache/infra.h
+++ b/services/cache/infra.h
@@ -153,6 +153,8 @@ struct rate_key {
/** ip ratelimit, 0 is off */
extern int infra_ip_ratelimit;
+/** ip ratelimit for DNS Cookie clients, 0 is off */
+extern int infra_ip_ratelimit_cookie;
/**
* key for ip_ratelimit lookups, a source IP.
@@ -419,13 +421,14 @@ int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name,
* @param addr: client address
* @param addrlen: client address length
* @param timenow: what time it is now.
+ * @param has_cookie: if the request came with a DNS Cookie.
* @param backoff: if backoff is enabled.
* @param buffer: with query for logging.
* @return 1 if it could be incremented. 0 if the increment overshot the
* ratelimit and the query should be dropped. */
int infra_ip_ratelimit_inc(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow,
- int backoff, struct sldns_buffer* buffer);
+ int has_cookie, int backoff, struct sldns_buffer* buffer);
/**
* Get memory used by the infra cache.
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index 95606aff5d4e..60f9b41e5f6c 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.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
@@ -79,9 +79,11 @@
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
-
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+#include <linux/net_tstamp.h>
+#endif
/** number of queued TCP connections for listen() */
-#define TCP_BACKLOG 256
+#define TCP_BACKLOG 256
#ifndef THREADS_DISABLED
/** lock on the counter of stream buffer memory */
@@ -187,7 +189,7 @@ systemd_get_activated(int family, int socktype, int listen,
log_err("systemd sd_listen_fds(): %s", strerror(-r));
return -1;
}
-
+
for(i = 0; i < r; i++) {
if(sd_is_socket(SD_LISTEN_FDS_START + i, family, socktype, listen)) {
s = SD_LISTEN_FDS_START + i;
@@ -253,7 +255,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
return -1;
}
#else
- if(WSAGetLastError() == WSAEAFNOSUPPORT ||
+ if(WSAGetLastError() == WSAEAFNOSUPPORT ||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
*noproto = 1;
return -1;
@@ -270,7 +272,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
#endif
if(listen) {
#ifdef SO_REUSEADDR
- if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
sock_strerror(errno));
@@ -368,9 +370,9 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t slen = (socklen_t)sizeof(got);
# ifdef SO_RCVBUFFORCE
/* Linux specific: try to use root permission to override
- * system limits on rcvbuf. The limit is stored in
+ * system limits on rcvbuf. The limit is stored in
* /proc/sys/net/core/rmem_max or sysctl net.core.rmem_max */
- if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
+ if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
if(errno != EPERM) {
log_err("setsockopt(..., SO_RCVBUFFORCE, "
@@ -381,7 +383,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
return -1;
}
# endif /* SO_RCVBUFFORCE */
- if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
+ if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
log_err("setsockopt(..., SO_RCVBUF, "
"...) failed: %s", sock_strerror(errno));
@@ -392,7 +394,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
}
/* check if we got the right thing or if system
* reduced to some system max. Warn if so */
- if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got,
+ if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got,
&slen) >= 0 && got < rcv/2) {
log_warn("so-rcvbuf %u was not granted. "
"Got %u. To fix: start with "
@@ -413,9 +415,9 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t slen = (socklen_t)sizeof(got);
# ifdef SO_SNDBUFFORCE
/* Linux specific: try to use root permission to override
- * system limits on sndbuf. The limit is stored in
+ * system limits on sndbuf. The limit is stored in
* /proc/sys/net/core/wmem_max or sysctl net.core.wmem_max */
- if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd,
+ if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
if(errno != EPERM) {
log_err("setsockopt(..., SO_SNDBUFFORCE, "
@@ -426,7 +428,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
return -1;
}
# endif /* SO_SNDBUFFORCE */
- if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd,
+ if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
log_err("setsockopt(..., SO_SNDBUF, "
"...) failed: %s", sock_strerror(errno));
@@ -437,7 +439,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
}
/* check if we got the right thing or if system
* reduced to some system max. Warn if so */
- if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&got,
+ if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&got,
&slen) >= 0 && got < snd/2) {
log_warn("so-sndbuf %u was not granted. "
"Got %u. To fix: start with "
@@ -469,7 +471,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
# endif
) {
int val=(v6only==2)?0:1;
- if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&val, (socklen_t)sizeof(val)) < 0) {
log_err("setsockopt(..., IPV6_V6ONLY"
", ...) failed: %s", sock_strerror(errno));
@@ -576,7 +578,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
int action;
# if defined(IP_PMTUDISC_OMIT)
action = IP_PMTUDISC_OMIT;
- if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
+ if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
if (errno != EINVAL) {
@@ -609,7 +611,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
/* the IP_DONTFRAG option if defined in the 11.0 OSX headers,
* but does not work on that version, so we exclude it */
int off = 0;
- if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
+ if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
&off, (socklen_t)sizeof(off)) < 0) {
log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s",
strerror(errno));
@@ -647,7 +649,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
if(WSAGetLastError() != WSAEADDRINUSE &&
WSAGetLastError() != WSAEADDRNOTAVAIL &&
!(WSAGetLastError() == WSAEACCES && verbosity < 4 && !listen)) {
- log_err_addr("can't bind socket",
+ log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr, addrlen);
}
@@ -749,7 +751,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
}
#endif
#ifdef SO_REUSEADDR
- if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
sock_strerror(errno));
@@ -793,7 +795,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
&& !got_fd_from_systemd
# endif
) {
- if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
sock_strerror(errno));
@@ -845,7 +847,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
addr->ai_addrlen);
}
#else
- log_err_addr("can't bind socket",
+ log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr->ai_addr,
addr->ai_addrlen);
@@ -873,7 +875,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
/* 5 is recommended on linux */
qlen = 5;
#endif
- if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen,
+ if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen,
sizeof(qlen))) == -1 ) {
#ifdef ENOPROTOOPT
/* squelch ENOPROTOOPT: freebsd server mode with kernel support
@@ -999,7 +1001,7 @@ err:
* Create socket from getaddrinfo results
*/
static int
-make_sock(int stype, const char* ifname, const char* port,
+make_sock(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock)
@@ -1015,10 +1017,10 @@ make_sock(int stype, const char* ifname, const char* port,
return -1;
}
#endif
- log_err("node %s:%s getaddrinfo: %s %s",
+ log_err("node %s:%s getaddrinfo: %s %s",
ifname?ifname:"default", port, gai_strerror(r),
#ifdef EAI_SYSTEM
- r==EAI_SYSTEM?(char*)strerror(errno):""
+ (r==EAI_SYSTEM?(char*)strerror(errno):"")
#else
""
#endif
@@ -1055,7 +1057,7 @@ make_sock(int stype, const char* ifname, const char* port,
/** make socket and first see if ifname contains port override info */
static int
-make_sock_port(int stype, const char* ifname, const char* port,
+make_sock_port(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock)
@@ -1114,9 +1116,28 @@ port_insert(struct listen_port** list, int s, enum listen_type ftype,
return 1;
}
+/** set fd to receive software timestamps */
+static int
+set_recvtimestamp(int s)
+{
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+ int opt = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
+ if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMPNS, (void*)&opt, (socklen_t)sizeof(opt)) < 0) {
+ log_err("setsockopt(..., SO_TIMESTAMPNS, ...) failed: %s",
+ strerror(errno));
+ return 0;
+ }
+ return 1;
+#else
+ log_err("packets timestamping is not supported on this platform");
+ (void)s;
+ return 0;
+#endif
+}
+
/** set fd to receive source address packet info */
static int
-set_recvpktinfo(int s, int family)
+set_recvpktinfo(int s, int family)
{
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)) || defined(IP_PKTINFO)
int on = 1;
@@ -1214,6 +1235,9 @@ if_is_ssl(const char* ifname, const char* port, int ssl_port,
* @param use_systemd: if true, fetch sockets from systemd.
* @param dnscrypt_port: dnscrypt service port number
* @param dscp: DSCP to use.
+ * @param sock_queue_timeout: the sock_queue_timeout from config. Seconds to
+ * wait to discard if UDP packets have waited for long in the socket
+ * buffer.
* @return: returns false on error.
*/
static int
@@ -1223,7 +1247,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct config_strlist* tls_additional_port, int https_port,
struct config_strlist* proxy_protocol_port,
int* reuseport, int transparent, int tcp_mss, int freebind,
- int http2_nodelay, int use_systemd, int dnscrypt_port, int dscp)
+ int http2_nodelay, int use_systemd, int dnscrypt_port, int dscp,
+ int sock_queue_timeout)
{
int s, noip6=0;
int is_https = if_is_https(ifname, port, https_port);
@@ -1252,7 +1277,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) {
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
@@ -1263,15 +1289,20 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
/* getting source addr packet info is highly non-portable */
if(!set_recvpktinfo(s, hints->ai_family)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
+ if (sock_queue_timeout && !set_recvtimestamp(s)) {
+ log_warn("socket timestamping is not available");
+ }
if(!port_insert(list, s, is_dnscrypt
?listen_type_udpancil_dnscrypt:listen_type_udpancil,
is_pp2, ub_sock)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
@@ -1283,7 +1314,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) {
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
@@ -1291,11 +1323,15 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
}
return 0;
}
+ if (sock_queue_timeout && !set_recvtimestamp(s)) {
+ log_warn("socket timestamping is not available");
+ }
if(!port_insert(list, s, is_dnscrypt
?listen_type_udp_dnscrypt:listen_type_udp,
is_pp2, ub_sock)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
@@ -1318,7 +1354,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1,
&noip6, 0, 0, reuseport, transparent, tcp_mss, nodelay,
freebind, use_systemd, dscp, ub_sock)) == -1) {
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
/*log_warn("IPv6 protocol not available");*/
@@ -1330,7 +1367,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
verbose(VERB_ALGO, "setup TCP for SSL service");
if(!port_insert(list, s, port_type, is_pp2, ub_sock)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
@@ -1338,7 +1376,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
return 1;
}
-/**
+/**
* Add items to commpoint list in front.
* @param c: commpoint to add.
* @param front: listen struct.
@@ -1389,7 +1427,7 @@ void listen_desetup_locks(void)
}
}
-struct listen_dnsport*
+struct listen_dnsport*
listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
@@ -1525,10 +1563,10 @@ listen_list_delete(struct listen_list* list)
}
}
-void
+void
listen_delete(struct listen_dnsport* front)
{
- if(!front)
+ if(!front)
return;
listen_list_delete(front->cps);
#ifdef USE_DNSCRYPT
@@ -1802,7 +1840,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
@@ -1819,7 +1857,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
@@ -1838,7 +1876,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
@@ -1854,7 +1892,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
@@ -1872,7 +1910,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
@@ -1888,7 +1926,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
@@ -1908,7 +1946,8 @@ void listening_ports_free(struct listen_port* list)
}
/* rc_ports don't have ub_socket */
if(list->socket) {
- freeaddrinfo(list->socket->addr);
+ if(list->socket->addr)
+ freeaddrinfo(list->socket->addr);
free(list->socket);
}
free(list);
@@ -1919,8 +1958,8 @@ void listening_ports_free(struct listen_port* list)
size_t listen_get_mem(struct listen_dnsport* listen)
{
struct listen_list* p;
- size_t s = sizeof(*listen) + sizeof(*listen->base) +
- sizeof(*listen->udp_buff) +
+ size_t s = sizeof(*listen) + sizeof(*listen->base) +
+ sizeof(*listen->udp_buff) +
sldns_buffer_capacity(listen->udp_buff);
#ifdef USE_DNSCRYPT
s += sizeof(*listen->dnscrypt_udp_buff);
@@ -2001,7 +2040,7 @@ void tcp_req_info_clear(struct tcp_req_info* req)
}
req->open_req_list = NULL;
req->num_open_req = 0;
-
+
/* free pending writable result packets */
item = req->done_req_list;
while(item) {
@@ -2060,7 +2099,7 @@ tcp_req_info_setup_listen(struct tcp_req_info* req)
wr = 1;
if(!req->read_is_closed)
rd = 1;
-
+
if(wr) {
req->cp->tcp_is_reading = 0;
comm_point_stop_listening(req->cp);
@@ -2196,7 +2235,7 @@ tcp_req_info_handle_readdone(struct tcp_req_info* req)
}
req->in_worker_handle = 0;
/* it should be waiting in the mesh for recursion.
- * If mesh failed to add a new entry and called commpoint_drop_reply.
+ * If mesh failed to add a new entry and called commpoint_drop_reply.
* Then the mesh state has been cleared. */
if(req->is_drop) {
/* the reply has been dropped, stream has been closed. */
@@ -2256,7 +2295,7 @@ tcp_req_info_add_result(struct tcp_req_info* req, uint8_t* buf, size_t len)
last = req->done_req_list;
while(last && last->next)
last = last->next;
-
+
/* create new element */
item = (struct tcp_req_done_item*)malloc(sizeof(*item));
if(!item) {
@@ -2615,7 +2654,7 @@ static int http2_query_read_done(struct http2_session* h2_session,
"buffer already assigned to stream");
return -1;
}
-
+
/* the c->buffer might be used by mesh_send_reply and no be cleard
* need to be cleared before use */
sldns_buffer_clear(h2_session->c->buffer);
diff --git a/services/localzone.c b/services/localzone.c
index 3536b7aaa91b..44da22d785d9 100644
--- a/services/localzone.c
+++ b/services/localzone.c
@@ -1308,6 +1308,7 @@ local_encode(struct query_info* qinfo, struct module_env* env,
else rep.ns_numrrsets = 1;
rep.rrset_count = 1;
rep.rrsets = &rrset;
+ rep.reason_bogus = LDNS_EDE_NONE;
udpsize = edns->udp_size;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
@@ -1603,7 +1604,7 @@ local_zone_does_not_cover(struct local_zone* z, struct query_info* qinfo,
struct local_data key;
struct local_data* ld = NULL;
struct local_rrset* lr = NULL;
- if(z->type == local_zone_always_transparent)
+ if(z->type == local_zone_always_transparent || z->type == local_zone_block_a)
return 1;
if(z->type != local_zone_transparent
&& z->type != local_zone_typetransparent
@@ -1680,6 +1681,16 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env,
|| lz_type == local_zone_always_transparent) {
/* no NODATA or NXDOMAINS for this zone type */
return 0;
+ } else if(lz_type == local_zone_block_a) {
+ /* Return NODATA for all A queries */
+ if(qinfo->qtype == LDNS_RR_TYPE_A) {
+ local_error_encode(qinfo, env, edns, repinfo, buf, temp,
+ LDNS_RCODE_NOERROR, (LDNS_RCODE_NOERROR|BIT_AA),
+ LDNS_EDE_NONE, NULL);
+ return 1;
+ }
+
+ return 0;
} else if(lz_type == local_zone_always_null) {
/* 0.0.0.0 or ::0 or noerror/nodata for this zone type,
* used for blocklists. */
@@ -1846,7 +1857,8 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
if(z && (lzt == local_zone_transparent ||
lzt == local_zone_typetransparent ||
lzt == local_zone_inform ||
- lzt == local_zone_always_transparent) &&
+ lzt == local_zone_always_transparent ||
+ lzt == local_zone_block_a) &&
local_zone_does_not_cover(z, qinfo, labs)) {
lock_rw_unlock(&z->lock);
z = NULL;
@@ -1894,6 +1906,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
if(lzt != local_zone_always_refuse
&& lzt != local_zone_always_transparent
+ && lzt != local_zone_block_a
&& lzt != local_zone_always_nxdomain
&& lzt != local_zone_always_nodata
&& lzt != local_zone_always_deny
@@ -1924,6 +1937,7 @@ const char* local_zone_type2str(enum localzone_type t)
case local_zone_inform_deny: return "inform_deny";
case local_zone_inform_redirect: return "inform_redirect";
case local_zone_always_transparent: return "always_transparent";
+ case local_zone_block_a: return "block_a";
case local_zone_always_refuse: return "always_refuse";
case local_zone_always_nxdomain: return "always_nxdomain";
case local_zone_always_nodata: return "always_nodata";
@@ -1958,6 +1972,8 @@ int local_zone_str2type(const char* type, enum localzone_type* t)
*t = local_zone_inform_redirect;
else if(strcmp(type, "always_transparent") == 0)
*t = local_zone_always_transparent;
+ else if(strcmp(type, "block_a") == 0)
+ *t = local_zone_block_a;
else if(strcmp(type, "always_refuse") == 0)
*t = local_zone_always_refuse;
else if(strcmp(type, "always_nxdomain") == 0)
diff --git a/services/localzone.h b/services/localzone.h
index 19534f7509ed..4456893ee112 100644
--- a/services/localzone.h
+++ b/services/localzone.h
@@ -88,6 +88,8 @@ enum localzone_type {
local_zone_inform_redirect,
/** resolve normally, even when there is local data */
local_zone_always_transparent,
+ /** resolve normally, even when there is local data but return NODATA for A queries */
+ local_zone_block_a,
/** answer with error, even when there is local data */
local_zone_always_refuse,
/** answer with nxdomain, even when there is local data */
diff --git a/services/mesh.c b/services/mesh.c
index 9007b6e08c32..52d14a2d1f54 100644
--- a/services/mesh.c
+++ b/services/mesh.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
@@ -63,82 +63,13 @@
#include "util/data/dname.h"
#include "respip/respip.h"
#include "services/listen_dnsport.h"
+#include "util/timeval_func.h"
#ifdef CLIENT_SUBNET
#include "edns-subnet/subnetmod.h"
#include "edns-subnet/edns-subnet.h"
#endif
-/** subtract timers and the values do not overflow or become negative */
-static void
-timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start)
-{
-#ifndef S_SPLINT_S
- time_t end_usec = end->tv_usec;
- d->tv_sec = end->tv_sec - start->tv_sec;
- if(end_usec < start->tv_usec) {
- end_usec += 1000000;
- d->tv_sec--;
- }
- d->tv_usec = end_usec - start->tv_usec;
-#endif
-}
-
-/** add timers and the values do not overflow or become negative */
-static void
-timeval_add(struct timeval* d, const struct timeval* add)
-{
-#ifndef S_SPLINT_S
- d->tv_sec += add->tv_sec;
- d->tv_usec += add->tv_usec;
- if(d->tv_usec >= 1000000 ) {
- d->tv_usec -= 1000000;
- d->tv_sec++;
- }
-#endif
-}
-
-/** divide sum of timers to get average */
-static void
-timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d)
-{
-#ifndef S_SPLINT_S
- size_t leftover;
- if(d <= 0) {
- avg->tv_sec = 0;
- avg->tv_usec = 0;
- return;
- }
- avg->tv_sec = sum->tv_sec / d;
- avg->tv_usec = sum->tv_usec / d;
- /* handle fraction from seconds divide */
- leftover = sum->tv_sec - avg->tv_sec*d;
- if(leftover <= 0)
- leftover = 0;
- avg->tv_usec += (((long long)leftover)*((long long)1000000))/d;
- if(avg->tv_sec < 0)
- avg->tv_sec = 0;
- if(avg->tv_usec < 0)
- avg->tv_usec = 0;
-#endif
-}
-
-/** histogram compare of time values */
-static int
-timeval_smaller(const struct timeval* x, const struct timeval* y)
-{
-#ifndef S_SPLINT_S
- if(x->tv_sec < y->tv_sec)
- return 1;
- else if(x->tv_sec == y->tv_sec) {
- if(x->tv_usec <= y->tv_usec)
- return 1;
- else return 0;
- }
- else return 0;
-#endif
-}
-
/**
* Compare two response-ip client info entries for the purpose of mesh state
* compare. It returns 0 if ci_a and ci_b are considered equal; otherwise
@@ -249,7 +180,7 @@ mesh_state_ref_compare(const void* ap, const void* bp)
return mesh_state_compare(a->s, b->s);
}
-struct mesh_area*
+struct mesh_area*
mesh_create(struct module_stack* stack, struct module_env* env)
{
struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
@@ -275,6 +206,7 @@ mesh_create(struct module_stack* stack, struct module_env* env)
mesh->stats_jostled = 0;
mesh->stats_dropped = 0;
mesh->ans_expired = 0;
+ mesh->ans_cachedb = 0;
mesh->max_reply_states = env->cfg->num_queries_per_thread;
mesh->max_forever_states = (mesh->max_reply_states+1)/2;
#ifndef S_SPLINT_S
@@ -298,7 +230,7 @@ mesh_delete_helper(rbnode_type* n)
* traversal and rbtree rebalancing do not work together */
}
-void
+void
mesh_delete(struct mesh_area* mesh)
{
if(!mesh)
@@ -341,7 +273,7 @@ int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf)
if(m && m->reply_list && m->list_select == mesh_jostle_list) {
/* how old is it? */
struct timeval age;
- timeval_subtract(&age, mesh->env->now_tv,
+ timeval_subtract(&age, mesh->env->now_tv,
&m->reply_list->start_time);
if(timeval_smaller(&mesh->jostle_max, &age)) {
/* its a goner */
@@ -517,6 +449,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
comm_point_send_reply(rep);
return;
}
+ /* set detached (it is now) */
+ mesh->num_detached_states++;
if(unique)
mesh_state_make_unique(s);
s->s.rpz_passthru = rpz_passthru;
@@ -525,13 +459,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
s->s.region);
if(!s->s.edns_opts_front_in) {
- log_err("mesh_state_create: out of memory; SERVFAIL");
+ log_err("edns_opt_copy_region: out of memory; SERVFAIL");
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL,
NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv))
edns->opt_list_inplace_cb_out = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
+ mesh_state_delete(&s->s);
return;
}
}
@@ -543,8 +478,6 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
- /* set detached (it is now) */
- mesh->num_detached_states++;
added = 1;
}
if(!s->reply_list && !s->cb_list) {
@@ -585,11 +518,11 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
/* move to either the forever or the jostle_list */
if(mesh->num_forever_states < mesh->max_forever_states) {
mesh->num_forever_states ++;
- mesh_list_insert(s, &mesh->forever_first,
+ mesh_list_insert(s, &mesh->forever_first,
&mesh->forever_last);
s->list_select = mesh_forever_list;
} else {
- mesh_list_insert(s, &mesh->jostle_first,
+ mesh_list_insert(s, &mesh->jostle_first,
&mesh->jostle_last);
s->list_select = mesh_jostle_list;
}
@@ -610,9 +543,9 @@ servfail_mem:
return;
}
-int
+int
mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
- uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
+ uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru)
{
struct mesh_state* s = NULL;
@@ -637,6 +570,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
if(!s) {
return 0;
}
+ /* set detached (it is now) */
+ mesh->num_detached_states++;
if(unique)
mesh_state_make_unique(s);
s->s.rpz_passthru = rpz_passthru;
@@ -644,6 +579,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
s->s.region);
if(!s->s.edns_opts_front_in) {
+ mesh_state_delete(&s->s);
return 0;
}
}
@@ -654,8 +590,6 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
- /* set detached (it is now) */
- mesh->num_detached_states++;
added = 1;
}
if(!s->reply_list && !s->cb_list) {
@@ -672,6 +606,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
}
/* add serve expired timer if not already there */
if(timeout && !mesh_serve_expired_init(s, timeout)) {
+ if(added)
+ mesh_state_delete(&s->s);
return 0;
}
/* update statistics */
@@ -773,7 +709,7 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh,
* attached its own ECS data. */
static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
struct query_info* qinfo, uint16_t qflags, time_t leeway, int run,
- int rpz_passthru, struct comm_reply* rep, struct edns_option* edns_list)
+ int rpz_passthru, struct sockaddr_storage* addr, struct edns_option* edns_list)
{
struct mesh_state* s = NULL;
struct edns_option* opt = NULL;
@@ -803,20 +739,10 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
return;
}
} else {
- /* Fake the ECS data from the client's IP */
- struct ecs_data ecs;
- memset(&ecs, 0, sizeof(ecs));
- subnet_option_from_ss(&rep->client_addr, &ecs, mesh->env->cfg);
- if(ecs.subnet_validdata == 0) {
- log_err("prefetch_subnet subnet_option_from_ss: invalid data");
- return;
- }
- subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in,
- &s->s, s->s.region);
- if(!s->s.edns_opts_front_in) {
- log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory");
- return;
- }
+ /* Store the client's address. Later in the subnet module,
+ * it is decided whether to include an ECS option or not.
+ */
+ s->s.client_addr = *addr;
}
#ifdef UNBOUND_DEBUG
n =
@@ -863,14 +789,14 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway, int rpz_passthru,
- struct comm_reply* rep, struct edns_option* opt_list)
+ struct sockaddr_storage* addr, struct edns_option* opt_list)
{
+ (void)addr;
(void)opt_list;
- (void)rep;
#ifdef CLIENT_SUBNET
- if(rep)
+ if(addr)
mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1,
- rpz_passthru, rep, opt_list);
+ rpz_passthru, addr, opt_list);
else
#endif
mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1,
@@ -900,7 +826,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
int i;
if(!region)
return NULL;
- mstate = (struct mesh_state*)regional_alloc(region,
+ mstate = (struct mesh_state*)regional_alloc(region,
sizeof(struct mesh_state));
if(!mstate) {
alloc_reg_release(env->alloc, region);
@@ -970,19 +896,13 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
return mstate;
}
-int
-mesh_state_is_unique(struct mesh_state* mstate)
-{
- return mstate->unique != NULL;
-}
-
void
mesh_state_make_unique(struct mesh_state* mstate)
{
mstate->unique = mstate;
}
-void
+void
mesh_state_cleanup(struct mesh_state* mstate)
{
struct mesh_area* mesh;
@@ -1028,7 +948,7 @@ mesh_state_cleanup(struct mesh_state* mstate)
alloc_reg_release(mstate->s.env->alloc, mstate->s.region);
}
-void
+void
mesh_state_delete(struct module_qstate* qstate)
{
struct mesh_area* mesh;
@@ -1041,10 +961,10 @@ mesh_state_delete(struct module_qstate* qstate)
mesh_detach_subs(&mstate->s);
if(mstate->list_select == mesh_forever_list) {
mesh->num_forever_states --;
- mesh_list_remove(mstate, &mesh->forever_first,
+ mesh_list_remove(mstate, &mesh->forever_first,
&mesh->forever_last);
} else if(mstate->list_select == mesh_jostle_list) {
- mesh_list_remove(mstate, &mesh->jostle_first,
+ mesh_list_remove(mstate, &mesh->jostle_first,
&mesh->jostle_last);
}
if(!mstate->reply_list && !mstate->cb_list
@@ -1116,7 +1036,7 @@ void mesh_detach_subs(struct module_qstate* qstate)
if(!ref->s->reply_list && !ref->s->cb_list
&& ref->s->super_set.count == 0) {
mesh->num_detached_states++;
- log_assert(mesh->num_detached_states +
+ log_assert(mesh->num_detached_states +
mesh->num_reply_states <= mesh->all.count);
}
}
@@ -1181,7 +1101,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
if(!mesh_state_attachment(qstate->mesh_info, sub))
return 0;
/* if it was a duplicate attachment, the count was not zero before */
- if(!sub->reply_list && !sub->cb_list && was_detached &&
+ if(!sub->reply_list && !sub->cb_list && was_detached &&
sub->super_set.count == 1) {
/* it used to be detached, before this one got added */
log_assert(mesh->num_detached_states > 0);
@@ -1251,7 +1171,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
else secure = 0;
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
- if(!rcode && (rep->security == sec_status_bogus ||
+ if(!rcode && rep && (rep->security == sec_status_bogus ||
rep->security == sec_status_secure_sentinel_fail)) {
if(!(reason = errinf_to_str_bogus(&m->s)))
rcode = LDNS_RCODE_SERVFAIL;
@@ -1280,10 +1200,10 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
LDNS_RCODE_NOERROR, &r->edns, NULL, m->s.region, start_time) ||
- !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
- r->qflags, r->buf, 0, 1,
- m->s.env->scratch, udp_size, &r->edns,
- (int)(r->edns.bits & EDNS_DO), secure))
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+ r->qflags, r->buf, 0, 1,
+ m->s.env->scratch, udp_size, &r->edns,
+ (int)(r->edns.bits & EDNS_DO), secure))
{
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
@@ -1291,7 +1211,8 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
} else {
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
- rep->security, reason, was_ratelimited);
+ (rep?rep->security:sec_status_unchecked),
+ reason, was_ratelimited);
}
}
free(reason);
@@ -1311,10 +1232,36 @@ mesh_is_rpz_respip_tcponly_action(struct mesh_state const* m)
}
static inline int
-mesh_is_udp(struct mesh_reply const* r) {
+mesh_is_udp(struct mesh_reply const* r)
+{
return r->query_reply.c->type == comm_udp;
}
+static inline void
+mesh_find_and_attach_ede_and_reason(struct mesh_state* m,
+ struct reply_info* rep, struct mesh_reply* r)
+{
+ /* OLD note:
+ * During validation the EDE code can be received via two
+ * code paths. One code path fills the reply_info EDE, and
+ * the other fills it in the errinf_strlist. These paths
+ * intersect at some points, but where is opaque due to
+ * the complexity of the validator. At the time of writing
+ * we make the choice to prefer the EDE from errinf_strlist
+ * but a compelling reason to do otherwise is just as valid
+ * NEW note:
+ * The compelling reason is that with caching support, the value
+ * in the reply_info is cached.
+ * The reason members of the reply_info struct should be
+ * updated as they are already cached. No reason to
+ * try and find the EDE information in errinf anymore.
+ */
+ if(rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&r->edns.opt_list_out,
+ m->s.region, rep->reason_bogus, rep->reason_bogus_str);
+ }
+}
+
/**
* Send reply to mesh reply entry
* @param m: mesh state to send it for.
@@ -1346,7 +1293,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
/* examine security status */
if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
- m->s.env->cfg->ignore_cd) && rep &&
+ m->s.env->cfg->ignore_cd) && rep &&
(rep->security <= sec_status_bogus ||
rep->security == sec_status_secure_sentinel_fail)) {
rcode = LDNS_RCODE_SERVFAIL;
@@ -1401,40 +1348,17 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, rcode, &r->edns, &r->query_reply, m->s.region, &r->start_time))
r->edns.opt_list_inplace_cb_out = NULL;
- } else {
+ } else {
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
&r->edns, &r->query_reply, m->s.region, &r->start_time))
r->edns.opt_list_inplace_cb_out = NULL;
}
- /* Send along EDE BOGUS EDNS0 option when answer is bogus */
- if(m->s.env->cfg->ede && rcode == LDNS_RCODE_SERVFAIL &&
- m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
- m->s.env->cfg->ignore_cd) && rep &&
- (rep->security <= sec_status_bogus ||
- rep->security == sec_status_secure_sentinel_fail)) {
- char *reason = m->s.env->cfg->val_log_level >= 2
- ? errinf_to_str_bogus(&m->s) : NULL;
-
- /* During validation the EDE code can be received via two
- * code paths. One code path fills the reply_info EDE, and
- * the other fills it in the errinf_strlist. These paths
- * intersect at some points, but where is opaque due to
- * the complexity of the validator. At the time of writing
- * we make the choice to prefer the EDE from errinf_strlist
- * but a compelling reason to do otherwise is just as valid
- */
- sldns_ede_code reason_bogus = errinf_to_reason_bogus(&m->s);
- if ((reason_bogus == LDNS_EDE_DNSSEC_BOGUS &&
- rep->reason_bogus != LDNS_EDE_NONE) ||
- reason_bogus == LDNS_EDE_NONE) {
- reason_bogus = rep->reason_bogus;
- }
-
- if(reason_bogus != LDNS_EDE_NONE) {
- edns_opt_list_append_ede(&r->edns.opt_list_out,
- m->s.region, reason_bogus, reason);
- }
- free(reason);
+ /* Send along EDE EDNS0 option when SERVFAILing; usually
+ * DNSSEC validation failures */
+ /* Since we are SERVFAILing here, CD bit and rep->security
+ * is already handled. */
+ if(m->s.env->cfg->ede && rep) {
+ mesh_find_and_attach_ede_and_reason(m, rep, r);
}
error_encode(r_buffer, rcode, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
@@ -1449,12 +1373,22 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
r->edns.bits &= EDNS_DO;
m->s.qinfo.qname = r->qname;
m->s.qinfo.local_alias = r->local_alias;
+
+ /* Attach EDE without SERVFAIL if the validation failed.
+ * Need to explicitly check for rep->security otherwise failed
+ * validation paths may attach to a secure answer. */
+ if(m->s.env->cfg->ede && rep &&
+ (rep->security <= sec_status_bogus ||
+ rep->security == sec_status_secure_sentinel_fail)) {
+ mesh_find_and_attach_ede_and_reason(m, rep, r);
+ }
+
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
LDNS_RCODE_NOERROR, &r->edns, &r->query_reply, m->s.region, &r->start_time) ||
- !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r_buffer, 0, 1, m->s.env->scratch,
udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO),
- secure))
+ secure))
{
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time))
@@ -1503,6 +1437,7 @@ void mesh_query_done(struct mesh_state* mstate)
struct reply_info* rep = (mstate->s.return_msg?
mstate->s.return_msg->rep:NULL);
struct timeval tv = {0, 0};
+ int i = 0;
/* No need for the serve expired timer anymore; we are going to reply. */
if(mstate->s.serve_expired_data) {
comm_timer_delete(mstate->s.serve_expired_data->timer);
@@ -1522,6 +1457,7 @@ void mesh_query_done(struct mesh_state* mstate)
}
}
for(r = mstate->reply_list; r; r = r->next) {
+ i++;
tv = r->start_time;
/* if a response-ip address block has been stored the
@@ -1533,16 +1469,6 @@ void mesh_query_done(struct mesh_state* mstate)
mstate->s.qinfo.qclass, r->local_alias,
&r->query_reply.client_addr,
r->query_reply.client_addrlen);
- if(mstate->s.env->cfg->stat_extended &&
- mstate->s.respip_action_info->rpz_used) {
- if(mstate->s.respip_action_info->rpz_disabled)
- mstate->s.env->mesh->rpz_action[RPZ_DISABLED_ACTION]++;
- if(mstate->s.respip_action_info->rpz_cname_override)
- mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
- else
- mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action(
- mstate->s.respip_action_info->action)]++;
- }
}
/* if this query is determined to be dropped during the
@@ -1573,6 +1499,27 @@ void mesh_query_done(struct mesh_state* mstate)
prev_buffer = r_buffer;
}
}
+ /* Account for each reply sent. */
+ if(i > 0 && mstate->s.respip_action_info &&
+ mstate->s.respip_action_info->addrinfo &&
+ mstate->s.env->cfg->stat_extended &&
+ mstate->s.respip_action_info->rpz_used) {
+ if(mstate->s.respip_action_info->rpz_disabled)
+ mstate->s.env->mesh->rpz_action[RPZ_DISABLED_ACTION] += i;
+ if(mstate->s.respip_action_info->rpz_cname_override)
+ mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION] += i;
+ else
+ mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action(
+ mstate->s.respip_action_info->action)] += i;
+ }
+ if(!mstate->s.is_drop && i > 0) {
+ if(mstate->s.env->cfg->stat_extended
+ && mstate->s.is_cachedb_answer) {
+ mstate->s.env->mesh->ans_cachedb += i;
+ }
+ }
+
+ /* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
@@ -1585,6 +1532,7 @@ void mesh_query_done(struct mesh_state* mstate)
mstate->s.env->mesh->num_detached_states++;
}
mstate->replies_sent = 1;
+
while((c = mstate->cb_list) != NULL) {
/* take this cb off the list; so that the list can be
* changed, eg. by adds from the callback routine */
@@ -1611,7 +1559,7 @@ void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
/* callback the function to inform super of result */
fptr_ok(fptr_whitelist_mod_inform_super(
mesh->mods.mod[ref->s->s.curmod]->inform_super));
- (*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s,
+ (*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s,
ref->s->s.curmod, &ref->s->s);
/* copy state that is always relevant to super */
copy_state_to_super(&mstate->s, ref->s->s.curmod, &ref->s->s);
@@ -1635,7 +1583,7 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
* desire aggregation).*/
key.unique = NULL;
key.s.client_info = cinfo;
-
+
result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
return result;
}
@@ -1644,7 +1592,7 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
uint16_t qid, uint16_t qflags)
{
- struct mesh_cb* r = regional_alloc(s->s.region,
+ struct mesh_cb* r = regional_alloc(s->s.region,
sizeof(struct mesh_cb));
if(!r)
return 0;
@@ -1776,7 +1724,7 @@ mesh_copy_qinfo(struct mesh_state* mstate, struct query_info** qinfop,
* Handles module finished.
* @param mesh: the mesh area.
* @param mstate: currently active mesh state.
- * Deleted if finished, calls _done and _supers to
+ * Deleted if finished, calls _done and _supers to
* send replies to clients and inform other mesh states.
* This in turn may create additional runnable mesh states.
* @param s: state at which the current module exited.
@@ -1810,7 +1758,7 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
}
if(s == module_restart_next) {
int curmod = mstate->s.curmod;
- for(; mstate->s.curmod < mesh->mods.num;
+ for(; mstate->s.curmod < mesh->mods.num;
mstate->s.curmod++) {
fptr_ok(fptr_whitelist_mod_clear(
mesh->mods.mod[mstate->s.curmod]->clear));
@@ -1842,9 +1790,21 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
if(s == module_finished) {
if(mstate->s.curmod == 0) {
struct query_info* qinfo = NULL;
+ struct edns_option* opt_list = NULL;
+ struct sockaddr_storage addr;
uint16_t qflags;
int rpz_p = 0;
+#ifdef CLIENT_SUBNET
+ struct edns_option* ecs;
+ if(mstate->s.need_refetch && mstate->reply_list &&
+ modstack_find(&mesh->mods, "subnetcache") != -1 &&
+ mstate->s.env->unique_mesh) {
+ addr = mstate->reply_list->query_reply.client_addr;
+ } else
+#endif
+ memset(&addr, 0, sizeof(addr));
+
mesh_query_done(mstate);
mesh_walk_supers(mesh, mstate);
@@ -1854,13 +1814,28 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
* we need to make a copy of the query info here. */
if(mstate->s.need_refetch) {
mesh_copy_qinfo(mstate, &qinfo, &qflags);
+#ifdef CLIENT_SUBNET
+ /* Make also a copy of the ecs option if any */
+ if((ecs = edns_opt_list_find(
+ mstate->s.edns_opts_front_in,
+ mstate->s.env->cfg->client_subnet_opcode)) != NULL) {
+ (void)edns_opt_list_append(&opt_list,
+ ecs->opt_code, ecs->opt_len,
+ ecs->opt_data,
+ mstate->s.env->scratch);
+ }
+#endif
rpz_p = mstate->s.rpz_passthru;
}
- mesh_state_delete(&mstate->s);
if(qinfo) {
- mesh_schedule_prefetch(mesh, qinfo, qflags,
- 0, 1, rpz_p);
+ mesh_state_delete(&mstate->s);
+ mesh_new_prefetch(mesh, qinfo, qflags, 0,
+ rpz_p,
+ addr.ss_family!=AF_UNSPEC?&addr:NULL,
+ opt_list);
+ } else {
+ mesh_state_delete(&mstate->s);
}
return 0;
}
@@ -1888,7 +1863,7 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
mstate->s.reply = NULL;
regional_free_all(mstate->s.env->scratch);
s = mstate->s.ext_state[mstate->s.curmod];
- verbose(VERB_ALGO, "mesh_run: %s module exit state is %s",
+ verbose(VERB_ALGO, "mesh_run: %s module exit state is %s",
mesh->mods.mod[mstate->s.curmod]->name, strextstate(s));
e = NULL;
if(mesh_continue(mesh, mstate, s, &ev))
@@ -1908,14 +1883,14 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
}
}
-void
+void
mesh_log_list(struct mesh_area* mesh)
{
char buf[30];
struct mesh_state* m;
int num = 0;
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
- snprintf(buf, sizeof(buf), "%d%s%s%s%s%s%s mod%d %s%s",
+ snprintf(buf, sizeof(buf), "%d%s%s%s%s%s%s mod%d %s%s",
num++, (m->s.is_priming)?"p":"", /* prime */
(m->s.is_valrec)?"v":"", /* prime */
(m->s.query_flags&BIT_RD)?"RD":"",
@@ -1924,18 +1899,18 @@ mesh_log_list(struct mesh_area* mesh)
(m->sub_set.count!=0)?"c":"", /* children */
m->s.curmod, (m->reply_list)?"rep":"", /*hasreply*/
(m->cb_list)?"cb":"" /* callbacks */
- );
+ );
log_query_info(VERB_ALGO, buf, &m->s.qinfo);
}
}
-void
+void
mesh_stats(struct mesh_area* mesh, const char* str)
{
verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, "
"%u detached), %u waiting replies, %u recursion replies "
- "sent, %d replies dropped, %d states jostled out",
- str, (unsigned)mesh->all.count,
+ "sent, %d replies dropped, %d states jostled out",
+ str, (unsigned)mesh->all.count,
(unsigned)mesh->num_reply_states,
(unsigned)mesh->num_detached_states,
(unsigned)mesh->num_reply_addrs,
@@ -1944,7 +1919,7 @@ mesh_stats(struct mesh_area* mesh, const char* str)
(unsigned)mesh->stats_jostled);
if(mesh->replies_sent > 0) {
struct timeval avg;
- timeval_divide(&avg, &mesh->replies_sum_wait,
+ timeval_divide(&avg, &mesh->replies_sum_wait,
mesh->replies_sent);
log_info("average recursion processing time "
ARG_LL "d.%6.6d sec",
@@ -1954,7 +1929,7 @@ mesh_stats(struct mesh_area* mesh, const char* str)
}
}
-void
+void
mesh_stats_clear(struct mesh_area* mesh)
{
if(!mesh)
@@ -1968,12 +1943,13 @@ mesh_stats_clear(struct mesh_area* mesh)
mesh->ans_secure = 0;
mesh->ans_bogus = 0;
mesh->ans_expired = 0;
+ mesh->ans_cachedb = 0;
memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*UB_STATS_RCODE_NUM);
memset(&mesh->rpz_action[0], 0, sizeof(size_t)*UB_STATS_RPZ_ACTION_NUM);
mesh->ans_nodata = 0;
}
-size_t
+size_t
mesh_get_mem(struct mesh_area* mesh)
{
struct mesh_state* m;
@@ -1987,7 +1963,7 @@ mesh_get_mem(struct mesh_area* mesh)
return s;
}
-int
+int
mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
uint16_t flags, int prime, int valrec)
{
@@ -2104,6 +2080,7 @@ mesh_serve_expired_callback(void* arg)
struct timeval tv = {0, 0};
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
+ int i = 0;
if(!qstate->serve_expired_data) return;
verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data");
comm_timer_delete(qstate->serve_expired_data->timer);
@@ -2175,6 +2152,7 @@ mesh_serve_expired_callback(void* arg)
log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep);
for(r = mstate->reply_list; r; r = r->next) {
+ i++;
tv = r->start_time;
/* If address info is returned, it means the action should be an
@@ -2184,16 +2162,6 @@ mesh_serve_expired_callback(void* arg)
qstate->qinfo.qtype, qstate->qinfo.qclass,
r->local_alias, &r->query_reply.client_addr,
r->query_reply.client_addrlen);
-
- if(qstate->env->cfg->stat_extended && actinfo.rpz_used) {
- if(actinfo.rpz_disabled)
- qstate->env->mesh->rpz_action[RPZ_DISABLED_ACTION]++;
- if(actinfo.rpz_cname_override)
- qstate->env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
- else
- qstate->env->mesh->rpz_action[
- respip_action_to_rpz_action(actinfo.action)]++;
- }
}
/* Add EDE Stale Answer (RCF8914). Ignore global ede as this is
@@ -2213,11 +2181,23 @@ mesh_serve_expired_callback(void* arg)
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
prev = r;
prev_buffer = r_buffer;
-
- /* Account for each reply sent. */
- mesh->ans_expired++;
-
}
+ /* Account for each reply sent. */
+ if(i > 0) {
+ mesh->ans_expired += i;
+ if(actinfo.addrinfo && qstate->env->cfg->stat_extended &&
+ actinfo.rpz_used) {
+ if(actinfo.rpz_disabled)
+ qstate->env->mesh->rpz_action[RPZ_DISABLED_ACTION] += i;
+ if(actinfo.rpz_cname_override)
+ qstate->env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION] += i;
+ else
+ qstate->env->mesh->rpz_action[
+ respip_action_to_rpz_action(actinfo.action)] += i;
+ }
+ }
+
+ /* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
@@ -2228,6 +2208,7 @@ mesh_serve_expired_callback(void* arg)
}
}
}
+
while((c = mstate->cb_list) != NULL) {
/* take this cb off the list; so that the list can be
* changed, eg. by adds from the callback routine */
diff --git a/services/mesh.h b/services/mesh.h
index 25121a67b3a5..d926cfc9dec3 100644
--- a/services/mesh.h
+++ b/services/mesh.h
@@ -114,6 +114,8 @@ struct mesh_area {
size_t stats_dropped;
/** stats, number of expired replies sent */
size_t ans_expired;
+ /** stats, number of cached replies from cachedb */
+ size_t ans_cachedb;
/** number of replies sent */
size_t replies_sent;
/** sum of waiting times for the replies */
@@ -335,13 +337,13 @@ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
* @param leeway: TTL leeway what to expire earlier for this update.
* @param rpz_passthru: if true, the rpz passthru was previously found and
* further rpz processing is stopped.
- * @param rep: comm_reply for the client; to be used when subnet is enabled.
+ * @param addr: sockaddr_storage for the client; to be used with subnet.
* @param opt_list: edns opt_list from the client; to be used when subnet is
* enabled.
*/
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway, int rpz_passthru,
- struct comm_reply* rep, struct edns_option* opt_list);
+ struct sockaddr_storage* addr, struct edns_option* opt_list);
/**
* Handle new event from the wire. A serviced query has returned.
@@ -479,14 +481,6 @@ struct mesh_state* mesh_state_create(struct module_env* env,
uint16_t qflags, int prime, int valrec);
/**
- * Check if the mesh state is unique.
- * A unique mesh state uses it's unique member to point to itself, else NULL.
- * @param mstate: mesh state to check.
- * @return true if the mesh state is unique, false otherwise.
- */
-int mesh_state_is_unique(struct mesh_state* mstate);
-
-/**
* Make a mesh state unique.
* A unique mesh state uses it's unique member to point to itself.
* @param mstate: mesh state to check.
diff --git a/services/modstack.c b/services/modstack.c
index da8e623c16da..a90d7178c410 100644
--- a/services/modstack.c
+++ b/services/modstack.c
@@ -120,12 +120,16 @@ modstack_config(struct module_stack* stack, const char* module_conf)
stack->mod[i] = module_factory(&module_conf);
if(!stack->mod[i]) {
char md[256];
+ char * s = md;
snprintf(md, sizeof(md), "%s", module_conf);
- if(strchr(md, ' ')) *(strchr(md, ' ')) = 0;
- if(strchr(md, '\t')) *(strchr(md, '\t')) = 0;
+ /* Leading spaces are present on errors. */
+ while (*s && isspace((unsigned char)*s))
+ s++;
+ if(strchr(s, ' ')) *(strchr(s, ' ')) = 0;
+ if(strchr(s, '\t')) *(strchr(s, '\t')) = 0;
log_err("Unknown value in module-config, module: '%s'."
" This module is not present (not compiled in),"
- " See the list of linked modules with unbound -V", md);
+ " See the list of linked modules with unbound -V", s);
return 0;
}
}
diff --git a/services/outside_network.c b/services/outside_network.c
index a4529ade52e0..2a219cbc6e92 100644
--- a/services/outside_network.c
+++ b/services/outside_network.c
@@ -551,8 +551,27 @@ reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr,
log_assert(&key_p != ((struct reuse_tcp*)result)->pending);
}
/* not found, return null */
+
+ /* It is possible that we search for something before the first element
+ * in the tree. Replace a null pointer with the first element.
+ */
+ if (!result) {
+ verbose(VERB_CLIENT, "reuse_tcp_find: taking first");
+ result = rbtree_first(&outnet->tcp_reuse);
+ }
+
if(!result || result == RBTREE_NULL)
return NULL;
+
+ /* It is possible that we got the previous address, but that the
+ * address we are looking for is in the tree. If the address we got
+ * is less than the address we are looking, then take the next entry.
+ */
+ if (reuse_cmp_addrportssl(result->key, &key_p.reuse) < 0) {
+ verbose(VERB_CLIENT, "reuse_tcp_find: key too low");
+ result = rbtree_next(result);
+ }
+
verbose(VERB_CLIENT, "reuse_tcp_find check inexact match");
/* inexact match, find one of possibly several connections to the
* same destination address, with the correct port, ssl, and
@@ -620,6 +639,15 @@ outnet_tcp_take_into_use(struct waiting_tcp* w)
log_assert(w->addrlen > 0);
pend->c->tcp_do_toggle_rw = 0;
pend->c->tcp_do_close = 0;
+
+ /* Consistency check, if we have ssl_upstream but no sslctx, then
+ * log an error and return failure.
+ */
+ if (w->ssl_upstream && !w->outnet->sslctx) {
+ log_err("SSL upstream requested but no SSL context");
+ return 0;
+ }
+
/* open socket */
s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss, w->outnet->ip_dscp);
diff --git a/services/rpz.c b/services/rpz.c
index e876f3f94834..6ce83cb66a35 100644
--- a/services/rpz.c
+++ b/services/rpz.c
@@ -1188,6 +1188,22 @@ rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint1
return z;
}
+/** Find entry for RR type in the list of rrsets for the clientip. */
+static struct local_rrset*
+rpz_find_synthesized_rrset(uint16_t qtype,
+ struct clientip_synthesized_rr* data)
+{
+ struct local_rrset* cursor = data->data;
+ while( cursor != NULL) {
+ struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
+ if(htons(qtype) == packed_rrset->type) {
+ return cursor;
+ }
+ cursor = cursor->next;
+ }
+ return NULL;
+}
+
/**
* Remove RR from RPZ's local-data
* @param z: local-zone for RPZ, holding write lock
@@ -1270,15 +1286,15 @@ rpz_rrset_delete_rr(struct resp_addr* raddr, uint16_t rr_type, uint8_t* rdata,
}
-/** Remove RR from RPZ's local-zone */
+/** Remove RR from rpz localzones structure */
static void
-rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
- enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
- uint8_t* rdatawl, size_t rdatalen)
+rpz_remove_local_zones_trigger(struct local_zones* zones, uint8_t* dname,
+ size_t dnamelen, enum rpz_action a, uint16_t rr_type,
+ uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen)
{
struct local_zone* z;
int delete_zone = 1;
- z = rpz_find_zone(r->local_zones, dname, dnamelen, rr_class,
+ z = rpz_find_zone(zones, dname, dnamelen, rr_class,
1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/);
if(!z) {
verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
@@ -1290,15 +1306,24 @@ rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
dnamelen, rr_type, rdatawl, rdatalen);
else if(a != localzone_type_to_rpz_action(z->type)) {
lock_rw_unlock(&z->lock);
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
return;
}
lock_rw_unlock(&z->lock);
if(delete_zone) {
- local_zones_del_zone(r->local_zones, z);
+ local_zones_del_zone(zones, z);
}
- lock_rw_unlock(&r->local_zones->lock);
- return;
+ lock_rw_unlock(&zones->lock);
+}
+
+/** Remove RR from RPZ's local-zone */
+static void
+rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
+ uint8_t* rdatawl, size_t rdatalen)
+{
+ rpz_remove_local_zones_trigger(r->local_zones, dname, dnamelen,
+ a, rr_type, rr_class, rdatawl, rdatalen);
}
static void
@@ -1335,15 +1360,159 @@ rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
lock_rw_unlock(&r->respip_set->lock);
}
+/** find and remove type from list of local_rrset entries*/
+static void
+del_local_rrset_from_list(struct local_rrset** list_head, uint16_t dtype)
+{
+ struct local_rrset* prev=NULL, *p=*list_head;
+ while(p && ntohs(p->rrset->rk.type) != dtype) {
+ prev = p;
+ p = p->next;
+ }
+ if(!p)
+ return; /* rrset type not found */
+ /* unlink it */
+ if(prev) prev->next = p->next;
+ else *list_head = p->next;
+ /* no memory recycling for zone deletions ... */
+}
+
+/** Delete client-ip trigger RR from its RRset and perhaps also the rrset
+ * from the linked list. Returns if the local data is empty and the node can
+ * be deleted too, or not. */
+static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node,
+ uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
+{
+ struct local_rrset* rrset;
+ struct packed_rrset_data* d;
+ size_t index;
+ rrset = rpz_find_synthesized_rrset(rr_type, node);
+ if(rrset == NULL)
+ return 0; /* type not found, ignore */
+ d = (struct packed_rrset_data*)rrset->rrset->entry.data;
+ if(!packed_rrset_find_rr(d, rdatawl, rdatalen, &index))
+ return 0; /* RR not found, ignore */
+ if(d->count == 1) {
+ /* regional alloc'd */
+ /* delete the type entry from the list */
+ del_local_rrset_from_list(&node->data, rr_type);
+ /* if the list is empty, the node can be removed too */
+ if(node->data == NULL)
+ return 1;
+ } else if (d->count > 1) {
+ if(!local_rrset_remove_rr(d, index))
+ return 0;
+ }
+ return 0;
+}
+
+/** remove trigger RR from clientip_syntheized set tree. */
+static void
+rpz_clientip_remove_trigger_rr(struct clientip_synthesized_rrset* set,
+ struct sockaddr_storage* addr, socklen_t addrlen, int net,
+ enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
+{
+ struct clientip_synthesized_rr* node;
+ int delete_node = 1;
+
+ lock_rw_wrlock(&set->lock);
+ node = (struct clientip_synthesized_rr*)addr_tree_find(&set->entries,
+ addr, addrlen, net);
+ if(node == NULL) {
+ /* netblock not found */
+ verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
+ "RPZ address, netblock not found");
+ lock_rw_unlock(&set->lock);
+ return;
+ }
+ lock_rw_wrlock(&node->lock);
+ if(a == RPZ_LOCAL_DATA_ACTION) {
+ /* remove RR, signal whether entry can be removed */
+ delete_node = rpz_remove_clientip_rr(node, rr_type, rdatawl,
+ rdatalen);
+ } else if(a != node->action) {
+ /* ignore the RR with different action specification */
+ delete_node = 0;
+ }
+ if(delete_node) {
+ rbtree_delete(&set->entries, node->node.node.key);
+ }
+ lock_rw_unlock(&set->lock);
+ lock_rw_unlock(&node->lock);
+ if(delete_node) {
+ lock_rw_destroy(&node->lock);
+ }
+}
+
+/** Remove clientip trigger RR from RPZ. */
+static void
+rpz_remove_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
+{
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+ if(a == RPZ_INVALID_ACTION)
+ return;
+ if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
+ return;
+ rpz_clientip_remove_trigger_rr(r->client_set, &addr, addrlen, net,
+ a, rr_type, rdatawl, rdatalen);
+}
+
+/** Remove nsip trigger RR from RPZ. */
+static void
+rpz_remove_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
+{
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+ if(a == RPZ_INVALID_ACTION)
+ return;
+ if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
+ return;
+ rpz_clientip_remove_trigger_rr(r->ns_set, &addr, addrlen, net,
+ a, rr_type, rdatawl, rdatalen);
+}
+
+/** Remove nsdname trigger RR from RPZ. */
+static void
+rpz_remove_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
+ uint8_t* rdatawl, size_t rdatalen)
+{
+ uint8_t* dname_stripped = NULL;
+ size_t dnamelen_stripped = 0;
+ if(a == RPZ_INVALID_ACTION)
+ return;
+ if(!rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped,
+ &dnamelen_stripped))
+ return;
+ rpz_remove_local_zones_trigger(r->nsdname_zones, dname_stripped,
+ dnamelen_stripped, a, rr_type, rr_class, rdatawl, rdatalen);
+ free(dname_stripped);
+}
+
void
-rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen,
- uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen)
+rpz_remove_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
+ size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl,
+ size_t rdatalen)
{
size_t policydnamelen;
enum rpz_trigger t;
enum rpz_action a;
uint8_t* policydname;
+ if(rpz_type_ignored(rr_type)) {
+ /* this rpz action is not valid, eg. this is the SOA or NS RR */
+ return;
+ }
+ if(!dname_subdomain_c(dname, azname)) {
+ /* not subdomain of the RPZ zone. */
+ return;
+ }
+
if(!(policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1)))
return;
@@ -1358,13 +1527,28 @@ rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen,
return;
}
t = rpz_dname_to_trigger(policydname, policydnamelen);
+ if(t == RPZ_INVALID_TRIGGER) {
+ /* skipping invalid trigger */
+ free(policydname);
+ return;
+ }
if(t == RPZ_QNAME_TRIGGER) {
rpz_remove_qname_trigger(r, policydname, policydnamelen, a,
rr_type, rr_class, rdatawl, rdatalen);
} else if(t == RPZ_RESPONSE_IP_TRIGGER) {
rpz_remove_response_ip_trigger(r, policydname, policydnamelen,
a, rr_type, rdatawl, rdatalen);
+ } else if(t == RPZ_CLIENT_IP_TRIGGER) {
+ rpz_remove_clientip_trigger(r, policydname, policydnamelen, a,
+ rr_type, rdatawl, rdatalen);
+ } else if(t == RPZ_NSIP_TRIGGER) {
+ rpz_remove_nsip_trigger(r, policydname, policydnamelen, a,
+ rr_type, rdatawl, rdatalen);
+ } else if(t == RPZ_NSDNAME_TRIGGER) {
+ rpz_remove_nsdname_trigger(r, policydname, policydnamelen, a,
+ rr_type, rr_class, rdatawl, rdatalen);
}
+ /* else it was an unsupported trigger, also skipped. */
free(policydname);
}
@@ -1563,21 +1747,6 @@ rpz_local_encode(struct module_env* env, struct query_info* qinfo,
return 1;
}
-static struct local_rrset*
-rpz_find_synthesized_rrset(uint16_t qtype,
- struct clientip_synthesized_rr* data)
-{
- struct local_rrset* cursor = data->data;
- while( cursor != NULL) {
- struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
- if(htons(qtype) == packed_rrset->type) {
- return cursor;
- }
- cursor = cursor->next;
- }
- return NULL;
-}
-
/** allocate SOA record ubrrsetkey in region */
static struct ub_packed_rrset_key*
make_soa_ubrrset(struct auth_zone* auth_zone, struct auth_rrset* soa,
@@ -1713,7 +1882,8 @@ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
0, /* ns */
0, /* ar */
0, /* total */
- sec_status_insecure);
+ sec_status_insecure,
+ LDNS_EDE_NONE);
if(msg->rep)
msg->rep->authoritative = 1;
if(!rpz_add_soa(msg->rep, ms, az))
@@ -1742,7 +1912,8 @@ rpz_synthesize_nxdomain(struct rpz* r, struct module_qstate* ms,
0, /* ns */
0, /* ar */
0, /* total */
- sec_status_insecure);
+ sec_status_insecure,
+ LDNS_EDE_NONE);
if(msg->rep)
msg->rep->authoritative = 1;
if(!rpz_add_soa(msg->rep, ms, az))
@@ -1772,7 +1943,8 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs
0, /* ns */
0, /* ar */
1, /* total */
- sec_status_insecure);
+ sec_status_insecure,
+ LDNS_EDE_NONE);
if(new_reply_info == NULL) {
log_err("out of memory");
return NULL;
diff --git a/services/rpz.h b/services/rpz.h
index 53781197aeec..e6d8bf566e16 100644
--- a/services/rpz.h
+++ b/services/rpz.h
@@ -84,10 +84,11 @@ enum rpz_action {
RPZ_CNAME_OVERRIDE_ACTION, /* RPZ CNAME action override*/
};
-struct clientip_synthesized_rrset{
+struct clientip_synthesized_rrset {
struct regional* region;
struct rbtree_type entries;
- lock_rw_type lock; /* lock on the respip tree */
+ /** lock on the entries tree */
+ lock_rw_type lock;
};
struct clientip_synthesized_rr {
@@ -95,10 +96,6 @@ struct clientip_synthesized_rr {
struct addr_tree_node node;
/** lock on the node item */
lock_rw_type lock;
- /** tag bitlist */
- uint8_t* taglist;
- /** length of the taglist (in bytes) */
- size_t taglen;
/** action for this address span */
enum rpz_action action;
/** "local data" for this node */
@@ -152,6 +149,7 @@ int rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dna
/**
* Delete policy matching RR, used for IXFR.
* @param r: the rpz to add the policy to.
+ * @param azname: dname of the auth-zone
* @param aznamelen: the length of the auth-zone name
* @param dname: dname of the RR
* @param dnamelen: length of the dname
@@ -160,9 +158,9 @@ int rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dna
* @param rdatawl: rdata of the RR, prepended with the rdata size
* @param rdatalen: length if the RR, including the prepended rdata size
*/
-void rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname,
- size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl,
- size_t rdatalen);
+void rpz_remove_rr(struct rpz* r, uint8_t* azname, size_t aznamelen,
+ uint8_t* dname, size_t dnamelen, uint16_t rr_type, uint16_t rr_class,
+ uint8_t* rdatawl, size_t rdatalen);
/**
* Walk over the RPZ zones to find and apply a QNAME trigger policy.