Exit early in CloudflareInterceptor when cloudflare challenge is interactive (#3842)
This commit is contained in:
+34
-17
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.network.interceptor
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.webkit.JavascriptInterface
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
import android.webkit.WebResourceResponse
|
import android.webkit.WebResourceResponse
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
@@ -32,18 +33,9 @@ class CloudflareInterceptor(
|
|||||||
|
|
||||||
override fun shouldIntercept(response: Response): Boolean {
|
override fun shouldIntercept(response: Response): Boolean {
|
||||||
// Check if Cloudflare anti-bot is on
|
// Check if Cloudflare anti-bot is on
|
||||||
return if (response.code in ERROR_CODES && response.header("Server") in SERVER_CHECK) {
|
// Checking the cf-mitigated header is the official way to detect a Cloudflare challenge:
|
||||||
val document = Jsoup.parse(
|
// https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/detect-response/
|
||||||
response.peekBody(Long.MAX_VALUE).string(),
|
return response.header("cf-mitigated") == "challenge" && response.header("Server") in SERVER_CHECK
|
||||||
response.request.url.toString(),
|
|
||||||
)
|
|
||||||
|
|
||||||
// solve with webview only on captcha, not on geo block
|
|
||||||
document.getElementById("challenge-error-title") != null ||
|
|
||||||
document.getElementById("challenge-error-text") != null
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun intercept(
|
override fun intercept(
|
||||||
@@ -87,6 +79,18 @@ class CloudflareInterceptor(
|
|||||||
executor.execute {
|
executor.execute {
|
||||||
webview = createWebView(originalRequest)
|
webview = createWebView(originalRequest)
|
||||||
|
|
||||||
|
webview.addJavascriptInterface(
|
||||||
|
object {
|
||||||
|
@Suppress("unused")
|
||||||
|
@JavascriptInterface
|
||||||
|
fun interactiveDetected() {
|
||||||
|
// The challenge cannot be solved non-interactively, abort.
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mihon",
|
||||||
|
)
|
||||||
|
|
||||||
webview.webViewClient = object : WebViewClient() {
|
webview.webViewClient = object : WebViewClient() {
|
||||||
override fun onPageFinished(view: WebView, url: String) {
|
override fun onPageFinished(view: WebView, url: String) {
|
||||||
fun isCloudFlareBypassed(): Boolean {
|
fun isCloudFlareBypassed(): Boolean {
|
||||||
@@ -100,9 +104,23 @@ class CloudflareInterceptor(
|
|||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url == origRequestUrl && !challengeFound) {
|
if (url == origRequestUrl) {
|
||||||
// The first request didn't return the challenge, abort.
|
if (!challengeFound) {
|
||||||
latch.countDown()
|
// The first request didn't return the challenge, abort.
|
||||||
|
latch.countDown()
|
||||||
|
} else {
|
||||||
|
// Listen for an interactiveBegin event
|
||||||
|
view.evaluateJavascript(
|
||||||
|
"""
|
||||||
|
addEventListener("message", ({data}) => {
|
||||||
|
if (data?.source === "cloudflare-challenge" && data?.event === "interactiveBegin") {
|
||||||
|
mihon.interactiveDetected();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
""".trimIndent(),
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +130,7 @@ class CloudflareInterceptor(
|
|||||||
errorResponse: WebResourceResponse?,
|
errorResponse: WebResourceResponse?,
|
||||||
) {
|
) {
|
||||||
if (request?.isForMainFrame == true) {
|
if (request?.isForMainFrame == true) {
|
||||||
if (errorResponse?.statusCode in ERROR_CODES) {
|
if (errorResponse?.responseHeaders["cf-mitigated"] == "challenge") {
|
||||||
// Found the Cloudflare challenge page.
|
// Found the Cloudflare challenge page.
|
||||||
challengeFound = true
|
challengeFound = true
|
||||||
} else {
|
} else {
|
||||||
@@ -151,7 +169,6 @@ class CloudflareInterceptor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ERROR_CODES = listOf(403, 503)
|
|
||||||
private val SERVER_CHECK = arrayOf("cloudflare-nginx", "cloudflare")
|
private val SERVER_CHECK = arrayOf("cloudflare-nginx", "cloudflare")
|
||||||
private val COOKIE_NAMES = listOf("cf_clearance")
|
private val COOKIE_NAMES = listOf("cf_clearance")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user