From 60feaff4becf4a39e08cccdf0e48b30ccc8c898e Mon Sep 17 00:00:00 2001 From: shellrow <81893184+shellrow@users.noreply.github.com> Date: Sun, 9 Jan 2022 01:38:47 +0900 Subject: [PATCH] [WIP] Improve Windows support Get gateway information using IP Helper API --- src/interface.rs | 5 ++ src/os/windows.rs | 169 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 149 insertions(+), 25 deletions(-) diff --git a/src/interface.rs b/src/interface.rs index 4e8d8d6..8d2616c 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -19,6 +19,9 @@ impl MacAddr { pub fn address(&self) -> String { format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", self.0,self.1,self.2,self.3,self.4,self.5) } + pub fn zero() -> MacAddr { + MacAddr(0,0,0,0,0,0) + } } impl std::fmt::Display for MacAddr { @@ -33,6 +36,7 @@ impl std::fmt::Display for MacAddr { pub struct Interface { pub index: u32, pub name: String, + pub description: Option, pub mac_addr: Option, pub ipv4: Vec, pub ipv6: Vec, @@ -70,6 +74,7 @@ pub fn get_default_interface() -> Result { let interface: Interface = Interface{ index: iface.index, name: iface.name, + description: None, mac_addr: mac_addr, ipv4: ipv4_vec, ipv6: ipv6_vec, diff --git a/src/os/windows.rs b/src/os/windows.rs index 60cf672..573f4fc 100644 --- a/src/os/windows.rs +++ b/src/os/windows.rs @@ -1,14 +1,64 @@ -use windows::Win32::NetworkManagement::IpHelper::{GetAdaptersInfo, IP_ADAPTER_INFO, IP_ADDR_STRING}; +use windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, NO_ERROR}; +use windows::Win32::NetworkManagement::IpHelper::{GetAdaptersInfo, IP_ADAPTER_INFO, IP_ADDR_STRING, SendARP}; use std::convert::TryInto; use std::mem; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::ffi::CStr; +use core::ffi::c_void; -pub const ERROR_BUFFER_OVERFLOW: u32 = 111; -pub const NO_ERROR: u32 = 0; +use crate::interface::{Interface, MacAddr}; +use crate::gateway::Gateway; -// Get network interface information using the IP Helper API -// TODO: Make more rusty way... +pub const EXCEPTION_INTERFACE_INDEX: u32 = 0; + +// Convert C string to Rust string without trailing null bytes +fn bytes_to_string(bytes: &[u8]) -> String { + let result: String = match CStr::from_bytes_with_nul(bytes) { + Ok(cstr) => { + match cstr.to_str() { + Ok(rstr) => rstr.to_string(), + Err(_) => cstr.to_string_lossy().replace("\u{0}", "").to_string(), + } + }, + Err(_) => { + String::from_utf8_lossy(bytes).replace("\u{0}", "").to_string() + } + }; + result +} + +#[cfg(target_endian = "little")] +fn htonl(val : u32) -> u32 { + let o3 = (val >> 24) as u8; + let o2 = (val >> 16) as u8; + let o1 = (val >> 8) as u8; + let o0 = val as u8; + (o0 as u32) << 24 | (o1 as u32) << 16 | (o2 as u32) << 8 | (o3 as u32) +} + +#[cfg(target_endian = "big")] +fn htonl(val : u32) -> u32 { + val +} + +fn get_mac_through_arp(src_ip: Ipv4Addr, dst_ip: Ipv4Addr) -> MacAddr { + let src_ip_int: u32 = htonl(u32::from(src_ip)); + let dst_ip_int: u32 = htonl(u32::from(dst_ip)); + let mut out_buf_len : u32 = 6; + let mut target_mac_addr: [u8; 6] = [0; 6]; + let res = unsafe { SendARP(dst_ip_int, src_ip_int, target_mac_addr.as_mut_ptr() as *mut c_void, &mut out_buf_len) }; + if res == NO_ERROR { + MacAddr::new(target_mac_addr) + }else{ + MacAddr::zero() + } +} + +// Get network interfaces using the IP Helper API +// TODO: Make more rusty ... // Reference: https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersinfo -pub fn get_interfaces() { +pub fn get_interfaces() -> Vec { + let mut interfaces: Vec = vec![]; let mut out_buf_len : u32 = mem::size_of::().try_into().unwrap(); let mut raw_adaptor_mem: Vec = Vec::with_capacity(out_buf_len as usize); let mut p_adaptor: *mut IP_ADAPTER_INFO; @@ -21,49 +71,118 @@ pub fn get_interfaces() { } } if res != NO_ERROR { - //TODO - println!("failed to get adapters info"); - // for test - std::process::exit(1); + return interfaces; } //Enumerate all adapters p_adaptor = unsafe { mem::transmute(&raw_adaptor_mem) }; while p_adaptor as u64 != 0 { unsafe { - let adapter = *p_adaptor; - let adapter_name = String::from_utf8_lossy(&adapter.AdapterName); - let adapter_desc = String::from_utf8_lossy(&adapter.Description); - let mac_addr = adapter.Address.to_vec(); - println!("{} {} {} {} {:?}", adapter.Index, adapter.ComboIndex, adapter_name, adapter_desc, mac_addr); + let adapter: IP_ADAPTER_INFO = *p_adaptor; + if adapter.Index == EXCEPTION_INTERFACE_INDEX{ + p_adaptor = (*p_adaptor).Next; + continue; + } + let adapter_name: String = bytes_to_string(&adapter.AdapterName); + let adapter_desc: String = bytes_to_string(&adapter.Description); + let mac_addr:[u8; 6] = adapter.Address[..6].try_into().unwrap_or([0, 0, 0, 0, 0, 0]); //Enumerate all IPs + let mut ipv4_vec: Vec = vec![]; + let mut ipv6_vec: Vec = vec![]; let mut p_ip_addr: *mut IP_ADDR_STRING; p_ip_addr = mem::transmute(&(*p_adaptor).IpAddressList); while p_ip_addr as u64 != 0 { - let ip_addr_string = *p_ip_addr; - let ip_addr = String::from_utf8_lossy(&ip_addr_string.IpAddress.String); - println!("{}", ip_addr); + let ip_addr_string: IP_ADDR_STRING = *p_ip_addr; + let ip_addr: String = bytes_to_string(&ip_addr_string.IpAddress.String); + match ip_addr.parse::() { + Ok(ip_addr) => { + match ip_addr { + IpAddr::V4(ipv4_addr) => { + ipv4_vec.push(ipv4_addr); + }, + IpAddr::V6(ipv6_addr) => { + ipv6_vec.push(ipv6_addr); + } + } + }, + Err(_) => {}, + } p_ip_addr = (*p_ip_addr).Next; } //Enumerate all gateways + let mut gateway_ips: Vec = vec![]; let mut p_gateway_addr: *mut IP_ADDR_STRING; p_gateway_addr = mem::transmute(&(*p_adaptor).GatewayList); while p_gateway_addr as u64 != 0 { - let gateway_addr_string = *p_gateway_addr; - let gateway_addr = String::from_utf8_lossy(&gateway_addr_string.IpAddress.String); - println!("{}", gateway_addr); + let gateway_addr_string: IP_ADDR_STRING = *p_gateway_addr; + let gateway_addr: String = bytes_to_string(&gateway_addr_string.IpAddress.String); + match gateway_addr.parse::() { + Ok(ip_addr) => { + gateway_ips.push(ip_addr); + }, + Err(_) => {}, + } p_gateway_addr = (*p_gateway_addr).Next; } - //TODO + let default_gateway: Option = match gateway_ips.get(0) { + Some(gateway_ip) => { + let gateway_ip: IpAddr = *gateway_ip; + let default_gateway: Option = if gateway_ip != IpAddr::V4(Ipv4Addr::UNSPECIFIED) { + match gateway_ip { + IpAddr::V4(dst_ip) => { + if let Some(src_ip) = ipv4_vec.get(0) { + let mac_addr = get_mac_through_arp(*src_ip, dst_ip); + let gateway = Gateway { + mac_addr: mac_addr, + ip_addr: IpAddr::V4(dst_ip), + }; + Some(gateway) + }else{ + None + } + }, + IpAddr::V6(_dst_ip) => { + None + }, + } + }else{ + None + }; + default_gateway + }, + None => None, + }; + let interface: Interface = Interface{ + index: adapter.Index, + name: adapter_name, + description: Some(adapter_desc), + mac_addr: Some(MacAddr::new(mac_addr)), + ipv4: ipv4_vec, + ipv6: ipv6_vec, + gateway: default_gateway, + }; + interfaces.push(interface); } unsafe { p_adaptor = (*p_adaptor).Next; } } + return interfaces; } #[cfg(test)] mod tests { - use crate::os::windows; + use std::net::Ipv4Addr; + use super::*; + #[test] + fn test_nw_interfaces() { + let interfaces = get_interfaces(); + for interface in interfaces { + println!("{:?}", interface); + } + } #[test] - fn list_nw_interfaces() { - windows::get_interfaces(); + fn test_arp() { + let src_ip: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 2); + let dst_ip: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1); + let mac_addr = get_mac_through_arp(src_ip, dst_ip); + println!("{}", mac_addr); } }