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