dnsrps.c 23.6 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
Evan Hunt's avatar
Evan Hunt committed
4 5 6
 * 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 http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10 11 12 13
 */

/*! \file */

14
#include <inttypes.h>
15
#include <stdbool.h>
16

17 18
#ifdef USE_DNSRPS

19 20
#include <stdlib.h>

21 22
#include <isc/mem.h>
#include <isc/string.h>
23
#include <isc/util.h>
24 25 26 27 28 29 30 31 32

#include <dns/db.h>
#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN
#include <dns/dnsrps.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/result.h>
#include <dns/rpz.h>

Evan Hunt's avatar
Evan Hunt committed
33
librpz_t *librpz;
34
librpz_emsg_t librpz_lib_open_emsg;
Evan Hunt's avatar
Evan Hunt committed
35
static void *librpz_handle;
36

Evan Hunt's avatar
Evan Hunt committed
37
#define RPSDB_MAGIC	   ISC_MAGIC('R', 'P', 'Z', 'F')
38 39
#define VALID_RPSDB(rpsdb) ((rpsdb)->common.impmagic == RPSDB_MAGIC)

Evan Hunt's avatar
Evan Hunt committed
40 41
#define RD_DB(r)      ((r)->private1)
#define RD_CUR_RR(r)  ((r)->private2)
42
#define RD_NEXT_RR(r) ((r)->resign)
Evan Hunt's avatar
Evan Hunt committed
43
#define RD_COUNT(r)   ((r)->privateuint4)
44 45

typedef struct {
46
	dns_rdatasetiter_t common;
Evan Hunt's avatar
Evan Hunt committed
47
	dns_rdatatype_t type;
48
	dns_rdataclass_t class;
Evan Hunt's avatar
Evan Hunt committed
49 50
	uint32_t ttl;
	uint count;
51
	librpz_idx_t next_rr;
52 53
} rpsdb_rdatasetiter_t;

Evan Hunt's avatar
Evan Hunt committed
54 55
static dns_dbmethods_t rpsdb_db_methods;
static dns_rdatasetmethods_t rpsdb_rdataset_methods;
56 57 58 59 60 61 62
static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods;

static librpz_clist_t *clist;

static isc_mutex_t dnsrps_mutex;

static void
Evan Hunt's avatar
Evan Hunt committed
63
dnsrps_lock(void *mutex0) {
64 65 66 67 68 69
	isc_mutex_t *mutex = mutex0;

	LOCK(mutex);
}

static void
Evan Hunt's avatar
Evan Hunt committed
70
dnsrps_unlock(void *mutex0) {
71 72 73 74 75 76
	isc_mutex_t *mutex = mutex0;

	UNLOCK(mutex);
}

static void
Evan Hunt's avatar
Evan Hunt committed
77
dnsrps_mutex_destroy(void *mutex0) {
78 79
	isc_mutex_t *mutex = mutex0;

80
	isc_mutex_destroy(mutex);
81 82 83
}

