-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
get_active_network_interface_address
- Loading branch information
Showing
1 changed file
with
102 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,120 @@ | ||
#[cfg(target_os = "windows")] | ||
pub fn get_active_network_interface_address() -> Option<std::net::IpAddr> { | ||
use std::ptr; | ||
pub fn get_active_network_interface_address() -> std::io::Result<std::net::IpAddr> { | ||
use std::net::SocketAddr; | ||
use windows::Win32::{ | ||
Foundation::{ERROR_BUFFER_OVERFLOW, NO_ERROR}, | ||
Foundation::{ERROR_BUFFER_OVERFLOW, WIN32_ERROR}, | ||
NetworkManagement::{ | ||
IpHelper::{GetAdaptersAddresses, GET_ADAPTERS_ADDRESSES_FLAGS, IF_TYPE_IEEE80211, IP_ADAPTER_ADDRESSES_LH}, | ||
IpHelper::{ | ||
GetAdaptersAddresses, GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_INCLUDE_PREFIX, IF_TYPE_ETHERNET_CSMACD, IF_TYPE_IEEE80211, | ||
IP_ADAPTER_ADDRESSES_LH, | ||
}, | ||
Ndis::IfOperStatusUp, | ||
}, | ||
Networking::WinSock::{AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6}, | ||
}; | ||
|
||
let mut ipv6 = None; | ||
pub(crate) fn get_adapters_addresses<F>(mut callback: F) -> std::io::Result<()> | ||
where | ||
F: FnMut(IP_ADAPTER_ADDRESSES_LH) -> std::io::Result<()>, | ||
{ | ||
let mut size = 0; | ||
let flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS; | ||
let family = AF_UNSPEC.0 as u32; | ||
|
||
unsafe { | ||
let mut addresses: *mut IP_ADAPTER_ADDRESSES_LH = ptr::null_mut(); | ||
let mut buffer_size: u32 = 0; | ||
let flags = GET_ADAPTERS_ADDRESSES_FLAGS::default(); | ||
let result = GetAdaptersAddresses(AF_UNSPEC.0 as u32, flags, Some(ptr::null_mut()), Some(addresses), &mut buffer_size); | ||
if result != ERROR_BUFFER_OVERFLOW.0 { | ||
return None; | ||
// Make an initial call to GetAdaptersAddresses to get the size needed into the size variable | ||
let result = unsafe { GetAdaptersAddresses(family, flags, None, None, &mut size) }; | ||
|
||
if WIN32_ERROR(result) != ERROR_BUFFER_OVERFLOW { | ||
WIN32_ERROR(result).ok()?; | ||
} | ||
let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size as usize); | ||
addresses = buffer.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH; | ||
let result = GetAdaptersAddresses(AF_UNSPEC.0 as u32, flags, Some(ptr::null_mut()), Some(addresses), &mut buffer_size); | ||
if result != NO_ERROR.0 { | ||
return None; | ||
// Allocate memory for the buffer | ||
let mut addresses: Vec<u8> = vec![0; (size + 4) as usize]; | ||
|
||
// Make a second call to GetAdaptersAddresses to get the actual data we want | ||
let result = unsafe { | ||
let addr = Some(addresses.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH); | ||
GetAdaptersAddresses(family, flags, None, addr, &mut size) | ||
}; | ||
|
||
WIN32_ERROR(result).ok()?; | ||
|
||
// If successful, output some information from the data we received | ||
let mut current_addresses = addresses.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH; | ||
while !current_addresses.is_null() { | ||
unsafe { | ||
callback(*current_addresses)?; | ||
current_addresses = (*current_addresses).Next; | ||
} | ||
} | ||
let mut current_address = addresses; | ||
while !current_address.is_null() { | ||
let adapter_address = &*current_address; | ||
if adapter_address.OperStatus == IfOperStatusUp && adapter_address.IfType == IF_TYPE_IEEE80211 { | ||
let mut current_ip_address = adapter_address.FirstUnicastAddress; | ||
while !current_ip_address.is_null() { | ||
let ip_address = &*current_ip_address; | ||
let sockaddr_ptr = ip_address.Address.lpSockaddr; | ||
let sockaddr = &*(sockaddr_ptr as *const SOCKADDR); | ||
match sockaddr.sa_family { | ||
AF_INET => { | ||
let sockaddr_in = &*(sockaddr_ptr as *const SOCKADDR_IN); | ||
let ip = sockaddr_in.sin_addr.S_un.S_addr; | ||
let ip_bytes = ip.to_ne_bytes(); | ||
let ip_addr = std::net::Ipv4Addr::from(ip_bytes); | ||
return Some(std::net::IpAddr::V4(ip_addr)); | ||
} | ||
AF_INET6 => { | ||
let sockaddr_in6 = &*(sockaddr_ptr as *const SOCKADDR_IN6); | ||
let ip = sockaddr_in6.sin6_addr.u.Byte; | ||
let ip_addr = std::net::Ipv6Addr::from(ip); | ||
ipv6 = Some(std::net::IpAddr::V6(ip_addr)); | ||
} | ||
_ => {} | ||
} | ||
current_ip_address = ip_address.Next; | ||
Ok(()) | ||
} | ||
|
||
pub(crate) unsafe fn sockaddr_to_socket_addr(sock_addr: *const SOCKADDR) -> std::io::Result<SocketAddr> { | ||
use std::io::{Error, ErrorKind}; | ||
let address = match (*sock_addr).sa_family { | ||
AF_INET => sockaddr_in_to_socket_addr(&*(sock_addr as *const SOCKADDR_IN)), | ||
AF_INET6 => sockaddr_in6_to_socket_addr(&*(sock_addr as *const SOCKADDR_IN6)), | ||
_ => return Err(Error::new(ErrorKind::Other, "Unsupported address type")), | ||
}; | ||
Ok(address) | ||
} | ||
|
||
pub(crate) unsafe fn sockaddr_in_to_socket_addr(sockaddr_in: &SOCKADDR_IN) -> SocketAddr { | ||
let ip_bytes = sockaddr_in.sin_addr.S_un.S_addr.to_ne_bytes(); | ||
let ip = std::net::IpAddr::from(ip_bytes); | ||
let port = u16::from_be(sockaddr_in.sin_port); | ||
SocketAddr::new(ip, port) | ||
} | ||
|
||
pub(crate) unsafe fn sockaddr_in6_to_socket_addr(sockaddr_in6: &SOCKADDR_IN6) -> SocketAddr { | ||
let ip = std::net::IpAddr::from(sockaddr_in6.sin6_addr.u.Byte); | ||
let port = u16::from_be(sockaddr_in6.sin6_port); | ||
SocketAddr::new(ip, port) | ||
} | ||
|
||
let mut addrs = vec![]; | ||
get_adapters_addresses(|adapter| { | ||
if adapter.OperStatus == IfOperStatusUp && (adapter.IfType == IF_TYPE_IEEE80211 || adapter.IfType == IF_TYPE_ETHERNET_CSMACD) { | ||
let mut iter_address = adapter.FirstUnicastAddress; | ||
while !iter_address.is_null() { | ||
let address = unsafe { &*iter_address }; | ||
{ | ||
let sockaddr_ptr = address.Address.lpSockaddr; | ||
let sockaddr = unsafe { &*(sockaddr_ptr as *const SOCKADDR) }; | ||
let a = unsafe { sockaddr_to_socket_addr(sockaddr)? }; | ||
addrs.push(a.ip()); | ||
} | ||
iter_address = address.Next; | ||
} | ||
current_address = adapter_address.Next; | ||
} | ||
} | ||
ipv6 | ||
Ok(()) | ||
})?; | ||
|
||
// find out ipv4 address or find out ipv6 address or return error | ||
use std::io::ErrorKind::NotFound; | ||
let addr = addrs.clone().into_iter().find(|addr| addr.is_ipv4()).unwrap_or( | ||
addrs | ||
.into_iter() | ||
.find(|addr| addr.is_ipv6()) | ||
.ok_or(std::io::Error::new(NotFound, "no active network interface address"))?, | ||
); | ||
|
||
Ok(addr) | ||
} | ||
|
||
#[cfg(target_family = "unix")] | ||
pub fn get_active_network_interface_address() -> Option<std::net::IpAddr> { | ||
pnet::datalink::interfaces().into_iter().find_map(|interface| { | ||
if interface.is_up() && interface.is_broadcast() && interface.is_running() && !interface.is_loopback() { | ||
let mut ips = interface.ips.clone(); | ||
ips.sort(); | ||
ips.iter().map(|ip_network| ip_network.ip()).next() | ||
} else { | ||
None | ||
} | ||
}) | ||
pub fn get_active_network_interface_address() -> std::io::Result<std::net::IpAddr> { | ||
use std::io::ErrorKind::NotFound; | ||
pnet::datalink::interfaces() | ||
.into_iter() | ||
.find_map(|interface| { | ||
if interface.is_up() && interface.is_broadcast() && interface.is_running() && !interface.is_loopback() { | ||
let mut ips = interface.ips.clone(); | ||
ips.sort(); | ||
ips.iter().map(|ip_network| ip_network.ip()).next() | ||
} else { | ||
None | ||
} | ||
}) | ||
.ok_or(std::io::Error::new(NotFound, "no active network interface address")) | ||
} |