feat: dial hint on Address for hosts that resolve DNS themselves

This commit is contained in:
randogoth 2026-09-29 12:46:58 +03:00
parent 45987fe03d
commit 5559fcce31
2 changed files with 44 additions and 1 deletions

View file

@ -35,6 +35,13 @@ pub struct Address {
pub port: u16,
pub scheme: Scheme,
pub identity: Option<[u8; KEY_LEN]>,
/// Where to dial instead of resolving `host` again: an embedder that
/// resolves DNS itself (an Android app, whose native getaddrinfo can be
/// dead while the platform resolver works) sets the address's IP here.
/// The hostname keeps every identity role — pins, PoP, display — so
/// `dial` is purely a routing hint, and `None` (the default) means
/// fumi resolves `host` itself.
pub dial: Option<String>,
}
impl Address {
@ -53,6 +60,15 @@ impl Address {
format!("{}{}/{}", self.scheme.prefix(), self.short(), b32(identity))
}
/// Routing hint: dial this address instead of resolving `host`. The
/// hostname is untouched — pins, proof-of-possession and display all
/// keep using it, so a dial override changes where the packets go and
/// nothing about who the server is.
pub fn with_dial(mut self, dial: &str) -> Address {
self.dial = Some(dial.to_string());
self
}
/// The Reticulum destination hash, for the `rns` carrier only.
pub fn destination(&self) -> Result<[u8; 16], Error> {
if self.scheme != Scheme::Rns {
@ -109,6 +125,7 @@ impl Address {
port,
scheme,
identity,
dial: None,
}
}
Scheme::Rns => {
@ -131,6 +148,7 @@ impl Address {
port: 0,
scheme,
identity,
dial: None,
}
}
})
@ -197,6 +215,23 @@ fn valid_username(name: &str) -> Result<(), Error> {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dial_hint_routes_without_renaming() {
let addr = Address::parse("alice@example.org:1961")
.unwrap()
.with_dial("192.0.2.10");
// The hint changes where packets go, never who the server is: pins,
// proof-of-possession and display all read `host`.
assert_eq!(addr.dial.as_deref(), Some("192.0.2.10"));
assert_eq!(addr.host, "example.org");
assert_eq!(addr.short(), "alice@example.org:1961");
assert_eq!(Address::parse("alice@example.org").unwrap().dial, None);
}
}
mod tests {
use super::*;
use crate::crypto::b32;

View file

@ -71,7 +71,15 @@ pub fn connect(
host: addr.host.clone(),
});
}
let transport = TcpTransport::connect(&addr.host, addr.port, pinned, timeout)?;
// The dial hint lets a host that resolves DNS itself (an
// Android app) reach the server by IP while `host` keeps its
// identity roles.
let transport = TcpTransport::connect(
addr.dial.as_deref().unwrap_or(&addr.host),
addr.port,
pinned,
timeout,
)?;
let unpinned_static = if pinned.is_none() {
Some(*transport.server_static())
} else {