change name to hiddifypackettunnel
This commit is contained in:
20
ios/HiddifyPacketTunnel/HiddifyPacketTunnel.entitlements
Normal file
20
ios/HiddifyPacketTunnel/HiddifyPacketTunnel.entitlements
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>packet-tunnel-provider</string>
|
||||
</array>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.$(BASE_BUNDLE_IDENTIFIER)</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
15
ios/HiddifyPacketTunnel/Info.plist
Normal file
15
ios/HiddifyPacketTunnel/Info.plist
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BASE_BUNDLE_IDENTIFIER</key>
|
||||
<string>$(BASE_BUNDLE_IDENTIFIER)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.networkextension.packet-tunnel</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
52
ios/HiddifyPacketTunnel/Logger.swift
Normal file
52
ios/HiddifyPacketTunnel/Logger.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Logger.swift
|
||||
// SingBoxPacketTunnel
|
||||
//
|
||||
// Created by GFWFighter on 10/24/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Logger {
|
||||
private static let queue = DispatchQueue.init(label: "\(FilePath.packageName).PacketTunnelLog", qos: .utility)
|
||||
|
||||
private let fileManager = FileManager.default
|
||||
private let url: URL
|
||||
|
||||
private var _fileHandle: FileHandle?
|
||||
private var fileHandle: FileHandle? {
|
||||
get {
|
||||
if let _fileHandle { return _fileHandle }
|
||||
let handle = try? FileHandle(forWritingTo: url)
|
||||
_fileHandle = handle
|
||||
return handle
|
||||
}
|
||||
}
|
||||
|
||||
private var lock = NSLock()
|
||||
|
||||
init(path: URL) {
|
||||
url = path
|
||||
}
|
||||
|
||||
func write(_ message: String) {
|
||||
Logger.queue.async { [message, unowned self] () in
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let output = message + "\n"
|
||||
do {
|
||||
if !self.fileManager.fileExists(atPath: url.path) {
|
||||
try output.write(to: url, atomically: true, encoding: .utf8)
|
||||
} else {
|
||||
guard let fileHandle else {
|
||||
return
|
||||
}
|
||||
fileHandle.seekToEndOfFile()
|
||||
if let data = output.data(using: .utf8) {
|
||||
fileHandle.write(data)
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
ios/HiddifyPacketTunnel/PacketTunnelProvider.swift
Normal file
37
ios/HiddifyPacketTunnel/PacketTunnelProvider.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// PacketTunnelProvider.swift
|
||||
// SingBoxPacketTunnel
|
||||
//
|
||||
// Created by GFWFighter on 7/24/1402 AP.
|
||||
//
|
||||
|
||||
import NetworkExtension
|
||||
|
||||
class PacketTunnelProvider: ExtensionProvider {
|
||||
|
||||
private var upload: Int64 = 0
|
||||
private var download: Int64 = 0
|
||||
// private var trafficLock: NSLock = NSLock()
|
||||
|
||||
// var trafficReader: TrafficReader!
|
||||
|
||||
override func startTunnel(options: [String : NSObject]?) async throws {
|
||||
try await super.startTunnel(options: options)
|
||||
/*trafficReader = TrafficReader { [unowned self] traffic in
|
||||
trafficLock.lock()
|
||||
upload += traffic.up
|
||||
download += traffic.down
|
||||
trafficLock.unlock()
|
||||
}*/
|
||||
}
|
||||
|
||||
override func handleAppMessage(_ messageData: Data) async -> Data? {
|
||||
let message = String(data: messageData, encoding: .utf8)
|
||||
switch message {
|
||||
case "stats":
|
||||
return "\(upload),\(download)".data(using: .utf8)!
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
25
ios/HiddifyPacketTunnel/PrivacyInfo.xcprivacy
Normal file
25
ios/HiddifyPacketTunnel/PrivacyInfo.xcprivacy
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
43
ios/HiddifyPacketTunnel/SingBox/Extension+RunBlocking.swift
Normal file
43
ios/HiddifyPacketTunnel/SingBox/Extension+RunBlocking.swift
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Extension+RunBlocking.swift
|
||||
// SingBoxPacketTunnel
|
||||
//
|
||||
// Created by GFWFighter on 7/25/1402 AP.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Libcore
|
||||
import NetworkExtension
|
||||
|
||||
func runBlocking<T>(_ block: @escaping () async -> T) -> T {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let box = resultBox<T>()
|
||||
Task.detached {
|
||||
let value = await block()
|
||||
box.result0 = value
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
return box.result0
|
||||
}
|
||||
|
||||
func runBlocking<T>(_ tBlock: @escaping () async throws -> T) throws -> T {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let box = resultBox<T>()
|
||||
Task.detached {
|
||||
do {
|
||||
let value = try await tBlock()
|
||||
box.result = .success(value)
|
||||
} catch {
|
||||
box.result = .failure(error)
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
return try box.result.get()
|
||||
}
|
||||
|
||||
private class resultBox<T> {
|
||||
var result: Result<T, Error>!
|
||||
var result0: T!
|
||||
}
|
||||
240
ios/HiddifyPacketTunnel/SingBox/ExtensionPlatformInterface.swift
Normal file
240
ios/HiddifyPacketTunnel/SingBox/ExtensionPlatformInterface.swift
Normal file
@@ -0,0 +1,240 @@
|
||||
//
|
||||
// ExtensionPlatformInterface.swift
|
||||
// SingBoxPacketTunnel
|
||||
//
|
||||
// Created by GFWFighter on 7/25/1402 AP.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Libcore
|
||||
import NetworkExtension
|
||||
|
||||
public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol, LibboxCommandServerHandlerProtocol {
|
||||
public func readWIFIState() -> LibboxWIFIState? {
|
||||
return nil;
|
||||
}
|
||||
|
||||
private let tunnel: ExtensionProvider
|
||||
private var networkSettings: NEPacketTunnelNetworkSettings?
|
||||
|
||||
init(_ tunnel: ExtensionProvider) {
|
||||
self.tunnel = tunnel
|
||||
}
|
||||
|
||||
public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer<Int32>?) throws {
|
||||
try runBlocking { [self] in
|
||||
try await openTun0(options, ret0_)
|
||||
}
|
||||
}
|
||||
|
||||
private func openTun0(_ options: LibboxTunOptionsProtocol?, _ ret0_: UnsafeMutablePointer<Int32>?) async throws {
|
||||
guard let options else {
|
||||
throw NSError(domain: "nil options", code: 0)
|
||||
}
|
||||
guard let ret0_ else {
|
||||
throw NSError(domain: "nil return pointer", code: 0)
|
||||
}
|
||||
|
||||
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
|
||||
if options.getAutoRoute() {
|
||||
settings.mtu = NSNumber(value: options.getMTU())
|
||||
|
||||
var error: NSError?
|
||||
let dnsServer = options.getDNSServerAddress(&error)
|
||||
if let error {
|
||||
throw error
|
||||
}
|
||||
settings.dnsSettings = NEDNSSettings(servers: [dnsServer])
|
||||
|
||||
var ipv4Address: [String] = []
|
||||
var ipv4Mask: [String] = []
|
||||
let ipv4AddressIterator = options.getInet4Address()!
|
||||
while ipv4AddressIterator.hasNext() {
|
||||
let ipv4Prefix = ipv4AddressIterator.next()!
|
||||
ipv4Address.append(ipv4Prefix.address())
|
||||
ipv4Mask.append(ipv4Prefix.mask())
|
||||
}
|
||||
let ipv4Settings = NEIPv4Settings(addresses: ipv4Address, subnetMasks: ipv4Mask)
|
||||
var ipv4Routes: [NEIPv4Route] = []
|
||||
let inet4RouteAddressIterator = options.getInet4RouteAddress()!
|
||||
if inet4RouteAddressIterator.hasNext() {
|
||||
while inet4RouteAddressIterator.hasNext() {
|
||||
let ipv4RoutePrefix = inet4RouteAddressIterator.next()!
|
||||
ipv4Routes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask()))
|
||||
}
|
||||
} else {
|
||||
ipv4Routes.append(NEIPv4Route.default())
|
||||
}
|
||||
for (index, address) in ipv4Address.enumerated() {
|
||||
ipv4Routes.append(NEIPv4Route(destinationAddress: address, subnetMask: ipv4Mask[index]))
|
||||
}
|
||||
ipv4Settings.includedRoutes = ipv4Routes
|
||||
settings.ipv4Settings = ipv4Settings
|
||||
|
||||
var ipv6Address: [String] = []
|
||||
var ipv6Prefixes: [NSNumber] = []
|
||||
let ipv6AddressIterator = options.getInet6Address()!
|
||||
while ipv6AddressIterator.hasNext() {
|
||||
let ipv6Prefix = ipv6AddressIterator.next()!
|
||||
ipv6Address.append(ipv6Prefix.address())
|
||||
ipv6Prefixes.append(NSNumber(value: ipv6Prefix.prefix()))
|
||||
}
|
||||
let ipv6Settings = NEIPv6Settings(addresses: ipv6Address, networkPrefixLengths: ipv6Prefixes)
|
||||
var ipv6Routes: [NEIPv6Route] = []
|
||||
let inet6RouteAddressIterator = options.getInet6RouteAddress()!
|
||||
if inet6RouteAddressIterator.hasNext() {
|
||||
while inet6RouteAddressIterator.hasNext() {
|
||||
let ipv6RoutePrefix = inet4RouteAddressIterator.next()!
|
||||
ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.description, networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix())))
|
||||
}
|
||||
} else {
|
||||
ipv6Routes.append(NEIPv6Route.default())
|
||||
}
|
||||
ipv6Settings.includedRoutes = ipv6Routes
|
||||
settings.ipv6Settings = ipv6Settings
|
||||
}
|
||||
|
||||
if options.isHTTPProxyEnabled() {
|
||||
let proxySettings = NEProxySettings()
|
||||
let proxyServer = NEProxyServer(address: options.getHTTPProxyServer(), port: Int(options.getHTTPProxyServerPort()))
|
||||
proxySettings.httpServer = proxyServer
|
||||
proxySettings.httpsServer = proxyServer
|
||||
settings.proxySettings = proxySettings
|
||||
}
|
||||
|
||||
networkSettings = settings
|
||||
try await tunnel.setTunnelNetworkSettings(settings)
|
||||
|
||||
if let tunFd = tunnel.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32 {
|
||||
ret0_.pointee = tunFd
|
||||
return
|
||||
}
|
||||
|
||||
let tunFdFromLoop = LibboxGetTunnelFileDescriptor()
|
||||
if tunFdFromLoop != -1 {
|
||||
ret0_.pointee = tunFdFromLoop
|
||||
} else {
|
||||
throw NSError(domain: "missing file descriptor", code: 0)
|
||||
}
|
||||
}
|
||||
|
||||
public func usePlatformAutoDetectControl() -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
public func autoDetectControl(_: Int32) throws {}
|
||||
|
||||
public func findConnectionOwner(_: Int32, sourceAddress _: String?, sourcePort _: Int32, destinationAddress _: String?, destinationPort _: Int32, ret0_ _: UnsafeMutablePointer<Int32>?) throws {
|
||||
throw NSError(domain: "not implemented", code: 0)
|
||||
}
|
||||
|
||||
public func packageName(byUid _: Int32, error _: NSErrorPointer) -> String {
|
||||
""
|
||||
}
|
||||
|
||||
public func uid(byPackageName _: String?, ret0_ _: UnsafeMutablePointer<Int32>?) throws {
|
||||
throw NSError(domain: "not implemented", code: 0)
|
||||
}
|
||||
|
||||
public func useProcFS() -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
public func writeLog(_ message: String?) {
|
||||
guard let message else {
|
||||
return
|
||||
}
|
||||
tunnel.writeMessage(message)
|
||||
}
|
||||
|
||||
public func usePlatformDefaultInterfaceMonitor() -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
public func startDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {}
|
||||
|
||||
public func closeDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {}
|
||||
|
||||
public func useGetter() -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
public func getInterfaces() throws -> LibboxNetworkInterfaceIteratorProtocol {
|
||||
throw NSError(domain: "not implemented", code: 0)
|
||||
}
|
||||
|
||||
public func underNetworkExtension() -> Bool {
|
||||
true
|
||||
}
|
||||
public func includeAllNetworks() -> Bool {
|
||||
#if !os(tvOS)
|
||||
// return SharedPreferences.includeAllNetworks.getBlocking()
|
||||
return false
|
||||
#else
|
||||
return false
|
||||
#endif
|
||||
}
|
||||
public func clearDNSCache() {
|
||||
guard let networkSettings else {
|
||||
return
|
||||
}
|
||||
tunnel.reasserting = true
|
||||
tunnel.setTunnelNetworkSettings(nil) { _ in
|
||||
}
|
||||
tunnel.setTunnelNetworkSettings(networkSettings) { _ in
|
||||
}
|
||||
tunnel.reasserting = false
|
||||
}
|
||||
|
||||
public func serviceReload() throws {
|
||||
runBlocking { [self] in
|
||||
await tunnel.reloadService()
|
||||
}
|
||||
}
|
||||
|
||||
public func getSystemProxyStatus() -> LibboxSystemProxyStatus? {
|
||||
let status = LibboxSystemProxyStatus()
|
||||
guard let networkSettings else {
|
||||
return status
|
||||
}
|
||||
guard let proxySettings = networkSettings.proxySettings else {
|
||||
return status
|
||||
}
|
||||
if proxySettings.httpServer == nil {
|
||||
return status
|
||||
}
|
||||
status.available = true
|
||||
status.enabled = proxySettings.httpEnabled
|
||||
return status
|
||||
}
|
||||
|
||||
public func setSystemProxyEnabled(_ isEnabled: Bool) throws {
|
||||
guard let networkSettings else {
|
||||
return
|
||||
}
|
||||
guard let proxySettings = networkSettings.proxySettings else {
|
||||
return
|
||||
}
|
||||
if proxySettings.httpServer == nil {
|
||||
return
|
||||
}
|
||||
if proxySettings.httpEnabled == isEnabled {
|
||||
return
|
||||
}
|
||||
proxySettings.httpEnabled = isEnabled
|
||||
proxySettings.httpsEnabled = isEnabled
|
||||
networkSettings.proxySettings = proxySettings
|
||||
try runBlocking {
|
||||
try await self.tunnel.setTunnelNetworkSettings(networkSettings)
|
||||
}
|
||||
}
|
||||
|
||||
public func postServiceClose() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func reset() {
|
||||
networkSettings = nil
|
||||
}
|
||||
|
||||
}
|
||||
167
ios/HiddifyPacketTunnel/SingBox/ExtensionProvider.swift
Normal file
167
ios/HiddifyPacketTunnel/SingBox/ExtensionProvider.swift
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
59
ios/HiddifyPacketTunnel/SingBox/SingBox.swift
Normal file
59
ios/HiddifyPacketTunnel/SingBox/SingBox.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// SingBox.swift
|
||||
// SingBoxPacketTunnel
|
||||
//
|
||||
// Created by GFWFighter on 7/25/1402 AP.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SingBox {
|
||||
static func setupConfig(config: String, mtu: Int = 9000) -> String? {
|
||||
guard
|
||||
let config = config.data(using: .utf8),
|
||||
var json = try? JSONSerialization
|
||||
.jsonObject(
|
||||
with: config,
|
||||
options: [.mutableLeaves, .mutableContainers]
|
||||
) as? [String:Any]
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
/*json["log"] = [
|
||||
"disabled": false,
|
||||
"level": "info",
|
||||
"output": "log",
|
||||
"timestamp": true
|
||||
] as [String:Any]
|
||||
json["experimental"] = [
|
||||
"clash_api": [
|
||||
"external_controller": "127.0.0.1:10864"
|
||||
]
|
||||
]
|
||||
json["inbounds"] = [
|
||||
[
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"auto_route": true,
|
||||
"mtu": mtu,
|
||||
"sniff": true
|
||||
] as [String:Any]
|
||||
]
|
||||
var routing = (json["route"] as? [String:Any]) ?? [
|
||||
"rules": [Any](),
|
||||
"auto_detect_interface": true,
|
||||
"final": (json["inbounds"] as? [[String:Any]])?.first?["tag"] ?? "proxy"
|
||||
]
|
||||
routing["geoip"] = [
|
||||
"path": FilePath.assetsDirectory.appendingPathComponent("geoip.db"),
|
||||
]
|
||||
routing["geosite"] = [
|
||||
"path": FilePath.assetsDirectory.appendingPathComponent("geosite.db"),
|
||||
]
|
||||
json["route"] = routing*/
|
||||
guard let data = try? JSONSerialization.data(withJSONObject: json) else {
|
||||
return nil
|
||||
}
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
70
ios/HiddifyPacketTunnel/TrafficReader.swift
Normal file
70
ios/HiddifyPacketTunnel/TrafficReader.swift
Normal file
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// TrafficReader.swift
|
||||
// SingBoxPacketTunnel
|
||||
//
|
||||
// Created by GFWFighter on 7/25/1402 AP.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct TrafficReaderUpdate: Codable {
|
||||
let up: Int64
|
||||
let down: Int64
|
||||
}
|
||||
|
||||
|
||||
class TrafficReader {
|
||||
private var task: URLSessionWebSocketTask!
|
||||
private let callback: (TrafficReaderUpdate) -> ()
|
||||
|
||||
init(onUpdate: @escaping (TrafficReaderUpdate) -> ()) {
|
||||
self.callback = onUpdate
|
||||
Task(priority: .background) { [weak self] () in
|
||||
await self?.setup()
|
||||
}
|
||||
}
|
||||
|
||||
private func setup() async {
|
||||
try? await Task.sleep(nanoseconds: 5_000_000_000)
|
||||
//return
|
||||
while true {
|
||||
do {
|
||||
let (_, response) = try await URLSession.shared.data(from: URL(string: "http://127.0.0.1:10864")!)
|
||||
let code = (response as? HTTPURLResponse)?.statusCode ?? -1
|
||||
if code >= 200 && code < 300 {
|
||||
break
|
||||
}
|
||||
} catch {
|
||||
// pass
|
||||
}
|
||||
try? await Task.sleep(nanoseconds: 5_000_000)
|
||||
}
|
||||
let task = URLSession.shared.webSocketTask(with: URL(string: "ws://127.0.0.1:10864/traffic")!)
|
||||
self.task = task
|
||||
read()
|
||||
task.resume()
|
||||
}
|
||||
|
||||
private func read() {
|
||||
task.receive { [weak self] result in
|
||||
switch result {
|
||||
case .failure(_):
|
||||
break
|
||||
case .success(let message):
|
||||
switch message {
|
||||
case .string(let message):
|
||||
guard let data = message.data(using: .utf8) else {
|
||||
break
|
||||
}
|
||||
guard let response = try? JSONDecoder().decode(TrafficReaderUpdate.self, from: data) else {
|
||||
break
|
||||
}
|
||||
self?.callback(response)
|
||||
default:
|
||||
break
|
||||
}
|
||||
self?.read()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user