From 5edf22de424823de095cd50ef22b692452975967 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Tue, 14 May 2024 14:22:38 +0200 Subject: [PATCH 1/3] docs(iroh-net): Minor tweaks in the public iroh_net::dns module This is a bunch of entirely too much nitpcking on the dns module's documentation, as this is all public: - Describe clearly what this is for and how it works. - Summary line is a single full sentence ending in a full stop. - Summary line is in 3rd person. --- iroh-net/src/dns/node_info.rs | 115 ++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 41 deletions(-) diff --git a/iroh-net/src/dns/node_info.rs b/iroh-net/src/dns/node_info.rs index b05ee734f1..48597de99e 100644 --- a/iroh-net/src/dns/node_info.rs +++ b/iroh-net/src/dns/node_info.rs @@ -1,5 +1,33 @@ -//! This module contains functions and structs to lookup node information from DNS -//! and to encode node information in Pkarr signed packets. +//! Support for handling DNS resource records for dialing by [`NodeId`]. +//! +//! Dialing by [`NodeId`] is supported by iroh nodes publishing [Pkarr] records to DNS +//! servers or the Mainline DHT. This module supports creating and parsing these records. +//! +//! DNS records are published under the following names: +//! +//! `_iroh.. TXT` +//! +//! - `_iroh` is the record name as defined by [`IROH_TXT_NAME`]. +//! +//! - `` is the [z-base-32] encoding of the [`NodeId`]. +//! +//! - `` is the domain name of the publishing DNS server, `dns.iroh.link` is +//! operated by number0. +//! +//! - `TXT` is the DNS record type. +//! +//! The returned TXT records must contain a string value of the form `key=value` as defined +//! in [RFC1464]. The following attributes are defined: +//! +//! - `relay=`: The home [`RelayUrl`] of this node. +//! +//! - `addr= `: A space-separated list of sockets addresses for this iroh node. +//! Each address is an IPv4 or IPv6 address with a port. +//! +//! [Pkarr]: https://app.pkarr.org +//! [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt +//! [RFC1464]: https://www.rfc-editor.org/rfc/rfc1464 +//! [`RelayUrl`]: iroh_base::node_addr::RelayUrl use std::{ collections::{BTreeMap, BTreeSet}, @@ -16,32 +44,34 @@ use url::Url; use crate::{key::SecretKey, AddrInfo, NodeAddr, NodeId}; -/// The DNS name for the iroh TXT record +/// The DNS name for the iroh TXT record. pub const IROH_TXT_NAME: &str = "_iroh"; -/// The attributes supported by iroh for `_iroh` DNS records +/// The attributes supported by iroh for `_iroh` DNS resource records. +/// +/// The resource record uses the lower-case names. #[derive( Debug, strum::Display, strum::AsRefStr, strum::EnumString, Hash, Eq, PartialEq, Ord, PartialOrd, )] #[strum(serialize_all = "kebab-case")] pub enum IrohAttr { - /// `relay`: URL of home relay + /// URL of home relay. Relay, - /// `addr`: Direct address + /// Direct address. Addr, } -/// Lookup node info by domain name +/// Looks up node info by DNS name. /// -/// The domain name must either contain an _iroh TXT record or be a CNAME record that leads to -/// an _iroh TXT record. -pub async fn lookup_by_domain(resolver: &TokioAsyncResolver, domain: &str) -> Result { - let attrs = TxtAttrs::::lookup_by_domain(resolver, domain).await?; +/// The resource records returned for `name` must either contain an `_iroh` TXT record or be +/// a CNAME record that leads to an `_iroh` TXT record. +pub async fn lookup_by_domain(resolver: &TokioAsyncResolver, name: &str) -> Result { + let attrs = TxtAttrs::::lookup_by_domain(resolver, name).await?; let info: NodeInfo = attrs.into(); Ok(info.into()) } -/// Lookup node info by node id and origin domain name. +/// Looks up node info by [`NodeId`] and origin domain name. pub async fn lookup_by_id( resolver: &TokioAsyncResolver, node_id: &NodeId, @@ -52,14 +82,14 @@ pub async fn lookup_by_id( Ok(info.into()) } -/// Encode a [`NodeId`] in [`z-base-32`] encoding. +/// Encodes a [`NodeId`] in [`z-base-32`] encoding. /// /// [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt pub fn to_z32(node_id: &NodeId) -> String { z32::encode(node_id.as_bytes()) } -/// Parse a [`NodeId`] from [`z-base-32`] encoding. +/// Parses a [`NodeId`] from [`z-base-32`] encoding. /// /// [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt pub fn from_z32(s: &str) -> Result { @@ -69,15 +99,15 @@ pub fn from_z32(s: &str) -> Result { Ok(node_id) } -/// Node info contained in a DNS _iroh TXT record. +/// Information about and iroh node which is contained in an `_iroh` TXT resource record. #[derive(derive_more::Debug, Clone, Eq, PartialEq)] pub struct NodeInfo { - /// The node id + /// The [`NodeId`]. pub node_id: NodeId, - /// Home relay server for this node + /// The advertised home relay server. #[debug("{:?}", self.relay_url.as_ref().map(|s| s.to_string()))] pub relay_url: Option, - /// Direct addresses + /// Any direct addresses. pub direct_addresses: BTreeSet, } @@ -143,7 +173,7 @@ impl From for AddrInfo { } impl NodeInfo { - /// Create a new [`NodeInfo`] from its parts. + /// Creates a new [`NodeInfo`] from its parts. pub fn new( node_id: NodeId, relay_url: Option, @@ -160,20 +190,21 @@ impl NodeInfo { self.into() } - /// Try to parse a [`NodeInfo`] from a set of DNS records. + /// Parses a [`NodeInfo`] from a set of DNS records. pub fn from_hickory_records(records: &[hickory_proto::rr::Record]) -> Result { let attrs = TxtAttrs::from_hickory_records(records)?; Ok(attrs.into()) } - /// Try to parse a [`NodeInfo`] from a [`pkarr::SignedPacket`]. + /// Parses a [`NodeInfo`] from a [`pkarr::SignedPacket`]. pub fn from_pkarr_signed_packet(packet: &pkarr::SignedPacket) -> Result { let attrs = TxtAttrs::from_pkarr_signed_packet(packet)?; Ok(attrs.into()) } - /// Create a [`pkarr::SignedPacket`] by constructing a DNS packet and - /// signing it with a [`SecretKey`]. + /// Creates a [`pkarr::SignedPacket`]. + /// + /// This constructs a DNS packet and signs it with a [`SecretKey`]. pub fn to_pkarr_signed_packet( &self, secret_key: &SecretKey, @@ -182,7 +213,7 @@ impl NodeInfo { self.to_attrs().to_pkarr_signed_packet(secret_key, ttl) } - /// Convert into a [`hickory_proto::rr::Record`] DNS record. + /// Converts into a [`hickory_proto::rr::Record`] DNS record. pub fn to_hickory_records( &self, origin: &str, @@ -194,10 +225,10 @@ impl NodeInfo { } } -/// Parse a [`NodeId`] from iroh DNS name. +/// Parses a [`NodeId`] from iroh DNS name. /// /// Takes a [`hickory_proto::rr::Name`] DNS name and expects the first label to be `_iroh` -/// and the second label to be a z32 encoded [`NodeId`]. Does not care about subsequent labels. +/// and the second label to be a z32 encoded [`NodeId`]. Ignores subsequent labels. pub(crate) fn node_id_from_hickory_name(name: &hickory_proto::rr::Name) -> Option { if name.num_labels() < 2 { return None; @@ -214,8 +245,9 @@ pub(crate) fn node_id_from_hickory_name(name: &hickory_proto::rr::Name) -> Optio /// Attributes parsed from `_iroh` TXT records. /// -/// This struct is generic over the key type. When using with String, this will parse all -/// attributes. Can also be used with an enum, if it implements [`FromStr`] and [`Display`]. +/// This struct is generic over the key type. When using with [`String`], this will parse +/// all attributes. Can also be used with an enum, if it implements [`FromStr`] and +/// [`Display`]. #[derive(Debug)] pub struct TxtAttrs { node_id: NodeId, @@ -223,7 +255,7 @@ pub struct TxtAttrs { } impl TxtAttrs { - /// Create from a node id and an iterator of key-value pairs. + /// Creates [`TxtAttrs`] from a node id and an iterator of key-value pairs. pub fn from_parts(node_id: NodeId, pairs: impl Iterator) -> Self { let mut attrs: BTreeMap> = BTreeMap::new(); for (k, v) in pairs { @@ -232,7 +264,7 @@ impl TxtAttrs { Self { attrs, node_id } } - /// Create from a node id and an iterator of "{key}={value}" strings. + /// Creates [`TxtAttrs`] from a node id and an iterator of "{key}={value}" strings. pub fn from_strings(node_id: NodeId, strings: impl Iterator) -> Result { let mut attrs: BTreeMap> = BTreeMap::new(); for s in strings { @@ -255,7 +287,7 @@ impl TxtAttrs { Ok(attrs) } - /// Lookup attributes for a node id and origin domain. + /// Looks up attributes by [`NodeId`] and origin domain. pub async fn lookup_by_id( resolver: &TokioAsyncResolver, node_id: &NodeId, @@ -265,23 +297,23 @@ impl TxtAttrs { TxtAttrs::lookup(resolver, name).await } - /// Lookup attributes for a domain. - pub async fn lookup_by_domain(resolver: &TokioAsyncResolver, domain: &str) -> Result { - let name = Name::from_str(domain)?; + /// Looks up attributes by DNS name. + pub async fn lookup_by_domain(resolver: &TokioAsyncResolver, name: &str) -> Result { + let name = Name::from_str(name)?; TxtAttrs::lookup(resolver, name).await } - /// Get a reference to the parsed attributes. + /// Returns the parsed attributes. pub fn attrs(&self) -> &BTreeMap> { &self.attrs } - /// Get the node id. + /// Returns the node id. pub fn node_id(&self) -> NodeId { self.node_id } - /// Try to parse a from a [`pkarr::SignedPacket`]. + /// Parses a [`pkarr::SignedPacket`]. pub fn from_pkarr_signed_packet(packet: &pkarr::SignedPacket) -> Result { use pkarr::dns::{self, rdata::RData}; let pubkey = packet.public_key(); @@ -301,7 +333,7 @@ impl TxtAttrs { Self::from_strings(node_id, txt_strs) } - /// Try to parse a from a set of DNS records. + /// Parses a set of DNS resource records. pub fn from_hickory_records(records: &[hickory_proto::rr::Record]) -> Result { use hickory_proto::rr; let mut records = records.iter().filter_map(|rr| match rr.data() { @@ -328,7 +360,7 @@ impl TxtAttrs { .flat_map(move |(k, vs)| vs.iter().map(move |v| format!("{k}={v}"))) } - /// Convert into list of [`hickory_proto::rr::Record`]. + /// Converts to a list of [`hickory_proto::rr::Record`] resource records. pub fn to_hickory_records( &self, origin: &str, @@ -345,8 +377,9 @@ impl TxtAttrs { Ok(records) } - /// Create a [`pkarr::SignedPacket`] by constructing a DNS packet and - /// signing it with a [`SecretKey`]. + /// Creates a [`pkarr::SignedPacket`] + /// + /// This constructs a DNS packet and signs it with a [`SecretKey`]. pub fn to_pkarr_signed_packet( &self, secret_key: &SecretKey, From c092b4af2266080d5d9266da57f842ac9b194445 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Tue, 14 May 2024 17:39:34 +0200 Subject: [PATCH 2/3] fix typo Co-authored-by: Divma <26765164+divagant-martian@users.noreply.github.com> --- iroh-net/src/dns/node_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iroh-net/src/dns/node_info.rs b/iroh-net/src/dns/node_info.rs index 48597de99e..2b44b4c7d2 100644 --- a/iroh-net/src/dns/node_info.rs +++ b/iroh-net/src/dns/node_info.rs @@ -99,7 +99,7 @@ pub fn from_z32(s: &str) -> Result { Ok(node_id) } -/// Information about and iroh node which is contained in an `_iroh` TXT resource record. +/// Information about the iroh node which is contained in an `_iroh` TXT resource record. #[derive(derive_more::Debug, Clone, Eq, PartialEq)] pub struct NodeInfo { /// The [`NodeId`]. From d08493a21f4c7bbe42324f69929c63988ce1a296 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Tue, 14 May 2024 17:49:59 +0200 Subject: [PATCH 3/3] Use IROH_TXT_NAME and N0_DNS_NODE_ORIGIN constants where possible --- iroh-net/src/dns/node_info.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/iroh-net/src/dns/node_info.rs b/iroh-net/src/dns/node_info.rs index 2b44b4c7d2..4e34107674 100644 --- a/iroh-net/src/dns/node_info.rs +++ b/iroh-net/src/dns/node_info.rs @@ -11,8 +11,8 @@ //! //! - `` is the [z-base-32] encoding of the [`NodeId`]. //! -//! - `` is the domain name of the publishing DNS server, `dns.iroh.link` is -//! operated by number0. +//! - `` is the domain name of the publishing DNS server, +//! [`N0_DNS_NODE_ORIGIN`] is the server operated by number0. //! //! - `TXT` is the DNS record type. //! @@ -28,6 +28,7 @@ //! [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt //! [RFC1464]: https://www.rfc-editor.org/rfc/rfc1464 //! [`RelayUrl`]: iroh_base::node_addr::RelayUrl +//! [`N0_DNS_NODE_ORIGIN`]: crate::discovery::dns::N0_DNS_NODE_ORIGIN use std::{ collections::{BTreeMap, BTreeSet}, @@ -47,7 +48,7 @@ use crate::{key::SecretKey, AddrInfo, NodeAddr, NodeId}; /// The DNS name for the iroh TXT record. pub const IROH_TXT_NAME: &str = "_iroh"; -/// The attributes supported by iroh for `_iroh` DNS resource records. +/// The attributes supported by iroh for [`IROH_TXT_NAME`] DNS resource records. /// /// The resource record uses the lower-case names. #[derive( @@ -63,8 +64,8 @@ pub enum IrohAttr { /// Looks up node info by DNS name. /// -/// The resource records returned for `name` must either contain an `_iroh` TXT record or be -/// a CNAME record that leads to an `_iroh` TXT record. +/// The resource records returned for `name` must either contain an [`IROH_TXT_NAME`] TXT +/// record or be a CNAME record that leads to an [`IROH_TXT_NAME`] TXT record. pub async fn lookup_by_domain(resolver: &TokioAsyncResolver, name: &str) -> Result { let attrs = TxtAttrs::::lookup_by_domain(resolver, name).await?; let info: NodeInfo = attrs.into(); @@ -99,7 +100,7 @@ pub fn from_z32(s: &str) -> Result { Ok(node_id) } -/// Information about the iroh node which is contained in an `_iroh` TXT resource record. +/// Information about the iroh node contained in an [`IROH_TXT_NAME`] TXT resource record. #[derive(derive_more::Debug, Clone, Eq, PartialEq)] pub struct NodeInfo { /// The [`NodeId`]. @@ -227,8 +228,9 @@ impl NodeInfo { /// Parses a [`NodeId`] from iroh DNS name. /// -/// Takes a [`hickory_proto::rr::Name`] DNS name and expects the first label to be `_iroh` -/// and the second label to be a z32 encoded [`NodeId`]. Ignores subsequent labels. +/// Takes a [`hickory_proto::rr::Name`] DNS name and expects the first label to be +/// [`IROH_TXT_NAME`] and the second label to be a z32 encoded [`NodeId`]. Ignores +/// subsequent labels. pub(crate) fn node_id_from_hickory_name(name: &hickory_proto::rr::Name) -> Option { if name.num_labels() < 2 { return None; @@ -243,7 +245,7 @@ pub(crate) fn node_id_from_hickory_name(name: &hickory_proto::rr::Name) -> Optio Some(node_id) } -/// Attributes parsed from `_iroh` TXT records. +/// Attributes parsed from [`IROH_TXT_NAME`] TXT records. /// /// This struct is generic over the key type. When using with [`String`], this will parse /// all attributes. Can also be used with an enum, if it implements [`FromStr`] and