@@ -32,8 +32,8 @@ use std::{
3232 time:: { Duration , Instant } ,
3333} ;
3434
35- use futures:: { SinkExt , StreamExt , channel:: mpsc} ;
36- use libp2p_core:: Multiaddr ;
35+ use futures:: { channel:: mpsc, SinkExt , StreamExt } ;
36+ use libp2p_core:: { multiaddr :: Protocol , Multiaddr } ;
3737use libp2p_identity:: PeerId ;
3838use libp2p_swarm:: ListenAddresses ;
3939use socket2:: { Domain , Socket , Type } ;
@@ -276,13 +276,25 @@ where
276276 "received query from remote address on address"
277277 ) ;
278278
279+ // Only send addresses that belong to this interface.
280+ // This prevents advertising loopback or other interface addresses
281+ // to peers that can't reach them.
282+ // Note: We collect into a Vec because build_query_response requires
283+ // an ExactSizeIterator, which Filter doesn't implement.
284+ let iface_ip = this. addr ;
285+ let relevant_addrs: Vec < _ > = this
286+ . listen_addresses
287+ . read ( )
288+ . unwrap_or_else ( |e| e. into_inner ( ) )
289+ . iter ( )
290+ . filter ( |multiaddr| addr_matches_interface ( multiaddr, iface_ip) )
291+ . cloned ( )
292+ . collect ( ) ;
293+
279294 this. send_buffer . extend ( build_query_response (
280295 query. query_id ( ) ,
281296 this. local_peer_id ,
282- this. listen_addresses
283- . read ( )
284- . unwrap_or_else ( |e| e. into_inner ( ) )
285- . iter ( ) ,
297+ relevant_addrs. iter ( ) ,
286298 this. ttl ,
287299 ) ) ;
288300 continue ;
@@ -335,3 +347,66 @@ where
335347 }
336348 }
337349}
350+
351+ /// Returns `true` if the first protocol component of `addr` is an IP address equal to
352+ /// `iface_ip`.
353+ ///
354+ /// Used when answering mDNS queries to advertise only the listening addresses that are
355+ /// reachable on the interface the query arrived on, instead of every listening address.
356+ fn addr_matches_interface ( addr : & Multiaddr , iface_ip : IpAddr ) -> bool {
357+ let addr_ip = match addr. iter ( ) . next ( ) {
358+ Some ( Protocol :: Ip4 ( v4) ) => Some ( IpAddr :: V4 ( v4) ) ,
359+ Some ( Protocol :: Ip6 ( v6) ) => Some ( IpAddr :: V6 ( v6) ) ,
360+ _ => None ,
361+ } ;
362+ addr_ip == Some ( iface_ip)
363+ }
364+
365+ #[ cfg( test) ]
366+ mod tests {
367+ use super :: * ;
368+
369+ #[ test]
370+ fn filter_addresses_by_interface_ip ( ) {
371+ let iface_ipv4: IpAddr = "192.168.1.100" . parse ( ) . unwrap ( ) ;
372+ let iface_ipv6: IpAddr = "fe80::1" . parse ( ) . unwrap ( ) ;
373+
374+ let addr_matching_v4: Multiaddr = "/ip4/192.168.1.100/tcp/1234" . parse ( ) . unwrap ( ) ;
375+ let addr_different_v4: Multiaddr = "/ip4/10.0.0.1/tcp/1234" . parse ( ) . unwrap ( ) ;
376+ let addr_loopback_v4: Multiaddr = "/ip4/127.0.0.1/tcp/1234" . parse ( ) . unwrap ( ) ;
377+ let addr_matching_v6: Multiaddr = "/ip6/fe80::1/tcp/1234" . parse ( ) . unwrap ( ) ;
378+ let addr_different_v6: Multiaddr = "/ip6/::1/tcp/1234" . parse ( ) . unwrap ( ) ;
379+
380+ // IPv4 interface
381+ assert ! (
382+ addr_matches_interface( & addr_matching_v4, iface_ipv4) ,
383+ "Should match same IPv4"
384+ ) ;
385+ assert ! (
386+ !addr_matches_interface( & addr_different_v4, iface_ipv4) ,
387+ "Should not match different IPv4"
388+ ) ;
389+ assert ! (
390+ !addr_matches_interface( & addr_loopback_v4, iface_ipv4) ,
391+ "Should not match loopback when interface is not loopback"
392+ ) ;
393+ assert ! (
394+ !addr_matches_interface( & addr_matching_v6, iface_ipv4) ,
395+ "Should not match IPv6 on IPv4 interface"
396+ ) ;
397+
398+ // IPv6 interface
399+ assert ! (
400+ addr_matches_interface( & addr_matching_v6, iface_ipv6) ,
401+ "Should match same IPv6"
402+ ) ;
403+ assert ! (
404+ !addr_matches_interface( & addr_different_v6, iface_ipv6) ,
405+ "Should not match different IPv6"
406+ ) ;
407+ assert ! (
408+ !addr_matches_interface( & addr_matching_v4, iface_ipv6) ,
409+ "Should not match IPv4 on IPv6 interface"
410+ ) ;
411+ }
412+ }
0 commit comments