From d695e2a7d43fada3b76bb55e75a07a37f5358e90 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Mon, 15 Apr 2024 16:24:08 +0300 Subject: [PATCH 01/12] WIP --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f9eec4c15..ee3f4c5eaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2489,7 +2489,7 @@ dependencies = [ [[package]] name = "iroh-dns-server" -version = "0.1.0" +version = "0.13.0" dependencies = [ "anyhow", "async-trait", @@ -2524,7 +2524,7 @@ dependencies = [ "tokio-rustls-acme", "tokio-stream", "tokio-util", - "toml 0.8.12", + "toml", "tower-http", "tower_governor", "tracing", From 644ee363bfa64bf4c09e191c0782dd806879b3cd Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Mon, 15 Apr 2024 17:47:53 +0300 Subject: [PATCH 02/12] Wire up pkarr dht fallback --- Cargo.lock | 50 +++++++++++++++++++++++++++++++++-- iroh-dns-server/Cargo.toml | 2 +- iroh-dns-server/src/config.rs | 4 +++ iroh-dns-server/src/server.rs | 8 +++++- iroh-dns-server/src/store.rs | 22 ++++++++++++--- 5 files changed, 79 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee3f4c5eaf..b25f4bb1bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2854,6 +2854,26 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "mainline" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907bd8136f5eb985f3c0faa70051d4809c7e0cfbcc5700bec2ceb5df0de118ca" +dependencies = [ + "bytes", + "crc", + "ed25519-dalek", + "flume", + "lru", + "rand", + "serde", + "serde_bencode", + "serde_bytes", + "sha1_smol", + "thiserror", + "tracing", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -3476,12 +3496,13 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4025a211a70a716314d4ea6464aed150f696deb81651bebf62f874cee5aac7" +checksum = "5ad73bf3327d38c904c81d3baeabff147a7eb77dccb78aae95c738c12e2e0abe" dependencies = [ "bytes", "ed25519-dalek", + "mainline", "rand", "reqwest", "self_cell", @@ -4530,6 +4551,25 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bencode" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70dfc7b7438b99896e7f8992363ab8e2c4ba26aa5ec675d32d1c3c2c33d413e" +dependencies = [ + "serde", + "serde_bytes", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -4643,6 +4683,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.8" diff --git a/iroh-dns-server/Cargo.toml b/iroh-dns-server/Cargo.toml index 96afd9d4bf..ff4fa0719e 100644 --- a/iroh-dns-server/Cargo.toml +++ b/iroh-dns-server/Cargo.toml @@ -27,7 +27,7 @@ http = "1.0.0" iroh-metrics = { version = "0.13.0", path = "../iroh-metrics" } lru = "0.12.3" parking_lot = "0.12.1" -pkarr = { version = "1.1.2", features = [ "async", "relay"], default_features = false } +pkarr = { version = "1.1.2", features = [ "async", "relay", "dht"], default_features = false } rcgen = "0.12.1" redb = "2.0.0" regex = "1.10.3" diff --git a/iroh-dns-server/src/config.rs b/iroh-dns-server/src/config.rs index 4f50fbc46b..6ec4475158 100644 --- a/iroh-dns-server/src/config.rs +++ b/iroh-dns-server/src/config.rs @@ -38,6 +38,9 @@ pub struct Config { /// The metrics server is started by default. To disable the metrics server, set to /// `Some(MetricsConfig::disabled())`. pub metrics: Option, + + /// Fall back to the dht for resolution. + pub dht_fallback: bool, } /// The config for the metrics server. @@ -128,6 +131,7 @@ impl Default for Config { rr_ns: Some("ns1.irohdns.example.".to_string()), }, metrics: None, + dht_fallback: false, } } } diff --git a/iroh-dns-server/src/server.rs b/iroh-dns-server/src/server.rs index 2b952a8c51..45857803d3 100644 --- a/iroh-dns-server/src/server.rs +++ b/iroh-dns-server/src/server.rs @@ -1,7 +1,10 @@ //! The main server which combines the DNS and HTTP(S) servers. +use std::sync::Arc; + use anyhow::Result; use iroh_metrics::metrics::start_metrics_server; +use pkarr::PkarrClient; use tracing::info; use crate::{ @@ -14,7 +17,10 @@ use crate::{ /// Spawn the server and run until the `Ctrl-C` signal is received, then shutdown. pub async fn run_with_config_until_ctrl_c(config: Config) -> Result<()> { - let store = ZoneStore::persistent(Config::signed_packet_store_path()?)?; + let mut store = ZoneStore::persistent(Config::signed_packet_store_path()?)?; + if config.dht_fallback { + store = store.with_pkarr(Some(Arc::new(PkarrClient::default()))); + }; let server = Server::spawn(config, store).await?; tokio::signal::ctrl_c().await?; info!("shutdown"); diff --git a/iroh-dns-server/src/store.rs b/iroh-dns-server/src/store.rs index 5877d00906..e123d09db4 100644 --- a/iroh-dns-server/src/store.rs +++ b/iroh-dns-server/src/store.rs @@ -7,7 +7,7 @@ use hickory_proto::rr::{Name, RecordSet, RecordType, RrKey}; use iroh_metrics::inc; use lru::LruCache; use parking_lot::Mutex; -use pkarr::SignedPacket; +use pkarr::{PkarrClient, SignedPacket}; use crate::{ metrics::Metrics, @@ -35,6 +35,7 @@ pub enum PacketSource { pub struct ZoneStore { cache: Arc>, store: Arc, + pkarr: Option>, } impl ZoneStore { @@ -50,12 +51,18 @@ impl ZoneStore { Ok(Self::new(packet_store)) } + /// Set pkarr client for fallback resolution. + pub fn with_pkarr(self, pkarr: Option>) -> Self { + Self { pkarr, ..self } + } + /// Create a new zone store. pub fn new(store: SignedPacketStore) -> Self { let zone_cache = ZoneCache::new(DEFAULT_CACHE_CAPACITY); Self { store: Arc::new(store), cache: Arc::new(Mutex::new(zone_cache)), + pkarr: None, } } @@ -79,8 +86,17 @@ impl ZoneStore { .insert_and_resolve(&packet, name, record_type); }; - // This would be where mainline discovery could be added. - + if let Some(pkarr) = self.pkarr.as_ref() { + let key = pkarr::PublicKey::try_from(*pubkey.as_bytes()).expect("valid public key"); + // use the more expensive `resolve_most_recent` here. It's just once + let packet_opt = pkarr.as_ref().resolve_most_recent(key).await; + if let Some(packet) = packet_opt { + return self + .cache + .lock() + .insert_and_resolve(&packet, name, record_type); + } + } Ok(None) } From b035c427589997a3635a050777717db30c7bfc10 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Mon, 15 Apr 2024 18:38:24 +0300 Subject: [PATCH 03/12] WIP dht fallback for DNS --- Cargo.lock | 1 + iroh-dns-server/Cargo.toml | 1 + iroh-dns-server/src/config.rs | 2 +- iroh-dns-server/src/store.rs | 50 +++++++++++++++++++++++++++++------ iroh-dns-server/src/util.rs | 4 ++- 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b25f4bb1bf..a94167656f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2529,6 +2529,7 @@ dependencies = [ "tower_governor", "tracing", "tracing-subscriber", + "ttl_cache", "url", "z32", ] diff --git a/iroh-dns-server/Cargo.toml b/iroh-dns-server/Cargo.toml index ff4fa0719e..bf34be013d 100644 --- a/iroh-dns-server/Cargo.toml +++ b/iroh-dns-server/Cargo.toml @@ -46,6 +46,7 @@ tower-http = { version = "0.5.2", features = ["cors", "trace"] } tower_governor = "0.3.2" tracing = "0.1.40" tracing-subscriber = "0.3.18" +ttl_cache = "0.5.1" url = "2.5.0" z32 = "1.1.1" diff --git a/iroh-dns-server/src/config.rs b/iroh-dns-server/src/config.rs index 6ec4475158..4a1ca203bd 100644 --- a/iroh-dns-server/src/config.rs +++ b/iroh-dns-server/src/config.rs @@ -131,7 +131,7 @@ impl Default for Config { rr_ns: Some("ns1.irohdns.example.".to_string()), }, metrics: None, - dht_fallback: false, + dht_fallback: true, } } } diff --git a/iroh-dns-server/src/store.rs b/iroh-dns-server/src/store.rs index e123d09db4..eb43d05432 100644 --- a/iroh-dns-server/src/store.rs +++ b/iroh-dns-server/src/store.rs @@ -1,6 +1,6 @@ //! Pkarr packet store used to resolve DNS queries. -use std::{collections::BTreeMap, num::NonZeroUsize, path::Path, sync::Arc}; +use std::{collections::BTreeMap, num::NonZeroUsize, path::Path, sync::Arc, time::Duration}; use anyhow::Result; use hickory_proto::rr::{Name, RecordSet, RecordType, RrKey}; @@ -8,6 +8,7 @@ use iroh_metrics::inc; use lru::LruCache; use parking_lot::Mutex; use pkarr::{PkarrClient, SignedPacket}; +use ttl_cache::TtlCache; use crate::{ metrics::Metrics, @@ -75,6 +76,7 @@ impl ZoneStore { name: &Name, record_type: RecordType, ) -> Result>> { + tracing::info!("{} {}", name, record_type); if let Some(rset) = self.cache.lock().resolve(pubkey, name, record_type) { return Ok(Some(rset)); } @@ -88,13 +90,17 @@ impl ZoneStore { if let Some(pkarr) = self.pkarr.as_ref() { let key = pkarr::PublicKey::try_from(*pubkey.as_bytes()).expect("valid public key"); - // use the more expensive `resolve_most_recent` here. It's just once + // use the more expensive `resolve_most_recent` here. + // + // it will be cached for some time. + tracing::info!("pkarr resolve {}", key.to_z32()); let packet_opt = pkarr.as_ref().resolve_most_recent(key).await; + tracing::info!("pkarr resolve done {:?}", packet_opt); if let Some(packet) = packet_opt { return self .cache .lock() - .insert_and_resolve(&packet, name, record_type); + .insert_and_resolve_dht(&packet, name, record_type); } } Ok(None) @@ -126,15 +132,21 @@ impl ZoneStore { } } -#[derive(Debug)] +#[derive(derive_more::Debug)] struct ZoneCache { + /// Cache for explicitly added entries cache: LruCache, + /// Cache for DHT entries, this must have a finite TTL + /// so we don't cache stale entries indefinitely. + #[debug("dht_cache")] + dht_cache: TtlCache, } impl ZoneCache { fn new(cap: usize) -> Self { let cache = LruCache::new(NonZeroUsize::new(cap).expect("capacity must be larger than 0")); - Self { cache } + let dht_cache = TtlCache::new(cap); + Self { cache, dht_cache } } fn resolve( @@ -143,9 +155,17 @@ impl ZoneCache { name: &Name, record_type: RecordType, ) -> Option> { - self.cache - .get(pubkey) - .and_then(|zone| zone.resolve(name, record_type)) + if let Some(zone) = self.cache.get(pubkey) { + if let Some(rset) = zone.resolve(name, record_type) { + return Some(rset); + } + }; + if let Some(zone) = self.dht_cache.get(pubkey) { + if let Some(rset) = zone.resolve(name, record_type) { + return Some(rset); + } + }; + None } fn insert_and_resolve( @@ -159,6 +179,19 @@ impl ZoneCache { Ok(self.resolve(&pubkey, name, record_type)) } + fn insert_and_resolve_dht( + &mut self, + signed_packet: &SignedPacket, + name: &Name, + record_type: RecordType, + ) -> Result>> { + let pubkey = PublicKeyBytes::from_signed_packet(signed_packet); + let cached_zone = CachedZone::from_signed_packet(signed_packet)?; + self.dht_cache + .insert(pubkey, cached_zone, Duration::from_secs(300)); + Ok(self.resolve(&pubkey, name, record_type)) + } + fn insert(&mut self, signed_packet: &SignedPacket) -> Result<()> { let pubkey = PublicKeyBytes::from_signed_packet(signed_packet); if self @@ -176,6 +209,7 @@ impl ZoneCache { fn remove(&mut self, pubkey: &PublicKeyBytes) { self.cache.pop(pubkey); + self.dht_cache.remove(pubkey); } } diff --git a/iroh-dns-server/src/util.rs b/iroh-dns-server/src/util.rs index 0b64f34b2e..b2f440b1b0 100644 --- a/iroh-dns-server/src/util.rs +++ b/iroh-dns-server/src/util.rs @@ -16,7 +16,9 @@ use hickory_proto::{ }; use pkarr::SignedPacket; -#[derive(derive_more::From, derive_more::Into, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive( + derive_more::From, derive_more::Into, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, +)] pub struct PublicKeyBytes([u8; 32]); impl PublicKeyBytes { From 71a4b356597b6bf16182fd5c9145523f51c23cd8 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Mon, 15 Apr 2024 19:08:13 +0300 Subject: [PATCH 04/12] WIP debugging --- iroh-dns-server/src/store.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/iroh-dns-server/src/store.rs b/iroh-dns-server/src/store.rs index eb43d05432..a34f537c45 100644 --- a/iroh-dns-server/src/store.rs +++ b/iroh-dns-server/src/store.rs @@ -161,7 +161,10 @@ impl ZoneCache { } }; if let Some(zone) = self.dht_cache.get(pubkey) { + tracing::info!("got hit from DHT cache {:?}", pubkey); + tracing::info!("calling zone resolve {} {}", name, record_type); if let Some(rset) = zone.resolve(name, record_type) { + tracing::info!("got asnwer {:?}", rset); return Some(rset); } }; @@ -187,6 +190,7 @@ impl ZoneCache { ) -> Result>> { let pubkey = PublicKeyBytes::from_signed_packet(signed_packet); let cached_zone = CachedZone::from_signed_packet(signed_packet)?; + tracing::info!("cached_zone {:?}", cached_zone); self.dht_cache .insert(pubkey, cached_zone, Duration::from_secs(300)); Ok(self.resolve(&pubkey, name, record_type)) @@ -235,6 +239,9 @@ impl CachedZone { fn resolve(&self, name: &Name, record_type: RecordType) -> Option> { let key = RrKey::new(name.into(), record_type); + for record in self.records.keys() { + tracing::info!("record {:?}", record); + } self.records.get(&key).cloned() } } From 77491013e8a970c2bef6a76d51b9c9a71b9de5df Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 16 Apr 2024 13:02:24 +0300 Subject: [PATCH 05/12] nicer init --- iroh-dns-server/src/server.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/iroh-dns-server/src/server.rs b/iroh-dns-server/src/server.rs index 45857803d3..1e8df93f11 100644 --- a/iroh-dns-server/src/server.rs +++ b/iroh-dns-server/src/server.rs @@ -1,10 +1,6 @@ //! The main server which combines the DNS and HTTP(S) servers. - -use std::sync::Arc; - use anyhow::Result; use iroh_metrics::metrics::start_metrics_server; -use pkarr::PkarrClient; use tracing::info; use crate::{ @@ -19,7 +15,7 @@ use crate::{ pub async fn run_with_config_until_ctrl_c(config: Config) -> Result<()> { let mut store = ZoneStore::persistent(Config::signed_packet_store_path()?)?; if config.dht_fallback { - store = store.with_pkarr(Some(Arc::new(PkarrClient::default()))); + store = store.with_pkarr(Some(Default::default())); }; let server = Server::spawn(config, store).await?; tokio::signal::ctrl_c().await?; From af2a9a2e80d20ea532db892b080ece297d23f967 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 16 Apr 2024 14:24:03 +0300 Subject: [PATCH 06/12] better logging --- iroh-dns-server/src/store.rs | 41 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/iroh-dns-server/src/store.rs b/iroh-dns-server/src/store.rs index a34f537c45..93c44d6c5f 100644 --- a/iroh-dns-server/src/store.rs +++ b/iroh-dns-server/src/store.rs @@ -8,6 +8,7 @@ use iroh_metrics::inc; use lru::LruCache; use parking_lot::Mutex; use pkarr::{PkarrClient, SignedPacket}; +use tracing::{debug, trace}; use ttl_cache::TtlCache; use crate::{ @@ -21,6 +22,8 @@ mod signed_packets; /// Cache up to 1 million pkarr zones by default pub const DEFAULT_CACHE_CAPACITY: usize = 1024 * 1024; +/// Default TTL for DHT cache entries +pub const DHT_CACHE_TTL: Duration = Duration::from_secs(300); /// Where a new pkarr packet comes from pub enum PacketSource { @@ -68,7 +71,6 @@ impl ZoneStore { } /// Resolve a DNS query. - // allow unused async: this will be async soon. #[allow(clippy::unused_async)] pub async fn resolve( &self, @@ -93,14 +95,16 @@ impl ZoneStore { // use the more expensive `resolve_most_recent` here. // // it will be cached for some time. - tracing::info!("pkarr resolve {}", key.to_z32()); + debug!("DHT resolve {}", key.to_z32()); let packet_opt = pkarr.as_ref().resolve_most_recent(key).await; - tracing::info!("pkarr resolve done {:?}", packet_opt); if let Some(packet) = packet_opt { + debug!("DHT resolve successful {:?}", packet.packet()); return self .cache .lock() .insert_and_resolve_dht(&packet, name, record_type); + } else { + debug!("DHT resolve failed"); } } Ok(None) @@ -155,20 +159,16 @@ impl ZoneCache { name: &Name, record_type: RecordType, ) -> Option> { - if let Some(zone) = self.cache.get(pubkey) { - if let Some(rset) = zone.resolve(name, record_type) { - return Some(rset); - } - }; - if let Some(zone) = self.dht_cache.get(pubkey) { - tracing::info!("got hit from DHT cache {:?}", pubkey); - tracing::info!("calling zone resolve {} {}", name, record_type); - if let Some(rset) = zone.resolve(name, record_type) { - tracing::info!("got asnwer {:?}", rset); - return Some(rset); - } + let zone = if let Some(zone) = self.cache.get(pubkey) { + trace!("cache hit {}", pubkey.to_z32()); + zone + } else if let Some(zone) = self.dht_cache.get(pubkey) { + trace!("dht cache hit {}", pubkey.to_z32()); + zone + } else { + return None; }; - None + zone.resolve(name, record_type) } fn insert_and_resolve( @@ -189,11 +189,10 @@ impl ZoneCache { record_type: RecordType, ) -> Result>> { let pubkey = PublicKeyBytes::from_signed_packet(signed_packet); - let cached_zone = CachedZone::from_signed_packet(signed_packet)?; - tracing::info!("cached_zone {:?}", cached_zone); - self.dht_cache - .insert(pubkey, cached_zone, Duration::from_secs(300)); - Ok(self.resolve(&pubkey, name, record_type)) + let zone = CachedZone::from_signed_packet(signed_packet)?; + let res = zone.resolve(name, record_type); + self.dht_cache.insert(pubkey, zone, DHT_CACHE_TTL); + Ok(res) } fn insert(&mut self, signed_packet: &SignedPacket) -> Result<()> { From 5d008dee28417c1d60b20d8a46bbccfde5473ed8 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 16 Apr 2024 14:28:17 +0300 Subject: [PATCH 07/12] adjust config files --- iroh-dns-server/config.dev.toml | 2 ++ iroh-dns-server/config.prod.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/iroh-dns-server/config.dev.toml b/iroh-dns-server/config.dev.toml index 80db595573..ef283d928b 100644 --- a/iroh-dns-server/config.dev.toml +++ b/iroh-dns-server/config.dev.toml @@ -16,3 +16,5 @@ default_ttl = 900 origins = ["irohdns.example.", "."] rr_a = "127.0.0.1" rr_ns = "ns1.irohdns.example." + +dht_fallback = false diff --git a/iroh-dns-server/config.prod.toml b/iroh-dns-server/config.prod.toml index 8dde5fb6ba..8f11e84369 100644 --- a/iroh-dns-server/config.prod.toml +++ b/iroh-dns-server/config.prod.toml @@ -11,3 +11,5 @@ default_ttl = 30 origins = ["irohdns.example.org", "."] rr_a = "203.0.10.10" rr_ns = "ns1.irohdns.example.org." + +dht_fallback = false From a21336286bb8f96170a2369689e45e75a58f382c Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 16 Apr 2024 14:46:01 +0300 Subject: [PATCH 08/12] make mainline a separate config section --- iroh-dns-server/config.dev.toml | 3 ++- iroh-dns-server/config.prod.toml | 3 ++- iroh-dns-server/src/config.rs | 27 ++++++++++++++++++++++++--- iroh-dns-server/src/main.rs | 2 ++ iroh-dns-server/src/server.rs | 5 +++-- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/iroh-dns-server/config.dev.toml b/iroh-dns-server/config.dev.toml index ef283d928b..a43b10364a 100644 --- a/iroh-dns-server/config.dev.toml +++ b/iroh-dns-server/config.dev.toml @@ -17,4 +17,5 @@ origins = ["irohdns.example.", "."] rr_a = "127.0.0.1" rr_ns = "ns1.irohdns.example." -dht_fallback = false +[mainline] +enabled = true diff --git a/iroh-dns-server/config.prod.toml b/iroh-dns-server/config.prod.toml index 8f11e84369..64b3f88f67 100644 --- a/iroh-dns-server/config.prod.toml +++ b/iroh-dns-server/config.prod.toml @@ -12,4 +12,5 @@ origins = ["irohdns.example.org", "."] rr_a = "203.0.10.10" rr_ns = "ns1.irohdns.example.org." -dht_fallback = false +[mainline] +enabled = false diff --git a/iroh-dns-server/src/config.rs b/iroh-dns-server/src/config.rs index 2d8d34f1f0..1041f62a72 100644 --- a/iroh-dns-server/src/config.rs +++ b/iroh-dns-server/src/config.rs @@ -41,8 +41,8 @@ pub struct Config { /// `Some(MetricsConfig::disabled())`. pub metrics: Option, - /// Fall back to the dht for resolution. - pub dht_fallback: bool, + /// Config for the mainline lookup. + pub mainline: Option, } /// The config for the metrics server. @@ -64,6 +64,20 @@ impl MetricsConfig { } } +/// The config for the metrics server. +#[derive(Debug, Serialize, Deserialize)] +pub struct MainlineConfig { + /// Set to true to enable the mainline lookup. + pub enabled: bool, +} + +#[allow(clippy::derivable_impls)] +impl Default for MainlineConfig { + fn default() -> Self { + Self { enabled: false } + } +} + impl Config { /// Load the config from a file. pub async fn load(path: impl AsRef) -> Result { @@ -106,6 +120,13 @@ impl Config { }, } } + + pub(crate) fn mainline_enabled(&self) -> bool { + self.mainline + .as_ref() + .map(|x| x.enabled) + .unwrap_or_default() + } } impl Default for Config { @@ -137,7 +158,7 @@ impl Default for Config { rr_ns: Some("ns1.irohdns.example.".to_string()), }, metrics: None, - dht_fallback: true, + mainline: None, } } } diff --git a/iroh-dns-server/src/main.rs b/iroh-dns-server/src/main.rs index 6a7f88d673..64a0529be0 100644 --- a/iroh-dns-server/src/main.rs +++ b/iroh-dns-server/src/main.rs @@ -26,8 +26,10 @@ async fn main() -> Result<()> { let args = Cli::parse(); let config = if let Some(path) = args.config { + debug!("loading config from {:?}", path); Config::load(path).await? } else { + debug!("using default config"); Config::default() }; diff --git a/iroh-dns-server/src/server.rs b/iroh-dns-server/src/server.rs index 1e8df93f11..0669cb6f48 100644 --- a/iroh-dns-server/src/server.rs +++ b/iroh-dns-server/src/server.rs @@ -14,8 +14,9 @@ use crate::{ /// Spawn the server and run until the `Ctrl-C` signal is received, then shutdown. pub async fn run_with_config_until_ctrl_c(config: Config) -> Result<()> { let mut store = ZoneStore::persistent(Config::signed_packet_store_path()?)?; - if config.dht_fallback { - store = store.with_pkarr(Some(Default::default())); + if config.mainline_enabled() { + info!("mainline fallback enabled"); + store = store.with_pkarr(Default::default()); }; let server = Server::spawn(config, store).await?; tokio::signal::ctrl_c().await?; From a6cb17209ced088dd60e0b5ddcfa060cbbed9449 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 16 Apr 2024 15:08:53 +0300 Subject: [PATCH 09/12] shut up clippy --- iroh-dns-server/src/util.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iroh-dns-server/src/util.rs b/iroh-dns-server/src/util.rs index b2f440b1b0..23740366bc 100644 --- a/iroh-dns-server/src/util.rs +++ b/iroh-dns-server/src/util.rs @@ -28,11 +28,11 @@ impl PublicKeyBytes { Ok(Self(bytes)) } - pub fn to_z32(&self) -> String { + pub fn to_z32(self) -> String { z32::encode(&self.0) } - pub fn to_bytes(&self) -> [u8; 32] { + pub fn to_bytes(self) -> [u8; 32] { self.0 } From c73d6a09bc80fbec0d93bc5c99236173e2c2e80b Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 16 Apr 2024 16:50:51 +0300 Subject: [PATCH 10/12] update pkarr --- Cargo.lock | 4 ++-- iroh-dns-server/Cargo.toml | 2 +- iroh-net/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d31f296836..97f11c2a8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3497,9 +3497,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad73bf3327d38c904c81d3baeabff147a7eb77dccb78aae95c738c12e2e0abe" +checksum = "242ae92dfb9d2ba3aaa9caf4723e72043bc50729ad05a763771771ba03196ffb" dependencies = [ "bytes", "ed25519-dalek", diff --git a/iroh-dns-server/Cargo.toml b/iroh-dns-server/Cargo.toml index 2f50316d64..8aed68b64d 100644 --- a/iroh-dns-server/Cargo.toml +++ b/iroh-dns-server/Cargo.toml @@ -27,7 +27,7 @@ http = "1.0.0" iroh-metrics = { version = "0.14.0", path = "../iroh-metrics" } lru = "0.12.3" parking_lot = "0.12.1" -pkarr = { version = "1.1.2", features = [ "async", "relay", "dht"], default_features = false } +pkarr = { version = "1.1.4", features = [ "async", "relay", "dht"], default_features = false } rcgen = "0.12.1" redb = "2.0.0" regex = "1.10.3" diff --git a/iroh-net/Cargo.toml b/iroh-net/Cargo.toml index c68203b095..09734481e5 100644 --- a/iroh-net/Cargo.toml +++ b/iroh-net/Cargo.toml @@ -40,7 +40,7 @@ libc = "0.2.139" num_enum = "0.7" once_cell = "1.18.0" parking_lot = "0.12.1" -pkarr = { version = "1.1.3", default-features = false, features = ["async", "relay"] } +pkarr = { version = "1.1.4", default-features = false, features = ["async", "relay"] } postcard = { version = "1", default-features = false, features = ["alloc", "use-std", "experimental-derive"] } quinn = "0.10" quinn-proto = "0.10.5" From 17ff6f65f14fddcb7843fba9b40345bfbdb53a95 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Mon, 29 Apr 2024 12:08:15 +0300 Subject: [PATCH 11/12] PR review: better docs for with_pkarr --- iroh-dns-server/src/store.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/iroh-dns-server/src/store.rs b/iroh-dns-server/src/store.rs index 93c44d6c5f..7c7ede9f7e 100644 --- a/iroh-dns-server/src/store.rs +++ b/iroh-dns-server/src/store.rs @@ -55,7 +55,10 @@ impl ZoneStore { Ok(Self::new(packet_store)) } - /// Set pkarr client for fallback resolution. + /// Configure a pkarr client for resolution of packets from the bittorrent + /// mainline DHT. + /// + /// This will be used only as a fallback if there is no local info available. pub fn with_pkarr(self, pkarr: Option>) -> Self { Self { pkarr, ..self } } From 64d4d6a387fd8e968c58e2b4e4b31c4ed4a7f163 Mon Sep 17 00:00:00 2001 From: Franz Heinzmann Date: Mon, 29 Apr 2024 13:20:31 +0200 Subject: [PATCH 12/12] test: add test for iroh-dns-server with mainline (#2250) ## Description This adds a test for iroh-dns-server with mainline fallback resolution. Needed to rework the config structure a little to not duplicate code too much. ## Breaking Changes are documented in #2180 ## Notes & open questions ## Change checklist - [x] Self-review. - [x] Documentation updates if relevant. - [x] Tests if relevant. - [x] All breaking changes documented. --- Cargo.lock | 1 + iroh-dns-server/Cargo.toml | 1 + iroh-dns-server/src/config.rs | 38 ++++++++++++++++++++++++++----- iroh-dns-server/src/lib.rs | 43 +++++++++++++++++++++++++++++++++-- iroh-dns-server/src/server.rs | 20 +++++++++++++--- iroh-dns-server/src/store.rs | 20 ++++++++++++---- 6 files changed, 108 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bf1cb5c85..c57fe9f62b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2521,6 +2521,7 @@ dependencies = [ "iroh-net", "iroh-test", "lru", + "mainline", "parking_lot", "pkarr", "rcgen 0.12.1", diff --git a/iroh-dns-server/Cargo.toml b/iroh-dns-server/Cargo.toml index 0fb9b2a1d5..4a14e7095d 100644 --- a/iroh-dns-server/Cargo.toml +++ b/iroh-dns-server/Cargo.toml @@ -54,3 +54,4 @@ z32 = "1.1.1" hickory-resolver = "0.24.0" iroh-net = { version = "0.14.0", path = "../iroh-net" } iroh-test = { path = "../iroh-test" } +mainline = "<1.5.0" diff --git a/iroh-dns-server/src/config.rs b/iroh-dns-server/src/config.rs index 1041f62a72..89222f9daf 100644 --- a/iroh-dns-server/src/config.rs +++ b/iroh-dns-server/src/config.rs @@ -69,12 +69,31 @@ impl MetricsConfig { pub struct MainlineConfig { /// Set to true to enable the mainline lookup. pub enabled: bool, + /// Set custom bootstrap nodes. + /// + /// Addresses can either be `domain:port` or `ipv4:port`. + /// + /// If empty this will use the default bittorrent mainline bootstrap nodes as defined by pkarr. + pub bootstrap: Option>, +} + +/// Configure the bootstrap servers for mainline DHT resolution. +#[derive(Debug, Serialize, Deserialize, Default)] +pub enum BootstrapOption { + /// Use the default bootstrap servers. + #[default] + Default, + /// Use custom bootstrap servers. + Custom(Vec), } #[allow(clippy::derivable_impls)] impl Default for MainlineConfig { fn default() -> Self { - Self { enabled: false } + Self { + enabled: false, + bootstrap: None, + } } } @@ -121,11 +140,18 @@ impl Config { } } - pub(crate) fn mainline_enabled(&self) -> bool { - self.mainline - .as_ref() - .map(|x| x.enabled) - .unwrap_or_default() + pub(crate) fn mainline_enabled(&self) -> Option { + match self.mainline.as_ref() { + None => None, + Some(MainlineConfig { enabled: false, .. }) => None, + Some(MainlineConfig { + bootstrap: Some(bootstrap), + .. + }) => Some(BootstrapOption::Custom(bootstrap.clone())), + Some(MainlineConfig { + bootstrap: None, .. + }) => Some(BootstrapOption::Default), + } } } diff --git a/iroh-dns-server/src/lib.rs b/iroh-dns-server/src/lib.rs index bb62a969e9..95e09bfc1e 100644 --- a/iroh-dns-server/src/lib.rs +++ b/iroh-dns-server/src/lib.rs @@ -28,10 +28,10 @@ mod tests { }, key::SecretKey, }; - use pkarr::SignedPacket; + use pkarr::{PkarrClient, SignedPacket}; use url::Url; - use crate::server::Server; + use crate::{config::BootstrapOption, server::Server}; #[tokio::test] async fn pkarr_publish_dns_resolve() -> Result<()> { @@ -177,6 +177,45 @@ mod tests { Ok(()) } + #[tokio::test] + async fn integration_mainline() -> Result<()> { + iroh_test::logging::setup_multithreaded(); + + // run a mainline testnet + let testnet = mainline::dht::Testnet::new(5); + let bootstrap = testnet.bootstrap.clone(); + + // spawn our server with mainline support + let (server, nameserver, _http_url) = + Server::spawn_for_tests_with_mainline(Some(BootstrapOption::Custom(bootstrap))).await?; + + let origin = "irohdns.example."; + + // create a signed packet + let secret_key = SecretKey::generate(); + let node_id = secret_key.public(); + let relay_url: Url = "https://relay.example.".parse()?; + let node_info = NodeInfo::new(node_id, Some(relay_url.clone()), Default::default()); + let signed_packet = node_info.to_pkarr_signed_packet(&secret_key, 30)?; + + // publish the signed packet to our DHT + let pkarr = PkarrClient::builder().bootstrap(&testnet.bootstrap).build(); + pkarr.publish(&signed_packet).await?; + + // resolve via DNS from our server, which will lookup from our DHT + let resolver = test_resolver(nameserver); + let res = lookup_by_id(&resolver, &node_id, origin).await?; + + assert_eq!(res.node_id, node_id); + assert_eq!(res.info.relay_url.map(Url::from), Some(relay_url)); + + server.shutdown().await?; + for node in testnet.nodes { + node.shutdown(); + } + Ok(()) + } + fn test_resolver(nameserver: SocketAddr) -> DnsResolver { let mut config = ResolverConfig::new(); let nameserver_config = NameServerConfig::new(nameserver, Protocol::Udp); diff --git a/iroh-dns-server/src/server.rs b/iroh-dns-server/src/server.rs index 0669cb6f48..c9580fa121 100644 --- a/iroh-dns-server/src/server.rs +++ b/iroh-dns-server/src/server.rs @@ -1,4 +1,5 @@ //! The main server which combines the DNS and HTTP(S) servers. + use anyhow::Result; use iroh_metrics::metrics::start_metrics_server; use tracing::info; @@ -14,9 +15,9 @@ use crate::{ /// Spawn the server and run until the `Ctrl-C` signal is received, then shutdown. pub async fn run_with_config_until_ctrl_c(config: Config) -> Result<()> { let mut store = ZoneStore::persistent(Config::signed_packet_store_path()?)?; - if config.mainline_enabled() { + if let Some(bootstrap) = config.mainline_enabled() { info!("mainline fallback enabled"); - store = store.with_pkarr(Default::default()); + store = store.with_mainline_fallback(bootstrap); }; let server = Server::spawn(config, store).await?; tokio::signal::ctrl_c().await?; @@ -89,6 +90,15 @@ impl Server { /// HTTP server. #[cfg(test)] pub async fn spawn_for_tests() -> Result<(Self, std::net::SocketAddr, url::Url)> { + Self::spawn_for_tests_with_mainline(None).await + } + + /// Spawn a server suitable for testing, while optionally enabling mainline with custom + /// bootstrap addresses. + #[cfg(test)] + pub async fn spawn_for_tests_with_mainline( + mainline: Option, + ) -> Result<(Self, std::net::SocketAddr, url::Url)> { use crate::config::MetricsConfig; use std::net::{IpAddr, Ipv4Addr}; @@ -100,7 +110,11 @@ impl Server { config.https = None; config.metrics = Some(MetricsConfig::disabled()); - let store = ZoneStore::in_memory()?; + let mut store = ZoneStore::in_memory()?; + if let Some(bootstrap) = mainline { + info!("mainline fallback enabled"); + store = store.with_mainline_fallback(bootstrap); + } let server = Self::spawn(config, store).await?; let dns_addr = server.dns_server.local_addr(); let http_addr = server.http_server.http_addr().expect("http is set"); diff --git a/iroh-dns-server/src/store.rs b/iroh-dns-server/src/store.rs index 7c7ede9f7e..dd8d911911 100644 --- a/iroh-dns-server/src/store.rs +++ b/iroh-dns-server/src/store.rs @@ -12,6 +12,7 @@ use tracing::{debug, trace}; use ttl_cache::TtlCache; use crate::{ + config::BootstrapOption, metrics::Metrics, util::{signed_packet_to_hickory_records_without_origin, PublicKeyBytes}, }; @@ -55,12 +56,23 @@ impl ZoneStore { Ok(Self::new(packet_store)) } - /// Configure a pkarr client for resolution of packets from the bittorrent - /// mainline DHT. + /// Configure a pkarr client for resolution of packets from the bittorent mainline DHT. /// /// This will be used only as a fallback if there is no local info available. - pub fn with_pkarr(self, pkarr: Option>) -> Self { - Self { pkarr, ..self } + /// + /// Optionally set custom bootstrap nodes. If `bootstrap` is empty it will use the default + /// mainline bootstrap nodes. + pub fn with_mainline_fallback(self, bootstrap: BootstrapOption) -> Self { + let pkarr_client = match bootstrap { + BootstrapOption::Default => PkarrClient::default(), + BootstrapOption::Custom(bootstrap) => { + PkarrClient::builder().bootstrap(&bootstrap).build() + } + }; + Self { + pkarr: Some(Arc::new(pkarr_client)), + ..self + } } /// Create a new zone store.