diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 4412f0d..71b4cdd 100755 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 08FCA0FF1E24BE1A0070984F /* example-gui-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 08FCA0FE1E24BE1A0070984F /* example-gui-config.json */; }; + 1C82DBA81FA96C7500B32551 /* obfs-local in Resources */ = {isa = PBXBuildFile; fileRef = 1C82DBA51FA96C7400B32551 /* obfs-local */; }; + 1C82DBAA1FA96FB600B32551 /* install_simple_obfs.sh in Resources */ = {isa = PBXBuildFile; fileRef = 1C82DBA91FA96F0300B32551 /* install_simple_obfs.sh */; }; 258E511BA910B0521B24DAB8 /* Pods_ShadowsocksX_NG.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 283ED1A8E9B711AC65670031 /* Pods_ShadowsocksX_NG.framework */; }; 9B07EFA71D048BBB0052D9DF /* ss-local in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA61D048BBB0052D9DF /* ss-local */; }; 9B07EFAC1D048E880052D9DF /* menu_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B07EFA81D048E880052D9DF /* menu_icon@2x.png */; }; @@ -136,6 +138,8 @@ /* Begin PBXFileReference section */ 08FCA0FE1E24BE1A0070984F /* example-gui-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "example-gui-config.json"; sourceTree = ""; }; 19083CFCED87354F006967FF /* Pods_ShadowsocksX_NGUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShadowsocksX_NGUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1C82DBA51FA96C7400B32551 /* obfs-local */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "obfs-local"; sourceTree = ""; }; + 1C82DBA91FA96F0300B32551 /* install_simple_obfs.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = install_simple_obfs.sh; sourceTree = ""; }; 1E7783AEDB4A3BDDC9FF16AC /* libPods-proxy_conf_helper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-proxy_conf_helper.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 283ED1A8E9B711AC65670031 /* Pods_ShadowsocksX_NG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShadowsocksX_NG.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 297AF069022A197FD8E9D226 /* Pods-proxy_conf_helper.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-proxy_conf_helper.release.xcconfig"; path = "Pods/Target Support Files/Pods-proxy_conf_helper/Pods-proxy_conf_helper.release.xcconfig"; sourceTree = ""; }; @@ -274,6 +278,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1C82DBA31FA96C7400B32551 /* simple-obfs */ = { + isa = PBXGroup; + children = ( + 1C82DBA51FA96C7400B32551 /* obfs-local */, + 1C82DBA91FA96F0300B32551 /* install_simple_obfs.sh */, + ); + path = "simple-obfs"; + sourceTree = ""; + }; 33F4F299F89330966F4526E4 /* Pods */ = { isa = PBXGroup; children = ( @@ -292,6 +305,7 @@ 9B07EFB01D048E900052D9DF /* Support Files */ = { isa = PBXGroup; children = ( + 1C82DBA31FA96C7400B32551 /* simple-obfs */, 9B16E5991F99FD0700E54DC5 /* icons8-Blind Filled-50.png */, 9B16E5971F99FD0700E54DC5 /* icons8-Eye Filled-50.png */, 9B938D931E864B38005F5636 /* menu_g_icon.png */, @@ -588,6 +602,7 @@ files = ( 9BBE7B751F508A0E00E8FFE5 /* fix_dir_owner.sh in Resources */, 9B3F7BFF1E82BF5B00C68B75 /* libev.4.dylib in Resources */, + 1C82DBAA1FA96FB600B32551 /* install_simple_obfs.sh in Resources */, 9B3F7C001E82BF5B00C68B75 /* libmbedcrypto.2.4.2.dylib in Resources */, 9B3F7C011E82BF5B00C68B75 /* libsodium.18.dylib in Resources */, 9B3F7C021E82BF5B00C68B75 /* libudns.0.dylib in Resources */, @@ -620,6 +635,7 @@ 9BAFE2E21E83ED7F00F71CCE /* PreferencesWinController.xib in Resources */, 9B0BFFEB1D0460A70040E62B /* Assets.xcassets in Resources */, 08FCA0FF1E24BE1A0070984F /* example-gui-config.json in Resources */, + 1C82DBA81FA96C7500B32551 /* obfs-local in Resources */, 9B938D9D1E864B38005F5636 /* menu_p_icon.png in Resources */, 9B938D9B1E864B38005F5636 /* menu_m_icon.png in Resources */, 9B3FFF271D0898EB0019A709 /* gfwlist.txt in Resources */, @@ -1018,6 +1034,7 @@ "$(inherited)", "$(SDKROOT)/usr/lib/system", "$(PROJECT_DIR)/ShadowsocksX-NG", + "$(PROJECT_DIR)/ShadowsocksX-NG/simple-obfs", ); MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_BUNDLE_IDENTIFIER = "com.qiuyuzhou.ShadowsocksX-NG"; @@ -1047,6 +1064,7 @@ "$(inherited)", "$(SDKROOT)/usr/lib/system", "$(PROJECT_DIR)/ShadowsocksX-NG", + "$(PROJECT_DIR)/ShadowsocksX-NG/simple-obfs", ); MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_BUNDLE_IDENTIFIER = "com.qiuyuzhou.ShadowsocksX-NG"; diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index dec3818..00e5665 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -82,6 +82,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele InstallSSLocal() InstallKcptunClient() InstallPrivoxy() + InstallSimpleObfs() // Prepare defaults let defaults = UserDefaults.standard defaults.register(defaults: [ diff --git a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib index b8940d8..6411dda 100644 --- a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib +++ b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib @@ -26,6 +26,8 @@ + + @@ -39,20 +41,20 @@ - + - + - + - + - + @@ -99,13 +101,13 @@ - + - + - + @@ -113,7 +115,7 @@ - + @@ -124,7 +126,7 @@ - + @@ -132,7 +134,7 @@ - + @@ -140,23 +142,47 @@ - + + + + + + + + + + + + + + + + + - + + + + + + + + + - + @@ -164,23 +190,23 @@ - + - + @@ -401,7 +427,7 @@ - + @@ -415,7 +441,7 @@ - + @@ -466,7 +492,7 @@ - + + + + + + + + + + + + + + + @@ -520,11 +560,15 @@ + + + + + - diff --git a/ShadowsocksX-NG/LaunchAgentUtils.swift b/ShadowsocksX-NG/LaunchAgentUtils.swift index f2054f6..04124aa 100644 --- a/ShadowsocksX-NG/LaunchAgentUtils.swift +++ b/ShadowsocksX-NG/LaunchAgentUtils.swift @@ -11,6 +11,7 @@ import Foundation let SS_LOCAL_VERSION = "3.0.5" let KCPTUN_CLIENT_VERSION = "20170322" let PRIVOXY_VERSION = "3.0.26.static" +let SIMPLE_OBFS_VERSION = "0.0.2" let APP_SUPPORT_DIR = "/Library/Application Support/ShadowsocksX-NG/" let LAUNCH_AGENT_DIR = "/Library/LaunchAgents/" let LAUNCH_AGENT_CONF_SSLOCAL_NAME = "com.qiuyuzhou.shadowsocksX-NG.local.plist" @@ -180,6 +181,27 @@ func SyncSSLocal() { SyncKcptun() } +// -------------------------------------------------------------------------------- +// MARK: simple-obfs + +func InstallSimpleObfs() { + let fileMgr = FileManager.default + let homeDir = NSHomeDirectory() + let appSupportDir = homeDir + APP_SUPPORT_DIR + if !fileMgr.fileExists(atPath: appSupportDir + "simple-obfs-\(SIMPLE_OBFS_VERSION)/obfs-local") + || !fileMgr.fileExists(atPath: appSupportDir + "plugins/obfs-local") { + let bundle = Bundle.main + let installerPath = bundle.path(forResource: "install_simple_obfs.sh", ofType: nil) + let task = Process.launchedProcess(launchPath: "/bin/sh", arguments: [installerPath!]) + task.waitUntilExit() + if task.terminationStatus == 0 { + NSLog("Install simple-obfs succeeded.") + } else { + NSLog("Install simple-obfs failed.") + } + } +} + // -------------------------------------------------------------------------------- // MARK: privoxy diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index 21b3951..eb9514d 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -27,6 +27,8 @@ class PreferencesWindowController: NSWindowController @IBOutlet weak var passwordTextField: NSTextField! @IBOutlet weak var passwordSecureTextField: NSSecureTextField! @IBOutlet weak var togglePasswordVisibleButton: NSButton! + @IBOutlet weak var pluginTextField: NSTextField! + @IBOutlet weak var pluginOptionsTextField: NSTextField! @IBOutlet weak var remarkTextField: NSTextField! @@ -264,6 +266,11 @@ class PreferencesWindowController: NSWindowController , options: [NSBindingOption.continuouslyUpdatesValue: true]) passwordSecureTextField.bind(NSBindingName(rawValue: "value"), to: editingProfile, withKeyPath: "password" , options: [NSBindingOption.continuouslyUpdatesValue: true]) + + pluginTextField.bind(NSBindingName(rawValue: "value"), to: editingProfile, withKeyPath: "plugin" + , options: [NSBindingOption.continuouslyUpdatesValue: true]) + pluginOptionsTextField.bind(NSBindingName(rawValue: "value"), to: editingProfile, withKeyPath: "pluginOptions" + , options: [NSBindingOption.continuouslyUpdatesValue: true]) remarkTextField.bind(NSBindingName(rawValue: "value"), to: editingProfile, withKeyPath: "remark" , options: [NSBindingOption.continuouslyUpdatesValue: true]) diff --git a/ShadowsocksX-NG/ServerProfile.swift b/ShadowsocksX-NG/ServerProfile.swift index 6770954..c8adf24 100644 --- a/ShadowsocksX-NG/ServerProfile.swift +++ b/ShadowsocksX-NG/ServerProfile.swift @@ -23,6 +23,10 @@ class ServerProfile: NSObject, NSCopying { @objc var enabledKcptun: Bool = false @objc var kcptunProfile = KcptunProfile() + // SIP003 Plugin + @objc var plugin: String = "" // empty string disables plugin + @objc var pluginOptions: String = "" + override init() { uuid = UUID().uuidString } @@ -113,6 +117,15 @@ class ServerProfile: NSObject, NSCopying { self.kcptunProfile.loadUrlQueryItems(items: items) } } + + if let pluginStr = parsedUrl.queryItems? + .filter({ $0.name == "plugin" }).first?.value { + let parts = pluginStr.split(separator: ";", maxSplits: 1) + if parts.count == 2 { + plugin = String(parts[0]) + pluginOptions = String(parts[1]) + } + } } public func copy(with zone: NSZone? = nil) -> Any { @@ -126,6 +139,8 @@ class ServerProfile: NSObject, NSCopying { copy.enabledKcptun = self.enabledKcptun copy.kcptunProfile = self.kcptunProfile.copy() as! KcptunProfile + copy.plugin = self.plugin + copy.pluginOptions = self.pluginOptions return copy; } @@ -148,6 +163,12 @@ class ServerProfile: NSObject, NSCopying { if let kcptunData = data["KcptunProfile"] { profile.kcptunProfile = KcptunProfile.fromDictionary(kcptunData as! [String:Any?]) } + if let plugin = data["Plugin"] as? String { + profile.plugin = plugin + } + if let pluginOptions = data["PluginOptions"] as? String { + profile.pluginOptions = pluginOptions + } } if let id = data["Id"] as? String { @@ -172,6 +193,8 @@ class ServerProfile: NSObject, NSCopying { d["OTA"] = ota as AnyObject? d["EnabledKcptun"] = NSNumber(value: enabledKcptun) d["KcptunProfile"] = kcptunProfile.toDictionary() as AnyObject + d["Plugin"] = plugin as AnyObject + d["PluginOptions"] = pluginOptions as AnyObject return d } @@ -198,6 +221,13 @@ class ServerProfile: NSObject, NSCopying { conf["server_port"] = NSNumber(value: serverPort as UInt16) } + if !plugin.isEmpty { + // all plugin binaries should be located in the plugins dir + // so that we don't have to mess up with PATH envvars + conf["plugin"] = "plugins/\(plugin)" as AnyObject + conf["plugin_opts"] = pluginOptions as AnyObject + } + return conf } @@ -298,6 +328,10 @@ class ServerProfile: NSObject, NSCopying { items.append(URLQueryItem(name: "Kcptun", value: enabledKcptun.description)) items.append(contentsOf: kcptunProfile.urlQueryItems()) } + if !plugin.isEmpty { + let value = "\(plugin);\(pluginOptions)" + items.append(URLQueryItem(name: "plugin", value: value)) + } var comps = URLComponents() diff --git a/ShadowsocksX-NG/simple-obfs/install_simple_obfs.sh b/ShadowsocksX-NG/simple-obfs/install_simple_obfs.sh new file mode 100644 index 0000000..3c13629 --- /dev/null +++ b/ShadowsocksX-NG/simple-obfs/install_simple_obfs.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +FILE_DIR=`dirname "${BASH_SOURCE[0]}"` +cd "$FILE_DIR" + +NGDir="$HOME/Library/Application Support/ShadowsocksX-NG" +TargetDir="$NGDir/simple-obfs-0.0.2" +PluginDir="$NGDir/plugins" + +echo ngdir: ${NGDir} + +mkdir -p "$TargetDir" +mkdir -p "$PluginDir" + +cp -f obfs-local "$TargetDir" + +ln -sfh "$TargetDir/obfs-local" "$PluginDir" + +echo done diff --git a/ShadowsocksX-NG/simple-obfs/obfs-local b/ShadowsocksX-NG/simple-obfs/obfs-local new file mode 100755 index 0000000..cce1a72 Binary files /dev/null and b/ShadowsocksX-NG/simple-obfs/obfs-local differ diff --git a/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings b/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings index a18ea8f..c3ee3a4 100644 --- a/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings +++ b/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings @@ -37,3 +37,9 @@ /* Class = "NSMenuItem"; title = "Clone"; ObjectID = "bl9-lq-u9V"; */ "bl9-lq-u9V.title" = "克隆"; + +/* Class = "NSTextFieldCell"; title = "Plugin:"; ObjectID = "gDt-yZ-Mqs"; */ +"gDt-yZ-Mqs.title" = "插件:"; + +/* Class = "NSTextFieldCell"; title = "Plugin Opts:"; ObjectID = "orT-7j-dxE"; */ +"orT-7j-dxE.title" = "插件选项:";