@@ -1,17 +1,13 @@ | |||
using NLog; | |||
using Shadowsocks.Properties; | |||
using Shadowsocks.Util; | |||
using Splat; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using Newtonsoft.Json; | |||
using Shadowsocks.Model; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Threading.Tasks; | |||
using System.Security.Cryptography; | |||
using System.Text.Json; | |||
namespace Shadowsocks.PAC | |||
{ | |||
@@ -25,32 +21,31 @@ namespace Shadowsocks.PAC | |||
} | |||
} | |||
public static class GeositeUpdater | |||
public class GeositeUpdater : IEnableLogger | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public event EventHandler<GeositeResultEventArgs> UpdateCompleted; | |||
public static event EventHandler<GeositeResultEventArgs> UpdateCompleted; | |||
public event ErrorEventHandler Error; | |||
public static event ErrorEventHandler Error; | |||
private readonly string DATABASE_PATH; | |||
private static readonly string DATABASE_PATH = Utils.GetTempPath("dlc.dat"); | |||
private readonly string GEOSITE_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat"; | |||
private readonly string GEOSITE_SHA256SUM_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat.sha256sum"; | |||
private byte[] geositeDB; | |||
private static readonly string GEOSITE_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat"; | |||
private static readonly string GEOSITE_SHA256SUM_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat.sha256sum"; | |||
private static byte[] geositeDB; | |||
public readonly Dictionary<string, IList<DomainObject>> Geosites = new Dictionary<string, IList<DomainObject>>(); | |||
public static readonly Dictionary<string, IList<DomainObject>> Geosites = new Dictionary<string, IList<DomainObject>>(); | |||
static GeositeUpdater() | |||
public GeositeUpdater(string dlcPath) | |||
{ | |||
DATABASE_PATH = dlcPath; | |||
if (File.Exists(DATABASE_PATH) && new FileInfo(DATABASE_PATH).Length > 0) | |||
{ | |||
geositeDB = File.ReadAllBytes(DATABASE_PATH); | |||
} | |||
else | |||
{ | |||
geositeDB = Resources.dlc_dat; | |||
File.WriteAllBytes(DATABASE_PATH, Resources.dlc_dat); | |||
geositeDB = Properties.Resources.dlc; | |||
File.WriteAllBytes(DATABASE_PATH, Properties.Resources.dlc); | |||
} | |||
LoadGeositeList(); | |||
} | |||
@@ -58,7 +53,7 @@ namespace Shadowsocks.PAC | |||
/// <summary> | |||
/// load new GeoSite data from geositeDB | |||
/// </summary> | |||
static void LoadGeositeList() | |||
private void LoadGeositeList() | |||
{ | |||
var list = GeositeList.Parser.ParseFrom(geositeDB); | |||
foreach (var item in list.Entries) | |||
@@ -67,42 +62,41 @@ namespace Shadowsocks.PAC | |||
} | |||
} | |||
public static void ResetEvent() | |||
public void ResetEvent() | |||
{ | |||
UpdateCompleted = null; | |||
Error = null; | |||
} | |||
public static async Task UpdatePACFromGeosite() | |||
public async Task UpdatePACFromGeosite(PACSettings pACSettings) | |||
{ | |||
string geositeUrl = GEOSITE_URL; | |||
string geositeSha256sumUrl = GEOSITE_SHA256SUM_URL; | |||
SHA256 mySHA256 = SHA256.Create(); | |||
var config = Program.MainController.GetCurrentConfiguration(); | |||
bool blacklist = config.geositePreferDirect; | |||
var httpClient = Program.MainController.GetHttpClient(); | |||
bool blacklist = pACSettings.PACDefaultToDirect; | |||
var httpClient = Locator.Current.GetService<HttpClient>(); | |||
if (!string.IsNullOrWhiteSpace(config.geositeUrl)) | |||
if (!string.IsNullOrWhiteSpace(pACSettings.CustomGeositeUrl)) | |||
{ | |||
logger.Info("Found custom Geosite URL in config file"); | |||
geositeUrl = config.geositeUrl; | |||
this.Log().Info("Found custom Geosite URL in config file"); | |||
geositeUrl = pACSettings.CustomGeositeUrl; | |||
} | |||
logger.Info($"Checking Geosite from {geositeUrl}"); | |||
this.Log().Info($"Checking Geosite from {geositeUrl}"); | |||
try | |||
{ | |||
// download checksum first | |||
var geositeSha256sum = await httpClient.GetStringAsync(geositeSha256sumUrl); | |||
geositeSha256sum = geositeSha256sum.Substring(0, 64).ToUpper(); | |||
logger.Info($"Got Sha256sum: {geositeSha256sum}"); | |||
this.Log().Info($"Got Sha256sum: {geositeSha256sum}"); | |||
// compare downloaded checksum with local geositeDB | |||
byte[] localDBHashBytes = mySHA256.ComputeHash(geositeDB); | |||
string localDBHash = BitConverter.ToString(localDBHashBytes).Replace("-", String.Empty); | |||
logger.Info($"Local Sha256sum: {localDBHash}"); | |||
this.Log().Info($"Local Sha256sum: {localDBHash}"); | |||
// if already latest | |||
if (geositeSha256sum == localDBHash) | |||
{ | |||
logger.Info("Local GeoSite DB is up to date."); | |||
this.Log().Info("Local GeoSite DB is up to date."); | |||
return; | |||
} | |||
@@ -112,15 +106,15 @@ namespace Shadowsocks.PAC | |||
// verify sha256sum | |||
byte[] downloadedDBHashBytes = mySHA256.ComputeHash(downloadedBytes); | |||
string downloadedDBHash = BitConverter.ToString(downloadedDBHashBytes).Replace("-", String.Empty); | |||
logger.Info($"Actual Sha256sum: {downloadedDBHash}"); | |||
this.Log().Info($"Actual Sha256sum: {downloadedDBHash}"); | |||
if (geositeSha256sum != downloadedDBHash) | |||
{ | |||
logger.Info("Sha256sum Verification: FAILED. Downloaded GeoSite DB is corrupted. Aborting the update."); | |||
this.Log().Info("Sha256sum Verification: FAILED. Downloaded GeoSite DB is corrupted. Aborting the update."); | |||
throw new Exception("Sha256sum mismatch"); | |||
} | |||
else | |||
{ | |||
logger.Info("Sha256sum Verification: PASSED. Applying to local GeoSite DB."); | |||
this.Log().Info("Sha256sum Verification: PASSED. Applying to local GeoSite DB."); | |||
} | |||
// write to geosite file | |||
@@ -130,7 +124,7 @@ namespace Shadowsocks.PAC | |||
// update stuff | |||
geositeDB = downloadedBytes; | |||
LoadGeositeList(); | |||
bool pacFileChanged = MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, blacklist); | |||
bool pacFileChanged = MergeAndWritePACFile(pACSettings.GeositeDirectGroups, pACSettings.GeositeProxiedGroups, blacklist); | |||
UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); | |||
} | |||
catch (Exception ex) | |||
@@ -147,12 +141,12 @@ namespace Shadowsocks.PAC | |||
/// <param name="proxiedGroups">A list of geosite groups configured for proxied connection.</param> | |||
/// <param name="blacklist">Whether to use blacklist mode. False for whitelist.</param> | |||
/// <returns></returns> | |||
public static bool MergeAndWritePACFile(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
public bool MergeAndWritePACFile(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
{ | |||
string abpContent = MergePACFile(directGroups, proxiedGroups, blacklist); | |||
if (File.Exists(PACDaemon.PAC_FILE)) | |||
{ | |||
string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); | |||
string original = File.ReadAllText(PACDaemon.PAC_FILE); | |||
if (original == abpContent) | |||
{ | |||
return false; | |||
@@ -167,7 +161,7 @@ namespace Shadowsocks.PAC | |||
/// </summary> | |||
/// <param name="group">The group name to check for.</param> | |||
/// <returns>True if the group exists. False if the group doesn't exist.</returns> | |||
public static bool CheckGeositeGroup(string group) => SeparateAttributeFromGroupName(group, out string groupName, out _) && Geosites.ContainsKey(groupName); | |||
public bool CheckGeositeGroup(string group) => SeparateAttributeFromGroupName(group, out string groupName, out _) && Geosites.ContainsKey(groupName); | |||
/// <summary> | |||
/// Separates the attribute (e.g. @cn) from a group name. | |||
@@ -177,7 +171,7 @@ namespace Shadowsocks.PAC | |||
/// <param name="groupName">The group name with the attribute stripped.</param> | |||
/// <param name="attribute">The attribute.</param> | |||
/// <returns>True for success. False for more than one '@'.</returns> | |||
private static bool SeparateAttributeFromGroupName(string group, out string groupName, out string attribute) | |||
private bool SeparateAttributeFromGroupName(string group, out string groupName, out string attribute) | |||
{ | |||
var splitGroupAttributeList = group.Split('@'); | |||
if (splitGroupAttributeList.Length == 1) // no attribute | |||
@@ -199,34 +193,39 @@ namespace Shadowsocks.PAC | |||
return true; | |||
} | |||
private static string MergePACFile(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
private string MergePACFile(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
{ | |||
string abpContent; | |||
if (File.Exists(PACDaemon.USER_ABP_FILE)) | |||
{ | |||
abpContent = FileManager.NonExclusiveReadAllText(PACDaemon.USER_ABP_FILE, Encoding.UTF8); | |||
abpContent = File.ReadAllText(PACDaemon.USER_ABP_FILE); | |||
} | |||
else | |||
{ | |||
abpContent = Resources.abp_js; | |||
abpContent = Properties.Resources.abp; | |||
} | |||
List<string> userruleLines = new List<string>(); | |||
if (File.Exists(PACDaemon.USER_RULE_FILE)) | |||
{ | |||
string userrulesString = FileManager.NonExclusiveReadAllText(PACDaemon.USER_RULE_FILE, Encoding.UTF8); | |||
string userrulesString = File.ReadAllText(PACDaemon.USER_RULE_FILE); | |||
userruleLines = ProcessUserRules(userrulesString); | |||
} | |||
List<string> ruleLines = GenerateRules(directGroups, proxiedGroups, blacklist); | |||
var jsonSerializerOptions = new JsonSerializerOptions() | |||
{ | |||
WriteIndented = true, | |||
}; | |||
abpContent = | |||
$@"var __USERRULES__ = {JsonConvert.SerializeObject(userruleLines, Formatting.Indented)}; | |||
var __RULES__ = {JsonConvert.SerializeObject(ruleLines, Formatting.Indented)}; | |||
$@"var __USERRULES__ = {JsonSerializer.Serialize(userruleLines, jsonSerializerOptions)}; | |||
var __RULES__ = {JsonSerializer.Serialize(ruleLines, jsonSerializerOptions)}; | |||
{abpContent}"; | |||
return abpContent; | |||
} | |||
private static List<string> ProcessUserRules(string content) | |||
private List<string> ProcessUserRules(string content) | |||
{ | |||
List<string> valid_lines = new List<string>(); | |||
using (var stringReader = new StringReader(content)) | |||
@@ -248,7 +247,7 @@ var __RULES__ = {JsonConvert.SerializeObject(ruleLines, Formatting.Indented)}; | |||
/// <param name="proxiedGroups">A list of geosite groups configured for proxied connection.</param> | |||
/// <param name="blacklist">Whether to use blacklist mode. False for whitelist.</param> | |||
/// <returns>A list of rule lines.</returns> | |||
private static List<string> GenerateRules(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
private List<string> GenerateRules(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
{ | |||
List<string> ruleLines; | |||
if (blacklist) // blocking + exception rules | |||
@@ -272,7 +271,7 @@ var __RULES__ = {JsonConvert.SerializeObject(ruleLines, Formatting.Indented)}; | |||
/// </summary> | |||
/// <param name="groups">A list of source groups.</param> | |||
/// <returns>A list of rule lines.</returns> | |||
private static List<string> GenerateBlockingRules(List<string> groups) | |||
private List<string> GenerateBlockingRules(List<string> groups) | |||
{ | |||
List<string> ruleLines = new List<string>(); | |||
foreach (var group in groups) | |||
@@ -337,7 +336,7 @@ var __RULES__ = {JsonConvert.SerializeObject(ruleLines, Formatting.Indented)}; | |||
/// </summary> | |||
/// <param name="groups">A list of source groups.</param> | |||
/// <returns>A list of rule lines.</returns> | |||
private static List<string> GenerateExceptionRules(List<string> groups) | |||
private List<string> GenerateExceptionRules(List<string> groups) | |||
=> GenerateBlockingRules(groups) | |||
.Select(r => $"@@{r}") // convert blocking rules to exception rules | |||
.ToList(); | |||
@@ -1,11 +1,6 @@ | |||
using NLog; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
using Shadowsocks.Util; | |||
using Splat; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
@@ -15,14 +10,11 @@ namespace Shadowsocks.PAC | |||
/// <summary> | |||
/// Processing the PAC file content | |||
/// </summary> | |||
public class PACDaemon | |||
public class PACDaemon : IEnableLogger | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public const string PAC_FILE = "pac.txt"; | |||
public const string USER_RULE_FILE = "user-rule.txt"; | |||
public const string USER_ABP_FILE = "abp.txt"; | |||
private Configuration config; | |||
FileSystemWatcher PACFileWatcher; | |||
FileSystemWatcher UserRuleFileWatcher; | |||
@@ -30,14 +22,17 @@ namespace Shadowsocks.PAC | |||
public event EventHandler PACFileChanged; | |||
public event EventHandler UserRuleFileChanged; | |||
public PACDaemon(Configuration config) | |||
private PACSettings _PACSettings; | |||
private GeositeUpdater _geositeUpdater; | |||
public PACDaemon(PACSettings pACSettings, string workingDirectory, string dlcPath) | |||
{ | |||
this.config = config; | |||
_PACSettings = pACSettings; | |||
_geositeUpdater = new GeositeUpdater(dlcPath); | |||
TouchPACFile(); | |||
TouchUserRuleFile(); | |||
this.WatchPacFile(); | |||
this.WatchUserRuleFile(); | |||
WatchPacFile(workingDirectory); | |||
WatchUserRuleFile(workingDirectory); | |||
} | |||
@@ -45,7 +40,7 @@ namespace Shadowsocks.PAC | |||
{ | |||
if (!File.Exists(PAC_FILE)) | |||
{ | |||
GeositeUpdater.MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, config.geositePreferDirect); | |||
_geositeUpdater.MergeAndWritePACFile(_PACSettings.GeositeDirectGroups, _PACSettings.GeositeProxiedGroups, _PACSettings.PACDefaultToDirect); | |||
} | |||
return PAC_FILE; | |||
} | |||
@@ -54,7 +49,7 @@ namespace Shadowsocks.PAC | |||
{ | |||
if (!File.Exists(USER_RULE_FILE)) | |||
{ | |||
File.WriteAllText(USER_RULE_FILE, Resources.user_rule); | |||
File.WriteAllText(USER_RULE_FILE, Properties.Resources.user_rule); | |||
} | |||
return USER_RULE_FILE; | |||
} | |||
@@ -63,16 +58,16 @@ namespace Shadowsocks.PAC | |||
{ | |||
if (!File.Exists(PAC_FILE)) | |||
{ | |||
GeositeUpdater.MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, config.geositePreferDirect); | |||
_geositeUpdater.MergeAndWritePACFile(_PACSettings.GeositeDirectGroups, _PACSettings.GeositeProxiedGroups, _PACSettings.PACDefaultToDirect); | |||
} | |||
return File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||
} | |||
private void WatchPacFile() | |||
private void WatchPacFile(string workingDirectory) | |||
{ | |||
PACFileWatcher?.Dispose(); | |||
PACFileWatcher = new FileSystemWatcher(Program.WorkingDirectory); | |||
PACFileWatcher = new FileSystemWatcher(workingDirectory); | |||
PACFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||
PACFileWatcher.Filter = PAC_FILE; | |||
PACFileWatcher.Changed += PACFileWatcher_Changed; | |||
@@ -82,10 +77,10 @@ namespace Shadowsocks.PAC | |||
PACFileWatcher.EnableRaisingEvents = true; | |||
} | |||
private void WatchUserRuleFile() | |||
private void WatchUserRuleFile(string workingDirectory) | |||
{ | |||
UserRuleFileWatcher?.Dispose(); | |||
UserRuleFileWatcher = new FileSystemWatcher(Program.WorkingDirectory); | |||
UserRuleFileWatcher = new FileSystemWatcher(workingDirectory); | |||
UserRuleFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||
UserRuleFileWatcher.Filter = USER_RULE_FILE; | |||
UserRuleFileWatcher.Changed += UserRuleFileWatcher_Changed; | |||
@@ -103,7 +98,7 @@ namespace Shadowsocks.PAC | |||
{ | |||
if (PACFileChanged != null) | |||
{ | |||
logger.Info($"Detected: PAC file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||
this.Log().Info($"Detected: PAC file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||
Task.Factory.StartNew(() => | |||
{ | |||
((FileSystemWatcher)sender).EnableRaisingEvents = false; | |||
@@ -118,7 +113,7 @@ namespace Shadowsocks.PAC | |||
{ | |||
if (UserRuleFileChanged != null) | |||
{ | |||
logger.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||
this.Log().Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||
Task.Factory.StartNew(() => | |||
{ | |||
((FileSystemWatcher)sender).EnableRaisingEvents = false; | |||
@@ -1,18 +1,17 @@ | |||
using Shadowsocks.Net; | |||
using Shadowsocks.Utilities; | |||
using Shadowsocks.Net.Crypto; | |||
using Splat; | |||
using System; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Text; | |||
using NLog; | |||
using Shadowsocks.Net; | |||
using Shadowsocks.Utilities; | |||
using Shadowsocks.Net.Crypto; | |||
using System.Reflection; | |||
namespace Shadowsocks.PAC | |||
{ | |||
public class PACServer : StreamService | |||
public class PACServer : StreamService, IEnableLogger | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public const string RESOURCE_NAME = "pac"; | |||
private string PacSecret | |||
@@ -27,23 +26,23 @@ namespace Shadowsocks.PAC | |||
} | |||
} | |||
private string _cachedPacSecret = ""; | |||
private bool _PACServerEnableSecret; | |||
public string PacUrl { get; private set; } = ""; | |||
private Configuration _config; | |||
private PACDaemon _pacDaemon; | |||
public PACServer(PACDaemon pacDaemon) | |||
public PACServer(PACDaemon pacDaemon, bool PACServerEnableSecret) | |||
{ | |||
_pacDaemon = pacDaemon; | |||
_PACServerEnableSecret = PACServerEnableSecret; | |||
} | |||
public void UpdatePACURL(Configuration config) | |||
public void UpdatePACURL(EndPoint localEndPoint) | |||
{ | |||
_config = config; | |||
string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; | |||
string usedSecret = _PACServerEnableSecret ? $"&secret={PacSecret}" : ""; | |||
string contentHash = GetHash(_pacDaemon.GetPACContent()); | |||
PacUrl = $"http://{config.LocalHost}:{config.localPort}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; | |||
logger.Debug("Set PAC URL:" + PacUrl); | |||
PacUrl = $"http://{localEndPoint}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; | |||
this.Log().Debug("Setting PAC URL: {PacUrl}"); | |||
} | |||
private static string GetHash(string content) | |||
@@ -81,7 +80,7 @@ namespace Shadowsocks.PAC | |||
string request = Encoding.UTF8.GetString(firstPacket, 0, length); | |||
string[] lines = request.Split('\r', '\n'); | |||
bool hostMatch = false, pathMatch = false, useSocks = false; | |||
bool secretMatch = !_config.secureLocalPac; | |||
bool secretMatch = !_PACServerEnableSecret; | |||
if (lines.Length < 2) // need at lease RequestLine + Host | |||
{ | |||
@@ -172,7 +171,7 @@ namespace Shadowsocks.PAC | |||
string pacContent = $"var __PROXY__ = '{proxy}';\n" + _pacDaemon.GetPACContent(); | |||
string responseHead = | |||
$@"HTTP/1.1 200 OK | |||
Server: ShadowsocksWindows/{UpdateChecker.Version} | |||
Server: ShadowsocksPAC/{Assembly.GetExecutingAssembly().GetName().Version} | |||
Content-Type: application/x-ns-proxy-autoconfig | |||
Content-Length: { Encoding.UTF8.GetBytes(pacContent).Length} | |||
Connection: Close | |||
@@ -183,7 +182,7 @@ Connection: Close | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
this.Log().Error(e, ""); | |||
socket.Close(); | |||
} | |||
} | |||
@@ -199,11 +198,6 @@ Connection: Close | |||
{ } | |||
} | |||
private string GetPACAddress(IPEndPoint localEndPoint, bool useSocks) | |||
{ | |||
return localEndPoint.AddressFamily == AddressFamily.InterNetworkV6 | |||
? $"{(useSocks ? "SOCKS5" : "PROXY")} [{localEndPoint.Address}]:{_config.localPort};" | |||
: $"{(useSocks ? "SOCKS5" : "PROXY")} {localEndPoint.Address}:{_config.localPort};"; | |||
} | |||
private string GetPACAddress(IPEndPoint localEndPoint, bool useSocks) => $"{(useSocks ? "SOCKS5" : "PROXY")} {localEndPoint};"; | |||
} | |||
} |
@@ -0,0 +1,82 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace Shadowsocks.PAC | |||
{ | |||
/// <summary> | |||
/// Settings used for PAC. | |||
/// </summary> | |||
public class PACSettings | |||
{ | |||
public PACSettings() | |||
{ | |||
PACDefaultToDirect = false; | |||
PACServerEnableSecret = true; | |||
RegeneratePacOnVersionUpdate = true; | |||
CustomPACUrl = ""; | |||
CustomGeositeUrl = ""; | |||
GeositeDirectGroups = new List<string>() | |||
{ | |||
"private", | |||
"cn", | |||
"geolocation-!cn@cn", | |||
}; | |||
GeositeProxiedGroups = new List<string>() | |||
{ | |||
"geolocation-!cn", | |||
}; | |||
} | |||
/// <summary> | |||
/// Controls whether direct connection is used for | |||
/// hostnames not matched by blocking rules | |||
/// or matched by exception rules. | |||
/// Defaults to false, or whitelist mode, | |||
/// where hostnames matching the above conditions | |||
/// are connected to via proxy. | |||
/// Enable it to use blacklist mode. | |||
/// </summary> | |||
public bool PACDefaultToDirect { get; set; } | |||
/// <summary> | |||
/// Controls whether the PAC server uses a secret | |||
/// to protect access to the PAC URL. | |||
/// Defaults to true. | |||
/// </summary> | |||
public bool PACServerEnableSecret { get; set; } | |||
/// <summary> | |||
/// Controls whether `pac.txt` should be regenerated | |||
/// when shadowsocks-windows is updated. | |||
/// Defaults to true, so new changes can be applied. | |||
/// Change it to false if you want to manage `pac.txt` | |||
/// yourself. | |||
/// </summary> | |||
public bool RegeneratePacOnVersionUpdate { get; set; } | |||
/// <summary> | |||
/// Specifies a custom PAC URL. | |||
/// Leave empty to use local PAC. | |||
/// </summary> | |||
public string CustomPACUrl { get; set; } | |||
/// <summary> | |||
/// Specifies a custom Geosite database URL. | |||
/// Leave empty to use the default source. | |||
/// </summary> | |||
public string CustomGeositeUrl { get; set; } | |||
/// <summary> | |||
/// A list of Geosite groups | |||
/// that we use direct connection for. | |||
/// </summary> | |||
public List<string> GeositeDirectGroups { get; set; } | |||
/// <summary> | |||
/// A list of Geosite groups | |||
/// that we always connect to via proxy. | |||
/// </summary> | |||
public List<string> GeositeProxiedGroups { get; set; } | |||
} | |||
} |
@@ -0,0 +1,112 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.42000 | |||
// | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
// </auto-generated> | |||
//------------------------------------------------------------------------------ | |||
namespace Shadowsocks.PAC.Properties { | |||
using System; | |||
/// <summary> | |||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||
/// </summary> | |||
// This class was auto-generated by the StronglyTypedResourceBuilder | |||
// class via a tool like ResGen or Visual Studio. | |||
// To add or remove a member, edit your .ResX file then rerun ResGen | |||
// with the /str option, or rebuild your VS project. | |||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] | |||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | |||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | |||
internal class Resources { | |||
private static global::System.Resources.ResourceManager resourceMan; | |||
private static global::System.Globalization.CultureInfo resourceCulture; | |||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] | |||
internal Resources() { | |||
} | |||
/// <summary> | |||
/// Returns the cached ResourceManager instance used by this class. | |||
/// </summary> | |||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | |||
internal static global::System.Resources.ResourceManager ResourceManager { | |||
get { | |||
if (object.ReferenceEquals(resourceMan, null)) { | |||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.PAC.Properties.Resources", typeof(Resources).Assembly); | |||
resourceMan = temp; | |||
} | |||
return resourceMan; | |||
} | |||
} | |||
/// <summary> | |||
/// Overrides the current thread's CurrentUICulture property for all | |||
/// resource lookups using this strongly typed resource class. | |||
/// </summary> | |||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | |||
internal static global::System.Globalization.CultureInfo Culture { | |||
get { | |||
return resourceCulture; | |||
} | |||
set { | |||
resourceCulture = value; | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to /* eslint-disable */ | |||
///// Was generated by gfwlist2pac in precise mode | |||
///// https://github.com/clowwindy/gfwlist2pac | |||
/// | |||
///// 2019-10-06: More 'javascript' way to interaction with main program | |||
///// 2019-02-08: Updated to support shadowsocks-windows user rules. | |||
/// | |||
///var proxy = __PROXY__; | |||
///var userrules = []; | |||
///var rules = []; | |||
/// | |||
///// convert to abp grammar | |||
///var re = /^(@@)?\|\|.*?[^\^]$/; | |||
///for (var i = 0; i < __RULES__.length; i++) { | |||
/// var s = __RULES__[i]; | |||
/// if (s.match(re)) s += "^"; | |||
/// rules.push(s); | |||
///} | |||
/// | |||
/// [rest of string was truncated]";. | |||
/// </summary> | |||
internal static string abp { | |||
get { | |||
return ResourceManager.GetString("abp", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] dlc { | |||
get { | |||
object obj = ResourceManager.GetObject("dlc", resourceCulture); | |||
return ((byte[])(obj)); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to ! Put user rules line by line in this file. | |||
///! See https://adblockplus.org/en/filter-cheatsheet | |||
///. | |||
/// </summary> | |||
internal static string user_rule { | |||
get { | |||
return ResourceManager.GetString("user_rule", resourceCulture); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,130 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<root> | |||
<!-- | |||
Microsoft ResX Schema | |||
Version 2.0 | |||
The primary goals of this format is to allow a simple XML format | |||
that is mostly human readable. The generation and parsing of the | |||
various data types are done through the TypeConverter classes | |||
associated with the data types. | |||
Example: | |||
... ado.net/XML headers & schema ... | |||
<resheader name="resmimetype">text/microsoft-resx</resheader> | |||
<resheader name="version">2.0</resheader> | |||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |||
<value>[base64 mime encoded serialized .NET Framework object]</value> | |||
</data> | |||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |||
<comment>This is a comment</comment> | |||
</data> | |||
There are any number of "resheader" rows that contain simple | |||
name/value pairs. | |||
Each data row contains a name, and value. The row also contains a | |||
type or mimetype. Type corresponds to a .NET class that support | |||
text/value conversion through the TypeConverter architecture. | |||
Classes that don't support this are serialized and stored with the | |||
mimetype set. | |||
The mimetype is used for serialized objects, and tells the | |||
ResXResourceReader how to depersist the object. This is currently not | |||
extensible. For a given mimetype the value must be set accordingly: | |||
Note - application/x-microsoft.net.object.binary.base64 is the format | |||
that the ResXResourceWriter will generate, however the reader can | |||
read any of the formats listed below. | |||
mimetype: application/x-microsoft.net.object.binary.base64 | |||
value : The object must be serialized with | |||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.soap.base64 | |||
value : The object must be serialized with | |||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.bytearray.base64 | |||
value : The object must be serialized into a byte array | |||
: using a System.ComponentModel.TypeConverter | |||
: and then encoded with base64 encoding. | |||
--> | |||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | |||
<xsd:element name="root" msdata:IsDataSet="true"> | |||
<xsd:complexType> | |||
<xsd:choice maxOccurs="unbounded"> | |||
<xsd:element name="metadata"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" use="required" type="xsd:string" /> | |||
<xsd:attribute name="type" type="xsd:string" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" /> | |||
<xsd:attribute ref="xml:space" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="assembly"> | |||
<xsd:complexType> | |||
<xsd:attribute name="alias" type="xsd:string" /> | |||
<xsd:attribute name="name" type="xsd:string" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="data"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | |||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |||
<xsd:attribute ref="xml:space" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="resheader"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" use="required" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:choice> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:schema> | |||
<resheader name="resmimetype"> | |||
<value>text/microsoft-resx</value> | |||
</resheader> | |||
<resheader name="version"> | |||
<value>2.0</value> | |||
</resheader> | |||
<resheader name="reader"> | |||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<resheader name="writer"> | |||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |||
<data name="abp" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Resources\abp.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value> | |||
</data> | |||
<data name="dlc" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Resources\dlc.dat;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</data> | |||
<data name="user_rule" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Resources\user-rule.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | |||
</data> | |||
</root> |
@@ -5,8 +5,36 @@ | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Shadowsocks.Net\Shadowsocks.Net.csproj" /> | |||
<None Remove="Resources\abp.js" /> | |||
<None Remove="Resources\dlc.dat" /> | |||
<None Remove="Resources\user-rule.txt" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Shadowsocks\Shadowsocks.csproj" /> | |||
<ProjectReference Include="..\Shadowsocks.Net\Shadowsocks.Net.csproj" /> | |||
<ProjectReference Include="..\Shadowsocks.Protobuf\Shadowsocks.Protobuf.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Resource Include="Resources\abp.js" /> | |||
<Resource Include="Resources\dlc.dat" /> | |||
<Resource Include="Resources\user-rule.txt" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Update="Properties\Resources.Designer.cs"> | |||
<DesignTime>True</DesignTime> | |||
<AutoGen>True</AutoGen> | |||
<DependentUpon>Resources.resx</DependentUpon> | |||
</Compile> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<EmbeddedResource Update="Properties\Resources.resx"> | |||
<Generator>ResXFileCodeGenerator</Generator> | |||
<LastGenOutput>Resources.Designer.cs</LastGenOutput> | |||
</EmbeddedResource> | |||
</ItemGroup> | |||
</Project> |