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 e5b8b83
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 e5b8b83

Please sign in to comment.