Home | History | Annotate | Line # | Download | only in generic
      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, &region);
    260 
    261 	/*
    262 	 * Order.
    263 	 */
    264 	num = uint16_fromregion(&region);
    265 	isc_region_consume(&region, 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(&region);
    274 	isc_region_consume(&region, 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(&region, true, target));
    283 	RETERR(str_totext(" ", target));
    284 
    285 	/*
    286 	 * Service.
    287 	 */
    288 	RETERR(txt_totext(&region, true, target));
    289 	RETERR(str_totext(" ", target));
    290 
    291 	/*
    292 	 * Regexp.
    293 	 */
    294 	RETERR(txt_totext(&region, true, target));
    295 	RETERR(str_totext(" ", target));
    296 
    297 	/*
    298 	 * Replacement.
    299 	 */
    300 	dns_name_fromregion(&name, &region);
    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, &region1);
    412 	dns_rdata_toregion(rdata2, &region2);
    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(&region1, 4);
    422 	isc_region_consume(&region2, 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(&region1, region1.base[0] + 1);
    433 	isc_region_consume(&region2, 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(&region1, region1.base[0] + 1);
    444 	isc_region_consume(&region2, 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(&region1, region1.base[0] + 1);
    455 	isc_region_consume(&region2, 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, &region1);
    464 	dns_name_fromregion(&name2, &region2);
    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, &region);
    494 	return isc_buffer_copyregion(target, &region);
    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