I am newbie using winapi in rust. I searched the whole web and can't solved this issue. I have use ManuallyDrop to ensure the all params data is valid and alive. But, the InternetSetOptionW invoke with INTERNET_OPTION_PER_CONNECTION_OPTION option always throw err. You can see the core code in the ending of code block.
A full test demo with unit test, you can found at https://github.com/greenhat616/sysproxy-rs.
Thanks for a lot !
The key core block:
use crate::{Error, Result, Sysproxy};
use std::ffi::c_void;
use std::{mem::size_of, mem::ManuallyDrop, net::SocketAddr, str::FromStr};
use windows::core::{w, HSTRING, PWSTR};
use windows::Win32::Foundation::BOOL;
use windows::Win32::NetworkManagement::Rras::{
RasEnumEntriesW, ERROR_BUFFER_TOO_SMALL, RASENTRYNAMEW,
};
use windows::Win32::Networking::WinHttp::{
WinHttpGetIEProxyConfigForCurrentUser, WINHTTP_CURRENT_USER_IE_PROXY_CONFIG,
};
use windows::Win32::Networking::WinInet::{
InternetGetProxyForUrl, InternetSetOptionW, INTERNET_OPTION_PER_CONNECTION_OPTION,
INTERNET_OPTION_PROXY_SETTINGS_CHANGED, INTERNET_OPTION_REFRESH,
INTERNET_PER_CONN_AUTOCONFIG_URL, INTERNET_PER_CONN_FLAGS, INTERNET_PER_CONN_OPTIONW,
INTERNET_PER_CONN_OPTIONW_0, INTERNET_PER_CONN_OPTION_LISTW, INTERNET_PER_CONN_PROXY_BYPASS,
INTERNET_PER_CONN_PROXY_SERVER, PROXY_TYPE_AUTO_DETECT, PROXY_TYPE_AUTO_PROXY_URL,
PROXY_TYPE_DIRECT, PROXY_TYPE_PROXY,
};
fn set_global_proxy(server: String, bypass: String) -> Result<()> {
let p_opts = &mut [ManuallyDrop::new(INTERNET_PER_CONN_OPTIONW::default()); 3];
p_opts[0].dwOption = INTERNET_PER_CONN_FLAGS;
p_opts[0].Value.dwValue = PROXY_TYPE_PROXY | PROXY_TYPE_DIRECT;
let s = server.encode_utf16().chain([0u16]).collect::<Vec<u16>>();
p_opts[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
p_opts[1].Value.pszValue = PWSTR::from_raw(s.as_ptr() as *mut u16);
let b = if bypass.is_empty() {
"<local>".encode_utf16().chain([0u16]).collect::<Vec<u16>>()
} else {
bypass
.clone()
.encode_utf16()
.chain([0u16])
.collect::<Vec<u16>>()
};
p_opts[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
p_opts[2].Value.pszValue = PWSTR::from_raw(b.as_ptr() as *mut u16);
let opts = INTERNET_PER_CONN_OPTION_LISTW {
dwSize: size_of::<INTERNET_PER_CONN_OPTION_LISTW>() as u32,
dwOptionCount: 3,
dwOptionError: 0,
pOptions: p_opts.as_mut_ptr() as *mut INTERNET_PER_CONN_OPTIONW,
pszConnection: PWSTR::null(),
};
apply(&opts)
}
fn apply(options: &INTERNET_PER_CONN_OPTION_LISTW) -> Result<()> {
let mut dw_cb = 0;
let mut dw_entries = 0;
let mut ret;
unsafe {
ret = RasEnumEntriesW(None, None, None, &mut dw_cb, &mut dw_entries);
}
if ret == ERROR_BUFFER_TOO_SMALL {
let mut entries = Vec::<RASENTRYNAMEW>::with_capacity(dw_cb as usize);
for _ in 0..dw_cb {
entries.push(RASENTRYNAMEW {
dwSize: size_of::<RASENTRYNAMEW>() as u32,
..Default::default()
});
}
unsafe {
ret = RasEnumEntriesW(
None,
None,
Some(entries.as_mut_ptr()),
&mut dw_cb,
&mut dw_entries,
);
}
match ret {
0 => {
println!("entries: {:?}", entries);
apply_connect(options, PWSTR::null())?;
for entry in entries.iter() {
apply_connect(
options,
PWSTR::from_raw(entry.szEntryName.as_ptr() as *mut u16),
)?;
}
return Ok(());
}
_ => return Err(SystemCallFailed::Raw(format!("RasEnumEntriesW: {}", ret)).into()),
}
}
if dw_entries > 1 {
return Err(SystemCallFailed::Raw("acquire buffer size".into()).into());
}
// No ras entry, set default only.
match apply_connect(options, PWSTR::null()) {
Ok(_) => Ok(()),
Err(e) => Err(e.into()),
}
}
fn apply_connect(
options: &INTERNET_PER_CONN_OPTION_LISTW,
conn: PWSTR,
) -> std::result::Result<(), SystemCallFailed> {
let opts = &mut options.clone();
opts.pszConnection = conn;
unsafe {
// setting options
let opts = opts as *const INTERNET_PER_CONN_OPTION_LISTW as *const c_void;
InternetSetOptionW(
None,
INTERNET_OPTION_PER_CONNECTION_OPTION,
Some(opts),
size_of::<INTERNET_PER_CONN_OPTION_LISTW>() as u32,
)
.unwrap(); // This invoke always return parmas invalid err
// propagating changes
InternetSetOptionW(None, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, None, 0)?;
// refreshing
InternetSetOptionW(None, INTERNET_OPTION_REFRESH, None, 0)?;
}
Ok(())
}