@@ -6,6 +6,12 @@ import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
66import kotlinx.coroutines.Dispatchers
77import kotlinx.coroutines.runBlocking
88import kotlinx.coroutines.withContext
9+ import org.xbill.DNS.AAAARecord
10+ import org.xbill.DNS.ARecord
11+ import org.xbill.DNS.CNAMERecord
12+ import org.xbill.DNS.Lookup
13+ import org.xbill.DNS.SRVRecord
14+ import org.xbill.DNS.Type
915import tech.aliorpse.mcutils.model.server.ColorAdapter
1016import tech.aliorpse.mcutils.model.server.Description
1117import tech.aliorpse.mcutils.model.server.DescriptionAdapter
@@ -22,7 +28,6 @@ import java.net.IDN
2228import java.net.InetSocketAddress
2329import java.net.Socket
2430import java.nio.charset.StandardCharsets
25- import javax.naming.directory.InitialDirContext
2631
2732/* *
2833 * Provides functionality to fetch and parse the status of a Java Minecraft server.
@@ -81,20 +86,22 @@ object JavaServer {
8186 ): JavaServerStatus = withContext(Dispatchers .IO ) {
8287 val asciiHost = IDN .toASCII(host)
8388
84- val (resolvedHost, resolvedPort ) = if (enableSrv) {
89+ val (srvTarget, srvPort ) = if (enableSrv) {
8590 resolveSrvRecord(asciiHost) ? : (asciiHost to port)
8691 } else {
8792 asciiHost to port
8893 }
8994
95+ val resolvedHost = resolveToIpOrHost(srvTarget) ? : srvTarget
96+
9097 Socket ().use { socket ->
9198 socket.soTimeout = timeout
92- socket.connect(InetSocketAddress (resolvedHost, resolvedPort ), timeout)
99+ socket.connect(InetSocketAddress (resolvedHost, srvPort ), timeout)
93100
94101 val out = DataOutputStream (socket.getOutputStream())
95102 val input = DataInputStream (socket.getInputStream())
96103
97- sendHandshake(out , asciiHost, resolvedPort )
104+ sendHandshake(out , asciiHost, srvPort )
98105 sendStatusRequest(out )
99106
100107 val jsonStr = readStatusResponse(input)
@@ -129,21 +136,63 @@ object JavaServer {
129136 getStatus(host, port, timeout, enableSrv)
130137 }
131138
132- private fun resolveSrvRecord (host : String ): Pair <String , Int >? {
139+ @Suppress(" ReturnCount" )
140+ fun resolveToIpOrHost (host : String , depth : Int = 5): String? {
141+ if (depth <= 0 ) return null
142+
143+ try {
144+ // A
145+ val lookupA = Lookup (host, Type .A )
146+ lookupA.run ()
147+ val aRecords = lookupA.result.takeIf { lookupA.result == Lookup .SUCCESSFUL }
148+ ?.let { lookupA.answers.filterIsInstance<ARecord >() }
149+
150+ if (! aRecords.isNullOrEmpty()) {
151+ return aRecords[0 ].address.hostAddress
152+ }
153+
154+ // AAAA
155+ val lookupAAAA = Lookup (host, Type .AAAA )
156+ lookupAAAA.run ()
157+ val aaaaRecords = lookupAAAA.result.takeIf { lookupAAAA.result == Lookup .SUCCESSFUL }
158+ ?.let { lookupAAAA.answers.filterIsInstance<AAAARecord >() }
159+
160+ if (! aaaaRecords.isNullOrEmpty()) {
161+ return aaaaRecords[0 ].address.hostAddress
162+ }
163+
164+ // CNAME
165+ val lookupCNAME = Lookup (host, Type .CNAME )
166+ lookupCNAME.run ()
167+ val cnameRecords = lookupCNAME.result.takeIf { lookupCNAME.result == Lookup .SUCCESSFUL }
168+ ?.let { lookupCNAME.answers.filterIsInstance<CNAMERecord >() }
169+
170+ if (! cnameRecords.isNullOrEmpty()) {
171+ val cnameTarget = cnameRecords[0 ].target.toString(true )
172+ if (cnameTarget != host) {
173+ return resolveToIpOrHost(cnameTarget, depth - 1 )
174+ }
175+ }
176+
177+ } catch (_: Exception ) {
178+ return null
179+ }
180+ return null
181+ }
182+
183+ fun resolveSrvRecord (host : String ): Pair <String , Int >? {
133184 return try {
134- val dnsContext = InitialDirContext ()
135- val records = dnsContext.getAttributes(
136- " _minecraft._tcp.$host " ,
137- arrayOf(" SRV" )
138- ).get(" SRV" )?.toString()?.lines()?.firstOrNull()
139-
140- records?.let {
141- val parts = it.trim().split(" \\ s+" .toRegex())
142- if (parts.size == 4 ) {
143- val target = parts[3 ].removeSuffix(" ." )
144- val port = parts[2 ].toInt()
185+ val lookup = Lookup (" _minecraft._tcp.$host " , Type .SRV )
186+ lookup.run ()
187+ if (lookup.result == Lookup .SUCCESSFUL && lookup.answers.isNotEmpty()) {
188+ val srv = lookup.answers.filterIsInstance<SRVRecord >().minByOrNull { it.priority }
189+ srv?.let {
190+ val target = it.target.toString(true ).removeSuffix(" ." )
191+ val port = it.port
145192 target to port
146- } else null
193+ }
194+ } else {
195+ null
147196 }
148197 } catch (_: Exception ) {
149198 null
0 commit comments