Skip to content

Commit

Permalink
Issue guardianproject#1193: Added support for mixing bridge types wit…
Browse files Browse the repository at this point in the history
…h custom bridges: Meek, Obfs4 and Webtunnel can now be used all in parallel.
  • Loading branch information
tladesignz authored and syphyr committed Jan 24, 2025
1 parent 1fb0350 commit 8a64d7c
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 35 deletions.
7 changes: 7 additions & 0 deletions app-tv/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
plugins {
alias(libs.plugins.kotlin.android)
}
apply from: "../commons.gradle"

android {
Expand Down Expand Up @@ -41,6 +44,9 @@ android {
archivesBaseName = "Orbot-TV-$versionName"
}
}
kotlinOptions {
jvmTarget = '17'
}
}

configurations {
Expand All @@ -65,6 +71,7 @@ dependencies {
implementation(libs.androidx.palette)
implementation(libs.androidx.recyclerview)
implementation(libs.apl.appintro)
implementation libs.androidx.core.ktx

androidTestImplementation(libs.fastlane.screengrab)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class CustomBridgeBottomSheet(private val callbacks: ConnectionHelperCallbacks)
OrbotBottomSheetDialogFragment() {
companion object {
const val TAG = "CustomBridgeBottomSheet"
private const val bridgeStatement = "obfs4"
private val bridgeStatement = Regex("(obfs4|meek|webtunnel)")
}

private lateinit var btnAction: Button
Expand Down Expand Up @@ -42,5 +42,4 @@ class CustomBridgeBottomSheet(private val callbacks: ConnectionHelperCallbacks)
btnAction.isEnabled =
!(etBridges.text.isEmpty() || !etBridges.text.contains(bridgeStatement))
}

}
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id "com.android.application" version "8.8.0" apply false
id "org.jetbrains.kotlin.android" version "2.0.0" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}
9 changes: 6 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ androidx-leanback-tab = "1.1.0-beta01"
androidx-localbroadcast = "1.1.0"
androidx-palette = "1.0.0"
androidx-preference = "1.2.1"
androidx-recyclerview = "1.3.2"
androidx-recyclerview = "1.4.0"
androidx-work = "2.10.0"
apl-appintro = "v4.2.3"
espresso-core = "3.6.1"
Expand All @@ -23,11 +23,12 @@ guardian-jtorctl = "0.4.8.13"
junit = "4.13.2"
junit-version = "1.2.1"
navigation-fragment-ktx = "2.8.5"
retrofit = "2.9.0"
rootbeer-lib = "0.1.0"
retrofit = "2.11.0"
rootbeer-lib = "0.1.1"
tor-android = "0.4.8.13"
pcap-core = "1.8.2"
pcap-factory = "1.8.2"
kotlin = "2.1.0"

