ns delegation

This commit is contained in:
Severin Buhler
2024-02-15 17:12:05 +01:00
parent cb04bae6ec
commit 05cefe7f6d
8 changed files with 147 additions and 38 deletions

4
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"

View File

@@ -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.

View File

@@ -23,8 +23,8 @@ impl MyHandler {
}
#[async_trait]
impl CustomHandler for MyHandler {
async fn lookup(&mut self, query: &Vec<u8>, _socket: DnsSocket) -> Result<Vec<u8>, CustomHandlerError> {
let result = self.pkarr.resolve(query).await;
async fn lookup(&mut self, query: &Vec<u8>, mut socket: DnsSocket) -> Result<Vec<u8>, CustomHandlerError> {
let result = self.pkarr.resolve(query, &mut socket).await;
match result {
Ok(reply) => Ok(reply),

View File

@@ -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<u8> {
pub async fn resolve_query<'a>(pkarr_packet: &Packet<'a>, query: &Packet<'a>, socket: &mut DnsSocket) -> Vec<u8> {
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<u
/**
* Resolves a question by filtering the pkarr packet and creating a corresponding reply.
*/
fn resolve_question<'a>(pkarr_packet: &Packet<'a>, question: &Question<'a>) -> Vec<u8> {
async fn resolve_question<'a>(pkarr_packet: &Packet<'a>, question: &Question<'a>, socket: &mut DnsSocket) -> Vec<u8> {
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<Resou
matches
}
/**
* Resolve name server ip
*/
async fn resolve_ns_ip<'a>(ns_name: &Name<'a>, socket: &mut DnsSocket) -> Option<Vec<SocketAddr>> {
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<SocketAddr> = 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<ResourceRecord<'a>>, socket: &mut DnsSocket) -> Option<Vec<u8>> {
if name_servers.len() == 0 {
return None;
};
let ns_names: Vec<Name<'_>> = 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<u8>, 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);
}
}

View File

@@ -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
}
}

View File

@@ -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<u8>) -> std::prelude::v1::Result<Vec<u8>, anyhow::Error> {
pub async fn resolve(&mut self, query: &Vec<u8>, socket: &mut DnsSocket) -> std::prelude::v1::Result<Vec<u8>, 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());
}

View File

@@ -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<u8>) -> std::prelude::v1::Result<Vec<u8>, anyhow::Error> {
pub async fn resolve(&mut self, query: &Vec<u8>, socket: &mut DnsSocket) -> std::prelude::v1::Result<Vec<u8>, 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);