@@ -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,33 @@ 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| {
291+ let first_component = multiaddr. iter ( ) . next ( ) ;
292+ let addr_ip = match first_component {
293+ Some ( Protocol :: Ip4 ( v4) ) => Some ( IpAddr :: V4 ( v4) ) ,
294+ Some ( Protocol :: Ip6 ( v6) ) => Some ( IpAddr :: V6 ( v6) ) ,
295+ _ => None ,
296+ } ;
297+ addr_ip == Some ( iface_ip)
298+ } )
299+ . cloned ( )
300+ . collect ( ) ;
301+
279302 this. send_buffer . extend ( build_query_response (
280303 query. query_id ( ) ,
281304 this. local_peer_id ,
282- this. listen_addresses
283- . read ( )
284- . unwrap_or_else ( |e| e. into_inner ( ) )
285- . iter ( ) ,
305+ relevant_addrs. iter ( ) ,
286306 this. ttl ,
287307 ) ) ;
288308 continue ;
@@ -335,3 +355,64 @@ where
335355 }
336356 }
337357}
358+
359+ #[ cfg( test) ]
360+ mod tests {
361+ use super :: * ;
362+
363+ #[ test]
364+ fn filter_addresses_by_interface_ip ( ) {
365+ // Test that addresses are correctly filtered by interface IP
366+ let iface_ipv4: IpAddr = "192.168.1.100" . parse ( ) . unwrap ( ) ;
367+ let iface_ipv6: IpAddr = "fe80::1" . parse ( ) . unwrap ( ) ;
368+
369+ let addr_matching_v4: Multiaddr = "/ip4/192.168.1.100/tcp/1234" . parse ( ) . unwrap ( ) ;
370+ let addr_different_v4: Multiaddr = "/ip4/10.0.0.1/tcp/1234" . parse ( ) . unwrap ( ) ;
371+ let addr_loopback_v4: Multiaddr = "/ip4/127.0.0.1/tcp/1234" . parse ( ) . unwrap ( ) ;
372+ let addr_matching_v6: Multiaddr = "/ip6/fe80::1/tcp/1234" . parse ( ) . unwrap ( ) ;
373+ let addr_different_v6: Multiaddr = "/ip6/::1/tcp/1234" . parse ( ) . unwrap ( ) ;
374+
375+ // Helper closure that mimics the filtering logic in poll
376+ let filter_fn = |multiaddr : & Multiaddr , iface_ip : IpAddr | {
377+ let first_component = multiaddr. iter ( ) . next ( ) ;
378+ let addr_ip = match first_component {
379+ Some ( Protocol :: Ip4 ( v4) ) => Some ( IpAddr :: V4 ( v4) ) ,
380+ Some ( Protocol :: Ip6 ( v6) ) => Some ( IpAddr :: V6 ( v6) ) ,
381+ _ => None ,
382+ } ;
383+ addr_ip == Some ( iface_ip)
384+ } ;
385+
386+ // IPv4 interface tests
387+ assert ! (
388+ filter_fn( & addr_matching_v4, iface_ipv4) ,
389+ "Should match same IPv4"
390+ ) ;
391+ assert ! (
392+ !filter_fn( & addr_different_v4, iface_ipv4) ,
393+ "Should not match different IPv4"
394+ ) ;
395+ assert ! (
396+ !filter_fn( & addr_loopback_v4, iface_ipv4) ,
397+ "Should not match loopback when interface is not loopback"
398+ ) ;
399+ assert ! (
400+ !filter_fn( & addr_matching_v6, iface_ipv4) ,
401+ "Should not match IPv6 on IPv4 interface"
402+ ) ;
403+
404+ // IPv6 interface tests
405+ assert ! (
406+ filter_fn( & addr_matching_v6, iface_ipv6) ,
407+ "Should match same IPv6"
408+ ) ;
409+ assert ! (
410+ !filter_fn( & addr_different_v6, iface_ipv6) ,
411+ "Should not match different IPv6"
412+ ) ;
413+ assert ! (
414+ !filter_fn( & addr_matching_v4, iface_ipv6) ,
415+ "Should not match IPv4 on IPv6 interface"
416+ ) ;
417+ }
418+ }
0 commit comments