mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 12:38:33 +00:00
Add system HTTP proxy support to libsignal-net.
Co-authored-by: Cody Henthorne <cody@signal.org>
This commit is contained in:
committed by
Cody Henthorne
parent
8e880fe117
commit
74c6e76808
@@ -97,6 +97,7 @@ class InstrumentationApplicationDependencyProvider(val application: Application,
|
||||
networkInterceptors = emptyList(),
|
||||
dns = Optional.of(SignalServiceNetworkAccess.DNS),
|
||||
signalProxy = Optional.empty(),
|
||||
systemHttpProxy = Optional.empty(),
|
||||
zkGroupServerPublicParams = Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS),
|
||||
genericServerPublicParams = Base64.decode(BuildConfig.GENERIC_SERVER_PUBLIC_PARAMS),
|
||||
backupServerPublicParams = Base64.decode(BuildConfig.BACKUP_SERVER_PUBLIC_PARAMS),
|
||||
|
||||
@@ -7,6 +7,7 @@ import okhttp3.OkHttpClient
|
||||
import org.signal.core.util.billing.BillingApi
|
||||
import org.signal.core.util.concurrent.DeadlockDetector
|
||||
import org.signal.core.util.concurrent.LatestValueObservable
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.core.util.resettableLazy
|
||||
import org.signal.libsignal.net.Network
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations
|
||||
@@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.components.TypingStatusSender
|
||||
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.PendingRetryReceiptCache
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies.authWebSocket
|
||||
import org.thoughtcrime.securesms.groups.GroupsV2Authorization
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager
|
||||
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository
|
||||
@@ -378,6 +380,16 @@ object AppDependencies {
|
||||
networkModule.openConnections()
|
||||
}
|
||||
|
||||
fun onSystemHttpProxyChange(host: String?, port: Int?): Boolean {
|
||||
val currentSystemProxy = signalServiceNetworkAccess.getConfiguration().systemHttpProxy.orNull()
|
||||
return if (currentSystemProxy?.host != host || currentSystemProxy?.port != port) {
|
||||
resetNetwork()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
interface Provider {
|
||||
fun providePushServiceSocket(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): PushServiceSocket
|
||||
fun provideGroupsV2Operations(signalServiceConfiguration: SignalServiceConfiguration): GroupsV2Operations
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Application
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.ProxyInfo
|
||||
import android.os.IBinder
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.app.NotificationCompat
|
||||
@@ -86,7 +87,10 @@ class IncomingMessageObserver(private val context: Application, private val auth
|
||||
|
||||
private val lock: ReentrantLock = ReentrantLock()
|
||||
private val connectionNecessarySemaphore = Semaphore(0)
|
||||
private val networkConnectionListener = NetworkConnectionListener(context) { isNetworkUnavailable ->
|
||||
private var previousProxyInfo: ProxyInfo? = null
|
||||
private val networkConnectionListener = NetworkConnectionListener(
|
||||
context,
|
||||
{ isNetworkUnavailable ->
|
||||
lock.withLock {
|
||||
AppDependencies.libsignalNetwork.onNetworkChange()
|
||||
if (isNetworkUnavailable()) {
|
||||
@@ -95,7 +99,17 @@ class IncomingMessageObserver(private val context: Application, private val auth
|
||||
}
|
||||
connectionNecessarySemaphore.release()
|
||||
}
|
||||
},
|
||||
{ proxyInfo ->
|
||||
if (proxyInfo != previousProxyInfo) {
|
||||
val networkReset = AppDependencies.onSystemHttpProxyChange(proxyInfo?.host, proxyInfo?.port)
|
||||
if (networkReset) {
|
||||
Log.i(TAG, "System proxy configuration changed, network reset.")
|
||||
}
|
||||
}
|
||||
previousProxyInfo = proxyInfo
|
||||
}
|
||||
)
|
||||
|
||||
private val messageContentProcessor = MessageContentProcessor(context)
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.LinkProperties
|
||||
import android.net.Network
|
||||
import android.net.ProxyInfo
|
||||
import android.os.Build
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
@@ -24,7 +26,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
* API 28+ only runs on lost networks, so it provides a conditional that's always true because that is guaranteed by the call site.
|
||||
* Earlier versions use [NetworkConstraint.isMet] to query the current network state upon receiving the broadcast.
|
||||
*/
|
||||
class NetworkConnectionListener(private val context: Context, private val onNetworkLost: (() -> Boolean) -> Unit) {
|
||||
class NetworkConnectionListener(private val context: Context, private val onNetworkLost: (() -> Boolean) -> Unit, private val onProxySettingsChanged: ((ProxyInfo?) -> Unit)) {
|
||||
companion object {
|
||||
private val TAG = Log.tag(NetworkConnectionListener::class.java)
|
||||
}
|
||||
@@ -55,6 +57,12 @@ class NetworkConnectionListener(private val context: Context, private val onNetw
|
||||
Log.d(TAG, "ConnectivityManager.NetworkCallback onLost()")
|
||||
onNetworkLost { true }
|
||||
}
|
||||
|
||||
override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
|
||||
super.onLinkPropertiesChanged(network, linkProperties)
|
||||
Log.d(TAG, "ConnectivityManager.NetworkCallback onLinkPropertiesChanged()")
|
||||
onProxySettingsChanged(linkProperties.httpProxy)
|
||||
}
|
||||
}
|
||||
|
||||
private val connectionReceiver = object : BroadcastReceiver() {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.thoughtcrime.securesms.push
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
||||
import okhttp3.CipherSuite
|
||||
import okhttp3.ConnectionSpec
|
||||
@@ -20,6 +23,7 @@ import org.thoughtcrime.securesms.net.SequentialDns
|
||||
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor
|
||||
import org.thoughtcrime.securesms.net.StaticDns
|
||||
import org.whispersystems.signalservice.api.push.TrustStore
|
||||
import org.whispersystems.signalservice.internal.configuration.HttpProxy
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
|
||||
@@ -28,6 +32,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalSvr2Url
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
import android.net.Proxy as AndroidProxy
|
||||
|
||||
/**
|
||||
* Provides a [SignalServiceConfiguration] to be used with our service layer.
|
||||
@@ -133,6 +138,28 @@ class SignalServiceNetworkAccess(context: Context) {
|
||||
.build()
|
||||
|
||||
private val APP_CONNECTION_SPEC = ConnectionSpec.MODERN_TLS
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun getSystemHttpProxy(context: Context): HttpProxy? {
|
||||
return if (Build.VERSION.SDK_INT >= 23) {
|
||||
val connectivityManager = ContextCompat.getSystemService(context, ConnectivityManager::class.java) ?: return null
|
||||
|
||||
connectivityManager
|
||||
.activeNetwork
|
||||
?.let { connectivityManager.getLinkProperties(it)?.httpProxy }
|
||||
?.takeIf { !it.exclusionList.contains(BuildConfig.SIGNAL_URL.stripProtocol()) }
|
||||
?.let { proxy -> HttpProxy(proxy.host, proxy.port) }
|
||||
} else {
|
||||
val host: String? = AndroidProxy.getHost(context)
|
||||
val port: Int = AndroidProxy.getPort(context)
|
||||
|
||||
if (host != null) {
|
||||
HttpProxy(host, port)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val serviceTrustStore: TrustStore = SignalServiceTrustStore(context)
|
||||
@@ -187,6 +214,7 @@ class SignalServiceNetworkAccess(context: Context) {
|
||||
networkInterceptors = interceptors,
|
||||
dns = Optional.of(DNS),
|
||||
signalProxy = Optional.empty(),
|
||||
systemHttpProxy = Optional.empty(),
|
||||
zkGroupServerPublicParams = zkGroupServerPublicParams,
|
||||
genericServerPublicParams = genericServerPublicParams,
|
||||
backupServerPublicParams = backupServerPublicParams,
|
||||
@@ -246,6 +274,7 @@ class SignalServiceNetworkAccess(context: Context) {
|
||||
networkInterceptors = interceptors,
|
||||
dns = Optional.of(DNS),
|
||||
signalProxy = if (SignalStore.proxy.isProxyEnabled) Optional.ofNullable(SignalStore.proxy.proxy) else Optional.empty(),
|
||||
systemHttpProxy = Optional.ofNullable(getSystemHttpProxy(context)),
|
||||
zkGroupServerPublicParams = zkGroupServerPublicParams,
|
||||
genericServerPublicParams = genericServerPublicParams,
|
||||
backupServerPublicParams = backupServerPublicParams,
|
||||
@@ -316,6 +345,7 @@ class SignalServiceNetworkAccess(context: Context) {
|
||||
networkInterceptors = interceptors,
|
||||
dns = Optional.of(DNS),
|
||||
signalProxy = Optional.empty(),
|
||||
systemHttpProxy = Optional.empty(),
|
||||
zkGroupServerPublicParams = zkGroupServerPublicParams,
|
||||
genericServerPublicParams = genericServerPublicParams,
|
||||
backupServerPublicParams = backupServerPublicParams,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.whispersystems.signalservice.internal.configuration
|
||||
|
||||
/**
|
||||
* HTTP Proxy configuration from Android OS configuration.
|
||||
*/
|
||||
class HttpProxy(val host: String, val port: Int)
|
||||
@@ -17,6 +17,7 @@ data class SignalServiceConfiguration(
|
||||
val networkInterceptors: List<Interceptor>,
|
||||
val dns: Optional<Dns>,
|
||||
val signalProxy: Optional<SignalProxy>,
|
||||
val systemHttpProxy: Optional<HttpProxy>,
|
||||
val zkGroupServerPublicParams: ByteArray,
|
||||
val genericServerPublicParams: ByteArray,
|
||||
val backupServerPublicParams: ByteArray,
|
||||
|
||||
@@ -30,18 +30,29 @@ fun Network.transformAndSetRemoteConfig(remoteConfig: Map<String, Any>) {
|
||||
* Helper method to apply settings from the SignalServiceConfiguration.
|
||||
*/
|
||||
fun Network.applyConfiguration(config: SignalServiceConfiguration) {
|
||||
val proxy = config.signalProxy.orNull()
|
||||
val signalProxy = config.signalProxy.orNull()
|
||||
val systemHttpProxy = config.systemHttpProxy.orNull()
|
||||
|
||||
if (proxy == null) {
|
||||
this.clearProxy()
|
||||
} else {
|
||||
when {
|
||||
(signalProxy != null) -> {
|
||||
try {
|
||||
this.setProxy(proxy.host, proxy.port)
|
||||
this.setProxy(signalProxy.host, signalProxy.port)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Invalid proxy configuration set! Failing connections until changed.")
|
||||
this.setInvalidProxy()
|
||||
}
|
||||
}
|
||||
(systemHttpProxy != null) -> {
|
||||
try {
|
||||
this.setProxy("http", systemHttpProxy.host, systemHttpProxy.port, "", "")
|
||||
} catch (e: IOException) {
|
||||
// The Android settings screen where this is set explicitly calls out that apps are allowed to
|
||||
// ignore the HTTP Proxy setting, so if using the specified proxy would cause us to break, let's
|
||||
// try just ignoring it and seeing if that still lets us connect.
|
||||
Log.w(TAG, "Failed to set system HTTP proxy, ignoring and continuing...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setCensorshipCircumventionEnabled(config.censored)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user