mirror of
https://github.com/aljazceru/pkdns.git
synced 2025-12-17 05:54:21 +01:00
feat: cname
This commit is contained in:
27
.vscode/launch.json
vendored
Normal file
27
.vscode/launch.json
vendored
Normal 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
106
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -95,7 +95,7 @@ Options:
|
||||
-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]
|
||||
-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]
|
||||
-d, --directory <directory> pknames source directory. [default: ~/.pknames]
|
||||
-h, --help Print help
|
||||
|
||||
26
src/main.rs
26
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<dyn Error>> {
|
||||
.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<dyn Error>> {
|
||||
|
||||
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<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
|
||||
262
src/packet_lookup.rs
Normal file
262
src/packet_lookup.rs
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<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.
|
||||
@@ -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<ResourceRecord<'_>> = 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<u8> = 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<PublicKey, _> = 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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user