1/*	$NetBSD: a6_38.c,v 1.1 2024/02/18 20:57:45 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/* RFC2874 */
17
18#ifndef RDATA_IN_1_A6_28_C
19#define RDATA_IN_1_A6_28_C
20
21#include <isc/net.h>
22
23#define RRTYPE_A6_ATTRIBUTES (0)
24
25static isc_result_t
26fromtext_in_a6(ARGS_FROMTEXT) {
27	isc_token_t token;
28	unsigned char addr[16];
29	unsigned char prefixlen;
30	unsigned char octets;
31	unsigned char mask;
32	dns_name_t name;
33	isc_buffer_t buffer;
34	bool ok;
35
36	REQUIRE(type == dns_rdatatype_a6);
37	REQUIRE(rdclass == dns_rdataclass_in);
38
39	UNUSED(type);
40	UNUSED(rdclass);
41	UNUSED(callbacks);
42
43	/*
44	 * Prefix length.
45	 */
46	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
47				      false));
48	if (token.value.as_ulong > 128U) {
49		RETTOK(ISC_R_RANGE);
50	}
51
52	prefixlen = (unsigned char)token.value.as_ulong;
53	RETERR(mem_tobuffer(target, &prefixlen, 1));
54
55	/*
56	 * Suffix.
57	 */
58	if (prefixlen != 128) {
59		/*
60		 * Prefix 0..127.
61		 */
62		octets = prefixlen / 8;
63		/*
64		 * Octets 0..15.
65		 */
66		RETERR(isc_lex_getmastertoken(lexer, &token,
67					      isc_tokentype_string, false));
68		if (inet_pton(AF_INET6, DNS_AS_STR(token), addr) != 1) {
69			RETTOK(DNS_R_BADAAAA);
70		}
71		mask = 0xff >> (prefixlen % 8);
72		addr[octets] &= mask;
73		RETERR(mem_tobuffer(target, &addr[octets], 16 - octets));
74	}
75
76	if (prefixlen == 0) {
77		return (ISC_R_SUCCESS);
78	}
79
80	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
81				      false));
82	dns_name_init(&name, NULL);
83	buffer_fromregion(&buffer, &token.value.as_region);
84	if (origin == NULL) {
85		origin = dns_rootname;
86	}
87	RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
88	ok = true;
89	if ((options & DNS_RDATA_CHECKNAMES) != 0) {
90		ok = dns_name_ishostname(&name, false);
91	}
92	if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0) {
93		RETTOK(DNS_R_BADNAME);
94	}
95	if (!ok && callbacks != NULL) {
96		warn_badname(&name, lexer, callbacks);
97	}
98	return (ISC_R_SUCCESS);
99}
100
101static isc_result_t
102totext_in_a6(ARGS_TOTEXT) {
103	isc_region_t sr, ar;
104	unsigned char addr[16];
105	unsigned char prefixlen;
106	unsigned char octets;
107	unsigned char mask;
108	char buf[sizeof("128")];
109	dns_name_t name;
110	dns_name_t prefix;
111	bool sub;
112
113	REQUIRE(rdata->type == dns_rdatatype_a6);
114	REQUIRE(rdata->rdclass == dns_rdataclass_in);
115	REQUIRE(rdata->length != 0);
116
117	dns_rdata_toregion(rdata, &sr);
118	prefixlen = sr.base[0];
119	INSIST(prefixlen <= 128);
120	isc_region_consume(&sr, 1);
121	snprintf(buf, sizeof(buf), "%u", prefixlen);
122	RETERR(str_totext(buf, target));
123	RETERR(str_totext(" ", target));
124
125	if (prefixlen != 128) {
126		octets = prefixlen / 8;
127		memset(addr, 0, sizeof(addr));
128		memmove(&addr[octets], sr.base, 16 - octets);
129		mask = 0xff >> (prefixlen % 8);
130		addr[octets] &= mask;
131		ar.base = addr;
132		ar.length = sizeof(addr);
133		RETERR(inet_totext(AF_INET6, tctx->flags, &ar, target));
134		isc_region_consume(&sr, 16 - octets);
135	}
136
137	if (prefixlen == 0) {
138		return (ISC_R_SUCCESS);
139	}
140
141	RETERR(str_totext(" ", target));
142	dns_name_init(&name, NULL);
143	dns_name_init(&prefix, NULL);
144	dns_name_fromregion(&name, &sr);
145	sub = name_prefix(&name, tctx->origin, &prefix);
146	return (dns_name_totext(&prefix, sub, target));
147}
148
149static isc_result_t
150fromwire_in_a6(ARGS_FROMWIRE) {
151	isc_region_t sr;
152	unsigned char prefixlen;
153	unsigned char octets;
154	unsigned char mask;
155	dns_name_t name;
156
157	REQUIRE(type == dns_rdatatype_a6);
158	REQUIRE(rdclass == dns_rdataclass_in);
159
160	UNUSED(type);
161	UNUSED(rdclass);
162
163	dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
164
165	isc_buffer_activeregion(source, &sr);
166	/*
167	 * Prefix length.
168	 */
169	if (sr.length < 1) {
170		return (ISC_R_UNEXPECTEDEND);
171	}
172	prefixlen = sr.base[0];
173	if (prefixlen > 128) {
174		return (ISC_R_RANGE);
175	}
176	isc_region_consume(&sr, 1);
177	RETERR(mem_tobuffer(target, &prefixlen, 1));
178	isc_buffer_forward(source, 1);
179
180	/*
181	 * Suffix.
182	 */
183	if (prefixlen != 128) {
184		octets = 16 - prefixlen / 8;
185		if (sr.length < octets) {
186			return (ISC_R_UNEXPECTEDEND);
187		}
188		mask = 0xff >> (prefixlen % 8);
189		if ((sr.base[0] & ~mask) != 0) {
190			return (DNS_R_FORMERR);
191		}
192		RETERR(mem_tobuffer(target, sr.base, octets));
193		isc_buffer_forward(source, octets);
194	}
195
196	if (prefixlen == 0) {
197		return (ISC_R_SUCCESS);
198	}
199
200	dns_name_init(&name, NULL);
201	return (dns_name_fromwire(&name, source, dctx, options, target));
202}
203
204static isc_result_t
205towire_in_a6(ARGS_TOWIRE) {
206	isc_region_t sr;
207	dns_name_t name;
208	dns_offsets_t offsets;
209	unsigned char prefixlen;
210	unsigned char octets;
211
212	REQUIRE(rdata->type == dns_rdatatype_a6);
213	REQUIRE(rdata->rdclass == dns_rdataclass_in);
214	REQUIRE(rdata->length != 0);
215
216	dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
217	dns_rdata_toregion(rdata, &sr);
218	prefixlen = sr.base[0];
219	INSIST(prefixlen <= 128);
220
221	octets = 1 + 16 - prefixlen / 8;
222	RETERR(mem_tobuffer(target, sr.base, octets));
223	isc_region_consume(&sr, octets);
224
225	if (prefixlen == 0) {
226		return (ISC_R_SUCCESS);
227	}
228
229	dns_name_init(&name, offsets);
230	dns_name_fromregion(&name, &sr);
231	return (dns_name_towire(&name, cctx, target));
232}
233
234static int
235compare_in_a6(ARGS_COMPARE) {
236	int order;
237	unsigned char prefixlen1, prefixlen2;
238	unsigned char octets;
239	dns_name_t name1;
240	dns_name_t name2;
241	isc_region_t region1;
242	isc_region_t region2;
243
244	REQUIRE(rdata1->type == rdata2->type);
245	REQUIRE(rdata1->rdclass == rdata2->rdclass);
246	REQUIRE(rdata1->type == dns_rdatatype_a6);
247	REQUIRE(rdata1->rdclass == dns_rdataclass_in);
248	REQUIRE(rdata1->length != 0);
249	REQUIRE(rdata2->length != 0);
250
251	dns_rdata_toregion(rdata1, &region1);
252	dns_rdata_toregion(rdata2, &region2);
253	prefixlen1 = region1.base[0];
254	prefixlen2 = region2.base[0];
255	isc_region_consume(&region1, 1);
256	isc_region_consume(&region2, 1);
257	if (prefixlen1 < prefixlen2) {
258		return (-1);
259	} else if (prefixlen1 > prefixlen2) {
260		return (1);
261	}
262	/*
263	 * Prefix lengths are equal.
264	 */
265	octets = 16 - prefixlen1 / 8;
266
267	if (octets > 0) {
268		order = memcmp(region1.base, region2.base, octets);
269		if (order < 0) {
270			return (-1);
271		} else if (order > 0) {
272			return (1);
273		}
274		/*
275		 * Address suffixes are equal.
276		 */
277		if (prefixlen1 == 0) {
278			return (order);
279		}
280		isc_region_consume(&region1, octets);
281		isc_region_consume(&region2, octets);
282	}
283
284	dns_name_init(&name1, NULL);
285	dns_name_init(&name2, NULL);
286	dns_name_fromregion(&name1, &region1);
287	dns_name_fromregion(&name2, &region2);
288	return (dns_name_rdatacompare(&name1, &name2));
289}
290
291static isc_result_t
292fromstruct_in_a6(ARGS_FROMSTRUCT) {
293	dns_rdata_in_a6_t *a6 = source;
294	isc_region_t region;
295	int octets;
296	uint8_t bits;
297	uint8_t first;
298	uint8_t mask;
299
300	REQUIRE(type == dns_rdatatype_a6);
301	REQUIRE(rdclass == dns_rdataclass_in);
302	REQUIRE(a6 != NULL);
303	REQUIRE(a6->common.rdtype == type);
304	REQUIRE(a6->common.rdclass == rdclass);
305
306	UNUSED(type);
307	UNUSED(rdclass);
308
309	if (a6->prefixlen > 128) {
310		return (ISC_R_RANGE);
311	}
312
313	RETERR(uint8_tobuffer(a6->prefixlen, target));
314
315	/* Suffix */
316	if (a6->prefixlen != 128) {
317		octets = 16 - a6->prefixlen / 8;
318		bits = a6->prefixlen % 8;
319		if (bits != 0) {
320			mask = 0xffU >> bits;
321			first = a6->in6_addr.s6_addr[16 - octets] & mask;
322			RETERR(uint8_tobuffer(first, target));
323			octets--;
324		}
325		if (octets > 0) {
326			RETERR(mem_tobuffer(target,
327					    a6->in6_addr.s6_addr + 16 - octets,
328					    octets));
329		}
330	}
331
332	if (a6->prefixlen == 0) {
333		return (ISC_R_SUCCESS);
334	}
335	dns_name_toregion(&a6->prefix, &region);
336	return (isc_buffer_copyregion(target, &region));
337}
338
339static isc_result_t
340tostruct_in_a6(ARGS_TOSTRUCT) {
341	dns_rdata_in_a6_t *a6 = target;
342	unsigned char octets;
343	dns_name_t name;
344	isc_region_t r;
345
346	REQUIRE(rdata->type == dns_rdatatype_a6);
347	REQUIRE(rdata->rdclass == dns_rdataclass_in);
348	REQUIRE(a6 != NULL);
349	REQUIRE(rdata->length != 0);
350
351	a6->common.rdclass = rdata->rdclass;
352	a6->common.rdtype = rdata->type;
353	ISC_LINK_INIT(&a6->common, link);
354
355	dns_rdata_toregion(rdata, &r);
356
357	a6->prefixlen = uint8_fromregion(&r);
358	isc_region_consume(&r, 1);
359	memset(a6->in6_addr.s6_addr, 0, sizeof(a6->in6_addr.s6_addr));
360
361	/*
362	 * Suffix.
363	 */
364	if (a6->prefixlen != 128) {
365		octets = 16 - a6->prefixlen / 8;
366		INSIST(r.length >= octets);
367		memmove(a6->in6_addr.s6_addr + 16 - octets, r.base, octets);
368		isc_region_consume(&r, octets);
369	}
370
371	/*
372	 * Prefix.
373	 */
374	dns_name_init(&a6->prefix, NULL);
375	if (a6->prefixlen != 0) {
376		dns_name_init(&name, NULL);
377		dns_name_fromregion(&name, &r);
378		RETERR(name_duporclone(&name, mctx, &a6->prefix));
379	}
380	a6->mctx = mctx;
381	return (ISC_R_SUCCESS);
382}
383
384static void
385freestruct_in_a6(ARGS_FREESTRUCT) {
386	dns_rdata_in_a6_t *a6 = source;
387
388	REQUIRE(a6 != NULL);
389	REQUIRE(a6->common.rdclass == dns_rdataclass_in);
390	REQUIRE(a6->common.rdtype == dns_rdatatype_a6);
391
392	if (a6->mctx == NULL) {
393		return;
394	}
395
396	if (dns_name_dynamic(&a6->prefix)) {
397		dns_name_free(&a6->prefix, a6->mctx);
398	}
399	a6->mctx = NULL;
400}
401
402static isc_result_t
403additionaldata_in_a6(ARGS_ADDLDATA) {
404	REQUIRE(rdata->type == dns_rdatatype_a6);
405	REQUIRE(rdata->rdclass == dns_rdataclass_in);
406
407	UNUSED(rdata);
408	UNUSED(add);
409	UNUSED(arg);
410
411	return (ISC_R_SUCCESS);
412}
413
414static isc_result_t
415digest_in_a6(ARGS_DIGEST) {
416	isc_region_t r1, r2;
417	unsigned char prefixlen, octets;
418	isc_result_t result;
419	dns_name_t name;
420
421	REQUIRE(rdata->type == dns_rdatatype_a6);
422	REQUIRE(rdata->rdclass == dns_rdataclass_in);
423
424	dns_rdata_toregion(rdata, &r1);
425	r2 = r1;
426	prefixlen = r1.base[0];
427	octets = 1 + 16 - prefixlen / 8;
428
429	r1.length = octets;
430	result = (digest)(arg, &r1);
431	if (result != ISC_R_SUCCESS) {
432		return (result);
433	}
434	if (prefixlen == 0) {
435		return (ISC_R_SUCCESS);
436	}
437
438	isc_region_consume(&r2, octets);
439	dns_name_init(&name, NULL);
440	dns_name_fromregion(&name, &r2);
441	return (dns_name_digest(&name, digest, arg));
442}
443
444static bool
445checkowner_in_a6(ARGS_CHECKOWNER) {
446	REQUIRE(type == dns_rdatatype_a6);
447	REQUIRE(rdclass == dns_rdataclass_in);
448
449	UNUSED(type);
450	UNUSED(rdclass);
451
452	return (dns_name_ishostname(name, wildcard));
453}
454
455static bool
456checknames_in_a6(ARGS_CHECKNAMES) {
457	isc_region_t region;
458	dns_name_t name;
459	unsigned int prefixlen;
460
461	REQUIRE(rdata->type == dns_rdatatype_a6);
462	REQUIRE(rdata->rdclass == dns_rdataclass_in);
463
464	UNUSED(owner);
465
466	dns_rdata_toregion(rdata, &region);
467	prefixlen = uint8_fromregion(&region);
468	if (prefixlen == 0) {
469		return (true);
470	}
471	isc_region_consume(&region, 1 + 16 - prefixlen / 8);
472	dns_name_init(&name, NULL);
473	dns_name_fromregion(&name, &region);
474	if (!dns_name_ishostname(&name, false)) {
475		if (bad != NULL) {
476			dns_name_clone(&name, bad);
477		}
478		return (false);
479	}
480	return (true);
481}
482
483static int
484casecompare_in_a6(ARGS_COMPARE) {
485	return (compare_in_a6(rdata1, rdata2));
486}
487
488#endif /* RDATA_IN_1_A6_38_C */
489