1/*	$NetBSD: apl_42.c,v 1.9 2024/02/21 22:52:14 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/* RFC3123 */
17
18#ifndef RDATA_IN_1_APL_42_C
19#define RDATA_IN_1_APL_42_C
20
21#define RRTYPE_APL_ATTRIBUTES (0)
22
23static isc_result_t
24fromtext_in_apl(ARGS_FROMTEXT) {
25	isc_token_t token;
26	unsigned char addr[16];
27	unsigned long afi;
28	uint8_t prefix;
29	uint8_t len;
30	bool neg;
31	char *cp, *ap, *slash;
32	int n;
33
34	REQUIRE(type == dns_rdatatype_apl);
35	REQUIRE(rdclass == dns_rdataclass_in);
36
37	UNUSED(type);
38	UNUSED(rdclass);
39	UNUSED(origin);
40	UNUSED(options);
41	UNUSED(callbacks);
42
43	do {
44		RETERR(isc_lex_getmastertoken(lexer, &token,
45					      isc_tokentype_string, true));
46		if (token.type != isc_tokentype_string) {
47			break;
48		}
49
50		cp = DNS_AS_STR(token);
51		neg = (*cp == '!');
52		if (neg) {
53			cp++;
54		}
55		afi = strtoul(cp, &ap, 10);
56		if (*ap++ != ':' || cp == ap) {
57			RETTOK(DNS_R_SYNTAX);
58		}
59		if (afi > 0xffffU) {
60			RETTOK(ISC_R_RANGE);
61		}
62		slash = strchr(ap, '/');
63		if (slash == NULL || slash == ap) {
64			RETTOK(DNS_R_SYNTAX);
65		}
66		RETTOK(isc_parse_uint8(&prefix, slash + 1, 10));
67		switch (afi) {
68		case 1:
69			*slash = '\0';
70			n = inet_pton(AF_INET, ap, addr);
71			*slash = '/';
72			if (n != 1) {
73				RETTOK(DNS_R_BADDOTTEDQUAD);
74			}
75			if (prefix > 32) {
76				RETTOK(ISC_R_RANGE);
77			}
78			for (len = 4; len > 0; len--) {
79				if (addr[len - 1] != 0) {
80					break;
81				}
82			}
83			break;
84
85		case 2:
86			*slash = '\0';
87			n = inet_pton(AF_INET6, ap, addr);
88			*slash = '/';
89			if (n != 1) {
90				RETTOK(DNS_R_BADAAAA);
91			}
92			if (prefix > 128) {
93				RETTOK(ISC_R_RANGE);
94			}
95			for (len = 16; len > 0; len--) {
96				if (addr[len - 1] != 0) {
97					break;
98				}
99			}
100			break;
101
102		default:
103			RETTOK(ISC_R_NOTIMPLEMENTED);
104		}
105		RETERR(uint16_tobuffer(afi, target));
106		RETERR(uint8_tobuffer(prefix, target));
107		RETERR(uint8_tobuffer(len | ((neg) ? 0x80 : 0), target));
108		RETERR(mem_tobuffer(target, addr, len));
109	} while (1);
110
111	/*
112	 * Let upper layer handle eol/eof.
113	 */
114	isc_lex_ungettoken(lexer, &token);
115
116	return (ISC_R_SUCCESS);
117}
118
119static isc_result_t
120totext_in_apl(ARGS_TOTEXT) {
121	isc_region_t sr;
122	isc_region_t ir;
123	uint16_t afi;
124	uint8_t prefix;
125	uint8_t len;
126	bool neg;
127	unsigned char buf[16];
128	char txt[sizeof(" !64000:")];
129	const char *sep = "";
130	int n;
131
132	REQUIRE(rdata->type == dns_rdatatype_apl);
133	REQUIRE(rdata->rdclass == dns_rdataclass_in);
134
135	UNUSED(tctx);
136
137	dns_rdata_toregion(rdata, &sr);
138	ir.base = buf;
139	ir.length = sizeof(buf);
140
141	while (sr.length > 0) {
142		INSIST(sr.length >= 4);
143		afi = uint16_fromregion(&sr);
144		isc_region_consume(&sr, 2);
145		prefix = *sr.base;
146		isc_region_consume(&sr, 1);
147		len = (*sr.base & 0x7f);
148		neg = (*sr.base & 0x80);
149		isc_region_consume(&sr, 1);
150		INSIST(len <= sr.length);
151		n = snprintf(txt, sizeof(txt), "%s%s%u:", sep, neg ? "!" : "",
152			     afi);
153		INSIST(n < (int)sizeof(txt));
154		RETERR(str_totext(txt, target));
155		switch (afi) {
156		case 1:
157			INSIST(len <= 4);
158			INSIST(prefix <= 32);
159			memset(buf, 0, sizeof(buf));
160			memmove(buf, sr.base, len);
161			RETERR(inet_totext(AF_INET, tctx->flags, &ir, target));
162			break;
163
164		case 2:
165			INSIST(len <= 16);
166			INSIST(prefix <= 128);
167			memset(buf, 0, sizeof(buf));
168			memmove(buf, sr.base, len);
169			RETERR(inet_totext(AF_INET6, tctx->flags, &ir, target));
170			break;
171
172		default:
173			return (ISC_R_NOTIMPLEMENTED);
174		}
175		n = snprintf(txt, sizeof(txt), "/%u", prefix);
176		INSIST(n < (int)sizeof(txt));
177		RETERR(str_totext(txt, target));
178		isc_region_consume(&sr, len);
179		sep = " ";
180	}
181	return (ISC_R_SUCCESS);
182}
183
184static isc_result_t
185fromwire_in_apl(ARGS_FROMWIRE) {
186	isc_region_t sr, sr2;
187	isc_region_t tr;
188	uint16_t afi;
189	uint8_t prefix;
190	uint8_t len;
191
192	REQUIRE(type == dns_rdatatype_apl);
193	REQUIRE(rdclass == dns_rdataclass_in);
194
195	UNUSED(type);
196	UNUSED(dctx);
197	UNUSED(rdclass);
198	UNUSED(options);
199
200	isc_buffer_activeregion(source, &sr);
201	isc_buffer_availableregion(target, &tr);
202	if (sr.length > tr.length) {
203		return (ISC_R_NOSPACE);
204	}
205	sr2 = sr;
206
207	/* Zero or more items */
208	while (sr.length > 0) {
209		if (sr.length < 4) {
210			return (ISC_R_UNEXPECTEDEND);
211		}
212		afi = uint16_fromregion(&sr);
213		isc_region_consume(&sr, 2);
214		prefix = *sr.base;
215		isc_region_consume(&sr, 1);
216		len = (*sr.base & 0x7f);
217		isc_region_consume(&sr, 1);
218		if (len > sr.length) {
219			return (ISC_R_UNEXPECTEDEND);
220		}
221		switch (afi) {
222		case 1:
223			if (prefix > 32 || len > 4) {
224				return (ISC_R_RANGE);
225			}
226			break;
227		case 2:
228			if (prefix > 128 || len > 16) {
229				return (ISC_R_RANGE);
230			}
231		}
232		if (len > 0 && sr.base[len - 1] == 0) {
233			return (DNS_R_FORMERR);
234		}
235		isc_region_consume(&sr, len);
236	}
237	isc_buffer_forward(source, sr2.length);
238	return (mem_tobuffer(target, sr2.base, sr2.length));
239}
240
241static isc_result_t
242towire_in_apl(ARGS_TOWIRE) {
243	UNUSED(cctx);
244
245	REQUIRE(rdata->type == dns_rdatatype_apl);
246	REQUIRE(rdata->rdclass == dns_rdataclass_in);
247
248	return (mem_tobuffer(target, rdata->data, rdata->length));
249}
250
251static int
252compare_in_apl(ARGS_COMPARE) {
253	isc_region_t r1;
254	isc_region_t r2;
255
256	REQUIRE(rdata1->type == rdata2->type);
257	REQUIRE(rdata1->rdclass == rdata2->rdclass);
258	REQUIRE(rdata1->type == dns_rdatatype_apl);
259	REQUIRE(rdata1->rdclass == dns_rdataclass_in);
260
261	dns_rdata_toregion(rdata1, &r1);
262	dns_rdata_toregion(rdata2, &r2);
263	return (isc_region_compare(&r1, &r2));
264}
265
266static isc_result_t
267fromstruct_in_apl(ARGS_FROMSTRUCT) {
268	dns_rdata_in_apl_t *apl = source;
269	isc_buffer_t b;
270
271	REQUIRE(type == dns_rdatatype_apl);
272	REQUIRE(rdclass == dns_rdataclass_in);
273	REQUIRE(apl != NULL);
274	REQUIRE(apl->common.rdtype == type);
275	REQUIRE(apl->common.rdclass == rdclass);
276	REQUIRE(apl->apl != NULL || apl->apl_len == 0);
277
278	isc_buffer_init(&b, apl->apl, apl->apl_len);
279	isc_buffer_add(&b, apl->apl_len);
280	isc_buffer_setactive(&b, apl->apl_len);
281	return (fromwire_in_apl(rdclass, type, &b, NULL, false, target));
282}
283
284static isc_result_t
285tostruct_in_apl(ARGS_TOSTRUCT) {
286	dns_rdata_in_apl_t *apl = target;
287	isc_region_t r;
288
289	REQUIRE(apl != NULL);
290	REQUIRE(rdata->type == dns_rdatatype_apl);
291	REQUIRE(rdata->rdclass == dns_rdataclass_in);
292
293	apl->common.rdclass = rdata->rdclass;
294	apl->common.rdtype = rdata->type;
295	ISC_LINK_INIT(&apl->common, link);
296
297	dns_rdata_toregion(rdata, &r);
298	apl->apl_len = r.length;
299	apl->apl = mem_maybedup(mctx, r.base, r.length);
300	if (apl->apl == NULL) {
301		return (ISC_R_NOMEMORY);
302	}
303
304	apl->offset = 0;
305	apl->mctx = mctx;
306	return (ISC_R_SUCCESS);
307}
308
309static void
310freestruct_in_apl(ARGS_FREESTRUCT) {
311	dns_rdata_in_apl_t *apl = source;
312
313	REQUIRE(apl != NULL);
314	REQUIRE(apl->common.rdtype == dns_rdatatype_apl);
315	REQUIRE(apl->common.rdclass == dns_rdataclass_in);
316
317	if (apl->mctx == NULL) {
318		return;
319	}
320	if (apl->apl != NULL) {
321		isc_mem_free(apl->mctx, apl->apl);
322	}
323	apl->mctx = NULL;
324}
325
326isc_result_t
327dns_rdata_apl_first(dns_rdata_in_apl_t *apl) {
328	uint32_t length;
329
330	REQUIRE(apl != NULL);
331	REQUIRE(apl->common.rdtype == dns_rdatatype_apl);
332	REQUIRE(apl->common.rdclass == dns_rdataclass_in);
333	REQUIRE(apl->apl != NULL || apl->apl_len == 0);
334
335	/*
336	 * If no APL return ISC_R_NOMORE.
337	 */
338	if (apl->apl == NULL) {
339		return (ISC_R_NOMORE);
340	}
341
342	/*
343	 * Sanity check data.
344	 */
345	INSIST(apl->apl_len > 3U);
346	length = apl->apl[apl->offset + 3] & 0x7f;
347	INSIST(4 + length <= apl->apl_len);
348
349	apl->offset = 0;
350	return (ISC_R_SUCCESS);
351}
352
353isc_result_t
354dns_rdata_apl_next(dns_rdata_in_apl_t *apl) {
355	uint32_t length;
356
357	REQUIRE(apl != NULL);
358	REQUIRE(apl->common.rdtype == dns_rdatatype_apl);
359	REQUIRE(apl->common.rdclass == dns_rdataclass_in);
360	REQUIRE(apl->apl != NULL || apl->apl_len == 0);
361
362	/*
363	 * No APL or have already reached the end return ISC_R_NOMORE.
364	 */
365	if (apl->apl == NULL || apl->offset == apl->apl_len) {
366		return (ISC_R_NOMORE);
367	}
368
369	/*
370	 * Sanity check data.
371	 */
372	INSIST(apl->offset < apl->apl_len);
373	INSIST(apl->apl_len > 3U);
374	INSIST(apl->offset <= apl->apl_len - 4U);
375	length = apl->apl[apl->offset + 3] & 0x7f;
376	/*
377	 * 16 to 32 bits promotion as 'length' is 32 bits so there is
378	 * no overflow problems.
379	 */
380	INSIST(4 + length + apl->offset <= apl->apl_len);
381
382	apl->offset += 4 + length;
383	return ((apl->offset < apl->apl_len) ? ISC_R_SUCCESS : ISC_R_NOMORE);
384}
385
386isc_result_t
387dns_rdata_apl_current(dns_rdata_in_apl_t *apl, dns_rdata_apl_ent_t *ent) {
388	uint32_t length;
389
390	REQUIRE(apl != NULL);
391	REQUIRE(apl->common.rdtype == dns_rdatatype_apl);
392	REQUIRE(apl->common.rdclass == dns_rdataclass_in);
393	REQUIRE(ent != NULL);
394	REQUIRE(apl->apl != NULL || apl->apl_len == 0);
395	REQUIRE(apl->offset <= apl->apl_len);
396
397	if (apl->offset == apl->apl_len) {
398		return (ISC_R_NOMORE);
399	}
400
401	/*
402	 * Sanity check data.
403	 */
404	INSIST(apl->apl_len > 3U);
405	INSIST(apl->offset <= apl->apl_len - 4U);
406	length = (apl->apl[apl->offset + 3] & 0x7f);
407	/*
408	 * 16 to 32 bits promotion as 'length' is 32 bits so there is
409	 * no overflow problems.
410	 */
411	INSIST(4 + length + apl->offset <= apl->apl_len);
412
413	ent->family = (apl->apl[apl->offset] << 8) + apl->apl[apl->offset + 1];
414	ent->prefix = apl->apl[apl->offset + 2];
415	ent->length = length;
416	ent->negative = (apl->apl[apl->offset + 3] & 0x80);
417	if (ent->length != 0) {
418		ent->data = &apl->apl[apl->offset + 4];
419	} else {
420		ent->data = NULL;
421	}
422	return (ISC_R_SUCCESS);
423}
424
425unsigned int
426dns_rdata_apl_count(const dns_rdata_in_apl_t *apl) {
427	return (apl->apl_len);
428}
429
430static isc_result_t
431additionaldata_in_apl(ARGS_ADDLDATA) {
432	REQUIRE(rdata->type == dns_rdatatype_apl);
433	REQUIRE(rdata->rdclass == dns_rdataclass_in);
434
435	UNUSED(rdata);
436	UNUSED(owner);
437	UNUSED(add);
438	UNUSED(arg);
439
440	return (ISC_R_SUCCESS);
441}
442
443static isc_result_t
444digest_in_apl(ARGS_DIGEST) {
445	isc_region_t r;
446
447	REQUIRE(rdata->type == dns_rdatatype_apl);
448	REQUIRE(rdata->rdclass == dns_rdataclass_in);
449
450	dns_rdata_toregion(rdata, &r);
451
452	return ((digest)(arg, &r));
453}
454
455static bool
456checkowner_in_apl(ARGS_CHECKOWNER) {
457	REQUIRE(type == dns_rdatatype_apl);
458	REQUIRE(rdclass == dns_rdataclass_in);
459
460	UNUSED(name);
461	UNUSED(type);
462	UNUSED(rdclass);
463	UNUSED(wildcard);
464
465	return (true);
466}
467
468static bool
469checknames_in_apl(ARGS_CHECKNAMES) {
470	REQUIRE(rdata->type == dns_rdatatype_apl);
471	REQUIRE(rdata->rdclass == dns_rdataclass_in);
472
473	UNUSED(rdata);
474	UNUSED(owner);
475	UNUSED(bad);
476
477	return (true);
478}
479
480static int
481casecompare_in_apl(ARGS_COMPARE) {
482	return (compare_in_apl(rdata1, rdata2));
483}
484
485#endif /* RDATA_IN_1_APL_42_C */
486