static void
Evan Hunt's avatar
Evan Hunt committed
84
dnsrps_log_fnc(librpz_log_level_t level, void *ctxt, const char *buf) {
85 86 87 88 89 90 91
	int isc_level;

	UNUSED(ctxt);

	/* Setting librpz_log_level in the configuration overrides the
	 * BIND9 logging levels. */
	if (level > LIBRPZ_LOG_TRACE1 &&
Evan Hunt's avatar
Evan Hunt committed
92 93
	    level <= librpz->log_level_val(LIBRPZ_LOG_INVALID))
	{
94
		level = LIBRPZ_LOG_TRACE1;
95
	}
96

97
	switch (level) {
98
	case LIBRPZ_LOG_FATAL:
99
	case LIBRPZ_LOG_ERROR: /* errors */
100 101 102 103
	default:
		isc_level = DNS_RPZ_ERROR_LEVEL;
		break;

104
	case LIBRPZ_LOG_TRACE1: /* big events such as dnsrpzd starts */
105 106 107
		isc_level = DNS_RPZ_INFO_LEVEL;
		break;

108
	case LIBRPZ_LOG_TRACE2: /* smaller dnsrpzd zone transfers */
109 110 111
		isc_level = DNS_RPZ_DEBUG_LEVEL1;
		break;

112
	case LIBRPZ_LOG_TRACE3: /* librpz hits */
113 114 115
		isc_level = DNS_RPZ_DEBUG_LEVEL2;
		break;

116
	case LIBRPZ_LOG_TRACE4: /* librpz lookups */
117 118 119 120 121 122 123 124 125 126 127 128
		isc_level = DNS_RPZ_DEBUG_LEVEL3;
		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
Evan Hunt's avatar
Evan Hunt committed
129
dns_dnsrps_server_create(void) {
130 131 132 133 134 135 136 137 138
	librpz_emsg_t emsg;

	INSIST(clist == NULL);
	INSIST(librpz == NULL);
	INSIST(librpz_handle == NULL);

	/*
	 * Notice if librpz is available.
	 */
139 140
	librpz = librpz_lib_open(&librpz_lib_open_emsg, &librpz_handle,
				 DNSRPS_LIBRPZ_PATH);
141 142 143
	/*
	 * Stop now without complaining if librpz is not available.
	 * Complain later if and when librpz is needed for a view with
144
	 * "dnsrps-enable yes" (including the default view).
145
	 */
146
	if (librpz == NULL) {
147
		return (ISC_R_SUCCESS);
148
	}
149

Ondřej Surý's avatar
Ondřej Surý committed
150
	isc_mutex_init(&dnsrps_mutex);
151

152
	librpz->set_log(dnsrps_log_fnc, NULL);
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170

	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
Evan Hunt's avatar
Evan Hunt committed
171
dns_dnsrps_server_destroy(void) {
172
	if (clist != NULL) {
173
		librpz->clist_detach(&clist);
174
	}
175 176 177 178

#ifdef LIBRPZ_USE_DLOPEN
	if (librpz != NULL) {
		INSIST(librpz_handle != NULL);
179
		if (dlclose(librpz_handle) != 0) {
180 181 182
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
				      DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
				      "dnsrps: dlclose(): %s", dlerror());
183
		}
184 185
		librpz_handle = NULL;
	}
186
#endif /* ifdef LIBRPZ_USE_DLOPEN */
187 188 189 190 191 192
}

/*
 * Ready dnsrps for a view.
 */
isc_result_t
Evan Hunt's avatar
Evan Hunt committed
193
dns_dnsrps_view_init(dns_rpz_zones_t *new, char *rps_cstr) {
194 195
	librpz_emsg_t emsg;

196 197 198
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
		      DNS_RPZ_DEBUG_LEVEL3, "dnsrps configuration \"%s\"",
		      rps_cstr);
199

200
	new->rps_client = librpz->client_create(&emsg, clist, rps_cstr, false);
201 202 203 204
	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);
205
		new->p.dnsrps_enabled = false;
206 207 208
		return (ISC_R_FAILURE);
	}

209
	new->p.dnsrps_enabled = true;
210 211 212 213 214 215 216
	return (ISC_R_SUCCESS);
}

/*
 * Connect to and start the dnsrps daemon, dnsrpzd.
 */
