1 /* $NetBSD: apl_42.c,v 1.10 2025/01/26 16:25:34 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 23 static isc_result_t 24 fromtext_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 119 static isc_result_t 120 totext_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 184 static isc_result_t 185 fromwire_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 199 isc_buffer_activeregion(source, &sr); 200 isc_buffer_availableregion(target, &tr); 201 if (sr.length > tr.length) { 202 return ISC_R_NOSPACE; 203 } 204 sr2 = sr; 205 206 /* Zero or more items */ 207 while (sr.length > 0) { 208 if (sr.length < 4) { 209 return ISC_R_UNEXPECTEDEND; 210 } 211 afi = uint16_fromregion(&sr); 212 isc_region_consume(&sr, 2); 213 prefix = *sr.base; 214 isc_region_consume(&sr, 1); 215 len = (*sr.base & 0x7f); 216 isc_region_consume(&sr, 1); 217 if (len > sr.length) { 218 return ISC_R_UNEXPECTEDEND; 219 } 220 switch (afi) { 221 case 1: 222 if (prefix > 32 || len > 4) { 223 return ISC_R_RANGE; 224 } 225 break; 226 case 2: 227 if (prefix > 128 || len > 16) { 228 return ISC_R_RANGE; 229 } 230 } 231 if (len > 0 && sr.base[len - 1] == 0) { 232 return DNS_R_FORMERR; 233 } 234 isc_region_consume(&sr, len); 235 } 236 isc_buffer_forward(source, sr2.length); 237 return mem_tobuffer(target, sr2.base, sr2.length); 238 } 239 240 static isc_result_t 241 towire_in_apl(ARGS_TOWIRE) { 242 UNUSED(cctx); 243 244 REQUIRE(rdata->type == dns_rdatatype_apl); 245 REQUIRE(rdata->rdclass == dns_rdataclass_in); 246 247 return mem_tobuffer(target, rdata->data, rdata->length); 248 } 249 250 static int 251 compare_in_apl(ARGS_COMPARE) { 252 isc_region_t r1; 253 isc_region_t r2; 254 255 REQUIRE(rdata1->type == rdata2->type); 256 REQUIRE(rdata1->rdclass == rdata2->rdclass); 257 REQUIRE(rdata1->type == dns_rdatatype_apl); 258 REQUIRE(rdata1->rdclass == dns_rdataclass_in); 259 260 dns_rdata_toregion(rdata1, &r1); 261 dns_rdata_toregion(rdata2, &r2); 262 return isc_region_compare(&r1, &r2); 263 } 264 265 static isc_result_t 266 fromstruct_in_apl(ARGS_FROMSTRUCT) { 267 dns_rdata_in_apl_t *apl = source; 268 isc_buffer_t b; 269 270 REQUIRE(type == dns_rdatatype_apl); 271 REQUIRE(rdclass == dns_rdataclass_in); 272 REQUIRE(apl != NULL); 273 REQUIRE(apl->common.rdtype == type); 274 REQUIRE(apl->common.rdclass == rdclass); 275 REQUIRE(apl->apl != NULL || apl->apl_len == 0); 276 277 isc_buffer_init(&b, apl->apl, apl->apl_len); 278 isc_buffer_add(&b, apl->apl_len); 279 isc_buffer_setactive(&b, apl->apl_len); 280 return fromwire_in_apl(rdclass, type, &b, DNS_DECOMPRESS_DEFAULT, 281 target); 282 } 283 284 static isc_result_t 285 tostruct_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 apl->offset = 0; 301 apl->mctx = mctx; 302 return ISC_R_SUCCESS; 303 } 304 305 static void 306 freestruct_in_apl(ARGS_FREESTRUCT) { 307 dns_rdata_in_apl_t *apl = source; 308 309 REQUIRE(apl != NULL); 310 REQUIRE(apl->common.rdtype == dns_rdatatype_apl); 311 REQUIRE(apl->common.rdclass == dns_rdataclass_in); 312 313 if (apl->mctx == NULL) { 314 return; 315 } 316 if (apl->apl != NULL) { 317 isc_mem_free(apl->mctx, apl->apl); 318 } 319 apl->mctx = NULL; 320 } 321 322 isc_result_t 323 dns_rdata_apl_first(dns_rdata_in_apl_t *apl) { 324 uint32_t length; 325 326 REQUIRE(apl != NULL); 327 REQUIRE(apl->common.rdtype == dns_rdatatype_apl); 328 REQUIRE(apl->common.rdclass == dns_rdataclass_in); 329 REQUIRE(apl->apl != NULL || apl->apl_len == 0); 330 331 /* 332 * If no APL return ISC_R_NOMORE. 333 */ 334 if (apl->apl == NULL) { 335 return ISC_R_NOMORE; 336 } 337 338 /* 339 * Sanity check data. 340 */ 341 INSIST(apl->apl_len > 3U); 342 length = apl->apl[apl->offset + 3] & 0x7f; 343 INSIST(4 + length <= apl->apl_len); 344 345 apl->offset = 0; 346 return ISC_R_SUCCESS; 347 } 348 349 isc_result_t 350 dns_rdata_apl_next(dns_rdata_in_apl_t *apl) { 351 uint32_t length; 352 353 REQUIRE(apl != NULL); 354 REQUIRE(apl->common.rdtype == dns_rdatatype_apl); 355 REQUIRE(apl->common.rdclass == dns_rdataclass_in); 356 REQUIRE(apl->apl != NULL || apl->apl_len == 0); 357 358 /* 359 * No APL or have already reached the end return ISC_R_NOMORE. 360 */ 361 if (apl->apl == NULL || apl->offset == apl->apl_len) { 362 return ISC_R_NOMORE; 363 } 364 365 /* 366 * Sanity check data. 367 */ 368 INSIST(apl->offset < apl->apl_len); 369 INSIST(apl->apl_len > 3U); 370 INSIST(apl->offset <= apl->apl_len - 4U); 371 length = apl->apl[apl->offset + 3] & 0x7f; 372 /* 373 * 16 to 32 bits promotion as 'length' is 32 bits so there is 374 * no overflow problems. 375 */ 376 INSIST(4 + length + apl->offset <= apl->apl_len); 377 378 apl->offset += 4 + length; 379 return (apl->offset < apl->apl_len) ? ISC_R_SUCCESS : ISC_R_NOMORE; 380 } 381 382 isc_result_t 383 dns_rdata_apl_current(dns_rdata_in_apl_t *apl, dns_rdata_apl_ent_t *ent) { 384 uint32_t length; 385 386 REQUIRE(apl != NULL); 387 REQUIRE(apl->common.rdtype == dns_rdatatype_apl); 388 REQUIRE(apl->common.rdclass == dns_rdataclass_in); 389 REQUIRE(ent != NULL); 390 REQUIRE(apl->apl != NULL || apl->apl_len == 0); 391 REQUIRE(apl->offset <= apl->apl_len); 392 393 if (apl->offset == apl->apl_len) { 394 return ISC_R_NOMORE; 395 } 396 397 /* 398 * Sanity check data. 399 */ 400 INSIST(apl->apl_len > 3U); 401 INSIST(apl->offset <= apl->apl_len - 4U); 402 length = (apl->apl[apl->offset + 3] & 0x7f); 403 /* 404 * 16 to 32 bits promotion as 'length' is 32 bits so there is 405 * no overflow problems. 406 */ 407 INSIST(4 + length + apl->offset <= apl->apl_len); 408 409 ent->family = (apl->apl[apl->offset] << 8) + apl->apl[apl->offset + 1]; 410 ent->prefix = apl->apl[apl->offset + 2]; 411 ent->length = length; 412 ent->negative = (apl->apl[apl->offset + 3] & 0x80); 413 if (ent->length != 0) { 414 ent->data = &apl->apl[apl->offset + 4]; 415 } else { 416 ent->data = NULL; 417 } 418 return ISC_R_SUCCESS; 419 } 420 421 unsigned int 422 dns_rdata_apl_count(const dns_rdata_in_apl_t *apl) { 423 return apl->apl_len; 424 } 425 426 static isc_result_t 427 additionaldata_in_apl(ARGS_ADDLDATA) { 428 REQUIRE(rdata->type == dns_rdatatype_apl); 429 REQUIRE(rdata->rdclass == dns_rdataclass_in); 430 431 UNUSED(rdata); 432 UNUSED(owner); 433 UNUSED(add); 434 UNUSED(arg); 435 436 return ISC_R_SUCCESS; 437 } 438 439 static isc_result_t 440 digest_in_apl(ARGS_DIGEST) { 441 isc_region_t r; 442 443 REQUIRE(rdata->type == dns_rdatatype_apl); 444 REQUIRE(rdata->rdclass == dns_rdataclass_in); 445 446 dns_rdata_toregion(rdata, &r); 447 448 return (digest)(arg, &r); 449 } 450 451 static bool 452 checkowner_in_apl(ARGS_CHECKOWNER) { 453 REQUIRE(type == dns_rdatatype_apl); 454 REQUIRE(rdclass == dns_rdataclass_in); 455 456 UNUSED(name); 457 UNUSED(type); 458 UNUSED(rdclass); 459 UNUSED(wildcard); 460 461 return true; 462 } 463 464 static bool 465 checknames_in_apl(ARGS_CHECKNAMES) { 466 REQUIRE(rdata->type == dns_rdatatype_apl); 467 REQUIRE(rdata->rdclass == dns_rdataclass_in); 468 469 UNUSED(rdata); 470 UNUSED(owner); 471 UNUSED(bad); 472 473 return true; 474 } 475 476 static int 477 casecompare_in_apl(ARGS_COMPARE) { 478 return compare_in_apl(rdata1, rdata2); 479 } 480 481 #endif /* RDATA_IN_1_APL_42_C */ 482