From b64c013a9bee3cbe2dde6bfa479d054982dd3202 Mon Sep 17 00:00:00 2001 From: shellrow <81893184+shellrow@users.noreply.github.com> Date: Sun, 9 Jan 2022 23:16:06 +0900 Subject: [PATCH] Refactoring for unix-like OS --- Cargo.toml | 4 +- README.md | 24 ++-- examples/list_interfaces.rs | 26 ++++ src/gateway.rs | 174 +++++--------------------- src/interface.rs | 133 ++++++-------------- src/lib.rs | 2 +- src/os/mod.rs | 2 +- src/os/unix.rs | 243 +++++++++++++++++++++++++++++++++++- src/os/windows.rs | 2 +- 9 files changed, 346 insertions(+), 264 deletions(-) create mode 100644 examples/list_interfaces.rs diff --git a/Cargo.toml b/Cargo.toml index d21b498..97be1fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,12 @@ keywords = ["network"] categories = ["network-programming"] license = "MIT" -[dependencies] +[target.'cfg(not(windows))'.dependencies] pnet_packet = "0.28" pnet_datalink = "0.28" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = [ "std" ] } -[dependencies.windows] +[target.'cfg(windows)'.dependencies.windows] version = "0.29.0" features = ["Win32_Foundation","Win32_NetworkManagement_IpHelper"] diff --git a/README.md b/README.md index 6498b33..5c7d9a7 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Get default network information Add `default-net` to your dependencies ```toml:Cargo.toml [dependencies] -default-net = "0.5.0" +default-net = "0.6.0" ``` ## Example @@ -28,6 +28,7 @@ fn main(){ println!("Default Interface"); println!("\tIndex: {}", default_interface.index); println!("\tName: {}", default_interface.name); + println!("\tDescription: {:?}", default_interface.description); if let Some(mac_addr) = default_interface.mac_addr { println!("\tMAC: {}", mac_addr); }else{ @@ -50,7 +51,17 @@ fn main(){ } ``` -## Note for Windows users +## Tested on +- Linux + - Kali 2021.1 (VMware) + - Ubuntu 20.04 +- macOS 11.6 +- Windows 10 20H2 + +For more details, see [examples][examples-url] or doc. + + +## For Windows users using v0.5.0 or lower To build [libpnet](https://github.com/libpnet/libpnet) on Windows, follow the instructions below. > ### Windows > * You must use a version of Rust which uses the MSVC toolchain @@ -62,12 +73,3 @@ To build [libpnet](https://github.com/libpnet/libpnet) on Windows, follow the in > in `WpdPack/Lib/x64/Packet.lib`, for the 32 bit toolchain, it is in `WpdPack/Lib/Packet.lib`. [Source](https://github.com/libpnet/libpnet/blob/master/README.md#windows "libpnet#windows") - -## Tested on -- Linux - - Kali 2021.1 (VMware) - - Ubuntu 20.04 -- macOS 11.6 -- Windows 10 20H2 - -For more details, see [examples][examples-url] or doc. diff --git a/examples/list_interfaces.rs b/examples/list_interfaces.rs new file mode 100644 index 0000000..e035159 --- /dev/null +++ b/examples/list_interfaces.rs @@ -0,0 +1,26 @@ +use default_net; + +fn main(){ + let interfaces = default_net::get_interfaces(); + for interface in interfaces { + println!("Interface"); + println!("\tIndex: {}", interface.index); + println!("\tName: {}", interface.name); + println!("\tDescription: {:?}", interface.description); + if let Some(mac_addr) = interface.mac_addr { + println!("\tMAC: {}", mac_addr); + }else{ + println!("\tMAC: (Failed to get mac address)"); + } + println!("\tIPv4: {:?}", interface.ipv4); + println!("\tIPv6: {:?}", interface.ipv6); + if let Some(gateway) = interface.gateway { + println!("Gateway"); + println!("\tMAC: {}", gateway.mac_addr); + println!("\tIP: {}", gateway.ip_addr); + }else { + println!("Gateway: (Not found)"); + } + println!(); + } +} diff --git a/src/gateway.rs b/src/gateway.rs index 187c1db..1ff5eb6 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -1,12 +1,8 @@ -use std::net::UdpSocket; -use std::time::{Duration, Instant}; -use std::net::{IpAddr}; -use pnet_packet::Packet; -use crate::interface::{self, MacAddr}; +use std::net::IpAddr; +use crate::interface::{MacAddr, Interface}; +use crate::os; -const TIMEOUT: u64 = 3000; - -/// Struct of default Gateway information +/// Structure of default Gateway information #[derive(Clone, Debug)] pub struct Gateway { pub mac_addr: MacAddr, @@ -15,151 +11,37 @@ pub struct Gateway { /// Get default Gateway pub fn get_default_gateway() -> Result { - let default_idx = match interface::get_default_interface_index() { - Some(idx) => idx, - None => return Err(String::from("Failed to get default interface")), - }; - let interfaces = pnet_datalink::interfaces(); - let interface = interfaces.into_iter().filter(|interface: &pnet_datalink::NetworkInterface| interface.index == default_idx).next().expect("Failed to get Interface"); - let config = pnet_datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: None, - write_timeout: None, - channel_type: pnet_datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - let (mut _tx, mut rx) = match pnet_datalink::channel(&interface, config) { - Ok(pnet_datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => panic!("Unknown channel type"), - Err(e) => panic!("Error happened {}", e), - }; - match send_udp_packet() { - Ok(_) => (), - Err(e) => return Err(format!("Failed to send UDP packet {}", e)), - } - receive_packets(&mut rx) -} - -fn send_udp_packet() -> Result<(), String> { - let buf = [0u8; 0]; - let socket = match UdpSocket::bind("0.0.0.0:0") { - Ok(s) => s, - Err(e) => return Err(format!("Failed to create UDP socket {}", e)), + let local_ip: IpAddr = match os::get_local_ipaddr(){ + Some(local_ip) => local_ip, + None => return Err(String::from("Local IP address not found")), }; - let dest: &str = "1.1.1.1:80"; - match socket.set_ttl(1) { - Ok(_) => (), - Err(e) => return Err(format!("Failed to set TTL {}", e)), - } - match socket.send_to(&buf, dest) { - Ok(_) => (), - Err(e) => return Err(format!("Failed to send data {}", e)), - } - Ok(()) -} - -fn receive_packets(rx: &mut Box) -> Result{ - let timeout = Duration::from_millis(TIMEOUT); - let start_time = Instant::now(); - loop { - match rx.next() { - Ok(frame) => { - let frame = match pnet_packet::ethernet::EthernetPacket::new(frame) { - Some(f) => f, - None => return Err(String::from("Failed to read packet")), - }; - match frame.get_ethertype() { - pnet_packet::ethernet::EtherTypes::Ipv4 => { - if let Some(ip_addr) = ipv4_handler(&frame) { - let gateway = Gateway { - mac_addr: MacAddr::new(frame.get_source().octets()), - ip_addr: ip_addr, - }; - return Ok(gateway); - } - }, - pnet_packet::ethernet::EtherTypes::Ipv6 => { - if let Some(ip_addr) = ipv6_handler(&frame) { - let gateway = Gateway { - mac_addr: MacAddr::new(frame.get_source().octets()), - ip_addr: ip_addr, - }; - return Ok(gateway); - } - }, - _ => {} + let interfaces: Vec = os::interfaces(); + for iface in interfaces { + match local_ip { + IpAddr::V4(local_ipv4) => { + if iface.ipv4.contains(&local_ipv4) { + if let Some(gateway) = iface.gateway { + return Ok(gateway); + } } }, - Err(e) => { - return Err(format!("An error occurred while reading: {}", e)); - } - } - if Instant::now().duration_since(start_time) > timeout { - return Err(String::from("Recieve timeout")); - }else{ - match send_udp_packet() { - Ok(_) => (), - Err(e) => return Err(format!("Failed to send UDP packet {}", e)), - } - } - } -} - -fn ipv4_handler(ethernet: &pnet_packet::ethernet::EthernetPacket) -> Option { - if let Some(packet) = pnet_packet::ipv4::Ipv4Packet::new(ethernet.payload()) { - match packet.get_next_level_protocol() { - pnet_packet::ip::IpNextHeaderProtocols::Icmp => { - return icmp_handler(&packet); - }, - _ => { - None - } - } - }else{ - None - } -} - -fn ipv6_handler(ethernet: &pnet_packet::ethernet::EthernetPacket) -> Option { - if let Some(packet) = pnet_packet::ipv6::Ipv6Packet::new(ethernet.payload()) { - match packet.get_next_header() { - pnet_packet::ip::IpNextHeaderProtocols::Icmpv6 => { - return icmpv6_handler(&packet); + IpAddr::V6(local_ipv6) => { + if iface.ipv6.contains(&local_ipv6) { + if let Some(gateway) = iface.gateway { + return Ok(gateway); + } + } }, - _ => { - None - } - } - }else{ - None - } -} - -fn icmp_handler(ip_packet: &pnet_packet::ipv4::Ipv4Packet) -> Option { - if let Some(packet) = pnet_packet::icmp::IcmpPacket::new(ip_packet.payload()) { - if packet.get_icmp_type() == pnet_packet::icmp::IcmpTypes::TimeExceeded { - let ipv4_addr = ip_packet.get_source(); - return Some(IpAddr::V4(ipv4_addr)) - }else{ - None } - }else{ - None } + Err(String::from("Default Gateway not found")) } -fn icmpv6_handler(ip_packet: &pnet_packet::ipv6::Ipv6Packet) -> Option { - if let Some(packet) = pnet_packet::icmpv6::Icmpv6Packet::new(ip_packet.payload()) { - if packet.get_icmpv6_type() == pnet_packet::icmpv6::Icmpv6Types::TimeExceeded { - let ipv6_addr = ip_packet.get_source(); - return Some(IpAddr::V6(ipv6_addr)) - }else{ - None - } - }else{ - None +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_default_gateway() { + println!("{:?}", get_default_gateway()); } } diff --git a/src/interface.rs b/src/interface.rs index 2d8039c..6ee8c16 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -1,9 +1,8 @@ -use std::net::UdpSocket; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::gateway::{self, Gateway}; +use crate::gateway::{Gateway}; use crate::os; -/// Struct of MAC address +/// Structure of MAC address #[derive(Clone, Debug)] pub struct MacAddr(u8, u8, u8, u8, u8, u8); @@ -32,7 +31,7 @@ impl std::fmt::Display for MacAddr { } } -/// Struct of default Network Interface information +/// Structure of default Network Interface information #[derive(Clone, Debug)] pub struct Interface { pub index: u32, @@ -44,13 +43,13 @@ pub struct Interface { pub gateway: Option, } -#[cfg(target_os = "windows")] +/// Get default Network Interface pub fn get_default_interface() -> Result { let local_ip: IpAddr = match os::get_local_ipaddr(){ Some(local_ip) => local_ip, None => return Err(String::from("Local IP address not found")), }; - let interfaces: Vec = os::get_interfaces(); + let interfaces: Vec = os::interfaces(); for iface in interfaces { match local_ip { IpAddr::V4(local_ipv4) => { @@ -68,103 +67,41 @@ pub fn get_default_interface() -> Result { Err(String::from("Default Interface not found")) } -#[cfg(not(target_os="windows"))] -/// Get default Interface -pub fn get_default_interface() -> Result { - let local_ip = get_local_ipaddr(); - let interfaces = pnet_datalink::interfaces(); - if let Some(local_ip) = local_ip { - for iface in interfaces{ - for ip in &iface.ips{ - if local_ip == ip.ip().to_string() { - let mac_addr: Option = match iface.mac { - Some(mac_addr) => Some(MacAddr::new(mac_addr.octets())), - None => None, - }; - let mut ipv4_vec: Vec = vec![]; - let mut ipv6_vec: Vec = vec![]; - for ip in &iface.ips { - match ip.ip() { - IpAddr::V4(ipv4_addr) => { - ipv4_vec.push(ipv4_addr); - }, - IpAddr::V6(ipv6_addr) => { - ipv6_vec.push(ipv6_addr); - }, - } - } - let default_gateway: Option = match gateway::get_default_gateway() { - Ok(gateway) => Some(gateway), - Err(_) => None, - }; - let interface: Interface = Interface{ - index: iface.index, - name: iface.name, - description: None, - mac_addr: mac_addr, - ipv4: ipv4_vec, - ipv6: ipv6_vec, - gateway: default_gateway, - }; - return Ok(interface); - } - } - } - return Err(String::from("")); - }else{ - return Err(String::from("")); - } -} - -/// Get default Interface index +/// Get default Network Interface index pub fn get_default_interface_index() -> Option { - let local_ip = get_local_ipaddr(); - let interfaces = pnet_datalink::interfaces(); - if let Some(local_ip) = local_ip { - for iface in interfaces { - for ip in iface.ips { - if local_ip == ip.ip().to_string() { - return Some(iface.index) - } - } - } - return None; - }else{ - return None; - } + os::default_interface_index() } -/// Get default Interface name +/// Get default Network Interface name pub fn get_default_interface_name() -> Option { - let local_ip = get_local_ipaddr(); - let interfaces = pnet_datalink::interfaces(); - if let Some(local_ip) = local_ip { - for iface in interfaces { - for ip in iface.ips { - if local_ip == ip.ip().to_string() { - return Some(iface.name) - } - } - } - return None; - }else{ - return None; - } + os::default_interface_name() } -fn get_local_ipaddr() -> Option { - let socket = match UdpSocket::bind("0.0.0.0:0") { - Ok(s) => s, - Err(_) => return None, - }; - - match socket.connect("1.1.1.1:80") { - Ok(()) => (), - Err(_) => return None, - }; +/// Get a list of available Network Interfaces +pub fn get_interfaces() -> Vec { + os::interfaces() +} - match socket.local_addr() { - Ok(addr) => return Some(addr.ip().to_string()), - Err(_) => return None, - }; +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_interfaces() { + let interfaces = get_interfaces(); + for interface in interfaces { + println!("{:#?}", interface); + } + } + #[test] + fn test_default_interface() { + println!("{:#?}", get_default_interface()); + } + #[test] + fn test_default_interface_index() { + println!("{:?}", get_default_interface_index()); + } + #[test] + fn test_default_interface_name() { + println!("{:?}", get_default_interface_name()); + } } diff --git a/src/lib.rs b/src/lib.rs index e255bef..cec9340 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,6 @@ pub mod gateway; pub use interface::Interface; pub use interface::get_default_interface; +pub use interface::get_interfaces; pub use gateway::Gateway; pub use gateway::get_default_gateway; -pub use os::get_interfaces; diff --git a/src/os/mod.rs b/src/os/mod.rs index 81aa7a7..bc54554 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -9,4 +9,4 @@ pub use self::windows::*; #[cfg(not(target_os="windows"))] mod unix; #[cfg(not(target_os="windows"))] -use self::unix::*; +pub use self::unix::*; diff --git a/src/os/unix.rs b/src/os/unix.rs index f35e66d..b30dfea 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -1,6 +1,241 @@ -use crate::interface::{Interface, MacAddr}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::UdpSocket; +use std::time::{Duration, Instant}; +use pnet_packet::Packet; +use crate::interface::{MacAddr, Interface}; +use crate::gateway::Gateway; -pub fn get_interfaces() -> Vec { - let interfaces: Vec = vec![]; - return interfaces; +const TIMEOUT: u64 = 3000; + +fn get_default_gateway(interface_index: u32) -> Result { + let interfaces = pnet_datalink::interfaces(); + let interface = interfaces.into_iter().filter(|interface: &pnet_datalink::NetworkInterface| interface.index == interface_index).next().expect("Failed to get Interface"); + let config = pnet_datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: None, + write_timeout: None, + channel_type: pnet_datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + let (mut _tx, mut rx) = match pnet_datalink::channel(&interface, config) { + Ok(pnet_datalink::Channel::Ethernet(tx, rx)) => (tx, rx), + Ok(_) => panic!("Unknown channel type"), + Err(e) => panic!("Error happened {}", e), + }; + match send_udp_packet() { + Ok(_) => (), + Err(e) => return Err(format!("Failed to send UDP packet {}", e)), + } + receive_packets(&mut rx) +} + +fn send_udp_packet() -> Result<(), String> { + let buf = [0u8; 0]; + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(e) => return Err(format!("Failed to create UDP socket {}", e)), + }; + let dst: &str = "1.1.1.1:80"; + match socket.set_ttl(1) { + Ok(_) => (), + Err(e) => return Err(format!("Failed to set TTL {}", e)), + } + match socket.send_to(&buf, dst) { + Ok(_) => (), + Err(e) => return Err(format!("Failed to send data {}", e)), + } + Ok(()) +} + +fn receive_packets(rx: &mut Box) -> Result{ + let timeout = Duration::from_millis(TIMEOUT); + let start_time = Instant::now(); + loop { + match rx.next() { + Ok(frame) => { + let frame = match pnet_packet::ethernet::EthernetPacket::new(frame) { + Some(f) => f, + None => return Err(String::from("Failed to read packet")), + }; + match frame.get_ethertype() { + pnet_packet::ethernet::EtherTypes::Ipv4 => { + if let Some(ip_addr) = ipv4_handler(&frame) { + let gateway = Gateway { + mac_addr: MacAddr::new(frame.get_source().octets()), + ip_addr: ip_addr, + }; + return Ok(gateway); + } + }, + pnet_packet::ethernet::EtherTypes::Ipv6 => { + if let Some(ip_addr) = ipv6_handler(&frame) { + let gateway = Gateway { + mac_addr: MacAddr::new(frame.get_source().octets()), + ip_addr: ip_addr, + }; + return Ok(gateway); + } + }, + _ => {} + } + }, + Err(e) => { + return Err(format!("An error occurred while reading: {}", e)); + } + } + if Instant::now().duration_since(start_time) > timeout { + return Err(String::from("Recieve timeout")); + }else{ + match send_udp_packet() { + Ok(_) => (), + Err(e) => return Err(format!("Failed to send UDP packet {}", e)), + } + } + } +} + +fn ipv4_handler(ethernet: &pnet_packet::ethernet::EthernetPacket) -> Option { + if let Some(packet) = pnet_packet::ipv4::Ipv4Packet::new(ethernet.payload()) { + match packet.get_next_level_protocol() { + pnet_packet::ip::IpNextHeaderProtocols::Icmp => { + return icmp_handler(&packet); + }, + _ => { + None + } + } + }else{ + None + } +} + +fn ipv6_handler(ethernet: &pnet_packet::ethernet::EthernetPacket) -> Option { + if let Some(packet) = pnet_packet::ipv6::Ipv6Packet::new(ethernet.payload()) { + match packet.get_next_header() { + pnet_packet::ip::IpNextHeaderProtocols::Icmpv6 => { + return icmpv6_handler(&packet); + }, + _ => { + None + } + } + }else{ + None + } +} + +fn icmp_handler(ip_packet: &pnet_packet::ipv4::Ipv4Packet) -> Option { + if let Some(packet) = pnet_packet::icmp::IcmpPacket::new(ip_packet.payload()) { + if packet.get_icmp_type() == pnet_packet::icmp::IcmpTypes::TimeExceeded { + let ipv4_addr = ip_packet.get_source(); + return Some(IpAddr::V4(ipv4_addr)) + }else{ + None + } + }else{ + None + } +} + +fn icmpv6_handler(ip_packet: &pnet_packet::ipv6::Ipv6Packet) -> Option { + if let Some(packet) = pnet_packet::icmpv6::Icmpv6Packet::new(ip_packet.payload()) { + if packet.get_icmpv6_type() == pnet_packet::icmpv6::Icmpv6Types::TimeExceeded { + let ipv6_addr = ip_packet.get_source(); + return Some(IpAddr::V6(ipv6_addr)) + }else{ + None + } + }else{ + None + } +} + +pub fn interfaces() -> Vec { + let mut result: Vec = vec![]; + let local_ip: IpAddr = match super::get_local_ipaddr(){ + Some(local_ip) => local_ip, + None => return result, + }; + let interfaces = pnet_datalink::interfaces(); + for iface in interfaces{ + let mac_addr: Option = match iface.mac { + Some(mac_addr) => Some(MacAddr::new(mac_addr.octets())), + None => None, + }; + let mut ipv4_vec: Vec = vec![]; + let mut ipv6_vec: Vec = vec![]; + let mut ips: Vec = vec![]; + for ip in &iface.ips { + match ip.ip() { + IpAddr::V4(ipv4_addr) => { + ipv4_vec.push(ipv4_addr); + }, + IpAddr::V6(ipv6_addr) => { + ipv6_vec.push(ipv6_addr); + }, + } + ips.push(ip.ip()); + } + let default_gateway: Option = if ips.contains(&local_ip) { + match get_default_gateway(iface.index) { + Ok(default_gateway) => Some(default_gateway), + Err(_) => None, + } + } else{ + None + }; + let desc: Option = if iface.description.is_empty() { + None + } else{ + Some(iface.description) + }; + let interface: Interface = Interface{ + index: iface.index, + name: iface.name, + description: desc, + mac_addr: mac_addr, + ipv4: ipv4_vec, + ipv6: ipv6_vec, + gateway: default_gateway, + }; + result.push(interface); + } + return result; +} + +/// Get default Interface index +pub fn default_interface_index() -> Option { + let local_ip: IpAddr = match super::get_local_ipaddr(){ + Some(local_ip) => local_ip, + None => return None, + }; + let interfaces = pnet_datalink::interfaces(); + for iface in interfaces { + for ip in iface.ips { + if local_ip == ip.ip() { + return Some(iface.index) + } + } + } + return None; +} + +/// Get default Interface name +pub fn default_interface_name() -> Option { + let local_ip: IpAddr = match super::get_local_ipaddr(){ + Some(local_ip) => local_ip, + None => return None, + }; + let interfaces = pnet_datalink::interfaces(); + for iface in interfaces { + for ip in iface.ips { + if local_ip == ip.ip() { + return Some(iface.name) + } + } + } + return None; } diff --git a/src/os/windows.rs b/src/os/windows.rs index 1f8e54d..5e44ad5 100644 --- a/src/os/windows.rs +++ b/src/os/windows.rs @@ -58,7 +58,7 @@ fn get_mac_through_arp(src_ip: Ipv4Addr, dst_ip: Ipv4Addr) -> MacAddr { // 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() -> Vec { +pub fn 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);