1 /*	$NetBSD: naptr_35.c,v 1.7 2022/09/23 12:15:31 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
txt_valid_regex(const unsigned char * txt)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
fromtext_naptr(ARGS_FROMTEXT)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
totext_naptr(ARGS_TOTEXT)245 totext_naptr(ARGS_TOTEXT) {
246 	isc_region_t region;
247 	dns_name_t name;
248 	dns_name_t prefix;
249 	bool sub;
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 	sub = name_prefix(&name, tctx->origin, &prefix);
302 	return (dns_name_totext(&prefix, sub, target));
303 }
304 
305 static isc_result_t
fromwire_naptr(ARGS_FROMWIRE)306 fromwire_naptr(ARGS_FROMWIRE) {
307 	dns_name_t name;
308 	isc_region_t sr;
309 	unsigned char *regex;
310 
311 	REQUIRE(type == dns_rdatatype_naptr);
312 
313 	UNUSED(type);
314 	UNUSED(rdclass);
315 
316 	dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
317 
318 	dns_name_init(&name, NULL);
319 
320 	/*
321 	 * Order, preference.
322 	 */
323 	isc_buffer_activeregion(source, &sr);
324 	if (sr.length < 4) {
325 		return (ISC_R_UNEXPECTEDEND);
326 	}
327 	RETERR(mem_tobuffer(target, sr.base, 4));
328 	isc_buffer_forward(source, 4);
329 
330 	/*
331 	 * Flags.
332 	 */
333 	RETERR(txt_fromwire(source, target));
334 
335 	/*
336 	 * Service.
337 	 */
338 	RETERR(txt_fromwire(source, target));
339 
340 	/*
341 	 * Regexp.
342 	 */
343 	regex = isc_buffer_used(target);
344 	RETERR(txt_fromwire(source, target));
345 	RETERR(txt_valid_regex(regex));
346 
347 	/*
348 	 * Replacement.
349 	 */
350 	return (dns_name_fromwire(&name, source, dctx, options, target));
351 }
352 
353 static isc_result_t
towire_naptr(ARGS_TOWIRE)354 towire_naptr(ARGS_TOWIRE) {
355 	dns_name_t name;
356 	dns_offsets_t offsets;
357 	isc_region_t sr;
358 
359 	REQUIRE(rdata->type == dns_rdatatype_naptr);
360 	REQUIRE(rdata->length != 0);
361 
362 	dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
363 	/*
364 	 * Order, preference.
365 	 */
366 	dns_rdata_toregion(rdata, &sr);
367 	RETERR(mem_tobuffer(target, sr.base, 4));
368 	isc_region_consume(&sr, 4);
369 
370 	/*
371 	 * Flags.
372 	 */
373 	RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1));
374 	isc_region_consume(&sr, sr.base[0] + 1);
375 
376 	/*
377 	 * Service.
378 	 */
379 	RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1));
380 	isc_region_consume(&sr, sr.base[0] + 1);
381 
382 	/*
383 	 * Regexp.
384 	 */
385 	RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1));
386 	isc_region_consume(&sr, sr.base[0] + 1);
387 
388 	/*
389 	 * Replacement.
390 	 */
391 	dns_name_init(&name, offsets);
392 	dns_name_fromregion(&name, &sr);
393 	return (dns_name_towire(&name, cctx, target));
394 }
395 
396 static int
compare_naptr(ARGS_COMPARE)397 compare_naptr(ARGS_COMPARE) {
398 	dns_name_t name1;
399 	dns_name_t name2;
400 	isc_region_t region1;
401 	isc_region_t region2;
402 	int order, len;
403 
404 	REQUIRE(rdata1->type == rdata2->type);
405 	REQUIRE(rdata1->rdclass == rdata2->rdclass);
406 	REQUIRE(rdata1->type == dns_rdatatype_naptr);
407 	REQUIRE(rdata1->length != 0);
408 	REQUIRE(rdata2->length != 0);
409 
410 	dns_rdata_toregion(rdata1, &region1);
411 	dns_rdata_toregion(rdata2, &region2);
412 
413 	/*
414 	 * Order, preference.
415 	 */
416 	order = memcmp(region1.base, region2.base, 4);
417 	if (order != 0) {
418 		return (order < 0 ? -1 : 1);
419 	}
420 	isc_region_consume(&region1, 4);
421 	isc_region_consume(&region2, 4);
422 
423 	/*
424 	 * Flags.
425 	 */
426 	len = ISC_MIN(region1.base[0], region2.base[0]);
427 	order = memcmp(region1.base, region2.base, len + 1);
428 	if (order != 0) {
429 		return (order < 0 ? -1 : 1);
430 	}
431 	isc_region_consume(&region1, region1.base[0] + 1);
432 	isc_region_consume(&region2, region2.base[0] + 1);
433 
434 	/*
435 	 * Service.
436 	 */
437 	len = ISC_MIN(region1.base[0], region2.base[0]);
438 	order = memcmp(region1.base, region2.base, len + 1);
439 	if (order != 0) {
440 		return (order < 0 ? -1 : 1);
441 	}
442 	isc_region_consume(&region1, region1.base[0] + 1);
443 	isc_region_consume(&region2, region2.base[0] + 1);
444 
445 	/*
446 	 * Regexp.
447 	 */
448 	len = ISC_MIN(region1.base[0], region2.base[0]);
449 	order = memcmp(region1.base, region2.base, len + 1);
450 	if (order != 0) {
451 		return (order < 0 ? -1 : 1);
452 	}
453 	isc_region_consume(&region1, region1.base[0] + 1);
454 	isc_region_consume(&region2, region2.base[0] + 1);
455 
456 	/*
457 	 * Replacement.
458 	 */
459 	dns_name_init(&name1, NULL);
460 	dns_name_init(&name2, NULL);
461 
462 	dns_name_fromregion(&name1, &region1);
463 	dns_name_fromregion(&name2, &region2);
464 
465 	return (dns_name_rdatacompare(&name1, &name2));
466 }
467 
468 static isc_result_t
fromstruct_naptr(ARGS_FROMSTRUCT)469 fromstruct_naptr(ARGS_FROMSTRUCT) {
470 	dns_rdata_naptr_t *naptr = source;
471 	isc_region_t region;
472 
473 	REQUIRE(type == dns_rdatatype_naptr);
474 	REQUIRE(naptr != NULL);
475 	REQUIRE(naptr->common.rdtype == type);
476 	REQUIRE(naptr->common.rdclass == rdclass);
477 	REQUIRE(naptr->flags != NULL || naptr->flags_len == 0);
478 	REQUIRE(naptr->service != NULL || naptr->service_len == 0);
479 	REQUIRE(naptr->regexp != NULL || naptr->regexp_len == 0);
480 
481 	UNUSED(type);
482 	UNUSED(rdclass);
483 
484 	RETERR(uint16_tobuffer(naptr->order, target));
485 	RETERR(uint16_tobuffer(naptr->preference, target));
486 	RETERR(uint8_tobuffer(naptr->flags_len, target));
487 	RETERR(mem_tobuffer(target, naptr->flags, naptr->flags_len));
488 	RETERR(uint8_tobuffer(naptr->service_len, target));
489 	RETERR(mem_tobuffer(target, naptr->service, naptr->service_len));
490 	RETERR(uint8_tobuffer(naptr->regexp_len, target));
491 	RETERR(mem_tobuffer(target, naptr->regexp, naptr->regexp_len));
492 	dns_name_toregion(&naptr->replacement, &region);
493 	return (isc_buffer_copyregion(target, &region));
494 }
495 
496 static isc_result_t
tostruct_naptr(ARGS_TOSTRUCT)497 tostruct_naptr(ARGS_TOSTRUCT) {
498 	dns_rdata_naptr_t *naptr = target;
499 	isc_region_t r;
500 	isc_result_t result;
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 	if (naptr->flags == NULL) {
528 		goto cleanup;
529 	}
530 	isc_region_consume(&r, naptr->flags_len);
531 
532 	naptr->service_len = uint8_fromregion(&r);
533 	isc_region_consume(&r, 1);
534 	INSIST(naptr->service_len <= r.length);
535 	naptr->service = mem_maybedup(mctx, r.base, naptr->service_len);
536 	if (naptr->service == NULL) {
537 		goto cleanup;
538 	}
539 	isc_region_consume(&r, naptr->service_len);
540 
541 	naptr->regexp_len = uint8_fromregion(&r);
542 	isc_region_consume(&r, 1);
543 	INSIST(naptr->regexp_len <= r.length);
544 	naptr->regexp = mem_maybedup(mctx, r.base, naptr->regexp_len);
545 	if (naptr->regexp == NULL) {
546 		goto cleanup;
547 	}
548 	isc_region_consume(&r, naptr->regexp_len);
549 
550 	dns_name_init(&name, NULL);
551 	dns_name_fromregion(&name, &r);
552 	dns_name_init(&naptr->replacement, NULL);
553 	result = name_duporclone(&name, mctx, &naptr->replacement);
554 	if (result != ISC_R_SUCCESS) {
555 		goto cleanup;
556 	}
557 	naptr->mctx = mctx;
558 	return (ISC_R_SUCCESS);
559 
560 cleanup:
561 	if (mctx != NULL && naptr->flags != NULL) {
562 		isc_mem_free(mctx, naptr->flags);
563 	}
564 	if (mctx != NULL && naptr->service != NULL) {
565 		isc_mem_free(mctx, naptr->service);
566 	}
567 	if (mctx != NULL && naptr->regexp != NULL) {
568 		isc_mem_free(mctx, naptr->regexp);
569 	}
570 	return (ISC_R_NOMEMORY);
571 }
572 
573 static void
freestruct_naptr(ARGS_FREESTRUCT)574 freestruct_naptr(ARGS_FREESTRUCT) {
575 	dns_rdata_naptr_t *naptr = source;
576 
577 	REQUIRE(naptr != NULL);
578 	REQUIRE(naptr->common.rdtype == dns_rdatatype_naptr);
579 
580 	if (naptr->mctx == NULL) {
581 		return;
582 	}
583 
584 	if (naptr->flags != NULL) {
585 		isc_mem_free(naptr->mctx, naptr->flags);
586 	}
587 	if (naptr->service != NULL) {
588 		isc_mem_free(naptr->mctx, naptr->service);
589 	}
590 	if (naptr->regexp != NULL) {
591 		isc_mem_free(naptr->mctx, naptr->regexp);
592 	}
593 	dns_name_free(&naptr->replacement, naptr->mctx);
594 	naptr->mctx = NULL;
595 }
596 
597 static isc_result_t
additionaldata_naptr(ARGS_ADDLDATA)598 additionaldata_naptr(ARGS_ADDLDATA) {
599 	dns_name_t name;
600 	dns_offsets_t offsets;
601 	isc_region_t sr;
602 	dns_rdatatype_t atype;
603 	unsigned int i, flagslen;
604 	char *cp;
605 
606 	REQUIRE(rdata->type == dns_rdatatype_naptr);
607 
608 	/*
609 	 * Order, preference.
610 	 */
611 	dns_rdata_toregion(rdata, &sr);
612 	isc_region_consume(&sr, 4);
613 
614 	/*
615 	 * Flags.
616 	 */
617 	atype = 0;
618 	flagslen = sr.base[0];
619 	cp = (char *)&sr.base[1];
620 	for (i = 0; i < flagslen; i++, cp++) {
621 		if (*cp == 'S' || *cp == 's') {
622 			atype = dns_rdatatype_srv;
623 			break;
624 		}
625 		if (*cp == 'A' || *cp == 'a') {
626 			atype = dns_rdatatype_a;
627 			break;
628 		}
629 	}
630 	isc_region_consume(&sr, flagslen + 1);
631 
632 	/*
633 	 * Service.
634 	 */
635 	isc_region_consume(&sr, sr.base[0] + 1);
636 
637 	/*
638 	 * Regexp.
639 	 */
640 	isc_region_consume(&sr, sr.base[0] + 1);
641 
642 	/*
643 	 * Replacement.
644 	 */
645 	dns_name_init(&name, offsets);
646 	dns_name_fromregion(&name, &sr);
647 
648 	if (atype != 0) {
649 		return ((add)(arg, &name, atype));
650 	}
651 
652 	return (ISC_R_SUCCESS);
653 }
654 
655 static isc_result_t
digest_naptr(ARGS_DIGEST)656 digest_naptr(ARGS_DIGEST) {
657 	isc_region_t r1, r2;
658 	unsigned int length, n;
659 	isc_result_t result;
660 	dns_name_t name;
661 
662 	REQUIRE(rdata->type == dns_rdatatype_naptr);
663 
664 	dns_rdata_toregion(rdata, &r1);
665 	r2 = r1;
666 	length = 0;
667 
668 	/*
669 	 * Order, preference.
670 	 */
671 	length += 4;
672 	isc_region_consume(&r2, 4);
673 
674 	/*
675 	 * Flags.
676 	 */
677 	n = r2.base[0] + 1;
678 	length += n;
679 	isc_region_consume(&r2, n);
680 
681 	/*
682 	 * Service.
683 	 */
684 	n = r2.base[0] + 1;
685 	length += n;
686 	isc_region_consume(&r2, n);
687 
688 	/*
689 	 * Regexp.
690 	 */
691 	n = r2.base[0] + 1;
692 	length += n;
693 	isc_region_consume(&r2, n);
694 
695 	/*
696 	 * Digest the RR up to the replacement name.
697 	 */
698 	r1.length = length;
699 	result = (digest)(arg, &r1);
700 	if (result != ISC_R_SUCCESS) {
701 		return (result);
702 	}
703 
704 	/*
705 	 * Replacement.
706 	 */
707 
708 	dns_name_init(&name, NULL);
709 	dns_name_fromregion(&name, &r2);
710 
711 	return (dns_name_digest(&name, digest, arg));
712 }
713 
714 static bool
checkowner_naptr(ARGS_CHECKOWNER)715 checkowner_naptr(ARGS_CHECKOWNER) {
716 	REQUIRE(type == dns_rdatatype_naptr);
717 
718 	UNUSED(name);
719 	UNUSED(type);
720 	UNUSED(rdclass);
721 	UNUSED(wildcard);
722 
723 	return (true);
724 }
725 
726 static bool
checknames_naptr(ARGS_CHECKNAMES)727 checknames_naptr(ARGS_CHECKNAMES) {
728 	REQUIRE(rdata->type == dns_rdatatype_naptr);
729 
730 	UNUSED(rdata);
731 	UNUSED(owner);
732 	UNUSED(bad);
733 
734 	return (true);
735 }
736 
737 static int
casecompare_naptr(ARGS_COMPARE)738 casecompare_naptr(ARGS_COMPARE) {
739 	return (compare_naptr(rdata1, rdata2));
740 }
741 
742 #endif /* RDATA_GENERIC_NAPTR_35_C */
743