Implemented session id.

This commit is contained in:
Koen J 2024-11-22 19:02:07 +01:00
parent 196e55899e
commit f32498a444
4 changed files with 47 additions and 8 deletions

View File

@ -32,6 +32,7 @@ import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.* import java.util.*
import java.util.concurrent.ThreadLocalRandom import java.util.concurrent.ThreadLocalRandom
@ -233,3 +234,9 @@ fun String.decodeUnicode(): String {
} }
return sb.toString() return sb.toString()
} }
fun ByteBuffer.toUtf8String(): String {
val remainingBytes = ByteArray(remaining())
get(remainingBytes)
return String(remainingBytes, Charsets.UTF_8)
}

View File

@ -284,12 +284,16 @@ class StateSync {
return@SyncSocketSession return@SyncSocketSession
} }
Logger.i(TAG, "Handshake complete with ${s.remotePublicKey}") Logger.i(TAG, "Handshake complete with (LocalPublicKey = ${s.localPublicKey}, RemotePublicKey = ${s.remotePublicKey})")
synchronized(_sessions) { synchronized(_sessions) {
session = _sessions[s.remotePublicKey] session = _sessions[s.remotePublicKey]
if (session == null) { if (session == null) {
session = SyncSession(remotePublicKey, onAuthorized = { session = SyncSession(remotePublicKey, onAuthorized = { it, isNewlyAuthorized, isNewSession ->
if (!isNewSession) {
return@SyncSession
}
Logger.i(TAG, "${s.remotePublicKey} authorized") Logger.i(TAG, "${s.remotePublicKey} authorized")
synchronized(_lastAddressStorage) { synchronized(_lastAddressStorage) {
_lastAddressStorage.setAndSave(remotePublicKey, s.remoteAddress) _lastAddressStorage.setAndSave(remotePublicKey, s.remoteAddress)
@ -358,6 +362,16 @@ class StateSync {
} }
}) })
} }
} else {
val publicKey = session!!.remotePublicKey
session!!.unauthorize(s)
session!!.close()
synchronized(_sessions) {
_sessions.remove(publicKey)
}
Logger.i(TAG, "Connection unauthorized for ${remotePublicKey} because not authorized and not on pairing activity to ask")
} }
} else { } else {
//Responder does not need to check because already approved //Responder does not need to check because already approved

View File

@ -21,6 +21,7 @@ import com.futo.platformplayer.sync.models.SendToDevicePackage
import com.futo.platformplayer.sync.models.SyncPlaylistsPackage import com.futo.platformplayer.sync.models.SyncPlaylistsPackage
import com.futo.platformplayer.sync.models.SyncSubscriptionGroupsPackage import com.futo.platformplayer.sync.models.SyncSubscriptionGroupsPackage
import com.futo.platformplayer.sync.models.SyncSubscriptionsPackage import com.futo.platformplayer.sync.models.SyncSubscriptionsPackage
import com.futo.platformplayer.toUtf8String
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
@ -30,6 +31,7 @@ import java.nio.ByteBuffer
import java.time.Instant import java.time.Instant
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.ZoneOffset import java.time.ZoneOffset
import java.util.UUID
interface IAuthorizable { interface IAuthorizable {
val isAuthorized: Boolean val isAuthorized: Boolean
@ -39,13 +41,16 @@ class SyncSession : IAuthorizable {
private val _socketSessions: MutableList<SyncSocketSession> = mutableListOf() private val _socketSessions: MutableList<SyncSocketSession> = mutableListOf()
private var _authorized: Boolean = false private var _authorized: Boolean = false
private var _remoteAuthorized: Boolean = false private var _remoteAuthorized: Boolean = false
private val _onAuthorized: (session: SyncSession) -> Unit private val _onAuthorized: (session: SyncSession, isNewlyAuthorized: Boolean, isNewSession: Boolean) -> Unit
private val _onUnauthorized: (session: SyncSession) -> Unit private val _onUnauthorized: (session: SyncSession) -> Unit
private val _onClose: (session: SyncSession) -> Unit private val _onClose: (session: SyncSession) -> Unit
private val _onConnectedChanged: (session: SyncSession, connected: Boolean) -> Unit private val _onConnectedChanged: (session: SyncSession, connected: Boolean) -> Unit
val remotePublicKey: String val remotePublicKey: String
override val isAuthorized get() = _authorized && _remoteAuthorized override val isAuthorized get() = _authorized && _remoteAuthorized
private var _wasAuthorized = false private var _wasAuthorized = false
private val _id = UUID.randomUUID()
private var _remoteId: UUID? = null
private var _lastAuthorizedRemoteId: UUID? = null
var connected: Boolean = false var connected: Boolean = false
private set(v) { private set(v) {
@ -55,7 +60,7 @@ class SyncSession : IAuthorizable {
} }
} }
constructor(remotePublicKey: String, onAuthorized: (session: SyncSession) -> Unit, onUnauthorized: (session: SyncSession) -> Unit, onConnectedChanged: (session: SyncSession, connected: Boolean) -> Unit, onClose: (session: SyncSession) -> Unit) { constructor(remotePublicKey: String, onAuthorized: (session: SyncSession, isNewlyAuthorized: Boolean, isNewSession: Boolean) -> Unit, onUnauthorized: (session: SyncSession) -> Unit, onConnectedChanged: (session: SyncSession, connected: Boolean) -> Unit, onClose: (session: SyncSession) -> Unit) {
this.remotePublicKey = remotePublicKey this.remotePublicKey = remotePublicKey
_onAuthorized = onAuthorized _onAuthorized = onAuthorized
_onUnauthorized = onUnauthorized _onUnauthorized = onUnauthorized
@ -77,7 +82,8 @@ class SyncSession : IAuthorizable {
} }
fun authorize(socketSession: SyncSocketSession) { fun authorize(socketSession: SyncSocketSession) {
socketSession.send(Opcode.NOTIFY_AUTHORIZED.value) Logger.i(TAG, "Sent AUTHORIZED with session id $_id")
socketSession.send(Opcode.NOTIFY_AUTHORIZED.value, 0u, ByteBuffer.wrap(_id.toString().toByteArray()))
_authorized = true _authorized = true
checkAuthorized() checkAuthorized()
} }
@ -95,9 +101,13 @@ class SyncSession : IAuthorizable {
} }
private fun checkAuthorized() { private fun checkAuthorized() {
if (!_wasAuthorized && isAuthorized) { if (isAuthorized) {
val isNewlyAuthorized = !_wasAuthorized;
val isNewSession = _lastAuthorizedRemoteId != _remoteId;
Logger.i(TAG, "onAuthorized (isNewlyAuthorized = $isNewlyAuthorized, isNewSession = $isNewSession)");
_onAuthorized.invoke(this, !_wasAuthorized, _lastAuthorizedRemoteId != _remoteId)
_wasAuthorized = true _wasAuthorized = true
_onAuthorized.invoke(this) _lastAuthorizedRemoteId = _remoteId
} }
} }
@ -126,12 +136,19 @@ class SyncSession : IAuthorizable {
when (opcode) { when (opcode) {
Opcode.NOTIFY_AUTHORIZED.value -> { Opcode.NOTIFY_AUTHORIZED.value -> {
val str = data.toUtf8String()
_remoteId = if (data.remaining() >= 0) UUID.fromString(str) else UUID.fromString("00000000-0000-0000-0000-000000000000")
_remoteAuthorized = true _remoteAuthorized = true
Logger.i(TAG, "Received AUTHORIZED with session id $_remoteId")
checkAuthorized() checkAuthorized()
return
} }
Opcode.NOTIFY_UNAUTHORIZED.value -> { Opcode.NOTIFY_UNAUTHORIZED.value -> {
_remoteId = null
_lastAuthorizedRemoteId = null
_remoteAuthorized = false _remoteAuthorized = false
_onUnauthorized(this) _onUnauthorized(this)
return
} }
//TODO: Handle any kind of packet (that is not necessarily authorized) //TODO: Handle any kind of packet (that is not necessarily authorized)
} }

View File

@ -10,6 +10,7 @@ import com.futo.platformplayer.noise.protocol.HandshakeState
import com.futo.platformplayer.states.StateSync import com.futo.platformplayer.states.StateSync
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
import java.util.UUID
class SyncSocketSession { class SyncSocketSession {
enum class Opcode(val value: UByte) { enum class Opcode(val value: UByte) {