1 /* $NetBSD: amtrelay_260.c,v 1.9 2026/01/29 18:37:51 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 #ifndef RDATA_GENERIC_AMTRELAY_260_C 17 #define RDATA_GENERIC_AMTRELAY_260_C 18 19 #include <string.h> 20 21 #include <isc/net.h> 22 23 #define RRTYPE_AMTRELAY_ATTRIBUTES (0) 24 25 static isc_result_t 26 fromtext_amtrelay(ARGS_FROMTEXT) { 27 isc_token_t token; 28 dns_name_t name; 29 isc_buffer_t buffer; 30 unsigned int discovery; 31 unsigned int gateway; 32 struct in_addr addr; 33 unsigned char addr6[16]; 34 isc_region_t region; 35 36 REQUIRE(type == dns_rdatatype_amtrelay); 37 38 UNUSED(type); 39 UNUSED(rdclass); 40 UNUSED(callbacks); 41 42 /* 43 * Precedence. 44 */ 45 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 46 false)); 47 if (token.value.as_ulong > 0xffU) { 48 RETTOK(ISC_R_RANGE); 49 } 50 RETERR(uint8_tobuffer(token.value.as_ulong, target)); 51 52 /* 53 * Discovery. 54 */ 55 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 56 false)); 57 if (token.value.as_ulong > 1U) { 58 RETTOK(ISC_R_RANGE); 59 } 60 discovery = token.value.as_ulong; 61 62 /* 63 * Gateway type. 64 */ 65 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 66 false)); 67 if (token.value.as_ulong > 0x7fU) { 68 RETTOK(ISC_R_RANGE); 69 } 70 RETERR(uint8_tobuffer(token.value.as_ulong | (discovery << 7), target)); 71 gateway = token.value.as_ulong; 72 73 /* 74 * Gateway (must exist). 75 */ 76 RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, 77 false)); 78 79 if (gateway > 3) { 80 return ISC_R_NOTIMPLEMENTED; 81 } 82 83 switch (gateway) { 84 case 0: 85 if (strcmp(DNS_AS_STR(token), ".") != 0) { 86 RETTOK(DNS_R_SYNTAX); 87 } 88 return ISC_R_SUCCESS; 89 case 1: 90 if (inet_pton(AF_INET, DNS_AS_STR(token), &addr) != 1) { 91 RETTOK(DNS_R_BADDOTTEDQUAD); 92 } 93 isc_buffer_availableregion(target, ®ion); 94 if (region.length < 4) { 95 return ISC_R_NOSPACE; 96 } 97 memmove(region.base, &addr, 4); 98 isc_buffer_add(target, 4); 99 return ISC_R_SUCCESS; 100 101 case 2: 102 if (inet_pton(AF_INET6, DNS_AS_STR(token), addr6) != 1) { 103 RETTOK(DNS_R_BADAAAA); 104 } 105 isc_buffer_availableregion(target, ®ion); 106 if (region.length < 16) { 107 return ISC_R_NOSPACE; 108 } 109 memmove(region.base, addr6, 16); 110 isc_buffer_add(target, 16); 111 return ISC_R_SUCCESS; 112 113 case 3: 114 dns_name_init(&name, NULL); 115 buffer_fromregion(&buffer, &token.value.as_region); 116 if (origin == NULL) { 117 origin = dns_rootname; 118 } 119 return dns_name_fromtext(&name, &buffer, origin, options, 120 target); 121 default: 122 UNREACHABLE(); 123 } 124 } 125 126 static isc_result_t 127 totext_amtrelay(ARGS_TOTEXT) { 128 isc_region_t region; 129 dns_name_t name; 130 char buf[sizeof("0 255 ")]; 131 unsigned char precedence; 132 unsigned char discovery; 133 unsigned char gateway; 134 135 UNUSED(tctx); 136 137 REQUIRE(rdata->type == dns_rdatatype_amtrelay); 138 REQUIRE(rdata->length >= 2); 139 140 if ((rdata->data[1] & 0x7f) > 3U) { 141 return ISC_R_NOTIMPLEMENTED; 142 } 143 144 /* 145 * Precedence. 146 */ 147 dns_rdata_toregion(rdata, ®ion); 148 precedence = uint8_fromregion(®ion); 149 isc_region_consume(®ion, 1); 150 snprintf(buf, sizeof(buf), "%u ", precedence); 151 RETERR(str_totext(buf, target)); 152 153 /* 154 * Discovery and Gateway type. 155 */ 156 gateway = uint8_fromregion(®ion); 157 discovery = gateway >> 7; 158 gateway &= 0x7f; 159 isc_region_consume(®ion, 1); 160 snprintf(buf, sizeof(buf), "%u %u ", discovery, gateway); 161 RETERR(str_totext(buf, target)); 162 163 /* 164 * Gateway. 165 */ 166 switch (gateway) { 167 case 0: 168 return str_totext(".", target); 169 170 case 1: 171 return inet_totext(AF_INET, tctx->flags, ®ion, target); 172 173 case 2: 174 return inet_totext(AF_INET6, tctx->flags, ®ion, target); 175 176 case 3: 177 dns_name_init(&name, NULL); 178 dns_name_fromregion(&name, ®ion); 179 return dns_name_totext(&name, 0, target); 180 181 default: 182 UNREACHABLE(); 183 } 184 return ISC_R_SUCCESS; 185 } 186 187 static isc_result_t 188 fromwire_amtrelay(ARGS_FROMWIRE) { 189 dns_name_t name; 190 isc_region_t region; 191 192 REQUIRE(type == dns_rdatatype_amtrelay); 193 194 UNUSED(type); 195 UNUSED(rdclass); 196 197 dctx = dns_decompress_setpermitted(dctx, false); 198 199 isc_buffer_activeregion(source, ®ion); 200 if (region.length < 2) { 201 return ISC_R_UNEXPECTEDEND; 202 } 203 204 switch (region.base[1] & 0x7f) { 205 case 0: 206 if (region.length != 2) { 207 return DNS_R_FORMERR; 208 } 209 isc_buffer_forward(source, region.length); 210 return mem_tobuffer(target, region.base, region.length); 211 212 case 1: 213 if (region.length != 6) { 214 return DNS_R_FORMERR; 215 } 216 isc_buffer_forward(source, region.length); 217 return mem_tobuffer(target, region.base, region.length); 218 219 case 2: 220 if (region.length != 18) { 221 return DNS_R_FORMERR; 222 } 223 isc_buffer_forward(source, region.length); 224 return mem_tobuffer(target, region.base, region.length); 225 226 case 3: 227 RETERR(mem_tobuffer(target, region.base, 2)); 228 isc_buffer_forward(source, 2); 229 dns_name_init(&name, NULL); 230 return dns_name_fromwire(&name, source, dctx, target); 231 232 default: 233 isc_buffer_forward(source, region.length); 234 return mem_tobuffer(target, region.base, region.length); 235 } 236 } 237 238 static isc_result_t 239 towire_amtrelay(ARGS_TOWIRE) { 240 isc_region_t region; 241 242 REQUIRE(rdata->type == dns_rdatatype_amtrelay); 243 REQUIRE(rdata->length != 0); 244 245 UNUSED(cctx); 246 247 dns_rdata_toregion(rdata, ®ion); 248 return mem_tobuffer(target, region.base, region.length); 249 } 250 251 static int 252 compare_amtrelay(ARGS_COMPARE) { 253 isc_region_t region1; 254 isc_region_t region2; 255 256 REQUIRE(rdata1->type == rdata2->type); 257 REQUIRE(rdata1->rdclass == rdata2->rdclass); 258 REQUIRE(rdata1->type == dns_rdatatype_amtrelay); 259 REQUIRE(rdata1->length >= 2); 260 REQUIRE(rdata2->length >= 2); 261 262 dns_rdata_toregion(rdata1, ®ion1); 263 dns_rdata_toregion(rdata2, ®ion2); 264 265 return isc_region_compare(®ion1, ®ion2); 266 } 267 268 static isc_result_t 269 fromstruct_amtrelay(ARGS_FROMSTRUCT) { 270 dns_rdata_amtrelay_t *amtrelay = source; 271 isc_region_t region; 272 uint32_t n; 273 274 REQUIRE(type == dns_rdatatype_amtrelay); 275 REQUIRE(amtrelay != NULL); 276 REQUIRE(amtrelay->common.rdtype == type); 277 REQUIRE(amtrelay->common.rdclass == rdclass); 278 279 UNUSED(type); 280 UNUSED(rdclass); 281 282 RETERR(uint8_tobuffer(amtrelay->precedence, target)); 283 n = (amtrelay->discovery ? 0x80 : 0) | amtrelay->gateway_type; 284 RETERR(uint8_tobuffer(n, target)); 285 286 switch (amtrelay->gateway_type) { 287 case 0: 288 return ISC_R_SUCCESS; 289 290 case 1: 291 n = ntohl(amtrelay->in_addr.s_addr); 292 return uint32_tobuffer(n, target); 293 294 case 2: 295 return mem_tobuffer(target, amtrelay->in6_addr.s6_addr, 16); 296 break; 297 298 case 3: 299 dns_name_toregion(&amtrelay->gateway, ®ion); 300 return isc_buffer_copyregion(target, ®ion); 301 break; 302 303 default: 304 return mem_tobuffer(target, amtrelay->data, amtrelay->length); 305 } 306 } 307 308 static isc_result_t 309 tostruct_amtrelay(ARGS_TOSTRUCT) { 310 isc_region_t region; 311 dns_rdata_amtrelay_t *amtrelay = target; 312 dns_name_t name; 313 uint32_t n; 314 315 REQUIRE(rdata->type == dns_rdatatype_amtrelay); 316 REQUIRE(amtrelay != NULL); 317 REQUIRE(rdata->length >= 2); 318 319 DNS_RDATACOMMON_INIT(amtrelay, rdata->type, rdata->rdclass); 320 321 dns_name_init(&amtrelay->gateway, NULL); 322 amtrelay->data = NULL; 323 324 dns_name_init(&name, NULL); 325 dns_rdata_toregion(rdata, ®ion); 326 327 amtrelay->precedence = uint8_fromregion(®ion); 328 isc_region_consume(®ion, 1); 329 330 amtrelay->gateway_type = uint8_fromregion(®ion); 331 amtrelay->discovery = (amtrelay->gateway_type & 0x80) != 0; 332 amtrelay->gateway_type &= 0x7f; 333 isc_region_consume(®ion, 1); 334 335 switch (amtrelay->gateway_type) { 336 case 0: 337 break; 338 339 case 1: 340 n = uint32_fromregion(®ion); 341 amtrelay->in_addr.s_addr = htonl(n); 342 isc_region_consume(®ion, 4); 343 break; 344 345 case 2: 346 memmove(amtrelay->in6_addr.s6_addr, region.base, 16); 347 isc_region_consume(®ion, 16); 348 break; 349 350 case 3: 351 dns_name_fromregion(&name, ®ion); 352 name_duporclone(&name, mctx, &amtrelay->gateway); 353 isc_region_consume(®ion, name_length(&name)); 354 break; 355 356 default: 357 if (region.length != 0) { 358 amtrelay->data = mem_maybedup(mctx, region.base, 359 region.length); 360 } 361 amtrelay->length = region.length; 362 } 363 amtrelay->mctx = mctx; 364 return ISC_R_SUCCESS; 365 } 366 367 static void 368 freestruct_amtrelay(ARGS_FREESTRUCT) { 369 dns_rdata_amtrelay_t *amtrelay = source; 370 371 REQUIRE(amtrelay != NULL); 372 REQUIRE(amtrelay->common.rdtype == dns_rdatatype_amtrelay); 373 374 if (amtrelay->mctx == NULL) { 375 return; 376 } 377 378 if (amtrelay->gateway_type == 3) { 379 dns_name_free(&amtrelay->gateway, amtrelay->mctx); 380 } 381 382 if (amtrelay->data != NULL) { 383 isc_mem_free(amtrelay->mctx, amtrelay->data); 384 } 385 386 amtrelay->mctx = NULL; 387 } 388 389 static isc_result_t 390 additionaldata_amtrelay(ARGS_ADDLDATA) { 391 REQUIRE(rdata->type == dns_rdatatype_amtrelay); 392 393 UNUSED(rdata); 394 UNUSED(owner); 395 UNUSED(add); 396 UNUSED(arg); 397 398 return ISC_R_SUCCESS; 399 } 400 401 static isc_result_t 402 digest_amtrelay(ARGS_DIGEST) { 403 isc_region_t region; 404 405 REQUIRE(rdata->type == dns_rdatatype_amtrelay); 406 407 dns_rdata_toregion(rdata, ®ion); 408 return (digest)(arg, ®ion); 409 } 410 411 static bool 412 checkowner_amtrelay(ARGS_CHECKOWNER) { 413 REQUIRE(type == dns_rdatatype_amtrelay); 414 415 UNUSED(name); 416 UNUSED(type); 417 UNUSED(rdclass); 418 UNUSED(wildcard); 419 420 return true; 421 } 422 423 static bool 424 checknames_amtrelay(ARGS_CHECKNAMES) { 425 REQUIRE(rdata->type == dns_rdatatype_amtrelay); 426 427 UNUSED(rdata); 428 UNUSED(owner); 429 UNUSED(bad); 430 431 return true; 432 } 433 434 static int 435 casecompare_amtrelay(ARGS_COMPARE) { 436 isc_region_t region1; 437 isc_region_t region2; 438 dns_name_t name1; 439 dns_name_t name2; 440 441 REQUIRE(rdata1->type == rdata2->type); 442 REQUIRE(rdata1->rdclass == rdata2->rdclass); 443 REQUIRE(rdata1->type == dns_rdatatype_amtrelay); 444 REQUIRE(rdata1->length >= 2); 445 REQUIRE(rdata2->length >= 2); 446 447 dns_rdata_toregion(rdata1, ®ion1); 448 dns_rdata_toregion(rdata2, ®ion2); 449 450 if (memcmp(region1.base, region2.base, 2) != 0 || 451 (region1.base[1] & 0x7f) != 3) 452 { 453 return isc_region_compare(®ion1, ®ion2); 454 } 455 456 dns_name_init(&name1, NULL); 457 dns_name_init(&name2, NULL); 458 459 isc_region_consume(®ion1, 2); 460 isc_region_consume(®ion2, 2); 461 462 dns_name_fromregion(&name1, ®ion1); 463 dns_name_fromregion(&name2, ®ion2); 464 465 return dns_name_rdatacompare(&name1, &name2); 466 } 467 468 #endif /* RDATA_GENERIC_AMTRELAY_260_C */ 469