Skip to main content

iroh_http_core/
addr.rs

1//! Node address parsing: tickets, bare node IDs, and JSON address info.
2
3use crate::encoding::parse_node_id;
4use crate::{CoreError, IrohEndpoint, NodeAddrInfo};
5
6/// Generate a ticket string for the given endpoint.
7///
8/// ISS-025: returns `Result` so serialization failures are surfaced to callers
9/// instead of being masked as empty strings.
10pub fn node_ticket(ep: &IrohEndpoint) -> Result<String, CoreError> {
11    let info = ep.node_addr();
12    serde_json::to_string(&info)
13        .map_err(|e| CoreError::internal(format!("failed to serialize node ticket: {e}")))
14}
15
16/// Parsed node address from a ticket string, bare node ID, or JSON address info.
17pub struct ParsedNodeAddr {
18    pub node_id: iroh::PublicKey,
19    pub relay_urls: Vec<iroh::RelayUrl>,
20    pub direct_addrs: Vec<std::net::SocketAddr>,
21}
22
23/// Parse a string that may be a bare node ID, a ticket string (JSON-encoded
24/// `NodeAddrInfo`), or a JSON object with `id` and `addrs` fields.
25///
26/// ISS-023: malformed entries cause a deterministic error rather than being
27/// silently discarded before they reach iroh's dialer.
28pub fn parse_node_addr(s: &str) -> Result<ParsedNodeAddr, CoreError> {
29    if let Ok(info) = serde_json::from_str::<NodeAddrInfo>(s) {
30        let node_id = parse_node_id(&info.id)?;
31        let mut relay_urls = Vec::new();
32        let mut direct_addrs = Vec::new();
33        for addr_str in &info.addrs {
34            if addr_str.contains("://") {
35                relay_urls.push(addr_str.parse::<iroh::RelayUrl>().map_err(|_| {
36                    CoreError::invalid_input(format!("malformed relay URL: {addr_str}"))
37                })?);
38                continue;
39            }
40            let addr = addr_str
41                .parse::<std::net::SocketAddr>()
42                .map_err(|_| CoreError::invalid_input(format!("malformed address: {addr_str}")))?;
43            // #346: a port-0 direct address is undialable; reject it here rather
44            // than letting a useless `:0` reach the dialer.
45            if addr.port() == 0 {
46                return Err(CoreError::invalid_input(format!(
47                    "direct address {addr_str} has no port (port 0)"
48                )));
49            }
50            direct_addrs.push(addr);
51        }
52        return Ok(ParsedNodeAddr {
53            node_id,
54            relay_urls,
55            direct_addrs,
56        });
57    }
58    let node_id = parse_node_id(s)?;
59    Ok(ParsedNodeAddr {
60        node_id,
61        relay_urls: Vec::new(),
62        direct_addrs: Vec::new(),
63    })
64}