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