1 /* $NetBSD: svcb_64.c,v 1.3 2023/01/25 21:43:30 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 /* draft-ietf-dnsop-svcb-https-02 */
17
18 #ifndef RDATA_IN_1_SVCB_64_C
19 #define RDATA_IN_1_SVCB_64_C
20
21 #define RRTYPE_SVCB_ATTRIBUTES 0
22
23 #define SVCB_MAN_KEY 0
24 #define SVCB_ALPN_KEY 1
25 #define SVCB_NO_DEFAULT_ALPN_KEY 2
26
27 /*
28 * Service Binding Parameter Registry
29 */
30 enum encoding {
31 sbpr_text,
32 sbpr_port,
33 sbpr_ipv4s,
34 sbpr_ipv6s,
35 sbpr_base64,
36 sbpr_empty,
37 sbpr_alpn,
38 sbpr_keylist,
39 sbpr_dohpath
40 };
41 static const struct {
42 const char *name; /* Restricted to lowercase LDH by registry. */
43 unsigned int value;
44 enum encoding encoding;
45 bool initial; /* Part of the first defined set of encodings. */
46 } sbpr[] = {
47 { "mandatory", 0, sbpr_keylist, true },
48 { "alpn", 1, sbpr_alpn, true },
49 { "no-default-alpn", 2, sbpr_empty, true },
50 { "port", 3, sbpr_port, true },
51 { "ipv4hint", 4, sbpr_ipv4s, true },
52 { "ech", 5, sbpr_base64, true },
53 { "ipv6hint", 6, sbpr_ipv6s, true },
54 { "dohpath", 7, sbpr_dohpath, false },
55 };
56
57 static isc_result_t
alpn_fromtxt(isc_textregion_t * source,isc_buffer_t * target)58 alpn_fromtxt(isc_textregion_t *source, isc_buffer_t *target) {
59 isc_textregion_t source0 = *source;
60 do {
61 RETERR(commatxt_fromtext(&source0, true, target));
62 } while (source0.length != 0);
63 return (ISC_R_SUCCESS);
64 }
65
66 static int
svckeycmp(const void * a1,const void * a2)67 svckeycmp(const void *a1, const void *a2) {
68 const unsigned char *u1 = a1, *u2 = a2;
69 if (*u1 != *u2) {
70 return (*u1 - *u2);
71 }
72 return (*(++u1) - *(++u2));
73 }
74
75 static isc_result_t
svcsortkeylist(isc_buffer_t * target,unsigned int used)76 svcsortkeylist(isc_buffer_t *target, unsigned int used) {
77 isc_region_t region;
78
79 isc_buffer_usedregion(target, ®ion);
80 isc_region_consume(®ion, used);
81 INSIST(region.length > 0U);
82 qsort(region.base, region.length / 2, 2, svckeycmp);
83 /* Reject duplicates. */
84 while (region.length >= 4) {
85 if (region.base[0] == region.base[2] &&
86 region.base[1] == region.base[3])
87 {
88 return (DNS_R_SYNTAX);
89 }
90 isc_region_consume(®ion, 2);
91 }
92 return (ISC_R_SUCCESS);
93 }
94
95 static isc_result_t
svcb_validate(uint16_t key,isc_region_t * region)96 svcb_validate(uint16_t key, isc_region_t *region) {
97 size_t i;
98
99 #ifndef ARRAYSIZE
100 /* defined in winnt.h */
101 #define ARRAYSIZE(x) (sizeof(x) / sizeof(*x))
102 #endif
103
104 for (i = 0; i < ARRAYSIZE(sbpr); i++) {
105 if (sbpr[i].value == key) {
106 switch (sbpr[i].encoding) {
107 case sbpr_port:
108 if (region->length != 2) {
109 return (DNS_R_FORMERR);
110 }
111 break;
112 case sbpr_ipv4s:
113 if ((region->length % 4) != 0 ||
114 region->length == 0)
115 {
116 return (DNS_R_FORMERR);
117 }
118 break;
119 case sbpr_ipv6s:
120 if ((region->length % 16) != 0 ||
121 region->length == 0)
122 {
123 return (DNS_R_FORMERR);
124 }
125 break;
126 case sbpr_alpn: {
127 if (region->length == 0) {
128 return (DNS_R_FORMERR);
129 }
130 while (region->length != 0) {
131 size_t l = *region->base + 1;
132 if (l == 1U || l > region->length) {
133 return (DNS_R_FORMERR);
134 }
135 isc_region_consume(region, l);
136 }
137 break;
138 }
139 case sbpr_keylist: {
140 if ((region->length % 2) != 0 ||
141 region->length == 0)
142 {
143 return (DNS_R_FORMERR);
144 }
145 /* In order? */
146 while (region->length >= 4) {
147 if (region->base[0] > region->base[2] ||
148 (region->base[0] ==
149 region->base[2] &&
150 region->base[1] >=
151 region->base[3]))
152 {
153 return (DNS_R_FORMERR);
154 }
155 isc_region_consume(region, 2);
156 }
157 break;
158 }
159 case sbpr_text:
160 case sbpr_base64:
161 break;
162 case sbpr_dohpath:
163 /*
164 * Minimum valid dohpath is "/{?dns}" as
165 * it MUST be relative (leading "/") and
166 * MUST contain "{?dns}".
167 */
168 if (region->length < 7) {
169 return (DNS_R_FORMERR);
170 }
171 /* MUST be relative */
172 if (region->base[0] != '/') {
173 return (DNS_R_FORMERR);
174 }
175 /* MUST be UTF8 */
176 if (!isc_utf8_valid(region->base,
177 region->length))
178 {
179 return (DNS_R_FORMERR);
180 }
181 /* MUST contain "{?dns}" */
182 if (strnstr((char *)region->base, "{?dns}",
183 region->length) == NULL)
184 {
185 return (DNS_R_FORMERR);
186 }
187 break;
188 case sbpr_empty:
189 if (region->length != 0) {
190 return (DNS_R_FORMERR);
191 }
192 break;
193 }
194 }
195 }
196 return (ISC_R_SUCCESS);
197 }
198
199 /*
200 * Parse keyname from region.
201 */
202 static isc_result_t
svc_keyfromregion(isc_textregion_t * region,char sep,uint16_t * value,isc_buffer_t * target)203 svc_keyfromregion(isc_textregion_t *region, char sep, uint16_t *value,
204 isc_buffer_t *target) {
205 char *e = NULL;
206 size_t i;
207 unsigned long ul;
208
209 /* Look for known key names. */
210 for (i = 0; i < ARRAYSIZE(sbpr); i++) {
211 size_t len = strlen(sbpr[i].name);
212 if (strncasecmp(region->base, sbpr[i].name, len) != 0 ||
213 (region->base[len] != 0 && region->base[len] != sep))
214 {
215 continue;
216 }
217 isc_textregion_consume(region, len);
218 ul = sbpr[i].value;
219 goto finish;
220 }
221 /* Handle keyXXXXX form. */
222 if (strncmp(region->base, "key", 3) != 0) {
223 return (DNS_R_SYNTAX);
224 }
225 isc_textregion_consume(region, 3);
226 /* Disallow [+-]XXXXX which is allowed by strtoul. */
227 if (region->length == 0 || *region->base == '-' || *region->base == '+')
228 {
229 return (DNS_R_SYNTAX);
230 }
231 /* No zero padding. */
232 if (region->length > 1 && *region->base == '0' &&
233 region->base[1] != sep)
234 {
235 return (DNS_R_SYNTAX);
236 }
237 ul = strtoul(region->base, &e, 10);
238 /* Valid number? */
239 if (e == region->base || (*e != sep && *e != 0)) {
240 return (DNS_R_SYNTAX);
241 }
242 if (ul > 0xffff) {
243 return (ISC_R_RANGE);
244 }
245 isc_textregion_consume(region, e - region->base);
246 finish:
247 if (sep == ',' && region->length == 1) {
248 return (DNS_R_SYNTAX);
249 }
250 /* Consume separator. */
251 if (region->length != 0) {
252 isc_textregion_consume(region, 1);
253 }
254 RETERR(uint16_tobuffer(ul, target));
255 if (value != NULL) {
256 *value = ul;
257 }
258 return (ISC_R_SUCCESS);
259 }
260
261 static isc_result_t
svc_fromtext(isc_textregion_t * region,isc_buffer_t * target)262 svc_fromtext(isc_textregion_t *region, isc_buffer_t *target) {
263 char *e = NULL;
264 char abuf[16];
265 char tbuf[sizeof("aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:255.255.255.255,")];
266 isc_buffer_t sb;
267 isc_region_t keyregion;
268 size_t len;
269 uint16_t key;
270 unsigned int i;
271 unsigned int used;
272 unsigned long ul;
273
274 for (i = 0; i < ARRAYSIZE(sbpr); i++) {
275 len = strlen(sbpr[i].name);
276 if (strncmp(region->base, sbpr[i].name, len) != 0 ||
277 (region->base[len] != 0 && region->base[len] != '='))
278 {
279 continue;
280 }
281
282 if (region->base[len] == '=') {
283 len++;
284 }
285
286 RETERR(uint16_tobuffer(sbpr[i].value, target));
287 isc_textregion_consume(region, len);
288
289 sb = *target;
290 RETERR(uint16_tobuffer(0, target)); /* length */
291
292 switch (sbpr[i].encoding) {
293 case sbpr_text:
294 case sbpr_dohpath:
295 RETERR(multitxt_fromtext(region, target));
296 break;
297 case sbpr_alpn:
298 RETERR(alpn_fromtxt(region, target));
299 break;
300 case sbpr_port:
301 if (!isdigit((unsigned char)*region->base)) {
302 return (DNS_R_SYNTAX);
303 }
304 ul = strtoul(region->base, &e, 10);
305 if (*e != '\0') {
306 return (DNS_R_SYNTAX);
307 }
308 if (ul > 0xffff) {
309 return (ISC_R_RANGE);
310 }
311 RETERR(uint16_tobuffer(ul, target));
312 break;
313 case sbpr_ipv4s:
314 do {
315 snprintf(tbuf, sizeof(tbuf), "%*s",
316 (int)(region->length), region->base);
317 e = strchr(tbuf, ',');
318 if (e != NULL) {
319 *e++ = 0;
320 isc_textregion_consume(region,
321 e - tbuf);
322 }
323 if (inet_pton(AF_INET, tbuf, abuf) != 1) {
324 return (DNS_R_SYNTAX);
325 }
326 mem_tobuffer(target, abuf, 4);
327 } while (e != NULL);
328 break;
329 case sbpr_ipv6s:
330 do {
331 snprintf(tbuf, sizeof(tbuf), "%*s",
332 (int)(region->length), region->base);
333 e = strchr(tbuf, ',');
334 if (e != NULL) {
335 *e++ = 0;
336 isc_textregion_consume(region,
337 e - tbuf);
338 }
339 if (inet_pton(AF_INET6, tbuf, abuf) != 1) {
340 return (DNS_R_SYNTAX);
341 }
342 mem_tobuffer(target, abuf, 16);
343 } while (e != NULL);
344 break;
345 case sbpr_base64:
346 RETERR(isc_base64_decodestring(region->base, target));
347 break;
348 case sbpr_empty:
349 if (region->length != 0) {
350 return (DNS_R_SYNTAX);
351 }
352 break;
353 case sbpr_keylist:
354 if (region->length == 0) {
355 return (DNS_R_SYNTAX);
356 }
357 used = isc_buffer_usedlength(target);
358 while (region->length != 0) {
359 RETERR(svc_keyfromregion(region, ',', NULL,
360 target));
361 }
362 RETERR(svcsortkeylist(target, used));
363 break;
364 default:
365 UNREACHABLE();
366 }
367
368 len = isc_buffer_usedlength(target) -
369 isc_buffer_usedlength(&sb) - 2;
370 RETERR(uint16_tobuffer(len, &sb)); /* length */
371 switch (sbpr[i].encoding) {
372 case sbpr_dohpath:
373 /*
374 * Apply constraints not applied by multitxt_fromtext.
375 */
376 keyregion.base = isc_buffer_used(&sb);
377 keyregion.length = isc_buffer_usedlength(target) -
378 isc_buffer_usedlength(&sb);
379 RETERR(svcb_validate(sbpr[i].value, &keyregion));
380 break;
381 default:
382 break;
383 }
384 return (ISC_R_SUCCESS);
385 }
386
387 RETERR(svc_keyfromregion(region, '=', &key, target));
388 if (region->length == 0) {
389 RETERR(uint16_tobuffer(0, target)); /* length */
390 /* Sanity check keyXXXXX form. */
391 keyregion.base = isc_buffer_used(target);
392 keyregion.length = 0;
393 return (svcb_validate(key, &keyregion));
394 }
395 sb = *target;
396 RETERR(uint16_tobuffer(0, target)); /* dummy length */
397 RETERR(multitxt_fromtext(region, target));
398 len = isc_buffer_usedlength(target) - isc_buffer_usedlength(&sb) - 2;
399 RETERR(uint16_tobuffer(len, &sb)); /* length */
400 /* Sanity check keyXXXXX form. */
401 keyregion.base = isc_buffer_used(&sb);
402 keyregion.length = len;
403 return (svcb_validate(key, &keyregion));
404 }
405
406 static const char *
svcparamkey(unsigned short value,enum encoding * encoding,char * buf,size_t len)407 svcparamkey(unsigned short value, enum encoding *encoding, char *buf,
408 size_t len) {
409 size_t i;
410 int n;
411
412 for (i = 0; i < ARRAYSIZE(sbpr); i++) {
413 if (sbpr[i].value == value && sbpr[i].initial) {
414 *encoding = sbpr[i].encoding;
415 return (sbpr[i].name);
416 }
417 }
418 n = snprintf(buf, len, "key%u", value);
419 INSIST(n > 0 && (unsigned)n < len);
420 *encoding = sbpr_text;
421 return (buf);
422 }
423
424 static isc_result_t
svcsortkeys(isc_buffer_t * target,unsigned int used)425 svcsortkeys(isc_buffer_t *target, unsigned int used) {
426 isc_region_t r1, r2, man = { .base = NULL, .length = 0 };
427 unsigned char buf[1024];
428 uint16_t mankey = 0;
429 bool have_alpn = false;
430
431 if (isc_buffer_usedlength(target) == used) {
432 return (ISC_R_SUCCESS);
433 }
434
435 /*
436 * Get the parameters into r1.
437 */
438 isc_buffer_usedregion(target, &r1);
439 isc_region_consume(&r1, used);
440
441 while (1) {
442 uint16_t key1, len1, key2, len2;
443 unsigned char *base1, *base2;
444
445 r2 = r1;
446
447 /*
448 * Get the first parameter.
449 */
450 base1 = r1.base;
451 key1 = uint16_fromregion(&r1);
452 isc_region_consume(&r1, 2);
453 len1 = uint16_fromregion(&r1);
454 isc_region_consume(&r1, 2);
455 isc_region_consume(&r1, len1);
456
457 /*
458 * Was there only one key left?
459 */
460 if (r1.length == 0) {
461 if (mankey != 0) {
462 /* Is this the last mandatory key? */
463 if (key1 != mankey || man.length != 0) {
464 return (DNS_R_INCONSISTENTRR);
465 }
466 } else if (key1 == SVCB_MAN_KEY) {
467 /* Lone mandatory field. */
468 return (DNS_R_DISALLOWED);
469 } else if (key1 == SVCB_NO_DEFAULT_ALPN_KEY &&
470 !have_alpn)
471 {
472 /* Missing required ALPN field. */
473 return (DNS_R_DISALLOWED);
474 }
475 return (ISC_R_SUCCESS);
476 }
477
478 /*
479 * Find the smallest parameter.
480 */
481 while (r1.length != 0) {
482 base2 = r1.base;
483 key2 = uint16_fromregion(&r1);
484 isc_region_consume(&r1, 2);
485 len2 = uint16_fromregion(&r1);
486 isc_region_consume(&r1, 2);
487 isc_region_consume(&r1, len2);
488 if (key2 == key1) {
489 return (DNS_R_DUPLICATE);
490 }
491 if (key2 < key1) {
492 base1 = base2;
493 key1 = key2;
494 len1 = len2;
495 }
496 }
497
498 /*
499 * Do we need to move the smallest parameter to the start?
500 */
501 if (base1 != r2.base) {
502 size_t offset = 0;
503 size_t bytes = len1 + 4;
504 size_t length = base1 - r2.base;
505
506 /*
507 * Move the smallest parameter to the start.
508 */
509 while (bytes > 0) {
510 size_t count;
511
512 if (bytes > sizeof(buf)) {
513 count = sizeof(buf);
514 } else {
515 count = bytes;
516 }
517 memmove(buf, base1, count);
518 memmove(r2.base + offset + count,
519 r2.base + offset, length);
520 memmove(r2.base + offset, buf, count);
521 base1 += count;
522 bytes -= count;
523 offset += count;
524 }
525 }
526
527 /*
528 * Check ALPN is present when NO-DEFAULT-ALPN is set.
529 */
530 if (key1 == SVCB_ALPN_KEY) {
531 have_alpn = true;
532 } else if (key1 == SVCB_NO_DEFAULT_ALPN_KEY && !have_alpn) {
533 /* Missing required ALPN field. */
534 return (DNS_R_DISALLOWED);
535 }
536
537 /*
538 * Check key against mandatory key list.
539 */
540 if (mankey != 0) {
541 if (key1 > mankey) {
542 return (DNS_R_INCONSISTENTRR);
543 }
544 if (key1 == mankey) {
545 if (man.length >= 2) {
546 mankey = uint16_fromregion(&man);
547 isc_region_consume(&man, 2);
548 } else {
549 mankey = 0;
550 }
551 }
552 }
553
554 /*
555 * Is this the mandatory key?
556 */
557 if (key1 == SVCB_MAN_KEY) {
558 man = r2;
559 man.length = len1 + 4;
560 isc_region_consume(&man, 4);
561 if (man.length >= 2) {
562 mankey = uint16_fromregion(&man);
563 isc_region_consume(&man, 2);
564 if (mankey == SVCB_MAN_KEY) {
565 return (DNS_R_DISALLOWED);
566 }
567 } else {
568 return (DNS_R_SYNTAX);
569 }
570 }
571
572 /*
573 * Consume the smallest parameter.
574 */
575 isc_region_consume(&r2, len1 + 4);
576 r1 = r2;
577 }
578 }
579
580 static isc_result_t
generic_fromtext_in_svcb(ARGS_FROMTEXT)581 generic_fromtext_in_svcb(ARGS_FROMTEXT) {
582 isc_token_t token;
583 dns_name_t name;
584 isc_buffer_t buffer;
585 bool alias;
586 bool ok = true;
587 unsigned int used;
588
589 UNUSED(type);
590 UNUSED(rdclass);
591 UNUSED(callbacks);
592
593 /*
594 * SvcPriority.
595 */
596 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
597 false));
598 if (token.value.as_ulong > 0xffffU) {
599 RETTOK(ISC_R_RANGE);
600 }
601 RETERR(uint16_tobuffer(token.value.as_ulong, target));
602
603 alias = token.value.as_ulong == 0;
604
605 /*
606 * TargetName.
607 */
608 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
609 false));
610 dns_name_init(&name, NULL);
611 buffer_fromregion(&buffer, &token.value.as_region);
612 if (origin == NULL) {
613 origin = dns_rootname;
614 }
615 RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
616 if (!alias && (options & DNS_RDATA_CHECKNAMES) != 0) {
617 ok = dns_name_ishostname(&name, false);
618 }
619 if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0) {
620 RETTOK(DNS_R_BADNAME);
621 }
622 if (!ok && callbacks != NULL) {
623 warn_badname(&name, lexer, callbacks);
624 }
625
626 if (alias) {
627 return (ISC_R_SUCCESS);
628 }
629
630 /*
631 * SvcParams
632 */
633 used = isc_buffer_usedlength(target);
634 while (1) {
635 RETERR(isc_lex_getmastertoken(lexer, &token,
636 isc_tokentype_qvpair, true));
637 if (token.type == isc_tokentype_eol ||
638 token.type == isc_tokentype_eof)
639 {
640 isc_lex_ungettoken(lexer, &token);
641 return (svcsortkeys(target, used));
642 }
643
644 if (token.type != isc_tokentype_string && /* key only */
645 token.type != isc_tokentype_qvpair &&
646 token.type != isc_tokentype_vpair)
647 {
648 RETTOK(DNS_R_SYNTAX);
649 }
650 RETTOK(svc_fromtext(&token.value.as_textregion, target));
651 }
652 }
653
654 static isc_result_t
fromtext_in_svcb(ARGS_FROMTEXT)655 fromtext_in_svcb(ARGS_FROMTEXT) {
656 REQUIRE(type == dns_rdatatype_svcb);
657 REQUIRE(rdclass == dns_rdataclass_in);
658 UNUSED(type);
659 UNUSED(rdclass);
660 UNUSED(callbacks);
661
662 return (generic_fromtext_in_svcb(CALL_FROMTEXT));
663 }
664
665 static isc_result_t
generic_totext_in_svcb(ARGS_TOTEXT)666 generic_totext_in_svcb(ARGS_TOTEXT) {
667 isc_region_t region;
668 dns_name_t name;
669 dns_name_t prefix;
670 bool sub;
671 char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
672 unsigned short num;
673 int n;
674
675 REQUIRE(rdata->length != 0);
676
677 dns_name_init(&name, NULL);
678 dns_name_init(&prefix, NULL);
679
680 dns_rdata_toregion(rdata, ®ion);
681
682 /*
683 * SvcPriority.
684 */
685 num = uint16_fromregion(®ion);
686 isc_region_consume(®ion, 2);
687 n = snprintf(buf, sizeof(buf), "%u ", num);
688 INSIST(n > 0 && (unsigned)n < sizeof(buf));
689 RETERR(str_totext(buf, target));
690
691 /*
692 * TargetName.
693 */
694 dns_name_fromregion(&name, ®ion);
695 isc_region_consume(®ion, name_length(&name));
696 sub = name_prefix(&name, tctx->origin, &prefix);
697 RETERR(dns_name_totext(&prefix, sub, target));
698
699 while (region.length > 0) {
700 isc_region_t r;
701 enum encoding encoding;
702
703 RETERR(str_totext(" ", target));
704
705 INSIST(region.length >= 2);
706 num = uint16_fromregion(®ion);
707 isc_region_consume(®ion, 2);
708 RETERR(str_totext(svcparamkey(num, &encoding, buf, sizeof(buf)),
709 target));
710
711 INSIST(region.length >= 2);
712 num = uint16_fromregion(®ion);
713 isc_region_consume(®ion, 2);
714
715 INSIST(region.length >= num);
716 r = region;
717 r.length = num;
718 isc_region_consume(®ion, num);
719 if (num == 0) {
720 continue;
721 }
722 if (encoding != sbpr_empty) {
723 RETERR(str_totext("=", target));
724 }
725 switch (encoding) {
726 case sbpr_text:
727 RETERR(multitxt_totext(&r, target));
728 break;
729 case sbpr_port:
730 num = uint16_fromregion(&r);
731 isc_region_consume(&r, 2);
732 n = snprintf(buf, sizeof(buf), "%u", num);
733 INSIST(n > 0 && (unsigned)n < sizeof(buf));
734 RETERR(str_totext(buf, target));
735 INSIST(r.length == 0U);
736 break;
737 case sbpr_ipv4s:
738 while (r.length > 0U) {
739 INSIST(r.length >= 4U);
740 inet_ntop(AF_INET, r.base, buf, sizeof(buf));
741 RETERR(str_totext(buf, target));
742 isc_region_consume(&r, 4);
743 if (r.length != 0U) {
744 RETERR(str_totext(",", target));
745 }
746 }
747 break;
748 case sbpr_ipv6s:
749 while (r.length > 0U) {
750 INSIST(r.length >= 16U);
751 inet_ntop(AF_INET6, r.base, buf, sizeof(buf));
752 RETERR(str_totext(buf, target));
753 isc_region_consume(&r, 16);
754 if (r.length != 0U) {
755 RETERR(str_totext(",", target));
756 }
757 }
758 break;
759 case sbpr_base64:
760 RETERR(isc_base64_totext(&r, 0, "", target));
761 break;
762 case sbpr_alpn:
763 INSIST(r.length != 0U);
764 RETERR(str_totext("\"", target));
765 while (r.length != 0) {
766 commatxt_totext(&r, false, true, target);
767 if (r.length != 0) {
768 RETERR(str_totext(",", target));
769 }
770 }
771 RETERR(str_totext("\"", target));
772 break;
773 case sbpr_empty:
774 INSIST(r.length == 0U);
775 break;
776 case sbpr_keylist:
777 while (r.length > 0) {
778 num = uint16_fromregion(&r);
779 isc_region_consume(&r, 2);
780 RETERR(str_totext(svcparamkey(num, &encoding,
781 buf, sizeof(buf)),
782 target));
783 if (r.length != 0) {
784 RETERR(str_totext(",", target));
785 }
786 }
787 break;
788 default:
789 UNREACHABLE();
790 }
791 }
792 return (ISC_R_SUCCESS);
793 }
794
795 static isc_result_t
totext_in_svcb(ARGS_TOTEXT)796 totext_in_svcb(ARGS_TOTEXT) {
797 REQUIRE(rdata->type == dns_rdatatype_svcb);
798 REQUIRE(rdata->rdclass == dns_rdataclass_in);
799 REQUIRE(rdata->length != 0);
800
801 return (generic_totext_in_svcb(CALL_TOTEXT));
802 }
803
804 static isc_result_t
generic_fromwire_in_svcb(ARGS_FROMWIRE)805 generic_fromwire_in_svcb(ARGS_FROMWIRE) {
806 dns_name_t name;
807 isc_region_t region, man = { .base = NULL, .length = 0 };
808 bool alias, first = true, have_alpn = false;
809 uint16_t lastkey = 0, mankey = 0;
810
811 UNUSED(type);
812 UNUSED(rdclass);
813
814 dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
815
816 dns_name_init(&name, NULL);
817
818 /*
819 * SvcPriority.
820 */
821 isc_buffer_activeregion(source, ®ion);
822 if (region.length < 2) {
823 return (ISC_R_UNEXPECTEDEND);
824 }
825 RETERR(mem_tobuffer(target, region.base, 2));
826 alias = uint16_fromregion(®ion) == 0;
827 isc_buffer_forward(source, 2);
828
829 /*
830 * TargetName.
831 */
832 RETERR(dns_name_fromwire(&name, source, dctx, options, target));
833
834 if (alias) {
835 return (ISC_R_SUCCESS);
836 }
837
838 /*
839 * SvcParams.
840 */
841 isc_buffer_activeregion(source, ®ion);
842 while (region.length > 0U) {
843 isc_region_t keyregion;
844 uint16_t key, len;
845
846 /*
847 * SvcParamKey
848 */
849 if (region.length < 2U) {
850 return (ISC_R_UNEXPECTEDEND);
851 }
852 RETERR(mem_tobuffer(target, region.base, 2));
853 key = uint16_fromregion(®ion);
854 isc_region_consume(®ion, 2);
855
856 /*
857 * Keys must be unique and in order.
858 */
859 if (!first && key <= lastkey) {
860 return (DNS_R_FORMERR);
861 }
862
863 /*
864 * Check mandatory keys.
865 */
866 if (mankey != 0) {
867 /* Missing mandatory key? */
868 if (key > mankey) {
869 return (DNS_R_FORMERR);
870 }
871 if (key == mankey) {
872 /* Get next mandatory key. */
873 if (man.length >= 2) {
874 mankey = uint16_fromregion(&man);
875 isc_region_consume(&man, 2);
876 } else {
877 mankey = 0;
878 }
879 }
880 }
881
882 /*
883 * Check alpn present when no-default-alpn is set.
884 */
885 if (key == SVCB_ALPN_KEY) {
886 have_alpn = true;
887 } else if (key == SVCB_NO_DEFAULT_ALPN_KEY && !have_alpn) {
888 return (DNS_R_FORMERR);
889 }
890
891 first = false;
892 lastkey = key;
893
894 /*
895 * SvcParamValue length.
896 */
897 if (region.length < 2U) {
898 return (ISC_R_UNEXPECTEDEND);
899 }
900 RETERR(mem_tobuffer(target, region.base, 2));
901 len = uint16_fromregion(®ion);
902 isc_region_consume(®ion, 2);
903
904 /*
905 * SvcParamValue.
906 */
907 if (region.length < len) {
908 return (ISC_R_UNEXPECTEDEND);
909 }
910
911 /*
912 * Remember manatory key.
913 */
914 if (key == SVCB_MAN_KEY) {
915 man = region;
916 man.length = len;
917 /* Get first mandatory key */
918 if (man.length >= 2) {
919 mankey = uint16_fromregion(&man);
920 isc_region_consume(&man, 2);
921 if (mankey == SVCB_MAN_KEY) {
922 return (DNS_R_FORMERR);
923 }
924 } else {
925 return (DNS_R_FORMERR);
926 }
927 }
928 keyregion = region;
929 keyregion.length = len;
930 RETERR(svcb_validate(key, &keyregion));
931 RETERR(mem_tobuffer(target, region.base, len));
932 isc_region_consume(®ion, len);
933 isc_buffer_forward(source, len + 4);
934 }
935
936 /*
937 * Do we have an outstanding mandatory key?
938 */
939 if (mankey != 0) {
940 return (DNS_R_FORMERR);
941 }
942
943 return (ISC_R_SUCCESS);
944 }
945
946 static isc_result_t
fromwire_in_svcb(ARGS_FROMWIRE)947 fromwire_in_svcb(ARGS_FROMWIRE) {
948 REQUIRE(type == dns_rdatatype_svcb);
949 REQUIRE(rdclass == dns_rdataclass_in);
950
951 return (generic_fromwire_in_svcb(CALL_FROMWIRE));
952 }
953
954 static isc_result_t
generic_towire_in_svcb(ARGS_TOWIRE)955 generic_towire_in_svcb(ARGS_TOWIRE) {
956 dns_name_t name;
957 dns_offsets_t offsets;
958 isc_region_t region;
959
960 REQUIRE(rdata->length != 0);
961
962 dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
963
964 /*
965 * SvcPriority.
966 */
967 dns_rdata_toregion(rdata, ®ion);
968 RETERR(mem_tobuffer(target, region.base, 2));
969 isc_region_consume(®ion, 2);
970
971 /*
972 * TargetName.
973 */
974 dns_name_init(&name, offsets);
975 dns_name_fromregion(&name, ®ion);
976 RETERR(dns_name_towire(&name, cctx, target));
977 isc_region_consume(®ion, name_length(&name));
978
979 /*
980 * SvcParams.
981 */
982 return (mem_tobuffer(target, region.base, region.length));
983 }
984
985 static isc_result_t
towire_in_svcb(ARGS_TOWIRE)986 towire_in_svcb(ARGS_TOWIRE) {
987 REQUIRE(rdata->type == dns_rdatatype_svcb);
988 REQUIRE(rdata->length != 0);
989
990 return (generic_towire_in_svcb(CALL_TOWIRE));
991 }
992
993 static int
compare_in_svcb(ARGS_COMPARE)994 compare_in_svcb(ARGS_COMPARE) {
995 isc_region_t region1;
996 isc_region_t region2;
997
998 REQUIRE(rdata1->type == rdata2->type);
999 REQUIRE(rdata1->rdclass == rdata2->rdclass);
1000 REQUIRE(rdata1->type == dns_rdatatype_svcb);
1001 REQUIRE(rdata1->rdclass == dns_rdataclass_in);
1002 REQUIRE(rdata1->length != 0);
1003 REQUIRE(rdata2->length != 0);
1004
1005 dns_rdata_toregion(rdata1, ®ion1);
1006 dns_rdata_toregion(rdata2, ®ion2);
1007
1008 return (isc_region_compare(®ion1, ®ion2));
1009 }
1010
1011 static isc_result_t
generic_fromstruct_in_svcb(ARGS_FROMSTRUCT)1012 generic_fromstruct_in_svcb(ARGS_FROMSTRUCT) {
1013 dns_rdata_in_svcb_t *svcb = source;
1014 isc_region_t region;
1015
1016 REQUIRE(svcb != NULL);
1017 REQUIRE(svcb->common.rdtype == type);
1018 REQUIRE(svcb->common.rdclass == rdclass);
1019
1020 UNUSED(type);
1021 UNUSED(rdclass);
1022
1023 RETERR(uint16_tobuffer(svcb->priority, target));
1024 dns_name_toregion(&svcb->svcdomain, ®ion);
1025 RETERR(isc_buffer_copyregion(target, ®ion));
1026
1027 return (mem_tobuffer(target, svcb->svc, svcb->svclen));
1028 }
1029
1030 static isc_result_t
fromstruct_in_svcb(ARGS_FROMSTRUCT)1031 fromstruct_in_svcb(ARGS_FROMSTRUCT) {
1032 dns_rdata_in_svcb_t *svcb = source;
1033
1034 REQUIRE(type == dns_rdatatype_svcb);
1035 REQUIRE(rdclass == dns_rdataclass_in);
1036 REQUIRE(svcb != NULL);
1037 REQUIRE(svcb->common.rdtype == type);
1038 REQUIRE(svcb->common.rdclass == rdclass);
1039
1040 return (generic_fromstruct_in_svcb(CALL_FROMSTRUCT));
1041 }
1042
1043 static isc_result_t
generic_tostruct_in_svcb(ARGS_TOSTRUCT)1044 generic_tostruct_in_svcb(ARGS_TOSTRUCT) {
1045 isc_region_t region;
1046 dns_rdata_in_svcb_t *svcb = target;
1047 dns_name_t name;
1048
1049 REQUIRE(svcb != NULL);
1050 REQUIRE(rdata->length != 0);
1051
1052 svcb->common.rdclass = rdata->rdclass;
1053 svcb->common.rdtype = rdata->type;
1054 ISC_LINK_INIT(&svcb->common, link);
1055
1056 dns_rdata_toregion(rdata, ®ion);
1057
1058 svcb->priority = uint16_fromregion(®ion);
1059 isc_region_consume(®ion, 2);
1060
1061 dns_name_init(&svcb->svcdomain, NULL);
1062 dns_name_init(&name, NULL);
1063 dns_name_fromregion(&name, ®ion);
1064 isc_region_consume(®ion, name_length(&name));
1065
1066 RETERR(name_duporclone(&name, mctx, &svcb->svcdomain));
1067 svcb->svclen = region.length;
1068 svcb->svc = mem_maybedup(mctx, region.base, region.length);
1069
1070 if (svcb->svc == NULL) {
1071 if (mctx != NULL) {
1072 dns_name_free(&svcb->svcdomain, svcb->mctx);
1073 }
1074 return (ISC_R_NOMEMORY);
1075 }
1076
1077 svcb->offset = 0;
1078 svcb->mctx = mctx;
1079
1080 return (ISC_R_SUCCESS);
1081 }
1082
1083 static isc_result_t
tostruct_in_svcb(ARGS_TOSTRUCT)1084 tostruct_in_svcb(ARGS_TOSTRUCT) {
1085 dns_rdata_in_svcb_t *svcb = target;
1086
1087 REQUIRE(rdata->rdclass == dns_rdataclass_in);
1088 REQUIRE(rdata->type == dns_rdatatype_svcb);
1089 REQUIRE(svcb != NULL);
1090 REQUIRE(rdata->length != 0);
1091
1092 return (generic_tostruct_in_svcb(CALL_TOSTRUCT));
1093 }
1094
1095 static void
generic_freestruct_in_svcb(ARGS_FREESTRUCT)1096 generic_freestruct_in_svcb(ARGS_FREESTRUCT) {
1097 dns_rdata_in_svcb_t *svcb = source;
1098
1099 REQUIRE(svcb != NULL);
1100
1101 if (svcb->mctx == NULL) {
1102 return;
1103 }
1104
1105 dns_name_free(&svcb->svcdomain, svcb->mctx);
1106 isc_mem_free(svcb->mctx, svcb->svc);
1107 svcb->mctx = NULL;
1108 }
1109
1110 static void
freestruct_in_svcb(ARGS_FREESTRUCT)1111 freestruct_in_svcb(ARGS_FREESTRUCT) {
1112 dns_rdata_in_svcb_t *svcb = source;
1113
1114 REQUIRE(svcb != NULL);
1115 REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
1116 REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
1117
1118 generic_freestruct_in_svcb(CALL_FREESTRUCT);
1119 }
1120
1121 static isc_result_t
generic_additionaldata_in_svcb(ARGS_ADDLDATA)1122 generic_additionaldata_in_svcb(ARGS_ADDLDATA) {
1123 UNUSED(rdata);
1124 UNUSED(add);
1125 UNUSED(arg);
1126
1127 return (ISC_R_SUCCESS);
1128 }
1129
1130 static isc_result_t
additionaldata_in_svcb(ARGS_ADDLDATA)1131 additionaldata_in_svcb(ARGS_ADDLDATA) {
1132 REQUIRE(rdata->type == dns_rdatatype_svcb);
1133 REQUIRE(rdata->rdclass == dns_rdataclass_in);
1134
1135 return (generic_additionaldata_in_svcb(CALL_ADDLDATA));
1136 }
1137
1138 static isc_result_t
digest_in_svcb(ARGS_DIGEST)1139 digest_in_svcb(ARGS_DIGEST) {
1140 isc_region_t region1;
1141
1142 REQUIRE(rdata->type == dns_rdatatype_svcb);
1143 REQUIRE(rdata->rdclass == dns_rdataclass_in);
1144
1145 dns_rdata_toregion(rdata, ®ion1);
1146 return ((digest)(arg, ®ion1));
1147 }
1148
1149 static bool
checkowner_in_svcb(ARGS_CHECKOWNER)1150 checkowner_in_svcb(ARGS_CHECKOWNER) {
1151 REQUIRE(type == dns_rdatatype_svcb);
1152 REQUIRE(rdclass == dns_rdataclass_in);
1153
1154 UNUSED(name);
1155 UNUSED(type);
1156 UNUSED(rdclass);
1157 UNUSED(wildcard);
1158
1159 return (true);
1160 }
1161
1162 static bool
generic_checknames_in_svcb(ARGS_CHECKNAMES)1163 generic_checknames_in_svcb(ARGS_CHECKNAMES) {
1164 isc_region_t region;
1165 dns_name_t name;
1166 bool alias;
1167
1168 UNUSED(owner);
1169
1170 dns_rdata_toregion(rdata, ®ion);
1171 INSIST(region.length > 1);
1172 alias = uint16_fromregion(®ion) == 0;
1173 isc_region_consume(®ion, 2);
1174 dns_name_init(&name, NULL);
1175 dns_name_fromregion(&name, ®ion);
1176 if (!alias && !dns_name_ishostname(&name, false)) {
1177 if (bad != NULL) {
1178 dns_name_clone(&name, bad);
1179 }
1180 return (false);
1181 }
1182 return (true);
1183 }
1184
1185 static bool
checknames_in_svcb(ARGS_CHECKNAMES)1186 checknames_in_svcb(ARGS_CHECKNAMES) {
1187 REQUIRE(rdata->type == dns_rdatatype_svcb);
1188 REQUIRE(rdata->rdclass == dns_rdataclass_in);
1189
1190 return (generic_checknames_in_svcb(CALL_CHECKNAMES));
1191 }
1192
1193 static int
casecompare_in_svcb(ARGS_COMPARE)1194 casecompare_in_svcb(ARGS_COMPARE) {
1195 return (compare_in_svcb(rdata1, rdata2));
1196 }
1197
1198 static isc_result_t
generic_rdata_in_svcb_first(dns_rdata_in_svcb_t * svcb)1199 generic_rdata_in_svcb_first(dns_rdata_in_svcb_t *svcb) {
1200 if (svcb->svclen == 0) {
1201 return (ISC_R_NOMORE);
1202 }
1203 svcb->offset = 0;
1204 return (ISC_R_SUCCESS);
1205 }
1206
1207 static isc_result_t
generic_rdata_in_svcb_next(dns_rdata_in_svcb_t * svcb)1208 generic_rdata_in_svcb_next(dns_rdata_in_svcb_t *svcb) {
1209 isc_region_t region;
1210 size_t len;
1211
1212 if (svcb->offset >= svcb->svclen) {
1213 return (ISC_R_NOMORE);
1214 }
1215
1216 region.base = svcb->svc + svcb->offset;
1217 region.length = svcb->svclen - svcb->offset;
1218 INSIST(region.length >= 4);
1219 isc_region_consume(®ion, 2);
1220 len = uint16_fromregion(®ion);
1221 INSIST(region.length >= len + 2);
1222 svcb->offset += len + 4;
1223 return (svcb->offset >= svcb->svclen ? ISC_R_NOMORE : ISC_R_SUCCESS);
1224 }
1225
1226 static void
generic_rdata_in_svcb_current(dns_rdata_in_svcb_t * svcb,isc_region_t * region)1227 generic_rdata_in_svcb_current(dns_rdata_in_svcb_t *svcb, isc_region_t *region) {
1228 size_t len;
1229
1230 INSIST(svcb->offset <= svcb->svclen);
1231
1232 region->base = svcb->svc + svcb->offset;
1233 region->length = svcb->svclen - svcb->offset;
1234 INSIST(region->length >= 4);
1235 isc_region_consume(region, 2);
1236 len = uint16_fromregion(region);
1237 INSIST(region->length >= len + 2);
1238 region->base = svcb->svc + svcb->offset;
1239 region->length = len + 4;
1240 }
1241
1242 isc_result_t
dns_rdata_in_svcb_first(dns_rdata_in_svcb_t * svcb)1243 dns_rdata_in_svcb_first(dns_rdata_in_svcb_t *svcb) {
1244 REQUIRE(svcb != NULL);
1245 REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
1246 REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
1247
1248 return (generic_rdata_in_svcb_first(svcb));
1249 }
1250
1251 isc_result_t
dns_rdata_in_svcb_next(dns_rdata_in_svcb_t * svcb)1252 dns_rdata_in_svcb_next(dns_rdata_in_svcb_t *svcb) {
1253 REQUIRE(svcb != NULL);
1254 REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
1255 REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
1256
1257 return (generic_rdata_in_svcb_next(svcb));
1258 }
1259
1260 void
dns_rdata_in_svcb_current(dns_rdata_in_svcb_t * svcb,isc_region_t * region)1261 dns_rdata_in_svcb_current(dns_rdata_in_svcb_t *svcb, isc_region_t *region) {
1262 REQUIRE(svcb != NULL);
1263 REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
1264 REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
1265 REQUIRE(region != NULL);
1266
1267 generic_rdata_in_svcb_current(svcb, region);
1268 }
1269
1270 #endif /* RDATA_IN_1_SVCB_64_C */
1271