From cf7e639cef6072992185068d20c692cf9a652d60 Mon Sep 17 00:00:00 2001 From: database64128 Date: Mon, 26 Oct 2020 20:03:22 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=93=20Split=20into=20multiple=20projec?= =?UTF-8?q?ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Shadowsocks - Shadowsocks.Common - Shadowsocks.Crypto - Shadowsocks.WPF --- .editorconfig | 100 + Shadowsocks.Common/Models/IoCManager.cs | 9 + Shadowsocks.Common/Shadowsocks.Common.csproj | 17 + .../SystemProxy/ProxyException.cs | 6 +- .../Utilities}/LoggerExtension.cs | 294 ++- .../Crypto/AEAD/AEADAesGcmNativeCrypto.cs | 10 +- .../Crypto/AEAD/AEADBouncyCastleCrypto.cs | 9 +- .../Crypto/AEAD/AEADCrypto.cs | 642 +++---- .../Crypto/AEAD/AEADNaClCrypto.cs | 26 +- .../Crypto}/CipherInfo.cs | 10 +- .../Crypto/CryptoBase.cs | 97 +- .../Crypto/CryptoFactory.cs | 242 +-- .../Crypto}/Exception/CryptoException.cs | 34 +- .../Crypto/ICrypto.cs | 28 +- .../Crypto}/RNG.cs | 100 +- .../Crypto}/Stream/ExtendedCfbBlockCipher.cs | 0 .../Stream/StreamAesBouncyCastleCrypto.cs | 10 +- .../Crypto/Stream/StreamChachaNaClCrypto.cs | 10 +- .../Crypto/Stream/StreamCrypto.cs | 360 ++-- .../Crypto/Stream/StreamPlainNativeCrypto.cs | 8 +- .../Crypto/Stream/StreamRc4NativeCrypto.cs | 9 +- Shadowsocks.Crypto/Crypto/TCPInfo.cs | 20 + Shadowsocks.Crypto/Shadowsocks.Crypto.csproj | 18 + .../Util}/CryptoUtils.cs | 8 +- Shadowsocks.WPF/App.xaml | 15 + Shadowsocks.WPF/App.xaml.cs | 20 + Shadowsocks.WPF/AssemblyInfo.cs | 10 + .../Assets}/shadowsocks.ico | Bin .../Assets}/ss128.pdn | Bin .../Assets}/ss32.pdn | Bin .../Assets}/ss32Fill.png | Bin .../Assets}/ss32In.png | Bin .../Assets}/ss32Out.png | Bin .../Assets}/ss32Outline.png | Bin .../Assets}/ssw128.png | Bin Shadowsocks.WPF/FodyWeavers.xml | 3 + Shadowsocks.WPF/FodyWeavers.xsd | 26 + .../Localization/LocalizationProvider.cs | 15 + .../Localization/Strings.Designer.cs | 2 +- .../Localization/Strings.fr.resx | 0 .../Localization/Strings.ja.resx | 0 .../Localization/Strings.ko.resx | 0 .../Localization/Strings.resx | 0 .../Localization/Strings.ru.resx | 0 .../Localization/Strings.zh-Hans.resx | 0 .../Localization/Strings.zh-Hant.resx | 376 ++-- .../PublishProfiles/FolderProfile.pubxml | 20 +- .../PublishProfiles/FolderProfile.pubxml.user | 0 .../Properties/PublishProfiles/win-x64.pubxml | 2 +- .../PublishProfiles/win-x64.pubxml.user | 0 .../Properties/PublishProfiles/win-x86.pubxml | 2 +- .../PublishProfiles/win-x86.pubxml.user | 8 +- Shadowsocks.WPF/Resource.Designer.cs | 133 ++ .../Resource.resx | 297 ++- Shadowsocks.WPF/Shadowsocks.WPF.csproj | 70 + Shadowsocks.WPF/Shadowsocks.WPF.csproj.user | 21 + .../ViewModels/ForwardProxyViewModel.cs | 2 +- .../ViewModels/HotkeysViewModel.cs | 2 +- .../ViewModels/MainWindowViewModel.cs | 14 + .../ViewModels/OnlineConfigViewModel.cs | 2 +- .../ViewModels/ServerSharingViewModel.cs | 2 +- .../VersionUpdatePromptViewModel.cs | 2 +- .../Views/ForwardProxyView.xaml | 6 +- .../Views/ForwardProxyView.xaml.cs | 4 +- .../Views/HotkeysView.xaml | 6 +- .../Views/HotkeysView.xaml.cs | 4 +- Shadowsocks.WPF/Views/MainWindow.xaml | 21 + Shadowsocks.WPF/Views/MainWindow.xaml.cs | 17 + .../Views/OnlineConfigView.xaml | 6 +- .../Views/OnlineConfigView.xaml.cs | 4 +- .../Views/ServerSharingView.xaml | 6 +- .../Views/ServerSharingView.xaml.cs | 4 +- .../Views/VersionUpdatePromptView.xaml | 6 +- .../Views/VersionUpdatePromptView.xaml.cs | 4 +- Shadowsocks/Assets/NLog.config | 13 + .../Data => Shadowsocks/Assets}/abp.js | 1620 ++++++++--------- .../Data => Shadowsocks/Assets}/dlc.dat | 0 .../Assets}/privoxy.exe.gz | Bin .../Assets}/privoxy_conf.txt | 16 +- .../Data => Shadowsocks/Assets}/user-rule.txt | 4 +- Shadowsocks/Resource.Designer.cs | 153 ++ Shadowsocks/Resource.resx | 139 ++ Shadowsocks/Shadowsocks.csproj | 31 + shadowsocks-csharp/Data/NLog.config | 13 - shadowsocks-csharp/FodyWeavers.xml | 6 - shadowsocks-csharp/FodyWeavers.xsd | 113 -- .../Localization/LocalizationProvider.cs | 13 - .../Properties/Resources.Designer.cs | 244 --- .../Properties/Settings.Designer.cs | 86 - .../Properties/Settings.settings | 21 - shadowsocks-windows.sln | 25 + 91 files changed, 3038 insertions(+), 2658 deletions(-) create mode 100644 .editorconfig create mode 100644 Shadowsocks.Common/Models/IoCManager.cs create mode 100644 Shadowsocks.Common/Shadowsocks.Common.csproj rename {shadowsocks-csharp/Util => Shadowsocks.Common}/SystemProxy/ProxyException.cs (90%) rename {shadowsocks-csharp/Controller => Shadowsocks.Common/Utilities}/LoggerExtension.cs (85%) rename shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs => Shadowsocks.Crypto/Crypto/AEAD/AEADAesGcmNativeCrypto.cs (88%) rename shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs => Shadowsocks.Crypto/Crypto/AEAD/AEADBouncyCastleCrypto.cs (91%) rename shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs => Shadowsocks.Crypto/Crypto/AEAD/AEADCrypto.cs (88%) rename shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs => Shadowsocks.Crypto/Crypto/AEAD/AEADNaClCrypto.cs (72%) rename {shadowsocks-csharp/Encryption => Shadowsocks.Crypto/Crypto}/CipherInfo.cs (92%) rename shadowsocks-csharp/Encryption/EncryptorBase.cs => Shadowsocks.Crypto/Crypto/CryptoBase.cs (83%) rename shadowsocks-csharp/Encryption/EncryptorFactory.cs => Shadowsocks.Crypto/Crypto/CryptoFactory.cs (74%) rename {shadowsocks-csharp/Encryption => Shadowsocks.Crypto/Crypto}/Exception/CryptoException.cs (84%) rename shadowsocks-csharp/Encryption/IEncryptor.cs => Shadowsocks.Crypto/Crypto/ICrypto.cs (79%) rename {shadowsocks-csharp/Encryption => Shadowsocks.Crypto/Crypto}/RNG.cs (91%) rename {shadowsocks-csharp/Encryption => Shadowsocks.Crypto/Crypto}/Stream/ExtendedCfbBlockCipher.cs (100%) rename shadowsocks-csharp/Encryption/Stream/StreamAesBouncyCastleEncryptor.cs => Shadowsocks.Crypto/Crypto/Stream/StreamAesBouncyCastleCrypto.cs (89%) rename shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs => Shadowsocks.Crypto/Crypto/Stream/StreamChachaNaClCrypto.cs (88%) rename shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs => Shadowsocks.Crypto/Crypto/Stream/StreamCrypto.cs (88%) rename shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs => Shadowsocks.Crypto/Crypto/Stream/StreamPlainNativeCrypto.cs (82%) rename shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs => Shadowsocks.Crypto/Crypto/Stream/StreamRc4NativeCrypto.cs (92%) create mode 100644 Shadowsocks.Crypto/Crypto/TCPInfo.cs create mode 100644 Shadowsocks.Crypto/Shadowsocks.Crypto.csproj rename {shadowsocks-csharp/Encryption => Shadowsocks.Crypto/Util}/CryptoUtils.cs (91%) create mode 100644 Shadowsocks.WPF/App.xaml create mode 100644 Shadowsocks.WPF/App.xaml.cs create mode 100644 Shadowsocks.WPF/AssemblyInfo.cs rename {shadowsocks-csharp => Shadowsocks.WPF/Assets}/shadowsocks.ico (100%) mode change 100755 => 100644 rename {shadowsocks-csharp/Resources => Shadowsocks.WPF/Assets}/ss128.pdn (100%) rename {shadowsocks-csharp/Resources => Shadowsocks.WPF/Assets}/ss32.pdn (100%) rename {shadowsocks-csharp/Resources => Shadowsocks.WPF/Assets}/ss32Fill.png (100%) rename {shadowsocks-csharp/Resources => Shadowsocks.WPF/Assets}/ss32In.png (100%) rename {shadowsocks-csharp/Resources => Shadowsocks.WPF/Assets}/ss32Out.png (100%) rename {shadowsocks-csharp/Resources => Shadowsocks.WPF/Assets}/ss32Outline.png (100%) rename {shadowsocks-csharp/Resources => Shadowsocks.WPF/Assets}/ssw128.png (100%) mode change 100755 => 100644 create mode 100644 Shadowsocks.WPF/FodyWeavers.xml create mode 100644 Shadowsocks.WPF/FodyWeavers.xsd create mode 100644 Shadowsocks.WPF/Localization/LocalizationProvider.cs rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.Designer.cs (99%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.fr.resx (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.ja.resx (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.ko.resx (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.resx (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.ru.resx (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.zh-Hans.resx (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Localization/Strings.zh-Hant.resx (97%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Properties/PublishProfiles/FolderProfile.pubxml (72%) rename shadowsocks-csharp/Properties/PublishProfiles/win-x64.pubxml.user => Shadowsocks.WPF/Properties/PublishProfiles/FolderProfile.pubxml.user (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Properties/PublishProfiles/win-x64.pubxml (88%) rename shadowsocks-csharp/Properties/PublishProfiles/win-x86.pubxml.user => Shadowsocks.WPF/Properties/PublishProfiles/win-x64.pubxml.user (100%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Properties/PublishProfiles/win-x86.pubxml (88%) rename shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml.user => Shadowsocks.WPF/Properties/PublishProfiles/win-x86.pubxml.user (93%) create mode 100644 Shadowsocks.WPF/Resource.Designer.cs rename shadowsocks-csharp/Properties/Resources.resx => Shadowsocks.WPF/Resource.resx (72%) mode change 100755 => 100644 create mode 100644 Shadowsocks.WPF/Shadowsocks.WPF.csproj create mode 100644 Shadowsocks.WPF/Shadowsocks.WPF.csproj.user rename {shadowsocks-csharp => Shadowsocks.WPF}/ViewModels/ForwardProxyViewModel.cs (99%) rename {shadowsocks-csharp => Shadowsocks.WPF}/ViewModels/HotkeysViewModel.cs (99%) create mode 100644 Shadowsocks.WPF/ViewModels/MainWindowViewModel.cs rename {shadowsocks-csharp => Shadowsocks.WPF}/ViewModels/OnlineConfigViewModel.cs (99%) rename {shadowsocks-csharp => Shadowsocks.WPF}/ViewModels/ServerSharingViewModel.cs (98%) rename {shadowsocks-csharp => Shadowsocks.WPF}/ViewModels/VersionUpdatePromptViewModel.cs (97%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/ForwardProxyView.xaml (96%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/ForwardProxyView.xaml.cs (98%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/HotkeysView.xaml (96%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/HotkeysView.xaml.cs (99%) create mode 100644 Shadowsocks.WPF/Views/MainWindow.xaml create mode 100644 Shadowsocks.WPF/Views/MainWindow.xaml.cs rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/OnlineConfigView.xaml (93%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/OnlineConfigView.xaml.cs (96%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/ServerSharingView.xaml (92%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/ServerSharingView.xaml.cs (96%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/VersionUpdatePromptView.xaml (92%) rename {shadowsocks-csharp => Shadowsocks.WPF}/Views/VersionUpdatePromptView.xaml.cs (95%) create mode 100644 Shadowsocks/Assets/NLog.config rename {shadowsocks-csharp/Data => Shadowsocks/Assets}/abp.js (96%) rename {shadowsocks-csharp/Data => Shadowsocks/Assets}/dlc.dat (100%) rename {shadowsocks-csharp/Data => Shadowsocks/Assets}/privoxy.exe.gz (100%) rename {shadowsocks-csharp/Data => Shadowsocks/Assets}/privoxy_conf.txt (96%) rename {shadowsocks-csharp/Data => Shadowsocks/Assets}/user-rule.txt (98%) mode change 100755 => 100644 create mode 100644 Shadowsocks/Resource.Designer.cs create mode 100644 Shadowsocks/Resource.resx create mode 100644 Shadowsocks/Shadowsocks.csproj delete mode 100644 shadowsocks-csharp/Data/NLog.config delete mode 100644 shadowsocks-csharp/FodyWeavers.xml delete mode 100644 shadowsocks-csharp/FodyWeavers.xsd delete mode 100644 shadowsocks-csharp/Localization/LocalizationProvider.cs delete mode 100644 shadowsocks-csharp/Properties/Resources.Designer.cs delete mode 100644 shadowsocks-csharp/Properties/Settings.Designer.cs delete mode 100644 shadowsocks-csharp/Properties/Settings.settings diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..22e5606c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,100 @@ +root = true +# EditorConfig: http://EditorConfig.org +# VS extension: https://marketplace.visualstudio.com/items?itemName = MadsKristensen.EditorConfig + +# 4 space indentation +[*] +charset = utf-8 +indent_style = space +indent_size = 4 + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers = false +csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +dotnet_naming_rule.private_constants_rule.severity = suggestion +dotnet_naming_rule.private_constants_rule.style = all_upper_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_static_readonly_rule.severity = suggestion +dotnet_naming_rule.private_static_readonly_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_style.all_upper_style.capitalization = all_upper +dotnet_naming_style.all_upper_style.word_separator = _ +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.lower_camel_case_style.required_prefix = _ +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none +dotnet_style_qualification_for_event = false:warning +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +# CSharp code style settings: +[*.cs] +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = true:suggestion + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = false:none +csharp_style_pattern_matching_over_as_with_null_check = false:none +csharp_style_inlined_variable_declaration = false:none +csharp_style_throw_expression = false:none +csharp_style_conditional_delegate_call = true:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = false +csharp_new_line_before_members_in_anonymous_types = false + +# Dotnet code style settings: +[*.{cs,vb}] +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = false +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:silent +dotnet_style_collection_initializer = true:silent +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + + +[*.{appxmanifest,asax,ascx,aspx,axml,build,config,cs,cshtml,csproj,css,dbml,discomap,dtd,htm,html,js,json,jsproj,jsx,lsproj,master,njsproj,nuspec,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,ts,tsx,vb,vbproj,xaml,xamlx,xml,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/Shadowsocks.Common/Models/IoCManager.cs b/Shadowsocks.Common/Models/IoCManager.cs new file mode 100644 index 00000000..5c176290 --- /dev/null +++ b/Shadowsocks.Common/Models/IoCManager.cs @@ -0,0 +1,9 @@ +using SimpleInjector; + +namespace Shadowsocks.Common.Model +{ + public static class IoCManager + { + public static Container Container { get; } = new Container(); + } +} diff --git a/Shadowsocks.Common/Shadowsocks.Common.csproj b/Shadowsocks.Common/Shadowsocks.Common.csproj new file mode 100644 index 00000000..5fb972fe --- /dev/null +++ b/Shadowsocks.Common/Shadowsocks.Common.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.1 + clowwindy & community 2020 + clowwindy & community 2020 + Shadowsocks Common + + + + + + + + + + diff --git a/shadowsocks-csharp/Util/SystemProxy/ProxyException.cs b/Shadowsocks.Common/SystemProxy/ProxyException.cs similarity index 90% rename from shadowsocks-csharp/Util/SystemProxy/ProxyException.cs rename to Shadowsocks.Common/SystemProxy/ProxyException.cs index 76e858cb..ec2866a3 100644 --- a/shadowsocks-csharp/Util/SystemProxy/ProxyException.cs +++ b/Shadowsocks.Common/SystemProxy/ProxyException.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; -namespace Shadowsocks.Util.SystemProxy +namespace Shadowsocks.Common.SystemProxy { enum ProxyExceptionType { diff --git a/shadowsocks-csharp/Controller/LoggerExtension.cs b/Shadowsocks.Common/Utilities/LoggerExtension.cs similarity index 85% rename from shadowsocks-csharp/Controller/LoggerExtension.cs rename to Shadowsocks.Common/Utilities/LoggerExtension.cs index 59bda840..9d6e1013 100644 --- a/shadowsocks-csharp/Controller/LoggerExtension.cs +++ b/Shadowsocks.Common/Utilities/LoggerExtension.cs @@ -1,151 +1,143 @@ -using System; -using System.ComponentModel; -using System.IO; -using System.Net.Sockets; -using System.Net; -using System.Diagnostics; -using System.Text; -using Shadowsocks.Util.SystemProxy; - -namespace NLog -{ - public static class LoggerExtension - { - // for key, iv, etc... - public static void Dump(this Logger logger, string tag, ReadOnlySpan arr) - { - logger.Dump(tag, arr.ToArray(), arr.Length); - } - public static void Dump(this Logger logger, string tag, byte[] arr, int length = -1) - { - if (arr == null) logger.Trace($@" -{tag}: -(null) - -"); - if (length == -1) length = arr.Length; - - if (!logger.IsTraceEnabled) return; - string hex = BitConverter.ToString(arr.AsSpan(0, Math.Min(arr.Length, length)).ToArray()).Replace("-", ""); - string content = $@" -{tag}: -{hex} - -"; - logger.Trace(content); - } - // for cipher and plain text, so we can use openssl to test - public static void DumpBase64(this Logger logger, string tag, ReadOnlySpan arr) - { - logger.DumpBase64(tag, arr.ToArray(), arr.Length); - } - public static void DumpBase64(this Logger logger, string tag, byte[] arr, int length = -1) - { - if (arr == null) logger.Trace($@" -{tag}: -(null) - -"); - if (length == -1) length = arr.Length; - - if (!logger.IsTraceEnabled) return; - string hex = Convert.ToBase64String(arr.AsSpan(0, Math.Min(arr.Length, length)).ToArray()); - string content = $@" -{tag}: -{hex} - -"; - logger.Trace(content); - } - - public static void Debug(this Logger logger, EndPoint local, EndPoint remote, int len, string header = null, string tailer = null) - { - if (logger.IsDebugEnabled) - { - if (header == null && tailer == null) - logger.Debug($"{local} => {remote} (size={len})"); - else if (header == null && tailer != null) - logger.Debug($"{local} => {remote} (size={len}), {tailer}"); - else if (header != null && tailer == null) - logger.Debug($"{header}: {local} => {remote} (size={len})"); - else - logger.Debug($"{header}: {local} => {remote} (size={len}), {tailer}"); - } - } - - public static void Debug(this Logger logger, Socket sock, int len, string header = null, string tailer = null) - { - if (logger.IsDebugEnabled) - { - logger.Debug(sock.LocalEndPoint, sock.RemoteEndPoint, len, header, tailer); - } - } - - public static void LogUsefulException(this Logger logger, Exception e) - { - // just log useful exceptions, not all of them - if (e is SocketException) - { - SocketException se = (SocketException)e; - if (se.SocketErrorCode == SocketError.ConnectionAborted) - { - // closed by browser when sending - // normally happens when download is canceled or a tab is closed before page is loaded - } - else if (se.SocketErrorCode == SocketError.ConnectionReset) - { - // received rst - } - else if (se.SocketErrorCode == SocketError.NotConnected) - { - // The application tried to send or receive data, and the System.Net.Sockets.Socket is not connected. - } - else if (se.SocketErrorCode == SocketError.HostUnreachable) - { - // There is no network route to the specified host. - } - else if (se.SocketErrorCode == SocketError.TimedOut) - { - // The connection attempt timed out, or the connected host has failed to respond. - } - else - { - logger.Warn(e); - } - } - else if (e is ObjectDisposedException) - { - } - else if (e is Win32Exception) - { - var ex = (Win32Exception)e; - - // Win32Exception (0x80004005): A 32 bit processes cannot access modules of a 64 bit process. - if ((uint)ex.ErrorCode != 0x80004005) - { - logger.Warn(e); - } - } - else if (e is ProxyException) - { - var ex = (ProxyException)e; - switch (ex.Type) - { - case ProxyExceptionType.FailToRun: - case ProxyExceptionType.QueryReturnMalformed: - case ProxyExceptionType.SysproxyExitError: - logger.Error($"sysproxy - {ex.Type.ToString()}:{ex.Message}"); - break; - case ProxyExceptionType.QueryReturnEmpty: - case ProxyExceptionType.Unspecific: - logger.Error($"sysproxy - {ex.Type.ToString()}"); - break; - } - } - else - { - logger.Warn(e); - } - } - } -} +using Shadowsocks.Common.SystemProxy; + +using System; +using System.ComponentModel; +using System.Net; +using System.Net.Sockets; + +namespace NLog +{ + public static class LoggerExtension + { + // for key, iv, etc... + public static void Dump(this Logger logger, string tag, ReadOnlySpan arr) + { + logger.Dump(tag, arr.ToArray(), arr.Length); + } + public static void Dump(this Logger logger, string tag, byte[] arr, int length = -1) + { + if (arr == null) logger.Trace($@" +{tag}: +(null) + +"); + if (length == -1) length = arr.Length; + + if (!logger.IsTraceEnabled) return; + string hex = BitConverter.ToString(arr.AsSpan(0, Math.Min(arr.Length, length)).ToArray()).Replace("-", ""); + string content = $@" +{tag}: +{hex} + +"; + logger.Trace(content); + } + // for cipher and plain text, so we can use openssl to test + public static void DumpBase64(this Logger logger, string tag, ReadOnlySpan arr) + { + logger.DumpBase64(tag, arr.ToArray(), arr.Length); + } + public static void DumpBase64(this Logger logger, string tag, byte[] arr, int length = -1) + { + if (arr == null) logger.Trace($@" +{tag}: +(null) + +"); + if (length == -1) length = arr.Length; + + if (!logger.IsTraceEnabled) return; + string hex = Convert.ToBase64String(arr.AsSpan(0, Math.Min(arr.Length, length)).ToArray()); + string content = $@" +{tag}: +{hex} + +"; + logger.Trace(content); + } + + public static void Debug(this Logger logger, EndPoint local, EndPoint remote, int len, string header = null, string tailer = null) + { + if (logger.IsDebugEnabled) + { + if (header == null && tailer == null) + logger.Debug($"{local} => {remote} (size={len})"); + else if (header == null && tailer != null) + logger.Debug($"{local} => {remote} (size={len}), {tailer}"); + else if (header != null && tailer == null) + logger.Debug($"{header}: {local} => {remote} (size={len})"); + else + logger.Debug($"{header}: {local} => {remote} (size={len}), {tailer}"); + } + } + + public static void Debug(this Logger logger, Socket sock, int len, string header = null, string tailer = null) + { + if (logger.IsDebugEnabled) + logger.Debug(sock.LocalEndPoint, sock.RemoteEndPoint, len, header, tailer); + + } + + public static void LogUsefulException(this Logger logger, Exception e) + { + // just log useful exceptions, not all of them + if (e is SocketException se) + { + if (se.SocketErrorCode == SocketError.ConnectionAborted) + { + // closed by browser when sending + // normally happens when download is canceled or a tab is closed before page is loaded + } + else if (se.SocketErrorCode == SocketError.ConnectionReset) + { + // received rst + } + else if (se.SocketErrorCode == SocketError.NotConnected) + { + // The application tried to send or receive data, and the System.Net.Sockets.Socket is not connected. + } + else if (se.SocketErrorCode == SocketError.HostUnreachable) + { + // There is no network route to the specified host. + } + else if (se.SocketErrorCode == SocketError.TimedOut) + { + // The connection attempt timed out, or the connected host has failed to respond. + } + else + { + logger.Warn(e); + } + } + else if (e is ObjectDisposedException) + { + } + else if (e is Win32Exception ex) + { + // Win32Exception (0x80004005): A 32 bit processes cannot access modules of a 64 bit process. + if ((uint)ex.ErrorCode != 0x80004005) + logger.Warn(e); + + } + else if (e is ProxyException pe) + { + switch (pe.Type) + { + case ProxyExceptionType.FailToRun: + case ProxyExceptionType.QueryReturnMalformed: + case ProxyExceptionType.SysproxyExitError: + logger.Error($"sysproxy - {pe.Type}:{pe.Message}"); + break; + case ProxyExceptionType.QueryReturnEmpty: + case ProxyExceptionType.Unspecific: + logger.Error($"sysproxy - {pe.Type}"); + break; + } + } + else + { + logger.Warn(e); + } + } + } +} diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs b/Shadowsocks.Crypto/Crypto/AEAD/AEADAesGcmNativeCrypto.cs similarity index 88% rename from shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs rename to Shadowsocks.Crypto/Crypto/AEAD/AEADAesGcmNativeCrypto.cs index 81cb3c34..b2552479 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/AEAD/AEADAesGcmNativeCrypto.cs @@ -1,12 +1,12 @@ -using System; +using System; using System.Collections.Generic; using System.Security.Cryptography; -namespace Shadowsocks.Encryption.AEAD +namespace Shadowsocks.Crypto.AEAD { - public class AEADAesGcmNativeEncryptor : AEADEncryptor + public class AEADAesGcmNativeCrypto : AEADCrypto { - public AEADAesGcmNativeEncryptor(string method, string password) : base(method, password) + public AEADAesGcmNativeCrypto(string method, string password) : base(method, password) { } @@ -51,4 +51,4 @@ namespace Shadowsocks.Encryption.AEAD return clen; } } -} \ No newline at end of file +} diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs b/Shadowsocks.Crypto/Crypto/AEAD/AEADBouncyCastleCrypto.cs similarity index 91% rename from shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs rename to Shadowsocks.Crypto/Crypto/AEAD/AEADBouncyCastleCrypto.cs index 950e4785..82433bbe 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/AEAD/AEADBouncyCastleCrypto.cs @@ -1,16 +1,17 @@ -using System; +using System; using System.Collections.Generic; + using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; -namespace Shadowsocks.Encryption.AEAD +namespace Shadowsocks.Crypto.AEAD { - public class AEADBouncyCastleEncryptor : AEADEncryptor + public class AEADBouncyCastleCrypto : AEADCrypto { IAeadCipher aead; bool enc; - public AEADBouncyCastleEncryptor(string method, string password) : base(method, password) + public AEADBouncyCastleCrypto(string method, string password) : base(method, password) { aead = cipherFamily switch { diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs b/Shadowsocks.Crypto/Crypto/AEAD/AEADCrypto.cs similarity index 88% rename from shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs rename to Shadowsocks.Crypto/Crypto/AEAD/AEADCrypto.cs index 5e5f985d..b3dbd644 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/AEAD/AEADCrypto.cs @@ -1,320 +1,322 @@ -using NLog; -using Shadowsocks.Controller; -using Shadowsocks.Encryption.Exception; -using Shadowsocks.Encryption.Stream; -using System; -using System.Collections.Generic; -using System.Net; -using System.Runtime.CompilerServices; -using System.Text; - -namespace Shadowsocks.Encryption.AEAD -{ - public abstract class AEADEncryptor : EncryptorBase - { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - // We are using the same saltLen and keyLen - private const string Info = "ss-subkey"; - private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info); - - // every connection should create its own buffer - private readonly byte[] buffer = new byte[65536]; - private int bufPtr = 0; - - public const int ChunkLengthBytes = 2; - public const uint ChunkLengthMask = 0x3FFFu; - - protected CipherFamily cipherFamily; - protected CipherInfo CipherInfo; - protected static byte[] masterKey = Array.Empty(); - protected byte[] sessionKey = Array.Empty(); - protected int keyLen; - protected int saltLen; - protected int tagLen; - protected int nonceLen; - - protected byte[] salt; - protected byte[] nonce; - - // Is first packet - protected bool saltReady; - - // Is first chunk(tcp request) - protected bool tcpRequestSent; - - public AEADEncryptor(string method, string password) - : base(method, password) - { - CipherInfo = GetCiphers()[method.ToLower()]; - cipherFamily = CipherInfo.Type; - AEADCipherParameter parameter = (AEADCipherParameter)CipherInfo.CipherParameter; - keyLen = parameter.KeySize; - saltLen = parameter.SaltSize; - tagLen = parameter.TagSize; - nonceLen = parameter.NonceSize; - - InitKey(password); - - salt = new byte[saltLen]; - // Initialize all-zero nonce for each connection - nonce = new byte[nonceLen]; - logger.Dump($"masterkey {instanceId}", masterKey, keyLen); - logger.Dump($"nonce {instanceId}", nonce, keyLen); - } - - protected abstract Dictionary GetCiphers(); - - protected void InitKey(string password) - { - byte[] passbuf = Encoding.UTF8.GetBytes(password); - // init master key - if (masterKey == null) - { - masterKey = new byte[keyLen]; - } - - if (masterKey.Length != keyLen) - { - Array.Resize(ref masterKey, keyLen); - } - - StreamEncryptor.LegacyDeriveKey(passbuf, masterKey, keyLen); - // init session key - sessionKey = new byte[keyLen]; - } - - public virtual void InitCipher(byte[] salt, bool isEncrypt) - { - this.salt = new byte[saltLen]; - Array.Copy(salt, this.salt, saltLen); - - CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0); - - logger.Dump($"salt {instanceId}", salt, saltLen); - logger.Dump($"sessionkey {instanceId}", sessionKey, keyLen); - } - - public abstract int CipherEncrypt(ReadOnlySpan plain, Span cipher); - public abstract int CipherDecrypt(Span plain, ReadOnlySpan cipher); - - #region TCP - - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int Encrypt(ReadOnlySpan plain, Span cipher) - { - // push data - Span tmp = buffer.AsSpan(0, plain.Length + bufPtr); - plain.CopyTo(tmp.Slice(bufPtr)); - - int outlength = 0; - if (!saltReady) - { - saltReady = true; - // Generate salt - byte[] saltBytes = RNG.GetBytes(saltLen); - InitCipher(saltBytes, true); - saltBytes.CopyTo(cipher); - outlength = saltLen; - } - - if (!tcpRequestSent) - { - tcpRequestSent = true; - - // read addr byte to encrypt - int encAddrBufLength = ChunkEncrypt(tmp.Slice(0, AddressBufferLength), cipher.Slice(outlength)); - tmp = tmp.Slice(AddressBufferLength); - outlength += encAddrBufLength; - } - - // handle other chunks - while (true) - { - // calculate next chunk size - int bufSize = tmp.Length; - if (bufSize <= 0) - { - return outlength; - } - - int chunklength = (int)Math.Min(bufSize, ChunkLengthMask); - // read next chunk - int encChunkLength = ChunkEncrypt(tmp.Slice(0, chunklength), cipher.Slice(outlength)); - tmp = tmp.Slice(chunklength); - outlength += encChunkLength; - - // check if we have enough space for outbuf - // if not, keep buf for next run, at this condition, buffer is not empty - if (outlength + TCPHandler.ChunkOverheadSize > TCPHandler.BufferSize) - { - logger.Debug("enc outbuf almost full, giving up"); - - // write rest data to head of shared buffer - tmp.CopyTo(buffer); - bufPtr = tmp.Length; - - return outlength; - } - // check if buffer empty - bufSize = tmp.Length; - if (bufSize <= 0) - { - logger.Debug("No more data to encrypt, leaving"); - return outlength; - } - } - } - - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int Decrypt(Span plain, ReadOnlySpan cipher) - { - int outlength = 0; - // drop all into buffer - Span tmp = buffer.AsSpan(0, cipher.Length + bufPtr); - cipher.CopyTo(tmp.Slice(bufPtr)); - int bufSize = tmp.Length; - - logger.Debug($"{instanceId} decrypt tcp, read salt: {!saltReady}"); - if (!saltReady) - { - // check if we get the leading salt - if (bufSize <= saltLen) - { - // need more, write back cache - tmp.CopyTo(buffer); - bufPtr = tmp.Length; - return outlength; - } - saltReady = true; - - byte[] salt = tmp.Slice(0, saltLen).ToArray(); - tmp = tmp.Slice(saltLen); - - InitCipher(salt, false); - } - - // handle chunks - while (true) - { - bufSize = tmp.Length; - // check if we have any data - if (bufSize <= 0) - { - logger.Trace("No data in buffer"); - return outlength; - } - - // first get chunk length - if (bufSize <= ChunkLengthBytes + tagLen) - { - // so we only have chunk length and its tag? - // wait more - logger.Trace($"{instanceId} not enough data to decrypt chunk. write {tmp.Length} byte back to buffer."); - tmp.CopyTo(buffer); - bufPtr = tmp.Length; - return outlength; - } - logger.Trace($"{instanceId} try decrypt to offset {outlength}"); - int len = ChunkDecrypt(plain.Slice(outlength), tmp); - if (len <= 0) - { - logger.Trace($"{instanceId} no chunk decrypted, write {tmp.Length} byte back to buffer."); - - // no chunk decrypted - tmp.CopyTo(buffer); - bufPtr = tmp.Length; - return outlength; - } - logger.Trace($"{instanceId} decrypted {len} to offset {outlength}"); - - // drop decrypted data - tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen); - outlength += len; - - // logger.Debug("aead dec outlength " + outlength); - if (outlength + 100 > TCPHandler.BufferSize) - { - logger.Trace($"{instanceId} output almost full, write {tmp.Length} byte back to buffer."); - tmp.CopyTo(buffer); - bufPtr = tmp.Length; - return outlength; - } - bufSize = tmp.Length; - // check if we already done all of them - if (bufSize <= 0) - { - bufPtr = 0; - logger.Debug($"{instanceId} no data in buffer, already all done"); - return outlength; - } - } - } - - #endregion - - #region UDP - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int EncryptUDP(ReadOnlySpan plain, Span cipher) - { - RNG.GetSpan(cipher.Slice(0, saltLen)); - InitCipher(cipher.Slice(0, saltLen).ToArray(), true); - return saltLen + CipherEncrypt(plain, cipher.Slice(saltLen)); - } - - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int DecryptUDP(Span plain, ReadOnlySpan cipher) - { - InitCipher(cipher.Slice(0, saltLen).ToArray(), false); - return CipherDecrypt(plain, cipher.Slice(saltLen)); - } - - #endregion - - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - private int ChunkEncrypt(ReadOnlySpan plain, Span cipher) - { - if (plain.Length > ChunkLengthMask) - { - logger.Error("enc chunk too big"); - throw new CryptoErrorException(); - } - - byte[] lenbuf = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)plain.Length)); - int cipherLenSize = CipherEncrypt(lenbuf, cipher); - CryptoUtils.SodiumIncrement(nonce); - int cipherDataSize = CipherEncrypt(plain, cipher.Slice(cipherLenSize)); - CryptoUtils.SodiumIncrement(nonce); - - return cipherLenSize + cipherDataSize; - } - - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - private int ChunkDecrypt(Span plain, ReadOnlySpan cipher) - { - // try to dec chunk len - byte[] chunkLengthByte = new byte[ChunkLengthBytes]; - CipherDecrypt(chunkLengthByte, cipher.Slice(0, ChunkLengthBytes + tagLen)); - ushort chunkLength = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(chunkLengthByte, 0)); - if (chunkLength > ChunkLengthMask) - { - // we get invalid chunk - logger.Error($"{instanceId} Invalid chunk length: {chunkLength}"); - throw new CryptoErrorException(); - } - // logger.Debug("Get the real chunk len:" + chunkLength); - int bufSize = cipher.Length; - if (bufSize < ChunkLengthBytes + tagLen /* we haven't remove them */+ chunkLength + tagLen) - { - logger.Debug($"{instanceId} need {ChunkLengthBytes + tagLen + chunkLength + tagLen}, but have {cipher.Length}"); - return 0; - } - CryptoUtils.SodiumIncrement(nonce); - // we have enough data to decrypt one chunk - // drop chunk len and its tag from buffer - int len = CipherDecrypt(plain, cipher.Slice(ChunkLengthBytes + tagLen, chunkLength + tagLen)); - CryptoUtils.SodiumIncrement(nonce); - logger.Trace($"{instanceId} decrypted {len} byte chunk used {ChunkLengthBytes + tagLen + chunkLength + tagLen} from {cipher.Length}"); - return len; - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Net; +using System.Runtime.CompilerServices; +using System.Text; + +using NLog; + +using Shadowsocks.Crypto.Exception; +using Shadowsocks.Crypto.Stream; + +namespace Shadowsocks.Crypto.AEAD +{ + public abstract class AEADCrypto : CryptoBase + { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + // We are using the same saltLen and keyLen + private const string Info = "ss-subkey"; + private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info); + + // every connection should create its own buffer + private readonly byte[] buffer = new byte[65536]; + private int bufPtr = 0; + + public const int ChunkLengthBytes = 2; + public const uint ChunkLengthMask = 0x3FFFu; + + protected CipherFamily cipherFamily; + protected CipherInfo CipherInfo; + protected static byte[] masterKey = Array.Empty(); + protected byte[] sessionKey = Array.Empty(); + protected int keyLen; + protected int saltLen; + protected int tagLen; + protected int nonceLen; + + protected byte[] salt; + protected byte[] nonce; + + // Is first packet + protected bool saltReady; + + // Is first chunk(tcp request) + protected bool tcpRequestSent; + + public AEADCrypto(string method, string password) + : base(method, password) + { + CipherInfo = GetCiphers()[method.ToLower()]; + cipherFamily = CipherInfo.Type; + AEADCipherParameter parameter = (AEADCipherParameter)CipherInfo.CipherParameter; + keyLen = parameter.KeySize; + saltLen = parameter.SaltSize; + tagLen = parameter.TagSize; + nonceLen = parameter.NonceSize; + + InitKey(password); + + salt = new byte[saltLen]; + // Initialize all-zero nonce for each connection + nonce = new byte[nonceLen]; + + logger.Dump($"masterkey {instanceId}", masterKey, keyLen); + logger.Dump($"nonce {instanceId}", nonce, keyLen); + } + + protected abstract Dictionary GetCiphers(); + + protected void InitKey(string password) + { + byte[] passbuf = Encoding.UTF8.GetBytes(password); + // init master key + if (masterKey == null) + { + masterKey = new byte[keyLen]; + } + + if (masterKey.Length != keyLen) + { + Array.Resize(ref masterKey, keyLen); + } + + StreamCrypto.LegacyDeriveKey(passbuf, masterKey, keyLen); + // init session key + sessionKey = new byte[keyLen]; + } + + public virtual void InitCipher(byte[] salt, bool isEncrypt) + { + this.salt = new byte[saltLen]; + Array.Copy(salt, this.salt, saltLen); + + CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0); + + logger.Dump($"salt {instanceId}", salt, saltLen); + logger.Dump($"sessionkey {instanceId}", sessionKey, keyLen); + } + + public abstract int CipherEncrypt(ReadOnlySpan plain, Span cipher); + public abstract int CipherDecrypt(Span plain, ReadOnlySpan cipher); + + #region TCP + + [MethodImpl(MethodImplOptions.Synchronized)] + public override int Encrypt(ReadOnlySpan plain, Span cipher) + { + // push data + Span tmp = buffer.AsSpan(0, plain.Length + bufPtr); + plain.CopyTo(tmp.Slice(bufPtr)); + + int outlength = 0; + if (!saltReady) + { + saltReady = true; + // Generate salt + byte[] saltBytes = RNG.GetBytes(saltLen); + InitCipher(saltBytes, true); + saltBytes.CopyTo(cipher); + outlength = saltLen; + } + + if (!tcpRequestSent) + { + tcpRequestSent = true; + + // read addr byte to encrypt + int encAddrBufLength = ChunkEncrypt(tmp.Slice(0, AddressBufferLength), cipher.Slice(outlength)); + tmp = tmp.Slice(AddressBufferLength); + outlength += encAddrBufLength; + } + + // handle other chunks + while (true) + { + // calculate next chunk size + int bufSize = tmp.Length; + if (bufSize <= 0) + { + return outlength; + } + + int chunklength = (int)Math.Min(bufSize, ChunkLengthMask); + // read next chunk + int encChunkLength = ChunkEncrypt(tmp.Slice(0, chunklength), cipher.Slice(outlength)); + tmp = tmp.Slice(chunklength); + outlength += encChunkLength; + + // check if we have enough space for outbuf + // if not, keep buf for next run, at this condition, buffer is not empty + if (outlength + TCPParameter.ChunkOverheadSize > TCPParameter.BufferSize) + { + logger.Debug("enc outbuf almost full, giving up"); + + // write rest data to head of shared buffer + tmp.CopyTo(buffer); + bufPtr = tmp.Length; + + return outlength; + } + // check if buffer empty + bufSize = tmp.Length; + if (bufSize <= 0) + { + logger.Debug("No more data to encrypt, leaving"); + return outlength; + } + } + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public override int Decrypt(Span plain, ReadOnlySpan cipher) + { + int outlength = 0; + // drop all into buffer + Span tmp = buffer.AsSpan(0, cipher.Length + bufPtr); + cipher.CopyTo(tmp.Slice(bufPtr)); + int bufSize = tmp.Length; + + logger.Debug($"{instanceId} decrypt tcp, read salt: {!saltReady}"); + if (!saltReady) + { + // check if we get the leading salt + if (bufSize <= saltLen) + { + // need more, write back cache + tmp.CopyTo(buffer); + bufPtr = tmp.Length; + return outlength; + } + saltReady = true; + + byte[] salt = tmp.Slice(0, saltLen).ToArray(); + tmp = tmp.Slice(saltLen); + + InitCipher(salt, false); + } + + // handle chunks + while (true) + { + bufSize = tmp.Length; + // check if we have any data + if (bufSize <= 0) + { + logger.Trace("No data in buffer"); + return outlength; + } + + // first get chunk length + if (bufSize <= ChunkLengthBytes + tagLen) + { + // so we only have chunk length and its tag? + // wait more + logger.Trace($"{instanceId} not enough data to decrypt chunk. write {tmp.Length} byte back to buffer."); + tmp.CopyTo(buffer); + bufPtr = tmp.Length; + return outlength; + } + logger.Trace($"{instanceId} try decrypt to offset {outlength}"); + int len = ChunkDecrypt(plain.Slice(outlength), tmp); + if (len <= 0) + { + logger.Trace($"{instanceId} no chunk decrypted, write {tmp.Length} byte back to buffer."); + + // no chunk decrypted + tmp.CopyTo(buffer); + bufPtr = tmp.Length; + return outlength; + } + logger.Trace($"{instanceId} decrypted {len} to offset {outlength}"); + + // drop decrypted data + tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen); + outlength += len; + + // logger.Debug("aead dec outlength " + outlength); + if (outlength + 100 > TCPParameter.BufferSize) + { + logger.Trace($"{instanceId} output almost full, write {tmp.Length} byte back to buffer."); + tmp.CopyTo(buffer); + bufPtr = tmp.Length; + return outlength; + } + bufSize = tmp.Length; + // check if we already done all of them + if (bufSize <= 0) + { + bufPtr = 0; + logger.Debug($"{instanceId} no data in buffer, already all done"); + return outlength; + } + } + } + + #endregion + + #region UDP + [MethodImpl(MethodImplOptions.Synchronized)] + public override int EncryptUDP(ReadOnlySpan plain, Span cipher) + { + RNG.GetSpan(cipher.Slice(0, saltLen)); + InitCipher(cipher.Slice(0, saltLen).ToArray(), true); + return saltLen + CipherEncrypt(plain, cipher.Slice(saltLen)); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public override int DecryptUDP(Span plain, ReadOnlySpan cipher) + { + InitCipher(cipher.Slice(0, saltLen).ToArray(), false); + return CipherDecrypt(plain, cipher.Slice(saltLen)); + } + + #endregion + + [MethodImpl(MethodImplOptions.Synchronized)] + private int ChunkEncrypt(ReadOnlySpan plain, Span cipher) + { + if (plain.Length > ChunkLengthMask) + { + logger.Error("enc chunk too big"); + throw new CryptoErrorException(); + } + + byte[] lenbuf = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)plain.Length)); + int cipherLenSize = CipherEncrypt(lenbuf, cipher); + CryptoUtils.SodiumIncrement(nonce); + int cipherDataSize = CipherEncrypt(plain, cipher.Slice(cipherLenSize)); + CryptoUtils.SodiumIncrement(nonce); + + return cipherLenSize + cipherDataSize; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + private int ChunkDecrypt(Span plain, ReadOnlySpan cipher) + { + // try to dec chunk len + byte[] chunkLengthByte = new byte[ChunkLengthBytes]; + CipherDecrypt(chunkLengthByte, cipher.Slice(0, ChunkLengthBytes + tagLen)); + ushort chunkLength = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(chunkLengthByte, 0)); + if (chunkLength > ChunkLengthMask) + { + // we get invalid chunk + logger.Error($"{instanceId} Invalid chunk length: {chunkLength}"); + throw new CryptoErrorException(); + } + // logger.Debug("Get the real chunk len:" + chunkLength); + int bufSize = cipher.Length; + if (bufSize < ChunkLengthBytes + tagLen /* we haven't remove them */+ chunkLength + tagLen) + { + logger.Debug($"{instanceId} need {ChunkLengthBytes + tagLen + chunkLength + tagLen}, but have {cipher.Length}"); + return 0; + } + CryptoUtils.SodiumIncrement(nonce); + // we have enough data to decrypt one chunk + // drop chunk len and its tag from buffer + int len = CipherDecrypt(plain, cipher.Slice(ChunkLengthBytes + tagLen, chunkLength + tagLen)); + CryptoUtils.SodiumIncrement(nonce); + logger.Trace($"{instanceId} decrypted {len} byte chunk used {ChunkLengthBytes + tagLen + chunkLength + tagLen} from {cipher.Length}"); + return len; + } + } +} diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs b/Shadowsocks.Crypto/Crypto/AEAD/AEADNaClCrypto.cs similarity index 72% rename from shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs rename to Shadowsocks.Crypto/Crypto/AEAD/AEADNaClCrypto.cs index 0634332f..f12b229b 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/AEAD/AEADNaClCrypto.cs @@ -1,15 +1,16 @@ -using NaCl.Core; -using NaCl.Core.Base; using System; using System.Collections.Generic; -namespace Shadowsocks.Encryption.AEAD +using NaCl.Core; +using NaCl.Core.Base; + +namespace Shadowsocks.Crypto.AEAD { - public class AEADNaClEncryptor : AEADEncryptor + public class AEADNaClCrypto : AEADCrypto { SnufflePoly1305 enc; - public AEADNaClEncryptor(string method, string password) : base(method, password) + public AEADNaClCrypto(string method, string password) : base(method, password) { } @@ -43,16 +44,19 @@ namespace Shadowsocks.Encryption.AEAD public override int CipherEncrypt(ReadOnlySpan plain, Span cipher) { - byte[] ct = enc.Encrypt(plain, null, nonce); - ct.CopyTo(cipher); - return ct.Length; + //byte[] ct = enc.Encrypt(plain, null, nonce); + //ct.CopyTo(cipher); + //return ct.Length; + + throw new NotImplementedException(); } public override int CipherDecrypt(Span plain, ReadOnlySpan cipher) { - byte[] pt = enc.Decrypt(cipher, null, nonce); - pt.CopyTo(plain); - return pt.Length; + //byte[] pt = enc.Decrypt(cipher, null, nonce); + //pt.CopyTo(plain); + //return pt.Length; + throw new NotImplementedException(); } } } diff --git a/shadowsocks-csharp/Encryption/CipherInfo.cs b/Shadowsocks.Crypto/Crypto/CipherInfo.cs similarity index 92% rename from shadowsocks-csharp/Encryption/CipherInfo.cs rename to Shadowsocks.Crypto/Crypto/CipherInfo.cs index 210a4027..93e1d83c 100644 --- a/shadowsocks-csharp/Encryption/CipherInfo.cs +++ b/Shadowsocks.Crypto/Crypto/CipherInfo.cs @@ -1,6 +1,4 @@ -using Shadowsocks.Controller; - -namespace Shadowsocks.Encryption +namespace Shadowsocks.Crypto { public enum CipherFamily { @@ -96,7 +94,9 @@ namespace Shadowsocks.Encryption public override string ToString() { - return StandardState == CipherStandardState.InUse ? Name : $"{Name} ({I18N.GetString(StandardState.ToString().ToLower())})"; + // TODO: + // return StandardState == CipherStandardState.InUse ? Name : $"{Name} ({I18N.GetString(StandardState.ToString().ToLower())})"; + return ""; } public string ToString(bool verbose) { @@ -108,4 +108,4 @@ namespace Shadowsocks.Encryption return $"{Name} {StandardState} {CipherParameter}"; } } -} \ No newline at end of file +} diff --git a/shadowsocks-csharp/Encryption/EncryptorBase.cs b/Shadowsocks.Crypto/Crypto/CryptoBase.cs similarity index 83% rename from shadowsocks-csharp/Encryption/EncryptorBase.cs rename to Shadowsocks.Crypto/Crypto/CryptoBase.cs index 7effb0c2..1fa95af9 100644 --- a/shadowsocks-csharp/Encryption/EncryptorBase.cs +++ b/Shadowsocks.Crypto/Crypto/CryptoBase.cs @@ -1,49 +1,48 @@ -using System; -using System.Runtime.CompilerServices; - -namespace Shadowsocks.Encryption -{ - public abstract class EncryptorBase : IEncryptor - { - private static int _currentId = 0; - - public const int MaxInputSize = 32768; - - public const int MAX_DOMAIN_LEN = 255; - public const int ADDR_PORT_LEN = 2; - public const int ADDR_ATYP_LEN = 1; - - public const int ATYP_IPv4 = 0x01; - public const int ATYP_DOMAIN = 0x03; - public const int ATYP_IPv6 = 0x04; - - public const int MD5Length = 16; - - // for debugging only, give it a number to trace data stream - public readonly int instanceId; - - protected EncryptorBase(string method, string password) - { - instanceId = _currentId; - _currentId++; - - Method = method; - Password = password; - } - - protected string Method; - protected string Password; - - public override string ToString() - { - return $"{instanceId}({Method},{Password})"; - } - - public abstract int Encrypt(ReadOnlySpan plain, Span cipher); - public abstract int Decrypt(Span plain, ReadOnlySpan cipher); - public abstract int EncryptUDP(ReadOnlySpan plain, Span cipher); - public abstract int DecryptUDP(Span plain, ReadOnlySpan cipher); - - public int AddressBufferLength { get; set; } = -1; - } -} \ No newline at end of file +using System; + +namespace Shadowsocks.Crypto +{ + public abstract class CryptoBase : ICrypto + { + private static int _currentId = 0; + + public const int MaxInputSize = 32768; + + public const int MAX_DOMAIN_LEN = 255; + public const int ADDR_PORT_LEN = 2; + public const int ADDR_ATYP_LEN = 1; + + public const int ATYP_IPv4 = 0x01; + public const int ATYP_DOMAIN = 0x03; + public const int ATYP_IPv6 = 0x04; + + public const int MD5Length = 16; + + // for debugging only, give it a number to trace data stream + public readonly int instanceId; + + protected CryptoBase(string method, string password) + { + instanceId = _currentId; + _currentId++; + + Method = method; + Password = password; + } + + protected string Method; + protected string Password; + + public override string ToString() + { + return $"{instanceId}({Method},{Password})"; + } + + public abstract int Encrypt(ReadOnlySpan plain, Span cipher); + public abstract int Decrypt(Span plain, ReadOnlySpan cipher); + public abstract int EncryptUDP(ReadOnlySpan plain, Span cipher); + public abstract int DecryptUDP(Span plain, ReadOnlySpan cipher); + + public int AddressBufferLength { get; set; } = -1; + } +} diff --git a/shadowsocks-csharp/Encryption/EncryptorFactory.cs b/Shadowsocks.Crypto/Crypto/CryptoFactory.cs similarity index 74% rename from shadowsocks-csharp/Encryption/EncryptorFactory.cs rename to Shadowsocks.Crypto/Crypto/CryptoFactory.cs index e3418257..01aaec72 100644 --- a/shadowsocks-csharp/Encryption/EncryptorFactory.cs +++ b/Shadowsocks.Crypto/Crypto/CryptoFactory.cs @@ -1,119 +1,123 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; -using Shadowsocks.Encryption.AEAD; -using Shadowsocks.Encryption.Stream; - -namespace Shadowsocks.Encryption -{ - public static class EncryptorFactory - { - public static string DefaultCipher = "chacha20-ietf-poly1305"; - - private static readonly Dictionary _registeredEncryptors = new Dictionary(); - private static readonly Dictionary ciphers = new Dictionary(); - private static readonly Type[] ConstructorTypes = { typeof(string), typeof(string) }; - - static EncryptorFactory() - { - foreach (var method in StreamPlainNativeEncryptor.SupportedCiphers()) - { - if (!_registeredEncryptors.ContainsKey(method.Key)) - { - ciphers.Add(method.Key, method.Value); - _registeredEncryptors.Add(method.Key, typeof(StreamPlainNativeEncryptor)); - } - } - foreach (var method in StreamRc4NativeEncryptor.SupportedCiphers()) - { - if (!_registeredEncryptors.ContainsKey(method.Key)) - { - ciphers.Add(method.Key, method.Value); - _registeredEncryptors.Add(method.Key, typeof(StreamRc4NativeEncryptor)); - } - } - foreach (var method in StreamAesCfbBouncyCastleEncryptor.SupportedCiphers()) - { - if (!_registeredEncryptors.ContainsKey(method.Key)) - { - ciphers.Add(method.Key, method.Value); - _registeredEncryptors.Add(method.Key, typeof(StreamAesCfbBouncyCastleEncryptor)); - } - } - foreach (var method in StreamChachaNaClEncryptor.SupportedCiphers()) - { - if (!_registeredEncryptors.ContainsKey(method.Key)) - { - ciphers.Add(method.Key, method.Value); - _registeredEncryptors.Add(method.Key, typeof(StreamChachaNaClEncryptor)); - } - } - - - foreach (var method in AEADAesGcmNativeEncryptor.SupportedCiphers()) - { - if (!_registeredEncryptors.ContainsKey(method.Key)) - { - ciphers.Add(method.Key, method.Value); - _registeredEncryptors.Add(method.Key, typeof(AEADAesGcmNativeEncryptor)); - } - } - foreach (var method in AEADNaClEncryptor.SupportedCiphers()) - { - if (!_registeredEncryptors.ContainsKey(method.Key)) - { - ciphers.Add(method.Key, method.Value); - _registeredEncryptors.Add(method.Key, typeof(AEADNaClEncryptor)); - } - } - } - - public static IEncryptor GetEncryptor(string method, string password) - { - if (string.IsNullOrEmpty(method)) - { - method = Model.Server.DefaultMethod; - } - - method = method.ToLowerInvariant(); - bool ok = _registeredEncryptors.TryGetValue(method, out Type t); - if (!ok) - { - t = _registeredEncryptors[DefaultCipher]; - } - - ConstructorInfo c = t?.GetConstructor(ConstructorTypes) ?? - throw new TypeLoadException("can't load constructor"); - if (c == null) throw new System.Exception("Invalid ctor"); - IEncryptor result = (IEncryptor)c.Invoke(new object[] { method, password }); - return result; - } - - public static string DumpRegisteredEncryptor() - { - var sb = new StringBuilder(); - sb.Append(Environment.NewLine); - sb.AppendLine("-------------------------"); - sb.AppendLine("Registered Encryptor Info"); - foreach (var encryptor in _registeredEncryptors) - { - sb.AppendLine($"{ciphers[encryptor.Key].ToString(true)} => {encryptor.Value.Name}"); - } - // use ----- instead of =======, so when user paste it to Github, it won't became title - sb.AppendLine("-------------------------"); - return sb.ToString(); - } - - public static CipherInfo GetCipherInfo(string name) - { - // TODO: Replace cipher when required not exist - return ciphers[name]; - } - - public static IEnumerable ListAvaliableCiphers() - { - return ciphers.Values; - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +using Shadowsocks.Common.Crypto; +using Shadowsocks.Common.Model; +using Shadowsocks.Crypto.AEAD; +using Shadowsocks.Crypto.Stream; + +namespace Shadowsocks.Crypto +{ + public static class CryptoFactory + { + public static string DefaultCipher = "chacha20-ietf-poly1305"; + + private static readonly Dictionary _registeredEncryptors = new Dictionary(); + private static readonly Dictionary ciphers = new Dictionary(); + private static readonly Type[] ConstructorTypes = { typeof(string), typeof(string) }; + + static CryptoFactory() + { + foreach (var method in StreamPlainNativeCrypto.SupportedCiphers()) + { + if (!_registeredEncryptors.ContainsKey(method.Key)) + { + ciphers.Add(method.Key, method.Value); + _registeredEncryptors.Add(method.Key, typeof(StreamPlainNativeCrypto)); + } + } + foreach (var method in StreamRc4NativeCrypto.SupportedCiphers()) + { + if (!_registeredEncryptors.ContainsKey(method.Key)) + { + ciphers.Add(method.Key, method.Value); + _registeredEncryptors.Add(method.Key, typeof(StreamRc4NativeCrypto)); + } + } + foreach (var method in StreamAesCfbBouncyCastleCrypto.SupportedCiphers()) + { + if (!_registeredEncryptors.ContainsKey(method.Key)) + { + ciphers.Add(method.Key, method.Value); + _registeredEncryptors.Add(method.Key, typeof(StreamAesCfbBouncyCastleCrypto)); + } + } + foreach (var method in StreamChachaNaClCrypto.SupportedCiphers()) + { + if (!_registeredEncryptors.ContainsKey(method.Key)) + { + ciphers.Add(method.Key, method.Value); + _registeredEncryptors.Add(method.Key, typeof(StreamChachaNaClCrypto)); + } + } + + + foreach (var method in AEADAesGcmNativeCrypto.SupportedCiphers()) + { + if (!_registeredEncryptors.ContainsKey(method.Key)) + { + ciphers.Add(method.Key, method.Value); + _registeredEncryptors.Add(method.Key, typeof(AEADAesGcmNativeCrypto)); + } + } + foreach (var method in AEADNaClCrypto.SupportedCiphers()) + { + if (!_registeredEncryptors.ContainsKey(method.Key)) + { + ciphers.Add(method.Key, method.Value); + _registeredEncryptors.Add(method.Key, typeof(AEADNaClCrypto)); + } + } + } + + public static ICrypto GetEncryptor(string method, string password) + { + if (string.IsNullOrEmpty(method)) + { + // todo + //method = IoCManager.Container.Resolve().GetDefaultMethod(); + } + + method = method.ToLowerInvariant(); + bool ok = _registeredEncryptors.TryGetValue(method, out Type t); + if (!ok) + { + t = _registeredEncryptors[DefaultCipher]; + } + + ConstructorInfo c = t?.GetConstructor(ConstructorTypes) ?? + throw new TypeLoadException("can't load constructor"); + if (c == null) throw new System.Exception("Invalid ctor"); + ICrypto result = (ICrypto)c.Invoke(new object[] { method, password }); + return result; + } + + public static string DumpRegisteredEncryptor() + { + var sb = new StringBuilder(); + sb.Append(Environment.NewLine); + sb.AppendLine("-------------------------"); + sb.AppendLine("Registered Encryptor Info"); + foreach (var encryptor in _registeredEncryptors) + { + sb.AppendLine($"{ciphers[encryptor.Key].ToString(true)} => {encryptor.Value.Name}"); + } + // use ----- instead of =======, so when user paste it to Github, it won't became title + sb.AppendLine("-------------------------"); + return sb.ToString(); + } + + public static CipherInfo GetCipherInfo(string name) + { + // TODO: Replace cipher when required not exist + return ciphers[name]; + } + + public static IEnumerable ListAvaliableCiphers() + { + return ciphers.Values; + } + } +} diff --git a/shadowsocks-csharp/Encryption/Exception/CryptoException.cs b/Shadowsocks.Crypto/Crypto/Exception/CryptoException.cs similarity index 84% rename from shadowsocks-csharp/Encryption/Exception/CryptoException.cs rename to Shadowsocks.Crypto/Crypto/Exception/CryptoException.cs index 2871affc..bf05486c 100644 --- a/shadowsocks-csharp/Encryption/Exception/CryptoException.cs +++ b/Shadowsocks.Crypto/Crypto/Exception/CryptoException.cs @@ -1,17 +1,17 @@ -namespace Shadowsocks.Encryption.Exception -{ - public class CryptoErrorException : System.Exception - { - public CryptoErrorException() - { - } - - public CryptoErrorException(string msg) : base(msg) - { - } - - public CryptoErrorException(string message, System.Exception innerException) : base(message, innerException) - { - } - } -} \ No newline at end of file +namespace Shadowsocks.Crypto.Exception +{ + public class CryptoErrorException : System.Exception + { + public CryptoErrorException() + { + } + + public CryptoErrorException(string msg) : base(msg) + { + } + + public CryptoErrorException(string message, System.Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/shadowsocks-csharp/Encryption/IEncryptor.cs b/Shadowsocks.Crypto/Crypto/ICrypto.cs similarity index 79% rename from shadowsocks-csharp/Encryption/IEncryptor.cs rename to Shadowsocks.Crypto/Crypto/ICrypto.cs index 060d57a7..48c5f210 100644 --- a/shadowsocks-csharp/Encryption/IEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/ICrypto.cs @@ -1,14 +1,14 @@ -using System; - -namespace Shadowsocks.Encryption -{ - public interface IEncryptor - { - /* length == -1 means not used */ - int AddressBufferLength { set; get; } - int Encrypt(ReadOnlySpan plain, Span cipher); - int Decrypt(Span plain, ReadOnlySpan cipher); - int EncryptUDP(ReadOnlySpan plain, Span cipher); - int DecryptUDP(Span plain, ReadOnlySpan cipher); - } -} +using System; + +namespace Shadowsocks.Crypto +{ + public interface ICrypto + { + /* length == -1 means not used */ + int AddressBufferLength { set; get; } + int Encrypt(ReadOnlySpan plain, Span cipher); + int Decrypt(Span plain, ReadOnlySpan cipher); + int EncryptUDP(ReadOnlySpan plain, Span cipher); + int DecryptUDP(Span plain, ReadOnlySpan cipher); + } +} diff --git a/shadowsocks-csharp/Encryption/RNG.cs b/Shadowsocks.Crypto/Crypto/RNG.cs similarity index 91% rename from shadowsocks-csharp/Encryption/RNG.cs rename to Shadowsocks.Crypto/Crypto/RNG.cs index 276a9b1c..5f8711d3 100644 --- a/shadowsocks-csharp/Encryption/RNG.cs +++ b/Shadowsocks.Crypto/Crypto/RNG.cs @@ -1,50 +1,50 @@ -using System; -using System.Security.Cryptography; - -namespace Shadowsocks.Encryption -{ - public static class RNG - { - private static RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider(); - - public static void Reload() - { - _rng.Dispose(); - _rng = new RNGCryptoServiceProvider(); - } - - public static void GetSpan(Span span) - { - _rng.GetBytes(span); - } - - public static Span GetSpan(int length) - { - Span span = new byte[length]; - _rng.GetBytes(span); - return span; - } - - public static byte[] GetBytes(int length) - { - byte[] buf = new byte[length]; - _rng.GetBytes(buf); - return buf; - } - - public static void GetBytes(byte[] buf, int len) - { - try - { - _rng.GetBytes(buf, 0, len); - } - catch - { - // the backup way - byte[] tmp = new byte[len]; - _rng.GetBytes(tmp); - Buffer.BlockCopy(tmp, 0, buf, 0, len); - } - } - } -} \ No newline at end of file +using System; +using System.Security.Cryptography; + +namespace Shadowsocks.Crypto +{ + public static class RNG + { + private static RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider(); + + public static void Reload() + { + _rng.Dispose(); + _rng = new RNGCryptoServiceProvider(); + } + + public static void GetSpan(Span span) + { + _rng.GetBytes(span); + } + + public static Span GetSpan(int length) + { + Span span = new byte[length]; + _rng.GetBytes(span); + return span; + } + + public static byte[] GetBytes(int length) + { + byte[] buf = new byte[length]; + _rng.GetBytes(buf); + return buf; + } + + public static void GetBytes(byte[] buf, int len) + { + try + { + _rng.GetBytes(buf, 0, len); + } + catch + { + // the backup way + byte[] tmp = new byte[len]; + _rng.GetBytes(tmp); + Buffer.BlockCopy(tmp, 0, buf, 0, len); + } + } + } +} diff --git a/shadowsocks-csharp/Encryption/Stream/ExtendedCfbBlockCipher.cs b/Shadowsocks.Crypto/Crypto/Stream/ExtendedCfbBlockCipher.cs similarity index 100% rename from shadowsocks-csharp/Encryption/Stream/ExtendedCfbBlockCipher.cs rename to Shadowsocks.Crypto/Crypto/Stream/ExtendedCfbBlockCipher.cs diff --git a/shadowsocks-csharp/Encryption/Stream/StreamAesBouncyCastleEncryptor.cs b/Shadowsocks.Crypto/Crypto/Stream/StreamAesBouncyCastleCrypto.cs similarity index 89% rename from shadowsocks-csharp/Encryption/Stream/StreamAesBouncyCastleEncryptor.cs rename to Shadowsocks.Crypto/Crypto/Stream/StreamAesBouncyCastleCrypto.cs index 9e0cb8db..fde7bb9b 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamAesBouncyCastleEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/Stream/StreamAesBouncyCastleCrypto.cs @@ -1,19 +1,19 @@ -using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Shadowsocks.Encryption.Stream +namespace Shadowsocks.Crypto.Stream { - public class StreamAesCfbBouncyCastleEncryptor : StreamEncryptor + public class StreamAesCfbBouncyCastleCrypto : StreamCrypto { readonly byte[] cfbBuf = new byte[MaxInputSize + 128]; int ptr = 0; readonly ExtendedCfbBlockCipher b; - public StreamAesCfbBouncyCastleEncryptor(string method, string password) : base(method, password) + public StreamAesCfbBouncyCastleCrypto(string method, string password) : base(method, password) { b = new ExtendedCfbBlockCipher(new AesEngine(), 128); } @@ -36,7 +36,7 @@ namespace Shadowsocks.Encryption.Stream return cipher.Length; } - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] + [MethodImpl( MethodImplOptions.AggressiveInlining)] private void CipherUpdate(ReadOnlySpan i, Span o) { Span ob = new byte[o.Length + 128]; diff --git a/shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs b/Shadowsocks.Crypto/Crypto/Stream/StreamChachaNaClCrypto.cs similarity index 88% rename from shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs rename to Shadowsocks.Crypto/Crypto/Stream/StreamChachaNaClCrypto.cs index 8346f171..3d007cc2 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/Stream/StreamChachaNaClCrypto.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using NaCl.Core; -namespace Shadowsocks.Encryption.Stream +namespace Shadowsocks.Crypto.Stream { - public class StreamChachaNaClEncryptor : StreamEncryptor + public class StreamChachaNaClCrypto : StreamCrypto { const int BlockSize = 64; @@ -22,7 +22,7 @@ namespace Shadowsocks.Encryption.Stream int remain = 0; // increase counter manually... int ic = 0; - public StreamChachaNaClEncryptor(string method, string password) : base(method, password) + public StreamChachaNaClCrypto(string method, string password) : base(method, password) { } @@ -36,7 +36,7 @@ namespace Shadowsocks.Encryption.Stream return CipherUpdate(plain, cipher, true); } - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int CipherUpdate(ReadOnlySpan i, Span o, bool enc) { // about performance problem: diff --git a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs b/Shadowsocks.Crypto/Crypto/Stream/StreamCrypto.cs similarity index 88% rename from shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs rename to Shadowsocks.Crypto/Crypto/Stream/StreamCrypto.cs index eacd25e6..f31485be 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/Stream/StreamCrypto.cs @@ -1,179 +1,181 @@ -using NLog; -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Text; - -namespace Shadowsocks.Encryption.Stream -{ - public abstract class StreamEncryptor : EncryptorBase - { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - // shared by TCP decrypt UDP encrypt and decrypt - protected static byte[] sharedBuffer = new byte[65536]; - - // Is first packet - protected bool ivReady; - - protected CipherFamily cipherFamily; - protected CipherInfo CipherInfo; - // long-time master key - protected static byte[] key = Array.Empty(); - protected byte[] iv = Array.Empty(); - protected int keyLen; - protected int ivLen; - - public StreamEncryptor(string method, string password) - : base(method, password) - { - CipherInfo = GetCiphers()[method.ToLower()]; - cipherFamily = CipherInfo.Type; - StreamCipherParameter parameter = (StreamCipherParameter)CipherInfo.CipherParameter; - keyLen = parameter.KeySize; - ivLen = parameter.IvSize; - - InitKey(password); - - logger.Dump($"key {instanceId}", key, keyLen); - } - - protected abstract Dictionary GetCiphers(); - - private void InitKey(string password) - { - byte[] passbuf = Encoding.UTF8.GetBytes(password); - key ??= new byte[keyLen]; - if (key.Length != keyLen) - { - Array.Resize(ref key, keyLen); - } - - LegacyDeriveKey(passbuf, key, keyLen); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void LegacyDeriveKey(byte[] password, byte[] key, int keylen) - { - byte[] result = new byte[password.Length + MD5Length]; - int i = 0; - byte[] md5sum = Array.Empty(); - while (i < keylen) - { - if (i == 0) - { - md5sum = CryptoUtils.MD5(password); - } - else - { - Array.Copy(md5sum, 0, result, 0, MD5Length); - Array.Copy(password, 0, result, MD5Length, password.Length); - md5sum = CryptoUtils.MD5(result); - } - Array.Copy(md5sum, 0, key, i, Math.Min(MD5Length, keylen - i)); - i += MD5Length; - } - } - - protected virtual void InitCipher(byte[] iv, bool isEncrypt) - { - if (ivLen == 0) - { - return; - } - - this.iv = new byte[ivLen]; - Array.Copy(iv, this.iv, ivLen); - } - - protected abstract int CipherEncrypt(ReadOnlySpan plain, Span cipher); - protected abstract int CipherDecrypt(Span plain, ReadOnlySpan cipher); - - #region TCP - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int Encrypt(ReadOnlySpan plain, Span cipher) - { - int cipherOffset = 0; - logger.Trace($"{instanceId} encrypt TCP, generate iv: {!ivReady}"); - if (!ivReady) - { - // Generate IV - byte[] ivBytes = RNG.GetBytes(ivLen); - InitCipher(ivBytes, true); - ivBytes.CopyTo(cipher); - cipherOffset = ivLen; - cipher = cipher.Slice(cipherOffset); - ivReady = true; - } - int clen = CipherEncrypt(plain, cipher); - - logger.DumpBase64($"plain {instanceId}", plain); - logger.DumpBase64($"cipher {instanceId}", cipher.Slice(0, clen)); - logger.Dump($"iv {instanceId}", iv, ivLen); - return clen + cipherOffset; - } - - private int recieveCtr = 0; - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int Decrypt(Span plain, ReadOnlySpan cipher) - { - logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); - - int cipherOffset = 0; - // is first packet, need read iv - if (!ivReady) - { - // push to buffer in case of not enough data - cipher.CopyTo(sharedBuffer.AsSpan(recieveCtr)); - recieveCtr += cipher.Length; - - // not enough data for read iv, return 0 byte data - if (recieveCtr <= ivLen) - { - return 0; - } - // start decryption - ivReady = true; - if (ivLen > 0) - { - // read iv - byte[] iv = sharedBuffer.AsSpan(0, ivLen).ToArray(); - InitCipher(iv, false); - } - else - { - InitCipher(Array.Empty(), false); - } - cipherOffset += ivLen; - } - - // read all data from buffer - int len = CipherDecrypt(plain, cipher.Slice(cipherOffset)); - logger.DumpBase64($"cipher {instanceId}", cipher.Slice(cipherOffset)); - logger.DumpBase64($"plain {instanceId}", plain.Slice(0, len)); - logger.Dump($"iv {instanceId}", iv, ivLen); - return len; - } - - #endregion - - #region UDP - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int EncryptUDP(ReadOnlySpan plain, Span cipher) - { - byte[] iv = RNG.GetBytes(ivLen); - iv.CopyTo(cipher); - InitCipher(iv, true); - return ivLen + CipherEncrypt(plain, cipher.Slice(ivLen)); - } - - [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] - public override int DecryptUDP(Span plain, ReadOnlySpan cipher) - { - InitCipher(cipher.Slice(0, ivLen).ToArray(), false); - return CipherDecrypt(plain, cipher.Slice(ivLen)); - } - - #endregion - } -} \ No newline at end of file +using NLog; + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Shadowsocks.Crypto.Stream +{ + public abstract class StreamCrypto : CryptoBase + { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + // shared by TCP decrypt UDP encrypt and decrypt + protected static byte[] sharedBuffer = new byte[65536]; + + // Is first packet + protected bool ivReady; + + protected CipherFamily cipherFamily; + protected CipherInfo CipherInfo; + // long-time master key + protected static byte[] key = Array.Empty(); + protected byte[] iv = Array.Empty(); + protected int keyLen; + protected int ivLen; + + public StreamCrypto(string method, string password) + : base(method, password) + { + CipherInfo = GetCiphers()[method.ToLower()]; + cipherFamily = CipherInfo.Type; + StreamCipherParameter parameter = (StreamCipherParameter)CipherInfo.CipherParameter; + keyLen = parameter.KeySize; + ivLen = parameter.IvSize; + + InitKey(password); + + logger.Dump($"key {instanceId}", key, keyLen); + } + + protected abstract Dictionary GetCiphers(); + + private void InitKey(string password) + { + byte[] passbuf = Encoding.UTF8.GetBytes(password); + key ??= new byte[keyLen]; + if (key.Length != keyLen) + { + Array.Resize(ref key, keyLen); + } + + LegacyDeriveKey(passbuf, key, keyLen); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LegacyDeriveKey(byte[] password, byte[] key, int keylen) + { + byte[] result = new byte[password.Length + MD5Length]; + int i = 0; + byte[] md5sum = Array.Empty(); + while (i < keylen) + { + if (i == 0) + { + md5sum = CryptoUtils.MD5(password); + } + else + { + Array.Copy(md5sum, 0, result, 0, MD5Length); + Array.Copy(password, 0, result, MD5Length, password.Length); + md5sum = CryptoUtils.MD5(result); + } + Array.Copy(md5sum, 0, key, i, Math.Min(MD5Length, keylen - i)); + i += MD5Length; + } + } + + protected virtual void InitCipher(byte[] iv, bool isEncrypt) + { + if (ivLen == 0) + { + return; + } + + this.iv = new byte[ivLen]; + Array.Copy(iv, this.iv, ivLen); + } + + protected abstract int CipherEncrypt(ReadOnlySpan plain, Span cipher); + protected abstract int CipherDecrypt(Span plain, ReadOnlySpan cipher); + + #region TCP + [MethodImpl(MethodImplOptions.Synchronized)] + public override int Encrypt(ReadOnlySpan plain, Span cipher) + { + int cipherOffset = 0; + logger.Trace($"{instanceId} encrypt TCP, generate iv: {!ivReady}"); + if (!ivReady) + { + // Generate IV + byte[] ivBytes = RNG.GetBytes(ivLen); + InitCipher(ivBytes, true); + ivBytes.CopyTo(cipher); + cipherOffset = ivLen; + cipher = cipher.Slice(cipherOffset); + ivReady = true; + } + int clen = CipherEncrypt(plain, cipher); + + logger.DumpBase64($"plain {instanceId}", plain); + logger.DumpBase64($"cipher {instanceId}", cipher.Slice(0, clen)); + logger.Dump($"iv {instanceId}", iv, ivLen); + return clen + cipherOffset; + } + + private int recieveCtr = 0; + [MethodImpl(MethodImplOptions.Synchronized)] + public override int Decrypt(Span plain, ReadOnlySpan cipher) + { + logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); + + int cipherOffset = 0; + // is first packet, need read iv + if (!ivReady) + { + // push to buffer in case of not enough data + cipher.CopyTo(sharedBuffer.AsSpan(recieveCtr)); + recieveCtr += cipher.Length; + + // not enough data for read iv, return 0 byte data + if (recieveCtr <= ivLen) + { + return 0; + } + // start decryption + ivReady = true; + if (ivLen > 0) + { + // read iv + byte[] iv = sharedBuffer.AsSpan(0, ivLen).ToArray(); + InitCipher(iv, false); + } + else + { + InitCipher(Array.Empty(), false); + } + cipherOffset += ivLen; + } + + // read all data from buffer + int len = CipherDecrypt(plain, cipher.Slice(cipherOffset)); + + logger.DumpBase64($"cipher {instanceId}", cipher.Slice(cipherOffset)); + logger.DumpBase64($"plain {instanceId}", plain.Slice(0, len)); + logger.Dump($"iv {instanceId}", iv, ivLen); + return len; + } + + #endregion + + #region UDP + [MethodImpl(MethodImplOptions.Synchronized)] + public override int EncryptUDP(ReadOnlySpan plain, Span cipher) + { + byte[] iv = RNG.GetBytes(ivLen); + iv.CopyTo(cipher); + InitCipher(iv, true); + return ivLen + CipherEncrypt(plain, cipher.Slice(ivLen)); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public override int DecryptUDP(Span plain, ReadOnlySpan cipher) + { + InitCipher(cipher.Slice(0, ivLen).ToArray(), false); + return CipherDecrypt(plain, cipher.Slice(ivLen)); + } + + #endregion + } +} diff --git a/shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs b/Shadowsocks.Crypto/Crypto/Stream/StreamPlainNativeCrypto.cs similarity index 82% rename from shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs rename to Shadowsocks.Crypto/Crypto/Stream/StreamPlainNativeCrypto.cs index 8293db18..c4b02c0c 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/Stream/StreamPlainNativeCrypto.cs @@ -1,12 +1,12 @@ -using System; +using System; using System.Collections.Generic; -namespace Shadowsocks.Encryption.Stream +namespace Shadowsocks.Crypto.Stream { - public class StreamPlainNativeEncryptor : StreamEncryptor + public class StreamPlainNativeCrypto : StreamCrypto { - public StreamPlainNativeEncryptor(string method, string password) : base(method, password) + public StreamPlainNativeCrypto(string method, string password) : base(method, password) { } diff --git a/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs b/Shadowsocks.Crypto/Crypto/Stream/StreamRc4NativeCrypto.cs similarity index 92% rename from shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs rename to Shadowsocks.Crypto/Crypto/Stream/StreamRc4NativeCrypto.cs index 41f55186..260a58ae 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs +++ b/Shadowsocks.Crypto/Crypto/Stream/StreamRc4NativeCrypto.cs @@ -1,14 +1,14 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Shadowsocks.Encryption.Stream +namespace Shadowsocks.Crypto.Stream { - public class StreamRc4NativeEncryptor : StreamEncryptor + public class StreamRc4NativeCrypto : StreamCrypto { byte[] realkey = new byte[256]; byte[] sbox = new byte[256]; - public StreamRc4NativeEncryptor(string method, string password) : base(method, password) + public StreamRc4NativeCrypto(string method, string password) : base(method, password) { } @@ -94,7 +94,6 @@ namespace Shadowsocks.Encryption.Stream return s; } - [MethodImpl(MethodImplOptions.AggressiveOptimization)] private void RC4(Span s, Span data, int length) { for (int n = 0; n < length; n++) diff --git a/Shadowsocks.Crypto/Crypto/TCPInfo.cs b/Shadowsocks.Crypto/Crypto/TCPInfo.cs new file mode 100644 index 00000000..663cd7ef --- /dev/null +++ b/Shadowsocks.Crypto/Crypto/TCPInfo.cs @@ -0,0 +1,20 @@ +using Shadowsocks.Crypto.AEAD; + +namespace Shadowsocks.Crypto +{ + public static class TCPParameter + { + // each recv size. + public const int RecvSize = 2048; + + // overhead of one chunk, reserved for AEAD ciphers + // /* two tags */ + public const int ChunkOverheadSize = 16 * 2 + AEADCrypto.ChunkLengthBytes; + + // max chunk size + public const uint MaxChunkSize = AEADCrypto.ChunkLengthMask + AEADCrypto.ChunkLengthBytes + 16 * 2; + + // In general, the ciphertext length, we should take overhead into account + public const int BufferSize = RecvSize + (int)MaxChunkSize + 32 /* max salt len */; + } +} diff --git a/Shadowsocks.Crypto/Shadowsocks.Crypto.csproj b/Shadowsocks.Crypto/Shadowsocks.Crypto.csproj new file mode 100644 index 00000000..4c42fabc --- /dev/null +++ b/Shadowsocks.Crypto/Shadowsocks.Crypto.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.1 + clowwindy & community 2020 + Shadowsocks Crypto + + + + + + + + + + + + diff --git a/shadowsocks-csharp/Encryption/CryptoUtils.cs b/Shadowsocks.Crypto/Util/CryptoUtils.cs similarity index 91% rename from shadowsocks-csharp/Encryption/CryptoUtils.cs rename to Shadowsocks.Crypto/Util/CryptoUtils.cs index 6b50769f..12c93fa0 100644 --- a/shadowsocks-csharp/Encryption/CryptoUtils.cs +++ b/Shadowsocks.Crypto/Util/CryptoUtils.cs @@ -1,4 +1,4 @@ -using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; @@ -6,7 +6,7 @@ using System; using System.Security.Cryptography; using System.Threading; -namespace Shadowsocks.Encryption +namespace Shadowsocks.Crypto { public static class CryptoUtils { @@ -14,14 +14,14 @@ namespace Shadowsocks.Encryption public static byte[] MD5(byte[] b) { - var hash = new byte[EncryptorBase.MD5Length]; + var hash = new byte[CryptoBase.MD5Length]; Md5Hasher.Value.TryComputeHash(b, hash, out _); return hash; } // currently useless, just keep api same public static Span MD5(Span span) { - Span hash = new byte[EncryptorBase.MD5Length]; + Span hash = new byte[CryptoBase.MD5Length]; Md5Hasher.Value.TryComputeHash(span, hash, out _); return hash; } diff --git a/Shadowsocks.WPF/App.xaml b/Shadowsocks.WPF/App.xaml new file mode 100644 index 00000000..b283b871 --- /dev/null +++ b/Shadowsocks.WPF/App.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/Shadowsocks.WPF/App.xaml.cs b/Shadowsocks.WPF/App.xaml.cs new file mode 100644 index 00000000..36eff132 --- /dev/null +++ b/Shadowsocks.WPF/App.xaml.cs @@ -0,0 +1,20 @@ +using ReactiveUI; + +using Splat; + +using System.Reflection; +using System.Windows; + +namespace Shadowsocks.WPF +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + public App() + { + Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); + } + } +} diff --git a/Shadowsocks.WPF/AssemblyInfo.cs b/Shadowsocks.WPF/AssemblyInfo.cs new file mode 100644 index 00000000..8b5504ec --- /dev/null +++ b/Shadowsocks.WPF/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/shadowsocks-csharp/shadowsocks.ico b/Shadowsocks.WPF/Assets/shadowsocks.ico old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/shadowsocks.ico rename to Shadowsocks.WPF/Assets/shadowsocks.ico diff --git a/shadowsocks-csharp/Resources/ss128.pdn b/Shadowsocks.WPF/Assets/ss128.pdn similarity index 100% rename from shadowsocks-csharp/Resources/ss128.pdn rename to Shadowsocks.WPF/Assets/ss128.pdn diff --git a/shadowsocks-csharp/Resources/ss32.pdn b/Shadowsocks.WPF/Assets/ss32.pdn similarity index 100% rename from shadowsocks-csharp/Resources/ss32.pdn rename to Shadowsocks.WPF/Assets/ss32.pdn diff --git a/shadowsocks-csharp/Resources/ss32Fill.png b/Shadowsocks.WPF/Assets/ss32Fill.png similarity index 100% rename from shadowsocks-csharp/Resources/ss32Fill.png rename to Shadowsocks.WPF/Assets/ss32Fill.png diff --git a/shadowsocks-csharp/Resources/ss32In.png b/Shadowsocks.WPF/Assets/ss32In.png similarity index 100% rename from shadowsocks-csharp/Resources/ss32In.png rename to Shadowsocks.WPF/Assets/ss32In.png diff --git a/shadowsocks-csharp/Resources/ss32Out.png b/Shadowsocks.WPF/Assets/ss32Out.png similarity index 100% rename from shadowsocks-csharp/Resources/ss32Out.png rename to Shadowsocks.WPF/Assets/ss32Out.png diff --git a/shadowsocks-csharp/Resources/ss32Outline.png b/Shadowsocks.WPF/Assets/ss32Outline.png similarity index 100% rename from shadowsocks-csharp/Resources/ss32Outline.png rename to Shadowsocks.WPF/Assets/ss32Outline.png diff --git a/shadowsocks-csharp/Resources/ssw128.png b/Shadowsocks.WPF/Assets/ssw128.png old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/Resources/ssw128.png rename to Shadowsocks.WPF/Assets/ssw128.png diff --git a/Shadowsocks.WPF/FodyWeavers.xml b/Shadowsocks.WPF/FodyWeavers.xml new file mode 100644 index 00000000..63fc1484 --- /dev/null +++ b/Shadowsocks.WPF/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Shadowsocks.WPF/FodyWeavers.xsd b/Shadowsocks.WPF/FodyWeavers.xsd new file mode 100644 index 00000000..f3ac4762 --- /dev/null +++ b/Shadowsocks.WPF/FodyWeavers.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/Shadowsocks.WPF/Localization/LocalizationProvider.cs b/Shadowsocks.WPF/Localization/LocalizationProvider.cs new file mode 100644 index 00000000..a2625b92 --- /dev/null +++ b/Shadowsocks.WPF/Localization/LocalizationProvider.cs @@ -0,0 +1,15 @@ +using Shadowsocks.Common.Model; + +using System.Reflection; + +using WPFLocalizeExtension.Extensions; + +namespace Shadowsocks.WPF.Localization +{ + public class LocalizationProvider : ILocalizationProvider + { + private static readonly string CallingAssemblyName = Assembly.GetCallingAssembly().GetName().Name; + + public T GetLocalizedValue(string key) => LocExtension.GetLocalizedValue($"{CallingAssemblyName}:Strings:{key}"); + } +} diff --git a/shadowsocks-csharp/Localization/Strings.Designer.cs b/Shadowsocks.WPF/Localization/Strings.Designer.cs similarity index 99% rename from shadowsocks-csharp/Localization/Strings.Designer.cs rename to Shadowsocks.WPF/Localization/Strings.Designer.cs index 6aa6738a..3d7f18ef 100644 --- a/shadowsocks-csharp/Localization/Strings.Designer.cs +++ b/Shadowsocks.WPF/Localization/Strings.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Shadowsocks.Localization { +namespace Shadowsocks.WPF.Localization { using System; diff --git a/shadowsocks-csharp/Localization/Strings.fr.resx b/Shadowsocks.WPF/Localization/Strings.fr.resx similarity index 100% rename from shadowsocks-csharp/Localization/Strings.fr.resx rename to Shadowsocks.WPF/Localization/Strings.fr.resx diff --git a/shadowsocks-csharp/Localization/Strings.ja.resx b/Shadowsocks.WPF/Localization/Strings.ja.resx similarity index 100% rename from shadowsocks-csharp/Localization/Strings.ja.resx rename to Shadowsocks.WPF/Localization/Strings.ja.resx diff --git a/shadowsocks-csharp/Localization/Strings.ko.resx b/Shadowsocks.WPF/Localization/Strings.ko.resx similarity index 100% rename from shadowsocks-csharp/Localization/Strings.ko.resx rename to Shadowsocks.WPF/Localization/Strings.ko.resx diff --git a/shadowsocks-csharp/Localization/Strings.resx b/Shadowsocks.WPF/Localization/Strings.resx similarity index 100% rename from shadowsocks-csharp/Localization/Strings.resx rename to Shadowsocks.WPF/Localization/Strings.resx diff --git a/shadowsocks-csharp/Localization/Strings.ru.resx b/Shadowsocks.WPF/Localization/Strings.ru.resx similarity index 100% rename from shadowsocks-csharp/Localization/Strings.ru.resx rename to Shadowsocks.WPF/Localization/Strings.ru.resx diff --git a/shadowsocks-csharp/Localization/Strings.zh-Hans.resx b/Shadowsocks.WPF/Localization/Strings.zh-Hans.resx similarity index 100% rename from shadowsocks-csharp/Localization/Strings.zh-Hans.resx rename to Shadowsocks.WPF/Localization/Strings.zh-Hans.resx diff --git a/shadowsocks-csharp/Localization/Strings.zh-Hant.resx b/Shadowsocks.WPF/Localization/Strings.zh-Hant.resx similarity index 97% rename from shadowsocks-csharp/Localization/Strings.zh-Hant.resx rename to Shadowsocks.WPF/Localization/Strings.zh-Hant.resx index 668c2b25..e52f7e3f 100644 --- a/shadowsocks-csharp/Localization/Strings.zh-Hant.resx +++ b/Shadowsocks.WPF/Localization/Strings.zh-Hant.resx @@ -1,189 +1,189 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 密碼 - - - Port - - - 類型 - - - 位址 - - - 逾時 (秒) - - - 取消 - - - 確定 - - - 切換系統 Proxy 狀態 - - - 切換系統 Proxy 模式 - - - 切換區域網路共用 - - - 顯示記錄檔 - - - 切換上一個伺服器 - - - 切換下一個伺服器 - - - 註冊所有快速鍵 - - - 啟動時註冊快速鍵 - - - 更新 - - - 全部更新 - - - 新增 - - - 跳過版本 - - - 暫不更新 - - - 發現可用更新 - - - 複製鏈接 - - - 複製 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 密碼 + + + Port + + + 類型 + + + 位址 + + + 逾時 (秒) + + + 取消 + + + 確定 + + + 切換系統 Proxy 狀態 + + + 切換系統 Proxy 模式 + + + 切換區域網路共用 + + + 顯示記錄檔 + + + 切換上一個伺服器 + + + 切換下一個伺服器 + + + 註冊所有快速鍵 + + + 啟動時註冊快速鍵 + + + 更新 + + + 全部更新 + + + 新增 + + + 跳過版本 + + + 暫不更新 + + + 發現可用更新 + + + 複製鏈接 + + + 複製 + \ No newline at end of file diff --git a/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml b/Shadowsocks.WPF/Properties/PublishProfiles/FolderProfile.pubxml similarity index 72% rename from shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml rename to Shadowsocks.WPF/Properties/PublishProfiles/FolderProfile.pubxml index 94842414..da619e3e 100644 --- a/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml +++ b/Shadowsocks.WPF/Properties/PublishProfiles/FolderProfile.pubxml @@ -1,12 +1,14 @@ - + - - - Release - Any CPU - bin\Release\netcoreapp3.1\publish\ - FileSystem - +--> + + + Debug + Any CPU + bin\Release\netcoreapp3.1\publish\ + FileSystem + netcoreapp3.1 + false + \ No newline at end of file diff --git a/shadowsocks-csharp/Properties/PublishProfiles/win-x64.pubxml.user b/Shadowsocks.WPF/Properties/PublishProfiles/FolderProfile.pubxml.user similarity index 100% rename from shadowsocks-csharp/Properties/PublishProfiles/win-x64.pubxml.user rename to Shadowsocks.WPF/Properties/PublishProfiles/FolderProfile.pubxml.user diff --git a/shadowsocks-csharp/Properties/PublishProfiles/win-x64.pubxml b/Shadowsocks.WPF/Properties/PublishProfiles/win-x64.pubxml similarity index 88% rename from shadowsocks-csharp/Properties/PublishProfiles/win-x64.pubxml rename to Shadowsocks.WPF/Properties/PublishProfiles/win-x64.pubxml index 8975df70..0cb60638 100644 --- a/shadowsocks-csharp/Properties/PublishProfiles/win-x64.pubxml +++ b/Shadowsocks.WPF/Properties/PublishProfiles/win-x64.pubxml @@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Release Any CPU - bin\Release\netcoreapp3.1\win-x64\publish\ + bin\Release\netcoreapp3.1\publish\ FileSystem netcoreapp3.1 win-x64 diff --git a/shadowsocks-csharp/Properties/PublishProfiles/win-x86.pubxml.user b/Shadowsocks.WPF/Properties/PublishProfiles/win-x64.pubxml.user similarity index 100% rename from shadowsocks-csharp/Properties/PublishProfiles/win-x86.pubxml.user rename to Shadowsocks.WPF/Properties/PublishProfiles/win-x64.pubxml.user diff --git a/shadowsocks-csharp/Properties/PublishProfiles/win-x86.pubxml b/Shadowsocks.WPF/Properties/PublishProfiles/win-x86.pubxml similarity index 88% rename from shadowsocks-csharp/Properties/PublishProfiles/win-x86.pubxml rename to Shadowsocks.WPF/Properties/PublishProfiles/win-x86.pubxml index b25dd23a..4e6685ac 100644 --- a/shadowsocks-csharp/Properties/PublishProfiles/win-x86.pubxml +++ b/Shadowsocks.WPF/Properties/PublishProfiles/win-x86.pubxml @@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Release Any CPU - bin\Release\netcoreapp3.1\win-x86\publish\ + bin\Release\netcoreapp3.1\publish\ FileSystem netcoreapp3.1 win-x86 diff --git a/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml.user b/Shadowsocks.WPF/Properties/PublishProfiles/win-x86.pubxml.user similarity index 93% rename from shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml.user rename to Shadowsocks.WPF/Properties/PublishProfiles/win-x86.pubxml.user index ede65b29..312c6e3b 100644 --- a/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/Shadowsocks.WPF/Properties/PublishProfiles/win-x86.pubxml.user @@ -1,6 +1,6 @@ - + - - +--> + + \ No newline at end of file diff --git a/Shadowsocks.WPF/Resource.Designer.cs b/Shadowsocks.WPF/Resource.Designer.cs new file mode 100644 index 00000000..b599894d --- /dev/null +++ b/Shadowsocks.WPF/Resource.Designer.cs @@ -0,0 +1,133 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Shadowsocks.WPF { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.WPF.Resource", typeof(Resource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] ss128 { + get { + object obj = ResourceManager.GetObject("ss128", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] ss32 { + get { + object obj = ResourceManager.GetObject("ss32", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap ss32Fill { + get { + object obj = ResourceManager.GetObject("ss32Fill", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap ss32In { + get { + object obj = ResourceManager.GetObject("ss32In", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap ss32Out { + get { + object obj = ResourceManager.GetObject("ss32Out", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap ss32Outline { + get { + object obj = ResourceManager.GetObject("ss32Outline", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap ssw128 { + get { + object obj = ResourceManager.GetObject("ssw128", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/shadowsocks-csharp/Properties/Resources.resx b/Shadowsocks.WPF/Resource.resx old mode 100755 new mode 100644 similarity index 72% rename from shadowsocks-csharp/Properties/Resources.resx rename to Shadowsocks.WPF/Resource.resx index 3a394f0e..136f7759 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/Shadowsocks.WPF/Resource.resx @@ -1,157 +1,142 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\Data\abp.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312 - - - ..\data\dlc.dat;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\Data\i18n.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - ..\data\nlog.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - ..\data\privoxy_conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - ..\data\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\Resources\ss32Fill.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\ss32In.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\ss32Out.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\ss32Outline.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\data\user-rule.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + assets\ss128.pdn;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + assets\ss32.pdn;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + assets\ss32fill.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + assets\ss32in.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + assets\ss32out.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + assets\ss32outline.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + assets\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/Shadowsocks.WPF/Shadowsocks.WPF.csproj b/Shadowsocks.WPF/Shadowsocks.WPF.csproj new file mode 100644 index 00000000..c8dcf349 --- /dev/null +++ b/Shadowsocks.WPF/Shadowsocks.WPF.csproj @@ -0,0 +1,70 @@ + + + + Exe + netcoreapp3.1 + true + clowwindy & community 2020 + Shadowsocks.WPF + Shadowsocks WPF GUI + 5.0.0.0 + 1.0.0 + Assets\shadowsocks.ico + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Strings.resx + + + True + True + Resource.resx + + + + + + ResXFileCodeGenerator + Strings.Designer.cs + + + ResXFileCodeGenerator + Resource.Designer.cs + + + + + + + + \ No newline at end of file diff --git a/Shadowsocks.WPF/Shadowsocks.WPF.csproj.user b/Shadowsocks.WPF/Shadowsocks.WPF.csproj.user new file mode 100644 index 00000000..93cbea4a --- /dev/null +++ b/Shadowsocks.WPF/Shadowsocks.WPF.csproj.user @@ -0,0 +1,21 @@ + + + + <_LastSelectedProfileId>D:\workspaces\repos\shadowsocks-windows\Shadowsocks.WPF\Properties\PublishProfiles\win-x64.pubxml + + + + Designer + + + + + Code + + + + + Designer + + + \ No newline at end of file diff --git a/shadowsocks-csharp/ViewModels/ForwardProxyViewModel.cs b/Shadowsocks.WPF/ViewModels/ForwardProxyViewModel.cs similarity index 99% rename from shadowsocks-csharp/ViewModels/ForwardProxyViewModel.cs rename to Shadowsocks.WPF/ViewModels/ForwardProxyViewModel.cs index f901cac8..607a2e71 100644 --- a/shadowsocks-csharp/ViewModels/ForwardProxyViewModel.cs +++ b/Shadowsocks.WPF/ViewModels/ForwardProxyViewModel.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Reactive; using System.Reactive.Linq; -namespace Shadowsocks.ViewModels +namespace Shadowsocks.WPF.ViewModels { public class ForwardProxyViewModel : ReactiveValidationObject { diff --git a/shadowsocks-csharp/ViewModels/HotkeysViewModel.cs b/Shadowsocks.WPF/ViewModels/HotkeysViewModel.cs similarity index 99% rename from shadowsocks-csharp/ViewModels/HotkeysViewModel.cs rename to Shadowsocks.WPF/ViewModels/HotkeysViewModel.cs index 141d2cf9..fd10fb9b 100644 --- a/shadowsocks-csharp/ViewModels/HotkeysViewModel.cs +++ b/Shadowsocks.WPF/ViewModels/HotkeysViewModel.cs @@ -7,7 +7,7 @@ using System.Reactive; using System.Text; using System.Windows.Input; -namespace Shadowsocks.ViewModels +namespace Shadowsocks.WPF.ViewModels { public class HotkeysViewModel : ReactiveObject { diff --git a/Shadowsocks.WPF/ViewModels/MainWindowViewModel.cs b/Shadowsocks.WPF/ViewModels/MainWindowViewModel.cs new file mode 100644 index 00000000..042a5d34 --- /dev/null +++ b/Shadowsocks.WPF/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,14 @@ +using ReactiveUI; +using Shadowsocks.Controller.Service; + +using System; + +namespace Shadowsocks.WPF.ViewModels +{ + public class MainWindowViewModel : ReactiveObject + { + public MainWindowViewModel() + { + } + } +} diff --git a/shadowsocks-csharp/ViewModels/OnlineConfigViewModel.cs b/Shadowsocks.WPF/ViewModels/OnlineConfigViewModel.cs similarity index 99% rename from shadowsocks-csharp/ViewModels/OnlineConfigViewModel.cs rename to Shadowsocks.WPF/ViewModels/OnlineConfigViewModel.cs index f803b351..2c4d1b5d 100644 --- a/shadowsocks-csharp/ViewModels/OnlineConfigViewModel.cs +++ b/Shadowsocks.WPF/ViewModels/OnlineConfigViewModel.cs @@ -15,7 +15,7 @@ using System.Reactive.Linq; using System.Text; using System.Windows; -namespace Shadowsocks.ViewModels +namespace Shadowsocks.WPF.ViewModels { public class OnlineConfigViewModel : ReactiveValidationObject { diff --git a/shadowsocks-csharp/ViewModels/ServerSharingViewModel.cs b/Shadowsocks.WPF/ViewModels/ServerSharingViewModel.cs similarity index 98% rename from shadowsocks-csharp/ViewModels/ServerSharingViewModel.cs rename to Shadowsocks.WPF/ViewModels/ServerSharingViewModel.cs index f1375690..9ddfbd84 100644 --- a/shadowsocks-csharp/ViewModels/ServerSharingViewModel.cs +++ b/Shadowsocks.WPF/ViewModels/ServerSharingViewModel.cs @@ -10,7 +10,7 @@ using System.Reactive; using System.Windows; using System.Windows.Media.Imaging; -namespace Shadowsocks.ViewModels +namespace Shadowsocks.WPF.ViewModels { public class ServerSharingViewModel : ReactiveObject { diff --git a/shadowsocks-csharp/ViewModels/VersionUpdatePromptViewModel.cs b/Shadowsocks.WPF/ViewModels/VersionUpdatePromptViewModel.cs similarity index 97% rename from shadowsocks-csharp/ViewModels/VersionUpdatePromptViewModel.cs rename to Shadowsocks.WPF/ViewModels/VersionUpdatePromptViewModel.cs index 0ae9f2d1..ab90d196 100644 --- a/shadowsocks-csharp/ViewModels/VersionUpdatePromptViewModel.cs +++ b/Shadowsocks.WPF/ViewModels/VersionUpdatePromptViewModel.cs @@ -3,7 +3,7 @@ using ReactiveUI; using Shadowsocks.Controller; using System.Reactive; -namespace Shadowsocks.ViewModels +namespace Shadowsocks.WPF.ViewModels { public class VersionUpdatePromptViewModel : ReactiveObject { diff --git a/shadowsocks-csharp/Views/ForwardProxyView.xaml b/Shadowsocks.WPF/Views/ForwardProxyView.xaml similarity index 96% rename from shadowsocks-csharp/Views/ForwardProxyView.xaml rename to Shadowsocks.WPF/Views/ForwardProxyView.xaml index 0c6a9319..c0754de0 100644 --- a/shadowsocks-csharp/Views/ForwardProxyView.xaml +++ b/Shadowsocks.WPF/Views/ForwardProxyView.xaml @@ -1,12 +1,12 @@  /// Interaction logic for ForwardProxyView.xaml diff --git a/shadowsocks-csharp/Views/HotkeysView.xaml b/Shadowsocks.WPF/Views/HotkeysView.xaml similarity index 96% rename from shadowsocks-csharp/Views/HotkeysView.xaml rename to Shadowsocks.WPF/Views/HotkeysView.xaml index 57527b2c..a01d5f83 100644 --- a/shadowsocks-csharp/Views/HotkeysView.xaml +++ b/Shadowsocks.WPF/Views/HotkeysView.xaml @@ -1,12 +1,12 @@  /// Interaction logic for HotkeysView.xaml diff --git a/Shadowsocks.WPF/Views/MainWindow.xaml b/Shadowsocks.WPF/Views/MainWindow.xaml new file mode 100644 index 00000000..110aa42a --- /dev/null +++ b/Shadowsocks.WPF/Views/MainWindow.xaml @@ -0,0 +1,21 @@ + + + diff --git a/Shadowsocks.WPF/Views/MainWindow.xaml.cs b/Shadowsocks.WPF/Views/MainWindow.xaml.cs new file mode 100644 index 00000000..2bf713d8 --- /dev/null +++ b/Shadowsocks.WPF/Views/MainWindow.xaml.cs @@ -0,0 +1,17 @@ +using ReactiveUI; + +using Shadowsocks.WPF.ViewModels; + +namespace Shadowsocks.WPF.Views +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : ReactiveWindow + { + public MainWindow() + { + InitializeComponent(); + } + } +} diff --git a/shadowsocks-csharp/Views/OnlineConfigView.xaml b/Shadowsocks.WPF/Views/OnlineConfigView.xaml similarity index 93% rename from shadowsocks-csharp/Views/OnlineConfigView.xaml rename to Shadowsocks.WPF/Views/OnlineConfigView.xaml index c024c036..6ba7ec58 100644 --- a/shadowsocks-csharp/Views/OnlineConfigView.xaml +++ b/Shadowsocks.WPF/Views/OnlineConfigView.xaml @@ -1,12 +1,12 @@  /// Interaction logic for OnlineConfigView.xaml diff --git a/shadowsocks-csharp/Views/ServerSharingView.xaml b/Shadowsocks.WPF/Views/ServerSharingView.xaml similarity index 92% rename from shadowsocks-csharp/Views/ServerSharingView.xaml rename to Shadowsocks.WPF/Views/ServerSharingView.xaml index b037c08d..a812a4c8 100644 --- a/shadowsocks-csharp/Views/ServerSharingView.xaml +++ b/Shadowsocks.WPF/Views/ServerSharingView.xaml @@ -1,12 +1,12 @@  /// Interaction logic for ServerSharingView.xaml diff --git a/shadowsocks-csharp/Views/VersionUpdatePromptView.xaml b/Shadowsocks.WPF/Views/VersionUpdatePromptView.xaml similarity index 92% rename from shadowsocks-csharp/Views/VersionUpdatePromptView.xaml rename to Shadowsocks.WPF/Views/VersionUpdatePromptView.xaml index cfd75b02..dabcc5d5 100644 --- a/shadowsocks-csharp/Views/VersionUpdatePromptView.xaml +++ b/Shadowsocks.WPF/Views/VersionUpdatePromptView.xaml @@ -1,12 +1,12 @@  /// Interaction logic for VersionUpdatePromptView.xaml diff --git a/Shadowsocks/Assets/NLog.config b/Shadowsocks/Assets/NLog.config new file mode 100644 index 00000000..d955c871 --- /dev/null +++ b/Shadowsocks/Assets/NLog.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/shadowsocks-csharp/Data/abp.js b/Shadowsocks/Assets/abp.js similarity index 96% rename from shadowsocks-csharp/Data/abp.js rename to Shadowsocks/Assets/abp.js index 1d5bcdcf..243607bd 100644 --- a/shadowsocks-csharp/Data/abp.js +++ b/Shadowsocks/Assets/abp.js @@ -1,810 +1,810 @@ -/* eslint-disable */ -// Was generated by gfwlist2pac in precise mode -// https://github.com/clowwindy/gfwlist2pac - -// 2019-10-06: More 'javascript' way to interaction with main program -// 2019-02-08: Updated to support shadowsocks-windows user rules. - -var proxy = __PROXY__; -var userrules = []; -var rules = []; - -// convert to abp grammar -for (var i = 0; i < __RULES__.length; i++) { - var s = __RULES__[i]; - if (s.substring(0, 2) == "||") s += "^"; - rules.push(s); -} - -for (var i = 0; i < __USERRULES__.length; i++) { - var s = __USERRULES__[i]; - if (s.substring(0, 2) == "||") s += "^"; - userrules.push(s); -} - -/* -* This file is part of Adblock Plus , -* Copyright (C) 2006-2014 Eyeo GmbH -* -* Adblock Plus is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License version 3 as -* published by the Free Software Foundation. -* -* Adblock Plus is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Adblock Plus. If not, see . -*/ - -function createDict() -{ - var result = {}; - result.__proto__ = null; - return result; -} - -function getOwnPropertyDescriptor(obj, key) -{ - if (obj.hasOwnProperty(key)) - { - return obj[key]; - } - return null; -} - -function extend(subclass, superclass, definition) -{ - if (Object.__proto__) - { - definition.__proto__ = superclass.prototype; - subclass.prototype = definition; - } - else - { - var tmpclass = function(){}, ret; - tmpclass.prototype = superclass.prototype; - subclass.prototype = new tmpclass(); - subclass.prototype.constructor = superclass; - for (var i in definition) - { - if (definition.hasOwnProperty(i)) - { - subclass.prototype[i] = definition[i]; - } - } - } -} - -function Filter(text) -{ - this.text = text; - this.subscriptions = []; -} -Filter.prototype = { - text: null, - subscriptions: null, - toString: function() - { - return this.text; - } -}; -Filter.knownFilters = createDict(); -Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; -Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; -Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; -Filter.fromText = function(text) -{ - if (text in Filter.knownFilters) - { - return Filter.knownFilters[text]; - } - var ret; - if (text.charAt(0) == "!") - { - ret = new CommentFilter(text); - } - else - { - ret = RegExpFilter.fromText(text); - } - Filter.knownFilters[ret.text] = ret; - return ret; -}; - -function InvalidFilter(text, reason) -{ - Filter.call(this, text); - this.reason = reason; -} -extend(InvalidFilter, Filter, { - reason: null -}); - -function CommentFilter(text) -{ - Filter.call(this, text); -} -extend(CommentFilter, Filter, { -}); - -function ActiveFilter(text, domains) -{ - Filter.call(this, text); - this.domainSource = domains; -} -extend(ActiveFilter, Filter, { - domainSource: null, - domainSeparator: null, - ignoreTrailingDot: true, - domainSourceIsUpperCase: false, - getDomains: function() - { - var prop = getOwnPropertyDescriptor(this, "domains"); - if (prop) - { - return prop; - } - var domains = null; - if (this.domainSource) - { - var source = this.domainSource; - if (!this.domainSourceIsUpperCase) - { - source = source.toUpperCase(); - } - var list = source.split(this.domainSeparator); - if (list.length == 1 && (list[0]).charAt(0) != "~") - { - domains = createDict(); - domains[""] = false; - if (this.ignoreTrailingDot) - { - list[0] = list[0].replace(/\.+$/, ""); - } - domains[list[0]] = true; - } - else - { - var hasIncludes = false; - for (var i = 0; i < list.length; i++) - { - var domain = list[i]; - if (this.ignoreTrailingDot) - { - domain = domain.replace(/\.+$/, ""); - } - if (domain == "") - { - continue; - } - var include; - if (domain.charAt(0) == "~") - { - include = false; - domain = domain.substr(1); - } - else - { - include = true; - hasIncludes = true; - } - if (!domains) - { - domains = createDict(); - } - domains[domain] = include; - } - domains[""] = !hasIncludes; - } - this.domainSource = null; - } - return this.domains; - }, - sitekeys: null, - isActiveOnDomain: function(docDomain, sitekey) - { - if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) - { - return false; - } - if (!this.getDomains()) - { - return true; - } - if (!docDomain) - { - return this.getDomains()[""]; - } - if (this.ignoreTrailingDot) - { - docDomain = docDomain.replace(/\.+$/, ""); - } - docDomain = docDomain.toUpperCase(); - while (true) - { - if (docDomain in this.getDomains()) - { - return this.domains[docDomain]; - } - var nextDot = docDomain.indexOf("."); - if (nextDot < 0) - { - break; - } - docDomain = docDomain.substr(nextDot + 1); - } - return this.domains[""]; - }, - isActiveOnlyOnDomain: function(docDomain) - { - if (!docDomain || !this.getDomains() || this.getDomains()[""]) - { - return false; - } - if (this.ignoreTrailingDot) - { - docDomain = docDomain.replace(/\.+$/, ""); - } - docDomain = docDomain.toUpperCase(); - for (var domain in this.getDomains()) - { - if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) - { - return false; - } - } - return true; - } -}); - -function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) -{ - ActiveFilter.call(this, text, domains, sitekeys); - if (contentType != null) - { - this.contentType = contentType; - } - if (matchCase) - { - this.matchCase = matchCase; - } - if (thirdParty != null) - { - this.thirdParty = thirdParty; - } - if (sitekeys != null) - { - this.sitekeySource = sitekeys; - } - if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/") - { - var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); - this.regexp = regexp; - } - else - { - this.regexpSource = regexpSource; - } -} -extend(RegExpFilter, ActiveFilter, { - domainSourceIsUpperCase: true, - length: 1, - domainSeparator: "|", - regexpSource: null, - getRegexp: function() - { - var prop = getOwnPropertyDescriptor(this, "regexp"); - if (prop) - { - return prop; - } - var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, ""); - var regexp = new RegExp(source, this.matchCase ? "" : "i"); - this.regexp = regexp; - return regexp; - }, - contentType: 2147483647, - matchCase: false, - thirdParty: null, - sitekeySource: null, - getSitekeys: function() - { - var prop = getOwnPropertyDescriptor(this, "sitekeys"); - if (prop) - { - return prop; - } - var sitekeys = null; - if (this.sitekeySource) - { - sitekeys = this.sitekeySource.split("|"); - this.sitekeySource = null; - } - this.sitekeys = sitekeys; - return this.sitekeys; - }, - matches: function(location, contentType, docDomain, thirdParty, sitekey) - { - if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) - { - return true; - } - return false; - } -}); -RegExpFilter.prototype["0"] = "#this"; -RegExpFilter.fromText = function(text) -{ - var blocking = true; - var origText = text; - if (text.indexOf("@@") == 0) - { - blocking = false; - text = text.substr(2); - } - var contentType = null; - var matchCase = null; - var domains = null; - var sitekeys = null; - var thirdParty = null; - var collapse = null; - var options; - var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; - if (match) - { - options = match[1].toUpperCase().split(","); - text = match.input.substr(0, match.index); - for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) - { - var option = options[_loopIndex6]; - var value = null; - var separatorIndex = option.indexOf("="); - if (separatorIndex >= 0) - { - value = option.substr(separatorIndex + 1); - option = option.substr(0, separatorIndex); - } - option = option.replace(/-/, "_"); - if (option in RegExpFilter.typeMap) - { - if (contentType == null) - { - contentType = 0; - } - contentType |= RegExpFilter.typeMap[option]; - } - else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap) - { - if (contentType == null) - { - contentType = RegExpFilter.prototype.contentType; - } - contentType &= ~RegExpFilter.typeMap[option.substr(1)]; - } - else if (option == "MATCH_CASE") - { - matchCase = true; - } - else if (option == "~MATCH_CASE") - { - matchCase = false; - } - else if (option == "DOMAIN" && typeof value != "undefined") - { - domains = value; - } - else if (option == "THIRD_PARTY") - { - thirdParty = true; - } - else if (option == "~THIRD_PARTY") - { - thirdParty = false; - } - else if (option == "COLLAPSE") - { - collapse = true; - } - else if (option == "~COLLAPSE") - { - collapse = false; - } - else if (option == "SITEKEY" && typeof value != "undefined") - { - sitekeys = value; - } - else - { - return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); - } - } - } - if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) - { - if (contentType == null) - { - contentType = RegExpFilter.prototype.contentType; - } - contentType &= ~RegExpFilter.typeMap.DOCUMENT; - } - try - { - if (blocking) - { - return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); - } - else - { - return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); - } - } - catch (e) - { - return new InvalidFilter(origText, e); - } -}; -RegExpFilter.typeMap = { - OTHER: 1, - SCRIPT: 2, - IMAGE: 4, - STYLESHEET: 8, - OBJECT: 16, - SUBDOCUMENT: 32, - DOCUMENT: 64, - XBL: 1, - PING: 1, - XMLHTTPREQUEST: 2048, - OBJECT_SUBREQUEST: 4096, - DTD: 1, - MEDIA: 16384, - FONT: 32768, - BACKGROUND: 4, - POPUP: 268435456, - ELEMHIDE: 1073741824 -}; -RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); - -function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) -{ - RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); - this.collapse = collapse; -} -extend(BlockingFilter, RegExpFilter, { - collapse: null -}); - -function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) -{ - RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); -} -extend(WhitelistFilter, RegExpFilter, { -}); - -function Matcher() -{ - this.clear(); -} -Matcher.prototype = { - filterByKeyword: null, - keywordByFilter: null, - clear: function() - { - this.filterByKeyword = createDict(); - this.keywordByFilter = createDict(); - }, - add: function(filter) - { - if (filter.text in this.keywordByFilter) - { - return; - } - var keyword = this.findKeyword(filter); - var oldEntry = this.filterByKeyword[keyword]; - if (typeof oldEntry == "undefined") - { - this.filterByKeyword[keyword] = filter; - } - else if (oldEntry.length == 1) - { - this.filterByKeyword[keyword] = [oldEntry, filter]; - } - else - { - oldEntry.push(filter); - } - this.keywordByFilter[filter.text] = keyword; - }, - remove: function(filter) - { - if (!(filter.text in this.keywordByFilter)) - { - return; - } - var keyword = this.keywordByFilter[filter.text]; - var list = this.filterByKeyword[keyword]; - if (list.length <= 1) - { - delete this.filterByKeyword[keyword]; - } - else - { - var index = list.indexOf(filter); - if (index >= 0) - { - list.splice(index, 1); - if (list.length == 1) - { - this.filterByKeyword[keyword] = list[0]; - } - } - } - delete this.keywordByFilter[filter.text]; - }, - findKeyword: function(filter) - { - var result = ""; - var text = filter.text; - if (Filter.regexpRegExp.test(text)) - { - return result; - } - var match = Filter.optionsRegExp.exec(text); - if (match) - { - text = match.input.substr(0, match.index); - } - if (text.substr(0, 2) == "@@") - { - text = text.substr(2); - } - var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); - if (!candidates) - { - return result; - } - var hash = this.filterByKeyword; - var resultCount = 16777215; - var resultLength = 0; - for (var i = 0, l = candidates.length; i < l; i++) - { - var candidate = candidates[i].substr(1); - var count = candidate in hash ? hash[candidate].length : 0; - if (count < resultCount || count == resultCount && candidate.length > resultLength) - { - result = candidate; - resultCount = count; - resultLength = candidate.length; - } - } - return result; - }, - hasFilter: function(filter) - { - return filter.text in this.keywordByFilter; - }, - getKeywordForFilter: function(filter) - { - if (filter.text in this.keywordByFilter) - { - return this.keywordByFilter[filter.text]; - } - else - { - return null; - } - }, - _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) - { - var list = this.filterByKeyword[keyword]; - for (var i = 0; i < list.length; i++) - { - var filter = list[i]; - if (filter == "#this") - { - filter = list; - } - if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) - { - return filter; - } - } - return null; - }, - matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) - { - var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); - if (candidates === null) - { - candidates = []; - } - candidates.push(""); - for (var i = 0, l = candidates.length; i < l; i++) - { - var substr = candidates[i]; - if (substr in this.filterByKeyword) - { - var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); - if (result) - { - return result; - } - } - } - return null; - } -}; - -function CombinedMatcher() -{ - this.blacklist = new Matcher(); - this.whitelist = new Matcher(); - this.resultCache = createDict(); -} -CombinedMatcher.maxCacheEntries = 1000; -CombinedMatcher.prototype = { - blacklist: null, - whitelist: null, - resultCache: null, - cacheEntries: 0, - clear: function() - { - this.blacklist.clear(); - this.whitelist.clear(); - this.resultCache = createDict(); - this.cacheEntries = 0; - }, - add: function(filter) - { - if (filter instanceof WhitelistFilter) - { - this.whitelist.add(filter); - } - else - { - this.blacklist.add(filter); - } - if (this.cacheEntries > 0) - { - this.resultCache = createDict(); - this.cacheEntries = 0; - } - }, - remove: function(filter) - { - if (filter instanceof WhitelistFilter) - { - this.whitelist.remove(filter); - } - else - { - this.blacklist.remove(filter); - } - if (this.cacheEntries > 0) - { - this.resultCache = createDict(); - this.cacheEntries = 0; - } - }, - findKeyword: function(filter) - { - if (filter instanceof WhitelistFilter) - { - return this.whitelist.findKeyword(filter); - } - else - { - return this.blacklist.findKeyword(filter); - } - }, - hasFilter: function(filter) - { - if (filter instanceof WhitelistFilter) - { - return this.whitelist.hasFilter(filter); - } - else - { - return this.blacklist.hasFilter(filter); - } - }, - getKeywordForFilter: function(filter) - { - if (filter instanceof WhitelistFilter) - { - return this.whitelist.getKeywordForFilter(filter); - } - else - { - return this.blacklist.getKeywordForFilter(filter); - } - }, - isSlowFilter: function(filter) - { - var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; - if (matcher.hasFilter(filter)) - { - return !matcher.getKeywordForFilter(filter); - } - else - { - return !matcher.findKeyword(filter); - } - }, - matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) - { - var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); - if (candidates === null) - { - candidates = []; - } - candidates.push(""); - var blacklistHit = null; - for (var i = 0, l = candidates.length; i < l; i++) - { - var substr = candidates[i]; - if (substr in this.whitelist.filterByKeyword) - { - var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); - if (result) - { - return result; - } - } - if (substr in this.blacklist.filterByKeyword && blacklistHit === null) - { - blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); - } - } - return blacklistHit; - }, - matchesAny: function(location, docDomain) - { - var key = location + " " + docDomain + " "; - if (key in this.resultCache) - { - return this.resultCache[key]; - } - var result = this.matchesAnyInternal(location, 0, docDomain, null, null); - if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) - { - this.resultCache = createDict(); - this.cacheEntries = 0; - } - this.resultCache[key] = result; - this.cacheEntries++; - return result; - } -}; - -var userrulesMatcher = new CombinedMatcher(); -var defaultMatcher = new CombinedMatcher(); - -var direct = 'DIRECT;'; - -for (var i = 0; i < userrules.length; i++) { - userrulesMatcher.add(Filter.fromText(userrules[i])); -} - -for (var i = 0; i < rules.length; i++) { - defaultMatcher.add(Filter.fromText(rules[i])); -} - -function FindProxyForURL(url, host) { - if (userrulesMatcher.matchesAny(url, host) instanceof BlockingFilter) { - return proxy; - } - 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; - } - return direct; -} +/* eslint-disable */ +// Was generated by gfwlist2pac in precise mode +// https://github.com/clowwindy/gfwlist2pac + +// 2019-10-06: More 'javascript' way to interaction with main program +// 2019-02-08: Updated to support shadowsocks-windows user rules. + +var proxy = __PROXY__; +var userrules = []; +var rules = []; + +// convert to abp grammar +for (var i = 0; i < __RULES__.length; i++) { + var s = __RULES__[i]; + if (s.substring(0, 2) == "||") s += "^"; + rules.push(s); +} + +for (var i = 0; i < __USERRULES__.length; i++) { + var s = __USERRULES__[i]; + if (s.substring(0, 2) == "||") s += "^"; + userrules.push(s); +} + +/* +* This file is part of Adblock Plus , +* Copyright (C) 2006-2014 Eyeo GmbH +* +* Adblock Plus is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 3 as +* published by the Free Software Foundation. +* +* Adblock Plus is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Adblock Plus. If not, see . +*/ + +function createDict() +{ + var result = {}; + result.__proto__ = null; + return result; +} + +function getOwnPropertyDescriptor(obj, key) +{ + if (obj.hasOwnProperty(key)) + { + return obj[key]; + } + return null; +} + +function extend(subclass, superclass, definition) +{ + if (Object.__proto__) + { + definition.__proto__ = superclass.prototype; + subclass.prototype = definition; + } + else + { + var tmpclass = function(){}, ret; + tmpclass.prototype = superclass.prototype; + subclass.prototype = new tmpclass(); + subclass.prototype.constructor = superclass; + for (var i in definition) + { + if (definition.hasOwnProperty(i)) + { + subclass.prototype[i] = definition[i]; + } + } + } +} + +function Filter(text) +{ + this.text = text; + this.subscriptions = []; +} +Filter.prototype = { + text: null, + subscriptions: null, + toString: function() + { + return this.text; + } +}; +Filter.knownFilters = createDict(); +Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; +Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; +Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; +Filter.fromText = function(text) +{ + if (text in Filter.knownFilters) + { + return Filter.knownFilters[text]; + } + var ret; + if (text.charAt(0) == "!") + { + ret = new CommentFilter(text); + } + else + { + ret = RegExpFilter.fromText(text); + } + Filter.knownFilters[ret.text] = ret; + return ret; +}; + +function InvalidFilter(text, reason) +{ + Filter.call(this, text); + this.reason = reason; +} +extend(InvalidFilter, Filter, { + reason: null +}); + +function CommentFilter(text) +{ + Filter.call(this, text); +} +extend(CommentFilter, Filter, { +}); + +function ActiveFilter(text, domains) +{ + Filter.call(this, text); + this.domainSource = domains; +} +extend(ActiveFilter, Filter, { + domainSource: null, + domainSeparator: null, + ignoreTrailingDot: true, + domainSourceIsUpperCase: false, + getDomains: function() + { + var prop = getOwnPropertyDescriptor(this, "domains"); + if (prop) + { + return prop; + } + var domains = null; + if (this.domainSource) + { + var source = this.domainSource; + if (!this.domainSourceIsUpperCase) + { + source = source.toUpperCase(); + } + var list = source.split(this.domainSeparator); + if (list.length == 1 && (list[0]).charAt(0) != "~") + { + domains = createDict(); + domains[""] = false; + if (this.ignoreTrailingDot) + { + list[0] = list[0].replace(/\.+$/, ""); + } + domains[list[0]] = true; + } + else + { + var hasIncludes = false; + for (var i = 0; i < list.length; i++) + { + var domain = list[i]; + if (this.ignoreTrailingDot) + { + domain = domain.replace(/\.+$/, ""); + } + if (domain == "") + { + continue; + } + var include; + if (domain.charAt(0) == "~") + { + include = false; + domain = domain.substr(1); + } + else + { + include = true; + hasIncludes = true; + } + if (!domains) + { + domains = createDict(); + } + domains[domain] = include; + } + domains[""] = !hasIncludes; + } + this.domainSource = null; + } + return this.domains; + }, + sitekeys: null, + isActiveOnDomain: function(docDomain, sitekey) + { + if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) + { + return false; + } + if (!this.getDomains()) + { + return true; + } + if (!docDomain) + { + return this.getDomains()[""]; + } + if (this.ignoreTrailingDot) + { + docDomain = docDomain.replace(/\.+$/, ""); + } + docDomain = docDomain.toUpperCase(); + while (true) + { + if (docDomain in this.getDomains()) + { + return this.domains[docDomain]; + } + var nextDot = docDomain.indexOf("."); + if (nextDot < 0) + { + break; + } + docDomain = docDomain.substr(nextDot + 1); + } + return this.domains[""]; + }, + isActiveOnlyOnDomain: function(docDomain) + { + if (!docDomain || !this.getDomains() || this.getDomains()[""]) + { + return false; + } + if (this.ignoreTrailingDot) + { + docDomain = docDomain.replace(/\.+$/, ""); + } + docDomain = docDomain.toUpperCase(); + for (var domain in this.getDomains()) + { + if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) + { + return false; + } + } + return true; + } +}); + +function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) +{ + ActiveFilter.call(this, text, domains, sitekeys); + if (contentType != null) + { + this.contentType = contentType; + } + if (matchCase) + { + this.matchCase = matchCase; + } + if (thirdParty != null) + { + this.thirdParty = thirdParty; + } + if (sitekeys != null) + { + this.sitekeySource = sitekeys; + } + if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/") + { + var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); + this.regexp = regexp; + } + else + { + this.regexpSource = regexpSource; + } +} +extend(RegExpFilter, ActiveFilter, { + domainSourceIsUpperCase: true, + length: 1, + domainSeparator: "|", + regexpSource: null, + getRegexp: function() + { + var prop = getOwnPropertyDescriptor(this, "regexp"); + if (prop) + { + return prop; + } + var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, ""); + var regexp = new RegExp(source, this.matchCase ? "" : "i"); + this.regexp = regexp; + return regexp; + }, + contentType: 2147483647, + matchCase: false, + thirdParty: null, + sitekeySource: null, + getSitekeys: function() + { + var prop = getOwnPropertyDescriptor(this, "sitekeys"); + if (prop) + { + return prop; + } + var sitekeys = null; + if (this.sitekeySource) + { + sitekeys = this.sitekeySource.split("|"); + this.sitekeySource = null; + } + this.sitekeys = sitekeys; + return this.sitekeys; + }, + matches: function(location, contentType, docDomain, thirdParty, sitekey) + { + if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) + { + return true; + } + return false; + } +}); +RegExpFilter.prototype["0"] = "#this"; +RegExpFilter.fromText = function(text) +{ + var blocking = true; + var origText = text; + if (text.indexOf("@@") == 0) + { + blocking = false; + text = text.substr(2); + } + var contentType = null; + var matchCase = null; + var domains = null; + var sitekeys = null; + var thirdParty = null; + var collapse = null; + var options; + var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; + if (match) + { + options = match[1].toUpperCase().split(","); + text = match.input.substr(0, match.index); + for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) + { + var option = options[_loopIndex6]; + var value = null; + var separatorIndex = option.indexOf("="); + if (separatorIndex >= 0) + { + value = option.substr(separatorIndex + 1); + option = option.substr(0, separatorIndex); + } + option = option.replace(/-/, "_"); + if (option in RegExpFilter.typeMap) + { + if (contentType == null) + { + contentType = 0; + } + contentType |= RegExpFilter.typeMap[option]; + } + else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap) + { + if (contentType == null) + { + contentType = RegExpFilter.prototype.contentType; + } + contentType &= ~RegExpFilter.typeMap[option.substr(1)]; + } + else if (option == "MATCH_CASE") + { + matchCase = true; + } + else if (option == "~MATCH_CASE") + { + matchCase = false; + } + else if (option == "DOMAIN" && typeof value != "undefined") + { + domains = value; + } + else if (option == "THIRD_PARTY") + { + thirdParty = true; + } + else if (option == "~THIRD_PARTY") + { + thirdParty = false; + } + else if (option == "COLLAPSE") + { + collapse = true; + } + else if (option == "~COLLAPSE") + { + collapse = false; + } + else if (option == "SITEKEY" && typeof value != "undefined") + { + sitekeys = value; + } + else + { + return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); + } + } + } + if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) + { + if (contentType == null) + { + contentType = RegExpFilter.prototype.contentType; + } + contentType &= ~RegExpFilter.typeMap.DOCUMENT; + } + try + { + if (blocking) + { + return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); + } + else + { + return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); + } + } + catch (e) + { + return new InvalidFilter(origText, e); + } +}; +RegExpFilter.typeMap = { + OTHER: 1, + SCRIPT: 2, + IMAGE: 4, + STYLESHEET: 8, + OBJECT: 16, + SUBDOCUMENT: 32, + DOCUMENT: 64, + XBL: 1, + PING: 1, + XMLHTTPREQUEST: 2048, + OBJECT_SUBREQUEST: 4096, + DTD: 1, + MEDIA: 16384, + FONT: 32768, + BACKGROUND: 4, + POPUP: 268435456, + ELEMHIDE: 1073741824 +}; +RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); + +function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) +{ + RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); + this.collapse = collapse; +} +extend(BlockingFilter, RegExpFilter, { + collapse: null +}); + +function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) +{ + RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); +} +extend(WhitelistFilter, RegExpFilter, { +}); + +function Matcher() +{ + this.clear(); +} +Matcher.prototype = { + filterByKeyword: null, + keywordByFilter: null, + clear: function() + { + this.filterByKeyword = createDict(); + this.keywordByFilter = createDict(); + }, + add: function(filter) + { + if (filter.text in this.keywordByFilter) + { + return; + } + var keyword = this.findKeyword(filter); + var oldEntry = this.filterByKeyword[keyword]; + if (typeof oldEntry == "undefined") + { + this.filterByKeyword[keyword] = filter; + } + else if (oldEntry.length == 1) + { + this.filterByKeyword[keyword] = [oldEntry, filter]; + } + else + { + oldEntry.push(filter); + } + this.keywordByFilter[filter.text] = keyword; + }, + remove: function(filter) + { + if (!(filter.text in this.keywordByFilter)) + { + return; + } + var keyword = this.keywordByFilter[filter.text]; + var list = this.filterByKeyword[keyword]; + if (list.length <= 1) + { + delete this.filterByKeyword[keyword]; + } + else + { + var index = list.indexOf(filter); + if (index >= 0) + { + list.splice(index, 1); + if (list.length == 1) + { + this.filterByKeyword[keyword] = list[0]; + } + } + } + delete this.keywordByFilter[filter.text]; + }, + findKeyword: function(filter) + { + var result = ""; + var text = filter.text; + if (Filter.regexpRegExp.test(text)) + { + return result; + } + var match = Filter.optionsRegExp.exec(text); + if (match) + { + text = match.input.substr(0, match.index); + } + if (text.substr(0, 2) == "@@") + { + text = text.substr(2); + } + var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); + if (!candidates) + { + return result; + } + var hash = this.filterByKeyword; + var resultCount = 16777215; + var resultLength = 0; + for (var i = 0, l = candidates.length; i < l; i++) + { + var candidate = candidates[i].substr(1); + var count = candidate in hash ? hash[candidate].length : 0; + if (count < resultCount || count == resultCount && candidate.length > resultLength) + { + result = candidate; + resultCount = count; + resultLength = candidate.length; + } + } + return result; + }, + hasFilter: function(filter) + { + return filter.text in this.keywordByFilter; + }, + getKeywordForFilter: function(filter) + { + if (filter.text in this.keywordByFilter) + { + return this.keywordByFilter[filter.text]; + } + else + { + return null; + } + }, + _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) + { + var list = this.filterByKeyword[keyword]; + for (var i = 0; i < list.length; i++) + { + var filter = list[i]; + if (filter == "#this") + { + filter = list; + } + if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) + { + return filter; + } + } + return null; + }, + matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) + { + var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); + if (candidates === null) + { + candidates = []; + } + candidates.push(""); + for (var i = 0, l = candidates.length; i < l; i++) + { + var substr = candidates[i]; + if (substr in this.filterByKeyword) + { + var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); + if (result) + { + return result; + } + } + } + return null; + } +}; + +function CombinedMatcher() +{ + this.blacklist = new Matcher(); + this.whitelist = new Matcher(); + this.resultCache = createDict(); +} +CombinedMatcher.maxCacheEntries = 1000; +CombinedMatcher.prototype = { + blacklist: null, + whitelist: null, + resultCache: null, + cacheEntries: 0, + clear: function() + { + this.blacklist.clear(); + this.whitelist.clear(); + this.resultCache = createDict(); + this.cacheEntries = 0; + }, + add: function(filter) + { + if (filter instanceof WhitelistFilter) + { + this.whitelist.add(filter); + } + else + { + this.blacklist.add(filter); + } + if (this.cacheEntries > 0) + { + this.resultCache = createDict(); + this.cacheEntries = 0; + } + }, + remove: function(filter) + { + if (filter instanceof WhitelistFilter) + { + this.whitelist.remove(filter); + } + else + { + this.blacklist.remove(filter); + } + if (this.cacheEntries > 0) + { + this.resultCache = createDict(); + this.cacheEntries = 0; + } + }, + findKeyword: function(filter) + { + if (filter instanceof WhitelistFilter) + { + return this.whitelist.findKeyword(filter); + } + else + { + return this.blacklist.findKeyword(filter); + } + }, + hasFilter: function(filter) + { + if (filter instanceof WhitelistFilter) + { + return this.whitelist.hasFilter(filter); + } + else + { + return this.blacklist.hasFilter(filter); + } + }, + getKeywordForFilter: function(filter) + { + if (filter instanceof WhitelistFilter) + { + return this.whitelist.getKeywordForFilter(filter); + } + else + { + return this.blacklist.getKeywordForFilter(filter); + } + }, + isSlowFilter: function(filter) + { + var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; + if (matcher.hasFilter(filter)) + { + return !matcher.getKeywordForFilter(filter); + } + else + { + return !matcher.findKeyword(filter); + } + }, + matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) + { + var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); + if (candidates === null) + { + candidates = []; + } + candidates.push(""); + var blacklistHit = null; + for (var i = 0, l = candidates.length; i < l; i++) + { + var substr = candidates[i]; + if (substr in this.whitelist.filterByKeyword) + { + var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); + if (result) + { + return result; + } + } + if (substr in this.blacklist.filterByKeyword && blacklistHit === null) + { + blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); + } + } + return blacklistHit; + }, + matchesAny: function(location, docDomain) + { + var key = location + " " + docDomain + " "; + if (key in this.resultCache) + { + return this.resultCache[key]; + } + var result = this.matchesAnyInternal(location, 0, docDomain, null, null); + if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) + { + this.resultCache = createDict(); + this.cacheEntries = 0; + } + this.resultCache[key] = result; + this.cacheEntries++; + return result; + } +}; + +var userrulesMatcher = new CombinedMatcher(); +var defaultMatcher = new CombinedMatcher(); + +var direct = 'DIRECT;'; + +for (var i = 0; i < userrules.length; i++) { + userrulesMatcher.add(Filter.fromText(userrules[i])); +} + +for (var i = 0; i < rules.length; i++) { + defaultMatcher.add(Filter.fromText(rules[i])); +} + +function FindProxyForURL(url, host) { + if (userrulesMatcher.matchesAny(url, host) instanceof BlockingFilter) { + return proxy; + } + 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; + } + return direct; +} diff --git a/shadowsocks-csharp/Data/dlc.dat b/Shadowsocks/Assets/dlc.dat similarity index 100% rename from shadowsocks-csharp/Data/dlc.dat rename to Shadowsocks/Assets/dlc.dat diff --git a/shadowsocks-csharp/Data/privoxy.exe.gz b/Shadowsocks/Assets/privoxy.exe.gz similarity index 100% rename from shadowsocks-csharp/Data/privoxy.exe.gz rename to Shadowsocks/Assets/privoxy.exe.gz diff --git a/shadowsocks-csharp/Data/privoxy_conf.txt b/Shadowsocks/Assets/privoxy_conf.txt similarity index 96% rename from shadowsocks-csharp/Data/privoxy_conf.txt rename to Shadowsocks/Assets/privoxy_conf.txt index bef1c726..e6529288 100644 --- a/shadowsocks-csharp/Data/privoxy_conf.txt +++ b/Shadowsocks/Assets/privoxy_conf.txt @@ -1,8 +1,8 @@ -listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ -toggle 0 -logfile ss_privoxy.log -show-on-task-bar 0 -activity-animation 0 -forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . -max-client-connections 2048 -hide-console +listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ +toggle 0 +logfile ss_privoxy.log +show-on-task-bar 0 +activity-animation 0 +forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . +max-client-connections 2048 +hide-console diff --git a/shadowsocks-csharp/Data/user-rule.txt b/Shadowsocks/Assets/user-rule.txt old mode 100755 new mode 100644 similarity index 98% rename from shadowsocks-csharp/Data/user-rule.txt rename to Shadowsocks/Assets/user-rule.txt index 8a7fab80..14bfe023 --- a/shadowsocks-csharp/Data/user-rule.txt +++ b/Shadowsocks/Assets/user-rule.txt @@ -1,2 +1,2 @@ -! Put user rules line by line in this file. -! See https://adblockplus.org/en/filter-cheatsheet +! Put user rules line by line in this file. +! See https://adblockplus.org/en/filter-cheatsheet diff --git a/Shadowsocks/Resource.Designer.cs b/Shadowsocks/Resource.Designer.cs new file mode 100644 index 00000000..ff15eaa2 --- /dev/null +++ b/Shadowsocks/Resource.Designer.cs @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Shadowsocks { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Resource", typeof(Resource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 /* eslint-disable */ + ///// Was generated by gfwlist2pac in precise mode + ///// https://github.com/clowwindy/gfwlist2pac + /// + ///// 2019-10-06: More 'javascript' way to interaction with main program + ///// 2019-02-08: Updated to support shadowsocks-windows user rules. + /// + ///var proxy = __PROXY__; + ///var userrules = []; + ///var rules = []; + /// + ///// convert to abp grammar + ///for (var i = 0; i < __RULES__.length; i++) { + /// var s = __RULES__[i]; + /// if (s.substring(0, 2) == "||") s += "^"; + /// rules.push(s); + ///} + /// + ///for (var i = 0; i < [字符串的其余部分被截断]"; 的本地化字符串。 + /// + internal static string ABP_JS { + get { + return ResourceManager.GetString("ABP_JS", resourceCulture); + } + } + + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] DLC_DAT { + get { + object obj = ResourceManager.GetObject("DLC_DAT", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// 查找类似 <?xml version="1.0" encoding="utf-8" ?> + ///<!-- Warning: Configuration may reset after shadowsocks upgrade. --> + ///<!-- If you messed it up, delete this file and Shadowsocks will create a new one. --> + ///<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + /// <targets> + /// <!-- This line is managed by Shadowsocks. Do not modify it unless you know what you are doing.--> + /// <target name="file" xsi:type="File" fileName="ss_win_temp\shadowsocks [字符串的其余部分被截断]"; 的本地化字符串。 + /// + internal static string NLOG_CONFIG { + get { + return ResourceManager.GetString("NLOG_CONFIG", resourceCulture); + } + } + + /// + /// 查找类似 listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ + ///toggle 0 + ///logfile ss_privoxy.log + ///show-on-task-bar 0 + ///activity-animation 0 + ///forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . + ///max-client-connections 2048 + ///hide-console + /// 的本地化字符串。 + /// + internal static string PRIVOXY_CONF { + get { + return ResourceManager.GetString("PRIVOXY_CONF", resourceCulture); + } + } + + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] PRIVOXY_EXE { + get { + object obj = ResourceManager.GetObject("PRIVOXY_EXE", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// 查找类似 ! Put user rules line by line in this file. + ///! See https://adblockplus.org/en/filter-cheatsheet + /// 的本地化字符串。 + /// + internal static string USER_RULE { + get { + return ResourceManager.GetString("USER_RULE", resourceCulture); + } + } + } +} diff --git a/Shadowsocks/Resource.resx b/Shadowsocks/Resource.resx new file mode 100644 index 00000000..04be637b --- /dev/null +++ b/Shadowsocks/Resource.resx @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + assets\abp.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312 + + + assets\dlc.dat;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + assets\nlog.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312 + + + assets\privoxy_conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312 + + + assets\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + assets\user-rule.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + \ No newline at end of file diff --git a/Shadowsocks/Shadowsocks.csproj b/Shadowsocks/Shadowsocks.csproj new file mode 100644 index 00000000..eb331c1c --- /dev/null +++ b/Shadowsocks/Shadowsocks.csproj @@ -0,0 +1,31 @@ + + + + netstandard2.1 + clowwindy & community 2020 + + + + + + + + + + + + + True + True + Resource.resx + + + + + + ResXFileCodeGenerator + Resource.Designer.cs + + + + diff --git a/shadowsocks-csharp/Data/NLog.config b/shadowsocks-csharp/Data/NLog.config deleted file mode 100644 index 05509cff..00000000 --- a/shadowsocks-csharp/Data/NLog.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/shadowsocks-csharp/FodyWeavers.xml b/shadowsocks-csharp/FodyWeavers.xml deleted file mode 100644 index 39c0beec..00000000 --- a/shadowsocks-csharp/FodyWeavers.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/shadowsocks-csharp/FodyWeavers.xsd b/shadowsocks-csharp/FodyWeavers.xsd deleted file mode 100644 index 91102829..00000000 --- a/shadowsocks-csharp/FodyWeavers.xsd +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks - - - - - A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. - - - - - A list of unmanaged 32 bit assembly names to include, delimited with line breaks. - - - - - A list of unmanaged 64 bit assembly names to include, delimited with line breaks. - - - - - The order of preloaded assemblies, delimited with line breaks. - - - - - - This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. - - - - - Controls if .pdbs for reference assemblies are also embedded. - - - - - Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. - - - - - As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. - - - - - Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. - - - - - Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. - - - - - A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | - - - - - A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. - - - - - A list of unmanaged 32 bit assembly names to include, delimited with |. - - - - - A list of unmanaged 64 bit assembly names to include, delimited with |. - - - - - The order of preloaded assemblies, delimited with |. - - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file diff --git a/shadowsocks-csharp/Localization/LocalizationProvider.cs b/shadowsocks-csharp/Localization/LocalizationProvider.cs deleted file mode 100644 index 0677bce1..00000000 --- a/shadowsocks-csharp/Localization/LocalizationProvider.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Reflection; -using WPFLocalizeExtension.Extensions; - -namespace Shadowsocks.Localization -{ - public static class LocalizationProvider - { - public static T GetLocalizedValue(string key) - { - return LocExtension.GetLocalizedValue(Assembly.GetCallingAssembly().GetName().Name + ":Strings:" + key); - } - } -} diff --git a/shadowsocks-csharp/Properties/Resources.Designer.cs b/shadowsocks-csharp/Properties/Resources.Designer.cs deleted file mode 100644 index 2c64d698..00000000 --- a/shadowsocks-csharp/Properties/Resources.Designer.cs +++ /dev/null @@ -1,244 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Shadowsocks.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to /* eslint-disable */ - ///// Was generated by gfwlist2pac in precise mode - ///// https://github.com/clowwindy/gfwlist2pac - /// - ///// 2019-10-06: More 'javascript' way to interaction with main program - ///// 2019-02-08: Updated to support shadowsocks-windows user rules. - /// - ///var proxy = __PROXY__; - ///var userrules = []; - ///var rules = []; - /// - ///// convert to abp grammar - ///for (var i = 0; i < __RULES__.length; i++) { - /// var s = __RULES__[i]; - /// if (s.substring(0, 2) == "||") s += "^"; - /// rules.push(s); - ///} - /// - ///for (var i = 0; i < [rest of string was truncated]";. - /// - public static string abp_js { - get { - return ResourceManager.GetString("abp_js", resourceCulture); - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - public static byte[] dlc_dat { - get { - object obj = ResourceManager.GetObject("dlc_dat", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Looks up a localized string similar to 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 [rest of string was truncated]";. - /// - public static string i18n_csv { - get { - return ResourceManager.GetString("i18n_csv", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> - ///<!-- Warning: Configuration may reset after shadowsocks upgrade. --> - ///<!-- If you messed it up, delete this file and Shadowsocks will create a new one. --> - ///<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - /// <targets> - /// <!-- This line is managed by Shadowsocks. Do not modify it unless you know what you are doing.--> - /// <target name="file" xsi:type="File" fileName="ss_win_temp\shadowsocks.log" writ [rest of string was truncated]";. - /// - public static string NLog_config { - get { - return ResourceManager.GetString("NLog_config", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ - ///toggle 0 - ///logfile ss_privoxy.log - ///show-on-task-bar 0 - ///activity-animation 0 - ///forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . - ///max-client-connections 2048 - ///hide-console - ///. - /// - public static string privoxy_conf { - get { - return ResourceManager.GetString("privoxy_conf", resourceCulture); - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - public static byte[] privoxy_exe { - get { - object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - public static System.Drawing.Bitmap ss32Fill { - get { - object obj = ResourceManager.GetObject("ss32Fill", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - public static System.Drawing.Bitmap ss32In { - get { - object obj = ResourceManager.GetObject("ss32In", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - public static System.Drawing.Bitmap ss32Out { - get { - object obj = ResourceManager.GetObject("ss32Out", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - public static System.Drawing.Bitmap ss32Outline { - get { - object obj = ResourceManager.GetObject("ss32Outline", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - public static System.Drawing.Bitmap ssw128 { - get { - object obj = ResourceManager.GetObject("ssw128", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - public static byte[] sysproxy_exe { - get { - object obj = ResourceManager.GetObject("sysproxy_exe", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - public static byte[] sysproxy64_exe { - get { - object obj = ResourceManager.GetObject("sysproxy64_exe", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Looks up a localized string similar to ! Put user rules line by line in this file. - ///! See https://adblockplus.org/en/filter-cheatsheet - ///. - /// - public static string user_rule { - get { - return ResourceManager.GetString("user_rule", resourceCulture); - } - } - } -} diff --git a/shadowsocks-csharp/Properties/Settings.Designer.cs b/shadowsocks-csharp/Properties/Settings.Designer.cs deleted file mode 100644 index 4cc88b29..00000000 --- a/shadowsocks-csharp/Properties/Settings.Designer.cs +++ /dev/null @@ -1,86 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace Shadowsocks.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("600")] - public int LogViewerWidth { - get { - return ((int)(this["LogViewerWidth"])); - } - set { - this["LogViewerWidth"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("400")] - public int LogViewerHeight { - get { - return ((int)(this["LogViewerHeight"])); - } - set { - this["LogViewerHeight"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool LogViewerMaximized { - get { - return ((bool)(this["LogViewerMaximized"])); - } - set { - this["LogViewerMaximized"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LogViewerTop { - get { - return ((int)(this["LogViewerTop"])); - } - set { - this["LogViewerTop"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LogViewerLeft { - get { - return ((int)(this["LogViewerLeft"])); - } - set { - this["LogViewerLeft"] = value; - } - } - } -} diff --git a/shadowsocks-csharp/Properties/Settings.settings b/shadowsocks-csharp/Properties/Settings.settings deleted file mode 100644 index e6469363..00000000 --- a/shadowsocks-csharp/Properties/Settings.settings +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - 600 - - - 400 - - - True - - - 0 - - - 0 - - - \ No newline at end of file diff --git a/shadowsocks-windows.sln b/shadowsocks-windows.sln index 806e8434..5f4391ec 100644 --- a/shadowsocks-windows.sln +++ b/shadowsocks-windows.sln @@ -12,6 +12,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShadowsocksTest", "test\Sha EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7BD54B24-BBD7-4DD8-99EF-DAEE6684B673}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig .gitignore = .gitignore appveyor.yml = appveyor.yml appveyor.yml.obsolete = appveyor.yml.obsolete @@ -23,6 +24,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks.Common", "Shadowsocks.Common\Shadowsocks.Common.csproj", "{61AF0616-8E90-46C4-908A-E182A5001AB5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks.Crypto", "Shadowsocks.Crypto\Shadowsocks.Crypto.csproj", "{AD8974F4-EFDC-4EC5-A147-54CD4934D295}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks", "Shadowsocks\Shadowsocks.csproj", "{85C2FC93-91F0-454F-AF0A-522FCD20827A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks.WPF", "Shadowsocks.WPF\Shadowsocks.WPF.csproj", "{EA1FB2D4-B5A7-47A6-B097-2F4D29E23010}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,6 +46,22 @@ Global {45913187-0685-4903-B250-DCEF0479CD86}.Debug|Any CPU.Build.0 = Debug|Any CPU {45913187-0685-4903-B250-DCEF0479CD86}.Release|Any CPU.ActiveCfg = Release|Any CPU {45913187-0685-4903-B250-DCEF0479CD86}.Release|Any CPU.Build.0 = Release|Any CPU + {61AF0616-8E90-46C4-908A-E182A5001AB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61AF0616-8E90-46C4-908A-E182A5001AB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61AF0616-8E90-46C4-908A-E182A5001AB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61AF0616-8E90-46C4-908A-E182A5001AB5}.Release|Any CPU.Build.0 = Release|Any CPU + {AD8974F4-EFDC-4EC5-A147-54CD4934D295}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD8974F4-EFDC-4EC5-A147-54CD4934D295}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD8974F4-EFDC-4EC5-A147-54CD4934D295}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD8974F4-EFDC-4EC5-A147-54CD4934D295}.Release|Any CPU.Build.0 = Release|Any CPU + {85C2FC93-91F0-454F-AF0A-522FCD20827A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85C2FC93-91F0-454F-AF0A-522FCD20827A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85C2FC93-91F0-454F-AF0A-522FCD20827A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85C2FC93-91F0-454F-AF0A-522FCD20827A}.Release|Any CPU.Build.0 = Release|Any CPU + {EA1FB2D4-B5A7-47A6-B097-2F4D29E23010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA1FB2D4-B5A7-47A6-B097-2F4D29E23010}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA1FB2D4-B5A7-47A6-B097-2F4D29E23010}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA1FB2D4-B5A7-47A6-B097-2F4D29E23010}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE