diff --git a/Cargo.lock b/Cargo.lock index f1749b5..4e767f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,9 +109,7 @@ dependencies = [ [[package]] name = "any-dns" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9187fc51343b681b1ab76d6af954d0684cb8202a2500723f42e2dbafd8a8dc2f" +version = "0.2.3" dependencies = [ "async-trait", "dyn-clone", diff --git a/Cargo.toml b/Cargo.toml index c032029..39f1ee6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ pkarr = { version = "1.0.4", features = ["dht", "async"]} zbase32 = "0.1.2" ttl_cache = "0.5.1" clap = "4.4.18" -any-dns = "0.2.1" +any-dns = {path = "../any-dns"} chrono = "0.4.33" tokio = { version = "1.36.0", features = ["full"] } async-trait = "0.1.77" diff --git a/README.md b/README.md index 6f53f48..0cbe9a9 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,20 @@ Options: -V, --version Print version ``` +## Limitations +Lookups on pkarr DNS records are limited. These two approach are supported: + +EASY - All in pkarr: +- Direct record resolution (A, AAAA, TXT, ...). +- CNAME pointing directly to another record in the same pkarr. +- No recursion. + +ADVANCED - Fully featured: +- Delegate your zone to a fully fledged name server ([bind9](https://ubuntu.com/server/docs/service-domain-name-service-dns)?). +- pkdns will forward the request the name server. + + +--- May the power ⚡ be with you. \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ffd6dbe..5035ff5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,8 +23,8 @@ impl MyHandler { } #[async_trait] impl CustomHandler for MyHandler { - async fn lookup(&mut self, query: &Vec, _socket: DnsSocket) -> Result, CustomHandlerError> { - let result = self.pkarr.resolve(query).await; + async fn lookup(&mut self, query: &Vec, mut socket: DnsSocket) -> Result, CustomHandlerError> { + let result = self.pkarr.resolve(query, &mut socket).await; match result { Ok(reply) => Ok(reply), diff --git a/src/packet_lookup.rs b/src/packet_lookup.rs index 980125f..bc90bd4 100644 --- a/src/packet_lookup.rs +++ b/src/packet_lookup.rs @@ -1,6 +1,8 @@ +use std::{net::{Ipv4Addr, Ipv6Addr, SocketAddr}, time::Duration}; + +use any_dns::DnsSocket; use simple_dns::{ - rdata::{self, RData}, - Name, Packet, Question, ResourceRecord, QTYPE, TYPE, + rdata::{self, RData}, Name, Packet, PacketFlag, Question, ResourceRecord, QTYPE, TYPE }; /** @@ -12,9 +14,9 @@ use simple_dns::{ /** * Uses a query to transforms a pkarr reply into an regular reply */ -pub fn resolve_query<'a>(pkarr_packet: &Packet<'a>, query: &Packet<'a>) -> Vec { +pub async fn resolve_query<'a>(pkarr_packet: &Packet<'a>, query: &Packet<'a>, socket: &mut DnsSocket) -> Vec { let question = query.questions.first().unwrap(); // Has at least 1 question based on previous checks. - let pkarr_reply = resolve_question(pkarr_packet, question); + let pkarr_reply = resolve_question(pkarr_packet, question, socket).await; let pkarr_reply = Packet::parse(&pkarr_reply).unwrap(); let mut reply = query.clone().into_reply(); @@ -28,7 +30,7 @@ pub fn resolve_query<'a>(pkarr_packet: &Packet<'a>, query: &Packet<'a>) -> Vec(pkarr_packet: &Packet<'a>, question: &Question<'a>) -> Vec { +async fn resolve_question<'a>(pkarr_packet: &Packet<'a>, question: &Question<'a>, socket: &mut DnsSocket) -> Vec { let mut reply = Packet::new_reply(0); let direct_matchs = direct_matches(pkarr_packet, &question.qname, &question.qtype); @@ -43,6 +45,13 @@ fn resolve_question<'a>(pkarr_packet: &Packet<'a>, question: &Question<'a>) -> V if reply.answers.len() == 0 { // Not found. Maybe we have a name server? reply.name_servers = find_nameserver(pkarr_packet, &question.qname); + if reply.name_servers.len() > 0 { + // Resolve ns + let ns_reply = resolve_with_ns(question, &reply.name_servers, socket).await; + if let Some(ns_reply) = ns_reply { + return ns_reply + } + } }; reply.build_bytes_vec_compressed().unwrap() @@ -102,10 +111,72 @@ fn find_nameserver<'a>(pkarr_packet: &Packet<'a>, qname: &Name<'a>) -> Vec(ns_name: &Name<'a>, socket: &mut DnsSocket) -> Option> { + let ns_question = Question::new(ns_name.clone(), QTYPE::TYPE(TYPE::A), simple_dns::QCLASS::CLASS(simple_dns::CLASS::IN), false); + let mut query = Packet::new_query(0); + query.questions.push(ns_question); + query.set_flags(PacketFlag::RECURSION_DESIRED); + let query = query.build_bytes_vec_compressed().unwrap(); + + let reply = socket.query(&query).await.ok()?; + let reply = Packet::parse(&reply).ok()?; + if reply.answers.len() == 0 { + return None; + }; + + let addresses: Vec = reply.answers.into_iter().filter_map(|record| { + match record.rdata { + RData::A(data) => { + let ip = Ipv4Addr::from(data.address); + Some(SocketAddr::new(ip.into(), 53)) + }, + RData::AAAA(data) => { + let ip = Ipv6Addr::from(data.address); + Some(SocketAddr::new(ip.into(), 53)) + }, + _ => None + } + }).collect(); + + Some(addresses) +} + +/** + * Resolves the question with a single ns redirection. + */ +async fn resolve_with_ns<'a>(question: &Question<'a>, name_servers: &Vec>, socket: &mut DnsSocket) -> Option> { + if name_servers.len() == 0 { + return None; + }; + + let ns_names: Vec> = name_servers.iter().filter_map(|record| { + if let RData::NS(data) = record.clone().rdata { + Some(data.0) + } else { + None + } + }).collect(); + + let ns_name = ns_names.first().unwrap(); + let addresses = resolve_ns_ip(ns_name, socket).await?; + let addr = addresses.first().unwrap(); + + let mut query = Packet::new_query(0); + query.questions.push(question.clone()); + query.set_flags(PacketFlag::RECURSION_DESIRED); + let query = query.build_bytes_vec_compressed().unwrap(); + + socket.forward(&query, addr, Duration::from_millis(1000)).await.ok() +} + #[cfg(test)] mod tests { use std::net::Ipv4Addr; + use any_dns::{DnsSocket, EmptyHandler, HandlerHolder}; use pkarr::{ dns::{Name, Packet, ResourceRecord}, Keypair, PublicKey, @@ -114,6 +185,11 @@ mod tests { use super::{resolve_query, resolve_question}; + async fn get_dnssocket() -> DnsSocket { + let handler = HandlerHolder::new(EmptyHandler::new()); + DnsSocket::new("127.0.0.1:20384".parse().unwrap(), "8.8.8.8:53".parse().unwrap(), handler, false).await.unwrap() + } + fn example_pkarr_reply() -> (Vec, PublicKey) { // pkarr.normalize_names makes sure all names end with the pubkey. // @ is invalid and just syntactic sugar on top of pkarr.normalize_names @@ -162,8 +238,8 @@ mod tests { (packet.build_bytes_vec_compressed().unwrap(), pubkey) } - #[test] - fn simple_a_question() { + #[tokio::test] + async fn simple_a_question() { let (pkarr_packet, pubkey) = example_pkarr_reply(); let pkarr_packet = Packet::parse(&pkarr_packet).unwrap(); let pubkey_z32 = pubkey.to_z32(); @@ -178,7 +254,8 @@ mod tests { false, ); - let reply = resolve_question(&pkarr_packet, &question); + let mut socket = get_dnssocket().await; + let reply = resolve_question(&pkarr_packet, &question, &mut socket).await; let reply = Packet::parse(&reply).unwrap(); assert_eq!(reply.answers.len(), 1); assert_eq!(reply.additional_records.len(), 0); @@ -188,8 +265,8 @@ mod tests { assert!(answer.match_qtype(qtype)); } - #[test] - fn a_question_with_cname() { + #[tokio::test] + async fn a_question_with_cname() { let (pkarr_packet, pubkey) = example_pkarr_reply(); let pkarr_packet = Packet::parse(&pkarr_packet).unwrap(); let pubkey_z32 = pubkey.to_z32(); @@ -204,7 +281,8 @@ mod tests { false, ); - let reply = resolve_question(&pkarr_packet, &question); + let mut socket = get_dnssocket().await; + let reply = resolve_question(&pkarr_packet, &question, &mut socket).await; let reply = Packet::parse(&reply).unwrap(); assert_eq!(reply.answers.len(), 2); assert_eq!(reply.additional_records.len(), 0); @@ -219,8 +297,8 @@ mod tests { assert!(answer2.match_qtype(qtype)); } - #[test] - fn a_question_with_ns() { + #[tokio::test] + async fn a_question_with_ns() { let (pkarr_packet, pubkey) = example_pkarr_reply(); let pkarr_packet = Packet::parse(&pkarr_packet).unwrap(); let pubkey_z32 = pubkey.to_z32(); @@ -234,8 +312,8 @@ mod tests { simple_dns::QCLASS::CLASS(simple_dns::CLASS::IN), false, ); - - let reply = resolve_question(&pkarr_packet, &question); + let mut socket = get_dnssocket().await; + let reply = resolve_question(&pkarr_packet, &question, &mut socket).await; let reply = Packet::parse(&reply).unwrap(); assert_eq!(reply.answers.len(), 0); assert_eq!(reply.additional_records.len(), 0); @@ -246,8 +324,8 @@ mod tests { assert!(ns1.match_qtype(simple_dns::QTYPE::TYPE(simple_dns::TYPE::NS))); } - #[test] - fn a_question_with_ns_subdomain() { + #[tokio::test] + async fn a_question_with_ns_subdomain() { let (pkarr_packet, pubkey) = example_pkarr_reply(); let pkarr_packet = Packet::parse(&pkarr_packet).unwrap(); let pubkey_z32 = pubkey.to_z32(); @@ -262,7 +340,8 @@ mod tests { false, ); - let reply = resolve_question(&pkarr_packet, &question); + let mut socket = get_dnssocket().await; + let reply = resolve_question(&pkarr_packet, &question, &mut socket).await; let reply = Packet::parse(&reply).unwrap(); assert_eq!(reply.answers.len(), 0); assert_eq!(reply.additional_records.len(), 0); @@ -273,8 +352,8 @@ mod tests { assert!(ns1.match_qtype(simple_dns::QTYPE::TYPE(simple_dns::TYPE::NS))); } - #[test] - fn simple_a_query() { + #[tokio::test] + async fn simple_a_query() { let (pkarr_packet, _pubkey) = example_pkarr_reply(); let pkarr_packet = Packet::parse(&pkarr_packet).unwrap(); @@ -286,6 +365,7 @@ mod tests { false, )]; - let _reply = resolve_query(&pkarr_packet, &query); + let mut socket = get_dnssocket().await; + let _reply = resolve_query(&pkarr_packet, &query, &mut socket); } } diff --git a/src/pkarr_cache.rs b/src/pkarr_cache.rs index d25a7a6..3fb1700 100644 --- a/src/pkarr_cache.rs +++ b/src/pkarr_cache.rs @@ -18,7 +18,7 @@ impl PkarrPacketTtlCache { pub async fn new(max_cache_ttl: u64) -> Self { PkarrPacketTtlCache { max_cache_ttl, - cache: Arc::new(Mutex::new(TtlCache::new(100_000))) + cache: Arc::new(Mutex::new(TtlCache::new(100_000))) // 1 pkarr packet is max 1KB. Therefore 100,000KB = 100MB } } diff --git a/src/pkarr_resolver.rs b/src/pkarr_resolver.rs index eb66d7b..1e0d2b0 100644 --- a/src/pkarr_resolver.rs +++ b/src/pkarr_resolver.rs @@ -1,3 +1,4 @@ +use any_dns::DnsSocket; use anyhow::anyhow; use crate::{packet_lookup::resolve_query, pkarr_cache::PkarrPacketTtlCache}; @@ -66,7 +67,7 @@ impl PkarrResolver { /** * Resolves a domain with pkarr. */ - pub async fn resolve(&mut self, query: &Vec) -> std::prelude::v1::Result, anyhow::Error> { + pub async fn resolve(&mut self, query: &Vec, socket: &mut DnsSocket) -> std::prelude::v1::Result, anyhow::Error> { let request = Packet::parse(query)?; let question_opt = request.questions.first(); @@ -92,7 +93,7 @@ impl PkarrResolver { } let pkarr_packet = packet_option.unwrap(); let pkarr_packet = Packet::parse(&pkarr_packet).unwrap(); - let reply = resolve_query(&pkarr_packet, &request); + let reply = resolve_query(&pkarr_packet, &request, socket).await; Ok(reply) } @@ -100,6 +101,7 @@ impl PkarrResolver { #[cfg(test)] mod tests { + use any_dns::{EmptyHandler, HandlerHolder}; use pkarr::{ dns::{Name, Packet, Question, ResourceRecord}, Keypair, SignedPacket, @@ -147,6 +149,11 @@ mod tests { result.expect("Should have published."); } + async fn get_dnssocket() -> DnsSocket { + let handler = HandlerHolder::new(EmptyHandler::new()); + DnsSocket::new("127.0.0.1:20384".parse().unwrap(), "8.8.8.8:53".parse().unwrap(), handler, false).await.unwrap() + } + #[tokio::test] async fn query_domain() { publish_record().await; @@ -164,7 +171,8 @@ mod tests { query.questions.push(question); let mut resolver = PkarrResolver::new(0).await; - let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap()).await; + let mut socket = get_dnssocket().await; + let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap(), &mut socket).await; assert!(result.is_ok()); let reply_bytes = result.unwrap(); let reply = Packet::parse(&reply_bytes).unwrap(); @@ -191,7 +199,8 @@ mod tests { ); query.questions.push(question); let mut resolver = PkarrResolver::new(0).await; - let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap()).await; + let mut socket = get_dnssocket().await; + let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap(), &mut socket).await; assert!(result.is_ok()); let reply_bytes = result.unwrap(); let reply = Packet::parse(&reply_bytes).unwrap(); @@ -215,7 +224,8 @@ mod tests { ); query.questions.push(question); let mut resolver = PkarrResolver::new(0).await; - let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap()).await; + let mut socket = get_dnssocket().await; + let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap(), &mut socket).await; assert!(result.is_err()); // println!("{}", result.unwrap_err()); } diff --git a/src/pknames_resolver.rs b/src/pknames_resolver.rs index 67a62bb..207e752 100644 --- a/src/pknames_resolver.rs +++ b/src/pknames_resolver.rs @@ -1,4 +1,5 @@ use crate::pkarr_resolver::PkarrResolver; +use any_dns::DnsSocket; use anyhow::anyhow; use pkarr::dns::{Name, Packet}; use pknames_core::resolve::resolve_standalone; @@ -37,10 +38,10 @@ impl PknamesResolver { Ok(full_domain) } - pub async fn resolve(&mut self, query: &Vec) -> std::prelude::v1::Result, anyhow::Error> { + pub async fn resolve(&mut self, query: &Vec, socket: &mut DnsSocket) -> std::prelude::v1::Result, anyhow::Error> { let original_query = Packet::parse(query)?; - let pkarr_result = self.pkarr.resolve(&query.clone()).await; + let pkarr_result = self.pkarr.resolve(&query.clone(), socket).await; if pkarr_result.is_ok() { return pkarr_result; // It was a pkarr hostname } @@ -56,7 +57,7 @@ impl PknamesResolver { let mut pkarr_query = original_query.clone(); pkarr_query.questions[0].qname = qname; let pkarr_query = pkarr_query.build_bytes_vec_compressed().unwrap(); - let pkarr_reply = self.pkarr.resolve(&pkarr_query).await?; + let pkarr_reply = self.pkarr.resolve(&pkarr_query, socket).await?; let pkarr_reply = Packet::parse(&pkarr_reply).unwrap(); let mut reply = original_query.clone().into_reply(); @@ -71,10 +72,16 @@ impl PknamesResolver { #[cfg(test)] mod tests { + use any_dns::{DnsSocket, EmptyHandler, HandlerHolder}; use pkarr::dns::{Name, Packet, Question}; use super::PknamesResolver; + async fn get_dnssocket() -> DnsSocket { + let handler = HandlerHolder::new(EmptyHandler::new()); + DnsSocket::new("127.0.0.1:20384".parse().unwrap(), "8.8.8.8:53".parse().unwrap(), handler, false).await.unwrap() + } + #[tokio::test] async fn query_pubkey() { let mut pknames = PknamesResolver::new(1, "~/.pknames").await; @@ -89,8 +96,8 @@ mod tests { ); query.questions.push(question); let query_bytes = query.build_bytes_vec_compressed().unwrap(); - - let result = pknames.resolve(&query_bytes).await; + let mut socket = get_dnssocket().await; + let result = pknames.resolve(&query_bytes, &mut socket).await; if result.is_err() { eprintln!("{:?}", result.unwrap_err()); assert!(false);