Compare commits

...

2 Commits

6 changed files with 62 additions and 125 deletions

View File

@ -1,69 +0,0 @@
@file:OptIn(DelicateCoroutinesApi::class)
package org.muellerssoftware.openproximitychat.common
import kotlinx.coroutines.*
import org.bitlet.weupnp.GatewayDevice
import org.bitlet.weupnp.GatewayDiscover
data class MappedPort(val port: Int, val protocol: String)
object UPnPManager {
private var gateway: GatewayDevice? = null
private val mappedPorts = mutableMapOf<Int, MappedPort>()
private var discoveryJob: Job = Job()
init {
runBlocking {
discoveryJob = GlobalScope.launch {
startGatewayDiscover()
}
}
}
fun startGatewayDiscover() {
val gatewayDiscoverer = GatewayDiscover()
for (i in 0..9) {
Logging.info("Searching for UPnP gateway... (Attempt ${i + 1})")
gatewayDiscoverer.discover()
gateway = GatewayDevice()
if (gatewayDiscoverer.validGateway != null) {
gateway = gatewayDiscoverer.validGateway
break
}
}
if (gateway == null) {
Logging.error("No UPnP gateway found!")
}
else {
Logging.info("UPnP gateway found: ${gateway!!.friendlyName}")
val ports = mappedPorts.toMutableMap()
mappedPorts.clear()
ports.map {
mapPort(it.key, it.value.port, it.value.protocol)
}
}
}
fun mapPort(port: Int, protocol: String = "TCP") {
mapPort(port, port, protocol)
}
fun mapPort(internalPort: Int, externalPort: Int, protocol: String = "TCP") {
if (gateway == null) {
mappedPorts[internalPort] = MappedPort(externalPort, protocol)
}
val succeeded = gateway?.addPortMapping(externalPort, internalPort, gateway!!.localAddress.hostAddress, "TCP", "OpenProximityChat mapped port")
if (succeeded == true) {
mappedPorts[internalPort] = MappedPort(externalPort, protocol)
}
}
fun unMapAll() {
mappedPorts.map {
gateway?.deletePortMapping(it.value.port, it.value.protocol)
}
}
}

View File

