mirror of
https://github.com/aljazceru/pkdns.git
synced 2025-12-17 05:54:21 +01:00
ns delegation
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
14
README.md
14
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.
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user