From 401770e05c71ecb5ae61a59d316069b4b78bf622 Mon Sep 17 00:00:00 2001 From: Cy Schubert Date: Mon, 18 Sep 2023 12:59:52 -0700 Subject: unbound: Vendor import 1.18.0 Release notes at https://www.nlnetlabs.nl/news/2023/Aug/30/unbound-1.18.0-released/ --- services/authzone.c | 10 +- services/cache/dns.c | 50 +++--- services/cache/infra.c | 75 ++++++--- services/cache/infra.h | 5 +- services/listen_dnsport.c | 153 +++++++++++------- services/localzone.c | 20 ++- services/localzone.h | 2 + services/mesh.c | 385 +++++++++++++++++++++------------------------ services/mesh.h | 14 +- services/modstack.c | 10 +- services/outside_network.c | 28 ++++ services/rpz.c | 230 +++++++++++++++++++++++---- services/rpz.h | 16 +- 13 files changed, 624 insertions(+), 374 deletions(-) (limited to 'services') 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 #endif - +#ifdef HAVE_LINUX_NET_TSTAMP_H +#include +#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 @@ -1679,6 +1680,16 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, } else if(lz_type == local_zone_typetransparent || 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, @@ -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. @@ -478,14 +480,6 @@ struct mesh_state* mesh_state_create(struct module_env* env, struct query_info* qinfo, struct respip_client_info* cinfo, 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. 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. -- cgit v1.3