use crate::{
error::{Error, OutOfRangeData},
session,
util::{self, UnsafeHandle},
wintun_raw, Wintun,
};
use std::{
ffi::OsStr,
net::{IpAddr, Ipv4Addr},
os::windows::prelude::OsStrExt,
process::Command,
ptr,
sync::Arc,
sync::OnceLock,
};
use windows::{
core::{GUID, PCSTR, PCWSTR},
Win32::{
Foundation::FALSE,
NetworkManagement::{
IpHelper::{ConvertLengthToIpv4Mask, IP_ADAPTER_ADDRESSES_LH},
Ndis::NET_LUID_LH,
},
System::{Com::CLSIDFromString, Threading::CreateEventA},
},
};
pub struct Adapter {
adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
wintun: Wintun,
guid: u128,
}
fn get_adapter_luid(wintun: &Wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> NET_LUID_LH {
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
unsafe { wintun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID) };
unsafe { std::mem::transmute(luid) }
}
impl Adapter {
pub fn get_name(&self) -> Result<String, Error> {
let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
let mut friendly_name = None;
util::get_adapters_addresses(|address| {
let name_iter = unsafe { address.AdapterName.to_string()? };
if name_iter == name {
friendly_name = unsafe { Some(address.FriendlyName.to_string()?) };
}
Ok(())
})?;
friendly_name.ok_or(format!("Unable to find matching {}", name).into())
}
pub fn set_name(&self, name: &str) -> Result<(), Error> {
let args = &[
"interface",
"set",
"interface",
&format!("name=\"{}\"", self.get_name()?),
&format!("newname=\"{}\"", name),
];
util::run_command("netsh", args)?;
Ok(())
}
pub fn get_guid(&self) -> u128 {
self.guid
}
pub fn create(wintun: &Wintun, name: &str, tunnel_type: &str, guid: Option<u128>) -> Result<Arc<Adapter>, Error> {
let name_utf16: Vec<_> = name.encode_utf16().chain(std::iter::once(0)).collect();
let tunnel_type_utf16: Vec<u16> = tunnel_type.encode_utf16().chain(std::iter::once(0)).collect();
let guid = match guid {
Some(guid) => guid,
None => GUID::new()?.to_u128(),
};
crate::log::set_default_logger_if_unset(wintun);
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(GUID::from_u128(guid)) };
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
let result = unsafe { wintun.WintunCreateAdapter(name_utf16.as_ptr(), tunnel_type_utf16.as_ptr(), guid_ptr) };
if result.is_null() {
Err("Failed to create adapter".into())
} else {
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
guid,
}))
}
}
pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, Error> {
let name_utf16: Vec<u16> = OsStr::new(name).encode_wide().chain(std::iter::once(0)).collect();
crate::log::set_default_logger_if_unset(wintun);
let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };
if result.is_null() {
Err("WintunOpenAdapter failed".into())
} else {
let mut guid = None;
util::get_adapters_addresses(|address: IP_ADAPTER_ADDRESSES_LH| {
let frindly_name = PCWSTR(address.FriendlyName.0 as *const u16);
let frindly_name = unsafe { frindly_name.to_string()? };
if frindly_name == name {
let adapter_name = unsafe { address.AdapterName.to_string()? };
let adapter_name_utf16: Vec<u16> = adapter_name.encode_utf16().chain(std::iter::once(0)).collect();
let adapter_name_ptr: *const u16 = adapter_name_utf16.as_ptr();
let adapter = unsafe { CLSIDFromString(PCWSTR(adapter_name_ptr))? };
guid = Some(adapter);
}
Ok(())
})?;
let guid = guid.ok_or("Unable to find matching GUID")?.to_u128();
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
guid,
}))
}
}
pub fn delete(self) -> Result<(), Error> {
drop(self);
Ok(())
}
pub fn start_session(self: &Arc<Self>, capacity: u32) -> Result<session::Session, Error> {
let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
if !range.contains(&capacity) {
return Err(Error::CapacityOutOfRange(OutOfRangeData { range, value: capacity }));
}
if !capacity.is_power_of_two() {
return Err(Error::CapacityNotPowerOfTwo(capacity));
}
let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };
if result.is_null() {
Err("WintunStartSession failed".into())
} else {
let shutdown_event = unsafe { CreateEventA(None, FALSE, FALSE, PCSTR::null())? };
Ok(session::Session {
session: UnsafeHandle(result),
wintun: self.wintun.clone(),
read_event: OnceLock::new(),
shutdown_event,
adapter: Arc::clone(self),
})
}
}
pub fn get_luid(&self) -> NET_LUID_LH {
get_adapter_luid(&self.wintun, self.adapter.0)
}
pub fn set_mtu(&self, mtu: usize) -> Result<(), Error> {
let name = self.get_name()?;
Ok(util::set_adapter_mtu(&name, mtu)?)
}
pub fn get_mtu(&self) -> Result<usize, Error> {
let luid = self.get_luid();
Ok(util::get_adapter_mtu(&luid)?)
}
pub fn get_adapter_index(&self) -> Result<u32, Error> {
let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
let mut adapter_index = None;
util::get_adapters_addresses(|address| {
let name_iter = unsafe { address.AdapterName.to_string()? };
if name_iter == name {
adapter_index = unsafe { Some(address.Anonymous1.Anonymous.IfIndex) };
}
Ok(())
})?;
adapter_index.ok_or(format!("Unable to find matching {}", name).into())
}
pub fn set_address(&self, address: Ipv4Addr) -> Result<(), Error> {
let binding = self.get_addresses()?;
let old_address = binding.iter().find(|addr| matches!(addr, IpAddr::V4(_)));
let mask = match old_address {
Some(IpAddr::V4(addr)) => self.get_netmask_of_address(&(*addr).into())?,
_ => "255.255.255.0".parse()?,
};
let gateway = self
.get_gateways()?
.iter()
.find(|addr| matches!(addr, IpAddr::V4(_)))
.cloned();
self.set_network_addresses_tuple(address.into(), mask, gateway)?;
Ok(())
}
pub fn set_gateway(&self, gateway: Option<Ipv4Addr>) -> Result<(), Error> {
let binding = self.get_addresses()?;
let address = binding.iter().find(|addr| matches!(addr, IpAddr::V4(_)));
let address = match address {
Some(IpAddr::V4(addr)) => addr,
_ => return Err("Unable to find IPv4 address".into()),
};
let mask = self.get_netmask_of_address(&(*address).into())?;
let gateway = gateway.map(|addr| addr.into());
self.set_network_addresses_tuple((*address).into(), mask, gateway)?;
Ok(())
}
pub fn set_netmask(&self, mask: Ipv4Addr) -> Result<(), Error> {
let binding = self.get_addresses()?;
let address = binding.iter().find(|addr| matches!(addr, IpAddr::V4(_)));
let address = match address {
Some(IpAddr::V4(addr)) => addr,
_ => return Err("Unable to find IPv4 address".into()),
};
let gateway = self
.get_gateways()?
.iter()
.find(|addr| matches!(addr, IpAddr::V4(_)))
.cloned();
self.set_network_addresses_tuple((*address).into(), mask.into(), gateway)?;
Ok(())
}
pub fn set_dns_servers(&self, dns_servers: &[IpAddr]) -> Result<(), Error> {
let interface = GUID::from(self.get_guid());
Ok(util::set_interface_dns_servers(interface, dns_servers)?)
}
pub fn set_network_addresses_tuple(
&self,
address: IpAddr,
mask: IpAddr,
gateway: Option<IpAddr>,
) -> Result<(), Error> {
let name = self.get_name()?;
let mut binding = Command::new("netsh");
let mut cmd = binding
.arg("interface")
.arg(if address.is_ipv4() { "ipv4" } else { "ipv6" })
.arg("set")
.arg("address")
.arg(format!("name=\"{}\"", name).as_str())
.arg("source=static")
.arg(format!("address={}", address).as_str())
.arg(format!("mask={}", mask).as_str());
if let Some(gateway) = gateway {
cmd = cmd.arg(format!("gateway={}", gateway).as_str());
}
let out = cmd.output()?;
if !out.status.success() {
return Err(format!("Failed to set address: {}", String::from_utf8_lossy(&out.stderr)).into());
}
Ok(())
}
pub fn get_addresses(&self) -> Result<Vec<IpAddr>, Error> {
let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
let mut adapter_addresses = vec![];
util::get_adapters_addresses(|adapter| {
let name_iter = unsafe { adapter.AdapterName.to_string()? };
if name_iter == name {
let mut current_address = adapter.FirstUnicastAddress;
while !current_address.is_null() {
let address = unsafe { (*current_address).Address };
let address = util::retrieve_ipaddr_from_socket_address(&address);
if let Err(err) = address {
log::error!("Failed to parse address: {}", err);
} else {
adapter_addresses.push(address?);
}
unsafe {
current_address = (*current_address).Next;
}
}
}
Ok(())
})?;
Ok(adapter_addresses)
}
pub fn get_gateways(&self) -> Result<Vec<IpAddr>, Error> {
let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
let mut gateways = vec![];
util::get_adapters_addresses(|adapter| {
let name_iter = unsafe { adapter.AdapterName.to_string()? };
if name_iter == name {
let mut current_gateway = adapter.FirstGatewayAddress;
while !current_gateway.is_null() {
let gateway = unsafe { (*current_gateway).Address };
let gateway = util::retrieve_ipaddr_from_socket_address(&gateway);
if let Err(err) = gateway {
log::error!("Failed to parse gateway: {}", err);
} else {
gateways.push(gateway?);
}
unsafe {
current_gateway = (*current_gateway).Next;
}
}
}
Ok(())
})?;
Ok(gateways)
}
pub fn get_netmask_of_address(&self, target_address: &IpAddr) -> Result<IpAddr, Error> {
let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
let mut subnet_mask = None;
util::get_adapters_addresses(|adapter| {
let name_iter = unsafe { adapter.AdapterName.to_string()? };
if name_iter == name {
let mut current_address = adapter.FirstUnicastAddress;
while !current_address.is_null() {
let address = unsafe { (*current_address).Address };
let address = util::retrieve_ipaddr_from_socket_address(&address);
if let Err(ref err) = address {
log::warn!("Failed to parse address: {}", err);
}
let address = address?;
if address == *target_address {
let masklength = unsafe { (*current_address).OnLinkPrefixLength };
match address {
IpAddr::V4(_) => {
let mut mask = 0_u32;
unsafe { ConvertLengthToIpv4Mask(masklength as u32, &mut mask as *mut u32)? };
subnet_mask = Some(IpAddr::V4(Ipv4Addr::from(mask.to_le_bytes())));
}
IpAddr::V6(_) => {
subnet_mask = Some(IpAddr::V6(util::ipv6_netmask_for_prefix(masklength)?));
}
}
break;
}
unsafe {
current_address = (*current_address).Next;
}
}
}
Ok(())
})?;
Ok(subnet_mask.ok_or("Unable to find matching address")?)
}
}
impl Drop for Adapter {
fn drop(&mut self) {
unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
self.adapter = UnsafeHandle(ptr::null_mut());
}
}