Skip to content

Commit

Permalink
Merge remote-tracking branch 'fork/meta-dev' into meta
Browse files Browse the repository at this point in the history
  • Loading branch information
mrFq1 committed Jul 17, 2022
2 parents 0e4f950 + c39c4c5 commit 9e12334
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 32 deletions.
34 changes: 20 additions & 14 deletions ClashX/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
let group = DispatchGroup()
var shouldWait = false

PrivilegedHelperManager.shared.helper()?.stopMeta()

if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) ||
NetworkChangeNotifier.hasInterfaceProxySetToClash() {
Expand Down Expand Up @@ -276,20 +278,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self.snifferMenuItem.state = config.sniffing ? .on : .off
}.disposed(by: disposeBag)

if !PrivilegedHelperManager.shared.isHelperCheckFinished.value &&
ConfigManager.shared.proxyPortAutoSet {
PrivilegedHelperManager.shared.isHelperCheckFinished
.filter({$0})
.take(1)
.take(while: {_ in ConfigManager.shared.proxyPortAutoSet})
.observe(on: MainScheduler.instance)
.bind(onNext: { _ in
SystemProxyManager.shared.enableProxy()
}).disposed(by: disposeBag)
} else if ConfigManager.shared.proxyPortAutoSet {
SystemProxyManager.shared.enableProxy()
}

if !PrivilegedHelperManager.shared.isHelperCheckFinished.value {
proxySettingMenuItem.target = nil
PrivilegedHelperManager.shared.isHelperCheckFinished
Expand Down Expand Up @@ -317,6 +305,22 @@ class AppDelegate: NSObject, NSApplicationDelegate {
updateConfig(showNotification: false)
}
}

func setupSystemData() {
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value &&
ConfigManager.shared.proxyPortAutoSet {
PrivilegedHelperManager.shared.isHelperCheckFinished
.filter({$0})
.take(1)
.take(while: {_ in ConfigManager.shared.proxyPortAutoSet})
.observe(on: MainScheduler.instance)
.bind(onNext: { _ in
SystemProxyManager.shared.enableProxy()
}).disposed(by: disposeBag)
} else if ConfigManager.shared.proxyPortAutoSet {
SystemProxyManager.shared.enableProxy()
}
}

func setupNetworkNotifier() {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
Expand Down Expand Up @@ -548,6 +552,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
ConfigManager.shared.isRunning = true
proxyModeMenuItem.isEnabled = true
dashboardMenuItem.isEnabled = true

setupSystemData()
} else {
ConfigManager.shared.isRunning = false
proxyModeMenuItem.isEnabled = false
Expand Down
4 changes: 2 additions & 2 deletions ProxyConfigHelper/Helper-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<key>CFBundleName</key>
<string>com.metacubex.ClashX.ProxyConfigHelper</string>
<key>CFBundleShortVersionString</key>
<string>1.2</string>
<string>1.3</string>
<key>CFBundleVersion</key>
<string>3</string>
<string>4</string>
<key>SMAuthorizedClients</key>
<array>
<string>anchor apple generic and identifier &quot;com.metacubex.ClashX.ProxyConfigHelper&quot; and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = MEWHFZ92DY)</string>
Expand Down
121 changes: 105 additions & 16 deletions ProxyConfigHelper/MetaTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@ class MetaTask: NSObject {
}
}

struct MetaCurl: Decodable {
let hello: String
}

let proc = Process()
var uiPath: String?
let procQueue = DispatchQueue(label: "com.metacubex.ClashX.ProxyConfigHelper.MetaProcess")
let procQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".MetaProcess")

var timer: DispatchSourceTimer?
let timerQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".timer")

