diff --git a/apps/rebreak-native/modules/rebreak-protection/android/src/main/java/expo/modules/rebreakprotection/filter/DnsFilter.kt b/apps/rebreak-native/modules/rebreak-protection/android/src/main/java/expo/modules/rebreakprotection/filter/DnsFilter.kt index 95897cd..def9dc0 100644 --- a/apps/rebreak-native/modules/rebreak-protection/android/src/main/java/expo/modules/rebreakprotection/filter/DnsFilter.kt +++ b/apps/rebreak-native/modules/rebreak-protection/android/src/main/java/expo/modules/rebreakprotection/filter/DnsFilter.kt @@ -24,6 +24,7 @@ object DnsFilter { private const val UPSTREAM_DNS = "1.1.1.1" private const val UPSTREAM_PORT = 53 private const val DNS_PORT = 53 + private val BYPASS_DOMAIN_SUFFIXES = listOf("rebreak.org", "rebreak.app") private val forwardPool: ThreadPoolExecutor = Executors.newCachedThreadPool() as ThreadPoolExecutor @@ -73,12 +74,17 @@ object DnsFilter { val srcIp = packet.copyOfRange(12, 16) val dstIp = packet.copyOfRange(16, 20) - if (hashList.matchesAnySuffix(domain)) { + // Eigene Infrastruktur-Domains nie blocken (OAuth-Callbacks, Backend-API). + val isBypass = BYPASS_DOMAIN_SUFFIXES.any { suffix -> + domain == suffix || domain.endsWith(".$suffix") + } + if (!isBypass && hashList.matchesAnySuffix(domain)) { Log.i(TAG, "BLOCKED: $domain") val resp = buildNxDomainResponse(packet, length, ihl, srcIp, dstIp, srcPort, dstPort) writeSynchronized(output, outputLock, resp) return } + if (isBypass) Log.d(TAG, "BYPASS (own domain): $domain") // Async forward — kopiere Buffer-Slice damit nicht überschrieben wird beim // nächsten read() im VpnService-Loop diff --git a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/PacketTunnelProvider.swift b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/PacketTunnelProvider.swift index 06b09cd..83516fd 100644 --- a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/PacketTunnelProvider.swift +++ b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/PacketTunnelProvider.swift @@ -51,6 +51,12 @@ private let TUNNEL_SUBNET_MASK = "255.255.255.0" private let UPSTREAM_DNS_HOST = "1.1.1.1" private let UPSTREAM_DNS_PORT: UInt16 = 53 +// Hardcodierter Bypass: eigene Infrastruktur-Domains — niemals blocken, +// unabhängig von Blocklist-Hash-Kollisionen oder In-Flight-Cap. +// Betrifft Supabase-Auth-Callbacks (db-staging.rebreak.org), +// Backend-API (staging.rebreak.org) und zukünftige prod-Domains. +private let BYPASS_DOMAIN_SUFFIXES = ["rebreak.org", "rebreak.app"] + // ─── Extension-Log-Store ────────────────────────────────────────────────────── /// Schreibt in den geteilten App-Group-Log-Store (`url_filter_logs`), den die @@ -273,9 +279,16 @@ class PacketTunnelProvider: NEPacketTunnelProvider { switch DnsFilter.classify(packet: packet, hashList: hashList) { case .block(let response, let domain): - ExtLog.write("BLOCKED: \(domain)") - // Synthetische NXDOMAIN-Response sofort zurück ins TUN. - writeToTun(response) + // Eigene Infrastruktur-Domains nie blocken — auch wenn Hash-Kollision. + // Betrifft OAuth-Callbacks (db-staging.rebreak.org) und Backend-API. + let isBypass = BYPASS_DOMAIN_SUFFIXES.contains { domain == $0 || domain.hasSuffix(".\($0)") } + if isBypass { + ExtLog.write("BYPASS (own domain): \(domain)") + forwardPacket(packet) + } else { + ExtLog.write("BLOCKED: \(domain)") + writeToTun(response) + } case .forward: forwardPacket(packet)