diff --git a/README.md b/README.md index cac9f2f..03b7e85 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # native-mdns A Kotlin multiplatform library for mdns announcer(mdns server), only broadcast is supported, -it depends on ktor-network +it depends on ktor-network. -support PTR. +Also, nmdns can be compiled to shared/static lib for other language usage, see [build.gradle.kts](build.gradle.kts) Supported platforms: 1. windows64(mingwx64) @@ -12,6 +12,11 @@ Supported platforms: 4. macosArm64 5. jvm(11 or later) +Supported srv type +1. A +2. PTR + +> AAAA is not supported AirPlay2(_airplay._tcp.local.) is tested @@ -35,7 +40,7 @@ kotlin { sourceSets { commonMain.dependencies { - implementation("cn.rtast.nmdns:native-mdns:0.0.1") + implementation("cn.rtast.nmdns:native-mdns:0.0.2") } } } @@ -44,17 +49,17 @@ kotlin { There is a simple airplay2 service ```kotlin - val service = createService( - serviceType = "_airplay._tcp.local.", - serviceName = "AP@RTAST", - hostname = "rtakland.local.", - ipAddress = "192.168.10.104", - port = 7001, - bindAddress = "192.168.10.104", - mdnsPort = 5356, - ) - while (true) { - service.broadcast() - delay(2000L) - } +val service = registerService( + serviceType = "_airplay._tcp.local.", + serviceName = "AP@RTAST", + hostname = "rtakland.local.", + ipAddress = "192.168.10.104", + port = 7001, + bindAddress = "192.168.10.104", + mdnsPort = 5356, +) +while (true) { + service.broadcast() + delay(2000L) // or use cn.rtast.nmdns.sleep1(2) sleep 2 seconds +} ``` \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index f3dfe56..8e26d3d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,19 +6,32 @@ plugins { } group = "cn.rtast.nmdns" -version = "0.0.1" +version = "0.0.2" repositories { mavenCentral() } kotlin { - linuxArm64() - linuxX64() - mingwX64() - macosArm64() + val nativeTargets = listOf( + linuxArm64(), + linuxX64(), + mingwX64(), + macosArm64() + ) jvm { compilerOptions { jvmTarget = JvmTarget.JVM_11 } } + nativeTargets.forEach { + it.binaries { + staticLib { + baseName = "nmdns" + } + sharedLib { + baseName = "nmdns" + } + } + } + sourceSets { commonMain.dependencies { implementation("io.ktor:ktor-network:3.2.3") @@ -42,3 +55,27 @@ publishing { } } } + +/** + * link all targets static lib + * Apple target can only be compiled on macos + */ +tasks.register("linkStaticLibrary") { + group = "nmdns" + dependsOn(tasks.named("linkReleaseStaticLinuxArm64")) + dependsOn(tasks.named("linkReleaseStaticLinuxX64")) + dependsOn(tasks.named("linkReleaseStaticMacosArm64")) + dependsOn(tasks.named("linkReleaseStaticMingwX64")) +} + +/** + * link all targrts to shared lib + * Apple target can only be compiled on macos + */ +tasks.register("linkSharedLibrary") { + group = "nmdns" + dependsOn(tasks.named("linkReleaseSharedLinuxArm64")) + dependsOn(tasks.named("linkReleaseSharedLinuxX64")) + dependsOn(tasks.named("linkReleaseSharedMacosArm64")) + dependsOn(tasks.named("linkReleaseSharedMingwX64")) +} \ No newline at end of file diff --git a/src/commonMain/kotlin/cn/rtast/nmdns/mac.kt b/src/commonMain/kotlin/cn/rtast/nmdns/mac.kt index 7422cda..d2e700d 100644 --- a/src/commonMain/kotlin/cn/rtast/nmdns/mac.kt +++ b/src/commonMain/kotlin/cn/rtast/nmdns/mac.kt @@ -40,4 +40,7 @@ private val macAddresses = listOf( "c3:c9:1c:3d:ae:dd" ) +/** + * get random mac address + */ fun randomMacAddress(): String = macAddresses.random() \ No newline at end of file diff --git a/src/commonMain/kotlin/cn/rtast/nmdns/nmdns.kt b/src/commonMain/kotlin/cn/rtast/nmdns/nmdns_server.kt similarity index 70% rename from src/commonMain/kotlin/cn/rtast/nmdns/nmdns.kt rename to src/commonMain/kotlin/cn/rtast/nmdns/nmdns_server.kt index 0389600..a0fed84 100644 --- a/src/commonMain/kotlin/cn/rtast/nmdns/nmdns.kt +++ b/src/commonMain/kotlin/cn/rtast/nmdns/nmdns_server.kt @@ -13,58 +13,62 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.io.Buffer -data class NMDNSServer( +data class NMDNSAnnouncer( /** * coroutine dispatcher for running server */ - val dispatcher: CoroutineDispatcher = Dispatchers.Default, + internal val dispatcher: CoroutineDispatcher = Dispatchers.Default, /** * service type - * default airplay + * airplay service type: _airplay._tcp.local. */ - val serviceType: String = "_airplay._tcp.local.", + internal val serviceType: String, /** * service name - * finally: $serviceName.$serviceType */ - val serviceName: String = "APS", + internal val serviceName: String, /** * local hostname */ - val hostname: String, + internal val hostname: String, /** * service ip, lan ip mostly */ - val ipAddress: String, + internal val ipAddress: String, /** * service port */ - val port: Int = 7004, + internal val port: Int = 7004, /** * mdns server port */ - val mdnsPort: Int = 3025, + internal val mdnsPort: Int = 3025, /** - * Txt record content + * txt record content */ - val txtRecords: List = listOf( - "deviceid=01:23:45:67:89:AB", - "model=AppleTV3,2C", - "features=0x5A7FFFF7,0x1E", - "srcvers=220.68", - "pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336" - ), + internal val txtRecords: List, /** * mdns socket server */ - val server: BoundDatagramSocket, + internal val server: BoundDatagramSocket, /** * srv packet */ - val packet: Buffer, + internal val packet: Buffer, ) -suspend fun createService( +/** + * register service + * airplay2 txt records + * listOf( + * "deviceid=${randomMacAddress()}", + * "model=AppleTV3,2C", + * "features=0x5A7FFFF7,0x1E", + * "srcvers=220.68", + * "pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336" + * ) + */ +suspend fun registerService( serviceType: String, serviceName: String, hostname: String, @@ -72,25 +76,29 @@ suspend fun createService( port: Int, mdnsPort: Int = 9872, bindAddress: String, - txtRecords: List = listOf( - "deviceid=${randomMacAddress()}", - "model=AppleTV3,2C", - "features=0x5A7FFFF7,0x1E", - "srcvers=220.68", - "pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336" - ), -): NMDNSServer { + txtRecords: List, +): NMDNSAnnouncer { val manager = SelectorManager(Dispatchers.Default) val serverSocket = aSocket(manager).udp() .bind(InetSocketAddress(bindAddress, mdnsPort)) - return NMDNSServer( - Dispatchers.Default, serviceType, serviceName, - hostname, ipAddress, port, mdnsPort, - txtRecords, serverSocket, buildPacket(serviceType, "$serviceName.$serviceType", hostname, ipAddress, port, txtRecords) + return NMDNSAnnouncer( + Dispatchers.Default, + serviceType, + serviceName, + hostname, + ipAddress, + port, + mdnsPort, + txtRecords, + serverSocket, + buildPacket(serviceType, "$serviceName.$serviceType", hostname, ipAddress, port, txtRecords) ) } -suspend fun NMDNSServer.broadcast() { +/** + * broadcast packet + */ +suspend fun NMDNSAnnouncer.broadcast() { this.server.send(Datagram(packet.copy(), InetSocketAddress("224.0.0.251", 5353))) } diff --git a/src/commonMain/kotlin/cn/rtast/nmdns/sleep1.kt b/src/commonMain/kotlin/cn/rtast/nmdns/sleep1.kt index 8c34515..bcc17c8 100644 --- a/src/commonMain/kotlin/cn/rtast/nmdns/sleep1.kt +++ b/src/commonMain/kotlin/cn/rtast/nmdns/sleep1.kt @@ -7,4 +7,8 @@ package cn.rtast.nmdns +/** + * block current thread + * unit: second + */ internal expect fun sleep1(time: Int) \ No newline at end of file diff --git a/src/jvmTest/kotlin/test/JvmTest.kt b/src/jvmTest/kotlin/test/JvmTest.kt index 14ca38c..9bff286 100644 --- a/src/jvmTest/kotlin/test/JvmTest.kt +++ b/src/jvmTest/kotlin/test/JvmTest.kt @@ -8,7 +8,8 @@ package test import cn.rtast.nmdns.broadcast -import cn.rtast.nmdns.createService +import cn.rtast.nmdns.randomMacAddress +import cn.rtast.nmdns.registerService import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.Test @@ -40,14 +41,21 @@ class JvmTest { // println("sent") // delay(2000L) // } - val service = createService( + val service = registerService( serviceType = "_airplay._tcp.local.", serviceName = "AP@RTAST", hostname = "rtakland.local.", ipAddress = "192.168.10.104", port = 7001, bindAddress = "192.168.10.104", - mdnsPort = 5356 + mdnsPort = 5356, + txtRecords = listOf( + "deviceid=${randomMacAddress()}", + "model=AppleTV3,2C", + "features=0x5A7FFFF7,0x1E", + "srcvers=220.68", + "pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336" + ) ) while (true) { service.broadcast() diff --git a/src/linuxTest/kotlin/test/LinuxTest.kt b/src/linuxTest/kotlin/test/LinuxTest.kt index fc52a6a..a65381d 100644 --- a/src/linuxTest/kotlin/test/LinuxTest.kt +++ b/src/linuxTest/kotlin/test/LinuxTest.kt @@ -1,16 +1,16 @@ /* - * Copyright © 2025 RTAkland - * Author: RTAkland - * Date: 9/29/25 - */ +* Copyright © 2025 RTAkland +* Author: RTAkland +* Date: 9/29/25 +*/ package test import cn.rtast.nmdns.broadcast -import cn.rtast.nmdns.createService +import cn.rtast.nmdns.randomMacAddress +import cn.rtast.nmdns.registerService import cn.rtast.nmdns.sleep1 -import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlin.test.Test @@ -18,14 +18,21 @@ class LinuxTest { @Test fun `test on linux`() { runBlocking { - val service = createService( + val service = registerService( serviceType = "_airplay._tcp.local.", serviceName = "AP@RTAST", hostname = "rtakland.local.", ipAddress = "192.168.10.104", port = 7001, bindAddress = "192.168.10.104", - mdnsPort = 5356 + mdnsPort = 5356, + txtRecords = listOf( + "deviceid=${randomMacAddress()}", + "model=AppleTV3,2C", + "features=0x5A7FFFF7,0x1E", + "srcvers=220.68", + "pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336" + ) ) while (true) { service.broadcast()