xref: /netbsd-src/external/mpl/bind/dist/bin/tests/system/dyndb/driver/zone.c (revision 345cf9fb81bd0411c53e25d62cd93bdcaa865312)
1 /*	$NetBSD: zone.c,v 1.6 2024/02/21 22:51:27 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0 AND ISC
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 /*
17  * Copyright (C) Red Hat
18  *
19  * Permission to use, copy, modify, and/or distribute this software for any
20  * purpose with or without fee is hereby granted, provided that the above
21  * copyright notice and this permission notice appear in all copies.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
24  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
25  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
26  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
27  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
28  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
29  * PERFORMANCE OF THIS SOFTWARE.
30  */
31 
32 /*
33  * Zone management.
34  */
35 
36 #include "zone.h"
37 #include <inttypes.h>
38 #include <stdbool.h>
39 
40 #include <isc/util.h>
41 
42 #include <dns/dyndb.h>
43 #include <dns/view.h>
44 #include <dns/zone.h>
45 
46 #include "instance.h"
47 #include "lock.h"
48 #include "log.h"
49 #include "util.h"
50 
51 extern const char *impname;
52 
53 /*
54  * Create a new zone with origin 'name'. The zone stay invisible to clients
55  * until it is explicitly added to a view.
56  */
57 isc_result_t
58 create_zone(sample_instance_t *const inst, dns_name_t *const name,
59 	    dns_zone_t **const rawp) {
60 	isc_result_t result;
61 	dns_zone_t *raw = NULL;
62 	const char *zone_argv[1];
63 	char zone_name[DNS_NAME_FORMATSIZE];
64 	dns_acl_t *acl_any = NULL;
65 
66 	REQUIRE(inst != NULL);
67 	REQUIRE(name != NULL);
68 	REQUIRE(rawp != NULL && *rawp == NULL);
69 
70 	zone_argv[0] = inst->db_name;
71 
72 	result = dns_zone_create(&raw, inst->mctx);
73 	if (result != ISC_R_SUCCESS) {
74 		log_write(ISC_LOG_ERROR, "create_zone: dns_zone_create -> %s\n",
75 			  isc_result_totext(result));
76 		goto cleanup;
77 	}
78 	result = dns_zone_setorigin(raw, name);
79 	if (result != ISC_R_SUCCESS) {
80 		log_write(ISC_LOG_ERROR,
81 			  "create_zone: dns_zone_setorigin -> %s\n",
82 			  isc_result_totext(result));
83 		goto cleanup;
84 	}
85 	dns_zone_setclass(raw, dns_rdataclass_in);
86 	dns_zone_settype(raw, dns_zone_primary);
87 	dns_zone_setdbtype(raw, 1, zone_argv);
88 
89 	result = dns_zonemgr_managezone(inst->zmgr, raw);
90 	if (result != ISC_R_SUCCESS) {
91 		log_write(ISC_LOG_ERROR,
92 			  "create_zone: dns_zonemgr_managezone -> %s\n",
93 			  isc_result_totext(result));
94 		goto cleanup;
95 	}
96 
97 	/* This is completely insecure - use some sensible values instead! */
98 	result = dns_acl_any(inst->mctx, &acl_any);
99 	if (result != ISC_R_SUCCESS) {
100 		log_write(ISC_LOG_ERROR, "create_zone: dns_acl_any -> %s\n",
101 			  isc_result_totext(result));
102 		goto cleanup;
103 	}
104 	dns_zone_setupdateacl(raw, acl_any);
105 	dns_zone_setqueryacl(raw, acl_any);
106 	dns_zone_setxfracl(raw, acl_any);
107 	dns_acl_detach(&acl_any);
108 
109 	*rawp = raw;
110 	return (ISC_R_SUCCESS);
111 
112 cleanup:
113 	dns_name_format(name, zone_name, DNS_NAME_FORMATSIZE);
114 	log_error_r("failed to create new zone '%s'", zone_name);
115 
116 	if (raw != NULL) {
117 		if (dns_zone_getmgr(raw) != NULL) {
118 			dns_zonemgr_releasezone(inst->zmgr, raw);
119 		}
120 		dns_zone_detach(&raw);
121 	}
122 	if (acl_any != NULL) {
123 		dns_acl_detach(&acl_any);
124 	}
125 
126 	return (result);
127 }
128 
129 /*
130  * Add zone to the view defined in inst->view. This will make the zone visible
131  * to clients.
132  */
133 static isc_result_t
134 publish_zone(sample_instance_t *inst, dns_zone_t *zone) {
135 	isc_result_t result;
136 	bool freeze = false;
137 	dns_zone_t *zone_in_view = NULL;
138 	dns_view_t *view_in_zone = NULL;
139 	isc_result_t lock_state = ISC_R_IGNORE;
140 
141 	REQUIRE(inst != NULL);
142 	REQUIRE(zone != NULL);
143 
144 	/* Return success if the zone is already in the view as expected. */
145 	result = dns_view_findzone(inst->view, dns_zone_getorigin(zone),
146 				   &zone_in_view);
147 	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
148 		goto cleanup;
149 	}
150 
151 	view_in_zone = dns_zone_getview(zone);
152 	if (view_in_zone != NULL) {
153 		/* Zone has a view set -> view should contain the same zone. */
154 		if (zone_in_view == zone) {
155 			/* Zone is already published in the right view. */
156 			CLEANUP_WITH(ISC_R_SUCCESS);
157 		} else if (view_in_zone != inst->view) {
158 			/*
159 			 * Un-published inactive zone will have
160 			 * inst->view in zone but will not be present
161 			 * in the view itself.
162 			 */
163 			dns_zone_log(zone, ISC_LOG_ERROR,
164 				     "zone->view doesn't "
165 				     "match data in the view");
166 			CLEANUP_WITH(ISC_R_UNEXPECTED);
167 		}
168 	}
169 
170 	if (zone_in_view != NULL) {
171 		dns_zone_log(zone, ISC_LOG_ERROR,
172 			     "cannot publish zone: view already "
173 			     "contains another zone with this name");
174 		CLEANUP_WITH(ISC_R_UNEXPECTED);
175 	}
176 
177 	run_exclusive_enter(inst, &lock_state);
178 	if (inst->view->frozen) {
179 		freeze = true;
180 		dns_view_thaw(inst->view);
181 	}
182 
183 	dns_zone_setview(zone, inst->view);
184 	result = dns_view_addzone(inst->view, zone);
185 	if (result != ISC_R_SUCCESS) {
186 		log_write(ISC_LOG_ERROR,
187 			  "publish_zone: dns_view_addzone -> %s\n",
188 			  isc_result_totext(result));
189 		goto cleanup;
190 	}
191 
192 cleanup:
193 	if (zone_in_view != NULL) {
194 		dns_zone_detach(&zone_in_view);
195 	}
196 	if (freeze) {
197 		dns_view_freeze(inst->view);
198 	}
199 	run_exclusive_exit(inst, lock_state);
200 
201 	return (result);
202 }
203 
204 /*
205  * @warning Never call this on raw part of in-line secure zone, call it only
206  * on the secure zone!
207  */
208 static isc_result_t
209 load_zone(dns_zone_t *zone) {
210 	isc_result_t result;
211 	bool zone_dynamic;
212 	uint32_t serial;
213 
214 	result = dns_zone_load(zone, false);
215 	if (result != ISC_R_SUCCESS && result != DNS_R_UPTODATE &&
216 	    result != DNS_R_DYNAMIC && result != DNS_R_CONTINUE)
217 	{
218 		goto cleanup;
219 	}
220 	zone_dynamic = (result == DNS_R_DYNAMIC);
221 
222 	result = dns_zone_getserial(zone, &serial);
223 	if (result != ISC_R_SUCCESS) {
224 		log_write(ISC_LOG_ERROR,
225 			  "load_zone: dns_zone_getserial -> %s\n",
226 			  isc_result_totext(result));
227 		goto cleanup;
228 	}
229 	dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u", serial);
230 
231 	if (zone_dynamic) {
232 		dns_zone_notify(zone);
233 	}
234 
235 cleanup:
236 	return (result);
237 }
238 
239 /*
240  * Add zone to view and call dns_zone_load().
241  */
242 isc_result_t
243 activate_zone(sample_instance_t *inst, dns_zone_t *raw) {
244 	isc_result_t result;
245 
246 	/*
247 	 * Zone has to be published *before* zone load
248 	 * otherwise it will race with zone->view != NULL check
249 	 * in zone_maintenance() in zone.c.
250 	 */
251 	result = publish_zone(inst, raw);
252 	if (result != ISC_R_SUCCESS) {
253 		dns_zone_log(raw, ISC_LOG_ERROR, "cannot add zone to view: %s",
254 			     isc_result_totext(result));
255 		goto cleanup;
256 	}
257 
258 	result = load_zone(raw);
259 	if (result != ISC_R_SUCCESS) {
260 		log_write(ISC_LOG_ERROR, "activate_zone: load_zone -> %s\n",
261 			  isc_result_totext(result));
262 		goto cleanup;
263 	}
264 
265 cleanup:
266 	return (result);
267 }
268