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