Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate In-App Internet censorship circumvention #4989

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ dependencies {

implementation libs.okhttp.tls
implementation libs.okhttp3.logging.interceptor
implementation libs.okhttp3.dnsoverhttps
implementation libs.retrofit
implementation libs.retrofit2.adapter.rxjava3
implementation libs.rxjava
Expand Down
2 changes: 2 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# --- OkHttp + Okio ---
-dontwarn okhttp3.**
-dontwarn okio.**
-keeppackagenames okhttp3.internal.publicsuffix.*
-adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz
# --- /OkHttp + Okio ---

# Glide
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ object OkHttpConnectionFactory {
.cookieJar(SharedPreferenceCookieManager.instance)
.cache(NET_CACHE)
.readTimeout(20, TimeUnit.SECONDS)
.dns(WikimediaDns)
.addInterceptor(UnsuccessfulResponseInterceptor())
.addNetworkInterceptor(CacheControlInterceptor())
.addInterceptor(CommonHeaderRequestInterceptor())
Expand All @@ -39,6 +40,10 @@ object OkHttpConnectionFactory {
.addInterceptor(TitleEncodeInterceptor())
.addInterceptor(HttpLoggingInterceptor().setLevel(Prefs.retrofitLogLevel))

if (Prefs.isCensorshipCircumventionEnabled) {
builder.install(OkHttpSSLSocketFactory)
}

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
val certFactory = CertificateFactory.getInstance("X.509")
val certificates = HandshakeCertificates.Builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.wikipedia.dataclient.okhttp
import android.util.Log
import okhttp3.OkHttpClient
import java.net.InetAddress
import java.net.Socket
import java.security.KeyStore
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
private val sslSocketFactory = SSLSocketFactory.getDefault() as SSLSocketFactory
private val censoredDomain = listOf(
"wikipedia.org",
"wikinews.org",
"wikiquote.org"
)
private fun String.isCensored() = censoredDomain.any { this == it || this.endsWith(".$it") }
object OkHttpSSLSocketFactory : SSLSocketFactory() {
override fun getDefaultCipherSuites(): Array<String> = sslSocketFactory.defaultCipherSuites
override fun getSupportedCipherSuites(): Array<String> = sslSocketFactory.supportedCipherSuites
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val address = s.inetAddress.hostAddress.takeIf { host.isCensored() || host in WIKIMEDIA_DOH_URL }
Log.d("OkHttSSLSocketFactory", "Host: $host Address: $address")
return sslSocketFactory.createSocket(s, address ?: host, port, autoClose) as SSLSocket
}
override fun createSocket(host: String, port: Int): Socket = sslSocketFactory.createSocket(host, port)
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket = sslSocketFactory.createSocket(host, port, localHost, localPort)
override fun createSocket(host: InetAddress, port: Int): Socket = sslSocketFactory.createSocket(host, port)
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket = sslSocketFactory.createSocket(address, port, localAddress, localPort)
}
fun OkHttpClient.Builder.install(sslSocketFactory: SSLSocketFactory) = apply {
val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())!!
factory.init(null as KeyStore?)
val manager = factory.trustManagers!!
val trustManager = manager.filterIsInstance<X509TrustManager>().first()
sslSocketFactory(sslSocketFactory, trustManager)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.wikipedia.dataclient.okhttp
import okhttp3.Dns
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.dnsoverhttps.DnsOverHttps
import java.net.InetAddress
const val WIKIMEDIA_DOH_URL = "https://wikimedia-dns.org/dns-query"
private fun buildDoHDNS() = DnsOverHttps.Builder()
.client(OkHttpConnectionFactory.client)
.url(WIKIMEDIA_DOH_URL.toHttpUrl())
.post(true)
.bootstrapDnsHosts(
InetAddress.getByName("185.71.138.138"),
InetAddress.getByName("2001:67c:930::1")
)
.build()
object WikimediaDns : Dns {
override fun lookup(hostname: String): List<InetAddress> {
return runCatching {
buildDoHDNS().lookup(hostname).takeIf { it.isNotEmpty() }
}.onFailure { it.printStackTrace() }
.getOrNull() ?: Dns.SYSTEM.lookup(hostname)
}
}
3 changes: 3 additions & 0 deletions app/src/main/java/org/wikipedia/settings/Prefs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ object Prefs {
get() = PrefsIoUtil.getBoolean(R.string.preference_key_reading_focus_mode, false)
set(value) = PrefsIoUtil.setBoolean(R.string.preference_key_reading_focus_mode, value)

val isCensorshipCircumventionEnabled
get() = PrefsIoUtil.getBoolean(R.string.preference_key_censorship_circumvention, true)

var fontFamily
get() = PrefsIoUtil.getString(R.string.preference_key_font_family, "").orEmpty().ifEmpty { "sans-serif" }
set(fontFamily) = PrefsIoUtil.setString(R.string.preference_key_font_family, fontFamily)
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values-zh/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@
<string name="preference_title_collapse_tables">折叠表格</string>
<string name="preference_summary_collapse_tables">自动折叠条目中的表格,如信息框、参考文献和注释。</string>
<string name="nav_item_donate">捐款</string>
<string name="preference_title_censorship_circumvention">审查规避</string>
<string name="preference_summary_censorship_circumvention">绕过基于DPI的网络审查</string>
<string name="error_voice_search_not_available">对不起,语言识别不可用。</string>
<string name="location_service_disabled">位置服务被禁用。</string>
<string name="enable_location_service">启用</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/preference_keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<string name="preference_key_customize_explore_feed">customizeExploreFeed</string>
<string name="preference_key_show_link_previews">showLinkPreviews</string>
<string name="preference_key_collapse_tables">collapseTables</string>
<string name="preference_key_censorship_circumvention">censorshipCircumvention</string>
<string name="preference_key_session_data">session_data</string>
<string name="preference_key_session_timeout">session_timeout</string>
<string name="preference_key_crashed_before_activity_created">crashedBeforeActivityCreated</string>
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@
<string name="preference_title_collapse_tables">Collapse tables</string>
<string name="preference_summary_collapse_tables">Automatically collapse tables in articles, such as infoboxes, references, and notes.</string>
<string name="nav_item_donate">Donate</string>
<string name="preference_title_censorship_circumvention">Censorship circumvention</string>
<string name="preference_summary_censorship_circumvention">Bypass DPI-based Internet filtering</string>
<string name="error_voice_search_not_available">Sorry, voice recognition is not available.</string>
<string name="location_service_disabled">Location services are disabled.</string>
<string name="enable_location_service">Enable</string>
Expand Down
7 changes: 6 additions & 1 deletion app/src/main/res/xml/preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
android:defaultValue="true"
android:title="@string/preference_title_collapse_tables"
android:summary="@string/preference_summary_collapse_tables" />
<org.wikipedia.settings.SwitchPreferenceMultiLine
android:key="@string/preference_key_censorship_circumvention"
android:defaultValue="true"
android:title="@string/preference_title_censorship_circumvention"
android:summary="@string/preference_summary_censorship_circumvention" />
<org.wikipedia.settings.PreferenceMultiLine
android:key="@string/preference_key_color_theme"
android:defaultValue="0"
Expand Down Expand Up @@ -59,4 +64,4 @@
android:title="@string/preference_title_prefer_offline_content"
android:summary="@string/preference_summary_prefer_offline_content" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceScreen>
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ okhttp-tls = { module = "com.squareup.okhttp3:okhttp-tls", version.ref = "okHttp
okhttp3-integration = { module = "com.github.bumptech.glide:okhttp3-integration", version.ref = "glideVersion" }
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okHttpVersion" }
okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okHttpVersion" }
okhttp3-dnsoverhttps = {module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okHttpVersion"}
palette-ktx = { module = "androidx.palette:palette-ktx", version.ref = "paletteKtx" }
paging-runtime-ktx = { module = "androidx.paging:paging-runtime-ktx", version.ref = "pagingRuntimeKtx" }
photoview = { module = "com.github.chrisbanes:PhotoView", version.ref = "photoview" }
Expand Down