diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 716de2e..7e91e06 100755 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 9B3FFF4F1D09D9D50019A709 /* ProxyConfHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B3FFF4E1D09D9D50019A709 /* ProxyConfHelper.m */; }; 9B3FFF541D09E2D10019A709 /* proxy_conf_helper in Resources */ = {isa = PBXBuildFile; fileRef = 9B3FFF441D09CD3B0019A709 /* proxy_conf_helper */; }; 9B9CBCAF1E263B1600FC61AA /* libpcre.1.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9B9CBCAD1E263A6600FC61AA /* libpcre.1.dylib */; }; + 9B9CBCA61E25E1DB00FC61AA /* KcptunProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B9CBCA51E25E1DB00FC61AA /* KcptunProfile.swift */; }; 9BA04B231D23D5A5005AAD7F /* ProxyConfTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */; }; 9BA04B261D24044D005AAD7F /* ProxyPreferencesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA04B241D24044D005AAD7F /* ProxyPreferencesController.swift */; }; 9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BB706A51D1B982300551F0E /* SWBApplication.m */; }; @@ -151,6 +152,7 @@ 9B3FFF501D09DAEA0019A709 /* proxy_conf_helper_version.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = proxy_conf_helper_version.h; sourceTree = ""; }; 9B3FFF511D09DBA20019A709 /* ShadowsocksX-NG-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ShadowsocksX-NG-Bridging-Header.h"; sourceTree = ""; }; 9B9CBCAD1E263A6600FC61AA /* libpcre.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libpcre.1.dylib; sourceTree = ""; }; + 9B9CBCA51E25E1DB00FC61AA /* KcptunProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KcptunProfile.swift; sourceTree = ""; }; 9BA04B211D23D5A5005AAD7F /* ProxyConfTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProxyConfTool.h; sourceTree = ""; }; 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProxyConfTool.m; sourceTree = ""; }; 9BA04B241D24044D005AAD7F /* ProxyPreferencesController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxyPreferencesController.swift; sourceTree = ""; }; @@ -298,6 +300,7 @@ 9B0BFFEC1D0460A70040E62B /* MainMenu.xib */, 9B0BFFEF1D0460A70040E62B /* Info.plist */, 9BEEF06D1D04DCE400FC52B3 /* ServerProfile.swift */, + 9B9CBCA51E25E1DB00FC61AA /* KcptunProfile.swift */, 9BEEF06F1D04DDB100FC52B3 /* ServerProfileManager.swift */, 9BEEF0771D04FE8A00FC52B3 /* LaunchAgentUtils.swift */, 9B3FFF0C1D05FEB30019A709 /* Utils.swift */, @@ -668,6 +671,7 @@ 9BA04B231D23D5A5005AAD7F /* ProxyConfTool.m in Sources */, 9BEEF0781D04FE8A00FC52B3 /* LaunchAgentUtils.swift in Sources */, C6E28E921DA79380004F8330 /* HTTPPreferencesWindowController.swift in Sources */, + 9B9CBCA61E25E1DB00FC61AA /* KcptunProfile.swift in Sources */, C8E42A6C1D4F270A0074C7EA /* UserRulesController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index a7fdeeb..f6a0f34 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -90,7 +90,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele "LocalHTTP.ListenAddress": "127.0.0.1", "LocalHTTP.ListenPort": NSNumber(value: 1087 as UInt16), "LocalHTTPOn": true, - "LocalHTTP.FollowGlobal": true + "LocalHTTP.FollowGlobal": true, + "Kcptun.LocalHost": "127.0.0.1", + "Kcptun.LocalPort": NSNumber(value: 8388), ]) statusItem = NSStatusBar.system().statusItem(withLength: AppDelegate.StatusItemIconWidth) diff --git a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib index 16b3928..66fc53c 100644 --- a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib +++ b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib @@ -9,8 +9,15 @@ - + + + + + + + + @@ -26,20 +33,20 @@ - - + + - + - + - + - + @@ -85,14 +92,14 @@ - - + + - + - + @@ -100,9 +107,9 @@ - + - + @@ -111,9 +118,9 @@ - + - + @@ -128,7 +135,7 @@ - + @@ -136,7 +143,7 @@ - + @@ -144,7 +151,7 @@ - + @@ -155,7 +162,7 @@ - + @@ -163,7 +170,7 @@ - + @@ -171,25 +178,15 @@ - + - - + @@ -197,60 +194,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSAllRomanInputSourcesLocaleIdentifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - + + + + + @@ -286,7 +435,7 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + @@ -352,6 +499,7 @@ DQ + diff --git a/ShadowsocksX-NG/KcptunProfile.swift b/ShadowsocksX-NG/KcptunProfile.swift new file mode 100644 index 0000000..bcdc6d4 --- /dev/null +++ b/ShadowsocksX-NG/KcptunProfile.swift @@ -0,0 +1,74 @@ +// +// KcptunProfile.swift +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 2017/1/11. +// Copyright © 2017年 qiuyuzhou. All rights reserved. +// + +import Foundation + + +class KcptunProfile: NSObject { + + var mode: String = "normal" + + var key: String = "it's a secrect" + var crypt: String = "aes" + var nocomp: Bool = true + var datashard: uint = 10 + var parityshard: uint = 3 + + + public func copy(with zone: NSZone? = nil) -> Any { + let copy = KcptunProfile() + copy.mode = self.mode + copy.key = self.key + copy.crypt = self.crypt + copy.nocomp = self.nocomp + copy.datashard = self.datashard + copy.parityshard = self.parityshard + return copy; + } + + func toDictionary() -> [String:AnyObject] { + let conf: [String: AnyObject] = [ + "mode": self.mode as AnyObject, + "key": self.key as AnyObject, + "crypt": self.crypt as AnyObject, + "nocomp": NSNumber(value: self.nocomp), + "datashard": NSNumber(value: self.datashard), + "parityshard": NSNumber(value: self.parityshard), + ] + return conf + } + + static func fromDictionary(_ data:[String:Any?]) -> KcptunProfile { + let profile = KcptunProfile() + profile.mode = data["mode"] as! String + profile.key = data["key"] as! String + profile.crypt = data["crypt"] as! String + profile.nocomp = (data["nocomp"] as! NSNumber).boolValue + profile.datashard = uint((data["datashard"] as! NSNumber).uintValue) + profile.parityshard = uint((data["parityshard"] as! NSNumber).uintValue) + + return profile + } + + func toJsonConfig() -> [String: AnyObject] { + let defaults = UserDefaults.standard + let localHost = defaults.string(forKey: "Kcptun.LocalHost") + let localPort = defaults.integer(forKey: "Kcptun.LocalPort") + + let conf: [String: AnyObject] = [ + "localaddr": "\(localHost):\(localPort)" as AnyObject, + "mode": self.mode as AnyObject, + "key": self.key as AnyObject, + "crypt": self.crypt as AnyObject, + "nocomp": NSNumber(value: self.nocomp), + "datashard": NSNumber(value: self.datashard), + "parityshard": NSNumber(value: self.parityshard), + ] + return conf + } +} diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index d9fe652..1fad2b4 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -14,6 +14,7 @@ class PreferencesWindowController: NSWindowController @IBOutlet weak var profilesTableView: NSTableView! @IBOutlet weak var profileBox: NSBox! + @IBOutlet weak var kcptunProfileBox: NSBox! @IBOutlet weak var hostTextField: NSTextField! @IBOutlet weak var portTextField: NSTextField! @@ -24,7 +25,13 @@ class PreferencesWindowController: NSWindowController @IBOutlet weak var otaCheckBoxBtn: NSButton! - @IBOutlet weak var copyURLBtn: NSButton! + @IBOutlet weak var kcptunCheckBoxBtn: NSButton! + @IBOutlet weak var kcptunCryptComboBox: NSComboBox! + @IBOutlet weak var kcptunKeyTextField: NSTextField! + @IBOutlet weak var kcptunModeComboBox: NSComboBox! + @IBOutlet weak var kcptunNocompCheckBoxBtn: NSButton! + @IBOutlet weak var kcptunDatashardTextField: NSTextField! + @IBOutlet weak var kcptunParityshardTextField: NSTextField! @IBOutlet weak var removeButton: NSButton! let tableViewDragType: String = "ss.server.profile.data" @@ -63,6 +70,28 @@ class PreferencesWindowController: NSWindowController "table", ]) + kcptunCryptComboBox.addItems(withObjectValues: [ + "none", + "aes", + "aes-128", + "aes-192", + "salsa20", + "blowfish", + "twofish", + "cast5", + "3des", + "tea", + "xtea", + "xor", + ]) + + kcptunModeComboBox.addItems(withObjectValues: [ + "default", + "normal", + "fast", + "fast2", + ]) + profilesTableView.reloadData() updateProfileBoxVisible() } @@ -189,6 +218,31 @@ class PreferencesWindowController: NSWindowController otaCheckBoxBtn.bind("value", to: editingProfile, withKeyPath: "ota" , options: [NSContinuouslyUpdatesValueBindingOption: true]) + + // -------------------------------------------------- + // Kcptun + kcptunCheckBoxBtn.bind("value", to: editingProfile, withKeyPath: "enabledKcptun" + , options: [NSContinuouslyUpdatesValueBindingOption: true]) + + kcptunProfileBox.bind("Hidden", to: editingProfile, withKeyPath: "enabledKcptun" + , options: [NSContinuouslyUpdatesValueBindingOption: false, + NSValueTransformerNameBindingOption: NSValueTransformerName.negateBooleanTransformerName]) + + kcptunNocompCheckBoxBtn.bind("value", to: editingProfile, withKeyPath: "kcptunProfile.nocomp", options: nil) + + kcptunModeComboBox.bind("value", to: editingProfile, withKeyPath: "kcptunProfile.mode", options: nil) + + kcptunCryptComboBox.bind("value", to: editingProfile, withKeyPath: "kcptunProfile.crypt", options: nil) + + kcptunKeyTextField.bind("value", to: editingProfile, withKeyPath: "kcptunProfile.key" + , options: [NSContinuouslyUpdatesValueBindingOption: true]) + + kcptunDatashardTextField.bind("value", to: editingProfile, withKeyPath: "kcptunProfile.datashard" + , options: [NSContinuouslyUpdatesValueBindingOption: true]) + + kcptunParityshardTextField.bind("value", to: editingProfile, withKeyPath: "kcptunProfile.parityshard" + , options: [NSContinuouslyUpdatesValueBindingOption: true]) + } else { editingProfile = nil hostTextField.unbind("value") @@ -200,6 +254,8 @@ class PreferencesWindowController: NSWindowController remarkTextField.unbind("value") otaCheckBoxBtn.unbind("value") + + kcptunCheckBoxBtn.unbind("value") } } diff --git a/ShadowsocksX-NG/ServerProfile.swift b/ShadowsocksX-NG/ServerProfile.swift index 8f7af5a..273f264 100644 --- a/ShadowsocksX-NG/ServerProfile.swift +++ b/ShadowsocksX-NG/ServerProfile.swift @@ -19,7 +19,10 @@ class ServerProfile: NSObject, NSCopying { var password:String = "" var remark:String = "" var ota: Bool = false // onetime authentication - + + var enabledKcptun: Bool = false + var kcptunProfile = KcptunProfile() + override init() { uuid = UUID().uuidString } @@ -104,6 +107,9 @@ class ServerProfile: NSObject, NSCopying { if let ota = data["OTA"] { profile.ota = ota as! Bool } + if let kcptunData = data["KcptunProfile"] { + profile.kcptunProfile = KcptunProfile.fromDictionary(kcptunData as! [String:Any?]) + } } if let id = data["Id"] as? String { @@ -126,23 +132,40 @@ class ServerProfile: NSObject, NSCopying { d["Password"] = password as AnyObject? d["Remark"] = remark as AnyObject? d["OTA"] = ota as AnyObject? + d["EnabledKcptun"] = enabledKcptun as AnyObject + d["KcptunProfile"] = kcptunProfile.toDictionary() as AnyObject return d } func toJsonConfig() -> [String: AnyObject] { - var conf: [String: AnyObject] = ["server": serverHost as AnyObject, - "server_port": NSNumber(value: serverPort as UInt16), - "password": password as AnyObject, + var conf: [String: AnyObject] = ["password": password as AnyObject, "method": method as AnyObject,] - + let defaults = UserDefaults.standard conf["local_port"] = NSNumber(value: UInt16(defaults.integer(forKey: "LocalSocks5.ListenPort")) as UInt16) conf["local_address"] = defaults.string(forKey: "LocalSocks5.ListenAddress") as AnyObject? conf["timeout"] = NSNumber(value: UInt32(defaults.integer(forKey: "LocalSocks5.Timeout")) as UInt32) conf["auth"] = NSNumber(value: ota as Bool) + + if enabledKcptun { + let localHost = defaults.string(forKey: "Kcptun.LocalHost") + let localPort = uint16(defaults.integer(forKey: "Kcptun.LocalPort")) + + conf["server"] = localHost as AnyObject + conf["server_port"] = NSNumber(value: localPort as UInt16) + } else { + conf["server"] = serverHost as AnyObject + conf["server_port"] = NSNumber(value: serverPort as UInt16) + } return conf } + + func toKcptunJsonConfig() -> [String: AnyObject] { + var conf = kcptunProfile.toJsonConfig() + conf["remoteaddr"] = "\(serverHost):\(serverPort)" as AnyObject + return conf + } func isValid() -> Bool { func validateIpAddress(_ ipToValidate: String) -> Bool {