|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- using Shadowsocks.Model;
- using Shadowsocks.Properties;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.IO.Compression;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Text.RegularExpressions;
-
- namespace Shadowsocks.Controller
- {
- class PACServer
- {
- private static int PORT = 8093;
- private static string PAC_FILE = "pac.txt";
- private static Configuration config;
-
- Socket _listener;
- FileSystemWatcher watcher;
-
- GfwListUpdater gfwlistUpdater;
-
- public event EventHandler PACFileChanged;
-
- public void Start(Configuration configuration)
- {
- try
- {
- config = configuration;
- // Create a TCP/IP socket.
- _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- IPEndPoint localEndPoint = null;
- if (configuration.shareOverLan)
- {
- localEndPoint = new IPEndPoint(IPAddress.Any, PORT);
- }
- else
- {
- localEndPoint = new IPEndPoint(IPAddress.Loopback, PORT);
- }
-
- // Bind the socket to the local endpoint and listen for incoming connections.
- _listener.Bind(localEndPoint);
- _listener.Listen(100);
- _listener.BeginAccept(
- new AsyncCallback(AcceptCallback),
- _listener);
-
- WatchPacFile();
- StartGfwListUpdater();
- }
- catch (SocketException)
- {
- _listener.Close();
- throw;
- }
- }
-
- public void Stop()
- {
- if (gfwlistUpdater != null)
- {
- gfwlistUpdater.Stop();
- gfwlistUpdater = null;
- }
- if (_listener != null)
- {
- _listener.Close();
- _listener = null;
- }
- }
-
- public string TouchPACFile()
- {
- if (File.Exists(PAC_FILE))
- {
- return PAC_FILE;
- }
- else
- {
- FileManager.UncompressFile(PAC_FILE, Resources.proxy_pac_txt);
- return PAC_FILE;
- }
- }
-
- // we don't even use it
- static byte[] requestBuf = new byte[2048];
-
- public void AcceptCallback(IAsyncResult ar)
- {
- Socket listener = (Socket)ar.AsyncState;
- try
- {
- Socket conn = listener.EndAccept(ar);
-
- object[] state = new object[] {
- conn,
- requestBuf
- };
-
- conn.BeginReceive(requestBuf, 0, requestBuf.Length, 0,
- new AsyncCallback(ReceiveCallback), state);
- }
- catch (ObjectDisposedException)
- {
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- }
- finally
- {
- try
- {
- listener.BeginAccept(
- new AsyncCallback(AcceptCallback),
- listener);
- }
- catch (ObjectDisposedException)
- {
- // do nothing
- }
- catch (Exception e)
- {
- Logging.LogUsefulException(e);
- }
- }
- }
-
- private string GetPACContent()
- {
- if (File.Exists(PAC_FILE))
- {
- return File.ReadAllText(PAC_FILE, Encoding.UTF8);
- }
- else
- {
- byte[] pacGZ = Resources.proxy_pac_txt;
- byte[] buffer = new byte[1024]; // builtin pac gzip size: maximum 100K
- MemoryStream sb = new MemoryStream();
- int n;
- using (GZipStream input = new GZipStream(new MemoryStream(pacGZ),
- CompressionMode.Decompress, false))
- {
- while((n = input.Read(buffer, 0, buffer.Length)) > 0)
- {
- sb.Write(buffer, 0, n);
- }
- return System.Text.Encoding.UTF8.GetString(sb.ToArray());
- }
- }
- }
-
- private void ReceiveCallback(IAsyncResult ar)
- {
- object[] state = (object[])ar.AsyncState;
-
- Socket conn = (Socket)state[0];
- byte[] requestBuf = (byte[])state[1];
- try
- {
- int bytesRead = conn.EndReceive(ar);
-
- string pac = GetPACContent();
-
- IPEndPoint localEndPoint = (IPEndPoint)conn.LocalEndPoint;
-
- string proxy = GetPACAddress(requestBuf, localEndPoint);
-
- pac = pac.Replace("__PROXY__", proxy);
-
- if (bytesRead > 0)
- {
- string text = String.Format(@"HTTP/1.1 200 OK
- Server: Shadowsocks
- Content-Type: application/x-ns-proxy-autoconfig
- Content-Length: {0}
- Connection: Close
-
- ", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
- byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
- conn.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), conn);
- Util.Util.ReleaseMemory();
- }
- else
- {
- conn.Close();
- }
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- conn.Close();
- }
- }
-
- private void SendCallback(IAsyncResult ar)
- {
- Socket conn = (Socket)ar.AsyncState;
- try
- {
- conn.Shutdown(SocketShutdown.Send);
- }
- catch
- { }
- }
-
- private void WatchPacFile()
- {
- if (watcher != null)
- {
- watcher.Dispose();
- }
- watcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
- watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
- watcher.Filter = PAC_FILE;
- watcher.Changed += Watcher_Changed;
- watcher.Created += Watcher_Changed;
- watcher.Deleted += Watcher_Changed;
- watcher.Renamed += Watcher_Changed;
- watcher.EnableRaisingEvents = true;
- }
-
- private void Watcher_Changed(object sender, FileSystemEventArgs e)
- {
- if (PACFileChanged != null)
- {
- PACFileChanged(this, new EventArgs());
- }
- }
-
- private string GetPACAddress(byte[] requestBuf, IPEndPoint localEndPoint)
- {
- string proxy = "PROXY " + localEndPoint.Address + ":8123;";
- //try
- //{
- // string requestString = Encoding.UTF8.GetString(requestBuf);
- // if (requestString.IndexOf("AppleWebKit") >= 0)
- // {
- // string address = "" + localEndPoint.Address + ":" + config.GetCurrentServer().local_port;
- // proxy = "SOCKS5 " + address + "; SOCKS " + address + ";";
- // }
- //}
- //catch (Exception e)
- //{
- // Console.WriteLine(e);
- //}
- return proxy;
- }
-
- private void StartGfwListUpdater()
- {
- if (gfwlistUpdater != null)
- {
- gfwlistUpdater.Stop();
- gfwlistUpdater = null;
- }
-
- gfwlistUpdater = new GfwListUpdater();
- gfwlistUpdater.GfwListChanged += gfwlistUpdater_GfwListChanged;
- IPEndPoint localEndPoint = (IPEndPoint)_listener.LocalEndPoint;
- gfwlistUpdater.proxy = new WebProxy(localEndPoint.Address.ToString(), 8123);
- gfwlistUpdater.useSystemProxy = false;
- /* Delay 30 seconds, wait proxy start up. */
- gfwlistUpdater.ScheduleUpdateTime(30);
- gfwlistUpdater.Start();
-
- }
-
- private void gfwlistUpdater_GfwListChanged(object sender, GfwListUpdater.GfwListChangedArgs e)
- {
- if (e.GfwList == null || e.GfwList.Length == 0) return;
- string pacfile = TouchPACFile();
- string pacContent = File.ReadAllText(pacfile);
- string oldDomains;
- if (ClearPacContent(ref pacContent, out oldDomains))
- {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("{");
- for (int i = 0; i < e.GfwList.Length; i++)
- {
- if (i == e.GfwList.Length - 1)
- sb.AppendFormat("\t\"{0}\": {1}\r\n", e.GfwList[i], 1);
- else
- sb.AppendFormat("\t\"{0}\": {1},\r\n", e.GfwList[i], 1);
- }
- sb.Append("}");
- string newDomains = sb.ToString();
- if (!string.Equals(oldDomains, newDomains))
- {
- pacContent = pacContent.Replace("__LAST_MODIFIED__", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
- pacContent = pacContent.Replace("__DOMAINS__", newDomains);
- File.WriteAllText(pacfile, pacContent);
- Console.WriteLine("gfwlist updated - " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
- }
- }
- else
- {
- Console.WriteLine("Broken pac file.");
- }
- }
-
- private bool ClearPacContent(ref string pacContent, out string oldDomains)
- {
- Regex regex = new Regex("(/\\*.*?\\*/\\s*)?var\\s+domains\\s*=\\s*(\\{(\\s*\"[^\"]*\"\\s*:\\s*\\d+\\s*,)*\\s*(\\s*\"[^\"]*\"\\s*:\\s*\\d+\\s*)\\})", RegexOptions.Singleline);
- Match m = regex.Match(pacContent);
- if (m.Success)
- {
- oldDomains = m.Result("$2");
- pacContent = regex.Replace(pacContent, "/* Last Modified: __LAST_MODIFIED__ */\r\nvar domains = __DOMAINS__");
- return true;
- }
- oldDomains = null;
- return false;
- }
-
-
- }
- }
|