From 827d7b239b9d56277c7d22002ac1d03889aa3939 Mon Sep 17 00:00:00 2001 From: lazywalker Date: Mon, 9 Feb 2026 21:14:38 +0800 Subject: [PATCH 1/2] fix(prefer_ip): fix bug query/response question sections are different --- Cargo.lock | 2 +- Cargo.toml | 2 +- examples/etc/config.yaml | 2 - src/plugins/flow/prefer_ipv4.rs | 67 +++++++++++++++++++++++++++-- src/plugins/flow/prefer_ipv6.rs | 76 ++++++++++++++++++++++++++++++--- 5 files changed, 136 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89fdc26..fafd047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1247,7 +1247,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazydns" -version = "0.3.12" +version = "0.3.13" dependencies = [ "anyhow", "async-stream", diff --git a/Cargo.toml b/Cargo.toml index d03c3f2..4547cc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "lazydns-macros"] [package] name = "lazydns" -version = "0.3.12" +version = "0.3.13" edition = "2024" authors = ["lazywalker "] description = "A light and fast DNS server/forwarder implementation in Rust" diff --git a/examples/etc/config.yaml b/examples/etc/config.yaml index 9a416b6..c0f5184 100644 --- a/examples/etc/config.yaml +++ b/examples/etc/config.yaml @@ -517,8 +517,6 @@ plugins: # or use the upstream_reject defined above # exec: $upstream_reject - - exec: prefer_ipv4 - - exec: $redirect # skip cache for dynamic domains, others use cache diff --git a/src/plugins/flow/prefer_ipv4.rs b/src/plugins/flow/prefer_ipv4.rs index 084c1a9..d1909c7 100644 --- a/src/plugins/flow/prefer_ipv4.rs +++ b/src/plugins/flow/prefer_ipv4.rs @@ -20,13 +20,23 @@ impl Plugin for PreferIpv4Plugin { } async fn execute(&self, ctx: &mut crate::plugin::Context) -> crate::Result<()> { - if let Some(response) = ctx.response_mut() { + // Only filter AAAA records if the query type is A + // If query type is AAAA, we should NOT filter AAAA records + let should_filter = ctx + .request() + .questions() + .first() + .map(|q| q.qtype() == RecordType::A) + .unwrap_or(false); + + if should_filter && let Some(response) = ctx.response_mut() { let answers = response.answers_mut(); answers.retain(|record| !matches!(record.rtype(), RecordType::AAAA)); let additional = response.additional_mut(); additional.retain(|record| !matches!(record.rtype(), RecordType::AAAA)); } + Ok(()) } } @@ -47,15 +57,19 @@ impl ExecPlugin for PreferIpv4Plugin { #[cfg(test)] mod tests { use super::*; - use crate::dns::{Message, RData, RecordClass, ResourceRecord}; + use crate::dns::{Message, Question, RData, RecordClass, ResourceRecord}; #[tokio::test] - async fn test_prefer_ipv4_plugin() { + async fn test_prefer_ipv4_plugin_filters_on_a_query() { let plugin = PreferIpv4Plugin::new(); assert_eq!(plugin.name(), "prefer_ipv4"); - let mut ctx = crate::plugin::Context::new(Message::new()); + // Test with A query - should filter AAAA records + let mut request = Message::new(); + request.add_question(Question::new("example.com", RecordType::A, RecordClass::IN)); + let mut response = Message::new(); + response.add_question(Question::new("example.com", RecordType::A, RecordClass::IN)); response.add_answer(ResourceRecord::new( "example.com", @@ -73,6 +87,7 @@ mod tests { RData::AAAA("2001:db8::1".parse().unwrap()), )); + let mut ctx = crate::plugin::Context::new(request); ctx.set_response(Some(response)); plugin.execute(&mut ctx).await.unwrap(); @@ -80,4 +95,48 @@ mod tests { assert_eq!(response.answers().len(), 1); assert!(matches!(response.answers()[0].rtype(), RecordType::A)); } + + #[tokio::test] + async fn test_prefer_ipv4_plugin_preserves_on_aaaa_query() { + let plugin = PreferIpv4Plugin::new(); + + // Test with AAAA query - should NOT filter AAAA records + let mut request = Message::new(); + request.add_question(Question::new( + "example.com", + RecordType::AAAA, + RecordClass::IN, + )); + + let mut response = Message::new(); + response.add_question(Question::new( + "example.com", + RecordType::AAAA, + RecordClass::IN, + )); + + response.add_answer(ResourceRecord::new( + "example.com", + RecordType::A, + RecordClass::IN, + 300, + RData::A("192.0.2.1".parse().unwrap()), + )); + + response.add_answer(ResourceRecord::new( + "example.com", + RecordType::AAAA, + RecordClass::IN, + 300, + RData::AAAA("2001:db8::1".parse().unwrap()), + )); + + let mut ctx = crate::plugin::Context::new(request); + ctx.set_response(Some(response)); + plugin.execute(&mut ctx).await.unwrap(); + + let response = ctx.response().unwrap(); + // Both records should be preserved when query type is AAAA + assert_eq!(response.answers().len(), 2); + } } diff --git a/src/plugins/flow/prefer_ipv6.rs b/src/plugins/flow/prefer_ipv6.rs index 6dc8b89..4f1febc 100644 --- a/src/plugins/flow/prefer_ipv6.rs +++ b/src/plugins/flow/prefer_ipv6.rs @@ -20,13 +20,23 @@ impl Plugin for PreferIpv6Plugin { } async fn execute(&self, ctx: &mut crate::plugin::Context) -> crate::Result<()> { - if let Some(response) = ctx.response_mut() { + // Only filter A records if the query type is AAAA + // If query type is A, we should NOT filter A records + let should_filter = ctx + .request() + .questions() + .first() + .map(|q| q.qtype() == RecordType::AAAA) + .unwrap_or(false); + + if should_filter && let Some(response) = ctx.response_mut() { let answers = response.answers_mut(); answers.retain(|record| !matches!(record.rtype(), RecordType::A)); let additional = response.additional_mut(); additional.retain(|record| !matches!(record.rtype(), RecordType::A)); } + Ok(()) } } @@ -47,15 +57,27 @@ impl ExecPlugin for PreferIpv6Plugin { #[cfg(test)] mod tests { use super::*; - use crate::dns::{Message, RData, RecordClass, ResourceRecord}; + use crate::dns::{Message, Question, RData, RecordClass, ResourceRecord}; #[tokio::test] - async fn test_prefer_ipv6_plugin() { + async fn test_prefer_ipv6_plugin_filters_on_aaaa_query() { let plugin = PreferIpv6Plugin::new(); assert_eq!(plugin.name(), "prefer_ipv6"); - let mut ctx = crate::plugin::Context::new(Message::new()); + // Test with AAAA query - should filter A records + let mut request = Message::new(); + request.add_question(Question::new( + "example.com", + RecordType::AAAA, + RecordClass::IN, + )); + let mut response = Message::new(); + response.add_question(Question::new( + "example.com", + RecordType::AAAA, + RecordClass::IN, + )); response.add_answer(ResourceRecord::new( "example.com", @@ -73,6 +95,7 @@ mod tests { RData::AAAA("2001:db8::1".parse().unwrap()), )); + let mut ctx = crate::plugin::Context::new(request); ctx.set_response(Some(response)); plugin.execute(&mut ctx).await.unwrap(); @@ -81,6 +104,42 @@ mod tests { assert!(matches!(response.answers()[0].rtype(), RecordType::AAAA)); } + #[tokio::test] + async fn test_prefer_ipv6_plugin_preserves_on_a_query() { + let plugin = PreferIpv6Plugin::new(); + + // Test with A query - should NOT filter A records + let mut request = Message::new(); + request.add_question(Question::new("example.com", RecordType::A, RecordClass::IN)); + + let mut response = Message::new(); + response.add_question(Question::new("example.com", RecordType::A, RecordClass::IN)); + + response.add_answer(ResourceRecord::new( + "example.com", + RecordType::A, + RecordClass::IN, + 300, + RData::A("192.0.2.1".parse().unwrap()), + )); + + response.add_answer(ResourceRecord::new( + "example.com", + RecordType::AAAA, + RecordClass::IN, + 300, + RData::AAAA("2001:db8::1".parse().unwrap()), + )); + + let mut ctx = crate::plugin::Context::new(request); + ctx.set_response(Some(response)); + plugin.execute(&mut ctx).await.unwrap(); + + let response = ctx.response().unwrap(); + // Both records should be preserved when query type is A + assert_eq!(response.answers().len(), 2); + } + #[tokio::test] async fn test_prefer_ipv6_no_response() { let plugin = PreferIpv6Plugin::new(); @@ -94,7 +153,14 @@ mod tests { #[tokio::test] async fn test_prefer_ipv6_additional_records() { let plugin = PreferIpv6Plugin::new(); - let mut ctx = crate::plugin::Context::new(Message::new()); + let mut request = Message::new(); + request.add_question(Question::new( + "ns.example.com", + RecordType::AAAA, + RecordClass::IN, + )); + + let mut ctx = crate::plugin::Context::new(request); let mut response = Message::new(); response.add_additional(ResourceRecord::new( From e498b2dd8b38f09accd9520a1301e3efd742de51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 13:16:25 +0000 Subject: [PATCH 2/2] chore(deps): bump anyhow from 1.0.100 to 1.0.101 Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.100 to 1.0.101. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.100...1.0.101) --- updated-dependencies: - dependency-name: anyhow dependency-version: 1.0.101 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fafd047..e7965f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] name = "arc-swap"