@ -25,7 +25,6 @@ object VoiceServer {
var handshook = mutableMapOf<UUID, InetAddress>() var handshook = mutableMapOf<UUID, InetAddress>()
init { init {
UPnPManager.mapPort(localAddress.port)
runBlocking { runBlocking {
startServer() startServer()
@ -75,7 +74,6 @@ object VoiceServer {
fun exit() { fun exit() {
Logging.info("VoiceServer exiting...") Logging.info("VoiceServer exiting...")
UPnPManager.unMapAll()
if (recieverJob.isActive) { if (recieverJob.isActive) {
runBlocking { runBlocking {
recieverJob.cancelAndJoin() recieverJob.cancelAndJoin()

View File

@ -1,10 +1,16 @@
@file:UseSerializers(UUIDAsStringSerializer::class, InstantAsStringSerializer::class) @file:UseSerializers(
org.muellerssoftware.openproximitychat.common.UUIDSerializer::class,
InstantAsStringSerializer::class
)
package org.muellerssoftware.openproximitychat.tracker package org.muellerssoftware.openproximitychat.tracker
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import org.h2.security.SHA3 import org.h2.security.SHA3
import org.jetbrains.exposed.dao.UUIDEntity
import org.jetbrains.exposed.dao.UUIDEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.UUIDTable import org.jetbrains.exposed.dao.id.UUIDTable
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
@ -26,6 +32,46 @@ class Client(
var sampledClients: List<UUID> var sampledClients: List<UUID>
) )
class ClientEntity(id: EntityID<UUID>): UUIDEntity(id) {
companion object : UUIDEntityClass<ClientEntity>(Clients)
var uuid by Clients.id
var name by Clients.name
var timeout by Clients.timeout
var ip by Clients.ip
var port by Clients.port
var passwordHash by Clients.passwordHash
var sampledClient1 by Clients.sampledClient1
var sampledClient2 by Clients.sampledClient2
var sampledClient3 by Clients.sampledClient3
var sampledClient4 by Clients.sampledClient4
var sampledClient5 by Clients.sampledClient5
var sampledClient6 by Clients.sampledClient6
var sampledClient7 by Clients.sampledClient7
var sampledClient8 by Clients.sampledClient8
fun toClient(): Client {
return Client(
uuid.value,
name,
timeout,
ip,
port,
passwordHash,
listOfNotNull(
sampledClient1,
sampledClient2,
sampledClient3,
sampledClient4,
sampledClient5,
sampledClient6,
sampledClient7,
sampledClient8
)
)
}
}
object Clients: UUIDTable() { object Clients: UUIDTable() {
var name = varchar("name", 16) var name = varchar("name", 16)
var ip = varchar("ip", 21) var ip = varchar("ip", 21)
@ -92,9 +138,9 @@ object DatabaseHandler {
return pw return pw
} }
private fun checkTimeout(client: Client): Client? { private fun checkTimeout(client: ClientEntity): ClientEntity? {
if (client.timeout < Instant.now()) { if (client.timeout < Instant.now()) {
removeClient(client.uuid) client.delete()
return null return null
} }
@ -102,29 +148,15 @@ object DatabaseHandler {
} }
fun getClient(name: String): Client? { fun getClient(name: String): Client? {
val client = transaction { return transaction {
Clients.select { Clients.name eq name }.map { ClientEntity.find { Clients.name eq name }.firstOrNull()
Client(it[Clients.id].value, it[Clients.name], it[Clients.timeout], it[Clients.ip], it[Clients.port], it[Clients.passwordHash], listOf( }?.let { checkTimeout(it) }?.toClient()
it[Clients.sampledClient1],it[Clients.sampledClient2],it[Clients.sampledClient3],it[Clients.sampledClient4],
it[Clients.sampledClient5],it[Clients.sampledClient6],it[Clients.sampledClient7],it[Clients.sampledClient8]
))
}.firstOrNull()
} ?: return null
return checkTimeout(client)
} }
fun getClient(id: UUID): Client? { fun getClient(id: UUID): Client? {
val client = transaction { return transaction {
Clients.select { Clients.id eq id }.map { ClientEntity.find { Clients.id eq id }.firstOrNull()
Client(it[Clients.id].value, it[Clients.name], it[Clients.timeout], it[Clients.ip], it[Clients.port], it[Clients.passwordHash], listOf( }?.let { checkTimeout(it) }?.toClient()
it[Clients.sampledClient1],it[Clients.sampledClient2],it[Clients.sampledClient3],it[Clients.sampledClient4],
it[Clients.sampledClient5],it[Clients.sampledClient6],it[Clients.sampledClient7],it[Clients.sampledClient8]
))
}.firstOrNull()
} ?: return null
return checkTimeout(client)
} }
fun doHeartbeat(id: UUID): String { fun doHeartbeat(id: UUID): String {
@ -148,9 +180,11 @@ object DatabaseHandler {
} }
fun checkCredentials(id: UUID, passwordHash: String): Boolean { fun checkCredentials(id: UUID, passwordHash: String): Boolean {
val client = getClient(id) ?: return false val client = transaction {
ClientEntity.find { Clients.id eq id }.firstOrNull()
}?.let { checkTimeout(it) }
return if (checkTimeout(client) != null) { return if (client != null) {
client.passwordHash == passwordHash client.passwordHash == passwordHash
} else { } else {
false false

View File

@ -6,6 +6,7 @@ import io.ktor.server.application.*
import io.ktor.utils.io.core.* import io.ktor.utils.io.core.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.selects.select import kotlinx.coroutines.selects.select
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import org.muellerssoftware.openproximitychat.common.Logging import org.muellerssoftware.openproximitychat.common.Logging
@ -35,7 +36,7 @@ fun Application.puncher() {
} }
transaction { transaction {
Clients.update({ Clients.id eq uuid }) { Clients.update({ Clients.id eq uuid and (Clients.port eq null) }) {
it[ip] = address.toJavaAddress().toString() it[ip] = address.toJavaAddress().toString()
}.run { }.run {
if (this == 0) { if (this == 0) {

View File

@ -1,4 +1,4 @@
@file:UseSerializers(UUIDAsStringSerializer::class) @file:UseSerializers(org.muellerssoftware.openproximitychat.common.UUIDSerializer::class)
package org.muellerssoftware.openproximitychat.tracker package org.muellerssoftware.openproximitychat.tracker

View File

@ -1,27 +0,0 @@
package org.muellerssoftware.openproximitychat.tracker
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.util.*
@OptIn(ExperimentalSerializationApi::class)
@Serializer(forClass = UUID::class)
object UUIDAsStringSerializer: KSerializer<UUID> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UUIDAsStringSerializer", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
val string = value.toString()
encoder.encodeString(string)
}
override fun deserialize(decoder: Decoder): UUID {
val string = decoder.decodeString()
return UUID.fromString(string)
}
}