// // PacketTunnelProvider.swift // neiyoujsql // // Created by C Auto on 2021/6/29. // import NetworkExtension //let appGroup = "group.com.naiaaa.calska2" class PacketTunnelProvider: NEPacketTunnelProvider { var message: PacketTunnelMessage? = nil var tunFd : Int32? var conf : String = "" var lastPath : NWPath? var appGroup : String = "" override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { // Add code here to start the process of connecting the tunnel. //tunFd = self.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int16 tunFd = getFD() if let appgroup = message?.appGroup { appGroup = appgroup } else { completionHandler(NSError(domain: "PacketTunnel", code: -1, userInfo: ["error" : "读取不到配置"])) return } // guard let appgroup = message?.appGroup else { // // } // // 启动Tun2scoks if let configData = message?.configData { conf = String(decoding: configData, as: UTF8.self) } else { completionHandler(NSError(domain: "PacketTunnel", code: -1, userInfo: ["error" : "读取不到配置"])) return } // 配置PacketTunel self.setupTunnel(message: message!) {[weak self] (error) in //self?.proxyPackets() let confWithFd = self?.conf.replacingOccurrences(of: "REPLACE-ME-WITH-THE-FD", with: String(self?.tunFd ?? 0)) // let url = FileManager().containerURL(forSecurityApplicationGroupIdentifier: self?.appGroup ?? "")!.appendingPathComponent("running_config.conf") let msg = confWithFd ?? "" let fileName = "/running_config.conf" // let fileManager = FileManager.default let file = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first let path = file! + fileName if !FileManager.default.fileExists(atPath: path) { FileManager.default.createFile(atPath: path, contents: nil, attributes: nil) } else { do{ //删除指定位置的内容 try FileManager.default.removeItem(atPath: path) print("Success to remove folder.") }catch{ print("Failder to remove folder") } FileManager.default.createFile(atPath: path, contents: nil, attributes: nil) } let handle = FileHandle(forWritingAtPath:path) handle?.write(msg.data(using: String.Encoding.utf8)!) try? handle?.close() // let status = leaf_test_config(String(path)) // // print(status) // let path = path.absoluteString // let start = path.index(path.startIndex, offsetBy: 7) // let subpath = path[start.. Void) { // Add code here to start the process of stopping the tunnel. leaf_shutdown(UInt16(self.tunFd ?? 0)) completionHandler() } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { message = try? JSONDecoder().decode(PacketTunnelMessage.self, from: messageData) if let handler = completionHandler { handler(messageData) } } override func sleep(completionHandler: @escaping () -> Void) { // Add code here to get ready to sleep. completionHandler() } override func wake() { // Add code here to wake up. } } extension PacketTunnelProvider { func getFD() -> Int32? { if #available(iOS 15, *) { var buf = [CChar](repeating: 0, count: Int(IFNAMSIZ)) let utunPrefix = "utun".utf8CString.dropLast() return (0...1024).first { (_ fd: Int32) -> Bool in var len = socklen_t(buf.count) return getsockopt(fd, 2, 2, &buf, &len) == 0 && buf.starts(with: utunPrefix) } } else { return self.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32 } } func setupTunnel(message: PacketTunnelMessage, _ completion: @escaping((_ error: Error?) -> Void)) { guard let serverIP = message.serverIP else { completion(NSError(domain: "PacketTunnel", code: -1, userInfo: ["error" : "没有IP地址"])) return } let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: serverIP) networkSettings.mtu = 1500 let ipv4Settings = NEIPv4Settings(addresses: [serverIP], subnetMasks: ["255.255.255.0"]) var includeRoutes: Array = [] for route in message.ipv4IncludedRoutes { includeRoutes.append(NEIPv4Route(destinationAddress: route.0, subnetMask: route.1)) } var excludeRoutes: Array = [] for route in message.ipv4ExcludedRoutes { excludeRoutes.append(NEIPv4Route(destinationAddress: route.0, subnetMask: route.1)) } ipv4Settings.includedRoutes = includeRoutes.count == 0 ? [NEIPv4Route.default()] : includeRoutes ipv4Settings.excludedRoutes = excludeRoutes networkSettings.ipv4Settings = ipv4Settings networkSettings.dnsSettings = NEDNSSettings(servers: message.dnsServers) let proxySettings = NEProxySettings() proxySettings.httpEnabled = true proxySettings.httpsEnabled = true proxySettings.autoProxyConfigurationEnabled = true // let httpsServer = NEProxyServer(address: "127.0.0.1", port: 1087) // httpsServer.authenticationRequired = false // proxySettings.httpsServer = httpsServer // proxySettings.httpServer = httpsServer proxySettings.exceptionList = message.proxyExeptionList proxySettings.matchDomains = message.proxyMatchDomains networkSettings.proxySettings = proxySettings self.setTunnelNetworkSettings(networkSettings) {error in completion(error) } } } extension PacketTunnelProvider { /// 监听网络状态,切换不同的网络的时候,需要重新连接vpn override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "defaultPath" { if self.defaultPath?.status == .satisfied && self.defaultPath != self.lastPath { if (self.lastPath == nil) { self.lastPath = self.defaultPath } else { NSLog("received network change notifcation") let xSeconds = 1.0 DispatchQueue.main.asyncAfter(deadline: .now() + xSeconds) { self.startTunnel(options: nil){ _ in } } } } else { self.lastPath = defaultPath } } } }