if-addrs-0.13.3/.cargo_vcs_info.json0000644000000001360000000000100126110ustar { "git": { "sha1": "9892ca4cbcda638f45c106cd19830a5f7e74fa0e" }, "path_in_vcs": "" }if-addrs-0.13.3/.github/workflows/CI.yml000064400000000000000000000030221046102023000160110ustar 00000000000000on: [push, pull_request] name: CI jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - name: Run check run: cargo check test: name: Test Suite runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] rust: ["1.56.0", stable] steps: - uses: actions/checkout@v4 - uses: maxim-lobanov/setup-xcode@v1 if: ${{ matrix.os == 'macos-latest' && matrix.rust == '1.56.0' }} with: xcode-version: latest-stable - uses: dtolnay/rust-toolchain@stable if: ${{ matrix.rust == 'stable' }} - uses: dtolnay/rust-toolchain@1.56.0 if: ${{ matrix.rust == '1.56.0' }} - name: Build run: cargo build - name: Test run: cargo test -- --show-output fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Run fmt run: cargo fmt --all -- --check clippy_check: name: Clippy Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: clippy - uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0 - name: Run clippy run: cargo clippy --all-features if-addrs-0.13.3/.github/workflows/Cross.yml000064400000000000000000000031001046102023000166040ustar 00000000000000# We could use `@actions-rs/cargo` Action ability to automatically install `cross` tool # in order to compile our application for some unusual targets. on: [push, pull_request] name: Cross-compile jobs: build: name: Build runs-on: ubuntu-latest strategy: matrix: target: - aarch64-linux-android - x86_64-unknown-freebsd - x86_64-unknown-netbsd steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: stable target: ${{ matrix.target }} - name: Build run: cargo build --target=${{ matrix.target }} apple: name: Apple Build on ${{ matrix.target }} runs-on: macos-latest strategy: matrix: include: - target: aarch64-apple-ios tier3: false - target: aarch64-apple-watchos tier3: true - target: aarch64-apple-tvos tier3: true - target: aarch64-apple-visionos tier3: true steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable if: ${{ !matrix.tier3 }} with: toolchain: 'stable' targets: ${{ matrix.target }} components: rust-src - uses: dtolnay/rust-toolchain@master if: ${{ matrix.tier3 }} with: toolchain: 'nightly' components: rust-src - name: Build run: cargo build --target ${{ matrix.target }} ${{ matrix.tier3 && '-Zbuild-std' || '' }} if-addrs-0.13.3/.gitignore000064400000000000000000000002711046102023000133710ustar 00000000000000*.exe *.lock *.rsproj tags* build/ build-tests/ target/ /.idea tests/tmp* src/tmp* /.project *.bootstrap.cache /bin/ # Generated by Editors *~ *.bk *.sublime-* *.swp # Misc .DS_Store if-addrs-0.13.3/CHANGELOG.md000064400000000000000000000023411046102023000132120ustar 00000000000000# if-addrs - Change Log ## Unreleased - Use Rust 1.56 stable and edition 2021 ## [0.7.0] - Fix support for Android 11 - Drop support for Android `<` 7 ## [0.6.7] - Add support for haiku ## [0.6.6] - Add support for illumos ## [0.6.5] - Drop `unwrap` dev dependency ## [0.6.4] - Support Rust 1.40.0 ## [0.6.3] - Fix Android build with Rust 2018 edition ## [0.6.2] - Fix Android build and add CI check ## [0.6.1] - Fixed Windows build issue after `winapi 0.3` upgrade ## [0.6.0] - forked from get_if_addrs - Rename to if-addrs - Replace `gcc` crate with `cc` crate - Upgrade `winapi` crate to 0.3 ## [0.5.3] - Update dependency version of get_if_addrs-sys to 0.1.1 - Update dependency version of c_linked_list to 1.1.1 ## [0.5.2] - Fix incorrect parsing of IPv6 addresses ## [0.5.1] - Fixed nullptr deref in unsafe code - Use Rust 1.24.0 stable / 2018-02-05 nightly - Use Clippy 0.0.186 ## [0.5.0] - Use rust 1.22.1 stable / 2017-12-02 nightly - rustfmt 0.9.0 and clippy-0.0.175 ## [0.4.1] - Fix build for android ## [0.4.0] - Replaced ip::IpAddr with std::IpAddr - Changed to support BSD - Updated lints - Documentation fixes ## [0.3.1] - Fix build on ARM ## [0.3.0] - Added a method on the interface object to get the ip addresses if-addrs-0.13.3/Cargo.lock0000644000000051570000000000100105740ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "if-addrs" version = "0.13.3" dependencies = [ "libc", "windows-sys", ] [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" if-addrs-0.13.3/Cargo.toml0000644000000035320000000000100106120ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "if-addrs" version = "0.13.3" authors = [ "MaidSafe Developers ", "Messense Lv ", ] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Return interface IP addresses on Posix and windows systems" readme = "README.md" license = "MIT OR BSD-3-Clause" repository = "https://github.com/messense/if-addrs" [package.metadata.docs.rs] all-features = true cargo-args = [ "-Z", "build-std", ] rustdoc-args = [ "--cfg", "docsrs", ] targets = [ "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "aarch64-apple-ios", "aarch64-apple-watchos", "aarch64-apple-tvos", "aarch64-apple-visionos", ] [lib] name = "if_addrs" path = "src/lib.rs" [[example]] name = "detect_interface_changes" path = "examples/detect_interface_changes.rs" [[example]] name = "list_interfaces" path = "examples/list_interfaces.rs" [features] link-local = [] [target.'cfg(not(target_os = "windows"))'.dependencies.libc] version = "0.2" [target.'cfg(target_os = "windows")'.dependencies.windows-sys] version = "0.52.0" features = [ "Win32_Foundation", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading", "Win32_Networking_WinSock", "Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", ] if-addrs-0.13.3/Cargo.toml.orig000064400000000000000000000020201046102023000142620ustar 00000000000000[package] authors = ["MaidSafe Developers ", "Messense Lv "] description = "Return interface IP addresses on Posix and windows systems" license = "MIT OR BSD-3-Clause" name = "if-addrs" readme = "README.md" repository = "https://github.com/messense/if-addrs" version = "0.13.3" edition = "2021" [target.'cfg(not(target_os = "windows"))'.dependencies] libc = "0.2" [target.'cfg(target_os = "windows")'.dependencies.windows-sys] version = "0.52.0" features = [ "Win32_Foundation", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading", "Win32_Networking_WinSock", "Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", ] [features] link-local = [] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "aarch64-apple-ios", "aarch64-apple-watchos", "aarch64-apple-tvos", "aarch64-apple-visionos"] cargo-args = ["-Z", "build-std"] if-addrs-0.13.3/LICENSE-BSD000064400000000000000000000027221046102023000130170ustar 00000000000000Copyright 2018 MaidSafe.net limited. Copyright 2020 messense Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if-addrs-0.13.3/LICENSE-MIT000064400000000000000000000020751046102023000130410ustar 00000000000000Copyright 2018 MaidSafe.net limited. Copyright 2020 messense Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. if-addrs-0.13.3/README.md000064400000000000000000000016031046102023000126600ustar 00000000000000# if-addrs https://crates.io/crates/if-addrs ## Overview Retrieve network interface info for all interfaces on the system: ```rust // List all of the machine's network interfaces for iface in if_addrs::get_if_addrs().unwrap() { println!("{:#?}", iface); } ``` Get notifications for changes in network interfaces: ```rust let mut notifier = if_addrs::IfChangeNotifier::new().unwrap(); loop { if let Ok(details) = notifier.wait(None) { println!("{:#?}", details); } } ``` ## License This SAFE Network library is dual-licensed under the Modified BSD ([LICENSE-BSD](LICENSE-BSD) https://opensource.org/licenses/BSD-3-Clause) or the MIT license ([LICENSE-MIT](LICENSE-MIT) http://opensource.org/licenses/MIT) at your option. ## Contribution Copyrights in the SAFE Network are retained by their contributors. No copyright assignment is required to contribute to this project. if-addrs-0.13.3/examples/detect_interface_changes.rs000064400000000000000000000010251046102023000205430ustar 00000000000000//! Interface change notifier example. #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn main() { let mut if_change_notifier = if_addrs::IfChangeNotifier::new().unwrap(); println!("Waiting for interface changes..."); loop { if let Ok(details) = if_change_notifier.wait(None) { println!("Network interfaces changed: {:#?}", details); } } } #[cfg(any(target_os = "macos", target_os = "ios"))] fn main() { panic!("Interface change API is not implemented for macOS or iOS"); } if-addrs-0.13.3/examples/list_interfaces.rs000064400000000000000000000012411046102023000167410ustar 00000000000000// Copyright 2018 MaidSafe.net limited. // // This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, // modified, or distributed except according to those terms. Please review the Licences for the // specific language governing permissions and limitations relating to use of the SAFE Network // Software. //! List interface example. fn main() { let ifaces = if_addrs::get_if_addrs().unwrap(); println!("Got list of interfaces"); println!("{:#?}", ifaces); } if-addrs-0.13.3/src/lib.rs000064400000000000000000000622431046102023000133130ustar 00000000000000// Copyright 2018 MaidSafe.net limited. // // This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, // modified, or distributed except according to those terms. Please review the Licences for the // specific language governing permissions and limitations relating to use of the SAFE Network // Software. #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(not(windows))] mod posix; #[cfg(all( not(windows), not(all( target_vendor = "apple", any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos" ) )), not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd") ))] mod posix_not_apple; mod sockaddr; #[cfg(windows)] mod windows; use std::io; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// Details about an interface on this host. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct Interface { /// The name of the interface. pub name: String, /// The address details of the interface. pub addr: IfAddr, /// The index of the interface. pub index: Option, /// (Windows only) A permanent and unique identifier for the interface. It /// cannot be modified by the user. It is typically a GUID string of the /// form: "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}", but this is not /// guaranteed by the Windows API. #[cfg(windows)] pub adapter_name: String, } impl Interface { /// Check whether this is a loopback interface. #[must_use] pub const fn is_loopback(&self) -> bool { self.addr.is_loopback() } /// Check whether this is a link local interface. #[must_use] pub const fn is_link_local(&self) -> bool { self.addr.is_link_local() } /// Get the IP address of this interface. #[must_use] pub const fn ip(&self) -> IpAddr { self.addr.ip() } } /// Details about the address of an interface on this host. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum IfAddr { /// This is an Ipv4 interface. V4(Ifv4Addr), /// This is an Ipv6 interface. V6(Ifv6Addr), } impl IfAddr { /// Check whether this is a loopback address. #[must_use] pub const fn is_loopback(&self) -> bool { match *self { IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_loopback(), IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_loopback(), } } /// Check whether this is a link local interface. #[must_use] pub const fn is_link_local(&self) -> bool { match *self { IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_link_local(), IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_link_local(), } } /// Get the IP address of this interface address. #[must_use] pub const fn ip(&self) -> IpAddr { match *self { IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip), IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip), } } } /// Details about the ipv4 address of an interface on this host. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct Ifv4Addr { /// The IP address of the interface. pub ip: Ipv4Addr, /// The netmask of the interface. pub netmask: Ipv4Addr, /// The CIDR prefix of the interface. pub prefixlen: u8, /// The broadcast address of the interface. pub broadcast: Option, } impl Ifv4Addr { /// Check whether this is a loopback address. #[must_use] pub const fn is_loopback(&self) -> bool { self.ip.is_loopback() } /// Check whether this is a link local address. #[must_use] pub const fn is_link_local(&self) -> bool { self.ip.is_link_local() } } /// Details about the ipv6 address of an interface on this host. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct Ifv6Addr { /// The IP address of the interface. pub ip: Ipv6Addr, /// The netmask of the interface. pub netmask: Ipv6Addr, /// The CIDR prefix of the interface. pub prefixlen: u8, /// The broadcast address of the interface. pub broadcast: Option, } impl Ifv6Addr { /// Check whether this is a loopback address. #[must_use] pub const fn is_loopback(&self) -> bool { self.ip.is_loopback() } /// Check whether this is a link local address. #[must_use] pub const fn is_link_local(&self) -> bool { let bytes = self.ip.octets(); bytes[0] == 0xfe && bytes[1] == 0x80 } } #[cfg(not(windows))] mod getifaddrs_posix { use libc::if_nametoindex; use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface}; use crate::posix::{self as ifaddrs, IfAddrs}; use crate::sockaddr; use std::ffi::CStr; use std::io; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// Return a vector of IP details for all the valid interfaces on this host. #[allow(unsafe_code)] pub fn get_if_addrs() -> io::Result> { let mut ret = Vec::::new(); let ifaddrs = IfAddrs::new()?; for ifaddr in ifaddrs.iter() { let addr = match sockaddr::to_ipaddr(ifaddr.ifa_addr) { None => continue, Some(IpAddr::V4(ipv4_addr)) => { let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) { Some(IpAddr::V4(netmask)) => netmask, _ => Ipv4Addr::new(0, 0, 0, 0), }; let broadcast = if (ifaddr.ifa_flags & 2) != 0 { match ifaddrs::do_broadcast(&ifaddr) { Some(IpAddr::V4(broadcast)) => Some(broadcast), _ => None, } } else { None }; let prefixlen = if cfg!(target_endian = "little") { u32::from_le_bytes(netmask.octets()).count_ones() as u8 } else { u32::from_be_bytes(netmask.octets()).count_ones() as u8 }; IfAddr::V4(Ifv4Addr { ip: ipv4_addr, netmask, prefixlen, broadcast, }) } Some(IpAddr::V6(ipv6_addr)) => { let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) { Some(IpAddr::V6(netmask)) => netmask, _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), }; let broadcast = if (ifaddr.ifa_flags & 2) != 0 { match ifaddrs::do_broadcast(&ifaddr) { Some(IpAddr::V6(broadcast)) => Some(broadcast), _ => None, } } else { None }; let prefixlen = if cfg!(target_endian = "little") { u128::from_le_bytes(netmask.octets()).count_ones() as u8 } else { u128::from_be_bytes(netmask.octets()).count_ones() as u8 }; IfAddr::V6(Ifv6Addr { ip: ipv6_addr, netmask, prefixlen, broadcast, }) } }; let name = unsafe { CStr::from_ptr(ifaddr.ifa_name) } .to_string_lossy() .into_owned(); let index = { let index = unsafe { if_nametoindex(ifaddr.ifa_name) }; // From `man if_nametoindex 3`: // The if_nametoindex() function maps the interface name specified in ifname to its // corresponding index. If the specified interface does not exist, it returns 0. if index == 0 { None } else { Some(index) } }; ret.push(Interface { name, addr, index }); } Ok(ret) } } /// Get a list of all the network interfaces on this machine along with their IP info. #[cfg(not(windows))] pub fn get_if_addrs() -> io::Result> { getifaddrs_posix::get_if_addrs() } #[cfg(windows)] mod getifaddrs_windows { use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface}; use crate::sockaddr; use crate::windows::IfAddrs; use std::io; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use windows_sys::Win32::Networking::WinSock::IpDadStatePreferred; /// Return a vector of IP details for all the valid interfaces on this host. pub fn get_if_addrs() -> io::Result> { let mut ret = Vec::::new(); let ifaddrs = IfAddrs::new()?; for ifaddr in ifaddrs.iter() { for addr in ifaddr.unicast_addresses() { if addr.DadState != IpDadStatePreferred { continue; } let addr = match sockaddr::to_ipaddr(addr.Address.lpSockaddr) { None => continue, Some(IpAddr::V4(ipv4_addr)) => { let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0); let mut item_broadcast = None; let item_prefix = addr.OnLinkPrefixLength; // Search prefixes for a prefix matching addr 'prefixloopv4: for prefix in ifaddr.prefixes() { let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr); match ipprefix { Some(IpAddr::V4(ref a)) => { let mut netmask: [u8; 4] = [0; 4]; for (n, netmask_elt) in netmask .iter_mut() .enumerate() .take((prefix.PrefixLength as usize + 7) / 8) { let x_byte = ipv4_addr.octets()[n]; let y_byte = a.octets()[n]; for m in 0..8 { if (n * 8) + m > prefix.PrefixLength as usize { break; } let bit = 1 << (7 - m); if (x_byte & bit) == (y_byte & bit) { *netmask_elt |= bit; } else { continue 'prefixloopv4; } } } item_netmask = Ipv4Addr::new( netmask[0], netmask[1], netmask[2], netmask[3], ); let mut broadcast: [u8; 4] = ipv4_addr.octets(); for n in 0..4 { broadcast[n] |= !netmask[n]; } item_broadcast = Some(Ipv4Addr::new( broadcast[0], broadcast[1], broadcast[2], broadcast[3], )); break 'prefixloopv4; } _ => continue, }; } IfAddr::V4(Ifv4Addr { ip: ipv4_addr, netmask: item_netmask, prefixlen: item_prefix, broadcast: item_broadcast, }) } Some(IpAddr::V6(ipv6_addr)) => { let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); let item_prefix = addr.OnLinkPrefixLength; // Search prefixes for a prefix matching addr 'prefixloopv6: for prefix in ifaddr.prefixes() { let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr); match ipprefix { Some(IpAddr::V6(ref a)) => { // Iterate the bits in the prefix, if they all match this prefix // is the right one, else try the next prefix let mut netmask: [u16; 8] = [0; 8]; for (n, netmask_elt) in netmask .iter_mut() .enumerate() .take((prefix.PrefixLength as usize + 15) / 16) { let x_word = ipv6_addr.segments()[n]; let y_word = a.segments()[n]; for m in 0..16 { if (n * 16) + m > prefix.PrefixLength as usize { break; } let bit = 1 << (15 - m); if (x_word & bit) == (y_word & bit) { *netmask_elt |= bit; } else { continue 'prefixloopv6; } } } item_netmask = Ipv6Addr::new( netmask[0], netmask[1], netmask[2], netmask[3], netmask[4], netmask[5], netmask[6], netmask[7], ); break 'prefixloopv6; } _ => continue, }; } IfAddr::V6(Ifv6Addr { ip: ipv6_addr, netmask: item_netmask, prefixlen: item_prefix, broadcast: None, }) } }; let index = match addr { IfAddr::V4(_) => ifaddr.ipv4_index(), IfAddr::V6(_) => ifaddr.ipv6_index(), }; ret.push(Interface { name: ifaddr.name(), addr, index, adapter_name: ifaddr.adapter_name(), }); } } Ok(ret) } } /// Get a list of all the network interfaces on this machine along with their IP info. #[cfg(windows)] pub fn get_if_addrs() -> io::Result> { getifaddrs_windows::get_if_addrs() } #[cfg(not(any( all( target_vendor = "apple", any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos" ) ), target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" )))] #[cfg_attr( docsrs, doc(cfg(any( not(target_vendor = "apple"), not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd") ))) )] mod if_change_notifier { use super::Interface; use std::collections::HashSet; use std::io; use std::time::{Duration, Instant}; #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum IfChangeType { Added(Interface), Removed(Interface), } #[cfg(windows)] type InternalIfChangeNotifier = crate::windows::WindowsIfChangeNotifier; #[cfg(not(windows))] type InternalIfChangeNotifier = crate::posix_not_apple::PosixIfChangeNotifier; /// (Not available on iOS/macOS) A utility to monitor for interface changes /// and report them, so you can handle events such as WiFi /// disconnection/flight mode/route changes pub struct IfChangeNotifier { inner: InternalIfChangeNotifier, last_ifs: HashSet, } impl IfChangeNotifier { /// Create a new interface change notifier. Returns an OS specific error /// if the network notifier could not be set up. pub fn new() -> io::Result { Ok(Self { inner: InternalIfChangeNotifier::new()?, last_ifs: HashSet::from_iter(super::get_if_addrs()?), }) } /// (Not available on iOS/macOS) Block until the OS reports that the /// network interface list has changed, or until an optional timeout. /// /// For example, if an ethernet connector is plugged/unplugged, or a /// WiFi network is connected to. /// /// The changed interfaces are returned. If an interface has both IPv4 /// and IPv6 addresses, you can expect both of them to be returned from /// a single call to `wait`. /// /// Returns an [`io::ErrorKind::WouldBlock`] error on timeout, or /// another error if the network notifier could not be read from. pub fn wait(&mut self, timeout: Option) -> io::Result> { let start = Instant::now(); loop { self.inner .wait(timeout.map(|t| t.saturating_sub(start.elapsed())))?; // something has changed - now we find out what (or whether it was spurious) let new_ifs = HashSet::from_iter(super::get_if_addrs()?); let mut changes: Vec = new_ifs .difference(&self.last_ifs) .cloned() .map(IfChangeType::Added) .collect(); changes.extend( self.last_ifs .difference(&new_ifs) .cloned() .map(IfChangeType::Removed), ); self.last_ifs = new_ifs; if !changes.is_empty() { return Ok(changes); } } } } } #[cfg(not(any( all( target_vendor = "apple", any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos" ) ), target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" )))] #[cfg_attr( docsrs, doc(cfg(any( not(target_vendor = "apple"), not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd") ))) )] pub use if_change_notifier::{IfChangeNotifier, IfChangeType}; #[cfg(test)] mod tests { use super::{get_if_addrs, Interface}; use std::io::Read; use std::net::{IpAddr, Ipv4Addr}; use std::process::{Command, Stdio}; use std::str::FromStr; use std::thread; use std::time::Duration; fn list_system_interfaces(cmd: &str, arg: &str) -> String { let start_cmd = if arg.is_empty() { Command::new(cmd).stdout(Stdio::piped()).spawn() } else { Command::new(cmd).arg(arg).stdout(Stdio::piped()).spawn() }; let mut process = match start_cmd { Err(why) => { println!("couldn't start cmd {} : {}", cmd, why); return String::new(); } Ok(process) => process, }; thread::sleep(Duration::from_millis(1000)); let _ = process.kill(); let result: Vec = process .stdout .unwrap() .bytes() .map(|x| x.unwrap()) .collect(); String::from_utf8(result).unwrap() } #[cfg(windows)] fn list_system_addrs() -> Vec { use std::net::Ipv6Addr; list_system_interfaces("ipconfig", "") .lines() .filter_map(|line| { println!("{}", line); if line.contains("Address") && !line.contains("Link-local") { let addr_s: Vec<&str> = line.split(" : ").collect(); if line.contains("IPv6") { return Some(IpAddr::V6(Ipv6Addr::from_str(addr_s[1]).unwrap())); } else if line.contains("IPv4") { return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap())); } } None }) .collect() } #[cfg(any(target_os = "linux", target_os = "android"))] fn list_system_addrs() -> Vec { list_system_interfaces("ip", "addr") .lines() .filter_map(|line| { println!("{}", line); if line.contains("inet ") { let addr_s: Vec<&str> = line.split_whitespace().collect(); let addr: Vec<&str> = addr_s[1].split('/').collect(); return Some(IpAddr::V4(Ipv4Addr::from_str(addr[0]).unwrap())); } None }) .collect() } #[cfg(any( target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", all( target_vendor = "apple", any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos" ) ) ))] fn list_system_addrs() -> Vec { list_system_interfaces("ifconfig", "") .lines() .filter_map(|line| { println!("{}", line); if line.contains("inet ") { let addr_s: Vec<&str> = line.split_whitespace().collect(); return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap())); } None }) .collect() } #[test] fn test_get_if_addrs() { let ifaces = get_if_addrs().unwrap(); println!("Local interfaces:"); println!("{:#?}", ifaces); // at least one loop back address assert!( 1 <= ifaces .iter() .filter(|interface| interface.is_loopback()) .count() ); // if index is set, it is non-zero for interface in &ifaces { if let Some(idx) = interface.index { assert!(idx > 0); } } // one address of IpV4(127.0.0.1) let is_loopback = |interface: &&Interface| interface.addr.ip() == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); assert_eq!(1, ifaces.iter().filter(is_loopback).count()); // each system address shall be listed let system_addrs = list_system_addrs(); assert!(!system_addrs.is_empty()); for addr in system_addrs { let mut listed = false; println!("\n checking whether {:?} has been properly listed \n", addr); for interface in &ifaces { if interface.addr.ip() == addr { listed = true; } assert!(interface.index.is_some()); } assert!(listed); } } #[cfg(not(any( all( target_vendor = "apple", any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos" ) ), target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" )))] #[test] fn test_if_notifier() { // Check that the interface notifier can start up and time out. No easy // way to programmatically add/remove interfaces, so set a timeout of 0. // Will cover a potential case of inadequate setup leading to an // immediate change notification. // // There is a small race condition from creation -> check that an // interface change *actually* occurs, so this test may spuriously fail // extremely rarely. let notifier = crate::IfChangeNotifier::new(); assert!(notifier.is_ok()); let mut notifier = notifier.unwrap(); assert!(notifier.wait(Some(Duration::ZERO)).is_err()); } } if-addrs-0.13.3/src/posix.rs000064400000000000000000000046461046102023000137120ustar 00000000000000// Copyright 2018 MaidSafe.net limited. // // This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, // modified, or distributed except according to those terms. Please review the Licences for the // specific language governing permissions and limitations relating to use of the SAFE Network // Software. use crate::sockaddr; use libc::{freeifaddrs, getifaddrs, ifaddrs}; use std::net::IpAddr; use std::{io, mem}; pub fn do_broadcast(ifaddr: &ifaddrs) -> Option { // On Linux-like systems, `ifa_ifu` is a union of `*ifa_dstaddr` and `*ifa_broadaddr`. #[cfg(any( target_os = "linux", target_os = "android", target_os = "l4re", target_os = "emscripten", target_os = "fuchsia", target_os = "hurd", ))] let sockaddr = ifaddr.ifa_ifu; // On BSD-like and embedded systems, only `ifa_dstaddr` is available. #[cfg(not(any( target_os = "linux", target_os = "android", target_os = "l4re", target_os = "emscripten", target_os = "fuchsia", target_os = "hurd", )))] let sockaddr = ifaddr.ifa_dstaddr; sockaddr::to_ipaddr(sockaddr) } pub struct IfAddrs { inner: *mut ifaddrs, } impl IfAddrs { #[allow(unsafe_code, clippy::new_ret_no_self)] pub fn new() -> io::Result { let mut ifaddrs = mem::MaybeUninit::uninit(); unsafe { if -1 == getifaddrs(ifaddrs.as_mut_ptr()) { return Err(io::Error::last_os_error()); } Ok(Self { inner: ifaddrs.assume_init(), }) } } pub fn iter(&self) -> IfAddrsIterator { IfAddrsIterator { next: self.inner } } } impl Drop for IfAddrs { #[allow(unsafe_code)] fn drop(&mut self) { unsafe { freeifaddrs(self.inner); } } } pub struct IfAddrsIterator { next: *mut ifaddrs, } impl Iterator for IfAddrsIterator { type Item = ifaddrs; #[allow(unsafe_code)] fn next(&mut self) -> Option { if self.next.is_null() { return None; }; Some(unsafe { let result = *self.next; self.next = (*self.next).ifa_next; result }) } } if-addrs-0.13.3/src/posix_not_apple.rs000064400000000000000000000054561046102023000157530ustar 00000000000000use std::io; use std::mem; use std::time::Duration; use libc::{ bind, c_int, c_void, close, recv, setsockopt, sockaddr_nl, socket, socklen_t, ssize_t, timeval, AF_NETLINK, NETLINK_ROUTE, SOCK_RAW, SOL_SOCKET, SO_RCVTIMEO, }; #[repr(transparent)] struct NetlinkSocket(c_int); impl NetlinkSocket { fn new() -> io::Result { Ok(NetlinkSocket(check_io(unsafe { socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) })?)) } } impl Drop for NetlinkSocket { fn drop(&mut self) { unsafe { close(self.0) }; } } fn check_io(res: c_int) -> io::Result { if res < 0 { Err(io::Error::last_os_error()) } else { Ok(res) } } fn check_recv(res: ssize_t) -> io::Result { if res < 0 { Err(io::Error::last_os_error()) } else { Ok(res) } } pub struct PosixIfChangeNotifier { socket: NetlinkSocket, } impl PosixIfChangeNotifier { pub fn new() -> io::Result { let socket = NetlinkSocket::new()?; let mut sockaddr: sockaddr_nl = unsafe { mem::zeroed() }; sockaddr.nl_family = AF_NETLINK as u16; sockaddr.nl_groups = 1; // RTNLGRP_LINK check_io(unsafe { bind( socket.0, &sockaddr as *const _ as *const libc::sockaddr, mem::size_of::() as libc::socklen_t, ) })?; Ok(Self { socket }) } pub fn wait(&self, timeout: Option) -> io::Result<()> { // TODO: When MSRV moves beyond Rust 1.66, this can be cleaner as // let mut socket = UdpSocket::from_raw_fd(socket); // socket.set_read_timeout(timeout)?; // socket.recv(&mut buf)?; let timeout = if let Some(timeout) = timeout { let mut t = timeval { tv_sec: timeout.as_secs().try_into().expect("timeout overflow"), tv_usec: timeout .subsec_micros() .try_into() .expect("timeout overflow"), }; // a timeout of 0 is infinity, so if the requested duration is too // small, make it nonzero if t.tv_sec == 0 && t.tv_usec == 0 { t.tv_usec = 1; } t } else { timeval { tv_sec: 0, tv_usec: 0, } }; check_io(unsafe { setsockopt( self.socket.0, SOL_SOCKET, SO_RCVTIMEO, core::ptr::addr_of!(timeout) as *const _, mem::size_of::() as socklen_t, ) })?; let mut buf = [0u8; 65536]; check_recv(unsafe { recv(self.socket.0, buf.as_mut_ptr() as *mut c_void, buf.len(), 0) })?; Ok(()) } } if-addrs-0.13.3/src/sockaddr.rs000064400000000000000000000075731046102023000143440ustar 00000000000000// Copyright 2018 MaidSafe.net limited. // // This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, // modified, or distributed except according to those terms. Please review the Licences for the // specific language governing permissions and limitations relating to use of the SAFE Network // Software. #[cfg(not(windows))] use libc::{sockaddr, sockaddr_in, sockaddr_in6, AF_INET, AF_INET6}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::ptr::NonNull; #[cfg(windows)] use windows_sys::Win32::Networking::WinSock::{ AF_INET, AF_INET6, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in, SOCKADDR_IN6 as sockaddr_in6, }; pub fn to_ipaddr(sockaddr: *const sockaddr) -> Option { if sockaddr.is_null() { return None; } SockAddr::new(sockaddr)?.as_ipaddr() } // Wrapper around a sockaddr pointer. Guaranteed to not be null. struct SockAddr { inner: NonNull, } impl SockAddr { #[allow(clippy::new_ret_no_self)] fn new(sockaddr: *const sockaddr) -> Option { NonNull::new(sockaddr as *mut _).map(|inner| Self { inner }) } #[cfg(not(windows))] fn as_ipaddr(&self) -> Option { match self.sockaddr_in() { Some(SockAddrIn::In(sa)) => { let b = sa.sin_addr.s_addr.to_ne_bytes(); Some(IpAddr::V4(Ipv4Addr::new(b[0], b[1], b[2], b[3]))) } Some(SockAddrIn::In6(sa)) => { // Ignore all fe80:: addresses as these are link locals #[cfg(not(feature = "link-local"))] if sa.sin6_addr.s6_addr[0] == 0xfe && sa.sin6_addr.s6_addr[1] == 0x80 { return None; } Some(IpAddr::V6(Ipv6Addr::from(sa.sin6_addr.s6_addr))) } None => None, } } #[cfg(windows)] fn as_ipaddr(&self) -> Option { match self.sockaddr_in() { Some(SockAddrIn::In(sa)) => { let s_addr = unsafe { sa.sin_addr.S_un.S_addr }; // Ignore all 169.254.x.x addresses as these are not active interfaces #[cfg(not(feature = "link-local"))] if s_addr & 65535 == 0xfea9 { return None; } let b = s_addr.to_ne_bytes(); Some(IpAddr::V4(Ipv4Addr::new(b[0], b[1], b[2], b[3]))) } Some(SockAddrIn::In6(sa)) => { let s6_addr = unsafe { sa.sin6_addr.u.Byte }; // Ignore all fe80:: addresses as these are link locals #[cfg(not(feature = "link-local"))] if s6_addr[0] == 0xfe && s6_addr[1] == 0x80 { return None; } Some(IpAddr::V6(Ipv6Addr::from(s6_addr.clone()))) } None => None, } } fn sockaddr_in(&self) -> Option { const AF_INET_U32: u32 = AF_INET as u32; const AF_INET6_U32: u32 = AF_INET6 as u32; match self.sa_family() { AF_INET_U32 => Some(SockAddrIn::In(self.sa_in())), AF_INET6_U32 => Some(SockAddrIn::In6(self.sa_in6())), _ => None, } } #[allow(unsafe_code)] fn sa_family(&self) -> u32 { unsafe { u32::from(self.inner.as_ref().sa_family) } } #[allow(unsafe_code)] #[allow(clippy::cast_ptr_alignment)] fn sa_in(&self) -> sockaddr_in { unsafe { *(self.inner.as_ptr() as *const sockaddr_in) } } #[allow(unsafe_code)] #[allow(clippy::cast_ptr_alignment)] fn sa_in6(&self) -> sockaddr_in6 { unsafe { *(self.inner.as_ptr() as *const sockaddr_in6) } } } enum SockAddrIn { In(sockaddr_in), In6(sockaddr_in6), } if-addrs-0.13.3/src/windows.rs000064400000000000000000000210661046102023000142350ustar 00000000000000// Copyright 2018 MaidSafe.net limited. // // This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, // modified, or distributed except according to those terms. Please review the Licences for the // specific language governing permissions and limitations relating to use of the SAFE Network // Software. use std::ffi::{c_void, CStr}; use std::sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender}; use std::time::Duration; use std::{io, ptr}; use windows_sys::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS, HANDLE}; use windows_sys::Win32::NetworkManagement::IpHelper::{ CancelMibChangeNotify2, GetAdaptersAddresses, NotifyIpInterfaceChange, GAA_FLAG_INCLUDE_PREFIX, GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_DNS_SERVER, GAA_FLAG_SKIP_MULTICAST, IP_ADAPTER_ADDRESSES_LH, IP_ADAPTER_PREFIX_XP, IP_ADAPTER_UNICAST_ADDRESS_LH, MIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE, }; use windows_sys::Win32::Networking::WinSock::AF_UNSPEC; use windows_sys::Win32::System::Memory::{ GetProcessHeap, HeapAlloc, HeapFree, HEAP_NONE, HEAP_ZERO_MEMORY, }; #[repr(transparent)] pub struct IpAdapterAddresses(*const IP_ADAPTER_ADDRESSES_LH); impl IpAdapterAddresses { #[allow(unsafe_code)] pub fn name(&self) -> String { let len = (0..) .take_while(|&i| unsafe { *(*self.0).FriendlyName.offset(i) } != 0) .count(); let slice = unsafe { std::slice::from_raw_parts((*self.0).FriendlyName, len) }; String::from_utf16_lossy(slice) } #[allow(unsafe_code)] pub fn adapter_name(&self) -> String { unsafe { CStr::from_ptr((*self.0).AdapterName as _) } .to_string_lossy() .into_owned() } pub fn ipv4_index(&self) -> Option { let if_index = unsafe { (*self.0).Anonymous1.Anonymous.IfIndex }; if if_index == 0 { None } else { Some(if_index) } } pub fn ipv6_index(&self) -> Option { let if_index = unsafe { (*self.0).Ipv6IfIndex }; if if_index == 0 { None } else { Some(if_index) } } pub fn prefixes(&self) -> PrefixesIterator { PrefixesIterator { _head: unsafe { &*self.0 }, next: unsafe { (*self.0).FirstPrefix }, } } pub fn unicast_addresses(&self) -> UnicastAddressesIterator { UnicastAddressesIterator { _head: unsafe { &*self.0 }, next: unsafe { (*self.0).FirstUnicastAddress }, } } } pub struct IfAddrs { inner: IpAdapterAddresses, } impl IfAddrs { #[allow(unsafe_code)] pub fn new() -> io::Result { let mut buffersize = 15000; let mut ifaddrs: *mut IP_ADAPTER_ADDRESSES_LH; loop { unsafe { ifaddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffersize as _) as *mut IP_ADAPTER_ADDRESSES_LH; if ifaddrs.is_null() { panic!("Failed to allocate buffer in get_if_addrs()"); } let retcode = GetAdaptersAddresses( 0, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX, ptr::null_mut(), ifaddrs, &mut buffersize, ); match retcode { ERROR_SUCCESS => break, ERROR_BUFFER_OVERFLOW => { HeapFree(GetProcessHeap(), HEAP_NONE, ifaddrs as _); buffersize *= 2; continue; } _ => { HeapFree(GetProcessHeap(), HEAP_NONE, ifaddrs as _); return Err(io::Error::last_os_error()); } } } } Ok(Self { inner: IpAdapterAddresses(ifaddrs), }) } pub fn iter(&self) -> IfAddrsIterator { IfAddrsIterator { _head: self, next: self.inner.0, } } } impl Drop for IfAddrs { #[allow(unsafe_code)] fn drop(&mut self) { unsafe { HeapFree(GetProcessHeap(), HEAP_NONE, self.inner.0 as _); } } } pub struct IfAddrsIterator<'a> { _head: &'a IfAddrs, next: *const IP_ADAPTER_ADDRESSES_LH, } impl<'a> Iterator for IfAddrsIterator<'a> { type Item = IpAdapterAddresses; #[allow(unsafe_code)] fn next(&mut self) -> Option { if self.next.is_null() { return None; }; Some(unsafe { let result = &*self.next; self.next = (*self.next).Next; IpAdapterAddresses(result) }) } } pub struct PrefixesIterator<'a> { _head: &'a IP_ADAPTER_ADDRESSES_LH, next: *const IP_ADAPTER_PREFIX_XP, } impl<'a> Iterator for PrefixesIterator<'a> { type Item = &'a IP_ADAPTER_PREFIX_XP; #[allow(unsafe_code)] fn next(&mut self) -> Option { if self.next.is_null() { return None; }; Some(unsafe { let result = &*self.next; self.next = (*self.next).Next; result }) } } pub struct UnicastAddressesIterator<'a> { _head: &'a IP_ADAPTER_ADDRESSES_LH, next: *const IP_ADAPTER_UNICAST_ADDRESS_LH, } impl<'a> Iterator for UnicastAddressesIterator<'a> { type Item = &'a IP_ADAPTER_UNICAST_ADDRESS_LH; #[allow(unsafe_code)] fn next(&mut self) -> Option { if self.next.is_null() { return None; }; Some(unsafe { let result = &*self.next; self.next = (*self.next).Next; result }) } } pub struct WindowsIfChangeNotifier { handle: HANDLE, // maintain constant memory address for callback fn tx: *mut Sender<()>, rx: Receiver<()>, } impl WindowsIfChangeNotifier { pub fn new() -> io::Result { let (tx, rx) = channel(); let mut ret = Self { handle: 0, tx: Box::into_raw(Box::new(tx)), rx, }; let stat = unsafe { // Notes on the function used here and alternatives that were // considered: // // NotifyAddrChange works pretty well, but only for IPv4, and the // API itself is a little awkward, requiring overlapped IO // // NotifyRouteChange2 doesn't catch interfaces going away (e.g. // unplugging ethernet, going into airplane mode) // // WlanRegisterNotification is only for WiFi // // Monitoring changes to MSFT_NetAdapter is possible, but requires // WMI/COM, which is undesirable // // NotifyIpInterfaceChange produces several spurious messages // (mostly related to WiFi speed, it seems), but they aren't too // frequent (at most, I've seen a batch of 3 every 10 seconds), so // don't pose a performance concern NotifyIpInterfaceChange( AF_UNSPEC, Some(if_change_callback), ret.tx as *const c_void, 0, &mut ret.handle, ) }; if stat != 0 { Err(io::Error::from_raw_os_error(stat as i32)) } else { Ok(ret) } } pub fn wait(&self, timeout: Option) -> io::Result<()> { if let Some(timeout) = timeout { self.rx.recv_timeout(timeout) } else { self.rx.recv().map_err(RecvTimeoutError::from) } .map_err(|_| io::Error::new(io::ErrorKind::WouldBlock, "Timed out")) } } impl Drop for WindowsIfChangeNotifier { fn drop(&mut self) { if self.handle != 0 { unsafe { CancelMibChangeNotify2(self.handle) }; } unsafe { drop(Box::from_raw(self.tx)) }; } } unsafe extern "system" fn if_change_callback( ctx: *const c_void, _row: *const MIB_IPINTERFACE_ROW, _notificationtype: MIB_NOTIFICATION_TYPE, ) { if let Some(tx) = (ctx as *const Sender<()>).as_ref() { tx.send(()).ok(); }; // note: `row` not used, as for all changes that we care for (interface // add/remove), all the member values are 0, so it's useless. }