2
2
* Nextcloud Android Library
3
3
*
4
4
* SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors
5
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk <[email protected] >
5
6
* SPDX-FileCopyrightText: 2022 Álvaro Brey <[email protected] >
6
7
* SPDX-License-Identifier: MIT
7
8
*/
8
9
package com.nextcloud.common
9
10
11
+ import android.os.Build
10
12
import androidx.annotation.VisibleForTesting
11
13
import com.nextcloud.android.lib.core.Clock
12
14
import com.nextcloud.android.lib.core.ClockImpl
@@ -15,6 +17,7 @@ import java.net.Inet4Address
15
17
import java.net.Inet6Address
16
18
import java.net.InetAddress
17
19
import java.net.UnknownHostException
20
+ import java.util.concurrent.ConcurrentHashMap
18
21
19
22
/* *
20
23
* DNS Cache which prefers IPv6 unless otherwise specified
@@ -24,12 +27,15 @@ object DNSCache {
24
27
25
28
// 30 seconds is the Java default. Let's keep it.
26
29
@VisibleForTesting
30
+ @Volatile
27
31
var ttlMillis: Long = DEFAULT_TTL
28
32
29
33
@VisibleForTesting
34
+ @Volatile
30
35
var clock: Clock = ClockImpl ()
31
36
32
37
@VisibleForTesting
38
+ @Volatile
33
39
var dns: Dns = Dns .SYSTEM
34
40
35
41
data class DNSInfo (
@@ -40,49 +46,47 @@ object DNSCache {
40
46
fun isExpired (): Boolean = clock.currentTimeMillis - timestamp > ttlMillis
41
47
}
42
48
43
- private val cache: MutableMap <String , DNSInfo > = HashMap ()
49
+ private val cache: ConcurrentHashMap <String , DNSInfo > = ConcurrentHashMap ()
44
50
45
51
@Throws(UnknownHostException ::class )
46
- @Synchronized
47
52
@JvmStatic
48
53
fun lookup (hostname : String ): List <InetAddress > {
49
54
val entry = cache[hostname]
50
55
if (entry?.addresses?.isNotEmpty() == true && ! entry.isExpired()) {
51
56
return entry.addresses
52
57
}
53
- val preferIPV4 =
54
- when (entry) {
55
- null -> false
56
- else -> entry.preferIPV4
57
- }
58
58
59
59
val addresses = dns.lookup(hostname).toMutableList()
60
60
if (addresses.isEmpty()) {
61
61
throw UnknownHostException (" Unknown host $hostname " )
62
62
}
63
- val sortedAddresses = sortAddresses(addresses, preferIPV4)
64
63
65
- val newEntry = DNSInfo (sortedAddresses, preferIPV4)
66
- cache[hostname] = newEntry
64
+ val preferIPV4 = entry?.preferIPV4 ? : false
65
+ val sortedAddresses = sortAddresses(addresses, preferIPV4)
66
+ cache[hostname] = DNSInfo (sortedAddresses, preferIPV4)
67
67
68
68
return sortedAddresses
69
69
}
70
70
71
71
/* *
72
72
* Set IP version preference for a hostname, and re-sort addresses if needed
73
73
*/
74
- @Synchronized
75
74
@JvmStatic
76
75
fun setIPVersionPreference (
77
76
hostname : String ,
78
77
preferIPV4 : Boolean
79
78
) {
80
- val entry = cache[hostname]
81
- if (entry != null ) {
82
- val addresses = sortAddresses(entry.addresses, preferIPV4)
83
- cache[hostname] = DNSInfo (addresses, preferIPV4)
79
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N ) {
80
+ cache.compute(hostname) { _, old ->
81
+ val addresses =
82
+ old?.addresses?.let {
83
+ sortAddresses(it, preferIPV4)
84
+ } ? : emptyList()
85
+ DNSInfo (addresses, preferIPV4)
86
+ }
84
87
} else {
85
- cache[hostname] = DNSInfo (emptyList(), preferIPV4)
88
+ val addresses = cache[hostname]?.addresses?.let { sortAddresses(it, preferIPV4) } ? : emptyList()
89
+ cache[hostname] = DNSInfo (addresses, preferIPV4)
86
90
}
87
91
}
88
92
@@ -92,7 +96,6 @@ object DNSCache {
92
96
* - The first address is an IPv6 address
93
97
* - There are IPv4 addresses available too
94
98
*/
95
- @Synchronized
96
99
@JvmStatic
97
100
fun isIPV6First (hostname : String ): Boolean {
98
101
val firstV6 = cache[hostname]?.addresses?.firstOrNull() is Inet6Address
@@ -103,7 +106,6 @@ object DNSCache {
103
106
/* *
104
107
* Clears the cache
105
108
*/
106
- @Synchronized
107
109
@JvmStatic
108
110
fun clear () {
109
111
cache.clear()
0 commit comments