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