View Javadoc
1   /*
2    * Copyright 2014 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  
17  package io.netty.resolver.dns;
18  
19  import io.netty.util.internal.PlatformDependent;
20  import io.netty.util.internal.ThreadLocalRandom;
21  import io.netty.util.internal.logging.InternalLogger;
22  import io.netty.util.internal.logging.InternalLoggerFactory;
23  
24  import java.lang.reflect.Method;
25  import java.net.InetAddress;
26  import java.net.InetSocketAddress;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.NoSuchElementException;
33  import java.util.Random;
34  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
35  
36  /**
37   * Provides a sequence of DNS server addresses to {@link DnsNameResolver}.  The {@link Iterator} created by the
38   * {@link Iterable}s returned by the factory methods of this class is infinite, which means {@link Iterator#hasNext()}
39   * will never return {@code false} and {@link Iterator#next()} will never raise a {@link NoSuchElementException}.
40   */
41  @SuppressWarnings("IteratorNextCanNotThrowNoSuchElementException")
42  public final class DnsServerAddresses {
43  
44      private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsServerAddresses.class);
45  
46      private static final List<InetSocketAddress> DEFAULT_NAME_SERVER_LIST;
47      private static final InetSocketAddress[] DEFAULT_NAME_SERVER_ARRAY;
48  
49      static {
50          final int DNS_PORT = 53;
51          final List<InetSocketAddress> defaultNameServers = new ArrayList<InetSocketAddress>(2);
52          try {
53              Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
54              Method open = configClass.getMethod("open");
55              Method nameservers = configClass.getMethod("nameservers");
56              Object instance = open.invoke(null);
57  
58              @SuppressWarnings("unchecked")
59              final List<String> list = (List<String>) nameservers.invoke(instance);
60              final int size = list.size();
61              for (int i = 0; i < size; i ++) {
62                  String dnsAddr = list.get(i);
63                  if (dnsAddr != null) {
64                      defaultNameServers.add(new InetSocketAddress(InetAddress.getByName(dnsAddr), DNS_PORT));
65                  }
66              }
67          } catch (Exception ignore) {
68              // Failed to get the system name server list.
69              // Will add the default name servers afterwards.
70          }
71  
72          if (!defaultNameServers.isEmpty()) {
73              if (logger.isDebugEnabled()) {
74                  logger.debug(
75                          "Default DNS servers: {} (sun.net.dns.ResolverConfiguration)", defaultNameServers);
76              }
77          } else {
78              Collections.addAll(
79                      defaultNameServers,
80                      new InetSocketAddress("8.8.8.8", DNS_PORT),
81                      new InetSocketAddress("8.8.4.4", DNS_PORT));
82  
83              if (logger.isWarnEnabled()) {
84                  logger.warn(
85                          "Default DNS servers: {} (Google Public DNS as a fallback)", defaultNameServers);
86              }
87          }
88  
89          DEFAULT_NAME_SERVER_LIST = Collections.unmodifiableList(defaultNameServers);
90          DEFAULT_NAME_SERVER_ARRAY = defaultNameServers.toArray(new InetSocketAddress[defaultNameServers.size()]);
91      }
92  
93      /**
94       * Returns the list of the system DNS server addresses.  If it failed to retrieve the list of the system DNS server
95       * addresses from the environment, it will return {@code "8.8.8.8"} and {@code "8.8.4.4"}, the addresses of the
96       * Google public DNS servers.  Note that the {@code Iterator} of the returned list is not infinite unlike other
97       * factory methods in this class.  To make the returned list infinite, pass it to the other factory method. e.g.
98       * <pre>
99       * addresses = {@link #sequential(Iterable) sequential}({@link #defaultAddresses()});
100      * </pre>
101      */
102     public static List<InetSocketAddress> defaultAddresses() {
103         return DEFAULT_NAME_SERVER_LIST;
104     }
105 
106     /**
107      * Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
108      * the DNS server addresses in a sequential order.
109      */
110     public static Iterable<InetSocketAddress> sequential(Iterable<? extends InetSocketAddress> addresses) {
111         return sequential0(sanitize(addresses));
112     }
113 
114     /**
115      * Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
116      * the DNS server addresses in a sequential order.
117      */
118     public static Iterable<InetSocketAddress> sequential(InetSocketAddress... addresses) {
119         return sequential0(sanitize(addresses));
120     }
121 
122     private static Iterable<InetSocketAddress> sequential0(final InetSocketAddress[] addresses) {
123         return new Iterable<InetSocketAddress>() {
124             @Override
125             public Iterator<InetSocketAddress> iterator() {
126                 return new SequentialAddressIterator(addresses, 0);
127             }
128         };
129     }
130 
131     /**
132      * Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
133      * the DNS server addresses in a shuffled order.
134      */
135     public static Iterable<InetSocketAddress> shuffled(Iterable<? extends InetSocketAddress> addresses) {
136         return shuffled0(sanitize(addresses));
137     }
138 
139     /**
140      * Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
141      * the DNS server addresses in a shuffled order.
142      */
143     public static Iterable<InetSocketAddress> shuffled(InetSocketAddress... addresses) {
144         return shuffled0(sanitize(addresses));
145     }
146 
147     private static Iterable<InetSocketAddress> shuffled0(final InetSocketAddress[] addresses) {
148         if (addresses.length == 1) {
149             return singleton(addresses[0]);
150         }
151 
152         return new Iterable<InetSocketAddress>() {
153             @Override
154             public Iterator<InetSocketAddress> iterator() {
155                 return new ShuffledAddressIterator(addresses);
156             }
157         };
158     }
159 
160     /**
161      * Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
162      * the DNS server addresses in a rotational order.  It is similar to {@link #sequential(Iterable)}, but each
163      * {@link Iterator} starts from a different starting point.  For example, the first {@link Iterable#iterator()}
164      * will iterate from the first DNS server address, the second one will iterate from the second DNS server address,
165      * and so on.
166      */
167     public static Iterable<InetSocketAddress> rotational(Iterable<? extends InetSocketAddress> addresses) {
168         return rotational0(sanitize(addresses));
169     }
170 
171     /**
172      * Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
173      * the DNS server addresses in a rotational order.  It is similar to {@link #sequential(Iterable)}, but each
174      * {@link Iterator} starts from a different starting point.  For example, the first {@link Iterable#iterator()}
175      * will iterate from the first DNS server address, the second one will iterate from the second DNS server address,
176      * and so on.
177      */
178     public static Iterable<InetSocketAddress> rotational(InetSocketAddress... addresses) {
179         return rotational0(sanitize(addresses));
180     }
181 
182     private static Iterable<InetSocketAddress> rotational0(final InetSocketAddress[] addresses) {
183         return new RotationalAddresses(addresses);
184     }
185 
186     /**
187      * Returns an infinite {@link Iterable} of the specified DNS server address, whose {@link Iterator} always
188      * return the same DNS server address.
189      */
190     public static Iterable<InetSocketAddress> singleton(final InetSocketAddress address) {
191         if (address == null) {
192             throw new NullPointerException("address");
193         }
194         if (address.isUnresolved()) {
195             throw new IllegalArgumentException("cannot use an unresolved DNS server address: " + address);
196         }
197 
198         return new Iterable<InetSocketAddress>() {
199 
200             private final Iterator<InetSocketAddress> iterator = new Iterator<InetSocketAddress>() {
201                 @Override
202                 public boolean hasNext() {
203                     return true;
204                 }
205 
206                 @Override
207                 public InetSocketAddress next() {
208                     return address;
209                 }
210 
211                 @Override
212                 public void remove() {
213                     throw new UnsupportedOperationException();
214                 }
215             };
216 
217             @Override
218             public Iterator<InetSocketAddress> iterator() {
219                 return iterator;
220             }
221         };
222     }
223 
224     private static InetSocketAddress[] sanitize(Iterable<? extends InetSocketAddress> addresses) {
225         if (addresses == null) {
226             throw new NullPointerException("addresses");
227         }
228 
229         final List<InetSocketAddress> list;
230         if (addresses instanceof Collection) {
231             list = new ArrayList<InetSocketAddress>(((Collection<?>) addresses).size());
232         } else {
233             list = new ArrayList<InetSocketAddress>(4);
234         }
235 
236         for (InetSocketAddress a : addresses) {
237             if (a == null) {
238                 break;
239             }
240             if (a.isUnresolved()) {
241                 throw new IllegalArgumentException("cannot use an unresolved DNS server address: " + a);
242             }
243             list.add(a);
244         }
245 
246         if (list.isEmpty()) {
247             return DEFAULT_NAME_SERVER_ARRAY;
248         }
249 
250         return list.toArray(new InetSocketAddress[list.size()]);
251     }
252 
253     private static InetSocketAddress[] sanitize(InetSocketAddress[] addresses) {
254         if (addresses == null) {
255             throw new NullPointerException("addresses");
256         }
257 
258         List<InetSocketAddress> list = new ArrayList<InetSocketAddress>(addresses.length);
259         for (InetSocketAddress a: addresses) {
260             if (a == null) {
261                 break;
262             }
263             if (a.isUnresolved()) {
264                 throw new IllegalArgumentException("cannot use an unresolved DNS server address: " + a);
265             }
266             list.add(a);
267         }
268 
269         if (list.isEmpty()) {
270             return DEFAULT_NAME_SERVER_ARRAY;
271         }
272 
273         return list.toArray(new InetSocketAddress[list.size()]);
274     }
275 
276     private DnsServerAddresses() { }
277 
278     private static final class SequentialAddressIterator implements Iterator<InetSocketAddress> {
279 
280         private final InetSocketAddress[] addresses;
281         private int i;
282 
283         SequentialAddressIterator(InetSocketAddress[] addresses, int startIdx) {
284             this.addresses = addresses;
285             i = startIdx;
286         }
287 
288         @Override
289         public boolean hasNext() {
290             return true;
291         }
292 
293         @Override
294         public InetSocketAddress next() {
295             int i = this.i;
296             InetSocketAddress next = addresses[i];
297             if (++ i < addresses.length) {
298                 this.i = i;
299             } else {
300                 this.i = 0;
301             }
302             return next;
303         }
304 
305         @Override
306         public void remove() {
307             throw new UnsupportedOperationException();
308         }
309     }
310 
311     private static final class ShuffledAddressIterator implements Iterator<InetSocketAddress> {
312 
313         private final InetSocketAddress[] addresses;
314         private int i;
315 
316         ShuffledAddressIterator(InetSocketAddress[] addresses) {
317             this.addresses = addresses.clone();
318 
319             shuffle();
320         }
321 
322         private void shuffle() {
323             final InetSocketAddress[] addresses = this.addresses;
324             final Random r = ThreadLocalRandom.current();
325 
326             for (int i = addresses.length - 1; i >= 0; i --) {
327                 InetSocketAddress tmp = addresses[i];
328                 int j = r.nextInt(i + 1);
329                 addresses[i] = addresses[j];
330                 addresses[j] = tmp;
331             }
332         }
333 
334         @Override
335         public boolean hasNext() {
336             return true;
337         }
338 
339         @Override
340         public InetSocketAddress next() {
341             int i = this.i;
342             InetSocketAddress next = addresses[i];
343             if (++ i < addresses.length) {
344                 this.i = i;
345             } else {
346                 this.i = 0;
347                 shuffle();
348             }
349             return next;
350         }
351 
352         @Override
353         public void remove() {
354             throw new UnsupportedOperationException();
355         }
356     }
357 
358     private static final class RotationalAddresses implements Iterable<InetSocketAddress> {
359 
360         private static final AtomicIntegerFieldUpdater<RotationalAddresses> startIdxUpdater;
361 
362         static {
363             AtomicIntegerFieldUpdater<RotationalAddresses> updater =
364                     PlatformDependent.newAtomicIntegerFieldUpdater(RotationalAddresses.class, "startIdx");
365 
366             if (updater == null) {
367                 updater = AtomicIntegerFieldUpdater.newUpdater(RotationalAddresses.class, "startIdx");
368             }
369 
370             startIdxUpdater = updater;
371         }
372 
373         private final InetSocketAddress[] addresses;
374         @SuppressWarnings("UnusedDeclaration")
375         private volatile int startIdx;
376 
377         RotationalAddresses(InetSocketAddress[] addresses) {
378             this.addresses = addresses;
379         }
380 
381         @Override
382         public Iterator<InetSocketAddress> iterator() {
383             for (;;) {
384                 int curStartIdx = startIdx;
385                 int nextStartIdx = curStartIdx + 1;
386                 if (nextStartIdx >= addresses.length) {
387                     nextStartIdx = 0;
388                 }
389                 if (startIdxUpdater.compareAndSet(this, curStartIdx, nextStartIdx)) {
390                     return new SequentialAddressIterator(addresses, curStartIdx);
391                 }
392             }
393         }
394     }
395 }