1224090Sdougb/*
2254897Serwin * Copyright (C) 2009-2011, 2013  Internet Systems Consortium, Inc. ("ISC")
3224090Sdougb *
4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any
5224090Sdougb * purpose with or without fee is hereby granted, provided that the above
6224090Sdougb * copyright notice and this permission notice appear in all copies.
7224090Sdougb *
8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10224090Sdougb * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14224090Sdougb * PERFORMANCE OF THIS SOFTWARE.
15224090Sdougb */
16224090Sdougb
17254897Serwin/* $Id: ecdb.c,v 1.10 2011/12/20 00:06:53 marka Exp $ */
18224090Sdougb
19224090Sdougb#include "config.h"
20224090Sdougb
21224090Sdougb#include <isc/result.h>
22224090Sdougb#include <isc/util.h>
23224090Sdougb#include <isc/mutex.h>
24224090Sdougb#include <isc/mem.h>
25224090Sdougb
26224090Sdougb#include <dns/db.h>
27224090Sdougb#include <dns/ecdb.h>
28224090Sdougb#include <dns/rdata.h>
29224090Sdougb#include <dns/rdataset.h>
30224090Sdougb#include <dns/rdatasetiter.h>
31224090Sdougb#include <dns/rdataslab.h>
32224090Sdougb
33224090Sdougb#define ECDB_MAGIC		ISC_MAGIC('E', 'C', 'D', 'B')
34224090Sdougb#define VALID_ECDB(db)		((db) != NULL && \
35224090Sdougb				 (db)->common.impmagic == ECDB_MAGIC)
36224090Sdougb
37224090Sdougb#define ECDBNODE_MAGIC		ISC_MAGIC('E', 'C', 'D', 'N')
38224090Sdougb#define VALID_ECDBNODE(ecdbn)	ISC_MAGIC_VALID(ecdbn, ECDBNODE_MAGIC)
39224090Sdougb
40224090Sdougb/*%
41224090Sdougb * The 'ephemeral' cache DB (ecdb) implementation.  An ecdb just provides
42224090Sdougb * temporary storage for ongoing name resolution with the common DB interfaces.
43224090Sdougb * It actually doesn't cache anything.  The implementation expects any stored
44224090Sdougb * data is released within a short period, and does not care about the
45224090Sdougb * scalability in terms of the number of nodes.
46224090Sdougb */
47224090Sdougb
48224090Sdougbtypedef struct dns_ecdb {
49224090Sdougb	/* Unlocked */
50224090Sdougb	dns_db_t			common;
51224090Sdougb	isc_mutex_t			lock;
52224090Sdougb
53224090Sdougb	/* Locked */
54224090Sdougb	unsigned int			references;
55224090Sdougb	ISC_LIST(struct dns_ecdbnode)	nodes;
56224090Sdougb} dns_ecdb_t;
57224090Sdougb
58224090Sdougbtypedef struct dns_ecdbnode {
59224090Sdougb	/* Unlocked */
60224090Sdougb	unsigned int			magic;
61224090Sdougb	isc_mutex_t			lock;
62224090Sdougb	dns_ecdb_t			*ecdb;
63224090Sdougb	dns_name_t			name;
64224090Sdougb	ISC_LINK(struct dns_ecdbnode)	link;
65224090Sdougb
66224090Sdougb	/* Locked */
67224090Sdougb	ISC_LIST(struct rdatasetheader)	rdatasets;
68224090Sdougb	unsigned int			references;
69224090Sdougb} dns_ecdbnode_t;
70224090Sdougb
71224090Sdougbtypedef struct rdatasetheader {
72224090Sdougb	dns_rdatatype_t			type;
73224090Sdougb	dns_ttl_t			ttl;
74224090Sdougb	dns_trust_t			trust;
75224090Sdougb	dns_rdatatype_t			covers;
76224090Sdougb	unsigned int			attributes;
77224090Sdougb
78224090Sdougb	ISC_LINK(struct rdatasetheader)	link;
79224090Sdougb} rdatasetheader_t;
80224090Sdougb
81224090Sdougb/* Copied from rbtdb.c */
82224090Sdougb#define RDATASET_ATTR_NXDOMAIN		0x0010
83254402Serwin#define RDATASET_ATTR_NEGATIVE		0x0100
84224090Sdougb#define NXDOMAIN(header) \
85224090Sdougb	(((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0)
86254402Serwin#define NEGATIVE(header) \
87254402Serwin	(((header)->attributes & RDATASET_ATTR_NEGATIVE) != 0)
88224090Sdougb
89224090Sdougbstatic isc_result_t dns_ecdb_create(isc_mem_t *mctx, dns_name_t *origin,
90224090Sdougb				    dns_dbtype_t type,
91224090Sdougb				    dns_rdataclass_t rdclass,
92224090Sdougb				    unsigned int argc, char *argv[],
93224090Sdougb				    void *driverarg, dns_db_t **dbp);
94224090Sdougb
95224090Sdougbstatic void rdataset_disassociate(dns_rdataset_t *rdataset);
96224090Sdougbstatic isc_result_t rdataset_first(dns_rdataset_t *rdataset);
97224090Sdougbstatic isc_result_t rdataset_next(dns_rdataset_t *rdataset);
98224090Sdougbstatic void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
99224090Sdougbstatic void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target);
100224090Sdougbstatic unsigned int rdataset_count(dns_rdataset_t *rdataset);
101224090Sdougbstatic void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
102224090Sdougb
103224090Sdougbstatic dns_rdatasetmethods_t rdataset_methods = {
104224090Sdougb	rdataset_disassociate,
105224090Sdougb	rdataset_first,
106224090Sdougb	rdataset_next,
107224090Sdougb	rdataset_current,
108224090Sdougb	rdataset_clone,
109224090Sdougb	rdataset_count,
110224090Sdougb	NULL,			/* addnoqname */
111224090Sdougb	NULL,			/* getnoqname */
112224090Sdougb	NULL,			/* addclosest */
113224090Sdougb	NULL,			/* getclosest */
114224090Sdougb	NULL,			/* getadditional */
115224090Sdougb	NULL,			/* setadditional */
116224090Sdougb	NULL,			/* putadditional */
117224090Sdougb	rdataset_settrust,	/* settrust */
118224090Sdougb	NULL			/* expire */
119224090Sdougb};
120224090Sdougb
121224090Sdougbtypedef struct ecdb_rdatasetiter {
122224090Sdougb	dns_rdatasetiter_t		common;
123224090Sdougb	rdatasetheader_t	       *current;
124224090Sdougb} ecdb_rdatasetiter_t;
125224090Sdougb
126224090Sdougbstatic void		rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
127224090Sdougbstatic isc_result_t	rdatasetiter_first(dns_rdatasetiter_t *iterator);
128224090Sdougbstatic isc_result_t	rdatasetiter_next(dns_rdatasetiter_t *iterator);
129224090Sdougbstatic void		rdatasetiter_current(dns_rdatasetiter_t *iterator,
130224090Sdougb					     dns_rdataset_t *rdataset);
131224090Sdougb
132224090Sdougbstatic dns_rdatasetitermethods_t rdatasetiter_methods = {
133224090Sdougb	rdatasetiter_destroy,
134224090Sdougb	rdatasetiter_first,
135224090Sdougb	rdatasetiter_next,
136224090Sdougb	rdatasetiter_current
137224090Sdougb};
138224090Sdougb
139224090Sdougbisc_result_t
140224090Sdougbdns_ecdb_register(isc_mem_t *mctx, dns_dbimplementation_t **dbimp) {
141224090Sdougb	REQUIRE(mctx != NULL);
142224090Sdougb	REQUIRE(dbimp != NULL && *dbimp == NULL);
143224090Sdougb
144224090Sdougb	return (dns_db_register("ecdb", dns_ecdb_create, NULL, mctx, dbimp));
145224090Sdougb}
146224090Sdougb
147224090Sdougbvoid
148224090Sdougbdns_ecdb_unregister(dns_dbimplementation_t **dbimp) {
149224090Sdougb	REQUIRE(dbimp != NULL && *dbimp != NULL);
150224090Sdougb
151224090Sdougb	dns_db_unregister(dbimp);
152224090Sdougb}
153224090Sdougb
154224090Sdougb/*%
155224090Sdougb * DB routines
156224090Sdougb */
157224090Sdougb
158224090Sdougbstatic void
159224090Sdougbattach(dns_db_t *source, dns_db_t **targetp) {
160224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)source;
161224090Sdougb
162224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
163224090Sdougb	REQUIRE(targetp != NULL && *targetp == NULL);
164224090Sdougb
165224090Sdougb	LOCK(&ecdb->lock);
166224090Sdougb	ecdb->references++;
167224090Sdougb	UNLOCK(&ecdb->lock);
168224090Sdougb
169224090Sdougb	*targetp = source;
170224090Sdougb}
171224090Sdougb
172224090Sdougbstatic void
173224090Sdougbdestroy_ecdb(dns_ecdb_t **ecdbp) {
174224090Sdougb	dns_ecdb_t *ecdb = *ecdbp;
175224090Sdougb	isc_mem_t *mctx = ecdb->common.mctx;
176224090Sdougb
177224090Sdougb	if (dns_name_dynamic(&ecdb->common.origin))
178224090Sdougb		dns_name_free(&ecdb->common.origin, mctx);
179224090Sdougb
180224090Sdougb	DESTROYLOCK(&ecdb->lock);
181224090Sdougb
182224090Sdougb	ecdb->common.impmagic = 0;
183224090Sdougb	ecdb->common.magic = 0;
184224090Sdougb
185224090Sdougb	isc_mem_putanddetach(&mctx, ecdb, sizeof(*ecdb));
186224090Sdougb
187224090Sdougb	*ecdbp = NULL;
188224090Sdougb}
189224090Sdougb
190224090Sdougbstatic void
191224090Sdougbdetach(dns_db_t **dbp) {
192224090Sdougb	dns_ecdb_t *ecdb;
193224090Sdougb	isc_boolean_t need_destroy = ISC_FALSE;
194224090Sdougb
195224090Sdougb	REQUIRE(dbp != NULL);
196224090Sdougb	ecdb = (dns_ecdb_t *)*dbp;
197224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
198224090Sdougb
199224090Sdougb	LOCK(&ecdb->lock);
200224090Sdougb	ecdb->references--;
201224090Sdougb	if (ecdb->references == 0 && ISC_LIST_EMPTY(ecdb->nodes))
202224090Sdougb		need_destroy = ISC_TRUE;
203224090Sdougb	UNLOCK(&ecdb->lock);
204224090Sdougb
205224090Sdougb	if (need_destroy)
206224090Sdougb		destroy_ecdb(&ecdb);
207224090Sdougb
208224090Sdougb	*dbp = NULL;
209224090Sdougb}
210224090Sdougb
211224090Sdougbstatic void
212224090Sdougbattachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
213224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
214224090Sdougb	dns_ecdbnode_t *node = (dns_ecdbnode_t *)source;
215224090Sdougb
216224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
217224090Sdougb	REQUIRE(VALID_ECDBNODE(node));
218224090Sdougb	REQUIRE(targetp != NULL && *targetp == NULL);
219224090Sdougb
220224090Sdougb	LOCK(&node->lock);
221224090Sdougb	INSIST(node->references > 0);
222224090Sdougb	node->references++;
223224090Sdougb	INSIST(node->references != 0);		/* Catch overflow. */
224224090Sdougb	UNLOCK(&node->lock);
225224090Sdougb
226224090Sdougb	*targetp = node;
227224090Sdougb}
228224090Sdougb
229224090Sdougbstatic void
230224090Sdougbdestroynode(dns_ecdbnode_t *node) {
231224090Sdougb	isc_mem_t *mctx;
232224090Sdougb	dns_ecdb_t *ecdb = node->ecdb;
233224090Sdougb	isc_boolean_t need_destroydb = ISC_FALSE;
234224090Sdougb	rdatasetheader_t *header;
235224090Sdougb
236224090Sdougb	mctx = ecdb->common.mctx;
237224090Sdougb
238224090Sdougb	LOCK(&ecdb->lock);
239224090Sdougb	ISC_LIST_UNLINK(ecdb->nodes, node, link);
240224090Sdougb	if (ecdb->references == 0 && ISC_LIST_EMPTY(ecdb->nodes))
241224090Sdougb		need_destroydb = ISC_TRUE;
242224090Sdougb	UNLOCK(&ecdb->lock);
243224090Sdougb
244224090Sdougb	dns_name_free(&node->name, mctx);
245224090Sdougb
246224090Sdougb	while ((header = ISC_LIST_HEAD(node->rdatasets)) != NULL) {
247224090Sdougb		unsigned int headersize;
248224090Sdougb
249224090Sdougb		ISC_LIST_UNLINK(node->rdatasets, header, link);
250224090Sdougb		headersize =
251224090Sdougb			dns_rdataslab_size((unsigned char *)header,
252224090Sdougb					   sizeof(*header));
253224090Sdougb		isc_mem_put(mctx, header, headersize);
254224090Sdougb	}
255224090Sdougb
256224090Sdougb	DESTROYLOCK(&node->lock);
257224090Sdougb
258224090Sdougb	node->magic = 0;
259224090Sdougb	isc_mem_put(mctx, node, sizeof(*node));
260224090Sdougb
261224090Sdougb	if (need_destroydb)
262224090Sdougb		destroy_ecdb(&ecdb);
263224090Sdougb}
264224090Sdougb
265224090Sdougbstatic void
266224090Sdougbdetachnode(dns_db_t *db, dns_dbnode_t **nodep) {
267224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
268224090Sdougb	dns_ecdbnode_t *node;
269224090Sdougb	isc_boolean_t need_destroy = ISC_FALSE;
270224090Sdougb
271224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
272224090Sdougb	REQUIRE(nodep != NULL);
273224090Sdougb	node = (dns_ecdbnode_t *)*nodep;
274224090Sdougb	REQUIRE(VALID_ECDBNODE(node));
275224090Sdougb
276224090Sdougb	UNUSED(ecdb);		/* in case REQUIRE() is empty */
277224090Sdougb
278224090Sdougb	LOCK(&node->lock);
279224090Sdougb	INSIST(node->references > 0);
280224090Sdougb	node->references--;
281224090Sdougb	if (node->references == 0)
282224090Sdougb		need_destroy = ISC_TRUE;
283224090Sdougb	UNLOCK(&node->lock);
284224090Sdougb
285224090Sdougb	if (need_destroy)
286224090Sdougb		destroynode(node);
287224090Sdougb
288224090Sdougb	*nodep = NULL;
289224090Sdougb}
290224090Sdougb
291224090Sdougbstatic isc_result_t
292224090Sdougbfind(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
293224090Sdougb    dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
294224090Sdougb    dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset,
295224090Sdougb    dns_rdataset_t *sigrdataset)
296224090Sdougb{
297224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
298224090Sdougb
299224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
300224090Sdougb
301224090Sdougb	UNUSED(name);
302224090Sdougb	UNUSED(version);
303224090Sdougb	UNUSED(type);
304224090Sdougb	UNUSED(options);
305224090Sdougb	UNUSED(now);
306224090Sdougb	UNUSED(nodep);
307224090Sdougb	UNUSED(foundname);
308224090Sdougb	UNUSED(rdataset);
309224090Sdougb	UNUSED(sigrdataset);
310224090Sdougb
311224090Sdougb	return (ISC_R_NOTFOUND);
312224090Sdougb}
313224090Sdougb
314224090Sdougbstatic isc_result_t
315224090Sdougbfindzonecut(dns_db_t *db, dns_name_t *name,
316224090Sdougb	    unsigned int options, isc_stdtime_t now,
317224090Sdougb	    dns_dbnode_t **nodep, dns_name_t *foundname,
318224090Sdougb	    dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
319224090Sdougb{
320224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
321224090Sdougb
322224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
323224090Sdougb
324224090Sdougb	UNUSED(name);
325224090Sdougb	UNUSED(options);
326224090Sdougb	UNUSED(now);
327224090Sdougb	UNUSED(nodep);
328224090Sdougb	UNUSED(foundname);
329224090Sdougb	UNUSED(rdataset);
330224090Sdougb	UNUSED(sigrdataset);
331224090Sdougb
332224090Sdougb	return (ISC_R_NOTFOUND);
333224090Sdougb}
334224090Sdougb
335224090Sdougbstatic isc_result_t
336224090Sdougbfindnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
337224090Sdougb	 dns_dbnode_t **nodep)
338224090Sdougb{
339224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
340224090Sdougb	isc_mem_t *mctx;
341224090Sdougb	dns_ecdbnode_t *node;
342224090Sdougb	isc_result_t result;
343224090Sdougb
344224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
345224090Sdougb	REQUIRE(nodep != NULL && *nodep == NULL);
346224090Sdougb
347224090Sdougb	UNUSED(name);
348224090Sdougb
349224090Sdougb	if (create != ISC_TRUE)	{
350224090Sdougb		/* an 'ephemeral' node is never reused. */
351224090Sdougb		return (ISC_R_NOTFOUND);
352224090Sdougb	}
353224090Sdougb
354224090Sdougb	mctx = ecdb->common.mctx;
355224090Sdougb	node = isc_mem_get(mctx, sizeof(*node));
356224090Sdougb	if (node == NULL)
357224090Sdougb		return (ISC_R_NOMEMORY);
358224090Sdougb
359224090Sdougb	result = isc_mutex_init(&node->lock);
360224090Sdougb	if (result != ISC_R_SUCCESS) {
361224090Sdougb		UNEXPECTED_ERROR(__FILE__, __LINE__,
362224090Sdougb				 "isc_mutex_init() failed: %s",
363224090Sdougb				 isc_result_totext(result));
364224090Sdougb		isc_mem_put(mctx, node, sizeof(*node));
365224090Sdougb		return (ISC_R_UNEXPECTED);
366224090Sdougb	}
367224090Sdougb
368224090Sdougb	dns_name_init(&node->name, NULL);
369224090Sdougb	result = dns_name_dup(name, mctx, &node->name);
370224090Sdougb	if (result != ISC_R_SUCCESS) {
371224090Sdougb		DESTROYLOCK(&node->lock);
372224090Sdougb		isc_mem_put(mctx, node, sizeof(*node));
373224090Sdougb		return (result);
374224090Sdougb	}
375224090Sdougb	node->ecdb= ecdb;
376224090Sdougb	node->references = 1;
377224090Sdougb	ISC_LIST_INIT(node->rdatasets);
378224090Sdougb
379224090Sdougb	ISC_LINK_INIT(node, link);
380224090Sdougb
381224090Sdougb	LOCK(&ecdb->lock);
382224090Sdougb	ISC_LIST_APPEND(ecdb->nodes, node, link);
383224090Sdougb	UNLOCK(&ecdb->lock);
384224090Sdougb
385224090Sdougb	node->magic = ECDBNODE_MAGIC;
386224090Sdougb
387224090Sdougb	*nodep = node;
388224090Sdougb
389224090Sdougb	return (ISC_R_SUCCESS);
390224090Sdougb}
391224090Sdougb
392224090Sdougbstatic void
393224090Sdougbbind_rdataset(dns_ecdb_t *ecdb, dns_ecdbnode_t *node,
394224090Sdougb	      rdatasetheader_t *header, dns_rdataset_t *rdataset)
395224090Sdougb{
396224090Sdougb	unsigned char *raw;
397224090Sdougb
398224090Sdougb	/*
399224090Sdougb	 * Caller must be holding the node lock.
400224090Sdougb	 */
401224090Sdougb
402224090Sdougb	REQUIRE(!dns_rdataset_isassociated(rdataset));
403224090Sdougb
404224090Sdougb	rdataset->methods = &rdataset_methods;
405224090Sdougb	rdataset->rdclass = ecdb->common.rdclass;
406224090Sdougb	rdataset->type = header->type;
407224090Sdougb	rdataset->covers = header->covers;
408224090Sdougb	rdataset->ttl = header->ttl;
409224090Sdougb	rdataset->trust = header->trust;
410224090Sdougb	if (NXDOMAIN(header))
411224090Sdougb		rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN;
412254402Serwin	if (NEGATIVE(header))
413254402Serwin		rdataset->attributes |= DNS_RDATASETATTR_NEGATIVE;
414224090Sdougb
415224090Sdougb	rdataset->private1 = ecdb;
416224090Sdougb	rdataset->private2 = node;
417224090Sdougb	raw = (unsigned char *)header + sizeof(*header);
418224090Sdougb	rdataset->private3 = raw;
419224090Sdougb	rdataset->count = 0;
420224090Sdougb
421224090Sdougb	/*
422224090Sdougb	 * Reset iterator state.
423224090Sdougb	 */
424224090Sdougb	rdataset->privateuint4 = 0;
425224090Sdougb	rdataset->private5 = NULL;
426224090Sdougb
427224090Sdougb	INSIST(node->references > 0);
428224090Sdougb	node->references++;
429224090Sdougb}
430224090Sdougb
431224090Sdougbstatic isc_result_t
432224090Sdougbaddrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
433224090Sdougb	    isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
434224090Sdougb	    dns_rdataset_t *addedrdataset)
435224090Sdougb{
436224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
437224090Sdougb	isc_region_t r;
438224090Sdougb	isc_result_t result = ISC_R_SUCCESS;
439224090Sdougb	isc_mem_t *mctx;
440224090Sdougb	dns_ecdbnode_t *ecdbnode = (dns_ecdbnode_t *)node;
441224090Sdougb	rdatasetheader_t *header;
442224090Sdougb
443224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
444224090Sdougb	REQUIRE(VALID_ECDBNODE(ecdbnode));
445224090Sdougb
446224090Sdougb	UNUSED(version);
447224090Sdougb	UNUSED(now);
448224090Sdougb	UNUSED(options);
449224090Sdougb
450224090Sdougb	mctx = ecdb->common.mctx;
451224090Sdougb
452224090Sdougb	LOCK(&ecdbnode->lock);
453224090Sdougb
454224090Sdougb	/*
455224090Sdougb	 * Sanity check: this implementation does not allow overriding an
456224090Sdougb	 * existing rdataset of the same type.
457224090Sdougb	 */
458224090Sdougb	for (header = ISC_LIST_HEAD(ecdbnode->rdatasets); header != NULL;
459224090Sdougb	     header = ISC_LIST_NEXT(header, link)) {
460224090Sdougb		INSIST(header->type != rdataset->type ||
461224090Sdougb		       header->covers != rdataset->covers);
462224090Sdougb	}
463224090Sdougb
464224090Sdougb	result = dns_rdataslab_fromrdataset(rdataset, mctx,
465224090Sdougb					    &r, sizeof(rdatasetheader_t));
466224090Sdougb	if (result != ISC_R_SUCCESS)
467224090Sdougb		goto unlock;
468224090Sdougb
469224090Sdougb	header = (rdatasetheader_t *)r.base;
470224090Sdougb	header->type = rdataset->type;
471224090Sdougb	header->ttl = rdataset->ttl;
472224090Sdougb	header->trust = rdataset->trust;
473224090Sdougb	header->covers = rdataset->covers;
474224090Sdougb	header->attributes = 0;
475224090Sdougb	if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
476224090Sdougb		header->attributes |= RDATASET_ATTR_NXDOMAIN;
477254402Serwin	if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
478254402Serwin		header->attributes |= RDATASET_ATTR_NEGATIVE;
479224090Sdougb	ISC_LINK_INIT(header, link);
480224090Sdougb	ISC_LIST_APPEND(ecdbnode->rdatasets, header, link);
481224090Sdougb
482224090Sdougb	if (addedrdataset == NULL)
483224090Sdougb		goto unlock;
484224090Sdougb
485224090Sdougb	bind_rdataset(ecdb, ecdbnode, header, addedrdataset);
486224090Sdougb
487224090Sdougb unlock:
488224090Sdougb	UNLOCK(&ecdbnode->lock);
489224090Sdougb
490224090Sdougb	return (result);
491224090Sdougb}
492224090Sdougb
493224090Sdougbstatic isc_result_t
494224090Sdougbdeleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
495224090Sdougb	       dns_rdatatype_t type, dns_rdatatype_t covers)
496224090Sdougb{
497224090Sdougb	UNUSED(db);
498224090Sdougb	UNUSED(node);
499224090Sdougb	UNUSED(version);
500224090Sdougb	UNUSED(type);
501224090Sdougb	UNUSED(covers);
502224090Sdougb
503224090Sdougb	return (ISC_R_NOTIMPLEMENTED);
504224090Sdougb}
505224090Sdougb
506224090Sdougbstatic isc_result_t
507224090Sdougbcreateiterator(dns_db_t *db, unsigned int options,
508224090Sdougb	       dns_dbiterator_t **iteratorp)
509224090Sdougb{
510224090Sdougb	UNUSED(db);
511224090Sdougb	UNUSED(options);
512224090Sdougb	UNUSED(iteratorp);
513224090Sdougb
514224090Sdougb	return (ISC_R_NOTIMPLEMENTED);
515224090Sdougb}
516224090Sdougb
517224090Sdougbstatic isc_result_t
518224090Sdougballrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
519224090Sdougb	     isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
520224090Sdougb{
521224090Sdougb	dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
522224090Sdougb	dns_ecdbnode_t *ecdbnode = (dns_ecdbnode_t *)node;
523224090Sdougb	isc_mem_t *mctx;
524224090Sdougb	ecdb_rdatasetiter_t *iterator;
525224090Sdougb
526224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
527224090Sdougb	REQUIRE(VALID_ECDBNODE(ecdbnode));
528224090Sdougb
529224090Sdougb	mctx = ecdb->common.mctx;
530224090Sdougb
531224090Sdougb	iterator = isc_mem_get(mctx, sizeof(ecdb_rdatasetiter_t));
532224090Sdougb	if (iterator == NULL)
533224090Sdougb		return (ISC_R_NOMEMORY);
534224090Sdougb
535224090Sdougb	iterator->common.magic = DNS_RDATASETITER_MAGIC;
536224090Sdougb	iterator->common.methods = &rdatasetiter_methods;
537224090Sdougb	iterator->common.db = db;
538224090Sdougb	iterator->common.node = NULL;
539224090Sdougb	attachnode(db, node, &iterator->common.node);
540224090Sdougb	iterator->common.version = version;
541224090Sdougb	iterator->common.now = now;
542224090Sdougb
543224090Sdougb	*iteratorp = (dns_rdatasetiter_t *)iterator;
544224090Sdougb
545224090Sdougb	return (ISC_R_SUCCESS);
546224090Sdougb}
547224090Sdougb
548224090Sdougbstatic dns_dbmethods_t ecdb_methods = {
549224090Sdougb	attach,
550224090Sdougb	detach,
551224090Sdougb	NULL,			/* beginload */
552224090Sdougb	NULL,			/* endload */
553224090Sdougb	NULL,			/* dump */
554224090Sdougb	NULL,			/* currentversion */
555224090Sdougb	NULL,			/* newversion */
556224090Sdougb	NULL,			/* attachversion */
557224090Sdougb	NULL,			/* closeversion */
558224090Sdougb	findnode,
559224090Sdougb	find,
560224090Sdougb	findzonecut,
561224090Sdougb	attachnode,
562224090Sdougb	detachnode,
563224090Sdougb	NULL,			/* expirenode */
564224090Sdougb	NULL,			/* printnode */
565224090Sdougb	createiterator,		/* createiterator */
566224090Sdougb	NULL,			/* findrdataset */
567224090Sdougb	allrdatasets,
568224090Sdougb	addrdataset,
569224090Sdougb	NULL,			/* subtractrdataset */
570224090Sdougb	deleterdataset,
571224090Sdougb	NULL,			/* issecure */
572224090Sdougb	NULL,			/* nodecount */
573224090Sdougb	NULL,			/* ispersistent */
574224090Sdougb	NULL,			/* overmem */
575224090Sdougb	NULL,			/* settask */
576224090Sdougb	NULL,			/* getoriginnode */
577224090Sdougb	NULL,			/* transfernode */
578224090Sdougb	NULL,			/* getnsec3parameters */
579224090Sdougb	NULL,			/* findnsec3node */
580224090Sdougb	NULL,			/* setsigningtime */
581224090Sdougb	NULL,			/* getsigningtime */
582224090Sdougb	NULL,			/* resigned */
583224090Sdougb	NULL,			/* isdnssec */
584224090Sdougb	NULL,			/* getrrsetstats */
585224090Sdougb	NULL,			/* rpz_enabled */
586254897Serwin	NULL,			/* rpz_findips */
587254897Serwin	NULL,			/* findnodeext */
588254897Serwin	NULL			/* findext */
589224090Sdougb};
590224090Sdougb
591224090Sdougbstatic isc_result_t
592224090Sdougbdns_ecdb_create(isc_mem_t *mctx, dns_name_t *origin, dns_dbtype_t type,
593224090Sdougb		dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
594224090Sdougb		void *driverarg, dns_db_t **dbp)
595224090Sdougb{
596224090Sdougb	dns_ecdb_t *ecdb;
597224090Sdougb	isc_result_t result;
598224090Sdougb
599224090Sdougb	REQUIRE(mctx != NULL);
600224090Sdougb	REQUIRE(origin == dns_rootname);
601224090Sdougb	REQUIRE(type == dns_dbtype_cache);
602224090Sdougb	REQUIRE(dbp != NULL && *dbp == NULL);
603224090Sdougb
604224090Sdougb	UNUSED(argc);
605224090Sdougb	UNUSED(argv);
606224090Sdougb	UNUSED(driverarg);
607224090Sdougb
608224090Sdougb	ecdb = isc_mem_get(mctx, sizeof(*ecdb));
609224090Sdougb	if (ecdb == NULL)
610224090Sdougb		return (ISC_R_NOMEMORY);
611224090Sdougb
612224090Sdougb	ecdb->common.attributes = DNS_DBATTR_CACHE;
613224090Sdougb	ecdb->common.rdclass = rdclass;
614224090Sdougb	ecdb->common.methods = &ecdb_methods;
615224090Sdougb	dns_name_init(&ecdb->common.origin, NULL);
616224090Sdougb	result = dns_name_dupwithoffsets(origin, mctx, &ecdb->common.origin);
617224090Sdougb	if (result != ISC_R_SUCCESS) {
618224090Sdougb		isc_mem_put(mctx, ecdb, sizeof(*ecdb));
619224090Sdougb		return (result);
620224090Sdougb	}
621224090Sdougb
622224090Sdougb	result = isc_mutex_init(&ecdb->lock);
623224090Sdougb	if (result != ISC_R_SUCCESS) {
624224090Sdougb		UNEXPECTED_ERROR(__FILE__, __LINE__,
625224090Sdougb				 "isc_mutex_init() failed: %s",
626224090Sdougb				 isc_result_totext(result));
627224090Sdougb		if (dns_name_dynamic(&ecdb->common.origin))
628224090Sdougb			dns_name_free(&ecdb->common.origin, mctx);
629224090Sdougb		isc_mem_put(mctx, ecdb, sizeof(*ecdb));
630224090Sdougb		return (ISC_R_UNEXPECTED);
631224090Sdougb	}
632224090Sdougb
633224090Sdougb	ecdb->references = 1;
634224090Sdougb	ISC_LIST_INIT(ecdb->nodes);
635224090Sdougb
636224090Sdougb	ecdb->common.mctx = NULL;
637224090Sdougb	isc_mem_attach(mctx, &ecdb->common.mctx);
638224090Sdougb	ecdb->common.impmagic = ECDB_MAGIC;
639224090Sdougb	ecdb->common.magic = DNS_DB_MAGIC;
640224090Sdougb
641224090Sdougb	*dbp = (dns_db_t *)ecdb;
642224090Sdougb
643224090Sdougb	return (ISC_R_SUCCESS);
644224090Sdougb}
645224090Sdougb
646224090Sdougb/*%
647224090Sdougb * Rdataset Methods
648224090Sdougb */
649224090Sdougb
650224090Sdougbstatic void
651224090Sdougbrdataset_disassociate(dns_rdataset_t *rdataset) {
652224090Sdougb	dns_db_t *db = rdataset->private1;
653224090Sdougb	dns_dbnode_t *node = rdataset->private2;
654224090Sdougb
655224090Sdougb	dns_db_detachnode(db, &node);
656224090Sdougb}
657224090Sdougb
658224090Sdougbstatic isc_result_t
659224090Sdougbrdataset_first(dns_rdataset_t *rdataset) {
660224090Sdougb	unsigned char *raw = rdataset->private3;
661224090Sdougb	unsigned int count;
662224090Sdougb
663224090Sdougb	count = raw[0] * 256 + raw[1];
664224090Sdougb	if (count == 0) {
665224090Sdougb		rdataset->private5 = NULL;
666224090Sdougb		return (ISC_R_NOMORE);
667224090Sdougb	}
668234010Sdougb#if DNS_RDATASET_FIXED
669234010Sdougb	raw += 2 + (4 * count);
670234010Sdougb#else
671224090Sdougb	raw += 2;
672234010Sdougb#endif
673224090Sdougb	/*
674224090Sdougb	 * The privateuint4 field is the number of rdata beyond the cursor
675224090Sdougb	 * position, so we decrement the total count by one before storing
676224090Sdougb	 * it.
677224090Sdougb	 */
678224090Sdougb	count--;
679224090Sdougb	rdataset->privateuint4 = count;
680224090Sdougb	rdataset->private5 = raw;
681224090Sdougb
682224090Sdougb	return (ISC_R_SUCCESS);
683224090Sdougb}
684224090Sdougb
685224090Sdougbstatic isc_result_t
686224090Sdougbrdataset_next(dns_rdataset_t *rdataset) {
687224090Sdougb	unsigned int count;
688224090Sdougb	unsigned int length;
689224090Sdougb	unsigned char *raw;
690224090Sdougb
691224090Sdougb	count = rdataset->privateuint4;
692224090Sdougb	if (count == 0)
693224090Sdougb		return (ISC_R_NOMORE);
694224090Sdougb	count--;
695224090Sdougb	rdataset->privateuint4 = count;
696224090Sdougb	raw = rdataset->private5;
697224090Sdougb	length = raw[0] * 256 + raw[1];
698234010Sdougb#if DNS_RDATASET_FIXED
699234010Sdougb	raw += length + 4;
700234010Sdougb#else
701224090Sdougb	raw += length + 2;
702234010Sdougb#endif
703224090Sdougb	rdataset->private5 = raw;
704224090Sdougb
705224090Sdougb	return (ISC_R_SUCCESS);
706224090Sdougb}
707224090Sdougb
708224090Sdougbstatic void
709224090Sdougbrdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
710224090Sdougb	unsigned char *raw = rdataset->private5;
711224090Sdougb	isc_region_t r;
712224090Sdougb	unsigned int length;
713224090Sdougb	unsigned int flags = 0;
714224090Sdougb
715224090Sdougb	REQUIRE(raw != NULL);
716224090Sdougb
717224090Sdougb	length = raw[0] * 256 + raw[1];
718234010Sdougb#if DNS_RDATASET_FIXED
719234010Sdougb	raw += 4;
720234010Sdougb#else
721224090Sdougb	raw += 2;
722234010Sdougb#endif
723224090Sdougb	if (rdataset->type == dns_rdatatype_rrsig) {
724224090Sdougb		if (*raw & DNS_RDATASLAB_OFFLINE)
725224090Sdougb			flags |= DNS_RDATA_OFFLINE;
726224090Sdougb		length--;
727224090Sdougb		raw++;
728224090Sdougb	}
729224090Sdougb	r.length = length;
730224090Sdougb	r.base = raw;
731224090Sdougb	dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
732224090Sdougb	rdata->flags |= flags;
733224090Sdougb}
734224090Sdougb
735224090Sdougbstatic void
736224090Sdougbrdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
737224090Sdougb	dns_db_t *db = source->private1;
738224090Sdougb	dns_dbnode_t *node = source->private2;
739224090Sdougb	dns_dbnode_t *cloned_node = NULL;
740224090Sdougb
741224090Sdougb	attachnode(db, node, &cloned_node);
742224090Sdougb	*target = *source;
743224090Sdougb
744224090Sdougb	/*
745224090Sdougb	 * Reset iterator state.
746224090Sdougb	 */
747224090Sdougb	target->privateuint4 = 0;
748224090Sdougb	target->private5 = NULL;
749224090Sdougb}
750224090Sdougb
751224090Sdougbstatic unsigned int
752224090Sdougbrdataset_count(dns_rdataset_t *rdataset) {
753224090Sdougb	unsigned char *raw = rdataset->private3;
754224090Sdougb	unsigned int count;
755224090Sdougb
756224090Sdougb	count = raw[0] * 256 + raw[1];
757224090Sdougb
758224090Sdougb	return (count);
759224090Sdougb}
760224090Sdougb
761224090Sdougbstatic void
762224090Sdougbrdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
763224090Sdougb	rdatasetheader_t *header = rdataset->private3;
764224090Sdougb
765224090Sdougb	header--;
766224090Sdougb	header->trust = rdataset->trust = trust;
767224090Sdougb}
768224090Sdougb
769224090Sdougb/*
770224090Sdougb * Rdataset Iterator Methods
771224090Sdougb */
772224090Sdougb
773224090Sdougbstatic void
774224090Sdougbrdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
775224090Sdougb	ecdb_rdatasetiter_t *ecdbiterator;
776224090Sdougb	isc_mem_t *mctx;
777224090Sdougb
778224090Sdougb	REQUIRE(iteratorp != NULL);
779224090Sdougb	ecdbiterator = (ecdb_rdatasetiter_t *)*iteratorp;
780224090Sdougb	REQUIRE(DNS_RDATASETITER_VALID(&ecdbiterator->common));
781224090Sdougb
782224090Sdougb	mctx = ecdbiterator->common.db->mctx;
783224090Sdougb
784224090Sdougb	ecdbiterator->common.magic = 0;
785224090Sdougb
786224090Sdougb	dns_db_detachnode(ecdbiterator->common.db, &ecdbiterator->common.node);
787224090Sdougb	isc_mem_put(mctx, ecdbiterator, sizeof(ecdb_rdatasetiter_t));
788224090Sdougb
789224090Sdougb	*iteratorp = NULL;
790224090Sdougb}
791224090Sdougb
792224090Sdougbstatic isc_result_t
793224090Sdougbrdatasetiter_first(dns_rdatasetiter_t *iterator) {
794224090Sdougb	ecdb_rdatasetiter_t *ecdbiterator = (ecdb_rdatasetiter_t *)iterator;
795224090Sdougb	dns_ecdbnode_t *ecdbnode = (dns_ecdbnode_t *)iterator->node;
796224090Sdougb
797224090Sdougb	REQUIRE(DNS_RDATASETITER_VALID(iterator));
798224090Sdougb
799224090Sdougb	if (ISC_LIST_EMPTY(ecdbnode->rdatasets))
800224090Sdougb		return (ISC_R_NOMORE);
801224090Sdougb	ecdbiterator->current = ISC_LIST_HEAD(ecdbnode->rdatasets);
802224090Sdougb	return (ISC_R_SUCCESS);
803224090Sdougb}
804224090Sdougb
805224090Sdougbstatic isc_result_t
806224090Sdougbrdatasetiter_next(dns_rdatasetiter_t *iterator) {
807224090Sdougb	ecdb_rdatasetiter_t *ecdbiterator = (ecdb_rdatasetiter_t *)iterator;
808224090Sdougb
809224090Sdougb	REQUIRE(DNS_RDATASETITER_VALID(iterator));
810224090Sdougb
811224090Sdougb	ecdbiterator->current = ISC_LIST_NEXT(ecdbiterator->current, link);
812224090Sdougb	if (ecdbiterator->current == NULL)
813224090Sdougb		return (ISC_R_NOMORE);
814224090Sdougb	else
815224090Sdougb		return (ISC_R_SUCCESS);
816224090Sdougb}
817224090Sdougb
818224090Sdougbstatic void
819224090Sdougbrdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
820224090Sdougb	ecdb_rdatasetiter_t *ecdbiterator = (ecdb_rdatasetiter_t *)iterator;
821224090Sdougb	dns_ecdb_t *ecdb;
822224090Sdougb
823224090Sdougb	ecdb = (dns_ecdb_t *)iterator->db;
824224090Sdougb	REQUIRE(VALID_ECDB(ecdb));
825224090Sdougb
826224090Sdougb	bind_rdataset(ecdb, iterator->node, ecdbiterator->current, rdataset);
827224090Sdougb}
828