isc_result_t
Evan Hunt's avatar
Evan Hunt committed
217
dns_dnsrps_connect(dns_rpz_zones_t *rpzs) {
218 219
	librpz_emsg_t emsg;

220
	if (rpzs == NULL || !rpzs->p.dnsrps_enabled) {
221
		return (ISC_R_SUCCESS);
222
	}
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

	/*
	 * 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,
Evan Hunt's avatar
Evan Hunt committed
254
			isc_mem_t *mctx, bool have_rd) {
255 256 257 258 259
	rpsdb_t *rpsdb;

	rpsdb = isc_mem_get(mctx, sizeof(*rpsdb));
	memset(rpsdb, 0, sizeof(*rpsdb));

260
	if (!librpz->rsp_create(emsg, &rpsdb->rsp, NULL, rpzs->rps_client,
Evan Hunt's avatar
Evan Hunt committed
261 262
				have_rd, false))
	{
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
		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;
	rpsdb->common.methods = &rpsdb_db_methods;
	rpsdb->common.rdclass = dns_rdataclass_in;
	dns_name_init(&rpsdb->common.origin, NULL);
	isc_mem_attach(mctx, &rpsdb->common.mctx);

	rpsdb->ref_cnt = 1;
	rpsdb->qname = qname;

	st->rpsdb = &rpsdb->common;
	return (ISC_R_SUCCESS);
}

/*
 * Convert a dnsrps policy to a classic BIND9 RPZ policy.
 */
dns_rpz_policy_t
Evan Hunt's avatar
Evan Hunt committed
289
dns_dnsrps_2policy(librpz_policy_t rps_policy) {
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
	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:
		INSIST(0);
312
		ISC_UNREACHABLE();
313 314 315 316 317 318 319
	}
}

/*
 * Convert a dnsrps trigger to a classic BIND9 RPZ rewrite or trigger type.
 */
dns_rpz_type_t
Evan Hunt's avatar
Evan Hunt committed
320
dns_dnsrps_trig2type(librpz_trig_t trig) {
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
	switch (trig) {
	case LIBRPZ_TRIG_BAD:
	default:
		return (DNS_RPZ_TYPE_BAD);
	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);
	}
}

/*
 * Convert a classic BIND9 RPZ rewrite or trigger type to a librpz trigger type.
 */
librpz_trig_t
Evan Hunt's avatar
Evan Hunt committed
342
dns_dnsrps_type2trig(dns_rpz_type_t type) {
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
	switch (type) {
	case DNS_RPZ_TYPE_BAD:
	default:
		return (LIBRPZ_TRIG_BAD);
	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);
	}
}

static void
Evan Hunt's avatar
Evan Hunt committed
361
rpsdb_attach(dns_db_t *source, dns_db_t **targetp) {
362 363 364 365 366 367 368 369 370 371 372 373
	rpsdb_t *rpsdb = (rpsdb_t *)source;

	REQUIRE(VALID_RPSDB(rpsdb));

	/*
	 * Use a simple count because only one thread uses any single rpsdb_t
	 */
	++rpsdb->ref_cnt;
	*targetp = source;
}

static void
Evan Hunt's avatar
Evan Hunt committed
374
rpsdb_detach(dns_db_t **dbp) {
375 376 377 378 379 380 381 382 383 384
	rpsdb_t *rpsdb = (rpsdb_t *)*dbp;

	REQUIRE(VALID_RPSDB(rpsdb));
	REQUIRE(rpsdb->ref_cnt > 0);

	*dbp = NULL;

	/*
	 * Simple count because only one thread uses a rpsdb_t.
	 */
385
	if (--rpsdb->ref_cnt != 0) {
386
		return;
387
	}
388 389 390 391 392 393 394

	librpz->rsp_detach(&rpsdb->rsp);
	rpsdb->common.impmagic = 0;
	isc_mem_putanddetach(&rpsdb->common.mctx, rpsdb, sizeof(*rpsdb));
}

static void
Evan Hunt's avatar
Evan Hunt committed
395
rpsdb_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
396 397 398 399
	rpsdb_t *rpsdb = (rpsdb_t *)db;

	REQUIRE(VALID_RPSDB(rpsdb));
	REQUIRE(targetp != NULL && *targetp == NULL);
400
	REQUIRE(source == &rpsdb->origin_node || source == &rpsdb->data_node);
401 402 403 404 405 406 407 408 409

	/*
	 * Simple count because only one thread uses a rpsdb_t.
	 */
	++rpsdb->ref_cnt;
	*targetp = source;
}

static void
Evan Hunt's avatar
Evan Hunt committed
410
rpsdb_detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
411 412 413 414 415 416 417 418 419 420 421
	rpsdb_t *rpsdb = (rpsdb_t *)db;

	REQUIRE(VALID_RPSDB(rpsdb));
	REQUIRE(*targetp == &rpsdb->origin_node ||
		*targetp == &rpsdb->data_node);

	*targetp = NULL;
	rpsdb_detach(&db);
}

