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.

PACServer.cs 6.7 kB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. using Shadowsocks.Model;
  2. using Shadowsocks.Properties;
  3. using Shadowsocks.Util;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.IO;
  8. using System.IO.Compression;
  9. using System.Net;
  10. using System.Net.Sockets;
  11. using System.Text;
  12. namespace Shadowsocks.Controller
  13. {
  14. class PACServer : Listener.Service
  15. {
  16. public static string PAC_FILE = "pac.txt";
  17. public static string USER_RULE_FILE = "user-rule.txt";
  18. FileSystemWatcher watcher;
  19. private Configuration _config;
  20. public event EventHandler PACFileChanged;
  21. public PACServer()
  22. {
  23. this.WatchPacFile();
  24. }
  25. public void UpdateConfiguration(Configuration config)
  26. {
  27. this._config = config;
  28. }
  29. public bool Handle(byte[] firstPacket, int length, Socket socket)
  30. {
  31. try
  32. {
  33. string request = Encoding.UTF8.GetString(firstPacket, 0, length);
  34. string[] lines = request.Split('\r', '\n');
  35. bool hostMatch = false, pathMatch = false, useSocks = false;
  36. foreach (string line in lines)
  37. {
  38. string[] kv = line.Split(new char[]{':'}, 2);
  39. if (kv.Length == 2)
  40. {
  41. if (kv[0] == "Host")
  42. {
  43. if (kv[1].Trim() == ((IPEndPoint)socket.LocalEndPoint).ToString())
  44. {
  45. hostMatch = true;
  46. }
  47. }
  48. else if (kv[0] == "User-Agent")
  49. {
  50. // we need to drop connections when changing servers
  51. /* if (kv[1].IndexOf("Chrome") >= 0)
  52. {
  53. useSocks = true;
  54. } */
  55. }
  56. }
  57. else if (kv.Length == 1)
  58. {
  59. if (line.IndexOf("pac") >= 0)
  60. {
  61. pathMatch = true;
  62. }
  63. }
  64. }
  65. if (hostMatch && pathMatch)
  66. {
  67. SendResponse(firstPacket, length, socket, useSocks);
  68. return true;
  69. }
  70. return false;
  71. }
  72. catch (ArgumentException)
  73. {
  74. return false;
  75. }
  76. }
  77. public string TouchPACFile()
  78. {
  79. if (File.Exists(PAC_FILE))
  80. {
  81. return PAC_FILE;
  82. }
  83. else
  84. {
  85. FileManager.UncompressFile(PAC_FILE, Resources.proxy_pac_txt);
  86. return PAC_FILE;
  87. }
  88. }
  89. internal string TouchUserRuleFile()
  90. {
  91. if (File.Exists(USER_RULE_FILE))
  92. {
  93. return USER_RULE_FILE;
  94. }
  95. else
  96. {
  97. File.WriteAllText(USER_RULE_FILE, Resources.user_rule);
  98. return USER_RULE_FILE;
  99. }
  100. }
  101. private string GetPACContent()
  102. {
  103. if (File.Exists(PAC_FILE))
  104. {
  105. return File.ReadAllText(PAC_FILE, Encoding.UTF8);
  106. }
  107. else
  108. {
  109. return Utils.UnGzip(Resources.proxy_pac_txt);
  110. }
  111. }
  112. public void SendResponse(byte[] firstPacket, int length, Socket socket, bool useSocks)
  113. {
  114. try
  115. {
  116. string pac = GetPACContent();
  117. IPEndPoint localEndPoint = (IPEndPoint)socket.LocalEndPoint;
  118. string proxy = GetPACAddress(firstPacket, length, localEndPoint, useSocks);
  119. pac = pac.Replace("__PROXY__", proxy);
  120. string text = String.Format(@"HTTP/1.1 200 OK
  121. Server: Shadowsocks
  122. Content-Type: application/x-ns-proxy-autoconfig
  123. Content-Length: {0}
  124. Connection: Close
  125. ", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
  126. byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
  127. socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket);
  128. Util.Utils.ReleaseMemory();
  129. }
  130. catch (Exception e)
  131. {
  132. Console.WriteLine(e);
  133. socket.Close();
  134. }
  135. }
  136. private void SendCallback(IAsyncResult ar)
  137. {
  138. Socket conn = (Socket)ar.AsyncState;
  139. try
  140. {
  141. conn.Shutdown(SocketShutdown.Send);
  142. }
  143. catch
  144. { }
  145. }
  146. private void WatchPacFile()
  147. {
  148. if (watcher != null)
  149. {
  150. watcher.Dispose();
  151. }
  152. watcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
  153. watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  154. watcher.Filter = PAC_FILE;
  155. watcher.Changed += Watcher_Changed;
  156. watcher.Created += Watcher_Changed;
  157. watcher.Deleted += Watcher_Changed;
  158. watcher.Renamed += Watcher_Changed;
  159. watcher.EnableRaisingEvents = true;
  160. }
  161. private void Watcher_Changed(object sender, FileSystemEventArgs e)
  162. {
  163. if (PACFileChanged != null)
  164. {
  165. PACFileChanged(this, new EventArgs());
  166. }
  167. }
  168. private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks)
  169. {
  170. //try
  171. //{
  172. // string requestString = Encoding.UTF8.GetString(requestBuf);
  173. // if (requestString.IndexOf("AppleWebKit") >= 0)
  174. // {
  175. // string address = "" + localEndPoint.Address + ":" + config.GetCurrentServer().local_port;
  176. // proxy = "SOCKS5 " + address + "; SOCKS " + address + ";";
  177. // }
  178. //}
  179. //catch (Exception e)
  180. //{
  181. // Console.WriteLine(e);
  182. //}
  183. return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";";
  184. }
  185. }
  186. }