/* $NetBSD: dnsrps.c,v 1.13 2025/01/26 16:25:22 christos Exp $ */ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include #ifdef USE_DNSRPS #include #include #include #include #include #define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN #include #include #include #include #include librpz_t *librpz = NULL; librpz_emsg_t librpz_lib_open_emsg; static void *librpz_handle = NULL; #define RPSDB_MAGIC ISC_MAGIC('R', 'P', 'Z', 'F') #define VALID_RPSDB(rpsdb) ((rpsdb)->common.impmagic == RPSDB_MAGIC) #define RD_DB(r) ((r)->rps.db) #define RD_CUR_RR(r) ((r)->rps.iter_pos) #define RD_NEXT_RR(r) ((r)->resign) #define RD_COUNT(r) ((r)->rps.iter_count) typedef struct { dns_rdatasetiter_t common; dns_rdatatype_t type; dns_rdataclass_t class; uint32_t ttl; uint count; librpz_idx_t next_rr; } rpsdb_rdatasetiter_t; static dns_dbmethods_t rpsdb_db_methods; static dns_rdatasetmethods_t rpsdb_rdataset_methods; static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods; static librpz_clist_t *clist; static isc_mutex_t dnsrps_mutex; static void dnsrps_lock(void *mutex0) { isc_mutex_t *mutex = mutex0; LOCK(mutex); } static void dnsrps_unlock(void *mutex0) { isc_mutex_t *mutex = mutex0; UNLOCK(mutex); } static void dnsrps_mutex_destroy(void *mutex0) { isc_mutex_t *mutex = mutex0; isc_mutex_destroy(mutex); } static void dnsrps_log_fnc(librpz_log_level_t level, void *ctxt, const char *buf) { int isc_level; UNUSED(ctxt); /* Setting librpz_log_level in the configuration overrides the * BIND9 logging levels. */ if (level > LIBRPZ_LOG_TRACE1 && level <= librpz->log_level_val(LIBRPZ_LOG_INVALID)) { level = LIBRPZ_LOG_TRACE1; } switch (level) { case LIBRPZ_LOG_TRACE1: /* big events such as dnsrpzd starts */ isc_level = DNS_RPZ_INFO_LEVEL; break; case LIBRPZ_LOG_TRACE2: /* smaller dnsrpzd zone transfers */ isc_level = DNS_RPZ_DEBUG_LEVEL1; break; case LIBRPZ_LOG_TRACE3: /* librpz hits */ isc_level = DNS_RPZ_DEBUG_LEVEL2; break; case LIBRPZ_LOG_TRACE4: /* librpz lookups */ isc_level = DNS_RPZ_DEBUG_LEVEL3; break; case LIBRPZ_LOG_FATAL: case LIBRPZ_LOG_ERROR: /* errors */ default: isc_level = DNS_RPZ_ERROR_LEVEL; break; } isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, isc_level, "dnsrps: %s", buf); } /* * Start dnsrps for the entire server. * This is not thread safe, but it is called by a single thread. */ isc_result_t dns_dnsrps_server_create(const char *librpz_path) { librpz_emsg_t emsg; INSIST(clist == NULL); INSIST(librpz == NULL); INSIST(librpz_handle == NULL); /* * Notice if librpz is available. */ librpz = librpz_lib_open(&librpz_lib_open_emsg, &librpz_handle, librpz_path); if (librpz == NULL) { return ISC_R_FILENOTFOUND; } isc_mutex_init(&dnsrps_mutex); librpz->set_log(dnsrps_log_fnc, NULL); clist = librpz->clist_create(&emsg, dnsrps_lock, dnsrps_unlock, dnsrps_mutex_destroy, &dnsrps_mutex, dns_lctx); if (clist == NULL) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, "dnsrps: %s", emsg.c); return ISC_R_NOMEMORY; } return ISC_R_SUCCESS; } /* * Stop dnsrps for the entire server. * This is not thread safe. */ void dns_dnsrps_server_destroy(void) { if (clist != NULL) { librpz->clist_detach(&clist); } #if DNSRPS_LIB_OPEN == 2 if (librpz != NULL) { INSIST(librpz_handle != NULL); if (dlclose(librpz_handle) != 0) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, "dnsrps: dlclose(): %s", dlerror()); } librpz_handle = NULL; librpz = NULL; } #endif } /* * Ready dnsrps for a view. */ isc_result_t dns_dnsrps_view_init(dns_rpz_zones_t *new, char *rps_cstr) { librpz_emsg_t emsg; isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_DEBUG_LEVEL3, "dnsrps configuration \"%s\"", rps_cstr); new->rps_client = librpz->client_create(&emsg, clist, rps_cstr, false); if (new->rps_client == NULL) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, "librpz->client_create(): %s", emsg.c); new->p.dnsrps_enabled = false; return ISC_R_FAILURE; } new->p.dnsrps_enabled = true; return ISC_R_SUCCESS; } /* * Connect to and start the dnsrps daemon, dnsrpzd. */ isc_result_t dns_dnsrps_connect(dns_rpz_zones_t *rpzs) { librpz_emsg_t emsg; if (rpzs == NULL || !rpzs->p.dnsrps_enabled) { return ISC_R_SUCCESS; } /* * Fail only if we failed to link to librpz. */ if (librpz == NULL) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, "librpz->connect(): %s", librpz_lib_open_emsg.c); return ISC_R_FAILURE; } if (!librpz->connect(&emsg, rpzs->rps_client, true)) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, "librpz->connect(): %s", emsg.c); return ISC_R_SUCCESS; } isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL, "dnsrps: librpz version %s", librpz->version); return ISC_R_SUCCESS; } /* * Get ready to try RPZ rewriting. */ isc_result_t dns_dnsrps_rewrite_init(librpz_emsg_t *emsg, dns_rpz_st_t *st, dns_rpz_zones_t *rpzs, const dns_name_t *qname, isc_mem_t *mctx, bool have_rd) { dns_rpsdb_t *rpsdb = NULL; rpsdb = isc_mem_get(mctx, sizeof(*rpsdb)); *rpsdb = (dns_rpsdb_t){ .common = { .methods = &rpsdb_db_methods, .rdclass = dns_rdataclass_in, }, .qname = qname, }; isc_refcount_init(&rpsdb->common.references, 1); if (!librpz->rsp_create(emsg, &rpsdb->rsp, NULL, rpzs->rps_client, have_rd, false)) { isc_mem_put(mctx, rpsdb, sizeof(*rpsdb)); return DNS_R_SERVFAIL; } if (rpsdb->rsp == NULL) { isc_mem_put(mctx, rpsdb, sizeof(*rpsdb)); return DNS_R_DISALLOWED; } rpsdb->common.magic = DNS_DB_MAGIC; rpsdb->common.impmagic = RPSDB_MAGIC; dns_name_init(&rpsdb->common.origin, NULL); isc_mem_attach(mctx, &rpsdb->common.mctx); st->rpsdb = &rpsdb->common; return ISC_R_SUCCESS; } /* * Convert a dnsrps policy to a classic BIND9 RPZ policy. */ dns_rpz_policy_t dns_dnsrps_2policy(librpz_policy_t rps_policy) { switch (rps_policy) { case LIBRPZ_POLICY_UNDEFINED: return DNS_RPZ_POLICY_MISS; case LIBRPZ_POLICY_PASSTHRU: return DNS_RPZ_POLICY_PASSTHRU; case LIBRPZ_POLICY_DROP: return DNS_RPZ_POLICY_DROP; case LIBRPZ_POLICY_TCP_ONLY: return DNS_RPZ_POLICY_TCP_ONLY; case LIBRPZ_POLICY_NXDOMAIN: return DNS_RPZ_POLICY_NXDOMAIN; case LIBRPZ_POLICY_NODATA: return DNS_RPZ_POLICY_NODATA; case LIBRPZ_POLICY_RECORD: case LIBRPZ_POLICY_CNAME: return DNS_RPZ_POLICY_RECORD; case LIBRPZ_POLICY_DELETED: case LIBRPZ_POLICY_GIVEN: case LIBRPZ_POLICY_DISABLED: default: UNREACHABLE(); } } /* * Convert a dnsrps trigger to a classic BIND9 RPZ rewrite or trigger type. */ dns_rpz_type_t dns_dnsrps_trig2type(librpz_trig_t trig) { switch (trig) { case LIBRPZ_TRIG_CLIENT_IP: return DNS_RPZ_TYPE_CLIENT_IP; case LIBRPZ_TRIG_QNAME: return DNS_RPZ_TYPE_QNAME; case LIBRPZ_TRIG_IP: return DNS_RPZ_TYPE_IP; case LIBRPZ_TRIG_NSDNAME: return DNS_RPZ_TYPE_NSDNAME; case LIBRPZ_TRIG_NSIP: return DNS_RPZ_TYPE_NSIP; case LIBRPZ_TRIG_BAD: default: return DNS_RPZ_TYPE_BAD; } } /* * Convert a classic BIND9 RPZ rewrite or trigger type to a librpz trigger type. */ librpz_trig_t dns_dnsrps_type2trig(dns_rpz_type_t type) { switch (type) { case DNS_RPZ_TYPE_CLIENT_IP: return LIBRPZ_TRIG_CLIENT_IP; case DNS_RPZ_TYPE_QNAME: return LIBRPZ_TRIG_QNAME; case DNS_RPZ_TYPE_IP: return LIBRPZ_TRIG_IP; case DNS_RPZ_TYPE_NSDNAME: return LIBRPZ_TRIG_NSDNAME; case DNS_RPZ_TYPE_NSIP: return LIBRPZ_TRIG_NSIP; case DNS_RPZ_TYPE_BAD: default: return LIBRPZ_TRIG_BAD; } } static void rpsdb_destroy(dns_db_t *db) { dns_rpsdb_t *rpsdb = (dns_rpsdb_t *)db; REQUIRE(VALID_RPSDB(rpsdb)); librpz->rsp_detach(&rpsdb->rsp); isc_refcount_destroy(&rpsdb->common.references); rpsdb->common.impmagic = 0; isc_mem_putanddetach(&rpsdb->common.mctx, rpsdb, sizeof(*rpsdb)); } static void rpsdb_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = (dns_rpsdb_t *)db; REQUIRE(VALID_RPSDB(rpsdb)); REQUIRE(targetp != NULL && *targetp == NULL); REQUIRE(source == &rpsdb->origin_node || source == &rpsdb->data_node); isc_refcount_increment(&rpsdb->common.references); *targetp = source; } static void rpsdb_detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = (dns_rpsdb_t *)db; REQUIRE(VALID_RPSDB(rpsdb)); REQUIRE(*targetp == &rpsdb->origin_node || *targetp == &rpsdb->data_node); *targetp = NULL; dns_db_detach(&db); } static isc_result_t rpsdb_findnode(dns_db_t *db, const dns_name_t *name, bool create, dns_dbnode_t **nodep DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = (dns_rpsdb_t *)db; dns_db_t *dbp = NULL; REQUIRE(VALID_RPSDB(rpsdb)); REQUIRE(nodep != NULL && *nodep == NULL); REQUIRE(!create); /* * A fake/shim rpsdb has two nodes. * One is the origin to support query_addsoa() in bin/named/query.c. * The other contains rewritten RRs. */ if (dns_name_equal(name, &db->origin)) { *nodep = &rpsdb->origin_node; } else { *nodep = &rpsdb->data_node; } dns_db_attach(db, &dbp); return ISC_R_SUCCESS; } static void rpsdb_bind_rdataset(dns_rdataset_t *rdataset, uint count, librpz_idx_t next_rr, dns_rdatatype_t type, uint16_t class, uint32_t ttl, dns_rpsdb_t *rpsdb) { dns_db_t *dbp = NULL; INSIST(rdataset->methods == NULL); /* We must be disassociated. */ REQUIRE(type != dns_rdatatype_none); rdataset->methods = &rpsdb_rdataset_methods; rdataset->rdclass = class; rdataset->type = type; rdataset->ttl = ttl; dbp = NULL; dns_db_attach(&rpsdb->common, &dbp); RD_DB(rdataset) = (dns_rpsdb_t *)dbp; RD_COUNT(rdataset) = count; RD_NEXT_RR(rdataset) = next_rr; RD_CUR_RR(rdataset) = NULL; } static isc_result_t rpsdb_bind_soa(dns_rdataset_t *rdataset, dns_rpsdb_t *rpsdb) { uint32_t ttl; librpz_emsg_t emsg; if (!librpz->rsp_soa(&emsg, &ttl, NULL, NULL, &rpsdb->result, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } rpsdb_bind_rdataset(rdataset, 1, LIBRPZ_IDX_BAD, dns_rdatatype_soa, dns_rdataclass_in, ttl, rpsdb); return ISC_R_SUCCESS; } /* * Forge an rdataset of the desired type from a librpz result. * This is written for simplicity instead of speed, because RPZ rewriting * should be rare compared to normal BIND operations. */ static isc_result_t rpsdb_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = (dns_rpsdb_t *)db; dns_rdatatype_t foundtype; dns_rdataclass_t class; uint32_t ttl; uint count; librpz_emsg_t emsg; UNUSED(version); UNUSED(covers); UNUSED(now); UNUSED(sigrdataset); REQUIRE(VALID_RPSDB(rpsdb)); if (node == &rpsdb->origin_node) { if (type == dns_rdatatype_any) { return ISC_R_SUCCESS; } if (type == dns_rdatatype_soa) { return rpsdb_bind_soa(rdataset, rpsdb); } return DNS_R_NXRRSET; } REQUIRE(node == &rpsdb->data_node); switch (rpsdb->result.policy) { case LIBRPZ_POLICY_NXDOMAIN: return DNS_R_NXDOMAIN; case LIBRPZ_POLICY_NODATA: return DNS_R_NXRRSET; case LIBRPZ_POLICY_RECORD: case LIBRPZ_POLICY_CNAME: break; case LIBRPZ_POLICY_UNDEFINED: case LIBRPZ_POLICY_DELETED: case LIBRPZ_POLICY_PASSTHRU: case LIBRPZ_POLICY_DROP: case LIBRPZ_POLICY_TCP_ONLY: case LIBRPZ_POLICY_GIVEN: case LIBRPZ_POLICY_DISABLED: default: librpz->log(LIBRPZ_LOG_ERROR, NULL, "impossible dnsrps policy %d at %s:%d", rpsdb->result.policy, __FILE__, __LINE__); return DNS_R_SERVFAIL; } if (type == dns_rdatatype_soa) { return rpsdb_bind_soa(rdataset, rpsdb); } /* * There is little to do for an ANY query. */ if (type == dns_rdatatype_any) { return ISC_R_SUCCESS; } /* * Reset to the start of the RRs. * This function is only used after a policy has been chosen, * and so without caring whether it is after recursion. */ if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } if (!librpz->rsp_rr(&emsg, &foundtype, &class, &ttl, NULL, &rpsdb->result, rpsdb->qname->ndata, rpsdb->qname->length, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } REQUIRE(foundtype != dns_rdatatype_none); /* * Ho many of the target RR type are available? */ count = 0; do { if (type == foundtype || type == dns_rdatatype_any) { ++count; } if (!librpz->rsp_rr(&emsg, &foundtype, NULL, NULL, NULL, &rpsdb->result, rpsdb->qname->ndata, rpsdb->qname->length, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } } while (foundtype != dns_rdatatype_none); if (count == 0) { return DNS_R_NXRRSET; } rpsdb_bind_rdataset(rdataset, count, rpsdb->result.next_rr, type, class, ttl, rpsdb); return ISC_R_SUCCESS; } static isc_result_t rpsdb_finddb(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset DNS__DB_FLARG) { dns_dbnode_t *node = NULL; UNUSED(version); UNUSED(options); UNUSED(now); UNUSED(sigrdataset); if (nodep == NULL) { node = NULL; nodep = &node; } rpsdb_findnode(db, name, false, nodep DNS__DB_FLARG_PASS); dns_name_copy(name, foundname); return rpsdb_findrdataset(db, *nodep, NULL, type, 0, 0, rdataset, sigrdataset DNS__DB_FLARG_PASS); } static isc_result_t rpsdb_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, unsigned int options, isc_stdtime_t now, dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = (dns_rpsdb_t *)db; rpsdb_rdatasetiter_t *rpsdb_iter = NULL; UNUSED(version); UNUSED(now); REQUIRE(VALID_RPSDB(rpsdb)); REQUIRE(node == &rpsdb->origin_node || node == &rpsdb->data_node); rpsdb_iter = isc_mem_get(rpsdb->common.mctx, sizeof(*rpsdb_iter)); *rpsdb_iter = ( rpsdb_rdatasetiter_t){ .common= {.magic = DNS_RDATASETITER_MAGIC, .methods = &rpsdb_rdatasetiter_methods, .db = db, .options = options, }, }; rpsdb_attachnode(db, node, &rpsdb_iter->common.node DNS__DB_FLARG_PASS); *iteratorp = &rpsdb_iter->common; return ISC_R_SUCCESS; } static bool rpsdb_issecure(dns_db_t *db) { UNUSED(db); return false; } static isc_result_t rpsdb_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = (dns_rpsdb_t *)db; REQUIRE(VALID_RPSDB(rpsdb)); REQUIRE(nodep != NULL && *nodep == NULL); rpsdb_attachnode(db, &rpsdb->origin_node, nodep DNS__DB_FLARG_PASS); return ISC_R_SUCCESS; } static void rpsdb_rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) { dns_db_t *db = NULL; /* * Detach the last RR delivered. */ if (RD_CUR_RR(rdataset) != NULL) { free(RD_CUR_RR(rdataset)); RD_CUR_RR(rdataset) = NULL; } db = (dns_db_t *)RD_DB(rdataset); RD_DB(rdataset) = NULL; dns_db_detach(&db); } static isc_result_t rpsdb_rdataset_next(dns_rdataset_t *rdataset) { dns_rpsdb_t *rpsdb = NULL; uint16_t type; dns_rdataclass_t class; librpz_rr_t *rr = NULL; librpz_emsg_t emsg; rpsdb = RD_DB(rdataset); /* * Detach the previous RR. */ if (RD_CUR_RR(rdataset) != NULL) { free(RD_CUR_RR(rdataset)); RD_CUR_RR(rdataset) = NULL; } /* * Get the next RR of the specified type. * SOAs differ. */ if (rdataset->type == dns_rdatatype_soa) { if (RD_NEXT_RR(rdataset) == LIBRPZ_IDX_NULL) { return ISC_R_NOMORE; } RD_NEXT_RR(rdataset) = LIBRPZ_IDX_NULL; if (!librpz->rsp_soa(&emsg, NULL, &rr, NULL, &rpsdb->result, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } RD_CUR_RR(rdataset) = rr; return ISC_R_SUCCESS; } rpsdb->result.next_rr = RD_NEXT_RR(rdataset); for (;;) { if (!librpz->rsp_rr(&emsg, &type, &class, NULL, &rr, &rpsdb->result, rpsdb->qname->ndata, rpsdb->qname->length, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } if (rdataset->type == type && rdataset->rdclass == class) { RD_CUR_RR(rdataset) = rr; RD_NEXT_RR(rdataset) = rpsdb->result.next_rr; return ISC_R_SUCCESS; } if (type == dns_rdatatype_none) { return ISC_R_NOMORE; } free(rr); } } static isc_result_t rpsdb_rdataset_first(dns_rdataset_t *rdataset) { dns_rpsdb_t *rpsdb = NULL; librpz_emsg_t emsg; rpsdb = RD_DB(rdataset); REQUIRE(VALID_RPSDB(rpsdb)); if (RD_CUR_RR(rdataset) != NULL) { free(RD_CUR_RR(rdataset)); RD_CUR_RR(rdataset) = NULL; } if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } if (rdataset->type == dns_rdatatype_soa) { RD_NEXT_RR(rdataset) = LIBRPZ_IDX_BAD; } else { RD_NEXT_RR(rdataset) = rpsdb->result.next_rr; } return rpsdb_rdataset_next(rdataset); } static void rpsdb_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { dns_rpsdb_t *rpsdb = NULL; librpz_rr_t *rr = NULL; isc_region_t r; rpsdb = RD_DB(rdataset); REQUIRE(VALID_RPSDB(rpsdb)); rr = RD_CUR_RR(rdataset); REQUIRE(rr != NULL); r.length = ntohs(rr->rdlength); r.base = rr->rdata; dns_rdata_fromregion(rdata, ntohs(rr->class), ntohs(rr->type), &r); } static void rpsdb_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = NULL; dns_db_t *dbp = NULL; INSIST(!ISC_LINK_LINKED(target, link)); *target = *source; ISC_LINK_INIT(target, link); rpsdb = RD_DB(source); REQUIRE(VALID_RPSDB(rpsdb)); dbp = NULL; dns_db_attach(&rpsdb->common, &dbp); RD_DB(target) = (dns_rpsdb_t *)dbp; RD_CUR_RR(target) = NULL; RD_NEXT_RR(target) = LIBRPZ_IDX_NULL; } static unsigned int rpsdb_rdataset_count(dns_rdataset_t *rdataset) { dns_rpsdb_t *rpsdb = NULL; rpsdb = RD_DB(rdataset); REQUIRE(VALID_RPSDB(rpsdb)); return RD_COUNT(rdataset); } static void rpsdb_rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = NULL; dns_rdatasetiter_t *iterator = NULL; isc_mem_t *mctx = NULL; iterator = *iteratorp; *iteratorp = NULL; rpsdb = (dns_rpsdb_t *)iterator->db; REQUIRE(VALID_RPSDB(rpsdb)); mctx = iterator->db->mctx; dns_db_detachnode(iterator->db, &iterator->node); isc_mem_put(mctx, iterator, sizeof(rpsdb_rdatasetiter_t)); } static isc_result_t rpsdb_rdatasetiter_next(dns_rdatasetiter_t *iter DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = NULL; rpsdb_rdatasetiter_t *rpsdb_iter = NULL; dns_rdatatype_t next_type, type; dns_rdataclass_t next_class, class; uint32_t ttl; librpz_emsg_t emsg; rpsdb = (dns_rpsdb_t *)iter->db; REQUIRE(VALID_RPSDB(rpsdb)); rpsdb_iter = (rpsdb_rdatasetiter_t *)iter; /* * This function is only used after a policy has been chosen, * and so without caring whether it is after recursion. */ if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } /* * Find the next class and type after the current class and type * among the RRs in current result. * As a side effect, count the number of those RRs. */ rpsdb_iter->count = 0; next_class = dns_rdataclass_reserved0; next_type = dns_rdatatype_none; for (;;) { if (!librpz->rsp_rr(&emsg, &type, &class, &ttl, NULL, &rpsdb->result, rpsdb->qname->ndata, rpsdb->qname->length, rpsdb->rsp)) { librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c); return DNS_R_SERVFAIL; } if (type == dns_rdatatype_none) { if (next_type == dns_rdatatype_none) { return ISC_R_NOMORE; } rpsdb_iter->type = next_type; rpsdb_iter->class = next_class; return ISC_R_SUCCESS; } /* * Skip RRs with the current class and type or before. */ if (rpsdb_iter->class > class || (rpsdb_iter->class = class && rpsdb_iter->type >= type)) { continue; } if (next_type == dns_rdatatype_none || next_class > class || (next_class == class && next_type > type)) { /* * This is the first of a subsequent class and type. */ next_type = type; next_class = class; rpsdb_iter->ttl = ttl; rpsdb_iter->count = 1; rpsdb_iter->next_rr = rpsdb->result.next_rr; } else if (next_type == type && next_class == class) { ++rpsdb_iter->count; } } } static isc_result_t rpsdb_rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = NULL; rpsdb_rdatasetiter_t *rpsdb_iter = NULL; rpsdb = (dns_rpsdb_t *)iterator->db; REQUIRE(VALID_RPSDB(rpsdb)); rpsdb_iter = (rpsdb_rdatasetiter_t *)iterator; rpsdb_iter->type = dns_rdatatype_none; rpsdb_iter->class = dns_rdataclass_reserved0; return rpsdb_rdatasetiter_next(iterator DNS__DB_FLARG_PASS); } static void rpsdb_rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset DNS__DB_FLARG) { dns_rpsdb_t *rpsdb = NULL; rpsdb_rdatasetiter_t *rpsdb_iter = NULL; rpsdb = (dns_rpsdb_t *)iterator->db; REQUIRE(VALID_RPSDB(rpsdb)); rpsdb_iter = (rpsdb_rdatasetiter_t *)iterator; REQUIRE(rpsdb_iter->type != dns_rdatatype_none); rpsdb_bind_rdataset(rdataset, rpsdb_iter->count, rpsdb_iter->next_rr, rpsdb_iter->type, rpsdb_iter->class, rpsdb_iter->ttl, rpsdb); } static dns_dbmethods_t rpsdb_db_methods = { .destroy = rpsdb_destroy, .findnode = rpsdb_findnode, .find = rpsdb_finddb, .attachnode = rpsdb_attachnode, .detachnode = rpsdb_detachnode, .findrdataset = rpsdb_findrdataset, .allrdatasets = rpsdb_allrdatasets, .issecure = rpsdb_issecure, .getoriginnode = rpsdb_getoriginnode, }; static dns_rdatasetmethods_t rpsdb_rdataset_methods = { .disassociate = rpsdb_rdataset_disassociate, .first = rpsdb_rdataset_first, .next = rpsdb_rdataset_next, .current = rpsdb_rdataset_current, .clone = rpsdb_rdataset_clone, .count = rpsdb_rdataset_count, }; static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods = { rpsdb_rdatasetiter_destroy, rpsdb_rdatasetiter_first, rpsdb_rdatasetiter_next, rpsdb_rdatasetiter_current }; #endif /* USE_DNSRPS */