Tor now listens to port changes and restart connections when it does change.

This commit is contained in:
Vitor Pamplona
2025-03-31 20:32:15 -04:00
parent 491d269f75
commit b3f1b093ed
6 changed files with 44 additions and 15 deletions
@@ -121,7 +121,7 @@ class ServiceManager(
HttpClientManager.setProxyNotReady() HttpClientManager.setProxyNotReady()
} }
} }
else -> HttpClientManager.setDefaultProxy(null) else -> HttpClientManager.setProxyNotReady()
} }
OtsResolver.ots = OtsResolver.ots =
@@ -136,7 +136,7 @@ class ServiceManager(
OkHttpCalendarBuilder { false }, OkHttpCalendarBuilder { false },
) )
HttpClientManager.setDefaultProxy(null) HttpClientManager.setProxyNotReady()
} }
// Convert this into a flow // Convert this into a flow
@@ -29,6 +29,7 @@ import com.vitorpamplona.amethyst.launchAndWaitAll
import com.vitorpamplona.amethyst.model.AccountSettings import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
import com.vitorpamplona.amethyst.tryAndWait import com.vitorpamplona.amethyst.tryAndWait
import com.vitorpamplona.amethyst.ui.tor.TorManager
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@@ -151,8 +152,8 @@ class RegisterAccounts(
.post(body) .post(body)
.build() .build()
// Always try via Tor for Amethyst. // Always try via Tor if active.
val client = HttpClientManager.getHttpClient(true) val client = HttpClientManager.getHttpClient(TorManager.isSocksReady())
val isSucess = client.newCall(request).execute().use { it.isSuccessful } val isSucess = client.newCall(request).execute().use { it.isSuccessful }
Log.i(tag, "Server registration $isSucess") Log.i(tag, "Server registration $isSucess")
@@ -62,6 +62,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginOrSignupScreen import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginOrSignupScreen
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.tor.TorManager import com.vitorpamplona.amethyst.ui.tor.TorManager
import com.vitorpamplona.amethyst.ui.tor.TorStatus
import com.vitorpamplona.amethyst.ui.tor.TorType import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@@ -160,6 +161,7 @@ fun ManageTorInstance(accountViewModel: AccountViewModel) {
val torSettings by accountViewModel.account.settings.torSettings.torType val torSettings by accountViewModel.account.settings.torSettings.torType
.collectAsStateWithLifecycle() .collectAsStateWithLifecycle()
if (torSettings == TorType.INTERNAL) { if (torSettings == TorType.INTERNAL) {
WatchConnection(accountViewModel)
ManageTorInstanceInner(accountViewModel) ManageTorInstanceInner(accountViewModel)
} }
} }
@@ -204,6 +206,17 @@ fun ManageTorInstanceInner(accountViewModel: AccountViewModel) {
} }
} }
@Composable
fun WatchConnection(accountViewModel: AccountViewModel) {
val status by TorManager.status.collectAsStateWithLifecycle()
LaunchedEffect(key1 = status, key2 = accountViewModel) {
if (status is TorStatus.Active) {
accountViewModel.changeProxyPort((status as TorStatus.Active).port)
}
}
}
@Composable @Composable
private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) { private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
if (accountViewModel.account.signer is NostrSignerExternal) { if (accountViewModel.account.signer is NostrSignerExternal) {
@@ -1265,6 +1265,12 @@ class AccountViewModel(
} }
} }
fun changeProxyPort(port: Int) {
viewModelScope.launch(Dispatchers.IO) {
Amethyst.instance.serviceManager.forceRestart()
}
}
class Factory( class Factory(
val accountSettings: AccountSettings, val accountSettings: AccountSettings,
val settings: SettingsState, val settings: SettingsState,
@@ -27,20 +27,33 @@ import android.content.ServiceConnection
import android.os.IBinder import android.os.IBinder
import android.util.Log import android.util.Log
import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager import kotlinx.coroutines.flow.MutableStateFlow
import org.torproject.jni.TorService import org.torproject.jni.TorService
import org.torproject.jni.TorService.LocalBinder import org.torproject.jni.TorService.LocalBinder
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
sealed class TorStatus {
data class Active(
val port: Int,
) : TorStatus()
object Off : TorStatus()
object Connecting : TorStatus()
}
object TorManager { object TorManager {
var runningIntent: Intent? = null var runningIntent: Intent? = null
var torService: TorService? = null var torService: TorService? = null
val status = MutableStateFlow<TorStatus>(TorStatus.Off)
// To make sure we don't start two services. // To make sure we don't start two services.
var isConnectingMutex = AtomicBoolean(false) var isConnectingMutex = AtomicBoolean(false)
fun startTorIfNotAlreadyOn(ctx: Context) { fun startTorIfNotAlreadyOn(ctx: Context) {
if (runningIntent == null && isConnectingMutex.compareAndSet(false, true)) { if (runningIntent == null && isConnectingMutex.compareAndSet(false, true)) {
status.tryEmit(TorStatus.Connecting)
try { try {
startTor(ctx) startTor(ctx)
} finally { } finally {
@@ -54,6 +67,7 @@ object TorManager {
runningIntent?.let { runningIntent?.let {
ctx.stopService(runningIntent) ctx.stopService(runningIntent)
} }
status.tryEmit(TorStatus.Off)
runningIntent = null runningIntent = null
torService = null torService = null
} }
@@ -80,14 +94,14 @@ object TorManager {
} }
} }
HttpClientManager.setDefaultProxyOnPort(socksPort()) status.tryEmit(TorStatus.Active(socksPort()))
Log.d("TorManager", "Tor Service Connected ${socksPort()}") Log.d("TorManager", "Tor Service Connected ${socksPort()}")
} }
override fun onServiceDisconnected(name: ComponentName) { override fun onServiceDisconnected(name: ComponentName) {
runningIntent = null runningIntent = null
torService = null torService = null
status.tryEmit(TorStatus.Off)
Log.d("TorManager", "Tor Service Disconected") Log.d("TorManager", "Tor Service Disconected")
} }
}, },
@@ -95,10 +109,7 @@ object TorManager {
) )
} }
fun isSocksReady() = fun isSocksReady() = torService?.let { it.socksPort > 0 } == true
torService?.let {
it.socksPort > 0
} ?: false
fun socksPort(): Int = torService?.socksPort ?: 9050 fun socksPort(): Int = torService?.socksPort ?: 9050
@@ -35,6 +35,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.AuthCmd
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.CloseCmd import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.CloseCmd
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.CountCmd import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.CountCmd
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.EventCmd import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.EventCmd
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebSocket import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebSocket
import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebSocketListener import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebSocketListener
@@ -312,10 +313,7 @@ class SimpleClientRelay(
if (isConnectionStarted()) { if (isConnectionStarted()) {
if (isReady) { if (isReady) {
if (filters.isNotEmpty()) { if (filters.isNotEmpty()) {
writeToSocket( writeToSocket(ReqCmd.toJson(requestId, filters))
com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
.toJson(requestId, filters),
)
afterEOSEPerSubscription[requestId] = false afterEOSEPerSubscription[requestId] = false
} }
} }