static isc_result_t
422
rpsdb_findnode(dns_db_t *db, const dns_name_t *name, bool create,
Evan Hunt's avatar
Evan Hunt committed
423 424
	       dns_dbnode_t **nodep) {
	rpsdb_t *rpsdb = (rpsdb_t *)db;
425 426 427 428 429 430 431 432 433 434 435
	dns_db_t *dbp;

	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.
	 */
436
	if (dns_name_equal(name, &db->origin)) {
437
		*nodep = &rpsdb->origin_node;
438
	} else {
439
		*nodep = &rpsdb->data_node;
440
	}
441 442 443 444 445 446 447 448 449
	dbp = NULL;
	rpsdb_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,
Evan Hunt's avatar
Evan Hunt committed
450
		    rpsdb_t *rpsdb) {
451 452
	dns_db_t *dbp;

453
	INSIST(rdataset->methods == NULL); /* We must be disassociated. */
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
	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) = dbp;
	RD_COUNT(rdataset) = count;
	RD_NEXT_RR(rdataset) = next_rr;
	RD_CUR_RR(rdataset) = NULL;
}

static isc_result_t
Evan Hunt's avatar
Evan Hunt committed
469 470
rpsdb_bind_soa(dns_rdataset_t *rdataset, rpsdb_t *rpsdb) {
	uint32_t ttl;
471 472
	librpz_emsg_t emsg;

473 474
	if (!librpz->rsp_soa(&emsg, &ttl, NULL, NULL, &rpsdb->result,
			     rpsdb->rsp)) {
475 476 477 478
		librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
		return (DNS_R_SERVFAIL);
	}
	rpsdb_bind_rdataset(rdataset, 1, LIBRPZ_IDX_BAD, dns_rdatatype_soa,
479
			    dns_rdataclass_in, ttl, rpsdb);
480 481 482 483 484 485 486 487 488 489 490 491
	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,
Evan Hunt's avatar
Evan Hunt committed
492 493
		   dns_rdataset_t *sigrdataset) {
	rpsdb_t *rpsdb = (rpsdb_t *)db;
494 495
	dns_rdatatype_t foundtype;
	dns_rdataclass_t class;
Evan Hunt's avatar
Evan Hunt committed
496 497
	uint32_t ttl;
	uint count;
498 499 500 501 502 503 504 505 506 507
	librpz_emsg_t emsg;

	UNUSED(version);
	UNUSED(covers);
	UNUSED(now);
	UNUSED(sigrdataset);

	REQUIRE(VALID_RPSDB(rpsdb));

	if (node == &rpsdb->origin_node) {
508
		if (type == dns_rdatatype_any) {
509
			return (ISC_R_SUCCESS);
510 511
		}
		if (type == dns_rdatatype_soa) {
512
			return (rpsdb_bind_soa(rdataset, rpsdb));
513
		}
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
		return (DNS_R_NXRRSET);
	}

	REQUIRE(node == &rpsdb->data_node);

	switch (rpsdb->result.policy) {
	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);

	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;
	}

544
	if (type == dns_rdatatype_soa) {
545
		return (rpsdb_bind_soa(rdataset, rpsdb));
546
	}
547 548 549 550

	/*
	 * There is little to do for an ANY query.
	 */
551
	if (type == dns_rdatatype_any) {
552
		return (ISC_R_SUCCESS);
553
	}
