1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.resolver;
17
18 import io.netty5.util.CharsetUtil;
19 import io.netty5.util.internal.ObjectUtil;
20 import io.netty5.util.internal.PlatformDependent;
21 import io.netty5.util.internal.SystemPropertyUtil;
22 import io.netty5.util.internal.logging.InternalLogger;
23 import io.netty5.util.internal.logging.InternalLoggerFactory;
24
25 import java.net.InetAddress;
26 import java.nio.charset.Charset;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.concurrent.atomic.AtomicLong;
32
33
34
35
36 public final class DefaultHostsFileEntriesResolver implements HostsFileEntriesResolver {
37
38 private static final InternalLogger logger =
39 InternalLoggerFactory.getInstance(DefaultHostsFileEntriesResolver.class);
40 private static final long DEFAULT_REFRESH_INTERVAL;
41
42 private final long refreshInterval;
43 private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime());
44 private final HostsFileEntriesProvider.Parser hostsFileParser;
45 private volatile Map<String, List<InetAddress>> inet4Entries;
46 private volatile Map<String, List<InetAddress>> inet6Entries;
47
48 static {
49 DEFAULT_REFRESH_INTERVAL = SystemPropertyUtil.getLong(
50 "io.netty5.hostsFileRefreshInterval", 0);
51
52 if (logger.isDebugEnabled()) {
53 logger.debug("-Dio.netty5.hostsFileRefreshInterval: {}", DEFAULT_REFRESH_INTERVAL);
54 }
55 }
56
57 public DefaultHostsFileEntriesResolver() {
58 this(HostsFileEntriesProvider.parser(), DEFAULT_REFRESH_INTERVAL);
59 }
60
61
62 DefaultHostsFileEntriesResolver(HostsFileEntriesProvider.Parser hostsFileParser, long refreshInterval) {
63 this.hostsFileParser = hostsFileParser;
64 this.refreshInterval = ObjectUtil.checkPositiveOrZero(refreshInterval, "refreshInterval");
65 HostsFileEntriesProvider entries = parseEntries(hostsFileParser);
66 inet4Entries = entries.ipv4Entries();
67 inet6Entries = entries.ipv6Entries();
68 }
69
70 @Override
71 public InetAddress address(String inetHost, ResolvedAddressTypes resolvedAddressTypes) {
72 return firstAddress(addresses(inetHost, resolvedAddressTypes));
73 }
74
75
76
77
78
79
80
81
82
83 public List<InetAddress> addresses(String inetHost, ResolvedAddressTypes resolvedAddressTypes) {
84 String normalized = normalize(inetHost);
85 ensureHostsFileEntriesAreFresh();
86
87 switch (resolvedAddressTypes) {
88 case IPV4_ONLY:
89 return inet4Entries.get(normalized);
90 case IPV6_ONLY:
91 return inet6Entries.get(normalized);
92 case IPV4_PREFERRED:
93 List<InetAddress> allInet4Addresses = inet4Entries.get(normalized);
94 return allInet4Addresses != null ? allAddresses(allInet4Addresses, inet6Entries.get(normalized)) :
95 inet6Entries.get(normalized);
96 case IPV6_PREFERRED:
97 List<InetAddress> allInet6Addresses = inet6Entries.get(normalized);
98 return allInet6Addresses != null ? allAddresses(allInet6Addresses, inet4Entries.get(normalized)) :
99 inet4Entries.get(normalized);
100 default:
101 throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes);
102 }
103 }
104
105 private void ensureHostsFileEntriesAreFresh() {
106 long interval = refreshInterval;
107 if (interval == 0) {
108 return;
109 }
110 long last = lastRefresh.get();
111 long currentTime = System.nanoTime();
112 if (currentTime - last > interval) {
113 if (lastRefresh.compareAndSet(last, currentTime)) {
114 HostsFileEntriesProvider entries = parseEntries(hostsFileParser);
115 inet4Entries = entries.ipv4Entries();
116 inet6Entries = entries.ipv6Entries();
117 }
118 }
119 }
120
121
122 String normalize(String inetHost) {
123 return inetHost.toLowerCase(Locale.ENGLISH);
124 }
125
126 private static List<InetAddress> allAddresses(List<InetAddress> a, List<InetAddress> b) {
127 List<InetAddress> result = new ArrayList<>(a.size() + (b == null ? 0 : b.size()));
128 result.addAll(a);
129 if (b != null) {
130 result.addAll(b);
131 }
132 return result;
133 }
134
135 private static InetAddress firstAddress(List<InetAddress> addresses) {
136 return addresses != null && !addresses.isEmpty() ? addresses.get(0) : null;
137 }
138
139 private static HostsFileEntriesProvider parseEntries(HostsFileEntriesProvider.Parser parser) {
140 if (PlatformDependent.isWindows()) {
141
142
143
144 return parser.parseSilently(Charset.defaultCharset(), CharsetUtil.UTF_16, CharsetUtil.UTF_8);
145 }
146 return parser.parseSilently();
147 }
148 }