@objc func setLaunchPath(_ path: String) {
proc.executableURL = .init(fileURLWithPath: path)
Expand All @@ -46,8 +51,9 @@ class MetaTask: NSObject {

func returnResult(_ re: String) {
guard !resultReturned else { return }
timer?.cancel()
timer = nil
resultReturned = true

DispatchQueue.main.async {
result(re)
}
Expand Down Expand Up @@ -114,10 +120,10 @@ class MetaTask: NSObject {
returnResult("Not found RESTful API port.")
return
}
let test = self.testListenPort(port)
if test.pid != 0,
test.pid == self.proc.processIdentifier,
test.addr == addr {
let testLP = self.testListenPort(port)
if testLP.pid != 0,
testLP.pid == self.proc.processIdentifier,
testLP.addr == addr {
serverResult.log = logs.joined(separator: "\n")
returnResult(serverResult.jsonString())
} else {
Expand All @@ -126,9 +132,13 @@ class MetaTask: NSObject {
}
*/

if $0.contains("Apply all configs finished.") {
serverResult.log = logs.joined(separator: "\n")
returnResult(serverResult.jsonString())
if $0.contains("RESTful API listening at:") {
if self.testExternalController(serverResult) {
serverResult.log = logs.joined(separator: "\n")
returnResult(serverResult.jsonString())
} else {
returnResult("Check RESTful API failed.")
}
}
}
}
Expand All @@ -149,11 +159,23 @@ class MetaTask: NSObject {
returnResult(results.joined(separator: "\n"))
}

self.timer = DispatchSource.makeTimerSource(queue: self.timerQueue)
self.timer?.schedule(deadline: .now(), repeating: .milliseconds(500))
self.timer?.setEventHandler {
guard self.testExternalController(serverResult) else {
return
}
serverResult.log = logs.joined(separator: "\n")
returnResult(serverResult.jsonString())
}

DispatchQueue.global().asyncAfter(deadline: .now() + 30) {
serverResult.log = logs.joined(separator: "\n")
returnResult(serverResult.jsonString())
}

try self.proc.run()
self.timer?.resume()
} catch let error {
returnResult("Start meta error, \(error.localizedDescription).")
}
Expand All @@ -163,7 +185,11 @@ class MetaTask: NSObject {
@objc func stop() {
DispatchQueue.main.async {
guard self.proc.isRunning else { return }
self.proc.interrupt()
let proc = Process()
proc.executableURL = .init(fileURLWithPath: "/bin/kill")
proc.arguments = ["-9", "\(self.proc.processIdentifier)"]
try? proc.run()
proc.waitUntilExit()
}
}

Expand Down Expand Up @@ -250,6 +276,63 @@ class MetaTask: NSObject {
return (Int32(pid) ?? 0, addr)
}

func findExternalControllerPort(_ port: Int) -> Int {
let proc = Process()
let pipe = Pipe()
proc.standardOutput = pipe
proc.executableURL = .init(fileURLWithPath: "/bin/bash")
proc.arguments = ["-c", "lsof -nP -iTCP -sTCP:LISTEN | grep LISTEN"]
try? proc.run()
proc.waitUntilExit()

let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let str = String(data: data, encoding: .utf8) else {
return port
}

let ports = str.split(separator: "\n").map {
String($0).split(separator: " ")
}.compactMap { re -> Int? in
guard re.count == 10,
let range = re[8].range(of: ":", options: .backwards) else { return nil }
let s = re[8]
let p = s[range.upperBound..<s.endIndex]
return Int(p)
}
guard ports.contains(port) else {
return port
}

var aPorts = Set(port..<65534)
aPorts.subtract(ports)
return aPorts.min() ?? port
}

func testExternalController(_ server: MetaServer) -> Bool {
let proc = Process()
let pipe = Pipe()
proc.standardOutput = pipe
proc.executableURL = .init(fileURLWithPath: "/usr/bin/curl")
var args = [server.externalController]
if server.secret != "" {
args.append(contentsOf: [
"--header",
"Authorization: Bearer \(server.secret)"
])
}

proc.arguments = args
try? proc.run()
proc.waitUntilExit()

let data = pipe.fileHandleForReading.readDataToEndOfFile()

guard let str = try? JSONDecoder().decode(MetaCurl.self, from: data),
str.hello == "clash.meta" else {
return false
}
return true
}

func formatMsg(_ msg: String) -> String {
let msgs = msg.split(separator: " ", maxSplits: 2).map(String.init)
Expand Down Expand Up @@ -281,14 +364,20 @@ class MetaTask: NSObject {
let content = String(data: data, encoding: .utf8) else {
return nil
}

let lines = content.split(separator: "\n").map(String.init)

let serverAddr = lines.first(where: { $0.starts(with: "external-controller: ") })?.dropFirst("external-controller: ".count) ?? ""

let serverSecret = lines.first(where: { $0.starts(with: "secret: ") })?.dropFirst("secret: ".count) ?? ""
func find(_ key: String) -> String {
var re = lines.first(where: { $0.starts(with: "\(key): ") })?.dropFirst("\(key): ".count) ?? ""

if re.hasPrefix("\"") && re.hasSuffix("\"")
|| re.hasPrefix("'") && re.hasSuffix("'") {
re.removeLast()
re.removeFirst()
}
return String(re)
}

return MetaServer(externalController: String(serverAddr),
secret: String(serverSecret))
return MetaServer(externalController: find("external-controller"),
secret: find("secret"))
}
}
4 changes: 4 additions & 0 deletions ProxyConfigHelper/ProxyConfigHelper.m
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,8 @@ - (void)verifyMetaWithConfPath:(NSString *)confPath ConfFilePath:(NSString *)con
reply(re);
}

- (void)stopMeta {
[self.metaTask stop];
}

@end
2 changes: 2 additions & 0 deletions ProxyConfigHelper/ProxyConfigRemoteProcessProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ typedef void(^dictReplyBlock)(NSDictionary *);
ConfFilePath:(NSString *)confFilePath
result:(stringReplyBlock)reply;

- (void)stopMeta;

- (void)getVersion:(stringReplyBlock)reply;

- (void)enableProxyWithPort:(int)port
Expand Down

0 comments on commit 9e12334

Please sign in to comment.