refactor: dedupe HTTPS_TYPE, record-walk, and test rdata builder

- Drop `const HTTPS_TYPE: u16 = 65;` in favor of `QueryType::HTTPS.to_num()`
  at the single call site — avoids a fresh magic number alongside the
  existing enum mapping in question.rs.
- Add `DnsPacket::for_each_record_mut` so `strip_https_ipv6_hints` stops
  hand-rolling the answers/authorities/resources walk; future section
  rewrites go through the same helper.
- Promote the SVCB test-rdata builder from `svcb::tests` to module scope
  as `pub(crate) #[cfg(test)] fn build_rdata`, and reuse it in the two
  pipeline tests in ctx.rs — kills ~20 lines of byte-fiddling and keeps
  one RDATA-construction code path.
This commit is contained in:
Razvan Dimescu
2026-04-19 05:58:47 +03:00
parent 22dd3cd222
commit 61ea2e510d
3 changed files with 68 additions and 66 deletions

View File

@@ -511,27 +511,17 @@ fn strip_dnssec_records(pkt: &mut DnsPacket) {
pkt.resources.retain(|r| !is_dnssec_record(r));
}
/// HTTPS RR type code (RFC 9460). Numa stores HTTPS/SVCB records as
/// `DnsRecord::UNKNOWN { qtype: 65, .. }` since it doesn't have a
/// dedicated variant.
const HTTPS_TYPE: u16 = 65;
fn strip_https_ipv6_hints(pkt: &mut DnsPacket) {
let rewrite = |rec: &mut DnsRecord| {
if let DnsRecord::UNKNOWN {
qtype: HTTPS_TYPE,
data,
..
} = rec
{
if let Some(new_data) = crate::svcb::strip_ipv6hint(data) {
*data = new_data;
let https_qtype = QueryType::HTTPS.to_num();
pkt.for_each_record_mut(|rec| {
if let DnsRecord::UNKNOWN { qtype, data, .. } = rec {
if *qtype == https_qtype {
if let Some(new_data) = crate::svcb::strip_ipv6hint(data) {
*data = new_data;
}
}
}
};
pkt.answers.iter_mut().for_each(rewrite);
pkt.authorities.iter_mut().for_each(rewrite);
pkt.resources.iter_mut().for_each(rewrite);
});
}
fn is_special_use_domain(qname: &str) -> bool {
@@ -1285,22 +1275,20 @@ mod tests {
#[tokio::test]
async fn pipeline_filter_aaaa_strips_ipv6hint_from_https() {
// Build an HTTPS record (type 65) with ipv6hint (key 6). Cache it,
// Build an HTTPS record (type 65) with alpn + ipv6hint, cache it,
// then query with filter_aaaa on — the returned rdata must have
// ipv6hint removed.
let mut rdata = Vec::new();
rdata.extend_from_slice(&1u16.to_be_bytes()); // priority
rdata.push(0); // empty target (".")
// alpn = ["h3"]
rdata.extend_from_slice(&1u16.to_be_bytes());
rdata.extend_from_slice(&3u16.to_be_bytes());
rdata.extend_from_slice(&[0x02, b'h', b'3']);
// ipv6hint = [2606:4700::1]
rdata.extend_from_slice(&6u16.to_be_bytes());
rdata.extend_from_slice(&16u16.to_be_bytes());
rdata.extend_from_slice(&[
0x26, 0x06, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
]);
// ipv6hint (20 bytes) removed.
let rdata = crate::svcb::build_rdata(
1,
&[],
&[
(1, vec![0x02, b'h', b'3']),
(
6,
vec![0x26, 0x06, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01],
),
],
);
let mut pkt = DnsPacket::new();
pkt.header.response = true;
@@ -1349,14 +1337,14 @@ mod tests {
// Regression guard for the DO-bit gate in resolve_query: modifying
// HTTPS rdata invalidates any accompanying RRSIG, so a DO=1 client
// must receive the record untouched even when filter_aaaa is on.
let mut rdata = Vec::new();
rdata.extend_from_slice(&1u16.to_be_bytes());
rdata.push(0);
rdata.extend_from_slice(&6u16.to_be_bytes());
rdata.extend_from_slice(&16u16.to_be_bytes());
rdata.extend_from_slice(&[
0x26, 0x06, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
]);
let rdata = crate::svcb::build_rdata(
1,
&[],
&[(
6,
vec![0x26, 0x06, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01],
)],
);
let mut pkt = DnsPacket::new();
pkt.header.response = true;