use std::net::SocketAddr; use clap::{Parser, ValueEnum}; use hickory_resolver::{ config::{NameServerConfig, Protocol, ResolverConfig}, AsyncResolver, }; use iroh_net::{ discovery::dns::{N0_DNS_NODE_ORIGIN_PROD, N0_DNS_NODE_ORIGIN_STAGING}, dns::{node_info::TxtAttrs, DnsResolver}, NodeId, }; const LOCALHOST_DNS: &str = "127.0.0.1:5300"; const EXAMPLE_ORIGIN: &str = "irohdns.example"; #[derive(ValueEnum, Clone, Debug, Default)] pub enum Env { /// Use the system's nameservers with origin domain of the n0 staging DNS server #[default] Staging, /// Use the system's nameservers with origin domain of the n0 production DNS server Prod, /// Use a localhost DNS server listening on port 5300 Dev, } #[derive(Debug, Parser)] struct Cli { #[clap(value_enum, short, long, default_value_t = Env::Staging)] env: Env, #[clap(subcommand)] command: Command, } #[derive(Debug, Parser)] enum Command { /// Resolve node info by node id. Node { node_id: NodeId }, /// Resolve node info by domain. Domain { domain: String }, } #[tokio::main] async fn main() -> anyhow::Result<()> { let args = Cli::parse(); let (resolver, origin) = match args.env { Env::Staging => ( iroh_net::dns::default_resolver().clone(), N0_DNS_NODE_ORIGIN_STAGING, ), Env::Prod => ( iroh_net::dns::default_resolver().clone(), N0_DNS_NODE_ORIGIN_PROD, ), Env::Dev => ( resolver_with_nameserver(LOCALHOST_DNS.parse()?), EXAMPLE_ORIGIN, ), }; let resolved = match args.command { Command::Node { node_id } => { TxtAttrs::::lookup_by_id(&resolver, &node_id, origin).await? } Command::Domain { domain } => { TxtAttrs::::lookup_by_name(&resolver, &domain).await? } }; println!("resolved node {}", resolved.node_id()); for (key, values) in resolved.attrs() { for value in values { println!(" {key}={value}"); } } Ok(()) } fn resolver_with_nameserver(nameserver: SocketAddr) -> DnsResolver { let mut config = ResolverConfig::new(); let nameserver_config = NameServerConfig::new(nameserver, Protocol::Udp); config.add_name_server(nameserver_config); AsyncResolver::tokio(config, Default::default()) }