554 555 556 557 558 559 560 561 562 563 564 565

	/*
	 * 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,
Evan Hunt's avatar
Evan Hunt committed
566 567
			    rpsdb->qname->length, rpsdb->rsp))
	{
568 569 570 571 572 573 574 575 576 577
		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 {
578
		if (type == foundtype || type == dns_rdatatype_any) {
579
			++count;
580
		}
581 582 583

		if (!librpz->rsp_rr(&emsg, &foundtype, NULL, NULL, NULL,
				    &rpsdb->result, rpsdb->qname->ndata,
Evan Hunt's avatar
Evan Hunt committed
584 585
				    rpsdb->qname->length, rpsdb->rsp))
		{
586 587 588 589
			librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
			return (DNS_R_SERVFAIL);
		}
	} while (foundtype != dns_rdatatype_none);
590
	if (count == 0) {
591
		return (DNS_R_NXRRSET);
592
	}
593 594
	rpsdb_bind_rdataset(rdataset, count, rpsdb->result.next_rr, type, class,
			    ttl, rpsdb);
595 596 597 598 599 600 601
	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,
Evan Hunt's avatar
Evan Hunt committed
602
	     dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
603 604 605 606 607 608 609 610 611 612 613 614
	dns_dbnode_t *node;

	UNUSED(version);
	UNUSED(options);
	UNUSED(now);
	UNUSED(sigrdataset);

	if (nodep == NULL) {
		node = NULL;
		nodep = &node;
	}
	rpsdb_findnode(db, name, false, nodep);
615
	dns_name_copynf(name, foundname);
616 617
	return (rpsdb_findrdataset(db, *nodep, NULL, type, 0, 0, rdataset,
				   sigrdataset));
618 619 620 621
}

static isc_result_t
rpsdb_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
Evan Hunt's avatar
Evan Hunt committed
622 623
		   isc_stdtime_t now, dns_rdatasetiter_t **iteratorp) {
	rpsdb_t *rpsdb = (rpsdb_t *)db;
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
	rpsdb_rdatasetiter_t *rpsdb_iter;

	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));

	memset(rpsdb_iter, 0, sizeof(*rpsdb_iter));
	rpsdb_iter->common.magic = DNS_RDATASETITER_MAGIC;
	rpsdb_iter->common.methods = &rpsdb_rdatasetiter_methods;
	rpsdb_iter->common.db = db;
	rpsdb_attachnode(db, node, &rpsdb_iter->common.node);

	*iteratorp = &rpsdb_iter->common;

	return (ISC_R_SUCCESS);
}

645
static bool
Evan Hunt's avatar
Evan Hunt committed
646
rpsdb_issecure(dns_db_t *db) {
647 648
	UNUSED(db);

649
	return (false);
650 651 652
}

static isc_result_t
Evan Hunt's avatar
Evan Hunt committed
653
rpsdb_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
654 655 656 657 658 659 660 661 662 663
	rpsdb_t *rpsdb = (rpsdb_t *)db;

	REQUIRE(VALID_RPSDB(rpsdb));
	REQUIRE(nodep != NULL && *nodep == NULL);

	rpsdb_attachnode(db, &rpsdb->origin_node, nodep);
	return (ISC_R_SUCCESS);
}

static void
Evan Hunt's avatar
Evan Hunt committed
664
rpsdb_rdataset_disassociate(dns_rdataset_t *rdataset) {
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
	dns_db_t *db;

	/*
	 * Detach the last RR delivered.
	 */
	if (RD_CUR_RR(rdataset) != NULL) {
		free(RD_CUR_RR(rdataset));
		RD_CUR_RR(rdataset) = NULL;
	}

	db = RD_DB(rdataset);
	RD_DB(rdataset) = NULL;
	dns_db_detach(&db);
}

static isc_result_t
Evan Hunt's avatar
Evan Hunt committed
681
rpsdb_rdataset_next(dns_rdataset_t *rdataset) {
682 683 684
	rpsdb_t *rpsdb;
	uint16_t type;
	dns_rdataclass_t class;
Evan Hunt's avatar
Evan Hunt committed
685
	librpz_rr_t *rr;
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
	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) {
703
		if (RD_NEXT_RR(rdataset) == LIBRPZ_IDX_NULL) {
704
			return (ISC_R_NOMORE);
705
		}
706
		RD_NEXT_RR(rdataset) = LIBRPZ_IDX_NULL;
707 708
		if (!librpz->rsp_soa(&emsg, NULL, &rr, NULL, &rpsdb->result,
				     rpsdb->rsp)) {
709 710 711 712 713 714 715 716 717 718 719
			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,
Evan Hunt's avatar
Evan Hunt committed
720 721
				    rpsdb->qname->length, rpsdb->rsp))
		{
722 723 724
			librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
			return (DNS_R_SERVFAIL);
		}
