From ae99cc77e9fa982c20b15e4ca843bfebe0916942 Mon Sep 17 00:00:00 2001 From: spectrum Date: Mon, 23 Mar 2026 19:04:59 +0200 Subject: [PATCH] revert: restore pre-fix OpenVPN NE flow --- .../ios/PacketTunnelProvider+OpenVPN.swift | 686 +----------------- .../platforms/ios/PacketTunnelProvider.swift | 110 +-- client/platforms/ios/ios_controller.mm | 40 +- 3 files changed, 31 insertions(+), 805 deletions(-) diff --git a/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift b/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift index 29c6e2131..118545c2c 100644 --- a/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift +++ b/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift @@ -15,12 +15,6 @@ struct OpenVPNConfig: Decodable { extension PacketTunnelProvider { func startOpenVPN(completionHandler: @escaping (Error?) -> Void) { - // Reset session-derived state so reconnects never reuse stale gateway/address data. - openVpnGatewayAddress = nil - openVpnLocalAddress = nil - openVpnLocalMask = nil - lastOpenVPNSettings = nil - guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol, let providerConfiguration = protocolConfiguration.providerConfiguration, let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else { @@ -32,20 +26,6 @@ extension PacketTunnelProvider { let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData) ovpnLog(.info, title: "config: ", message: openVPNConfig.str) let ovpnConfiguration = Data(openVPNConfig.config.utf8) - splitTunnelType = openVPNConfig.splitTunnelType - splitTunnelSites = openVPNConfig.splitTunnelSites - openVpnDnsServers = Self.extractDnsServers(from: openVPNConfig.config) - openVpnRemoteAddress = Self.extractRemoteHost(from: openVPNConfig.config) - openVpnRedirectGatewayDef1 = Self.hasRedirectGatewayDef1(in: openVPNConfig.config) - if let openVpnRemoteAddress { - ovpnLog(.info, title: "Remote", message: "host=\(openVpnRemoteAddress)") - } - if !openVpnDnsServers.isEmpty { - ovpnLog(.info, title: "DNS", message: "servers=\(openVpnDnsServers)") - } - if openVpnRedirectGatewayDef1 { - ovpnLog(.info, title: "IPv4Routes", message: "redirect-gateway def1 detected") - } setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler) } catch { ovpnLog(.error, message: "Can't parse OpenVPN config: \(error.localizedDescription)") @@ -93,11 +73,6 @@ extension PacketTunnelProvider { let digestString = digest.map { String(format: "%02x", $0) }.joined() ovpnLog(.info, title: "ConfigDigest", message: digestString) - let hasCertTag = configString.contains("") && configString.contains("") - let hasKeyTag = configString.contains("") && configString.contains("") - let hasAuthUserPass = configString.contains("auth-user-pass") - ovpnLog(.info, title: "ConfigCreds", message: "inlineCert=\(hasCertTag) inlineKey=\(hasKeyTag) authUserPass=\(hasAuthUserPass)") - let hasTlsAuthOpen = configString.contains("") let hasTlsAuthClose = configString.contains("") ovpnLog(.info, title: "ConfigFlags", message: "tls-auth open=\(hasTlsAuthOpen) close=\(hasTlsAuthClose)") @@ -108,98 +83,27 @@ extension PacketTunnelProvider { ovpnLog(.debug, title: "ConfigHead", message: head) ovpnLog(.debug, title: "ConfigTail", message: tail) - if hasTlsAuthOpen && hasTlsAuthClose { - ovpnLog(.info, title: "TLSAuthSanitized", message: "preserve original tls-auth block") + if let start = configString.range(of: ""), + let end = configString.range(of: "", range: start.upperBound.. Void ) { - guard var effectiveSettings = networkSettings else { - ovpnLog(.info, title: "SetTunnelNetworkSettings", message: "nil settings; skipping update") - completionHandler(nil) - return - } - let splitType = splitTunnelType ?? 0 - - if let ipv4Settings = effectiveSettings.ipv4Settings { - openVpnLocalAddress = ipv4Settings.addresses.first - openVpnLocalMask = ipv4Settings.subnetMasks.first - } - - let serverIP = openVPNAdapter.connectionInformation?.serverIP - let configRemote = openVpnRemoteAddress - let serverEndpoint: String? = { - if let ip = serverIP, Self.isIPv4Address(ip) { return ip } - if let ip = configRemote, Self.isIPv4Address(ip) { return ip } - return effectiveSettings.tunnelRemoteAddress - }() - - if let serverEndpoint, - Self.isIPv4Address(serverEndpoint), - effectiveSettings.tunnelRemoteAddress != serverEndpoint { - let updatedSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: serverEndpoint) - updatedSettings.ipv4Settings = effectiveSettings.ipv4Settings - updatedSettings.ipv6Settings = effectiveSettings.ipv6Settings - updatedSettings.dnsSettings = effectiveSettings.dnsSettings - updatedSettings.proxySettings = effectiveSettings.proxySettings - updatedSettings.mtu = effectiveSettings.mtu - effectiveSettings = updatedSettings - ovpnLog(.info, title: "Remote", message: "tunnelRemoteAddress set to server=\(serverEndpoint)") - } else if let serverEndpoint, !Self.isIPv4Address(serverEndpoint) { - ovpnLog(.info, title: "Remote", message: "skip tunnelRemoteAddress override; non-ip serverEndpoint=\(serverEndpoint)") - } - // In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers // send empty string to NEDNSSettings.matchDomains - if let dnsSettings = effectiveSettings.dnsSettings { - if dnsSettings.servers.isEmpty, !openVpnDnsServers.isEmpty { - let newSettings = NEDNSSettings(servers: openVpnDnsServers) - newSettings.matchDomains = dnsSettings.matchDomains - effectiveSettings.dnsSettings = newSettings - } - } else if !openVpnDnsServers.isEmpty { - let newSettings = NEDNSSettings(servers: openVpnDnsServers) - effectiveSettings.dnsSettings = newSettings - } + networkSettings?.dnsSettings?.matchDomains = [""] - effectiveSettings.dnsSettings?.matchDomains = [""] - if let dnsSettings = effectiveSettings.dnsSettings { - let servers = dnsSettings.servers.joined(separator: ",") - let domains = dnsSettings.matchDomains?.joined(separator: ",") ?? "" - ovpnLog(.info, title: "DNS", message: "servers=[\(servers)] matchDomains=[\(domains)]") - } else { - ovpnLog(.error, title: "DNS", message: "dnsSettings is nil") - } - - let tunnelRemote = effectiveSettings.tunnelRemoteAddress - if !tunnelRemote.isEmpty { - ovpnLog(.info, title: "Remote", message: "tunnelRemoteAddress=\(tunnelRemote)") - } else if let remoteAddress = openVpnRemoteAddress { - ovpnLog(.info, title: "Remote", message: "tunnelRemoteAddress is empty, configRemote=\(remoteAddress)") - } - - if let ipv4Settings = effectiveSettings.ipv4Settings { - let included = (ipv4Settings.includedRoutes ?? []).map { "\($0.destinationAddress)/\($0.destinationSubnetMask)" } - let excluded = (ipv4Settings.excludedRoutes ?? []).map { "\($0.destinationAddress)/\($0.destinationSubnetMask)" } - let addresses = ipv4Settings.addresses.joined(separator: ",") - let masks = ipv4Settings.subnetMasks.joined(separator: ",") - let router: String -#if os(macOS) - if #available(macOS 13.0, *) { - router = ipv4Settings.router ?? "" - } else { - router = "" - } -#else - router = "" -#endif - ovpnLog(.info, title: "IPv4RoutesPre", message: "addresses=[\(addresses)] masks=[\(masks)] router=\(router) included=\(included) excluded=\(excluded)") - } else { - ovpnLog(.error, title: "IPv4RoutesPre", message: "ipv4Settings is nil") - } - - if let ipv6Settings = effectiveSettings.ipv6Settings { - let included = (ipv6Settings.includedRoutes ?? []).map { "\($0.destinationAddress)/\($0.destinationNetworkPrefixLength)" } - let excluded = (ipv6Settings.excludedRoutes ?? []).map { "\($0.destinationAddress)/\($0.destinationNetworkPrefixLength)" } - let addresses = ipv6Settings.addresses.joined(separator: ",") - let prefixes = ipv6Settings.networkPrefixLengths.map { "\($0)" }.joined(separator: ",") - ovpnLog(.info, title: "IPv6RoutesPre", message: "addresses=[\(addresses)] prefixes=[\(prefixes)] included=\(included) excluded=\(excluded)") - } - - if splitType == 1 { + if splitTunnelType == 1 { var ipv4IncludedRoutes = [NEIPv4Route]() guard let splitTunnelSites else { @@ -390,8 +195,9 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate { } } - effectiveSettings.ipv4Settings?.includedRoutes = ipv4IncludedRoutes - } else if splitType == 2 { + networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes + } else { + if splitTunnelType == 2 { var ipv4ExcludedRoutes = [NEIPv4Route]() var ipv4IncludedRoutes = [NEIPv4Route]() var ipv6IncludedRoutes = [NEIPv6Route]() @@ -419,418 +225,14 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate { destinationAddress: "\(allIPv6.address)", networkPrefixLength: NSNumber(value: allIPv6.networkPrefixLength))) } - effectiveSettings.ipv4Settings?.includedRoutes = ipv4IncludedRoutes - effectiveSettings.ipv6Settings?.includedRoutes = ipv6IncludedRoutes - effectiveSettings.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes - } else { - // Full tunnel: rely on adapter-provided routes. - } - - if let serverEndpoint, - Self.isIPv4Address(serverEndpoint), - let ipv4Settings = effectiveSettings.ipv4Settings { - let hostMask = "255.255.255.255" - var excluded = ipv4Settings.excludedRoutes ?? [] - let alreadyExcluded = excluded.contains { - $0.destinationAddress == serverEndpoint && $0.destinationSubnetMask == hostMask - } - if !alreadyExcluded { - excluded.append(NEIPv4Route(destinationAddress: serverEndpoint, subnetMask: hostMask)) - ipv4Settings.excludedRoutes = excluded - ovpnLog(.info, title: "IPv4Routes", message: "excluded remoteAddress=\(serverEndpoint)") - } - } else if let serverEndpoint { - ovpnLog(.info, title: "IPv4Routes", message: "skip explicit remote exclude; non-ip server=\(serverEndpoint)") - } - - let localAddr = openVpnLocalAddress - var net30Gateway: String? - if let localAddr, let mask = openVpnLocalMask { - net30Gateway = Self.net30Peer(for: localAddr, mask: mask) - } - var gateway = net30Gateway - if let adapterGateway = openVPNAdapter.connectionInformation?.gatewayIPv4, !adapterGateway.isEmpty { - if let localAddr, adapterGateway == localAddr { - ovpnLog(.info, title: "IPv4Gateway", message: "ignore adapter gateway equal to local address=\(adapterGateway)") - } else if let net30Gateway, net30Gateway != adapterGateway { - ovpnLog(.info, title: "IPv4Gateway", message: "ignore mismatched adapter gateway=\(adapterGateway), using net30 peer=\(net30Gateway)") - } else { - gateway = adapterGateway + networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes + networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes + networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes } } - openVpnGatewayAddress = gateway - if let gateway, !gateway.isEmpty { - ovpnLog(.info, title: "IPv4Gateway", message: "gateway=\(gateway)") - } -#if os(macOS) - if splitType == 0, let gateway, !gateway.isEmpty, effectiveSettings.tunnelRemoteAddress != gateway { - let updatedSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: gateway) - updatedSettings.ipv4Settings = effectiveSettings.ipv4Settings - updatedSettings.ipv6Settings = effectiveSettings.ipv6Settings - updatedSettings.dnsSettings = effectiveSettings.dnsSettings - updatedSettings.proxySettings = effectiveSettings.proxySettings - updatedSettings.mtu = effectiveSettings.mtu - effectiveSettings = updatedSettings - ovpnLog(.info, title: "Remote", message: "tunnelRemoteAddress set to gateway=\(gateway) on macOS full-tunnel") - } -#endif -#if os(macOS) - if var ipv4Settings = effectiveSettings.ipv4Settings { - if splitType == 0 { - let hasNet30Mask = ipv4Settings.subnetMasks.contains("255.255.255.252") - if hasNet30Mask { - let normalizedMasks = Array(repeating: "255.255.255.255", - count: ipv4Settings.subnetMasks.count) - let normalized = NEIPv4Settings(addresses: ipv4Settings.addresses, - subnetMasks: normalizedMasks) - normalized.includedRoutes = ipv4Settings.includedRoutes - normalized.excludedRoutes = ipv4Settings.excludedRoutes - if #available(macOS 13.0, *) { - normalized.router = ipv4Settings.router - } - ipv4Settings = normalized - ovpnLog(.info, title: "IPv4Routes", message: "normalized net30 /30 masks to /32 on macOS full-tunnel") - } - - if let gateway, !gateway.isEmpty { - if #available(macOS 13.0, *) { - ipv4Settings.router = gateway - ovpnLog(.info, title: "IPv4Routes", message: "set ipv4 router=\(gateway) on macOS full-tunnel") - } - } - - var included = ipv4Settings.includedRoutes ?? [] - let hasDefault = included.contains { - $0.destinationAddress == "0.0.0.0" && $0.destinationSubnetMask == "0.0.0.0" - } - if hasDefault { - included.removeAll { - $0.destinationAddress == "0.0.0.0" && $0.destinationSubnetMask == "0.0.0.0" - } - } - let hasDef1Low = included.contains { - $0.destinationAddress == "0.0.0.0" && $0.destinationSubnetMask == "128.0.0.0" - } - let hasDef1High = included.contains { - $0.destinationAddress == "128.0.0.0" && $0.destinationSubnetMask == "128.0.0.0" - } - if (hasDefault || openVpnRedirectGatewayDef1) && !(hasDef1Low && hasDef1High) { - if !hasDef1Low { - let route = NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "128.0.0.0") - if let gateway, !gateway.isEmpty { - route.gatewayAddress = gateway - } - included.append(route) - } - if !hasDef1High { - let route = NEIPv4Route(destinationAddress: "128.0.0.0", subnetMask: "128.0.0.0") - if let gateway, !gateway.isEmpty { - route.gatewayAddress = gateway - } - included.append(route) - } - ovpnLog(.info, title: "IPv4Routes", message: "ensured def1 routes (/1 + /1) on macOS full-tunnel") - } - if let gateway, !gateway.isEmpty { - included = included.map { route in - let isDef1 = - (route.destinationAddress == "0.0.0.0" && route.destinationSubnetMask == "128.0.0.0") || - (route.destinationAddress == "128.0.0.0" && route.destinationSubnetMask == "128.0.0.0") - guard isDef1 else { return route } - if route.gatewayAddress == gateway { - return route - } - let updatedRoute = NEIPv4Route(destinationAddress: route.destinationAddress, - subnetMask: route.destinationSubnetMask) - updatedRoute.gatewayAddress = gateway - return updatedRoute - } - ovpnLog(.info, title: "IPv4Routes", message: "set gateway=\(gateway) on macOS def1 routes") - } - ipv4Settings.includedRoutes = included - effectiveSettings.ipv4Settings = ipv4Settings - } - } -#endif - if let ipv4Settings = effectiveSettings.ipv4Settings { - let included = (ipv4Settings.includedRoutes ?? []).map { - let gw = $0.gatewayAddress ?? "" - return "\($0.destinationAddress)/\($0.destinationSubnetMask) gw=\(gw)" - } - let excluded = (ipv4Settings.excludedRoutes ?? []).map { "\($0.destinationAddress)/\($0.destinationSubnetMask)" } - let addresses = ipv4Settings.addresses.joined(separator: ",") - let masks = ipv4Settings.subnetMasks.joined(separator: ",") - let router: String -#if os(macOS) - if #available(macOS 13.0, *) { - router = ipv4Settings.router ?? "" - } else { - router = "" - } -#else - router = "" -#endif - ovpnLog(.info, title: "IPv4Routes", message: "addresses=[\(addresses)] masks=[\(masks)] router=\(router) included=\(included) excluded=\(excluded)") - } else { - ovpnLog(.error, title: "IPv4Routes", message: "ipv4Settings is nil") - } - - if let ipv6Settings = effectiveSettings.ipv6Settings { - let included = (ipv6Settings.includedRoutes ?? []).map { "\($0.destinationAddress)/\($0.destinationNetworkPrefixLength)" } - let excluded = (ipv6Settings.excludedRoutes ?? []).map { "\($0.destinationAddress)/\($0.destinationNetworkPrefixLength)" } - let addresses = ipv6Settings.addresses.joined(separator: ",") - let prefixes = ipv6Settings.networkPrefixLengths.map { "\($0)" }.joined(separator: ",") - ovpnLog(.info, title: "IPv6Routes", message: "addresses=[\(addresses)] prefixes=[\(prefixes)] included=\(included) excluded=\(excluded)") - } -#if os(macOS) - if effectiveSettings.ipv6Settings != nil { - effectiveSettings.ipv6Settings = nil - ovpnLog(.info, title: "IPv6", message: "cleared ipv6Settings on macOS") - } -#endif - - lastOpenVPNSettings = effectiveSettings - // Set the network settings for the current tunneling session. - setTunnelNetworkSettings(effectiveSettings) { error in - if let error { - ovpnLog(.error, title: "SetTunnelNetworkSettings", message: error.localizedDescription) - } else { - ovpnLog(.info, title: "SetTunnelNetworkSettings", message: "ok") - } - completionHandler(error) - } - } - - private static func extractDnsServers(from config: String) -> [String] { - let lines = config.split(whereSeparator: \.isNewline) - var servers: [String] = [] - for line in lines { - let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) - if trimmed.hasPrefix("dhcp-option DNS ") { - let parts = trimmed.split(separator: " ") - if let last = parts.last { - servers.append(String(last)) - } - } - } - return servers - } - - private static func extractRemoteHost(from config: String) -> String? { - let lines = config.split(whereSeparator: \.isNewline) - for line in lines { - let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) - if trimmed.hasPrefix("remote ") { - let parts = trimmed.split(separator: " ") - if parts.count >= 2 { - return String(parts[1]) - } - } - } - return nil - } - - private static func hasRedirectGatewayDef1(in config: String) -> Bool { - let lines = config.split(whereSeparator: \.isNewline) - for line in lines { - let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) - if trimmed.hasPrefix("redirect-gateway") { - return trimmed.split(whereSeparator: { $0 == " " || $0 == "\t" }).contains("def1") - } - } - return false - } - - private static func net30Peer(for address: String, mask: String) -> String? { - guard mask == "255.255.255.252" else { return nil } - let parts = address.split(separator: ".") - guard parts.count == 4 else { return nil } - var octets: [Int] = [] - for part in parts { - guard let num = Int(part), num >= 0 && num <= 255 else { return nil } - octets.append(num) - } - let ip = (octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3] - let network = ip & ~3 - let host = ip - network - let peerHost: Int - switch host { - case 1: peerHost = 2 - case 2: peerHost = 1 - default: return nil - } - let peerIP = network + peerHost - return "\((peerIP >> 24) & 0xff).\((peerIP >> 16) & 0xff).\((peerIP >> 8) & 0xff).\(peerIP & 0xff)" - } - - private func logOpenVPNConnectionInfo() { - guard let info = ovpnAdapter?.connectionInformation else { return } - let message = "vpnIPv4=\(info.vpnIPv4 ?? "") gatewayIPv4=\(info.gatewayIPv4 ?? "") serverIP=\(info.serverIP ?? "") tun=\(info.tunName ?? "")" - ovpnLog(.info, title: "ConnInfo", message: message) - } - - private static func normalizeInlineBlock( - in config: String, - tag: String, - beginMarkers: [String], - endMarkers: [String] - ) -> String { - guard !beginMarkers.isEmpty, !endMarkers.isEmpty else { return config } - - var normalizedConfig = config - let openTag = "<\(tag)>" - let closeTag = "" - var searchStart = normalizedConfig.startIndex - - while let openRange = normalizedConfig.range(of: openTag, range: searchStart..= beginIndex { - let extracted = lines[beginIndex...endIndex].joined(separator: "\n") - let replacement = "<\(tag)>\n\(extracted)\n" - normalizedConfig.replaceSubrange(openRange.lowerBound.. linesIn=\(lines.count) linesOut=\(endIndex - beginIndex + 1)") - searchStart = normalizedConfig.index(openRange.lowerBound, offsetBy: replacement.count) - } else { - ovpnLog(.error, title: "ConfigInline", message: "tag=<\(tag)> missing markers, keeping original body") - searchStart = closeRange.upperBound - } - } - - return normalizedConfig - } - - - private static func stripUnsupportedOptions(forOpenVPNAdapter config: String) -> String { - let unsupportedTokens: Set = [ - "block-ipv6", - "script-security", - "up", - "down", - "resolv-retry", - "persist-key", - "persist-tun", - "compat-mode", - "disable-dco" - ] - let inlineBlockTags: Set = [ - "ca", - "cert", - "key", - "pkcs12", - "tls-auth", - "tls-crypt", - "tls-crypt-v2", - "secret", - "crl-verify", - "extra-certs" - ] - - var removed: [String: Int] = [:] - var normalized: [String: Int] = [:] - var output: [String] = [] - var activeInlineTag: String? - - for rawLine in config.split(whereSeparator: \.isNewline) { - let line = String(rawLine) - let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) - if trimmed.isEmpty { - output.append(line) - continue - } - - let trimmedLowercased = trimmed.lowercased() - - if let currentInlineTag = activeInlineTag { - output.append(line) - if trimmedLowercased == "" { - activeInlineTag = nil - } - continue - } - - if trimmedLowercased.hasPrefix("<"), - trimmedLowercased.hasSuffix(">"), - !trimmedLowercased.hasPrefix(" Bool { - let parts = value.split(separator: ".") - if parts.count != 4 { return false } - for part in parts { - guard let num = Int(part), num >= 0 && num <= 255 else { return false } - } - return true + setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler) } // Process events returned by the OpenVPN library @@ -848,9 +250,6 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate { startHandler(nil) self.startHandler = nil - - logOpenVPNConnectionInfo() - refreshOpenVPNSettingsAfterConnect() case .disconnected: guard let stopHandler = stopHandler else { return } @@ -893,41 +292,4 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate { // Handle log messages ovpnLog(.info, message: logMessage) } - - func openVPNAdapterDidReceiveClockTick(_ openVPNAdapter: OpenVPNAdapter) { - let now = Date() - if now.timeIntervalSince(lastOpenVPNStatsLogTime) < 5 { - return - } - lastOpenVPNStatsLogTime = now - - let transport = openVPNAdapter.transportStatistics - let iface = openVPNAdapter.interfaceStatistics - let transportLine = "transport bytesIn=\(transport.bytesIn) bytesOut=\(transport.bytesOut) packetsIn=\(transport.packetsIn) packetsOut=\(transport.packetsOut)" - let ifaceLine = "iface bytesIn=\(iface.bytesIn) bytesOut=\(iface.bytesOut) packetsIn=\(iface.packetsIn) packetsOut=\(iface.packetsOut) errorsIn=\(iface.errorsIn) errorsOut=\(iface.errorsOut)" - ovpnLog(.info, title: "Stats", message: "\(transportLine) | \(ifaceLine)") - } - - private func refreshOpenVPNSettingsAfterConnect() { - let localAddr = openVpnLocalAddress - var net30Gateway: String? - if let localAddr, let mask = openVpnLocalMask { - net30Gateway = Self.net30Peer(for: localAddr, mask: mask) - } - var gateway = net30Gateway - if let adapterGateway = ovpnAdapter?.connectionInformation?.gatewayIPv4, !adapterGateway.isEmpty { - if let localAddr, adapterGateway == localAddr { - ovpnLog(.info, title: "IPv4Gateway", message: "post-connect ignoring adapter gateway equal to local address=\(adapterGateway)") - } else if let net30Gateway, net30Gateway != adapterGateway { - ovpnLog(.info, title: "IPv4Gateway", message: "post-connect keeping net30 peer=\(net30Gateway), adapter gateway=\(adapterGateway)") - } else { - gateway = adapterGateway - } - } - - guard let gateway, !gateway.isEmpty else { return } - openVpnGatewayAddress = gateway - ovpnLog(.info, title: "IPv4Gateway", message: "post-connect gateway=\(gateway)") - } - } diff --git a/client/platforms/ios/PacketTunnelProvider.swift b/client/platforms/ios/PacketTunnelProvider.swift index 7b5f3d899..8a6784133 100644 --- a/client/platforms/ios/PacketTunnelProvider.swift +++ b/client/platforms/ios/PacketTunnelProvider.swift @@ -48,14 +48,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { var splitTunnelType: Int? var splitTunnelSites: [String]? - var openVpnDnsServers: [String] = [] - var openVpnRemoteAddress: String? - var openVpnRedirectGatewayDef1 = false - var openVpnLocalAddress: String? - var openVpnLocalMask: String? - var openVpnGatewayAddress: String? - var lastOpenVPNSettings: NEPacketTunnelNetworkSettings? - var lastOpenVPNStatsLogTime = Date.distantPast let vpnReachability = OpenVPNReachability() @@ -86,8 +78,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider { guard hasMeaningfulChange, let proto = self.protoType else { return } - // WireGuard/AWG and OpenVPN manage network changes internally; avoid restarting the tunnel here. - if proto == .wireguard || proto == .openvpn { + // WireGuard/AWG manages network changes internally; avoid restarting the tunnel here. + if proto == .wireguard { return } @@ -187,15 +179,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId) neLog(.info, message: "Start tunnel") - if let vpnProto = protocolConfiguration as? NEVPNProtocol { - if #available(iOS 14.0, macOS 11.0, *) { - var details = "includeAllNetworks=\(vpnProto.includeAllNetworks)" - if #available(iOS 14.2, macOS 11.0, *) { - details += " excludeLocalNetworks=\(vpnProto.excludeLocalNetworks)" - } - neLog(.info, title: "Protocol", message: details) - } - } if let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol { let providerConfiguration = protocolConfiguration.providerConfiguration @@ -340,8 +323,6 @@ extension WireGuardLogLevel { final class PacketTunnelFlowAdapter: NSObject, OpenVPNAdapterPacketFlow { private let flow: NEPacketTunnelFlow - private var readLogCounter = 0 - private var writeLogCounter = 0 init(flow: NEPacketTunnelFlow) { self.flow = flow @@ -350,98 +331,15 @@ final class PacketTunnelFlowAdapter: NSObject, OpenVPNAdapterPacketFlow { @objc(readPacketsWithCompletionHandler:) func readPackets(completionHandler: @escaping ([Data], [NSNumber]) -> Void) { - flow.readPackets { packets, protocols in -#if os(macOS) - if self.readLogCounter < 20, let firstPacket = packets.first, let firstProtocol = protocols.first { - let prefix = firstPacket.prefix(12).map { String(format: "%02x", $0) }.joined() - let header = Self.describePacketHeader(firstPacket) - ovpnLog(.info, title: "FlowRead", message: "count=\(packets.count) proto0=\(firstProtocol) len0=\(firstPacket.count) prefix=\(prefix) \(header)") - self.readLogCounter += 1 - } -#endif - completionHandler(packets, protocols) - } + flow.readPackets(completionHandler: completionHandler) } @objc(writePackets:withProtocols:) func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool { -#if os(macOS) - if writeLogCounter < 20, let firstPacket = packets.first, let firstProtocol = protocols.first { - let prefix = firstPacket.prefix(12).map { String(format: "%02x", $0) }.joined() - let header = Self.describePacketHeader(firstPacket) - ovpnLog(.info, title: "FlowWrite", message: "count=\(packets.count) proto0=\(firstProtocol) len0=\(firstPacket.count) prefix=\(prefix) \(header)") - writeLogCounter += 1 - } -#endif - return flow.writePackets(packets, withProtocols: protocols) - } - - private static func describePacketHeader(_ packet: Data) -> String { - guard let versionNibble = packet.first.map({ Int($0 >> 4) }) else { - return "ip=unknown" - } - - if versionNibble == 4, packet.count >= 20 { - let ihl = Int(packet[0] & 0x0f) * 4 - guard ihl >= 20, packet.count >= ihl else { - return "ip=ipv4 malformed" - } - - let proto = packet[9] - let src = "\(packet[12]).\(packet[13]).\(packet[14]).\(packet[15])" - let dst = "\(packet[16]).\(packet[17]).\(packet[18]).\(packet[19])" - let l4Offset = ihl - let ports: String - if (proto == 6 || proto == 17) && packet.count >= l4Offset + 4 { - let srcPort = (UInt16(packet[l4Offset]) << 8) | UInt16(packet[l4Offset + 1]) - let dstPort = (UInt16(packet[l4Offset + 2]) << 8) | UInt16(packet[l4Offset + 3]) - ports = "sport=\(srcPort) dport=\(dstPort)" - } else { - ports = "sport=- dport=-" - } - let protoName: String - switch proto { - case 1: protoName = "ICMP" - case 6: protoName = "TCP" - case 17: protoName = "UDP" - default: protoName = "P\(proto)" - } - return "ip=ipv4 src=\(src) dst=\(dst) proto=\(protoName) \(ports)" - } - - if versionNibble == 6, packet.count >= 40 { - let proto = packet[6] - func hex16(_ start: Int) -> String { - let value = (UInt16(packet[start]) << 8) | UInt16(packet[start + 1]) - return String(format: "%x", value) - } - let src = stride(from: 8, to: 24, by: 2).map(hex16).joined(separator: ":") - let dst = stride(from: 24, to: 40, by: 2).map(hex16).joined(separator: ":") - let l4Offset = 40 - let ports: String - if (proto == 6 || proto == 17) && packet.count >= l4Offset + 4 { - let srcPort = (UInt16(packet[l4Offset]) << 8) | UInt16(packet[l4Offset + 1]) - let dstPort = (UInt16(packet[l4Offset + 2]) << 8) | UInt16(packet[l4Offset + 3]) - ports = "sport=\(srcPort) dport=\(dstPort)" - } else { - ports = "sport=- dport=-" - } - let protoName: String - switch proto { - case 58: protoName = "ICMPv6" - case 6: protoName = "TCP" - case 17: protoName = "UDP" - default: protoName = "P\(proto)" - } - return "ip=ipv6 src=\(src) dst=\(dst) proto=\(protoName) \(ports)" - } - - return "ip=v\(versionNibble) len=\(packet.count)" + flow.writePackets(packets, withProtocols: protocols) } } -extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {} - extension NEProviderStopReason { var amneziaDescription: String { switch self { diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index 3a04e238f..9302680bd 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -785,38 +785,8 @@ bool IosController::startOpenVPN(const QString &config) NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init]; tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID]; - QByteArray configUtf8 = config.toUtf8(); - NSData *ovpnConfigData = [NSData dataWithBytes:configUtf8.constData() length:configUtf8.size()]; - tunnelProtocol.providerConfiguration = @{@"ovpn": ovpnConfigData}; + tunnelProtocol.providerConfiguration = @{@"ovpn": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]}; tunnelProtocol.serverAddress = m_serverAddress; - if (@available(iOS 14.0, macOS 11.0, *)) { - int splitTunnelType = 0; - QJsonParseError parseError; - QJsonDocument doc = QJsonDocument::fromJson(config.toUtf8(), &parseError); - if (parseError.error == QJsonParseError::NoError && doc.isObject()) { - QJsonObject obj = doc.object(); - splitTunnelType = obj.value(config_key::splitTunnelType).toInt(0); - } -#if defined(MACOS_NE) - // On macOS NE use route-based full tunnel. includeAllNetworks enables - // policy-based drop-all mode and causes enforceRoutes to be ignored. - tunnelProtocol.includeAllNetworks = NO; - if (splitTunnelType == 0) { - tunnelProtocol.enforceRoutes = YES; - if (@available(iOS 14.2, macOS 11.0, *)) { - tunnelProtocol.excludeLocalNetworks = YES; - } - } -#else - tunnelProtocol.includeAllNetworks = (splitTunnelType == 0); - if (@available(iOS 14.2, macOS 11.0, *)) { - // Keep existing iOS behavior. - if (splitTunnelType == 0) { - tunnelProtocol.excludeLocalNetworks = NO; - } - } -#endif - } m_currentTunnel.protocolConfiguration = tunnelProtocol; @@ -829,9 +799,7 @@ bool IosController::startWireGuard(const QString &config) NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init]; tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID]; - QByteArray configUtf8 = config.toUtf8(); - NSData *wgConfigData = [NSData dataWithBytes:configUtf8.constData() length:configUtf8.size()]; - tunnelProtocol.providerConfiguration = @{@"wireguard": wgConfigData}; + tunnelProtocol.providerConfiguration = @{@"wireguard": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]}; tunnelProtocol.serverAddress = m_serverAddress; m_currentTunnel.protocolConfiguration = tunnelProtocol; @@ -845,9 +813,7 @@ bool IosController::startXray(const QString &config) NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init]; tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID]; - QByteArray configUtf8 = config.toUtf8(); - NSData *xrayConfigData = [NSData dataWithBytes:configUtf8.constData() length:configUtf8.size()]; - tunnelProtocol.providerConfiguration = @{@"xray": xrayConfigData}; + tunnelProtocol.providerConfiguration = @{@"xray": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]}; tunnelProtocol.serverAddress = m_serverAddress; m_currentTunnel.protocolConfiguration = tunnelProtocol;