diff --git a/shadowsocks-csharp/Util/SystemProxy/INTERNET_OPTION.cs b/shadowsocks-csharp/Util/SystemProxy/INTERNET_OPTION.cs
index 584b8e97..169d7ebf 100644
--- a/shadowsocks-csharp/Util/SystemProxy/INTERNET_OPTION.cs
+++ b/shadowsocks-csharp/Util/SystemProxy/INTERNET_OPTION.cs
@@ -30,7 +30,13 @@ namespace Shadowsocks.Util.SystemProxy
INTERNET_OPTION_SETTINGS_CHANGED = 39,
// Causes the proxy data to be reread from the registry for a handle.
- INTERNET_OPTION_REFRESH = 37
+ INTERNET_OPTION_REFRESH = 37,
+
+ // Alerts the current WinInet instance that proxy settings have changed
+ // and that they must update with the new settings.
+ // To alert all available WinInet instances, set the Buffer parameter of
+ // InternetSetOption to NULL and BufferLength to 0 when passing this option.
+ INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95
}
}
diff --git a/shadowsocks-csharp/Util/SystemProxy/INTERNET_PER_CONN_OPTION_LIST.cs b/shadowsocks-csharp/Util/SystemProxy/INTERNET_PER_CONN_OPTION_LIST.cs
index 601cde38..8541e0b7 100644
--- a/shadowsocks-csharp/Util/SystemProxy/INTERNET_PER_CONN_OPTION_LIST.cs
+++ b/shadowsocks-csharp/Util/SystemProxy/INTERNET_PER_CONN_OPTION_LIST.cs
@@ -22,7 +22,7 @@ using System.Runtime.InteropServices;
namespace Shadowsocks.Util.SystemProxy
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct INTERNET_PER_CONN_OPTION_LIST : IDisposable
{
public int Size;
diff --git a/shadowsocks-csharp/Util/SystemProxy/NativeMethods.cs b/shadowsocks-csharp/Util/SystemProxy/NativeMethods.cs
index ab90c569..e5dd0578 100644
--- a/shadowsocks-csharp/Util/SystemProxy/NativeMethods.cs
+++ b/shadowsocks-csharp/Util/SystemProxy/NativeMethods.cs
@@ -26,7 +26,7 @@ namespace Shadowsocks.Util.SystemProxy
///
/// Sets an Internet option.
///
- [DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true)]
+ [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool InternetSetOption(
IntPtr hInternet,
INTERNET_OPTION dwOption,
diff --git a/shadowsocks-csharp/Util/SystemProxy/RAS.cs b/shadowsocks-csharp/Util/SystemProxy/RAS.cs
new file mode 100644
index 00000000..8192a860
--- /dev/null
+++ b/shadowsocks-csharp/Util/SystemProxy/RAS.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Shadowsocks.Util.SystemProxy
+{
+ public static class RAS
+ {
+ private enum RasFieldSizeConstants
+ {
+ #region original header
+
+ //#if (WINVER >= 0x400)
+ //#define RAS_MaxEntryName 256
+ //#define RAS_MaxDeviceName 128
+ //#define RAS_MaxCallbackNumber RAS_MaxPhoneNumber
+ //#else
+ //#define RAS_MaxEntryName 20
+ //#define RAS_MaxDeviceName 32
+ //#define RAS_MaxCallbackNumber 48
+ //#endif
+
+ #endregion
+
+ RAS_MaxEntryName = 256,
+ RAS_MaxPath = 260
+ }
+
+ private const int ERROR_SUCCESS = 0;
+ private const int RASBASE = 600;
+ private const int ERROR_BUFFER_TOO_SMALL = RASBASE + 3;
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ private struct RasEntryName
+ {
+ #region original header
+
+ //#define RASENTRYNAMEW struct tagRASENTRYNAMEW
+ //RASENTRYNAMEW
+ //{
+ // DWORD dwSize;
+ // WCHAR szEntryName[RAS_MaxEntryName + 1];
+ //
+ //#if (WINVER >= 0x500)
+ // //
+ // // If this flag is REN_AllUsers then its a
+ // // system phonebook.
+ // //
+ // DWORD dwFlags;
+ // WCHAR szPhonebookPath[MAX_PATH + 1];
+ //#endif
+ //};
+ //
+ //#define RASENTRYNAMEA struct tagRASENTRYNAMEA
+ //RASENTRYNAMEA
+ //{
+ // DWORD dwSize;
+ // CHAR szEntryName[RAS_MaxEntryName + 1];
+ //
+ //#if (WINVER >= 0x500)
+ // DWORD dwFlags;
+ // CHAR szPhonebookPath[MAX_PATH + 1];
+ //#endif
+ //};
+
+ #endregion
+
+ public int dwSize;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst=(int)RasFieldSizeConstants.RAS_MaxEntryName + 1)]
+ public string szEntryName;
+
+ public int dwFlags;
+
+ [MarshalAs(UnmanagedType.ByValTStr,SizeConst=(int)RasFieldSizeConstants.RAS_MaxPath + 1)]
+ public string szPhonebookPath;
+ }
+
+ [DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
+ private static extern uint RasEnumEntries(
+ string reserved, // reserved, must be NULL
+ string lpszPhonebook, // pointer to full path and file name of phone-book file
+ [In, Out] RasEntryName[] lprasentryname, // buffer to receive phone-book entries
+ ref int lpcb, // size in bytes of buffer
+ out int lpcEntries // number of entries written to buffer
+ );
+
+ ///
+ /// Get all entries from RAS
+ ///
+ ///
+ ///
+ /// 0: success with entries
+ /// 1: success but no entries found
+ /// 2: failed
+ ///
+ public static uint GetAllConns(ref string[] allConns)
+ {
+ int lpNames = 1;
+ int entryNameSize = 0;
+ int lpSize = 0;
+ uint retval = ERROR_SUCCESS;
+ RasEntryName[] names = null;
+
+ entryNameSize = Marshal.SizeOf(typeof(RasEntryName));
+ lpSize = lpNames * entryNameSize;
+
+ names = new RasEntryName[lpNames];
+ names[0].dwSize = entryNameSize;
+
+ retval = RAS.RasEnumEntries(null, null, names, ref lpSize, out lpNames);
+
+ //if we have more than one connection, we need to resize
+ if (retval == ERROR_BUFFER_TOO_SMALL)
+ {
+ names = new RasEntryName[lpNames];
+ for (int i = 0; i < names.Length; i++)
+ {
+ names[i].dwSize = entryNameSize;
+ }
+
+ retval = RAS.RasEnumEntries(null, null, names, ref lpSize, out lpNames);
+
+ }
+
+ if (retval == ERROR_SUCCESS)
+ {
+ if (lpNames == 0)
+ {
+ // no entries found.
+ return 1;
+ }
+
+ allConns = new string[names.Length];
+
+ for (int i = 0; i < names.Length; i++)
+ {
+ allConns[i] = names[i].szEntryName;
+ }
+ return 0;
+ }
+ else
+ {
+ return 2;
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/shadowsocks-csharp/Util/SystemProxy/WinINet.cs b/shadowsocks-csharp/Util/SystemProxy/WinINet.cs
index a95a3b5d..a24d1197 100644
--- a/shadowsocks-csharp/Util/SystemProxy/WinINet.cs
+++ b/shadowsocks-csharp/Util/SystemProxy/WinINet.cs
@@ -19,17 +19,19 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
+using Shadowsocks.Controller;
namespace Shadowsocks.Util.SystemProxy
{
public static class WinINet
{
///
- /// Set IE settings for LAN connection.
+ /// Set IE settings.
///
- public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL)
+ private static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL, string connName)
{
List _optionlist = new List();
+
if (enable)
{
if (global)
@@ -38,17 +40,18 @@ namespace Shadowsocks.Util.SystemProxy
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS_UI,
- Value = { dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_PROXY }
+ Value = { dwValue = (int)(INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_PROXY
+ | INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_DIRECT) }
});
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER,
- Value = { pszValue = Marshal.StringToHGlobalAnsi(proxyServer) }
+ Value = { pszValue = Marshal.StringToHGlobalAuto(proxyServer) }
});
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS,
- Value = { pszValue = Marshal.StringToHGlobalAnsi("") }
+ Value = { pszValue = Marshal.StringToHGlobalAuto("") }
});
}
else
@@ -62,7 +65,7 @@ namespace Shadowsocks.Util.SystemProxy
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_AUTOCONFIG_URL,
- Value = { pszValue = Marshal.StringToHGlobalAnsi(pacURL) }
+ Value = { pszValue = Marshal.StringToHGlobalAuto(pacURL) }
});
}
}
@@ -101,8 +104,9 @@ namespace Shadowsocks.Util.SystemProxy
// Return the unmanaged size of an object in bytes.
optionList.Size = Marshal.SizeOf(optionList);
- // IntPtr.Zero means LAN connection.
- optionList.Connection = IntPtr.Zero;
+ optionList.Connection = connName.IsNullOrEmpty()
+ ? IntPtr.Zero // NULL means LAN
+ : Marshal.StringToHGlobalAuto(connName); // TODO: not working if contains Chinese
optionList.OptionCount = _optionlist.Count;
optionList.OptionError = 0;
@@ -132,15 +136,56 @@ namespace Shadowsocks.Util.SystemProxy
// Notify the system that the registry settings have been changed and cause
// the proxy data to be reread from the registry for a handle.
- NativeMethods.InternetSetOption(
+// bReturn = NativeMethods.InternetSetOption(
+// IntPtr.Zero,
+// INTERNET_OPTION.INTERNET_OPTION_SETTINGS_CHANGED,
+// IntPtr.Zero, 0);
+// if ( ! bReturn )
+// {
+// Logging.Error("InternetSetOption:INTERNET_OPTION_SETTINGS_CHANGED");
+// }
+
+ bReturn = NativeMethods.InternetSetOption(
IntPtr.Zero,
- INTERNET_OPTION.INTERNET_OPTION_SETTINGS_CHANGED,
+ INTERNET_OPTION.INTERNET_OPTION_PROXY_SETTINGS_CHANGED,
IntPtr.Zero, 0);
+ if (!bReturn)
+ {
+ Logging.Error("InternetSetOption:INTERNET_OPTION_PROXY_SETTINGS_CHANGED");
+ }
- NativeMethods.InternetSetOption(
+ bReturn = NativeMethods.InternetSetOption(
IntPtr.Zero,
INTERNET_OPTION.INTERNET_OPTION_REFRESH,
IntPtr.Zero, 0);
+ if (!bReturn)
+ {
+ Logging.Error("InternetSetOption:INTERNET_OPTION_REFRESH");
+ }
+ }
+
+ public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL)
+ {
+ string[] allConnections = null;
+ var ret = RAS.GetAllConns(ref allConnections);
+
+ if (ret == 2)
+ throw new Exception("Cannot get all connections");
+
+ if (ret == 1)
+ {
+ // no entries, only set LAN
+ SetIEProxy(enable, global, proxyServer, pacURL, null);
+ }
+ else if (ret == 0)
+ {
+ // found entries, set LAN and each connection
+ SetIEProxy(enable, global, proxyServer, pacURL, null);
+ foreach (string connName in allConnections)
+ {
+ SetIEProxy(enable, global, proxyServer, pacURL, connName);
+ }
+ }
}
}
}
diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj
index f4ce3ff0..77460a90 100644
--- a/shadowsocks-csharp/shadowsocks-csharp.csproj
+++ b/shadowsocks-csharp/shadowsocks-csharp.csproj
@@ -190,6 +190,7 @@
+