213 lines
8.1 KiB
Swift
213 lines
8.1 KiB
Swift
//
|
||
// PacketTunnelProvider.swift
|
||
// OneTouchPT
|
||
//
|
||
// Created by SU on 2025/6/16.
|
||
//
|
||
|
||
import Foundation
|
||
import NetworkExtension
|
||
import Tun2SocksKit
|
||
import OTClient
|
||
|
||
|
||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||
private let cli_shared = YjlNeiHeClient.shared
|
||
private let config = YjlNeiHeConfig()
|
||
private var isAlive = false
|
||
|
||
override func startTunnel(options: [String : NSObject]?) async throws {
|
||
|
||
self.ONETopenLog()
|
||
await cli_shared.SetLayout()
|
||
self.cli_shared.SheZhiRizhi({ message in
|
||
NSLog("SheZhiRizhi log: \(message!)clashLog")
|
||
})
|
||
await config.tiJiaoNeiHe(cli: cli_shared)
|
||
let cjson = await cli_shared.HuoQuPeiZhiPro()
|
||
|
||
let cs = try JSONSerialization.jsonObject(with: cjson.data(using: .utf8)!, options: []) as! [String: Any]
|
||
let mp = cs["mixed-port"] as? Int ?? 0
|
||
if (mp != 0) {
|
||
NSLog("[SheZhiRizhi]mp: \(mp)")
|
||
}
|
||
|
||
let port = mp != 0 ? mp : cs["port"] as? Int ?? 0
|
||
let sp = mp != 0 ? mp : cs["socks-port"] as? Int ?? 0
|
||
NSLog("[SheZhiRizhi]startTunnel port: \(port), socksPort: \(sp)")
|
||
|
||
try await self.setTunnelNetworkSettings(initHttpSettings(port))
|
||
if (sp != 0) {
|
||
Socks5Tunnel.run(withConfig: .string(content: createTunnelConfig(socksPort: sp))) { code in
|
||
NSLog("[SheZhiRizhi]Socks5Tunnel ret: \(code)")
|
||
}
|
||
}
|
||
isAlive = true
|
||
self.addObserver(self, forKeyPath:"defaultPath", context: nil)
|
||
}
|
||
|
||
// 重写观察回调
|
||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||
if keyPath == "defaultPath" {
|
||
// 当物理网络或 VPN 路由发生变化时,此代码块会触发
|
||
let newPath = self.defaultPath
|
||
NSLog("[SheZhiRizhi]address: \(String(describing: newPath))")
|
||
print("默认网络路径已变动: \(String(describing: newPath))")
|
||
}
|
||
}
|
||
func ONETopenLog(){
|
||
let fileONETManager = FileManager.default
|
||
let containerURL = fileONETManager.containerURL(forSecurityApplicationGroupIdentifier: "group.com.jsq.yijianlian")
|
||
let ONETlogFilePath = containerURL?.appendingPathComponent("tunnel.log").path ?? ""
|
||
|
||
fileONETManager.createFile(atPath: ONETlogFilePath, contents: nil, attributes: nil)
|
||
|
||
freopen(ONETlogFilePath.cString(using: .ascii), "w+", stdout)
|
||
freopen(ONETlogFilePath.cString(using: .ascii), "w+", stderr)
|
||
}
|
||
|
||
override func stopTunnel(with reason: NEProviderStopReason) async {
|
||
isAlive = false
|
||
Socks5Tunnel.quit()
|
||
}
|
||
|
||
override func handleAppMessage(_ data: Data) async -> Data? {
|
||
let params = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
|
||
let method = params["method"] as! String
|
||
let args = params["args"] as? [String : Any]
|
||
switch(method) {
|
||
case "JoinAppLog":
|
||
return wrapAppMessageResult(await cli_shared.JoinAppLog())
|
||
|
||
case "YanChiCeShi":
|
||
let nName = args!["proxyName"] as! String
|
||
let u = args!["url"] as! String
|
||
let to = args!["timeout"] as! Int
|
||
return wrapAppMessageResult(await cli_shared.YanChiCeShi(nName: nName, u: u, to: to))
|
||
case "SetLayout":
|
||
return wrapAppMessageResult(await cli_shared.SetLayout())
|
||
case "XiuGaiJieDian":
|
||
//修改节点
|
||
//组名
|
||
let pName = args!["pName"] as! String
|
||
//节点名
|
||
let sName = args!["sName"] as! String
|
||
let name3 = ""
|
||
return wrapAppMessageResult(await cli_shared.XiuGaiJieDian(pName: pName, sName: sName, name3: name3))
|
||
case "NeiHeChuShi":
|
||
let hDir = args!["hDir"] as! String
|
||
return wrapAppMessageResult(await cli_shared.HeNeiHeChuShi(hDir: hDir))
|
||
case "GuanBiAllCons":
|
||
return wrapAppMessageResult(await cli_shared.GuanBiAllCons())
|
||
case "GuanBiDangQianCon":
|
||
let nameId = args!["nameId"] as! String
|
||
return wrapAppMessageResult(await cli_shared.GuanBiDangQianCon(nameId: nameId))
|
||
case "GetUserInfo":
|
||
return wrapAppMessageResult(await cli_shared.GetUserInfo())
|
||
case "HuoQuSuoYouCons":
|
||
return wrapAppMessageResult(await cli_shared.HuoQuSuoYouCons())
|
||
case "HuoQuPeiZhi":
|
||
return wrapAppMessageResult(await cli_shared.HuoQuPeiZhi())
|
||
case "HuoQuPeiZhiPro":
|
||
return wrapAppMessageResult(await cli_shared.HuoQuPeiZhiPro())
|
||
case "HuoQuShuXing":
|
||
return wrapAppMessageResult(await cli_shared.HuoQuShuXing())
|
||
case "HuoQuDaiLi":
|
||
return wrapAppMessageResult(await cli_shared.HuoQuDaiLi())
|
||
case "HuoQuTongJi":
|
||
return wrapAppMessageResult(await cli_shared.HuoQuTongJi())
|
||
case "HuoQuSuiDaoMoShi":
|
||
return wrapAppMessageResult(await cli_shared.HuoQuSuiDaoMoShi())
|
||
case "PanDuanPeiZhi":
|
||
let cp = args!["cp"] as! String
|
||
return wrapAppMessageResult(await cli_shared.PanDuanPeiZhi(cp: cp))
|
||
case "SetAppInfo":
|
||
return wrapAppMessageResult(await cli_shared.SetAppInfo(appinfo: ""))
|
||
case "JieXiPeiZhi":
|
||
return wrapAppMessageResult(await cli_shared.JieXiPeiZhi())
|
||
case "SheZhiPeiZhi":
|
||
let cp = args!["cp"] as! String
|
||
return wrapAppMessageResult(await cli_shared.SheZhiPeiZhi(cp: cp))
|
||
case "SheZhiZhuYeLuJing":
|
||
let LuJing = args!["LuJing"] as! String
|
||
return wrapAppMessageResult(await cli_shared.SheZhiZhuYeLuJing(LuJing: LuJing))
|
||
case "SheZhiSuiDaoMoShi":
|
||
let moShi = args!["moShi"] as! String
|
||
return wrapAppMessageResult(await cli_shared.SheZhiSuiDaoMoShi(moShi: moShi))
|
||
case "ppap":
|
||
return wrapAppMessageResult(await cli_shared.ppap())
|
||
case "KaiQiRiZhi":
|
||
return wrapAppMessageResult(await cli_shared.KaiQiRiZhi())
|
||
case "TingZhiRiZhi":
|
||
return wrapAppMessageResult(await cli_shared.TingZhiRiZhi())
|
||
case "isAlive":
|
||
return wrapAppMessageResult(isAlive)
|
||
default:
|
||
break
|
||
}
|
||
return nil
|
||
}
|
||
}
|
||
|
||
private func initHttpSettings(_ p: Int) -> NEPacketTunnelNetworkSettings {
|
||
let netSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "254.1.1.1")
|
||
netSettings.mtu = 9000
|
||
netSettings.ipv4Settings = {
|
||
let netSettings = NEIPv4Settings(addresses: ["198.18.0.1"], subnetMasks: ["255.255.0.0"])
|
||
netSettings.includedRoutes = [NEIPv4Route.default()]
|
||
return netSettings
|
||
}()
|
||
netSettings.ipv6Settings = {
|
||
let netSettings = NEIPv6Settings(addresses: ["fd6e:a81b:704f:1211::1"], networkPrefixLengths: [64])
|
||
netSettings.includedRoutes = [NEIPv6Route.default()]
|
||
return netSettings
|
||
}()
|
||
netSettings.dnsSettings = NEDNSSettings(servers: ["127.0.0.1"])
|
||
netSettings.proxySettings = {
|
||
let netSettings = NEProxySettings();
|
||
netSettings.httpServer = NEProxyServer(address: "::1", port: p)
|
||
netSettings.httpsServer = NEProxyServer(address: "::1", port: p)
|
||
netSettings.httpEnabled = true
|
||
netSettings.httpsEnabled = true
|
||
netSettings.matchDomains = [""]
|
||
return netSettings
|
||
}()
|
||
return netSettings
|
||
}
|
||
|
||
private func createTunnelConfig(socksPort: Int) -> String {
|
||
return """
|
||
tunnel:
|
||
mtu: 9000
|
||
|
||
socks5:
|
||
port: \(socksPort)
|
||
address: ::1
|
||
udp: 'udp'
|
||
|
||
misc:
|
||
task-stack-size: 2048
|
||
connect-timeout: 5000
|
||
read-write-timeout: 60000
|
||
log-file: stderr
|
||
log-level: info
|
||
limit-nofile: 65535
|
||
"""
|
||
}
|
||
|
||
private func wrapAppMessageResult(_ retStrr: Any?) -> Data? {
|
||
if (retStrr == nil || retStrr is Void) {
|
||
return nil
|
||
}
|
||
if (retStrr is Int) {
|
||
return withUnsafeBytes(of: retStrr as! Int) { Data($0) }
|
||
}
|
||
if (retStrr is Bool) {
|
||
return withUnsafeBytes(of: (retStrr as! Bool) ? 1 : 0) { Data($0) }
|
||
}
|
||
if (retStrr is String) {
|
||
return (retStrr as! String).data(using: .utf8)
|
||
}
|
||
return nil
|
||
}
|