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