@@ -4,34 +4,29 @@ use std::hash::{DefaultHasher, Hash, Hasher};
44use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddrV4 , SocketAddrV6 } ;
55use std:: { io, ops} ;
66
7- /// Get local (non-loopback) IPv4.
8- #[ cfg( target_os = "linux" ) ]
9- pub fn get_local_addr ( ) -> io:: Result < Ipv4Addr > {
10- let hostname_out = std:: process:: Command :: new ( "hostname" ) . arg ( "-I" ) . output ( ) ?;
11- let ipv4_string = String :: from_utf8_lossy ( & hostname_out. stdout )
12- . split_once ( ' ' )
13- . ok_or_else ( || io:: Error :: other ( "Unexpected output from 'hostname -I'" ) ) ?
14- . 0
15- . to_string ( ) ;
16- ipv4_string. parse :: < Ipv4Addr > ( ) . map_err ( io:: Error :: other)
7+ #[ cfg( not( windows) ) ]
8+ pub ( crate ) fn get_local_addr ( mut predicate : impl FnMut ( & IpAddr ) -> bool ) -> Option < IpAddr > {
9+ sysinfo:: Networks :: new_with_refreshed_list ( )
10+ . values ( )
11+ . flat_map ( sysinfo:: NetworkData :: ip_networks)
12+ . filter_map ( |network| predicate ( & network. addr ) . then_some ( network. addr ) )
13+ . next ( )
1714}
1815
19- /// Get local (non-loopback) IPv4.
2016#[ cfg( windows) ]
21- pub fn get_local_addr ( ) -> io :: Result < Ipv4Addr > {
22- // naively uses first connected adapter
23- ipconfig:: get_adapters ( )
24- . map_err ( io :: Error :: other ) ?
17+ pub ( crate ) fn get_local_addr ( predicate : impl FnMut ( & IpAddr ) -> bool ) -> Option < IpAddr > {
18+ // can't use sysinfo on Windows because it doesn't return SW adapters, e.g. loopback
19+ let adapters = ipconfig:: get_adapters ( ) . ok ( ) ? ;
20+ adapters
2521 . iter ( )
2622 . filter ( |adapter| matches ! ( adapter. oper_status( ) , ipconfig:: OperStatus :: IfOperStatusUp ) )
2723 . flat_map ( ipconfig:: Adapter :: ip_addresses)
28- . find_map ( |addr| match addr {
29- IpAddr :: V4 ( ipv4) => Some ( * ipv4) ,
30- _ => None ,
31- } )
32- . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: NotFound , "IPv4 not found" ) )
24+ . cloned ( )
25+ . find ( predicate)
3326}
3427
28+ // ------------------------------------------------------------------------------------------------
29+
3530#[ cfg( windows) ]
3631fn get_adapter_addrs < ' a > (
3732 adapters : impl IntoIterator < Item = & ' a ipconfig:: Adapter > ,
@@ -104,10 +99,7 @@ pub fn get_bind_addr_v6(interface: Option<&str>) -> Ipv6Addr {
10499 Ipv6Addr :: UNSPECIFIED
105100}
106101
107- #[ cfg( not( any( target_os = "linux" , windows) ) ) ]
108- pub fn get_local_addr ( ) -> io:: Result < Ipv4Addr > {
109- Ok ( Ipv4Addr :: UNSPECIFIED )
110- }
102+ // ------------------------------------------------------------------------------------------------
111103
112104#[ doc( hidden) ]
113105pub fn set_so_sndbuf_internal < ' s > ( socket : impl Into < SockRef < ' s > > , value : usize , module : & str ) {
@@ -123,6 +115,24 @@ pub fn set_so_rcvbuf_internal<'s>(socket: impl Into<SockRef<'s>>, value: usize,
123115 }
124116}
125117
118+ /// Set SO_SNDBUF on a socket.
119+ #[ macro_export]
120+ macro_rules! set_so_sndbuf {
121+ ( $sock: expr, $size: expr) => { {
122+ $crate:: net:: set_so_sndbuf_internal( $sock, $size, std:: module_path!( ) ) ;
123+ } } ;
124+ }
125+
126+ /// Set SO_RCVBUF on a socket.
127+ #[ macro_export]
128+ macro_rules! set_so_rcvbuf {
129+ ( $sock: expr, $size: expr) => { {
130+ $crate:: net:: set_so_rcvbuf_internal( $sock, $size, std:: module_path!( ) ) ;
131+ } } ;
132+ }
133+
134+ // ------------------------------------------------------------------------------------------------
135+
126136/// Bind a socket to a specific network interface. Does nothing on Windows.
127137#[ cfg( target_os = "windows" ) ]
128138pub fn bind_to_interface < ' s > ( _socket : impl Into < SockRef < ' s > > , _interface : & str ) -> io:: Result < ( ) > {
@@ -176,21 +186,7 @@ pub fn bind_to_interface<'s>(socket: impl Into<SockRef<'s>>, interface: &str) ->
176186 Ok ( ( ) )
177187}
178188
179- /// Set SO_SNDBUF on a socket.
180- #[ macro_export]
181- macro_rules! set_so_sndbuf {
182- ( $sock: expr, $size: expr) => { {
183- $crate:: net:: set_so_sndbuf_internal( $sock, $size, std:: module_path!( ) ) ;
184- } } ;
185- }
186-
187- /// Set SO_RCVBUF on a socket.
188- #[ macro_export]
189- macro_rules! set_so_rcvbuf {
190- ( $sock: expr, $size: expr) => { {
191- $crate:: net:: set_so_rcvbuf_internal( $sock, $size, std:: module_path!( ) ) ;
192- } } ;
193- }
189+ // ------------------------------------------------------------------------------------------------
194190
195191const DYNAMIC_PORT_RANGE : ops:: Range < u32 > = 49152 ..65536 ;
196192
@@ -332,4 +328,15 @@ mod tests {
332328 let addr = get_bind_addr_v6 ( Some ( iface) ) ;
333329 assert_eq ! ( addr, Ipv6Addr :: LOCALHOST ) ;
334330 }
331+
332+ #[ test]
333+ fn test_local_addr_predicate ( ) {
334+ let addr = get_local_addr ( |addr| addr. is_loopback ( ) && addr. is_ipv4 ( ) ) ;
335+ assert_eq ! ( addr, Some ( IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ) ) ;
336+
337+ let addr = get_local_addr ( |addr| !addr. is_loopback ( ) && !addr. is_unspecified ( ) ) ;
338+ let addr = addr. expect ( "failed to find non-loopback address" ) ;
339+ assert ! ( !addr. is_loopback( ) ) ;
340+ assert ! ( !addr. is_unspecified( ) ) ;
341+ }
335342}
0 commit comments