lines = new ArrayList<>(Arrays.asList("tor", "--verify-config", // must always be here
+ "--RunAsDaemon", "0",
+ "-f", getTorrc(context).getAbsolutePath(),
+ "--defaults-torrc", getDefaultsTorrc(context).getAbsolutePath(),
+ "--ignore-missing-torrc",
+ "--SyslogIdentityTag", TAG,
+ "--CacheDirectory", new File(context.getCacheDir(), TAG).getAbsolutePath(),
+ "--DataDirectory", getAppTorHandlerDataDir(context).getAbsolutePath(),
+ "--ControlPort", String.valueOf(controlPort),
+ "--SOCKSPort", String.valueOf(socksPort),
+ "--HTTPTunnelPort", String.valueOf(httpTunnelPort),
+ "--CookieAuthentication", "1",
+ "--LogMessageDomains", "1",
+ "--TruncateLogFile", "1"
+ ));
+
+ if (ENABLE_CONTROL_SOCKET) {
+ lines.addAll(Arrays.asList(
+ "--ControlSocket", getControlSocket(context).getAbsolutePath()
+ ));
+ }
+
+ String[] verifyLines = lines.toArray(new String[0]);
+ if (!mainConfigurationSetCommandLine(verifyLines)) {
+ throw new IllegalArgumentException("Setting command line failed: " + Arrays.toString(verifyLines));
+ }
+
+ int result = runMain(); // run verify
+ if (result != 0) {
+ throw new IllegalArgumentException("Bad command flags: " + Arrays.toString(verifyLines));
+ }
+
+ if (ENABLE_CONTROL_SOCKET) {
+ controlPortThreadStarted = new CountDownLatch(1);
+ controlPortThread.start();
+ controlPortThreadStarted.await();
+ }
+
+ String[] runLines = new String[lines.size() - 1];
+ runLines[0] = "tor";
+ for (int i = 2; i < lines.size(); i++) {
+ runLines[i - 1] = lines.get(i);
+ }
+ if (!mainConfigurationSetCommandLine(runLines)) {
+ throw new IllegalArgumentException("Setting command line failed: " + Arrays.toString(runLines));
+ }
+ if (ENABLE_CONTROL_SOCKET && !mainConfigurationSetupControlSocket()) {
+ throw new IllegalStateException("Setting up ControlPort failed!");
+ }
+ if (runMain() != 0) {
+ throw new IllegalStateException("Tor could not start!");
+ }
+
+ } catch (IllegalStateException | IllegalArgumentException | InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ mainConfigurationFree();
+ runLock.unlock();
+ }
+ }
+ };
+
+ /**
+ * Start Tor in a {@link Thread} with the minimum required config. The
+ * rest of the config should happen via the Control Port. First Tor
+ * runs with {@code --verify-config} to check the command line flags and
+ * any {@code torrc} config. Then finally Tor is
+ * started in its own {@code Thread}.
+ *
+ * Tor daemon does not output early debug messages to logcat, only after it
+ * tries to connect to the ports. So it is important that Tor does not run
+ * into port conflicts when first starting.
+ *
+ * @see #32036 output debug logs to logcat as early as possible on Android
+ * @see options that must be on the command line
+ */
+ public void startTorThread() {
+ if (runLock.isLocked()) {
+ Log.i(TAG, "Waiting for lock");
+ }
+ runLock.lock();
+ Log.i(TAG, "Acquired lock");
+ torThread.start();
+ }
+
+}
diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt
index 29491191a0..b879283539 100644
--- a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt
+++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt
@@ -2,14 +2,19 @@ package com.quietmobile.Utils
import android.content.Context
import android.util.Log
-import com.quietmobile.Utils.Const.TAG_NOTICE
import java.io.*
+import java.math.BigInteger
import java.net.ConnectException
import java.net.InetSocketAddress
import java.net.Socket
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
import java.security.SecureRandom
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
+import kotlin.math.pow
+import kotlin.random.Random
object Utils {
fun writeToFile(file: String, data: String, context: Context) {
@@ -30,6 +35,12 @@ object Utils {
return dataDirectory.absolutePath
}
+ fun generateRandomInt(length: Int = 4): Int {
+ val start = 10.0.pow((length - 1).toDouble()).toInt()
+ val end = 10.0.pow(length.toDouble()).toInt() - 1
+ return Random.nextInt(start, end)
+ }
+
fun generateRandomString(length: Int): String {
val CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
val secureRandom = SecureRandom()
@@ -82,4 +93,11 @@ object Utils {
}
}
+ @Throws(IOException::class)
+ @JvmStatic
+ fun readFileAsHex(filePath: Path?): String? {
+ val fileBytes = Files.readAllBytes(filePath)
+ return BigInteger(1, fileBytes).toString(16)
+ }
+
}