use iroh::address_lookup::{
AddressLookup, AddressLookupBuilder, AddressLookupBuilderError, EndpointData, EndpointInfo,
Item,
};
#[derive(Debug)]
pub(crate) struct Hygienic<B>(pub(crate) B);
impl<B: AddressLookupBuilder> AddressLookupBuilder for Hygienic<B> {
fn into_address_lookup(
self,
endpoint: &iroh::Endpoint,
) -> Result<impl AddressLookup, AddressLookupBuilderError> {
Ok(HygienicLookup(self.0.into_address_lookup(endpoint)?))
}
}
pub(crate) fn wrap<L: AddressLookup>(inner: L) -> impl AddressLookup {
HygienicLookup(inner)
}
#[derive(Debug)]
struct HygienicLookup<L>(L);
impl<L: AddressLookup> AddressLookup for HygienicLookup<L> {
fn publish(&self, data: &EndpointData) {
self.0.publish(data);
}
fn resolve(
&self,
endpoint_id: iroh::EndpointId,
) -> Option<n0_future::boxed::BoxStream<Result<Item, iroh::address_lookup::Error>>> {
let inner = self.0.resolve(endpoint_id)?;
Some(Box::pin(n0_future::StreamExt::map(inner, |r| {
r.map(filter_item)
})))
}
}
pub(crate) fn filter_item(item: Item) -> Item {
let info = item.endpoint_info().clone();
let id = info.endpoint_id;
let mut data = info.data;
let kept: Vec<iroh::TransportAddr> = data
.addrs()
.filter(|a| crate::daemon::dial::is_dialable_addr(a))
.cloned()
.collect();
data.clear_ip_addrs();
data.add_addrs(
kept.into_iter()
.filter(|a| matches!(a, iroh::TransportAddr::Ip(_))),
);
Item::new(
EndpointInfo::from_parts(id, data),
item.provenance(),
item.last_updated(),
)
}
#[cfg(test)]
mod tests {
use super::filter_item;
use iroh::address_lookup::{EndpointData, EndpointInfo, Item};
fn item_of(addrs: Vec<iroh::TransportAddr>) -> (iroh::EndpointId, Item) {
let id = iroh::SecretKey::from_bytes(&[77u8; 32]).public();
let mut data = EndpointData::new(addrs);
data.set_user_data(Some("hello".parse().expect("valid user data")));
(
id,
Item::new(EndpointInfo::from_parts(id, data), "test", Some(42)),
)
}
#[derive(Debug)]
struct Fake(Vec<iroh::TransportAddr>);
impl iroh::address_lookup::AddressLookup for Fake {
fn resolve(
&self,
endpoint_id: iroh::EndpointId,
) -> Option<n0_future::boxed::BoxStream<Result<Item, iroh::address_lookup::Error>>>
{
let info =
EndpointInfo::from(iroh::EndpointAddr::from_parts(endpoint_id, self.0.clone()));
let item = Item::new(info, "fake", None);
Some(Box::pin(n0_future::stream::iter(vec![
Ok(item.clone()),
Err(iroh::address_lookup::Error::from_err(
"fake",
std::io::Error::other("boom"),
)),
Ok(item),
])))
}
}
#[tokio::test]
async fn the_wrapper_filters_what_it_yields() {
use n0_future::StreamExt as _;
let inner = Fake(vec![
iroh::TransportAddr::Ip("0.0.0.0:53".parse().unwrap()),
iroh::TransportAddr::Ip("224.0.0.1:1900".parse().unwrap()),
iroh::TransportAddr::Ip("192.168.4.4:4433".parse().unwrap()),
]);
let wrapped = super::HygienicLookup(inner);
let id = iroh::SecretKey::from_bytes(&[78u8; 32]).public();
let mut stream = iroh::address_lookup::AddressLookup::resolve(&wrapped, id)
.expect("the wrapper delegates and yields a stream");
let mut seen = Vec::new();
while let Some(r) = stream.next().await {
seen.push(r);
}
assert_eq!(seen.len(), 3, "every item is forwarded, errors included");
assert!(seen[1].is_err(), "an inline error passes through unmapped");
for (i, r) in [(0usize, &seen[0]), (2, &seen[2])] {
let it = r.as_ref().unwrap_or_else(|_| panic!("item {i} ok"));
let addr = iroh::EndpointAddr::from(it.endpoint_info().clone());
assert_eq!(
addr.addrs.len(),
1,
"EVERY yielded item is filtered, not just the first: {addr:?}"
);
assert_eq!(addr.id, id);
}
}
#[test]
fn a_resolved_address_that_cannot_be_a_peer_is_stripped() {
let (id, item) = item_of(vec![
iroh::TransportAddr::Ip("0.0.0.0:53".parse().unwrap()),
iroh::TransportAddr::Ip("224.0.0.1:1900".parse().unwrap()),
iroh::TransportAddr::Ip("255.255.255.255:80".parse().unwrap()),
iroh::TransportAddr::Ip("[::ffff:224.0.0.1]:1900".parse().unwrap()),
iroh::TransportAddr::Ip("192.168.4.4:4433".parse().unwrap()),
]);
let out = filter_item(item);
let addr = iroh::EndpointAddr::from(out.endpoint_info().clone());
assert_eq!(
addr.addrs.len(),
1,
"only the dialable address survives resolution: {addr:?}"
);
assert_eq!(
addr.id, id,
"and the item still names the endpoint it resolved"
);
}
#[test]
fn legitimate_resolution_and_metadata_survive() {
let (id, item) = item_of(vec![
iroh::TransportAddr::Relay("https://relay.example".parse().unwrap()),
iroh::TransportAddr::Ip("192.168.1.5:4433".parse().unwrap()),
iroh::TransportAddr::Ip("[2001:db8::1]:4433".parse().unwrap()),
iroh::TransportAddr::Ip("[fe80::1]:4433".parse().unwrap()),
iroh::TransportAddr::Ip("127.0.0.1:4433".parse().unwrap()),
]);
let out = filter_item(item);
assert_eq!(
out.provenance(),
"test",
"the source label must survive — iroh uses it to attribute paths"
);
assert_eq!(out.last_updated(), Some(42));
assert_eq!(
out.endpoint_info()
.data
.user_data()
.map(ToString::to_string),
Some("hello".to_string()),
"user_data is the endpoint's own annotation and nothing to do with reachability — \
dropping it silently changes behaviour for anyone who reads it"
);
let ordered: Vec<String> = out
.endpoint_info()
.data
.addrs()
.map(ToString::to_string)
.collect();
assert!(
ordered.iter().position(|a| a.contains("192.168.1.5"))
< ordered.iter().position(|a| a.contains("2001:db8")),
"the surviving addresses keep their relative order, not a re-sorted one: {ordered:?}"
);
let addr = iroh::EndpointAddr::from(out.endpoint_info().clone());
assert_eq!(addr.id, id);
assert_eq!(
addr.addrs.len(),
5,
"every legitimate class survives, the relay URL above all: {addr:?}"
);
assert!(
addr.addrs
.iter()
.any(|a| matches!(a, iroh::TransportAddr::Relay(_))),
"dropping the relay would disable relay-mediated connectivity for every resolved \
peer — the failure mode of an ingress filter is breaking discovery, not leaking: \
{addr:?}"
);
}
}