1/*	$NetBSD: rriterator.c,v 1.8 2024/02/21 22:52:08 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18/***
19 *** Imports
20 ***/
21
22#include <inttypes.h>
23
24#include <isc/result.h>
25#include <isc/string.h>
26#include <isc/util.h>
27
28#include <dns/db.h>
29#include <dns/dbiterator.h>
30#include <dns/rdata.h>
31#include <dns/rdataset.h>
32#include <dns/rdatasetiter.h>
33#include <dns/rriterator.h>
34
35/***
36 *** RRiterator methods
37 ***/
38
39isc_result_t
40dns_rriterator_init(dns_rriterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
41		    isc_stdtime_t now) {
42	isc_result_t result;
43	it->magic = RRITERATOR_MAGIC;
44	it->db = db;
45	it->dbit = NULL;
46	it->ver = ver;
47	it->now = now;
48	it->node = NULL;
49	result = dns_db_createiterator(it->db, 0, &it->dbit);
50	if (result != ISC_R_SUCCESS) {
51		return (result);
52	}
53	it->rdatasetit = NULL;
54	dns_rdata_init(&it->rdata);
55	dns_rdataset_init(&it->rdataset);
56	dns_fixedname_init(&it->fixedname);
57	INSIST(!dns_rdataset_isassociated(&it->rdataset));
58	it->result = ISC_R_SUCCESS;
59	return (it->result);
60}
61
62isc_result_t
63dns_rriterator_first(dns_rriterator_t *it) {
64	REQUIRE(VALID_RRITERATOR(it));
65	/* Reset state */
66	if (dns_rdataset_isassociated(&it->rdataset)) {
67		dns_rdataset_disassociate(&it->rdataset);
68	}
69	if (it->rdatasetit != NULL) {
70		dns_rdatasetiter_destroy(&it->rdatasetit);
71	}
72	if (it->node != NULL) {
73		dns_db_detachnode(it->db, &it->node);
74	}
75	it->result = dns_dbiterator_first(it->dbit);
76
77	/*
78	 * The top node may be empty when out of zone glue exists.
79	 * Walk the tree to find the first node with data.
80	 */
81	while (it->result == ISC_R_SUCCESS) {
82		it->result = dns_dbiterator_current(
83			it->dbit, &it->node,
84			dns_fixedname_name(&it->fixedname));
85		if (it->result != ISC_R_SUCCESS) {
86			return (it->result);
87		}
88
89		it->result = dns_db_allrdatasets(it->db, it->node, it->ver, 0,
90						 it->now, &it->rdatasetit);
91		if (it->result != ISC_R_SUCCESS) {
92			return (it->result);
93		}
94
95		it->result = dns_rdatasetiter_first(it->rdatasetit);
96		if (it->result != ISC_R_SUCCESS) {
97			/*
98			 * This node is empty. Try next node.
99			 */
100			dns_rdatasetiter_destroy(&it->rdatasetit);
101			dns_db_detachnode(it->db, &it->node);
102			it->result = dns_dbiterator_next(it->dbit);
103			continue;
104		}
105		dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
106		dns_rdataset_getownercase(&it->rdataset,
107					  dns_fixedname_name(&it->fixedname));
108		it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
109		it->result = dns_rdataset_first(&it->rdataset);
110		return (it->result);
111	}
112	return (it->result);
113}
114
115isc_result_t
116dns_rriterator_nextrrset(dns_rriterator_t *it) {
117	REQUIRE(VALID_RRITERATOR(it));
118	if (dns_rdataset_isassociated(&it->rdataset)) {
119		dns_rdataset_disassociate(&it->rdataset);
120	}
121	it->result = dns_rdatasetiter_next(it->rdatasetit);
122	/*
123	 * The while loop body is executed more than once
124	 * only when an empty dbnode needs to be skipped.
125	 */
126	while (it->result == ISC_R_NOMORE) {
127		dns_rdatasetiter_destroy(&it->rdatasetit);
128		dns_db_detachnode(it->db, &it->node);
129		it->result = dns_dbiterator_next(it->dbit);
130		if (it->result == ISC_R_NOMORE) {
131			/* We are at the end of the entire database. */
132			return (it->result);
133		}
134		if (it->result != ISC_R_SUCCESS) {
135			return (it->result);
136		}
137		it->result = dns_dbiterator_current(
138			it->dbit, &it->node,
139			dns_fixedname_name(&it->fixedname));
140		if (it->result != ISC_R_SUCCESS) {
141			return (it->result);
142		}
143		it->result = dns_db_allrdatasets(it->db, it->node, it->ver, 0,
144						 it->now, &it->rdatasetit);
145		if (it->result != ISC_R_SUCCESS) {
146			return (it->result);
147		}
148		it->result = dns_rdatasetiter_first(it->rdatasetit);
149	}
150	if (it->result != ISC_R_SUCCESS) {
151		return (it->result);
152	}
153	dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
154	dns_rdataset_getownercase(&it->rdataset,
155				  dns_fixedname_name(&it->fixedname));
156	it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
157	it->result = dns_rdataset_first(&it->rdataset);
158	return (it->result);
159}
160
161isc_result_t
162dns_rriterator_next(dns_rriterator_t *it) {
163	REQUIRE(VALID_RRITERATOR(it));
164	if (it->result != ISC_R_SUCCESS) {
165		return (it->result);
166	}
167
168	INSIST(it->dbit != NULL);
169	INSIST(it->node != NULL);
170	INSIST(it->rdatasetit != NULL);
171
172	it->result = dns_rdataset_next(&it->rdataset);
173	if (it->result == ISC_R_NOMORE) {
174		return (dns_rriterator_nextrrset(it));
175	}
176	return (it->result);
177}
178
179void
180dns_rriterator_pause(dns_rriterator_t *it) {
181	REQUIRE(VALID_RRITERATOR(it));
182	RUNTIME_CHECK(dns_dbiterator_pause(it->dbit) == ISC_R_SUCCESS);
183}
184
185void
186dns_rriterator_destroy(dns_rriterator_t *it) {
187	REQUIRE(VALID_RRITERATOR(it));
188	if (dns_rdataset_isassociated(&it->rdataset)) {
189		dns_rdataset_disassociate(&it->rdataset);
190	}
191	if (it->rdatasetit != NULL) {
192		dns_rdatasetiter_destroy(&it->rdatasetit);
193	}
194	if (it->node != NULL) {
195		dns_db_detachnode(it->db, &it->node);
196	}
197	dns_dbiterator_destroy(&it->dbit);
198}
199
200void
201dns_rriterator_current(dns_rriterator_t *it, dns_name_t **name, uint32_t *ttl,
202		       dns_rdataset_t **rdataset, dns_rdata_t **rdata) {
203	REQUIRE(name != NULL && *name == NULL);
204	REQUIRE(VALID_RRITERATOR(it));
205	REQUIRE(it->result == ISC_R_SUCCESS);
206	REQUIRE(rdataset == NULL || *rdataset == NULL);
207	REQUIRE(rdata == NULL || *rdata == NULL);
208
209	*name = dns_fixedname_name(&it->fixedname);
210	*ttl = it->rdataset.ttl;
211
212	dns_rdata_reset(&it->rdata);
213	dns_rdataset_current(&it->rdataset, &it->rdata);
214
215	if (rdataset != NULL) {
216		*rdataset = &it->rdataset;
217	}
218
219	if (rdata != NULL) {
220		*rdata = &it->rdata;
221	}
222}
223