diff --git a/examples/default_gateway.rs b/examples/default_gateway.rs index 530a839..e07d41b 100644 --- a/examples/default_gateway.rs +++ b/examples/default_gateway.rs @@ -1,8 +1,8 @@ use default_net; fn main(){ - match default_net::get_default_gateway() { - Ok(default_gateway) => {println!("Default Gateway: {}",default_gateway)}, - Err(e) => {println!("{}",e)}, - } + let default_gateway = default_net::get_default_gateway(); + println!("Default Gateway"); + println!("IP {:?}", default_gateway.ip); + println!("MAC {:?}", default_gateway.mac); } diff --git a/examples/default_interface.rs b/examples/default_interface.rs new file mode 100644 index 0000000..3d88b0e --- /dev/null +++ b/examples/default_interface.rs @@ -0,0 +1,15 @@ +use default_net; + +fn main(){ + if let Some(default_interface) = default_net::get_default_interface(){ + println!("Index {}", default_interface.index); + println!("Name {}", default_interface.name); + println!("MAC {:?}", default_interface.mac); + println!("IPv4 {:?}", default_interface.ipv4); + println!("IPv6 {:?}", default_interface.ipv6); + println!("Gateway IP {:?}", default_interface.gateway.ip); + println!("Gateway MAC {:?}", default_interface.gateway.mac); + }else{ + println!("Failed to get default interface info"); + } +} diff --git a/src/gateway.rs b/src/gateway.rs new file mode 100644 index 0000000..29968d9 --- /dev/null +++ b/src/gateway.rs @@ -0,0 +1,301 @@ +use std::net::UdpSocket; +use std::time::{Duration, Instant}; +use std::net::{IpAddr, Ipv4Addr}; +use pnet::packet::Packet; +use pnet::packet::MutablePacket; + +#[cfg(any(unix, macos))] +use pnet::transport::TransportChannelType::Layer4; +#[cfg(any(unix, macos))] +use pnet::transport::TransportProtocol::Ipv4; +#[cfg(any(unix, macos))] +use pnet::transport::icmp_packet_iter; + +#[cfg(target_os = "windows")] +use pnet::datalink; + +use crate::interface; + +pub struct Gateway { + pub ip: Option, + pub mac: Option, +} + +pub fn get_default_gateway() -> Gateway { + let mut gateway: Gateway = Gateway { + ip: None, + mac: None, + }; + let default_gateway_ip: Option = match get_default_gateway_ip(){ + Ok(gateway_ip) => Some(gateway_ip), + Err(_) => None, + }; + gateway.ip = default_gateway_ip.clone(); + if let Some(gateway_ip) = default_gateway_ip.clone(){ + let default_gateway_mac: Option = match get_default_gateway_mac(gateway_ip.to_string()) { + Ok(gateway_mac) => Some(gateway_mac), + Err(_) => None, + }; + gateway.mac = default_gateway_mac; + } + return gateway; +} + +pub fn get_default_gateway_ip() -> Result{ + send_udp_packet(); + let timeout = Duration::from_millis(3000); + let r = receive_icmp_packets(pnet::packet::icmp::IcmpTypes::TimeExceeded, &timeout); + return r; +} + +pub fn get_default_gateway_mac(gateway_ip: String) -> Result{ + match gateway_ip.parse::(){ + Ok(ipv4_addr) => { + if let Some(gateway_mac) = get_mac_through_arp(ipv4_addr){ + return Ok(gateway_mac); + }else{ + return Err(String::from("Failed to get gateway mac address")); + } + }, + Err(_) => return Err(String::from("Failed to get gateway mac address")), + } +} + +fn send_udp_packet(){ + let buf = [0u8; 0]; + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(_) => panic!("error!"), + }; + let dest: &str = "8.8.8.8:80"; + socket.set_ttl(1).unwrap(); + socket.send_to(&buf, dest).unwrap(); +} + +#[cfg(any(unix, macos))] +fn receive_icmp_packets(icmp_type: pnet::packet::icmp::IcmpType, timeout: &Duration) -> Result{ + let protocol = Layer4(Ipv4(pnet::packet::ip::IpNextHeaderProtocols::Icmp)); + let (mut _tx, mut rx) = match pnet::transport::transport_channel(4096, protocol) { + Ok((tx, rx)) => (tx, rx), + Err(e) => panic!("Error happened {}", e), + }; + let mut iter = icmp_packet_iter(&mut rx); + let start_time = Instant::now(); + loop { + match iter.next_with_timeout(*timeout) { + Ok(r) => { + if let Some((packet, addr)) = r { + if packet.get_icmp_type() == icmp_type { + match addr { + IpAddr::V4(ipv4_addr) =>{return Ok(ipv4_addr.to_string())}, + IpAddr::V6(ipv6_addr) =>{return Ok(ipv6_addr.to_string())}, + } + } + }else{ + return Err(String::from("Failed to read packet")); + } + }, + Err(e) => { + return Err(format!("An error occurred while reading: {}", e)); + } + } + if Instant::now().duration_since(start_time) > *timeout { + return Err(String::from("timeout")); + }else{ + send_udp_packet(); + } + } +} + +#[cfg(target_os = "windows")] +fn receive_icmp_packets(icmp_type: pnet::packet::icmp::IcmpType, timeout: &Duration) -> Result{ + /* + let protocol = Layer4(Ipv4(pnet::packet::ip::IpNextHeaderProtocols::Icmp)); + let (mut _tx, mut rx) = match pnet::transport::transport_channel(4096, protocol) { + Ok((tx, rx)) => (tx, rx), + Err(e) => panic!("Error happened {}", e), + }; + let mut iter = icmp_packet_iter(&mut rx); + let start_time = Instant::now(); + loop { + match iter.next() { + Ok((packet, addr)) => { + if packet.get_icmp_type() == icmp_type { + match addr { + IpAddr::V4(ipv4_addr) =>{return Ok(ipv4_addr.to_string())}, + IpAddr::V6(ipv6_addr) =>{return Ok(ipv6_addr.to_string())}, + } + } + }, + Err(e) => { + return Err(format!("An error occurred while reading: {}", e)); + } + } + if Instant::now().duration_since(start_time) > *timeout { + return Err(String::from("timeout")); + }else{ + send_udp_packet(); + } + } + */ + let default_idx = interface::get_default_interface_index().unwrap(); + 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 (mut _tx, mut rx) = match datalink::channel(&interface, Default::default()) { + Ok(pnet::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), + Ok(_) => panic!("Unknown channel type"), + Err(e) => panic!("Error happened {}", e), + }; + receive_packets(&mut rx, icmp_type, timeout) +} + +#[cfg(target_os = "windows")] +fn receive_packets(rx: &mut Box, icmp_type: pnet::packet::icmp::IcmpType, timeout: &Duration) -> Result{ + let start_time = Instant::now(); + loop { + match rx.next() { + Ok(frame) => { + let frame = pnet::packet::ethernet::EthernetPacket::new(frame).unwrap(); + match frame.get_ethertype() { + pnet::packet::ethernet::EtherTypes::Ipv4 => { + if let Some(ip_addr) = ipv4_handler(&frame, icmp_type){ + return Ok(ip_addr); + } + }, + pnet::packet::ethernet::EtherTypes::Ipv6 => { + if let Some(ip_addr) = ipv6_handler(&frame, icmp_type){ + return Ok(ip_addr); + } + }, + _ => { + //println!("Not a ipv4 or ipv6"); + } + } + }, + Err(e) => { + return Err(format!("An error occurred while reading: {}", e)); + } + } + if Instant::now().duration_since(start_time) > *timeout { + return Err(String::from("timeout")); + }else{ + send_udp_packet(); + } + } +} + +#[cfg(target_os = "windows")] +fn ipv4_handler(ethernet: &pnet::packet::ethernet::EthernetPacket, icmp_type: pnet::packet::icmp::IcmpType) -> 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, icmp_type); + }, + _ => { + None + } + } + }else{ + None + } +} + +#[cfg(target_os = "windows")] +fn ipv6_handler(ethernet: &pnet::packet::ethernet::EthernetPacket, icmp_type: pnet::packet::icmp::IcmpType) -> 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, icmp_type); + }, + _ => { + None + } + } + }else{ + None + } +} + +#[cfg(target_os = "windows")] +fn icmp_handler(ip_packet: &pnet::packet::ipv4::Ipv4Packet, icmp_type: pnet::packet::icmp::IcmpType) -> Option { + if let Some(packet) = pnet::packet::icmp::IcmpPacket::new(ip_packet.payload()){ + if packet.get_icmp_type() == icmp_type { + let ipv4_addr = ip_packet.get_source(); + return Some(ipv4_addr.to_string()) + }else{ + None + } + }else{ + None + } +} + +#[cfg(target_os = "windows")] +fn icmpv6_handler(ip_packet: &pnet::packet::ipv6::Ipv6Packet, icmp_type: pnet::packet::icmp::IcmpType) -> Option { + if let Some(packet) = pnet::packet::icmp::IcmpPacket::new(ip_packet.payload()){ + if packet.get_icmp_type() == icmp_type { + let ipv6_addr = ip_packet.get_source(); + return Some(ipv6_addr.to_string()) + }else{ + None + } + }else{ + None + } +} + +fn get_mac_through_arp(dst_ip: Ipv4Addr) -> Option { + let default_idx = interface::get_default_interface_index().unwrap(); + 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 src_ip = interface.ips.iter().find(|ip| ip.is_ipv4()) + .map(|ip| match ip.ip() { + IpAddr::V4(ip) => ip, + _ => unreachable!(), + }) + .unwrap(); + let (mut sender, mut receiver) = match pnet::datalink::channel(&interface, Default::default()) { + Ok(pnet::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), + Ok(_) => panic!("Unknown channel type"), + Err(e) => panic!("Error happened {}", e), + }; + + let mut ethernet_buffer = [0u8; 42]; + let mut ethernet_packet = pnet::packet::ethernet::MutableEthernetPacket::new(&mut ethernet_buffer).unwrap(); + + ethernet_packet.set_destination(pnet::datalink::MacAddr::broadcast()); + ethernet_packet.set_source(interface.mac.unwrap()); + ethernet_packet.set_ethertype(pnet::packet::ethernet::EtherTypes::Arp); + + let mut arp_buffer = [0u8; 28]; + let mut arp_packet = pnet::packet::arp::MutableArpPacket::new(&mut arp_buffer).unwrap(); + + arp_packet.set_hardware_type(pnet::packet::arp::ArpHardwareTypes::Ethernet); + arp_packet.set_protocol_type(pnet::packet::ethernet::EtherTypes::Ipv4); + arp_packet.set_hw_addr_len(6); + arp_packet.set_proto_addr_len(4); + arp_packet.set_operation(pnet::packet::arp::ArpOperations::Request); + arp_packet.set_sender_hw_addr(interface.mac.unwrap()); + arp_packet.set_sender_proto_addr(src_ip); + arp_packet.set_target_hw_addr(pnet::datalink::MacAddr::zero()); + arp_packet.set_target_proto_addr(dst_ip); + + ethernet_packet.set_payload(arp_packet.packet_mut()); + + sender.send_to(ethernet_packet.packet(), None).unwrap().unwrap(); + + let mut target_mac_addr: pnet::datalink::MacAddr = pnet::datalink::MacAddr::zero(); + + for _x in 0..2 { + let buf = receiver.next().unwrap(); + let arp = pnet::packet::arp::ArpPacket::new(&buf[pnet::packet::ethernet::MutableEthernetPacket::minimum_packet_size()..]).unwrap(); + if arp.get_sender_hw_addr() != interface.mac.unwrap() { + target_mac_addr = arp.get_sender_hw_addr(); + } + } + if target_mac_addr == pnet::datalink::MacAddr::zero() { + return None; + }else{ + return Some(target_mac_addr.to_string()); + } +} diff --git a/src/interface.rs b/src/interface.rs new file mode 100644 index 0000000..d0e6c42 --- /dev/null +++ b/src/interface.rs @@ -0,0 +1,106 @@ +use std::net::UdpSocket; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use pnet::datalink; +use crate::gateway; + +pub struct Interface { + pub index: u32, + pub name: String, + pub mac: Option, + pub ipv4: Vec, + pub ipv6: Vec, + pub gateway: gateway::Gateway, +} + +fn get_local_ipaddr() -> Option { + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(_) => return None, + }; + + match socket.connect("8.8.8.8:80") { + Ok(()) => (), + Err(_) => return None, + }; + + match socket.local_addr() { + Ok(addr) => return Some(addr.ip().to_string()), + Err(_) => return None, + }; +} + +pub fn get_default_interface()-> Option { + let local_ip = get_local_ipaddr(); + let all_interfaces = datalink::interfaces(); + if let Some(local_ip) = local_ip { + for iface in all_interfaces{ + for ip in &iface.ips{ + if local_ip == ip.ip().to_string() { + let mac_addr: Option = match iface.mac { + Some(mac_addr) => Some(mac_addr.to_string()), + 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 = gateway::get_default_gateway(); + let interface: Interface = Interface{ + index: iface.index, + name: iface.name, + mac: mac_addr, + ipv4: ipv4_vec, + ipv6: ipv6_vec, + gateway: default_gateway, + }; + return Some(interface); + } + } + } + return None; + }else{ + return None; + } +} + +pub fn get_default_interface_index() -> Option { + let local_ip = get_local_ipaddr(); + let all_interfaces = datalink::interfaces(); + if let Some(local_ip) = local_ip { + for iface in all_interfaces{ + for ip in iface.ips{ + if local_ip == ip.ip().to_string() { + return Some(iface.index) + } + } + } + return None; + }else{ + return None; + } +} + +pub fn get_default_interface_name() -> Option { + let local_ip = get_local_ipaddr(); + let all_interfaces = datalink::interfaces(); + if let Some(local_ip) = local_ip { + for iface in all_interfaces{ + for ip in iface.ips{ + if local_ip == ip.ip().to_string() { + return Some(iface.name) + } + } + } + return None; + }else{ + return None; + } +} diff --git a/src/lib.rs b/src/lib.rs index b5dbf22..868f2b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,250 +1,15 @@ -use std::net::UdpSocket; -use std::time::{Duration, Instant}; -use std::net::IpAddr; -use pnet::datalink; - -#[cfg(any(unix, macos))] -use pnet::transport::TransportChannelType::Layer4; -#[cfg(any(unix, macos))] -use pnet::transport::TransportProtocol::Ipv4; -#[cfg(any(unix, macos))] -use pnet::transport::icmp_packet_iter; - -#[cfg(target_os = "windows")] -use pnet::packet::Packet; - -pub fn get_default_gateway() -> Result { - send_udp_packet(); - let timeout = Duration::from_millis(3000); - let r = receive_icmp_packets(pnet::packet::icmp::IcmpTypes::TimeExceeded, &timeout); - return r; -} - -pub fn send_udp_packet(){ - let buf = [0u8; 0]; - let socket = match UdpSocket::bind("0.0.0.0:0") { - Ok(s) => s, - Err(_) => panic!("error!"), - }; - let dest: &str = "8.8.8.8:80"; - socket.set_ttl(1).unwrap(); - socket.send_to(&buf, dest).unwrap(); -} - -#[cfg(any(unix, macos))] -pub fn receive_icmp_packets(icmp_type: pnet::packet::icmp::IcmpType, timeout: &Duration) -> Result{ - let protocol = Layer4(Ipv4(pnet::packet::ip::IpNextHeaderProtocols::Icmp)); - let (mut _tx, mut rx) = match pnet::transport::transport_channel(4096, protocol) { - Ok((tx, rx)) => (tx, rx), - Err(e) => panic!("Error happened {}", e), - }; - let mut iter = icmp_packet_iter(&mut rx); - let start_time = Instant::now(); - loop { - match iter.next_with_timeout(*timeout) { - Ok(r) => { - if let Some((packet, addr)) = r { - if packet.get_icmp_type() == icmp_type { - match addr { - IpAddr::V4(ipv4_addr) =>{return Ok(ipv4_addr.to_string())}, - IpAddr::V6(ipv6_addr) =>{return Ok(ipv6_addr.to_string())}, - } - } - }else{ - return Err(String::from("Failed to read packet")); - } - }, - Err(e) => { - return Err(format!("An error occurred while reading: {}", e)); - } - } - if Instant::now().duration_since(start_time) > *timeout { - return Err(String::from("timeout")); - }else{ - send_udp_packet(); - } - } -} - -#[cfg(target_os = "windows")] -fn receive_icmp_packets(icmp_type: pnet::packet::icmp::IcmpType, timeout: &Duration) -> Result{ - /* - let protocol = Layer4(Ipv4(pnet::packet::ip::IpNextHeaderProtocols::Icmp)); - let (mut _tx, mut rx) = match pnet::transport::transport_channel(4096, protocol) { - Ok((tx, rx)) => (tx, rx), - Err(e) => panic!("Error happened {}", e), - }; - let mut iter = icmp_packet_iter(&mut rx); - let start_time = Instant::now(); - loop { - match iter.next() { - Ok((packet, addr)) => { - if packet.get_icmp_type() == icmp_type { - match addr { - IpAddr::V4(ipv4_addr) =>{return Ok(ipv4_addr.to_string())}, - IpAddr::V6(ipv6_addr) =>{return Ok(ipv6_addr.to_string())}, - } - } - }, - Err(e) => { - return Err(format!("An error occurred while reading: {}", e)); - } - } - if Instant::now().duration_since(start_time) > *timeout { - return Err(String::from("timeout")); - }else{ - send_udp_packet(); - } - } - */ - let default_idx = get_default_interface_index().unwrap(); - 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 (mut _tx, mut rx) = match datalink::channel(&interface, Default::default()) { - Ok(pnet::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => panic!("Unknown channel type"), - Err(e) => panic!("Error happened {}", e), - }; - receive_packets(&mut rx, icmp_type, timeout) -} - -#[cfg(target_os = "windows")] -fn receive_packets(rx: &mut Box, icmp_type: pnet::packet::icmp::IcmpType, timeout: &Duration) -> Result{ - let start_time = Instant::now(); - loop { - match rx.next() { - Ok(frame) => { - let frame = pnet::packet::ethernet::EthernetPacket::new(frame).unwrap(); - match frame.get_ethertype() { - pnet::packet::ethernet::EtherTypes::Ipv4 => { - if let Some(ip_addr) = ipv4_handler(&frame, icmp_type){ - return Ok(ip_addr); - } - }, - pnet::packet::ethernet::EtherTypes::Ipv6 => { - if let Some(ip_addr) = ipv6_handler(&frame, icmp_type){ - return Ok(ip_addr); - } - }, - _ => { - //println!("Not a ipv4 or ipv6"); - } - } - }, - Err(e) => { - return Err(format!("An error occurred while reading: {}", e)); - } - } - if Instant::now().duration_since(start_time) > *timeout { - return Err(String::from("timeout")); - }else{ - send_udp_packet(); - } - } -} - -#[cfg(target_os = "windows")] -fn ipv4_handler(ethernet: &pnet::packet::ethernet::EthernetPacket, icmp_type: pnet::packet::icmp::IcmpType) -> 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, icmp_type); - }, - _ => { - None - } - } - }else{ - None - } -} - -#[cfg(target_os = "windows")] -fn ipv6_handler(ethernet: &pnet::packet::ethernet::EthernetPacket, icmp_type: pnet::packet::icmp::IcmpType) -> 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, icmp_type); - }, - _ => { - None - } - } - }else{ - None - } -} - -#[cfg(target_os = "windows")] -fn icmp_handler(ip_packet: &pnet::packet::ipv4::Ipv4Packet, icmp_type: pnet::packet::icmp::IcmpType) -> Option { - if let Some(packet) = pnet::packet::icmp::IcmpPacket::new(ip_packet.payload()){ - if packet.get_icmp_type() == icmp_type { - let ipv4_addr = ip_packet.get_source(); - return Some(ipv4_addr.to_string()) - }else{ - None - } - }else{ - None - } -} - -#[cfg(target_os = "windows")] -fn icmpv6_handler(ip_packet: &pnet::packet::ipv6::Ipv6Packet, icmp_type: pnet::packet::icmp::IcmpType) -> Option { - if let Some(packet) = pnet::packet::icmp::IcmpPacket::new(ip_packet.payload()){ - if packet.get_icmp_type() == icmp_type { - let ipv6_addr = ip_packet.get_source(); - return Some(ipv6_addr.to_string()) - }else{ - None - } - }else{ - None - } -} - -pub fn get_local_ipaddr() -> Option { - let socket = match UdpSocket::bind("0.0.0.0:0") { - Ok(s) => s, - Err(_) => return None, - }; - - match socket.connect("8.8.8.8:80") { - Ok(()) => (), - Err(_) => return None, - }; - - match socket.local_addr() { - Ok(addr) => return Some(addr.ip().to_string()), - Err(_) => return None, - }; -} - -pub fn get_default_interface_index() -> Option { - let local_ip = get_local_ipaddr(); - let all_interfaces = datalink::interfaces(); - if let Some(local_ip) = local_ip { - for iface in all_interfaces{ - for ip in iface.ips{ - match ip.ip() { - IpAddr::V4(ipv4) => { - if local_ip == ipv4.to_string() { - return Some(iface.index) - } - }, - IpAddr::V6(ipv6) => { - if local_ip == ipv6.to_string() { - return Some(iface.index) - } - }, - } - } - } - return None; - }else{ - return None; - } -} +mod interface; +mod gateway; + +pub use interface::Interface; +pub use interface::get_default_interface; +pub use interface::get_default_interface_index; +pub use interface::get_default_interface_name; + +pub use gateway::Gateway; +pub use gateway::get_default_gateway; +pub use gateway::get_default_gateway_ip; +pub use gateway::get_default_gateway_mac; #[cfg(test)] mod tests {