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