import Foundation import Libbox import NetworkExtension #if canImport(CoreWLAN) import CoreWLAN #endif public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol, LibboxCommandServerHandlerProtocol { private let tunnel: ExtensionProvider private var networkSettings: NEPacketTunnelNetworkSettings? init(_ tunnel: ExtensionProvider) { self.tunnel = tunnel } public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer?) throws { try runBlocking { [self] in try await openTun0(options, ret0_) } } private func openTun0(_ options: LibboxTunOptionsProtocol?, _ ret0_: UnsafeMutablePointer?) 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 autoRouteUseSubRangesByDefault = await SharedPreferences.autoRouteUseSubRangesByDefault.get() let excludeAPNs = await SharedPreferences.excludeAPNsRoute.get() 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] = [] var ipv4ExcludeRoutes: [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 if autoRouteUseSubRangesByDefault { ipv4Routes.append(NEIPv4Route(destinationAddress: "1.0.0.0", subnetMask: "255.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "2.0.0.0", subnetMask: "254.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "4.0.0.0", subnetMask: "252.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "8.0.0.0", subnetMask: "248.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "16.0.0.0", subnetMask: "240.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "32.0.0.0", subnetMask: "224.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "64.0.0.0", subnetMask: "192.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "128.0.0.0", subnetMask: "128.0.0.0")) } else { ipv4Routes.append(NEIPv4Route.default()) } let inet4RouteExcludeAddressIterator = options.getInet4RouteExcludeAddress()! while inet4RouteExcludeAddressIterator.hasNext() { let ipv4RoutePrefix = inet4RouteExcludeAddressIterator.next()! ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask())) } if await SharedPreferences.excludeDefaultRoute.get(), !ipv4Routes.isEmpty { if !ipv4ExcludeRoutes.contains(where: { it in it.destinationAddress == "0.0.0.0" && it.destinationSubnetMask == "255.255.255.254" }) { ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "255.255.255.254")) } } if excludeAPNs, !ipv4Routes.isEmpty { if !ipv4ExcludeRoutes.contains(where: { it in it.destinationAddress == "17.0.0.0" && it.destinationSubnetMask == "255.0.0.0" }) { ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0")) } } ipv4Settings.includedRoutes = ipv4Routes ipv4Settings.excludedRoutes = ipv4ExcludeRoutes 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] = [] var ipv6ExcludeRoutes: [NEIPv6Route] = [] let inet6RouteAddressIterator = options.getInet6RouteAddress()! if inet6RouteAddressIterator.hasNext() { while inet6RouteAddressIterator.hasNext() { let ipv6RoutePrefix = inet6RouteAddressIterator.next()! ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix()))) } } else if autoRouteUseSubRangesByDefault { ipv6Routes.append(NEIPv6Route(destinationAddress: "100::", networkPrefixLength: 8)) ipv6Routes.append(NEIPv6Route(destinationAddress: "200::", networkPrefixLength: 7)) ipv6Routes.append(NEIPv6Route(destinationAddress: "400::", networkPrefixLength: 6)) ipv6Routes.append(NEIPv6Route(destinationAddress: "800::", networkPrefixLength: 5)) ipv6Routes.append(NEIPv6Route(destinationAddress: "1000::", networkPrefixLength: 4)) ipv6Routes.append(NEIPv6Route(destinationAddress: "2000::", networkPrefixLength: 3)) ipv6Routes.append(NEIPv6Route(destinationAddress: "4000::", networkPrefixLength: 2)) ipv6Routes.append(NEIPv6Route(destinationAddress: "8000::", networkPrefixLength: 1)) } else { ipv6Routes.append(NEIPv6Route.default()) } let inet6RouteExcludeAddressIterator = options.getInet6RouteExcludeAddress()! while inet6RouteExcludeAddressIterator.hasNext() { let ipv6RoutePrefix = inet6RouteExcludeAddressIterator.next()! ipv6ExcludeRoutes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix()))) } ipv6Settings.includedRoutes = ipv6Routes ipv6Settings.excludedRoutes = ipv6ExcludeRoutes 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 if await SharedPreferences.systemProxyEnabled.get() { proxySettings.httpEnabled = true proxySettings.httpsEnabled = true } if excludeAPNs { proxySettings.exceptionList = ["push.apple.com"] } 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?) throws { throw NSError(domain: "not implemented", code: 0) } public func packageName(byUid _: Int32, error _: NSErrorPointer) -> String { "" } public func uid(byPackageName _: String?, ret0_ _: UnsafeMutablePointer?) 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 clearDNSCache() { guard let networkSettings else { return } tunnel.reasserting = true tunnel.setTunnelNetworkSettings(nil) { _ in } tunnel.setTunnelNetworkSettings(networkSettings) { _ in } tunnel.reasserting = false } public func readWIFIState() -> LibboxWIFIState? { #if os(iOS) let network = runBlocking { await NEHotspotNetwork.fetchCurrent() } guard let network else { return nil } return LibboxWIFIState(network.ssid, wifiBSSID: network.bssid)! #elseif os(macOS) guard let interface = CWWiFiClient.shared().interface() else { return nil } guard let ssid = interface.ssid() else { return nil } guard let bssid = interface.bssid() else { return nil } return LibboxWIFIState(ssid, wifiBSSID: bssid)! #else return nil #endif } public func serviceReload() throws { runBlocking { [self] in await tunnel.reloadService() } } public func postServiceClose() { reset() tunnel.postServiceClose() } 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) } } func reset() { networkSettings = nil } }