You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

PrivoxyRunner.cs 6.0 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. using System;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using Shadowsocks.Model;
  10. using Shadowsocks.Properties;
  11. using Shadowsocks.Util;
  12. using Shadowsocks.Util.ProcessManagement;
  13. namespace Shadowsocks.Controller
  14. {
  15. class PrivoxyRunner
  16. {
  17. private static int _uid;
  18. private static string _uniqueConfigFile;
  19. private static Job _privoxyJob;
  20. private Process _process;
  21. private int _runningPort;
  22. static PrivoxyRunner()
  23. {
  24. try
  25. {
  26. _uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance.
  27. _uniqueConfigFile = $"privoxy_{_uid}.conf";
  28. _privoxyJob = new Job();
  29. FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
  30. }
  31. catch (IOException e)
  32. {
  33. Logging.LogUsefulException(e);
  34. }
  35. }
  36. public int RunningPort => _runningPort;
  37. public void Start(Configuration configuration)
  38. {
  39. if (_process == null)
  40. {
  41. Process[] existingPrivoxy = Process.GetProcessesByName("ss_privoxy");
  42. foreach (Process p in existingPrivoxy.Where(IsChildProcess))
  43. {
  44. KillProcess(p);
  45. }
  46. string privoxyConfig = Resources.privoxy_conf;
  47. _runningPort = GetFreePort(configuration.isIPv6Enabled);
  48. privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
  49. privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString());
  50. privoxyConfig = configuration.isIPv6Enabled
  51. ? privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "[::]" : "[::1]")
  52. .Replace("__SOCKS_HOST__", "[::1]")
  53. : privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1")
  54. .Replace("__SOCKS_HOST__", "127.0.0.1");
  55. FileManager.ByteArrayToFile(Utils.GetTempPath(_uniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig));
  56. _process = new Process
  57. {
  58. // Configure the process using the StartInfo properties.
  59. StartInfo =
  60. {
  61. FileName = "ss_privoxy.exe",
  62. Arguments = _uniqueConfigFile,
  63. WorkingDirectory = Utils.GetTempPath(),
  64. WindowStyle = ProcessWindowStyle.Hidden,
  65. UseShellExecute = true,
  66. CreateNoWindow = true
  67. }
  68. };
  69. _process.Start();
  70. /*
  71. * Add this process to job obj associated with this ss process, so that
  72. * when ss exit unexpectedly, this process will be forced killed by system.
  73. */
  74. _privoxyJob.AddProcess(_process.Handle);
  75. }
  76. }
  77. public void Stop()
  78. {
  79. if (_process != null)
  80. {
  81. KillProcess(_process);
  82. _process.Dispose();
  83. _process = null;
  84. }
  85. }
  86. private static void KillProcess(Process p)
  87. {
  88. try
  89. {
  90. p.CloseMainWindow();
  91. p.WaitForExit(100);
  92. if (!p.HasExited)
  93. {
  94. p.Kill();
  95. p.WaitForExit();
  96. }
  97. }
  98. catch (Exception e)
  99. {
  100. Logging.LogUsefulException(e);
  101. }
  102. }
  103. /*
  104. * We won't like to kill other ss instances' ss_privoxy.exe.
  105. * This function will check whether the given process is created
  106. * by this process by checking the module path or command line.
  107. *
  108. * Since it's required to put ss in different dirs to run muti instances,
  109. * different instance will create their unique "privoxy_UID.conf" where
  110. * UID is hash of ss's location.
  111. */
  112. private static bool IsChildProcess(Process process)
  113. {
  114. try
  115. {
  116. /*
  117. * Under PortableMode, we could identify it by the path of ss_privoxy.exe.
  118. */
  119. var path = process.MainModule.FileName;
  120. return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
  121. }
  122. catch (Exception ex)
  123. {
  124. /*
  125. * Sometimes Process.GetProcessesByName will return some processes that
  126. * are already dead, and that will cause exceptions here.
  127. * We could simply ignore those exceptions.
  128. */
  129. Logging.LogUsefulException(ex);
  130. return false;
  131. }
  132. }
  133. private int GetFreePort(bool isIPv6 = false)
  134. {
  135. int defaultPort = 8123;
  136. try
  137. {
  138. // TCP stack please do me a favor
  139. TcpListener l = new TcpListener(isIPv6 ? IPAddress.IPv6Loopback : IPAddress.Loopback, 0);
  140. l.Start();
  141. var port = ((IPEndPoint)l.LocalEndpoint).Port;
  142. l.Stop();
  143. return port;
  144. }
  145. catch (Exception e)
  146. {
  147. // in case access denied
  148. Logging.LogUsefulException(e);
  149. return defaultPort;
  150. }
  151. }
  152. }
  153. }