v2rayN icon indicating copy to clipboard operation
v2rayN copied to clipboard

[Code Bug]: ProxySetting.SetProxy() & SetIEProxy()

Open mega-optimus opened this issue 1 year ago • 2 comments

https://github.com/2dust/v2rayN/blob/dafc83aa534ff71022f5faf6b116f6cd2b3ac6f0/v2rayN/v2rayN/Handler/SysProxyHandle.cs#L82

Where both functions are called:

 ProxySetting.SetProxy(strProxy, strExceptions, 2);
 SetIEProxy(true, strProxy, strExceptions);

ProxySetting.SetProxy() internally calls the InternetSetOption() API to change system proxy. SetIEProxy() internally calls sysproxy.exe, which internally calls the InternetSetOption() API to change system proxy. They are the same thing.

Calling sysproxy.exe seems to trigger various problems, thus ProxySetting.SetProxy() is preferred. https://github.com/2dust/v2rayN/issues?q=sysproxy.exe+

=================== Update regarding https://github.com/2dust/v2rayN/issues/2233 : Currently ProxySetting.SetProxy() doesn't quite work, because its call to InternetSetOption() is not proper.

  1. From https://github.com/Noisyfox/sysproxy/blob/54133d2fff6f0dabffa3eaa971d7fdb7a6e1b48e/sysproxy/main.c#L85 , we can see that InternetSetOption() is called 3 times, with different flags.
  2. From https://github.com/Noisyfox/sysproxy/blob/54133d2fff6f0dabffa3eaa971d7fdb7a6e1b48e/sysproxy/main.c#L116 , we can see that the change is applied to all RAS entries, besides the default connection.

mega-optimus avatar May 19 '23 05:05 mega-optimus

Please help fix this bug

2dust avatar May 21 '23 01:05 2dust

Please help fix this bug

I'll try, but feel free to take it.

mega-optimus avatar May 21 '23 02:05 mega-optimus

@2dust 最近有空研究了一下这个问题,发现在sysproxy里面调用了RasEnumEntries枚举所有网卡,然后对每个网卡调用InternetSetOption。 参考代码:https://github.com/shadowsocks/sysproxy/blob/master/sysproxy/main.c#L119

而这个v2rayN C#代码没调用RasEnumEntries, pszConnection 没有赋值的结果是null,即LAN connection,而PPPoE是WAN connection造成无法工作。参考:https://learn.microsoft.com/en-us/windows/win32/wininet/setting-and-retrieving-internet-options

此外目前C#代码里没有调用INTERNET_OPTION_PROXY_SETTINGS_CHANGED和INTERNET_OPTION_REFRESH通知系统其他应用配置更新了。

修复办法就是照着sysproxy里去实现。不过我这里没有PPPoE的网络,无法调试验证。下面是范例代码:

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;

public class ProxySettings
{
    // Define P/Invoke signatures
    [DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);

    [DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
    private static extern uint RasEnumEntries(string reserved, string lpszPhonebook, [In, Out] RASENTRYNAME[] lprasentryname, ref int lpcb, ref int lpcEntries);

    // Define necessary constants and structs...

    private const int RAS_MaxEntryName = 256;
    private const int RAS_MaxDeviceType = 16;
    private const int RAS_MaxDeviceName = 128;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct RASENTRYNAME
    {
        public int dwSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
        public string szEntryName;
        // Other fields omitted...
    }

    // 省略声明INTERNET_PER_CONN_OPTION, INTERNET_PER_CONN_OPTION_LIST, and other structs/constants...

    public static void SetSocks5Proxy(string proxy)
    {
        foreach (var connection in EnumerateRasEntries())
        {
            SetProxy(connection, PROXY_TYPE_PROXY, proxy);
        }
    }

    public static void SetDirectConnection()
    {
        foreach (var connection in EnumerateRasEntries())
        {
            SetProxy(connection, PROXY_TYPE_DIRECT, null);
        }
    }
    // 枚举所有连接
    private static IEnumerable<string> EnumerateRasEntries()
    {
        var entries = new List<string>();
        int bufSize = Marshal.SizeOf(typeof(RASENTRYNAME));
        RASENTRYNAME[] rasEntryNames = new RASENTRYNAME[1];
        rasEntryNames[0].dwSize = bufSize;
        int size = bufSize;
        int numEntries = 0;

        uint result = RasEnumEntries(null, null, rasEntryNames, ref size, ref numEntries);

        if (result != 0)
        {
            throw new ApplicationException("RasEnumEntries failed.");
        }

        for (int i = 0; i < numEntries; i++)
        {
            entries.Add(rasEntryNames[i].szEntryName);
        }

        return entries;
    }
   //设置代理服务器,connectionName是上面枚举的连接名称,proxyType是代理类型,proxy是代理服务器地址
    private static void SetProxy(string connectionName, int proxyType, string proxy)
    {
        INTERNET_PER_CONN_OPTION[] options = new INTERNET_PER_CONN_OPTION[2];

        options[0].Option = INTERNET_PER_CONN_FLAGS;
        options[0].Value.IntValue = proxyType;

        options[1].Option = INTERNET_PER_CONN_PROXY_SERVER;
        if (proxyType == PROXY_TYPE_PROXY)
        {
            options[1].Value.StringPtrValue = Marshal.StringToHGlobalAuto(proxy);
        }
        else
        {
            options[1].Value.StringPtrValue = IntPtr.Zero;
        }

        INTERNET_PER_CONN_OPTION_LIST optionList = new INTERNET_PER_CONN_OPTION_LIST();

        int listSize = Marshal.SizeOf(optionList);
        optionList.Size = listSize;
        optionList.Connection = Marshal.StringToHGlobalAuto(connectionName); // Use the connection name
        optionList.OptionCount = options.Length;
        optionList.OptionError = 0;

        int optionSize = Marshal.SizeOf(options[0]);
        IntPtr optionPtr = Marshal.AllocCoTaskMem(optionSize * options.Length);
        for (int i = 0; i < options.Length; ++i)
        {
            IntPtr opt = new IntPtr(optionPtr.ToInt64() + (i * optionSize));
            Marshal.StructureToPtr(options[i], opt, false);
        }

        optionList.Options = optionPtr;

        // Apply the new settings
        Marshal.StructureToPtr(optionList, optionPtr, false);
        bool result = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_PER_CONNECTION_OPTION, optionPtr, listSize);

        // Notify the system that the registry settings have been changed and cause them to be refreshed
        InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
        InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);

        if (!result)
        {
            throw new ApplicationException("Setting proxy failed.");
        }

        // Free the allocated memory
        Marshal.FreeCoTaskMem(optionPtr);
        Marshal.FreeHGlobal(optionList.Connection);
        if (proxyType == PROXY_TYPE_PROXY)
        {
            Marshal.FreeHGlobal(options[1].Value.StringPtrValue);
        }
    }

}

// Usage example
ProxySettings.SetSocks5Proxy("127.0.0.1:1080"); // Set SOCKS5 proxy
ProxySettings.SetDirectConnection();            // Set direct connection (no proxy)

flywhc avatar Nov 23 '23 14:11 flywhc