725
		if (rdataset->type == type && rdataset->rdclass == class) {
726 727 728 729
			RD_CUR_RR(rdataset) = rr;
			RD_NEXT_RR(rdataset) = rpsdb->result.next_rr;
			return (ISC_R_SUCCESS);
		}
730
		if (type == dns_rdatatype_none) {
731
			return (ISC_R_NOMORE);
732
		}
733 734 735 736 737
		free(rr);
	}
}

static isc_result_t
Evan Hunt's avatar
Evan Hunt committed
738 739
rpsdb_rdataset_first(dns_rdataset_t *rdataset) {
	rpsdb_t *rpsdb;
740 741 742 743 744 745 746 747 748 749 750 751 752 753
	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);
	}
754
	if (rdataset->type == dns_rdatatype_soa) {
755
		RD_NEXT_RR(rdataset) = LIBRPZ_IDX_BAD;
756
	} else {
757
		RD_NEXT_RR(rdataset) = rpsdb->result.next_rr;
758
	}
759 760 761 762 763

	return (rpsdb_rdataset_next(rdataset));
}

static void
Evan Hunt's avatar
Evan Hunt committed
764 765
rpsdb_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
	rpsdb_t *rpsdb;
766 767 768 769 770 771 772 773 774 775 776 777 778 779
	librpz_rr_t *rr;
	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
Evan Hunt's avatar
Evan Hunt committed
780 781
rpsdb_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
	rpsdb_t *rpsdb;
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
	dns_db_t *dbp;

	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) = dbp;
	RD_CUR_RR(target) = NULL;
	RD_NEXT_RR(target) = LIBRPZ_IDX_NULL;
}

static unsigned int
Evan Hunt's avatar
Evan Hunt committed
797
rpsdb_rdataset_count(dns_rdataset_t *rdataset) {
798 799 800 801 802 803 804 805 806
	rpsdb_t *rpsdb;

	rpsdb = RD_DB(rdataset);
	REQUIRE(VALID_RPSDB(rpsdb));

	return (RD_COUNT(rdataset));
}

static void
Evan Hunt's avatar
Evan Hunt committed
807 808
rpsdb_rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
	rpsdb_t *rpsdb;
809
	dns_rdatasetiter_t *iterator;
Evan Hunt's avatar
Evan Hunt committed
810
	isc_mem_t *mctx;
811 812

	iterator = *iteratorp;
813
	*iteratorp = NULL;
