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