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_DNS = "1.1.1.1"
|
||||||
private const val UPSTREAM_PORT = 53
|
private const val UPSTREAM_PORT = 53
|
||||||
private const val DNS_PORT = 53
|
private const val DNS_PORT = 53
|
||||||
|
private val BYPASS_DOMAIN_SUFFIXES = listOf("rebreak.org", "rebreak.app")
|
||||||
|
|
||||||
private val forwardPool: ThreadPoolExecutor =
|
private val forwardPool: ThreadPoolExecutor =
|
||||||
Executors.newCachedThreadPool() as ThreadPoolExecutor
|
Executors.newCachedThreadPool() as ThreadPoolExecutor
|
||||||
@ -73,12 +74,17 @@ object DnsFilter {
|
|||||||
val srcIp = packet.copyOfRange(12, 16)
|
val srcIp = packet.copyOfRange(12, 16)
|
||||||
val dstIp = packet.copyOfRange(16, 20)
|
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")
|
Log.i(TAG, "BLOCKED: $domain")
|
||||||
val resp = buildNxDomainResponse(packet, length, ihl, srcIp, dstIp, srcPort, dstPort)
|
val resp = buildNxDomainResponse(packet, length, ihl, srcIp, dstIp, srcPort, dstPort)
|
||||||
writeSynchronized(output, outputLock, resp)
|
writeSynchronized(output, outputLock, resp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (isBypass) Log.d(TAG, "BYPASS (own domain): $domain")
|
||||||
|
|
||||||
// Async forward — kopiere Buffer-Slice damit nicht überschrieben wird beim
|
// Async forward — kopiere Buffer-Slice damit nicht überschrieben wird beim
|
||||||
// nächsten read() im VpnService-Loop
|
// 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_HOST = "1.1.1.1"
|
||||||
private let UPSTREAM_DNS_PORT: UInt16 = 53
|
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 ──────────────────────────────────────────────────────
|
// ─── Extension-Log-Store ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Schreibt in den geteilten App-Group-Log-Store (`url_filter_logs`), den die
|
/// 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) {
|
switch DnsFilter.classify(packet: packet, hashList: hashList) {
|
||||||
case .block(let response, let domain):
|
case .block(let response, let domain):
|
||||||
ExtLog.write("BLOCKED: \(domain)")
|
// Eigene Infrastruktur-Domains nie blocken — auch wenn Hash-Kollision.
|
||||||
// Synthetische NXDOMAIN-Response sofort zurück ins TUN.
|
// Betrifft OAuth-Callbacks (db-staging.rebreak.org) und Backend-API.
|
||||||
writeToTun(response)
|
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:
|
case .forward:
|
||||||
forwardPacket(packet)
|
forwardPacket(packet)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user