local_ip_address/
lib.rs

1/*!
2# Local IP Address
3
4Retrieve system's local IP address and Network Interfaces/Adapters on
5Linux, Windows, and macOS (and other BSD-based systems).
6
7## Usage
8
9Get the local IP address of your system by executing the `local_ip` function:
10
11```rust
12use local_ip_address::local_ip;
13
14let my_local_ip = local_ip();
15
16if let Ok(my_local_ip) = my_local_ip {
17    println!("This is my local IP address: {:?}", my_local_ip);
18} else {
19    println!("Error getting local IP: {:?}", my_local_ip);
20}
21```
22
23Retrieve all the available network interfaces from both, the `AF_INET` and
24the `AF_INET6` family by executing the `list_afinet_netifas` function:
25
26```rust
27use local_ip_address::list_afinet_netifas;
28
29let network_interfaces = list_afinet_netifas();
30
31if let Ok(network_interfaces) = network_interfaces {
32    for (name, ip) in network_interfaces.iter() {
33        println!("{}:\t{:?}", name, ip);
34    }
35} else {
36    println!("Error getting network interfaces: {:?}", network_interfaces);
37}
38```
39
40Underlying approach on retrieving network interfaces or the local IP address
41may differ based on the running operative system.
42
43OS | Approach
44--- | ---
45Linux | Establishes a Netlink socket interchange to retrieve network interfaces
46BSD-based & Android | Uses of `getifaddrs` to retrieve network interfaces
47Windows | Consumes Win32 API's to retrieve the network adapters table
48
49Supported BSD-based systems include:
50  - macOS
51  - FreeBSD
52  - OpenBSD
53  - NetBSD
54  - DragonFly
55*/
56
57use std::net::IpAddr;
58
59mod error;
60
61pub use error::Error;
62
63#[cfg(target_os = "linux")]
64pub mod linux;
65#[cfg(target_os = "linux")]
66pub use crate::linux::*;
67
68#[cfg(any(
69    target_os = "freebsd",
70    target_os = "openbsd",
71    target_os = "netbsd",
72    target_os = "dragonfly",
73    target_os = "macos",
74    target_os = "android",
75    target_os = "ios",
76))]
77pub mod unix;
78
79#[cfg(any(
80    target_os = "freebsd",
81    target_os = "openbsd",
82    target_os = "netbsd",
83    target_os = "dragonfly",
84    target_os = "macos",
85    target_os = "android",
86    target_os = "ios",
87))]
88pub use crate::unix::*;
89
90#[cfg(target_family = "windows")]
91pub mod windows;
92#[cfg(target_family = "windows")]
93pub use crate::windows::*;
94
95/// Retrieves the local IPv4 address of the machine in the local network from
96/// the `AF_INET` family.
97///
98/// A different approach is taken based on the operative system.
99///
100/// For linux based systems the Netlink socket communication is used to
101/// retrieve the local network interface.
102///
103/// For BSD-based systems the `getifaddrs` approach is taken using `libc`
104///
105/// For Windows systems Win32's IP Helper is used to gather the Local IP
106/// address
107pub fn local_ip() -> Result<IpAddr, Error> {
108    #[cfg(target_os = "linux")]
109    {
110        crate::linux::local_ip()
111    }
112
113    #[cfg(any(
114        target_os = "freebsd",
115        target_os = "openbsd",
116        target_os = "netbsd",
117        target_os = "dragonfly",
118        target_os = "macos",
119        target_os = "android",
120        target_os = "ios",
121    ))]
122    {
123        let ifas = crate::unix::list_afinet_netifas_info()?;
124
125        ifas.into_iter()
126            .find_map(|ifa| {
127                if !ifa.is_loopback && ifa.addr.is_ipv4() && !ifa.is_mobile_data() {
128                    Some(ifa.addr)
129                } else {
130                    None
131                }
132            })
133            .ok_or(Error::LocalIpAddressNotFound)
134    }
135
136    #[cfg(target_os = "windows")]
137    {
138        use windows_sys::Win32::Networking::WinSock::AF_INET;
139
140        let ip_addresses = crate::windows::list_local_ip_addresses(AF_INET)?;
141
142        ip_addresses
143            .into_iter()
144            .find(|ip_address| matches!(ip_address, IpAddr::V4(_)))
145            .ok_or(Error::LocalIpAddressNotFound)
146    }
147
148    // A catch-all case to error if not implemented for OS
149    #[cfg(not(any(
150        target_os = "linux",
151        target_os = "windows",
152        target_os = "macos",
153        target_os = "freebsd",
154        target_os = "openbsd",
155        target_os = "netbsd",
156        target_os = "dragonfly",
157        target_os = "android",
158        target_os = "ios",
159    )))]
160    {
161        Err(Error::PlatformNotSupported(
162            std::env::consts::OS.to_string(),
163        ))
164    }
165}
166
167/// Retrieves the local IPv6 address of the machine in the local network from
168/// the `AF_INET6` family.
169///
170/// A different approach is taken based on the operative system.
171///
172/// For linux based systems the Netlink socket communication is used to
173/// retrieve the local network interface.
174///
175/// For BSD-based systems the `getifaddrs` approach is taken using `libc`
176///
177/// For Windows systems Win32's IP Helper is used to gather the Local IP
178/// address
179pub fn local_ipv6() -> Result<IpAddr, Error> {
180    #[cfg(target_os = "linux")]
181    {
182        crate::linux::local_ipv6()
183    }
184
185    #[cfg(any(
186        target_os = "freebsd",
187        target_os = "openbsd",
188        target_os = "netbsd",
189        target_os = "dragonfly",
190        target_os = "macos",
191        target_os = "android",
192        target_os = "ios",
193    ))]
194    {
195        let ifas = crate::unix::list_afinet_netifas_info()?;
196
197        ifas.into_iter()
198            .find_map(|ifa| {
199                if !ifa.is_loopback && ifa.addr.is_ipv6() && !ifa.is_mobile_data() {
200                    Some(ifa.addr)
201                } else {
202                    None
203                }
204            })
205            .ok_or(Error::LocalIpAddressNotFound)
206    }
207
208    #[cfg(target_os = "windows")]
209    {
210        use windows_sys::Win32::Networking::WinSock::AF_INET6;
211
212        let ip_addresses = crate::windows::list_local_ip_addresses(AF_INET6)?;
213
214        ip_addresses
215            .into_iter()
216            .find(|ip_address| matches!(ip_address, IpAddr::V6(_)))
217            .ok_or(Error::LocalIpAddressNotFound)
218    }
219
220    // A catch-all case to error if not implemented for OS
221    #[cfg(not(any(
222        target_os = "linux",
223        target_os = "windows",
224        target_os = "macos",
225        target_os = "freebsd",
226        target_os = "openbsd",
227        target_os = "netbsd",
228        target_os = "dragonfly",
229        target_os = "android",
230        target_os = "ios",
231    )))]
232    {
233        Err(Error::PlatformNotSupported(
234            std::env::consts::OS.to_string(),
235        ))
236    }
237}
238
239// A catch-all function to error if not implemented for OS
240#[cfg(not(any(
241    target_os = "linux",
242    target_os = "windows",
243    target_os = "macos",
244    target_os = "freebsd",
245    target_os = "openbsd",
246    target_os = "netbsd",
247    target_os = "dragonfly",
248    target_os = "android",
249    target_os = "ios",
250)))]
251pub fn list_afinet_netifas() -> Result<Vec<(String, IpAddr)>, Error> {
252    Err(Error::PlatformNotSupported(
253        std::env::consts::OS.to_string(),
254    ))
255}
256
257mod tests {
258    #[allow(unused_imports)]
259    use super::*;
260
261    #[test]
262    #[cfg(target_os = "linux")]
263    fn find_local_ip() {
264        let my_local_ip = local_ip();
265
266        assert!(matches!(my_local_ip, Ok(IpAddr::V4(_))));
267        println!("Linux 'local_ip': {:?}", my_local_ip);
268    }
269
270    #[test]
271    #[cfg(any(
272        target_os = "freebsd",
273        target_os = "openbsd",
274        target_os = "netbsd",
275        target_os = "dragonfly",
276        target_os = "macos",
277        target_os = "android",
278        target_os = "ios",
279    ))]
280    fn find_local_ip() {
281        let my_local_ip = local_ip();
282
283        assert!(matches!(my_local_ip, Ok(IpAddr::V4(_))));
284        println!("Unix 'local_ip': {:?}", my_local_ip);
285    }
286
287    #[test]
288    #[cfg(target_os = "windows")]
289    fn find_local_ip() {
290        let my_local_ip = local_ip();
291
292        assert!(matches!(my_local_ip, Ok(IpAddr::V4(_))));
293        println!("Windows 'local_ip': {:?}", my_local_ip);
294    }
295
296    #[test]
297    #[cfg(target_os = "linux")]
298    fn find_network_interfaces() {
299        let network_interfaces = list_afinet_netifas();
300
301        assert!(network_interfaces.is_ok());
302        assert!(!network_interfaces.unwrap().is_empty());
303    }
304
305    #[test]
306    #[cfg(any(
307        target_os = "freebsd",
308        target_os = "openbsd",
309        target_os = "netbsd",
310        target_os = "dragonfly",
311        target_os = "macos",
312        target_os = "android",
313        target_os = "ios",
314    ))]
315    fn find_network_interfaces() {
316        let network_interfaces = list_afinet_netifas();
317
318        assert!(network_interfaces.is_ok());
319        assert!(!network_interfaces.unwrap().is_empty());
320    }
321
322    #[test]
323    #[cfg(target_os = "windows")]
324    fn find_network_interfaces() {
325        let network_interfaces = list_afinet_netifas();
326
327        assert!(network_interfaces.is_ok());
328        assert!(!network_interfaces.unwrap().is_empty());
329    }
330}