@@ -1,5 +1,9 @@
using System;
using Newtonsoft.Json;
using Shadowsocks.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -114,16 +118,29 @@ namespace Shadowsocks.Util.SystemProxy
}
}
public class WinINetSetting
{
public InternetPerConnectionFlags Flags = InternetPerConnectionFlags.Direct;
public string ProxyServer;
public string ProxyBypass;
public string AutoConfigUrl;
}
public class WinINet
{
// TODO: Save, Restore,
// TODO: Query, Set,
private const string SettingFile = "wininet-setting.json";
private static WinINetSetting initialSetting;
static WinINet()
{
Load();
}
public static void ProxyGlobal(string server, string bypass)
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
GetOption(InternetPerConnectionOptionEnum.FlagsUI ,InternetPerConnectionFlags.Proxy),
GetOption(InternetPerConnectionOptionEnum.Flags,InternetPerConnectionFlags.Proxy|InternetPerConnectionFlags.Direct ),
GetOption(InternetPerConnectionOptionEnum.ProxyServer,server),
GetOption(InternetPerConnectionOptionEnum.ProxyBypass,bypass),
};
@@ -133,8 +150,8 @@ namespace Shadowsocks.Util.SystemProxy
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
GetOption(InternetPerConnectionOptionEnum.FlagsUI ,InternetPerConnectionFlags.AutoProxyUrl),
GetOption(InternetPerConnectionOptionEnum.ProxyServer ,url),
GetOption(InternetPerConnectionOptionEnum.Flags,InternetPerConnectionFlags.AutoProxyUrl|InternetPerConnectionFlags.Direct ),
GetOption(InternetPerConnectionOptionEnum.AutoConfigUrl ,url),
};
Exec(options);
}
@@ -142,10 +159,124 @@ namespace Shadowsocks.Util.SystemProxy
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
GetOption(InternetPerConnectionOptionEnum.FlagsUI,InternetPerConnectionFlags.Direct),
GetOption(InternetPerConnectionOptionEnum.Flags,InternetPerConnectionFlags.Direct),
};
Exec(options);
}
private static void Load()
{
try
{
string configContent = File.ReadAllText(Utils.GetTempPath(SettingFile));
initialSetting = JsonConvert.DeserializeObject<WinINetSetting>(configContent);
}
catch (Exception)
{
// Suppress all exceptions. finally block will initialize new user config settings.
}
finally
{
initialSetting ??= new WinINetSetting();
}
}
private static void Save()
{
try
{
using (StreamWriter sw = new StreamWriter(File.Open(Utils.GetTempPath(SettingFile), FileMode.Create)))
{
string jsonString = JsonConvert.SerializeObject(initialSetting, Formatting.Indented);
sw.Write(jsonString);
sw.Flush();
}
}
catch (IOException e)
{
// logger.LogUsefulException(e);
}
}
private static void Record()
{
initialSetting ??= Query();
}
public static void Restore()
{
Set(initialSetting);
}
public static void Set(WinINetSetting setting)
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
GetOption(InternetPerConnectionOptionEnum.Flags,setting.Flags),
GetOption(InternetPerConnectionOptionEnum.ProxyServer,setting.ProxyServer),
GetOption(InternetPerConnectionOptionEnum.ProxyBypass,setting.ProxyBypass),
GetOption(InternetPerConnectionOptionEnum.AutoConfigUrl,setting.AutoConfigUrl),
};
Exec(options);
}
public static void Reset()
{
Set(new WinINetSetting
{
Flags = InternetPerConnectionFlags.Direct,
ProxyServer = "",
ProxyBypass = "",
AutoConfigUrl = "",
});
}
public static WinINetSetting Query()
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.FlagsUI},
new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.ProxyServer},
new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.ProxyBypass},
new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.AutoConfigUrl},
};
var (unmanagedList, listSize) = PrepareOptionList(options, null);
bool ok = InternetQueryOption(IntPtr.Zero, (int)InternetOptions.PerConnectionOption, unmanagedList, ref listSize);
if (!ok) throw new Exception();
var proxy = new WinINetSetting();
InternetPerConnectionOptionList ret = Marshal.PtrToStructure<InternetPerConnectionOptionList>(unmanagedList);
IntPtr p = ret.pOptions;
int nOption = ret.OptionCount;
List<InternetPerConnectionOption> outOptions = new List<InternetPerConnectionOption>();
for (int i = 0; i < nOption; i++)
{
var o = Marshal.PtrToStructure<InternetPerConnectionOption>(p);
outOptions.Add(o);
p += Marshal.SizeOf(o);
}
foreach (var o in outOptions)
{
switch ((InternetPerConnectionOptionEnum)o.dwOption)
{
case InternetPerConnectionOptionEnum.FlagsUI:
case InternetPerConnectionOptionEnum.Flags:
proxy.Flags = (InternetPerConnectionFlags)o.Value.dwValue;
break;
case InternetPerConnectionOptionEnum.AutoConfigUrl:
proxy.AutoConfigUrl = Marshal.PtrToStringAuto(o.Value.pszValue);
break;
case InternetPerConnectionOptionEnum.ProxyBypass:
proxy.ProxyBypass = Marshal.PtrToStringAuto(o.Value.pszValue);
break;
case InternetPerConnectionOptionEnum.ProxyServer:
proxy.ProxyServer = Marshal.PtrToStringAuto(o.Value.pszValue);
break;
default:
break;
}
}
return proxy;
}
private static InternetPerConnectionOption GetOption(
InternetPerConnectionOptionEnum option,
@@ -177,16 +308,7 @@ namespace Shadowsocks.Util.SystemProxy
};
}
private static void Exec(List<InternetPerConnectionOption> options)
{
Exec(options, null);
foreach (var conn in RAS.GetAllConnections())
{
Exec(options, conn);
}
}
private static void Exec(List<InternetPerConnectionOption> options, string connName)
private static (IntPtr, int) PrepareOptionList(List<InternetPerConnectionOption> options, string connName)
{
int len = options.Sum(o => Marshal.SizeOf(o));
@@ -201,10 +323,10 @@ namespace Shadowsocks.Util.SystemProxy
InternetPerConnectionOptionList optionList = new InternetPerConnectionOptionList
{
pOptions = buf,
OptionCount = options.Count,
Connection = string.IsNullOrEmpty(connName)
? IntPtr.Zero
: Marshal.StringToHGlobalAuto(connName),
OptionCount = options.Count,
OptionError = 0,
};
int listSize = Marshal.SizeOf(optionList);
@@ -212,6 +334,34 @@ namespace Shadowsocks.Util.SystemProxy
IntPtr unmanagedList = Marshal.AllocCoTaskMem(listSize);
Marshal.StructureToPtr(optionList, unmanagedList, true);
return (unmanagedList, listSize);
}
private static void ClearOptionList(IntPtr list)
{
InternetPerConnectionOptionList l = Marshal.PtrToStructure<InternetPerConnectionOptionList>(list);
Marshal.FreeCoTaskMem(l.pOptions);
Marshal.FreeCoTaskMem(list);
}
private static void Exec(List<InternetPerConnectionOption> options)
{
// TODO: optimize load and save
Load();
Record();
Exec(options, null);
foreach (var conn in RAS.GetAllConnections())
{
Exec(options, conn);
}
Save();
}
private static void Exec(List<InternetPerConnectionOption> options, string connName)
{
var (unmanagedList, listSize) = PrepareOptionList(options, connName);
bool ok = InternetSetOption(
IntPtr.Zero,
@@ -220,10 +370,12 @@ namespace Shadowsocks.Util.SystemProxy
listSize
);
Marshal.FreeCoTaskMem(buf);
Marshal.FreeCoTaskMem(unmanagedList);
if (!ok) throw new Exception();
if (!ok)
{
int errno = Marshal.GetLastWin32Error();
throw new Win32Exception(errno);
}
ClearOptionList(unmanagedList);
ok = InternetSetOption(
IntPtr.Zero,
(int)InternetOptions.ProxySettingChanged,
@@ -243,6 +395,9 @@ namespace Shadowsocks.Util.SystemProxy
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
}
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool InternetQueryOption(IntPtr hInternet, uint dwOption, IntPtr lpBuffer, ref int lpdwBufferLength);
}
}