@@ -1,23 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net.Sockets; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using Shadowsocks.Util.Sockets; | |||||
namespace Shadowsocks.Controller.Service | |||||
{ | |||||
class Http2Socks5 : Listener.Service | |||||
{ | |||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) | |||||
{ | |||||
if (socket.ProtocolType != ProtocolType.Tcp) | |||||
{ | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,261 @@ | |||||
using System; | |||||
using System.Net; | |||||
using System.Net.Sockets; | |||||
using Shadowsocks.Util.Sockets; | |||||
namespace Shadowsocks.Controller | |||||
{ | |||||
class PortForwarder : Listener.Service | |||||
{ | |||||
int _targetPort; | |||||
public PortForwarder(int targetPort) | |||||
{ | |||||
this._targetPort = targetPort; | |||||
} | |||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) | |||||
{ | |||||
if (socket.ProtocolType != ProtocolType.Tcp) | |||||
{ | |||||
return false; | |||||
} | |||||
new Handler().Start(firstPacket, length, socket, this._targetPort); | |||||
return true; | |||||
} | |||||
class Handler | |||||
{ | |||||
private byte[] _firstPacket; | |||||
private int _firstPacketLength; | |||||
private Socket _local; | |||||
private WrappedSocket _remote; | |||||
private bool _closed = false; | |||||
private bool _localShutdown = false; | |||||
private bool _remoteShutdown = false; | |||||
public const int RecvSize = 16384; | |||||
// remote receive buffer | |||||
private byte[] remoteRecvBuffer = new byte[RecvSize]; | |||||
// connection receive buffer | |||||
private byte[] connetionRecvBuffer = new byte[RecvSize]; | |||||
// instance-based lock | |||||
private readonly object _Lock = new object(); | |||||
public void Start(byte[] firstPacket, int length, Socket socket, int targetPort) | |||||
{ | |||||
this._firstPacket = firstPacket; | |||||
this._firstPacketLength = length; | |||||
this._local = socket; | |||||
try | |||||
{ | |||||
EndPoint remoteEP = SocketUtil.GetEndPoint("127.0.0.1", targetPort); | |||||
// Connect to the remote endpoint. | |||||
_remote = new WrappedSocket(); | |||||
_remote.BeginConnect(remoteEP, ConnectCallback, null); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void ConnectCallback(IAsyncResult ar) | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
_remote.EndConnect(ar); | |||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||||
HandshakeReceive(); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void HandshakeReceive() | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
_remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, new AsyncCallback(StartPipe), null); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void StartPipe(IAsyncResult ar) | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
_remote.EndSend(ar); | |||||
_remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
_local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
int bytesRead = _remote.EndReceive(ar); | |||||
if (bytesRead > 0) | |||||
{ | |||||
_local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeConnectionSendCallback), null); | |||||
} | |||||
else | |||||
{ | |||||
_local.Shutdown(SocketShutdown.Send); | |||||
_localShutdown = true; | |||||
CheckClose(); | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
int bytesRead = _local.EndReceive(ar); | |||||
if (bytesRead > 0) | |||||
{ | |||||
_remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeRemoteSendCallback), null); | |||||
} | |||||
else | |||||
{ | |||||
_remote.Shutdown(SocketShutdown.Send); | |||||
_remoteShutdown = true; | |||||
CheckClose(); | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void PipeRemoteSendCallback(IAsyncResult ar) | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
_remote.EndSend(ar); | |||||
_local.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void PipeConnectionSendCallback(IAsyncResult ar) | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
_local.EndSend(ar); | |||||
_remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
this.Close(); | |||||
} | |||||
} | |||||
private void CheckClose() | |||||
{ | |||||
if (_localShutdown && _remoteShutdown) | |||||
{ | |||||
this.Close(); | |||||
} | |||||
} | |||||
public void Close() | |||||
{ | |||||
lock (_Lock) | |||||
{ | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
_closed = true; | |||||
} | |||||
if (_local != null) | |||||
{ | |||||
try | |||||
{ | |||||
_local.Shutdown(SocketShutdown.Both); | |||||
_local.Close(); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
if (_remote != null) | |||||
{ | |||||
try | |||||
{ | |||||
_remote.Shutdown(SocketShutdown.Both); | |||||
_remote.Dispose(); | |||||
} | |||||
catch (SocketException e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,216 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel; | |||||
using System.Diagnostics; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Net.NetworkInformation; | |||||
using System.Net.Sockets; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
using System.Windows.Forms; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
using Shadowsocks.Util.ProcessManagement; | |||||
namespace Shadowsocks.Controller | |||||
{ | |||||
class PrivoxyRunner | |||||
{ | |||||
private static int Uid; | |||||
private static string UniqueConfigFile; | |||||
private static Job PrivoxyJob; | |||||
private Process _process; | |||||
private int _runningPort; | |||||
static PrivoxyRunner() | |||||
{ | |||||
try | |||||
{ | |||||
Uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance. | |||||
UniqueConfigFile = $"privoxy_{Uid}.conf"; | |||||
PrivoxyJob = new Job(); | |||||
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe); | |||||
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll); | |||||
} | |||||
catch (IOException e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
public int RunningPort => _runningPort; | |||||
public void Start(Configuration configuration) | |||||
{ | |||||
if (_process == null) | |||||
{ | |||||
Process[] existingPrivoxy = Process.GetProcessesByName("ss_privoxy"); | |||||
foreach (Process p in existingPrivoxy.Where(IsChildProcess)) | |||||
{ | |||||
KillProcess(p); | |||||
} | |||||
string privoxyConfig = Resources.privoxy_conf; | |||||
_runningPort = this.GetFreePort(); | |||||
privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); | |||||
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString()); | |||||
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); | |||||
FileManager.ByteArrayToFile(Utils.GetTempPath(UniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig)); | |||||
_process = new Process(); | |||||
// Configure the process using the StartInfo properties. | |||||
_process.StartInfo.FileName = "ss_privoxy.exe"; | |||||
_process.StartInfo.Arguments = UniqueConfigFile; | |||||
_process.StartInfo.WorkingDirectory = Utils.GetTempPath(); | |||||
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; | |||||
_process.StartInfo.UseShellExecute = true; | |||||
_process.StartInfo.CreateNoWindow = true; | |||||
_process.Start(); | |||||
/* | |||||
* Add this process to job obj associated with this ss process, so that | |||||
* when ss exit unexpectedly, this process will be forced killed by system. | |||||
*/ | |||||
PrivoxyJob.AddProcess(_process.Handle); | |||||
} | |||||
RefreshTrayArea(); | |||||
} | |||||
public void Stop() | |||||
{ | |||||
if (_process != null) | |||||
{ | |||||
KillProcess(_process); | |||||
_process.Dispose(); | |||||
_process = null; | |||||
} | |||||
RefreshTrayArea(); | |||||
} | |||||
private static void KillProcess(Process p) | |||||
{ | |||||
try | |||||
{ | |||||
p.CloseMainWindow(); | |||||
p.WaitForExit(100); | |||||
if (!p.HasExited) | |||||
{ | |||||
p.Kill(); | |||||
p.WaitForExit(); | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
/* | |||||
* We won't like to kill other ss instances' ss_privoxy.exe. | |||||
* This function will check whether the given process is created | |||||
* by this process by checking the module path or command line. | |||||
* | |||||
* Since it's required to put ss in different dirs to run muti instances, | |||||
* different instance will create their unique "privoxy_UID.conf" where | |||||
* UID is hash of ss's location. | |||||
*/ | |||||
private static bool IsChildProcess(Process process) | |||||
{ | |||||
try | |||||
{ | |||||
if (Utils.IsPortableMode()) | |||||
{ | |||||
/* | |||||
* Under PortableMode, we could identify it by the path of ss_privoxy.exe. | |||||
*/ | |||||
var path = process.MainModule.FileName; | |||||
return Utils.GetTempPath("ss_privoxy.exe").Equals(path); | |||||
} | |||||
else | |||||
{ | |||||
var cmd = process.GetCommandLine(); | |||||
return cmd.Contains(UniqueConfigFile); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
/* | |||||
* Sometimes Process.GetProcessesByName will return some processes that | |||||
* are already dead, and that will cause exceptions here. | |||||
* We could simply ignore those exceptions. | |||||
*/ | |||||
Logging.LogUsefulException(ex); | |||||
return false; | |||||
} | |||||
} | |||||
private int GetFreePort() | |||||
{ | |||||
int defaultPort = 8123; | |||||
try | |||||
{ | |||||
// TCP stack please do me a favor | |||||
TcpListener l = new TcpListener(IPAddress.Loopback, 0); | |||||
l.Start(); | |||||
var port = ((IPEndPoint)l.LocalEndpoint).Port; | |||||
l.Stop(); | |||||
return port; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
// in case access denied | |||||
Logging.LogUsefulException(e); | |||||
return defaultPort; | |||||
} | |||||
} | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
public struct RECT | |||||
{ | |||||
public int left; | |||||
public int top; | |||||
public int right; | |||||
public int bottom; | |||||
} | |||||
[DllImport("user32.dll")] | |||||
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); | |||||
[DllImport("user32.dll")] | |||||
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); | |||||
[DllImport("user32.dll")] | |||||
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); | |||||
[DllImport("user32.dll")] | |||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); | |||||
public void RefreshTrayArea() | |||||
{ | |||||
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null); | |||||
IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null); | |||||
IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null); | |||||
IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area"); | |||||
if (notificationAreaHandle == IntPtr.Zero) | |||||
{ | |||||
notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area"); | |||||
IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null); | |||||
IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area"); | |||||
RefreshTrayArea(overflowNotificationAreaHandle); | |||||
} | |||||
RefreshTrayArea(notificationAreaHandle); | |||||
} | |||||
private static void RefreshTrayArea(IntPtr windowHandle) | |||||
{ | |||||
const uint wmMousemove = 0x0200; | |||||
RECT rect; | |||||
GetClientRect(windowHandle, out rect); | |||||
for (var x = 0; x < rect.right; x += 5) | |||||
for (var y = 0; y < rect.bottom; y += 5) | |||||
SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x); | |||||
} | |||||
} | |||||
} |
@@ -30,6 +30,7 @@ namespace Shadowsocks.Controller | |||||
private PACServer _pacServer; | private PACServer _pacServer; | ||||
private Configuration _config; | private Configuration _config; | ||||
private StrategyManager _strategyManager; | private StrategyManager _strategyManager; | ||||
private PrivoxyRunner privoxyRunner; | |||||
private GFWListUpdater gfwListUpdater; | private GFWListUpdater gfwListUpdater; | ||||
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; | public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; | ||||
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } | public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } | ||||
@@ -264,6 +265,10 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_listener.Stop(); | _listener.Stop(); | ||||
} | } | ||||
if (privoxyRunner != null) | |||||
{ | |||||
privoxyRunner.Stop(); | |||||
} | |||||
if (_config.enabled) | if (_config.enabled) | ||||
{ | { | ||||
SystemProxy.Update(_config, true, null); | SystemProxy.Update(_config, true, null); | ||||
@@ -427,7 +432,11 @@ namespace Shadowsocks.Controller | |||||
// some logic in configuration updated the config when saving, we need to read it again | // some logic in configuration updated the config when saving, we need to read it again | ||||
_config = Configuration.Load(); | _config = Configuration.Load(); | ||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | ||||
if (privoxyRunner == null) | |||||
{ | |||||
privoxyRunner = new PrivoxyRunner(); | |||||
} | |||||
if (_pacServer == null) | if (_pacServer == null) | ||||
{ | { | ||||
_pacServer = new PACServer(); | _pacServer = new PACServer(); | ||||
@@ -448,7 +457,11 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_listener.Stop(); | _listener.Stop(); | ||||
} | } | ||||
// don't put PrivoxyRunner.Start() before pacServer.Stop() | |||||
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1 | |||||
// though UseShellExecute is set to true now | |||||
// http://stackoverflow.com/questions/10235093/socket-doesnt-close-after-application-exits-if-a-launched-process-is-open | |||||
privoxyRunner.Stop(); | |||||
try | try | ||||
{ | { | ||||
var strategy = GetCurrentStrategy(); | var strategy = GetCurrentStrategy(); | ||||
@@ -457,12 +470,15 @@ namespace Shadowsocks.Controller | |||||
strategy.ReloadServers(); | strategy.ReloadServers(); | ||||
} | } | ||||
privoxyRunner.Start(_config); | |||||
TCPRelay tcpRelay = new TCPRelay(this, _config); | TCPRelay tcpRelay = new TCPRelay(this, _config); | ||||
UDPRelay udpRelay = new UDPRelay(this); | UDPRelay udpRelay = new UDPRelay(this); | ||||
List<Listener.IService> services = new List<Listener.IService>(); | List<Listener.IService> services = new List<Listener.IService>(); | ||||
services.Add(tcpRelay); | services.Add(tcpRelay); | ||||
services.Add(udpRelay); | services.Add(udpRelay); | ||||
services.Add(_pacServer); | services.Add(_pacServer); | ||||
services.Add(new PortForwarder(privoxyRunner.RunningPort)); | |||||
_listener = new Listener(services); | _listener = new Listener(services); | ||||
_listener.Start(_config); | _listener.Start(_config); | ||||
} | } | ||||
@@ -0,0 +1,5 @@ | |||||
listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ | |||||
show-on-task-bar 0 | |||||
activity-animation 0 | |||||
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . | |||||
hide-console |
@@ -111,6 +111,40 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// </summary> | |||||
internal static byte[] mgwz_dll { | |||||
get { | |||||
object obj = ResourceManager.GetObject("mgwz_dll", resourceCulture); | |||||
return ((byte[])(obj)); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// 查找类似 listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ | |||||
///show-on-task-bar 0 | |||||
///activity-animation 0 | |||||
///forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . | |||||
///hide-console | |||||
/// 的本地化字符串。 | |||||
/// </summary> | |||||
internal static string privoxy_conf { | |||||
get { | |||||
return ResourceManager.GetString("privoxy_conf", resourceCulture); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// </summary> | |||||
internal static byte[] privoxy_exe { | |||||
get { | |||||
object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture); | |||||
return ((byte[])(obj)); | |||||
} | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | /// 查找 System.Byte[] 类型的本地化资源。 | ||||
/// </summary> | /// </summary> | ||||
@@ -213,29 +247,29 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找类似 # translation for Traditional Chinese | |||||
/// | |||||
///Shadowsocks=Shadowsocks | |||||
/// | |||||
///# Menu items | |||||
/// | |||||
///Enable System Proxy=啟用系統代理 | |||||
///Mode=系統代理模式 | |||||
///PAC=PAC 模式 | |||||
///Global=全局模式 | |||||
///Servers=伺服器 | |||||
///Edit Servers...=編輯伺服器... | |||||
///Statistics Config...=統計配置... | |||||
///Start on Boot=開機啟動 | |||||
///Forward Proxy...=正向代理設置... | |||||
///Allow Clients from LAN=允許來自區域網路的連接 | |||||
///Local PAC=使用本地 PAC | |||||
///Online PAC=使用在線 PAC | |||||
///Edit Local PAC File...=編輯本地 PAC 文件... | |||||
///Update Local PAC from GFWList=從 GFWList 更新本地 PAC | |||||
///Edit User Rule for GFWList...=編輯 GFWList 的用戶規則... | |||||
///Show QRCode...=顯示 QR 碼... | |||||
///S [字符串的其余部分被截断]"; 的本地化字符串。 | |||||
/// 查找类似 # translation for Traditional Chinese | |||||
/// | |||||
///Shadowsocks=Shadowsocks | |||||
/// | |||||
///# Menu items | |||||
/// | |||||
///Enable System Proxy=啟用系統代理 | |||||
///Mode=系統代理模式 | |||||
///PAC=PAC 模式 | |||||
///Global=全局模式 | |||||
///Servers=伺服器 | |||||
///Edit Servers...=編輯伺服器... | |||||
///Statistics Config...=統計配置... | |||||
///Start on Boot=開機啟動 | |||||
///Forward Proxy...=正向代理設置... | |||||
///Allow Clients from LAN=允許來自區域網路的連接 | |||||
///Local PAC=使用本地 PAC | |||||
///Online PAC=使用在線 PAC | |||||
///Edit Local PAC File...=編輯本地 PAC 文件... | |||||
///Update Local PAC from GFWList=從 GFWList 更新本地 PAC | |||||
///Edit User Rule for GFWList...=編輯 GFWList 的用戶規則... | |||||
///Show QRCode...=顯示 QR 碼... | |||||
///Scan QRCode from Screen [字符串的其余部分被截断]"; 的本地化字符串。 | |||||
/// </summary> | /// </summary> | ||||
internal static string zh_tw { | internal static string zh_tw { | ||||
get { | get { | ||||
@@ -127,6 +127,15 @@ | |||||
<data name="libsscrypto_dll" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="libsscrypto_dll" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\data\libsscrypto.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | <value>..\data\libsscrypto.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||
</data> | </data> | ||||
<data name="mgwz_dll" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||||
<value>..\data\mgwz.dll.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</data> | |||||
<data name="privoxy_conf" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||||
<value>..\data\privoxy_conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | |||||
</data> | |||||
<data name="privoxy_exe" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||||
<value>..\data\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</data> | |||||
<data name="proxy_pac_txt" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="proxy_pac_txt" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | <value>..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||
</data> | </data> | ||||
@@ -0,0 +1,179 @@ | |||||
using System; | |||||
using System.Diagnostics; | |||||
using System.Runtime.InteropServices; | |||||
using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Util.ProcessManagement | |||||
{ | |||||
/* | |||||
* See: | |||||
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net | |||||
*/ | |||||
public class Job : IDisposable | |||||
{ | |||||
private IntPtr handle = IntPtr.Zero; | |||||
public Job() | |||||
{ | |||||
handle = CreateJobObject(IntPtr.Zero, null); | |||||
var extendedInfoPtr = IntPtr.Zero; | |||||
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION | |||||
{ | |||||
LimitFlags = 0x2000 | |||||
}; | |||||
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION | |||||
{ | |||||
BasicLimitInformation = info | |||||
}; | |||||
try | |||||
{ | |||||
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); | |||||
extendedInfoPtr = Marshal.AllocHGlobal(length); | |||||
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); | |||||
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, | |||||
(uint) length)) | |||||
throw new Exception(string.Format("Unable to set information. Error: {0}", | |||||
Marshal.GetLastWin32Error())); | |||||
} | |||||
finally | |||||
{ | |||||
if (extendedInfoPtr != IntPtr.Zero) | |||||
{ | |||||
Marshal.FreeHGlobal(extendedInfoPtr); | |||||
extendedInfoPtr = IntPtr.Zero; | |||||
} | |||||
} | |||||
} | |||||
public bool AddProcess(IntPtr processHandle) | |||||
{ | |||||
var succ = AssignProcessToJobObject(handle, processHandle); | |||||
if (!succ) | |||||
{ | |||||
Logging.Error("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error()); | |||||
} | |||||
return succ; | |||||
} | |||||
public bool AddProcess(int processId) | |||||
{ | |||||
return AddProcess(Process.GetProcessById(processId).Handle); | |||||
} | |||||
#region IDisposable | |||||
private bool disposed; | |||||
public void Dispose() | |||||
{ | |||||
Dispose(true); | |||||
GC.SuppressFinalize(this); | |||||
} | |||||
protected virtual void Dispose(bool disposing) | |||||
{ | |||||
if (disposed) return; | |||||
disposed = true; | |||||
if (disposing) | |||||
{ | |||||
// no managed objects to free | |||||
} | |||||
if (handle != IntPtr.Zero) | |||||
{ | |||||
CloseHandle(handle); | |||||
handle = IntPtr.Zero; | |||||
} | |||||
} | |||||
~Job() | |||||
{ | |||||
Dispose(false); | |||||
} | |||||
#endregion | |||||
#region Interop | |||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] | |||||
private static extern IntPtr CreateJobObject(IntPtr a, string lpName); | |||||
[DllImport("kernel32.dll", SetLastError = true)] | |||||
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength); | |||||
[DllImport("kernel32.dll", SetLastError = true)] | |||||
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); | |||||
[DllImport("kernel32.dll", SetLastError = true)] | |||||
[return: MarshalAs(UnmanagedType.Bool)] | |||||
private static extern bool CloseHandle(IntPtr hObject); | |||||
#endregion | |||||
} | |||||
#region Helper classes | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
struct IO_COUNTERS | |||||
{ | |||||
public UInt64 ReadOperationCount; | |||||
public UInt64 WriteOperationCount; | |||||
public UInt64 OtherOperationCount; | |||||
public UInt64 ReadTransferCount; | |||||
public UInt64 WriteTransferCount; | |||||
public UInt64 OtherTransferCount; | |||||
} | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
struct JOBOBJECT_BASIC_LIMIT_INFORMATION | |||||
{ | |||||
public Int64 PerProcessUserTimeLimit; | |||||
public Int64 PerJobUserTimeLimit; | |||||
public UInt32 LimitFlags; | |||||
public UIntPtr MinimumWorkingSetSize; | |||||
public UIntPtr MaximumWorkingSetSize; | |||||
public UInt32 ActiveProcessLimit; | |||||
public UIntPtr Affinity; | |||||
public UInt32 PriorityClass; | |||||
public UInt32 SchedulingClass; | |||||
} | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
public struct SECURITY_ATTRIBUTES | |||||
{ | |||||
public UInt32 nLength; | |||||
public IntPtr lpSecurityDescriptor; | |||||
public Int32 bInheritHandle; | |||||
} | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION | |||||
{ | |||||
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; | |||||
public IO_COUNTERS IoInfo; | |||||
public UIntPtr ProcessMemoryLimit; | |||||
public UIntPtr JobMemoryLimit; | |||||
public UIntPtr PeakProcessMemoryUsed; | |||||
public UIntPtr PeakJobMemoryUsed; | |||||
} | |||||
public enum JobObjectInfoType | |||||
{ | |||||
AssociateCompletionPortInformation = 7, | |||||
BasicLimitInformation = 2, | |||||
BasicUIRestrictions = 4, | |||||
EndOfJobTimeInformation = 6, | |||||
ExtendedLimitInformation = 9, | |||||
SecurityLimitInformation = 5, | |||||
GroupInformation = 11 | |||||
} | |||||
#endregion | |||||
} |
@@ -0,0 +1,31 @@ | |||||
using System.Diagnostics; | |||||
using System.Management; | |||||
using System.Text; | |||||
namespace Shadowsocks.Util.ProcessManagement | |||||
{ | |||||
static class ThreadUtil | |||||
{ | |||||
/* | |||||
* See: | |||||
* http://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c | |||||
*/ | |||||
public static string GetCommandLine(this Process process) | |||||
{ | |||||
var commandLine = new StringBuilder(process.MainModule.FileName); | |||||
commandLine.Append(" "); | |||||
using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) | |||||
{ | |||||
foreach (var @object in searcher.Get()) | |||||
{ | |||||
commandLine.Append(@object["CommandLine"]); | |||||
commandLine.Append(" "); | |||||
} | |||||
} | |||||
return commandLine.ToString(); | |||||
} | |||||
} | |||||
} |
@@ -140,7 +140,6 @@ | |||||
<Compile Include="3rd\zxing\ResultPoint.cs" /> | <Compile Include="3rd\zxing\ResultPoint.cs" /> | ||||
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> | <Compile Include="3rd\zxing\ResultPointCallback.cs" /> | ||||
<Compile Include="3rd\zxing\WriterException.cs" /> | <Compile Include="3rd\zxing\WriterException.cs" /> | ||||
<Compile Include="Controller\Service\Http2Socks5.cs" /> | |||||
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | <Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | ||||
<Compile Include="Model\HotKeyConfig.cs" /> | <Compile Include="Model\HotKeyConfig.cs" /> | ||||
<Compile Include="Model\ProxyConfig.cs" /> | <Compile Include="Model\ProxyConfig.cs" /> | ||||
@@ -161,6 +160,7 @@ | |||||
<Compile Include="Controller\I18N.cs" /> | <Compile Include="Controller\I18N.cs" /> | ||||
<Compile Include="Controller\Service\Listener.cs" /> | <Compile Include="Controller\Service\Listener.cs" /> | ||||
<Compile Include="Controller\Logging.cs" /> | <Compile Include="Controller\Logging.cs" /> | ||||
<Compile Include="Controller\Service\PortForwarder.cs" /> | |||||
<Compile Include="Controller\Service\UDPRelay.cs" /> | <Compile Include="Controller\Service\UDPRelay.cs" /> | ||||
<Compile Include="Controller\Service\UpdateChecker.cs" /> | <Compile Include="Controller\Service\UpdateChecker.cs" /> | ||||
<Compile Include="Encryption\EncryptorBase.cs" /> | <Compile Include="Encryption\EncryptorBase.cs" /> | ||||
@@ -189,6 +189,8 @@ | |||||
<Compile Include="Settings.cs" /> | <Compile Include="Settings.cs" /> | ||||
<Compile Include="StringEx.cs" /> | <Compile Include="StringEx.cs" /> | ||||
<Compile Include="Controller\System\Hotkeys\Hotkeys.cs" /> | <Compile Include="Controller\System\Hotkeys\Hotkeys.cs" /> | ||||
<Compile Include="Util\ProcessManagement\Job.cs" /> | |||||
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" /> | |||||
<Compile Include="Encryption\RNG.cs" /> | <Compile Include="Encryption\RNG.cs" /> | ||||
<Compile Include="Util\Sockets\LineReader.cs" /> | <Compile Include="Util\Sockets\LineReader.cs" /> | ||||
<Compile Include="Util\Sockets\SocketUtil.cs" /> | <Compile Include="Util\Sockets\SocketUtil.cs" /> | ||||
@@ -204,6 +206,7 @@ | |||||
<DependentUpon>ConfigForm.cs</DependentUpon> | <DependentUpon>ConfigForm.cs</DependentUpon> | ||||
</Compile> | </Compile> | ||||
<Compile Include="Controller\Service\TCPRelay.cs" /> | <Compile Include="Controller\Service\TCPRelay.cs" /> | ||||
<Compile Include="Controller\Service\PrivoxyRunner.cs" /> | |||||
<Compile Include="Program.cs" /> | <Compile Include="Program.cs" /> | ||||
<Compile Include="Properties\AssemblyInfo.cs" /> | <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
<Compile Include="Controller\ShadowsocksController.cs" /> | <Compile Include="Controller\ShadowsocksController.cs" /> | ||||
@@ -281,6 +284,8 @@ | |||||
</None> | </None> | ||||
<None Include="Data\abp.js.gz" /> | <None Include="Data\abp.js.gz" /> | ||||
<None Include="Data\libsscrypto.dll.gz" /> | <None Include="Data\libsscrypto.dll.gz" /> | ||||
<None Include="Data\mgwz.dll.gz" /> | |||||
<None Include="Data\privoxy.exe.gz" /> | |||||
<None Include="Data\proxy.pac.txt.gz" /> | <None Include="Data\proxy.pac.txt.gz" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -297,6 +302,7 @@ | |||||
<None Include="Resources\ss24.png" /> | <None Include="Resources\ss24.png" /> | ||||
<None Include="Resources\ssw128.png" /> | <None Include="Resources\ssw128.png" /> | ||||
<Content Include="Data\cn.txt" /> | <Content Include="Data\cn.txt" /> | ||||
<Content Include="Data\privoxy_conf.txt" /> | |||||
<Content Include="Data\user-rule.txt" /> | <Content Include="Data\user-rule.txt" /> | ||||
<Content Include="Data\zh_tw.txt" /> | <Content Include="Data\zh_tw.txt" /> | ||||
<Content Include="FodyWeavers.xml"> | <Content Include="FodyWeavers.xml"> | ||||