fix(vpn): bypass own domains in DNS filter (rebreak.org, rebreak.app)
OAuth-Callbacks gehen an db-staging.rebreak.org — wenn der In-Flight-Cap kurz erreicht wird, kriegt das SERVFAIL statt einer echten Antwort. Eigene Infrastruktur-Domains explizit als Bypass deklariert: werden nie aus der Blocklist geblockt und umgehen den In-Flight-Zähler nicht (Forward läuft weiterhin normal, aber Block-Entscheidung wird übersprungen). Gilt für iOS (PacketTunnelProvider) und Android (DnsFilter) gleichzeitig. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b1d382bada
commit
617312f367
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user