From 815587fbe682f968f6a54c021a30b27bee41030b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Thu, 2 Jul 2026 11:38:40 +0200 Subject: [PATCH 1/2] Consume the preference before reading the locator in NID/L64/L32 tostruct tostruct_nid() and tostruct_l64() read the 16-bit preference with the non-consuming uint16_fromregion() and then memmove()'d the whole remaining region -- still ten octets, still anchored at the preference -- into the eight-octet nid[]/l64[] arrays. That folded the preference into the first two locator octets and stored two octets past the end of the array. tostruct_l32() shares the root cause: it read the 32-bit locator from the same unconsumed offset, so the value was built from the preference plus the first two locator octets. Consume the two preference octets first, matching the sibling tostruct_lp(), and assert the expected framing on the fixed-size types. (cherry picked from commit 5aa279bbea7851ae35a11356c091d3228ad0941f) --- lib/dns/rdata/generic/l32_105.c | 2 ++ lib/dns/rdata/generic/l64_106.c | 2 ++ lib/dns/rdata/generic/nid_104.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/lib/dns/rdata/generic/l32_105.c b/lib/dns/rdata/generic/l32_105.c index 9657711bb8..5f2d8933bb 100644 --- a/lib/dns/rdata/generic/l32_105.c +++ b/lib/dns/rdata/generic/l32_105.c @@ -156,6 +156,8 @@ tostruct_l32(ARGS_TOSTRUCT) { dns_rdata_toregion(rdata, ®ion); l32->pref = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + INSIST(region.length == sizeof(l32->l32)); n = uint32_fromregion(®ion); l32->l32.s_addr = htonl(n); return ISC_R_SUCCESS; diff --git a/lib/dns/rdata/generic/l64_106.c b/lib/dns/rdata/generic/l64_106.c index 5e9b42e451..493264fa55 100644 --- a/lib/dns/rdata/generic/l64_106.c +++ b/lib/dns/rdata/generic/l64_106.c @@ -151,6 +151,8 @@ tostruct_l64(ARGS_TOSTRUCT) { dns_rdata_toregion(rdata, ®ion); l64->pref = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + INSIST(region.length == sizeof(l64->l64)); memmove(l64->l64, region.base, region.length); return ISC_R_SUCCESS; } diff --git a/lib/dns/rdata/generic/nid_104.c b/lib/dns/rdata/generic/nid_104.c index 80d0533057..275772b9e4 100644 --- a/lib/dns/rdata/generic/nid_104.c +++ b/lib/dns/rdata/generic/nid_104.c @@ -151,6 +151,8 @@ tostruct_nid(ARGS_TOSTRUCT) { dns_rdata_toregion(rdata, ®ion); nid->pref = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + INSIST(region.length == sizeof(nid->nid)); memmove(nid->nid, region.base, region.length); return ISC_R_SUCCESS; } From 4e6028883d0ab1581015e6c2408f44953685c327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Thu, 2 Jul 2026 11:38:44 +0200 Subject: [PATCH 2/2] Add NID/L64/L32 round-trip coverage to the rdata unit test These types had no entries in the rdata test table, so the tostruct/fromstruct round-trip in check_struct_conversions() never ran against them -- which is why the missing preference consume in their tostruct routines went unnoticed for years. Add text and wire vectors for all three. Assisted-by: Claude:claude-fable-5 (cherry picked from commit f28bea94e3e0fc788014dc3dd2ca32d3df8ae2c1) --- tests/dns/rdata_test.c | 100 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/tests/dns/rdata_test.c b/tests/dns/rdata_test.c index 6dd6e0c0da..7af9c56329 100644 --- a/tests/dns/rdata_test.c +++ b/tests/dns/rdata_test.c @@ -2278,6 +2278,70 @@ ISC_RUN_TEST_IMPL(key) { dns_rdatatype_key, sizeof(dns_rdata_key_t)); } +/* + * L32 tests (RFC 6742): a 16-bit Preference followed by a 32-bit Locator32. + * The wire form is a fixed six octets. The struct round-trip guards against + * the Preference being folded back into the locator. + */ +ISC_RUN_TEST_IMPL(l32) { + text_ok_t text_ok[] = { + /* RFC 6742 section 4.2 examples. */ + TEXT_VALID("10 10.1.2.0"), + TEXT_VALID("20 10.1.4.0"), + /* Preference out of range. */ + TEXT_INVALID("65536 10.1.2.0"), + /* Locator32 is not a dotted quad. */ + TEXT_INVALID("10 2001:db8::1"), + TEXT_SENTINEL(), + }; + wire_ok_t wire_ok[] = { + /* Preference + 32-bit Locator32 == 6 octets. */ + WIRE_VALID(0x00, 0x0a, 0x0a, 0x01, 0x02, 0x00), + /* Too short. */ + WIRE_INVALID(0x00, 0x0a, 0x0a, 0x01, 0x02), + /* Too long. */ + WIRE_INVALID(0x00, 0x0a, 0x0a, 0x01, 0x02, 0x00, 0x00), + WIRE_SENTINEL(), + }; + + check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in, + dns_rdatatype_l32, sizeof(dns_rdata_l32_t)); +} + +/* + * L64 tests (RFC 6742): a 16-bit Preference followed by a 64-bit Locator64. + * The wire form is a fixed ten octets. + */ +ISC_RUN_TEST_IMPL(l64) { + text_ok_t text_ok[] = { + /* RFC 6742 section 4.3 example. */ + TEXT_VALID("10 2001:db8:1140:1000"), + /* Leading zeroes in a group are dropped on output. */ + TEXT_VALID_CHANGED("20 2001:0db8:2140:2000", + "20 2001:db8:2140:2000"), + /* Preference out of range. */ + TEXT_INVALID("65536 2001:db8:1140:1000"), + /* Wrong number of groups. */ + TEXT_INVALID("10 2001:db8:1140"), + TEXT_SENTINEL(), + }; + wire_ok_t wire_ok[] = { + /* Preference + 64-bit Locator64 == 10 octets. */ + WIRE_VALID(0x00, 0x0a, 0x20, 0x01, 0x0d, 0xb8, 0x11, 0x40, 0x10, + 0x00), + /* Too short. */ + WIRE_INVALID(0x00, 0x0a, 0x20, 0x01, 0x0d, 0xb8, 0x11, 0x40, + 0x10), + /* Too long. */ + WIRE_INVALID(0x00, 0x0a, 0x20, 0x01, 0x0d, 0xb8, 0x11, 0x40, + 0x10, 0x00, 0x00), + WIRE_SENTINEL(), + }; + + check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in, + dns_rdatatype_l64, sizeof(dns_rdata_l64_t)); +} + /* * LOC tests. */ @@ -2340,6 +2404,39 @@ ISC_RUN_TEST_IMPL(loc) { dns_rdatatype_loc, sizeof(dns_rdata_loc_t)); } +/* + * NID tests (RFC 6742): a 16-bit Preference followed by a 64-bit Node ID. + * The wire form is a fixed ten octets. + */ +ISC_RUN_TEST_IMPL(nid) { + text_ok_t text_ok[] = { + /* RFC 6742 section 4.1 examples. */ + TEXT_VALID("10 14:4fff:ff20:ee64"), + TEXT_VALID_CHANGED("20 0015:5fff:ff21:ee65", + "20 15:5fff:ff21:ee65"), + /* Preference out of range. */ + TEXT_INVALID("65536 14:4fff:ff20:ee64"), + /* Wrong number of groups. */ + TEXT_INVALID("10 14:4fff:ff20"), + TEXT_SENTINEL(), + }; + wire_ok_t wire_ok[] = { + /* Preference + 64-bit Node ID == 10 octets. */ + WIRE_VALID(0x00, 0x0a, 0x00, 0x14, 0x4f, 0xff, 0xff, 0x20, 0xee, + 0x64), + /* Too short. */ + WIRE_INVALID(0x00, 0x0a, 0x00, 0x14, 0x4f, 0xff, 0xff, 0x20, + 0xee), + /* Too long. */ + WIRE_INVALID(0x00, 0x0a, 0x00, 0x14, 0x4f, 0xff, 0xff, 0x20, + 0xee, 0x64, 0x00), + WIRE_SENTINEL(), + }; + + check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in, + dns_rdatatype_nid, sizeof(dns_rdata_nid_t)); +} + /* * http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt * @@ -3406,7 +3503,10 @@ ISC_TEST_ENTRY(hip) ISC_TEST_ENTRY(https_svcb) ISC_TEST_ENTRY(isdn) ISC_TEST_ENTRY(key) +ISC_TEST_ENTRY(l32) +ISC_TEST_ENTRY(l64) ISC_TEST_ENTRY(loc) +ISC_TEST_ENTRY(nid) ISC_TEST_ENTRY(nimloc) ISC_TEST_ENTRY(nsec) ISC_TEST_ENTRY(nsec3)