[libraries]
android-material = { group = "com.google.android.material", name = "material", version.ref = "android-material" }
Expand Down Expand Up @@ -64,3 +65,5 @@ rootbeer-lib = { module = "com.scottyab:rootbeer-lib", version.ref = "rootbeer-l
tor-android = { group = "info.guardianproject", name = "tor-android", version.ref = "tor-android" }
pcap-core = { group = "org.pcap4j", name = "pcap4j-core", version.ref = "pcap-core" }
pcap-factory = { group = "org.pcap4j", name = "pcap4j-packetfactory-static", version.ref = "pcap-factory" }
[plugins]
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
7 changes: 7 additions & 0 deletions intentintegrator/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
plugins {
alias(libs.plugins.kotlin.android)
}
apply plugin: "com.android.library"

android {
Expand All @@ -21,8 +24,12 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}

dependencies {
implementation(libs.androidx.appcompat)
implementation libs.androidx.core.ktx
}
7 changes: 7 additions & 0 deletions orbotservice/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
plugins {
alias(libs.plugins.kotlin.android)
}
apply plugin: "com.android.library"

android {
Expand Down Expand Up @@ -36,6 +39,9 @@ android {
textReport false
xmlReport false
}
kotlinOptions {
jvmTarget = '17'
}
}

dependencies {
Expand All @@ -54,4 +60,5 @@ dependencies {
implementation(libs.pcap.factory)

// implementation(files("../libs/geoip.jar"))
implementation libs.androidx.core.ktx
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import net.freehaven.tor.control.TorControlCommands;
import net.freehaven.tor.control.TorControlConnection;

import org.torproject.android.service.util.Bridge;
import org.torproject.android.service.util.CustomTorResourceInstaller;
import org.torproject.android.service.util.PowerConnectionReceiver;
import org.torproject.android.service.util.Prefs;
Expand Down Expand Up @@ -137,16 +138,6 @@ public void stopped(String s, Exception e) {

return mIptProxy;
}
/**
* @param bridgeList bridges that were manually entered into Orbot settings
* @return Array with each bridge as an element, no whitespace entries see issue #289...
*/
private static String[] parseBridgesFromSettings(String bridgeList) {
// this regex replaces lines that only contain whitespace with an empty String
bridgeList = bridgeList.trim().replaceAll("(?m)^[ \t]*\r?\n", "");
return bridgeList.split("\\n");
}

public void debug(String msg) {
Log.d(TAG, msg);

Expand Down Expand Up @@ -299,12 +290,12 @@ private void stopTorAsync(boolean showNotification) {
// todo this needs to handle a lot of different cases that haven't been defined yet
// todo particularly this is true for the smart connection case...
if (connectionPathway.startsWith(Prefs.PATHWAY_SNOWFLAKE) || Prefs.getPrefSmartTrySnowflake()) {
// mIptProxy.stop
mIptProxy.stop(IPtProxy.Snowflake);
} else if (connectionPathway.equals(Prefs.PATHWAY_CUSTOM) || Prefs.getPrefSmartTryObfs4() != null) {
// IPtProxy.stopLyrebird();
}
else if (connectionPathway.equals(Prefs.PATHWAY_CUSTOM) || Prefs.getPrefSmartTryObfs4() != null) {
mIptProxy.stop(IPtProxy.MeekLite);
mIptProxy.stop(IPtProxy.Obfs4);

mIptProxy.stop(IPtProxy.Webtunnel);
}

stopTor();
Expand Down Expand Up @@ -1204,7 +1195,7 @@ private StringBuffer processSettingsImpl(StringBuffer extraLines) throws IOExcep
if (pathway.startsWith(Prefs.PATHWAY_SNOWFLAKE) || Prefs.getPrefSmartTrySnowflake()) {
extraLines = processSettingsImplSnowflake(extraLines);
} else if (pathway.equals(Prefs.PATHWAY_CUSTOM) || Prefs.getPrefSmartTryObfs4() != null) {
extraLines = processSettingsImplObfs4(extraLines);
extraLines = processSettingsLyrebird(extraLines);
}
}
var fileGeoIP = new File(appBinHome, GEOIP_ASSET_KEY);
Expand Down Expand Up @@ -1274,20 +1265,32 @@ private StringBuffer processSettingsImplSnowflake(StringBuffer extraLines) {
return extraLines;
}

private StringBuffer processSettingsImplObfs4(StringBuffer extraLines) {
Log.d(TAG, "in obfs4 torrc config");
extraLines.append("ClientTransportPlugin obfs4 socks5 127.0.0.1:" + mIptProxy.port(IPtProxy.Obfs4)).append('\n');
private StringBuffer processSettingsLyrebird(StringBuffer extraLines) {
Log.d(TAG, "in Lyrebird torrc config");

var customBridges = getCustomBridges();

var bridgeList = "";
if (Prefs.getConnectionPathway().equals(Prefs.PATHWAY_CUSTOM)) {
bridgeList = Prefs.getBridgesList();
} else bridgeList = Prefs.getPrefSmartTryObfs4();
var customBridges = parseBridgesFromSettings(bridgeList);
for (var b : customBridges)
for (String transport : Bridge.getTransports(customBridges)) {
extraLines
.append(String.format(Locale.US, "ClientTransportPlugin %s socks5 127.0.0.1:%d",
transport, mIptProxy.port(transport)))
.append('\n');
}

for (var b : customBridges) {
extraLines.append("Bridge ").append(b).append("\n");
}

return extraLines;
}

private List<Bridge> getCustomBridges() {
return Bridge.parseBridges(
Prefs.getConnectionPathway().equals(Prefs.PATHWAY_CUSTOM)
? Prefs.getBridgesList()
: Prefs.getPrefSmartTryObfs4());
}

private StringBuffer processSettingsImplDirectPathway(StringBuffer extraLines) {
var prefs = Prefs.getSharedPrefs(getApplicationContext());
extraLines.append("UseBridges 0").append('\n');
Expand Down Expand Up @@ -1528,11 +1531,12 @@ public void run() {
} else if (connectionPathway.equals(Prefs.PATHWAY_SNOWFLAKE_AMP)) {
startSnowflakeClientAmpRendezvous();
} else if (connectionPathway.equals(Prefs.PATHWAY_CUSTOM) || Prefs.getPrefSmartTryObfs4() != null) {
//IPtProxy.startLyrebird("DEBUG", false, false, null);
try {
mIptProxy.start(IPtProxy.Obfs4,"");
} catch (Exception e) {
throw new RuntimeException(e);
for (var transport : Bridge.getTransports(getCustomBridges())) {
try {
mIptProxy.start(transport, "");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
startTor();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.torproject.android.service.util

/**
* Parser for bridge lines.
*/
@Suppress("MemberVisibilityCanBePrivate", "unused")
class Bridge(var raw: String) {

val rawPieces
get() = raw.split(" ")

val transport
get() = rawPieces.firstOrNull()

val address
get() = rawPieces.getOrNull(1)

val ip
get() = address?.split(":")?.firstOrNull()

val port
get() = address?.split(":")?.lastOrNull()?.toInt()

val fingerprint1
get() = rawPieces.getOrNull(2)

val fingerprint2
get() = rawPieces.firstOrNull { it.startsWith("fingerprint=") }
?.split("=")?.lastOrNull()

val url
get() = rawPieces.firstOrNull { it.startsWith("url=") }
?.split("=")?.lastOrNull()

val front
get() = rawPieces.firstOrNull { it.startsWith("front=") }
?.split("=")?.lastOrNull()

val fronts
get() = rawPieces.firstOrNull { it.startsWith("fronts=") }
?.split("=")?.lastOrNull()?.split(",")?.filter { it.isNotEmpty() }

val cert
get() = rawPieces.firstOrNull { it.startsWith("cert=") }
?.split("=")?.lastOrNull()

val iatMode
get() = rawPieces.firstOrNull { it.startsWith("iat-mode=") }
?.split("=")?.lastOrNull()

val ice
get() = rawPieces.firstOrNull { it.startsWith("ice=") }
?.split("=")?.lastOrNull()

val utlsImitate
get() = rawPieces.firstOrNull { it.startsWith("utls-imitate=") }
?.split("=")?.lastOrNull()

val ver
get() = rawPieces.firstOrNull { it.startsWith("ver=") }
?.split("=")?.lastOrNull()


override fun toString(): String {
return raw
}

companion object {

@JvmStatic
fun parseBridges(bridges: String): List<Bridge> {
return bridges
.split("\n")
.mapNotNull {
val b = it.trim()
if (b.isNotEmpty()) Bridge(b) else null
}
}

@JvmStatic
fun getTransports(bridges: List<Bridge>): Set<String> {
return bridges.mapNotNull { it.transport }.toSet()
}
}
}

0 comments on commit 8a64d7c

Please sign in to comment.