22 * Nextcloud Android Library
33 *
44 * SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors
5+ * SPDX-FileCopyrightText: 2025 Alper Ozturk <alper.ozturk@nextcloud.com>
56 * SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
67 * SPDX-License-Identifier: MIT
78 */
89package com.nextcloud.common
910
11+ import android.os.Build
12+ import androidx.annotation.RequiresApi
1013import androidx.annotation.VisibleForTesting
1114import com.nextcloud.android.lib.core.Clock
1215import com.nextcloud.android.lib.core.ClockImpl
@@ -15,6 +18,7 @@ import java.net.Inet4Address
1518import java.net.Inet6Address
1619import java.net.InetAddress
1720import java.net.UnknownHostException
21+ import java.util.concurrent.ConcurrentHashMap
1822
1923/* *
2024 * DNS Cache which prefers IPv6 unless otherwise specified
@@ -24,12 +28,15 @@ object DNSCache {
2428
2529 // 30 seconds is the Java default. Let's keep it.
2630 @VisibleForTesting
31+ @Volatile
2732 var ttlMillis: Long = DEFAULT_TTL
2833
2934 @VisibleForTesting
35+ @Volatile
3036 var clock: Clock = ClockImpl ()
3137
3238 @VisibleForTesting
39+ @Volatile
3340 var dns: Dns = Dns .SYSTEM
3441
3542 data class DNSInfo (
@@ -40,49 +47,43 @@ object DNSCache {
4047 fun isExpired (): Boolean = clock.currentTimeMillis - timestamp > ttlMillis
4148 }
4249
43- private val cache: MutableMap <String , DNSInfo > = HashMap ()
50+ private val cache: ConcurrentHashMap <String , DNSInfo > = ConcurrentHashMap ()
4451
4552 @Throws(UnknownHostException ::class )
46- @Synchronized
4753 @JvmStatic
4854 fun lookup (hostname : String ): List <InetAddress > {
4955 val entry = cache[hostname]
5056 if (entry?.addresses?.isNotEmpty() == true && ! entry.isExpired()) {
5157 return entry.addresses
5258 }
53- val preferIPV4 =
54- when (entry) {
55- null -> false
56- else -> entry.preferIPV4
57- }
5859
5960 val addresses = dns.lookup(hostname).toMutableList()
6061 if (addresses.isEmpty()) {
6162 throw UnknownHostException (" Unknown host $hostname " )
6263 }
63- val sortedAddresses = sortAddresses(addresses, preferIPV4)
6464
65- val newEntry = DNSInfo (sortedAddresses, preferIPV4)
66- cache[hostname] = newEntry
65+ val preferIPV4 = entry?.preferIPV4 ? : false
66+ val sortedAddresses = sortAddresses(addresses, preferIPV4)
67+ cache[hostname] = DNSInfo (sortedAddresses, preferIPV4)
6768
6869 return sortedAddresses
6970 }
7071
7172 /* *
7273 * Set IP version preference for a hostname, and re-sort addresses if needed
7374 */
74- @Synchronized
75+ @RequiresApi( Build . VERSION_CODES . N )
7576 @JvmStatic
7677 fun setIPVersionPreference (
7778 hostname : String ,
7879 preferIPV4 : Boolean
7980 ) {
80- val entry = cache[hostname]
81- if (entry != null ) {
82- val addresses = sortAddresses(entry .addresses, preferIPV4)
83- cache[hostname] = DNSInfo (addresses , preferIPV4)
84- } else {
85- cache[hostname] = DNSInfo (emptyList() , preferIPV4)
81+ cache.compute(hostname) { _, old ->
82+ val addresses =
83+ old? .addresses?. let {
84+ sortAddresses(it , preferIPV4)
85+ } ? : emptyList()
86+ DNSInfo (addresses , preferIPV4)
8687 }
8788 }
8889
@@ -92,7 +93,6 @@ object DNSCache {
9293 * - The first address is an IPv6 address
9394 * - There are IPv4 addresses available too
9495 */
95- @Synchronized
9696 @JvmStatic
9797 fun isIPV6First (hostname : String ): Boolean {
9898 val firstV6 = cache[hostname]?.addresses?.firstOrNull() is Inet6Address
@@ -103,7 +103,6 @@ object DNSCache {
103103 /* *
104104 * Clears the cache
105105 */
106- @Synchronized
107106 @JvmStatic
108107 fun clear () {
109108 cache.clear()
0 commit comments