1 /* $NetBSD: naptr_35.c,v 1.9 2025/01/26 16:25:32 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 naptr->common.rdclass = rdata->rdclass; 508 naptr->common.rdtype = rdata->type; 509 ISC_LINK_INIT(&naptr->common, link); 510 511 naptr->flags = NULL; 512 naptr->service = NULL; 513 naptr->regexp = NULL; 514 515 dns_rdata_toregion(rdata, &r); 516 517 naptr->order = uint16_fromregion(&r); 518 isc_region_consume(&r, 2); 519 520 naptr->preference = uint16_fromregion(&r); 521 isc_region_consume(&r, 2); 522 523 naptr->flags_len = uint8_fromregion(&r); 524 isc_region_consume(&r, 1); 525 INSIST(naptr->flags_len <= r.length); 526 naptr->flags = mem_maybedup(mctx, r.base, naptr->flags_len); 527 isc_region_consume(&r, naptr->flags_len); 528 529 naptr->service_len = uint8_fromregion(&r); 530 isc_region_consume(&r, 1); 531 INSIST(naptr->service_len <= r.length); 532 naptr->service = mem_maybedup(mctx, r.base, naptr->service_len); 533 isc_region_consume(&r, naptr->service_len); 534 535 naptr->regexp_len = uint8_fromregion(&r); 536 isc_region_consume(&r, 1); 537 INSIST(naptr->regexp_len <= r.length); 538 naptr->regexp = mem_maybedup(mctx, r.base, naptr->regexp_len); 539 isc_region_consume(&r, naptr->regexp_len); 540 541 dns_name_init(&name, NULL); 542 dns_name_fromregion(&name, &r); 543 dns_name_init(&naptr->replacement, NULL); 544 name_duporclone(&name, mctx, &naptr->replacement); 545 naptr->mctx = mctx; 546 return ISC_R_SUCCESS; 547 } 548 549 static void 550 freestruct_naptr(ARGS_FREESTRUCT) { 551 dns_rdata_naptr_t *naptr = source; 552 553 REQUIRE(naptr != NULL); 554 REQUIRE(naptr->common.rdtype == dns_rdatatype_naptr); 555 556 if (naptr->mctx == NULL) { 557 return; 558 } 559 560 if (naptr->flags != NULL) { 561 isc_mem_free(naptr->mctx, naptr->flags); 562 } 563 if (naptr->service != NULL) { 564 isc_mem_free(naptr->mctx, naptr->service); 565 } 566 if (naptr->regexp != NULL) { 567 isc_mem_free(naptr->mctx, naptr->regexp); 568 } 569 dns_name_free(&naptr->replacement, naptr->mctx); 570 naptr->mctx = NULL; 571 } 572 573 static isc_result_t 574 additionaldata_naptr(ARGS_ADDLDATA) { 575 dns_name_t name; 576 dns_offsets_t offsets; 577 isc_region_t sr; 578 dns_rdatatype_t atype; 579 unsigned int i, flagslen; 580 char *cp; 581 582 REQUIRE(rdata->type == dns_rdatatype_naptr); 583 584 UNUSED(owner); 585 586 /* 587 * Order, preference. 588 */ 589 dns_rdata_toregion(rdata, &sr); 590 isc_region_consume(&sr, 4); 591 592 /* 593 * Flags. 594 */ 595 atype = 0; 596 flagslen = sr.base[0]; 597 cp = (char *)&sr.base[1]; 598 for (i = 0; i < flagslen; i++, cp++) { 599 if (*cp == 'S' || *cp == 's') { 600 atype = dns_rdatatype_srv; 601 break; 602 } 603 if (*cp == 'A' || *cp == 'a') { 604 atype = dns_rdatatype_a; 605 break; 606 } 607 } 608 isc_region_consume(&sr, flagslen + 1); 609 610 /* 611 * Service. 612 */ 613 isc_region_consume(&sr, sr.base[0] + 1); 614 615 /* 616 * Regexp. 617 */ 618 isc_region_consume(&sr, sr.base[0] + 1); 619 620 /* 621 * Replacement. 622 */ 623 dns_name_init(&name, offsets); 624 dns_name_fromregion(&name, &sr); 625 626 if (atype != 0) { 627 return (add)(arg, &name, atype, NULL DNS__DB_FILELINE); 628 } 629 630 return ISC_R_SUCCESS; 631 } 632 633 static isc_result_t 634 digest_naptr(ARGS_DIGEST) { 635 isc_region_t r1, r2; 636 unsigned int length, n; 637 isc_result_t result; 638 dns_name_t name; 639 640 REQUIRE(rdata->type == dns_rdatatype_naptr); 641 642 dns_rdata_toregion(rdata, &r1); 643 r2 = r1; 644 length = 0; 645 646 /* 647 * Order, preference. 648 */ 649 length += 4; 650 isc_region_consume(&r2, 4); 651 652 /* 653 * Flags. 654 */ 655 n = r2.base[0] + 1; 656 length += n; 657 isc_region_consume(&r2, n); 658 659 /* 660 * Service. 661 */ 662 n = r2.base[0] + 1; 663 length += n; 664 isc_region_consume(&r2, n); 665 666 /* 667 * Regexp. 668 */ 669 n = r2.base[0] + 1; 670 length += n; 671 isc_region_consume(&r2, n); 672 673 /* 674 * Digest the RR up to the replacement name. 675 */ 676 r1.length = length; 677 result = (digest)(arg, &r1); 678 if (result != ISC_R_SUCCESS) { 679 return result; 680 } 681 682 /* 683 * Replacement. 684 */ 685 686 dns_name_init(&name, NULL); 687 dns_name_fromregion(&name, &r2); 688 689 return dns_name_digest(&name, digest, arg); 690 } 691 692 static bool 693 checkowner_naptr(ARGS_CHECKOWNER) { 694 REQUIRE(type == dns_rdatatype_naptr); 695 696 UNUSED(name); 697 UNUSED(type); 698 UNUSED(rdclass); 699 UNUSED(wildcard); 700 701 return true; 702 } 703 704 static bool 705 checknames_naptr(ARGS_CHECKNAMES) { 706 REQUIRE(rdata->type == dns_rdatatype_naptr); 707 708 UNUSED(rdata); 709 UNUSED(owner); 710 UNUSED(bad); 711 712 return true; 713 } 714 715 static int 716 casecompare_naptr(ARGS_COMPARE) { 717 return compare_naptr(rdata1, rdata2); 718 } 719 720 #endif /* RDATA_GENERIC_NAPTR_35_C */ 721