feat: cname

This commit is contained in:
Severin Buhler
2024-02-04 11:52:11 +01:00
parent fdd4b619f9
commit 07e63c887e
8 changed files with 467 additions and 64 deletions

27
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Dns Server",
"cargo": {
"args": [
"build",
"--bin=pkdns",
"--package=pkdns"
],
"filter": {
"name": "pkdns",
"kind": "bin"
}
},
"args": [
],
"cwd": "${workspaceFolder}"
},
]
}

106
Cargo.lock generated
View File

@@ -35,6 +35,21 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.11" version = "0.6.11"
@@ -91,7 +106,7 @@ checksum = "6edefda8000f200c72cfd369d8a46fb56523f25e6fa7ee713d5c9e6c08044dc0"
dependencies = [ dependencies = [
"ctrlc", "ctrlc",
"dyn-clone", "dyn-clone",
"simple-dns 0.6.0", "simple-dns",
"thiserror", "thiserror",
] ]
@@ -330,12 +345,35 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.0",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.18" version = "4.4.18"
@@ -375,6 +413,12 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.12" version = "0.2.12"
@@ -738,6 +782,29 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.5.0" version = "0.5.0"
@@ -981,16 +1048,16 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]] [[package]]
name = "pkarr" name = "pkarr"
version = "1.0.1" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52fb91b6f9777a6cd25ea754efcdba5c90d9decf151290439816b3d2827063ff" checksum = "817373b3240d80ad1c73721c038cea6d9297f880445920ee132698a5f8fa6c69"
dependencies = [ dependencies = [
"bytes", "bytes",
"ed25519-dalek", "ed25519-dalek",
"mainline", "mainline",
"rand", "rand",
"self_cell", "self_cell",
"simple-dns 0.5.7", "simple-dns",
"thiserror", "thiserror",
"url", "url",
"z32", "z32",
@@ -1008,23 +1075,24 @@ dependencies = [
[[package]] [[package]]
name = "pkdns" name = "pkdns"
version = "0.1.2" version = "0.2.0"
dependencies = [ dependencies = [
"any-dns", "any-dns",
"chrono",
"clap", "clap",
"ctrlc", "ctrlc",
"pkarr", "pkarr",
"pknames_core", "pknames_core",
"simple-dns 0.6.0", "simple-dns",
"ttl_cache", "ttl_cache",
"zbase32", "zbase32",
] ]
[[package]] [[package]]
name = "pknames_core" name = "pknames_core"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "690208ab4a642c0a34adb4f0d054fa11555c6f56edaf01e0f5bf5bd55932b072" checksum = "d082731b49918edc3351686006706cae093ff59a9f3092cef533a43423d94043"
dependencies = [ dependencies = [
"assert_approx_eq", "assert_approx_eq",
"burn", "burn",
@@ -1365,15 +1433,6 @@ dependencies = [
"rand_core", "rand_core",
] ]
[[package]]
name = "simple-dns"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae9a3fcdadafb6d97f4c0e007e4247b114ee0f119f650c3cbf3a8b3a1479694"
dependencies = [
"bitflags 2.4.2",
]
[[package]] [[package]]
name = "simple-dns" name = "simple-dns"
version = "0.6.0" version = "0.6.0"
@@ -1757,6 +1816,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
@@ -1891,9 +1959,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]] [[package]]
name = "z32" name = "z32"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4e62ec361f7259399a83b7983270dd0249067d94600c50475e65dca64e24083" checksum = "a5c2c4e1757d25c3bfa9e137b15e458bc028bf569c2c22e59ecf9f62545d45fd"
[[package]] [[package]]
name = "zbase32" name = "zbase32"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "pkdns" name = "pkdns"
version = "0.1.2" version = "0.2.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -8,9 +8,10 @@ edition = "2021"
[dependencies] [dependencies]
ctrlc = "3.4.2" ctrlc = "3.4.2"
simple-dns = "0.6.0" simple-dns = "0.6.0"
pknames_core = "0.1.0" pknames_core = "0.1.1"
pkarr = "1.0.1" pkarr = "1.0.4"
zbase32 = "0.1.2" zbase32 = "0.1.2"
ttl_cache = "0.5.1" ttl_cache = "0.5.1"
clap = "4.4.18" clap = "4.4.18"
any-dns = "0.1.1" any-dns = "0.1.1"
chrono = "0.4.33"

View File

@@ -95,7 +95,7 @@ Options:
-f, --forward <forward> ICANN fallback DNS server. IP:Port [default: 192.168.1.1:53] -f, --forward <forward> ICANN fallback DNS server. IP:Port [default: 192.168.1.1:53]
-s, --socket <socket> Socket the server should listen on. IP:Port [default: 0.0.0.0:53] -s, --socket <socket> Socket the server should listen on. IP:Port [default: 0.0.0.0:53]
-v, --verbose Show verbose output. -v, --verbose Show verbose output.
--no-cache Disable DHT packet caching. --cache-ttl <cache-ttl> Pkarr packet cache ttl in seconds.
--threads <threads> Number of threads to process dns queries. [default: 4] --threads <threads> Number of threads to process dns queries. [default: 4]
-d, --directory <directory> pknames source directory. [default: ~/.pknames] -d, --directory <directory> pknames source directory. [default: ~/.pknames]
-h, --help Print help -h, --help Print help

View File

@@ -7,6 +7,7 @@ use std::{error::Error, net::SocketAddr, sync::mpsc::channel, time::Instant};
mod pkarr_cache; mod pkarr_cache;
mod pkarr_resolver; mod pkarr_resolver;
mod pknames_resolver; mod pknames_resolver;
mod packet_lookup;
#[derive(Clone)] #[derive(Clone)]
struct MyHandler { struct MyHandler {
@@ -76,11 +77,10 @@ fn main() -> Result<(), Box<dyn Error>> {
.help("Show verbose output."), .help("Show verbose output."),
) )
.arg( .arg(
clap::Arg::new("no-cache") clap::Arg::new("cache-ttl")
.long("no-cache") .long("cache-ttl")
.required(false) .required(false)
.num_args(0) .help("Pkarr packet cache ttl in seconds."),
.help("Disable DHT packet caching."),
) )
.arg( .arg(
clap::Arg::new("threads") clap::Arg::new("threads")
@@ -100,7 +100,9 @@ fn main() -> Result<(), Box<dyn Error>> {
let matches = cmd.get_matches(); let matches = cmd.get_matches();
let verbose: bool = *matches.get_one("verbose").unwrap(); let verbose: bool = *matches.get_one("verbose").unwrap();
let no_cache: bool = *matches.get_one("no-cache").unwrap(); let default_cache_ttl = "60".to_string();
let cache_ttl: &String = matches.get_one("cache-ttl").unwrap_or(&default_cache_ttl);
let cache_ttl: u64 = cache_ttl.parse().expect("cache-ttl should be a valid valid positive integer (u64).");
let directory: &String = matches.get_one("directory").unwrap(); let directory: &String = matches.get_one("directory").unwrap();
let threads: &String = matches.get_one("threads").unwrap(); let threads: &String = matches.get_one("threads").unwrap();
let threads: u8 = threads.parse().expect("threads should be valid positive integer."); let threads: u8 = threads.parse().expect("threads should be valid positive integer.");
@@ -116,8 +118,8 @@ fn main() -> Result<(), Box<dyn Error>> {
if verbose { if verbose {
println!("Verbose mode"); println!("Verbose mode");
} }
if no_cache { if cache_ttl != 60 {
println!("Disabled DHT cache") println!("Set cache-ttl to {cache_ttl}s")
} }
if threads != 4 { if threads != 4 {
println!("Use {} threads", threads); println!("Use {} threads", threads);
@@ -131,16 +133,8 @@ fn main() -> Result<(), Box<dyn Error>> {
let max_ttl = if no_cache {
1
} else {
60*60
};
let anydns = Builder::new() let anydns = Builder::new()
.handler(MyHandler::new(max_ttl, directory)) .handler(MyHandler::new(cache_ttl, directory))
.threads(threads) .threads(threads)
.verbose(verbose) .verbose(verbose)
.icann_resolver(forward) .icann_resolver(forward)

262
src/packet_lookup.rs Normal file
View File

@@ -0,0 +1,262 @@
use simple_dns::{rdata::{self, RData}, Name, Packet, Question, ResourceRecord, QTYPE, TYPE};
/**
* Handles all possible ways on how to resolve a query into a reply.
* Does not support forwards, only recursive queries.
* Max CNAME depth == 1.
*/
/**
* 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> {
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 = Packet::parse(&pkarr_reply).unwrap();
let mut reply = query.clone().into_reply();
reply.answers = pkarr_reply.answers;
reply.additional_records = pkarr_reply.additional_records;
reply.name_servers = pkarr_reply.name_servers;
reply.build_bytes_vec_compressed().unwrap()
}
/**
* 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> {
let mut reply = Packet::new_reply(0);
let direct_matchs = direct_matches(pkarr_packet, &question.qname, &question.qtype);
reply.answers.extend(direct_matchs.clone());
if reply.answers.len() == 0 {
// Not found. Maybe it is a cname?
let cname_matches = resolve_cname_for(pkarr_packet, question);
reply.answers.extend(cname_matches);
};
if reply.answers.len() == 0 {
// Not found. Maybe we have a name server?
reply.name_servers = find_nameserver(pkarr_packet, &question.qname);
};
reply.build_bytes_vec_compressed().unwrap()
}
/**
* Resolve a cnames for a given. Only goes to max 1 depth. CNAME always needs to point to a A/AAAA record.
*/
fn resolve_cname_for<'a>(pkarr_packet: &Packet<'a>, question: &Question<'a>) -> Vec<ResourceRecord<'a>> {
let cname_matches = direct_matches(pkarr_packet, &question.qname, &QTYPE::TYPE(TYPE::CNAME));
let additional_data: Vec<ResourceRecord<'_>> = cname_matches.iter().flat_map(|cname| {
let cname_content = if let RData::CNAME(rdata::CNAME(cname_pointer)) = &cname.rdata {
cname_pointer
} else {
panic!("Should be cname");
};
let matches = direct_matches(pkarr_packet, &cname_content, &question.qtype);
matches
}).collect();
let mut result = vec![];
result.extend(cname_matches);
result.extend(additional_data);
result
}
/**
* Resolve direct qname and qtype record matches.
*/
fn direct_matches<'a>(pkarr_packet: &Packet<'a>, qname: &Name<'a>, qtype: &QTYPE) -> Vec<ResourceRecord<'a>> {
let matches: Vec<ResourceRecord<'_>> = pkarr_packet.answers.iter()
.filter(|record| {
record.name == *qname && record.match_qtype(*qtype)
})
.map(|record| record.clone())
.collect();
matches
}
/**
* Find nameserver for given qname.
*/
fn find_nameserver<'a>(pkarr_packet: &Packet<'a>, qname: &Name<'a>) -> Vec<ResourceRecord<'a>> {
let matches: Vec<ResourceRecord<'_>> = pkarr_packet.answers.iter()
.filter(|record| {
record.match_qtype(QTYPE::TYPE(TYPE::NS)) && (qname.is_subdomain_of(&record.name) || record.name == *qname)
})
.map(|record| record.clone())
.collect();
matches
}
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use pkarr::{
dns::{Name, Packet, ResourceRecord}, Keypair, PublicKey
};
use simple_dns::{rdata::RData, Question};
use super::{resolve_query, resolve_question};
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
// No ending dot.
let keypair = Keypair::random();
let pubkey = keypair.public_key();
let pubkey_z32 = keypair.to_z32();
let mut packet = Packet::new_reply(0);
let name = Name::new(&pubkey_z32).unwrap();
let ip: Ipv4Addr = "127.0.0.1".parse().unwrap();
let answer1 = ResourceRecord::new(
name.clone(), simple_dns::CLASS::IN, 100, RData::A(ip.into())
);
packet.answers.push(answer1);
let name = format!("pknames.p2p.{pubkey_z32}");
let name = Name::new(&name).unwrap();
let ip: Ipv4Addr = "127.0.0.1".parse().unwrap();
let answer1 = ResourceRecord::new(
name.clone(), simple_dns::CLASS::IN, 100, RData::A(ip.into())
);
packet.answers.push(answer1);
let name = format!("www.pknames.p2p.{pubkey_z32}");
let name = Name::new(&name).unwrap();
let data = format!("pknames.p2p.{pubkey_z32}");
let data = Name::new(&data).unwrap();
let answer3 = ResourceRecord::new(
name.clone(), simple_dns::CLASS::IN, 100, RData::CNAME(simple_dns::rdata::CNAME(data))
);
packet.answers.push(answer3);
let name = format!("other.{pubkey_z32}");
let name = Name::new(&name).unwrap();
let data = format!("my.ns.example.com");
let data = Name::new(&data).unwrap();
let answer4 = ResourceRecord::new(
name.clone(), simple_dns::CLASS::IN, 100, RData::NS(simple_dns::rdata::NS(data))
);
packet.answers.push(answer4);
(packet.build_bytes_vec_compressed().unwrap(), pubkey)
}
#[test]
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();
let name = format!("pknames.p2p.{pubkey_z32}");
let name = Name::new(&name).unwrap();
let qtype = simple_dns::QTYPE::TYPE(simple_dns::TYPE::A);
let question = Question::new(name.clone(), qtype, simple_dns::QCLASS::CLASS(simple_dns::CLASS::IN), false);
let reply = resolve_question(&pkarr_packet, &question);
let reply = Packet::parse(&reply).unwrap();
assert_eq!(reply.answers.len(), 1);
assert_eq!(reply.additional_records.len(), 0);
assert_eq!(reply.name_servers.len(), 0);
let answer = reply.answers.first().unwrap();
assert_eq!(answer.name, name);
assert!(answer.match_qtype(qtype));
}
#[test]
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();
let name = format!("www.pknames.p2p.{pubkey_z32}");
let name = Name::new(&name).unwrap();
let qtype = simple_dns::QTYPE::TYPE(simple_dns::TYPE::A);
let question = Question::new(name.clone(), qtype, simple_dns::QCLASS::CLASS(simple_dns::CLASS::IN), false);
let reply = resolve_question(&pkarr_packet, &question);
let reply = Packet::parse(&reply).unwrap();
assert_eq!(reply.answers.len(), 2);
assert_eq!(reply.additional_records.len(), 0);
assert_eq!(reply.name_servers.len(), 0);
let answer1 = reply.answers.get(0).unwrap();
assert_eq!(answer1.name, name);
assert!(answer1.match_qtype(simple_dns::QTYPE::TYPE(simple_dns::TYPE::CNAME)));
let answer2 = reply.answers.get(1).unwrap();
assert_eq!(answer2.name.to_string(), format!("pknames.p2p.{pubkey_z32}"));
assert!(answer2.match_qtype(qtype));
}
#[test]
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();
let name = format!("other.{pubkey_z32}");
let name = Name::new(&name).unwrap();
let qtype = simple_dns::QTYPE::TYPE(simple_dns::TYPE::A);
let question = Question::new(name.clone(), qtype, simple_dns::QCLASS::CLASS(simple_dns::CLASS::IN), false);
let reply = resolve_question(&pkarr_packet, &question);
let reply = Packet::parse(&reply).unwrap();
assert_eq!(reply.answers.len(), 0);
assert_eq!(reply.additional_records.len(), 0);
assert_eq!(reply.name_servers.len(), 1);
let ns1 = reply.name_servers.get(0).unwrap();
assert_eq!(ns1.name, name);
assert!(ns1.match_qtype(simple_dns::QTYPE::TYPE(simple_dns::TYPE::NS)));
}
#[test]
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();
let name = format!("sub.other.{pubkey_z32}");
let name = Name::new(&name).unwrap();
let qtype = simple_dns::QTYPE::TYPE(simple_dns::TYPE::A);
let question = Question::new(name.clone(), qtype, simple_dns::QCLASS::CLASS(simple_dns::CLASS::IN), false);
let reply = resolve_question(&pkarr_packet, &question);
let reply = Packet::parse(&reply).unwrap();
assert_eq!(reply.answers.len(), 0);
assert_eq!(reply.additional_records.len(), 0);
assert_eq!(reply.name_servers.len(), 1);
let ns1 = reply.name_servers.get(0).unwrap();
assert_eq!(ns1.name.to_string(), format!("other.{pubkey_z32}"));
assert!(ns1.match_qtype(simple_dns::QTYPE::TYPE(simple_dns::TYPE::NS)));
}
#[test]
fn simple_a_query() {
let (pkarr_packet, pubkey) = example_pkarr_reply();
let pkarr_packet = Packet::parse(&pkarr_packet).unwrap();
let mut query = Packet::new_query(0);
query.questions = vec![
Question::new(Name::new("pknames.p2p").unwrap(), simple_dns::QTYPE::TYPE(simple_dns::TYPE::A), simple_dns::QCLASS::CLASS(simple_dns::CLASS::IN), false)
];
let _reply = resolve_query(&pkarr_packet, &query);
}
}

View File

@@ -1,11 +1,23 @@
use std::{error::Error, sync::{Arc, Mutex}}; use std::{error::Error, sync::{Arc, Mutex}};
use pkarr::{ use pkarr::{
dns::{Packet, ResourceRecord}, dns::{Packet, Question, ResourceRecord, QTYPE, TYPE}, PkarrClient, PublicKey, SignedPacket
PkarrClient, PublicKey,
}; };
use chrono::{DateTime, Utc};
use crate::{packet_lookup::resolve_query, pkarr_cache::PkarrPacketTtlCache};
use crate::pkarr_cache::PkarrPacketTtlCache;
trait SignedPacketTimestamp {
fn chrono_timestamp(&self) -> DateTime<Utc>;
}
impl SignedPacketTimestamp for SignedPacket {
fn chrono_timestamp(&self) -> DateTime<Utc> {
let timestamp = self.timestamp()/1_000_000;
let timestamp = DateTime::from_timestamp((timestamp as u32).into(), 0).unwrap();
timestamp
}
}
/** /**
* Pkarr resolver with cache. * Pkarr resolver with cache.
@@ -50,9 +62,10 @@ impl PkarrResolver {
if packet_option.is_none() { if packet_option.is_none() {
return None; return None;
}; };
let reply_bytes = packet_option.unwrap().packet().build_bytes_vec().unwrap(); let signed_packet = packet_option.unwrap();
cache.add(pubkey.clone(), reply_bytes); let reply_bytes = signed_packet.packet().build_bytes_vec_compressed().unwrap();
cache.get(pubkey) cache.add(pubkey.clone(), reply_bytes.clone());
Some(reply_bytes)
} }
/** /**
@@ -82,19 +95,11 @@ impl PkarrResolver {
if packet_option.is_none() { if packet_option.is_none() {
return Err("No pkarr packet found for pubkey".into()); return Err("No pkarr packet found for pubkey".into());
} }
let packet = packet_option.unwrap(); let pkarr_packet = packet_option.unwrap();
let packet = Packet::parse(&packet).unwrap(); let pkarr_packet = Packet::parse(&pkarr_packet).unwrap();
let reply = resolve_query(&pkarr_packet, &request);
let matching_records: Vec<ResourceRecord<'_>> = packet.answers.iter() Ok(reply)
.filter(|record| record.match_qclass(question.qclass) && record.match_qtype(question.qtype) && record.name == question.qname)
.map(|record| record.clone())
.collect();
let mut reply = request.into_reply();
reply.answers = matching_records;
// println!("Pkarr reply {:?} with {} ms", reply, start.elapsed().as_millis());
let reply_bytes: Vec<u8> = reply.build_bytes_vec()?;
Ok(reply_bytes)
} }
} }
@@ -104,9 +109,10 @@ mod tests {
dns::{Name, Packet, Question, ResourceRecord}, dns::{Name, Packet, Question, ResourceRecord},
Keypair, SignedPacket, Keypair, SignedPacket,
}; };
use simple_dns::rdata::A;
// use simple_dns::{Name, Question, Packet}; // use simple_dns::{Name, Question, Packet};
use super::*; use super::*;
use std::net::Ipv4Addr; use std::{fmt::format, net::Ipv4Addr};
use zbase32; use zbase32;
fn get_test_keypair() -> Keypair { fn get_test_keypair() -> Keypair {
@@ -163,7 +169,7 @@ mod tests {
query.questions.push(question); query.questions.push(question);
let mut resolver = PkarrResolver::new(0); let mut resolver = PkarrResolver::new(0);
let result = resolver.resolve(&query.build_bytes_vec().unwrap()); let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap());
assert!(result.is_ok()); assert!(result.is_ok());
let reply_bytes = result.unwrap(); let reply_bytes = result.unwrap();
let reply = Packet::parse(&reply_bytes).unwrap(); let reply = Packet::parse(&reply_bytes).unwrap();
@@ -190,7 +196,7 @@ mod tests {
); );
query.questions.push(question); query.questions.push(question);
let mut resolver = PkarrResolver::new(0); let mut resolver = PkarrResolver::new(0);
let result = resolver.resolve(&query.build_bytes_vec().unwrap()); let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap());
assert!(result.is_ok()); assert!(result.is_ok());
let reply_bytes = result.unwrap(); let reply_bytes = result.unwrap();
let reply = Packet::parse(&reply_bytes).unwrap(); let reply = Packet::parse(&reply_bytes).unwrap();
@@ -214,7 +220,7 @@ mod tests {
); );
query.questions.push(question); query.questions.push(question);
let mut resolver = PkarrResolver::new(0); let mut resolver = PkarrResolver::new(0);
let result = resolver.resolve(&query.build_bytes_vec().unwrap()); let result = resolver.resolve(&query.build_bytes_vec_compressed().unwrap());
assert!(result.is_err()); assert!(result.is_err());
// println!("{}", result.unwrap_err()); // println!("{}", result.unwrap_err());
} }
@@ -233,4 +239,49 @@ mod tests {
let trying: Result<PublicKey, _> = domain.try_into(); let trying: Result<PublicKey, _> = domain.try_into();
assert!(trying.is_err()); assert!(trying.is_err());
} }
#[test]
fn pkarr_invalid_packet1() {
let pubkey = PkarrResolver::parse_pkarr_uri("7fmjpcuuzf54hw18bsgi3zihzyh4awseeuq5tmojefaezjbd64cy").unwrap();
let mut resolver = PkarrResolver::new(0);
let result = resolver.resolve_pubkey_respect_cache(&pubkey);
// assert!(result.is_some());
}
#[test]
fn pkarr_invalid_packet2() {
let pubkey = PkarrResolver::parse_pkarr_uri("7fmjpcuuzf54hw18bsgi3zihzyh4awseeuq5tmojefaezjbd64cy").unwrap();
let client = PkarrClient::new();
let signed_packet = client.resolve(pubkey).unwrap();
println!("Timestamp {}", signed_packet.chrono_timestamp());
let reply_bytes = signed_packet.packet().build_bytes_vec_compressed().unwrap();
Packet::parse(&reply_bytes).unwrap();
}
#[test]
fn pkarr_invalid_packet3() {
let keypair = Keypair::random();
let pubkey_z32 = keypair.to_z32();
// Construct reply with single CNAME record.
let mut packet = Packet::new_reply(0);
let name = Name::new("www.pknames.p2p").unwrap();
let data = format!("pknames.p2p.{pubkey_z32}");
let data = Name::new(&data).unwrap();
let answer3 = ResourceRecord::new(
name.clone(), simple_dns::CLASS::IN, 100, simple_dns::rdata::RData::CNAME(simple_dns::rdata::CNAME(data))
);
packet.answers.push(answer3);
// Sign packet
let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap();
// Serialize and parse again
let reply_bytes = signed_packet.packet().build_bytes_vec().unwrap();
Packet::parse(&reply_bytes).unwrap(); // Fail
}
} }

View File

@@ -53,7 +53,7 @@ impl PknamesResolver {
let qname = Name::new(&pkarr_domain).unwrap(); let qname = Name::new(&pkarr_domain).unwrap();
let mut pkarr_query = original_query.clone(); let mut pkarr_query = original_query.clone();
pkarr_query.questions[0].qname = qname; pkarr_query.questions[0].qname = qname;
let pkarr_query = pkarr_query.build_bytes_vec().unwrap(); let pkarr_query = pkarr_query.build_bytes_vec_compressed().unwrap();
let pkarr_reply = self.pkarr.resolve(&pkarr_query)?; let pkarr_reply = self.pkarr.resolve(&pkarr_query)?;
let pkarr_reply = Packet::parse(&pkarr_reply).unwrap(); let pkarr_reply = Packet::parse(&pkarr_reply).unwrap();
@@ -63,7 +63,7 @@ impl PknamesResolver {
answer.name = question.qname.clone(); answer.name = question.qname.clone();
reply.answers.push(answer); reply.answers.push(answer);
}; };
Ok(reply.build_bytes_vec().unwrap()) Ok(reply.build_bytes_vec_compressed().unwrap())
} }
} }
@@ -83,7 +83,7 @@ mod tests {
let name = Name::new("pknames.p2p").unwrap(); let name = Name::new("pknames.p2p").unwrap();
let question = Question::new(name, pkarr::dns::QTYPE::TYPE(pkarr::dns::TYPE::A), pkarr::dns::QCLASS::CLASS(pkarr::dns::CLASS::IN), false); let question = Question::new(name, pkarr::dns::QTYPE::TYPE(pkarr::dns::TYPE::A), pkarr::dns::QCLASS::CLASS(pkarr::dns::CLASS::IN), false);
query.questions.push(question); query.questions.push(question);
let query_bytes = query.build_bytes_vec().unwrap(); let query_bytes = query.build_bytes_vec_compressed().unwrap();
let result = pknames.resolve(&query_bytes); let result = pknames.resolve(&query_bytes);
if result.is_err() { if result.is_err() {