1 1.9 christos /* $NetBSD: soa_6.c,v 1.10 2026/01/29 18:37:53 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.7 christos * SPDX-License-Identifier: MPL-2.0 7 1.7 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.6 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos #ifndef RDATA_GENERIC_SOA_6_C 17 1.1 christos #define RDATA_GENERIC_SOA_6_C 18 1.1 christos 19 1.1 christos #define RRTYPE_SOA_ATTRIBUTES (DNS_RDATATYPEATTR_SINGLETON) 20 1.1 christos 21 1.7 christos static isc_result_t 22 1.1 christos fromtext_soa(ARGS_FROMTEXT) { 23 1.1 christos isc_token_t token; 24 1.1 christos dns_name_t name; 25 1.1 christos isc_buffer_t buffer; 26 1.1 christos int i; 27 1.3 christos uint32_t n; 28 1.3 christos bool ok; 29 1.1 christos 30 1.1 christos REQUIRE(type == dns_rdatatype_soa); 31 1.1 christos 32 1.1 christos UNUSED(type); 33 1.1 christos UNUSED(rdclass); 34 1.1 christos UNUSED(callbacks); 35 1.1 christos 36 1.5 christos if (origin == NULL) { 37 1.1 christos origin = dns_rootname; 38 1.5 christos } 39 1.1 christos 40 1.1 christos for (i = 0; i < 2; i++) { 41 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, 42 1.5 christos isc_tokentype_string, false)); 43 1.1 christos 44 1.1 christos dns_name_init(&name, NULL); 45 1.1 christos buffer_fromregion(&buffer, &token.value.as_region); 46 1.5 christos RETTOK(dns_name_fromtext(&name, &buffer, origin, options, 47 1.5 christos target)); 48 1.3 christos ok = true; 49 1.5 christos if ((options & DNS_RDATA_CHECKNAMES) != 0) { 50 1.1 christos switch (i) { 51 1.1 christos case 0: 52 1.3 christos ok = dns_name_ishostname(&name, false); 53 1.1 christos break; 54 1.1 christos case 1: 55 1.1 christos ok = dns_name_ismailbox(&name); 56 1.1 christos break; 57 1.1 christos } 58 1.5 christos } 59 1.5 christos if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0) { 60 1.1 christos RETTOK(DNS_R_BADNAME); 61 1.5 christos } 62 1.5 christos if (!ok && callbacks != NULL) { 63 1.1 christos warn_badname(&name, lexer, callbacks); 64 1.5 christos } 65 1.1 christos } 66 1.1 christos 67 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 68 1.3 christos false)); 69 1.1 christos RETERR(uint32_tobuffer(token.value.as_ulong, target)); 70 1.1 christos 71 1.1 christos for (i = 0; i < 4; i++) { 72 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, 73 1.5 christos isc_tokentype_string, false)); 74 1.1 christos RETTOK(dns_counter_fromtext(&token.value.as_textregion, &n)); 75 1.1 christos RETERR(uint32_tobuffer(n, target)); 76 1.1 christos } 77 1.1 christos 78 1.9 christos return ISC_R_SUCCESS; 79 1.1 christos } 80 1.1 christos 81 1.5 christos static const char *soa_fieldnames[5] = { "serial", "refresh", "retry", "expire", 82 1.5 christos "minimum" }; 83 1.1 christos 84 1.7 christos static isc_result_t 85 1.1 christos totext_soa(ARGS_TOTEXT) { 86 1.1 christos isc_region_t dregion; 87 1.1 christos dns_name_t mname; 88 1.1 christos dns_name_t rname; 89 1.1 christos dns_name_t prefix; 90 1.9 christos unsigned int opts; 91 1.1 christos int i; 92 1.3 christos bool multiline; 93 1.3 christos bool comm; 94 1.1 christos 95 1.1 christos REQUIRE(rdata->type == dns_rdatatype_soa); 96 1.1 christos REQUIRE(rdata->length != 0); 97 1.1 christos 98 1.3 christos multiline = ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0); 99 1.3 christos if (multiline) { 100 1.3 christos comm = ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0); 101 1.3 christos } else { 102 1.3 christos comm = false; 103 1.3 christos } 104 1.1 christos 105 1.1 christos dns_name_init(&mname, NULL); 106 1.1 christos dns_name_init(&rname, NULL); 107 1.1 christos dns_name_init(&prefix, NULL); 108 1.1 christos 109 1.1 christos dns_rdata_toregion(rdata, &dregion); 110 1.1 christos 111 1.1 christos dns_name_fromregion(&mname, &dregion); 112 1.1 christos isc_region_consume(&dregion, name_length(&mname)); 113 1.1 christos 114 1.1 christos dns_name_fromregion(&rname, &dregion); 115 1.1 christos isc_region_consume(&dregion, name_length(&rname)); 116 1.1 christos 117 1.9 christos opts = name_prefix(&mname, tctx->origin, &prefix) 118 1.9 christos ? DNS_NAME_OMITFINALDOT 119 1.9 christos : 0; 120 1.9 christos RETERR(dns_name_totext(&prefix, opts, target)); 121 1.1 christos 122 1.1 christos RETERR(str_totext(" ", target)); 123 1.1 christos 124 1.9 christos opts = name_prefix(&rname, tctx->origin, &prefix) 125 1.9 christos ? DNS_NAME_OMITFINALDOT 126 1.9 christos : 0; 127 1.9 christos RETERR(dns_name_totext(&prefix, opts, target)); 128 1.1 christos 129 1.5 christos if (multiline) { 130 1.5 christos RETERR(str_totext(" (", target)); 131 1.5 christos } 132 1.1 christos RETERR(str_totext(tctx->linebreak, target)); 133 1.1 christos 134 1.1 christos for (i = 0; i < 5; i++) { 135 1.1 christos char buf[sizeof("0123456789 ; ")]; 136 1.1 christos unsigned long num; 137 1.1 christos num = uint32_fromregion(&dregion); 138 1.1 christos isc_region_consume(&dregion, 4); 139 1.1 christos snprintf(buf, sizeof(buf), comm ? "%-10lu ; " : "%lu", num); 140 1.1 christos RETERR(str_totext(buf, target)); 141 1.1 christos if (comm) { 142 1.1 christos RETERR(str_totext(soa_fieldnames[i], target)); 143 1.1 christos /* Print times in week/day/hour/minute/second form */ 144 1.1 christos if (i >= 1) { 145 1.1 christos RETERR(str_totext(" (", target)); 146 1.5 christos RETERR(dns_ttl_totext(num, true, true, target)); 147 1.1 christos RETERR(str_totext(")", target)); 148 1.1 christos } 149 1.1 christos RETERR(str_totext(tctx->linebreak, target)); 150 1.1 christos } else if (i < 4) { 151 1.1 christos RETERR(str_totext(tctx->linebreak, target)); 152 1.1 christos } 153 1.1 christos } 154 1.1 christos 155 1.5 christos if (multiline) { 156 1.1 christos RETERR(str_totext(")", target)); 157 1.5 christos } 158 1.1 christos 159 1.9 christos return ISC_R_SUCCESS; 160 1.1 christos } 161 1.1 christos 162 1.7 christos static isc_result_t 163 1.1 christos fromwire_soa(ARGS_FROMWIRE) { 164 1.1 christos dns_name_t mname; 165 1.1 christos dns_name_t rname; 166 1.1 christos isc_region_t sregion; 167 1.1 christos isc_region_t tregion; 168 1.1 christos 169 1.1 christos REQUIRE(type == dns_rdatatype_soa); 170 1.1 christos 171 1.1 christos UNUSED(type); 172 1.1 christos UNUSED(rdclass); 173 1.1 christos 174 1.9 christos dctx = dns_decompress_setpermitted(dctx, true); 175 1.1 christos 176 1.1 christos dns_name_init(&mname, NULL); 177 1.1 christos dns_name_init(&rname, NULL); 178 1.1 christos 179 1.9 christos RETERR(dns_name_fromwire(&mname, source, dctx, target)); 180 1.9 christos RETERR(dns_name_fromwire(&rname, source, dctx, target)); 181 1.1 christos 182 1.1 christos isc_buffer_activeregion(source, &sregion); 183 1.1 christos isc_buffer_availableregion(target, &tregion); 184 1.1 christos 185 1.5 christos if (sregion.length < 20) { 186 1.9 christos return ISC_R_UNEXPECTEDEND; 187 1.5 christos } 188 1.5 christos if (tregion.length < 20) { 189 1.9 christos return ISC_R_NOSPACE; 190 1.5 christos } 191 1.1 christos 192 1.1 christos memmove(tregion.base, sregion.base, 20); 193 1.1 christos isc_buffer_forward(source, 20); 194 1.1 christos isc_buffer_add(target, 20); 195 1.1 christos 196 1.9 christos return ISC_R_SUCCESS; 197 1.1 christos } 198 1.1 christos 199 1.7 christos static isc_result_t 200 1.1 christos towire_soa(ARGS_TOWIRE) { 201 1.1 christos isc_region_t sregion; 202 1.1 christos isc_region_t tregion; 203 1.1 christos dns_name_t mname; 204 1.1 christos dns_name_t rname; 205 1.1 christos dns_offsets_t moffsets; 206 1.1 christos dns_offsets_t roffsets; 207 1.1 christos 208 1.1 christos REQUIRE(rdata->type == dns_rdatatype_soa); 209 1.1 christos REQUIRE(rdata->length != 0); 210 1.1 christos 211 1.9 christos dns_compress_setpermitted(cctx, true); 212 1.1 christos 213 1.1 christos dns_name_init(&mname, moffsets); 214 1.1 christos dns_name_init(&rname, roffsets); 215 1.1 christos 216 1.1 christos dns_rdata_toregion(rdata, &sregion); 217 1.1 christos 218 1.1 christos dns_name_fromregion(&mname, &sregion); 219 1.1 christos isc_region_consume(&sregion, name_length(&mname)); 220 1.9 christos RETERR(dns_name_towire(&mname, cctx, target, NULL)); 221 1.1 christos 222 1.1 christos dns_name_fromregion(&rname, &sregion); 223 1.1 christos isc_region_consume(&sregion, name_length(&rname)); 224 1.9 christos RETERR(dns_name_towire(&rname, cctx, target, NULL)); 225 1.1 christos 226 1.1 christos isc_buffer_availableregion(target, &tregion); 227 1.5 christos if (tregion.length < 20) { 228 1.9 christos return ISC_R_NOSPACE; 229 1.5 christos } 230 1.1 christos 231 1.1 christos memmove(tregion.base, sregion.base, 20); 232 1.1 christos isc_buffer_add(target, 20); 233 1.9 christos return ISC_R_SUCCESS; 234 1.1 christos } 235 1.1 christos 236 1.7 christos static int 237 1.1 christos compare_soa(ARGS_COMPARE) { 238 1.1 christos isc_region_t region1; 239 1.1 christos isc_region_t region2; 240 1.1 christos dns_name_t name1; 241 1.1 christos dns_name_t name2; 242 1.1 christos int order; 243 1.1 christos 244 1.1 christos REQUIRE(rdata1->type == rdata2->type); 245 1.1 christos REQUIRE(rdata1->rdclass == rdata2->rdclass); 246 1.1 christos REQUIRE(rdata1->type == dns_rdatatype_soa); 247 1.1 christos REQUIRE(rdata1->length != 0); 248 1.1 christos REQUIRE(rdata2->length != 0); 249 1.1 christos 250 1.1 christos dns_name_init(&name1, NULL); 251 1.1 christos dns_name_init(&name2, NULL); 252 1.1 christos 253 1.1 christos dns_rdata_toregion(rdata1, ®ion1); 254 1.1 christos dns_rdata_toregion(rdata2, ®ion2); 255 1.1 christos 256 1.1 christos dns_name_fromregion(&name1, ®ion1); 257 1.1 christos dns_name_fromregion(&name2, ®ion2); 258 1.1 christos 259 1.1 christos order = dns_name_rdatacompare(&name1, &name2); 260 1.5 christos if (order != 0) { 261 1.9 christos return order; 262 1.5 christos } 263 1.1 christos 264 1.1 christos isc_region_consume(®ion1, name_length(&name1)); 265 1.1 christos isc_region_consume(®ion2, name_length(&name2)); 266 1.1 christos 267 1.1 christos dns_name_init(&name1, NULL); 268 1.1 christos dns_name_init(&name2, NULL); 269 1.1 christos 270 1.1 christos dns_name_fromregion(&name1, ®ion1); 271 1.1 christos dns_name_fromregion(&name2, ®ion2); 272 1.1 christos 273 1.1 christos order = dns_name_rdatacompare(&name1, &name2); 274 1.5 christos if (order != 0) { 275 1.9 christos return order; 276 1.5 christos } 277 1.1 christos 278 1.1 christos isc_region_consume(®ion1, name_length(&name1)); 279 1.1 christos isc_region_consume(®ion2, name_length(&name2)); 280 1.1 christos 281 1.9 christos return isc_region_compare(®ion1, ®ion2); 282 1.1 christos } 283 1.1 christos 284 1.7 christos static isc_result_t 285 1.1 christos fromstruct_soa(ARGS_FROMSTRUCT) { 286 1.1 christos dns_rdata_soa_t *soa = source; 287 1.1 christos isc_region_t region; 288 1.1 christos 289 1.1 christos REQUIRE(type == dns_rdatatype_soa); 290 1.4 christos REQUIRE(soa != NULL); 291 1.1 christos REQUIRE(soa->common.rdtype == type); 292 1.1 christos REQUIRE(soa->common.rdclass == rdclass); 293 1.1 christos 294 1.1 christos UNUSED(type); 295 1.1 christos UNUSED(rdclass); 296 1.1 christos 297 1.1 christos dns_name_toregion(&soa->origin, ®ion); 298 1.1 christos RETERR(isc_buffer_copyregion(target, ®ion)); 299 1.1 christos dns_name_toregion(&soa->contact, ®ion); 300 1.1 christos RETERR(isc_buffer_copyregion(target, ®ion)); 301 1.1 christos RETERR(uint32_tobuffer(soa->serial, target)); 302 1.1 christos RETERR(uint32_tobuffer(soa->refresh, target)); 303 1.1 christos RETERR(uint32_tobuffer(soa->retry, target)); 304 1.1 christos RETERR(uint32_tobuffer(soa->expire, target)); 305 1.9 christos return uint32_tobuffer(soa->minimum, target); 306 1.1 christos } 307 1.1 christos 308 1.7 christos static isc_result_t 309 1.1 christos tostruct_soa(ARGS_TOSTRUCT) { 310 1.1 christos isc_region_t region; 311 1.1 christos dns_rdata_soa_t *soa = target; 312 1.1 christos dns_name_t name; 313 1.1 christos 314 1.1 christos REQUIRE(rdata->type == dns_rdatatype_soa); 315 1.4 christos REQUIRE(soa != NULL); 316 1.1 christos REQUIRE(rdata->length != 0); 317 1.1 christos 318 1.10 christos DNS_RDATACOMMON_INIT(soa, rdata->type, rdata->rdclass); 319 1.1 christos 320 1.1 christos dns_rdata_toregion(rdata, ®ion); 321 1.1 christos 322 1.1 christos dns_name_init(&name, NULL); 323 1.1 christos dns_name_fromregion(&name, ®ion); 324 1.1 christos isc_region_consume(®ion, name_length(&name)); 325 1.1 christos dns_name_init(&soa->origin, NULL); 326 1.8 christos name_duporclone(&name, mctx, &soa->origin); 327 1.1 christos 328 1.1 christos dns_name_fromregion(&name, ®ion); 329 1.1 christos isc_region_consume(®ion, name_length(&name)); 330 1.1 christos dns_name_init(&soa->contact, NULL); 331 1.8 christos name_duporclone(&name, mctx, &soa->contact); 332 1.1 christos 333 1.1 christos soa->serial = uint32_fromregion(®ion); 334 1.1 christos isc_region_consume(®ion, 4); 335 1.1 christos 336 1.1 christos soa->refresh = uint32_fromregion(®ion); 337 1.1 christos isc_region_consume(®ion, 4); 338 1.1 christos 339 1.1 christos soa->retry = uint32_fromregion(®ion); 340 1.1 christos isc_region_consume(®ion, 4); 341 1.1 christos 342 1.1 christos soa->expire = uint32_fromregion(®ion); 343 1.1 christos isc_region_consume(®ion, 4); 344 1.1 christos 345 1.1 christos soa->minimum = uint32_fromregion(®ion); 346 1.1 christos 347 1.1 christos soa->mctx = mctx; 348 1.9 christos return ISC_R_SUCCESS; 349 1.1 christos } 350 1.1 christos 351 1.7 christos static void 352 1.1 christos freestruct_soa(ARGS_FREESTRUCT) { 353 1.1 christos dns_rdata_soa_t *soa = source; 354 1.1 christos 355 1.4 christos REQUIRE(soa != NULL); 356 1.1 christos REQUIRE(soa->common.rdtype == dns_rdatatype_soa); 357 1.1 christos 358 1.5 christos if (soa->mctx == NULL) { 359 1.1 christos return; 360 1.5 christos } 361 1.1 christos 362 1.1 christos dns_name_free(&soa->origin, soa->mctx); 363 1.1 christos dns_name_free(&soa->contact, soa->mctx); 364 1.1 christos soa->mctx = NULL; 365 1.1 christos } 366 1.1 christos 367 1.7 christos static isc_result_t 368 1.1 christos additionaldata_soa(ARGS_ADDLDATA) { 369 1.8 christos REQUIRE(rdata->type == dns_rdatatype_soa); 370 1.8 christos 371 1.1 christos UNUSED(rdata); 372 1.8 christos UNUSED(owner); 373 1.1 christos UNUSED(add); 374 1.1 christos UNUSED(arg); 375 1.1 christos 376 1.9 christos return ISC_R_SUCCESS; 377 1.1 christos } 378 1.1 christos 379 1.7 christos static isc_result_t 380 1.1 christos digest_soa(ARGS_DIGEST) { 381 1.1 christos isc_region_t r; 382 1.1 christos dns_name_t name; 383 1.1 christos 384 1.1 christos REQUIRE(rdata->type == dns_rdatatype_soa); 385 1.1 christos 386 1.1 christos dns_rdata_toregion(rdata, &r); 387 1.1 christos 388 1.1 christos dns_name_init(&name, NULL); 389 1.1 christos dns_name_fromregion(&name, &r); 390 1.1 christos RETERR(dns_name_digest(&name, digest, arg)); 391 1.1 christos isc_region_consume(&r, name_length(&name)); 392 1.1 christos 393 1.1 christos dns_name_init(&name, NULL); 394 1.1 christos dns_name_fromregion(&name, &r); 395 1.1 christos RETERR(dns_name_digest(&name, digest, arg)); 396 1.1 christos isc_region_consume(&r, name_length(&name)); 397 1.1 christos 398 1.9 christos return (digest)(arg, &r); 399 1.1 christos } 400 1.1 christos 401 1.7 christos static bool 402 1.1 christos checkowner_soa(ARGS_CHECKOWNER) { 403 1.1 christos REQUIRE(type == dns_rdatatype_soa); 404 1.1 christos 405 1.1 christos UNUSED(name); 406 1.1 christos UNUSED(type); 407 1.1 christos UNUSED(rdclass); 408 1.1 christos UNUSED(wildcard); 409 1.1 christos 410 1.9 christos return true; 411 1.1 christos } 412 1.1 christos 413 1.7 christos static bool 414 1.1 christos checknames_soa(ARGS_CHECKNAMES) { 415 1.1 christos isc_region_t region; 416 1.1 christos dns_name_t name; 417 1.1 christos 418 1.1 christos REQUIRE(rdata->type == dns_rdatatype_soa); 419 1.1 christos 420 1.1 christos UNUSED(owner); 421 1.1 christos 422 1.1 christos dns_rdata_toregion(rdata, ®ion); 423 1.1 christos dns_name_init(&name, NULL); 424 1.1 christos dns_name_fromregion(&name, ®ion); 425 1.3 christos if (!dns_name_ishostname(&name, false)) { 426 1.5 christos if (bad != NULL) { 427 1.1 christos dns_name_clone(&name, bad); 428 1.5 christos } 429 1.9 christos return false; 430 1.1 christos } 431 1.1 christos isc_region_consume(®ion, name_length(&name)); 432 1.1 christos dns_name_fromregion(&name, ®ion); 433 1.1 christos if (!dns_name_ismailbox(&name)) { 434 1.5 christos if (bad != NULL) { 435 1.1 christos dns_name_clone(&name, bad); 436 1.5 christos } 437 1.9 christos return false; 438 1.1 christos } 439 1.9 christos return true; 440 1.1 christos } 441 1.1 christos 442 1.7 christos static int 443 1.1 christos casecompare_soa(ARGS_COMPARE) { 444 1.9 christos return compare_soa(rdata1, rdata2); 445 1.1 christos } 446 1.1 christos 447 1.5 christos #endif /* RDATA_GENERIC_SOA_6_C */ 448