1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.resolver.dns.macos;
17
18 import io.netty.resolver.dns.DnsServerAddressStream;
19 import io.netty.resolver.dns.DnsServerAddressStreamProvider;
20 import io.netty.resolver.dns.DnsServerAddressStreamProviders;
21 import io.netty.resolver.dns.DnsServerAddresses;
22 import io.netty.util.internal.ClassInitializerUtil;
23 import io.netty.util.internal.NativeLibraryLoader;
24 import io.netty.util.internal.PlatformDependent;
25 import io.netty.util.internal.StringUtil;
26 import io.netty.util.internal.ThrowableUtil;
27 import io.netty.util.internal.logging.InternalLogger;
28 import io.netty.util.internal.logging.InternalLoggerFactory;
29
30 import java.net.InetSocketAddress;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.atomic.AtomicLong;
38
39
40
41
42
43
44 public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider {
45
46 private static final Comparator<DnsResolver> RESOLVER_COMPARATOR =
47 new Comparator<DnsResolver>() {
48 @Override
49 public int compare(DnsResolver r1, DnsResolver r2) {
50
51
52 return r1.searchOrder() < r2.searchOrder() ? 1 : r1.searchOrder() == r2.searchOrder() ? 0 : -1;
53 }
54 };
55
56 private static final Throwable UNAVAILABILITY_CAUSE;
57
58 private static final InternalLogger logger =
59 InternalLoggerFactory.getInstance(MacOSDnsServerAddressStreamProvider.class);
60
61
62 private static final long REFRESH_INTERVAL = TimeUnit.SECONDS.toNanos(10);
63
64 static {
65
66
67
68
69
70 ClassInitializerUtil.tryLoadClasses(MacOSDnsServerAddressStreamProvider.class,
71
72 byte[].class, String.class
73 );
74
75 Throwable cause = null;
76 try {
77 loadNativeLibrary();
78 } catch (Throwable error) {
79 cause = error;
80 }
81 UNAVAILABILITY_CAUSE = cause;
82 }
83
84 private static void loadNativeLibrary() {
85 if (!PlatformDependent.isOsx()) {
86 throw new IllegalStateException("Only supported on MacOS/OSX");
87 }
88 String staticLibName = "netty_resolver_dns_native_macos";
89 String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
90 ClassLoader cl = PlatformDependent.getClassLoader(MacOSDnsServerAddressStreamProvider.class);
91 try {
92 NativeLibraryLoader.load(sharedLibName, cl);
93 } catch (UnsatisfiedLinkError e1) {
94 try {
95 NativeLibraryLoader.load(staticLibName, cl);
96 logger.debug("Failed to load {}", sharedLibName, e1);
97 } catch (UnsatisfiedLinkError e2) {
98 ThrowableUtil.addSuppressed(e1, e2);
99 throw e1;
100 }
101 }
102 }
103
104 public static boolean isAvailable() {
105 return UNAVAILABILITY_CAUSE == null;
106 }
107
108 public static void ensureAvailability() {
109 if (UNAVAILABILITY_CAUSE != null) {
110 throw (Error) new UnsatisfiedLinkError(
111 "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
112 }
113 }
114
115 public static Throwable unavailabilityCause() {
116 return UNAVAILABILITY_CAUSE;
117 }
118
119 public MacOSDnsServerAddressStreamProvider() {
120 ensureAvailability();
121 currentMappings = retrieveCurrentMappings();
122 lastRefresh = new AtomicLong(System.nanoTime());
123 }
124
125 private volatile Map<String, DnsServerAddresses> currentMappings;
126 private final AtomicLong lastRefresh;
127
128 private static Map<String, DnsServerAddresses> retrieveCurrentMappings() {
129 DnsResolver[] resolvers = resolvers();
130
131 if (resolvers == null || resolvers.length == 0) {
132 return Collections.emptyMap();
133 }
134 Arrays.sort(resolvers, RESOLVER_COMPARATOR);
135 Map<String, DnsServerAddresses> resolverMap = new HashMap<String, DnsServerAddresses>(resolvers.length);
136 for (DnsResolver resolver: resolvers) {
137
138 if ("mdns".equalsIgnoreCase(resolver.options())) {
139 continue;
140 }
141 InetSocketAddress[] nameservers = resolver.nameservers();
142 if (nameservers == null || nameservers.length == 0) {
143 continue;
144 }
145 String domain = resolver.domain();
146 if (domain == null) {
147
148 domain = StringUtil.EMPTY_STRING;
149 }
150 InetSocketAddress[] servers = resolver.nameservers();
151 for (int a = 0; a < servers.length; a++) {
152 InetSocketAddress address = servers[a];
153
154 if (address.getPort() == 0) {
155 int port = resolver.port();
156 if (port == 0) {
157 port = 53;
158 }
159 servers[a] = new InetSocketAddress(address.getAddress(), port);
160 }
161 }
162
163 resolverMap.put(domain, DnsServerAddresses.sequential(servers));
164 }
165 return resolverMap;
166 }
167
168 @Override
169 public DnsServerAddressStream nameServerAddressStream(String hostname) {
170 long last = lastRefresh.get();
171 Map<String, DnsServerAddresses> resolverMap = currentMappings;
172 if (System.nanoTime() - last > REFRESH_INTERVAL) {
173
174
175 if (lastRefresh.compareAndSet(last, System.nanoTime())) {
176 resolverMap = currentMappings = retrieveCurrentMappings();
177 }
178 }
179
180 final String originalHostname = hostname;
181 for (;;) {
182 int i = hostname.indexOf('.', 1);
183 if (i < 0 || i == hostname.length() - 1) {
184
185 DnsServerAddresses addresses = resolverMap.get(StringUtil.EMPTY_STRING);
186 if (addresses != null) {
187 return addresses.stream();
188 }
189 return DnsServerAddressStreamProviders.unixDefault().nameServerAddressStream(originalHostname);
190 }
191
192 DnsServerAddresses addresses = resolverMap.get(hostname);
193 if (addresses != null) {
194 return addresses.stream();
195 }
196
197 hostname = hostname.substring(i + 1);
198 }
199 }
200
201 private static native DnsResolver[] resolvers();
202 }