170 lines
5.2 KiB
Swift
170 lines
5.2 KiB
Swift
//
|
|
// ExtensionProvider.swift
|
|
// SingBoxPacketTunnel
|
|
//
|
|
// Created by GFWFighter on 7/25/1402 AP.
|
|
//
|
|
|
|
import Foundation
|
|
import Libcore
|
|
import NetworkExtension
|
|
|
|
open class ExtensionProvider: NEPacketTunnelProvider {
|
|
public static let errorFile = FilePath.workingDirectory.appendingPathComponent("network_extension_error")
|
|
|
|
private var commandServer: LibboxCommandServer!
|
|
private var boxService: LibboxBoxService!
|
|
private var systemProxyAvailable = false
|
|
private var systemProxyEnabled = false
|
|
private var platformInterface: ExtensionPlatformInterface!
|
|
private var config: String!
|
|
|
|
override open func startTunnel(options: [String: NSObject]?) async throws {
|
|
try? FileManager.default.removeItem(at: ExtensionProvider.errorFile)
|
|
try? FileManager.default.removeItem(at: FilePath.workingDirectory.appendingPathComponent("TestLog"))
|
|
|
|
let disableMemoryLimit = (options?["DisableMemoryLimit"] as? NSString as? String ?? "NO") == "YES"
|
|
|
|
guard let config = options?["Config"] as? NSString as? String else {
|
|
writeFatalError("(packet-tunnel) error: config not provided")
|
|
return
|
|
}
|
|
guard let config = SingBox.setupConfig(config: config) else {
|
|
writeFatalError("(packet-tunnel) error: config is invalid")
|
|
return
|
|
}
|
|
self.config = config
|
|
|
|
do {
|
|
try FileManager.default.createDirectory(at: FilePath.workingDirectory, withIntermediateDirectories: true)
|
|
} catch {
|
|
writeFatalError("(packet-tunnel) error: create working directory: \(error.localizedDescription)")
|
|
return
|
|
}
|
|
|
|
LibboxSetup(
|
|
FilePath.sharedDirectory.relativePath,
|
|
FilePath.workingDirectory.relativePath,
|
|
FilePath.cacheDirectory.relativePath,
|
|
false
|
|
)
|
|
|
|
var error: NSError?
|
|
LibboxRedirectStderr(FilePath.cacheDirectory.appendingPathComponent("stderr.log").relativePath, &error)
|
|
if let error {
|
|
writeError("(packet-tunnel) redirect stderr error: \(error.localizedDescription)")
|
|
}
|
|
|
|
LibboxSetMemoryLimit(!disableMemoryLimit)
|
|
|
|
if platformInterface == nil {
|
|
platformInterface = ExtensionPlatformInterface(self)
|
|
}
|
|
commandServer = LibboxNewCommandServer(platformInterface, Int32(30))
|
|
do {
|
|
try commandServer.start()
|
|
} catch {
|
|
writeFatalError("(packet-tunnel): log server start error: \(error.localizedDescription)")
|
|
return
|
|
}
|
|
writeMessage("(packet-tunnel) log server started")
|
|
await startService()
|
|
}
|
|
|
|
func writeMessage(_ message: String) {
|
|
if let commandServer {
|
|
commandServer.writeMessage(message)
|
|
} else {
|
|
NSLog(message)
|
|
}
|
|
}
|
|
|
|
func writeError(_ message: String) {
|
|
writeMessage(message)
|
|
try? message.write(to: ExtensionProvider.errorFile, atomically: true, encoding: .utf8)
|
|
}
|
|
|
|
public func writeFatalError(_ message: String) {
|
|
#if DEBUG
|
|
NSLog(message)
|
|
#endif
|
|
writeError(message)
|
|
cancelTunnelWithError(NSError(domain: message, code: 0))
|
|
}
|
|
|
|
private func startService() async {
|
|
let configContent = config
|
|
var error: NSError?
|
|
let service = LibboxNewService(configContent, platformInterface, &error)
|
|
if let error {
|
|
writeError("(packet-tunnel) error: create service: \(error.localizedDescription)")
|
|
return
|
|
}
|
|
guard let service else {
|
|
return
|
|
}
|
|
do {
|
|
try service.start()
|
|
} catch {
|
|
writeError("(packet-tunnel) error: start service: \(error.localizedDescription)")
|
|
return
|
|
}
|
|
boxService = service
|
|
commandServer.setService(service)
|
|
}
|
|
|
|
private func stopService() {
|
|
if let service = boxService {
|
|
do {
|
|
try service.close()
|
|
} catch {
|
|
writeError("(packet-tunnel) error: stop service: \(error.localizedDescription)")
|
|
}
|
|
boxService = nil
|
|
commandServer.setService(nil)
|
|
}
|
|
if let platformInterface {
|
|
platformInterface.reset()
|
|
}
|
|
}
|
|
|
|
func reloadService() async {
|
|
writeMessage("(packet-tunnel) reloading service")
|
|
reasserting = true
|
|
defer {
|
|
reasserting = false
|
|
}
|
|
stopService()
|
|
await startService()
|
|
}
|
|
func postServiceClose() async {
|
|
// TODO
|
|
}
|
|
|
|
override open func stopTunnel(with reason: NEProviderStopReason) async {
|
|
writeMessage("(packet-tunnel) stopping, reason: \(reason)")
|
|
stopService()
|
|
if let server = commandServer {
|
|
try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC)
|
|
try? server.close()
|
|
commandServer = nil
|
|
}
|
|
}
|
|
|
|
override open func handleAppMessage(_ messageData: Data) async -> Data? {
|
|
messageData
|
|
}
|
|
|
|
override open func sleep() async {
|
|
if let boxService {
|
|
boxService.pause()
|
|
}
|
|
}
|
|
|
|
override open func wake() {
|
|
if let boxService {
|
|
boxService.wake()
|
|
}
|
|
}
|
|
}
|