View Javadoc
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 }