Skip to content

Commit aa6b8f4

Browse files
committed
fix(dht): keep WebRTC reachability unverified
1 parent a15bb53 commit aa6b8f4

2 files changed

Lines changed: 65 additions & 55 deletions

File tree

src/dht_network_manager.rs

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,24 +1507,11 @@ fn map_iterative_lookup_run_error(error: LookupRunError<P2PError>) -> P2PError {
15071507
}
15081508
}
15091509

1510-
fn supplemental_reachability(native: &[(MultiAddr, AddressType)]) -> KnownReachability {
1511-
if native
1512-
.iter()
1513-
.any(|(_, reachability)| *reachability == AddressType::Direct)
1514-
{
1515-
KnownReachability::Direct
1516-
} else if native
1517-
.iter()
1518-
.any(|(_, reachability)| *reachability == AddressType::Lan)
1510+
fn normalize_known_webrtc_reachability(record: &mut TransportAddressRecord) {
1511+
if record.transport == KnownTransport::WebRtcDirect.id()
1512+
&& KnownReachability::from_id(record.reachability).is_some()
15191513
{
1520-
KnownReachability::Lan
1521-
} else if native
1522-
.iter()
1523-
.any(|(_, reachability)| *reachability == AddressType::Relay)
1524-
{
1525-
KnownReachability::Relay
1526-
} else {
1527-
KnownReachability::Unverified
1514+
record.reachability = KnownReachability::Unverified.id();
15281515
}
15291516
}
15301517

@@ -3345,10 +3332,10 @@ impl DhtNetworkManager {
33453332
/// the complete V2 record to appropriate peers.
33463333
///
33473334
/// Supplemental addresses never enter the legacy `DHTNode` address list.
3348-
/// WebRTC Direct is represented by the V2 transport identifier and inherits
3349-
/// the reachability of this node's canonical QUIC address set. A node known
3350-
/// only through a relay therefore does not accidentally advertise its
3351-
/// browser UDP listener as publicly reachable.
3335+
/// WebRTC Direct has no relayed address form, so supplemental WebRTC
3336+
/// endpoints are always published as [`KnownReachability::Unverified`].
3337+
/// Reachability of a native QUIC socket is deliberately not reused for a
3338+
/// different transport or UDP port.
33523339
pub async fn set_supplemental_self_addresses(&self, addresses: Vec<MultiAddr>) {
33533340
let mut filtered = Vec::new();
33543341
for address in addresses {
@@ -3402,9 +3389,8 @@ impl DhtNetworkManager {
34023389
Err(error) => warn!(address = %address, %error, "failed to encode V2 QUIC address"),
34033390
}
34043391
}
3405-
let supplemental_reachability = supplemental_reachability(native);
34063392
for address in self.supplemental_self_addresses.read().await.iter() {
3407-
match TransportAddressRecord::from_multiaddr(address, supplemental_reachability) {
3393+
match TransportAddressRecord::from_multiaddr(address, KnownReachability::Unverified) {
34083394
Ok(Some(record)) if !records.contains(&record) => records.push(record),
34093395
Ok(_) => {}
34103396
Err(error) => {
@@ -3420,21 +3406,20 @@ impl DhtNetworkManager {
34203406
/// for `peer_id`.
34213407
///
34223408
/// Unknown future transports and reachability identifiers remain stored
3423-
/// opaquely and are not returned by this typed view.
3409+
/// opaquely and are not returned by this typed view. Known WebRTC Direct
3410+
/// records are always projected as [`KnownReachability::Unverified`].
34243411
pub async fn supplemental_address_records_for_peer(
34253412
&self,
34263413
peer_id: &PeerId,
34273414
) -> Vec<(MultiAddr, KnownReachability)> {
34283415
if peer_id == self.peer_id() {
3429-
let native = self.local_dht_node().await.typed_addresses();
3430-
let reachability = supplemental_reachability(&native);
34313416
return self
34323417
.supplemental_self_addresses
34333418
.read()
34343419
.await
34353420
.iter()
34363421
.cloned()
3437-
.map(|address| (address, reachability))
3422+
.map(|address| (address, KnownReachability::Unverified))
34383423
.collect();
34393424
}
34403425
let Some(set) = self
@@ -3455,7 +3440,11 @@ impl DhtNetworkManager {
34553440
.filter(|record| record.transport != KnownTransport::Quic.id())
34563441
.filter_map(|record| {
34573442
let address = record.decode_known().ok().flatten()?;
3458-
let reachability = KnownReachability::from_id(record.reachability)?;
3443+
let reachability = if address.is_webrtc_direct() {
3444+
KnownReachability::Unverified
3445+
} else {
3446+
KnownReachability::from_id(record.reachability)?
3447+
};
34593448
Some((address, reachability))
34603449
})
34613450
.collect()
@@ -3464,9 +3453,8 @@ impl DhtNetworkManager {
34643453
/// Return decoded, non-QUIC V2 addresses currently known for `peer_id`.
34653454
///
34663455
/// This compatibility projection preserves the address-only API used by
3467-
/// existing native applications. New transport selectors should use
3468-
/// [`Self::supplemental_address_records_for_peer`] so they can enforce
3469-
/// reachability policy.
3456+
/// applications whose transport has no reachability variants, including
3457+
/// WebRTC Direct.
34703458
pub async fn supplemental_addresses_for_peer(&self, peer_id: &PeerId) -> Vec<MultiAddr> {
34713459
self.supplemental_address_records_for_peer(peer_id)
34723460
.await
@@ -3484,7 +3472,8 @@ impl DhtNetworkManager {
34843472
let mut bounded = Vec::new();
34853473
let mut decoded = Vec::new();
34863474

3487-
for record in records.into_iter().take(MAX_TRANSPORT_ADDRESS_RECORDS) {
3475+
for mut record in records.into_iter().take(MAX_TRANSPORT_ADDRESS_RECORDS) {
3476+
normalize_known_webrtc_reachability(&mut record);
34883477
if !record.is_within_wire_bounds() || bounded.contains(&record) {
34893478
continue;
34903479
}
@@ -3525,7 +3514,11 @@ impl DhtNetworkManager {
35253514
{
35263515
if bounded[*index].legacy_reachability().is_some() {
35273516
bounded[*index].reachability =
3528-
KnownReachability::from_legacy(*canonical_reachability).id();
3517+
if bounded[*index].transport == KnownTransport::WebRtcDirect.id() {
3518+
KnownReachability::Unverified.id()
3519+
} else {
3520+
KnownReachability::from_legacy(*canonical_reachability).id()
3521+
};
35293522
}
35303523
} else {
35313524
keep[*index] = false;
@@ -6564,26 +6557,36 @@ mod tests {
65646557
}
65656558

65666559
#[test]
6567-
fn supplemental_transport_inherits_strongest_native_reachability() {
6568-
let direct = typed_addresses(vec![(
6569-
"/ip4/203.0.113.9/udp/10000/quic",
6570-
AddressType::Direct,
6571-
)]);
6572-
let relayed = typed_addresses(vec![(
6573-
"/ip4/203.0.113.10/udp/10001/quic",
6574-
AddressType::Relay,
6575-
)]);
6576-
assert_eq!(
6577-
supplemental_reachability(&direct),
6578-
KnownReachability::Direct
6579-
);
6560+
fn known_webrtc_reachability_is_always_unverified() {
6561+
let peer_id = pid(44);
6562+
let quic: MultiAddr = "/ip4/203.0.113.7/udp/12000/quic".parse().unwrap();
6563+
let webrtc = MultiAddr::webrtc_direct(
6564+
crate::WebRtcDirectAddr::new(
6565+
"203.0.113.7:42768".parse().unwrap(),
6566+
crate::WebRtcCertificateHash::new([0x44; 32]),
6567+
)
6568+
.unwrap(),
6569+
)
6570+
.with_peer_id(peer_id);
6571+
let quic_record = TransportAddressRecord::from_multiaddr(&quic, KnownReachability::Direct)
6572+
.unwrap()
6573+
.unwrap();
6574+
let mut webrtc_record =
6575+
TransportAddressRecord::from_multiaddr(&webrtc, KnownReachability::Direct)
6576+
.unwrap()
6577+
.unwrap();
6578+
// Model a pre-fix or malicious wire record. Ingress normalization must
6579+
// not let a known WebRTC endpoint claim a QUIC-style reachability tier.
6580+
webrtc_record.reachability = KnownReachability::Direct.id();
6581+
normalize_known_webrtc_reachability(&mut webrtc_record);
6582+
65806583
assert_eq!(
6581-
supplemental_reachability(&relayed),
6582-
KnownReachability::Relay
6584+
KnownReachability::from_id(quic_record.reachability),
6585+
Some(KnownReachability::Direct)
65836586
);
65846587
assert_eq!(
6585-
supplemental_reachability(&[]),
6586-
KnownReachability::Unverified
6588+
KnownReachability::from_id(webrtc_record.reachability),
6589+
Some(KnownReachability::Unverified)
65876590
);
65886591
}
65896592

src/transport_address.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ pub struct TransportAddressRecord {
127127

128128
impl TransportAddressRecord {
129129
/// Encode a currently supported address.
130+
///
131+
/// The supplied reachability applies to native QUIC. WebRTC Direct has no
132+
/// relay transport today, so its reachability is always encoded as
133+
/// [`KnownReachability::Unverified`].
130134
pub fn from_multiaddr(
131135
address: &MultiAddr,
132136
reachability: KnownReachability,
@@ -138,6 +142,10 @@ impl TransportAddressRecord {
138142
} else {
139143
return Ok(None);
140144
};
145+
let reachability = match transport {
146+
KnownTransport::Quic => reachability,
147+
KnownTransport::WebRtcDirect => KnownReachability::Unverified,
148+
};
141149

142150
Ok(Some(Self {
143151
transport: transport.id(),
@@ -215,7 +223,7 @@ mod tests {
215223
}
216224

217225
#[test]
218-
fn webrtc_direct_record_round_trips_independently_of_reachability() {
226+
fn webrtc_direct_record_is_always_unverified() {
219227
let peer_id = PeerId::from_bytes([0x22; 32]);
220228
let address = MultiAddr::webrtc_direct(
221229
WebRtcDirectAddr::new(
@@ -225,10 +233,9 @@ mod tests {
225233
.unwrap(),
226234
)
227235
.with_peer_id(peer_id);
228-
let record =
229-
TransportAddressRecord::from_multiaddr(&address, KnownReachability::Unverified)
230-
.unwrap()
231-
.unwrap();
236+
let record = TransportAddressRecord::from_multiaddr(&address, KnownReachability::Direct)
237+
.unwrap()
238+
.unwrap();
232239

233240
assert_eq!(record.transport, KnownTransport::WebRtcDirect.id());
234241
assert_eq!(record.legacy_reachability(), Some(AddressType::Unverified));

0 commit comments

Comments
 (0)