e.g. Dial-up connection and VPN - use INTERNET_OPTION_PROXY_SETTINGS_CHANGED instead of INTERNET_OPTION_SETTINGS_CHANGED Signed-off-by: Syrone Wong <wong.syrone@gmail.com>tags/3.3.4
@@ -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 | |||
} | |||
} |
@@ -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; | |||
@@ -26,7 +26,7 @@ namespace Shadowsocks.Util.SystemProxy | |||
/// <summary> | |||
/// Sets an Internet option. | |||
/// </summary> | |||
[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, | |||
@@ -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 | |||
); | |||
/// <summary> | |||
/// Get all entries from RAS | |||
/// </summary> | |||
/// <param name="allConns"></param> | |||
/// <returns> | |||
/// 0: success with entries | |||
/// 1: success but no entries found | |||
/// 2: failed | |||
/// </returns> | |||
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; | |||
} | |||
} | |||
} | |||
} |
@@ -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 | |||
{ | |||
/// <summary> | |||
/// Set IE settings for LAN connection. | |||
/// Set IE settings. | |||
/// </summary> | |||
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<INTERNET_PER_CONN_OPTION> _optionlist = new List<INTERNET_PER_CONN_OPTION>(); | |||
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("<local>") } | |||
Value = { pszValue = Marshal.StringToHGlobalAuto("<local>") } | |||
}); | |||
} | |||
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); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -190,6 +190,7 @@ | |||
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" /> | |||
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION_LIST.cs" /> | |||
<Compile Include="Util\SystemProxy\NativeMethods.cs" /> | |||
<Compile Include="Util\SystemProxy\RAS.cs" /> | |||
<Compile Include="Util\SystemProxy\WinINet.cs" /> | |||
<Compile Include="Util\Util.cs" /> | |||
<Compile Include="View\ConfigForm.cs"> | |||