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