1 /* $NetBSD: naptr_35.c,v 1.10 2026/01/29 18:37:52 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 /* RFC2915 */ 17 18 #ifndef RDATA_GENERIC_NAPTR_35_C 19 #define RDATA_GENERIC_NAPTR_35_C 20 21 #define RRTYPE_NAPTR_ATTRIBUTES (0) 22 23 #include <isc/regex.h> 24 25 /* 26 * Check the wire format of the Regexp field. 27 * Don't allow embedded NUL's. 28 */ 29 static isc_result_t 30 txt_valid_regex(const unsigned char *txt) { 31 unsigned int nsub = 0; 32 char regex[256]; 33 char *cp; 34 bool flags = false; 35 bool replace = false; 36 unsigned char c; 37 unsigned char delim; 38 unsigned int len; 39 int n; 40 41 len = *txt++; 42 if (len == 0U) { 43 return ISC_R_SUCCESS; 44 } 45 46 delim = *txt++; 47 len--; 48 49 /* 50 * Digits, backslash and flags can't be delimiters. 51 */ 52 switch (delim) { 53 case '0': 54 case '1': 55 case '2': 56 case '3': 57 case '4': 58 case '5': 59 case '6': 60 case '7': 61 case '8': 62 case '9': 63 case '\\': 64 case 'i': 65 case 0: 66 return DNS_R_SYNTAX; 67 } 68 69 cp = regex; 70 while (len-- > 0) { 71 c = *txt++; 72 if (c == 0) { 73 return DNS_R_SYNTAX; 74 } 75 if (c == delim && !replace) { 76 replace = true; 77 continue; 78 } else if (c == delim && !flags) { 79 flags = true; 80 continue; 81 } else if (c == delim) { 82 return DNS_R_SYNTAX; 83 } 84 /* 85 * Flags are not escaped. 86 */ 87 if (flags) { 88 switch (c) { 89 case 'i': 90 continue; 91 default: 92 return DNS_R_SYNTAX; 93 } 94 } 95 if (!replace) { 96 *cp++ = c; 97 } 98 if (c == '\\') { 99 if (len == 0) { 100 return DNS_R_SYNTAX; 101 } 102 c = *txt++; 103 if (c == 0) { 104 return DNS_R_SYNTAX; 105 } 106 len--; 107 if (replace) { 108 switch (c) { 109 case '0': 110 return DNS_R_SYNTAX; 111 case '1': 112 if (nsub < 1) { 113 nsub = 1; 114 } 115 break; 116 case '2': 117 if (nsub < 2) { 118 nsub = 2; 119 } 120 break; 121 case '3': 122 if (nsub < 3) { 123 nsub = 3; 124 } 125 break; 126 case '4': 127 if (nsub < 4) { 128 nsub = 4; 129 } 130 break; 131 case '5': 132 if (nsub < 5) { 133 nsub = 5; 134 } 135 break; 136 case '6': 137 if (nsub < 6) { 138 nsub = 6; 139 } 140 break; 141 case '7': 142 if (nsub < 7) { 143 nsub = 7; 144 } 145 break; 146 case '8': 147 if (nsub < 8) { 148 nsub = 8; 149 } 150 break; 151 case '9': 152 if (nsub < 9) { 153 nsub = 9; 154 } 155 break; 156 } 157 } 158 if (!replace) { 159 *cp++ = c; 160 } 161 } 162 } 163 if (!flags) { 164 return DNS_R_SYNTAX; 165 } 166 *cp = '\0'; 167 n = isc_regex_validate(regex); 168 if (n < 0 || nsub > (unsigned int)n) { 169 return DNS_R_SYNTAX; 170 } 171 return ISC_R_SUCCESS; 172 } 173 174 static isc_result_t 175 fromtext_naptr(ARGS_FROMTEXT) { 176 isc_token_t token; 177 dns_name_t name; 178 isc_buffer_t buffer; 179 unsigned char *regex; 180 181 REQUIRE(type == dns_rdatatype_naptr); 182 183 UNUSED(type); 184 UNUSED(rdclass); 185 UNUSED(callbacks); 186 187 /* 188 * Order. 189 */ 190 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 191 false)); 192 if (token.value.as_ulong > 0xffffU) { 193 RETTOK(ISC_R_RANGE); 194 } 195 RETERR(uint16_tobuffer(token.value.as_ulong, target)); 196 197 /* 198 * Preference. 199 */ 200 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 201 false)); 202 if (token.value.as_ulong > 0xffffU) { 203 RETTOK(ISC_R_RANGE); 204 } 205 RETERR(uint16_tobuffer(token.value.as_ulong, target)); 206 207 /* 208 * Flags. 209 */ 210 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring, 211 false)); 212 RETTOK(txt_fromtext(&token.value.as_textregion, target)); 213 214 /* 215 * Service. 216 */ 217 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring, 218 false)); 219 RETTOK(txt_fromtext(&token.value.as_textregion, target)); 220 221 /* 222 * Regexp. 223 */ 224 regex = isc_buffer_used(target); 225 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring, 226 false)); 227 RETTOK(txt_fromtext(&token.value.as_textregion, target)); 228 RETTOK(txt_valid_regex(regex)); 229 230 /* 231 * Replacement. 232 */ 233 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, 234 false)); 235 dns_name_init(&name, NULL); 236 buffer_fromregion(&buffer, &token.value.as_region); 237 if (origin == NULL) { 238 origin = dns_rootname; 239 } 240 RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target)); 241 return ISC_R_SUCCESS; 242 } 243 244 static isc_result_t 245 totext_naptr(ARGS_TOTEXT) { 246 isc_region_t region; 247 dns_name_t name; 248 dns_name_t prefix; 249 unsigned int opts; 250 char buf[sizeof("64000")]; 251 unsigned short num; 252 253 REQUIRE(rdata->type == dns_rdatatype_naptr); 254 REQUIRE(rdata->length != 0); 255 256 dns_name_init(&name, NULL); 257 dns_name_init(&prefix, NULL); 258 259 dns_rdata_toregion(rdata, ®ion); 260 261 /* 262 * Order. 263 */ 264 num = uint16_fromregion(®ion); 265 isc_region_consume(®ion, 2); 266 snprintf(buf, sizeof(buf), "%u", num); 267 RETERR(str_totext(buf, target)); 268 RETERR(str_totext(" ", target)); 269 270 /* 271 * Preference. 272 */ 273 num = uint16_fromregion(®ion); 274 isc_region_consume(®ion, 2); 275 snprintf(buf, sizeof(buf), "%u", num); 276 RETERR(str_totext(buf, target)); 277 RETERR(str_totext(" ", target)); 278 279 /* 280 * Flags. 281 */ 282 RETERR(txt_totext(®ion, true, target)); 283 RETERR(str_totext(" ", target)); 284 285 /* 286 * Service. 287 */ 288 RETERR(txt_totext(®ion, true, target)); 289 RETERR(str_totext(" ", target)); 290 291 /* 292 * Regexp. 293 */ 294 RETERR(txt_totext(®ion, true, target)); 295 RETERR(str_totext(" ", target)); 296 297 /* 298 * Replacement. 299 */ 300 dns_name_fromregion(&name, ®ion); 301 opts = name_prefix(&name, tctx->origin, &prefix) ? DNS_NAME_OMITFINALDOT 302 : 0; 303 return dns_name_totext(&prefix, opts, target); 304 } 305 306 static isc_result_t 307 fromwire_naptr(ARGS_FROMWIRE) { 308 dns_name_t name; 309 isc_region_t sr; 310 unsigned char *regex; 311 312 REQUIRE(type == dns_rdatatype_naptr); 313 314 UNUSED(type); 315 UNUSED(rdclass); 316 317 dctx = dns_decompress_setpermitted(dctx, false); 318 319 dns_name_init(&name, NULL); 320 321 /* 322 * Order, preference. 323 */ 324 isc_buffer_activeregion(source, &sr); 325 if (sr.length < 4) { 326 return ISC_R_UNEXPECTEDEND; 327 } 328 RETERR(mem_tobuffer(target, sr.base, 4)); 329 isc_buffer_forward(source, 4); 330 331 /* 332 * Flags. 333 */ 334 RETERR(txt_fromwire(source, target)); 335 336 /* 337 * Service. 338 */ 339 RETERR(txt_fromwire(source, target)); 340 341 /* 342 * Regexp. 343 */ 344 regex = isc_buffer_used(target); 345 RETERR(txt_fromwire(source, target)); 346 RETERR(txt_valid_regex(regex)); 347 348 /* 349 * Replacement. 350 */ 351 return dns_name_fromwire(&name, source, dctx, target); 352 } 353 354 static isc_result_t 355 towire_naptr(ARGS_TOWIRE) { 356 dns_name_t name; 357 dns_offsets_t offsets; 358 isc_region_t sr; 359 360 REQUIRE(rdata->type == dns_rdatatype_naptr); 361 REQUIRE(rdata->length != 0); 362 363 dns_compress_setpermitted(cctx, false); 364 /* 365 * Order, preference. 366 */ 367 dns_rdata_toregion(rdata, &sr); 368 RETERR(mem_tobuffer(target, sr.base, 4)); 369 isc_region_consume(&sr, 4); 370 371 /* 372 * Flags. 373 */ 374 RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1)); 375 isc_region_consume(&sr, sr.base[0] + 1); 376 377 /* 378 * Service. 379 */ 380 RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1)); 381 isc_region_consume(&sr, sr.base[0] + 1); 382 383 /* 384 * Regexp. 385 */ 386 RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1)); 387 isc_region_consume(&sr, sr.base[0] + 1); 388 389 /* 390 * Replacement. 391 */ 392 dns_name_init(&name, offsets); 393 dns_name_fromregion(&name, &sr); 394 return dns_name_towire(&name, cctx, target, NULL); 395 } 396 397 static int 398 compare_naptr(ARGS_COMPARE) { 399 dns_name_t name1; 400 dns_name_t name2; 401 isc_region_t region1; 402 isc_region_t region2; 403 int order, len; 404 405 REQUIRE(rdata1->type == rdata2->type); 406 REQUIRE(rdata1->rdclass == rdata2->rdclass); 407 REQUIRE(rdata1->type == dns_rdatatype_naptr); 408 REQUIRE(rdata1->length != 0); 409 REQUIRE(rdata2->length != 0); 410 411 dns_rdata_toregion(rdata1, ®ion1); 412 dns_rdata_toregion(rdata2, ®ion2); 413 414 /* 415 * Order, preference. 416 */ 417 order = memcmp(region1.base, region2.base, 4); 418 if (order != 0) { 419 return order < 0 ? -1 : 1; 420 } 421 isc_region_consume(®ion1, 4); 422 isc_region_consume(®ion2, 4); 423 424 /* 425 * Flags. 426 */ 427 len = ISC_MIN(region1.base[0], region2.base[0]); 428 order = memcmp(region1.base, region2.base, len + 1); 429 if (order != 0) { 430 return order < 0 ? -1 : 1; 431 } 432 isc_region_consume(®ion1, region1.base[0] + 1); 433 isc_region_consume(®ion2, region2.base[0] + 1); 434 435 /* 436 * Service. 437 */ 438 len = ISC_MIN(region1.base[0], region2.base[0]); 439 order = memcmp(region1.base, region2.base, len + 1); 440 if (order != 0) { 441 return order < 0 ? -1 : 1; 442 } 443 isc_region_consume(®ion1, region1.base[0] + 1); 444 isc_region_consume(®ion2, region2.base[0] + 1); 445 446 /* 447 * Regexp. 448 */ 449 len = ISC_MIN(region1.base[0], region2.base[0]); 450 order = memcmp(region1.base, region2.base, len + 1); 451 if (order != 0) { 452 return order < 0 ? -1 : 1; 453 } 454 isc_region_consume(®ion1, region1.base[0] + 1); 455 isc_region_consume(®ion2, region2.base[0] + 1); 456 457 /* 458 * Replacement. 459 */ 460 dns_name_init(&name1, NULL); 461 dns_name_init(&name2, NULL); 462 463 dns_name_fromregion(&name1, ®ion1); 464 dns_name_fromregion(&name2, ®ion2); 465 466 return dns_name_rdatacompare(&name1, &name2); 467 } 468 469 static isc_result_t 470 fromstruct_naptr(ARGS_FROMSTRUCT) { 471 dns_rdata_naptr_t *naptr = source; 472 isc_region_t region; 473 474 REQUIRE(type == dns_rdatatype_naptr); 475 REQUIRE(naptr != NULL); 476 REQUIRE(naptr->common.rdtype == type); 477 REQUIRE(naptr->common.rdclass == rdclass); 478 REQUIRE(naptr->flags != NULL || naptr->flags_len == 0); 479 REQUIRE(naptr->service != NULL || naptr->service_len == 0); 480 REQUIRE(naptr->regexp != NULL || naptr->regexp_len == 0); 481 482 UNUSED(type); 483 UNUSED(rdclass); 484 485 RETERR(uint16_tobuffer(naptr->order, target)); 486 RETERR(uint16_tobuffer(naptr->preference, target)); 487 RETERR(uint8_tobuffer(naptr->flags_len, target)); 488 RETERR(mem_tobuffer(target, naptr->flags, naptr->flags_len)); 489 RETERR(uint8_tobuffer(naptr->service_len, target)); 490 RETERR(mem_tobuffer(target, naptr->service, naptr->service_len)); 491 RETERR(uint8_tobuffer(naptr->regexp_len, target)); 492 RETERR(mem_tobuffer(target, naptr->regexp, naptr->regexp_len)); 493 dns_name_toregion(&naptr->replacement, ®ion); 494 return isc_buffer_copyregion(target, ®ion); 495 } 496 497 static isc_result_t 498 tostruct_naptr(ARGS_TOSTRUCT) { 499 dns_rdata_naptr_t *naptr = target; 500 isc_region_t r; 501 dns_name_t name; 502 503 REQUIRE(rdata->type == dns_rdatatype_naptr); 504 REQUIRE(naptr != NULL); 505 REQUIRE(rdata->length != 0); 506 507 DNS_RDATACOMMON_INIT(naptr, rdata->type, rdata->rdclass); 508 509 naptr->flags = NULL; 510 naptr->service = NULL; 511 naptr->regexp = NULL; 512 513 dns_rdata_toregion(rdata, &r); 514 515 naptr->order = uint16_fromregion(&r); 516 isc_region_consume(&r, 2); 517 518 naptr->preference = uint16_fromregion(&r); 519 isc_region_consume(&r, 2); 520 521 naptr->flags_len = uint8_fromregion(&r); 522 isc_region_consume(&r, 1); 523 INSIST(naptr->flags_len <= r.length); 524 naptr->flags = mem_maybedup(mctx, r.base, naptr->flags_len); 525 isc_region_consume(&r, naptr->flags_len); 526 527 naptr->service_len = uint8_fromregion(&r); 528 isc_region_consume(&r, 1); 529 INSIST(naptr->service_len <= r.length); 530 naptr->service = mem_maybedup(mctx, r.base, naptr->service_len); 531 isc_region_consume(&r, naptr->service_len); 532 533 naptr->regexp_len = uint8_fromregion(&r); 534 isc_region_consume(&r, 1); 535 INSIST(naptr->regexp_len <= r.length); 536 naptr->regexp = mem_maybedup(mctx, r.base, naptr->regexp_len); 537 isc_region_consume(&r, naptr->regexp_len); 538 539 dns_name_init(&name, NULL); 540 dns_name_fromregion(&name, &r); 541 dns_name_init(&naptr->replacement, NULL); 542 name_duporclone(&name, mctx, &naptr->replacement); 543 naptr->mctx = mctx; 544 return ISC_R_SUCCESS; 545 } 546 547 static void 548 freestruct_naptr(ARGS_FREESTRUCT) { 549 dns_rdata_naptr_t *naptr = source; 550 551 REQUIRE(naptr != NULL); 552 REQUIRE(naptr->common.rdtype == dns_rdatatype_naptr); 553 554 if (naptr->mctx == NULL) { 555 return; 556 } 557 558 if (naptr->flags != NULL) { 559 isc_mem_free(naptr->mctx, naptr->flags); 560 } 561 if (naptr->service != NULL) { 562 isc_mem_free(naptr->mctx, naptr->service); 563 } 564 if (naptr->regexp != NULL) { 565 isc_mem_free(naptr->mctx, naptr->regexp); 566 } 567 dns_name_free(&naptr->replacement, naptr->mctx); 568 naptr->mctx = NULL; 569 } 570 571 static isc_result_t 572 additionaldata_naptr(ARGS_ADDLDATA) { 573 dns_name_t name; 574 dns_offsets_t offsets; 575 isc_region_t sr; 576 dns_rdatatype_t atype; 577 unsigned int i, flagslen; 578 char *cp; 579 580 REQUIRE(rdata->type == dns_rdatatype_naptr); 581 582 UNUSED(owner); 583 584 /* 585 * Order, preference. 586 */ 587 dns_rdata_toregion(rdata, &sr); 588 isc_region_consume(&sr, 4); 589 590 /* 591 * Flags. 592 */ 593 atype = 0; 594 flagslen = sr.base[0]; 595 cp = (char *)&sr.base[1]; 596 for (i = 0; i < flagslen; i++, cp++) { 597 if (*cp == 'S' || *cp == 's') { 598 atype = dns_rdatatype_srv; 599 break; 600 } 601 if (*cp == 'A' || *cp == 'a') { 602 atype = dns_rdatatype_a; 603 break; 604 } 605 } 606 isc_region_consume(&sr, flagslen + 1); 607 608 /* 609 * Service. 610 */ 611 isc_region_consume(&sr, sr.base[0] + 1); 612 613 /* 614 * Regexp. 615 */ 616 isc_region_consume(&sr, sr.base[0] + 1); 617 618 /* 619 * Replacement. 620 */ 621 dns_name_init(&name, offsets); 622 dns_name_fromregion(&name, &sr); 623 624 if (atype != 0) { 625 return (add)(arg, &name, atype, NULL DNS__DB_FILELINE); 626 } 627 628 return ISC_R_SUCCESS; 629 } 630 631 static isc_result_t 632 digest_naptr(ARGS_DIGEST) { 633 isc_region_t r1, r2; 634 unsigned int length, n; 635 isc_result_t result; 636 dns_name_t name; 637 638 REQUIRE(rdata->type == dns_rdatatype_naptr); 639 640 dns_rdata_toregion(rdata, &r1); 641 r2 = r1; 642 length = 0; 643 644 /* 645 * Order, preference. 646 */ 647 length += 4; 648 isc_region_consume(&r2, 4); 649 650 /* 651 * Flags. 652 */ 653 n = r2.base[0] + 1; 654 length += n; 655 isc_region_consume(&r2, n); 656 657 /* 658 * Service. 659 */ 660 n = r2.base[0] + 1; 661 length += n; 662 isc_region_consume(&r2, n); 663 664 /* 665 * Regexp. 666 */ 667 n = r2.base[0] + 1; 668 length += n; 669 isc_region_consume(&r2, n); 670 671 /* 672 * Digest the RR up to the replacement name. 673 */ 674 r1.length = length; 675 result = (digest)(arg, &r1); 676 if (result != ISC_R_SUCCESS) { 677 return result; 678 } 679 680 /* 681 * Replacement. 682 */ 683 684 dns_name_init(&name, NULL); 685 dns_name_fromregion(&name, &r2); 686 687 return dns_name_digest(&name, digest, arg); 688 } 689 690 static bool 691 checkowner_naptr(ARGS_CHECKOWNER) { 692 REQUIRE(type == dns_rdatatype_naptr); 693 694 UNUSED(name); 695 UNUSED(type); 696 UNUSED(rdclass); 697 UNUSED(wildcard); 698 699 return true; 700 } 701 702 static bool 703 checknames_naptr(ARGS_CHECKNAMES) { 704 REQUIRE(rdata->type == dns_rdatatype_naptr); 705 706 UNUSED(rdata); 707 UNUSED(owner); 708 UNUSED(bad); 709 710 return true; 711 } 712 713 static int 714 casecompare_naptr(ARGS_COMPARE) { 715 return compare_naptr(rdata1, rdata2); 716 } 717 718 #endif /* RDATA_GENERIC_NAPTR_35_C */ 719