trying to rewrite using WINAPI library https://learn.microsoft.com/en-us/windows/win32/wnet/enumerating-network-resources on Rust
let dw_result: DWORD;
let mut h_enum: LPHANDLE = null_mut();
let mut lpnr_local: LPNETRESOURCEW = null_mut();
dw_result = WNetOpenEnumW(RESOURCE_GLOBALNET, // all network resources
RESOURCETYPE_ANY, // all resources
0, // enumerate all resources
lpnr_local, // NULL first time the function is called
h_enum);
if dw_result != WN_NO_ERROR {
println!("WnetOpenEnum failed with error {:?}\n", dw_result);
}
But this code assign 487 into dw_result which means ERROR_INVALID_ADDRESS
And I can't get what is wrong
I guess your doubt is the difference between LPHANDLE and HANDLE.
LP stands for Long Pointer. It's a pointer to a handle.
If you want to use LPHANDLE, please give it a legal address。
Like this:(C++)
HANDLE ph;
LPHANDLE hEnum = &ph;
Or simply use HANDLE.
let mut h_enum: HANDLE;
...
dw_result = WNetOpenEnumW(RESOURCE_GLOBALNET, // all network resources
RESOURCETYPE_ANY, // all resources
0, // enumerate all resources
lpnr_local, // NULL first time the function is called
&h_enum);
Related: LPHANDLE vs. HANDLE
Related
I'm trying to fetch information regarding the network interfaces available on the system via GetInterfaceInfo using Microsoft's windows crate. This requires me to do some unsafe operations, and I get it to work for one interface, but not two:
#[cfg(test)]
mod tests {
use super::*;
use windows::{
core::*, Data::Xml::Dom::*, Win32::Foundation::*, Win32::NetworkManagement::IpHelper::*,
Win32::System::Threading::*, Win32::UI::WindowsAndMessaging::*,
};
#[test]
fn main() {
unsafe {
let mut dw_out_buf_len: u32 = 0;
let mut dw_ret_val =
GetInterfaceInfo(std::ptr::null_mut(), &mut dw_out_buf_len as *mut u32);
if dw_ret_val != ERROR_INSUFFICIENT_BUFFER.0 {
panic!();
}
println!("Size: {}", dw_out_buf_len);
// allocate that amount of memory, which will be used as a buffer
let mut ip_interface_info = Vec::with_capacity(dw_out_buf_len as usize);
let mut ptr = ip_interface_info.as_mut_ptr() as *mut IP_INTERFACE_INFO;
dw_ret_val = GetInterfaceInfo(ptr, &mut dw_out_buf_len as *mut u32);
println!("Num adapters: {}", (*ptr).NumAdapters);
for i in 0..(*ptr).NumAdapters as usize {
println!(
"\tAdapter index: {}\n\tAdapter name: {}",
(*ptr).Adapter[i].Index,
String::from_utf16(&(*ptr).Adapter[i].Name).unwrap()
);
}
}
}
}
It crashes when I'm trying to access the second entry (even though there should be two available):
panicked at 'index out of bounds: the len is 1 but the index is 1'
The struct IP_INTERFACE_INFO containing all data has a field called Adapter which seems to be limited to only be array size of 1. Am I reading this correctly? How is it then supposed to hold multiple adapters?
#[repr(C)]
#[doc = "*Required features: `\"Win32_NetworkManagement_IpHelper\"`*"]
pub struct IP_INTERFACE_INFO {
pub NumAdapters: i32,
pub Adapter: [IP_ADAPTER_INDEX_MAP; 1],
}
It appears that IP_INTERFACE_INFO uses a C flexible array member, which often uses the [1] syntax. The C++ example in Managing Interfaces Using GetInterfaceInfo corroborates this usage:
for (i = 0; i < (unsigned int) pInterfaceInfo->NumAdapters; i++) {
printf(" Adapter Index[%d]: %ld\n", i,
pInterfaceInfo->Adapter[i].Index);
printf(" Adapter Name[%d]: %ws\n\n", i,
pInterfaceInfo->Adapter[i].Name);
}
The equivalent in Rust would be to take the single-element array, get the raw pointer to it, then iterate over that. There are lots of details to be aware of, such as allocation alignment and pointer provenance. Here's an annotated example:
use std::{
alloc::{GlobalAlloc, Layout, System},
mem,
ptr::{self, addr_of},
slice,
};
use windows::{
Win32::Foundation::*,
Win32::NetworkManagement::IpHelper::{
GetInterfaceInfo, IP_ADAPTER_INDEX_MAP, IP_INTERFACE_INFO,
},
};
fn main() {
unsafe {
// Perform the first call to know how many bytes to allocate
let mut raw_buf_len = 0;
let ret_val = GetInterfaceInfo(ptr::null_mut(), &mut raw_buf_len);
assert_eq!(
ret_val, ERROR_INSUFFICIENT_BUFFER.0,
"Expected to get the required buffer size, was {ret_val:?}",
);
// Allocate an appropriately sized *and aligned* buffer to store the result
let buf_len = raw_buf_len.try_into().expect("Invalid buffer length");
let layout = Layout::from_size_align(buf_len, mem::align_of::<IP_INTERFACE_INFO>())
.expect("Could not calculate the appropriate memory layout");
let base_ptr = System.alloc(layout);
let ip_interface_info = base_ptr.cast();
// Perform the second call to get the data
let ret_val = GetInterfaceInfo(ip_interface_info, &mut raw_buf_len);
assert_eq!(
ret_val, NO_ERROR.0,
"Could not get the data on the second call: {ret_val:?}",
);
// Construct a pointer to the adapter array that preserves the provenance of the original pointer
let adapter_ptr = addr_of!((*ip_interface_info).Adapter);
let adapter_ptr = adapter_ptr.cast::<IP_ADAPTER_INDEX_MAP>();
// Combine the pointer and length into a Rust slice
let n_adapters = (*ip_interface_info).NumAdapters;
let n_adapters = n_adapters.try_into().expect("Invalid adapter count");
let adapters = slice::from_raw_parts(adapter_ptr, n_adapters);
println!("Num adapters: {}", adapters.len());
for adapter in adapters {
let IP_ADAPTER_INDEX_MAP {
Index: index,
Name: name,
} = adapter;
// The fixed-size buffer contains data after the UTF-16 NUL character
let name_end = name.iter().position(|&c| c == 0).unwrap_or(name.len());
let name = String::from_utf16_lossy(&name[..name_end]);
println!("Adapter index: {index}\nAdapter name: {name}",);
}
// Free the allocation. This should be wrapped in a type that
// implements `Drop` so we don't leak memory when unwinding a panic.
System.dealloc(base_ptr, layout);
}
}
I am quite new to Rust. I'm trying to build a global cache using lru::LruCache and a RwLock for safety. It needs to be globally accessible based on my program's architecture.
//Size to take up 5MB
const CACHE_ENTRIES: usize = (GIBYTE as usize/ 200) / (BLOCK_SIZE);
pub type CacheEntry = LruCache<i32, Bytes>;
static mut CACHE : CacheEntry = LruCache::new(CACHE_ENTRIES);
lazy_static!{
static ref BLOCKCACHE: RwLock<CacheEntry> = RwLock::new(CACHE);
}
//this gets called from another setup function
async fn download_block(&self,
context: &BlockContext,
buf: &mut [u8],
count: u32, //bytes to read
offset: u64,
buf_index: u32
) -> Result<u32> {
let block_index = context.block_index;
let block_data : Bytes = match {BLOCKCACHE.read().unwrap().get(&block_index)} {
Some(data) => data.to_owned(),
None => download_block_from_remote(context).await.unwrap().to_owned(),
};
//the rest of the function does stuff with the block_data
}
async fn download_block_from_remote(context: &BlockContext) -> Result<Bytes>{
//code to download block data from remote into block_data
{BLOCKCACHE.write().unwrap().put(block_index, block_data.clone())};
Ok(block_data)
}
Right now I am getting an error on this line:
let block_data = match {BLOCKCACHE.read().unwrap().get(&block_index)} {
"cannot borrow as mutable" for the value inside the braces.
"help: trait DerefMut is required to modify through a dereference, but it is not implemented for std::sync::RwLockReadGuard<'_, LruCache<i32, bytes::Bytes>>"
I have gotten some other errors involving ownership and mutability, but I can't seem to get rid of this. Is anyone able to offer guidance on how to get this to work, or set me on the right path if it's just not possible/feasible?
The problem here is that LruCache::get() requires mutable access to the cache object. (Reason: it's a cache object, which has to change its internal state when querying things for the actual caching)
Therefore, if you use an RwLock, you need to use the write() method instead of the read() method.
That said, the fact that get() requires mutable access makes the entire RwLock pretty pointless, and I'd use a normal Mutex instead. There is very rarely the necessity to use an RwLock as it has more overhead compared to a simple Mutex.
I would like to get my username in an std::String using the windows-rs crate.
use bindings::Windows::Win32::{
System::WindowsProgramming::GetUserNameW,
Foundation::PWSTR,
};
fn main() {
let mut pcbbuffer: u32 = 255;
let mut helper: u16 = 0;
let lpbuffer = PWSTR(&mut helper);
println!("lpbuffer: {:?}\npcbbuffer: {:?}", lpbuffer, pcbbuffer);
unsafe {
let success = GetUserNameW(lpbuffer, &mut pcbbuffer);
println!("GetUserNameW succeeded: {:?}\nlpbuffer: {:?}\npcbbuffer: {:?}", success.as_bool(), lpbuffer, pcbbuffer);
}
}
produces the output:
lpbuffer: PWSTR(0xca20f5f76e)
pcbbuffer: 255
GetUserNameW succeeded: true
lpbuffer: PWSTR(0x7200650073)
pcbbuffer: 5
The username is "user" that's 4 + 1 terminating character = 5 which is good. I also see the GetUserNameW function succeeded and the pointer to the string changed.
What are the next steps?
The code as posted works by coincidence alone. It sports a spectacular buffer overflow, hardly what you'd want to see in Rust code. Specifically, you're taking the address of a single u16 value, and pass it into an API, telling it that the pointed-to memory were 255 elements in size.
That needs to be solved: You will have to allocate a buffer large enough to hold the API's output first.
Converting a UTF-16 encoded string to a Rust String with its native encoding can be done using several different ways, such as String::from_utf16_lossy().
The following code roughly sketches out the approach:
fn main() {
let mut cb_buffer = 257_u32;
// Create a buffer of the required size
let mut buffer = Vec::<u16>::with_capacity(cb_buffer as usize);
// Construct a `PWSTR` by taking the address to the first element in the buffer
let lp_buffer = PWSTR(buffer.as_mut_ptr());
let result = unsafe { GetUserNameW(lp_buffer, &mut cb_buffer) };
// If the API returned success, and more than 0 characters were written
if result.as_bool() && cb_buffer > 0 {
// Construct a slice over the valid data
let buffer = unsafe { slice::from_raw_parts(lp_buffer.0, cb_buffer as usize - 1) };
// And convert from UTF-16 to Rust's native encoding
let user_name = String::from_utf16_lossy(buffer);
println!("User name: {}", user_name);
}
}
I'm trying to subscribe to Windows events using EvtSubscribe from the winapi crate, but I'm getting ERROR_INVALID_PARAMETER.
I can not find an example in Rust, but did find a C++ example.
My code that produces ERROR_INVALID_PARAMETER:
fn main() {
unsafe {
let mut callback: winapi::um::winevt::EVT_SUBSCRIBE_CALLBACK = None;
let mut session = std::ptr::null_mut();
let mut signal_event = std::ptr::null_mut();
let mut bookmark = std::ptr::null_mut();
let mut context = std::ptr::null_mut();
let channel_path = "Security";
let channel_path: winnt::LPWSTR = to_wchar(channel_path);
let query = "Event/System[EventID=4624]";
let query: winnt::LPWSTR = to_wchar(query);
let event_handle = winevt::EvtSubscribe(
session,
signal_event,
channel_path,
query,
bookmark,
context,
callback,
winevt::EvtSubscribeStartAtOldestRecord,
);
//println!("{:?}", &event_handle);
println!("{:?}", &winapi::um::errhandlingapi::GetLastError());
} //unsafe end
}
fn to_vec(str: &str) -> Vec<u16> {
return OsStr::new(str)
.encode_wide()
.chain(Some(0).into_iter())
.collect();
}
fn to_wchar(str: &str) -> *mut u16 {
return to_vec(str).as_mut_ptr();
}
The documentation for EvtSubscribe states:
SignalEvent
[...] This parameter must be NULL if the Callback parameter is not
NULL.
Callback
[...] This parameter must be NULL if the SignalEvent parameter is
not NULL.
The unstated implication here is that exactly one of these parameters must be provided. Passing both is explicitly disallowed, but passing neither would not make sense, as otherwise there would be no way for your code to receive the event.
Passing one of these values should cause the code to start working.
Editorially, this is a good example of where a Rust enum would have been a better way to model the API. This would clearly show that the two options are mutually exclusive and one is required:
enum Subscriber {
EventObject(HANDLE),
Callback(EVT_SUBSCRIBE_CALLBACK),
}
Incidentally, your implementation of to_wchar is incorrect and likely leads to memory unsafety. to_vec allocates memory, you take a pointer to it, then that memory is deallocated, creating a dangling pointer. The bad pointer is read by the C code inside of the unsafe block — part of the reason unsafe is needed.
You either need to use mem::forget, as shown in How to expose a Rust `Vec<T>` to FFI? (and then you need to prevent leaking the memory somehow), or you need to take a reference to the data instead of taking the raw pointer.
This appears to partially work but I cannot get the string value to print
pub fn test() {
let mut buf: Vec<u16> = vec![0; 64];
let mut sz: DWORD = 0;
unsafe {
advapi32::GetUserNameW(buf.as_mut_ptr(), &mut sz);
}
let str1 = OsString::from_wide(&buf).into_string().unwrap();
println!("Here: {} {}", sz, str1);
}
Prints:
Here: 10
When I expect it to also print
Here: 10 <username>
As a test, the C version
TCHAR buf[100];
DWORD sz;
GetUserName(buf, &sz);
seems to populate buf fine.
GetUserName
You should re-read the API documentation for GetUserName to recall how the arguments work:
lpnSize [in, out]
On input, this variable specifies the size of the
lpBuffer buffer, in TCHARs. On output, the variable receives the
number of TCHARs copied to the buffer, including the terminating null
character. If lpBuffer is too small, the function fails and
GetLastError returns ERROR_INSUFFICIENT_BUFFER. This parameter
receives the required buffer size, including the terminating null
character.
TL;DR:
On input: caller tells the API how many spaces the buffer has.
On success: API tells the caller how many spaces were used.
On failure: API tells the caller how many spaces were needed.
C version
This has a fixed-size stack-allocated array of 100 TCHARs.
This code is broken and unsafe because sz is uninitialized. This allows the API to write an undefined number of characters to a buffer that's only 100 long. If the username is over 100 characters, you've just introduced a security hole into your program.
Rust version
The Rust code is broken in a much better way. sz is set to zero, which means "you may write zero entries of data", so it writes zero entries. Thus, the Vec buffer is full of zeros and the resulting string is empty. The buffer is reported too small to receive the username, so GetUserNameW sets sz to the number of characters that the buffer needs to have allocated.
What to do
One "fix" would be to set sz to the length of your array. However, this is likely to have over- or under-allocated the buffer.
If you are ok with a truncated string (and I'm not sure if TCHAR strings can be split arbitrarily, I know UTF-8 cannot), then it would be better to use a fixed-size array like the C code.
If you want to more appropriately allocate memory to call this type of WinAPI function, see What is the right way to allocate data to pass to an FFI call?.
extern crate advapi32;
extern crate winapi;
use std::ptr;
fn get_user_name() -> String {
unsafe {
let mut size = 0;
let retval = advapi32::GetUserNameW(ptr::null_mut(), &mut size);
assert_eq!(retval, 0, "Should have failed");
let mut username = Vec::with_capacity(size as usize);
let retval = advapi32::GetUserNameW(username.as_mut_ptr(), &mut size);
assert_ne!(retval, 0, "Perform better error handling");
assert!((size as usize) <= username.capacity());
username.set_len(size as usize);
// Beware: This leaves the trailing NUL character in the final string,
// you may want to remove it!
String::from_utf16(&username).unwrap()
}
}
fn main() {
println!("{:?}", get_user_name()); // "IEUser\u{0}"
}