Browse Source

Migrate to WinINet

pull/2874/head
Student Main 5 years ago
parent
commit
2efbcc08e5
2 changed files with 182 additions and 27 deletions
  1. +4
    -4
      shadowsocks-csharp/Controller/System/SystemProxy.cs
  2. +178
    -23
      shadowsocks-csharp/Util/SystemProxy/WinINet.cs

+ 4
- 4
shadowsocks-csharp/Controller/System/SystemProxy.cs View File

@@ -31,7 +31,7 @@ namespace Shadowsocks.Controller
{
if (global)
{
Sysproxy.SetIEProxy(true, true, "localhost:" + config.localPort.ToString(), null);
WinINet.ProxyGlobal("localhost:" + config.localPort.ToString(), "<local>");
}
else
{
@@ -45,12 +45,12 @@ namespace Shadowsocks.Controller
pacUrl = pacSrv.PacUrl;
}
Sysproxy.SetIEProxy(true, false, null, pacUrl);
WinINet.ProxyPAC(pacUrl);
}
}
else
{
Sysproxy.SetIEProxy(false, false, null, null);
WinINet.Restore();
}
}
catch (ProxyException ex)
@@ -61,7 +61,7 @@ namespace Shadowsocks.Controller
var ret = MessageBox.Show(I18N.GetString("Error occured when process proxy setting, do you want reset current setting and retry?"), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (ret == DialogResult.Yes)
{
Sysproxy.ResetIEProxy();
WinINet.Reset();
Update(config, forceDisable, pacSrv, true);
}
}


+ 178
- 23
shadowsocks-csharp/Util/SystemProxy/WinINet.cs View File

@@ -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);

}
}

Loading…
Cancel
Save