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