|
- 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.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
- {
- get
- {
- return _runningPort;
- }
- }
-
- public void Start(Configuration configuration)
- {
- Server server = configuration.GetCurrentServer();
- 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 = 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
- {
- IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
- IPEndPoint[] tcpEndPoints = properties.GetActiveTcpListeners();
-
- List<int> usedPorts = new List<int>();
- foreach (IPEndPoint endPoint in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners())
- {
- usedPorts.Add(endPoint.Port);
- }
- for (int port = defaultPort; port <= 65535; port++)
- {
- if (!usedPorts.Contains(port))
- {
- return port;
- }
- }
- }
- catch (Exception e)
- {
- // in case access denied
- Logging.LogUsefulException(e);
- return defaultPort;
- }
- throw new Exception("No free port found.");
- }
-
- [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);
- }
- }
- }
|