@@ -6,6 +6,7 @@ import com.ismartcoding.lib.logcat.LogCat
66import java.net.DatagramPacket
77import java.net.DatagramSocket
88import java.net.Inet4Address
9+ import java.net.Inet6Address
910import java.net.InetAddress
1011import java.net.InetSocketAddress
1112import java.net.MulticastSocket
@@ -62,12 +63,21 @@ object MdnsHostResponder {
6263 MulticastSocket (null ).apply {
6364 reuseAddress = true
6465 soTimeout = 1000
65- bind(InetSocketAddress (MDNS_PORT ))
66+ // Explicitly bind to the IPv4 wildcard.
67+ // InetSocketAddress(port) resolves to the system-preferred wildcard, which on
68+ // Samsung Android 13+ (preferIPv6Addresses=true) becomes [::]:5353 — an IPv6
69+ // socket. Joining an IPv4 multicast group on an IPv6 socket silently fails,
70+ // leaving this socket unable to receive any mDNS queries.
71+ bind(InetSocketAddress (InetAddress .getByName(" 0.0.0.0" ), MDNS_PORT ))
72+ var joinCount = 0
6673 for ((iface, ip) in candidates) {
6774 runCatching { joinGroup(groupSockAddr, iface) }
68- .onSuccess { LogCat .d(" mDNS joined ${iface.name} (${ip.hostAddress} )" ) }
75+ .onSuccess { joinCount ++ ; LogCat .d(" mDNS joined ${iface.name} (${ip.hostAddress} )" ) }
6976 .onFailure { LogCat .e(" mDNS joinGroup ${iface.name} : ${it.message} " ) }
7077 }
78+ if (joinCount == 0 ) {
79+ LogCat .e(" mDNS: no interface joined multicast group — responder will not receive queries" )
80+ }
7181 }
7282 }.getOrElse {
7383 lock?.let { l -> runCatching { l.release() } }
@@ -103,7 +113,9 @@ object MdnsHostResponder {
103113 val packet = DatagramPacket (buf, buf.size)
104114 try {
105115 s.receive(packet)
106- val senderIp = packet.address as ? Inet4Address ? : continue
116+ // extractInet4Address handles both plain Inet4Address and IPv4-mapped IPv6
117+ // addresses (::ffff:x.x.x.x) that a dual-stack socket may report.
118+ val senderIp = extractInet4Address(packet.address) ? : continue
107119 val fresh = candidateInterfaces()
108120 if (fresh.isEmpty()) continue
109121 val (_, localIp) = findResponseIface(senderIp, fresh)
@@ -129,6 +141,24 @@ object MdnsHostResponder {
129141 }.onFailure { LogCat .e(" mDNS send to ${dest.hostAddress} : ${it.message} " ) }
130142 }
131143
144+ /* *
145+ * Returns the IPv4 address from [addr], unwrapping IPv4-mapped IPv6 addresses
146+ * (::ffff:x.x.x.x) that a dual-stack socket reports for IPv4 senders.
147+ */
148+ internal fun extractInet4Address (addr : InetAddress ): Inet4Address ? {
149+ if (addr is Inet4Address ) return addr
150+ if (addr is Inet6Address ) {
151+ val b = addr.address
152+ // IPv4-mapped format: 10 zero bytes + 0xFF 0xFF + 4 IPv4 bytes
153+ if (b.size == 16 && b[0 ] == 0 .toByte() && b[10 ] == 0xFF .toByte() && b[11 ] == 0xFF .toByte() &&
154+ b.take(10 ).all { it == 0 .toByte() }
155+ ) {
156+ return runCatching { InetAddress .getByAddress(b.copyOfRange(12 , 16 )) as ? Inet4Address }.getOrNull()
157+ }
158+ }
159+ return null
160+ }
161+
132162 internal fun normalizeHostname (value : String ): String {
133163 val trimmed = value.trim().trim(' .' ).lowercase()
134164 if (trimmed.isEmpty()) return " "
0 commit comments