# Conflicts: # appveyor.yml # shadowsocks-csharp/Controller/Service/TCPRelay.cs # shadowsocks-csharp/Program.cs # shadowsocks-csharp/Properties/Resources.Designer.cs # shadowsocks-csharp/View/MenuViewController.cs # shadowsocks-csharp/packages.config # shadowsocks-csharp/shadowsocks-csharp.csprojpull/2895/head
@@ -0,0 +1,39 @@ | |||||
--- | |||||
name: Bug report (English) | |||||
about: Create a report to help us improve | |||||
title: '' | |||||
labels: '' | |||||
assignees: '' | |||||
--- | |||||
<!-- | |||||
- Shadowsocks is a non-profit open source project. If you bought the service from a provider, please contact them. | |||||
- If you have questions rather than Shadowsocks Windows client, please go to https://github.com/shadowsocks | |||||
- Please read Wiki carefully, especially https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting | |||||
- And search from Issue Board https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||||
- Please include the following information. Questions lacking details will be closed. | |||||
--> | |||||
### Describe the bug | |||||
### Environment | |||||
- Shadowsocks client version: | |||||
- OS version: | |||||
- .NET version: | |||||
### Steps you have tried | |||||
### What did you expect to see? | |||||
### What did you see instead? | |||||
### Config and error log in detail (with all sensitive info masked) | |||||
``` | |||||
PASTE LOG HERE | |||||
``` |
@@ -0,0 +1,39 @@ | |||||
--- | |||||
name: Bug报告 (中文) | |||||
about: 反馈Bug | |||||
title: '' | |||||
labels: bug report | |||||
assignees: '' | |||||
--- | |||||
<!-- | |||||
- 影梭(Shadowsocks)是一个开源非盈利项目,不提供任何托管服务。如果你是从服务提供商购买的服务,请联系他们。 | |||||
- 如果你有非影梭Windows客户端相关的问题,请去 https://github.com/shadowsocks | |||||
- 提问前请先阅读wiki https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting. | |||||
- 并在Issue Board中搜索 https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||||
- 请按照以下格式描述你的问题,描述不清的问题将会被关闭。 | |||||
--> | |||||
### 简要描述问题 | |||||
### 环境 | |||||
- Shadowsocks客户端版本: | |||||
- 操作系统版本: | |||||
- .NET版本: | |||||
### 操作步骤 | |||||
### 期望的结果 | |||||
### 实际结果 | |||||
### 配置文件和日志文件(请隐去敏感信息) | |||||
``` | |||||
在此粘贴日志 | |||||
``` |
@@ -0,0 +1,20 @@ | |||||
--- | |||||
name: Feature request | |||||
about: Suggest an idea for this project | |||||
title: '' | |||||
labels: '' | |||||
assignees: '' | |||||
--- | |||||
**Is your feature request related to a problem? Please describe.** | |||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] | |||||
**Describe the solution you'd like** | |||||
A clear and concise description of what you want to happen. | |||||
**Describe alternatives you've considered** | |||||
A clear and concise description of any alternative solutions or features you've considered. | |||||
**Additional context** | |||||
Add any other context or screenshots about the feature request here. |
@@ -1,29 +0,0 @@ | |||||
<!-- | |||||
- Shadowsocks is a non-profit open source project. If you bought the service from a provider, please contact them. | |||||
影梭(Shadowsocks)是一个开源非盈利项目,不提供任何托管服务。如果你是从服务提供商购买的服务,请联系他们。 | |||||
- If you have questions rather than Shadowsocks Windows client, please go to https://github.com/shadowsocks | |||||
如果你有非影梭Windows客户端相关的问题,请去 https://github.com/shadowsocks | |||||
- Please read Wiki carefully, especially https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting | |||||
提问前请先阅读wiki https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting. | |||||
- And search from Issue Board https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||||
并在Issue Board中搜索 https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||||
- Please include the following information. Questions lacking details will be closed. | |||||
请按照以下格式描述你的问题,描述不清的问题将会被关闭。 | |||||
--> | |||||
### Shadowsocks version / 影梭版本 | |||||
### Environment (Operating system, .NET Framework, etc) / 使用环境(操作系统,.NET Framework等) | |||||
### Steps you have tried / 操作步骤 | |||||
### What did you expect to see? / 期望的结果 | |||||
### What did you see instead? / 实际结果 | |||||
### Config and error log in detail (with all sensitive info masked) / 配置文件和日志文件(请隐去敏感信息) |
@@ -9,7 +9,7 @@ | |||||
1. System proxy configuration | 1. System proxy configuration | ||||
2. PAC mode and global mode | 2. PAC mode and global mode | ||||
3. [GFWList] and user rules | |||||
3. [GeoSite] and user rules | |||||
4. Supports HTTP proxy | 4. Supports HTTP proxy | ||||
5. Supports server auto switching | 5. Supports server auto switching | ||||
6. Supports UDP relay (see Usage) | 6. Supports UDP relay (see Usage) | ||||
@@ -37,7 +37,7 @@ port in `Servers -> Edit Servers` | |||||
1. You can change PAC rules by editing the PAC file. When you save the PAC file | 1. You can change PAC rules by editing the PAC file. When you save the PAC file | ||||
with any editor, Shadowsocks will notify browsers about the change automatically | with any editor, Shadowsocks will notify browsers about the change automatically | ||||
2. You can also update PAC file from [GFWList] \(maintained by 3rd party) | |||||
2. You can also update PAC file from [GeoSite] \(maintained by 3rd party) | |||||
3. You can also use online PAC URL | 3. You can also use online PAC URL | ||||
For Windows10 Store and related applications, please execute the following command under Admin privilege: | For Windows10 Store and related applications, please execute the following command under Admin privilege: | ||||
@@ -144,7 +144,7 @@ Sysproxy () https://github.com/Noisyfox/sysproxy | |||||
[Appveyor]: https://ci.appveyor.com/project/celeron533/shadowsocks-windows | [Appveyor]: https://ci.appveyor.com/project/celeron533/shadowsocks-windows | ||||
[Build Status]: https://ci.appveyor.com/api/projects/status/tfw57q6eecippsl5/branch/master?svg=true | [Build Status]: https://ci.appveyor.com/api/projects/status/tfw57q6eecippsl5/branch/master?svg=true | ||||
[release page]: https://github.com/shadowsocks/shadowsocks-csharp/releases | [release page]: https://github.com/shadowsocks/shadowsocks-csharp/releases | ||||
[GFWList]: https://github.com/gfwlist/gfwlist | |||||
[GeoSite]: https://github.com/v2ray/domain-list-community | |||||
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side | [Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side | ||||
[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E | [中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E | ||||
[Visual Studio 2017]: https://www.visualstudio.com/downloads/ | [Visual Studio 2017]: https://www.visualstudio.com/downloads/ | ||||
@@ -102,7 +102,7 @@ before_build: | |||||
# scripts to run after build (working directory and environment changes are persisted from the previous steps) | # scripts to run after build (working directory and environment changes are persisted from the previous steps) | ||||
after_build: | after_build: | ||||
ps: | | |||||
- ps: |+ | |||||
function CalculateHash($file) | function CalculateHash($file) | ||||
{ | { | ||||
$newLine = "`r`n" | $newLine = "`r`n" | ||||
@@ -162,21 +162,7 @@ after_build: | |||||
# providers: Local, FTP, WebDeploy, AzureCS, AzureBlob, S3, NuGet, Environment | # providers: Local, FTP, WebDeploy, AzureCS, AzureBlob, S3, NuGet, Environment | ||||
# provider names are case-sensitive! | # provider names are case-sensitive! | ||||
deploy: | |||||
# Deploy to GitHub Releases | |||||
- provider: GitHub | |||||
auth_token: | |||||
secure: ZrRlVe3eWp1ccIVZcmFrI7vaCxwz5ewIMSmaPUTjMGyC1rVRlYm7nWWi6Pzkpe0A | |||||
description: '%APPVEYOR_BUILD_VERSION%' | |||||
artifact: Shadowsocks-%APPVEYOR_BUILD_VERSION%.zip, Shadowsocks-%APPVEYOR_BUILD_VERSION%.zip.hash | |||||
draft: true | |||||
prerelease: true | |||||
on: | |||||
branch: master # release from master branch only | |||||
configuration: Release | |||||
APPVEYOR_REPO_TAG: true # deploy on tag push only | |||||
# deploy: | |||||
# # scripts to run before deployment | # # scripts to run before deployment | ||||
# before_deploy: | # before_deploy: | ||||
@@ -0,0 +1,203 @@ | |||||
using NLog; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using Newtonsoft.Json; | |||||
using Shadowsocks.Model; | |||||
using System.Net; | |||||
namespace Shadowsocks.Controller | |||||
{ | |||||
public class GeositeResultEventArgs : EventArgs | |||||
{ | |||||
public bool Success; | |||||
public GeositeResultEventArgs(bool success) | |||||
{ | |||||
this.Success = success; | |||||
} | |||||
} | |||||
public static class GeositeUpdater | |||||
{ | |||||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||||
public static event EventHandler<GeositeResultEventArgs> UpdateCompleted; | |||||
public static event ErrorEventHandler Error; | |||||
private static readonly string DATABASE_PATH = Utils.GetTempPath("dlc.dat"); | |||||
private static readonly string GEOSITE_URL = "https://github.com/v2ray/domain-list-community/raw/release/dlc.dat"; | |||||
public static readonly Dictionary<string, IList<DomainObject>> Geosites = new Dictionary<string, IList<DomainObject>>(); | |||||
static GeositeUpdater() | |||||
{ | |||||
if (!File.Exists(DATABASE_PATH)) | |||||
{ | |||||
File.WriteAllBytes(DATABASE_PATH, Resources.dlc_dat); | |||||
} | |||||
LoadGeositeList(); | |||||
} | |||||
static void LoadGeositeList(byte[] data = null) | |||||
{ | |||||
data = data ?? File.ReadAllBytes(DATABASE_PATH); | |||||
var list = GeositeList.Parser.ParseFrom(data); | |||||
foreach (var item in list.Entries) | |||||
{ | |||||
Geosites[item.GroupName.ToLower()] = item.Domains; | |||||
} | |||||
} | |||||
public static void ResetEvent() | |||||
{ | |||||
UpdateCompleted = null; | |||||
Error = null; | |||||
} | |||||
public static void UpdatePACFromGeosite(Configuration config) | |||||
{ | |||||
string geositeUrl = GEOSITE_URL; | |||||
string group = config.geositeGroup; | |||||
bool blacklist = config.geositeBlacklistMode; | |||||
if (!string.IsNullOrWhiteSpace(config.geositeUrl)) | |||||
{ | |||||
logger.Info("Found custom Geosite URL in config file"); | |||||
geositeUrl = config.geositeUrl; | |||||
} | |||||
logger.Info($"Checking Geosite from {geositeUrl}"); | |||||
WebClient http = new WebClient(); | |||||
if (config.enabled) | |||||
{ | |||||
http.Proxy = new WebProxy( | |||||
config.isIPv6Enabled | |||||
? $"[{IPAddress.IPv6Loopback}]" | |||||
: IPAddress.Loopback.ToString(), | |||||
config.localPort); | |||||
} | |||||
http.DownloadDataCompleted += (o, e) => | |||||
{ | |||||
try | |||||
{ | |||||
File.WriteAllBytes(DATABASE_PATH, e.Result); | |||||
LoadGeositeList(); | |||||
bool pacFileChanged = MergeAndWritePACFile(group, blacklist); | |||||
UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Error?.Invoke(null, new ErrorEventArgs(ex)); | |||||
} | |||||
}; | |||||
http.DownloadDataAsync(new Uri(geositeUrl)); | |||||
} | |||||
public static bool MergeAndWritePACFile(string group, bool blacklist) | |||||
{ | |||||
IList<DomainObject> domains = Geosites[group]; | |||||
string abpContent = MergePACFile(domains, blacklist); | |||||
if (File.Exists(PACDaemon.PAC_FILE)) | |||||
{ | |||||
string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); | |||||
if (original == abpContent) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
File.WriteAllText(PACDaemon.PAC_FILE, abpContent, Encoding.UTF8); | |||||
return true; | |||||
} | |||||
private static string MergePACFile(IList<DomainObject> domains, bool blacklist) | |||||
{ | |||||
string abpContent; | |||||
if (File.Exists(PACDaemon.USER_ABP_FILE)) | |||||
{ | |||||
abpContent = FileManager.NonExclusiveReadAllText(PACDaemon.USER_ABP_FILE, Encoding.UTF8); | |||||
} | |||||
else | |||||
{ | |||||
abpContent = Resources.abp_js; | |||||
} | |||||
List<string> userruleLines = new List<string>(); | |||||
if (File.Exists(PACDaemon.USER_RULE_FILE)) | |||||
{ | |||||
string userrulesString = FileManager.NonExclusiveReadAllText(PACDaemon.USER_RULE_FILE, Encoding.UTF8); | |||||
userruleLines = PreProcessGFWList(userrulesString); | |||||
} | |||||
List<string> gfwLines = GeositeToGFWList(domains, blacklist); | |||||
abpContent = | |||||
$@"var __USERRULES__ = {JsonConvert.SerializeObject(userruleLines, Formatting.Indented)}; | |||||
var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; | |||||
{abpContent}"; | |||||
return abpContent; | |||||
} | |||||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||||
private static List<string> PreProcessGFWList(string content) | |||||
{ | |||||
List<string> valid_lines = new List<string>(); | |||||
using (var sr = new StringReader(content)) | |||||
{ | |||||
foreach (var line in sr.NonWhiteSpaceLines()) | |||||
{ | |||||
if (line.BeginWithAny(IgnoredLineBegins)) | |||||
continue; | |||||
valid_lines.Add(line); | |||||
} | |||||
} | |||||
return valid_lines; | |||||
} | |||||
private static List<string> GeositeToGFWList(IList<DomainObject> domains, bool blacklist) | |||||
{ | |||||
return blacklist ? GeositeToGFWListBlack(domains) : GeositeToGFWListWhite(domains); | |||||
} | |||||
private static List<string> GeositeToGFWListBlack(IList<DomainObject> domains) | |||||
{ | |||||
List<string> ret = new List<string>(domains.Count + 100);// 100 overhead | |||||
foreach (var d in domains) | |||||
{ | |||||
string domain = d.Value; | |||||
switch (d.Type) | |||||
{ | |||||
case DomainObject.Types.Type.Plain: | |||||
ret.Add(domain); | |||||
break; | |||||
case DomainObject.Types.Type.Regex: | |||||
ret.Add($"/{domain}/"); | |||||
break; | |||||
case DomainObject.Types.Type.Domain: | |||||
ret.Add($"||{domain}"); | |||||
break; | |||||
case DomainObject.Types.Type.Full: | |||||
ret.Add($"|http://{domain}"); | |||||
ret.Add($"|https://{domain}"); | |||||
break; | |||||
} | |||||
} | |||||
return ret; | |||||
} | |||||
private static List<string> GeositeToGFWListWhite(IList<DomainObject> domains) | |||||
{ | |||||
return GeositeToGFWListBlack(domains) | |||||
.Select(r => $"@@{r}") // convert to whitelist | |||||
.Prepend("/.*/") // blacklist all other site | |||||
.ToList(); | |||||
} | |||||
} | |||||
} |
@@ -1,137 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Net; | |||||
using System.Text; | |||||
using Newtonsoft.Json; | |||||
using NLog; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | |||||
{ | |||||
public class GFWListUpdater | |||||
{ | |||||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||||
private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"; | |||||
public event EventHandler<ResultEventArgs> UpdateCompleted; | |||||
public event ErrorEventHandler Error; | |||||
public class ResultEventArgs : EventArgs | |||||
{ | |||||
public bool Success; | |||||
public ResultEventArgs(bool success) | |||||
{ | |||||
this.Success = success; | |||||
} | |||||
} | |||||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||||
{ | |||||
try | |||||
{ | |||||
File.WriteAllText(Utils.GetTempPath("gfwlist.txt"), e.Result, Encoding.UTF8); | |||||
bool pacFileChanged = MergeAndWritePACFile(e.Result); | |||||
UpdateCompleted?.Invoke(this, new ResultEventArgs(pacFileChanged)); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Error?.Invoke(this, new ErrorEventArgs(ex)); | |||||
} | |||||
} | |||||
public static bool MergeAndWritePACFile(string gfwListResult) | |||||
{ | |||||
string abpContent = MergePACFile(gfwListResult); | |||||
if (File.Exists(PACDaemon.PAC_FILE)) | |||||
{ | |||||
string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); | |||||
if (original == abpContent) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
File.WriteAllText(PACDaemon.PAC_FILE, abpContent, Encoding.UTF8); | |||||
return true; | |||||
} | |||||
private static string MergePACFile(string gfwListResult) | |||||
{ | |||||
string abpContent; | |||||
if (File.Exists(PACDaemon.USER_ABP_FILE)) | |||||
{ | |||||
abpContent = FileManager.NonExclusiveReadAllText(PACDaemon.USER_ABP_FILE, Encoding.UTF8); | |||||
} | |||||
else | |||||
{ | |||||
abpContent = Resources.abp_js; | |||||
} | |||||
List<string> userruleLines = new List<string>(); | |||||
if (File.Exists(PACDaemon.USER_RULE_FILE)) | |||||
{ | |||||
string userrulesString = FileManager.NonExclusiveReadAllText(PACDaemon.USER_RULE_FILE, Encoding.UTF8); | |||||
userruleLines = ParseToValidList(userrulesString); | |||||
} | |||||
List<string> gfwLines = new List<string>(); | |||||
gfwLines = ParseBase64ToValidList(gfwListResult); | |||||
abpContent = | |||||
$@"var __USERRULES__ = {JsonConvert.SerializeObject(userruleLines, Formatting.Indented)}; | |||||
var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; | |||||
{abpContent}"; | |||||
return abpContent; | |||||
} | |||||
public void UpdatePACFromGFWList(Configuration config) | |||||
{ | |||||
string gfwListUrl = GFWLIST_URL; | |||||
if (!string.IsNullOrWhiteSpace(config.gfwListUrl)) | |||||
{ | |||||
logger.Info("Found custom GFWListURL in config file"); | |||||
gfwListUrl = config.gfwListUrl; | |||||
} | |||||
logger.Info($"Checking GFWList from {gfwListUrl}"); | |||||
WebClient http = new WebClient(); | |||||
if (config.enabled) | |||||
{ | |||||
http.Proxy = new WebProxy( | |||||
config.isIPv6Enabled | |||||
? $"[{IPAddress.IPv6Loopback.ToString()}]" | |||||
: IPAddress.Loopback.ToString(), | |||||
config.localPort); | |||||
} | |||||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||||
http.DownloadStringAsync(new Uri(gfwListUrl)); | |||||
} | |||||
public static List<string> ParseBase64ToValidList(string response) | |||||
{ | |||||
byte[] bytes = Convert.FromBase64String(response); | |||||
string content = Encoding.ASCII.GetString(bytes); | |||||
return ParseToValidList(content); | |||||
} | |||||
private static List<string> ParseToValidList(string content) | |||||
{ | |||||
List<string> valid_lines = new List<string>(); | |||||
using (var sr = new StringReader(content)) | |||||
{ | |||||
foreach (var line in sr.NonWhiteSpaceLines()) | |||||
{ | |||||
if (line.BeginWithAny(IgnoredLineBegins)) | |||||
continue; | |||||
valid_lines.Add(line); | |||||
} | |||||
} | |||||
return valid_lines; | |||||
} | |||||
} | |||||
} |
@@ -1,4 +1,5 @@ | |||||
using NLog; | using NLog; | ||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using System; | using System; | ||||
@@ -21,6 +22,7 @@ namespace Shadowsocks.Controller | |||||
public const string PAC_FILE = "pac.txt"; | public const string PAC_FILE = "pac.txt"; | ||||
public const string USER_RULE_FILE = "user-rule.txt"; | public const string USER_RULE_FILE = "user-rule.txt"; | ||||
public const string USER_ABP_FILE = "abp.txt"; | public const string USER_ABP_FILE = "abp.txt"; | ||||
private Configuration config; | |||||
FileSystemWatcher PACFileWatcher; | FileSystemWatcher PACFileWatcher; | ||||
FileSystemWatcher UserRuleFileWatcher; | FileSystemWatcher UserRuleFileWatcher; | ||||
@@ -28,8 +30,9 @@ namespace Shadowsocks.Controller | |||||
public event EventHandler PACFileChanged; | public event EventHandler PACFileChanged; | ||||
public event EventHandler UserRuleFileChanged; | public event EventHandler UserRuleFileChanged; | ||||
public PACDaemon() | |||||
public PACDaemon(Configuration config) | |||||
{ | { | ||||
this.config = config; | |||||
TouchPACFile(); | TouchPACFile(); | ||||
TouchUserRuleFile(); | TouchUserRuleFile(); | ||||
@@ -42,7 +45,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (!File.Exists(PAC_FILE)) | if (!File.Exists(PAC_FILE)) | ||||
{ | { | ||||
File.WriteAllText(PAC_FILE, Resources.default_abp_rule + Resources.abp_js); | |||||
GeositeUpdater.MergeAndWritePACFile(config.geositeGroup, config.geositeBlacklistMode); | |||||
} | } | ||||
return PAC_FILE; | return PAC_FILE; | ||||
} | } | ||||
@@ -58,14 +61,11 @@ namespace Shadowsocks.Controller | |||||
internal string GetPACContent() | internal string GetPACContent() | ||||
{ | { | ||||
if (File.Exists(PAC_FILE)) | |||||
{ | |||||
return File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||||
} | |||||
else | |||||
if (!File.Exists(PAC_FILE)) | |||||
{ | { | ||||
return Resources.default_abp_rule + Resources.abp_js; | |||||
GeositeUpdater.MergeAndWritePACFile(config.geositeGroup, config.geositeBlacklistMode); | |||||
} | } | ||||
return File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||||
} | } | ||||
@@ -1,10 +1,12 @@ | |||||
using NLog; | |||||
using System; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Timers; | using System.Timers; | ||||
using NLog; | |||||
using Shadowsocks.Controller.Strategy; | using Shadowsocks.Controller.Strategy; | ||||
using Shadowsocks.Encryption; | using Shadowsocks.Encryption; | ||||
using Shadowsocks.Encryption.AEAD; | using Shadowsocks.Encryption.AEAD; | ||||
@@ -12,16 +14,22 @@ using Shadowsocks.Encryption.Exception; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Proxy; | using Shadowsocks.Proxy; | ||||
using Shadowsocks.Util.Sockets; | using Shadowsocks.Util.Sockets; | ||||
using static Shadowsocks.Encryption.EncryptorBase; | using static Shadowsocks.Encryption.EncryptorBase; | ||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
class TCPRelay : StreamService | class TCPRelay : StreamService | ||||
{ | { | ||||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||||
private ShadowsocksController _controller; | |||||
public event EventHandler<SSTCPConnectedEventArgs> OnConnected; | |||||
public event EventHandler<SSTransmitEventArgs> OnInbound; | |||||
public event EventHandler<SSTransmitEventArgs> OnOutbound; | |||||
public event EventHandler<SSRelayEventArgs> OnFailed; | |||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||||
private readonly ShadowsocksController _controller; | |||||
private DateTime _lastSweepTime; | private DateTime _lastSweepTime; | ||||
private Configuration _config; | |||||
private readonly Configuration _config; | |||||
public ISet<TCPHandler> Handlers { get; set; } | public ISet<TCPHandler> Handlers { get; set; } | ||||
@@ -47,7 +55,7 @@ namespace Shadowsocks.Controller | |||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
TCPHandler handler = new TCPHandler(_controller, _config, this, socket); | |||||
TCPHandler handler = new TCPHandler(_controller, _config, socket); | |||||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | ||||
lock (Handlers) | lock (Handlers) | ||||
@@ -85,9 +93,24 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (socket.ProtocolType != ProtocolType.Tcp | if (socket.ProtocolType != ProtocolType.Tcp | ||||
|| (length < 2 || firstPacket[0] != 5)) | || (length < 2 || firstPacket[0] != 5)) | ||||
{ | |||||
return false; | return false; | ||||
} | |||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
TCPHandler handler = new TCPHandler(_controller, _config, this, socket); | |||||
TCPHandler handler = new TCPHandler(_controller, _config, socket); | |||||
handler.OnConnected += OnConnected; | |||||
handler.OnInbound += OnInbound; | |||||
handler.OnOutbound += OnOutbound; | |||||
handler.OnFailed += OnFailed; | |||||
handler.OnClosed += (h, arg) => | |||||
{ | |||||
lock (Handlers) | |||||
{ | |||||
Handlers.Remove(handler); | |||||
} | |||||
}; | |||||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | ||||
lock (Handlers) | lock (Handlers) | ||||
@@ -98,8 +121,12 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_lastSweepTime = now; | _lastSweepTime = now; | ||||
foreach (TCPHandler handler1 in Handlers) | foreach (TCPHandler handler1 in Handlers) | ||||
{ | |||||
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | ||||
{ | |||||
handlersToClose.Add(handler1); | handlersToClose.Add(handler1); | ||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
foreach (TCPHandler handler1 in handlersToClose) | foreach (TCPHandler handler1 in handlersToClose) | ||||
@@ -128,26 +155,46 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
handlersToClose.ForEach(h => h.Close()); | handlersToClose.ForEach(h => h.Close()); | ||||
} | } | ||||
} | |||||
public void UpdateInboundCounter(Server server, long n) | |||||
public class SSRelayEventArgs : EventArgs | |||||
{ | |||||
public readonly Server server; | |||||
public SSRelayEventArgs(Server server) | |||||
{ | { | ||||
_controller.UpdateInboundCounter(server, n); | |||||
this.server = server; | |||||
} | } | ||||
} | |||||
public void UpdateOutboundCounter(Server server, long n) | |||||
public class SSTransmitEventArgs : SSRelayEventArgs | |||||
{ | |||||
public readonly long length; | |||||
public SSTransmitEventArgs(Server server, long length) : base(server) | |||||
{ | { | ||||
_controller.UpdateOutboundCounter(server, n); | |||||
this.length = length; | |||||
} | } | ||||
} | |||||
public class SSTCPConnectedEventArgs : SSRelayEventArgs | |||||
{ | |||||
public readonly TimeSpan latency; | |||||
public void UpdateLatency(Server server, TimeSpan latency) | |||||
public SSTCPConnectedEventArgs(Server server, TimeSpan latency) : base(server) | |||||
{ | { | ||||
_controller.UpdateLatency(server, latency); | |||||
this.latency = latency; | |||||
} | } | ||||
} | } | ||||
internal class TCPHandler | internal class TCPHandler | ||||
{ | { | ||||
class AsyncSession | |||||
public event EventHandler<SSTCPConnectedEventArgs> OnConnected; | |||||
public event EventHandler<SSTransmitEventArgs> OnInbound; | |||||
public event EventHandler<SSTransmitEventArgs> OnOutbound; | |||||
public event EventHandler<SSRelayEventArgs> OnClosed; | |||||
public event EventHandler<SSRelayEventArgs> OnFailed; | |||||
private class AsyncSession | |||||
{ | { | ||||
public IProxy Remote { get; } | public IProxy Remote { get; } | ||||
@@ -157,7 +204,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
class AsyncSession<T> : AsyncSession | |||||
private class AsyncSession<T> : AsyncSession | |||||
{ | { | ||||
public T State { get; set; } | public T State { get; set; } | ||||
@@ -172,7 +219,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private static Logger Logger = LogManager.GetCurrentClassLogger(); | |||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); | |||||
private readonly int _serverTimeout; | private readonly int _serverTimeout; | ||||
private readonly int _proxyTimeout; | private readonly int _proxyTimeout; | ||||
@@ -191,10 +238,9 @@ namespace Shadowsocks.Controller | |||||
public DateTime lastActivity; | public DateTime lastActivity; | ||||
private ShadowsocksController _controller; | |||||
private Configuration _config; | |||||
private TCPRelay _tcprelay; | |||||
private Socket _connection; | |||||
private readonly ShadowsocksController _controller; | |||||
private readonly ProxyConfig _config; | |||||
private readonly Socket _connection; | |||||
private IEncryptor encryptor; | private IEncryptor encryptor; | ||||
// workaround | // workaround | ||||
@@ -220,16 +266,16 @@ namespace Shadowsocks.Controller | |||||
private int _totalWrite = 0; | private int _totalWrite = 0; | ||||
// remote -> local proxy (ciphertext, before decrypt) | // remote -> local proxy (ciphertext, before decrypt) | ||||
private byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||||
private readonly byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||||
// client -> local proxy (plaintext, before encrypt) | // client -> local proxy (plaintext, before encrypt) | ||||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||||
private readonly byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||||
// local proxy -> remote (plaintext, after decrypt) | // local proxy -> remote (plaintext, after decrypt) | ||||
private byte[] _remoteSendBuffer = new byte[BufferSize]; | |||||
private readonly byte[] _remoteSendBuffer = new byte[BufferSize]; | |||||
// local proxy -> client (ciphertext, before decrypt) | // local proxy -> client (ciphertext, before decrypt) | ||||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||||
private readonly byte[] _connetionSendBuffer = new byte[BufferSize]; | |||||
private bool _connectionShutdown = false; | private bool _connectionShutdown = false; | ||||
private bool _remoteShutdown = false; | private bool _remoteShutdown = false; | ||||
@@ -247,11 +293,11 @@ namespace Shadowsocks.Controller | |||||
private EndPoint _destEndPoint = null; | private EndPoint _destEndPoint = null; | ||||
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) | |||||
// TODO: decouple controller | |||||
public TCPHandler(ShadowsocksController controller, Configuration config, Socket socket) | |||||
{ | { | ||||
_controller = controller; | _controller = controller; | ||||
_config = config; | |||||
_tcprelay = tcprelay; | |||||
_config = config.proxy; | |||||
_connection = socket; | _connection = socket; | ||||
_proxyTimeout = config.proxy.proxyTimeout * 1000; | _proxyTimeout = config.proxy.proxyTimeout * 1000; | ||||
_serverTimeout = config.GetCurrentServer().timeout * 1000; | _serverTimeout = config.GetCurrentServer().timeout * 1000; | ||||
@@ -264,7 +310,9 @@ namespace Shadowsocks.Controller | |||||
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, | Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, | ||||
_destEndPoint); | _destEndPoint); | ||||
if (server == null || server.server == "") | if (server == null || server.server == "") | ||||
{ | |||||
throw new ArgumentException("No server configured"); | throw new ArgumentException("No server configured"); | ||||
} | |||||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | ||||
decryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | decryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | ||||
@@ -286,20 +334,31 @@ namespace Shadowsocks.Controller | |||||
private void CheckClose() | private void CheckClose() | ||||
{ | { | ||||
if (_connectionShutdown && _remoteShutdown) | if (_connectionShutdown && _remoteShutdown) | ||||
{ | |||||
Close(); | Close(); | ||||
} | |||||
} | |||||
private void ErrorClose(Exception e) | |||||
{ | |||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
} | } | ||||
public void Close() | public void Close() | ||||
{ | { | ||||
lock (_closeConnLock) | lock (_closeConnLock) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
_closed = true; | _closed = true; | ||||
} | } | ||||
lock (_tcprelay.Handlers) | |||||
{ | |||||
_tcprelay.Handlers.Remove(this); | |||||
} | |||||
OnClosed?.Invoke(this, new SSRelayEventArgs(_server)); | |||||
try | try | ||||
{ | { | ||||
_connection.Shutdown(SocketShutdown.Both); | _connection.Shutdown(SocketShutdown.Both); | ||||
@@ -314,7 +373,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var remote = _currentRemoteSession.Remote; | |||||
IProxy remote = _currentRemoteSession.Remote; | |||||
remote.Shutdown(SocketShutdown.Both); | remote.Shutdown(SocketShutdown.Both); | ||||
remote.Close(); | remote.Close(); | ||||
} | } | ||||
@@ -327,7 +386,11 @@ namespace Shadowsocks.Controller | |||||
private void HandshakeReceive() | private void HandshakeReceive() | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
int bytesRead = _firstPacketLength; | int bytesRead = _firstPacketLength; | ||||
@@ -344,18 +407,23 @@ namespace Shadowsocks.Controller | |||||
HandshakeSendCallback, null); | HandshakeSendCallback, null); | ||||
} | } | ||||
else | else | ||||
{ | |||||
Close(); | Close(); | ||||
} | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
private void HandshakeSendCallback(IAsyncResult ar) | private void HandshakeSendCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
_connection.EndSend(ar); | _connection.EndSend(ar); | ||||
@@ -367,20 +435,23 @@ namespace Shadowsocks.Controller | |||||
// +-----+-----+-------+------+----------+----------+ | // +-----+-----+-------+------+----------+----------+ | ||||
// Skip first 3 bytes, and read 2 more bytes to analysis the address. | // Skip first 3 bytes, and read 2 more bytes to analysis the address. | ||||
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length. | // 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length. | ||||
// TODO validate | |||||
// validate is unnecessary, we did it in first packet, but we can do it in future version | |||||
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + ADDR_ATYP_LEN + 1, SocketFlags.None, | _connection.BeginReceive(_connetionRecvBuffer, 0, 3 + ADDR_ATYP_LEN + 1, SocketFlags.None, | ||||
HandshakeReceive2Callback, null); | |||||
AddressReceiveCallback, null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
private void HandshakeReceive2Callback(IAsyncResult ar) | |||||
private void AddressReceiveCallback(IAsyncResult ar) | |||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
int bytesRead = _connection.EndReceive(ar); | int bytesRead = _connection.EndReceive(ar); | ||||
@@ -398,7 +469,7 @@ namespace Shadowsocks.Controller | |||||
// +----+-----+-------+------+----------+----------+ | // +----+-----+-------+------+----------+----------+ | ||||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | ||||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, | _connection.BeginSend(response, 0, response.Length, SocketFlags.None, | ||||
ResponseCallback, null); | |||||
ConnectResponseCallback, null); | |||||
break; | break; | ||||
case CMD_UDP_ASSOC: | case CMD_UDP_ASSOC: | ||||
ReadAddress(HandleUDPAssociate); | ReadAddress(HandleUDPAssociate); | ||||
@@ -419,12 +490,11 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
private void ResponseCallback(IAsyncResult ar) | |||||
private void ConnectResponseCallback(IAsyncResult ar) | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
@@ -434,8 +504,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
@@ -474,15 +543,19 @@ namespace Shadowsocks.Controller | |||||
private void OnAddressFullyRead(IAsyncResult ar) | private void OnAddressFullyRead(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
int bytesRead = _connection.EndReceive(ar); | int bytesRead = _connection.EndReceive(ar); | ||||
var states = (object[])ar.AsyncState; | |||||
object[] states = (object[])ar.AsyncState; | |||||
int bytesRemain = (int)states[0]; | int bytesRemain = (int)states[0]; | ||||
var onSuccess = (Action)states[1]; | |||||
Action onSuccess = (Action)states[1]; | |||||
if (bytesRead >= bytesRemain) | if (bytesRead >= bytesRemain) | ||||
{ | { | ||||
@@ -529,8 +602,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
@@ -558,7 +630,11 @@ namespace Shadowsocks.Controller | |||||
private void ReadAll(IAsyncResult ar) | private void ReadAll(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
if (ar.AsyncState != null) | if (ar.AsyncState != null) | ||||
@@ -576,13 +652,14 @@ namespace Shadowsocks.Controller | |||||
ReadAll, null); | ReadAll, null); | ||||
} | } | ||||
else | else | ||||
{ | |||||
Close(); | Close(); | ||||
} | |||||
} | } | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
@@ -627,9 +704,9 @@ namespace Shadowsocks.Controller | |||||
serverEP = pluginEP; | serverEP = pluginEP; | ||||
remote = new DirectConnect(); | remote = new DirectConnect(); | ||||
} | } | ||||
else if (_config.proxy.useProxy) | |||||
else if (_config.useProxy) | |||||
{ | { | ||||
switch (_config.proxy.proxyType) | |||||
switch (_config.proxyType) | |||||
{ | { | ||||
case ProxyConfig.PROXY_SOCKS5: | case ProxyConfig.PROXY_SOCKS5: | ||||
remote = new Socks5Proxy(); | remote = new Socks5Proxy(); | ||||
@@ -640,14 +717,14 @@ namespace Shadowsocks.Controller | |||||
default: | default: | ||||
throw new NotSupportedException("Unknown forward proxy."); | throw new NotSupportedException("Unknown forward proxy."); | ||||
} | } | ||||
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort); | |||||
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
remote = new DirectConnect(); | remote = new DirectConnect(); | ||||
} | } | ||||
var session = new AsyncSession(remote); | |||||
AsyncSession session = new AsyncSession(remote); | |||||
lock (_closeConnLock) | lock (_closeConnLock) | ||||
{ | { | ||||
if (_closed) | if (_closed) | ||||
@@ -675,14 +752,13 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
private void ProxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | private void ProxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
{ | { | ||||
var timer = (ProxyTimer)sender; | |||||
ProxyTimer timer = (ProxyTimer)sender; | |||||
timer.Elapsed -= ProxyConnectTimer_Elapsed; | timer.Elapsed -= ProxyConnectTimer_Elapsed; | ||||
timer.Enabled = false; | timer.Enabled = false; | ||||
timer.Dispose(); | timer.Dispose(); | ||||
@@ -692,7 +768,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
var proxy = timer.Session.Remote; | |||||
IProxy proxy = timer.Session.Remote; | |||||
Logger.Info($"Proxy {proxy.ProxyEndPoint} timed out"); | Logger.Info($"Proxy {proxy.ProxyEndPoint} timed out"); | ||||
proxy.Close(); | proxy.Close(); | ||||
@@ -707,15 +783,15 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||||
AsyncSession<ProxyTimer> session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||||
ProxyTimer timer = session.State; | ProxyTimer timer = session.State; | ||||
var destEndPoint = timer.DestEndPoint; | |||||
var server = timer.Server; | |||||
EndPoint destEndPoint = timer.DestEndPoint; | |||||
Server server = timer.Server; | |||||
timer.Elapsed -= ProxyConnectTimer_Elapsed; | timer.Elapsed -= ProxyConnectTimer_Elapsed; | ||||
timer.Enabled = false; | timer.Enabled = false; | ||||
timer.Dispose(); | timer.Dispose(); | ||||
var remote = session.Remote; | |||||
IProxy remote = session.Remote; | |||||
// Complete the connection. | // Complete the connection. | ||||
remote.EndConnectProxy(ar); | remote.EndConnectProxy(ar); | ||||
@@ -737,9 +813,9 @@ namespace Shadowsocks.Controller | |||||
_destConnected = false; | _destConnected = false; | ||||
NetworkCredential auth = null; | NetworkCredential auth = null; | ||||
if (_config.proxy.useAuth) | |||||
if (_config.useAuth) | |||||
{ | { | ||||
auth = new NetworkCredential(_config.proxy.authUser, _config.proxy.authPwd); | |||||
auth = new NetworkCredential(_config.authUser, _config.authPwd); | |||||
} | } | ||||
// Connect to the remote endpoint. | // Connect to the remote endpoint. | ||||
@@ -751,14 +827,13 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
private void DestConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | private void DestConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
{ | { | ||||
var timer = (ServerTimer)sender; | |||||
ServerTimer timer = (ServerTimer)sender; | |||||
timer.Elapsed -= DestConnectTimer_Elapsed; | timer.Elapsed -= DestConnectTimer_Elapsed; | ||||
timer.Enabled = false; | timer.Enabled = false; | ||||
timer.Dispose(); | timer.Dispose(); | ||||
@@ -768,10 +843,9 @@ namespace Shadowsocks.Controller | |||||
return; | return; | ||||
} | } | ||||
var session = timer.Session; | |||||
AsyncSession session = timer.Session; | |||||
Server server = timer.Server; | Server server = timer.Server; | ||||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||||
strategy?.SetFailure(server); | |||||
OnFailed?.Invoke(this, new SSRelayEventArgs(_server)); | |||||
Logger.Info($"{server.FriendlyName()} timed out"); | Logger.Info($"{server.FriendlyName()} timed out"); | ||||
session.Remote.Close(); | session.Remote.Close(); | ||||
Close(); | Close(); | ||||
@@ -779,17 +853,21 @@ namespace Shadowsocks.Controller | |||||
private void ConnectCallback(IAsyncResult ar) | private void ConnectCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||||
AsyncSession<ServerTimer> session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||||
ServerTimer timer = session.State; | ServerTimer timer = session.State; | ||||
_server = timer.Server; | _server = timer.Server; | ||||
timer.Elapsed -= DestConnectTimer_Elapsed; | timer.Elapsed -= DestConnectTimer_Elapsed; | ||||
timer.Enabled = false; | timer.Enabled = false; | ||||
timer.Dispose(); | timer.Dispose(); | ||||
var remote = session.Remote; | |||||
IProxy remote = session.Remote; | |||||
// Complete the connection. | // Complete the connection. | ||||
remote.EndConnectDest(ar); | remote.EndConnectDest(ar); | ||||
@@ -797,10 +875,9 @@ namespace Shadowsocks.Controller | |||||
Logger.Debug($"Socket connected to ss server: {_server.FriendlyName()}"); | Logger.Debug($"Socket connected to ss server: {_server.FriendlyName()}"); | ||||
var latency = DateTime.Now - _startConnectTime; | |||||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||||
strategy?.UpdateLatency(_server, latency); | |||||
_tcprelay.UpdateLatency(_server, latency); | |||||
TimeSpan latency = DateTime.Now - _startConnectTime; | |||||
OnConnected?.Invoke(this, new SSTCPConnectedEventArgs(_server, latency)); | |||||
StartPipe(session); | StartPipe(session); | ||||
} | } | ||||
@@ -811,11 +888,9 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (_server != null) | if (_server != null) | ||||
{ | { | ||||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||||
strategy?.SetFailure(_server); | |||||
OnFailed?.Invoke(this, new SSRelayEventArgs(_server)); | |||||
} | } | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
@@ -824,7 +899,7 @@ namespace Shadowsocks.Controller | |||||
int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength); | int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength); | ||||
if (available > 0) | if (available > 0) | ||||
{ | { | ||||
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available, | |||||
int size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available, | |||||
SocketFlags.None); | SocketFlags.None); | ||||
_firstPacketLength += size; | _firstPacketLength += size; | ||||
@@ -833,7 +908,11 @@ namespace Shadowsocks.Controller | |||||
private void StartPipe(AsyncSession session) | private void StartPipe(AsyncSession session) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
_startReceivingTime = DateTime.Now; | _startReceivingTime = DateTime.Now; | ||||
@@ -846,20 +925,24 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | private void PipeRemoteReceiveCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession)ar.AsyncState; | |||||
AsyncSession session = (AsyncSession)ar.AsyncState; | |||||
int bytesRead = session.Remote.EndReceive(ar); | int bytesRead = session.Remote.EndReceive(ar); | ||||
_totalRead += bytesRead; | _totalRead += bytesRead; | ||||
_tcprelay.UpdateInboundCounter(_server, bytesRead); | |||||
OnInbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesRead)); | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
lastActivity = DateTime.Now; | lastActivity = DateTime.Now; | ||||
@@ -889,8 +972,6 @@ namespace Shadowsocks.Controller | |||||
Logger.Debug($"start sending {bytesToSend}"); | Logger.Debug($"start sending {bytesToSend}"); | ||||
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, | _connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, | ||||
PipeConnectionSendCallback, new object[] { session, bytesToSend }); | PipeConnectionSendCallback, new object[] { session, bytesToSend }); | ||||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||||
strategy?.UpdateLastRead(_server); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -901,20 +982,23 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | private void PipeConnectionReceiveCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
int bytesRead = _connection.EndReceive(ar); | int bytesRead = _connection.EndReceive(ar); | ||||
var session = (AsyncSession)ar.AsyncState; | |||||
var remote = session.Remote; | |||||
AsyncSession session = (AsyncSession)ar.AsyncState; | |||||
IProxy remote = session.Remote; | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
@@ -929,8 +1013,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
@@ -952,22 +1035,25 @@ namespace Shadowsocks.Controller | |||||
return; | return; | ||||
} | } | ||||
} | } | ||||
_tcprelay.UpdateOutboundCounter(_server, bytesToSend); | |||||
OnOutbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesToSend)); | |||||
_startSendingTime = DateTime.Now; | _startSendingTime = DateTime.Now; | ||||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, | session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, | ||||
PipeRemoteSendCallback, new object[] { session, bytesToSend }); | PipeRemoteSendCallback, new object[] { session, bytesToSend }); | ||||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||||
strategy?.UpdateLastWrite(_server); | |||||
} | } | ||||
private void PipeRemoteSendCallback(IAsyncResult ar) | private void PipeRemoteSendCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (_closed) | |||||
{ | |||||
return; | |||||
} | |||||
try | try | ||||
{ | { | ||||
var container = (object[])ar.AsyncState; | |||||
var session = (AsyncSession)container[0]; | |||||
var bytesShouldSend = (int)container[1]; | |||||
object[] container = (object[])ar.AsyncState; | |||||
AsyncSession session = (AsyncSession)container[0]; | |||||
int bytesShouldSend = (int)container[1]; | |||||
int bytesSent = session.Remote.EndSend(ar); | int bytesSent = session.Remote.EndSend(ar); | ||||
int bytesRemaining = bytesShouldSend - bytesSent; | int bytesRemaining = bytesShouldSend - bytesSent; | ||||
if (bytesRemaining > 0) | if (bytesRemaining > 0) | ||||
@@ -983,8 +1069,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
@@ -993,11 +1078,11 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var container = (object[])ar.AsyncState; | |||||
var session = (AsyncSession)container[0]; | |||||
var bytesShouldSend = (int)container[1]; | |||||
var bytesSent = _connection.EndSend(ar); | |||||
var bytesRemaining = bytesShouldSend - bytesSent; | |||||
object[] container = (object[])ar.AsyncState; | |||||
AsyncSession session = (AsyncSession)container[0]; | |||||
int bytesShouldSend = (int)container[1]; | |||||
int bytesSent = _connection.EndSend(ar); | |||||
int bytesRemaining = bytesShouldSend - bytesSent; | |||||
if (bytesRemaining > 0) | if (bytesRemaining > 0) | ||||
{ | { | ||||
Logger.Info("reconstruct _remoteSendBuffer to re-send"); | Logger.Info("reconstruct _remoteSendBuffer to re-send"); | ||||
@@ -1011,8 +1096,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logger.LogUsefulException(e); | |||||
Close(); | |||||
ErrorClose(e); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -35,7 +35,6 @@ namespace Shadowsocks.Controller | |||||
private Configuration _config; | private Configuration _config; | ||||
private StrategyManager _strategyManager; | private StrategyManager _strategyManager; | ||||
private PrivoxyRunner privoxyRunner; | private PrivoxyRunner privoxyRunner; | ||||
private GFWListUpdater gfwListUpdater; | |||||
private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer; | private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer; | ||||
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; | public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; | ||||
@@ -80,9 +79,9 @@ namespace Shadowsocks.Controller | |||||
public event EventHandler<PathEventArgs> PACFileReadyToOpen; | public event EventHandler<PathEventArgs> PACFileReadyToOpen; | ||||
public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen; | public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen; | ||||
public event EventHandler<GFWListUpdater.ResultEventArgs> UpdatePACFromGFWListCompleted; | |||||
public event EventHandler<GeositeResultEventArgs> UpdatePACFromGeositeCompleted; | |||||
public event ErrorEventHandler UpdatePACFromGFWListError; | |||||
public event ErrorEventHandler UpdatePACFromGeositeError; | |||||
public event ErrorEventHandler Errored; | public event ErrorEventHandler Errored; | ||||
@@ -217,25 +216,25 @@ namespace Shadowsocks.Controller | |||||
StatisticsStrategyConfiguration.Save(configuration); | StatisticsStrategyConfiguration.Save(configuration); | ||||
} | } | ||||
public bool AskAddServerBySSURL(string ssURL) | |||||
{ | |||||
var dr = MessageBox.Show(I18N.GetString("Import from URL: {0} ?", ssURL), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo); | |||||
if (dr == DialogResult.Yes) | |||||
{ | |||||
public bool AskAddServerBySSURL(string ssURL) | |||||
{ | |||||
var dr = MessageBox.Show(I18N.GetString("Import from URL: {0} ?", ssURL), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo); | |||||
if (dr == DialogResult.Yes) | |||||
{ | |||||
if (AddServerBySSURL(ssURL)) | if (AddServerBySSURL(ssURL)) | ||||
{ | { | ||||
MessageBox.Show(I18N.GetString("Successfully imported from {0}", ssURL)); | MessageBox.Show(I18N.GetString("Successfully imported from {0}", ssURL)); | ||||
return true; | return true; | ||||
} | |||||
} | |||||
else | else | ||||
{ | { | ||||
MessageBox.Show(I18N.GetString("Failed to import. Please check if the link is valid.")); | MessageBox.Show(I18N.GetString("Failed to import. Please check if the link is valid.")); | ||||
} | |||||
} | |||||
return false; | |||||
} | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
public bool AddServerBySSURL(string ssURL) | public bool AddServerBySSURL(string ssURL) | ||||
{ | { | ||||
try | try | ||||
@@ -412,12 +411,9 @@ namespace Shadowsocks.Controller | |||||
return $"ss://{url}{tag}"; | return $"ss://{url}{tag}"; | ||||
} | } | ||||
public void UpdatePACFromGFWList() | |||||
public void UpdatePACFromGeosite() | |||||
{ | { | ||||
if (gfwListUpdater != null) | |||||
{ | |||||
gfwListUpdater.UpdatePACFromGFWList(_config); | |||||
} | |||||
GeositeUpdater.UpdatePACFromGeosite(_config); | |||||
} | } | ||||
public void UpdateStatisticsConfiguration(bool enabled) | public void UpdateStatisticsConfiguration(bool enabled) | ||||
@@ -486,29 +482,32 @@ namespace Shadowsocks.Controller | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | ConfigChanged?.Invoke(this, new EventArgs()); | ||||
} | } | ||||
public void UpdateLatency(Server server, TimeSpan latency) | |||||
public void UpdateLatency(object sender, SSTCPConnectedEventArgs args) | |||||
{ | { | ||||
GetCurrentStrategy()?.UpdateLatency(args.server, args.latency); | |||||
if (_config.availabilityStatistics) | if (_config.availabilityStatistics) | ||||
{ | { | ||||
availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds); | |||||
availabilityStatistics.UpdateLatency(args.server, (int)args.latency.TotalMilliseconds); | |||||
} | } | ||||
} | } | ||||
public void UpdateInboundCounter(Server server, long n) | |||||
public void UpdateInboundCounter(object sender, SSTransmitEventArgs args) | |||||
{ | { | ||||
Interlocked.Add(ref _inboundCounter, n); | |||||
GetCurrentStrategy()?.UpdateLastRead(args.server); | |||||
Interlocked.Add(ref _inboundCounter, args.length); | |||||
if (_config.availabilityStatistics) | if (_config.availabilityStatistics) | ||||
{ | { | ||||
availabilityStatistics.UpdateInboundCounter(server, n); | |||||
availabilityStatistics.UpdateInboundCounter(args.server, args.length); | |||||
} | } | ||||
} | } | ||||
public void UpdateOutboundCounter(Server server, long n) | |||||
public void UpdateOutboundCounter(object sender, SSTransmitEventArgs args) | |||||
{ | { | ||||
Interlocked.Add(ref _outboundCounter, n); | |||||
GetCurrentStrategy()?.UpdateLastWrite(args.server); | |||||
Interlocked.Add(ref _outboundCounter, args.length); | |||||
if (_config.availabilityStatistics) | if (_config.availabilityStatistics) | ||||
{ | { | ||||
availabilityStatistics.UpdateOutboundCounter(server, n); | |||||
availabilityStatistics.UpdateOutboundCounter(args.server, args.length); | |||||
} | } | ||||
} | } | ||||
@@ -524,15 +523,15 @@ namespace Shadowsocks.Controller | |||||
privoxyRunner = privoxyRunner ?? new PrivoxyRunner(); | privoxyRunner = privoxyRunner ?? new PrivoxyRunner(); | ||||
_pacDaemon = _pacDaemon ?? new PACDaemon(); | |||||
_pacDaemon = _pacDaemon ?? new PACDaemon(_config); | |||||
_pacDaemon.PACFileChanged += PacDaemon_PACFileChanged; | _pacDaemon.PACFileChanged += PacDaemon_PACFileChanged; | ||||
_pacDaemon.UserRuleFileChanged += PacDaemon_UserRuleFileChanged; | _pacDaemon.UserRuleFileChanged += PacDaemon_UserRuleFileChanged; | ||||
_pacServer = _pacServer ?? new PACServer(_pacDaemon); | _pacServer = _pacServer ?? new PACServer(_pacDaemon); | ||||
_pacServer.UpdatePACURL(_config); // So PACServer works when system proxy disabled. | _pacServer.UpdatePACURL(_config); // So PACServer works when system proxy disabled. | ||||
gfwListUpdater = gfwListUpdater ?? new GFWListUpdater(); | |||||
gfwListUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; | |||||
gfwListUpdater.Error += PacServer_PACUpdateError; | |||||
GeositeUpdater.ResetEvent(); | |||||
GeositeUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; | |||||
GeositeUpdater.Error += PacServer_PACUpdateError; | |||||
availabilityStatistics.UpdateConfiguration(this); | availabilityStatistics.UpdateConfiguration(this); | ||||
_tcpListener?.Stop(); | _tcpListener?.Stop(); | ||||
@@ -553,6 +552,11 @@ namespace Shadowsocks.Controller | |||||
privoxyRunner.Start(_config); | privoxyRunner.Start(_config); | ||||
TCPRelay tcpRelay = new TCPRelay(this, _config); | TCPRelay tcpRelay = new TCPRelay(this, _config); | ||||
tcpRelay.OnConnected += UpdateLatency; | |||||
tcpRelay.OnInbound += UpdateInboundCounter; | |||||
tcpRelay.OnOutbound += UpdateOutboundCounter; | |||||
tcpRelay.OnFailed += (o, e) => GetCurrentStrategy()?.SetFailure(e.server); | |||||
UDPRelay udpRelay = new UDPRelay(this); | UDPRelay udpRelay = new UDPRelay(this); | ||||
_tcpListener = new TCPListener(_config, new List<IStreamService> | _tcpListener = new TCPListener(_config, new List<IStreamService> | ||||
{ | { | ||||
@@ -612,27 +616,20 @@ namespace Shadowsocks.Controller | |||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
} | } | ||||
private void PacServer_PACUpdateCompleted(object sender, GFWListUpdater.ResultEventArgs e) | |||||
private void PacServer_PACUpdateCompleted(object sender, GeositeResultEventArgs e) | |||||
{ | { | ||||
UpdatePACFromGFWListCompleted?.Invoke(this, e); | |||||
UpdatePACFromGeositeCompleted?.Invoke(this, e); | |||||
} | } | ||||
private void PacServer_PACUpdateError(object sender, ErrorEventArgs e) | private void PacServer_PACUpdateError(object sender, ErrorEventArgs e) | ||||
{ | { | ||||
UpdatePACFromGFWListError?.Invoke(this, e); | |||||
UpdatePACFromGeositeError?.Invoke(this, e); | |||||
} | } | ||||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | ||||
private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) | private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) | ||||
{ | { | ||||
if (!File.Exists(Utils.GetTempPath("gfwlist.txt"))) | |||||
{ | |||||
UpdatePACFromGFWList(); | |||||
} | |||||
else | |||||
{ | |||||
GFWListUpdater.MergeAndWritePACFile(FileManager.NonExclusiveReadAllText(Utils.GetTempPath("gfwlist.txt"))); | |||||
} | |||||
GeositeUpdater.MergeAndWritePACFile(_config.geositeGroup, _config.geositeBlacklistMode); | |||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
} | } | ||||
@@ -786,6 +786,10 @@ function FindProxyForURL(url, host) { | |||||
if (userrulesMatcher.matchesAny(url, host) instanceof WhitelistFilter) { | if (userrulesMatcher.matchesAny(url, host) instanceof WhitelistFilter) { | ||||
return direct; | return direct; | ||||
} | } | ||||
// Hack for Geosite, it provides a whitelist... | |||||
if (defaultMatcher.matchesAny(url, host) instanceof WhitelistFilter) { | |||||
return direct; | |||||
} | |||||
if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) { | if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) { | ||||
return proxy; | return proxy; | ||||
} | } | ||||
@@ -12,22 +12,22 @@ Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowso | |||||
,,,,,, | ,,,,,, | ||||
System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,Proxy système | System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,Proxy système | ||||
Disable,Отключен,禁用,禁用,無効,비활성화,Désactiver | Disable,Отключен,禁用,禁用,無効,비활성화,Désactiver | ||||
PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PAC,PAC,PAC | |||||
PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PAC,프록시 자동 구성 (PAC),PAC | |||||
Global,Для всей системы,全局模式,全局模式,全般,전역,Global | Global,Для всей системы,全局模式,全局模式,全般,전역,Global | ||||
Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | ||||
Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | ||||
Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques… | Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques… | ||||
Start on Boot,Автозагрузка,开机启动,開機啟動,システムと同時に起動,시스템 시작 시에 시작하기,Démarrage automatique | Start on Boot,Автозагрузка,开机启动,開機啟動,システムと同時に起動,시스템 시작 시에 시작하기,Démarrage automatique | ||||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,,, | |||||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,,ss:// 링크 연결, | |||||
Forward Proxy...,Прямой прокси…,正向代理设置...,正向 Proxy 設定...,フォワードプロキシの設定...,포워드 프록시,Forward-proxy… | Forward Proxy...,Прямой прокси…,正向代理设置...,正向 Proxy 設定...,フォワードプロキシの設定...,포워드 프록시,Forward-proxy… | ||||
Allow other Devices to connect,Общий доступ к подключению,允许其他设备连入,允許其他裝置連入,他のデバイスからの接続を許可する,다른 기기에서 연결 허용,Autoriser d'autres appareils à se connecter | Allow other Devices to connect,Общий доступ к подключению,允许其他设备连入,允許其他裝置連入,他のデバイスからの接続を許可する,다른 기기에서 연결 허용,Autoriser d'autres appareils à se connecter | ||||
Local PAC,Локальный PAC,使用本地 PAC,使用本機 PAC,ローカル PAC,로컬 PAC,PAC local | |||||
Online PAC,Удаленный PAC,使用在线 PAC,使用線上 PAC,オンライン PAC,온라인 PAC,PAC en ligne | |||||
Edit Local PAC File...,Редактировать локальный PAC…,编辑本地 PAC 文件...,編輯本機 PAC 檔案...,ローカル PAC ファイルの編集...,로컬 PAC 파일 수정,Modifier le fichier PAC local ... | |||||
Update Local PAC from GFWList,Обновить локальный PAC из GFWList,从 GFWList 更新本地 PAC,從 GFWList 更新本機 PAC,GFWList からローカル PAC を更新,GFWList에서 로컬 PAC 파일 업데이트,Mettre à jour le PAC local à partir de GFWList | |||||
Edit User Rule for GFWList...,Редактировать свои правила для GFWList,编辑 GFWList 的用户规则...,編輯 GFWList 的使用者規則...,ユーザールールの編集...,GFWList 사용자 수정,Modifier la règle utilisateur pour GFWList ... | |||||
Secure Local PAC,Безопасный URL локального PAC,保护本地 PAC,安全本機 PAC,ローカル PAC を保護,로컬 PAC 암호화,Sécuriser PAC local | |||||
Copy Local PAC URL,Копировать URL локального PAC,复制本地 PAC 网址,複製本機 PAC 網址,ローカル PAC URL をコピー,로컬 PAC 파일 URL 복사,Copier l'URL du PAC local | |||||
Local PAC,Локальный PAC,使用本地 PAC,使用本機 PAC,ローカル PAC,로컬 프록시 자동 구성,PAC local | |||||
Online PAC,Удаленный PAC,使用在线 PAC,使用線上 PAC,オンライン PAC,온라인 프록시 자동 구성,PAC en ligne | |||||
Edit Local PAC File...,Редактировать локальный PAC…,编辑本地 PAC 文件...,編輯本機 PAC 檔案...,ローカル PAC ファイルの編集...,로컬 프록시 자동 구성 파일 수정,Modifier le fichier PAC local ... | |||||
Update Local PAC from Geosite,Обновить локальный PAC из Geosite,从 Geosite 更新本地 PAC,從 Geosite 更新本機 PAC,Geosite からローカル PAC を更新,Geosite에서 로컬 프록시 자동 구성 파일 업데이트,Mettre à jour le PAC local à partir de Geosite | |||||
Edit User Rule for Geosite...,Редактировать свои правила для Geosite,编辑 Geosite 的用户规则...,編輯 Geosite 的使用者規則...,ユーザールールの編集...,Geosite 사용자 수정,Modifier la règle utilisateur pour Geosite ... | |||||
Secure Local PAC,Безопасный URL локального PAC,保护本地 PAC,安全本機 PAC,ローカル PAC を保護,로컬 프록시 자동 구성 파일 암호화,Sécuriser PAC local | |||||
Copy Local PAC URL,Копировать URL локального PAC,复制本地 PAC 网址,複製本機 PAC 網址,ローカル PAC URL をコピー,로컬 프록시 자동 구성 파일 URL 복사,Copier l'URL du PAC local | |||||
Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... | Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... | ||||
Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | ||||
Import URL from Clipboard...,Импорт адреса из буфера обмена…,从剪贴板导入URL...,從剪貼簿匯入 URL...,クリップボードから URL をインポート...,클립보드에서 URL 가져오기…,Importer l'URL du presse-papiers ... | Import URL from Clipboard...,Импорт адреса из буфера обмена…,从剪贴板导入URL...,從剪貼簿匯入 URL...,クリップボードから URL をインポート...,클립보드에서 URL 가져오기…,Importer l'URL du presse-papiers ... | ||||
@@ -66,17 +66,17 @@ Plugin Options,Опции плагина,插件选项,外掛程式選項,プラ | |||||
Need Plugin Argument,Требуются аргументы,需要命令行参数,,,플러그인 인자가 필요함,Besoin d'un argument de plugin | Need Plugin Argument,Требуются аргументы,需要命令行参数,,,플러그인 인자가 필요함,Besoin d'un argument de plugin | ||||
Plugin Arguments,Аргументы,插件参数,外掛程式參數,プラグインの引数,플러그인 인자,Arguments du plugin | Plugin Arguments,Аргументы,插件参数,外掛程式參數,プラグインの引数,플러그인 인자,Arguments du plugin | ||||
Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port proxy | Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port proxy | ||||
Portable Mode,Переносимый режим,便携模式,便攜模式,ポータブルモード,포터블(Portable) 모드,Mode portable | |||||
Restart required,Требуется перезапуск программы,需要重新启动SS,需要重新啟動SS,再起動SSが必要,재시작 필요함,Redémarrage nécessaire | |||||
Portable Mode,Переносимый режим,便携模式,便攜模式,ポータブルモード,포터블 모드,Mode portable | |||||
Restart required,Требуется перезапуск программы,需要重新启动SS,需要重新啟動SS,再起動SSが必要,재시작이 필요합니다,Redémarrage nécessaire | |||||
Remarks,Примечания,备注,註解,付記,알림,Remarques | Remarks,Примечания,备注,註解,付記,알림,Remarques | ||||
Timeout(Sec),Таймаут(сек),超时(秒),逾時 (秒),タイムアウト (秒),시간초과(Timeout) (초),Délai d'attente(sec) | |||||
Timeout(Sec),Таймаут(сек),超时(秒),逾時 (秒),タイムアウト (秒),시간 초과 (초),Délai d'attente(sec) | |||||
OK,ОК,确定,確定,OK,확인,OK | OK,ОК,确定,確定,OK,확인,OK | ||||
Cancel,Отмена,取消,取消,キャンセル,취소,Annuler | Cancel,Отмена,取消,取消,キャンセル,취소,Annuler | ||||
Apply,Применить,应用,應用,適用,적용,Appliquer | Apply,Применить,应用,應用,適用,적용,Appliquer | ||||
New server,Новый сервер,未配置的服务器,新伺服器,新規サーバー,새 서버,Nouveau serveur | New server,Новый сервер,未配置的服务器,新伺服器,新規サーバー,새 서버,Nouveau serveur | ||||
Move &Up,Выше,上移(&U),上移 (&U),上に移動 (&U),위로 (&U),Monter | Move &Up,Выше,上移(&U),上移 (&U),上に移動 (&U),위로 (&U),Monter | ||||
Move D&own,Ниже,下移(&O),下移 (&O),下に移動 (&O),아래로 (&O),Descendre | Move D&own,Ниже,下移(&O),下移 (&O),下に移動 (&O),아래로 (&O),Descendre | ||||
deprecated,Устаревшее,不推荐,不推薦,非推奨,폐기됨(deprecated),Obsolète | |||||
deprecated,Устаревшее,不推荐,不推薦,非推奨,더 이상 사용되지 않음,Obsolète | |||||
"Encryption method {0} not exist, will replace with {1}",,加密方法{0}不存在,将使用{1}代替,,,{0} 암호화 방식이 존재하지 않으므로 {1}로 대체될 것입니다.,"Méthode de chiffrement {0} n'existe pas, sera remplacée par {1}" | "Encryption method {0} not exist, will replace with {1}",,加密方法{0}不存在,将使用{1}代替,,,{0} 암호화 방식이 존재하지 않으므로 {1}로 대체될 것입니다.,"Méthode de chiffrement {0} n'existe pas, sera remplacée par {1}" | ||||
,,,,,, | ,,,,,, | ||||
#Statistics Config,,,,,, | #Statistics Config,,,,,, | ||||
@@ -116,8 +116,8 @@ Use Proxy,Использовать прокси,使用代理,使用 Proxy,プロ | |||||
Proxy Type,Тип прокси,代理类型,Proxy 類型,プロキシの種類,프록시 종류,Type de proxy | Proxy Type,Тип прокси,代理类型,Proxy 類型,プロキシの種類,프록시 종류,Type de proxy | ||||
Proxy Addr,Адрес прокси,代理地址,Proxy 位址,プロキシアドレス,프록시 주소,Adresse de proxy | Proxy Addr,Адрес прокси,代理地址,Proxy 位址,プロキシアドレス,프록시 주소,Adresse de proxy | ||||
Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port de proxy | Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port de proxy | ||||
"If server has a plugin, proxy will not be used","Если сервер использует плагины, прокси НЕ будет использоваться",若服务器含有插件,代理将不被使用,若伺服器含有外掛程式,Proxy 將不被使用,サーバーにプラグインがある場合、プロキシは利用されません,"만약 서버가 플러그인이 있다면, 프록시는용되지 않을 것입니다.","Si le serveur a un plugin, le proxy ne sera pas utilisé" | |||||
Use Auth,Требуется авторизация,使用认证,使用認證,認証を利用する,서버 증명(Auth) 사용,Utiliser l'authentification | |||||
"If server has a plugin, proxy will not be used","Если сервер использует плагины, прокси НЕ будет использоваться",若服务器含有插件,代理将不被使用,若伺服器含有外掛程式,Proxy 將不被使用,サーバーにプラグインがある場合、プロキシは利用されません,서버에 플러그인이 설치 되어 있는 경우 프록시를 사용할 수 없습니다.,"Si le serveur a un plugin, le proxy ne sera pas utilisé" | |||||
Use Auth,Требуется авторизация,使用认证,使用認證,認証を利用する,서버 인증 사용,Utiliser l'authentification | |||||
User Name,Пользователь,用户名,認證用戶,認証ユーザ,사용자 이름,Nom d'utilisateur | User Name,Пользователь,用户名,認證用戶,認証ユーザ,사용자 이름,Nom d'utilisateur | ||||
Auth Pwd,Пароль,认证密码,認證口令,認証パスワード,비밀번호,Mot de passe d'authentification | Auth Pwd,Пароль,认证密码,認證口令,認証パスワード,비밀번호,Mot de passe d'authentification | ||||
,,,,,, | ,,,,,, | ||||
@@ -142,9 +142,9 @@ QRCode and URL,QRCode и URL,二维码与 URL,QR 碼與 URL,QR コードと URL, | |||||
,,,,,, | ,,,,,, | ||||
# PAC Url Form,,,,,, | # PAC Url Form,,,,,, | ||||
,,,,,, | ,,,,,, | ||||
Edit Online PAC URL,Изменение URL удаленного PAC,编辑在线 PAC 网址,編輯線上 PAC 網址,オンライン PAC URL の編集,온라인 PAC URL 수정,Modifier l'URL du PAC en ligne | |||||
Edit Online PAC URL...,Редактировать URL удаленного PAC…,编辑在线 PAC 网址...,編輯線上 PAC 網址...,オンライン PAC URL の編集...,온라인 PAC URL 수정…,Modifier l'URL du PAC en ligne ... | |||||
Please input PAC Url,Введите URL адрес для PAC-файла,请输入 PAC 网址,請輸入 PAC 網址,PAC URLを入力して下さい,PAC URL을 입력하세요,Veuillez saisir l'URL PAC | |||||
Edit Online PAC URL,Изменение URL удаленного PAC,编辑在线 PAC 网址,編輯線上 PAC 網址,オンライン PAC URL の編集,온라인 프록시 자동 구성 URL 수정,Modifier l'URL du PAC en ligne | |||||
Edit Online PAC URL...,Редактировать URL удаленного PAC…,编辑在线 PAC 网址...,編輯線上 PAC 網址...,オンライン PAC URL の編集...,온라인 프록시 자동 구성 URL 수정…,Modifier l'URL du PAC en ligne ... | |||||
Please input PAC Url,Введите URL адрес для PAC-файла,请输入 PAC 网址,請輸入 PAC 網址,PAC URLを入力して下さい,프록시 자동 구성 URL을 입력하세요,Veuillez saisir l'URL PAC | |||||
,,,,,, | ,,,,,, | ||||
# HotkeySettings Form,,,,,, | # HotkeySettings Form,,,,,, | ||||
,,,,,, | ,,,,,, | ||||
@@ -164,7 +164,7 @@ Port {0} already in use,Порт {0} уже используется,端口 {0} | |||||
Port {0} is reserved by system,Порт {0} зарезервирован системой,端口 {0} 是系统保留端口,連接埠號碼 {0} 由系統保留, ポート番号 {0} はシステムによって予約されています,{0}번 포트는 시스템에서 사용 중입니다.,Port {0} réservé par le système | Port {0} is reserved by system,Порт {0} зарезервирован системой,端口 {0} 是系统保留端口,連接埠號碼 {0} 由系統保留, ポート番号 {0} はシステムによって予約されています,{0}번 포트는 시스템에서 사용 중입니다.,Port {0} réservé par le système | ||||
Invalid server address,Неверный адрес сервера,非法服务器地址,無效伺服器位址,サーバーアドレスが無効です。,올바르지 않은 서버 주소입니다.,Adresse de serveur non valide | Invalid server address,Неверный адрес сервера,非法服务器地址,無效伺服器位址,サーバーアドレスが無効です。,올바르지 않은 서버 주소입니다.,Adresse de serveur non valide | ||||
Illegal port number format,Неверный числовой формат порта,非法端口格式,無效連接埠號碼格式,ポート番号のフォーマットが無効です。,올바르지 않은 포트 번호 형식입니다.,Format de numéro de port illégal | Illegal port number format,Неверный числовой формат порта,非法端口格式,無效連接埠號碼格式,ポート番号のフォーマットが無効です。,올바르지 않은 포트 번호 형식입니다.,Format de numéro de port illégal | ||||
Illegal timeout format,Неверный формат таймаута,非法超时格式,無效逾時格式,タイムアウト値のフォーマットが無効です。,올바르지 않은 시간초과(Timeout) 형식입니다.,Format de délai d'attente illégal | |||||
Illegal timeout format,Неверный формат таймаута,非法超时格式,無效逾時格式,タイムアウト値のフォーマットが無効です。,올바르지 않은 시간 초과 형식입니다.,Format de délai d'attente illégal | |||||
Server IP can not be blank,IP-адрес сервера не может быть пустым,服务器 IP 不能为空,伺服器 IP 不能為空,サーバー IP が指定されていません。,서버 IP는 비어있으면 안됩니다.,L'adresse IP du serveur ne peut pas être vide | Server IP can not be blank,IP-адрес сервера не может быть пустым,服务器 IP 不能为空,伺服器 IP 不能為空,サーバー IP が指定されていません。,서버 IP는 비어있으면 안됩니다.,L'adresse IP du serveur ne peut pas être vide | ||||
Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide | Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide | ||||
Port out of range,Порт выходит за допустимый диапазон,端口超出范围,連接埠號碼超出範圍,ポート番号は範囲外です。,올바른 포트 범위가 아닙니다.,Port hors de portée | Port out of range,Порт выходит за допустимый диапазон,端口超出范围,連接埠號碼超出範圍,ポート番号は範囲外です。,올바른 포트 범위가 아닙니다.,Port hors de portée | ||||
@@ -176,9 +176,9 @@ Shadowsocks is here,Shadowsocks находится здесь,Shadowsocks 在这 | |||||
You can turn on/off Shadowsocks in the context menu,Вы можете управлять Shadowsocks из контекстного меню,可以在右键菜单中开关 Shadowsocks,可以在右鍵選項單中開關 Shadowsocks,コンテキストメニューを使って、Shadowsocks を有効または無効にすることができます。,프로그램 메뉴에서 Shadowsocks를 끄고 켤 수 있습니다.,Vous pouvez activer / désactiver Shadowsocks dans le menu contextuel | You can turn on/off Shadowsocks in the context menu,Вы можете управлять Shadowsocks из контекстного меню,可以在右键菜单中开关 Shadowsocks,可以在右鍵選項單中開關 Shadowsocks,コンテキストメニューを使って、Shadowsocks を有効または無効にすることができます。,프로그램 메뉴에서 Shadowsocks를 끄고 켤 수 있습니다.,Vous pouvez activer / désactiver Shadowsocks dans le menu contextuel | ||||
System Proxy Enabled,Системный прокси включен,系统代理已启用,系統 Proxy 已啟用,システム プロキシが有効です。,시스템 프록시가 활성화되었습니다.,Proxy système activé | System Proxy Enabled,Системный прокси включен,系统代理已启用,系統 Proxy 已啟用,システム プロキシが有効です。,시스템 프록시가 활성화되었습니다.,Proxy système activé | ||||
System Proxy Disabled,Системный прокси отключен,系统代理未启用,系統 Proxy 未啟用,システム プロキシが無効です。,시스템 프록시가 비활성화되었습니다.,Proxy système désactivé | System Proxy Disabled,Системный прокси отключен,系统代理未启用,系統 Proxy 未啟用,システム プロキシが無効です。,시스템 프록시가 비활성화되었습니다.,Proxy système désactivé | ||||
Failed to update PAC file ,Не удалось обновить PAC файл,更新 PAC 文件失败,更新 PAC 檔案失敗,PAC の更新に失敗しました。,PAC 파일을 업데이트하는데 실패했습니다.,Impossible de mettre à jour le fichier PAC | |||||
PAC updated,PAC файл обновлен,更新 PAC 成功,更新 PAC 成功,PAC を更新しました。,PAC 파일이 업데이트되었습니다.,PAC mis à jour | |||||
No updates found. Please report to GFWList if you have problems with it.,Обновлений не найдено. Сообщите авторам GFWList если у вас возникли проблемы.,未发现更新。如有问题请提交给 GFWList。,未發現更新。如有問題請報告至 GFWList。,お使いのバージョンは最新です。問題がある場合は、GFWList に報告して下さい。,사용 가능한 업데이트를 찾지 못했습니다. 문제가 있다면 GFWList로 전송해주세요.,Aucune mise à jour trouvée. Veuillez signaler à GFWList si vous avez des problèmes concernant. | |||||
Failed to update PAC file ,Не удалось обновить PAC файл,更新 PAC 文件失败,更新 PAC 檔案失敗,PAC の更新に失敗しました。,프록시 자동 구성 파일을 업데이트하는데 실패했습니다.,Impossible de mettre à jour le fichier PAC | |||||
PAC updated,PAC файл обновлен,更新 PAC 成功,更新 PAC 成功,PAC を更新しました。,프록시 자동 구성 파일이 업데이트되었습니다.,PAC mis à jour | |||||
No updates found. Please report to Geosite if you have problems with it.,Обновлений не найдено. Сообщите авторам Geosite если у вас возникли проблемы.,未发现更新。如有问题请提交给 Geosite。,未發現更新。如有問題請報告至 Geosite。,お使いのバージョンは最新です。問題がある場合は、Geosite に報告して下さい。,사용 가능한 업데이트를 찾지 못했습니다. 문제가 있다면 Geosite로 전송해주세요.,Aucune mise à jour trouvée. Veuillez signaler à Geosite si vous avez des problèmes concernant. | |||||
No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode не обнаружен. Попробуйте увеличить изображение или переместить его в центр экрана.,未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置,未發現 QR 碼,嘗試把它放大或移動到靠近熒幕中間的位置,QR コードが見つかりませんでした。コードを大きくするか、画面の中央に移動して下さい。,QR코드를 찾을 수 없습니다. 가운데로 화면을 이동시키거나 확대해보세요.,Aucun QRCode trouvé. Essayez de zoomer ou de le déplacer vers le centre de l'écran. | No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode не обнаружен. Попробуйте увеличить изображение или переместить его в центр экрана.,未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置,未發現 QR 碼,嘗試把它放大或移動到靠近熒幕中間的位置,QR コードが見つかりませんでした。コードを大きくするか、画面の中央に移動して下さい。,QR코드를 찾을 수 없습니다. 가운데로 화면을 이동시키거나 확대해보세요.,Aucun QRCode trouvé. Essayez de zoomer ou de le déplacer vers le centre de l'écran. | ||||
Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. | Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. | ||||
Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification. | Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification. | ||||
@@ -190,25 +190,25 @@ Successfully imported from {0},Успешно импортировано из {0 | |||||
Failed to import. Please check if the link is valid.,,导入失败,请检查链接是否有效。,導入失敗,請檢查鏈接是否有效。,,, | Failed to import. Please check if the link is valid.,,导入失败,请检查链接是否有效。,導入失敗,請檢查鏈接是否有效。,,, | ||||
System Proxy On: ,Системный прокси:,系统代理已启用:,系統 Proxy 已啟用:,システム プロキシが有効:,시스템 프록시 활성화됨: ,Proxy système activé: | System Proxy On: ,Системный прокси:,系统代理已启用:,系統 Proxy 已啟用:,システム プロキシが有効:,시스템 프록시 활성화됨: ,Proxy système activé: | ||||
Running: Port {0},Запущен на порту {0},正在运行:端口 {0},正在執行:連接埠號碼 {0},実行中:ポート {0},실행 중: 포트 {0}번,En cours d'exécution: port {0} | Running: Port {0},Запущен на порту {0},正在运行:端口 {0},正在執行:連接埠號碼 {0},実行中:ポート {0},실행 중: 포트 {0}번,En cours d'exécution: port {0} | ||||
"Unexpected error, shadowsocks will exit. Please report to","Непредвиденная ошибка, пожалуйста сообщите на",非预期错误,Shadowsocks将退出。请提交此错误到,非預期錯誤,Shadowsocks 將結束。請報告此錯誤至,予想外のエラーが発生したため、Shadowsocks を終了します。詳しくは下記までお問い合わせ下さい:,알 수 없는 오류로 Shadowsocks가 종료될 것입니다. 오류를 제보해주세요:,Shadowsocks va quitter en présence d/érreur inattendue. Veuillez signaler à | |||||
"Unsupported operating system, use Windows Vista at least.","Операционная система не поддерживается, минимальные системные требования: Windows Vista или выше.",不支持的操作系统版本,最低需求为Windows Vista。,不支援的作業系統版本,最低需求為 Windows Vista。,お使いの OS はサポートされていません。Windows Vista 以降の OS で実行して下さい。,"지원하지 않는 운영체제입니다, 최소한 Windows Vista가 필요합니다.","Système d'exploitation incompatible, veuillez utiliser Windows Vista ou ultérieure." | |||||
"Unsupported .NET Framework, please update to {0} or later.","Версия .NET Framework не поддерживается, минимальные системные требования: {0} или выше.",当前 .NET Framework 版本过低,请升级至{0}或更新版本。,目前 .NET Framework 版本過低,最低需求為{0}。,お使いの .NET Framework はサポートされていません。{0} 以降のバンジョーをインストールして下さい。,"지원하지 않는 .NET 프레임워크입니다, {0} 또는 상위 버전으로 업데이트해주세요.",".NET Framework incompatible, veuillez mettre à jour vers {0} ou ultérieure." | |||||
"Unexpected error, shadowsocks will exit. Please report to","Непредвиденная ошибка, пожалуйста сообщите на",非预期错误,Shadowsocks将退出。请提交此错误到,非預期錯誤,Shadowsocks 將結束。請報告此錯誤至,予想外のエラーが発生したため、Shadowsocks を終了します。詳しくは下記までお問い合わせ下さい:,알 수 없는 오류로 Shadowsocks가 종료될 것입니다. 오류를 여기로 제보해주세요:,Shadowsocks va quitter en présence d/érreur inattendue. Veuillez signaler à | |||||
"Unsupported operating system, use Windows Vista at least.","Операционная система не поддерживается, минимальные системные требования: Windows Vista или выше.",不支持的操作系统版本,最低需求为Windows Vista。,不支援的作業系統版本,最低需求為 Windows Vista。,お使いの OS はサポートされていません。Windows Vista 以降の OS で実行して下さい。,지원하지 않는 운영체제입니다. 최소 Windows Vista가 필요합니다.,"Système d'exploitation incompatible, veuillez utiliser Windows Vista ou ultérieure." | |||||
"Unsupported .NET Framework, please update to {0} or later.","Версия .NET Framework не поддерживается, минимальные системные требования: {0} или выше.",当前 .NET Framework 版本过低,请升级至{0}或更新版本。,目前 .NET Framework 版本過低,最低需求為{0}。,お使いの .NET Framework はサポートされていません。{0} 以降のバンジョーをインストールして下さい。,지원하지 않는 .NET 프레임워크입니다. {0} 또는 상위 버전으로 업데이트해주세요.,".NET Framework incompatible, veuillez mettre à jour vers {0} ou ultérieure." | |||||
Proxy request failed,Не удалось выполнить запрос,代理请求失败,Proxy 要求失敗,プロキシ要求が失敗しました。,프록시 요청에 실패했습니다.,Échec de la demande de proxy | Proxy request failed,Не удалось выполнить запрос,代理请求失败,Proxy 要求失敗,プロキシ要求が失敗しました。,프록시 요청에 실패했습니다.,Échec de la demande de proxy | ||||
Proxy handshake failed,Не удалось выполнить хэндшейк,代理握手失败,Proxy 交握失敗,プロキシ ハンドシェイクに失敗しました。,프록시 핸드쉐이크에 실패했습니다.,Échec de la prise de contact par proxy | Proxy handshake failed,Не удалось выполнить хэндшейк,代理握手失败,Proxy 交握失敗,プロキシ ハンドシェイクに失敗しました。,프록시 핸드쉐이크에 실패했습니다.,Échec de la prise de contact par proxy | ||||
Register hotkey failed,Не удалось применить настройки горячих клавиш,注册快捷键失败,註冊快速鍵失敗,ホットキーの登錄に失敗しました。,단축키 등록에 실패했습니다.,Échec de l'enregistrement du raccourci clavier | Register hotkey failed,Не удалось применить настройки горячих клавиш,注册快捷键失败,註冊快速鍵失敗,ホットキーの登錄に失敗しました。,단축키 등록에 실패했습니다.,Échec de l'enregistrement du raccourci clavier | ||||
Cannot parse hotkey: {0},Не удалось распознать следующие горячие клавиши: {0},解析快捷键失败: {0},剖析快速鍵失敗: {0},ホットキーを解析できません: {0},단축키를 해석할 수 없습니다: {0},Impossible d'analyser le raccourci clavier: {0} | Cannot parse hotkey: {0},Не удалось распознать следующие горячие клавиши: {0},解析快捷键失败: {0},剖析快速鍵失敗: {0},ホットキーを解析できません: {0},단축키를 해석할 수 없습니다: {0},Impossible d'analyser le raccourci clavier: {0} | ||||
"Timeout is invalid, it should not exceed {0}",Таймаут не может превышать значение {0},超时无效,不应超过 {0},逾時無效,不應超過 {0},タイムアウト値が無効です。{0} 以下の値を指定して下さい。,"올바르지 않은 시간초과(Timeout) 설정입니다, {0}을 초과하지 않아야 합니다.","Le délai d'attente invalide, il ne doit pas dépasser {0}" | |||||
"Timeout is invalid, it should not exceed {0}",Таймаут не может превышать значение {0},超时无效,不应超过 {0},逾時無效,不應超過 {0},タイムアウト値が無効です。{0} 以下の値を指定して下さい。,올바르지 않은 시간 초과 설정입니다. {0}을 초과하지 않아야 합니다.,"Le délai d'attente invalide, il ne doit pas dépasser {0}" | |||||
Cannot find the plugin program file,Файл плагина не найден,找不到插件程序文件,找不到外掛程式文件,,플러그인 프로그램 파일을 찾을 수 없습니다.,Impossible de trouver le fichier du programme du plugin | Cannot find the plugin program file,Файл плагина не найден,找不到插件程序文件,找不到外掛程式文件,,플러그인 프로그램 파일을 찾을 수 없습니다.,Impossible de trouver le fichier du programme du plugin | ||||
,,,,,, | ,,,,,, | ||||
Operation failure,Операция завершилась неудачей,操作失败,,,작업에 실패했습니다.,Operation failure | Operation failure,Операция завершилась неудачей,操作失败,,,작업에 실패했습니다.,Operation failure | ||||
Auto save failed,Автоматическое сохранение не удалось,自动保存失败,,,자동 저장에 실패했습니다.,Échec de l'enregistrement automatique | Auto save failed,Автоматическое сохранение не удалось,自动保存失败,,,자동 저장에 실패했습니다.,Échec de l'enregistrement automatique | ||||
Whether to discard unconfigured servers,Внесенные изменения будут утеряны,是否丢弃未配置的服务器,,,완전하지 않은 서버 설정 변경 사항을 폐기하시겠습니까?,Eliminer les serveurs non configurés ou non | |||||
"Invalid server address, Cannot automatically save or discard changes",Неверный адрес сервера. Невозможно сохранить или отменить изменения,非法服务器地址,无法自动保存,是否丢弃修改,,,"서버 주소가 올바르지 않아 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Adresse de serveur invalide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Illegal port number format, Cannot automatically save or discard changes",Неверный числовой адрес порта. Невозможно сохранить или отменить изменения,非法端口格式,无法自动保存,是否丢弃修改,,,"포트 번호가 올바르지 않아 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Format de numéro de port illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Password can not be blank, Cannot automatically save or discard changes",Пароль не может быть пустым. Невозможно сохранить или отменить изменения,密码不能为空,无法自动保存,是否丢弃修改,,,"비밀번호가 비어있어 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Le mot de passe ne peut pas être vide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Illegal timeout format, Cannot automatically save or discard changes",Неверный формат таймаута. Невозможно сохранить или отменить изменения,非法超时格式,无法自动保存,是否丢弃修改,,,"시간초과(Timeout) 형식이 올바르지 않아 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Format de délai d'attente illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
Whether to discard unconfigured servers,Внесенные изменения будут утеряны,是否丢弃未配置的服务器,,,구성되지 않은 서버 설정 변경 사항을 폐기하시겠습니까?,Eliminer les serveurs non configurés ou non | |||||
"Invalid server address, Cannot automatically save or discard changes",Неверный адрес сервера. Невозможно сохранить или отменить изменения,非法服务器地址,无法自动保存,是否丢弃修改,,,"서버 주소가 올바르지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Adresse de serveur invalide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Illegal port number format, Cannot automatically save or discard changes",Неверный числовой адрес порта. Невозможно сохранить или отменить изменения,非法端口格式,无法自动保存,是否丢弃修改,,,"포트 번호가 올바르지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Format de numéro de port illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Password can not be blank, Cannot automatically save or discard changes",Пароль не может быть пустым. Невозможно сохранить или отменить изменения,密码不能为空,无法自动保存,是否丢弃修改,,,"비밀번호를 입력하지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Le mot de passe ne peut pas être vide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Illegal timeout format, Cannot automatically save or discard changes",Неверный формат таймаута. Невозможно сохранить или отменить изменения,非法超时格式,无法自动保存,是否丢弃修改,,,시간 초과 형식이 올바르지 않아 자동 저장 할 수 없습니다. 변경 사항을 폐기하시겠습니까?,"Format de délai d'attente illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
,,,,,, | ,,,,,, | ||||
"Error occured when process proxy setting, do you want reset current setting and retry?",Произошла ошибка при обработке настроек. Хотите сбросить текущие настройки и попробовать снова?,处理代理设置时发生错误,是否重置当前代理设置并重试?,,,프록시 설정을 처리하는데에 오류가 발생했습니다. 현재 설정을 폐기하고 다시시도하시겠습니까?,Une erreur s'est produite lors du processus de configuration du proxy. Voulez-vous réinitialiser le paramètre actuel et réessayer? | |||||
"Error occured when process proxy setting, do you want reset current setting and retry?",Произошла ошибка при обработке настроек. Хотите сбросить текущие настройки и попробовать снова?,处理代理设置时发生错误,是否重置当前代理设置并重试?,,,프록시 설정을 처리하는데에 오류가 발생했습니다. 현재 설정을 폐기하고 다시 시도하시겠습니까?,Une erreur s'est produite lors du processus de configuration du proxy. Voulez-vous réinitialiser le paramètre actuel et réessayer? | |||||
"Unrecoverable proxy setting error occured, see log for detail","Произошла серьезная ошибка, подробности можно узнать в журналах",发生不可恢复的代理设置错误,查看日志以取得详情,,,복구 불가능한 프록시 설정 오류가 발생했습니다. 자세한 정보는 로그를 참조하세요.,"Une erreur de paramètre de proxy irrécupérable s'est produite, consultez le journal pour plus de détails" | "Unrecoverable proxy setting error occured, see log for detail","Произошла серьезная ошибка, подробности можно узнать в журналах",发生不可恢复的代理设置错误,查看日志以取得详情,,,복구 불가능한 프록시 설정 오류가 발생했습니다. 자세한 정보는 로그를 참조하세요.,"Une erreur de paramètre de proxy irrécupérable s'est produite, consultez le journal pour plus de détails" | ||||
Auth user can not be blank,Пользователь не может быть пустым,认证用户不能为空,認證用戶不能為空,認証ユーザが指定されていません。,증명 정보의 사용자 이름은 비어있을 수 없습니다.,L'utilisateur d'authentification ne peut pas être vide | |||||
Auth pwd can not be blank,Пароль не может быть пустым,认证密码不能为空,認證口令不能為空,認証パスワードが指定されていません。,증명 정보의 비밀번호는 비어있을 수 없습니다.,Le mot de passe d'authentification ne peut pas être vide | |||||
Auth user can not be blank,Пользователь не может быть пустым,认证用户不能为空,認證用戶不能為空,認証ユーザが指定されていません。,인증 정보의 사용자 이름은 비어있을 수 없습니다.,L'utilisateur d'authentification ne peut pas être vide | |||||
Auth pwd can not be blank,Пароль не может быть пустым,认证密码不能为空,認證口令不能為空,認証パスワードが指定されていません。,인증 정보의 비밀번호는 비어있을 수 없습니다.,Le mot de passe d'authentification ne peut pas être vide |
@@ -29,7 +29,10 @@ namespace Shadowsocks.Model | |||||
public bool portableMode = true; | public bool portableMode = true; | ||||
public bool showPluginOutput; | public bool showPluginOutput; | ||||
public string pacUrl; | public string pacUrl; | ||||
public string gfwListUrl; | |||||
public string geositeUrl; | |||||
public string geositeGroup = "geolocation-!cn"; | |||||
public bool geositeBlacklistMode = true; | |||||
public bool useOnlinePac; | public bool useOnlinePac; | ||||
public bool secureLocalPac = true; | public bool secureLocalPac = true; | ||||
public bool availabilityStatistics; | public bool availabilityStatistics; | ||||
@@ -93,10 +96,11 @@ namespace Shadowsocks.Model | |||||
public static Configuration Load() | public static Configuration Load() | ||||
{ | { | ||||
Configuration config; | |||||
try | try | ||||
{ | { | ||||
string configContent = File.ReadAllText(CONFIG_FILE); | string configContent = File.ReadAllText(CONFIG_FILE); | ||||
Configuration config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||||
config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||||
config.isDefault = false; | config.isDefault = false; | ||||
if (UpdateChecker.Asset.CompareVersion(UpdateChecker.Version, config.version ?? "0") > 0) | if (UpdateChecker.Asset.CompareVersion(UpdateChecker.Version, config.version ?? "0") > 0) | ||||
{ | { | ||||
@@ -124,37 +128,12 @@ namespace Shadowsocks.Model | |||||
//TODO if remote host(server) do not support IPv6 (or DNS resolve AAAA TYPE record) disable IPv6? | //TODO if remote host(server) do not support IPv6 (or DNS resolve AAAA TYPE record) disable IPv6? | ||||
config.proxy.CheckConfig(); | config.proxy.CheckConfig(); | ||||
try | |||||
{ | |||||
config.nLogConfig = NLogConfig.LoadXML(); | |||||
switch (config.nLogConfig.GetLogLevel()) | |||||
{ | |||||
case NLogConfig.LogLevel.Fatal: | |||||
case NLogConfig.LogLevel.Error: | |||||
case NLogConfig.LogLevel.Warn: | |||||
case NLogConfig.LogLevel.Info: | |||||
config.isVerboseLogging = false; | |||||
break; | |||||
case NLogConfig.LogLevel.Debug: | |||||
case NLogConfig.LogLevel.Trace: | |||||
config.isVerboseLogging = true; | |||||
break; | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
// todo: route the error to UI since there is no log file in this scenario | |||||
logger.Error(e, "Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||||
} | |||||
return config; | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
if (!(e is FileNotFoundException)) | if (!(e is FileNotFoundException)) | ||||
logger.LogUsefulException(e); | logger.LogUsefulException(e); | ||||
return new Configuration | |||||
config = new Configuration | |||||
{ | { | ||||
index = 0, | index = 0, | ||||
isDefault = true, | isDefault = true, | ||||
@@ -169,6 +148,31 @@ namespace Shadowsocks.Model | |||||
hotkey = new HotkeyConfig(), | hotkey = new HotkeyConfig(), | ||||
}; | }; | ||||
} | } | ||||
try | |||||
{ | |||||
config.nLogConfig = NLogConfig.LoadXML(); | |||||
switch (config.nLogConfig.GetLogLevel()) | |||||
{ | |||||
case NLogConfig.LogLevel.Fatal: | |||||
case NLogConfig.LogLevel.Error: | |||||
case NLogConfig.LogLevel.Warn: | |||||
case NLogConfig.LogLevel.Info: | |||||
config.isVerboseLogging = false; | |||||
break; | |||||
case NLogConfig.LogLevel.Debug: | |||||
case NLogConfig.LogLevel.Trace: | |||||
config.isVerboseLogging = true; | |||||
break; | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
// todo: route the error to UI since there is no log file in this scenario | |||||
logger.Error(e, "Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||||
} | |||||
return config; | |||||
} | } | ||||
public static void Save(Configuration config) | public static void Save(Configuration config) | ||||
@@ -0,0 +1,753 @@ | |||||
// <auto-generated> | |||||
// Generated by the protocol buffer compiler. DO NOT EDIT! | |||||
// source: geosite.proto | |||||
// </auto-generated> | |||||
#pragma warning disable 1591, 0612, 3021 | |||||
#region Designer generated code | |||||
using pb = global::Google.Protobuf; | |||||
using pbc = global::Google.Protobuf.Collections; | |||||
using pbr = global::Google.Protobuf.Reflection; | |||||
using scg = global::System.Collections.Generic; | |||||
/// <summary>Holder for reflection information generated from geosite.proto</summary> | |||||
public static partial class GeositeReflection { | |||||
#region Descriptor | |||||
/// <summary>File descriptor for geosite.proto</summary> | |||||
public static pbr::FileDescriptor Descriptor { | |||||
get { return descriptor; } | |||||
} | |||||
private static pbr::FileDescriptor descriptor; | |||||
static GeositeReflection() { | |||||
byte[] descriptorData = global::System.Convert.FromBase64String( | |||||
string.Concat( | |||||
"Cg1nZW9zaXRlLnByb3RvIvMBCgxEb21haW5PYmplY3QSIAoEdHlwZRgBIAEo", | |||||
"DjISLkRvbWFpbk9iamVjdC5UeXBlEg0KBXZhbHVlGAIgASgJEioKCWF0dHJp", | |||||
"YnV0ZRgDIAMoCzIXLkRvbWFpbk9iamVjdC5BdHRyaWJ1dGUaUgoJQXR0cmli", | |||||
"dXRlEgsKA2tleRgBIAEoCRIUCgpib29sX3ZhbHVlGAIgASgISAASEwoJaW50", | |||||
"X3ZhbHVlGAMgASgDSABCDQoLdHlwZWRfdmFsdWUiMgoEVHlwZRIJCgVQbGFp", | |||||
"bhAAEgkKBVJlZ2V4EAESCgoGRG9tYWluEAISCAoERnVsbBADIj0KB0dlb3Np", | |||||
"dGUSEgoKZ3JvdXBfbmFtZRgBIAEoCRIeCgdkb21haW5zGAIgAygLMg0uRG9t", | |||||
"YWluT2JqZWN0IigKC0dlb3NpdGVMaXN0EhkKB2VudHJpZXMYASADKAsyCC5H", | |||||
"ZW9zaXRlYgZwcm90bzM=")); | |||||
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, | |||||
new pbr::FileDescriptor[] { }, | |||||
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { | |||||
new pbr::GeneratedClrTypeInfo(typeof(global::DomainObject), global::DomainObject.Parser, new[]{ "Type", "Value", "Attribute" }, null, new[]{ typeof(global::DomainObject.Types.Type) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::DomainObject.Types.Attribute), global::DomainObject.Types.Attribute.Parser, new[]{ "Key", "BoolValue", "IntValue" }, new[]{ "TypedValue" }, null, null, null)}), | |||||
new pbr::GeneratedClrTypeInfo(typeof(global::Geosite), global::Geosite.Parser, new[]{ "GroupName", "Domains" }, null, null, null, null), | |||||
new pbr::GeneratedClrTypeInfo(typeof(global::GeositeList), global::GeositeList.Parser, new[]{ "Entries" }, null, null, null, null) | |||||
})); | |||||
} | |||||
#endregion | |||||
} | |||||
#region Messages | |||||
/// <summary> | |||||
/// DomainObject for routing decision. | |||||
/// </summary> | |||||
public sealed partial class DomainObject : pb::IMessage<DomainObject> { | |||||
private static readonly pb::MessageParser<DomainObject> _parser = new pb::MessageParser<DomainObject>(() => new DomainObject()); | |||||
private pb::UnknownFieldSet _unknownFields; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pb::MessageParser<DomainObject> Parser { get { return _parser; } } | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pbr::MessageDescriptor Descriptor { | |||||
get { return global::GeositeReflection.Descriptor.MessageTypes[0]; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
pbr::MessageDescriptor pb::IMessage.Descriptor { | |||||
get { return Descriptor; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public DomainObject() { | |||||
OnConstruction(); | |||||
} | |||||
partial void OnConstruction(); | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public DomainObject(DomainObject other) : this() { | |||||
type_ = other.type_; | |||||
value_ = other.value_; | |||||
attribute_ = other.attribute_.Clone(); | |||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public DomainObject Clone() { | |||||
return new DomainObject(this); | |||||
} | |||||
/// <summary>Field number for the "type" field.</summary> | |||||
public const int TypeFieldNumber = 1; | |||||
private global::DomainObject.Types.Type type_ = global::DomainObject.Types.Type.Plain; | |||||
/// <summary> | |||||
/// DomainObject matching type. | |||||
/// </summary> | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public global::DomainObject.Types.Type Type { | |||||
get { return type_; } | |||||
set { | |||||
type_ = value; | |||||
} | |||||
} | |||||
/// <summary>Field number for the "value" field.</summary> | |||||
public const int ValueFieldNumber = 2; | |||||
private string value_ = ""; | |||||
/// <summary> | |||||
/// DomainObject value. | |||||
/// </summary> | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public string Value { | |||||
get { return value_; } | |||||
set { | |||||
value_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); | |||||
} | |||||
} | |||||
/// <summary>Field number for the "attribute" field.</summary> | |||||
public const int AttributeFieldNumber = 3; | |||||
private static readonly pb::FieldCodec<global::DomainObject.Types.Attribute> _repeated_attribute_codec | |||||
= pb::FieldCodec.ForMessage(26, global::DomainObject.Types.Attribute.Parser); | |||||
private readonly pbc::RepeatedField<global::DomainObject.Types.Attribute> attribute_ = new pbc::RepeatedField<global::DomainObject.Types.Attribute>(); | |||||
/// <summary> | |||||
/// Attributes of this domain. May be used for filtering. | |||||
/// </summary> | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public pbc::RepeatedField<global::DomainObject.Types.Attribute> Attribute { | |||||
get { return attribute_; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override bool Equals(object other) { | |||||
return Equals(other as DomainObject); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public bool Equals(DomainObject other) { | |||||
if (ReferenceEquals(other, null)) { | |||||
return false; | |||||
} | |||||
if (ReferenceEquals(other, this)) { | |||||
return true; | |||||
} | |||||
if (Type != other.Type) return false; | |||||
if (Value != other.Value) return false; | |||||
if(!attribute_.Equals(other.attribute_)) return false; | |||||
return Equals(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override int GetHashCode() { | |||||
int hash = 1; | |||||
if (Type != global::DomainObject.Types.Type.Plain) hash ^= Type.GetHashCode(); | |||||
if (Value.Length != 0) hash ^= Value.GetHashCode(); | |||||
hash ^= attribute_.GetHashCode(); | |||||
if (_unknownFields != null) { | |||||
hash ^= _unknownFields.GetHashCode(); | |||||
} | |||||
return hash; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override string ToString() { | |||||
return pb::JsonFormatter.ToDiagnosticString(this); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void WriteTo(pb::CodedOutputStream output) { | |||||
if (Type != global::DomainObject.Types.Type.Plain) { | |||||
output.WriteRawTag(8); | |||||
output.WriteEnum((int) Type); | |||||
} | |||||
if (Value.Length != 0) { | |||||
output.WriteRawTag(18); | |||||
output.WriteString(Value); | |||||
} | |||||
attribute_.WriteTo(output, _repeated_attribute_codec); | |||||
if (_unknownFields != null) { | |||||
_unknownFields.WriteTo(output); | |||||
} | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public int CalculateSize() { | |||||
int size = 0; | |||||
if (Type != global::DomainObject.Types.Type.Plain) { | |||||
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type); | |||||
} | |||||
if (Value.Length != 0) { | |||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Value); | |||||
} | |||||
size += attribute_.CalculateSize(_repeated_attribute_codec); | |||||
if (_unknownFields != null) { | |||||
size += _unknownFields.CalculateSize(); | |||||
} | |||||
return size; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(DomainObject other) { | |||||
if (other == null) { | |||||
return; | |||||
} | |||||
if (other.Type != global::DomainObject.Types.Type.Plain) { | |||||
Type = other.Type; | |||||
} | |||||
if (other.Value.Length != 0) { | |||||
Value = other.Value; | |||||
} | |||||
attribute_.Add(other.attribute_); | |||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(pb::CodedInputStream input) { | |||||
uint tag; | |||||
while ((tag = input.ReadTag()) != 0) { | |||||
switch(tag) { | |||||
default: | |||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); | |||||
break; | |||||
case 8: { | |||||
Type = (global::DomainObject.Types.Type) input.ReadEnum(); | |||||
break; | |||||
} | |||||
case 18: { | |||||
Value = input.ReadString(); | |||||
break; | |||||
} | |||||
case 26: { | |||||
attribute_.AddEntriesFrom(input, _repeated_attribute_codec); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#region Nested types | |||||
/// <summary>Container for nested types declared in the DomainObject message type.</summary> | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static partial class Types { | |||||
/// <summary> | |||||
/// Type of domain value. | |||||
/// </summary> | |||||
public enum Type { | |||||
/// <summary> | |||||
/// The value is used as is. | |||||
/// </summary> | |||||
[pbr::OriginalName("Plain")] Plain = 0, | |||||
/// <summary> | |||||
/// The value is used as a regular expression. | |||||
/// </summary> | |||||
[pbr::OriginalName("Regex")] Regex = 1, | |||||
/// <summary> | |||||
/// The value is a root domain. | |||||
/// </summary> | |||||
[pbr::OriginalName("Domain")] Domain = 2, | |||||
/// <summary> | |||||
/// The value is a domain. | |||||
/// </summary> | |||||
[pbr::OriginalName("Full")] Full = 3, | |||||
} | |||||
public sealed partial class Attribute : pb::IMessage<Attribute> { | |||||
private static readonly pb::MessageParser<Attribute> _parser = new pb::MessageParser<Attribute>(() => new Attribute()); | |||||
private pb::UnknownFieldSet _unknownFields; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pb::MessageParser<Attribute> Parser { get { return _parser; } } | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pbr::MessageDescriptor Descriptor { | |||||
get { return global::DomainObject.Descriptor.NestedTypes[0]; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
pbr::MessageDescriptor pb::IMessage.Descriptor { | |||||
get { return Descriptor; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public Attribute() { | |||||
OnConstruction(); | |||||
} | |||||
partial void OnConstruction(); | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public Attribute(Attribute other) : this() { | |||||
key_ = other.key_; | |||||
switch (other.TypedValueCase) { | |||||
case TypedValueOneofCase.BoolValue: | |||||
BoolValue = other.BoolValue; | |||||
break; | |||||
case TypedValueOneofCase.IntValue: | |||||
IntValue = other.IntValue; | |||||
break; | |||||
} | |||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public Attribute Clone() { | |||||
return new Attribute(this); | |||||
} | |||||
/// <summary>Field number for the "key" field.</summary> | |||||
public const int KeyFieldNumber = 1; | |||||
private string key_ = ""; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public string Key { | |||||
get { return key_; } | |||||
set { | |||||
key_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); | |||||
} | |||||
} | |||||
/// <summary>Field number for the "bool_value" field.</summary> | |||||
public const int BoolValueFieldNumber = 2; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public bool BoolValue { | |||||
get { return typedValueCase_ == TypedValueOneofCase.BoolValue ? (bool) typedValue_ : false; } | |||||
set { | |||||
typedValue_ = value; | |||||
typedValueCase_ = TypedValueOneofCase.BoolValue; | |||||
} | |||||
} | |||||
/// <summary>Field number for the "int_value" field.</summary> | |||||
public const int IntValueFieldNumber = 3; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public long IntValue { | |||||
get { return typedValueCase_ == TypedValueOneofCase.IntValue ? (long) typedValue_ : 0L; } | |||||
set { | |||||
typedValue_ = value; | |||||
typedValueCase_ = TypedValueOneofCase.IntValue; | |||||
} | |||||
} | |||||
private object typedValue_; | |||||
/// <summary>Enum of possible cases for the "typed_value" oneof.</summary> | |||||
public enum TypedValueOneofCase { | |||||
None = 0, | |||||
BoolValue = 2, | |||||
IntValue = 3, | |||||
} | |||||
private TypedValueOneofCase typedValueCase_ = TypedValueOneofCase.None; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public TypedValueOneofCase TypedValueCase { | |||||
get { return typedValueCase_; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void ClearTypedValue() { | |||||
typedValueCase_ = TypedValueOneofCase.None; | |||||
typedValue_ = null; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override bool Equals(object other) { | |||||
return Equals(other as Attribute); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public bool Equals(Attribute other) { | |||||
if (ReferenceEquals(other, null)) { | |||||
return false; | |||||
} | |||||
if (ReferenceEquals(other, this)) { | |||||
return true; | |||||
} | |||||
if (Key != other.Key) return false; | |||||
if (BoolValue != other.BoolValue) return false; | |||||
if (IntValue != other.IntValue) return false; | |||||
if (TypedValueCase != other.TypedValueCase) return false; | |||||
return Equals(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override int GetHashCode() { | |||||
int hash = 1; | |||||
if (Key.Length != 0) hash ^= Key.GetHashCode(); | |||||
if (typedValueCase_ == TypedValueOneofCase.BoolValue) hash ^= BoolValue.GetHashCode(); | |||||
if (typedValueCase_ == TypedValueOneofCase.IntValue) hash ^= IntValue.GetHashCode(); | |||||
hash ^= (int) typedValueCase_; | |||||
if (_unknownFields != null) { | |||||
hash ^= _unknownFields.GetHashCode(); | |||||
} | |||||
return hash; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override string ToString() { | |||||
return pb::JsonFormatter.ToDiagnosticString(this); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void WriteTo(pb::CodedOutputStream output) { | |||||
if (Key.Length != 0) { | |||||
output.WriteRawTag(10); | |||||
output.WriteString(Key); | |||||
} | |||||
if (typedValueCase_ == TypedValueOneofCase.BoolValue) { | |||||
output.WriteRawTag(16); | |||||
output.WriteBool(BoolValue); | |||||
} | |||||
if (typedValueCase_ == TypedValueOneofCase.IntValue) { | |||||
output.WriteRawTag(24); | |||||
output.WriteInt64(IntValue); | |||||
} | |||||
if (_unknownFields != null) { | |||||
_unknownFields.WriteTo(output); | |||||
} | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public int CalculateSize() { | |||||
int size = 0; | |||||
if (Key.Length != 0) { | |||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Key); | |||||
} | |||||
if (typedValueCase_ == TypedValueOneofCase.BoolValue) { | |||||
size += 1 + 1; | |||||
} | |||||
if (typedValueCase_ == TypedValueOneofCase.IntValue) { | |||||
size += 1 + pb::CodedOutputStream.ComputeInt64Size(IntValue); | |||||
} | |||||
if (_unknownFields != null) { | |||||
size += _unknownFields.CalculateSize(); | |||||
} | |||||
return size; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(Attribute other) { | |||||
if (other == null) { | |||||
return; | |||||
} | |||||
if (other.Key.Length != 0) { | |||||
Key = other.Key; | |||||
} | |||||
switch (other.TypedValueCase) { | |||||
case TypedValueOneofCase.BoolValue: | |||||
BoolValue = other.BoolValue; | |||||
break; | |||||
case TypedValueOneofCase.IntValue: | |||||
IntValue = other.IntValue; | |||||
break; | |||||
} | |||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(pb::CodedInputStream input) { | |||||
uint tag; | |||||
while ((tag = input.ReadTag()) != 0) { | |||||
switch(tag) { | |||||
default: | |||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); | |||||
break; | |||||
case 10: { | |||||
Key = input.ReadString(); | |||||
break; | |||||
} | |||||
case 16: { | |||||
BoolValue = input.ReadBool(); | |||||
break; | |||||
} | |||||
case 24: { | |||||
IntValue = input.ReadInt64(); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endregion | |||||
} | |||||
public sealed partial class Geosite : pb::IMessage<Geosite> { | |||||
private static readonly pb::MessageParser<Geosite> _parser = new pb::MessageParser<Geosite>(() => new Geosite()); | |||||
private pb::UnknownFieldSet _unknownFields; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pb::MessageParser<Geosite> Parser { get { return _parser; } } | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pbr::MessageDescriptor Descriptor { | |||||
get { return global::GeositeReflection.Descriptor.MessageTypes[1]; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
pbr::MessageDescriptor pb::IMessage.Descriptor { | |||||
get { return Descriptor; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public Geosite() { | |||||
OnConstruction(); | |||||
} | |||||
partial void OnConstruction(); | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public Geosite(Geosite other) : this() { | |||||
groupName_ = other.groupName_; | |||||
domains_ = other.domains_.Clone(); | |||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public Geosite Clone() { | |||||
return new Geosite(this); | |||||
} | |||||
/// <summary>Field number for the "group_name" field.</summary> | |||||
public const int GroupNameFieldNumber = 1; | |||||
private string groupName_ = ""; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public string GroupName { | |||||
get { return groupName_; } | |||||
set { | |||||
groupName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); | |||||
} | |||||
} | |||||
/// <summary>Field number for the "domains" field.</summary> | |||||
public const int DomainsFieldNumber = 2; | |||||
private static readonly pb::FieldCodec<global::DomainObject> _repeated_domains_codec | |||||
= pb::FieldCodec.ForMessage(18, global::DomainObject.Parser); | |||||
private readonly pbc::RepeatedField<global::DomainObject> domains_ = new pbc::RepeatedField<global::DomainObject>(); | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public pbc::RepeatedField<global::DomainObject> Domains { | |||||
get { return domains_; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override bool Equals(object other) { | |||||
return Equals(other as Geosite); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public bool Equals(Geosite other) { | |||||
if (ReferenceEquals(other, null)) { | |||||
return false; | |||||
} | |||||
if (ReferenceEquals(other, this)) { | |||||
return true; | |||||
} | |||||
if (GroupName != other.GroupName) return false; | |||||
if(!domains_.Equals(other.domains_)) return false; | |||||
return Equals(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override int GetHashCode() { | |||||
int hash = 1; | |||||
if (GroupName.Length != 0) hash ^= GroupName.GetHashCode(); | |||||
hash ^= domains_.GetHashCode(); | |||||
if (_unknownFields != null) { | |||||
hash ^= _unknownFields.GetHashCode(); | |||||
} | |||||
return hash; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override string ToString() { | |||||
return pb::JsonFormatter.ToDiagnosticString(this); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void WriteTo(pb::CodedOutputStream output) { | |||||
if (GroupName.Length != 0) { | |||||
output.WriteRawTag(10); | |||||
output.WriteString(GroupName); | |||||
} | |||||
domains_.WriteTo(output, _repeated_domains_codec); | |||||
if (_unknownFields != null) { | |||||
_unknownFields.WriteTo(output); | |||||
} | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public int CalculateSize() { | |||||
int size = 0; | |||||
if (GroupName.Length != 0) { | |||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(GroupName); | |||||
} | |||||
size += domains_.CalculateSize(_repeated_domains_codec); | |||||
if (_unknownFields != null) { | |||||
size += _unknownFields.CalculateSize(); | |||||
} | |||||
return size; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(Geosite other) { | |||||
if (other == null) { | |||||
return; | |||||
} | |||||
if (other.GroupName.Length != 0) { | |||||
GroupName = other.GroupName; | |||||
} | |||||
domains_.Add(other.domains_); | |||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(pb::CodedInputStream input) { | |||||
uint tag; | |||||
while ((tag = input.ReadTag()) != 0) { | |||||
switch(tag) { | |||||
default: | |||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); | |||||
break; | |||||
case 10: { | |||||
GroupName = input.ReadString(); | |||||
break; | |||||
} | |||||
case 18: { | |||||
domains_.AddEntriesFrom(input, _repeated_domains_codec); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
public sealed partial class GeositeList : pb::IMessage<GeositeList> { | |||||
private static readonly pb::MessageParser<GeositeList> _parser = new pb::MessageParser<GeositeList>(() => new GeositeList()); | |||||
private pb::UnknownFieldSet _unknownFields; | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pb::MessageParser<GeositeList> Parser { get { return _parser; } } | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public static pbr::MessageDescriptor Descriptor { | |||||
get { return global::GeositeReflection.Descriptor.MessageTypes[2]; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
pbr::MessageDescriptor pb::IMessage.Descriptor { | |||||
get { return Descriptor; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public GeositeList() { | |||||
OnConstruction(); | |||||
} | |||||
partial void OnConstruction(); | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public GeositeList(GeositeList other) : this() { | |||||
entries_ = other.entries_.Clone(); | |||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public GeositeList Clone() { | |||||
return new GeositeList(this); | |||||
} | |||||
/// <summary>Field number for the "entries" field.</summary> | |||||
public const int EntriesFieldNumber = 1; | |||||
private static readonly pb::FieldCodec<global::Geosite> _repeated_entries_codec | |||||
= pb::FieldCodec.ForMessage(10, global::Geosite.Parser); | |||||
private readonly pbc::RepeatedField<global::Geosite> entries_ = new pbc::RepeatedField<global::Geosite>(); | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public pbc::RepeatedField<global::Geosite> Entries { | |||||
get { return entries_; } | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override bool Equals(object other) { | |||||
return Equals(other as GeositeList); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public bool Equals(GeositeList other) { | |||||
if (ReferenceEquals(other, null)) { | |||||
return false; | |||||
} | |||||
if (ReferenceEquals(other, this)) { | |||||
return true; | |||||
} | |||||
if(!entries_.Equals(other.entries_)) return false; | |||||
return Equals(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override int GetHashCode() { | |||||
int hash = 1; | |||||
hash ^= entries_.GetHashCode(); | |||||
if (_unknownFields != null) { | |||||
hash ^= _unknownFields.GetHashCode(); | |||||
} | |||||
return hash; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public override string ToString() { | |||||
return pb::JsonFormatter.ToDiagnosticString(this); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void WriteTo(pb::CodedOutputStream output) { | |||||
entries_.WriteTo(output, _repeated_entries_codec); | |||||
if (_unknownFields != null) { | |||||
_unknownFields.WriteTo(output); | |||||
} | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public int CalculateSize() { | |||||
int size = 0; | |||||
size += entries_.CalculateSize(_repeated_entries_codec); | |||||
if (_unknownFields != null) { | |||||
size += _unknownFields.CalculateSize(); | |||||
} | |||||
return size; | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(GeositeList other) { | |||||
if (other == null) { | |||||
return; | |||||
} | |||||
entries_.Add(other.entries_); | |||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); | |||||
} | |||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute] | |||||
public void MergeFrom(pb::CodedInputStream input) { | |||||
uint tag; | |||||
while ((tag = input.ReadTag()) != 0) { | |||||
switch(tag) { | |||||
default: | |||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); | |||||
break; | |||||
case 10: { | |||||
entries_.AddEntriesFrom(input, _repeated_entries_codec); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endregion | |||||
#endregion Designer generated code |
@@ -0,0 +1,43 @@ | |||||
syntax = "proto3"; | |||||
// DomainObject for routing decision. | |||||
message DomainObject { | |||||
// Type of domain value. | |||||
enum Type { | |||||
// The value is used as is. | |||||
Plain = 0; | |||||
// The value is used as a regular expression. | |||||
Regex = 1; | |||||
// The value is a root domain. | |||||
Domain = 2; | |||||
// The value is a domain. | |||||
Full = 3; | |||||
} | |||||
// DomainObject matching type. | |||||
Type type = 1; | |||||
// DomainObject value. | |||||
string value = 2; | |||||
message Attribute { | |||||
string key = 1; | |||||
oneof typed_value { | |||||
bool bool_value = 2; | |||||
int64 int_value = 3; | |||||
} | |||||
} | |||||
// Attributes of this domain. May be used for filtering. | |||||
repeated Attribute attribute = 3; | |||||
} | |||||
message Geosite { | |||||
string group_name = 1; | |||||
repeated DomainObject domains = 2; | |||||
} | |||||
message GeositeList{ | |||||
repeated Geosite entries = 1; | |||||
} |
@@ -85,51 +85,29 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找类似 var __USERRULES__ = []; | |||||
///var __RULES__ = [ | |||||
/// "|http://85.17.73.31/", | |||||
/// "||agnesb.fr", | |||||
/// "||akiba-web.com", | |||||
/// "||altrec.com", | |||||
/// "||angela-merkel.de", | |||||
/// "||angola.org", | |||||
/// "||apartmentratings.com", | |||||
/// "||apartments.com", | |||||
/// "||arena.taipei", | |||||
/// "||asianspiss.com", | |||||
/// "||assimp.org", | |||||
/// "||athenaeizou.com", | |||||
/// "||azubu.tv", | |||||
/// "||bankmobilevibe.com", | |||||
/// "||banorte.com", | |||||
/// "||bash-hackers.org", | |||||
/// "||beeg.com", | |||||
/// "||global.bing.com", | |||||
/// "||bloombergview.com", | |||||
/// " [字符串的其余部分被截断]"; 的本地化字符串。 | |||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// </summary> | /// </summary> | ||||
internal static string default_abp_rule { | |||||
internal static byte[] dlc_dat { | |||||
get { | get { | ||||
return ResourceManager.GetString("default_abp_rule", resourceCulture); | |||||
object obj = ResourceManager.GetObject("dlc_dat", resourceCulture); | |||||
return ((byte[])(obj)); | |||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找类似 en,ru-RU,zh-CN,zh-TW,ja | |||||
///#Restart program to apply translation,,,, | |||||
///#This is comment line,,,, | |||||
///#Always keep language name at head of file,,,, | |||||
///#Language name is output in log,,,, | |||||
///"#You can find it by search ""Current language is:""",,,, | |||||
///#Please use UTF-8 with BOM encoding so we can edit it in Excel,,,, | |||||
///,,,, | |||||
///Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks | |||||
///,,,, | |||||
///#Menu,,,, | |||||
///,,,, | |||||
///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ | |||||
///Disable,Отключен,禁用,禁用,無効 | |||||
///PAC,Сценарий настройки (PAC),PA [字符串的其余部分被截断]"; 的本地化字符串。 | |||||
/// 查找类似 en,ru-RU,zh-CN,zh-TW,ja,ko,fr | |||||
///#Restart program to apply translation,,,,,, | |||||
///#This is comment line,,,,,, | |||||
///#Always keep language name at head of file,,,,,, | |||||
///#Language name is output in log,,,,,, | |||||
///"#You can find it by search ""Current language is:""",,,,,, | |||||
///#Please use UTF-8 with BOM encoding so we can edit it in Excel,,,,,, | |||||
///,,,,,, | |||||
///Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks | |||||
///,,,,,, | |||||
///#Menu,,,,,, | |||||
///,,,,,, | |||||
///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,P [字符串的其余部分被截断]"; 的本地化字符串。 | |||||
/// </summary> | /// </summary> | ||||
internal static string i18n_csv { | internal static string i18n_csv { | ||||
get { | get { | ||||
@@ -121,8 +121,8 @@ | |||||
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Data\abp.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312</value> | <value>..\Data\abp.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312</value> | ||||
</data> | </data> | ||||
<data name="default_abp_rule" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||||
<value>..\Data\default-abp-rule.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | |||||
<data name="dlc_dat" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||||
<value>..\data\dlc.dat;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</data> | </data> | ||||
<data name="i18n_csv" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="i18n_csv" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Data\i18n.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | <value>..\Data\i18n.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | ||||
@@ -48,7 +48,7 @@ namespace Shadowsocks.View | |||||
private ToolStripMenuItem localPACItem; | private ToolStripMenuItem localPACItem; | ||||
private ToolStripMenuItem onlinePACItem; | private ToolStripMenuItem onlinePACItem; | ||||
private ToolStripMenuItem editLocalPACItem; | private ToolStripMenuItem editLocalPACItem; | ||||
private ToolStripMenuItem updateFromGFWListItem; | |||||
private ToolStripMenuItem updateFromGeositeItem; | |||||
private ToolStripMenuItem editGFWUserRuleItem; | private ToolStripMenuItem editGFWUserRuleItem; | ||||
private ToolStripMenuItem editOnlinePACItem; | private ToolStripMenuItem editOnlinePACItem; | ||||
private ToolStripMenuItem secureLocalPacUrlToggleItem; | private ToolStripMenuItem secureLocalPacUrlToggleItem; | ||||
@@ -88,8 +88,8 @@ namespace Shadowsocks.View | |||||
controller.ShowPluginOutputChanged += controller_ShowPluginOutputChanged; | controller.ShowPluginOutputChanged += controller_ShowPluginOutputChanged; | ||||
controller.EnableGlobalChanged += controller_EnableGlobalChanged; | controller.EnableGlobalChanged += controller_EnableGlobalChanged; | ||||
controller.Errored += controller_Errored; | controller.Errored += controller_Errored; | ||||
controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted; | |||||
controller.UpdatePACFromGFWListError += controller_UpdatePACFromGFWListError; | |||||
controller.UpdatePACFromGeositeCompleted += controller_UpdatePACFromGeositeCompleted; | |||||
controller.UpdatePACFromGeositeError += controller_UpdatePACFromGeositeError; | |||||
_notifyIcon = new NotifyIcon(); | _notifyIcon = new NotifyIcon(); | ||||
UpdateTrayIconAndNotifyText(); | UpdateTrayIconAndNotifyText(); | ||||
@@ -310,8 +310,8 @@ namespace Shadowsocks.View | |||||
this.onlinePACItem = CreateToolStripMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)), | this.onlinePACItem = CreateToolStripMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)), | ||||
new ToolStripSeparator(), | new ToolStripSeparator(), | ||||
this.editLocalPACItem = CreateToolStripMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)), | this.editLocalPACItem = CreateToolStripMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)), | ||||
this.updateFromGFWListItem = CreateToolStripMenuItem("Update Local PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)), | |||||
this.editGFWUserRuleItem = CreateToolStripMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)), | |||||
this.updateFromGeositeItem = CreateToolStripMenuItem("Update Local PAC from Geosite", new EventHandler(this.UpdatePACFromGeositeItem_Click)), | |||||
this.editGFWUserRuleItem = CreateToolStripMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_Click)), | |||||
this.secureLocalPacUrlToggleItem = CreateToolStripMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)), | this.secureLocalPacUrlToggleItem = CreateToolStripMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)), | ||||
CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), | CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), | ||||
this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), | this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), | ||||
@@ -390,17 +390,17 @@ namespace Shadowsocks.View | |||||
_notifyIcon.ShowBalloonTip(timeout); | _notifyIcon.ShowBalloonTip(timeout); | ||||
} | } | ||||
void controller_UpdatePACFromGFWListError(object sender, System.IO.ErrorEventArgs e) | |||||
void controller_UpdatePACFromGeositeError(object sender, System.IO.ErrorEventArgs e) | |||||
{ | { | ||||
ShowBalloonTip(I18N.GetString("Failed to update PAC file"), e.GetException().Message, ToolTipIcon.Error, 5000); | ShowBalloonTip(I18N.GetString("Failed to update PAC file"), e.GetException().Message, ToolTipIcon.Error, 5000); | ||||
logger.LogUsefulException(e.GetException()); | logger.LogUsefulException(e.GetException()); | ||||
} | } | ||||
void controller_UpdatePACFromGFWListCompleted(object sender, GFWListUpdater.ResultEventArgs e) | |||||
void controller_UpdatePACFromGeositeCompleted(object sender, GeositeResultEventArgs e) | |||||
{ | { | ||||
string result = e.Success | string result = e.Success | ||||
? I18N.GetString("PAC updated") | ? I18N.GetString("PAC updated") | ||||
: I18N.GetString("No updates found. Please report to GFWList if you have problems with it."); | |||||
: I18N.GetString("No updates found. Please report to Geosite if you have problems with it."); | |||||
ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000); | ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000); | ||||
} | } | ||||
@@ -447,7 +447,7 @@ namespace Shadowsocks.View | |||||
VerboseLoggingToggleItem.Checked = config.isVerboseLogging; | VerboseLoggingToggleItem.Checked = config.isVerboseLogging; | ||||
ShowPluginOutputToggleItem.Checked = config.showPluginOutput; | ShowPluginOutputToggleItem.Checked = config.showPluginOutput; | ||||
AutoStartupItem.Checked = AutoStartup.Check(); | AutoStartupItem.Checked = AutoStartup.Check(); | ||||
ProtocolHandlerItem.Checked = ProtocolHandler.Check(); | |||||
ProtocolHandlerItem.Checked = ProtocolHandler.Check(); | |||||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | ||||
localPACItem.Checked = !onlinePACItem.Checked; | localPACItem.Checked = !onlinePACItem.Checked; | ||||
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | ||||
@@ -705,12 +705,12 @@ namespace Shadowsocks.View | |||||
controller.TouchPACFile(); | controller.TouchPACFile(); | ||||
} | } | ||||
private void UpdatePACFromGFWListItem_Click(object sender, EventArgs e) | |||||
private void UpdatePACFromGeositeItem_Click(object sender, EventArgs e) | |||||
{ | { | ||||
controller.UpdatePACFromGFWList(); | |||||
controller.UpdatePACFromGeosite(); | |||||
} | } | ||||
private void EditUserRuleFileForGFWListItem_Click(object sender, EventArgs e) | |||||
private void EditUserRuleFileForGeositeItem_Click(object sender, EventArgs e) | |||||
{ | { | ||||
controller.TouchUserRuleFile(); | controller.TouchUserRuleFile(); | ||||
} | } | ||||
@@ -867,16 +867,16 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
MessageBox.Show(I18N.GetString("Failed to update registry")); | MessageBox.Show(I18N.GetString("Failed to update registry")); | ||||
} | } | ||||
LoadCurrentConfiguration(); | |||||
LoadCurrentConfiguration(); | |||||
} | } | ||||
private void ProtocolHandlerItem_Click(object sender, EventArgs e) | |||||
{ | |||||
ProtocolHandlerItem.Checked = !ProtocolHandlerItem.Checked; | |||||
if (!ProtocolHandler.Set(ProtocolHandlerItem.Checked)) | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Failed to update registry")); | |||||
} | |||||
LoadCurrentConfiguration(); | |||||
private void ProtocolHandlerItem_Click(object sender, EventArgs e) | |||||
{ | |||||
ProtocolHandlerItem.Checked = !ProtocolHandlerItem.Checked; | |||||
if (!ProtocolHandler.Set(ProtocolHandlerItem.Checked)) | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Failed to update registry")); | |||||
} | |||||
LoadCurrentConfiguration(); | |||||
} | } | ||||
private void LocalPACItem_Click(object sender, EventArgs e) | private void LocalPACItem_Click(object sender, EventArgs e) | ||||
@@ -944,14 +944,14 @@ namespace Shadowsocks.View | |||||
if (this.localPACItem.Checked) | if (this.localPACItem.Checked) | ||||
{ | { | ||||
this.editLocalPACItem.Enabled = true; | this.editLocalPACItem.Enabled = true; | ||||
this.updateFromGFWListItem.Enabled = true; | |||||
this.updateFromGeositeItem.Enabled = true; | |||||
this.editGFWUserRuleItem.Enabled = true; | this.editGFWUserRuleItem.Enabled = true; | ||||
this.editOnlinePACItem.Enabled = false; | this.editOnlinePACItem.Enabled = false; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.editLocalPACItem.Enabled = false; | this.editLocalPACItem.Enabled = false; | ||||
this.updateFromGFWListItem.Enabled = false; | |||||
this.updateFromGeositeItem.Enabled = false; | |||||
this.editGFWUserRuleItem.Enabled = false; | this.editGFWUserRuleItem.Enabled = false; | ||||
this.editOnlinePACItem.Enabled = true; | this.editOnlinePACItem.Enabled = true; | ||||
} | } | ||||
@@ -5,6 +5,7 @@ | |||||
<package id="Costura.Fody" version="3.3.3" targetFramework="net472" /> | <package id="Costura.Fody" version="3.3.3" targetFramework="net472" /> | ||||
<package id="Fody" version="4.2.1" targetFramework="net472" developmentDependency="true" /> | <package id="Fody" version="4.2.1" targetFramework="net472" developmentDependency="true" /> | ||||
<package id="GlobalHotKey" version="1.1.0" targetFramework="net472" /> | <package id="GlobalHotKey" version="1.1.0" targetFramework="net472" /> | ||||
<package id="Google.Protobuf" version="3.11.4" targetFramework="net472" /> | |||||
<package id="NaCl.Core" version="1.2.0" targetFramework="net472" /> | <package id="NaCl.Core" version="1.2.0" targetFramework="net472" /> | ||||
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net472" /> | <package id="Newtonsoft.Json" version="12.0.2" targetFramework="net472" /> | ||||
<package id="NLog" version="4.6.8" targetFramework="net472" /> | <package id="NLog" version="4.6.8" targetFramework="net472" /> | ||||
@@ -79,7 +79,7 @@ | |||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
<PackageReference Include="Google.Protobuf" Version="3.11.4" /> | |||||
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" /> | <PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" /> | ||||
<PackageReference Include="NaCl.Core" Version="1.2.0" /> | <PackageReference Include="NaCl.Core" Version="1.2.0" /> | ||||
@@ -96,7 +96,7 @@ | |||||
<ItemGroup> | <ItemGroup> | ||||
<Resource Include="Data\abp.js" /> | <Resource Include="Data\abp.js" /> | ||||
<Resource Include="Data\default-abp-rule.js" /> | |||||
<Resource Include="Data\dlc.dat" /> | |||||
<Resource Include="Data\i18n.csv" /> | <Resource Include="Data\i18n.csv" /> | ||||
<Resource Include="Data\NLog.config" /> | <Resource Include="Data\NLog.config" /> | ||||
<Resource Include="Data\privoxy.exe.gz" /> | <Resource Include="Data\privoxy.exe.gz" /> | ||||