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"
|
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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
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_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);
|
||||||
@@ -130,17 +132,9 @@ 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
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 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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user