From: Witold Krecicki Date: Mon, 20 Feb 2017 10:57:28 +0000 (+0100) Subject: 4576. [func] The RPZ implementation has been substantially refactored... X-Git-Tag: v9.12.0a1~438 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fa9b4de7166479fa5d2708921bd13eb98d95acd7;p=thirdparty%2Fbind9.git 4576. [func] The RPZ implementation has been substantially refactored for improved performance and reliability. [RT #43449] --- diff --git a/CHANGES b/CHANGES index dea792b4084..13987f4f78a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +4576. [func] The RPZ implementation has been substantially + refactored for improved performance and reliability. + [RT #43449] + 4575. [security] DNS64 with "break-dnssec yes;" can result in an assertion failure. (CVE-2017-3136) [RT #44653] diff --git a/bin/named/server.c b/bin/named/server.c index f7c8cc19fdd..b512b5ddfb7 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -1872,11 +1872,12 @@ configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name, static isc_result_t configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element, isc_boolean_t recursive_only_def, dns_ttl_t ttl_def, - const dns_rpz_zone_t *old, isc_boolean_t *old_rpz_okp) + isc_uint32_t minupdateint_def, const dns_rpz_zone_t *old, + isc_boolean_t *old_rpz_okp) { const cfg_obj_t *rpz_obj, *obj; const char *str; - dns_rpz_zone_t *new; + dns_rpz_zone_t *zone = NULL; isc_result_t result; dns_rpz_num_t rpz_num; @@ -1891,127 +1892,118 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element, return (ISC_R_FAILURE); } - new = isc_mem_get(view->rpzs->mctx, sizeof(*new)); - if (new == NULL) { - cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, - "no memory for response policy zones"); - return (ISC_R_NOMEMORY); - } - - memset(new, 0, sizeof(*new)); - result = isc_refcount_init(&new->refs, 1); + result = dns_rpz_new_zone(view->rpzs, &zone); if (result != ISC_R_SUCCESS) { - isc_mem_put(view->rpzs->mctx, new, sizeof(*new)); + cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, + "Error creating new RPZ zone : %s", + isc_result_totext(result)); return (result); } - dns_name_init(&new->origin, NULL); - dns_name_init(&new->client_ip, NULL); - dns_name_init(&new->ip, NULL); - dns_name_init(&new->nsdname, NULL); - dns_name_init(&new->nsip, NULL); - dns_name_init(&new->passthru, NULL); - dns_name_init(&new->drop, NULL); - dns_name_init(&new->tcp_only, NULL); - dns_name_init(&new->cname, NULL); - new->num = view->rpzs->p.num_zones++; - view->rpzs->zones[new->num] = new; obj = cfg_tuple_get(rpz_obj, "recursive-only"); if (cfg_obj_isvoid(obj) ? recursive_only_def : cfg_obj_asboolean(obj)) { - view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(new->num); + view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(zone->num); } else { - view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(new->num); + view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(zone->num); } obj = cfg_tuple_get(rpz_obj, "log"); if (!cfg_obj_isvoid(obj) && !cfg_obj_asboolean(obj)) { - view->rpzs->p.no_log |= DNS_RPZ_ZBIT(new->num); + view->rpzs->p.no_log |= DNS_RPZ_ZBIT(zone->num); } else { - view->rpzs->p.no_log &= ~DNS_RPZ_ZBIT(new->num); + view->rpzs->p.no_log &= ~DNS_RPZ_ZBIT(zone->num); } obj = cfg_tuple_get(rpz_obj, "max-policy-ttl"); if (cfg_obj_isuint32(obj)) { - new->max_policy_ttl = cfg_obj_asuint32(obj); + zone->max_policy_ttl = cfg_obj_asuint32(obj); + } else { + zone->max_policy_ttl = ttl_def; + } + + obj = cfg_tuple_get(rpz_obj, "min-update-interval"); + if (cfg_obj_isuint32(obj)) { + zone->min_update_int = cfg_obj_asuint32(obj); } else { - new->max_policy_ttl = ttl_def; + zone->min_update_int = minupdateint_def; } - if (*old_rpz_okp && new->max_policy_ttl != old->max_policy_ttl) + + if (*old_rpz_okp && zone->max_policy_ttl != old->max_policy_ttl) *old_rpz_okp = ISC_FALSE; str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name")); - result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone"); + result = configure_rpz_name(view, rpz_obj, &zone->origin, str, "zone"); if (result != ISC_R_SUCCESS) return (result); - if (dns_name_equal(&new->origin, dns_rootname)) { + if (dns_name_equal(&zone->origin, dns_rootname)) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid zone name '%s'", str); return (DNS_R_EMPTYLABEL); } for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones-1; ++rpz_num) { if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin, - &new->origin)) { + &zone->origin)) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "duplicate '%s'", str); result = DNS_R_DUPLICATE; return (result); } } - if (*old_rpz_okp && !dns_name_equal(&old->origin, &new->origin)) + if (*old_rpz_okp && !dns_name_equal(&old->origin, &zone->origin)) *old_rpz_okp = ISC_FALSE; - result = configure_rpz_name2(view, rpz_obj, &new->client_ip, - DNS_RPZ_CLIENT_IP_ZONE, &new->origin); + result = configure_rpz_name2(view, rpz_obj, &zone->client_ip, + DNS_RPZ_CLIENT_IP_ZONE, &zone->origin); if (result != ISC_R_SUCCESS) return (result); - result = configure_rpz_name2(view, rpz_obj, &new->ip, - DNS_RPZ_IP_ZONE, &new->origin); + result = configure_rpz_name2(view, rpz_obj, &zone->ip, + DNS_RPZ_IP_ZONE, &zone->origin); if (result != ISC_R_SUCCESS) return (result); - result = configure_rpz_name2(view, rpz_obj, &new->nsdname, - DNS_RPZ_NSDNAME_ZONE, &new->origin); + result = configure_rpz_name2(view, rpz_obj, &zone->nsdname, + DNS_RPZ_NSDNAME_ZONE, &zone->origin); if (result != ISC_R_SUCCESS) return (result); - result = configure_rpz_name2(view, rpz_obj, &new->nsip, - DNS_RPZ_NSIP_ZONE, &new->origin); + result = configure_rpz_name2(view, rpz_obj, &zone->nsip, + DNS_RPZ_NSIP_ZONE, &zone->origin); if (result != ISC_R_SUCCESS) return (result); - result = configure_rpz_name(view, rpz_obj, &new->passthru, + result = configure_rpz_name(view, rpz_obj, &zone->passthru, DNS_RPZ_PASSTHRU_NAME, "name"); if (result != ISC_R_SUCCESS) return (result); - result = configure_rpz_name(view, rpz_obj, &new->drop, + result = configure_rpz_name(view, rpz_obj, &zone->drop, DNS_RPZ_DROP_NAME, "name"); if (result != ISC_R_SUCCESS) return (result); - result = configure_rpz_name(view, rpz_obj, &new->tcp_only, + result = configure_rpz_name(view, rpz_obj, &zone->tcp_only, DNS_RPZ_TCP_ONLY_NAME, "name"); if (result != ISC_R_SUCCESS) return (result); obj = cfg_tuple_get(rpz_obj, "policy"); if (cfg_obj_isvoid(obj)) { - new->policy = DNS_RPZ_POLICY_GIVEN; + zone->policy = DNS_RPZ_POLICY_GIVEN; } else { str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name")); - new->policy = dns_rpz_str2policy(str); - INSIST(new->policy != DNS_RPZ_POLICY_ERROR); - if (new->policy == DNS_RPZ_POLICY_CNAME) { + zone->policy = dns_rpz_str2policy(str); + INSIST(zone->policy != DNS_RPZ_POLICY_ERROR); + if (zone->policy == DNS_RPZ_POLICY_CNAME) { str = cfg_obj_asstring(cfg_tuple_get(obj, "cname")); - result = configure_rpz_name(view, rpz_obj, &new->cname, + result = configure_rpz_name(view, rpz_obj, &zone->cname, str, "cname"); if (result != ISC_R_SUCCESS) return (result); } } - if (*old_rpz_okp && (new->policy != old->policy || - !dns_name_equal(&old->cname, &new->cname))) + if (*old_rpz_okp && (zone->policy != old->policy || + !dns_name_equal(&old->cname, &zone->cname))) *old_rpz_okp = ISC_FALSE; return (ISC_R_SUCCESS); @@ -2025,7 +2017,8 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj, const cfg_obj_t *sub_obj; isc_boolean_t recursive_only_def; dns_ttl_t ttl_def; - dns_rpz_zones_t *new; + isc_uint32_t minupdateint_def; + dns_rpz_zones_t *zones; const dns_rpz_zones_t *old; dns_view_t *pview; const dns_rpz_zone_t *old_zone; @@ -2038,10 +2031,12 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj, if (zone_element == NULL) return (ISC_R_SUCCESS); - result = dns_rpz_new_zones(&view->rpzs, view->mctx); + result = dns_rpz_new_zones(&view->rpzs, view->mctx, + ns_g_taskmgr, ns_g_timermgr); if (result != ISC_R_SUCCESS) return (result); - new = view->rpzs; + + zones = view->rpzs; sub_obj = cfg_tuple_get(rpz_obj, "recursive-only"); if (!cfg_obj_isvoid(sub_obj) && @@ -2053,9 +2048,9 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj, sub_obj = cfg_tuple_get(rpz_obj, "break-dnssec"); if (!cfg_obj_isvoid(sub_obj) && cfg_obj_asboolean(sub_obj)) - new->p.break_dnssec = ISC_TRUE; + zones->p.break_dnssec = ISC_TRUE; else - new->p.break_dnssec = ISC_FALSE; + zones->p.break_dnssec = ISC_FALSE; sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl"); if (cfg_obj_isuint32(sub_obj)) @@ -2063,23 +2058,29 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj, else ttl_def = DNS_RPZ_MAX_TTL_DEFAULT; + sub_obj = cfg_tuple_get(rpz_obj, "min-update-interval"); + if (cfg_obj_isuint32(sub_obj)) + minupdateint_def = cfg_obj_asuint32(sub_obj); + else + minupdateint_def = DNS_RPZ_MINUPDATEINT_DEF; + sub_obj = cfg_tuple_get(rpz_obj, "min-ns-dots"); if (cfg_obj_isuint32(sub_obj)) - new->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1; + zones->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1; else - new->p.min_ns_labels = 2; + zones->p.min_ns_labels = 2; sub_obj = cfg_tuple_get(rpz_obj, "qname-wait-recurse"); if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) - new->p.qname_wait_recurse = ISC_TRUE; + zones->p.qname_wait_recurse = ISC_TRUE; else - new->p.qname_wait_recurse = ISC_FALSE; + zones->p.qname_wait_recurse = ISC_FALSE; sub_obj = cfg_tuple_get(rpz_obj, "nsip-wait-recurse"); if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) - new->p.nsip_wait_recurse = ISC_TRUE; + zones->p.nsip_wait_recurse = ISC_TRUE; else - new->p.nsip_wait_recurse = ISC_FALSE; + zones->p.nsip_wait_recurse = ISC_FALSE; pview = NULL; result = dns_viewlist_find(&ns_g_server->viewlist, @@ -2106,7 +2107,8 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj, } result = configure_rpz_zone(view, zone_element, recursive_only_def, ttl_def, - old_zone, old_rpz_okp); + minupdateint_def, old_zone, + old_rpz_okp); if (result != ISC_R_SUCCESS) { if (pview != NULL) dns_view_detach(&pview); @@ -2119,7 +2121,7 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj, * zones are unchanged, then use the same policy data. * Data for individual zones that must be reloaded will be merged. */ - if (old != NULL && memcmp(&old->p, &new->p, sizeof(new->p)) != 0) + if (old != NULL && memcmp(&old->p, &zones->p, sizeof(zones->p)) != 0) *old_rpz_okp = ISC_FALSE; if (*old_rpz_okp) { dns_rpz_detach_rpzs(&view->rpzs); diff --git a/bin/tests/system/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c index 699971baa79..f75aca81a3a 100644 --- a/bin/tests/system/dyndb/driver/db.c +++ b/bin/tests/system/dyndb/driver/db.c @@ -510,24 +510,6 @@ getrrsetstats(dns_db_t *db) { } -static void -rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) { - sampledb_t *sampledb = (sampledb_t *) db; - - REQUIRE(VALID_SAMPLEDB(sampledb)); - - dns_db_rpz_attach(sampledb->rbtdb, rpzs, rpz_num); -} - -static isc_result_t -rpz_ready(dns_db_t *db) { - sampledb_t *sampledb = (sampledb_t *) db; - - REQUIRE(VALID_SAMPLEDB(sampledb)); - - return (dns_db_rpz_ready(sampledb->rbtdb)); -} - static isc_result_t findnodeext(dns_db_t *db, const dns_name_t *name, isc_boolean_t create, dns_clientinfomethods_t *methods, @@ -617,8 +599,8 @@ static dns_dbmethods_t sampledb_methods = { resigned, isdnssec, getrrsetstats, - rpz_attach, - rpz_ready, + NULL, + NULL, findnodeext, findext, setcachestats, diff --git a/bin/tests/system/rpz/ns3/named.conf b/bin/tests/system/rpz/ns3/named.conf index 908400a9411..862ae9cd599 100644 --- a/bin/tests/system/rpz/ns3/named.conf +++ b/bin/tests/system/rpz/ns3/named.conf @@ -44,6 +44,7 @@ options { } min-ns-dots 0 qname-wait-recurse yes + min-update-interval 0 ; }; diff --git a/bin/tests/system/rpz/ns6/named.conf b/bin/tests/system/rpz/ns6/named.conf index 28600e0114d..b8d3ac24988 100644 --- a/bin/tests/system/rpz/ns6/named.conf +++ b/bin/tests/system/rpz/ns6/named.conf @@ -19,7 +19,7 @@ options { forward only; forwarders { 10.53.0.3; }; - response-policy { zone "policy1"; }; + response-policy { zone "policy1" min-update-interval 0; }; }; key rndc_key { diff --git a/bin/tests/system/rpz/ns7/named.conf b/bin/tests/system/rpz/ns7/named.conf index d2a98e83767..a4fe1716e63 100644 --- a/bin/tests/system/rpz/ns7/named.conf +++ b/bin/tests/system/rpz/ns7/named.conf @@ -17,7 +17,9 @@ options { listen-on { 10.53.0.7; }; listen-on-v6 { none; }; - response-policy { zone "policy2"; } qname-wait-recurse no; + response-policy { zone "policy2"; } + qname-wait-recurse no + min-update-interval 0; }; key rndc_key { diff --git a/bin/tests/system/rpzrecurse/tests.sh b/bin/tests/system/rpzrecurse/tests.sh index 799b5f66207..42144f8f1af 100644 --- a/bin/tests/system/rpzrecurse/tests.sh +++ b/bin/tests/system/rpzrecurse/tests.sh @@ -24,6 +24,7 @@ run_server() { echo "I:starting resolver using named.$TESTNAME.conf" cp -f ns2/named.$TESTNAME.conf ns2/named.conf $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns2 + sleep 3 } run_query() { diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 899c7f9f107..82f93e881ae 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4409,305 +4409,306 @@ badresp:1,adberr:0,findfail:0,valfail:0] statement in the named.conf file: -options { - [ attach-cache cache_name ; ] - [ version version_string ; ] - [ hostname hostname_string ; ] - [ server-id server_id_string ; ] - [ directory path_name ; ] - [ dnstap { message_type ; ... } ; ] - [ dnstap-output ( | ) path_name [ size size_spec ] [ versions ( number | ) ] ; ] - [ dnstap-identity ( string | | ) ; ] - [ dnstap-version ( string | ) ; ] - [ fstrm-set-buffer-hint number ; ] - [ fstrm-set-flush-timeout number ; ] - [ fstrm-set-input-queue-size number ; ] - [ fstrm-set-output-notify-threshold number ; ] - [ fstrm-set-output-queue-model ( | ) ; ] - [ fstrm-set-output-queue-size number ; ] - [ fstrm-set-reopen-interval number ; ] - [ geoip-directory path_name ; ] - [ key-directory path_name ; ] - [ managed-keys-directory path_name ; ] - [ named-xfer path_name ; ] - [ tkey-gssapi-keytab path_name ; ] - [ tkey-gssapi-credential principal ; ] - [ tkey-domain domain_name ; ] - [ tkey-dhkey key_name key_tag ; ] - [ cache-file path_name ; ] - [ dump-file path_name ; ] - [ bindkeys-file path_name ; ] - [ lock-file path_name ; ] - [ secroots-file path_name ; ] - [ session-keyfile path_name ; ] - [ session-keyname key_name ; ] - [ session-keyalg algorithm_id ; ] - [ memstatistics yes_or_no ; ] - [ memstatistics-file path_name ; ] - [ pid-file path_name ; ] - [ recursing-file path_name ; ] - [ statistics-file path_name ; ] - [ zone-statistics ( | | ) ; ] - [ auth-nxdomain yes_or_no ; ] - [ nxdomain-redirect string ; ] - [ deallocate-on-exit yes_or_no ; ] - [ dialup dialup_option ; ] - [ fake-iquery yes_or_no ; ] - [ fetch-glue yes_or_no ; ] - [ flush-zones-on-shutdown yes_or_no ; ] - [ has-old-clients yes_or_no ; ] - [ host-statistics yes_or_no ; ] - [ host-statistics-max number ; ] - [ minimal-any yes_or_no ; ] - [ minimal-responses ( yes_or_no | | ) ; ] - [ multiple-cnames yes_or_no ; ] - [ notify ( yes_or_no | | ) ; ] - [ recursion yes_or_no ; ] - [ send-cookie yes_or_no ; ] - [ require-server-cookie yes_or_no ; ] - [ cookie-algorithm algorithm_id ; ] - [ cookie-secret secret_string ; ] - [ nocookie-udp-size number ; ] - [ request-nsid yes_or_no ; ] - [ rfc2308-type1 yes_or_no ; ] - [ use-id-pool yes_or_no ; ] - [ maintain-ixfr-base yes_or_no ; ] - [ ixfr-from-differences ( yes_or_no | | ) ; ] - [ auto-dnssec ( | | ) ; ] - [ dnssec-enable yes_or_no ; ] - [ dnssec-validation ( yes_or_no | ) ; ] - [ dnssec-lookaside ( | | domain trust-anchor domain ) ; ] - [ dnssec-must-be-secure domain yes_or_no ; ] - [ dnssec-accept-expired yes_or_no ; ] - [ forward ( | ) ; ] - [ forwarders { - ( ip_addr [ port ip_port ] [ dscp ip_dscp ] ; ) - ... - } ; ] - [ dual-stack-servers [ port ip_port ] [ dscp ip_dscp ] { - ( ( domain_name | ip_addr ) [ port ip_port ] [ dscp ip_dscp ] ; ) - ... - } ; ] - [ check-names ( | | ) - ( | | ) ; ] - [ check-dup-records ( | | ) ; ] - [ check-mx ( | | ) ; ] - [ check-wildcard yes_or_no ; ] - [ check-integrity yes_or_no ; ] - [ check-mx-cname ( | | ) ; ] - [ check-srv-cname ( | | ) ; ] - [ check-sibling yes_or_no ; ] - [ check-spf ( | ) ; ] - [ allow-new-zones yes_or_no ; ] - [ allow-notify { address_match_list } ; ] - [ allow-query { address_match_list } ; ] - [ allow-query-on { address_match_list } ; ] - [ allow-query-cache { address_match_list } ; ] - [ allow-query-cache-on { address_match_list } ; ] - [ allow-transfer { address_match_list } ; ] - [ allow-recursion { address_match_list } ; ] - [ allow-recursion-on { address_match_list } ; ] - [ allow-update { address_match_list } ] - [ allow-update-forwarding { address_match_list } ; ] - [ automatic-interface-scan yes_or_no ; ] - [ geoip-use-ecs yes_or_no ; ] - [ update-check-ksk yes_or_no ; ] - [ dnssec-update-mode ( | ) ; ] - [ dnssec-dnskey-kskonly yes_or_no ; ] - [ dnssec-loadkeys-interval number ; ] - [ dnssec-secure-to-insecure yes_or_no ; ] - [ try-tcp-refresh yes_or_no ; ] - [ allow-v6-synthesis { address_match_list } ; ] - [ blackhole { address_match_list } ; ] - [ keep-response-order { address_match_list } ; ] - [ no-case-compress { address_match_list } ; ] - [ message-compression yes_or_no ; ] - [ use-v4-udp-ports { port_list } ; ] - [ avoid-v4-udp-ports { port_list } ; ] - [ use-v6-udp-ports { port_list } ; ] - [ avoid-v6-udp-ports { port_list } ; ] - [ listen-on [ port ip_port ] [ dscp ip_dscp ] { address_match_list } ; ] - [ listen-on-v6 [ port ip_port ] [ dscp ip_dscp ] { address_match_list } ; ] - [ query-source ( [ address ] ( ip4_addr | ) ) - [ port ( ip_port | ) ] [ dscp ip_dscp ] ] ; - [ query-source-v6 ( [ address ] ( ip6_addr | ) ) - [ port ( ip_port | ) ] [ dscp ip_dscp ] ] ; - [ use-queryport-pool yes_or_no ; ] - [ queryport-pool-ports number ; ] - [ queryport-pool-updateinterval number ; ] - [ max-records number ; ] - [ max-transfer-time-in number ; ] - [ max-transfer-time-out number ; ] - [ max-transfer-idle-in number ; ] - [ max-transfer-idle-out number ; ] - [ reserved-sockets number ; ] - [ recursive-clients number ; ] - [ tcp-clients number ; ] - [ clients-per-query number ; ] - [ max-clients-per-query number ; ] - [ fetches-per-server number [ ( | ) ] ; ] - [ fetches-per-zone number [ ( | ) ] ; ] - [ fetch-quota-params number fixedpoint fixedpoint fixedpoint ; ] - [ notify-rate number ; ] - [ startup-notify-rate number ; ] - [ serial-query-rate number ; ] - [ serial-queries number ; ] - [ tcp-listen-queue number ; ] - [ tcp-initial-timeout number; ] - [ tcp-idle-timeout number; ] - [ tcp-keepalive-timeout number; ] - [ tcp-advertised-timeout number; ] - [ transfer-format ( | ) ; ] - [ transfer-message-size number ; ] - [ transfers-in number ; ] - [ transfers-out number ; ] - [ transfers-per-ns number ; ] - [ transfer-source ( ip4_addr | ) - [ port ip_port ] [ dscp ip_dscp ] ; ] - [ transfer-source-v6 ( ip6_addr | ) - [ port ip_port ] [ dscp ip_dscp ] ; ] - [ alt-transfer-source ( ip4_addr | ) - [ port ip_port ] [ dscp ip_dscp ] ; ] - [ alt-transfer-source-v6 ( ip6_addr | ) - [ port ip_port ] [ dscp ip_dscp ] ; ] - [ use-alt-transfer-source yes_or_no ; ] - [ notify-delay seconds ; ] - [ notify-source ( ip4_addr | ) - [ port ip_port ] [ dscp ip_dscp ] ; ] - [ notify-source-v6 ( ip6_addr | ) - [ port ip_port ] [ dscp ip_dscp ] ; ] - [ notify-to-soa yes_or_no ; ] - [ also-notify [ port ip_port] [ dscp ip_dscp] { - ( masters | ip_addr [ port ip_port ] ) [ key key_name ] ; - ... - } ; ] - [ max-ixfr-log-size number ; ] - [ max-journal-size size_spec ; ] - [ coresize size_spec ; ] - [ datasize size_spec ; ] - [ files size_spec ; ] - [ stacksize size_spec ; ] - [ cleaning-interval number ; ] - [ heartbeat-interval number ; ] - [ interface-interval number ; ] - [ statistics-interval number ; ] - [ topology { address_match_list } ; ] - [ sortlist { address_match_list } ; ] - [ rrset-order { order_spec ; ... } ; ] - [ lame-ttl number ; ] - [ max-ncache-ttl number ; ] - [ max-cache-ttl number ; ] - [ max-zone-ttl ( | number ) ; ] - [ serial-update-method ( | | ) ; ] - [ servfail-ttl number ; ] - [ sig-validity-interval number [number] ; ] - [ sig-signing-nodes number ; ] - [ sig-signing-signatures number ; ] - [ sig-signing-type number ; ] - [ min-roots number ; ] - [ use-ixfr yes_or_no ; ] - [ provide-ixfr yes_or_no ; ] - [ request-ixfr yes_or_no ; ] - [ request-expire yes_or_no ; ] - [ treat-cr-as-space yes_or_no ; ] - [ min-refresh-time number ; ] - [ max-refresh-time number ; ] - [ min-retry-time number ; ] - [ max-retry-time number ; ] - [ nta-lifetime duration ; ] - [ nta-recheck duration ; ] - [ port ip_port ; ] - [ dscp ip_dscp ; ] - [ additional-from-auth yes_or_no ; ] - [ additional-from-cache yes_or_no ; ] - [ random-device path_name ; ] - [ max-cache-size size_or_percent ; ] - [ match-mapped-addresses yes_or_no ; ] - [ filter-aaaa-on-v4 ( yes_or_no | ) ; ] - [ filter-aaaa-on-v6 ( yes_or_no | ) ; ] - [ filter-aaaa { address_match_list } ; ] - [ dns64 ipv6-prefix { - [ clients { address_match_list } ; ] - [ mapped { address_match_list } ; ] - [ exclude { address_match_list } ; ] - [ suffix ip6-address ; ] - [ recursive-only yes_or_no ; ] - [ break-dnssec yes_or_no ; ] - } ; ] - [ dns64-server name ] - [ dns64-contact name ] - [ preferred-glue ( | | ); ] - [ edns-udp-size number ; ] - [ max-udp-size number ; ] - [ response-padding { address_match_list } block-size number ; ] - [ max-rsa-exponent-size number ; ] - [ root-delegation-only [ exclude { namelist } ] ; ] - [ querylog yes_or_no ; ] - [ disable-algorithms domain { algorithm ; ... } ; ] - [ disable-ds-digests domain { digest_type ; ... } ; ] - [ acache-enable yes_or_no ; ] - [ acache-cleaning-interval number ; ] - [ max-acache-size size_spec ; ] - [ max-recursion-depth number ; ] - [ max-recursion-queries number ; ] - [ masterfile-format ( | | ) ; ] - [ masterfile-style ( | ) ; ] - [ empty-server name ; ] - [ empty-contact name ; ] - [ empty-zones-enable yes_or_no ; ] - [ disable-empty-zone zone_name ; ] - [ zero-no-soa-ttl yes_or_no ; ] - [ zero-no-soa-ttl-cache yes_or_no ; ] - [ resolver-query-timeout number ; ] - [ deny-answer-addresses { address_match_list } - [ except-from { namelist } ] ; ] - [ deny-answer-aliases { namelist } - [ except-from { namelist } ] ; ] - [ prefetch number [ number ] ; ] - [ rate-limit { - [ responses-per-second number ; ] - [ referrals-per-second number ; ] - [ nodata-per-second number ; ] - [ nxdomains-per-second number ; ] - [ errors-per-second number ; ] - [ all-per-second number ; ] - [ window number ; ] - [ log-only yes_or_no ; ] - [ qps-scale number ; ] - [ ipv4-prefix-length number ; ] - [ ipv6-prefix-length number ; ] - [ slip number ; ] - [ exempt-clients { address_match_list } ; ] - [ max-table-size number ; ] - [ min-table-size number ; ] - } ; ] - [ response-policy { - zone zone_name - [ policy ( given | disabled | passthru | drop | - tcp-only | nxdomain | nodata | cname domain ) ] - [ recursive-only yes_or_no ] - [ log yes_or_no ] - [ max-policy-ttl number ] ; - ... - } [ recursive-only yes_or_no ] - [ max-policy-ttl number ] - [ break-dnssec yes_or_no ] - [ min-ns-dots number ] - [ nsip-wait-recurse yes_or_no ] - [ qname-wait-recurse yes_or_no ] ; ] - [ catalog-zones { - zone quoted_string - [ [ port ip_port ] [ dscp ip_dscp ] { - ( masters_list | ip_addr [port ip_port] [ key key_name] ) ; - ... - } ] - [ zone-directory path_name ] - [ in-memory yes_or_no ] - [ min-update-interval interval ] ; - ... - } ; ] - [ v6-bias number ; ] -} ; ] +options { + attach-cache cache_name; + version version_string; + hostname hostname_string; + server-id server_id_string; + directory path_name; + dnstap { message_type; ... }; + dnstap-output ( file | unix ) path_name; + dnstap-identity ( string | hostname | none ); + dnstap-version ( string | none ); + fstrm-set-buffer-hint number ; + fstrm-set-flush-timeout number ; + fstrm-set-input-queue-size number ; + fstrm-set-output-notify-threshold number ; + fstrm-set-output-queue-model ( mpsc | + spsc ) ; + fstrm-set-output-queue-size number ; + fstrm-set-reopen-interval number ; + geoip-directory path_name; + key-directory path_name; + managed-keys-directory path_name; + named-xfer path_name; + tkey-gssapi-keytab path_name; + tkey-gssapi-credential principal; + tkey-domain domainname; + tkey-dhkey key_name key_tag; + cache-file path_name; + dump-file path_name; + bindkeys-file path_name; + lock-file path_name; + secroots-file path_name; + session-keyfile path_name; + session-keyname key_name; + session-keyalg algorithm_id; + memstatistics yes_or_no; + memstatistics-file path_name; + pid-file path_name; + recursing-file path_name; + statistics-file path_name; + zone-statistics full | terse | none; + auth-nxdomain yes_or_no; + nxdomain-redirect string; + deallocate-on-exit yes_or_no; + dialup dialup_option; + fake-iquery yes_or_no; + fetch-glue yes_or_no; + flush-zones-on-shutdown yes_or_no; + has-old-clients yes_or_no; + host-statistics yes_or_no; + host-statistics-max number; + minimal-any yes_or_no; + minimal-responses (yes_or_no | no-auth | no-auth-recursive); + multiple-cnames yes_or_no; + notify yes_or_no | explicit | master-only; + recursion yes_or_no; + send-cookie yes_or_no; + require-server-cookie yes_or_no; + cookie-algorithm algorithm_id; + cookie-secret secret_string; + nocookie-udp-size number ; + request-nsid yes_or_no; + rfc2308-type1 yes_or_no; + use-id-pool yes_or_no; + maintain-ixfr-base yes_or_no; + ixfr-from-differences (yes_or_no | master | slave); + auto-dnssec allow|maintain|off; + dnssec-enable yes_or_no; + dnssec-validation (yes_or_no | auto); + dnssec-lookaside ( auto | + no | + domain trust-anchor domain ); + dnssec-must-be-secure domain yes_or_no; + dnssec-accept-expired yes_or_no; + forward ( only | first ); + forwarders { ip_addr port ip_port dscp ip_dscp ; ... }; + dual-stack-servers port ip_port dscp ip_dscp { + ( domain_name port ip_port dscp ip_dscp | + ip_addr port ip_port dscp ip_dscp) ; + ... }; + check-names ( master | slave | response ) + ( warn | fail | ignore ); + check-dup-records ( warn | fail | ignore ); + check-mx ( warn | fail | ignore ); + check-wildcard yes_or_no; + check-integrity yes_or_no; + check-mx-cname ( warn | fail | ignore ); + check-srv-cname ( warn | fail | ignore ); + check-sibling yes_or_no; + check-spf ( warn | ignore ); + allow-new-zones { yes_or_no }; + allow-notify { address_match_list }; + allow-query { address_match_list }; + allow-query-on { address_match_list }; + allow-query-cache { address_match_list }; + allow-query-cache-on { address_match_list }; + allow-transfer { address_match_list }; + allow-recursion { address_match_list }; + allow-recursion-on { address_match_list }; + allow-update { address_match_list }; + allow-update-forwarding { address_match_list }; + automatic-interface-scan { yes_or_no }; + geoip-use-ecs yes_or_no; + update-check-ksk yes_or_no; + dnssec-update-mode ( maintain | no-resign ); + dnssec-dnskey-kskonly yes_or_no; + dnssec-loadkeys-interval number; + dnssec-secure-to-insecure yes_or_no ; + try-tcp-refresh yes_or_no; + allow-v6-synthesis { address_match_list }; + blackhole { address_match_list }; + keep-response-order { address_match_list }; + no-case-compress { address_match_list }; + message-compression yes_or_no ; + use-v4-udp-ports { port_list }; + avoid-v4-udp-ports { port_list }; + use-v6-udp-ports { port_list }; + avoid-v6-udp-ports { port_list }; + listen-on port ip_port dscp ip_dscp { address_match_list }; + listen-on-v6 port ip_port dscp ip_dscp +{ address_match_list }; + query-source ( ( ip4_addr | * ) + port ( ip_port | * ) + dscp ip_dscp | + address ( ip4_addr | * ) + port ( ip_port | * ) ) + dscp ip_dscp ; + query-source-v6 ( ( ip6_addr | * ) + port ( ip_port | * ) + dscp ip_dscp | + address ( ip6_addr | * ) + port ( ip_port | * ) ) + dscp ip_dscp ; + use-queryport-pool yes_or_no; + queryport-pool-ports number; + queryport-pool-updateinterval number; + max-transfer-time-in number; + max-transfer-time-out number; + max-transfer-idle-in number; + max-transfer-idle-out number; + reserved-sockets number; + recursive-clients number; + tcp-clients number; + clients-per-query number ; + max-clients-per-query number ; + fetches-per-server number (drop | fail); + fetch-quota-params number fixedpoint fixedpoint fixedpoint ; + fetches-per-zone number (drop | fail); + notify-rate number; + startup-notify-rate number; + serial-query-rate number; + serial-queries number; + tcp-listen-queue number; + transfer-format ( one-answer | many-answers ); + transfer-message-size number; + transfers-in number; + transfers-out number; + transfers-per-ns number; + transfer-source (ip4_addr | *) port ip_port dscp ip_dscp ; + transfer-source-v6 (ip6_addr | *) port ip_port dscp ip_dscp ; + alt-transfer-source (ip4_addr | *) port ip_port dscp ip_dscp ; + alt-transfer-source-v6 (ip6_addr | *) port ip_port dscp ip_dscp ; + use-alt-transfer-source yes_or_no; + notify-delay seconds ; + notify-source (ip4_addr | *) port ip_port dscp ip_dscp ; + notify-source-v6 (ip6_addr | *) port ip_port dscp ip_dscp ; + notify-to-soa yes_or_no ; + also-notify port ip_port dscp ip_dscp { ( masters | ip_addr + port ip_port ) key keyname ; ... }; + max-ixfr-log-size number; + max-journal-size size_spec; + coresize size_spec ; + datasize size_spec ; + files size_spec ; + stacksize size_spec ; + cleaning-interval number; + heartbeat-interval number; + interface-interval number; + statistics-interval number; + topology { address_match_list }; + sortlist { address_match_list }; + rrset-order { order_spec ; order_spec ; ... }; + lame-ttl number; + max-ncache-ttl number; + max-cache-ttl number; + max-zone-ttl ( unlimited | number ; + serial-update-method increment|unixtime|date; + servfail-ttl number; + sig-validity-interval number number ; + sig-signing-nodes number ; + sig-signing-signatures number ; + sig-signing-type number ; + min-roots number; + use-ixfr yes_or_no ; + provide-ixfr yes_or_no; + request-ixfr yes_or_no; + request-expire yes_or_no; + treat-cr-as-space yes_or_no ; + min-refresh-time number ; + max-refresh-time number ; + min-retry-time number ; + max-retry-time number ; + nta-lifetime duration ; + nta-recheck duration ; + port ip_port; + dscp ip_dscp ; + additional-from-auth yes_or_no ; + additional-from-cache yes_or_no ; + random-device path_name ; + max-cache-size size_or_percent ; + match-mapped-addresses yes_or_no; + filter-aaaa-on-v4 ( yes_or_no | break-dnssec ); + filter-aaaa-on-v6 ( yes_or_no | break-dnssec ); + filter-aaaa { address_match_list }; + dns64 ipv6-prefix { + clients { address_match_list }; + mapped { address_match_list }; + exclude { address_match_list }; + suffix IPv6-address; + recursive-only yes_or_no; + break-dnssec yes_or_no; + }; ; + dns64-server name + dns64-contact name + preferred-glue ( A | AAAA | NONE ); + edns-udp-size number; + max-udp-size number; + max-rsa-exponent-size number; + root-delegation-only exclude { namelist } ; + querylog yes_or_no ; + disable-algorithms domain { algorithm; + algorithm; }; + disable-ds-digests domain { digest_type; + digest_type; }; + acache-enable yes_or_no ; + acache-cleaning-interval number; + max-acache-size size_spec ; + max-recursion-depth number ; + max-recursion-queries number ; + masterfile-format + (text|raw|map) ; + masterfile-style + (relative|full) ; + empty-server name ; + empty-contact name ; + empty-zones-enable yes_or_no ; + disable-empty-zone zone_name ; + zero-no-soa-ttl yes_or_no ; + zero-no-soa-ttl-cache yes_or_no ; + resolver-query-timeout number ; + deny-answer-addresses { address_match_list } except-from { namelist } ; + deny-answer-aliases { namelist } except-from { namelist } ; + prefetch number number ; + + rate-limit { + responses-per-second number ; + referrals-per-second number ; + nodata-per-second number ; + nxdomains-per-second number ; + errors-per-second number ; + all-per-second number ; + window number ; + log-only yes_or_no ; + qps-scale number ; + ipv4-prefix-length number ; + ipv6-prefix-length number ; + slip number ; + exempt-clients { address_match_list } ; + max-table-size number ; + min-table-size number ; + } ; + response-policy { + zone zone_name + policy (given | disabled | passthru | drop | + tcp-only | nxdomain | nodata | cname domain) + recursive-only yes_or_no + log yes_or_no + max-policy-ttl number + min-update-interval number + ; ... + } recursive-only yes_or_no + max-policy-ttl number + min-update-interval number + break-dnssec yes_or_no + min-ns-dots number + nsip-wait-recurse yes_or_no + qname-wait-recurse yes_or_no + automatic-interface-scan yes_or_no + ; + catalog-zones { + zone quoted_string + default-masters + port ip_port + dscp ip_dscp + { ( masters_list | ip_addr port ip_port key key ) ; ... } + in-memory yes_or_no + min-update-interval interval + ; ... }; + ; + v6-bias number ; +}; @@ -10735,6 +10736,15 @@ example.com CNAME rpz-tcp-only. turn off rewrite logging for a particular response policy zone. By default, all rewrites are logged. + + + Updates to RPZ zones are processed asynchronously; if there + is more than one update pending they are bundled together. + If an update to a RPZ zone (for example, via IXFR) happens less + than seconds after the most + recent update, then the changes will not be carried out until this + interval has elapsed. The default is 5 seconds. +
Response Rate Limiting diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 298beb3510f..57f563844c4 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -228,6 +228,19 @@ + The Response Policy Zone (RPZ) implementation has been + substantially refactored: updates to the RPZ summary + database are no longer directly performed by the zone + database but by a separate function that is called when + a policy zone is updated. This improves both performance + and reliability when policy zones receive frequent updates. + Summary database updates can be rate-limited by using the + min-update-interval option in a + response-policy statement. [RT #43449] + + + + dnstap now stores both the local and remote addresses for all messages, instead of only the remote address. The default output format for dnstap-read has diff --git a/doc/misc/options b/doc/misc/options index b32e37a7c96..c697de264fd 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -302,10 +302,11 @@ options { response-padding { ; ... } block-size ; response-policy { zone [ log ] [ - max-policy-ttl ] [ policy ( cname | disabled | drop | - given | no-op | nodata | nxdomain | passthru | tcp-only - ) ] [ recursive-only ]; ... } [ - break-dnssec ] [ max-policy-ttl ] [ + max-policy-ttl ] [ min-update-interval ] [ + policy ( cname | disabled | drop | given | no-op | nodata | + nxdomain | passthru | tcp-only ) ] [ + recursive-only ]; ... } [ break-dnssec ] [ + max-policy-ttl ] [ min-update-interval ] [ min-ns-dots ] [ nsip-wait-recurse ] [ qname-wait-recurse ] [ recursive-only ]; rfc2308-type1 ; // not yet implemented @@ -610,10 +611,11 @@ view [ ] { response-padding { ; ... } block-size ; response-policy { zone [ log ] [ - max-policy-ttl ] [ policy ( cname | disabled | drop | - given | no-op | nodata | nxdomain | passthru | tcp-only - ) ] [ recursive-only ]; ... } [ - break-dnssec ] [ max-policy-ttl ] [ + max-policy-ttl ] [ min-update-interval ] [ + policy ( cname | disabled | drop | given | no-op | nodata | + nxdomain | passthru | tcp-only | ) ] [ + recursive-only ]; ... } [ break-dnssec ] [ + max-policy-ttl ] [ min-update-interval ] [ min-ns-dots ] [ nsip-wait-recurse ] [ qname-wait-recurse ] [ recursive-only ]; rfc2308-type1 ; // not yet implemented diff --git a/lib/dns/db.c b/lib/dns/db.c index c707e943802..c0d649ab928 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -1048,7 +1048,7 @@ dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset, * it is dealing with a database that understands response policy zones. */ void -dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) { +dns_db_rpz_attach(dns_db_t *db, void *rpzs, isc_uint8_t rpz_num) { REQUIRE(db->methods->rpz_attach != NULL); (db->methods->rpz_attach)(db, rpzs, rpz_num); } diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 44da8fa3ecd..e6a73a0b340 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -56,7 +56,6 @@ #include #include #include -#include #include ISC_LANG_BEGINDECLS @@ -167,8 +166,8 @@ typedef struct dns_dbmethods { dns_dbversion_t *version); isc_boolean_t (*isdnssec)(dns_db_t *db); dns_stats_t *(*getrrsetstats)(dns_db_t *db); - void (*rpz_attach)(dns_db_t *db, dns_rpz_zones_t *rpzs, - dns_rpz_num_t rpz_num); + void (*rpz_attach)(dns_db_t *db, void *rpzs, + isc_uint8_t rpz_num); isc_result_t (*rpz_ready)(dns_db_t *db); isc_result_t (*findnodeext)(dns_db_t *db, const dns_name_t *name, isc_boolean_t create, @@ -1635,14 +1634,16 @@ dns_db_setcachestats(dns_db_t *db, isc_stats_t *stats); */ void -dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num); +dns_db_rpz_attach(dns_db_t *db, void *rpzs, isc_uint8_t rpz_num) + ISC_DEPRECATED; /*%< * Attach the response policy information for a view to a database for a * zone for the view. */ isc_result_t -dns_db_rpz_ready(dns_db_t *db); +dns_db_rpz_ready(dns_db_t *db) + ISC_DEPRECATED; /*%< * Finish loading a response policy zone. */ diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index 2023f01e98b..91249b7f050 100644 --- a/lib/dns/include/dns/events.h +++ b/lib/dns/include/dns/events.h @@ -75,6 +75,7 @@ #define DNS_EVENT_CATZADDZONE (ISC_EVENTCLASS_DNS + 54) #define DNS_EVENT_CATZMODZONE (ISC_EVENTCLASS_DNS + 55) #define DNS_EVENT_CATZDELZONE (ISC_EVENTCLASS_DNS + 56) +#define DNS_EVENT_RPZUPDATED (ISC_EVENTCLASS_DNS + 57) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/lib/dns/include/dns/rpz.h b/lib/dns/include/dns/rpz.h index d7350a93796..baf4eeb3361 100644 --- a/lib/dns/include/dns/rpz.h +++ b/lib/dns/include/dns/rpz.h @@ -15,6 +15,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -37,7 +41,6 @@ ISC_LANG_BEGINDECLS #define DNS_RPZ_DROP_NAME DNS_RPZ_PREFIX"drop" #define DNS_RPZ_TCP_ONLY_NAME DNS_RPZ_PREFIX"tcp-only" - typedef isc_uint8_t dns_rpz_prefix_t; typedef enum { @@ -118,20 +121,38 @@ struct dns_rpz_triggers { * A single response policy zone. */ typedef struct dns_rpz_zone dns_rpz_zone_t; +typedef struct dns_rpz_zones dns_rpz_zones_t; + struct dns_rpz_zone { - isc_refcount_t refs; - dns_rpz_num_t num; /* ordinal in list of policy zones */ - dns_name_t origin; /* Policy zone name */ - dns_name_t client_ip; /* DNS_RPZ_CLIENT_IP_ZONE.origin. */ - dns_name_t ip; /* DNS_RPZ_IP_ZONE.origin. */ - dns_name_t nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */ - dns_name_t nsip; /* DNS_RPZ_NSIP_ZONE.origin. */ - dns_name_t passthru; /* DNS_RPZ_PASSTHRU_NAME. */ - dns_name_t drop; /* DNS_RPZ_DROP_NAME. */ - dns_name_t tcp_only; /* DNS_RPZ_TCP_ONLY_NAME. */ - dns_name_t cname; /* override value for ..._CNAME */ - dns_ttl_t max_policy_ttl; + isc_refcount_t refs; + dns_rpz_num_t num; /* ordinal in list of policy zones */ + dns_name_t origin; /* Policy zone name */ + dns_name_t client_ip; /* DNS_RPZ_CLIENT_IP_ZONE.origin. */ + dns_name_t ip; /* DNS_RPZ_IP_ZONE.origin. */ + dns_name_t nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */ + dns_name_t nsip; /* DNS_RPZ_NSIP_ZONE.origin. */ + dns_name_t passthru; /* DNS_RPZ_PASSTHRU_NAME. */ + dns_name_t drop; /* DNS_RPZ_DROP_NAME. */ + dns_name_t tcp_only; /* DNS_RPZ_TCP_ONLY_NAME. */ + dns_name_t cname; /* override value for ..._CNAME */ + dns_ttl_t max_policy_ttl; dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */ + + isc_uint32_t min_update_int;/* minimal interval between updates */ + isc_ht_t *nodes; /* entries in zone */ + dns_rpz_zones_t *rpzs; /* owner */ + isc_time_t lastupdated; /* last time the zone was processed */ + isc_boolean_t updatepending; /* there is an update pending/waiting */ + isc_boolean_t updaterunning; /* there is an update running */ + dns_db_t *db; /* zones database */ + dns_dbversion_t *dbversion; /* version we will be updating to */ + dns_db_t *updb; /* zones database we're working on */ + dns_dbversion_t *updbversion; /* version we're currently working on */ + dns_dbiterator_t *updbit; /* iterator to use when updating */ + isc_ht_t *newnodes; /* entries in zone being updated */ + isc_boolean_t db_registered; /* is the notify event registered? */ + isc_timer_t *updatetimer; + isc_event_t updateevent; }; /* @@ -176,7 +197,6 @@ struct dns_rpz_popt { /* * Response policy zones known to a view. */ -typedef struct dns_rpz_zones dns_rpz_zones_t; struct dns_rpz_zones { dns_rpz_popt_t p; dns_rpz_zone_t *zones[DNS_RPZ_MAX_ZONES]; @@ -215,6 +235,9 @@ struct dns_rpz_zones { dns_rpz_triggers_t total_triggers; isc_mem_t *mctx; + isc_taskmgr_t *taskmgr; + isc_timermgr_t *timermgr; + isc_task_t *updater; isc_refcount_t refs; /* * One lock for short term read-only search that guarantees the @@ -311,6 +334,7 @@ typedef struct { #define DNS_RPZ_TTL_DEFAULT 5 #define DNS_RPZ_MAX_TTL_DEFAULT DNS_RPZ_TTL_DEFAULT +#define DNS_RPZ_MINUPDATEINT_DEF 60 /* * So various response policy zone messages can be turned up or down. @@ -336,7 +360,14 @@ dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset, dns_name_t *selfname); isc_result_t -dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx); +dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx, + isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr); + +isc_result_t +dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp); + +isc_result_t +dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg); void dns_rpz_attach_rpzs(dns_rpz_zones_t *source, dns_rpz_zones_t **target); @@ -346,11 +377,13 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp); isc_result_t dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp, - dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num); + dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) + ISC_DEPRECATED; isc_result_t dns_rpz_ready(dns_rpz_zones_t *rpzs, - dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num); + dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num) + ISC_DEPRECATED; isc_result_t dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 278b6f8aa17..c8f0167ee1e 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include #include @@ -251,8 +250,6 @@ typedef isc_uint64_t rbtdb_serial_t; #define resign_insert resign_insert64 #define resign_sooner resign_sooner64 #define resigned resigned64 -#define rpz_attach rpz_attach64 -#define rpz_ready rpz_ready64 #define serialize serialize64 #define set_index set_index64 #define set_ttl set_ttl64 @@ -678,9 +675,6 @@ struct dns_rbtdb { dns_rbt_t * tree; dns_rbt_t * nsec; dns_rbt_t * nsec3; - dns_rpz_zones_t *rpzs; - dns_rpz_num_t rpz_num; - dns_rpz_zones_t *load_rpzs; /* Unlocked */ unsigned int quantum; @@ -1296,19 +1290,6 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { if (rbtdb->cachestats != NULL) isc_stats_detach(&rbtdb->cachestats); - if (rbtdb->load_rpzs != NULL) { - /* - * We must be cleaning up after a failed zone loading. - */ - REQUIRE(rbtdb->rpzs != NULL && - rbtdb->rpz_num < rbtdb->rpzs->p.num_zones); - dns_rpz_detach_rpzs(&rbtdb->load_rpzs); - } - if (rbtdb->rpzs != NULL) { - REQUIRE(rbtdb->rpz_num < rbtdb->rpzs->p.num_zones); - dns_rpz_detach_rpzs(&rbtdb->rpzs); - } - isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks, rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); isc_rwlock_destroy(&rbtdb->tree_lock); @@ -1924,7 +1905,6 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { dns_fixedname_t fname; dns_name_t *name; isc_result_t result = ISC_R_UNEXPECTED; - unsigned int node_has_rpz; INSIST(!ISC_LINK_LINKED(node, deadlink)); @@ -1951,11 +1931,7 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { name = dns_fixedname_name(&fname); dns_rbt_fullnamefromnode(node, name); - node_has_rpz = node->rpz; result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); - if (result == ISC_R_SUCCESS && - rbtdb->rpzs != NULL && node_has_rpz) - dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name); break; case DNS_RBT_NSEC_HAS_NSEC: dns_fixedname_init(&fname); @@ -1988,11 +1964,7 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { isc_result_totext(result)); } } - node_has_rpz = node->rpz; result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); - if (result == ISC_R_SUCCESS && - rbtdb->rpzs != NULL && node_has_rpz) - dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name); break; case DNS_RBT_NSEC_NSEC: result = dns_rbt_deletenode(rbtdb->nsec, node, ISC_FALSE); @@ -3010,34 +2982,6 @@ findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, const dns_name_t *name, reactivate_node(rbtdb, node, locktype); - /* - * Always try to add the policy zone data, because this node might - * already have been implicitly created by the previous addition of - * a longer domain. A common example is adding *.example.com - * (implicitly creating example.com) followed by explicitly adding - * example.com. - */ - if (create && rbtdb->rpzs != NULL && tree == rbtdb->tree) { - dns_fixedname_t fnamef; - dns_name_t *fname; - - dns_fixedname_init(&fnamef); - fname = dns_fixedname_name(&fnamef); - dns_rbt_fullnamefromnode(node, fname); - result = dns_rpz_add(rbtdb->rpzs, rbtdb->rpz_num, fname); - if (result == ISC_R_SUCCESS) - node->rpz = 1; - if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) { - /* - * It is too late to give up, so merely complain. - */ - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, - DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, - "dns_rpz_add(): %s", - isc_result_totext(result)); - } - } - RWUNLOCK(&rbtdb->tree_lock, locktype); *nodep = (dns_dbnode_t *)node; @@ -4944,45 +4888,6 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, return (result); } -/* - * Connect this RBTDB to the response policy zone summary data for the view. - */ -static void -rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) { - dns_rbtdb_t * rbtdb; - - rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); - - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - REQUIRE(rbtdb->rpzs == NULL && rbtdb->rpz_num == DNS_RPZ_INVALID_NUM); - dns_rpz_attach_rpzs(rpzs, &rbtdb->rpzs); - rbtdb->rpz_num = rpz_num; - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); -} - -/* - * Enable this RBTDB as a response policy zone. - */ -static isc_result_t -rpz_ready(dns_db_t *db) { - dns_rbtdb_t * rbtdb; - isc_result_t result; - - rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); - - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - if (rbtdb->rpzs == NULL) { - INSIST(rbtdb->rpz_num == DNS_RPZ_INVALID_NUM); - result = ISC_R_SUCCESS; - } else { - result = dns_rpz_ready(rbtdb->rpzs, &rbtdb->load_rpzs, - rbtdb->rpz_num); - } - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - return (result); -} static isc_result_t cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, @@ -7127,35 +7032,10 @@ static isc_result_t loadnode(dns_rbtdb_t *rbtdb, const dns_name_t *name, dns_rbtnode_t **nodep, isc_boolean_t hasnsec) { - isc_result_t noderesult, rpzresult, nsecresult, tmpresult; + isc_result_t noderesult, nsecresult, tmpresult; dns_rbtnode_t *nsecnode = NULL, *node = NULL; noderesult = dns_rbt_addnode(rbtdb->tree, name, &node); - if (rbtdb->rpzs != NULL && - (noderesult == ISC_R_SUCCESS || noderesult == ISC_R_EXISTS)) { - rpzresult = dns_rpz_add(rbtdb->load_rpzs, rbtdb->rpz_num, - name); - if (rpzresult == ISC_R_SUCCESS) { - node->rpz = 1; - } else if (noderesult != ISC_R_EXISTS) { - /* - * Remove the node we just added above. - */ - tmpresult = dns_rbt_deletenode(rbtdb->tree, node, - ISC_FALSE); - if (tmpresult != ISC_R_SUCCESS) - isc_log_write(dns_lctx, - DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, - ISC_LOG_WARNING, - "loading_addrdataset: " - "dns_rbt_deletenode: %s after " - "dns_rbt_addnode(NSEC): %s", - isc_result_totext(tmpresult), - isc_result_totext(ISC_R_SUCCESS)); - noderesult = rpzresult; - } - } if (!hasnsec) goto done; if (noderesult == ISC_R_EXISTS) { @@ -7196,21 +7076,12 @@ loadnode(dns_rbtdb_t *rbtdb, const dns_name_t *name, dns_rbtnode_t **nodep, } if (noderesult == ISC_R_SUCCESS) { - unsigned int node_has_rpz; /* * Remove the node we just added above. */ - node_has_rpz = node->rpz; tmpresult = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); - if (tmpresult == ISC_R_SUCCESS) { - /* - * Clean rpz entries added above. - */ - if (rbtdb->rpzs != NULL && node_has_rpz) - dns_rpz_delete(rbtdb->load_rpzs, - rbtdb->rpz_num, name); - } else { + if (tmpresult != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, @@ -7541,18 +7412,6 @@ beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) { RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - if (rbtdb->rpzs != NULL) { - isc_result_t result; - - result = dns_rpz_beginload(&rbtdb->load_rpzs, - rbtdb->rpzs, rbtdb->rpz_num); - if (result != ISC_R_SUCCESS) { - isc_mem_put(rbtdb->common.mctx, loadctx, - sizeof(*loadctx)); - return (result); - } - } - REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING)) == 0); rbtdb->attributes |= RBTDB_ATTR_LOADING; @@ -8222,8 +8081,8 @@ static dns_dbmethods_t zone_methods = { resigned, isdnssec, NULL, - rpz_attach, - rpz_ready, + NULL, + NULL, NULL, NULL, NULL, @@ -8545,9 +8404,6 @@ dns_rbtdb_create } rbtdb->attributes = 0; rbtdb->task = NULL; - rbtdb->rpzs = NULL; - rbtdb->load_rpzs = NULL; - rbtdb->rpz_num = DNS_RPZ_INVALID_NUM; /* * Version Initialization. diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index 10c3f356f05..a5d539cb060 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -18,14 +18,18 @@ #include #include #include +#include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -72,6 +76,22 @@ * 5 labels all of which are numbers, and a prefix between 1 and 32. */ +/* + * Nodes hashtable calculation parameters + */ +#define DNS_RPZ_HTSIZE_MAX 24 +#define DNS_RPZ_HTSIZE_DIV 3 + +/* + * Maximum number of nodes to process per quantum + */ +#define DNS_RPZ_QUANTUM 1024 + +static void +dns_rpz_update_from_db(dns_rpz_zone_t *rpz); + +static void +dns_rpz_update_taskaction(isc_task_t *task, isc_event_t *event); /* * Use a private definition of IPv6 addresses because s6_addr32 is not @@ -1150,7 +1170,8 @@ search(dns_rpz_zones_t *rpzs, * The node lacked relevant data, * but will have it now. */ - cur->set.client_ip |= tgt_set->client_ip; + cur->set.client_ip |= + tgt_set->client_ip; cur->set.ip |= tgt_set->ip; cur->set.nsip |= tgt_set->nsip; set_sum_pair(cur); @@ -1378,54 +1399,576 @@ rpz_node_deleter(void *nm_data, void *mctx) { * Get ready for a new set of policy zones. */ isc_result_t -dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx) { - dns_rpz_zones_t *new; +dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx, + isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr) +{ + dns_rpz_zones_t *zones; isc_result_t result; REQUIRE(rpzsp != NULL && *rpzsp == NULL); - *rpzsp = NULL; + zones = isc_mem_get(mctx, sizeof(*zones)); + if (zones == NULL) + return (ISC_R_NOMEMORY); + memset(zones, 0, sizeof(*zones)); - new = isc_mem_get(mctx, sizeof(*new)); - if (new == NULL) + result = isc_rwlock_init(&zones->search_lock, 0, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_rwlock; + + result = isc_mutex_init(&zones->maint_lock); + if (result != ISC_R_SUCCESS) + goto cleanup_mutex; + + result = isc_refcount_init(&zones->refs, 1); + if (result != ISC_R_SUCCESS) + goto cleanup_refcount; + + result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &zones->rbt); + if (result != ISC_R_SUCCESS) + goto cleanup_rbt; + + result = isc_task_create(taskmgr, 0, &zones->updater); + if (result != ISC_R_SUCCESS) + goto cleanup_task; + + isc_mem_attach(mctx, &zones->mctx); + zones->timermgr = timermgr; + zones->taskmgr = taskmgr; + + *rpzsp = zones; + return (ISC_R_SUCCESS); + +cleanup_task: + dns_rbt_destroy(&zones->rbt); + +cleanup_rbt: + isc_refcount_decrement(&zones->refs, NULL); + isc_refcount_destroy(&zones->refs); + +cleanup_refcount: + DESTROYLOCK(&zones->maint_lock); + +cleanup_mutex: + isc_rwlock_destroy(&zones->search_lock); + +cleanup_rwlock: + isc_mem_put(mctx, zones, sizeof(*zones)); + + return (result); +} + +isc_result_t +dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp) { + dns_rpz_zone_t *zone; + isc_result_t result; + + REQUIRE(rpzp != NULL && *rpzp == NULL); + REQUIRE(rpzs != NULL); + if (rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES) { + return (ISC_R_NOSPACE); + } + + zone = isc_mem_get(rpzs->mctx, sizeof(*zone)); + if (zone == NULL) { return (ISC_R_NOMEMORY); - memset(new, 0, sizeof(*new)); + } + + memset(zone, 0, sizeof(*zone)); + result = isc_refcount_init(&zone->refs, 1); + if (result != ISC_R_SUCCESS) + goto cleanup_refcount; + + result = isc_timer_create(rpzs->timermgr, isc_timertype_inactive, + NULL, NULL, rpzs->updater, + dns_rpz_update_taskaction, + zone, &zone->updatetimer); + if (result != ISC_R_SUCCESS) + goto cleanup_timer; + + /* + * This will never be used, but costs us nothing and + * simplifies update_from_db + */ + + result = isc_ht_init(&zone->nodes, rpzs->mctx, 1); + if (result != ISC_R_SUCCESS) + goto cleanup_ht; + + dns_name_init(&zone->origin, NULL); + dns_name_init(&zone->client_ip, NULL); + dns_name_init(&zone->ip, NULL); + dns_name_init(&zone->nsdname, NULL); + dns_name_init(&zone->nsip, NULL); + dns_name_init(&zone->passthru, NULL); + dns_name_init(&zone->drop, NULL); + dns_name_init(&zone->tcp_only, NULL); + dns_name_init(&zone->cname, NULL); + + isc_time_settoepoch(&zone->lastupdated); + zone->updatepending = ISC_FALSE; + zone->updaterunning = ISC_FALSE; + zone->db = NULL; + zone->dbversion = NULL; + zone->updb = NULL; + zone->updbversion = NULL; + zone->updbit = NULL; + zone->rpzs = rpzs; + zone->db_registered = ISC_FALSE; + + zone->num = rpzs->p.num_zones++; + rpzs->zones[zone->num] = zone; + + *rpzp = zone; + + return (ISC_R_SUCCESS); + +cleanup_ht: + isc_timer_detach(&zone->updatetimer); + +cleanup_timer: + isc_refcount_decrement(&zone->refs, NULL); + isc_refcount_destroy(&zone->refs); + +cleanup_refcount: + isc_mem_put(zone->rpzs->mctx, zone, sizeof(*zone)); + + return (result); +} + +isc_result_t +dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg) { + dns_rpz_zone_t *zone = (dns_rpz_zone_t *) fn_arg; + isc_time_t now; + isc_uint64_t tdiff; + isc_result_t result = ISC_R_SUCCESS; + char dname[DNS_NAME_FORMATSIZE]; + + REQUIRE(DNS_DB_VALID(db)); + REQUIRE(zone != NULL); + + LOCK(&zone->rpzs->maint_lock); + REQUIRE(zone->db_registered); + + + /* New zone came as AXFR */ + if (zone->db != NULL && zone->db != db) { + /* We need to clean up the old DB */ + if (zone->dbversion != NULL) + dns_db_closeversion(zone->db, &zone->dbversion, + ISC_FALSE); + dns_db_updatenotify_unregister(zone->db, + dns_rpz_dbupdate_callback, + zone); + dns_db_detach(&zone->db); + } + + if (zone->db == NULL) { + RUNTIME_CHECK(zone->dbversion == NULL); + dns_db_attach(db, &zone->db); + } + + if (!zone->updatepending && !zone->updaterunning) { + zone->updatepending = ISC_TRUE; + isc_time_now(&now); + tdiff = isc_time_microdiff(&now, &zone->lastupdated) / 1000000; + if (tdiff < zone->min_update_int) { + isc_uint64_t defer = zone->min_update_int - tdiff; + isc_interval_t interval; + dns_name_format(&zone->origin, dname, + DNS_NAME_FORMATSIZE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_INFO, + "rpz: %s: new zone version came " + "too soon, deferring update for " + "%llu seconds", dname, defer); + isc_interval_set(&interval, defer, 0); + dns_db_currentversion(zone->db, &zone->dbversion); + result = isc_timer_reset(zone->updatetimer, + isc_timertype_once, + NULL, &interval, ISC_TRUE); + if (result != ISC_R_SUCCESS) + goto cleanup; + } else { + isc_event_t *event; + + dns_db_currentversion(zone->db, &zone->dbversion); + ISC_EVENT_INIT(&zone->updateevent, + sizeof(zone->updateevent), 0, NULL, + DNS_EVENT_RPZUPDATED, + dns_rpz_update_taskaction, + zone, zone, NULL, NULL); + event = &zone->updateevent; + isc_task_send(zone->rpzs->updater, &event); + } + } else { + zone->updatepending = ISC_TRUE; + dns_name_format(&zone->origin, dname, DNS_NAME_FORMATSIZE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3), + "rpz: %s: update already queued or running", dname); + if (zone->dbversion != NULL) + dns_db_closeversion(zone->db, &zone->dbversion, + ISC_FALSE); + dns_db_currentversion(zone->db, &zone->dbversion); + } + + cleanup: + UNLOCK(&zone->rpzs->maint_lock); + + return (result); +} + +static void +dns_rpz_update_taskaction(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_rpz_zone_t *zone; + + REQUIRE(event != NULL); + REQUIRE(event->ev_arg != NULL); + + UNUSED(task); + zone = (dns_rpz_zone_t *) event->ev_arg; + LOCK(&zone->rpzs->maint_lock); + zone->updatepending = ISC_FALSE; + zone->updaterunning = ISC_TRUE; + dns_rpz_update_from_db(zone); + result = isc_timer_reset(zone->updatetimer, isc_timertype_inactive, + NULL, NULL, ISC_TRUE); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + result = isc_time_now(&zone->lastupdated); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + UNLOCK(&zone->rpzs->maint_lock); + isc_event_free(&event); +} + +static isc_result_t +setup_update(dns_rpz_zone_t *rpz) { + isc_result_t result; + char domain[DNS_NAME_FORMATSIZE]; + unsigned int nodecount; + uint32_t hashsize; + + dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_INFO, + "rpz: %s: reload start", domain); + + nodecount = dns_db_nodecount(rpz->updb); + hashsize = 1; + while (nodecount != 0 && + hashsize <= (DNS_RPZ_HTSIZE_MAX + DNS_RPZ_HTSIZE_DIV)) + { + hashsize++; + nodecount >>=1; + } + + if (hashsize > DNS_RPZ_HTSIZE_DIV) + hashsize -= DNS_RPZ_HTSIZE_DIV; + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_INFO, + "rpz: %s: using hashtable size %d", + domain, hashsize); - result = isc_rwlock_init(&new->search_lock, 0, 0); + result = isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize); if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, new, sizeof(*new)); - return (result); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: failed to initialize hashtable - %s", + domain, isc_result_totext(result)); + goto cleanup; } - result = isc_mutex_init(&new->maint_lock); + result = dns_db_createiterator(rpz->updb, DNS_DB_NONSEC3, &rpz->updbit); if (result != ISC_R_SUCCESS) { - isc_rwlock_destroy(&new->search_lock); - isc_mem_put(mctx, new, sizeof(*new)); - return (result); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: failed to create DB iterator - %s", + domain, isc_result_totext(result)); + goto cleanup; } - result = isc_refcount_init(&new->refs, 1); + result = dns_dbiterator_first(rpz->updbit); if (result != ISC_R_SUCCESS) { - DESTROYLOCK(&new->maint_lock); - isc_rwlock_destroy(&new->search_lock); - isc_mem_put(mctx, new, sizeof(*new)); - return (result); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: failed to get db iterator - %s", + domain, isc_result_totext(result)); + goto cleanup; } - result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &new->rbt); + cleanup: if (result != ISC_R_SUCCESS) { - isc_refcount_decrement(&new->refs, NULL); - isc_refcount_destroy(&new->refs); - DESTROYLOCK(&new->maint_lock); - isc_rwlock_destroy(&new->search_lock); - isc_mem_put(mctx, new, sizeof(*new)); - return (result); + if (rpz->updbit != NULL) + dns_dbiterator_destroy(&rpz->updbit); + if (rpz->newnodes != NULL) + isc_ht_destroy(&rpz->newnodes); + dns_db_closeversion(rpz->updb, &rpz->updbversion, ISC_FALSE); } - isc_mem_attach(mctx, &new->mctx); + return (result); +} - *rpzsp = new; - return (ISC_R_SUCCESS); +static void +finish_update(dns_rpz_zone_t *rpz) { + isc_result_t result; + isc_ht_t *tmpht = NULL; + isc_ht_iter_t *iter = NULL; + dns_fixedname_t fname; + char dname[DNS_NAME_FORMATSIZE]; + dns_name_t *name; + + /* + * Iterate over old ht with existing nodes deleted to delete + * deleted nodes from RPZ + */ + result = isc_ht_iter_create(rpz->nodes, &iter); + if (result != ISC_R_SUCCESS) { + char domain[DNS_NAME_FORMATSIZE]; + + dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: failed to create HT iterator - %s", + domain, isc_result_totext(result)); + goto cleanup; + } + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(iter)) + { + isc_region_t region; + unsigned char *key; + size_t keysize; + + isc_ht_iter_currentkey(iter, &key, &keysize); + region.base = key; + region.length = keysize; + dns_name_fromregion(name, ®ion); + dns_rpz_delete(rpz->rpzs, rpz->num, name); + } + + tmpht = rpz->nodes; + rpz->nodes = rpz->newnodes; + rpz->newnodes = tmpht; + + LOCK(&rpz->rpzs->maint_lock); + rpz->updaterunning = ISC_FALSE; + /* + * If there's an update pending schedule it + */ + if (rpz->updatepending == ISC_TRUE) { + isc_uint64_t defer = rpz->min_update_int; + isc_interval_t interval; + dns_name_format(&rpz->origin, dname, + DNS_NAME_FORMATSIZE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_INFO, + "rpz: %s: new zone version came " + "too soon, deferring update for " + "%llu seconds", dname, defer); + isc_interval_set(&interval, defer, 0); + result = isc_timer_reset(rpz->updatetimer, + isc_timertype_once, + NULL, &interval, ISC_TRUE); + } + UNLOCK(&rpz->rpzs->maint_lock); + +cleanup: + if (iter != NULL) + isc_ht_iter_destroy(&iter); +} + +static void +update_quantum(isc_task_t *task, isc_event_t *event) { + isc_result_t result = ISC_R_SUCCESS; + dns_dbnode_t *node = NULL; + dns_rpz_zone_t *rpz; + char domain[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixname; + dns_name_t *name; + int count = 0; + + UNUSED(task); + + REQUIRE(event != NULL); + REQUIRE(event->ev_arg != NULL); + + rpz = (dns_rpz_zone_t *) event->ev_arg; + isc_event_free(&event); + + REQUIRE(rpz->updbit != NULL); + REQUIRE(rpz->newnodes != NULL); + + dns_fixedname_init(&fixname); + name = dns_fixedname_name(&fixname); + + dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE); + + while (result == ISC_R_SUCCESS && count++ < DNS_RPZ_QUANTUM) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_rdatasetiter_t *rdsiter = NULL; + + result = dns_dbiterator_current(rpz->updbit, &node, name); + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: failed to get dbiterator - %s", + domain, isc_result_totext(result)); + dns_db_detachnode(rpz->updb, &node); + break; + } + + result = dns_db_allrdatasets(rpz->updb, node, rpz->updbversion, 0, + &rdsiter); + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: failed to fetch " + "rrdatasets - %s", + domain, isc_result_totext(result)); + dns_db_detachnode(rpz->updb, &node); + break; + } + + result = dns_rdatasetiter_first(rdsiter); + dns_rdatasetiter_destroy(&rdsiter); + if (result != ISC_R_SUCCESS) { /* empty non-terminal */ + if (result != ISC_R_NOMORE) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: error %s while creating " + "rdatasetiter", + domain, isc_result_totext(result)); + dns_db_detachnode(rpz->updb, &node); + result = dns_dbiterator_next(rpz->updbit); + continue; + } + + result = isc_ht_add(rpz->newnodes, name->ndata, + name->length, rpz); + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s, adding node %s to HT error %s", + domain, namebuf, + isc_result_totext(result)); + dns_db_detachnode(rpz->updb, &node); + result = dns_dbiterator_next(rpz->updbit); + continue; + } + + result = isc_ht_find(rpz->nodes, name->ndata, + name->length, NULL); + if (result == ISC_R_SUCCESS) { + isc_ht_delete(rpz->nodes, name->ndata, name->length); + } else { /* not found */ + result = dns_rpz_add(rpz->rpzs, rpz->num, name); + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, + ISC_LOG_ERROR, + "rpz: %s: adding node %s " + "to RPZ error %s", + domain, namebuf, + isc_result_totext(result)); + } else { + dns_name_format(name, namebuf, sizeof(namebuf)); + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, + ISC_LOG_DEBUG(3), + "rpz: %s: adding node %s", + domain, namebuf); + } + } + + dns_db_detachnode(rpz->updb, &node); + result = dns_dbiterator_next(rpz->updbit); + } + + if (result == ISC_R_SUCCESS) { + isc_event_t *nevent; + /* + * Pause the iterator so that the DB is not locked + */ + dns_dbiterator_pause(rpz->updbit); + /* + * We finished a quantum; trigger the next one and return + */ + ISC_EVENT_INIT(&rpz->updateevent, + sizeof(rpz->updateevent), 0, NULL, + DNS_EVENT_RPZUPDATED, + update_quantum, + rpz, rpz, NULL, NULL); + nevent = &rpz->updateevent; + isc_task_send(rpz->rpzs->updater, &nevent); + return; + } else if (result == ISC_R_NOMORE) { + /* + * All done. + */ + finish_update(rpz); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_INFO, + "rpz: %s: reload done", domain); + } + + /* + * If we're here, we've either finished or something went wrong, + * so clean up. + */ + if (rpz->updbit != NULL) + dns_dbiterator_destroy(&rpz->updbit); + if (rpz->newnodes != NULL) + isc_ht_destroy(&rpz->newnodes); + dns_db_closeversion(rpz->updb, &rpz->updbversion, ISC_FALSE); + dns_db_detach(&rpz->updb); +} + +static void +dns_rpz_update_from_db(dns_rpz_zone_t *rpz) { + isc_result_t result; + isc_event_t *event; + + REQUIRE(rpz != NULL); + REQUIRE(DNS_DB_VALID(rpz->db)); + REQUIRE(rpz->updb == NULL); + REQUIRE(rpz->updbversion == NULL); + REQUIRE(rpz->updbit == NULL); + REQUIRE(rpz->newnodes == NULL); + + dns_db_attach(rpz->db, &rpz->updb); + rpz->updbversion = rpz->dbversion; + rpz->dbversion = NULL; + + result = setup_update(rpz); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + event = &rpz->updateevent; + ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), + 0, NULL, DNS_EVENT_RPZUPDATED, + update_quantum, rpz, rpz, NULL, NULL); + isc_task_send(rpz->rpzs->updater, &event); + return; + + cleanup: + if (rpz->updbit != NULL) + dns_dbiterator_destroy(&rpz->updbit); + if (rpz->newnodes != NULL) + isc_ht_destroy(&rpz->newnodes); + dns_db_closeversion(rpz->updb, &rpz->updbversion, ISC_FALSE); + dns_db_detach(&rpz->updb); } /* @@ -1494,6 +2037,16 @@ rpz_detach(dns_rpz_zone_t **rpzp, dns_rpz_zones_t *rpzs) { dns_name_free(&rpz->tcp_only, rpzs->mctx); if (dns_name_dynamic(&rpz->cname)) dns_name_free(&rpz->cname, rpzs->mctx); + if (rpz->db_registered) + dns_db_updatenotify_unregister(rpz->db, + dns_rpz_dbupdate_callback, rpz); + if (rpz->dbversion != NULL) + dns_db_closeversion(rpz->db, &rpz->dbversion, + ISC_FALSE); + if (rpz->db) + dns_db_detach(&rpz->db); + isc_ht_destroy(&rpz->nodes); + isc_timer_detach(&rpz->updatetimer); isc_mem_put(rpzs->mctx, rpz, sizeof(*rpz)); } @@ -1538,345 +2091,37 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) { DESTROYLOCK(&rpzs->maint_lock); isc_rwlock_destroy(&rpzs->search_lock); isc_refcount_destroy(&rpzs->refs); + isc_task_destroy(&rpzs->updater); isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs)); } } /* - * Create empty summary database to load one zone. - * The RBTDB write tree lock must be held. + * Deprecated and removed. */ isc_result_t dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) { - dns_rpz_zones_t *load_rpzs; - dns_rpz_zone_t *rpz; - dns_rpz_zbits_t tgt; - isc_result_t result; - - REQUIRE(rpz_num < rpzs->p.num_zones); - rpz = rpzs->zones[rpz_num]; - REQUIRE(rpz != NULL); - - /* - * When reloading a zone, there are usually records among the summary - * data for the zone. Some of those records might be deleted by the - * reloaded zone data. To deal with that case: - * reload the new zone data into a new blank summary database - * if the reload fails, discard the new summary database - * if the new zone data is acceptable, copy the records for the - * other zones into the new summary CIDR and RBT databases - * and replace the old summary databases with the new, and - * correct the triggers and have values for the updated - * zone. - * - * At the first attempt to load a zone, there is no summary data - * for the zone and so no records that need to be deleted. - * This is also the most common case of policy zone loading. - * Most policy zone maintenance should be by incremental changes - * and so by the addition and deletion of individual records. - * Detect that case and load records the first time into the - * operational summary database - */ - tgt = DNS_RPZ_ZBIT(rpz_num); - LOCK(&rpzs->maint_lock); - RWLOCK(&rpzs->search_lock, isc_rwlocktype_write); - if ((rpzs->load_begun & tgt) == 0) { - /* - * There is no existing version of the target zone. - */ - rpzs->load_begun |= tgt; - dns_rpz_attach_rpzs(rpzs, load_rpzsp); - } else { - /* - * Setup the new RPZ struct with empty summary trees. - */ - result = dns_rpz_new_zones(load_rpzsp, rpzs->mctx); - if (result != ISC_R_SUCCESS) - return (result); - load_rpzs = *load_rpzsp; - /* - * Initialize some members so that dns_rpz_add() works. - */ - load_rpzs->p.num_zones = rpzs->p.num_zones; - memset(&load_rpzs->triggers, 0, sizeof(load_rpzs->triggers)); - load_rpzs->zones[rpz_num] = rpz; - isc_refcount_increment(&rpz->refs, NULL); - } - - RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write); - UNLOCK(&rpzs->maint_lock); + UNUSED(load_rpzsp); + UNUSED(rpzs); + UNUSED(rpz_num); - return (ISC_R_SUCCESS); + return (ISC_R_NOTIMPLEMENTED); } /* - * This function updates "have" bits and also the qname_skip_recurse - * mask. It must be called when holding a write lock on rpzs->search_lock. - */ -static void -fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) { - dns_rpz_num_t n; - dns_rpz_triggers_t old_totals; - dns_rpz_zbits_t zbit; - char namebuf[DNS_NAME_FORMATSIZE]; - - /* - * rpzs->total_triggers is only used to log a message below. - */ - - memmove(&old_totals, &rpzs->total_triggers, sizeof(old_totals)); - memset(&rpzs->total_triggers, 0, sizeof(rpzs->total_triggers)); - -#define SET_TRIG(n, zbit, type) \ - if (rpzs->triggers[n].type == 0U) { \ - rpzs->have.type &= ~zbit; \ - } else { \ - rpzs->total_triggers.type += rpzs->triggers[n].type; \ - rpzs->have.type |= zbit; \ - } - - for (n = 0; n < rpzs->p.num_zones; ++n) { - zbit = DNS_RPZ_ZBIT(n); - SET_TRIG(n, zbit, client_ipv4); - SET_TRIG(n, zbit, client_ipv6); - SET_TRIG(n, zbit, qname); - SET_TRIG(n, zbit, ipv4); - SET_TRIG(n, zbit, ipv6); - SET_TRIG(n, zbit, nsdname); - SET_TRIG(n, zbit, nsipv4); - SET_TRIG(n, zbit, nsipv6); - } - -#undef SET_TRIG - - fix_qname_skip_recurse(rpzs); - - dns_name_format(&rpzs->zones[rpz_num]->origin, - namebuf, sizeof(namebuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, - DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL, - "(re)loading policy zone '%s' changed from" - " %lu to %lu qname, %lu to %lu nsdname," - " %lu to %lu IP, %lu to %lu NSIP," - " %lu to %lu CLIENTIP entries", - namebuf, - (unsigned long) old_totals.qname, - (unsigned long) rpzs->total_triggers.qname, - (unsigned long) old_totals.nsdname, - (unsigned long) rpzs->total_triggers.nsdname, - (unsigned long) old_totals.ipv4 + old_totals.ipv6, - (unsigned long) (rpzs->total_triggers.ipv4 + - rpzs->total_triggers.ipv6), - (unsigned long) old_totals.nsipv4 + old_totals.nsipv6, - (unsigned long) (rpzs->total_triggers.nsipv4 + - rpzs->total_triggers.nsipv6), - (unsigned long) old_totals.client_ipv4 + - old_totals.client_ipv6, - (unsigned long) (rpzs->total_triggers.client_ipv4 + - rpzs->total_triggers.client_ipv6)); -} - -/* - * Finish loading one zone. This function is called during a commit when - * a RPZ zone loading is complete. The RBTDB write tree lock must be - * held. - * - * Here, rpzs is a pointer to the view's common rpzs - * structure. *load_rpzsp is a rpzs structure that is local to the - * RBTDB, which is used during a single zone's load. - * - * During the zone load, i.e., between dns_rpz_beginload() and - * dns_rpz_ready(), only the zone that is being loaded updates - * *load_rpzsp. These updates in the summary databases inside load_rpzsp - * are made only for the rpz_num (and corresponding bit) of that - * zone. Nothing else reads or writes *load_rpzsp. The view's common - * rpzs is used during this time for queries. - * - * When zone loading is complete and we arrive here, the parts of the - * summary databases (CIDR and nsdname+qname RBT trees) from the view's - * common rpzs struct have to be merged into the summary databases of - * *load_rpzsp, as the summary databases of the view's common rpzs - * struct may have changed during the time the zone was being loaded. - * - * The function below carries out the merge. During the merge, it holds - * the maint_lock of the view's common rpzs struct so that it is not - * updated while the merging is taking place. - * - * After the merging is carried out, *load_rpzsp contains the most - * current state of the rpzs structure, i.e., the summary trees contain - * data for the new zone that was just loaded, as well as all other - * zones. - * - * Pointers to the summary databases of *load_rpzsp (CIDR and - * nsdname+qname RBT trees) are then swapped into the view's common rpz - * struct, so that the query path can continue using it. During the - * swap, the search_lock of the view's common rpz struct is acquired so - * that queries are paused while this swap occurs. - * - * The trigger counts for the new zone are also copied into the view's - * common rpz struct, and some other summary counts and masks are - * updated. + * Deprecated and removed. */ isc_result_t dns_rpz_ready(dns_rpz_zones_t *rpzs, dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num) { - dns_rpz_zones_t *load_rpzs; - const dns_rpz_cidr_node_t *cnode, *next_cnode, *parent_cnode; - dns_rpz_cidr_node_t *found; - dns_rpz_zbits_t new_bit; - dns_rpz_addr_zbits_t new_ip; - dns_rbt_t *rbt; - dns_rbtnodechain_t chain; - dns_rbtnode_t *nmnode; - dns_rpz_nm_data_t *nm_data, new_data; - dns_fixedname_t labelf, originf, namef; - dns_name_t *label, *origin, *name; - isc_result_t result; - - INSIST(rpzs != NULL); - LOCK(&rpzs->maint_lock); - load_rpzs = *load_rpzsp; - INSIST(load_rpzs != NULL); - - if (load_rpzs == rpzs) { - /* - * This is a successful initial zone loading, perhaps - * for a new instance of a view. - */ - RWLOCK(&rpzs->search_lock, isc_rwlocktype_write); - fix_triggers(rpzs, rpz_num); - RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write); - UNLOCK(&rpzs->maint_lock); - dns_rpz_detach_rpzs(load_rpzsp); - return (ISC_R_SUCCESS); - } - - LOCK(&load_rpzs->maint_lock); - RWLOCK(&load_rpzs->search_lock, isc_rwlocktype_write); + UNUSED(rpzs); + UNUSED(load_rpzsp); + UNUSED(rpz_num); - /* - * Unless there is only one policy zone, copy the other policy zones - * from the old policy structure to the new summary databases. - */ - if (rpzs->p.num_zones > 1) { - new_bit = ~DNS_RPZ_ZBIT(rpz_num); - - /* - * Copy to the radix tree. - */ - for (cnode = rpzs->cidr; cnode != NULL; cnode = next_cnode) { - new_ip.ip = cnode->set.ip & new_bit; - new_ip.client_ip = cnode->set.client_ip & new_bit; - new_ip.nsip = cnode->set.nsip & new_bit; - if (new_ip.client_ip != 0 || - new_ip.ip != 0 || - new_ip.nsip != 0) { - result = search(load_rpzs, - &cnode->ip, cnode->prefix, - &new_ip, ISC_TRUE, &found); - if (result == ISC_R_NOMEMORY) - goto unlock_and_detach; - INSIST(result == ISC_R_SUCCESS); - } - /* - * Do down and to the left as far as possible. - */ - next_cnode = cnode->child[0]; - if (next_cnode != NULL) - continue; - /* - * Go up until we find a branch to the right where - * we previously took the branch to the left. - */ - for (;;) { - parent_cnode = cnode->parent; - if (parent_cnode == NULL) - break; - if (parent_cnode->child[0] == cnode) { - next_cnode = parent_cnode->child[1]; - if (next_cnode != NULL) - break; - } - cnode = parent_cnode; - } - } - - /* - * Copy to the summary RBT. - */ - dns_fixedname_init(&namef); - name = dns_fixedname_name(&namef); - dns_fixedname_init(&labelf); - label = dns_fixedname_name(&labelf); - dns_fixedname_init(&originf); - origin = dns_fixedname_name(&originf); - dns_rbtnodechain_init(&chain, NULL); - result = dns_rbtnodechain_first(&chain, rpzs->rbt, NULL, NULL); - while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { - result = dns_rbtnodechain_current(&chain, label, origin, - &nmnode); - INSIST(result == ISC_R_SUCCESS); - nm_data = nmnode->data; - if (nm_data != NULL) { - new_data.set.qname = (nm_data->set.qname & - new_bit); - new_data.set.ns = nm_data->set.ns & new_bit; - new_data.wild.qname = (nm_data->wild.qname & - new_bit); - new_data.wild.ns = nm_data->wild.ns & new_bit; - if (new_data.set.qname != 0 || - new_data.set.ns != 0 || - new_data.wild.qname != 0 || - new_data.wild.ns != 0) { - result = dns_name_concatenate(label, - origin, name, NULL); - INSIST(result == ISC_R_SUCCESS); - result = add_nm(load_rpzs, name, - &new_data); - if (result != ISC_R_SUCCESS) - goto unlock_and_detach; - } - } - result = dns_rbtnodechain_next(&chain, NULL, NULL); - } - if (result != ISC_R_NOMORE && result != ISC_R_NOTFOUND) { - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, - DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, - "dns_rpz_ready(): unexpected %s", - isc_result_totext(result)); - goto unlock_and_detach; - } - } - - /* - * Exchange the summary databases. - */ - RWLOCK(&rpzs->search_lock, isc_rwlocktype_write); - - rpzs->triggers[rpz_num] = load_rpzs->triggers[rpz_num]; - fix_triggers(rpzs, rpz_num); - - found = rpzs->cidr; - rpzs->cidr = load_rpzs->cidr; - load_rpzs->cidr = found; - - rbt = rpzs->rbt; - rpzs->rbt = load_rpzs->rbt; - load_rpzs->rbt = rbt; - - RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write); - - result = ISC_R_SUCCESS; - - unlock_and_detach: - UNLOCK(&rpzs->maint_lock); - RWUNLOCK(&load_rpzs->search_lock, isc_rwlocktype_write); - UNLOCK(&load_rpzs->maint_lock); - dns_rpz_detach_rpzs(load_rpzsp); - return (result); + return (ISC_R_NOTIMPLEMENTED); } /* @@ -1893,11 +2138,10 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones); rpz = rpzs->zones[rpz_num]; REQUIRE(rpz != NULL); + RWLOCK(&rpzs->search_lock, isc_rwlocktype_write); rpz_type = type_from_name(rpz, src_name); - LOCK(&rpzs->maint_lock); - RWLOCK(&rpzs->search_lock, isc_rwlocktype_write); switch (rpz_type) { case DNS_RPZ_TYPE_QNAME: @@ -1912,9 +2156,8 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, case DNS_RPZ_TYPE_BAD: break; } - RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write); - UNLOCK(&rpzs->maint_lock); + return (result); } @@ -1966,7 +2209,8 @@ del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, tgt->set.nsip &= ~tgt_set.nsip; set_sum_pair(tgt); - adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_FALSE); + adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, + ISC_FALSE); /* * We might need to delete 2 nodes. @@ -2071,7 +2315,8 @@ del_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, result = dns_rbt_deletenode(rpzs->rbt, nmnode, ISC_FALSE); if (result != ISC_R_SUCCESS) { /* - * bin/tests/system/rpz/tests.sh looks for "rpz.*failed". + * bin/tests/system/rpz/tests.sh looks for + * "rpz.*failed". */ dns_name_format(src_name, namebuf, sizeof(namebuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, @@ -2098,11 +2343,10 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, rpz = rpzs->zones[rpz_num]; REQUIRE(rpz != NULL); - rpz_type = type_from_name(rpz, src_name); - - LOCK(&rpzs->maint_lock); RWLOCK(&rpzs->search_lock, isc_rwlocktype_write); + rpz_type = type_from_name(rpz, src_name); + switch (rpz_type) { case DNS_RPZ_TYPE_QNAME: case DNS_RPZ_TYPE_NSDNAME: @@ -2118,7 +2362,6 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, } RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write); - UNLOCK(&rpzs->maint_lock); } /* @@ -2142,9 +2385,9 @@ dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type, dns_rpz_have_t have; int i; - LOCK(&rpzs->maint_lock); + RWLOCK(&rpzs->search_lock, isc_rwlocktype_read); have = rpzs->have; - UNLOCK(&rpzs->maint_lock); + RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read); /* * Convert IP address to CIDR tree key. diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 98040f2bb2c..e7cbb6fa53d 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -960,11 +960,13 @@ dns_rootns_create dns_rpz_add dns_rpz_attach_rpzs dns_rpz_beginload +dns_rpz_dbupdate_callback dns_rpz_decode_cname dns_rpz_delete dns_rpz_detach_rpzs dns_rpz_find_ip dns_rpz_find_name +dns_rpz_new_zone dns_rpz_new_zones dns_rpz_policy2str dns_rpz_ready diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 39f4a636aba..e503039b812 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -1752,10 +1752,15 @@ dns_zone_get_rpz_num(dns_zone_t *zone) { */ void dns_zone_rpz_enable_db(dns_zone_t *zone, dns_db_t *db) { - if (zone->rpz_num != DNS_RPZ_INVALID_NUM) { - REQUIRE(zone->rpzs != NULL); - dns_db_rpz_attach(db, zone->rpzs, zone->rpz_num); - } + isc_result_t result; + if (zone->rpz_num == DNS_RPZ_INVALID_NUM) + return; + REQUIRE(zone->rpzs != NULL); + zone->rpzs->zones[zone->rpz_num]->db_registered = ISC_TRUE; + result = dns_db_updatenotify_register(db, + dns_rpz_dbupdate_callback, + zone->rpzs->zones[zone->rpz_num]); + REQUIRE(result == ISC_R_SUCCESS); } void @@ -4619,9 +4624,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, if (result != ISC_R_SUCCESS) goto cleanup; } else { - result = dns_db_rpz_ready(db); - if (result != ISC_R_SUCCESS) - goto cleanup; zone_attachdb(zone, db); ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); DNS_ZONE_SETFLAG(zone, @@ -14471,10 +14473,6 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { if (inline_raw(zone)) REQUIRE(LOCKED_ZONE(zone->secure)); - result = dns_db_rpz_ready(db); - if (result != ISC_R_SUCCESS) - return (result); - result = zone_get_from_db(zone, db, &nscount, &soacount, NULL, NULL, NULL, NULL, NULL, NULL); if (result == ISC_R_SUCCESS) { diff --git a/lib/isc/ht.c b/lib/isc/ht.c index e68dca41126..536f04fe6b0 100644 --- a/lib/isc/ht.c +++ b/lib/isc/ht.c @@ -157,14 +157,15 @@ isc_ht_find(const isc_ht_t *ht, const unsigned char *key, REQUIRE(ISC_HT_VALID(ht)); REQUIRE(key != NULL && keysize > 0); - REQUIRE(valuep != NULL); hash = isc_hash_function(key, keysize, ISC_TRUE, NULL); node = ht->table[hash & ht->mask]; while (node != NULL) { if (keysize == node->keysize && - memcmp(key, node->key, keysize) == 0) { - *valuep = node->value; + memcmp(key, node->key, keysize) == 0) + { + if (valuep != NULL) + *valuep = node->value; return (ISC_R_SUCCESS); } node = node->next; diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h index 25819b8ed53..1a6f44f2635 100644 --- a/lib/isc/include/isc/ht.h +++ b/lib/isc/include/isc/ht.h @@ -61,7 +61,9 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize, /*% * Find a node matching 'key'/'keysize' in hashtable 'ht'; - * if found, set 'value' to its value + * if found, set '*valuep' to its value. (If 'valuep' is NULL, + * then simply return SUCCESS or NOTFOUND to indicate whether the + * key exists in the hashtable.) * * Requires: * \li 'ht' is a valid hashtable diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index ca8342305ef..badbcf9af72 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1401,8 +1401,9 @@ static cfg_type_t cfg_type_dnstapoutput = { * zone [ policy (given|disabled|passthru|drop|tcp-only| * nxdomain|nodata|cname ) ] * [ recursive-only yes|no ] [ log yes|no ] - * [ max-policy-ttl number ] ; + * [ max-policy-ttl number ] [ min-update-interval number ] ; * } [ recursive-only yes|no ] [ max-policy-ttl number ] + * [ min-update-interval number ] * [ break-dnssec yes|no ] [ min-ns-dots number ] * [ qname-wait-recurse yes|no ] ; */ @@ -1592,6 +1593,7 @@ static cfg_tuplefielddef_t rpz_zone_fields[] = { { "zone name", &cfg_type_rpz_zone, 0 }, { "log", &cfg_type_boolean, 0 }, { "max-policy-ttl", &cfg_type_uint32, 0 }, + { "min-update-interval", &cfg_type_uint32, 0 }, { "policy", &cfg_type_rpz_policy, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } @@ -1610,6 +1612,7 @@ static cfg_tuplefielddef_t rpz_fields[] = { { "zone list", &cfg_type_rpz_list, 0 }, { "break-dnssec", &cfg_type_boolean, 0 }, { "max-policy-ttl", &cfg_type_uint32, 0 }, + { "min-update-interval", &cfg_type_uint32, 0 }, { "min-ns-dots", &cfg_type_uint32, 0 }, { "nsip-wait-recurse", &cfg_type_boolean, 0 }, { "qname-wait-recurse", &cfg_type_boolean, 0 },