diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/unbound/ax_pkg_swig.m4 | 139 | ||||
-rw-r--r-- | usr.sbin/unbound/services/rpz.c | 342 | ||||
-rw-r--r-- | usr.sbin/unbound/services/rpz.h | 8 | ||||
-rw-r--r-- | usr.sbin/unbound/testcode/checklocks.c | 24 | ||||
-rw-r--r-- | usr.sbin/unbound/testcode/fake_event.c | 72 | ||||
-rw-r--r-- | usr.sbin/unbound/testcode/replay.c | 14 | ||||
-rw-r--r-- | usr.sbin/unbound/testcode/replay.h | 9 | ||||
-rw-r--r-- | usr.sbin/unbound/testcode/testbound.c | 6 |
8 files changed, 518 insertions, 96 deletions
diff --git a/usr.sbin/unbound/ax_pkg_swig.m4 b/usr.sbin/unbound/ax_pkg_swig.m4 new file mode 100644 index 00000000000..7a4196ff5df --- /dev/null +++ b/usr.sbin/unbound/ax_pkg_swig.m4 @@ -0,0 +1,139 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pkg_swig.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PKG_SWIG([major.minor.micro], [action-if-found], [action-if-not-found]) +# +# DESCRIPTION +# +# This macro searches for a SWIG installation on your system. If found, +# then SWIG is AC_SUBST'd; if not found, then $SWIG is empty. If SWIG is +# found, then SWIG_LIB is set to the SWIG library path, and AC_SUBST'd. +# +# You can use the optional first argument to check if the version of the +# available SWIG is greater than or equal to the value of the argument. It +# should have the format: N[.N[.N]] (N is a number between 0 and 999. Only +# the first N is mandatory.) If the version argument is given (e.g. +# 1.3.17), AX_PKG_SWIG checks that the swig package is this version number +# or higher. +# +# As usual, action-if-found is executed if SWIG is found, otherwise +# action-if-not-found is executed. +# +# In configure.in, use as: +# +# AX_PKG_SWIG(1.3.17, [], [ AC_MSG_ERROR([SWIG is required to build..]) ]) +# AX_SWIG_ENABLE_CXX +# AX_SWIG_MULTI_MODULE_SUPPORT +# AX_SWIG_PYTHON +# +# LICENSE +# +# Copyright (c) 2008 Sebastian Huber <sebastian-huber@web.de> +# Copyright (c) 2008 Alan W. Irwin +# Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net> +# Copyright (c) 2008 Andrew Collier +# Copyright (c) 2011 Murray Cumming <murrayc@openismus.com> +# Copyright (c) 2018 Reini Urban <rurban@cpan.org> +# Copyright (c) 2021 Vincent Danjean <Vincent.Danjean@ens-lyon.org> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <https://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 15 + +AC_DEFUN([AX_PKG_SWIG],[ + # Find path to the "swig" executable. + AC_PATH_PROGS([SWIG],[swig swig3.0 swig2.0]) + if test -z "$SWIG" ; then + m4_ifval([$3],[$3],[:]) + elif test -z "$1" ; then + m4_ifval([$2],[$2],[:]) + else + AC_MSG_CHECKING([SWIG version]) + [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`] + AC_MSG_RESULT([$swig_version]) + if test -n "$swig_version" ; then + # Calculate the required version number components + [required=$1] + [required_major=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_major" ; then + [required_major=0] + fi + [required=`echo $required. | sed 's/[0-9]*[^0-9]//'`] + [required_minor=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_minor" ; then + [required_minor=0] + fi + [required=`echo $required. | sed 's/[0-9]*[^0-9]//'`] + [required_patch=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_patch" ; then + [required_patch=0] + fi + # Calculate the available version number components + [available=$swig_version] + [available_major=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_major" ; then + [available_major=0] + fi + [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] + [available_minor=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_minor" ; then + [available_minor=0] + fi + [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] + [available_patch=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_patch" ; then + [available_patch=0] + fi + # Convert the version tuple into a single number for easier comparison. + # Using base 100 should be safe since SWIG internally uses BCD values + # to encode its version number. + required_swig_vernum=`expr $required_major \* 10000 \ + \+ $required_minor \* 100 \+ $required_patch` + available_swig_vernum=`expr $available_major \* 10000 \ + \+ $available_minor \* 100 \+ $available_patch` + + if test $available_swig_vernum -lt $required_swig_vernum; then + AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version.]) + SWIG='' + m4_ifval([$3],[$3],[]) + else + AC_MSG_CHECKING([for SWIG library]) + SWIG_LIB=`$SWIG -swiglib | tr '\r\n' ' '` + AC_MSG_RESULT([$SWIG_LIB]) + m4_ifval([$2],[$2],[]) + fi + else + AC_MSG_WARN([cannot determine SWIG version]) + SWIG='' + m4_ifval([$3],[$3],[]) + fi + fi + AC_SUBST([SWIG_LIB]) +]) diff --git a/usr.sbin/unbound/services/rpz.c b/usr.sbin/unbound/services/rpz.c index 18d76c07bff..f036cc5fd64 100644 --- a/usr.sbin/unbound/services/rpz.c +++ b/usr.sbin/unbound/services/rpz.c @@ -478,43 +478,30 @@ new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen) return rrset; } -struct rpz* -rpz_create(struct config_auth* p) +/** delete the cname override */ +static void +delete_cname_override(struct rpz* r) { - struct rpz* r = calloc(1, sizeof(*r)); - if(!r) - goto err; - - r->region = regional_create_custom(sizeof(struct regional)); - if(!r->region) { - goto err; - } - - if(!(r->local_zones = local_zones_create())){ - goto err; - } - - r->nsdname_zones = local_zones_create(); - if(r->local_zones == NULL){ - goto err; - } - - if(!(r->respip_set = respip_set_create())) { - goto err; - } - - r->client_set = rpz_clientip_synthesized_set_create(); - if(r->client_set == NULL) { - goto err; + if(r->cname_override) { + /* The cname override is what is allocated in the region. */ + regional_free_all(r->region); + r->cname_override = NULL; } +} - r->ns_set = rpz_clientip_synthesized_set_create(); - if(r->ns_set == NULL) { - goto err; +/** Apply rpz config elements to the rpz structure, false on failure. */ +static int +rpz_apply_cfg_elements(struct rpz* r, struct config_auth* p) +{ + if(p->rpz_taglist && p->rpz_taglistlen) { + r->taglistlen = p->rpz_taglistlen; + r->taglist = memdup(p->rpz_taglist, r->taglistlen); + if(!r->taglist) { + log_err("malloc failure on RPZ taglist alloc"); + return 0; + } } - r->taglistlen = p->rpz_taglistlen; - r->taglist = memdup(p->rpz_taglist, r->taglistlen); if(p->rpz_action_override) { r->action_override = rpz_config_to_action(p->rpz_action_override); } @@ -528,17 +515,17 @@ rpz_create(struct config_auth* p) if(!p->rpz_cname) { log_err("rpz: override with cname action found, but no " "rpz-cname-override configured"); - goto err; + return 0; } if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) { log_err("rpz: cannot parse cname override: %s", p->rpz_cname); - goto err; + return 0; } r->cname_override = new_cname_override(r->region, nm, nmlen); if(!r->cname_override) { - goto err; + return 0; } } r->log = p->rpz_log; @@ -546,9 +533,49 @@ rpz_create(struct config_auth* p) if(p->rpz_log_name) { if(!(r->log_name = strdup(p->rpz_log_name))) { log_err("malloc failure on RPZ log_name strdup"); - goto err; + return 0; } } + return 1; +} + +struct rpz* +rpz_create(struct config_auth* p) +{ + struct rpz* r = calloc(1, sizeof(*r)); + if(!r) + goto err; + + r->region = regional_create_custom(sizeof(struct regional)); + if(!r->region) { + goto err; + } + + if(!(r->local_zones = local_zones_create())){ + goto err; + } + + r->nsdname_zones = local_zones_create(); + if(r->local_zones == NULL){ + goto err; + } + + if(!(r->respip_set = respip_set_create())) { + goto err; + } + + r->client_set = rpz_clientip_synthesized_set_create(); + if(r->client_set == NULL) { + goto err; + } + + r->ns_set = rpz_clientip_synthesized_set_create(); + if(r->ns_set == NULL) { + goto err; + } + + if(!rpz_apply_cfg_elements(r, p)) + goto err; return r; err: if(r) { @@ -571,6 +598,32 @@ err: return NULL; } +int +rpz_config(struct rpz* r, struct config_auth* p) +{ + /* If the zonefile changes, it is read later, after which + * rpz_clear and rpz_finish_config is called. */ + + /* free taglist, if any */ + if(r->taglist) { + free(r->taglist); + r->taglist = NULL; + r->taglistlen = 0; + } + + /* free logname, if any */ + if(r->log_name) { + free(r->log_name); + r->log_name = NULL; + } + + delete_cname_override(r); + + if(!rpz_apply_cfg_elements(r, p)) + return 0; + return 1; +} + /** * Remove RPZ zone name from dname * Copy dname to newdname, without the originlen number of trailing bytes @@ -1191,16 +1244,20 @@ rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint1 /** 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 clientip_synthesized_rr* data, int alias_ok) { - struct local_rrset* cursor = data->data; + struct local_rrset* cursor = data->data, *cname = NULL; while( cursor != NULL) { struct packed_rrset_key* packed_rrset = &cursor->rrset->rk; if(htons(qtype) == packed_rrset->type) { return cursor; } + if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok) + cname = cursor; cursor = cursor->next; } + if(alias_ok) + return cname; return NULL; } @@ -1386,7 +1443,7 @@ static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node, struct local_rrset* rrset; struct packed_rrset_data* d; size_t index; - rrset = rpz_find_synthesized_rrset(rr_type, node); + rrset = rpz_find_synthesized_rrset(rr_type, node, 0); if(rrset == NULL) return 0; /* type not found, ignore */ d = (struct packed_rrset_data*)rrset->rrset->entry.data; @@ -1789,7 +1846,7 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, } /* check query type / rr type */ - rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr); + rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: unable to find local-data for query"); rrset_count = 0; @@ -1823,6 +1880,28 @@ nodata: rrset_count, rcode, rsoa); } +/** Apply the cname override action, during worker request callback. + * false on failure. */ +static int +rpz_apply_cname_override_action(struct rpz* r, + struct query_info* qinfo, struct regional* temp) +{ + if(!r) + return 0; + qinfo->local_alias = regional_alloc_zero(temp, + sizeof(struct local_rrset)); + if(qinfo->local_alias == NULL) + return 0; /* out of memory */ + qinfo->local_alias->rrset = respip_copy_rrset(r->cname_override, temp); + if(qinfo->local_alias->rrset == NULL) { + qinfo->local_alias = NULL; + return 0; /* out of memory */ + } + qinfo->local_alias->rrset->rk.dname = qinfo->qname; + qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + return 1; +} + /** add additional section SOA record to the reply. * Since this gets fed into the normal iterator answer creation, it * gets minimal-responses applied to it, that can remove the additional SOA @@ -1933,6 +2012,7 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return NULL; } + msg->qinfo = *qi; new_reply_info = construct_reply_info_base(ms->region, LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, 1, /* qd */ @@ -1975,40 +2055,42 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs static inline struct dns_msg* rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, - struct clientip_synthesized_rr* data, struct auth_zone* az) + struct query_info* qi, struct clientip_synthesized_rr* data, + struct auth_zone* az) { - struct query_info* qi = &ms->qinfo; struct local_rrset* rrset; - rrset = rpz_find_synthesized_rrset(qi->qtype, data); + rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* copy'n'paste from localzone.c */ static struct local_rrset* local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { - struct local_rrset* p; + struct local_rrset* p, *cname = NULL; type = htons(type); for(p = data->rrsets; p; p = p->next) { if(p->rrset->rk.type == type) return p; if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) - return p; + cname = p; } + if(alias_ok) + return cname; return NULL; } /* based on localzone.c:local_data_answer() */ static inline struct dns_msg* rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, - struct local_zone* z, struct matched_delegation_point const* match, - struct auth_zone* az) + struct query_info* qi, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct local_data key; struct local_data* ld; @@ -2029,13 +2111,13 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - rrset = local_data_find_type(ld, ms->qinfo.qtype, 1); + rrset = local_data_find_type(ld, qi->qtype, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsdname: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* like local_data_answer for qname triggers after a cname */ @@ -2052,17 +2134,70 @@ rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, key.namelabs = dname_count_labels(qinfo->qname); ld = (struct local_data*)rbtree_search(&z->data, &key.node); if(ld == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: name not found"); + verbose(VERB_ALGO, "rpz: qname: name not found"); return NULL; } rrset = local_data_find_type(ld, qinfo->qtype, 1); if(rrset == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: type not found"); + verbose(VERB_ALGO, "rpz: qname: type not found"); return NULL; } return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az); } +/** Synthesize a CNAME message for RPZ action override */ +static struct dns_msg* +rpz_synthesize_cname_override_msg(struct rpz* r, struct module_qstate* ms, + struct query_info* qinfo) +{ + struct dns_msg* msg = NULL; + struct reply_info* new_reply_info; + struct ub_packed_rrset_key* rp; + + msg = rpz_dns_msg_new(ms->region); + if(msg == NULL) { return NULL; } + + msg->qinfo = *qinfo; + new_reply_info = construct_reply_info_base(ms->region, + LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, + 1, /* qd */ + 0, /* ttl */ + 0, /* prettl */ + 0, /* expttl */ + 1, /* an */ + 0, /* ns */ + 0, /* ar */ + 1, /* total */ + sec_status_insecure, + LDNS_EDE_NONE); + if(new_reply_info == NULL) { + log_err("out of memory"); + return NULL; + } + new_reply_info->authoritative = 1; + + rp = respip_copy_rrset(r->cname_override, ms->region); + if(rp == NULL) { + log_err("out of memory"); + return NULL; + } + rp->rk.dname = qinfo->qname; + rp->rk.dname_len = qinfo->qname_len; + /* this rrset is from the rpz data, or synthesized. + * It is not actually from the network, so we flag it with this + * flags as a fake RRset. If later the cache is used to look up + * rrsets, then the fake ones are not returned (if you look without + * the flag). For like CNAME lookups from the iterator or A, AAAA + * lookups for nameserver targets, it would use the without flag + * actual data. So that the actual network data and fake data + * are kept track of separately. */ + rp->rk.flags |= PACKED_RRSET_RPZ; + new_reply_info->rrsets[0] = rp; + + msg->rep = new_reply_info; + return msg; +} + static int rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo, @@ -2072,17 +2207,8 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, struct local_data* ld = NULL; int ret = 0; if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { - qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset)); - if(qinfo->local_alias == NULL) { - return 0; /* out of memory */ - } - qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override, - sizeof(*r->cname_override)); - if(qinfo->local_alias->rrset == NULL) { - return 0; /* out of memory */ - } - qinfo->local_alias->rrset->rk.dname = qinfo->qname; - qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + if(!rpz_apply_cname_override_action(r, qinfo, temp)) + return 0; if(r->log) { log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION, qinfo, repinfo, NULL, r->log_name); @@ -2134,8 +2260,9 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* } static struct dns_msg* -rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, - struct clientip_synthesized_rr* raddr, struct auth_zone* az) +rpz_apply_nsip_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct clientip_synthesized_rr* raddr, + struct auth_zone* az) { enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; @@ -2148,16 +2275,16 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2166,17 +2293,20 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); @@ -2194,9 +2324,9 @@ done: } static struct dns_msg* -rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, - struct local_zone* z, struct matched_delegation_point const* match, - struct auth_zone* az) +rpz_apply_nsdname_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct dns_msg* ret = NULL; enum rpz_action action = localzone_type_to_rpz_action(z->type); @@ -2209,10 +2339,10 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2221,19 +2351,22 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: - verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); ret = NULL; } @@ -2324,7 +2457,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* /* the nsdname has precedence over the nsip triggers */ z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, - ms->qinfo.qclass, &match); + is->qchase.qclass, &match); if(z != NULL) { lock_rw_unlock(&a->lock); break; @@ -2347,9 +2480,9 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* if(z) { lock_rw_unlock(&z->lock); } - return rpz_apply_nsip_trigger(ms, r, raddr, a); + return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a); } - return rpz_apply_nsdname_trigger(ms, r, z, &match, a); + return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a); } struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, @@ -2412,10 +2545,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, dname_str(is->qchase.qname, nm); dname_str(z->name, zn); if(strcmp(zn, nm) != 0) - verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s", zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); else - verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s", nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); } switch(localzone_type_to_rpz_action(lzt)) { @@ -2444,7 +2577,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, ms->rpz_passthru = 1; break; default: - verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); ret = NULL; } @@ -2472,8 +2605,21 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action); + if(node != NULL && *r_out && + (*r_out)->action_override != RPZ_NO_OVERRIDE_ACTION) { + client_action = (*r_out)->action_override; + } if(client_action == RPZ_PASSTHRU_ACTION) { + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); *passthru = 1; + ret = 0; + goto done; } if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && client_action != RPZ_PASSTHRU_ACTION)) { @@ -2488,14 +2634,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, if(client_action == RPZ_LOCAL_DATA_ACTION) { rpz_apply_clientip_localdata_action(node, env, qinfo, edns, repinfo, buf, temp, *a_out); + ret = 1; + } else if(client_action == RPZ_CNAME_OVERRIDE_ACTION) { + if(!rpz_apply_cname_override_action(*r_out, qinfo, + temp)) { + ret = 0; + goto done; + } + ret = 0; } else { - if(*r_out && (*r_out)->log) - log_rpz_apply( - (node?"clientip":"qname"), - ((*z_out)?(*z_out)->name:NULL), - (node?&node->node:NULL), - client_action, qinfo, repinfo, NULL, - (*r_out)->log_name); local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, rpz_action_to_localzone_type(client_action)); @@ -2503,8 +2650,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) == LDNS_RCODE_NXDOMAIN) LDNS_RA_CLR(sldns_buffer_begin(buf)); + ret = 1; } - ret = 1; + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); goto done; } ret = -1; diff --git a/usr.sbin/unbound/services/rpz.h b/usr.sbin/unbound/services/rpz.h index e6d8bf566e1..7f409087f77 100644 --- a/usr.sbin/unbound/services/rpz.h +++ b/usr.sbin/unbound/services/rpz.h @@ -226,6 +226,14 @@ int rpz_clear(struct rpz* r); struct rpz* rpz_create(struct config_auth* p); /** + * Change config on rpz, after reload. + * @param r: the rpz structure. + * @param p: the config that was read. + * @return false on failure. + */ +int rpz_config(struct rpz* r, struct config_auth* p); + +/** * String for RPZ action enum * @param a: RPZ action to get string for * @return: string for RPZ action diff --git a/usr.sbin/unbound/testcode/checklocks.c b/usr.sbin/unbound/testcode/checklocks.c index 1b5ef282b5e..d1c87746730 100644 --- a/usr.sbin/unbound/testcode/checklocks.c +++ b/usr.sbin/unbound/testcode/checklocks.c @@ -68,6 +68,17 @@ static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS]; int check_locking_order = 1; /** the pid of this runset, reasonably unique. */ static pid_t check_lock_pid; +/** + * Should checklocks print a trace of the lock and unlock calls. + * It uses fprintf for that because the log function uses a lock and that + * would loop otherwise. + */ +static int verbose_locking = 0; +/** + * Assume lock 0 0 (create_thread, create_instance), is the log lock and + * do not print for that. Otherwise the output is full of log lock accesses. + */ +static int verbose_locking_not_loglock = 1; /** print all possible debug info on the state of the system */ static void total_debug_info(void); @@ -508,6 +519,9 @@ checklock_rdlock(enum check_lock_type type, struct checked_lock* lock, if(key_deleted) return; + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_rdlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); log_assert(type == check_lock_rwlock); checklock_lockit(type, lock, func, file, line, try_rd, timed_rd, &lock->u.rwlock, 0, 0); @@ -528,6 +542,9 @@ checklock_wrlock(enum check_lock_type type, struct checked_lock* lock, if(key_deleted) return; log_assert(type == check_lock_rwlock); + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_wrlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); checklock_lockit(type, lock, func, file, line, try_wr, timed_wr, &lock->u.rwlock, 0, 1); } @@ -565,6 +582,9 @@ checklock_lock(enum check_lock_type type, struct checked_lock* lock, if(key_deleted) return; log_assert(type != check_lock_rwlock); + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_lock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); switch(type) { case check_lock_mutex: checklock_lockit(type, lock, func, file, line, @@ -602,6 +622,10 @@ checklock_unlock(enum check_lock_type type, struct checked_lock* lock, if(lock->hold_count <= 0) lock_error(lock, func, file, line, "too many unlocks"); + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_unlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); + /* store this point as last touched by */ lock->holder = thr; lock->hold_count --; diff --git a/usr.sbin/unbound/testcode/fake_event.c b/usr.sbin/unbound/testcode/fake_event.c index 13970c37726..09269289dd4 100644 --- a/usr.sbin/unbound/testcode/fake_event.c +++ b/usr.sbin/unbound/testcode/fake_event.c @@ -52,6 +52,7 @@ #include "util/data/msgreply.h" #include "util/data/msgencode.h" #include "util/data/dname.h" +#include "util/storage/slabhash.h" #include "util/edns.h" #include "util/config_file.h" #include "services/listen_dnsport.h" @@ -65,6 +66,7 @@ #include "sldns/wire2str.h" #include "sldns/str2wire.h" #include "daemon/remote.h" +#include "daemon/daemon.h" #include "util/timeval_func.h" #include <signal.h> struct worker; @@ -154,6 +156,8 @@ repevt_string(enum replay_event_type t) case repevt_assign: return "ASSIGN"; case repevt_traffic: return "TRAFFIC"; case repevt_infra_rtt: return "INFRA_RTT"; + case repevt_flush_message: return "FLUSH_MESSAGE"; + case repevt_expire_message: return "EXPIRE_MESSAGE"; default: return "UNKNOWN"; } } @@ -691,6 +695,66 @@ do_infra_rtt(struct replay_runtime* runtime) free(dp); } +/** Flush message from message cache. */ +static void +do_flush_message(struct replay_runtime* runtime) +{ + struct replay_moment* now = runtime->now; + uint8_t rr[1024]; + size_t rr_len = sizeof(rr), dname_len = 0; + hashvalue_type h; + struct query_info k; + + if(sldns_str2wire_rr_question_buf(now->string, rr, &rr_len, + &dname_len, NULL, 0, NULL, 0) != 0) + fatal_exit("could not parse '%s'", now->string); + + log_info("remove message %s", now->string); + k.qname = rr; + k.qname_len = dname_len; + k.qtype = sldns_wirerr_get_type(rr, rr_len, dname_len); + k.qclass = sldns_wirerr_get_class(rr, rr_len, dname_len); + k.local_alias = NULL; + h = query_info_hash(&k, 0); + slabhash_remove(runtime->daemon->env->msg_cache, h, &k); +} + +/** Expire message from message cache. */ +static void +do_expire_message(struct replay_runtime* runtime) +{ + struct replay_moment* now = runtime->now; + uint8_t rr[1024]; + size_t rr_len = sizeof(rr), dname_len = 0; + hashvalue_type h; + struct query_info k; + struct lruhash_entry* e; + + if(sldns_str2wire_rr_question_buf(now->string, rr, &rr_len, + &dname_len, NULL, 0, NULL, 0) != 0) + fatal_exit("could not parse '%s'", now->string); + + log_info("expire message %s", now->string); + k.qname = rr; + k.qname_len = dname_len; + k.qtype = sldns_wirerr_get_type(rr, rr_len, dname_len); + k.qclass = sldns_wirerr_get_class(rr, rr_len, dname_len); + k.local_alias = NULL; + h = query_info_hash(&k, 0); + + e = slabhash_lookup(runtime->daemon->env->msg_cache, h, &k, 0); + if(e) { + struct msgreply_entry* msg = (struct msgreply_entry*)e->key; + struct reply_info* rep = (struct reply_info*)msg->entry.data; + time_t expired = runtime->now_secs; + expired -= 3; + rep->ttl = expired; + rep->prefetch_ttl = expired; + rep->serve_expired_ttl = expired; + lock_rw_unlock(&msg->entry.lock); + } +} + /** perform exponential backoff on the timeout */ static void expon_timeout_backoff(struct replay_runtime* runtime) @@ -796,6 +860,14 @@ do_moment_and_advance(struct replay_runtime* runtime) do_infra_rtt(runtime); advance_moment(runtime); break; + case repevt_flush_message: + do_flush_message(runtime); + advance_moment(runtime); + break; + case repevt_expire_message: + do_expire_message(runtime); + advance_moment(runtime); + break; default: fatal_exit("testbound: unknown event type %d", runtime->now->evt_type); diff --git a/usr.sbin/unbound/testcode/replay.c b/usr.sbin/unbound/testcode/replay.c index f896a5512c5..95dde405f64 100644 --- a/usr.sbin/unbound/testcode/replay.c +++ b/usr.sbin/unbound/testcode/replay.c @@ -348,6 +348,20 @@ replay_moment_read(char* remain, FILE* in, const char* name, mom->string = strdup(m); if(!mom->string) fatal_exit("out of memory"); if(!mom->variable) fatal_exit("out of memory"); + } else if(parse_keyword(&remain, "FLUSH_MESSAGE")) { + mom->evt_type = repevt_flush_message; + while(isspace((unsigned char)*remain)) + remain++; + strip_end_white(remain); + mom->string = strdup(remain); + if(!mom->string) fatal_exit("out of memory"); + } else if(parse_keyword(&remain, "EXPIRE_MESSAGE")) { + mom->evt_type = repevt_expire_message; + while(isspace((unsigned char)*remain)) + remain++; + strip_end_white(remain); + mom->string = strdup(remain); + if(!mom->string) fatal_exit("out of memory"); } else { log_err("%d: unknown event type %s", pstate->lineno, remain); free(mom); diff --git a/usr.sbin/unbound/testcode/replay.h b/usr.sbin/unbound/testcode/replay.h index 0271dff0393..809e8ee3977 100644 --- a/usr.sbin/unbound/testcode/replay.h +++ b/usr.sbin/unbound/testcode/replay.h @@ -85,6 +85,8 @@ * The file contents is macro expanded before match. * o CHECK_TEMPFILE [fname] - followed by FILE_BEGIN [to match] FILE_END * o INFRA_RTT [ip] [dp] [rtt] - update infra cache entry with rtt. + * o FLUSH_MESSAGE name type class - flushes entry in message cache. + * o EXPIRE_MESSAGE name type class - expires entry in message cache. * o ERROR * ; following entry starts on the next line, ENTRY_BEGIN. * ; more STEP items @@ -148,6 +150,7 @@ struct fake_timer; struct replay_var; struct infra_cache; struct sldns_buffer; +struct daemon; /** * A replay scenario. @@ -212,6 +215,10 @@ struct replay_moment { repevt_assign, /** store infra rtt cache entry: addr and string (int) */ repevt_infra_rtt, + /** flush message cache entry */ + repevt_flush_message, + /** expire message cache entry */ + repevt_expire_message, /** cause traffic to flow */ repevt_traffic } @@ -297,6 +304,8 @@ struct replay_runtime { /** ref the infra cache (was passed to outside_network_create) */ struct infra_cache* infra; + /** the daemon structure passed in worker call to remote accept open */ + struct daemon* daemon; /** the current time in seconds */ time_t now_secs; diff --git a/usr.sbin/unbound/testcode/testbound.c b/usr.sbin/unbound/testcode/testbound.c index ec627cc8deb..f023860e086 100644 --- a/usr.sbin/unbound/testcode/testbound.c +++ b/usr.sbin/unbound/testcode/testbound.c @@ -48,6 +48,7 @@ #include "testcode/fake_event.h" #include "daemon/remote.h" #include "libunbound/worker.h" +#include "daemon/worker.h" #include "util/config_file.h" #include "sldns/keyraw.h" #ifdef UB_ON_WINDOWS @@ -532,9 +533,10 @@ void daemon_remote_clear(struct daemon_remote* ATTR_UNUSED(rc)) } int daemon_remote_open_accept(struct daemon_remote* ATTR_UNUSED(rc), - struct listen_port* ATTR_UNUSED(ports), - struct worker* ATTR_UNUSED(worker)) + struct listen_port* ATTR_UNUSED(ports), struct worker* worker) { + struct replay_runtime* runtime = (struct replay_runtime*)worker->base; + runtime->daemon = worker->daemon; return 1; } |