Add holepunch helper and a bunch of other stuff
This commit is contained in:
parent
55587fa9a5
commit
20b3b43394
@ -43,6 +43,7 @@ dependencies {
|
||||
implementation("io.ktor:ktor-server-auth-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-config-yaml:$ktor_version")
|
||||
implementation("io.ktor:ktor-network:$ktor_version")
|
||||
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||
|
||||
// Logging
|
||||
|
@ -8,6 +8,7 @@ import org.h2.security.SHA3
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import java.time.Instant
|
||||
@ -20,6 +21,7 @@ class Client(
|
||||
var name: String,
|
||||
var timeout: Instant,
|
||||
var ip: String,
|
||||
var port: Int?,
|
||||
var passwordHash: String,
|
||||
var sampledClients: List<UUID>
|
||||
)
|
||||
@ -27,6 +29,7 @@ class Client(
|
||||
object Clients: UUIDTable() {
|
||||
var name = varchar("name", 16)
|
||||
var ip = varchar("ip", 21)
|
||||
var port = integer("port").nullable()
|
||||
var timeout = timestamp("timeout")
|
||||
var passwordHash = varchar("passwordHash", 96)
|
||||
var sampledClient1 = uuid("sampledClient1")
|
||||
@ -72,6 +75,7 @@ object DatabaseHandler {
|
||||
it[Clients.id] = id
|
||||
it[Clients.name] = name
|
||||
it[Clients.ip] = ip
|
||||
it[port] = null
|
||||
it[passwordHash] = pwHash
|
||||
it[timeout] = java.time.LocalDateTime.now().toInstant(java.time.ZoneOffset.UTC) + java.time.Duration.ofMinutes(5)
|
||||
it[sampledClient1] = sampledClients[0]
|
||||
@ -100,7 +104,7 @@ object DatabaseHandler {
|
||||
fun getClient(name: String): Client? {
|
||||
val client = transaction {
|
||||
Clients.select { Clients.name eq name }.map {
|
||||
Client(it[Clients.id].value, it[Clients.name], it[Clients.timeout], it[Clients.ip], it[Clients.passwordHash], listOf(
|
||||
Client(it[Clients.id].value, it[Clients.name], it[Clients.timeout], it[Clients.ip], it[Clients.port], it[Clients.passwordHash], listOf(
|
||||
it[Clients.sampledClient1],it[Clients.sampledClient2],it[Clients.sampledClient3],it[Clients.sampledClient4],
|
||||
it[Clients.sampledClient5],it[Clients.sampledClient6],it[Clients.sampledClient7],it[Clients.sampledClient8]
|
||||
))
|
||||
@ -113,7 +117,7 @@ object DatabaseHandler {
|
||||
fun getClient(id: UUID): Client? {
|
||||
val client = transaction {
|
||||
Clients.select { Clients.id eq id }.map {
|
||||
Client(it[Clients.id].value, it[Clients.name], it[Clients.timeout], it[Clients.ip], it[Clients.passwordHash], listOf(
|
||||
Client(it[Clients.id].value, it[Clients.name], it[Clients.timeout], it[Clients.ip], it[Clients.port], it[Clients.passwordHash], listOf(
|
||||
it[Clients.sampledClient1],it[Clients.sampledClient2],it[Clients.sampledClient3],it[Clients.sampledClient4],
|
||||
it[Clients.sampledClient5],it[Clients.sampledClient6],it[Clients.sampledClient7],it[Clients.sampledClient8]
|
||||
))
|
||||
@ -153,6 +157,12 @@ object DatabaseHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fun cullOutOfDateEntries() {
|
||||
transaction {
|
||||
Clients.deleteWhere { timeout less java.time.LocalDateTime.now().toInstant(java.time.ZoneOffset.UTC) }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun clear() {
|
||||
transaction {
|
||||
Clients.deleteAll()
|
||||
|
@ -0,0 +1,56 @@
|
||||
package org.muellerssoftware.openproximitychat.tracker
|
||||
|
||||
import io.ktor.network.selector.*
|
||||
import io.ktor.network.sockets.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.selects.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.update
|
||||
import org.muellerssoftware.openproximitychat.common.Logging
|
||||
import java.util.*
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
|
||||
fun Application.puncher() {
|
||||
val job = GlobalScope.launch {
|
||||
val selectorManager = SelectorManager(Dispatchers.IO)
|
||||
val serverSocket = aSocket(selectorManager).udp().bind()
|
||||
|
||||
Logging.info("HolePunch listening on ${serverSocket.localAddress}")
|
||||
|
||||
select<Unit> {
|
||||
serverSocket.incoming.onReceive {
|
||||
val address = it.address
|
||||
val packet = it.packet
|
||||
val packetBytes = packet.readByteBuffer()
|
||||
|
||||
val uuid: UUID?
|
||||
|
||||
try {
|
||||
uuid = UUID.fromString(packetBytes.array().decodeToString())
|
||||
} catch (e: Exception) {
|
||||
Logging.error("Received invalid UUID from $address")
|
||||
return@onReceive
|
||||
}
|
||||
|
||||
transaction {
|
||||
Clients.update({ Clients.id eq uuid }) {
|
||||
it[ip] = address.toJavaAddress().toString()
|
||||
}.run {
|
||||
if (this == 0) {
|
||||
Logging.info("Received packet from unknown client $uuid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return@onReceive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
environment.monitor.subscribe(ApplicationStopping) {
|
||||
Logging.info("Closing HolePunch")
|
||||
job.cancel()
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package org.muellerssoftware.openproximitychat.tracker
|
||||
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class Scheduler(private val task: Runnable) {
|
||||
private val executor = Executors.newScheduledThreadPool(1)
|
||||
|
||||
fun scheduleExecution(every: Every) {
|
||||
|
||||
val taskWrapper = Runnable {
|
||||
task.run()
|
||||
}
|
||||
|
||||
executor.scheduleWithFixedDelay(taskWrapper, every.n, every.n, every.unit)
|
||||
}
|
||||
|
||||
|
||||
fun stop() {
|
||||
executor.shutdown()
|
||||
|
||||
try {
|
||||
executor.awaitTermination(1, TimeUnit.MINUTES)
|
||||
} catch (_: InterruptedException) {}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
data class Every(val n: Long, val unit: TimeUnit)
|
@ -10,21 +10,28 @@ import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import org.muellerssoftware.openproximitychat.common.Logging
|
||||
import org.muellerssoftware.openproximitychat.common.UPnPManager
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
|
||||
|
||||
fun Application.module() {
|
||||
fun Application.tracker() {
|
||||
Logging.logger = log
|
||||
|
||||
val cullScheduler = Scheduler {
|
||||
log.info("Culling clients")
|
||||
DatabaseHandler.cullOutOfDateEntries()
|
||||
}
|
||||
|
||||
environment.monitor.subscribe(ApplicationStarted) {
|
||||
log.info("Application started")
|
||||
UPnPManager.mapPort(this.environment.config.port, "TCP")
|
||||
cullScheduler.scheduleExecution(Every(1, TimeUnit.MINUTES))
|
||||
}
|
||||
|
||||
environment.monitor.subscribe(ApplicationStopping) {
|
||||
log.info("Application stopping")
|
||||
UPnPManager.unMapAll()
|
||||
cullScheduler.stop()
|
||||
|
||||
}
|
||||
|
||||
authentication {
|
||||
@ -93,7 +100,7 @@ fun Application.module() {
|
||||
val id = DatabaseHandler.addClient(
|
||||
request.id,
|
||||
request.name,
|
||||
this.call.request.origin.remoteHost + ":${this.call.request.origin.port}",
|
||||
call.request.origin.remoteHost,
|
||||
request.sampledClients
|
||||
)
|
||||
call.respond(HttpStatusCode.OK, RegisterResponse(id))
|
@ -3,4 +3,5 @@ ktor:
|
||||
port: 0
|
||||
application:
|
||||
modules:
|
||||
- org.muellerssoftware.openproximitychat.tracker.ApplicationKt.module
|
||||
- org.muellerssoftware.openproximitychat.tracker.TrackerApplicationKt.tracker
|
||||
- org.muellerssoftware.openproximitychat.tracker.HolePunchApplicationKt.puncher
|
@ -11,7 +11,7 @@ import java.util.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ApplicationKtTest {
|
||||
class TrackerApplicationKtTest {
|
||||
private val sampledClientsTestVal = listOf(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID())
|
||||
|
||||
@Before
|
||||
@ -213,9 +213,7 @@ class ApplicationKtTest {
|
||||
)
|
||||
basicAuth("00000000-0000-0000-0000-000000000001", pw_hash)
|
||||
}.apply {
|
||||
val clientCorrect = DatabaseHandler.getClient(UUID.fromString("00000000-0000-0000-0000-000000000001"))
|
||||
assertEquals(200, this.status.value)
|
||||
assertEquals((this.body() as SearchResponse).ip, clientCorrect!!.ip)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user