# 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 | |||
2. PAC mode and global mode | |||
3. [GFWList] and user rules | |||
3. [GeoSite] and user rules | |||
4. Supports HTTP proxy | |||
5. Supports server auto switching | |||
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 | |||
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 | |||
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 | |||
[Build Status]: https://ci.appveyor.com/api/projects/status/tfw57q6eecippsl5/branch/master?svg=true | |||
[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 | |||
[中文说明]: 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/ | |||
@@ -102,7 +102,7 @@ before_build: | |||
# scripts to run after build (working directory and environment changes are persisted from the previous steps) | |||
after_build: | |||
ps: | | |||
- ps: |+ | |||
function CalculateHash($file) | |||
{ | |||
$newLine = "`r`n" | |||
@@ -162,21 +162,7 @@ after_build: | |||
# providers: Local, FTP, WebDeploy, AzureCS, AzureBlob, S3, NuGet, Environment | |||
# 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 | |||
# 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 Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
using Shadowsocks.Util; | |||
using System; | |||
@@ -21,6 +22,7 @@ namespace Shadowsocks.Controller | |||
public const string PAC_FILE = "pac.txt"; | |||
public const string USER_RULE_FILE = "user-rule.txt"; | |||
public const string USER_ABP_FILE = "abp.txt"; | |||
private Configuration config; | |||
FileSystemWatcher PACFileWatcher; | |||
FileSystemWatcher UserRuleFileWatcher; | |||
@@ -28,8 +30,9 @@ namespace Shadowsocks.Controller | |||
public event EventHandler PACFileChanged; | |||
public event EventHandler UserRuleFileChanged; | |||
public PACDaemon() | |||
public PACDaemon(Configuration config) | |||
{ | |||
this.config = config; | |||
TouchPACFile(); | |||
TouchUserRuleFile(); | |||
@@ -42,7 +45,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
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; | |||
} | |||
@@ -58,14 +61,11 @@ namespace Shadowsocks.Controller | |||
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.Linq; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Timers; | |||
using NLog; | |||
using Shadowsocks.Controller.Strategy; | |||
using Shadowsocks.Encryption; | |||
using Shadowsocks.Encryption.AEAD; | |||
@@ -12,16 +14,22 @@ using Shadowsocks.Encryption.Exception; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Proxy; | |||
using Shadowsocks.Util.Sockets; | |||
using static Shadowsocks.Encryption.EncryptorBase; | |||
namespace Shadowsocks.Controller | |||
{ | |||
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 Configuration _config; | |||
private readonly Configuration _config; | |||
public ISet<TCPHandler> Handlers { get; set; } | |||
@@ -47,7 +55,7 @@ namespace Shadowsocks.Controller | |||
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>(); | |||
lock (Handlers) | |||
@@ -85,9 +93,24 @@ namespace Shadowsocks.Controller | |||
{ | |||
if (socket.ProtocolType != ProtocolType.Tcp | |||
|| (length < 2 || firstPacket[0] != 5)) | |||
{ | |||
return false; | |||
} | |||
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>(); | |||
lock (Handlers) | |||
@@ -98,8 +121,12 @@ namespace Shadowsocks.Controller | |||
{ | |||
_lastSweepTime = now; | |||
foreach (TCPHandler handler1 in Handlers) | |||
{ | |||
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | |||
{ | |||
handlersToClose.Add(handler1); | |||
} | |||
} | |||
} | |||
} | |||
foreach (TCPHandler handler1 in handlersToClose) | |||
@@ -128,26 +155,46 @@ namespace Shadowsocks.Controller | |||
} | |||
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 | |||
{ | |||
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; } | |||
@@ -157,7 +204,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
class AsyncSession<T> : AsyncSession | |||
private class AsyncSession<T> : AsyncSession | |||
{ | |||
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 _proxyTimeout; | |||
@@ -191,10 +238,9 @@ namespace Shadowsocks.Controller | |||
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; | |||
// workaround | |||
@@ -220,16 +266,16 @@ namespace Shadowsocks.Controller | |||
private int _totalWrite = 0; | |||
// 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) | |||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||
private readonly byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||
// 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) | |||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private readonly byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private bool _connectionShutdown = false; | |||
private bool _remoteShutdown = false; | |||
@@ -247,11 +293,11 @@ namespace Shadowsocks.Controller | |||
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; | |||
_config = config; | |||
_tcprelay = tcprelay; | |||
_config = config.proxy; | |||
_connection = socket; | |||
_proxyTimeout = config.proxy.proxyTimeout * 1000; | |||
_serverTimeout = config.GetCurrentServer().timeout * 1000; | |||
@@ -264,7 +310,9 @@ namespace Shadowsocks.Controller | |||
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, | |||
_destEndPoint); | |||
if (server == null || server.server == "") | |||
{ | |||
throw new ArgumentException("No server configured"); | |||
} | |||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | |||
decryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | |||
@@ -286,20 +334,31 @@ namespace Shadowsocks.Controller | |||
private void CheckClose() | |||
{ | |||
if (_connectionShutdown && _remoteShutdown) | |||
{ | |||
Close(); | |||
} | |||
} | |||
private void ErrorClose(Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
} | |||
public void Close() | |||
{ | |||
lock (_closeConnLock) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
_closed = true; | |||
} | |||
lock (_tcprelay.Handlers) | |||
{ | |||
_tcprelay.Handlers.Remove(this); | |||
} | |||
OnClosed?.Invoke(this, new SSRelayEventArgs(_server)); | |||
try | |||
{ | |||
_connection.Shutdown(SocketShutdown.Both); | |||
@@ -314,7 +373,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
var remote = _currentRemoteSession.Remote; | |||
IProxy remote = _currentRemoteSession.Remote; | |||
remote.Shutdown(SocketShutdown.Both); | |||
remote.Close(); | |||
} | |||
@@ -327,7 +386,11 @@ namespace Shadowsocks.Controller | |||
private void HandshakeReceive() | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _firstPacketLength; | |||
@@ -344,18 +407,23 @@ namespace Shadowsocks.Controller | |||
HandshakeSendCallback, null); | |||
} | |||
else | |||
{ | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void HandshakeSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_connection.EndSend(ar); | |||
@@ -367,20 +435,23 @@ namespace Shadowsocks.Controller | |||
// +-----+-----+-------+------+----------+----------+ | |||
// 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. | |||
// 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, | |||
HandshakeReceive2Callback, null); | |||
AddressReceiveCallback, null); | |||
} | |||
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 | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
@@ -398,7 +469,7 @@ namespace Shadowsocks.Controller | |||
// +----+-----+-------+------+----------+----------+ | |||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | |||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, | |||
ResponseCallback, null); | |||
ConnectResponseCallback, null); | |||
break; | |||
case CMD_UDP_ASSOC: | |||
ReadAddress(HandleUDPAssociate); | |||
@@ -419,12 +490,11 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void ResponseCallback(IAsyncResult ar) | |||
private void ConnectResponseCallback(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
@@ -434,8 +504,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -474,15 +543,19 @@ namespace Shadowsocks.Controller | |||
private void OnAddressFullyRead(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
var states = (object[])ar.AsyncState; | |||
object[] states = (object[])ar.AsyncState; | |||
int bytesRemain = (int)states[0]; | |||
var onSuccess = (Action)states[1]; | |||
Action onSuccess = (Action)states[1]; | |||
if (bytesRead >= bytesRemain) | |||
{ | |||
@@ -529,8 +602,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -558,7 +630,11 @@ namespace Shadowsocks.Controller | |||
private void ReadAll(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
if (ar.AsyncState != null) | |||
@@ -576,13 +652,14 @@ namespace Shadowsocks.Controller | |||
ReadAll, null); | |||
} | |||
else | |||
{ | |||
Close(); | |||
} | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -627,9 +704,9 @@ namespace Shadowsocks.Controller | |||
serverEP = pluginEP; | |||
remote = new DirectConnect(); | |||
} | |||
else if (_config.proxy.useProxy) | |||
else if (_config.useProxy) | |||
{ | |||
switch (_config.proxy.proxyType) | |||
switch (_config.proxyType) | |||
{ | |||
case ProxyConfig.PROXY_SOCKS5: | |||
remote = new Socks5Proxy(); | |||
@@ -640,14 +717,14 @@ namespace Shadowsocks.Controller | |||
default: | |||
throw new NotSupportedException("Unknown forward proxy."); | |||
} | |||
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort); | |||
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort); | |||
} | |||
else | |||
{ | |||
remote = new DirectConnect(); | |||
} | |||
var session = new AsyncSession(remote); | |||
AsyncSession session = new AsyncSession(remote); | |||
lock (_closeConnLock) | |||
{ | |||
if (_closed) | |||
@@ -675,14 +752,13 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void ProxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | |||
{ | |||
var timer = (ProxyTimer)sender; | |||
ProxyTimer timer = (ProxyTimer)sender; | |||
timer.Elapsed -= ProxyConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
@@ -692,7 +768,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
return; | |||
} | |||
var proxy = timer.Session.Remote; | |||
IProxy proxy = timer.Session.Remote; | |||
Logger.Info($"Proxy {proxy.ProxyEndPoint} timed out"); | |||
proxy.Close(); | |||
@@ -707,15 +783,15 @@ namespace Shadowsocks.Controller | |||
} | |||
try | |||
{ | |||
var session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||
AsyncSession<ProxyTimer> session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||
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.Enabled = false; | |||
timer.Dispose(); | |||
var remote = session.Remote; | |||
IProxy remote = session.Remote; | |||
// Complete the connection. | |||
remote.EndConnectProxy(ar); | |||
@@ -737,9 +813,9 @@ namespace Shadowsocks.Controller | |||
_destConnected = false; | |||
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. | |||
@@ -751,14 +827,13 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void DestConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | |||
{ | |||
var timer = (ServerTimer)sender; | |||
ServerTimer timer = (ServerTimer)sender; | |||
timer.Elapsed -= DestConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
@@ -768,10 +843,9 @@ namespace Shadowsocks.Controller | |||
return; | |||
} | |||
var session = timer.Session; | |||
AsyncSession session = timer.Session; | |||
Server server = timer.Server; | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.SetFailure(server); | |||
OnFailed?.Invoke(this, new SSRelayEventArgs(_server)); | |||
Logger.Info($"{server.FriendlyName()} timed out"); | |||
session.Remote.Close(); | |||
Close(); | |||
@@ -779,17 +853,21 @@ namespace Shadowsocks.Controller | |||
private void ConnectCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||
AsyncSession<ServerTimer> session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||
ServerTimer timer = session.State; | |||
_server = timer.Server; | |||
timer.Elapsed -= DestConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
var remote = session.Remote; | |||
IProxy remote = session.Remote; | |||
// Complete the connection. | |||
remote.EndConnectDest(ar); | |||
@@ -797,10 +875,9 @@ namespace Shadowsocks.Controller | |||
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); | |||
} | |||
@@ -811,11 +888,9 @@ namespace Shadowsocks.Controller | |||
{ | |||
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); | |||
if (available > 0) | |||
{ | |||
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available, | |||
int size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available, | |||
SocketFlags.None); | |||
_firstPacketLength += size; | |||
@@ -833,7 +908,11 @@ namespace Shadowsocks.Controller | |||
private void StartPipe(AsyncSession session) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_startReceivingTime = DateTime.Now; | |||
@@ -846,20 +925,24 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var session = (AsyncSession)ar.AsyncState; | |||
AsyncSession session = (AsyncSession)ar.AsyncState; | |||
int bytesRead = session.Remote.EndReceive(ar); | |||
_totalRead += bytesRead; | |||
_tcprelay.UpdateInboundCounter(_server, bytesRead); | |||
OnInbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesRead)); | |||
if (bytesRead > 0) | |||
{ | |||
lastActivity = DateTime.Now; | |||
@@ -889,8 +972,6 @@ namespace Shadowsocks.Controller | |||
Logger.Debug($"start sending {bytesToSend}"); | |||
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, | |||
PipeConnectionSendCallback, new object[] { session, bytesToSend }); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastRead(_server); | |||
} | |||
else | |||
{ | |||
@@ -901,20 +982,23 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
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) | |||
{ | |||
@@ -929,8 +1013,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -952,22 +1035,25 @@ namespace Shadowsocks.Controller | |||
return; | |||
} | |||
} | |||
_tcprelay.UpdateOutboundCounter(_server, bytesToSend); | |||
OnOutbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesToSend)); | |||
_startSendingTime = DateTime.Now; | |||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, | |||
PipeRemoteSendCallback, new object[] { session, bytesToSend }); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastWrite(_server); | |||
} | |||
private void PipeRemoteSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
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 bytesRemaining = bytesShouldSend - bytesSent; | |||
if (bytesRemaining > 0) | |||
@@ -983,8 +1069,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -993,11 +1078,11 @@ namespace Shadowsocks.Controller | |||
{ | |||
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) | |||
{ | |||
Logger.Info("reconstruct _remoteSendBuffer to re-send"); | |||
@@ -1011,8 +1096,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
} |
@@ -35,7 +35,6 @@ namespace Shadowsocks.Controller | |||
private Configuration _config; | |||
private StrategyManager _strategyManager; | |||
private PrivoxyRunner privoxyRunner; | |||
private GFWListUpdater gfwListUpdater; | |||
private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer; | |||
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; | |||
@@ -80,9 +79,9 @@ namespace Shadowsocks.Controller | |||
public event EventHandler<PathEventArgs> PACFileReadyToOpen; | |||
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; | |||
@@ -217,25 +216,25 @@ namespace Shadowsocks.Controller | |||
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)) | |||
{ | |||
MessageBox.Show(I18N.GetString("Successfully imported from {0}", ssURL)); | |||
return true; | |||
} | |||
} | |||
else | |||
{ | |||
MessageBox.Show(I18N.GetString("Failed to import. Please check if the link is valid.")); | |||
} | |||
} | |||
return false; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
public bool AddServerBySSURL(string ssURL) | |||
{ | |||
try | |||
@@ -412,12 +411,9 @@ namespace Shadowsocks.Controller | |||
return $"ss://{url}{tag}"; | |||
} | |||
public void UpdatePACFromGFWList() | |||
public void UpdatePACFromGeosite() | |||
{ | |||
if (gfwListUpdater != null) | |||
{ | |||
gfwListUpdater.UpdatePACFromGFWList(_config); | |||
} | |||
GeositeUpdater.UpdatePACFromGeosite(_config); | |||
} | |||
public void UpdateStatisticsConfiguration(bool enabled) | |||
@@ -486,29 +482,32 @@ namespace Shadowsocks.Controller | |||
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) | |||
{ | |||
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) | |||
{ | |||
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) | |||
{ | |||
availabilityStatistics.UpdateOutboundCounter(server, n); | |||
availabilityStatistics.UpdateOutboundCounter(args.server, args.length); | |||
} | |||
} | |||
@@ -524,15 +523,15 @@ namespace Shadowsocks.Controller | |||
privoxyRunner = privoxyRunner ?? new PrivoxyRunner(); | |||
_pacDaemon = _pacDaemon ?? new PACDaemon(); | |||
_pacDaemon = _pacDaemon ?? new PACDaemon(_config); | |||
_pacDaemon.PACFileChanged += PacDaemon_PACFileChanged; | |||
_pacDaemon.UserRuleFileChanged += PacDaemon_UserRuleFileChanged; | |||
_pacServer = _pacServer ?? new PACServer(_pacDaemon); | |||
_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); | |||
_tcpListener?.Stop(); | |||
@@ -553,6 +552,11 @@ namespace Shadowsocks.Controller | |||
privoxyRunner.Start(_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); | |||
_tcpListener = new TCPListener(_config, new List<IStreamService> | |||
{ | |||
@@ -612,27 +616,20 @@ namespace Shadowsocks.Controller | |||
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) | |||
{ | |||
UpdatePACFromGFWListError?.Invoke(this, e); | |||
UpdatePACFromGeositeError?.Invoke(this, e); | |||
} | |||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||
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(); | |||
} | |||
@@ -786,6 +786,10 @@ function FindProxyForURL(url, host) { | |||
if (userrulesMatcher.matchesAny(url, host) instanceof WhitelistFilter) { | |||
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) { | |||
return proxy; | |||
} | |||
@@ -12,22 +12,22 @@ Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowso | |||
,,,,,, | |||
System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,Proxy système | |||
Disable,Отключен,禁用,禁用,無効,비활성화,Désactiver | |||
PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PAC,PAC,PAC | |||
PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PAC,프록시 자동 구성 (PAC),PAC | |||
Global,Для всей системы,全局模式,全局模式,全般,전역,Global | |||
Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | |||
Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | |||
Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques… | |||
Start on Boot,Автозагрузка,开机启动,開機啟動,システムと同時に起動,시스템 시작 시에 시작하기,Démarrage automatique | |||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,,, | |||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,,ss:// 링크 연결, | |||
Forward Proxy...,Прямой прокси…,正向代理设置...,正向 Proxy 設定...,フォワードプロキシの設定...,포워드 프록시,Forward-proxy… | |||
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 ... | |||
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 ... | |||
@@ -66,17 +66,17 @@ Plugin Options,Опции плагина,插件选项,外掛程式選項,プラ | |||
Need Plugin Argument,Требуются аргументы,需要命令行参数,,,플러그인 인자가 필요함,Besoin d'un argument de plugin | |||
Plugin Arguments,Аргументы,插件参数,外掛程式參數,プラグインの引数,플러그인 인자,Arguments du plugin | |||
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 | |||
Timeout(Sec),Таймаут(сек),超时(秒),逾時 (秒),タイムアウト (秒),시간초과(Timeout) (초),Délai d'attente(sec) | |||
Timeout(Sec),Таймаут(сек),超时(秒),逾時 (秒),タイムアウト (秒),시간 초과 (초),Délai d'attente(sec) | |||
OK,ОК,确定,確定,OK,확인,OK | |||
Cancel,Отмена,取消,取消,キャンセル,취소,Annuler | |||
Apply,Применить,应用,應用,適用,적용,Appliquer | |||
New server,Новый сервер,未配置的服务器,新伺服器,新規サーバー,새 서버,Nouveau serveur | |||
Move &Up,Выше,上移(&U),上移 (&U),上に移動 (&U),위로 (&U),Monter | |||
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}" | |||
,,,,,, | |||
#Statistics Config,,,,,, | |||
@@ -116,8 +116,8 @@ Use Proxy,Использовать прокси,使用代理,使用 Proxy,プロ | |||
Proxy Type,Тип прокси,代理类型,Proxy 類型,プロキシの種類,프록시 종류,Type de proxy | |||
Proxy Addr,Адрес прокси,代理地址,Proxy 位址,プロキシアドレス,프록시 주소,Adresse 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 | |||
Auth Pwd,Пароль,认证密码,認證口令,認証パスワード,비밀번호,Mot de passe d'authentification | |||
,,,,,, | |||
@@ -142,9 +142,9 @@ QRCode and URL,QRCode и URL,二维码与 URL,QR 碼與 URL,QR コードと URL, | |||
,,,,,, | |||
# 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,,,,,, | |||
,,,,,, | |||
@@ -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 | |||
Invalid server address,Неверный адрес сервера,非法服务器地址,無效伺服器位址,サーバーアドレスが無効です。,올바르지 않은 서버 주소입니다.,Adresse de serveur non valide | |||
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 | |||
Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide | |||
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 | |||
System Proxy Enabled,Системный прокси включен,系统代理已启用,系統 Proxy 已啟用,システム プロキシが有効です。,시스템 프록시가 활성화되었습니다.,Proxy système activé | |||
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. | |||
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. | |||
@@ -190,25 +190,25 @@ Successfully imported from {0},Успешно импортировано из {0 | |||
Failed to import. Please check if the link is valid.,,导入失败,请检查链接是否有效。,導入失敗,請檢查鏈接是否有效。,,, | |||
System Proxy On: ,Системный прокси:,系统代理已启用:,系統 Proxy 已啟用:,システム プロキシが有効:,시스템 프록시 활성화됨: ,Proxy système activé: | |||
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 handshake failed,Не удалось выполнить хэндшейк,代理握手失败,Proxy 交握失敗,プロキシ ハンドシェイクに失敗しました。,프록시 핸드쉐이크에 실패했습니다.,Échec de la prise de contact par proxy | |||
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} | |||
"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 | |||
,,,,,, | |||
Operation failure,Операция завершилась неудачей,操作失败,,,작업에 실패했습니다.,Operation failure | |||
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" | |||
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 showPluginOutput; | |||
public string pacUrl; | |||
public string gfwListUrl; | |||
public string geositeUrl; | |||
public string geositeGroup = "geolocation-!cn"; | |||
public bool geositeBlacklistMode = true; | |||
public bool useOnlinePac; | |||
public bool secureLocalPac = true; | |||
public bool availabilityStatistics; | |||
@@ -93,10 +96,11 @@ namespace Shadowsocks.Model | |||
public static Configuration Load() | |||
{ | |||
Configuration config; | |||
try | |||
{ | |||
string configContent = File.ReadAllText(CONFIG_FILE); | |||
Configuration config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||
config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||
config.isDefault = false; | |||
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? | |||
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) | |||
{ | |||
if (!(e is FileNotFoundException)) | |||
logger.LogUsefulException(e); | |||
return new Configuration | |||
config = new Configuration | |||
{ | |||
index = 0, | |||
isDefault = true, | |||
@@ -169,6 +148,31 @@ namespace Shadowsocks.Model | |||
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) | |||
@@ -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> | |||
/// 查找类似 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> | |||
internal static string default_abp_rule { | |||
internal static byte[] dlc_dat { | |||
get { | |||
return ResourceManager.GetString("default_abp_rule", resourceCulture); | |||
object obj = ResourceManager.GetObject("dlc_dat", resourceCulture); | |||
return ((byte[])(obj)); | |||
} | |||
} | |||
/// <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> | |||
internal static string i18n_csv { | |||
get { | |||
@@ -121,8 +121,8 @@ | |||
<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> | |||
</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 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> | |||
@@ -48,7 +48,7 @@ namespace Shadowsocks.View | |||
private ToolStripMenuItem localPACItem; | |||
private ToolStripMenuItem onlinePACItem; | |||
private ToolStripMenuItem editLocalPACItem; | |||
private ToolStripMenuItem updateFromGFWListItem; | |||
private ToolStripMenuItem updateFromGeositeItem; | |||
private ToolStripMenuItem editGFWUserRuleItem; | |||
private ToolStripMenuItem editOnlinePACItem; | |||
private ToolStripMenuItem secureLocalPacUrlToggleItem; | |||
@@ -88,8 +88,8 @@ namespace Shadowsocks.View | |||
controller.ShowPluginOutputChanged += controller_ShowPluginOutputChanged; | |||
controller.EnableGlobalChanged += controller_EnableGlobalChanged; | |||
controller.Errored += controller_Errored; | |||
controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted; | |||
controller.UpdatePACFromGFWListError += controller_UpdatePACFromGFWListError; | |||
controller.UpdatePACFromGeositeCompleted += controller_UpdatePACFromGeositeCompleted; | |||
controller.UpdatePACFromGeositeError += controller_UpdatePACFromGeositeError; | |||
_notifyIcon = new NotifyIcon(); | |||
UpdateTrayIconAndNotifyText(); | |||
@@ -310,8 +310,8 @@ namespace Shadowsocks.View | |||
this.onlinePACItem = CreateToolStripMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)), | |||
new ToolStripSeparator(), | |||
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)), | |||
CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), | |||
this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), | |||
@@ -390,17 +390,17 @@ namespace Shadowsocks.View | |||
_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); | |||
logger.LogUsefulException(e.GetException()); | |||
} | |||
void controller_UpdatePACFromGFWListCompleted(object sender, GFWListUpdater.ResultEventArgs e) | |||
void controller_UpdatePACFromGeositeCompleted(object sender, GeositeResultEventArgs e) | |||
{ | |||
string result = e.Success | |||
? 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); | |||
} | |||
@@ -447,7 +447,7 @@ namespace Shadowsocks.View | |||
VerboseLoggingToggleItem.Checked = config.isVerboseLogging; | |||
ShowPluginOutputToggleItem.Checked = config.showPluginOutput; | |||
AutoStartupItem.Checked = AutoStartup.Check(); | |||
ProtocolHandlerItem.Checked = ProtocolHandler.Check(); | |||
ProtocolHandlerItem.Checked = ProtocolHandler.Check(); | |||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | |||
localPACItem.Checked = !onlinePACItem.Checked; | |||
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | |||
@@ -705,12 +705,12 @@ namespace Shadowsocks.View | |||
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(); | |||
} | |||
@@ -867,16 +867,16 @@ namespace Shadowsocks.View | |||
{ | |||
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) | |||
@@ -944,14 +944,14 @@ namespace Shadowsocks.View | |||
if (this.localPACItem.Checked) | |||
{ | |||
this.editLocalPACItem.Enabled = true; | |||
this.updateFromGFWListItem.Enabled = true; | |||
this.updateFromGeositeItem.Enabled = true; | |||
this.editGFWUserRuleItem.Enabled = true; | |||
this.editOnlinePACItem.Enabled = false; | |||
} | |||
else | |||
{ | |||
this.editLocalPACItem.Enabled = false; | |||
this.updateFromGFWListItem.Enabled = false; | |||
this.updateFromGeositeItem.Enabled = false; | |||
this.editGFWUserRuleItem.Enabled = false; | |||
this.editOnlinePACItem.Enabled = true; | |||
} | |||
@@ -5,6 +5,7 @@ | |||
<package id="Costura.Fody" version="3.3.3" targetFramework="net472" /> | |||
<package id="Fody" version="4.2.1" targetFramework="net472" developmentDependency="true" /> | |||
<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="Newtonsoft.Json" version="12.0.2" targetFramework="net472" /> | |||
<package id="NLog" version="4.6.8" targetFramework="net472" /> | |||
@@ -79,7 +79,7 @@ | |||
<PrivateAssets>all</PrivateAssets> | |||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |||
</PackageReference> | |||
<PackageReference Include="Google.Protobuf" Version="3.11.4" /> | |||
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" /> | |||
<PackageReference Include="NaCl.Core" Version="1.2.0" /> | |||
@@ -96,7 +96,7 @@ | |||
<ItemGroup> | |||
<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\NLog.config" /> | |||
<Resource Include="Data\privoxy.exe.gz" /> | |||