mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-29 22:24:29 +02:00
Added settings for enabling/disabling remote sync features. Fixed device pairing success showing too early.
This commit is contained in:
parent
4e195dfbc3
commit
b460f9915d
@ -936,6 +936,15 @@ class Settings : FragmentedStorageFileJson() {
|
|||||||
|
|
||||||
@FormField(R.string.connect_last, FieldForm.TOGGLE, R.string.connect_last_description, 3)
|
@FormField(R.string.connect_last, FieldForm.TOGGLE, R.string.connect_last_description, 3)
|
||||||
var connectLast: Boolean = true;
|
var connectLast: Boolean = true;
|
||||||
|
|
||||||
|
@FormField(R.string.discover_through_relay, FieldForm.TOGGLE, R.string.discover_through_relay_description, 3)
|
||||||
|
var discoverThroughRelay: Boolean = true;
|
||||||
|
|
||||||
|
@FormField(R.string.pair_through_relay, FieldForm.TOGGLE, R.string.pair_through_relay_description, 3)
|
||||||
|
var pairThroughRelay: Boolean = true;
|
||||||
|
|
||||||
|
@FormField(R.string.connect_through_relay, FieldForm.TOGGLE, R.string.connect_through_relay_description, 3)
|
||||||
|
var connectThroughRelay: Boolean = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FormField(R.string.info, FieldForm.GROUP, -1, 21)
|
@FormField(R.string.info, FieldForm.GROUP, -1, 21)
|
||||||
|
@ -32,11 +32,11 @@ import com.futo.platformplayer.sync.internal.ChannelSocket
|
|||||||
import com.futo.platformplayer.sync.internal.GJSyncOpcodes
|
import com.futo.platformplayer.sync.internal.GJSyncOpcodes
|
||||||
import com.futo.platformplayer.sync.internal.IAuthorizable
|
import com.futo.platformplayer.sync.internal.IAuthorizable
|
||||||
import com.futo.platformplayer.sync.internal.IChannel
|
import com.futo.platformplayer.sync.internal.IChannel
|
||||||
|
import com.futo.platformplayer.sync.internal.LinkType
|
||||||
import com.futo.platformplayer.sync.internal.Opcode
|
import com.futo.platformplayer.sync.internal.Opcode
|
||||||
import com.futo.platformplayer.sync.internal.SyncDeviceInfo
|
import com.futo.platformplayer.sync.internal.SyncDeviceInfo
|
||||||
import com.futo.platformplayer.sync.internal.SyncKeyPair
|
import com.futo.platformplayer.sync.internal.SyncKeyPair
|
||||||
import com.futo.platformplayer.sync.internal.SyncSession
|
import com.futo.platformplayer.sync.internal.SyncSession
|
||||||
import com.futo.platformplayer.sync.internal.SyncSession.Companion
|
|
||||||
import com.futo.platformplayer.sync.internal.SyncSocketSession
|
import com.futo.platformplayer.sync.internal.SyncSocketSession
|
||||||
import com.futo.platformplayer.sync.models.SendToDevicePackage
|
import com.futo.platformplayer.sync.models.SendToDevicePackage
|
||||||
import com.futo.platformplayer.sync.models.SyncPlaylistsPackage
|
import com.futo.platformplayer.sync.models.SyncPlaylistsPackage
|
||||||
@ -52,13 +52,11 @@ import kotlinx.coroutines.withContext
|
|||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.lang.Thread.sleep
|
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.channels.Channel
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
@ -88,6 +86,7 @@ class StateSync {
|
|||||||
val pairingCode: String? get() = _pairingCode
|
val pairingCode: String? get() = _pairingCode
|
||||||
private var _relaySession: SyncSocketSession? = null
|
private var _relaySession: SyncSocketSession? = null
|
||||||
private var _threadRelay: Thread? = null
|
private var _threadRelay: Thread? = null
|
||||||
|
private val _remotePendingStatusUpdate = mutableMapOf<String, (complete: Boolean?, message: String) -> Unit>()
|
||||||
|
|
||||||
var keyPair: DHState? = null
|
var keyPair: DHState? = null
|
||||||
var publicKey: String? = null
|
var publicKey: String? = null
|
||||||
@ -157,10 +156,7 @@ class StateSync {
|
|||||||
|
|
||||||
while (_started) {
|
while (_started) {
|
||||||
val socket = serverSocket.accept()
|
val socket = serverSocket.accept()
|
||||||
val session = createSocketSession(socket, true) { session ->
|
val session = createSocketSession(socket, true)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
session.startAsResponder()
|
session.startAsResponder()
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
@ -219,137 +215,124 @@ class StateSync {
|
|||||||
}.apply { start() }
|
}.apply { start() }
|
||||||
}
|
}
|
||||||
|
|
||||||
_threadRelay = Thread {
|
if (Settings.instance.synchronization.discoverThroughRelay) {
|
||||||
while (_started) {
|
_threadRelay = Thread {
|
||||||
try {
|
while (_started) {
|
||||||
Log.i(TAG, "Starting relay session...")
|
try {
|
||||||
|
Log.i(TAG, "Starting relay session...")
|
||||||
|
|
||||||
var socketClosed = false;
|
var socketClosed = false;
|
||||||
val socket = Socket(RELAY_SERVER, 9000)
|
val socket = Socket(RELAY_SERVER, 9000)
|
||||||
_relaySession = SyncSocketSession(
|
_relaySession = SyncSocketSession(
|
||||||
(socket.remoteSocketAddress as InetSocketAddress).address.hostAddress!!,
|
(socket.remoteSocketAddress as InetSocketAddress).address.hostAddress!!,
|
||||||
keyPair!!,
|
keyPair!!,
|
||||||
LittleEndianDataInputStream(socket.getInputStream()),
|
LittleEndianDataInputStream(socket.getInputStream()),
|
||||||
LittleEndianDataOutputStream(socket.getOutputStream()),
|
LittleEndianDataOutputStream(socket.getOutputStream()),
|
||||||
isHandshakeAllowed = { _, pk, pairingCode ->
|
isHandshakeAllowed = { linkType, syncSocketSession, publicKey, pairingCode -> isHandshakeAllowed(linkType, syncSocketSession, publicKey, pairingCode) },
|
||||||
Log.v(TAG, "Check if handshake allowed from '$pk'.")
|
onNewChannel = { _, c ->
|
||||||
if (pk == RELAY_PUBLIC_KEY)
|
val remotePublicKey = c.remotePublicKey
|
||||||
return@SyncSocketSession true
|
if (remotePublicKey == null) {
|
||||||
|
Log.e(TAG, "Remote public key should never be null in onNewChannel.")
|
||||||
synchronized(_authorizedDevices) {
|
return@SyncSocketSession
|
||||||
if (_authorizedDevices.values.contains(pk))
|
|
||||||
return@SyncSocketSession true
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.v(TAG, "Check if handshake allowed with pairing code '$pairingCode' with active pairing code '$_pairingCode'.")
|
|
||||||
if (_pairingCode == null || pairingCode.isNullOrEmpty())
|
|
||||||
return@SyncSocketSession false
|
|
||||||
|
|
||||||
_pairingCode == pairingCode
|
|
||||||
},
|
|
||||||
onNewChannel = { _, c ->
|
|
||||||
val remotePublicKey = c.remotePublicKey
|
|
||||||
if (remotePublicKey == null) {
|
|
||||||
Log.e(TAG, "Remote public key should never be null in onNewChannel.")
|
|
||||||
return@SyncSocketSession
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "New channel established from relay (pk: '$remotePublicKey').")
|
|
||||||
|
|
||||||
var session: SyncSession?
|
|
||||||
synchronized(_sessions) {
|
|
||||||
session = _sessions[remotePublicKey]
|
|
||||||
if (session == null) {
|
|
||||||
val remoteDeviceName = synchronized(_nameStorage) {
|
|
||||||
_nameStorage.get(remotePublicKey)
|
|
||||||
}
|
|
||||||
session = createNewSyncSession(remotePublicKey, remoteDeviceName) { }
|
|
||||||
_sessions[remotePublicKey] = session!!
|
|
||||||
}
|
}
|
||||||
session!!.addChannel(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.setDataHandler { _, channel, opcode, subOpcode, data ->
|
Log.i(TAG, "New channel established from relay (pk: '$remotePublicKey').")
|
||||||
session?.handlePacket(opcode, subOpcode, data)
|
|
||||||
}
|
var session: SyncSession?
|
||||||
c.setCloseHandler { channel ->
|
synchronized(_sessions) {
|
||||||
session?.removeChannel(channel)
|
session = _sessions[remotePublicKey]
|
||||||
}
|
if (session == null) {
|
||||||
},
|
val remoteDeviceName = synchronized(_nameStorage) {
|
||||||
onChannelEstablished = { _, channel, isResponder ->
|
_nameStorage.get(remotePublicKey)
|
||||||
handleAuthorization(channel, isResponder)
|
|
||||||
},
|
|
||||||
onClose = { socketClosed = true },
|
|
||||||
onHandshakeComplete = { relaySession ->
|
|
||||||
Thread {
|
|
||||||
try {
|
|
||||||
while (_started && !socketClosed) {
|
|
||||||
val unconnectedAuthorizedDevices = synchronized(_authorizedDevices) {
|
|
||||||
_authorizedDevices.values.filter { !isConnected(it) }.toTypedArray()
|
|
||||||
}
|
}
|
||||||
|
session = createNewSyncSession(remotePublicKey, remoteDeviceName)
|
||||||
|
_sessions[remotePublicKey] = session!!
|
||||||
|
}
|
||||||
|
session!!.addChannel(c)
|
||||||
|
}
|
||||||
|
|
||||||
relaySession.publishConnectionInformation(unconnectedAuthorizedDevices, PORT, true, false, false, true)
|
c.setDataHandler { _, channel, opcode, subOpcode, data ->
|
||||||
|
session?.handlePacket(opcode, subOpcode, data)
|
||||||
|
}
|
||||||
|
c.setCloseHandler { channel ->
|
||||||
|
session?.removeChannel(channel)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChannelEstablished = { _, channel, isResponder ->
|
||||||
|
handleAuthorization(channel, isResponder)
|
||||||
|
},
|
||||||
|
onClose = { socketClosed = true },
|
||||||
|
onHandshakeComplete = { relaySession ->
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
while (_started && !socketClosed) {
|
||||||
|
val unconnectedAuthorizedDevices = synchronized(_authorizedDevices) {
|
||||||
|
_authorizedDevices.values.filter { !isConnected(it) }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
val connectionInfos = runBlocking { relaySession.requestBulkConnectionInfo(unconnectedAuthorizedDevices) }
|
relaySession.publishConnectionInformation(unconnectedAuthorizedDevices, PORT, Settings.instance.synchronization.discoverThroughRelay, false, false, Settings.instance.synchronization.discoverThroughRelay && Settings.instance.synchronization.connectThroughRelay)
|
||||||
|
|
||||||
for ((targetKey, connectionInfo) in connectionInfos) {
|
val connectionInfos = runBlocking { relaySession.requestBulkConnectionInfo(unconnectedAuthorizedDevices) }
|
||||||
val potentialLocalAddresses = connectionInfo.ipv4Addresses.union(connectionInfo.ipv6Addresses)
|
|
||||||
.filter { it != connectionInfo.remoteIp }
|
for ((targetKey, connectionInfo) in connectionInfos) {
|
||||||
if (connectionInfo.allowLocalDirect) {
|
val potentialLocalAddresses = connectionInfo.ipv4Addresses.union(connectionInfo.ipv6Addresses)
|
||||||
Thread {
|
.filter { it != connectionInfo.remoteIp }
|
||||||
|
if (connectionInfo.allowLocalDirect) {
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
Log.v(TAG, "Attempting to connect directly, locally to '$targetKey'.")
|
||||||
|
connect(potentialLocalAddresses.map { it }.toTypedArray(), PORT, targetKey, null)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.e(TAG, "Failed to start direct connection using connection info with $targetKey.", e)
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectionInfo.allowRemoteDirect) {
|
||||||
|
// TODO: Implement direct remote connection if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectionInfo.allowRemoteHolePunched) {
|
||||||
|
// TODO: Implement hole punching if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectionInfo.allowRemoteRelayed && Settings.instance.synchronization.connectThroughRelay) {
|
||||||
try {
|
try {
|
||||||
Log.v(TAG, "Attempting to connect directly, locally to '$targetKey'.")
|
Log.v(TAG, "Attempting relayed connection with '$targetKey'.")
|
||||||
connect(potentialLocalAddresses.map { it }.toTypedArray(), PORT, targetKey, null)
|
runBlocking { relaySession.startRelayedChannel(targetKey, null) }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Log.e(TAG, "Failed to start direct connection using connection info with $targetKey.", e)
|
Log.e(TAG, "Failed to start relayed channel with $targetKey.", e)
|
||||||
}
|
}
|
||||||
}.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionInfo.allowRemoteDirect) {
|
|
||||||
// TODO: Implement direct remote connection if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionInfo.allowRemoteHolePunched) {
|
|
||||||
// TODO: Implement hole punching if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionInfo.allowRemoteProxied) {
|
|
||||||
try {
|
|
||||||
Log.v(TAG, "Attempting relayed connection with '$targetKey'.")
|
|
||||||
runBlocking { relaySession.startRelayedChannel(targetKey, null) }
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Log.e(TAG, "Failed to start relayed channel with $targetKey.", e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread.sleep(15000)
|
||||||
}
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
Thread.sleep(15000)
|
Log.e(TAG, "Unhandled exception in relay session.", e)
|
||||||
|
relaySession.stop()
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
}.start()
|
||||||
Log.e(TAG, "Unhandled exception in relay session.", e)
|
}
|
||||||
relaySession.stop()
|
)
|
||||||
}
|
|
||||||
}.start()
|
_relaySession!!.authorizable = object : IAuthorizable {
|
||||||
|
override val isAuthorized: Boolean get() = true
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
_relaySession!!.authorizable = object : IAuthorizable {
|
_relaySession!!.startAsInitiator(RELAY_PUBLIC_KEY, null)
|
||||||
override val isAuthorized: Boolean get() = true
|
|
||||||
|
Log.i(TAG, "Started relay session.")
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.e(TAG, "Relay session failed.", e)
|
||||||
|
Thread.sleep(5000)
|
||||||
|
} finally {
|
||||||
|
_relaySession?.stop()
|
||||||
|
_relaySession = null
|
||||||
}
|
}
|
||||||
|
|
||||||
_relaySession!!.startAsInitiator(RELAY_PUBLIC_KEY, null)
|
|
||||||
|
|
||||||
Log.i(TAG, "Started relay session.")
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Log.e(TAG, "Relay session failed.", e)
|
|
||||||
Thread.sleep(5000)
|
|
||||||
} finally {
|
|
||||||
_relaySession?.stop()
|
|
||||||
_relaySession = null
|
|
||||||
}
|
}
|
||||||
}
|
}.apply { start() }
|
||||||
}.apply { start() }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDeviceName(): String {
|
private fun getDeviceName(): String {
|
||||||
@ -680,7 +663,19 @@ class StateSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNewSyncSession(remotePublicKey: String, remoteDeviceName: String?, onAuthorized: ((SyncSession) -> Unit)?): SyncSession {
|
private fun onAuthorized(remotePublicKey: String) {
|
||||||
|
synchronized(_remotePendingStatusUpdate) {
|
||||||
|
_remotePendingStatusUpdate.remove(remotePublicKey)?.invoke(true, "Authorized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onUnuthorized(remotePublicKey: String) {
|
||||||
|
synchronized(_remotePendingStatusUpdate) {
|
||||||
|
_remotePendingStatusUpdate.remove(remotePublicKey)?.invoke(false, "Unauthorized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNewSyncSession(remotePublicKey: String, remoteDeviceName: String?): SyncSession {
|
||||||
return SyncSession(
|
return SyncSession(
|
||||||
remotePublicKey,
|
remotePublicKey,
|
||||||
onAuthorized = { it, isNewlyAuthorized, isNewSession ->
|
onAuthorized = { it, isNewlyAuthorized, isNewSession ->
|
||||||
@ -694,8 +689,8 @@ class StateSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.i(TAG, "${remotePublicKey} authorized (name: ${it.displayName})")
|
Logger.i(TAG, "$remotePublicKey authorized (name: ${it.displayName})")
|
||||||
onAuthorized?.invoke(it)
|
onAuthorized(remotePublicKey)
|
||||||
_authorizedDevices.addDistinct(remotePublicKey)
|
_authorizedDevices.addDistinct(remotePublicKey)
|
||||||
_authorizedDevices.save()
|
_authorizedDevices.save()
|
||||||
deviceUpdatedOrAdded.emit(it.remotePublicKey, it)
|
deviceUpdatedOrAdded.emit(it.remotePublicKey, it)
|
||||||
@ -705,13 +700,16 @@ class StateSync {
|
|||||||
onUnauthorized = {
|
onUnauthorized = {
|
||||||
unauthorize(remotePublicKey)
|
unauthorize(remotePublicKey)
|
||||||
|
|
||||||
|
Logger.i(TAG, "$remotePublicKey unauthorized (name: ${it.displayName})")
|
||||||
|
onUnuthorized(remotePublicKey)
|
||||||
|
|
||||||
synchronized(_sessions) {
|
synchronized(_sessions) {
|
||||||
it.close()
|
it.close()
|
||||||
_sessions.remove(remotePublicKey)
|
_sessions.remove(remotePublicKey)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onConnectedChanged = { it, connected ->
|
onConnectedChanged = { it, connected ->
|
||||||
Logger.i(TAG, "$remotePublicKey connected: " + connected)
|
Logger.i(TAG, "$remotePublicKey connected: $connected")
|
||||||
deviceUpdatedOrAdded.emit(it.remotePublicKey, it)
|
deviceUpdatedOrAdded.emit(it.remotePublicKey, it)
|
||||||
},
|
},
|
||||||
onClose = {
|
onClose = {
|
||||||
@ -723,6 +721,10 @@ class StateSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deviceRemoved.emit(it.remotePublicKey)
|
deviceRemoved.emit(it.remotePublicKey)
|
||||||
|
|
||||||
|
synchronized(_remotePendingStatusUpdate) {
|
||||||
|
_remotePendingStatusUpdate.remove(remotePublicKey)?.invoke(false, "Connection closed")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dataHandler = { it, opcode, subOpcode, data ->
|
dataHandler = { it, opcode, subOpcode, data ->
|
||||||
handleData(it, opcode, subOpcode, data)
|
handleData(it, opcode, subOpcode, data)
|
||||||
@ -731,7 +733,30 @@ class StateSync {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSocketSession(socket: Socket, isResponder: Boolean, onAuthorized: (session: SyncSession) -> Unit): SyncSocketSession {
|
private fun isHandshakeAllowed(linkType: LinkType, syncSocketSession: SyncSocketSession, publicKey: String, pairingCode: String?): Boolean {
|
||||||
|
Log.v(TAG, "Check if handshake allowed from '$publicKey'.")
|
||||||
|
if (publicKey == RELAY_PUBLIC_KEY)
|
||||||
|
return true
|
||||||
|
|
||||||
|
synchronized(_authorizedDevices) {
|
||||||
|
if (_authorizedDevices.values.contains(publicKey)) {
|
||||||
|
if (linkType == LinkType.Relayed && !Settings.instance.synchronization.connectThroughRelay)
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v(TAG, "Check if handshake allowed with pairing code '$pairingCode' with active pairing code '$_pairingCode'.")
|
||||||
|
if (_pairingCode == null || pairingCode.isNullOrEmpty())
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (linkType == LinkType.Relayed && !Settings.instance.synchronization.pairThroughRelay)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return _pairingCode == pairingCode
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSocketSession(socket: Socket, isResponder: Boolean): SyncSocketSession {
|
||||||
var session: SyncSession? = null
|
var session: SyncSession? = null
|
||||||
var channelSocket: ChannelSocket? = null
|
var channelSocket: ChannelSocket? = null
|
||||||
return SyncSocketSession(
|
return SyncSocketSession(
|
||||||
@ -743,21 +768,7 @@ class StateSync {
|
|||||||
if (channelSocket != null)
|
if (channelSocket != null)
|
||||||
session?.removeChannel(channelSocket!!)
|
session?.removeChannel(channelSocket!!)
|
||||||
},
|
},
|
||||||
isHandshakeAllowed = { _, pk, pairingCode ->
|
isHandshakeAllowed = { linkType, syncSocketSession, publicKey, pairingCode -> isHandshakeAllowed(linkType, syncSocketSession, publicKey, pairingCode) },
|
||||||
Logger.v(TAG, "Check if handshake allowed from '${pk}'.")
|
|
||||||
|
|
||||||
synchronized (_authorizedDevices)
|
|
||||||
{
|
|
||||||
if (_authorizedDevices.values.contains(pk))
|
|
||||||
return@SyncSocketSession true
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.v(TAG, "Check if handshake allowed with pairing code '${pairingCode}' with active pairing code '${_pairingCode}'.");
|
|
||||||
if (_pairingCode == null || pairingCode.isNullOrEmpty())
|
|
||||||
return@SyncSocketSession false
|
|
||||||
|
|
||||||
return@SyncSocketSession _pairingCode == pairingCode
|
|
||||||
},
|
|
||||||
onHandshakeComplete = { s ->
|
onHandshakeComplete = { s ->
|
||||||
val remotePublicKey = s.remotePublicKey
|
val remotePublicKey = s.remotePublicKey
|
||||||
if (remotePublicKey == null) {
|
if (remotePublicKey == null) {
|
||||||
@ -780,7 +791,7 @@ class StateSync {
|
|||||||
_lastAddressStorage.setAndSave(remotePublicKey, s.remoteAddress)
|
_lastAddressStorage.setAndSave(remotePublicKey, s.remoteAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
session = createNewSyncSession(remotePublicKey, remoteDeviceName, onAuthorized)
|
session = createNewSyncSession(remotePublicKey, remoteDeviceName)
|
||||||
_sessions[remotePublicKey] = session!!
|
_sessions[remotePublicKey] = session!!
|
||||||
}
|
}
|
||||||
session!!.addChannel(channelSocket!!)
|
session!!.addChannel(channelSocket!!)
|
||||||
@ -912,15 +923,19 @@ class StateSync {
|
|||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.e(TAG, "Failed to connect directly", e)
|
Logger.e(TAG, "Failed to connect directly", e)
|
||||||
val relaySession = _relaySession
|
val relaySession = _relaySession
|
||||||
if (relaySession != null) {
|
if (relaySession != null && Settings.instance.synchronization.pairThroughRelay) {
|
||||||
onStatusUpdate?.invoke(null, "Connecting via relay...")
|
onStatusUpdate?.invoke(null, "Connecting via relay...")
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
|
if (onStatusUpdate != null) {
|
||||||
|
synchronized(_remotePendingStatusUpdate) {
|
||||||
|
_remotePendingStatusUpdate[deviceInfo.publicKey] = onStatusUpdate
|
||||||
|
}
|
||||||
|
}
|
||||||
relaySession.startRelayedChannel(deviceInfo.publicKey, deviceInfo.pairingCode)
|
relaySession.startRelayedChannel(deviceInfo.publicKey, deviceInfo.pairingCode)
|
||||||
onStatusUpdate?.invoke(true, "Connected")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Failed to connect.")
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -930,8 +945,11 @@ class StateSync {
|
|||||||
val socket = getConnectedSocket(addresses.map { InetAddress.getByName(it) }, port) ?: throw Exception("Failed to connect")
|
val socket = getConnectedSocket(addresses.map { InetAddress.getByName(it) }, port) ?: throw Exception("Failed to connect")
|
||||||
onStatusUpdate?.invoke(null, "Handshaking...")
|
onStatusUpdate?.invoke(null, "Handshaking...")
|
||||||
|
|
||||||
val session = createSocketSession(socket, false) { s ->
|
val session = createSocketSession(socket, false)
|
||||||
onStatusUpdate?.invoke(true, "Authorized")
|
if (onStatusUpdate != null) {
|
||||||
|
synchronized(_remotePendingStatusUpdate) {
|
||||||
|
_remotePendingStatusUpdate[publicKey] = onStatusUpdate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.startAsInitiator(publicKey, pairingCode)
|
session.startAsInitiator(publicKey, pairingCode)
|
||||||
|
@ -38,7 +38,7 @@ class SyncSocketSession {
|
|||||||
private val _onHandshakeComplete: ((session: SyncSocketSession) -> Unit)?
|
private val _onHandshakeComplete: ((session: SyncSocketSession) -> Unit)?
|
||||||
private val _onNewChannel: ((session: SyncSocketSession, channel: ChannelRelayed) -> Unit)?
|
private val _onNewChannel: ((session: SyncSocketSession, channel: ChannelRelayed) -> Unit)?
|
||||||
private val _onChannelEstablished: ((session: SyncSocketSession, channel: ChannelRelayed, isResponder: Boolean) -> Unit)?
|
private val _onChannelEstablished: ((session: SyncSocketSession, channel: ChannelRelayed, isResponder: Boolean) -> Unit)?
|
||||||
private val _isHandshakeAllowed: ((session: SyncSocketSession, remotePublicKey: String, pairingCode: String?) -> Boolean)?
|
private val _isHandshakeAllowed: ((linkType: LinkType, session: SyncSocketSession, remotePublicKey: String, pairingCode: String?) -> Boolean)?
|
||||||
private var _cipherStatePair: CipherStatePair? = null
|
private var _cipherStatePair: CipherStatePair? = null
|
||||||
private var _remotePublicKey: String? = null
|
private var _remotePublicKey: String? = null
|
||||||
val remotePublicKey: String? get() = _remotePublicKey
|
val remotePublicKey: String? get() = _remotePublicKey
|
||||||
@ -74,7 +74,7 @@ class SyncSocketSession {
|
|||||||
val allowLocalDirect: Boolean,
|
val allowLocalDirect: Boolean,
|
||||||
val allowRemoteDirect: Boolean,
|
val allowRemoteDirect: Boolean,
|
||||||
val allowRemoteHolePunched: Boolean,
|
val allowRemoteHolePunched: Boolean,
|
||||||
val allowRemoteProxied: Boolean
|
val allowRemoteRelayed: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -87,7 +87,7 @@ class SyncSocketSession {
|
|||||||
onData: ((session: SyncSocketSession, opcode: UByte, subOpcode: UByte, data: ByteBuffer) -> Unit)? = null,
|
onData: ((session: SyncSocketSession, opcode: UByte, subOpcode: UByte, data: ByteBuffer) -> Unit)? = null,
|
||||||
onNewChannel: ((session: SyncSocketSession, channel: ChannelRelayed) -> Unit)? = null,
|
onNewChannel: ((session: SyncSocketSession, channel: ChannelRelayed) -> Unit)? = null,
|
||||||
onChannelEstablished: ((session: SyncSocketSession, channel: ChannelRelayed, isResponder: Boolean) -> Unit)? = null,
|
onChannelEstablished: ((session: SyncSocketSession, channel: ChannelRelayed, isResponder: Boolean) -> Unit)? = null,
|
||||||
isHandshakeAllowed: ((session: SyncSocketSession, remotePublicKey: String, pairingCode: String?) -> Boolean)? = null
|
isHandshakeAllowed: ((linkType: LinkType, session: SyncSocketSession, remotePublicKey: String, pairingCode: String?) -> Boolean)? = null
|
||||||
) {
|
) {
|
||||||
_inputStream = inputStream
|
_inputStream = inputStream
|
||||||
_outputStream = outputStream
|
_outputStream = outputStream
|
||||||
@ -278,7 +278,7 @@ class SyncSocketSession {
|
|||||||
responder.remotePublicKey.getPublicKey(remoteKeyBytes, 0)
|
responder.remotePublicKey.getPublicKey(remoteKeyBytes, 0)
|
||||||
_remotePublicKey = Base64.getEncoder().encodeToString(remoteKeyBytes)
|
_remotePublicKey = Base64.getEncoder().encodeToString(remoteKeyBytes)
|
||||||
|
|
||||||
return (_remotePublicKey != _localPublicKey && (_isHandshakeAllowed?.invoke(this, _remotePublicKey!!, pairingCode) ?: true)).also {
|
return (_remotePublicKey != _localPublicKey && (_isHandshakeAllowed?.invoke(LinkType.Direct, this, _remotePublicKey!!, pairingCode) ?: true)).also {
|
||||||
if (!it) stop()
|
if (!it) stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,7 +420,7 @@ class SyncSocketSession {
|
|||||||
val length = pairingProtocol.readMessage(pairingMessage, 0, pairingMessageLength, plaintext, 0)
|
val length = pairingProtocol.readMessage(pairingMessage, 0, pairingMessageLength, plaintext, 0)
|
||||||
String(plaintext, 0, length, Charsets.UTF_8)
|
String(plaintext, 0, length, Charsets.UTF_8)
|
||||||
} else null
|
} else null
|
||||||
val isAllowed = publicKey != _localPublicKey && (_isHandshakeAllowed?.invoke(this, publicKey, pairingCode) ?: true)
|
val isAllowed = publicKey != _localPublicKey && (_isHandshakeAllowed?.invoke(LinkType.Relayed, this, publicKey, pairingCode) ?: true)
|
||||||
if (!isAllowed) {
|
if (!isAllowed) {
|
||||||
val rp = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN)
|
val rp = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN)
|
||||||
rp.putInt(2) // Status code for not allowed
|
rp.putInt(2) // Status code for not allowed
|
||||||
@ -649,8 +649,8 @@ class SyncSocketSession {
|
|||||||
val allowLocalDirect = info.get() != 0.toByte()
|
val allowLocalDirect = info.get() != 0.toByte()
|
||||||
val allowRemoteDirect = info.get() != 0.toByte()
|
val allowRemoteDirect = info.get() != 0.toByte()
|
||||||
val allowRemoteHolePunched = info.get() != 0.toByte()
|
val allowRemoteHolePunched = info.get() != 0.toByte()
|
||||||
val allowRemoteProxied = info.get() != 0.toByte()
|
val allowRemoteRelayed = info.get() != 0.toByte()
|
||||||
return ConnectionInfo(port, name, remoteIp, ipv4Addresses, ipv6Addresses, allowLocalDirect, allowRemoteDirect, allowRemoteHolePunched, allowRemoteProxied)
|
return ConnectionInfo(port, name, remoteIp, ipv4Addresses, ipv6Addresses, allowLocalDirect, allowRemoteDirect, allowRemoteHolePunched, allowRemoteRelayed)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleNotify(subOpcode: UByte, data: ByteBuffer, sourceChannel: ChannelRelayed?) {
|
private fun handleNotify(subOpcode: UByte, data: ByteBuffer, sourceChannel: ChannelRelayed?) {
|
||||||
@ -920,7 +920,7 @@ class SyncSocketSession {
|
|||||||
allowLocalDirect: Boolean,
|
allowLocalDirect: Boolean,
|
||||||
allowRemoteDirect: Boolean,
|
allowRemoteDirect: Boolean,
|
||||||
allowRemoteHolePunched: Boolean,
|
allowRemoteHolePunched: Boolean,
|
||||||
allowRemoteProxied: Boolean
|
allowRemoteRelayed: Boolean
|
||||||
) {
|
) {
|
||||||
if (authorizedKeys.size > 255) throw IllegalArgumentException("Number of authorized keys exceeds 255")
|
if (authorizedKeys.size > 255) throw IllegalArgumentException("Number of authorized keys exceeds 255")
|
||||||
|
|
||||||
@ -960,7 +960,7 @@ class SyncSocketSession {
|
|||||||
data.put(if (allowLocalDirect) 1 else 0)
|
data.put(if (allowLocalDirect) 1 else 0)
|
||||||
data.put(if (allowRemoteDirect) 1 else 0)
|
data.put(if (allowRemoteDirect) 1 else 0)
|
||||||
data.put(if (allowRemoteHolePunched) 1 else 0)
|
data.put(if (allowRemoteHolePunched) 1 else 0)
|
||||||
data.put(if (allowRemoteProxied) 1 else 0)
|
data.put(if (allowRemoteRelayed) 1 else 0)
|
||||||
|
|
||||||
val handshakeSize = 48 // Noise handshake size for N pattern
|
val handshakeSize = 48 // Noise handshake size for N pattern
|
||||||
|
|
||||||
|
@ -372,6 +372,12 @@
|
|||||||
<string name="connect_discovered_description">Allow device to search for and initiate connection with known paired devices</string>
|
<string name="connect_discovered_description">Allow device to search for and initiate connection with known paired devices</string>
|
||||||
<string name="connect_last">Try connect last</string>
|
<string name="connect_last">Try connect last</string>
|
||||||
<string name="connect_last_description">Allow device to automatically connect to last known</string>
|
<string name="connect_last_description">Allow device to automatically connect to last known</string>
|
||||||
|
<string name="discover_through_relay">Discover through relay</string>
|
||||||
|
<string name="discover_through_relay_description">Allow paired devices to be discovered and connected to through the relay</string>
|
||||||
|
<string name="pair_through_relay">Pair through relay</string>
|
||||||
|
<string name="pair_through_relay_description">Allow devices to be paired through the relay</string>
|
||||||
|
<string name="connect_through_relay">Connection through relay</string>
|
||||||
|
<string name="connect_through_relay_description">Allow devices to be connected to through the relay</string>
|
||||||
<string name="gesture_controls">Gesture controls</string>
|
<string name="gesture_controls">Gesture controls</string>
|
||||||
<string name="volume_slider">Volume slider</string>
|
<string name="volume_slider">Volume slider</string>
|
||||||
<string name="volume_slider_descr">Enable slide gesture to change volume</string>
|
<string name="volume_slider_descr">Enable slide gesture to change volume</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user