use crate::error::{Error, Result, check_wnet, wnet_extended_error};
use crate::strings::{from_pwstr, len_u32, to_wide};
use tracing::{debug, trace};
use windows_sys::Win32::Foundation::{
ERROR_EXTENDED_ERROR, ERROR_MORE_DATA, ERROR_NO_MORE_ITEMS, HANDLE, NO_ERROR,
};
use windows_sys::Win32::NetworkManagement::WNet;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct NetResource {
pub local_name: Option<String>,
pub remote_name: Option<String>,
}
#[derive(Debug)]
pub struct Resources {
handle: HANDLE,
buf: Vec<u64>,
finished: bool,
}
impl Iterator for Resources {
type Item = Result<NetResource>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
for _ in 0..4 {
let mut count = 1u32;
let mut size = len_u32(self.buf.len() * size_of::<u64>());
let status = unsafe {
WNet::WNetEnumResourceW(
self.handle,
&raw mut count,
self.buf.as_mut_ptr().cast(),
&raw mut size,
)
};
debug!("WNetEnumResourceW returned {status} (entries={count}, bytes={size})");
match status {
NO_ERROR if count == 0 => {
trace!("zero-entry success; treating as end of enumeration");
self.finished = true;
return None;
}
NO_ERROR => {
let resource = unsafe {
let raw = &*self.buf.as_ptr().cast::<WNet::NETRESOURCEW>();
NetResource {
local_name: from_pwstr(raw.lpLocalName),
remote_name: from_pwstr(raw.lpRemoteName),
}
};
return Some(Ok(resource));
}
ERROR_NO_MORE_ITEMS => {
self.finished = true;
return None;
}
ERROR_MORE_DATA => {
self.buf = vec![0u64; (size as usize).div_ceil(size_of::<u64>())];
}
ERROR_EXTENDED_ERROR => {
self.finished = true;
return Some(Err(wnet_extended_error()));
}
code => {
self.finished = true;
return Some(Err(Error::Windows(code)));
}
}
}
self.finished = true;
Some(Err(Error::Windows(ERROR_MORE_DATA)))
}
}
impl Drop for Resources {
fn drop(&mut self) {
let status = unsafe { WNet::WNetCloseEnum(self.handle) };
debug!("WNetCloseEnum returned {status}");
}
}
fn open(scope: u32, root_remote: Option<&str>) -> Result<Resources> {
let remote = root_remote.map(to_wide).transpose()?;
let root = remote.as_ref().map(|remote| WNet::NETRESOURCEW {
dwUsage: WNet::RESOURCEUSAGE_CONTAINER,
lpRemoteName: remote.as_ptr().cast_mut(),
..Default::default()
});
let mut handle: HANDLE = std::ptr::null_mut();
let status = unsafe {
WNet::WNetOpenEnumW(
scope,
WNet::RESOURCETYPE_DISK,
0,
root.as_ref().map_or(std::ptr::null(), std::ptr::from_ref),
&raw mut handle,
)
};
debug!("WNetOpenEnumW returned {status}");
check_wnet(status)?;
Ok(Resources {
handle,
buf: vec![0u64; 2048], finished: false,
})
}
pub fn connections() -> Result<Resources> {
trace!("enumerating active disk-share connections");
open(WNet::RESOURCE_CONNECTED, None)
}
pub fn remembered() -> Result<Resources> {
trace!("enumerating remembered disk-share mappings");
open(WNet::RESOURCE_REMEMBERED, None)
}
pub fn server_shares(server: &str) -> Result<Resources> {
trace!("enumerating disk shares on {server}");
open(WNet::RESOURCE_GLOBALNET, Some(server))
}