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