Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions iroh-base/src/relay_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,43 @@ impl From<Url> for RelayUrl {
}
}

impl RelayUrl {
/// Returns the URL while removing the final dot in the relay URL's domain name.
///
/// By default, we add a final dot to relay URLs to make sure that DNS resolution always
/// considers them as top-level domains without appending a search suffix. When using
/// the URL for TLS hostname verification, usually a domain name without a final
/// dot is expected. So when using the URL in the context of HTTPS or TLS, use
/// this function to get the URL without the final dot.
pub fn without_final_dot(&self) -> Url {
let mut url = self.0.deref().clone();
if let Some(domain) = url.domain() {
if let Some(stripped) = domain.strip_suffix('.') {
let stripped = stripped.to_string();
url.set_host(Some(&stripped)).ok();
}
}
url
}

/// Return the string representation of the host (domain or IP address) for this URL, if any.
///
/// If the host is a domain, and the domain ends with a final dot, the final dot is removed.
///
/// See [`Self::without_final_dot`] for details on when you might want to use this.
pub fn host_str_without_final_dot(&self) -> Option<&str> {
if let Some(domain) = self.0.domain() {
if let Some(stripped) = domain.strip_suffix('.') {
Some(stripped)
} else {
Some(domain)
}
} else {
self.0.host_str()
}
}
}

/// Can occur when parsing a string into a [`RelayUrl`].
#[derive(Debug, Snafu)]
#[snafu(display("Failed to parse"))]
Expand Down Expand Up @@ -127,5 +164,16 @@ mod tests {

let url3 = RelayUrl::from(Url::parse("https://example.com/").unwrap());
assert_eq!(url, url3);

// tests `RelayUrl::without_final_dot`
assert_eq!(url.deref(), &Url::parse("https://example.com.").unwrap());
assert_eq!(
url.without_final_dot(),
Url::parse("https://example.com").unwrap()
);

// tests `RelayUrl::host_str_without_final_dot`
assert_eq!(url.host_str(), Some("example.com."));
assert_eq!(url.host_str_without_final_dot(), Some("example.com"));
}
}
10 changes: 8 additions & 2 deletions iroh/src/net_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,10 @@ async fn run_probe_v4(
reportgen::maybe_to_mapped_addr(ip_mapped_addrs.as_ref(), relay_addr_orig.into());

debug!(?relay_addr_orig, ?relay_addr, "relay addr v4");
let host = relay_node.url.host_str().context("missing host url")?;
let host = relay_node
.url
.host_str_without_final_dot()
.context("missing host url")?;
let conn = quic_client.create_conn(relay_addr, host).await?;
let mut receiver = conn.observed_external_addr();

Expand Down Expand Up @@ -761,7 +764,10 @@ async fn run_probe_v6(
reportgen::maybe_to_mapped_addr(ip_mapped_addrs.as_ref(), relay_addr_orig.into());

debug!(?relay_addr_orig, ?relay_addr, "relay addr v6");
let host = relay_node.url.host_str().context("missing host url")?;
let host = relay_node
.url
.host_str_without_final_dot()
.context("missing host url")?;
let conn = quic_client.create_conn(relay_addr, host).await?;
let mut receiver = conn.observed_external_addr();

Expand Down
6 changes: 3 additions & 3 deletions iroh/src/net_report/reportgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ async fn check_captive_portal(
// length is limited; see is_challenge_char in bin/iroh-relay for more
// details.

let host_name = url.host_str().unwrap_or_default();
let host_name = url.host_str_without_final_dot().unwrap_or_default();
let challenge = format!("ts_{host_name}");
let portal_url = format!("http://{host_name}/generate_204");
let res = client
Expand Down Expand Up @@ -771,7 +771,6 @@ async fn run_https_probe(
#[cfg(any(test, feature = "test-utils"))] insecure_skip_relay_cert_verify: bool,
) -> Result<HttpsProbeReport, MeasureHttpsLatencyError> {
trace!("HTTPS probe start");
let url = relay_node.join(RELAY_PROBE_PATH)?;

// This should also use same connection establishment as relay client itself, which
// needs to be more configurable so users can do more crazy things:
Expand All @@ -784,7 +783,7 @@ async fn run_https_probe(
}

#[cfg(not(wasm_browser))]
if let Some(Host::Domain(domain)) = url.host() {
if let Some(Host::Domain(domain)) = relay_node.host() {
// Use our own resolver rather than getaddrinfo
//
// Be careful, a non-zero port will override the port in the URI.
Expand All @@ -809,6 +808,7 @@ async fn run_https_probe(
.context(measure_https_latency_error::CreateReqwestClientSnafu)?;

let start = Instant::now();
let url = relay_node.without_final_dot().join(RELAY_PROBE_PATH)?;
let response = client
.request(reqwest::Method::GET, url)
.send()
Expand Down
Loading