Why InternetSetOptionW with INTERNET_OPTION_PER_CONNECTION_OPTION always return paramerter invalid?

55 views Asked by At

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(())
}
0

There are 0 answers