1 /*
2 * Copyright 2015 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package io.netty.resolver.dns;
17
18 import io.netty.channel.ChannelFactory;
19 import io.netty.channel.EventLoop;
20 import io.netty.channel.ReflectiveChannelFactory;
21 import io.netty.channel.socket.DatagramChannel;
22 import io.netty.channel.socket.InternetProtocolFamily;
23 import io.netty.channel.socket.SocketChannel;
24 import io.netty.channel.socket.SocketProtocolFamily;
25 import io.netty.resolver.HostsFileEntriesResolver;
26 import io.netty.resolver.ResolvedAddressTypes;
27 import io.netty.util.concurrent.Future;
28 import io.netty.util.internal.EmptyArrays;
29 import io.netty.util.internal.ObjectUtil;
30 import io.netty.util.internal.logging.InternalLogger;
31 import io.netty.util.internal.logging.InternalLoggerFactory;
32
33 import java.net.InetSocketAddress;
34 import java.net.SocketAddress;
35 import java.util.Arrays;
36 import java.util.LinkedHashSet;
37 import java.util.Set;
38
39 import static io.netty.util.internal.ObjectUtil.checkNotNull;
40 import static io.netty.util.internal.ObjectUtil.intValue;
41
42 /**
43 * A {@link DnsNameResolver} builder.
44 */
45 public final class DnsNameResolverBuilder {
46
47 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolverBuilder.class);
48 static final SocketAddress DEFAULT_LOCAL_ADDRESS = new InetSocketAddress(0);
49
50 volatile EventLoop eventLoop;
51 private ChannelFactory<? extends DatagramChannel> datagramChannelFactory;
52 private ChannelFactory<? extends SocketChannel> socketChannelFactory;
53 private boolean retryOnTimeout;
54
55 private DnsCache resolveCache;
56 private DnsCnameCache cnameCache;
57 private AuthoritativeDnsServerCache authoritativeDnsServerCache;
58 private SocketAddress localAddress = DEFAULT_LOCAL_ADDRESS;
59 private Integer minTtl;
60 private Integer maxTtl;
61 private Integer negativeTtl;
62 private long queryTimeoutMillis = -1;
63 private ResolvedAddressTypes resolvedAddressTypes = DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
64 private boolean completeOncePreferredResolved;
65 private boolean recursionDesired = true;
66 private int maxQueriesPerResolve = -1;
67 private boolean traceEnabled;
68 private int maxPayloadSize = 4096;
69 private boolean optResourceEnabled = true;
70 private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
71 private DnsServerAddressStreamProvider dnsServerAddressStreamProvider =
72 DnsServerAddressStreamProviders.platformDefault();
73 private DnsServerAddressStream queryDnsServerAddressStream;
74 private DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory =
75 NoopDnsQueryLifecycleObserverFactory.INSTANCE;
76 private String[] searchDomains;
77 private int ndots = -1;
78 private boolean decodeIdn = true;
79
80 private int maxNumConsolidation;
81 private DnsNameResolverChannelStrategy datagramChannelStrategy = DnsNameResolverChannelStrategy.ChannelPerResolver;
82 private boolean caseRandomizationEnabled;
83 private boolean dnsCookiesEnabled;
84
85 /**
86 * Creates a new builder.
87 */
88 public DnsNameResolverBuilder() {
89 }
90
91 /**
92 * Creates a new builder.
93 *
94 * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS
95 * servers.
96 */
97 public DnsNameResolverBuilder(EventLoop eventLoop) {
98 eventLoop(eventLoop);
99 }
100
101 /**
102 * Sets the {@link EventLoop} which will perform the communication with the DNS servers.
103 *
104 * @param eventLoop the {@link EventLoop}
105 * @return {@code this}
106 */
107 public DnsNameResolverBuilder eventLoop(EventLoop eventLoop) {
108 this.eventLoop = eventLoop;
109 return this;
110 }
111
112 @Deprecated
113 protected ChannelFactory<? extends DatagramChannel> channelFactory() {
114 return this.datagramChannelFactory;
115 }
116
117 ChannelFactory<? extends DatagramChannel> datagramChannelFactory() {
118 return this.datagramChannelFactory;
119 }
120
121 /**
122 * Sets the {@link ChannelFactory} that will create a {@link DatagramChannel}.
123 * <p>
124 * If <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> should be supported as well it is required
125 * to call the {@link #socketChannelFactory(ChannelFactory)} or {@link #socketChannelType(Class)} method.
126 *
127 * @param datagramChannelFactory the {@link ChannelFactory}
128 * @return {@code this}
129 * @deprecated use {@link #datagramChannelFactory(ChannelFactory)}
130 */
131 @Deprecated
132 public DnsNameResolverBuilder channelFactory(ChannelFactory<? extends DatagramChannel> datagramChannelFactory) {
133 datagramChannelFactory(datagramChannelFactory);
134 return this;
135 }
136
137 /**
138 * Sets the {@link ChannelFactory} that will create a {@link DatagramChannel}.
139 * <p>
140 * If <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> should be supported as well it is required
141 * to call the {@link #socketChannelFactory(ChannelFactory)} or {@link #socketChannelType(Class)} method.
142 *
143 * @param datagramChannelFactory the {@link ChannelFactory}
144 * @return {@code this}
145 */
146 public DnsNameResolverBuilder datagramChannelFactory(
147 ChannelFactory<? extends DatagramChannel> datagramChannelFactory) {
148 this.datagramChannelFactory = datagramChannelFactory;
149 return this;
150 }
151
152 /**
153 * Sets the {@link ChannelFactory} as a {@link ReflectiveChannelFactory} of this type.
154 * Use as an alternative to {@link #channelFactory(ChannelFactory)}.
155 * <p>
156 * If <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> should be supported as well it is required
157 * to call the {@link #socketChannelFactory(ChannelFactory)} or {@link #socketChannelType(Class)} method.
158 *
159 * @param channelType the type
160 * @return {@code this}
161 * @deprecated use {@link #datagramChannelType(Class)}
162 */
163 @Deprecated
164 public DnsNameResolverBuilder channelType(Class<? extends DatagramChannel> channelType) {
165 return datagramChannelFactory(new ReflectiveChannelFactory<DatagramChannel>(channelType));
166 }
167
168 /**
169 * Sets the {@link ChannelFactory} as a {@link ReflectiveChannelFactory} of this type.
170 * Use as an alternative to {@link #datagramChannelFactory(ChannelFactory)}.
171 * <p>
172 * If <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> should be supported as well it is required
173 * to call the {@link #socketChannelFactory(ChannelFactory)} or {@link #socketChannelType(Class)} method.
174 *
175 * @param channelType the type
176 * @return {@code this}
177 */
178 public DnsNameResolverBuilder datagramChannelType(Class<? extends DatagramChannel> channelType) {
179 return datagramChannelFactory(new ReflectiveChannelFactory<DatagramChannel>(channelType));
180 }
181
182 /**
183 * Sets the {@link ChannelFactory} that will create a {@link SocketChannel} for
184 * <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> if needed.
185 * <p>
186 * TCP fallback is <strong>not</strong> enabled by default and must be enabled by providing a non-null
187 * {@link ChannelFactory} for this method.
188 *
189 * @param channelFactory the {@link ChannelFactory} or {@code null}
190 * if <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> should not be supported.
191 * By default, TCP fallback is not enabled.
192 * @return {@code this}
193 */
194 public DnsNameResolverBuilder socketChannelFactory(ChannelFactory<? extends SocketChannel> channelFactory) {
195 return socketChannelFactory(channelFactory, false);
196 }
197
198 /**
199 * Sets the {@link ChannelFactory} as a {@link ReflectiveChannelFactory} of this type for
200 * <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> if needed.
201 * Use as an alternative to {@link #socketChannelFactory(ChannelFactory)}.
202 * <p>
203 * TCP fallback is <strong>not</strong> enabled by default and must be enabled by providing a non-null
204 * {@code channelType} for this method.
205 *
206 * @param channelType the type or {@code null} if <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a>
207 * should not be supported. By default, TCP fallback is not enabled.
208 * @return {@code this}
209 */
210 public DnsNameResolverBuilder socketChannelType(Class<? extends SocketChannel> channelType) {
211 return socketChannelType(channelType, false);
212 }
213
214 /**
215 * Sets the {@link ChannelFactory} that will create a {@link SocketChannel} for
216 * <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> if needed.
217 * <p>
218 * TCP fallback is <strong>not</strong> enabled by default and must be enabled by providing a non-null
219 * {@link ChannelFactory} for this method.
220 *
221 * @param channelFactory the {@link ChannelFactory} or {@code null}
222 * if <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> should not be supported.
223 * By default, TCP fallback is not enabled.
224 * @param retryOnTimeout if {@code true} the {@link DnsNameResolver} will also fallback to TCP if a timeout
225 * was detected, if {@code false} it will only try to use TCP if the response was marked
226 * as truncated.
227 * @return {@code this}
228 */
229 public DnsNameResolverBuilder socketChannelFactory(
230 ChannelFactory<? extends SocketChannel> channelFactory, boolean retryOnTimeout) {
231 this.socketChannelFactory = channelFactory;
232 this.retryOnTimeout = retryOnTimeout;
233 return this;
234 }
235
236 /**
237 * Sets the {@link ChannelFactory} as a {@link ReflectiveChannelFactory} of this type for
238 * <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a> if needed.
239 * Use as an alternative to {@link #socketChannelFactory(ChannelFactory)}.
240 * <p>
241 * TCP fallback is <strong>not</strong> enabled by default and must be enabled by providing a non-null
242 * {@code channelType} for this method.
243 *
244 * @param channelType the type or {@code null} if <a href="https://tools.ietf.org/html/rfc7766">TCP fallback</a>
245 * should not be supported. By default, TCP fallback is not enabled.
246 * @param retryOnTimeout if {@code true} the {@link DnsNameResolver} will also fallback to TCP if a timeout
247 * was detected, if {@code false} it will only try to use TCP if the response was marked
248 * as truncated.
249 * @return {@code this}
250 */
251 public DnsNameResolverBuilder socketChannelType(
252 Class<? extends SocketChannel> channelType, boolean retryOnTimeout) {
253 if (channelType == null) {
254 return socketChannelFactory(null, retryOnTimeout);
255 }
256 return socketChannelFactory(new ReflectiveChannelFactory<SocketChannel>(channelType), retryOnTimeout);
257 }
258
259 /**
260 * Sets the cache for resolution results.
261 *
262 * @param resolveCache the DNS resolution results cache
263 * @return {@code this}
264 */
265 public DnsNameResolverBuilder resolveCache(DnsCache resolveCache) {
266 this.resolveCache = resolveCache;
267 return this;
268 }
269
270 /**
271 * Sets the cache for {@code CNAME} mappings.
272 *
273 * @param cnameCache the cache used to cache {@code CNAME} mappings for a domain.
274 * @return {@code this}
275 */
276 public DnsNameResolverBuilder cnameCache(DnsCnameCache cnameCache) {
277 this.cnameCache = cnameCache;
278 return this;
279 }
280
281 /**
282 * Set the factory used to generate objects which can observe individual DNS queries.
283 * @param lifecycleObserverFactory the factory used to generate objects which can observe individual DNS queries.
284 * @return {@code this}
285 */
286 public DnsNameResolverBuilder dnsQueryLifecycleObserverFactory(DnsQueryLifecycleObserverFactory
287 lifecycleObserverFactory) {
288 this.dnsQueryLifecycleObserverFactory = checkNotNull(lifecycleObserverFactory, "lifecycleObserverFactory");
289 return this;
290 }
291
292 /**
293 * Sets the cache for authoritative NS servers
294 *
295 * @param authoritativeDnsServerCache the authoritative NS servers cache
296 * @return {@code this}
297 * @deprecated Use {@link #authoritativeDnsServerCache(AuthoritativeDnsServerCache)}
298 */
299 @Deprecated
300 public DnsNameResolverBuilder authoritativeDnsServerCache(DnsCache authoritativeDnsServerCache) {
301 this.authoritativeDnsServerCache = new AuthoritativeDnsServerCacheAdapter(authoritativeDnsServerCache);
302 return this;
303 }
304
305 /**
306 * Sets the cache for authoritative NS servers
307 *
308 * @param authoritativeDnsServerCache the authoritative NS servers cache
309 * @return {@code this}
310 */
311 public DnsNameResolverBuilder authoritativeDnsServerCache(AuthoritativeDnsServerCache authoritativeDnsServerCache) {
312 this.authoritativeDnsServerCache = authoritativeDnsServerCache;
313 return this;
314 }
315
316 /**
317 * Configure the address that will be used to bind too. If {@code null} the default will be used.
318 * @param localAddress the bind address
319 * @return {@code this}
320 */
321 public DnsNameResolverBuilder localAddress(SocketAddress localAddress) {
322 this.localAddress = localAddress;
323 return this;
324 }
325
326 /**
327 * Sets the minimum and maximum TTL of the cached DNS resource records (in seconds). If the TTL of the DNS
328 * resource record returned by the DNS server is less than the minimum TTL or greater than the maximum TTL,
329 * this resolver will ignore the TTL from the DNS server and use the minimum TTL or the maximum TTL instead
330 * respectively.
331 * The default value is {@code 0} and {@link Integer#MAX_VALUE}, which practically tells this resolver to
332 * respect the TTL from the DNS server.
333 *
334 * @param minTtl the minimum TTL
335 * @param maxTtl the maximum TTL
336 * @return {@code this}
337 */
338 public DnsNameResolverBuilder ttl(int minTtl, int maxTtl) {
339 this.maxTtl = maxTtl;
340 this.minTtl = minTtl;
341 return this;
342 }
343
344 /**
345 * Sets the TTL of the cache for the failed DNS queries (in seconds).
346 *
347 * @param negativeTtl the TTL for failed cached queries
348 * @return {@code this}
349 */
350 public DnsNameResolverBuilder negativeTtl(int negativeTtl) {
351 this.negativeTtl = negativeTtl;
352 return this;
353 }
354
355 /**
356 * Sets the timeout of each DNS query performed by this resolver (in milliseconds).
357 * {@code 0} disables the timeout. If not set or a negative number is set, the default timeout is used.
358 *
359 * @param queryTimeoutMillis the query timeout
360 * @return {@code this}
361 */
362 public DnsNameResolverBuilder queryTimeoutMillis(long queryTimeoutMillis) {
363 this.queryTimeoutMillis = queryTimeoutMillis;
364 return this;
365 }
366
367 /**
368 * Compute a {@link ResolvedAddressTypes} from some {@link InternetProtocolFamily}s.
369 * An empty input will return the default value, based on "java.net" System properties.
370 * Valid inputs are (), (IPv4), (IPv6), (Ipv4, IPv6) and (IPv6, IPv4).
371 *
372 * @param internetProtocolFamilies a valid sequence of {@link InternetProtocolFamily}s
373 * @return a {@link ResolvedAddressTypes}
374 * @deprecated use {@link #toResolvedAddressTypes(SocketProtocolFamily...)}
375 */
376 @Deprecated
377 public static ResolvedAddressTypes computeResolvedAddressTypes(InternetProtocolFamily... internetProtocolFamilies) {
378 if (internetProtocolFamilies == null || internetProtocolFamilies.length == 0) {
379 return DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
380 }
381 if (internetProtocolFamilies.length > 2) {
382 throw new IllegalArgumentException("No more than 2 InternetProtocolFamilies");
383 }
384 return toResolvedAddressTypes(toSocketProtocolFamilies(internetProtocolFamilies));
385 }
386
387 private static SocketProtocolFamily[] toSocketProtocolFamilies(InternetProtocolFamily... internetProtocolFamilies) {
388 if (internetProtocolFamilies == null || internetProtocolFamilies.length == 0) {
389 return null;
390 }
391 SocketProtocolFamily[] socketProtocolFamilies = new SocketProtocolFamily[internetProtocolFamilies.length];
392 for (int i = 0; i < internetProtocolFamilies.length; i++) {
393 socketProtocolFamilies[i] = internetProtocolFamilies[i].toSocketProtocolFamily();
394 }
395 return socketProtocolFamilies;
396 }
397
398 /**
399 * Compute a {@link ResolvedAddressTypes} from some {@link SocketProtocolFamily}s.
400 * An empty input will return the default value, based on "java.net" System properties.
401 * Valid inputs are (), (IPv4), (IPv6), (Ipv4, IPv6) and (IPv6, IPv4).
402 * @param socketProtocolFamilies a valid sequence of {@link SocketProtocolFamily}s
403 * @return a {@link ResolvedAddressTypes}
404 */
405 public static ResolvedAddressTypes toResolvedAddressTypes(SocketProtocolFamily... socketProtocolFamilies) {
406 if (socketProtocolFamilies == null || socketProtocolFamilies.length == 0) {
407 return DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
408 }
409 if (socketProtocolFamilies.length > 2) {
410 throw new IllegalArgumentException("No more than 2 socketProtocolFamilies");
411 }
412
413 switch(socketProtocolFamilies[0]) {
414 case INET:
415 return (socketProtocolFamilies.length >= 2
416 && socketProtocolFamilies[1] == SocketProtocolFamily.INET6) ?
417 ResolvedAddressTypes.IPV4_PREFERRED: ResolvedAddressTypes.IPV4_ONLY;
418 case INET6:
419 return (socketProtocolFamilies.length >= 2
420 && socketProtocolFamilies[1] == SocketProtocolFamily.INET) ?
421 ResolvedAddressTypes.IPV6_PREFERRED: ResolvedAddressTypes.IPV6_ONLY;
422 default:
423 throw new IllegalArgumentException(
424 "Couldn't resolve ResolvedAddressTypes from InternetProtocolFamily array");
425 }
426 }
427
428 /**
429 * Sets the list of the protocol families of the address resolved.
430 * You can use {@link DnsNameResolverBuilder#computeResolvedAddressTypes(InternetProtocolFamily...)}
431 * to get a {@link ResolvedAddressTypes} out of some {@link InternetProtocolFamily}s.
432 *
433 * @param resolvedAddressTypes the address types
434 * @return {@code this}
435 */
436 public DnsNameResolverBuilder resolvedAddressTypes(ResolvedAddressTypes resolvedAddressTypes) {
437 this.resolvedAddressTypes = resolvedAddressTypes;
438 return this;
439 }
440
441 /**
442 * If {@code true} {@link DnsNameResolver#resolveAll(String)} will notify the returned {@link Future} as
443 * soon as all queries for the preferred address-type are complete.
444 *
445 * @param completeOncePreferredResolved {@code true} to enable, {@code false} to disable.
446 * @return {@code this}
447 */
448 public DnsNameResolverBuilder completeOncePreferredResolved(boolean completeOncePreferredResolved) {
449 this.completeOncePreferredResolved = completeOncePreferredResolved;
450 return this;
451 }
452
453 /**
454 * Sets if this resolver has to send a DNS query with the RD (recursion desired) flag set.
455 *
456 * @param recursionDesired true if recursion is desired
457 * @return {@code this}
458 */
459 public DnsNameResolverBuilder recursionDesired(boolean recursionDesired) {
460 this.recursionDesired = recursionDesired;
461 return this;
462 }
463
464 /**
465 * Sets the maximum allowed number of DNS queries to send when resolving a host name.
466 *
467 * @param maxQueriesPerResolve the max number of queries
468 * @return {@code this}
469 */
470 public DnsNameResolverBuilder maxQueriesPerResolve(int maxQueriesPerResolve) {
471 this.maxQueriesPerResolve = maxQueriesPerResolve;
472 return this;
473 }
474
475 /**
476 * Sets if this resolver should generate the detailed trace information in an exception message so that
477 * it is easier to understand the cause of resolution failure.
478 *
479 * @param traceEnabled true if trace is enabled
480 * @return {@code this}
481 * @deprecated Prefer to {@linkplain #dnsQueryLifecycleObserverFactory(DnsQueryLifecycleObserverFactory) configure}
482 * a {@link LoggingDnsQueryLifeCycleObserverFactory} instead.
483 */
484 @Deprecated
485 public DnsNameResolverBuilder traceEnabled(boolean traceEnabled) {
486 this.traceEnabled = traceEnabled;
487 return this;
488 }
489
490 /**
491 * Sets the capacity of the datagram packet buffer (in bytes). The default value is {@code 4096} bytes.
492 *
493 * @param maxPayloadSize the capacity of the datagram packet buffer
494 * @return {@code this}
495 */
496 public DnsNameResolverBuilder maxPayloadSize(int maxPayloadSize) {
497 this.maxPayloadSize = maxPayloadSize;
498 return this;
499 }
500
501 /**
502 * Enable the automatic inclusion of a optional records that tries to give the remote DNS server a hint about
503 * how much data the resolver can read per response. Some DNSServer may not support this and so fail to answer
504 * queries. If you find problems you may want to disable this.
505 *
506 * @param optResourceEnabled if optional records inclusion is enabled
507 * @return {@code this}
508 */
509 public DnsNameResolverBuilder optResourceEnabled(boolean optResourceEnabled) {
510 this.optResourceEnabled = optResourceEnabled;
511 return this;
512 }
513
514 /**
515 * @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to first check
516 * if the hostname is locally aliased.
517 * @return {@code this}
518 */
519 public DnsNameResolverBuilder hostsFileEntriesResolver(HostsFileEntriesResolver hostsFileEntriesResolver) {
520 this.hostsFileEntriesResolver = hostsFileEntriesResolver;
521 return this;
522 }
523
524 protected DnsServerAddressStreamProvider nameServerProvider() {
525 return this.dnsServerAddressStreamProvider;
526 }
527
528 /**
529 * Set the {@link DnsServerAddressStreamProvider} which is used to determine which DNS server is used to resolve
530 * each hostname.
531 * @return {@code this}.
532 */
533 public DnsNameResolverBuilder nameServerProvider(DnsServerAddressStreamProvider dnsServerAddressStreamProvider) {
534 this.dnsServerAddressStreamProvider =
535 checkNotNull(dnsServerAddressStreamProvider, "dnsServerAddressStreamProvider");
536 return this;
537 }
538
539 protected DnsServerAddressStream queryServerAddressStream() {
540 return this.queryDnsServerAddressStream;
541 }
542
543 /**
544 * Set the {@link DnsServerAddressStream} which provides the server address for DNS queries.
545 * @return {@code this}.
546 */
547 public DnsNameResolverBuilder queryServerAddressStream(DnsServerAddressStream queryServerAddressStream) {
548 this.queryDnsServerAddressStream =
549 checkNotNull(queryServerAddressStream, "queryServerAddressStream");
550 return this;
551 }
552
553 /**
554 * Set the list of search domains of the resolver.
555 *
556 * @param searchDomains the search domains
557 * @return {@code this}
558 */
559 public DnsNameResolverBuilder searchDomains(Iterable<String> searchDomains) {
560 checkNotNull(searchDomains, "searchDomains");
561
562 final Set<String> domains = new LinkedHashSet<String>(4);
563
564 for (String f : searchDomains) {
565 if (f == null) {
566 break;
567 }
568
569 // Avoid duplicate entries.
570 domains.add(f);
571 }
572
573 this.searchDomains = domains.toArray(EmptyArrays.EMPTY_STRINGS);
574 return this;
575 }
576
577 /**
578 * Set the number of dots which must appear in a name before an initial absolute query is made.
579 * The default value is {@code 1}.
580 *
581 * @param ndots the ndots value
582 * @return {@code this}
583 */
584 public DnsNameResolverBuilder ndots(int ndots) {
585 this.ndots = ndots;
586 return this;
587 }
588
589 DnsCache getOrNewCache() {
590 if (this.resolveCache != null) {
591 return this.resolveCache;
592 }
593 return new DefaultDnsCache(intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE), intValue(negativeTtl, 0));
594 }
595
596 AuthoritativeDnsServerCache getOrNewAuthoritativeDnsServerCache() {
597 if (this.authoritativeDnsServerCache != null) {
598 return this.authoritativeDnsServerCache;
599 }
600 return new DefaultAuthoritativeDnsServerCache(
601 intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE),
602 // Let us use the sane ordering as DnsNameResolver will be used when returning
603 // nameservers from the cache.
604 new NameServerComparator(DnsNameResolver.addressType(
605 DnsNameResolver.preferredAddressType(resolvedAddressTypes))));
606 }
607
608 private DnsServerAddressStream newQueryServerAddressStream(
609 DnsServerAddressStreamProvider dnsServerAddressStreamProvider) {
610 return new ThreadLocalNameServerAddressStream(dnsServerAddressStreamProvider);
611 }
612
613 DnsCnameCache getOrNewCnameCache() {
614 if (this.cnameCache != null) {
615 return this.cnameCache;
616 }
617 return new DefaultDnsCnameCache(
618 intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE));
619 }
620
621 /**
622 * Set if domain / host names should be decoded to unicode when received.
623 * See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
624 *
625 * @param decodeIdn if should get decoded
626 * @return {@code this}
627 */
628 public DnsNameResolverBuilder decodeIdn(boolean decodeIdn) {
629 this.decodeIdn = decodeIdn;
630 return this;
631 }
632
633 /**
634 * Set the maximum size of the cache that is used to consolidate lookups for different hostnames when in-flight.
635 * This means if multiple lookups are done for the same hostname and still in-flight only one actual query will
636 * be made and the result will be cascaded to the others.
637 *
638 * @param maxNumConsolidation the maximum lookups to consolidate (different hostnames), or {@code 0} if
639 * no consolidation should be performed.
640 * @return {@code this}
641 */
642 public DnsNameResolverBuilder consolidateCacheSize(int maxNumConsolidation) {
643 this.maxNumConsolidation = ObjectUtil.checkPositiveOrZero(maxNumConsolidation, "maxNumConsolidation");
644 return this;
645 }
646
647 /**
648 * Set the strategy that is used to determine how a {@link DatagramChannel} is used by the resolver for sending
649 * queries over UDP protocol.
650 *
651 * @param datagramChannelStrategy the {@link DnsNameResolverChannelStrategy} to use when doing queries over
652 * UDP protocol.
653 * @return {@code this}
654 */
655 public DnsNameResolverBuilder datagramChannelStrategy(DnsNameResolverChannelStrategy datagramChannelStrategy) {
656 this.datagramChannelStrategy = ObjectUtil.checkNotNull(datagramChannelStrategy, "datagramChannelStrategy");
657 return this;
658 }
659
660 /**
661 * Enables or disables the randomization of the case of the name in the questions of UDP queries, also known as
662 * "0x20". The response is only accepted if it echoes the name with exactly the same case, which adds entropy
663 * that an off-path attacker needs to guess in order to spoof a response, on top of the query id and the source
664 * port. See <a href="https://tools.ietf.org/html/rfc5452#section-9.2">RFC 5452</a>.
665 * <p>
666 * Some nameservers do not preserve the case of the name in their responses. Queries to these will time out
667 * if this is enabled. This is disabled by default.
668 *
669 * @param caseRandomizationEnabled {@code true} if the case of the name should be randomized.
670 * @return {@code this}
671 */
672 public DnsNameResolverBuilder caseRandomizationEnabled(boolean caseRandomizationEnabled) {
673 this.caseRandomizationEnabled = caseRandomizationEnabled;
674 return this;
675 }
676
677 /**
678 * Enables or disables the sending of a random client cookie with every UDP query, as defined by
679 * <a href="https://tools.ietf.org/html/rfc7873">RFC 7873</a>. A response is rejected if it carries a cookie
680 * that does not echo the client cookie. Once a nameserver was seen to echo the client cookie, responses from it
681 * that omit the cookie are rejected as well.
682 * <p>
683 * The cookie is part of the OPT record, so this has no effect on queries that do not include one, which is the
684 * case if {@link #optResourceEnabled(boolean)} is {@code false} or if the query already has an OPT record.
685 * Nameservers that require a valid server cookie and so answer with {@code BADCOOKIE} are not supported.
686 * This is disabled by default.
687 *
688 * @param dnsCookiesEnabled {@code true} if DNS cookies should be used.
689 * @return {@code this}
690 */
691 public DnsNameResolverBuilder dnsCookiesEnabled(boolean dnsCookiesEnabled) {
692 this.dnsCookiesEnabled = dnsCookiesEnabled;
693 return this;
694 }
695
696 /**
697 * Returns a new {@link DnsNameResolver} instance.
698 *
699 * @return a {@link DnsNameResolver}
700 */
701 public DnsNameResolver build() {
702 if (eventLoop == null) {
703 throw new IllegalStateException("eventLoop should be specified to build a DnsNameResolver.");
704 }
705
706 if (resolveCache != null && (minTtl != null || maxTtl != null || negativeTtl != null)) {
707 logger.debug("resolveCache and TTLs are mutually exclusive. TTLs are ignored.");
708 }
709
710 if (cnameCache != null && (minTtl != null || maxTtl != null || negativeTtl != null)) {
711 logger.debug("cnameCache and TTLs are mutually exclusive. TTLs are ignored.");
712 }
713
714 if (authoritativeDnsServerCache != null && (minTtl != null || maxTtl != null || negativeTtl != null)) {
715 logger.debug("authoritativeDnsServerCache and TTLs are mutually exclusive. TTLs are ignored.");
716 }
717
718 DnsCache resolveCache = getOrNewCache();
719 DnsCnameCache cnameCache = getOrNewCnameCache();
720 AuthoritativeDnsServerCache authoritativeDnsServerCache = getOrNewAuthoritativeDnsServerCache();
721
722 DnsServerAddressStream queryDnsServerAddressStream = this.queryDnsServerAddressStream != null ?
723 this.queryDnsServerAddressStream : newQueryServerAddressStream(dnsServerAddressStreamProvider);
724
725 return new DnsNameResolver(
726 eventLoop,
727 datagramChannelFactory,
728 socketChannelFactory,
729 retryOnTimeout,
730 resolveCache,
731 cnameCache,
732 authoritativeDnsServerCache,
733 localAddress,
734 dnsQueryLifecycleObserverFactory,
735 queryTimeoutMillis,
736 resolvedAddressTypes,
737 recursionDesired,
738 maxQueriesPerResolve,
739 traceEnabled,
740 maxPayloadSize,
741 optResourceEnabled,
742 hostsFileEntriesResolver,
743 dnsServerAddressStreamProvider,
744 queryDnsServerAddressStream,
745 searchDomains,
746 ndots,
747 decodeIdn,
748 completeOncePreferredResolved,
749 maxNumConsolidation,
750 datagramChannelStrategy,
751 caseRandomizationEnabled,
752 dnsCookiesEnabled);
753 }
754
755 /**
756 * Creates a copy of this {@link DnsNameResolverBuilder}
757 *
758 * @return {@link DnsNameResolverBuilder}
759 */
760 public DnsNameResolverBuilder copy() {
761 DnsNameResolverBuilder copiedBuilder = new DnsNameResolverBuilder();
762
763 if (eventLoop != null) {
764 copiedBuilder.eventLoop(eventLoop);
765 }
766
767 if (datagramChannelFactory != null) {
768 copiedBuilder.datagramChannelFactory(datagramChannelFactory);
769 }
770
771 copiedBuilder.socketChannelFactory(socketChannelFactory, retryOnTimeout);
772
773 if (resolveCache != null) {
774 copiedBuilder.resolveCache(resolveCache);
775 }
776
777 if (cnameCache != null) {
778 copiedBuilder.cnameCache(cnameCache);
779 }
780 if (maxTtl != null && minTtl != null) {
781 copiedBuilder.ttl(minTtl, maxTtl);
782 }
783
784 if (negativeTtl != null) {
785 copiedBuilder.negativeTtl(negativeTtl);
786 }
787
788 if (authoritativeDnsServerCache != null) {
789 copiedBuilder.authoritativeDnsServerCache(authoritativeDnsServerCache);
790 }
791
792 if (dnsQueryLifecycleObserverFactory != null) {
793 copiedBuilder.dnsQueryLifecycleObserverFactory(dnsQueryLifecycleObserverFactory);
794 }
795
796 copiedBuilder.queryTimeoutMillis(queryTimeoutMillis);
797 copiedBuilder.resolvedAddressTypes(resolvedAddressTypes);
798 copiedBuilder.recursionDesired(recursionDesired);
799 copiedBuilder.maxQueriesPerResolve(maxQueriesPerResolve);
800 copiedBuilder.traceEnabled(traceEnabled);
801 copiedBuilder.maxPayloadSize(maxPayloadSize);
802 copiedBuilder.optResourceEnabled(optResourceEnabled);
803 copiedBuilder.hostsFileEntriesResolver(hostsFileEntriesResolver);
804
805 if (dnsServerAddressStreamProvider != null) {
806 copiedBuilder.nameServerProvider(dnsServerAddressStreamProvider);
807 }
808
809 if (queryDnsServerAddressStream != null) {
810 copiedBuilder.queryServerAddressStream(queryDnsServerAddressStream);
811 }
812
813 if (searchDomains != null) {
814 copiedBuilder.searchDomains(Arrays.asList(searchDomains));
815 }
816
817 copiedBuilder.ndots(ndots);
818 copiedBuilder.decodeIdn(decodeIdn);
819 copiedBuilder.completeOncePreferredResolved(completeOncePreferredResolved);
820 copiedBuilder.localAddress(localAddress);
821 copiedBuilder.consolidateCacheSize(maxNumConsolidation);
822 copiedBuilder.datagramChannelStrategy(datagramChannelStrategy);
823 copiedBuilder.caseRandomizationEnabled(caseRandomizationEnabled);
824 copiedBuilder.dnsCookiesEnabled(dnsCookiesEnabled);
825 return copiedBuilder;
826 }
827 }