1
0
Fork 0
bind9/lib/dns/dnsrps.c
Daniel Baumann f66ff7eae6
Adding upstream version 1:9.20.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:32:37 +02:00

933 lines
23 KiB
C

/*
* 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 <inttypes.h>
#include <stdbool.h>
#ifdef USE_DNSRPS
#include <stdlib.h>
#include <isc/mem.h>
#include <isc/string.h>
#include <isc/util.h>
#include <dns/db.h>
#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN
#include <isc/result.h>
#include <dns/dnsrps.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/rpz.h>
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 */