diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2cae8b0 --- /dev/null +++ b/.vscode/launch.json @@ -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}" + }, + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3878597..59cf345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,21 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "anstream" version = "0.6.11" @@ -91,7 +106,7 @@ checksum = "6edefda8000f200c72cfd369d8a46fb56523f25e6fa7ee713d5c9e6c08044dc0" dependencies = [ "ctrlc", "dyn-clone", - "simple-dns 0.6.0", + "simple-dns", "thiserror", ] @@ -330,12 +345,35 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "clap" version = "4.4.18" @@ -375,6 +413,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -738,6 +782,29 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "idna" version = "0.5.0" @@ -981,16 +1048,16 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pkarr" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fb91b6f9777a6cd25ea754efcdba5c90d9decf151290439816b3d2827063ff" +checksum = "817373b3240d80ad1c73721c038cea6d9297f880445920ee132698a5f8fa6c69" dependencies = [ "bytes", "ed25519-dalek", "mainline", "rand", "self_cell", - "simple-dns 0.5.7", + "simple-dns", "thiserror", "url", "z32", @@ -1008,23 +1075,24 @@ dependencies = [ [[package]] name = "pkdns" -version = "0.1.2" +version = "0.2.0" dependencies = [ "any-dns", + "chrono", "clap", "ctrlc", "pkarr", "pknames_core", - "simple-dns 0.6.0", + "simple-dns", "ttl_cache", "zbase32", ] [[package]] name = "pknames_core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "690208ab4a642c0a34adb4f0d054fa11555c6f56edaf01e0f5bf5bd55932b072" +checksum = "d082731b49918edc3351686006706cae093ff59a9f3092cef533a43423d94043" dependencies = [ "assert_approx_eq", "burn", @@ -1365,15 +1433,6 @@ dependencies = [ "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]] name = "simple-dns" version = "0.6.0" @@ -1757,6 +1816,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "windows-sys" version = "0.48.0" @@ -1891,9 +1959,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "z32" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e62ec361f7259399a83b7983270dd0249067d94600c50475e65dca64e24083" +checksum = "a5c2c4e1757d25c3bfa9e137b15e458bc028bf569c2c22e59ecf9f62545d45fd" [[package]] name = "zbase32" diff --git a/Cargo.toml b/Cargo.toml index 4e9b0ae..e14fdfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pkdns" -version = "0.1.2" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -8,9 +8,10 @@ edition = "2021" [dependencies] ctrlc = "3.4.2" simple-dns = "0.6.0" -pknames_core = "0.1.0" -pkarr = "1.0.1" +pknames_core = "0.1.1" +pkarr = "1.0.4" zbase32 = "0.1.2" ttl_cache = "0.5.1" clap = "4.4.18" any-dns = "0.1.1" +chrono = "0.4.33" diff --git a/README.md b/README.md index 2802d03..6f53f48 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Options: -f, --forward ICANN fallback DNS server. IP:Port [default: 192.168.1.1:53] -s, --socket Socket the server should listen on. IP:Port [default: 0.0.0.0:53] -v, --verbose Show verbose output. - --no-cache Disable DHT packet caching. + --cache-ttl Pkarr packet cache ttl in seconds. --threads Number of threads to process dns queries. [default: 4] -d, --directory pknames source directory. [default: ~/.pknames] -h, --help Print help diff --git a/src/main.rs b/src/main.rs index 79e359f..f75707e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use std::{error::Error, net::SocketAddr, sync::mpsc::channel, time::Instant}; mod pkarr_cache; mod pkarr_resolver; mod pknames_resolver; +mod packet_lookup; #[derive(Clone)] struct MyHandler { @@ -76,11 +77,10 @@ fn main() -> Result<(), Box> { .help("Show verbose output."), ) .arg( - clap::Arg::new("no-cache") - .long("no-cache") + clap::Arg::new("cache-ttl") + .long("cache-ttl") .required(false) - .num_args(0) - .help("Disable DHT packet caching."), + .help("Pkarr packet cache ttl in seconds."), ) .arg( clap::Arg::new("threads") @@ -100,7 +100,9 @@ fn main() -> Result<(), Box> { let matches = cmd.get_matches(); 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 threads: &String = matches.get_one("threads").unwrap(); let threads: u8 = threads.parse().expect("threads should be valid positive integer."); @@ -116,8 +118,8 @@ fn main() -> Result<(), Box> { if verbose { println!("Verbose mode"); } - if no_cache { - println!("Disabled DHT cache") + if cache_ttl != 60 { + println!("Set cache-ttl to {cache_ttl}s") } if threads != 4 { println!("Use {} threads", threads); @@ -130,17 +132,9 @@ fn main() -> Result<(), Box> { } - - - - let max_ttl = if no_cache { - 1 - } else { - 60*60 - }; let anydns = Builder::new() - .handler(MyHandler::new(max_ttl, directory)) + .handler(MyHandler::new(cache_ttl, directory)) .threads(threads) .verbose(verbose) .icann_resolver(forward) diff --git a/src/packet_lookup.rs b/src/packet_lookup.rs new file mode 100644 index 0000000..4eeea93 --- /dev/null +++ b/src/packet_lookup.rs @@ -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 { + 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 { + 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> { + let cname_matches = direct_matches(pkarr_packet, &question.qname, &QTYPE::TYPE(TYPE::CNAME)); + + let additional_data: Vec> = 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> { + let matches: Vec> = 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> { + let matches: Vec> = 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, 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); + } + + +} \ No newline at end of file diff --git a/src/pkarr_resolver.rs b/src/pkarr_resolver.rs index 1ad6cd0..1e7b47b 100644 --- a/src/pkarr_resolver.rs +++ b/src/pkarr_resolver.rs @@ -1,11 +1,23 @@ use std::{error::Error, sync::{Arc, Mutex}}; use pkarr::{ - dns::{Packet, ResourceRecord}, - PkarrClient, PublicKey, + dns::{Packet, Question, ResourceRecord, QTYPE, TYPE}, PkarrClient, PublicKey, SignedPacket }; +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; +} + +impl SignedPacketTimestamp for SignedPacket { + fn chrono_timestamp(&self) -> DateTime { + let timestamp = self.timestamp()/1_000_000; + let timestamp = DateTime::from_timestamp((timestamp as u32).into(), 0).unwrap(); + timestamp + } +} /** * Pkarr resolver with cache. @@ -50,9 +62,10 @@ impl PkarrResolver { if packet_option.is_none() { return None; }; - let reply_bytes = packet_option.unwrap().packet().build_bytes_vec().unwrap(); - cache.add(pubkey.clone(), reply_bytes); - cache.get(pubkey) + let signed_packet = packet_option.unwrap(); + let reply_bytes = signed_packet.packet().build_bytes_vec_compressed().unwrap(); + cache.add(pubkey.clone(), reply_bytes.clone()); + Some(reply_bytes) } /** @@ -82,19 +95,11 @@ impl PkarrResolver { if packet_option.is_none() { return Err("No pkarr packet found for pubkey".into()); } - let packet = packet_option.unwrap(); - let packet = Packet::parse(&packet).unwrap(); + let pkarr_packet = packet_option.unwrap(); + let pkarr_packet = Packet::parse(&pkarr_packet).unwrap(); + let reply = resolve_query(&pkarr_packet, &request); - let matching_records: Vec> = packet.answers.iter() - .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 = reply.build_bytes_vec()?; - Ok(reply_bytes) + Ok(reply) } } @@ -104,9 +109,10 @@ mod tests { dns::{Name, Packet, Question, ResourceRecord}, Keypair, SignedPacket, }; + use simple_dns::rdata::A; // use simple_dns::{Name, Question, Packet}; use super::*; - use std::net::Ipv4Addr; + use std::{fmt::format, net::Ipv4Addr}; use zbase32; fn get_test_keypair() -> Keypair { @@ -163,7 +169,7 @@ mod tests { query.questions.push(question); 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()); let reply_bytes = result.unwrap(); let reply = Packet::parse(&reply_bytes).unwrap(); @@ -190,7 +196,7 @@ mod tests { ); query.questions.push(question); 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()); let reply_bytes = result.unwrap(); let reply = Packet::parse(&reply_bytes).unwrap(); @@ -214,7 +220,7 @@ mod tests { ); query.questions.push(question); 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()); // println!("{}", result.unwrap_err()); } @@ -233,4 +239,49 @@ mod tests { let trying: Result = domain.try_into(); 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 + } + + } diff --git a/src/pknames_resolver.rs b/src/pknames_resolver.rs index bff8f95..2f046e3 100644 --- a/src/pknames_resolver.rs +++ b/src/pknames_resolver.rs @@ -53,7 +53,7 @@ impl PknamesResolver { let qname = Name::new(&pkarr_domain).unwrap(); let mut pkarr_query = original_query.clone(); 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 = Packet::parse(&pkarr_reply).unwrap(); @@ -63,7 +63,7 @@ impl PknamesResolver { answer.name = question.qname.clone(); 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 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); - let query_bytes = query.build_bytes_vec().unwrap(); + let query_bytes = query.build_bytes_vec_compressed().unwrap(); let result = pknames.resolve(&query_bytes); if result.is_err() {