|
| 1 | +use std::str::FromStr; |
| 2 | + |
| 3 | +use anyhow::{bail, Result}; |
| 4 | +use clap::{Parser, ValueEnum}; |
| 5 | +use iroh_net::{ |
| 6 | + discovery::{ |
| 7 | + dns::N0_DNS_NODE_ORIGIN, |
| 8 | + pkarr_publish::{PkarrRelayClient, N0_DNS_PKARR_RELAY}, |
| 9 | + }, |
| 10 | + dns::node_info::{to_z32, NodeInfo, IROH_TXT_NAME}, |
| 11 | + key::SecretKey, |
| 12 | + NodeId, |
| 13 | +}; |
| 14 | +use url::Url; |
| 15 | + |
| 16 | +const LOCALHOST_PKARR: &str = "http://localhost:8080/pkarr"; |
| 17 | +const EXAMPLE_ORIGIN: &str = "irohdns.example"; |
| 18 | + |
| 19 | +#[derive(ValueEnum, Clone, Debug, Default, Copy, strum::Display)] |
| 20 | +#[strum(serialize_all = "kebab-case")] |
| 21 | +pub enum Env { |
| 22 | + /// Use the pkarr relay run by number0. |
| 23 | + #[default] |
| 24 | + Default, |
| 25 | + /// Use a relay listening at http://localhost:8080 |
| 26 | + Dev, |
| 27 | +} |
| 28 | + |
| 29 | +/// Publish a record to an irohdns server. |
| 30 | +/// |
| 31 | +/// You have to set the IROH_SECRET environment variable to the node secret for which to publish. |
| 32 | +#[derive(Parser, Debug)] |
| 33 | +struct Cli { |
| 34 | + /// Environment to publish to. |
| 35 | + #[clap(value_enum, short, long, default_value_t = Env::Default)] |
| 36 | + env: Env, |
| 37 | + /// Pkarr Relay URL. If set, the --env option will be ignored. |
| 38 | + #[clap(long, conflicts_with = "env")] |
| 39 | + pkarr_relay: Option<Url>, |
| 40 | + /// Home relay server to publish for this node |
| 41 | + relay_url: Url, |
| 42 | + /// Create a new node secret if IROH_SECRET is unset. Only for development / debugging. |
| 43 | + #[clap(short, long)] |
| 44 | + create: bool, |
| 45 | +} |
| 46 | + |
| 47 | +#[tokio::main] |
| 48 | +async fn main() -> Result<()> { |
| 49 | + tracing_subscriber::fmt::init(); |
| 50 | + let args = Cli::parse(); |
| 51 | + |
| 52 | + let secret_key = match std::env::var("IROH_SECRET") { |
| 53 | + Ok(s) => SecretKey::from_str(&s)?, |
| 54 | + Err(_) if args.create => { |
| 55 | + let s = SecretKey::generate(); |
| 56 | + println!("Generated a new node secret. To reuse, set"); |
| 57 | + println!("IROH_SECRET={s}"); |
| 58 | + s |
| 59 | + } |
| 60 | + Err(_) => { |
| 61 | + bail!("Environtment variable IROH_SECRET is not set. To create a new secret, use the --create option.") |
| 62 | + } |
| 63 | + }; |
| 64 | + |
| 65 | + let node_id = secret_key.public(); |
| 66 | + let pkarr_relay = match (args.pkarr_relay, args.env) { |
| 67 | + (Some(pkarr_relay), _) => pkarr_relay, |
| 68 | + (None, Env::Default) => N0_DNS_PKARR_RELAY.parse().expect("valid url"), |
| 69 | + (None, Env::Dev) => LOCALHOST_PKARR.parse().expect("valid url"), |
| 70 | + }; |
| 71 | + |
| 72 | + println!("announce {node_id}:"); |
| 73 | + println!(" relay={}", args.relay_url); |
| 74 | + println!(); |
| 75 | + println!("publish to {pkarr_relay} ..."); |
| 76 | + |
| 77 | + let pkarr = PkarrRelayClient::new(pkarr_relay); |
| 78 | + let node_info = NodeInfo::new(node_id, Some(args.relay_url)); |
| 79 | + let signed_packet = node_info.to_pkarr_signed_packet(&secret_key, 30)?; |
| 80 | + pkarr.publish(&signed_packet).await?; |
| 81 | + |
| 82 | + println!("signed packet published."); |
| 83 | + println!("resolve with:"); |
| 84 | + |
| 85 | + match args.env { |
| 86 | + Env::Default => { |
| 87 | + println!(" cargo run --example resolve -- node {}", node_id); |
| 88 | + println!(" dig {} TXT", fmt_domain(&node_id, N0_DNS_NODE_ORIGIN)) |
| 89 | + } |
| 90 | + Env::Dev => { |
| 91 | + println!( |
| 92 | + " cargo run --example resolve -- --env dev node {}", |
| 93 | + node_id |
| 94 | + ); |
| 95 | + println!( |
| 96 | + " dig @localhost -p 5300 {} TXT", |
| 97 | + fmt_domain(&node_id, EXAMPLE_ORIGIN) |
| 98 | + ) |
| 99 | + } |
| 100 | + } |
| 101 | + Ok(()) |
| 102 | +} |
| 103 | + |
| 104 | +fn fmt_domain(node_id: &NodeId, origin: &str) -> String { |
| 105 | + format!("{}.{}.{}", IROH_TXT_NAME, to_z32(node_id), origin) |
| 106 | +} |
0 commit comments