814 815 816 817 818 819 820 821 822
	rpsdb = (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
Evan Hunt's avatar
Evan Hunt committed
823 824
rpsdb_rdatasetiter_next(dns_rdatasetiter_t *iter) {
	rpsdb_t *rpsdb;
825
	rpsdb_rdatasetiter_t *rpsdb_iter;
Evan Hunt's avatar
Evan Hunt committed
826 827 828 829
	dns_rdatatype_t next_type, type;
	dns_rdataclass_t next_class, class;
	uint32_t ttl;
	librpz_emsg_t emsg;
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851

	rpsdb = (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 (;;) {
852 853
		if (!librpz->rsp_rr(&emsg, &type, &class, &ttl, NULL,
				    &rpsdb->result, rpsdb->qname->ndata,
Evan Hunt's avatar
Evan Hunt committed
854 855
				    rpsdb->qname->length, rpsdb->rsp))
		{
856 857 858 859
			librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
			return (DNS_R_SERVFAIL);
		}
		if (type == dns_rdatatype_none) {
860
			if (next_type == dns_rdatatype_none) {
861
				return (ISC_R_NOMORE);
862
			}
863 864 865 866 867 868 869 870
			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 ||
Evan Hunt's avatar
Evan Hunt committed
871 872
		    (rpsdb_iter->class = class && rpsdb_iter->type >= type))
		{
873
			continue;
874
		}
875
		if (next_type == dns_rdatatype_none || next_class > class ||
Evan Hunt's avatar
Evan Hunt committed
876 877
		    (next_class == class && next_type > type))
		{
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
			/*
			 * 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
Evan Hunt's avatar
Evan Hunt committed
893 894
rpsdb_rdatasetiter_first(dns_rdatasetiter_t *iterator) {
	rpsdb_t *rpsdb;
895 896 897 898 899 900 901 902 903 904 905 906 907
	rpsdb_rdatasetiter_t *rpsdb_iter;

	rpsdb = (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));
}

static void
rpsdb_rdatasetiter_current(dns_rdatasetiter_t *iterator,
Evan Hunt's avatar
Evan Hunt committed
908 909
			   dns_rdataset_t *rdataset) {
	rpsdb_t *rpsdb;
910 911 912 913 914 915 916
	rpsdb_rdatasetiter_t *rpsdb_iter;

	rpsdb = (rpsdb_t *)iterator->db;
	REQUIRE(VALID_RPSDB(rpsdb));
	rpsdb_iter = (rpsdb_rdatasetiter_t *)iterator;
	REQUIRE(rpsdb_iter->type != dns_rdatatype_none);

917 918 919
	rpsdb_bind_rdataset(rdataset, rpsdb_iter->count, rpsdb_iter->next_rr,
			    rpsdb_iter->type, rpsdb_iter->class,
			    rpsdb_iter->ttl, rpsdb);
920 921 922 923 924
}

static dns_dbmethods_t rpsdb_db_methods = {
	rpsdb_attach,
	rpsdb_detach,
925 926 927 928 929 930 931 932
	NULL, /* beginload */
	NULL, /* endload */
	NULL, /* serialize */
	NULL, /* dump */
	NULL, /* currentversion */
	NULL, /* newversion */
	NULL, /* attachversion */
	NULL, /* closeversion */
933 934
	rpsdb_findnode,
	rpsdb_finddb,
935
	NULL, /* findzonecut*/
936 937
	rpsdb_attachnode,
	rpsdb_detachnode,
938 939 940
	NULL, /* expirenode */
	NULL, /* printnode */
	NULL, /* createiterator */
941 942
	rpsdb_findrdataset,
	rpsdb_allrdatasets,
943 944 945
	NULL, /* addrdataset */
	NULL, /* subtractrdataset */
	NULL, /* deleterdataset */
946
	rpsdb_issecure,
947 948 949 950
	NULL, /* nodecount */
	NULL, /* ispersistent */
	NULL, /* overmem */
	NULL, /* settask */
951
	rpsdb_getoriginnode,
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
	NULL, /* transfernode */
	NULL, /* getnsec3parameters */
	NULL, /* findnsec3node */
	NULL, /* setsigningtime */
	NULL, /* getsigningtime */
	NULL, /* resigned */
	NULL, /* isdnssec */
	NULL, /* getrrsetstats */
	NULL, /* rpz_attach */
	NULL, /* rpz_ready */
	NULL, /* findnodeext */
	NULL, /* findext */
	NULL, /* setcachestats */
	NULL, /* hashsize */
	NULL, /* nodefullname */
	NULL, /* getsize */
	NULL, /* setservestalettl */
	NULL, /* getservestalettl */
970 971
	NULL, /* setgluecachestats */
	NULL  /* adjusthashsize */
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
};

static dns_rdatasetmethods_t rpsdb_rdataset_methods = {
	rpsdb_rdataset_disassociate,
	rpsdb_rdataset_first,
	rpsdb_rdataset_next,
	rpsdb_rdataset_current,
	rpsdb_rdataset_clone,
	rpsdb_rdataset_count,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods = {
994 995
	rpsdb_rdatasetiter_destroy, rpsdb_rdatasetiter_first,
	rpsdb_rdatasetiter_next, rpsdb_rdatasetiter_current
996 997 998
};

#endif /* USE_DNSRPS */